]> Cypherpunks repositories - gostls13.git/commitdiff
debug/elf: suport files with >= 65280 (0xff00) sections
authorZeke Lu <lvzecai@gmail.com>
Mon, 26 Sep 2022 20:46:24 +0000 (20:46 +0000)
committerGopher Robot <gobot@golang.org>
Tue, 27 Sep 2022 16:50:30 +0000 (16:50 +0000)
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: 797c16480bc76524ff1a7c01ed09b759fcd7d45b
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>

src/cmd/internal/buildid/note.go
src/debug/elf/file.go
src/debug/elf/file_test.go

index f5b6fc565f36dcc6852cba2da03a6ed1bd9e087c..ab98701fb569f22ba0c4da7a9091da6e413e395e 100644 (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
        }
index aff2b00aae3bcf84283a316e2fa65f2461a31ea7..db07a2daff8800eef0188cdfd2fb4fba9948efa3 100644 (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)
index c0decdd66e482dfe21f7abe7ec3e89a44cf292c6..fe72a1908f36c132a32c6a65272e94ead326f36d 100644 (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" +