Skip to content

Commit 771f575

Browse files
committed
add tests
1 parent 520d6e6 commit 771f575

File tree

5 files changed

+118
-10
lines changed

5 files changed

+118
-10
lines changed

src/cmd/link/doc.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Flags:
4848
-aslr
4949
Enable ASLR for buildmode=c-shared on windows (default true).
5050
-bindnow
51-
Mark ELF object for immediate function binding (default false).
51+
Mark a dynamically linked ELF object for immediate function binding (default false).
5252
-buildid id
5353
Record id as Go toolchain build id.
5454
-buildmode mode

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

+7-7
Original file line numberDiff line numberDiff line change
@@ -1056,17 +1056,17 @@ func elfdynhash(ctxt *Link) {
10561056
}
10571057

10581058
s = ldr.CreateSymForUpdate(".dynamic", 0)
1059+
1060+
var dtFlags1 elf.DynFlag1
10591061
if *flagBindNow {
1062+
dtFlags1 |= elf.DF_1_NOW
10601063
Elfwritedynent(ctxt.Arch, s, elf.DT_FLAGS, uint64(elf.DF_BIND_NOW))
10611064
}
1062-
switch {
1063-
case ctxt.BuildMode == BuildModePIE && *flagBindNow:
1064-
Elfwritedynent(ctxt.Arch, s, elf.DT_FLAGS_1, uint64(elf.DF_1_PIE)|uint64(elf.DF_1_NOW))
1065-
case ctxt.BuildMode == BuildModePIE && !*flagBindNow:
1066-
Elfwritedynent(ctxt.Arch, s, elf.DT_FLAGS_1, uint64(elf.DF_1_PIE))
1067-
case ctxt.BuildMode != BuildModePIE && *flagBindNow:
1068-
Elfwritedynent(ctxt.Arch, s, elf.DT_FLAGS_1, uint64(elf.DF_1_NOW))
1065+
if ctxt.BuildMode == BuildModePIE {
1066+
dtFlags1 |= elf.DF_1_PIE
10691067
}
1068+
Elfwritedynent(ctxt.Arch, s, elf.DT_FLAGS_1, uint64(dtFlags1))
1069+
10701070
elfverneed = nfile
10711071
if elfverneed != 0 {
10721072
elfWriteDynEntSym(ctxt, s, elf.DT_VERNEED, gnuVersionR.Sym())

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

+106
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package ld
88

99
import (
1010
"debug/elf"
11+
"fmt"
1112
"internal/testenv"
1213
"os"
1314
"path/filepath"
@@ -182,3 +183,108 @@ func main() {
182183
}
183184
}
184185
}
186+
187+
func TestElfBindNow(t *testing.T) {
188+
t.Parallel()
189+
testenv.MustHaveGoBuild(t)
190+
testenv.MustInternalLink(t, false)
191+
192+
const prog = `package main; import _ "net"; func main() {}`
193+
194+
tests := []struct {
195+
name string
196+
args []string
197+
wantDf1Now bool
198+
wantDf1Pie bool
199+
wantDfBindNow bool
200+
}{
201+
{name: "default"},
202+
{
203+
name: "pie",
204+
args: []string{"-buildmode=pie"},
205+
wantDf1Pie: true,
206+
},
207+
{
208+
name: "bindnow",
209+
args: []string{"-ldflags", "-bindnow"},
210+
wantDf1Now: true,
211+
wantDfBindNow: true,
212+
},
213+
{
214+
name: "bindnow-pie",
215+
args: []string{"-buildmode=pie", "-ldflags", "-bindnow"},
216+
wantDf1Now: true,
217+
wantDf1Pie: true,
218+
wantDfBindNow: true,
219+
},
220+
}
221+
222+
gotDynFlag := func(flags []uint64, dynFlag uint64) bool {
223+
for _, flag := range flags {
224+
if gotFlag := dynFlag&flag != 0; gotFlag {
225+
return true
226+
}
227+
}
228+
229+
return false
230+
}
231+
232+
for _, test := range tests {
233+
t.Run(
234+
test.name, func(t *testing.T) {
235+
var (
236+
gotDfBindNow, gotDf1Now, gotDf1Pie bool
237+
238+
dir = t.TempDir()
239+
src = filepath.Join(dir, fmt.Sprintf("elf_%s.go", test.name))
240+
binFile = filepath.Join(dir, test.name)
241+
)
242+
243+
if err := os.WriteFile(src, []byte(prog), 0666); err != nil {
244+
t.Fatal(err)
245+
}
246+
247+
cmdArgs := append([]string{"build", "-o", binFile}, append(test.args, src)...)
248+
cmd := testenv.Command(t, testenv.GoToolPath(t), cmdArgs...)
249+
250+
if out, err := cmd.CombinedOutput(); err != nil {
251+
t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
252+
}
253+
254+
fi, err := os.Open(binFile)
255+
if err != nil {
256+
t.Fatalf("failed to open built file: %v", err)
257+
}
258+
defer fi.Close()
259+
260+
elfFile, err := elf.NewFile(fi)
261+
if err != nil {
262+
t.Skip("The system may not support ELF, skipped.")
263+
}
264+
defer elfFile.Close()
265+
266+
flags, err := elfFile.DynValue(elf.DT_FLAGS)
267+
if err != nil {
268+
t.Fatalf("failed to get DT_FLAGS: %v", err)
269+
}
270+
271+
flags1, err := elfFile.DynValue(elf.DT_FLAGS_1)
272+
if err != nil {
273+
t.Fatalf("failed to get DT_FLAGS_1: %v", err)
274+
}
275+
276+
if gotDfBindNow = gotDynFlag(flags, uint64(elf.DF_BIND_NOW)); gotDfBindNow != test.wantDfBindNow {
277+
t.Fatalf("DT_FLAGS BIND_NOW flag is %v, want: %v", gotDfBindNow, test.wantDfBindNow)
278+
}
279+
280+
if gotDf1Now = gotDynFlag(flags1, uint64(elf.DF_1_NOW)); gotDf1Now != test.wantDf1Now {
281+
t.Fatalf("DT_FLAGS_1 DF_1_NOW flag is %v, want: %v", gotDf1Now, test.wantDf1Now)
282+
}
283+
284+
if gotDf1Pie = gotDynFlag(flags1, uint64(elf.DF_1_PIE)); gotDf1Pie != test.wantDf1Pie {
285+
t.Fatalf("DT_FLAGS_1 DF_1_PIE flag is %v, want: %v", gotDf1Pie, test.wantDf1Pie)
286+
}
287+
},
288+
)
289+
}
290+
}

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -1600,7 +1600,9 @@ func (ctxt *Link) hostlink() {
16001600

16011601
var altLinker string
16021602
if ctxt.IsELF && (ctxt.DynlinkingGo() || *flagBindNow) {
1603-
// We force all symbol resolution to be done at program startup
1603+
// For ELF targets, when producing dynamically linked Go code
1604+
// or when immediate binding is explicitly requested,
1605+
// we force all symbol resolution to be done at program startup
16041606
// because lazy PLT resolution can use large amounts of stack at
16051607
// times we cannot allow it to do so.
16061608
argv = append(argv, "-Wl,-z,now")

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func init() {
6363
// Flags used by the linker. The exported flags are used by the architecture-specific packages.
6464
var (
6565
flagBuildid = flag.String("buildid", "", "record `id` as Go toolchain build id")
66-
flagBindNow = flag.Bool("bindnow", false, "mark ELF object for immediate function binding")
66+
flagBindNow = flag.Bool("bindnow", false, "mark a dynamically linked ELF object for immediate function binding")
6767

6868
flagOutfile = flag.String("o", "", "write output to `file`")
6969
flagPluginPath = flag.String("pluginpath", "", "full path name for plugin")

0 commit comments

Comments
 (0)