Skip to content

Commit 24e9707

Browse files
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 <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Michael Hudson-Doyle <[email protected]>
1 parent 2725522 commit 24e9707

File tree

8 files changed

+140
-15
lines changed

8 files changed

+140
-15
lines changed

src/cmd/cgo/gcc.go

+2
Original file line numberDiff line numberDiff line change
@@ -1638,6 +1638,8 @@ func (p *Package) gccCmd() []string {
16381638
c = append(c, "-maix64")
16391639
c = append(c, "-mcmodel=large")
16401640
}
1641+
// disable LTO so we get an object whose symbols we can read
1642+
c = append(c, "-fno-lto")
16411643
c = append(c, "-") //read input from standard input
16421644
return c
16431645
}

src/cmd/cgo/out.go

+13-3
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,18 @@ func (p *Package) writeDefs() {
168168
if *gccgo {
169169
fmt.Fprintf(fc, "extern byte *%s;\n", n.C)
170170
} else {
171-
fmt.Fprintf(fm, "extern char %s[];\n", n.C)
172-
fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C)
171+
// Force a reference to all symbols so that
172+
// the external linker will add DT_NEEDED
173+
// entries as needed on ELF systems.
174+
// Treat function variables differently
175+
// to avoid type confict errors from LTO
176+
// (Link Time Optimization).
177+
if n.Kind == "fpvar" {
178+
fmt.Fprintf(fm, "extern void %s();\n", n.C)
179+
} else {
180+
fmt.Fprintf(fm, "extern char %s[];\n", n.C)
181+
fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C)
182+
}
173183
fmt.Fprintf(fgo2, "//go:linkname __cgo_%s %s\n", n.C, n.C)
174184
fmt.Fprintf(fgo2, "//go:cgo_import_static %s\n", n.C)
175185
fmt.Fprintf(fgo2, "var __cgo_%s byte\n", n.C)
@@ -1042,7 +1052,7 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
10421052
// This unpacks the argument struct above and calls the Go function.
10431053
fmt.Fprintf(fgo2, "func _cgoexp%s_%s(a *%s) {\n", cPrefix, exp.ExpName, gotype)
10441054

1045-
fmt.Fprintf(fm, "int _cgoexp%s_%s;\n", cPrefix, exp.ExpName)
1055+
fmt.Fprintf(fm, "void _cgoexp%s_%s(void* p){}\n", cPrefix, exp.ExpName)
10461056

10471057
if gccResult != "void" {
10481058
// Write results back to frame.

src/cmd/dist/test.go

+22-7
Original file line numberDiff line numberDiff line change
@@ -722,14 +722,29 @@ func (t *tester) registerTests() {
722722
},
723723
})
724724
if t.hasCxx() {
725-
t.tests = append(t.tests, distTest{
726-
name: "swig_callback",
727-
heading: "../misc/swig/callback",
728-
fn: func(dt *distTest) error {
729-
t.addCmd(dt, "misc/swig/callback", t.goTest())
730-
return nil
725+
t.tests = append(t.tests,
726+
distTest{
727+
name: "swig_callback",
728+
heading: "../misc/swig/callback",
729+
fn: func(dt *distTest) error {
730+
t.addCmd(dt, "misc/swig/callback", t.goTest())
731+
return nil
732+
},
733+
},
734+
distTest{
735+
name: "swig_callback_lto",
736+
heading: "../misc/swig/callback",
737+
fn: func(dt *distTest) error {
738+
cmd := t.addCmd(dt, "misc/swig/callback", t.goTest())
739+
cmd.Env = append(os.Environ(),
740+
"CGO_CFLAGS=-flto",
741+
"CGO_CXXFLAGS=-flto",
742+
"CGO_LDFLAGS=-flto",
743+
)
744+
return nil
745+
},
731746
},
732-
})
747+
)
733748
}
734749
}
735750
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# tests golang.org/issue/43830
2+
3+
[!cgo] skip 'skipping test without cgo'
4+
[openbsd] env CC='clang'
5+
[openbsd] [!exec:clang] skip 'skipping test without clang present'
6+
[!openbsd] env CC='gcc'
7+
[!openbsd] [!exec:gcc] skip 'skipping test without gcc present'
8+
9+
env CGO_CFLAGS='-Wno-ignored-optimization-argument -flto -ffat-lto-objects'
10+
11+
go build main.go
12+
13+
-- main.go --
14+
15+
package main
16+
17+
import "fmt"
18+
19+
// #include "hello.h"
20+
import "C"
21+
22+
func main() {
23+
hello := C.hello
24+
fmt.Printf("%v\n", hello)
25+
}
26+
27+
-- hello.h --
28+
29+
#include <stdio.h>
30+
31+
void hello(void) {
32+
printf("hello\n");
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# tests golang.org/issue/43830
2+
3+
[!cgo] skip 'skipping test without cgo'
4+
[openbsd] env CC='clang'
5+
[openbsd] [!exec:clang] skip 'skipping test without clang present'
6+
[!openbsd] env CC='gcc'
7+
[!openbsd] [!exec:gcc] skip 'skipping test without gcc present'
8+
9+
env CGO_CFLAGS='-Wno-ignored-optimization-argument -flto -ffat-lto-objects'
10+
11+
go build main.go add.go
12+
13+
-- main.go --
14+
15+
package main
16+
17+
/*
18+
int c_add(int a, int b) {
19+
return myadd(a, b);
20+
}
21+
*/
22+
import "C"
23+
24+
func main() {
25+
println(C.c_add(1, 2))
26+
}
27+
28+
-- add.go --
29+
30+
package main
31+
32+
import "C"
33+
34+
/* test */
35+
36+
//export myadd
37+
func myadd(a C.int, b C.int) C.int {
38+
return a + b
39+
}

src/cmd/link/internal/ld/ar.go

+4
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ func hostArchive(ctxt *Link, name string) {
124124

125125
libgcc := sym.Library{Pkg: "libgcc"}
126126
h := ldobj(ctxt, f, &libgcc, l, pname, name)
127+
if h.ld == nil {
128+
Errorf(nil, "%s unrecognized object file at offset %d", name, off)
129+
continue
130+
}
127131
f.MustSeek(h.off, 0)
128132
h.ld(ctxt, f, h.pkg, h.length, h.pn)
129133
}

src/cmd/link/internal/ld/config.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -241,14 +241,18 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) {
241241
return true, "dynamically linking with a shared library"
242242
}
243243

