The linker is told once, not twice
novox/hq 04-ISSUES/161. A repeated flag is not a merged one. The Go command takes the last -ldflags and drops the first, so passing the toolchain's flags and then the system stamp as a second one produced a binary that knew its system and had lost -s -w: 12.2MB against 8.5MB, with its debug info intact. My own comment said the linker "accepts and merges" them. It does not, and I found out by reading the file the build produced rather than by reading the comment again. Linker flags are now the toolchain's own list, composed into one flag with the stamp. A test refuses a compile line that carries -ldflags itself, because that is what makes two.
This commit is contained in:
@@ -877,13 +877,20 @@ func compile(ctx context.Context, run Runner, tree string, chain Toolchain,
|
|||||||
base,
|
base,
|
||||||
}
|
}
|
||||||
invocation = append(invocation, chain.Compile...)
|
invocation = append(invocation, chain.Compile...)
|
||||||
// What it was built for, linked in. The compile line carries `-ldflags` already; this appends a
|
// **One `-ldflags`, composed here.** A repeated flag is not a merged one: the Go command takes
|
||||||
// second one, which the Go linker accepts and merges. A host with no system refuses every
|
// the last and drops the first, so passing the toolchain's flags and then the system stamp as a
|
||||||
// declaration before it applies anything (novox/hq 04-ISSUES/161), and it is the artifact that
|
// second `-ldflags` produced a binary that knew its system and had lost `-s -w` — half again the
|
||||||
// knows — the target is a property of the artifact rather than of the recipe (ADR 0142).
|
// size, with its debug info (novox/hq 04-ISSUES/161).
|
||||||
|
//
|
||||||
|
// What it was built for is the one thing taken from the artifact, and ADR 0142 says why: the
|
||||||
|
// target is a property of the artifact rather than of the recipe. A host with no system refuses
|
||||||
|
// every declaration before it applies anything.
|
||||||
|
linker := append([]string(nil), chain.LinkerFlags...)
|
||||||
if chain.SystemStamp != "" && strings.TrimSpace(a.System) != "" {
|
if chain.SystemStamp != "" && strings.TrimSpace(a.System) != "" {
|
||||||
invocation = append(invocation, "-ldflags",
|
linker = append(linker, "-X", chain.SystemStamp+"="+strings.TrimSpace(a.System))
|
||||||
"-X "+chain.SystemStamp+"="+strings.TrimSpace(a.System))
|
}
|
||||||
|
if len(linker) > 0 {
|
||||||
|
invocation = append(invocation, "-ldflags", strings.Join(linker, " "))
|
||||||
}
|
}
|
||||||
if chain.OutputFlag != "" {
|
if chain.OutputFlag != "" {
|
||||||
// A compiler pointed at a package is told the file to write, not the directory: the name a
|
// A compiler pointed at a package is told the file to write, not the directory: the name a
|
||||||
|
|||||||
@@ -47,3 +47,30 @@ func TestTheStampIsTheOneThingTakenFromTheArtifact(t *testing.T) {
|
|||||||
t.Fatalf("the compile line takes something from the module: %q", joined)
|
t.Fatalf("the compile line takes something from the module: %q", joined)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTheLinkerIsToldOnceNotTwice(t *testing.T) {
|
||||||
|
// A repeated flag is not a merged one: the Go command takes the last -ldflags and drops the
|
||||||
|
// first. Passing the toolchain's flags and then the stamp separately produced a binary that knew
|
||||||
|
// its system and had lost -s -w — 12.2MB against 8.5MB, with its debug info (04-ISSUES/161).
|
||||||
|
chain, err := ToolchainFor("go")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for _, arg := range chain.Compile {
|
||||||
|
if arg == "-ldflags" {
|
||||||
|
t.Fatal("the compile line carries -ldflags, so composing one here makes two")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(chain.LinkerFlags) == 0 {
|
||||||
|
t.Fatal("the go toolchain passes no linker flags, so the binary keeps its debug info")
|
||||||
|
}
|
||||||
|
var stripped bool
|
||||||
|
for _, f := range chain.LinkerFlags {
|
||||||
|
if f == "-s" {
|
||||||
|
stripped = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !stripped {
|
||||||
|
t.Fatalf("the go toolchain does not strip: %v", chain.LinkerFlags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -49,6 +49,14 @@ type Toolchain struct {
|
|||||||
// is named as it will be FOUND, inside the unpacked bundle, so the source is the same path with
|
// is named as it will be FOUND, inside the unpacked bundle, so the source is the same path with
|
||||||
// the output directory taken off the front and this on the end.
|
// the output directory taken off the front and this on the end.
|
||||||
SourceExt string
|
SourceExt string
|
||||||
|
// LinkerFlags are passed to the linker as one flag, together with the system stamp below.
|
||||||
|
//
|
||||||
|
// **Separate from Compile because a repeated flag is not a merged one.** They were in the compile
|
||||||
|
// line, and appending the stamp as a second `-ldflags` meant the Go command took the last and
|
||||||
|
// dropped the first — so the binary gained its system and lost `-s -w`, growing by half and
|
||||||
|
// carrying its debug info. The mistake was believing a comment rather than reading the file it
|
||||||
|
// produced (novox/hq 04-ISSUES/161).
|
||||||
|
LinkerFlags []string
|
||||||
// SystemStamp is the variable this language's linker fills with the artifact's declared system,
|
// SystemStamp is the variable this language's linker fills with the artifact's declared system,
|
||||||
// for a language whose binaries are pinned to one at link time (novox/hq ADR 0005).
|
// for a language whose binaries are pinned to one at link time (novox/hq ADR 0005).
|
||||||
//
|
//
|
||||||
@@ -126,8 +134,11 @@ var toolchains = []Toolchain{
|
|||||||
// rather than from the linker: two builds of one commit produce the same bytes.
|
// rather than from the linker: two builds of one commit produce the same bytes.
|
||||||
Compile: []string{
|
Compile: []string{
|
||||||
"env", "CGO_ENABLED=0", "GOFLAGS=-trimpath",
|
"env", "CGO_ENABLED=0", "GOFLAGS=-trimpath",
|
||||||
"go", "build", "-ldflags", "-s -w",
|
"go", "build",
|
||||||
},
|
},
|
||||||
|
// Stripped of symbols and debug info: what a machine holds is a file it runs, not one it
|
||||||
|
// debugs, and the difference measured 12.2MB against 8.5MB.
|
||||||
|
LinkerFlags: []string{"-s", "-w"},
|
||||||
OutputFlag: "-o",
|
OutputFlag: "-o",
|
||||||
// Pointed at the package the artifact is built `from`, compiled whole. Go writes the binary
|
// Pointed at the package the artifact is built `from`, compiled whole. Go writes the binary
|
||||||
// into the output directory, named after the package — so the bundle a machine unpacks is a
|
// into the output directory, named after the package — so the bundle a machine unpacks is a
|
||||||
|
|||||||
Reference in New Issue
Block a user