Skip to content

debug/elf: suport files with >= 65280 (0xff00) sections #55295

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/cmd/internal/buildid/note.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,19 @@ var elfGNUNote = []byte("GNU\x00")
// at least 4 kB out, in data.
func readELF(name string, f *os.File, data []byte) (buildid string, err error) {
// Assume the note content is in the data, already read.
// Rewrite the ELF header to set shnum to 0, so that we can pass
// Rewrite the ELF header to set shoff and shnum to 0, so that we can pass
// the data to elf.NewFile and it will decode the Prog list but not
// try to read the section headers and the string table from disk.
// That's a waste of I/O when all we care about is the Prog list
// and the one ELF note.
switch elf.Class(data[elf.EI_CLASS]) {
case elf.ELFCLASS32:
data[32], data[33], data[34], data[35] = 0, 0, 0, 0
data[48] = 0
data[49] = 0
case elf.ELFCLASS64:
data[40], data[41], data[42], data[43] = 0, 0, 0, 0
data[44], data[45], data[46], data[47] = 0, 0, 0, 0
data[60] = 0
data[61] = 0
}
Expand Down
46 changes: 46 additions & 0 deletions src/debug/elf/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,52 @@ func NewFile(r io.ReaderAt) (*File, error) {
f.Progs[i] = p
}

// If the number of sections is greater than or equal to SHN_LORESERVE
// (0xff00), shnum has the value zero and the actual number of section
// header table entries is contained in the sh_size field of the section
// header at index 0.
if shoff > 0 && shnum == 0 {
var typ, link uint32
sr.Seek(shoff, seekStart)
switch f.Class {
case ELFCLASS32:
sh := new(Section32)
if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
return nil, err
}
shnum = int(sh.Size)
typ = sh.Type
link = sh.Link
case ELFCLASS64:
sh := new(Section64)
if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
return nil, err
}
shnum = int(sh.Size)
typ = sh.Type
link = sh.Link
}
if SectionType(typ) != SHT_NULL {
return nil, &FormatError{shoff, "invalid type of the initial section", SectionType(typ)}
}

if shnum < int(SHN_LORESERVE) {
return nil, &FormatError{shoff, "invalid ELF shnum contained in sh_size", shnum}
}

// If the section name string table section index is greater than or
// equal to SHN_LORESERVE (0xff00), this member has the value
// SHN_XINDEX (0xffff) and the actual index of the section name
// string table section is contained in the sh_link field of the
// section header at index 0.
if shstrndx == int(SHN_XINDEX) {
shstrndx = int(link)
if shstrndx < int(SHN_LORESERVE) {
return nil, &FormatError{shoff, "invalid ELF shstrndx contained in sh_link", shstrndx}
}
}
}