244+
if unknownObjFormat {
245+
return true, "some input objects have an unrecognized file format"
246+
}
247+
244248
return false, ""
245249
}
246250

247251
// determineLinkMode sets ctxt.LinkMode.
248252
//
249253
// It is called after flags are processed and inputs are processed,
250254
// so the ctxt.LinkMode variable has an initial value from the -linkmode
251-
// flag and the iscgo externalobj variables are set.
255+
// flag and the iscgo, externalobj, and unknownObjFormat variables are set.
252256
func determineLinkMode(ctxt *Link) {
253257
extNeeded, extReason := mustLinkExternal(ctxt)
254258
via := ""

src/cmd/link/internal/ld/lib.go

+22-4
Original file line numberDiff line numberDiff line change
@@ -343,10 +343,16 @@ var (
343343
const pkgdef = "__.PKGDEF"
344344

345345
var (
346-
// Set if we see an object compiled by the host compiler that is not
347-
// from a package that is known to support internal linking mode.
346+
// externalobj is set to true if we see an object compiled by
347+
// the host compiler that is not from a package that is known
348+
// to support internal linking mode.
348349
externalobj = false
349-
theline string
350+
351+
// unknownObjFormat is set to true if we see an object whose
352+
// format we don't recognize.
353+
unknownObjFormat = false
354+
355+
theline string
350356
)
351357

352358
func Lflag(ctxt *Link, arg string) {
@@ -1065,6 +1071,10 @@ func hostobjs(ctxt *Link) {
10651071
}
10661072

10671073
f.MustSeek(h.off, 0)
1074+
if h.ld == nil {
1075+
Errorf(nil, "%s: unrecognized object file format", h.pn)
1076+
continue
1077+
}
10681078
h.ld(ctxt, f, h.pkg, h.length, h.pn)
10691079
f.Close()
10701080
}
@@ -1855,6 +1865,14 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string,
18551865
return ldhostobj(ldxcoff, ctxt.HeadType, f, pkg, length, pn, file)
18561866
}
18571867

1868+
if c1 != 'g' || c2 != 'o' || c3 != ' ' || c4 != 'o' {
1869+
// An unrecognized object is just passed to the external linker.
1870+
// If we try to read symbols from this object, we will
1871+
// report an error at that time.
1872+
unknownObjFormat = true
1873+
return ldhostobj(nil, ctxt.HeadType, f, pkg, length, pn, file)
1874+
}
1875+
18581876
/* check the header */
18591877
line, err := f.ReadString('\n')
18601878
if err != nil {
@@ -1874,7 +1892,7 @@ func ldobj(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, pn string,
18741892
return nil
18751893
}
18761894

1877-
Errorf(nil, "%s: not an object file: @%d %02x%02x%02x%02x", pn, start, c1, c2, c3, c4)
1895+
Errorf(nil, "%s: not an object file: @%d %q", pn, start, line)
18781896
return nil
18791897
}
18801898

0 commit comments

Comments
 (0)