diff options
Diffstat (limited to 'src/debug')
| -rw-r--r-- | src/debug/dwarf/buf.go | 2 | ||||
| -rw-r--r-- | src/debug/dwarf/line.go | 2 | ||||
| -rw-r--r-- | src/debug/dwarf/typeunit.go | 4 | ||||
| -rw-r--r-- | src/debug/elf/elf.go | 4 | ||||
| -rw-r--r-- | src/debug/elf/file.go | 40 | ||||
| -rw-r--r-- | src/debug/gosym/pclntab.go | 6 | ||||
| -rw-r--r-- | src/debug/gosym/symtab.go | 4 | ||||
| -rw-r--r-- | src/debug/pe/file.go | 199 | ||||
| -rw-r--r-- | src/debug/pe/pe.go | 24 | ||||
| -rw-r--r-- | src/debug/pe/section.go | 111 | ||||
| -rw-r--r-- | src/debug/pe/string.go | 63 | ||||
| -rw-r--r-- | src/debug/pe/symbol.go | 95 |
12 files changed, 365 insertions, 189 deletions
diff --git a/src/debug/dwarf/buf.go b/src/debug/dwarf/buf.go index 7443043c11..24d266db10 100644 --- a/src/debug/dwarf/buf.go +++ b/src/debug/dwarf/buf.go @@ -157,7 +157,7 @@ func (b *buf) addr() uint64 { case 4: return uint64(b.uint32()) case 8: - return uint64(b.uint64()) + return b.uint64() } b.error("unknown address size") return 0 diff --git a/src/debug/dwarf/line.go b/src/debug/dwarf/line.go index b3b91ade62..ed82feef92 100644 --- a/src/debug/dwarf/line.go +++ b/src/debug/dwarf/line.go @@ -361,7 +361,7 @@ func (r *LineReader) step(entry *LineEntry) bool { // Special opcode [DWARF2 6.2.5.1, DWARF4 6.2.5.1] adjustedOpcode := opcode - r.opcodeBase r.advancePC(adjustedOpcode / r.lineRange) - lineDelta := r.lineBase + int(adjustedOpcode)%r.lineRange + lineDelta := r.lineBase + adjustedOpcode%r.lineRange r.state.Line += lineDelta goto emit } diff --git a/src/debug/dwarf/typeunit.go b/src/debug/dwarf/typeunit.go index ed42547386..652e02d917 100644 --- a/src/debug/dwarf/typeunit.go +++ b/src/debug/dwarf/typeunit.go @@ -76,7 +76,7 @@ func (d *Data) parseTypes(name string, types []byte) error { data: b.bytes(int(n - (b.off - hdroff))), atable: atable, asize: int(asize), - vers: int(vers), + vers: vers, is64: dwarf64, }, toff: Offset(toff), @@ -101,7 +101,7 @@ func (d *Data) sigToType(sig uint64) (Type, error) { b := makeBuf(d, tu, tu.name, tu.off, tu.data) r := &typeUnitReader{d: d, tu: tu, b: b} - t, err := d.readType(tu.name, r, Offset(tu.toff), make(map[Offset]Type), nil) + t, err := d.readType(tu.name, r, tu.toff, make(map[Offset]Type), nil) if err != nil { return nil, err } diff --git a/src/debug/elf/elf.go b/src/debug/elf/elf.go index af881c2495..3f43d4d896 100644 --- a/src/debug/elf/elf.go +++ b/src/debug/elf/elf.go @@ -2060,8 +2060,8 @@ type Rela32 struct { Addend int32 /* Addend. */ } -func R_SYM32(info uint32) uint32 { return uint32(info >> 8) } -func R_TYPE32(info uint32) uint32 { return uint32(info & 0xff) } +func R_SYM32(info uint32) uint32 { return info >> 8 } +func R_TYPE32(info uint32) uint32 { return info & 0xff } func R_INFO32(sym, typ uint32) uint32 { return sym<<8 | typ } // ELF32 Symbol. diff --git a/src/debug/elf/file.go b/src/debug/elf/file.go index c28a964b73..c173ea9331 100644 --- a/src/debug/elf/file.go +++ b/src/debug/elf/file.go @@ -269,7 +269,7 @@ func NewFile(r io.ReaderAt) (*File, error) { switch f.Class { case ELFCLASS32: hdr := new(Header32) - sr.Seek(0, os.SEEK_SET) + sr.Seek(0, io.SeekStart) if err := binary.Read(sr, f.ByteOrder, hdr); err != nil { return nil, err } @@ -288,13 +288,13 @@ func NewFile(r io.ReaderAt) (*File, error) { shstrndx = int(hdr.Shstrndx) case ELFCLASS64: hdr := new(Header64) - sr.Seek(0, os.SEEK_SET) + sr.Seek(0, io.SeekStart) if err := binary.Read(sr, f.ByteOrder, hdr); err != nil { return nil, err } f.Type = Type(hdr.Type) f.Machine = Machine(hdr.Machine) - f.Entry = uint64(hdr.Entry) + f.Entry = hdr.Entry if v := Version(hdr.Version); v != f.Version { return nil, &FormatError{0, "mismatched ELF version", v} } @@ -315,7 +315,7 @@ func NewFile(r io.ReaderAt) (*File, error) { f.Progs = make([]*Prog, phnum) for i := 0; i < phnum; i++ { off := phoff + int64(i)*int64(phentsize) - sr.Seek(off, os.SEEK_SET) + sr.Seek(off, io.SeekStart) p := new(Prog) switch f.Class { case ELFCLASS32: @@ -341,12 +341,12 @@ func NewFile(r io.ReaderAt) (*File, error) { p.ProgHeader = ProgHeader{ Type: ProgType(ph.Type), Flags: ProgFlag(ph.Flags), - Off: uint64(ph.Off), - Vaddr: uint64(ph.Vaddr), - Paddr: uint64(ph.Paddr), - Filesz: uint64(ph.Filesz), - Memsz: uint64(ph.Memsz), - Align: uint64(ph.Align), + Off: ph.Off, + Vaddr: ph.Vaddr, + Paddr: ph.Paddr, + Filesz: ph.Filesz, + Memsz: ph.Memsz, + Align: ph.Align, } } p.sr = io.NewSectionReader(r, int64(p.Off), int64(p.Filesz)) @@ -359,7 +359,7 @@ func NewFile(r io.ReaderAt) (*File, error) { names := make([]uint32, shnum) for i := 0; i < shnum; i++ { off := shoff + int64(i)*int64(shentsize) - sr.Seek(off, os.SEEK_SET) + sr.Seek(off, io.SeekStart) s := new(Section) switch f.Class { case ELFCLASS32: @@ -374,8 +374,8 @@ func NewFile(r io.ReaderAt) (*File, error) { Addr: uint64(sh.Addr), Offset: uint64(sh.Off), FileSize: uint64(sh.Size), - Link: uint32(sh.Link), - Info: uint32(sh.Info), + Link: sh.Link, + Info: sh.Info, Addralign: uint64(sh.Addralign), Entsize: uint64(sh.Entsize), } @@ -388,13 +388,13 @@ func NewFile(r io.ReaderAt) (*File, error) { s.SectionHeader = SectionHeader{ Type: SectionType(sh.Type), Flags: SectionFlag(sh.Flags), - Offset: uint64(sh.Off), - FileSize: uint64(sh.Size), - Addr: uint64(sh.Addr), - Link: uint32(sh.Link), - Info: uint32(sh.Info), - Addralign: uint64(sh.Addralign), - Entsize: uint64(sh.Entsize), + Offset: sh.Off, + FileSize: sh.Size, + Addr: sh.Addr, + Link: sh.Link, + Info: sh.Info, + Addralign: sh.Addralign, + Entsize: sh.Entsize, } } s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.FileSize)) diff --git a/src/debug/gosym/pclntab.go b/src/debug/gosym/pclntab.go index 01a9f11f05..e859d5aed5 100644 --- a/src/debug/gosym/pclntab.go +++ b/src/debug/gosym/pclntab.go @@ -167,7 +167,7 @@ func (t *LineTable) go12Init() { // Check header: 4-byte magic, two zeros, pc quantum, pointer size. t.go12 = -1 // not Go 1.2 until proven otherwise if len(t.Data) < 16 || t.Data[4] != 0 || t.Data[5] != 0 || - (t.Data[6] != 1 && t.Data[6] != 4) || // pc quantum + (t.Data[6] != 1 && t.Data[6] != 2 && t.Data[6] != 4) || // pc quantum (t.Data[7] != 4 && t.Data[7] != 8) { // pointer size return } @@ -207,8 +207,8 @@ func (t *LineTable) go12Funcs() []Func { funcs := make([]Func, n) for i := range funcs { f := &funcs[i] - f.Entry = uint64(t.uintptr(t.functab[2*i*int(t.ptrsize):])) - f.End = uint64(t.uintptr(t.functab[(2*i+2)*int(t.ptrsize):])) + f.Entry = t.uintptr(t.functab[2*i*int(t.ptrsize):]) + f.End = t.uintptr(t.functab[(2*i+2)*int(t.ptrsize):]) info := t.Data[t.uintptr(t.functab[(2*i+1)*int(t.ptrsize):]):] f.LineTable = t f.FrameSize = int(t.binary.Uint32(info[t.ptrsize+2*4:])) diff --git a/src/debug/gosym/symtab.go b/src/debug/gosym/symtab.go index 49e154fd8e..c8fa9a0b38 100644 --- a/src/debug/gosym/symtab.go +++ b/src/debug/gosym/symtab.go @@ -294,8 +294,8 @@ func NewTable(symtab []byte, pcln *LineTable) (*Table, error) { t.Syms = t.Syms[0 : n+1] ts := &t.Syms[n] ts.Type = s.typ - ts.Value = uint64(s.value) - ts.GoType = uint64(s.gotype) + ts.Value = s.value + ts.GoType = s.gotype switch s.typ { default: // rewrite name to use . instead of ยท (c2 b7) diff --git a/src/debug/pe/file.go b/src/debug/pe/file.go index 1acc368e1b..1cd84d5727 100644 --- a/src/debug/pe/file.go +++ b/src/debug/pe/file.go @@ -8,11 +8,9 @@ package pe import ( "debug/dwarf" "encoding/binary" - "errors" "fmt" "io" "os" - "strconv" ) // A File represents an open PE file. @@ -20,83 +18,13 @@ type File struct { FileHeader OptionalHeader interface{} // of type *OptionalHeader32 or *OptionalHeader64 Sections []*Section - Symbols []*Symbol + Symbols []*Symbol // COFF symbols with auxiliary symbol records removed + COFFSymbols []COFFSymbol // all COFF symbols (including auxiliary symbol records) + StringTable StringTable closer io.Closer } -type SectionHeader struct { - Name string - VirtualSize uint32 - VirtualAddress uint32 - Size uint32 - Offset uint32 - PointerToRelocations uint32 - PointerToLineNumbers uint32 - NumberOfRelocations uint16 - NumberOfLineNumbers uint16 - Characteristics uint32 -} - -type Section struct { - SectionHeader - - // Embed ReaderAt for ReadAt method. - // Do not embed SectionReader directly - // to avoid having Read and Seek. - // If a client wants Read and Seek it must use - // Open() to avoid fighting over the seek offset - // with other clients. - io.ReaderAt - sr *io.SectionReader -} - -type Symbol struct { - Name string - Value uint32 - SectionNumber int16 - Type uint16 - StorageClass uint8 -} - -type ImportDirectory struct { - OriginalFirstThunk uint32 - TimeDateStamp uint32 - ForwarderChain uint32 - Name uint32 - FirstThunk uint32 - - dll string -} - -// Data reads and returns the contents of the PE section. -func (s *Section) Data() ([]byte, error) { - dat := make([]byte, s.sr.Size()) - n, err := s.sr.ReadAt(dat, 0) - if n == len(dat) { - err = nil - } - return dat[0:n], err -} - -// Open returns a new ReadSeeker reading the PE section. -func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } - -type FormatError struct { - off int64 - msg string - val interface{} -} - -func (e *FormatError) Error() string { - msg := e.msg - if e.val != nil { - msg += fmt.Sprintf(" '%v'", e.val) - } - msg += fmt.Sprintf(" in record at byte %#x", e.off) - return msg -} - // Open opens the named file using os.Open and prepares it for use as a PE binary. func Open(name string) (*File, error) { f, err := os.Open(name) @@ -129,6 +57,8 @@ var ( sizeofOptionalHeader64 = uint16(binary.Size(OptionalHeader64{})) ) +// TODO(brainman): add Load function, as a replacement for NewFile, that does not call removeAuxSymbols (for performance) + // NewFile creates a new File for accessing a PE binary in an underlying reader. func NewFile(r io.ReaderAt) (*File, error) { f := new(File) @@ -144,66 +74,42 @@ func NewFile(r io.ReaderAt) (*File, error) { var sign [4]byte 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.") + return nil, fmt.Errorf("Invalid PE COFF file signature of %v.", sign) } base = signoff + 4 } else { base = int64(0) } - sr.Seek(base, os.SEEK_SET) + sr.Seek(base, io.SeekStart) if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil { return nil, err } - 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.") + switch f.FileHeader.Machine { + case IMAGE_FILE_MACHINE_UNKNOWN, IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_I386: + default: + return nil, fmt.Errorf("Unrecognised COFF file header machine value of 0x%x.", f.FileHeader.Machine) } - 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 - } + var err error - // 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) - } + // Read string table. + f.StringTable, err = readStringTable(&f.FileHeader, sr) + if err != nil { + return nil, err + } + + // Read symbol table. + f.COFFSymbols, err = readCOFFSymbols(&f.FileHeader, sr) + if err != nil { + return nil, err + } + f.Symbols, err = removeAuxSymbols(f.COFFSymbols, f.StringTable) + if err != nil { + return nil, err } // Read optional header. - sr.Seek(base, os.SEEK_SET) + sr.Seek(base, io.SeekStart) if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil { return nil, err } @@ -235,12 +141,9 @@ func NewFile(r io.ReaderAt) (*File, error) { if err := binary.Read(sr, binary.LittleEndian, sh); err != nil { return nil, err } - var name string - if sh.Name[0] == '\x2F' { - si, _ := strconv.Atoi(cstring(sh.Name[1:])) - name, _ = getString(ss, si) - } else { - name = cstring(sh.Name[0:]) + name, err := sh.fullName(f.StringTable) + if err != nil { + return nil, err } s := new(Section) s.SectionHeader = SectionHeader{ @@ -259,14 +162,15 @@ func NewFile(r io.ReaderAt) (*File, error) { s.ReaderAt = s.sr f.Sections[i] = s } - return f, nil -} - -func cstring(b []byte) string { - var i int - for i = 0; i < len(b) && b[i] != 0; i++ { + for i := range f.Sections { + var err error + f.Sections[i].Relocs, err = readRelocs(&f.Sections[i].SectionHeader, sr) + if err != nil { + return nil, err + } } - return string(b[0:i]) + + return f, nil } // getString extracts a string from symbol string table. @@ -320,6 +224,18 @@ func (f *File) DWARF() (*dwarf.Data, error) { return dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str) } +// TODO(brainman): document ImportDirectory once we decide what to do with it. + +type ImportDirectory struct { + OriginalFirstThunk uint32 + TimeDateStamp uint32 + ForwarderChain uint32 + Name uint32 + FirstThunk uint32 + + dll string +} + // ImportedSymbols returns the names of all symbols // referred to by the binary f that are expected to be // satisfied by other libraries at dynamic load time. @@ -347,6 +263,12 @@ func (f *File) ImportedSymbols() ([]string, error) { } ida = append(ida, dt) } + // TODO(brainman): this needs to be rewritten + // ds.Data() return contets of .idata section. Why store in variable called "names"? + // Why we are retrieving it second time? We already have it in "d", and it is not modified anywhere. + // getString does not extracts a string from symbol string table (as getString doco says). + // Why ds.Data() called again and again in the loop? + // Needs test before rewrite. names, _ := ds.Data() var all []string for _, dt := range ida { @@ -395,3 +317,12 @@ func (f *File) ImportedLibraries() ([]string, error) { // cgo -dynimport don't use this for windows PE, so just return. return nil, nil } + +// FormatError is unused. +// The type is retained for compatibility. +type FormatError struct { +} + +func (e *FormatError) Error() string { + return "unknown error" +} diff --git a/src/debug/pe/pe.go b/src/debug/pe/pe.go index 6af40e2c78..8050d59c70 100644 --- a/src/debug/pe/pe.go +++ b/src/debug/pe/pe.go @@ -86,30 +86,6 @@ type OptionalHeader64 struct { DataDirectory [16]DataDirectory } -type SectionHeader32 struct { - Name [8]uint8 - VirtualSize uint32 - VirtualAddress uint32 - SizeOfRawData uint32 - PointerToRawData uint32 - PointerToRelocations uint32 - PointerToLineNumbers uint32 - NumberOfRelocations uint16 - NumberOfLineNumbers uint16 - 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 diff --git a/src/debug/pe/section.go b/src/debug/pe/section.go new file mode 100644 index 0000000000..5d881577d3 --- /dev/null +++ b/src/debug/pe/section.go @@ -0,0 +1,111 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pe + +import ( + "encoding/binary" + "fmt" + "io" + "strconv" +) + +// SectionHeader32 represents real PE COFF section header. +type SectionHeader32 struct { + Name [8]uint8 + VirtualSize uint32 + VirtualAddress uint32 + SizeOfRawData uint32 + PointerToRawData uint32 + PointerToRelocations uint32 + PointerToLineNumbers uint32 + NumberOfRelocations uint16 + NumberOfLineNumbers uint16 + Characteristics uint32 +} + +// fullName finds real name of section sh. Normally name is stored +// in sh.Name, but if it is longer then 8 characters, it is stored +// in COFF string table st instead. +func (sh *SectionHeader32) fullName(st StringTable) (string, error) { + if sh.Name[0] != '/' { + return cstring(sh.Name[:]), nil + } + i, err := strconv.Atoi(cstring(sh.Name[1:])) + if err != nil { + return "", err + } + return st.String(uint32(i)) +} + +// TODO(brainman): copy all IMAGE_REL_* consts from ldpe.go here + +// Reloc represents a PE COFF relocation. +// Each section contains its own relocation list. +type Reloc struct { + VirtualAddress uint32 + SymbolTableIndex uint32 + Type uint16 +} + +func readRelocs(sh *SectionHeader, r io.ReadSeeker) ([]Reloc, error) { + if sh.NumberOfRelocations <= 0 { + return nil, nil + } + _, err := r.Seek(int64(sh.PointerToRelocations), io.SeekStart) + if err != nil { + return nil, fmt.Errorf("fail to seek to %q section relocations: %v", sh.Name, err) + } + relocs := make([]Reloc, sh.NumberOfRelocations) + err = binary.Read(r, binary.LittleEndian, relocs) + if err != nil { + return nil, fmt.Errorf("fail to read section relocations: %v", err) + } + return relocs, nil +} + +// SectionHeader is similar to SectionHeader32 with Name +// field replaced by Go string. +type SectionHeader struct { + Name string + VirtualSize uint32 + VirtualAddress uint32 + Size uint32 + Offset uint32 + PointerToRelocations uint32 + PointerToLineNumbers uint32 + NumberOfRelocations uint16 + NumberOfLineNumbers uint16 + Characteristics uint32 +} + +// Section provides access to PE COFF section. +type Section struct { + SectionHeader + Relocs []Reloc + + // Embed ReaderAt for ReadAt method. + // Do not embed SectionReader directly + // to avoid having Read and Seek. + // If a client wants Read and Seek it must use + // Open() to avoid fighting over the seek offset + // with other clients. + io.ReaderAt + sr *io.SectionReader +} + +// Data reads and returns the contents of the PE section s. +func (s *Section) Data() ([]byte, error) { + dat := make([]byte, s.sr.Size()) + n, err := s.sr.ReadAt(dat, 0) + if n == len(dat) { + err = nil + } + return dat[0:n], err +} + +// Open returns a new ReadSeeker reading the PE section s. +func (s *Section) Open() io.ReadSeeker { + return io.NewSectionReader(s.sr, 0, 1<<63-1) +} diff --git a/src/debug/pe/string.go b/src/debug/pe/string.go new file mode 100644 index 0000000000..e00bd97dd4 --- /dev/null +++ b/src/debug/pe/string.go @@ -0,0 +1,63 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pe + +import ( + "encoding/binary" + "fmt" + "io" +) + +// cstring converts ASCII byte sequence b to string. +// It stops once it finds 0 or reaches end of b. +func cstring(b []byte) string { + var i int + for i = 0; i < len(b) && b[i] != 0; i++ { + } + return string(b[:i]) +} + +// StringTable is a COFF string table. +type StringTable []byte + +func readStringTable(fh *FileHeader, r io.ReadSeeker) (StringTable, error) { + // COFF string table is located right after COFF symbol table. + offset := fh.PointerToSymbolTable + COFFSymbolSize*fh.NumberOfSymbols + _, err := r.Seek(int64(offset), io.SeekStart) + if err != nil { + return nil, fmt.Errorf("fail to seek to string table: %v", err) + } + var l uint32 + err = binary.Read(r, binary.LittleEndian, &l) + if err != nil { + return nil, fmt.Errorf("fail to read string table length: %v", err) + } + // string table length includes itself + if l <= 4 { + return nil, nil + } + l -= 4 + buf := make([]byte, l) + _, err = io.ReadFull(r, buf) + if err != nil { + return nil, fmt.Errorf("fail to read string table: %v", err) + } + return StringTable(buf), nil +} + +// TODO(brainman): decide if start parameter should be int instead of uint32 + +// String extracts string from COFF string table st at offset start. +func (st StringTable) String(start uint32) (string, error) { + // start includes 4 bytes of string table length + if start < 4 { + return "", fmt.Errorf("offset %d is before the start of string table", start) + } + start -= 4 + if int(start) > len(st) { + return "", fmt.Errorf("offset %d is beyond the end of string table", start) + } + return cstring(st[start:]), nil +} diff --git a/src/debug/pe/symbol.go b/src/debug/pe/symbol.go new file mode 100644 index 0000000000..3cf5a101e7 --- /dev/null +++ b/src/debug/pe/symbol.go @@ -0,0 +1,95 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pe + +import ( + "encoding/binary" + "fmt" + "io" +) + +const COFFSymbolSize = 18 + +// COFFSymbol represents single COFF symbol table record. +type COFFSymbol struct { + Name [8]uint8 + Value uint32 + SectionNumber int16 + Type uint16 + StorageClass uint8 + NumberOfAuxSymbols uint8 +} + +func readCOFFSymbols(fh *FileHeader, r io.ReadSeeker) ([]COFFSymbol, error) { + if fh.NumberOfSymbols <= 0 { + return nil, nil + } + _, err := r.Seek(int64(fh.PointerToSymbolTable), io.SeekStart) + if err != nil { + return nil, fmt.Errorf("fail to seek to symbol table: %v", err) + } + syms := make([]COFFSymbol, fh.NumberOfSymbols) + err = binary.Read(r, binary.LittleEndian, syms) + if err != nil { + return nil, fmt.Errorf("fail to read symbol table: %v", err) + } + return syms, nil +} + +// isSymNameOffset checks symbol name if it is encoded as offset into string table. +func isSymNameOffset(name [8]byte) (bool, uint32) { + if name[0] == 0 && name[1] == 0 && name[2] == 0 && name[3] == 0 { + return true, binary.LittleEndian.Uint32(name[4:]) + } + return false, 0 +} + +// FullName finds real name of symbol sym. Normally name is stored +// in sym.Name, but if it is longer then 8 characters, it is stored +// in COFF string table st instead. +func (sym *COFFSymbol) FullName(st StringTable) (string, error) { + if ok, offset := isSymNameOffset(sym.Name); ok { + return st.String(offset) + } + return cstring(sym.Name[:]), nil +} + +func removeAuxSymbols(allsyms []COFFSymbol, st StringTable) ([]*Symbol, error) { + if len(allsyms) == 0 { + return nil, nil + } + syms := make([]*Symbol, 0) + aux := uint8(0) + for _, sym := range allsyms { + if aux > 0 { + aux-- + continue + } + name, err := sym.FullName(st) + if err != nil { + return nil, err + } + aux = sym.NumberOfAuxSymbols + s := &Symbol{ + Name: name, + Value: sym.Value, + SectionNumber: sym.SectionNumber, + Type: sym.Type, + StorageClass: sym.StorageClass, + } + syms = append(syms, s) + } + return syms, nil +} + +// Symbol is similar to COFFSymbol with Name field replaced +// by Go string. Symbol also does not have NumberOfAuxSymbols. +type Symbol struct { + Name string + Value uint32 + SectionNumber int16 + Type uint16 + StorageClass uint8 +} |