// Read section headers
f.Sections = make([]*Section, shnum)
names := make([]uint32, shnum)
Expand Down
260 changes: 253 additions & 7 deletions src/debug/elf/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"compress/gzip"
"debug/dwarf"
"encoding/binary"
"fmt"
"io"
"math/rand"
"net"
Expand Down Expand Up @@ -230,26 +231,26 @@ func TestOpen(t *testing.T) {
continue
}
defer f.Close()
if !reflect.DeepEqual(f.FileHeader, tt.hdr) {
if f.FileHeader != tt.hdr {
t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.FileHeader, tt.hdr)
continue
}
for i, s := range f.Sections {
if i >= len(tt.sections) {
break
}
sh := &tt.sections[i]
if !reflect.DeepEqual(&s.SectionHeader, sh) {
t.Errorf("open %s, section %d:\n\thave %#v\n\twant %#v\n", tt.file, i, &s.SectionHeader, sh)
sh := tt.sections[i]
if s.SectionHeader != sh {
t.Errorf("open %s, section %d:\n\thave %#v\n\twant %#v\n", tt.file, i, s.SectionHeader, sh)
}
}
for i, p := range f.Progs {
if i >= len(tt.progs) {
break
}
ph := &tt.progs[i]
if !reflect.DeepEqual(&p.ProgHeader, ph) {
t.Errorf("open %s, program %d:\n\thave %#v\n\twant %#v\n", tt.file, i, &p.ProgHeader, ph)
ph := tt.progs[i]
if p.ProgHeader != ph {
t.Errorf("open %s, program %d:\n\thave %#v\n\twant %#v\n", tt.file, i, p.ProgHeader, ph)
}
}
tn := len(tt.sections)
Expand Down Expand Up @@ -944,6 +945,251 @@ func TestNoSectionOverlaps(t *testing.T) {
}
}

// TestLargeNumberOfSections tests the case that a file has greater than or
// equal to 65280 (0xff00) sections.
func TestLargeNumberOfSections(t *testing.T) {
// A file with >= 0xff00 sections is too big, so we will construct it on the
// fly. The original file "y.o" is generated by these commands:
// 1. generate "y.c":
// for i in `seq 1 65288`; do
// printf -v x "%04x" i;
// echo "int var_$x __attribute__((section(\"section_$x\"))) = $i;"
// done > y.c
// 2. compile: gcc -c y.c -m32
//
// $readelf -h y.o
// ELF Header:
// Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
// Class: ELF32
// Data: 2's complement, little endian
// Version: 1 (current)
// OS/ABI: UNIX - System V
// ABI Version: 0
// Type: REL (Relocatable file)
// Machine: Intel 80386
// Version: 0x1
// Entry point address: 0x0
// Start of program headers: 0 (bytes into file)
// Start of section headers: 3003468 (bytes into file)
// Flags: 0x0
// Size of this header: 52 (bytes)
// Size of program headers: 0 (bytes)
// Number of program headers: 0
// Size of section headers: 40 (bytes)
// Number of section headers: 0 (65298)
// Section header string table index: 65535 (65297)
//
// $readelf -S y.o
// There are 65298 section headers, starting at offset 0x2dd44c:
// Section Headers:
// [Nr] Name Type Addr Off Size ES Flg Lk Inf Al
// [ 0] NULL 00000000 000000 00ff12 00 65297 0 0
// [ 1] .text PROGBITS 00000000 000034 000000 00 AX 0 0 1
// [ 2] .data PROGBITS 00000000 000034 000000 00 WA 0 0 1
// [ 3] .bss NOBITS 00000000 000034 000000 00 WA 0 0 1
// [ 4] section_0001 PROGBITS 00000000 000034 000004 00 WA 0 0 4
// [ 5] section_0002 PROGBITS 00000000 000038 000004 00 WA 0 0 4
// [ section_0003 ~ section_ff06 truncated ]
// [65290] section_ff07 PROGBITS 00000000 03fc4c 000004 00 WA 0 0 4
// [65291] section_ff08 PROGBITS 00000000 03fc50 000004 00 WA 0 0 4
// [65292] .comment PROGBITS 00000000 03fc54 000027 01 MS 0 0 1
// [65293] .note.GNU-stack PROGBITS 00000000 03fc7b 000000 00 0 0 1
// [65294] .symtab SYMTAB 00000000 03fc7c 0ff0a0 10 65296 2 4
// [65295] .symtab_shndx SYMTAB SECTION 00000000 13ed1c 03fc28 04 65294 0 4
// [65296] .strtab STRTAB 00000000 17e944 08f74d 00 0 0 1
// [65297] .shstrtab STRTAB 00000000 20e091 0cf3bb 00 0 0 1

var buf bytes.Buffer

{
buf.Grow(0x55AF1C) // 3003468 + 40 * 65298

h := Header32{
Ident: [16]byte{0x7F, 'E', 'L', 'F', 0x01, 0x01, 0x01},
Type: 1,
Machine: 3,
Version: 1,
Shoff: 0x2DD44C,
Ehsize: 0x34,
Shentsize: 0x28,
Shnum: 0,
Shstrndx: 0xFFFF,
}
binary.Write(&buf, binary.LittleEndian, h)

// Zero out sections [1]~[65294].
buf.Write(bytes.Repeat([]byte{0}, 0x13ED1C-binary.Size(h)))

// Write section [65295]. Section [65295] are all zeros except for the
// last 48 bytes.
buf.Write(bytes.Repeat([]byte{0}, 0x03FC28-12*4))
for i := 0; i < 12; i++ {
binary.Write(&buf, binary.LittleEndian, uint32(0xFF00|i))
}

// Write section [65296].
buf.Write([]byte{0})
buf.Write([]byte("y.c\x00"))
for i := 1; i <= 65288; i++ {
// var_0001 ~ var_ff08
name := fmt.Sprintf("var_%04x", i)
buf.Write([]byte(name))
buf.Write([]byte{0})
}

// Write section [65297].
buf.Write([]byte{0})
buf.Write([]byte(".symtab\x00"))
buf.Write([]byte(".strtab\x00"))
buf.Write([]byte(".shstrtab\x00"))
buf.Write([]byte(".text\x00"))
buf.Write([]byte(".data\x00"))
buf.Write([]byte(".bss\x00"))
for i := 1; i <= 65288; i++ {
// s_0001 ~ s_ff08
name := fmt.Sprintf("section_%04x", i)
buf.Write([]byte(name))
buf.Write([]byte{0})
}
buf.Write([]byte(".comment\x00"))
buf.Write([]byte(".note.GNU-stack\x00"))
buf.Write([]byte(".symtab_shndx\x00"))

// Write section header table.
// NULL
binary.Write(&buf, binary.LittleEndian, Section32{Name: 0, Size: 0xFF12, Link: 0xFF11})
// .text
binary.Write(&buf, binary.LittleEndian, Section32{
Name: 0x1B,
Type: uint32(SHT_PROGBITS),
Flags: uint32(uint32(SHF_ALLOC | SHF_EXECINSTR)),
Off: 0x34,
Addralign: 0x01,
})
// .data
binary.Write(&buf, binary.LittleEndian, Section32{
Name: 0x21,
Type: uint32(SHT_PROGBITS),
Flags: uint32(SHF_WRITE | SHF_ALLOC),
Off: 0x34,
Addralign: 0x01,
})
// .bss
binary.Write(&buf, binary.LittleEndian, Section32{
Name: 0x27,
Type: uint32(SHT_NOBITS),
Flags: uint32(SHF_WRITE | SHF_ALLOC),
Off: 0x34,
Addralign: 0x01,
})
// s_1 ~ s_65537
for i := 0; i < 65288; i++ {
s := Section32{
Name: uint32(0x2C + i*13),
Type: uint32(SHT_PROGBITS),
Flags: uint32(SHF_WRITE | SHF_ALLOC),
Off: uint32(0x34 + i*4),
Size: 0x04,
Addralign: 0x04,
}
binary.Write(&buf, binary.LittleEndian, s)
}
// .comment
binary.Write(&buf, binary.LittleEndian, Section32{
Name: 0x0CF394,
Type: uint32(SHT_PROGBITS),
Flags: uint32(SHF_MERGE | SHF_STRINGS),
Off: 0x03FC54,
Size: 0x27,
Addralign: 0x01,
Entsize: 0x01,
})
// .note.GNU-stack
binary.Write(&buf, binary.LittleEndian, Section32{
Name: 0x0CF39D,
Type: uint32(SHT_PROGBITS),
Off: 0x03FC7B,
Addralign: 0x01,
})
// .symtab
binary.Write(&buf, binary.LittleEndian, Section32{
Name: 0x01,
Type: uint32(SHT_SYMTAB),
Off: 0x03FC7C,
Size: 0x0FF0A0,
Link: 0xFF10,
Info: 0x02,
Addralign: 0x04,
Entsize: 0x10,
})
// .symtab_shndx
binary.Write(&buf, binary.LittleEndian, Section32{
Name: 0x0CF3AD,
Type: uint32(SHT_SYMTAB_SHNDX),
Off: 0x13ED1C,
Size: 0x03FC28,
Link: 0xFF0E,
Addralign: 0x04,
Entsize: 0x04,
})
// .strtab
binary.Write(&buf, binary.LittleEndian, Section32{
Name: 0x09,
Type: uint32(SHT_STRTAB),
Off: 0x17E944,
Size: 0x08F74D,
Addralign: 0x01,
})
// .shstrtab
binary.Write(&buf, binary.LittleEndian, Section32{
Name: 0x11,
Type: uint32(SHT_STRTAB),
Off: 0x20E091,
Size: 0x0CF3BB,
Addralign: 0x01,
})
}

data := buf.Bytes()

f, err := NewFile(bytes.NewReader(data))
if err != nil {
t.Errorf("cannot create file from data: %v", err)
}
defer f.Close()

wantFileHeader := FileHeader{
Class: ELFCLASS32,
Data: ELFDATA2LSB,
Version: EV_CURRENT,
OSABI: ELFOSABI_NONE,
ByteOrder: binary.LittleEndian,
Type: ET_REL,
Machine: EM_386,
}
if f.FileHeader != wantFileHeader {
t.Errorf("\nhave %#v\nwant %#v\n", f.FileHeader, wantFileHeader)
}

wantSectionNum := 65298
if len(f.Sections) != wantSectionNum {
t.Errorf("len(Sections) = %d, want %d", len(f.Sections), wantSectionNum)
}

wantSectionHeader := SectionHeader{
Name: "section_0007",
Type: SHT_PROGBITS,
Flags: SHF_WRITE + SHF_ALLOC,
Offset: 0x4c,
Size: 0x4,
Addralign: 0x4,
FileSize: 0x4,
}
if f.Sections[10].SectionHeader != wantSectionHeader {
t.Errorf("\nhave %#v\nwant %#v\n", f.Sections[10].SectionHeader, wantSectionHeader)
}
}

func TestIssue10996(t *testing.T) {
data := []byte("\u007fELF\x02\x01\x010000000000000" +
"\x010000000000000000000" +
Expand Down