From c244cf46b017b23b295f00b1c7e322ba2251d760 Mon Sep 17 00:00:00 2001 From: Derek Parker Date: Mon, 2 Aug 2021 14:35:51 -0700 Subject: [PATCH] Backport fix allowing LTO to be enabled on cgo sources Resolves: rhbz#1904567 --- cgo-lto-fix.patch | 289 ++++++++++++++++++++++++++++++++++++++++++++++ golang.spec | 8 ++ 2 files changed, 297 insertions(+) create mode 100644 cgo-lto-fix.patch diff --git a/cgo-lto-fix.patch b/cgo-lto-fix.patch new file mode 100644 index 0000000..2aca0e3 --- /dev/null +++ b/cgo-lto-fix.patch @@ -0,0 +1,289 @@ +From 24e9707cbfa6b1ed6abdd4b11f9ddaf3aac5ad88 Mon Sep 17 00:00:00 2001 +From: Ian Lance Taylor +Date: Tue, 25 May 2021 16:31:41 -0700 +Subject: [PATCH] cmd/link, cmd/cgo: support -flto in CFLAGS + +The linker now accepts unrecognized object files in external linking mode. +These objects will simply be passed to the external linker. +This permits using -flto which can generate pure byte code objects, +whose symbol table the linker does not know how to read. + +The cgo tool now passes -fno-lto when generating objects whose symbols +it needs to read. The cgo tool now emits matching types in different +objects, so that the lto linker does not report a mismatch. + +This is based on https://golang.org/cl/293290 by Derek Parker. + +For #43505 +Fixes #43830 +Fixes #46295 + +Change-Id: I6787de213417466784ddef5af8899e453b4ae1ad +Reviewed-on: https://go-review.googlesource.com/c/go/+/322614 +Trust: Ian Lance Taylor +Run-TryBot: Ian Lance Taylor +TryBot-Result: Go Bot +Reviewed-by: Michael Hudson-Doyle +--- + +diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go +index ae61725..a73e998 100644 +--- a/src/cmd/cgo/gcc.go ++++ b/src/cmd/cgo/gcc.go +@@ -1638,6 +1638,8 @@ + c = append(c, "-maix64") + c = append(c, "-mcmodel=large") + } ++ // disable LTO so we get an object whose symbols we can read ++ c = append(c, "-fno-lto") + c = append(c, "-") //read input from standard input + return c + } +diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go +index 8c31d5b..94152f4 100644 +--- a/src/cmd/cgo/out.go ++++ b/src/cmd/cgo/out.go +@@ -168,8 +168,18 @@ + if *gccgo { + fmt.Fprintf(fc, "extern byte *%s;\n", n.C) + } else { +- fmt.Fprintf(fm, "extern char %s[];\n", n.C) +- fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C) ++ // Force a reference to all symbols so that ++ // the external linker will add DT_NEEDED ++ // entries as needed on ELF systems. ++ // Treat function variables differently ++ // to avoid type confict errors from LTO ++ // (Link Time Optimization). ++ if n.Kind == "fpvar" { ++ fmt.Fprintf(fm, "extern void %s();\n", n.C) ++ } else { ++ fmt.Fprintf(fm, "extern char %s[];\n", n.C) ++ fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C) ++ } + fmt.Fprintf(fgo2, "//go:linkname __cgo_%s %s\n", n.C, n.C) + fmt.Fprintf(fgo2, "//go:cgo_import_static %s\n", n.C) + fmt.Fprintf(fgo2, "var __cgo_%s byte\n", n.C) +@@ -1042,7 +1052,7 @@ + fmt.Fprintf(fgo2, "//go:cgo_export_static _cgoexp%s_%s\n", cPrefix, exp.ExpName) + fmt.Fprintf(fgo2, "func _cgoexp%s_%s(a *%s) {\n", cPrefix, exp.ExpName, gotype) + +- fmt.Fprintf(fm, "int _cgoexp%s_%s;\n", cPrefix, exp.ExpName) ++ fmt.Fprintf(fm, "void _cgoexp%s_%s(void* p){}\n", cPrefix, exp.ExpName) + + if gccResult != "void" { + // Write results back to frame. +diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go +index 50bf80b..bc49c6d 100644 +--- a/src/cmd/dist/test.go ++++ b/src/cmd/dist/test.go +@@ -722,14 +722,29 @@ + }, + }) + if t.hasCxx() { +- t.tests = append(t.tests, distTest{ +- name: "swig_callback", +- heading: "../misc/swig/callback", +- fn: func(dt *distTest) error { +- t.addCmd(dt, "misc/swig/callback", t.goTest()) +- return nil ++ t.tests = append(t.tests, ++ distTest{ ++ name: "swig_callback", ++ heading: "../misc/swig/callback", ++ fn: func(dt *distTest) error { ++ t.addCmd(dt, "misc/swig/callback", t.goTest()) ++ return nil ++ }, + }, +- }) ++ distTest{ ++ name: "swig_callback_lto", ++ heading: "../misc/swig/callback", ++ fn: func(dt *distTest) error { ++ cmd := t.addCmd(dt, "misc/swig/callback", t.goTest()) ++ cmd.Env = append(os.Environ(), ++ "CGO_CFLAGS=-flto", ++ "CGO_CXXFLAGS=-flto", ++ "CGO_LDFLAGS=-flto", ++ ) ++ return nil ++ }, ++ }, ++ ) + } + } + } +diff --git a/src/cmd/go/testdata/script/cgo_lto2_issue43830.txt b/src/cmd/go/testdata/script/cgo_lto2_issue43830.txt +new file mode 100644 +index 0000000..e2483ba +--- /dev/null ++++ b/src/cmd/go/testdata/script/cgo_lto2_issue43830.txt +@@ -0,0 +1,33 @@ ++# tests golang.org/issue/43830 ++ ++[!cgo] skip 'skipping test without cgo' ++[openbsd] env CC='clang' ++[openbsd] [!exec:clang] skip 'skipping test without clang present' ++[!openbsd] env CC='gcc' ++[!openbsd] [!exec:gcc] skip 'skipping test without gcc present' ++ ++env CGO_CFLAGS='-Wno-ignored-optimization-argument -flto -ffat-lto-objects' ++ ++go build main.go ++ ++-- main.go -- ++ ++package main ++ ++import "fmt" ++ ++// #include "hello.h" ++import "C" ++ ++func main() { ++ hello := C.hello ++ fmt.Printf("%v\n", hello) ++} ++ ++-- hello.h -- ++ ++#include ++ ++void hello(void) { ++ printf("hello\n"); ++} +diff --git a/src/cmd/go/testdata/script/cgo_lto_issue43830.txt b/src/cmd/go/testdata/script/cgo_lto_issue43830.txt +new file mode 100644 +index 0000000..06ab2f3 +--- /dev/null ++++ b/src/cmd/go/testdata/script/cgo_lto_issue43830.txt +@@ -0,0 +1,39 @@ ++# tests golang.org/issue/43830 ++ ++[!cgo] skip 'skipping test without cgo' ++[openbsd] env CC='clang' ++[openbsd] [!exec:clang] skip 'skipping test without clang present' ++[!openbsd] env CC='gcc' ++[!openbsd] [!exec:gcc] skip 'skipping test without gcc present' ++ ++env CGO_CFLAGS='-Wno-ignored-optimization-argument -flto -ffat-lto-objects' ++ ++go build main.go add.go ++ ++-- main.go -- ++ ++package main ++ ++/* ++int c_add(int a, int b) { ++ return myadd(a, b); ++} ++*/ ++import "C" ++ ++func main() { ++ println(C.c_add(1, 2)) ++} ++ ++-- add.go -- ++ ++package main ++ ++import "C" ++ ++/* test */ ++ ++//export myadd ++func myadd(a C.int, b C.int) C.int { ++ return a + b ++} +diff --git a/src/cmd/link/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go +index 22f53a4..23915f9 100644 +--- a/src/cmd/link/internal/ld/ar.go ++++ b/src/cmd/link/internal/ld/ar.go +@@ -124,6 +124,10 @@ + + libgcc := sym.Library{Pkg: "libgcc"} + h := ldobj(ctxt, f, &libgcc, l, pname, name) ++ if h.ld == nil { ++ Errorf(nil, "%s unrecognized object file at offset %d", name, off) ++ continue ++ } + f.MustSeek(h.off, 0) + h.ld(ctxt, f, h.pkg, h.length, h.pn) + } +diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go +index ae0d752..20f1d0b 100644 +--- a/src/cmd/link/internal/ld/config.go ++++ b/src/cmd/link/internal/ld/config.go +@@ -241,6 +241,10 @@ + return true, "dynamically linking with a shared library" + } + ++ if unknownObjFormat { ++ return true, "some input objects have an unrecognized file format" ++ } ++ + return false, "" + } + +@@ -248,7 +252,7 @@ + // + // It is called after flags are processed and inputs are processed, + // so the ctxt.LinkMode variable has an initial value from the -linkmode +-// flag and the iscgo externalobj variables are set. ++// flag and the iscgo, externalobj, and unknownObjFormat variables are set. + func determineLinkMode(ctxt *Link) { + extNeeded, extReason := mustLinkExternal(ctxt) + via := "" +diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go +index e8f001b..644faeb 100644 +--- a/src/cmd/link/internal/ld/lib.go ++++ b/src/cmd/link/internal/ld/lib.go +@@ -343,10 +343,16 @@ + const pkgdef = "__.PKGDEF" + + var ( +- // Set if we see an object compiled by the host compiler that is not +- // from a package that is known to support internal linking mode. ++ // externalobj is set to true if we see an object compiled by ++ // the host compiler that is not from a package that is known ++ // to support internal linking mode. + externalobj = false +- theline string ++ ++ // unknownObjFormat is set to true if we see an object whose ++ // format we don't recognize. ++ unknownObjFormat = false ++ ++ theline string + ) + + func Lflag(ctxt *Link, arg string) { +@@ -1065,6 +1071,10 @@ + } + + f.MustSeek(h.off, 0) ++ if h.ld == nil { ++ Errorf(nil, "%s: unrecognized object file format", h.pn) ++ continue ++ } + h.ld(ctxt, f, h.pkg, h.length, h.pn) + f.Close() + } +@@ -1855,6 +1865,14 @@ + return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file) + } + ++ if c1 != 'g' || c2 != 'o' || c3 != ' ' || c4 != 'o' { ++ // An unrecognized object is just passed to the external linker. ++ // If we try to read symbols from this object, we will ++ // report an error at that time. ++ unknownObjFormat = true ++ return ldhostobj(nil, ctxt.HeadType, f, pkg, length, pn, file) ++ } ++ + /* check the header */ + line, err := f.ReadString('\n') + if err != nil { diff --git a/golang.spec b/golang.spec index 26802f1..ad3759b 100644 --- a/golang.spec +++ b/golang.spec @@ -154,6 +154,8 @@ Patch1939923: skip_test_rhbz1939923.patch # Port to openssl 3.0 Patch1952381: rhbz1952381.patch +Patch1904567: cgo-lto-fix.patch + # Having documentation separate was broken Obsoletes: %{name}-docs < 1.1-4 @@ -253,6 +255,8 @@ Requires: %{name} = %{version}-%{release} %patch1939923 -p1 +%patch1904567 -p1 + cp %{SOURCE1} ./src/runtime/ %build @@ -566,6 +570,10 @@ cd .. %endif %changelog +* Mon Aug 2 2021 Derek Parker - 1.16.6-2 +- Backport fix allowing LTO to be enabled on cgo sources +- Resolves: rhbz#1904567 + * Tue Jul 20 2021 Derek Parker - 1.16.6-1 - Rebase to 1.16.6 - Resolves: rhbz#1984124