Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 94074f2

Browse files
Kevaundraykevaundray
Kevaundray
authored andcommitted
initial commit
1 parent ffc85ee commit 94074f2

File tree

3 files changed

+173
-1
lines changed

3 files changed

+173
-1
lines changed

‎src/cmd/link/elf_test.go

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ package main
5959
func main() {}
6060
`
6161

62+
var goSourceWithData = `
63+
package main
64+
var globalVar = 42
65+
func main() { println(&globalVar) }
66+
`
67+
6268
// The linker used to crash if an ELF input file had multiple text sections
6369
// with the same name.
6470
func TestSectionsWithSameName(t *testing.T) {
@@ -569,3 +575,150 @@ func TestFlagR(t *testing.T) {
569575
t.Errorf("executable failed to run: %v\n%s", err, out)
570576
}
571577
}
578+
579+
func TestFlagD(t *testing.T) {
580+
// Test that using the -D flag to specify data section address generates
581+
// a working binary with data at the specified address.
582+
// (Test only on ELF for now.)
583+
testenv.MustHaveGoBuild(t)
584+
t.Parallel()
585+
tmpdir := t.TempDir()
586+
src := filepath.Join(tmpdir, "x.go")
587+
if err := os.WriteFile(src, []byte(goSourceWithData), 0444); err != nil {
588+
t.Fatal(err)
589+
}
590+
exe := filepath.Join(tmpdir, "x.exe")
591+
592+
dataAddr := "0x10000000"
593+
expectedAddr := uint64(0x10000000)
594+
595+
cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-D="+dataAddr, "-o", exe, src)
596+
if out, err := cmd.CombinedOutput(); err != nil {
597+
t.Fatalf("build failed: %v, output:\n%s", err, out)
598+
}
599+
600+
cmd = testenv.Command(t, exe)
601+
if out, err := cmd.CombinedOutput(); err != nil {
602+
t.Errorf("executable failed to run: %v\n%s", err, out)
603+
}
604+
605+
ef, err := elf.Open(exe)
606+
if err != nil {
607+
t.Fatalf("open elf file failed: %v", err)
608+
}
609+
defer ef.Close()
610+
611+
// Find the first data-related section to verify segment placement
612+
var firstDataSectionAddr uint64
613+
var found bool = false
614+
for _, sec := range ef.Sections {
615+
if sec.Type == elf.SHT_PROGBITS || sec.Type == elf.SHT_NOBITS {
616+
617+
// These sections are writable, allocated at runtime, but not executable
618+
isWrite := sec.Flags&elf.SHF_WRITE != 0
619+
isExec := sec.Flags&elf.SHF_EXECINSTR != 0
620+
isAlloc := sec.Flags&elf.SHF_ALLOC != 0
621+
622+
if isWrite && !isExec && isAlloc {
623+
addrLower := sec.Addr < firstDataSectionAddr
624+
if !found || addrLower {
625+
firstDataSectionAddr = sec.Addr
626+
found = true
627+
}
628+
}
629+
}
630+
}
631+
632+
if !found {
633+
t.Fatalf("can't find any writable data sections")
634+
}
635+
if firstDataSectionAddr != expectedAddr {
636+
t.Errorf("data section starts at 0x%x, expected 0x%x", firstDataSectionAddr, expectedAddr)
637+
}
638+
}
639+
640+
func TestFlagDWithR(t *testing.T) {
641+
// Test that using the -D flag with -R flag works together.
642+
// Note: The data address gets rounded to the specified alignment quantum.
643+
testenv.MustHaveGoBuild(t)
644+
t.Parallel()
645+
tmpdir := t.TempDir()
646+
src := filepath.Join(tmpdir, "x.go")
647+
if err := os.WriteFile(src, []byte(goSourceWithData), 0444); err != nil {
648+
t.Fatal(err)
649+
}
650+
exe := filepath.Join(tmpdir, "x.exe")
651+
652+
// Use an unaligned address that tests -D and -R rounding
653+
dataAddr := "0x30001234"
654+
roundQuantum := "8192"
655+
expectedAddr := uint64(0x30002000) // Should be rounded up to next 8KB boundary
656+
657+
cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-D="+dataAddr+" -R="+roundQuantum, "-o", exe, src)
658+
if out, err := cmd.CombinedOutput(); err != nil {
659+
t.Fatalf("build failed: %v, output:\n%s", err, out)
660+
}
661+
662+
cmd = testenv.Command(t, exe)
663+
if out, err := cmd.CombinedOutput(); err != nil {
664+
t.Errorf("executable failed to run: %v\n%s", err, out)
665+
}
666+
667+
ef, err := elf.Open(exe)
668+
if err != nil {
669+
t.Fatalf("open elf file failed: %v", err)
670+
}
671+
defer ef.Close()
672+
673+
// Find the first data-related section to verify segment placement
674+
var firstDataSectionAddr uint64
675+
var found bool = false
676+
for _, sec := range ef.Sections {
677+
if sec.Type == elf.SHT_PROGBITS || sec.Type == elf.SHT_NOBITS {
678+
679+
// These sections are writable, allocated at runtime, but not executable
680+
isWrite := sec.Flags&elf.SHF_WRITE != 0
681+
isExec := sec.Flags&elf.SHF_EXECINSTR != 0
682+
isAlloc := sec.Flags&elf.SHF_ALLOC != 0
683+
684+
if isWrite && !isExec && isAlloc {
685+
addrLower := sec.Addr < firstDataSectionAddr
686+
if !found || addrLower {
687+
firstDataSectionAddr = sec.Addr
688+
found = true
689+
}
690+
}
691+
}
692+
}
693+
694+
if !found {
695+
t.Fatalf("can't find any writable data sections")
696+
}
697+
if firstDataSectionAddr != expectedAddr {
698+
t.Errorf("data section starts at 0x%x, expected 0x%x",
699+
firstDataSectionAddr, expectedAddr)
700+
}
701+
}
702+
703+
// TODO: sanity check, delete this
704+
func TestRounding(t *testing.T) {
705+
testCases := []struct {
706+
input int64
707+
quantum int64
708+
expected int64
709+
}{
710+
{0x30000000, 0x2000, 0x30000000}, // Already aligned
711+
{0x30002000, 0x2000, 0x30002000}, // Exactly on boundary
712+
{0x30001234, 0x2000, 0x30002000},
713+
{0x30001000, 0x2000, 0x30002000},
714+
{0x30001fff, 0x2000, 0x30002000},
715+
}
716+
717+
for _, tc := range testCases {
718+
result := ld.Rnd(tc.input, tc.quantum)
719+
if result != tc.expected {
720+
t.Errorf("Rnd(0x%x, 0x%x) = 0x%x, expected 0x%x",
721+
tc.input, tc.quantum, result, tc.expected)
722+
}
723+
}
724+
}

‎src/cmd/link/internal/ld/data.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2881,7 +2881,12 @@ func (ctxt *Link) address() []*sym.Segment {
28812881
}
28822882
order = append(order, &Segdata)
28832883
Segdata.Rwx = 06
2884-
Segdata.Vaddr = va
2884+
if *FlagDataAddr != -1 {
2885+
Segdata.Vaddr = uint64(Rnd(*FlagDataAddr, *FlagRound))
2886+
va = Segdata.Vaddr
2887+
} else {
2888+
Segdata.Vaddr = va
2889+
}
28852890
var data *sym.Section
28862891
var noptr *sym.Section
28872892
var bss *sym.Section
@@ -3135,6 +3140,19 @@ func (ctxt *Link) layout(order []*sym.Segment) uint64 {
31353140
// should ensure that this segment's
31363141
// VA ≡ Fileoff mod FlagRound.
31373142
seg.Fileoff = uint64(Rnd(int64(prev.Fileoff+prev.Filelen), *FlagRound))
3143+
// If -D flag was used to place data segment at a specific address,
3144+
// we may have a non-contiguous layout. In this case, we need to
3145+
// adjust the file offset to maintain the congruence requirement.
3146+
if seg == &Segdata && *FlagDataAddr != -1 {
3147+
// Adjust file offset to maintain VA ≡ Fileoff mod FlagRound
3148+
adjustment := int64(seg.Vaddr%uint64(*FlagRound)) - int64(seg.Fileoff%uint64(*FlagRound))
3149+
if adjustment != 0 {
3150+
seg.Fileoff = uint64(int64(seg.Fileoff) + adjustment)
3151+
if adjustment < 0 {
3152+
seg.Fileoff += uint64(*FlagRound)
3153+
}
3154+
}
3155+
}
31383156
if seg.Vaddr%uint64(*FlagRound) != seg.Fileoff%uint64(*FlagRound) {
31393157
Exitf("bad segment rounding (Vaddr=%#x Fileoff=%#x FlagRound=%#x)", seg.Vaddr, seg.Fileoff, *FlagRound)
31403158
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ var (
105105
FlagStrictDups = flag.Int("strictdups", 0, "sanity check duplicate symbol contents during object file reading (1=warn 2=err).")
106106
FlagRound = flag.Int64("R", -1, "set address rounding `quantum`")
107107
FlagTextAddr = flag.Int64("T", -1, "set the start address of text symbols")
108+
FlagDataAddr = flag.Int64("D", -1, "set the start address of data symbols")
108109
FlagFuncAlign = flag.Int("funcalign", 0, "set function align to `N` bytes")
109110
flagEntrySymbol = flag.String("E", "", "set `entry` symbol name")
110111
flagPruneWeakMap = flag.Bool("pruneweakmap", true, "prune weak mapinit refs")

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /