Skip to content

Commit 4bc3410

Browse files
cmd/link: build shstrtab from ELF sections
Since before Go 1 the Go linker has handled ELF by first building the ELF section string table, and then pointing ELF section headers to it. This duplicates code as sections are effectively created twice, once with the name and then again with the full section header. The code duplication also means that it's easy to create unnecessary section names; for example, every internally linked Go program currently contains the string ".go.fuzzcntrs" although most do not have a section by that name. This CL changes the linker to simply build the section string table after all the sections are known. Change-Id: I27ba15b2af3dc1b8d7436b6c409f818aa8e6bfb4 Reviewed-on: https://go-review.googlesource.com/c/go/+/718840 Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: David Chase <drchase@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
1 parent b0c278b commit 4bc3410

File tree

2 files changed

+62
-200
lines changed

2 files changed

+62
-200
lines changed

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

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2394,28 +2394,6 @@ func (d *dwctxt) collectUnitLocs(u *sym.CompilationUnit) []loader.Sym {
23942394
return syms
23952395
}
23962396

2397-
// Add DWARF section names to the section header string table, by calling add
2398-
// on each name. ELF only.
2399-
func dwarfaddshstrings(ctxt *Link, add func(string)) {
2400-
if *FlagW { // disable dwarf
2401-
return
2402-
}
2403-
2404-
secs := []string{"abbrev", "frame", "info", "loc", "line", "gdb_scripts"}
2405-
if buildcfg.Experiment.Dwarf5 {
2406-
secs = append(secs, "addr", "rnglists", "loclists")
2407-
} else {
2408-
secs = append(secs, "ranges", "loc")
2409-
}
2410-
2411-
for _, sec := range secs {
2412-
add(".debug_" + sec)
2413-
if ctxt.IsExternal() {
2414-
add(elfRelType + ".debug_" + sec)
2415-
}
2416-
}
2417-
}
2418-
24192397
func dwarfaddelfsectionsyms(ctxt *Link) {
24202398
if *FlagW { // disable dwarf
24212399
return

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

Lines changed: 62 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,15 @@ import (
7171
// ElfEhdr is the ELF file header.
7272
type ElfEhdr elf.Header64
7373

74-
// ElfShdr is an ELF section entry, plus the section index.
74+
// ElfShdr is an ELF section table entry.
7575
type ElfShdr struct {
7676
elf.Section64
7777

78+
// nameString is the section name as a string.
79+
// This is not to be confused with Name,
80+
// inherited from elf.Section64, which is an offset.
81+
nameString string
82+
7883
// The section index, set by elfSortShdrs.
7984
// Don't read this directly, use elfShdrShnum.
8085
shnum elf.SectionIndex
@@ -109,7 +114,7 @@ const (
109114
ELF32RELSIZE = 8
110115
)
111116

112-
var elfstrdat, elfshstrdat []byte
117+
var elfstrdat []byte
113118

114119
// ELFRESERVE is the total amount of space to reserve at the
115120
// start of the file for Header, PHeaders, SHeaders, and interp.
@@ -158,15 +163,6 @@ type ELFArch struct {
158163
DynamicReadOnly bool
159164
}
160165

161-
type Elfstring struct {
162-
s string
163-
off int
164-
}
165-
166-
var elfstr [100]Elfstring
167-
168-
var nelfstr int
169-
170166
var buildinfo []byte
171167

172168
// Elfinit initializes the global ehdr variable that holds the ELF header.
@@ -413,15 +409,46 @@ func elfSortShdrs(ctxt *Link) {
413409
shdrSorted = true
414410
}
415411

416-
func elfsetstring(ctxt *Link, s loader.Sym, str string, off int) {
417-
if nelfstr >= len(elfstr) {
418-
ctxt.Errorf(s, "too many elf strings")
419-
errorexit()
412+
// elfWriteShstrtab writes out the ELF section string table.
413+
// It also sets the Name field of the section headers.
414+
// It returns the length of the string table.
415+
func elfWriteShstrtab(ctxt *Link) uint32 {
416+
// Map from section name to shstrtab offset.
417+
m := make(map[string]uint32, len(shdr))
418+
419+
m[""] = 0
420+
ctxt.Out.WriteByte(0)
421+
off := uint32(1)
422+
423+
writeString := func(s string) {
424+
m[s] = off
425+
ctxt.Out.WriteString(s)
426+
ctxt.Out.WriteByte(0)
427+
off += uint32(len(s)) + 1
428+
}
429+
430+
// As a minor optimization, do the relocation sections first,
431+
// as they may let us reuse the suffix.
432+
// That is, the offset for ".text" can point into ".rel.text".
433+
// We don't do a full suffix search as the relocation sections
434+
// are likely to be the only match.
435+
for _, sh := range shdr {
436+
if suffix, ok := strings.CutPrefix(sh.nameString, elfRelType); ok {
437+
m[suffix] = off + uint32(len(elfRelType))
438+
writeString(sh.nameString)
439+
}
440+
}
441+
442+
for _, sh := range shdr {
443+
if shOff, ok := m[sh.nameString]; ok {
444+
sh.Name = shOff
445+
} else {
446+
sh.Name = off
447+
writeString(sh.nameString)
448+
}
420449
}
421450

422-
elfstr[nelfstr].s = str
423-
elfstr[nelfstr].off = off
424-
nelfstr++
451+
return off
425452
}
426453

427454
func elfwritephdrs(out *OutBuf) uint32 {
@@ -450,15 +477,16 @@ func newElfPhdr() *ElfPhdr {
450477
return e
451478
}
452479

453-
func newElfShdr(name int64) *ElfShdr {
480+
func newElfShdr(name string) *ElfShdr {
454481
if shdrSorted {
455482
Errorf("internal error: creating a section header after they were sorted")
456483
errorexit()
457484
}
458485

459-
e := new(ElfShdr)
460-
e.Name = uint32(name)
461-
e.shnum = -1 // make invalid for now, set by elfSortShdrs
486+
e := &ElfShdr{
487+
nameString: name,
488+
shnum: -1, // make invalid for now, set by elfSortShdrs
489+
}
462490
shdr = append(shdr, e)
463491
return e
464492
}
@@ -1165,36 +1193,20 @@ func elfphrelro(seg *sym.Segment) {
11651193
ph.Align = uint64(*FlagRound)
11661194
}
11671195

1196+
// elfshname finds or creates a section given its name.
11681197
func elfshname(name string) *ElfShdr {
1169-
for i := 0; i < nelfstr; i++ {
1170-
if name != elfstr[i].s {
1171-
continue
1172-
}
1173-
off := elfstr[i].off
1174-
for _, sh := range shdr {
1175-
if sh.Name == uint32(off) {
1176-
return sh
1177-
}
1198+
for _, sh := range shdr {
1199+
if sh.nameString == name {
1200+
return sh
11781201
}
1179-
return newElfShdr(int64(off))
11801202
}
1181-
Exitf("cannot find elf name %s", name)
1182-
return nil
1203+
return newElfShdr(name)
11831204
}
11841205

1185-
// Create an ElfShdr for the section with name.
1186-
// Create a duplicate if one already exists with that name.
1206+
// elfshnamedup creates a new section with a given name.
1207+
// If there is an existing section with this name, it creates a duplicate.
11871208
func elfshnamedup(name string) *ElfShdr {
1188-
for i := 0; i < nelfstr; i++ {
1189-
if name == elfstr[i].s {
1190-
off := elfstr[i].off
1191-
return newElfShdr(int64(off))
1192-
}
1193-
}
1194-
1195-
Errorf("cannot find elf name %s", name)
1196-
errorexit()
1197-
return nil
1209+
return newElfShdr(name)
11981210
}
11991211

12001212
func elfshalloc(sect *sym.Section) *ElfShdr {
@@ -1446,140 +1458,11 @@ func addgonote(ctxt *Link, sectionName string, tag uint32, desc []byte) {
14461458
func (ctxt *Link) doelf() {
14471459
ldr := ctxt.loader
14481460

1449-
// Predefine strings we need for section headers.
1450-
1451-
addshstr := func(s string) int {
1452-
off := len(elfshstrdat)
1453-
elfshstrdat = append(elfshstrdat, s...)
1454-
elfshstrdat = append(elfshstrdat, 0)
1455-
return off
1456-
}
1457-
1458-
shstrtabAddstring := func(s string) {
1459-
off := addshstr(s)
1460-
elfsetstring(ctxt, 0, s, off)
1461-
}
1462-
1463-
shstrtabAddstring("")
1464-
shstrtabAddstring(".text")
1465-
shstrtabAddstring(".noptrdata")
1466-
shstrtabAddstring(".data")
1467-
shstrtabAddstring(".bss")
1468-
shstrtabAddstring(".noptrbss")
1469-
shstrtabAddstring(".go.fuzzcntrs")
1470-
shstrtabAddstring(".go.buildinfo")
1471-
shstrtabAddstring(".go.fipsinfo")
1472-
if ctxt.IsMIPS() {
1473-
shstrtabAddstring(".MIPS.abiflags")
1474-
shstrtabAddstring(".gnu.attributes")
1475-
}
1476-
1477-
// generate .tbss section for dynamic internal linker or external
1478-
// linking, so that various binutils could correctly calculate
1479-
// PT_TLS size. See https://golang.org/issue/5200.
1480-
if !*FlagD || ctxt.IsExternal() {
1481-
shstrtabAddstring(".tbss")
1482-
}
1483-
if ctxt.IsNetbsd() {
1484-
shstrtabAddstring(".note.netbsd.ident")
1485-
if *flagRace {
1486-
shstrtabAddstring(".note.netbsd.pax")
1487-
}
1488-
}
1489-
if ctxt.IsOpenbsd() {
1490-
shstrtabAddstring(".note.openbsd.ident")
1491-
}
1492-
if ctxt.IsFreebsd() {
1493-
shstrtabAddstring(".note.tag")
1494-
}
1495-
if len(buildinfo) > 0 {
1496-
shstrtabAddstring(".note.gnu.build-id")
1497-
}
1498-
if *flagBuildid != "" {
1499-
shstrtabAddstring(".note.go.buildid")
1500-
}
1501-
shstrtabAddstring(".elfdata")
1502-
shstrtabAddstring(".rodata")
1503-
shstrtabAddstring(".gopclntab")
1504-
// See the comment about data.rel.ro.FOO section names in data.go.
1505-
relro_prefix := ""
1506-
if ctxt.UseRelro() {
1507-
shstrtabAddstring(".data.rel.ro")
1508-
relro_prefix = ".data.rel.ro"
1509-
}
1510-
shstrtabAddstring(relro_prefix + ".typelink")
1511-
shstrtabAddstring(relro_prefix + ".itablink")
1512-
15131461
if ctxt.IsExternal() {
15141462
*FlagD = true
1515-
1516-
shstrtabAddstring(elfRelType + ".text")
1517-
shstrtabAddstring(elfRelType + ".rodata")
1518-
shstrtabAddstring(elfRelType + relro_prefix + ".typelink")
1519-
shstrtabAddstring(elfRelType + relro_prefix + ".itablink")
1520-
shstrtabAddstring(elfRelType + ".noptrdata")
1521-
shstrtabAddstring(elfRelType + ".data")
1522-
if ctxt.UseRelro() {
1523-
shstrtabAddstring(elfRelType + ".data.rel.ro")
1524-
}
1525-
shstrtabAddstring(elfRelType + ".go.buildinfo")
1526-
shstrtabAddstring(elfRelType + ".go.fipsinfo")
1527-
if ctxt.IsMIPS() {
1528-
shstrtabAddstring(elfRelType + ".MIPS.abiflags")
1529-
shstrtabAddstring(elfRelType + ".gnu.attributes")
1530-
}
1531-
1532-
// add a .note.GNU-stack section to mark the stack as non-executable
1533-
shstrtabAddstring(".note.GNU-stack")
1534-
1535-
if ctxt.IsShared() {
1536-
shstrtabAddstring(".note.go.abihash")
1537-
shstrtabAddstring(".note.go.pkg-list")
1538-
shstrtabAddstring(".note.go.deps")
1539-
}
1540-
}
1541-
1542-
hasinitarr := ctxt.linkShared
1543-
1544-
// Shared library initializer.
1545-
switch ctxt.BuildMode {
1546-
case BuildModeCArchive, BuildModeCShared, BuildModeShared, BuildModePlugin:
1547-
hasinitarr = true
1548-
}
1549-
1550-
if hasinitarr {
1551-
shstrtabAddstring(".init_array")
1552-
shstrtabAddstring(elfRelType + ".init_array")
1553-
}
1554-
1555-
if !*FlagS {
1556-
shstrtabAddstring(".symtab")
1557-
shstrtabAddstring(".strtab")
15581463
}
1559-
if !*FlagW {
1560-
dwarfaddshstrings(ctxt, shstrtabAddstring)
1561-
}
1562-
1563-
shstrtabAddstring(".shstrtab")
15641464

15651465
if !*FlagD { // -d suppresses dynamic loader format
1566-
shstrtabAddstring(".interp")
1567-
shstrtabAddstring(".hash")
1568-
shstrtabAddstring(".got")
1569-
if ctxt.IsPPC64() {
1570-
shstrtabAddstring(".glink")
1571-
}
1572-
shstrtabAddstring(".got.plt")
1573-
shstrtabAddstring(".dynamic")
1574-
shstrtabAddstring(".dynsym")
1575-
shstrtabAddstring(".dynstr")
1576-
shstrtabAddstring(elfRelType)
1577-
shstrtabAddstring(elfRelType + ".plt")
1578-
1579-
shstrtabAddstring(".plt")
1580-
shstrtabAddstring(".gnu.version")
1581-
shstrtabAddstring(".gnu.version_r")
1582-
15831466
// dynamic symbol table - first entry all zeros
15841467
dynsym := ldr.CreateSymForUpdate(".dynsym", 0)
15851468

@@ -2291,13 +2174,14 @@ elfobj:
22912174
sh := elfshname(".shstrtab")
22922175
eh.Shstrndx = uint16(elfShdrShnum(sh))
22932176

2177+
var shstrtabLen uint32
22942178
ctxt.Out.SeekSet(symo)
22952179
if *FlagS {
2296-
ctxt.Out.Write(elfshstrdat)
2180+
shstrtabLen = elfWriteShstrtab(ctxt)
22972181
} else {
22982182
asmElfSym(ctxt)
22992183
ctxt.Out.Write(elfstrdat)
2300-
ctxt.Out.Write(elfshstrdat)
2184+
shstrtabLen = elfWriteShstrtab(ctxt)
23012185
if ctxt.IsExternal() {
23022186
elfEmitReloc(ctxt)
23032187
}
@@ -2328,7 +2212,7 @@ elfobj:
23282212
sh = elfshname(".shstrtab")
23292213
sh.Type = uint32(elf.SHT_STRTAB)
23302214
sh.Off = shstroff
2331-
sh.Size = uint64(len(elfshstrdat))
2215+
sh.Size = uint64(shstrtabLen)
23322216
sh.Addralign = 1
23332217

23342218
// Main header

0 commit comments

Comments
 (0)