summaryrefslogtreecommitdiff
path: root/src/pkg/debug
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/debug')
-rw-r--r--src/pkg/debug/dwarf/const.go1
-rw-r--r--src/pkg/debug/dwarf/entry.go4
-rw-r--r--src/pkg/debug/elf/file.go53
-rw-r--r--src/pkg/debug/elf/file_test.go134
-rw-r--r--src/pkg/debug/elf/testdata/gcc-amd64-openbsd-debug-with-rela.objbin0 -> 6544 bytes
-rw-r--r--src/pkg/debug/elf/testdata/hello-world-core.gzbin0 -> 12678 bytes
-rw-r--r--src/pkg/debug/gosym/pclntab_test.go14
-rw-r--r--src/pkg/debug/gosym/symtab.go127
-rw-r--r--src/pkg/debug/pe/file.go68
-rw-r--r--src/pkg/debug/pe/file_test.go30
-rw-r--r--src/pkg/debug/pe/pe.go11
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
new file mode 100644
index 000000000..f62b1ea1c
--- /dev/null
+++ b/src/pkg/debug/elf/testdata/gcc-amd64-openbsd-debug-with-rela.obj
Binary files differ
diff --git a/src/pkg/debug/elf/testdata/hello-world-core.gz b/src/pkg/debug/elf/testdata/hello-world-core.gz
new file mode 100644
index 000000000..806af6edb
--- /dev/null
+++ b/src/pkg/debug/elf/testdata/hello-world-core.gz
Binary files differ
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