t.Errorf("expected error message to contain %q, got:\n%s", expectedError, out)
}
}
+
+func TestELFHeadersSorted(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // We can only test this for internal linking mode.
+ // For external linking the external linker will
+ // decide how to sort the sections.
+ testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
+
+ t.Parallel()
+
+ tmpdir := t.TempDir()
+ src := filepath.Join(tmpdir, "x.go")
+ if err := os.WriteFile(src, []byte(goSourceWithData), 0o444); err != nil {
+ t.Fatal(err)
+ }
+
+ exe := filepath.Join(tmpdir, "x.exe")
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-toolexec", os.Args[0], "-ldflags=-linkmode=internal", "-o", exe, src)
+ cmd = testenv.CleanCmdEnv(cmd)
+ cmd.Env = append(cmd.Env, "LINK_TEST_TOOLEXEC=1")
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Fatalf("build failed: %v, output:\n%s", err, out)
+ }
+
+ ef, err := elf.Open(exe)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ef.Close()
+
+ // After the first zero section header,
+ // we should see allocated sections,
+ // then unallocated sections.
+ // The allocated sections should be sorted by address.
+ i := 1
+ lastAddr := uint64(0)
+ for i < len(ef.Sections) {
+ sec := ef.Sections[i]
+ if sec.Flags&elf.SHF_ALLOC == 0 {
+ break
+ }
+ if sec.Addr < lastAddr {
+ t.Errorf("section %d %q address %#x less than previous address %#x", i, sec.Name, sec.Addr, lastAddr)
+ }
+ lastAddr = sec.Addr
+ i++
+ }
+
+ firstUnalc := i
+ for i < len(ef.Sections) {
+ sec := ef.Sections[i]
+ if sec.Flags&elf.SHF_ALLOC != 0 {
+ t.Errorf("allocated section %d %q follows first unallocated section %d %q", i, sec.Name, firstUnalc, ef.Sections[firstUnalc].Name)
+ }
+ i++
+ }
+}
"cmd/internal/sys"
"cmd/link/internal/loader"
"cmd/link/internal/sym"
+ "cmp"
"debug/elf"
"encoding/binary"
"encoding/hex"
// ElfShdr is an ELF section entry, plus the section index.
type ElfShdr struct {
elf.Section64
+
+ // The section index, set by elfSortShdrs.
+ // Don't read this directly, use elfShdrShnum.
shnum elf.SectionIndex
+
+ // Because we don't compute the final section number
+ // until late in the link, when the link and info fields
+ // hold section indexes, we store pointers, and fetch
+ // the final section index when we write them out.
+ link *ElfShdr
+ info *ElfShdr
+
+ // We compute the section offsets of reloc sections
+ // after we create the ELF section header.
+ // This field lets us fetch the section offset and size.
+ relocSect *sym.Section
}
// ElfPhdr is the ELF program, or segment, header.
// target platform uses.
elfRelType string
- ehdr ElfEhdr
- phdr = make([]*ElfPhdr, 0, 8)
- shdr = make([]*ElfShdr, 0, 64)
+ ehdr ElfEhdr
+ phdr = make([]*ElfPhdr, 0, 8)
+ shdr = make([]*ElfShdr, 0, 64)
+ shdrSorted bool
interp string
)
out.Write32(uint32(e.Align))
}
+// elfShdrShnum returns the section index of an ElfShdr.
+func elfShdrShnum(e *ElfShdr) elf.SectionIndex {
+ if e.shnum == -1 {
+ Errorf("internal error: retrieved section index before it is set")
+ errorexit()
+ }
+ return e.shnum
+}
+
+// elfShdrOff returns the section offset for an ElfShdr.
+func elfShdrOff(e *ElfShdr) uint64 {
+ if e.relocSect != nil {
+ if e.Off != 0 {
+ Errorf("internal error: ElfShdr relocSect == %p Off == %d", e.relocSect, e.Off)
+ errorexit()
+ }
+ return e.relocSect.Reloff
+ }
+ return e.Off
+}
+
+// elfShdrSize returns the section size for an ElfShdr.
+func elfShdrSize(e *ElfShdr) uint64 {
+ if e.relocSect != nil {
+ if e.Size != 0 {
+ Errorf("internal error: ElfShdr relocSect == %p Size == %d", e.relocSect, e.Size)
+ errorexit()
+ }
+ return e.relocSect.Rellen
+ }
+ return e.Size
+}
+
+// elfShdrLink returns the link value for an ElfShdr.
+func elfShdrLink(e *ElfShdr) uint32 {
+ if e.link != nil {
+ if e.Link != 0 {
+ Errorf("internal error: ElfShdr link == %p Link == %d", e.link, e.Link)
+ errorexit()
+ }
+ return uint32(elfShdrShnum(e.link))
+ }
+ return e.Link
+}
+
+// elfShdrInfo returns the info value for an ElfShdr.
+func elfShdrInfo(e *ElfShdr) uint32 {
+ if e.info != nil {
+ if e.Info != 0 {
+ Errorf("internal error: ElfShdr info == %p Info == %d", e.info, e.Info)
+ errorexit()
+ }
+ return uint32(elfShdrShnum(e.info))
+ }
+ return e.Info
+}
+
func elf64shdr(out *OutBuf, e *ElfShdr) {
out.Write32(e.Name)
out.Write32(e.Type)
out.Write64(e.Flags)
out.Write64(e.Addr)
- out.Write64(e.Off)
- out.Write64(e.Size)
- out.Write32(e.Link)
- out.Write32(e.Info)
+ out.Write64(elfShdrOff(e))
+ out.Write64(elfShdrSize(e))
+ out.Write32(elfShdrLink(e))
+ out.Write32(elfShdrInfo(e))
out.Write64(e.Addralign)
out.Write64(e.Entsize)
}
out.Write32(e.Type)
out.Write32(uint32(e.Flags))
out.Write32(uint32(e.Addr))
- out.Write32(uint32(e.Off))
- out.Write32(uint32(e.Size))
- out.Write32(e.Link)
- out.Write32(e.Info)
+ out.Write32(uint32(elfShdrOff(e)))
+ out.Write32(uint32(elfShdrSize(e)))
+ out.Write32(elfShdrLink(e))
+ out.Write32(elfShdrInfo(e))
out.Write32(uint32(e.Addralign))
out.Write32(uint32(e.Entsize))
}
return uint32(ehdr.Shnum) * ELF32SHDRSIZE
}
+// elfSortShdrs sorts the section headers so that allocated sections
+// are first, in address order. This isn't required for correctness,
+// but it makes the ELF file easier for humans to read.
+// We only do this for an executable, not an object file.
+func elfSortShdrs(ctxt *Link) {
+ if ctxt.LinkMode != LinkExternal {
+ // Use [1:] to leave the empty section header zero in place.
+ slices.SortStableFunc(shdr[1:], func(a, b *ElfShdr) int {
+ isAllocated := func(h *ElfShdr) bool {
+ return elf.SectionFlag(h.Flags)&elf.SHF_ALLOC != 0
+ }
+ if isAllocated(a) {
+ if isAllocated(b) {
+ if r := cmp.Compare(a.Addr, b.Addr); r != 0 {
+ return r
+ }
+ // With same address, sort smallest
+ // section first.
+ return cmp.Compare(a.Size, b.Size)
+ }
+ // Allocated before unallocated.
+ return -1
+ }
+ if isAllocated(b) {
+ // Allocated before unallocated.
+ return 1
+ }
+ return 0
+ })
+ }
+ for i, h := range shdr {
+ h.shnum = elf.SectionIndex(i)
+ }
+ shdrSorted = true
+}
+
func elfsetstring(ctxt *Link, s loader.Sym, str string, off int) {
if nelfstr >= len(elfstr) {
ctxt.Errorf(s, "too many elf strings")
}
func newElfShdr(name int64) *ElfShdr {
+ if shdrSorted {
+ Errorf("internal error: creating a section header after they were sorted")
+ errorexit()
+ }
+
e := new(ElfShdr)
e.Name = uint32(name)
- e.shnum = elf.SectionIndex(ehdr.Shnum)
+ e.shnum = -1 // make invalid for now, set by elfSortShdrs
shdr = append(shdr, e)
ehdr.Shnum++
return e
// its own .rela.text.
if sect.Name == ".text" {
- if sh.Info != 0 && sh.Info != uint32(sect.Elfsect.(*ElfShdr).shnum) {
+ if sh.info != nil && sh.info != sect.Elfsect.(*ElfShdr) {
sh = elfshnamedup(elfRelType + sect.Name)
}
}
if typ == elf.SHT_RELA {
sh.Entsize += uint64(arch.RegSize)
}
- sh.Link = uint32(elfshname(".symtab").shnum)
- sh.Info = uint32(sect.Elfsect.(*ElfShdr).shnum)
- sh.Off = sect.Reloff
- sh.Size = sect.Rellen
+ sh.link = elfshname(".symtab")
+ sh.info = sect.Elfsect.(*ElfShdr)
+ sh.relocSect = sect
sh.Addralign = uint64(arch.RegSize)
return sh
}
var symo int64
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(elfshstrdat)
- if ctxt.IsExternal() {
- elfEmitReloc(ctxt)
- }
- }
- ctxt.Out.SeekSet(0)
ldr := ctxt.loader
eh := getElfEhdr()
sh.Entsize = ELF32SYMSIZE
}
sh.Addralign = uint64(ctxt.Arch.RegSize)
- sh.Link = uint32(elfshname(".dynstr").shnum)
+ sh.link = elfshname(".dynstr")
- // sh.info is the index of first non-local symbol (number of local symbols)
+ // sh.Info is the index of first non-local symbol (number of local symbols)
s := ldr.Lookup(".dynsym", 0)
i := uint32(0)
for sub := s; sub != 0; sub = ldr.SubSym(sub) {
sh.Type = uint32(elf.SHT_GNU_VERSYM)
sh.Flags = uint64(elf.SHF_ALLOC)
sh.Addralign = 2
- sh.Link = uint32(elfshname(".dynsym").shnum)
+ sh.link = elfshname(".dynsym")
sh.Entsize = 2
shsym(sh, ldr, ldr.Lookup(".gnu.version", 0))
sh.Flags = uint64(elf.SHF_ALLOC)
sh.Addralign = uint64(ctxt.Arch.RegSize)
sh.Info = uint32(elfverneed)
- sh.Link = uint32(elfshname(".dynstr").shnum)
+ sh.link = elfshname(".dynstr")
shsym(sh, ldr, ldr.Lookup(".gnu.version_r", 0))
}
sh.Flags = uint64(elf.SHF_ALLOC)
sh.Entsize = ELF64RELASIZE
sh.Addralign = uint64(ctxt.Arch.RegSize)
- sh.Link = uint32(elfshname(".dynsym").shnum)
- sh.Info = uint32(elfshname(".plt").shnum)
+ sh.link = elfshname(".dynsym")
+ sh.info = elfshname(".plt")
shsym(sh, ldr, ldr.Lookup(".rela.plt", 0))
sh = elfshname(".rela")
sh.Flags = uint64(elf.SHF_ALLOC)
sh.Entsize = ELF64RELASIZE
sh.Addralign = 8
- sh.Link = uint32(elfshname(".dynsym").shnum)
+ sh.link = elfshname(".dynsym")
shsym(sh, ldr, ldr.Lookup(".rela", 0))
} else {
sh := elfshname(".rel.plt")
sh.Flags = uint64(elf.SHF_ALLOC)
sh.Entsize = ELF32RELSIZE
sh.Addralign = 4
- sh.Link = uint32(elfshname(".dynsym").shnum)
+ sh.link = elfshname(".dynsym")
shsym(sh, ldr, ldr.Lookup(".rel.plt", 0))
sh = elfshname(".rel")
sh.Flags = uint64(elf.SHF_ALLOC)
sh.Entsize = ELF32RELSIZE
sh.Addralign = 4
- sh.Link = uint32(elfshname(".dynsym").shnum)
+ sh.link = elfshname(".dynsym")
shsym(sh, ldr, ldr.Lookup(".rel", 0))
}
sh.Flags = uint64(elf.SHF_ALLOC)
sh.Entsize = 4
sh.Addralign = uint64(ctxt.Arch.RegSize)
- sh.Link = uint32(elfshname(".dynsym").shnum)
+ sh.link = elfshname(".dynsym")
shsym(sh, ldr, ldr.Lookup(".hash", 0))
// sh and elf.PT_DYNAMIC for .dynamic section
sh.Flags = uint64(elf.SHF_ALLOC + elf.SHF_WRITE)
sh.Entsize = 2 * uint64(ctxt.Arch.RegSize)
sh.Addralign = uint64(ctxt.Arch.RegSize)
- sh.Link = uint32(elfshname(".dynstr").shnum)
+ sh.link = elfshname(".dynstr")
shsym(sh, ldr, ldr.Lookup(".dynamic", 0))
ph := newElfPhdr()
ph.Type = elf.PT_DYNAMIC
}
elfobj:
- sh := elfshname(".shstrtab")
- eh.Shstrndx = uint16(sh.shnum)
-
if ctxt.IsMIPS() {
- sh = elfshname(".MIPS.abiflags")
+ sh := elfshname(".MIPS.abiflags")
sh.Type = uint32(elf.SHT_MIPS_ABIFLAGS)
sh.Flags = uint64(elf.SHF_ALLOC)
sh.Addralign = 8
sh.Flags = 0
}
+ elfSortShdrs(ctxt)
+
+ sh := elfshname(".shstrtab")
+ eh.Shstrndx = uint16(elfShdrShnum(sh))
+
+ ctxt.Out.SeekSet(symo)
+ if *FlagS {
+ ctxt.Out.Write(elfshstrdat)
+ } else {
+ asmElfSym(ctxt)
+ ctxt.Out.Write(elfstrdat)
+ ctxt.Out.Write(elfshstrdat)
+ if ctxt.IsExternal() {
+ elfEmitReloc(ctxt)
+ }
+ }
+ ctxt.Out.SeekSet(0)
+
var shstroff uint64
if !*FlagS {
sh := elfshname(".symtab")
sh.Size = uint64(symSize)
sh.Addralign = uint64(ctxt.Arch.RegSize)
sh.Entsize = 8 + 2*uint64(ctxt.Arch.RegSize)
- sh.Link = uint32(elfshname(".strtab").shnum)
+ sh.link = elfshname(".strtab")
sh.Info = uint32(elfglobalsymndx)
sh = elfshname(".strtab")