mirror of
https://github.com/golang/go.git
synced 2026-02-05 18:35:03 +03:00
cmd/link: allow one to specify the data section in the internal linker
Fixes #74945
Change-Id: Ia73a8dcdf707222e822522daaa7f31a38b1c31e6
GitHub-Last-Rev: da1526ad8c
GitHub-Pull-Request: golang/go#75117
Reviewed-on: https://go-review.googlesource.com/c/go/+/698355
Reviewed-by: Mark Freeman <markfreeman@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
committed by
Cherry Mui
parent
cdb3d467fa
commit
cf5e993177
@@ -59,6 +59,12 @@ package main
|
||||
func main() {}
|
||||
`
|
||||
|
||||
var goSourceWithData = `
|
||||
package main
|
||||
var globalVar = 42
|
||||
func main() { println(&globalVar) }
|
||||
`
|
||||
|
||||
// The linker used to crash if an ELF input file had multiple text sections
|
||||
// with the same name.
|
||||
func TestSectionsWithSameName(t *testing.T) {
|
||||
@@ -569,3 +575,106 @@ func TestFlagR(t *testing.T) {
|
||||
t.Errorf("executable failed to run: %v\n%s", err, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlagD(t *testing.T) {
|
||||
// Test that using the -D flag to specify data section address generates
|
||||
// a working binary with data at the specified address.
|
||||
t.Parallel()
|
||||
testFlagD(t, "0x10000000", "", 0x10000000)
|
||||
}
|
||||
|
||||
func TestFlagDUnaligned(t *testing.T) {
|
||||
// Test that using the -D flag with an unaligned address errors out
|
||||
t.Parallel()
|
||||
testFlagDError(t, "0x10000123", "", "invalid -D value 0x10000123")
|
||||
}
|
||||
|
||||
func TestFlagDWithR(t *testing.T) {
|
||||
// Test that using the -D flag with -R flag errors on unaligned address.
|
||||
t.Parallel()
|
||||
testFlagDError(t, "0x30001234", "8192", "invalid -D value 0x30001234")
|
||||
}
|
||||
|
||||
func testFlagD(t *testing.T, dataAddr string, roundQuantum string, expectedAddr uint64) {
|
||||
testenv.MustHaveGoBuild(t)
|
||||
tmpdir := t.TempDir()
|
||||
src := filepath.Join(tmpdir, "x.go")
|
||||
if err := os.WriteFile(src, []byte(goSourceWithData), 0444); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
exe := filepath.Join(tmpdir, "x.exe")
|
||||
|
||||
// Build linker flags
|
||||
ldflags := "-D=" + dataAddr
|
||||
if roundQuantum != "" {
|
||||
ldflags += " -R=" + roundQuantum
|
||||
}
|
||||
|
||||
cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags="+ldflags, "-o", exe, src)
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Fatalf("build failed: %v, output:\n%s", err, out)
|
||||
}
|
||||
|
||||
cmd = testenv.Command(t, exe)
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Errorf("executable failed to run: %v\n%s", err, out)
|
||||
}
|
||||
|
||||
ef, err := elf.Open(exe)
|
||||
if err != nil {
|
||||
t.Fatalf("open elf file failed: %v", err)
|
||||
}
|
||||
defer ef.Close()
|
||||
|
||||
// Find the first data-related section to verify segment placement
|
||||
var firstDataSectionAddr uint64
|
||||
var found bool = false
|
||||
for _, sec := range ef.Sections {
|
||||
if sec.Type == elf.SHT_PROGBITS || sec.Type == elf.SHT_NOBITS {
|
||||
// These sections are writable, allocated at runtime, but not executable
|
||||
isWrite := sec.Flags&elf.SHF_WRITE != 0
|
||||
isExec := sec.Flags&elf.SHF_EXECINSTR != 0
|
||||
isAlloc := sec.Flags&elf.SHF_ALLOC != 0
|
||||
|
||||
if isWrite && !isExec && isAlloc {
|
||||
addrLower := sec.Addr < firstDataSectionAddr
|
||||
if !found || addrLower {
|
||||
firstDataSectionAddr = sec.Addr
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatalf("can't find any writable data sections")
|
||||
}
|
||||
if firstDataSectionAddr != expectedAddr {
|
||||
t.Errorf("data section starts at 0x%x, expected 0x%x", firstDataSectionAddr, expectedAddr)
|
||||
}
|
||||
}
|
||||
|
||||
func testFlagDError(t *testing.T, dataAddr string, roundQuantum string, expectedError string) {
|
||||
testenv.MustHaveGoBuild(t)
|
||||
tmpdir := t.TempDir()
|
||||
src := filepath.Join(tmpdir, "x.go")
|
||||
if err := os.WriteFile(src, []byte(goSourceWithData), 0444); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
exe := filepath.Join(tmpdir, "x.exe")
|
||||
|
||||
// Build linker flags
|
||||
ldflags := "-D=" + dataAddr
|
||||
if roundQuantum != "" {
|
||||
ldflags += " -R=" + roundQuantum
|
||||
}
|
||||
|
||||
cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags="+ldflags, "-o", exe, src)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err == nil {
|
||||
t.Fatalf("expected build to fail with unaligned data address, but it succeeded")
|
||||
}
|
||||
if !strings.Contains(string(out), expectedError) {
|
||||
t.Errorf("expected error message to contain %q, got:\n%s", expectedError, out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2881,7 +2881,12 @@ func (ctxt *Link) address() []*sym.Segment {
|
||||
}
|
||||
order = append(order, &Segdata)
|
||||
Segdata.Rwx = 06
|
||||
Segdata.Vaddr = va
|
||||
if *FlagDataAddr != -1 {
|
||||
Segdata.Vaddr = uint64(*FlagDataAddr)
|
||||
va = Segdata.Vaddr
|
||||
} else {
|
||||
Segdata.Vaddr = va
|
||||
}
|
||||
var data *sym.Section
|
||||
var noptr *sym.Section
|
||||
var bss *sym.Section
|
||||
|
||||
@@ -442,3 +442,25 @@ func d()
|
||||
t.Errorf("Trampoline b-tramp0 exists unnecessarily")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRounding(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input int64
|
||||
quantum int64
|
||||
expected int64
|
||||
}{
|
||||
{0x30000000, 0x2000, 0x30000000}, // Already aligned
|
||||
{0x30002000, 0x2000, 0x30002000}, // Exactly on boundary
|
||||
{0x30001234, 0x2000, 0x30002000},
|
||||
{0x30001000, 0x2000, 0x30002000},
|
||||
{0x30001fff, 0x2000, 0x30002000},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
result := Rnd(tc.input, tc.quantum)
|
||||
if result != tc.expected {
|
||||
t.Errorf("Rnd(0x%x, 0x%x) = 0x%x, expected 0x%x",
|
||||
tc.input, tc.quantum, result, tc.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +105,7 @@ var (
|
||||
FlagStrictDups = flag.Int("strictdups", 0, "sanity check duplicate symbol contents during object file reading (1=warn 2=err).")
|
||||
FlagRound = flag.Int64("R", -1, "set address rounding `quantum`")
|
||||
FlagTextAddr = flag.Int64("T", -1, "set the start address of text symbols")
|
||||
FlagDataAddr = flag.Int64("D", -1, "set the start address of data symbols")
|
||||
FlagFuncAlign = flag.Int("funcalign", 0, "set function align to `N` bytes")
|
||||
flagEntrySymbol = flag.String("E", "", "set `entry` symbol name")
|
||||
flagPruneWeakMap = flag.Bool("pruneweakmap", true, "prune weak mapinit refs")
|
||||
@@ -317,6 +318,10 @@ func Main(arch *sys.Arch, theArch Arch) {
|
||||
bench.Start("Archinit")
|
||||
thearch.Archinit(ctxt)
|
||||
|
||||
if *FlagDataAddr != -1 && *FlagDataAddr%*FlagRound != 0 {
|
||||
Exitf("invalid -D value 0x%x: not aligned to rounding quantum 0x%x", *FlagDataAddr, *FlagRound)
|
||||
}
|
||||
|
||||
if ctxt.linkShared && !ctxt.IsELF {
|
||||
Exitf("-linkshared can only be used on elf systems")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user