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

The spec https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html
states:

1. e_shnum: If the number of sections is greater than or equal to
SHN_LORESERVE (0xff00), this member 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.

2. e_shstrndx: 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.

This CL makes these changes to support files with >= 0xff00 sections:

1. if shoff > 0 && shnum == 0, read sh_size from the initial section
header entry as shnum.
2. if shstrndx == SHN_XINDEX, read sh_link from the initial section
header entry as shstrndx.

It returns an error if the type of the initial section is not SHT_NULL.

A file with >= 0xff00 sections is too big to include in the repository,
so the test case constructs one on the fly, with some of the sections
zeroed out.

While here, remove the unnecessary use of reflect.DeepEqual in the test.

Fixes #55294.

Change-Id: I15ec43612c7cce6e8decfe4e81da3a5b16de47f7
GitHub-Last-Rev: 797c16480b
GitHub-Pull-Request: golang/go#55295
Reviewed-on: https://go-review.googlesource.com/c/go/+/432255
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
This commit is contained in:
Zeke Lu 2022-09-26 20:46:24 +00:00 committed by Gopher Robot
parent 1e4989c336
commit 7d157fd0eb
3 changed files with 303 additions and 8 deletions

View file

@ -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
}

View file

@ -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)

View file

@ -9,6 +9,7 @@ import (
"compress/gzip"
"debug/dwarf"
"encoding/binary"
"fmt"
"io"
"math/rand"
"net"
@ -230,7 +231,7 @@ 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
}
@ -238,18 +239,18 @@ func TestOpen(t *testing.T) {
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)
@ -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" +