1
0
mirror of https://github.com/golang/go synced 2024-09-30 22:18:32 -06:00

cmd/link: fix malformed .shstrtab section

For ELF targets, the code in the go linker that generates the
".shstrtab" section was using a loader symbol to accumulate the
contents of the section, then setting the section type to
sym.SELFROSECT. This resulted in a section whose offset indicated that
it fell into a loadable ELF segment, which is not how the .shstrtab is
supposed to work (it should be outside of all loadable segments,
similar to .strtab and .symtab). The peculiar .shstrtab caused
confusion in third party tools that operate on ELF files, notably
llvm-strip.

This patch rewrites the .shstrtab generation code to avoid using a
loader.Symbol and instead accumulate the contents of the section into
a regular byte slice, then emit the section's data in the same way
that .strtab is handled.

Fixes #62600.

Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest,gotip-windows-amd64-longtest
Change-Id: Ie54020d7b2d779d3ac9f5465fd505217d0681f79
Reviewed-on: https://go-review.googlesource.com/c/go/+/528036
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:
Than McIntosh 2023-09-13 09:26:41 -04:00
parent 584aad5c57
commit 3251006291
3 changed files with 90 additions and 16 deletions

View File

@ -155,7 +155,7 @@ const (
* marshal a 32-bit representation from the 64-bit structure.
*/
var Elfstrdat []byte
var elfstrdat, elfshstrdat []byte
/*
* Total amount of space to reserve at the start of the file
@ -1386,12 +1386,16 @@ func (ctxt *Link) doelf() {
ldr := ctxt.loader
/* predefine strings we need for section headers */
shstrtab := ldr.CreateSymForUpdate(".shstrtab", 0)
shstrtab.SetType(sym.SELFROSECT)
addshstr := func(s string) int {
off := len(elfshstrdat)
elfshstrdat = append(elfshstrdat, s...)
elfshstrdat = append(elfshstrdat, 0)
return off
}
shstrtabAddstring := func(s string) {
off := shstrtab.Addstring(s)
off := addshstr(s)
elfsetstring(ctxt, 0, s, int(off))
}
@ -1746,12 +1750,16 @@ func Asmbelfsetup() {
func asmbElf(ctxt *Link) {
var symo int64
if !*FlagS {
symo = int64(Segdwarf.Fileoff + Segdwarf.Filelen)
symo = Rnd(symo, int64(ctxt.Arch.PtrSize))
symo = int64(Segdwarf.Fileoff + Segdwarf.Filelen)
symo = Rnd(symo, int64(ctxt.Arch.PtrSize))
ctxt.Out.SeekSet(symo)
if *FlagS {
ctxt.Out.Write(elfshstrdat)
} else {
ctxt.Out.SeekSet(symo)
asmElfSym(ctxt)
ctxt.Out.Write(Elfstrdat)
ctxt.Out.Write(elfstrdat)
ctxt.Out.Write(elfshstrdat)
if ctxt.IsExternal() {
elfEmitReloc(ctxt)
}
@ -2155,9 +2163,6 @@ func asmbElf(ctxt *Link) {
elfobj:
sh := elfshname(".shstrtab")
sh.Type = uint32(elf.SHT_STRTAB)
sh.Addralign = 1
shsym(sh, ldr, ldr.Lookup(".shstrtab", 0))
eh.Shstrndx = uint16(sh.shnum)
if ctxt.IsMIPS() {
@ -2184,6 +2189,7 @@ elfobj:
elfshname(".symtab")
elfshname(".strtab")
}
elfshname(".shstrtab")
for _, sect := range Segtext.Sections {
elfshbits(ctxt.LinkMode, sect)
@ -2226,6 +2232,7 @@ elfobj:
sh.Flags = 0
}
var shstroff uint64
if !*FlagS {
sh := elfshname(".symtab")
sh.Type = uint32(elf.SHT_SYMTAB)
@ -2239,10 +2246,19 @@ elfobj:
sh = elfshname(".strtab")
sh.Type = uint32(elf.SHT_STRTAB)
sh.Off = uint64(symo) + uint64(symSize)
sh.Size = uint64(len(Elfstrdat))
sh.Size = uint64(len(elfstrdat))
sh.Addralign = 1
shstroff = sh.Off + sh.Size
} else {
shstroff = uint64(symo)
}
sh = elfshname(".shstrtab")
sh.Type = uint32(elf.SHT_STRTAB)
sh.Off = shstroff
sh.Size = uint64(len(elfshstrdat))
sh.Addralign = 1
/* Main header */
copy(eh.Ident[:], elf.ELFMAG)

View File

@ -125,3 +125,61 @@ func TestNoDuplicateNeededEntries(t *testing.T) {
t.Errorf("Got %d entries for `libc.so`, want %d", got, want)
}
}
func TestShStrTabAttributesIssue62600(t *testing.T) {
t.Parallel()
testenv.MustHaveGoBuild(t)
dir := t.TempDir()
const prog = `
package main
func main() {
println("whee")
}
`
src := filepath.Join(dir, "issue62600.go")
if err := os.WriteFile(src, []byte(prog), 0666); err != nil {
t.Fatal(err)
}
binFile := filepath.Join(dir, "issue62600")
cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", binFile, src)
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
}
fi, err := os.Open(binFile)
if err != nil {
t.Fatalf("failed to open built file: %v", err)
}
defer fi.Close()
elfFile, err := elf.NewFile(fi)
if err != nil {
t.Skip("The system may not support ELF, skipped.")
}
section := elfFile.Section(".shstrtab")
if section == nil {
t.Fatal("no .shstrtab")
}
// The .shstrtab section should have a zero address, non-zero
// size, no ALLOC flag, and the offset should not fall into any of
// the segments defined by the program headers.
if section.Addr != 0 {
t.Fatalf("expected Addr == 0 for .shstrtab got %x", section.Addr)
}
if section.Size == 0 {
t.Fatal("expected nonzero Size for .shstrtab got 0")
}
if section.Flags&elf.SHF_ALLOC != 0 {
t.Fatal("expected zero alloc flag got nonzero for .shstrtab")
}
for idx, p := range elfFile.Progs {
if section.Offset >= p.Off && section.Offset < p.Off+p.Filesz {
t.Fatalf("badly formed .shstrtab, is contained in segment %d", idx)
}
}
}

View File

@ -45,14 +45,14 @@ import (
// Symbol table.
func putelfstr(s string) int {
if len(Elfstrdat) == 0 && s != "" {
if len(elfstrdat) == 0 && s != "" {
// first entry must be empty string
putelfstr("")
}
off := len(Elfstrdat)
Elfstrdat = append(Elfstrdat, s...)
Elfstrdat = append(Elfstrdat, 0)
off := len(elfstrdat)
elfstrdat = append(elfstrdat, s...)
elfstrdat = append(elfstrdat, 0)
return off
}