diff options
Diffstat (limited to 'src/pkg/debug')
-rw-r--r-- | src/pkg/debug/dwarf/const.go | 1 | ||||
-rw-r--r-- | src/pkg/debug/dwarf/entry.go | 4 | ||||
-rw-r--r-- | src/pkg/debug/elf/file.go | 53 | ||||
-rw-r--r-- | src/pkg/debug/elf/file_test.go | 134 | ||||
-rw-r--r-- | src/pkg/debug/elf/testdata/gcc-amd64-openbsd-debug-with-rela.obj | bin | 0 -> 6544 bytes | |||
-rw-r--r-- | src/pkg/debug/elf/testdata/hello-world-core.gz | bin | 0 -> 12678 bytes | |||
-rw-r--r-- | src/pkg/debug/gosym/pclntab_test.go | 14 | ||||
-rw-r--r-- | src/pkg/debug/gosym/symtab.go | 127 | ||||
-rw-r--r-- | src/pkg/debug/pe/file.go | 68 | ||||
-rw-r--r-- | src/pkg/debug/pe/file_test.go | 30 | ||||
-rw-r--r-- | src/pkg/debug/pe/pe.go | 11 |
11 files changed, 372 insertions, 70 deletions
diff --git a/src/pkg/debug/dwarf/const.go b/src/pkg/debug/dwarf/const.go index 918b153d0..ad696dc32 100644 --- a/src/pkg/debug/dwarf/const.go +++ b/src/pkg/debug/dwarf/const.go @@ -207,6 +207,7 @@ const ( formRef8 format = 0x14 formRefUdata format = 0x15 formIndirect format = 0x16 + formFlagPresent format = 0x19 ) // A Tag is the classification (the type) of an Entry. diff --git a/src/pkg/debug/dwarf/entry.go b/src/pkg/debug/dwarf/entry.go index 2885d8fa2..f376e4088 100644 --- a/src/pkg/debug/dwarf/entry.go +++ b/src/pkg/debug/dwarf/entry.go @@ -185,6 +185,10 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry { // flag case formFlag: val = b.uint8() == 1 + case formFlagPresent: + // The attribute is implicitly indicated as present, and no value is + // encoded in the debugging information entry itself. + val = true // reference to other entry case formRefAddr: diff --git a/src/pkg/debug/elf/file.go b/src/pkg/debug/elf/file.go index 184ca8375..acb9817af 100644 --- a/src/pkg/debug/elf/file.go +++ b/src/pkg/debug/elf/file.go @@ -31,6 +31,7 @@ type FileHeader struct { ByteOrder binary.ByteOrder Type Type Machine Machine + Entry uint64 } // A File represents an open ELF file. @@ -240,6 +241,7 @@ func NewFile(r io.ReaderAt) (*File, error) { } f.Type = Type(hdr.Type) f.Machine = Machine(hdr.Machine) + f.Entry = uint64(hdr.Entry) if v := Version(hdr.Version); v != f.Version { return nil, &FormatError{0, "mismatched ELF version", v} } @@ -258,6 +260,7 @@ func NewFile(r io.ReaderAt) (*File, error) { } f.Type = Type(hdr.Type) f.Machine = Machine(hdr.Machine) + f.Entry = uint64(hdr.Entry) if v := Version(hdr.Version); v != f.Version { return nil, &FormatError{0, "mismatched ELF version", v} } @@ -269,7 +272,8 @@ func NewFile(r io.ReaderAt) (*File, error) { shnum = int(hdr.Shnum) shstrndx = int(hdr.Shstrndx) } - if shstrndx < 0 || shstrndx >= shnum { + + if shnum > 0 && shoff > 0 && (shstrndx < 0 || shstrndx >= shnum) { return nil, &FormatError{0, "invalid ELF shstrndx", shstrndx} } @@ -364,6 +368,10 @@ func NewFile(r io.ReaderAt) (*File, error) { f.Sections[i] = s } + if len(f.Sections) == 0 { + return f, nil + } + // Load section header string table. shstrtab, err := f.Sections[shstrndx].Data() if err != nil { @@ -414,10 +422,6 @@ func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, error) { return nil, nil, errors.New("cannot load string table section") } - // The first entry is all zeros. - var skip [Sym32Size]byte - symtab.Read(skip[0:]) - symbols := make([]Symbol, symtab.Len()/Sym32Size) i := 0 @@ -457,10 +461,6 @@ func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) { return nil, nil, errors.New("cannot load string table section") } - // The first entry is all zeros. - var skip [Sym64Size]byte - symtab.Read(skip[0:]) - symbols := make([]Symbol, symtab.Len()/Sym64Size) i := 0 @@ -705,8 +705,8 @@ func (f *File) gnuVersionInit(str []byte) { // gnuVersion adds Library and Version information to sym, // which came from offset i of the symbol table. func (f *File) gnuVersion(i int, sym *ImportedSymbol) { - // Each entry is two bytes; skip undef entry at beginning. - i = (i + 1) * 2 + // Each entry is two bytes. + i = i * 2 if i >= len(f.gnuVersym) { return } @@ -723,6 +723,20 @@ func (f *File) gnuVersion(i int, sym *ImportedSymbol) { // referred to by the binary f that are expected to be // linked with the binary at dynamic link time. func (f *File) ImportedLibraries() ([]string, error) { + return f.DynString(DT_NEEDED) +} + +// DynString returns the strings listed for the given tag in the file's dynamic +// section. +// +// The tag must be one that takes string values: DT_NEEDED, DT_SONAME, DT_RPATH, or +// DT_RUNPATH. +func (f *File) DynString(tag DynTag) ([]string, error) { + switch tag { + case DT_NEEDED, DT_SONAME, DT_RPATH, DT_RUNPATH: + default: + return nil, fmt.Errorf("non-string-valued tag %v", tag) + } ds := f.SectionByType(SHT_DYNAMIC) if ds == nil { // not dynamic, so no libraries @@ -738,25 +752,24 @@ func (f *File) ImportedLibraries() ([]string, error) { } var all []string for len(d) > 0 { - var tag DynTag - var value uint64 + var t DynTag + var v uint64 switch f.Class { case ELFCLASS32: - tag = DynTag(f.ByteOrder.Uint32(d[0:4])) - value = uint64(f.ByteOrder.Uint32(d[4:8])) + t = DynTag(f.ByteOrder.Uint32(d[0:4])) + v = uint64(f.ByteOrder.Uint32(d[4:8])) d = d[8:] case ELFCLASS64: - tag = DynTag(f.ByteOrder.Uint64(d[0:8])) - value = f.ByteOrder.Uint64(d[8:16]) + t = DynTag(f.ByteOrder.Uint64(d[0:8])) + v = f.ByteOrder.Uint64(d[8:16]) d = d[16:] } - if tag == DT_NEEDED { - s, ok := getString(str, int(value)) + if t == tag { + s, ok := getString(str, int(v)) if ok { all = append(all, s) } } } - return all, nil } diff --git a/src/pkg/debug/elf/file_test.go b/src/pkg/debug/elf/file_test.go index 98f2723c8..f9aa7265a 100644 --- a/src/pkg/debug/elf/file_test.go +++ b/src/pkg/debug/elf/file_test.go @@ -5,10 +5,14 @@ package elf import ( + "bytes" + "compress/gzip" "debug/dwarf" "encoding/binary" + "io" "net" "os" + "path" "reflect" "runtime" "testing" @@ -19,12 +23,13 @@ type fileTest struct { hdr FileHeader sections []SectionHeader progs []ProgHeader + needed []string } var fileTests = []fileTest{ { "testdata/gcc-386-freebsd-exec", - FileHeader{ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_FREEBSD, 0, binary.LittleEndian, ET_EXEC, EM_386}, + FileHeader{ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_FREEBSD, 0, binary.LittleEndian, ET_EXEC, EM_386, 0x80483cc}, []SectionHeader{ {"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, {".interp", SHT_PROGBITS, SHF_ALLOC, 0x80480d4, 0xd4, 0x15, 0x0, 0x0, 0x1, 0x0}, @@ -64,10 +69,11 @@ var fileTests = []fileTest{ {PT_LOAD, PF_R + PF_W, 0x5fc, 0x80495fc, 0x80495fc, 0xd8, 0xf8, 0x1000}, {PT_DYNAMIC, PF_R + PF_W, 0x60c, 0x804960c, 0x804960c, 0x98, 0x98, 0x4}, }, + []string{"libc.so.6"}, }, { "testdata/gcc-amd64-linux-exec", - FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0, binary.LittleEndian, ET_EXEC, EM_X86_64}, + FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0, binary.LittleEndian, ET_EXEC, EM_X86_64, 0x4003e0}, []SectionHeader{ {"", SHT_NULL, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, {".interp", SHT_PROGBITS, SHF_ALLOC, 0x400200, 0x200, 0x1c, 0x0, 0x0, 0x1, 0x0}, @@ -117,6 +123,32 @@ var fileTests = []fileTest{ {PT_LOOS + 0x474E550, PF_R, 0x5b8, 0x4005b8, 0x4005b8, 0x24, 0x24, 0x4}, {PT_LOOS + 0x474E551, PF_R + PF_W, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8}, }, + []string{"libc.so.6"}, + }, + { + "testdata/hello-world-core.gz", + FileHeader{ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_NONE, 0x0, binary.LittleEndian, ET_CORE, EM_X86_64, 0x0}, + []SectionHeader{}, + []ProgHeader{ + {Type: PT_NOTE, Flags: 0x0, Off: 0x3f8, Vaddr: 0x0, Paddr: 0x0, Filesz: 0x8ac, Memsz: 0x0, Align: 0x0}, + {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x1000, Vaddr: 0x400000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_R, Off: 0x1000, Vaddr: 0x401000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x2000, Vaddr: 0x402000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3000, Vaddr: 0x7f54078b8000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1b5000, Align: 0x1000}, + {Type: PT_LOAD, Flags: 0x0, Off: 0x3000, Vaddr: 0x7f5407a6d000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x1ff000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_R, Off: 0x3000, Vaddr: 0x7f5407c6c000, Paddr: 0x0, Filesz: 0x4000, Memsz: 0x4000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x7000, Vaddr: 0x7f5407c70000, Paddr: 0x0, Filesz: 0x2000, Memsz: 0x2000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x9000, Vaddr: 0x7f5407c72000, Paddr: 0x0, Filesz: 0x5000, Memsz: 0x5000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0xe000, Vaddr: 0x7f5407c77000, Paddr: 0x0, Filesz: 0x0, Memsz: 0x22000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0xe000, Vaddr: 0x7f5407e81000, Paddr: 0x0, Filesz: 0x3000, Memsz: 0x3000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x11000, Vaddr: 0x7f5407e96000, Paddr: 0x0, Filesz: 0x3000, Memsz: 0x3000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_R, Off: 0x14000, Vaddr: 0x7f5407e99000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x15000, Vaddr: 0x7f5407e9a000, Paddr: 0x0, Filesz: 0x2000, Memsz: 0x2000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_W + PF_R, Off: 0x17000, Vaddr: 0x7fff79972000, Paddr: 0x0, Filesz: 0x23000, Memsz: 0x23000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3a000, Vaddr: 0x7fff799f8000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000}, + {Type: PT_LOAD, Flags: PF_X + PF_R, Off: 0x3b000, Vaddr: 0xffffffffff600000, Paddr: 0x0, Filesz: 0x1000, Memsz: 0x1000, Align: 0x1000}, + }, + nil, }, } @@ -124,9 +156,18 @@ func TestOpen(t *testing.T) { for i := range fileTests { tt := &fileTests[i] - f, err := Open(tt.file) + var f *File + var err error + if path.Ext(tt.file) == ".gz" { + var r io.ReaderAt + if r, err = decompress(tt.file); err == nil { + f, err = NewFile(r) + } + } else { + f, err = Open(tt.file) + } if err != nil { - t.Error(err) + t.Errorf("cannot open file %s: %v", tt.file, err) continue } if !reflect.DeepEqual(f.FileHeader, tt.hdr) { @@ -161,26 +202,69 @@ func TestOpen(t *testing.T) { if tn != fn { t.Errorf("open %s: len(Progs) = %d, want %d", tt.file, fn, tn) } + tl := tt.needed + fl, err := f.ImportedLibraries() + if err != nil { + t.Error(err) + } + if !reflect.DeepEqual(tl, fl) { + t.Errorf("open %s: DT_NEEDED = %v, want %v", tt.file, tl, fl) + } } } +// elf.NewFile requires io.ReaderAt, which compress/gzip cannot +// provide. Decompress the file to a bytes.Reader. +func decompress(gz string) (io.ReaderAt, error) { + in, err := os.Open(gz) + if err != nil { + return nil, err + } + defer in.Close() + r, err := gzip.NewReader(in) + if err != nil { + return nil, err + } + var out bytes.Buffer + _, err = io.Copy(&out, r) + return bytes.NewReader(out.Bytes()), err +} + +type relocationTestEntry struct { + entryNumber int + entry *dwarf.Entry +} + type relocationTest struct { - file string - firstEntry *dwarf.Entry + file string + entries []relocationTestEntry } var relocationTests = []relocationTest{ { "testdata/go-relocation-test-gcc441-x86-64.obj", - &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}, + []relocationTestEntry{ + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, + }, }, { "testdata/go-relocation-test-gcc441-x86.obj", - &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "t.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x5)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}, + []relocationTestEntry{ + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "t.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x5)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, + }, }, { "testdata/go-relocation-test-gcc424-x86-64.obj", - &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}, + []relocationTestEntry{ + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, + }, + }, + { + "testdata/gcc-amd64-openbsd-debug-with-rela.obj", + []relocationTestEntry{ + {203, &dwarf.Entry{Offset: 0xc62, Tag: dwarf.TagMember, Children: false, Field: []dwarf.Field{{Attr: dwarf.AttrName, Val: "it_interval"}, {Attr: dwarf.AttrDeclFile, Val: int64(7)}, {Attr: dwarf.AttrDeclLine, Val: int64(236)}, {Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f)}, {Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x0}}}}}, + {204, &dwarf.Entry{Offset: 0xc70, Tag: dwarf.TagMember, Children: false, Field: []dwarf.Field{{Attr: dwarf.AttrName, Val: "it_value"}, {Attr: dwarf.AttrDeclFile, Val: int64(7)}, {Attr: dwarf.AttrDeclLine, Val: int64(237)}, {Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f)}, {Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x10}}}}}, + }, }, } @@ -196,20 +280,24 @@ func TestDWARFRelocations(t *testing.T) { t.Error(err) continue } - reader := dwarf.Reader() - // Checking only the first entry is sufficient since it has - // many different strings. If the relocation had failed, all - // the string offsets would be zero and all the strings would - // end up being the same. - firstEntry, err := reader.Next() - if err != nil { - t.Error(err) - continue - } - - if !reflect.DeepEqual(test.firstEntry, firstEntry) { - t.Errorf("#%d: mismatch: got:%#v want:%#v", i, firstEntry, test.firstEntry) - continue + for _, testEntry := range test.entries { + reader := dwarf.Reader() + for j := 0; j < testEntry.entryNumber; j++ { + entry, err := reader.Next() + if entry == nil || err != nil { + t.Errorf("Failed to skip to entry %d: %v", testEntry.entryNumber, err) + continue + } + } + entry, err := reader.Next() + if err != nil { + t.Error(err) + continue + } + if !reflect.DeepEqual(testEntry.entry, entry) { + t.Errorf("#%d/%d: mismatch: got:%#v want:%#v", i, testEntry.entryNumber, entry, testEntry.entry) + continue + } } } } diff --git a/src/pkg/debug/elf/testdata/gcc-amd64-openbsd-debug-with-rela.obj b/src/pkg/debug/elf/testdata/gcc-amd64-openbsd-debug-with-rela.obj Binary files differnew file mode 100644 index 000000000..f62b1ea1c --- /dev/null +++ b/src/pkg/debug/elf/testdata/gcc-amd64-openbsd-debug-with-rela.obj diff --git a/src/pkg/debug/elf/testdata/hello-world-core.gz b/src/pkg/debug/elf/testdata/hello-world-core.gz Binary files differnew file mode 100644 index 000000000..806af6edb --- /dev/null +++ b/src/pkg/debug/elf/testdata/hello-world-core.gz diff --git a/src/pkg/debug/gosym/pclntab_test.go b/src/pkg/debug/gosym/pclntab_test.go index ade704335..20acba612 100644 --- a/src/pkg/debug/gosym/pclntab_test.go +++ b/src/pkg/debug/gosym/pclntab_test.go @@ -52,6 +52,14 @@ func dotest() bool { return true } +func endtest() { + if pclineTempDir != "" { + os.RemoveAll(pclineTempDir) + pclineTempDir = "" + pclinetestBinary = "" + } +} + func getTable(t *testing.T) *Table { f, tab := crack(os.Args[0], t) f.Close() @@ -95,6 +103,7 @@ func TestLineFromAline(t *testing.T) { if !dotest() { return } + defer endtest() tab := getTable(t) @@ -129,7 +138,7 @@ func TestLineFromAline(t *testing.T) { if !ok { t.Errorf("file %s starts on line %d", path, line) } else if line != ll+1 { - t.Errorf("expected next line of file %s to be %d, got %d", path, ll+1, line) + t.Fatalf("expected next line of file %s to be %d, got %d", path, ll+1, line) } lastline[path] = line } @@ -142,6 +151,7 @@ func TestLineAline(t *testing.T) { if !dotest() { return } + defer endtest() tab := getTable(t) @@ -183,7 +193,7 @@ func TestPCLine(t *testing.T) { if !dotest() { return } - defer os.RemoveAll(pclineTempDir) + defer endtest() f, tab := crack(pclinetestBinary, t) text := f.Section(".text") diff --git a/src/pkg/debug/gosym/symtab.go b/src/pkg/debug/gosym/symtab.go index 52d7d55a3..81ed4fb27 100644 --- a/src/pkg/debug/gosym/symtab.go +++ b/src/pkg/debug/gosym/symtab.go @@ -13,6 +13,7 @@ package gosym // and the Go format is the runtime source, specifically ../../runtime/symtab.c. import ( + "bytes" "encoding/binary" "fmt" "strconv" @@ -98,24 +99,116 @@ type Table struct { } type sym struct { - value uint32 - gotype uint32 + value uint64 + gotype uint64 typ byte name []byte } +var littleEndianSymtab = []byte{0xFD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00} +var bigEndianSymtab = []byte{0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x00, 0x00} + +var oldLittleEndianSymtab = []byte{0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00} + func walksymtab(data []byte, fn func(sym) error) error { + var order binary.ByteOrder = binary.BigEndian + newTable := false + switch { + case bytes.HasPrefix(data, oldLittleEndianSymtab): + // Same as Go 1.0, but little endian. + // Format was used during interim development between Go 1.0 and Go 1.1. + // Should not be widespread, but easy to support. + data = data[6:] + order = binary.LittleEndian + case bytes.HasPrefix(data, bigEndianSymtab): + newTable = true + case bytes.HasPrefix(data, littleEndianSymtab): + newTable = true + order = binary.LittleEndian + } + var ptrsz int + if newTable { + if len(data) < 8 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + ptrsz = int(data[7]) + if ptrsz != 4 && ptrsz != 8 { + return &DecodingError{7, "invalid pointer size", ptrsz} + } + data = data[8:] + } var s sym p := data - for len(p) >= 6 { - s.value = binary.BigEndian.Uint32(p[0:4]) - typ := p[4] - if typ&0x80 == 0 { - return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ} + for len(p) >= 4 { + var typ byte + if newTable { + // Symbol type, value, Go type. + typ = p[0] & 0x3F + wideValue := p[0]&0x40 != 0 + goType := p[0]&0x80 != 0 + if typ < 26 { + typ += 'A' + } else { + typ += 'a' - 26 + } + s.typ = typ + p = p[1:] + if wideValue { + if len(p) < ptrsz { + return &DecodingError{len(data), "unexpected EOF", nil} + } + // fixed-width value + if ptrsz == 8 { + s.value = order.Uint64(p[0:8]) + p = p[8:] + } else { + s.value = uint64(order.Uint32(p[0:4])) + p = p[4:] + } + } else { + // varint value + s.value = 0 + shift := uint(0) + for len(p) > 0 && p[0]&0x80 != 0 { + s.value |= uint64(p[0]&0x7F) << shift + shift += 7 + p = p[1:] + } + if len(p) == 0 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + s.value |= uint64(p[0]) << shift + p = p[1:] + } + if goType { + if len(p) < ptrsz { + return &DecodingError{len(data), "unexpected EOF", nil} + } + // fixed-width go type + if ptrsz == 8 { + s.gotype = order.Uint64(p[0:8]) + p = p[8:] + } else { + s.gotype = uint64(order.Uint32(p[0:4])) + p = p[4:] + } + } + } else { + // Value, symbol type. + s.value = uint64(order.Uint32(p[0:4])) + if len(p) < 5 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + typ = p[4] + if typ&0x80 == 0 { + return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ} + } + typ &^= 0x80 + s.typ = typ + p = p[5:] } - typ &^= 0x80 - s.typ = typ - p = p[5:] + + // Name. var i int var nnul int for i = 0; i < len(p); i++ { @@ -134,13 +227,21 @@ func walksymtab(data []byte, fn func(sym) error) error { } } } - if i+nnul+4 > len(p) { + if len(p) < i+nnul { return &DecodingError{len(data), "unexpected EOF", nil} } s.name = p[0:i] i += nnul - s.gotype = binary.BigEndian.Uint32(p[i : i+4]) - p = p[i+4:] + p = p[i:] + + if !newTable { + if len(p) < 4 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + // Go type. + s.gotype = uint64(order.Uint32(p[:4])) + p = p[4:] + } fn(s) } return nil diff --git a/src/pkg/debug/pe/file.go b/src/pkg/debug/pe/file.go index 6b98a5f45..f521566ef 100644 --- a/src/pkg/debug/pe/file.go +++ b/src/pkg/debug/pe/file.go @@ -19,6 +19,7 @@ import ( type File struct { FileHeader Sections []*Section + Symbols []*Symbol closer io.Closer } @@ -49,6 +50,14 @@ type Section struct { sr *io.SectionReader } +type Symbol struct { + Name string + Value uint32 + SectionNumber int16 + Type uint16 + StorageClass uint8 +} + type ImportDirectory struct { OriginalFirstThunk uint32 TimeDateStamp uint32 @@ -122,12 +131,13 @@ func NewFile(r io.ReaderAt) (*File, error) { } var base int64 if dosheader[0] == 'M' && dosheader[1] == 'Z' { + signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:])) var sign [4]byte - r.ReadAt(sign[0:], int64(dosheader[0x3c])) + r.ReadAt(sign[:], signoff) if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) { return nil, errors.New("Invalid PE File Format.") } - base = int64(dosheader[0x3c]) + 4 + base = signoff + 4 } else { base = int64(0) } @@ -138,16 +148,52 @@ func NewFile(r io.ReaderAt) (*File, error) { if f.FileHeader.Machine != IMAGE_FILE_MACHINE_UNKNOWN && f.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && f.FileHeader.Machine != IMAGE_FILE_MACHINE_I386 { return nil, errors.New("Invalid PE File Format.") } - // get symbol string table - sr.Seek(int64(f.FileHeader.PointerToSymbolTable+18*f.FileHeader.NumberOfSymbols), os.SEEK_SET) - var l uint32 - if err := binary.Read(sr, binary.LittleEndian, &l); err != nil { - return nil, err - } - ss := make([]byte, l) - if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+18*f.FileHeader.NumberOfSymbols)); err != nil { - return nil, err + + var ss []byte + if f.FileHeader.NumberOfSymbols > 0 { + // Get COFF string table, which is located at the end of the COFF symbol table. + sr.Seek(int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols), os.SEEK_SET) + var l uint32 + if err := binary.Read(sr, binary.LittleEndian, &l); err != nil { + return nil, err + } + ss = make([]byte, l) + if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols)); err != nil { + return nil, err + } + + // Process COFF symbol table. + sr.Seek(int64(f.FileHeader.PointerToSymbolTable), os.SEEK_SET) + aux := uint8(0) + for i := 0; i < int(f.FileHeader.NumberOfSymbols); i++ { + cs := new(COFFSymbol) + if err := binary.Read(sr, binary.LittleEndian, cs); err != nil { + return nil, err + } + if aux > 0 { + aux-- + continue + } + var name string + if cs.Name[0] == 0 && cs.Name[1] == 0 && cs.Name[2] == 0 && cs.Name[3] == 0 { + si := int(binary.LittleEndian.Uint32(cs.Name[4:])) + name, _ = getString(ss, si) + } else { + name = cstring(cs.Name[:]) + } + aux = cs.NumberOfAuxSymbols + s := &Symbol{ + Name: name, + Value: cs.Value, + SectionNumber: cs.SectionNumber, + Type: cs.Type, + StorageClass: cs.StorageClass, + } + f.Symbols = append(f.Symbols, s) + } } + + // Process sections. sr.Seek(base, os.SEEK_SET) binary.Read(sr, binary.LittleEndian, &f.FileHeader) sr.Seek(int64(f.FileHeader.SizeOfOptionalHeader), os.SEEK_CUR) //Skip OptionalHeader diff --git a/src/pkg/debug/pe/file_test.go b/src/pkg/debug/pe/file_test.go index 2815d720b..c0f9fcb95 100644 --- a/src/pkg/debug/pe/file_test.go +++ b/src/pkg/debug/pe/file_test.go @@ -13,6 +13,7 @@ type fileTest struct { file string hdr FileHeader sections []*SectionHeader + symbols []*Symbol } var fileTests = []fileTest{ @@ -33,6 +34,24 @@ var fileTests = []fileTest{ {".debug_pubtypes", 0, 0, 38, 1370, 1580, 0, 1, 0, 1108344832}, {".debug_aranges", 0, 0, 32, 1408, 1590, 0, 2, 0, 1108344832}, }, + []*Symbol{ + {".file", 0x0, -2, 0x0, 0x67}, + {"_main", 0x0, 1, 0x20, 0x2}, + {".text", 0x0, 1, 0x0, 0x3}, + {".data", 0x0, 2, 0x0, 0x3}, + {".bss", 0x0, 3, 0x0, 0x3}, + {".debug_abbrev", 0x0, 4, 0x0, 0x3}, + {".debug_info", 0x0, 5, 0x0, 0x3}, + {".debug_line", 0x0, 6, 0x0, 0x3}, + {".rdata", 0x0, 7, 0x0, 0x3}, + {".debug_frame", 0x0, 8, 0x0, 0x3}, + {".debug_loc", 0x0, 9, 0x0, 0x3}, + {".debug_pubnames", 0x0, 10, 0x0, 0x3}, + {".debug_pubtypes", 0x0, 11, 0x0, 0x3}, + {".debug_aranges", 0x0, 12, 0x0, 0x3}, + {"___main", 0x0, 0, 0x20, 0x2}, + {"_puts", 0x0, 0, 0x20, 0x2}, + }, }, { "testdata/gcc-386-mingw-exec", @@ -54,6 +73,7 @@ var fileTests = []fileTest{ {Name: ".debug_frame", VirtualSize: 0x34, VirtualAddress: 0xe000, Size: 0x200, Offset: 0x3800, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42300000}, {Name: ".debug_loc", VirtualSize: 0x38, VirtualAddress: 0xf000, Size: 0x200, Offset: 0x3a00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000}, }, + []*Symbol{}, }, } @@ -86,7 +106,15 @@ func TestOpen(t *testing.T) { if tn != fn { t.Errorf("open %s: len(Sections) = %d, want %d", tt.file, fn, tn) } - + for i, have := range f.Symbols { + if i >= len(tt.symbols) { + break + } + want := tt.symbols[i] + if !reflect.DeepEqual(have, want) { + t.Errorf("open %s, symbol %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want) + } + } } } diff --git a/src/pkg/debug/pe/pe.go b/src/pkg/debug/pe/pe.go index b3dab739a..0606217b3 100644 --- a/src/pkg/debug/pe/pe.go +++ b/src/pkg/debug/pe/pe.go @@ -27,6 +27,17 @@ type SectionHeader32 struct { Characteristics uint32 } +const COFFSymbolSize = 18 + +type COFFSymbol struct { + Name [8]uint8 + Value uint32 + SectionNumber int16 + Type uint16 + StorageClass uint8 + NumberOfAuxSymbols uint8 +} + const ( IMAGE_FILE_MACHINE_UNKNOWN = 0x0 IMAGE_FILE_MACHINE_AM33 = 0x1d3 |