| | |
| | |
| | |
| |
|
| | |
| |
|
| | package objfile |
| |
|
| | import ( |
| | "debug/dwarf" |
| | "debug/pe" |
| | "fmt" |
| | "io" |
| | "slices" |
| | "sort" |
| | ) |
| |
|
| | type peFile struct { |
| | pe *pe.File |
| | } |
| |
|
| | func openPE(r io.ReaderAt) (rawFile, error) { |
| | f, err := pe.NewFile(r) |
| | if err != nil { |
| | return nil, err |
| | } |
| | return &peFile{f}, nil |
| | } |
| |
|
| | func (f *peFile) symbols() ([]Sym, error) { |
| | |
| | |
| | var addrs []uint64 |
| |
|
| | imageBase, _ := f.imageBase() |
| |
|
| | var syms []Sym |
| | for _, s := range f.pe.Symbols { |
| | const ( |
| | N_UNDEF = 0 |
| | N_ABS = -1 |
| | N_DEBUG = -2 |
| | ) |
| | sym := Sym{Name: s.Name, Addr: uint64(s.Value), Code: '?'} |
| | switch s.SectionNumber { |
| | case N_UNDEF: |
| | sym.Code = 'U' |
| | case N_ABS: |
| | sym.Code = 'C' |
| | case N_DEBUG: |
| | sym.Code = '?' |
| | default: |
| | if s.SectionNumber < 0 || len(f.pe.Sections) < int(s.SectionNumber) { |
| | return nil, fmt.Errorf("invalid section number in symbol table") |
| | } |
| | sect := f.pe.Sections[s.SectionNumber-1] |
| | const ( |
| | text = 0x20 |
| | data = 0x40 |
| | bss = 0x80 |
| | permW = 0x80000000 |
| | ) |
| | ch := sect.Characteristics |
| | switch { |
| | case ch&text != 0: |
| | sym.Code = 'T' |
| | case ch&data != 0: |
| | if ch&permW == 0 { |
| | sym.Code = 'R' |
| | } else { |
| | sym.Code = 'D' |
| | } |
| | case ch&bss != 0: |
| | sym.Code = 'B' |
| | } |
| | sym.Addr += imageBase + uint64(sect.VirtualAddress) |
| | } |
| | syms = append(syms, sym) |
| | addrs = append(addrs, sym.Addr) |
| | } |
| |
|
| | slices.Sort(addrs) |
| | for i := range syms { |
| | j := sort.Search(len(addrs), func(x int) bool { return addrs[x] > syms[i].Addr }) |
| | if j < len(addrs) { |
| | syms[i].Size = int64(addrs[j] - syms[i].Addr) |
| | } |
| | } |
| |
|
| | return syms, nil |
| | } |
| |
|
| | func (f *peFile) pcln() (textStart uint64, pclntab []byte, err error) { |
| | imageBase, err := f.imageBase() |
| | if err != nil { |
| | return 0, nil, err |
| | } |
| |
|
| | if sect := f.pe.Section(".text"); sect != nil { |
| | textStart = imageBase + uint64(sect.VirtualAddress) |
| | } |
| | if pclntab, err = loadPETable(f.pe, "runtime.pclntab", "runtime.epclntab"); err != nil { |
| | |
| | |
| | var err2 error |
| | if pclntab, err2 = loadPETable(f.pe, "pclntab", "epclntab"); err2 != nil { |
| | return 0, nil, err |
| | } |
| | } |
| | return textStart, pclntab, nil |
| | } |
| |
|
| | func (f *peFile) text() (textStart uint64, text []byte, err error) { |
| | imageBase, err := f.imageBase() |
| | if err != nil { |
| | return 0, nil, err |
| | } |
| |
|
| | sect := f.pe.Section(".text") |
| | if sect == nil { |
| | return 0, nil, fmt.Errorf("text section not found") |
| | } |
| | textStart = imageBase + uint64(sect.VirtualAddress) |
| | text, err = sect.Data() |
| | return |
| | } |
| |
|
| | func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) { |
| | for _, s := range f.Symbols { |
| | if s.Name != name { |
| | continue |
| | } |
| | if s.SectionNumber <= 0 { |
| | return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber) |
| | } |
| | if len(f.Sections) < int(s.SectionNumber) { |
| | return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections)) |
| | } |
| | return s, nil |
| | } |
| | return nil, fmt.Errorf("no %s symbol found", name) |
| | } |
| |
|
| | func loadPETable(f *pe.File, sname, ename string) ([]byte, error) { |
| | ssym, err := findPESymbol(f, sname) |
| | if err != nil { |
| | return nil, err |
| | } |
| | esym, err := findPESymbol(f, ename) |
| | if err != nil { |
| | return nil, err |
| | } |
| | if ssym.SectionNumber != esym.SectionNumber { |
| | return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename) |
| | } |
| | sect := f.Sections[ssym.SectionNumber-1] |
| | data, err := sect.Data() |
| | if err != nil { |
| | return nil, err |
| | } |
| | return data[ssym.Value:esym.Value], nil |
| | } |
| |
|
| | func (f *peFile) goarch() string { |
| | switch f.pe.Machine { |
| | case pe.IMAGE_FILE_MACHINE_I386: |
| | return "386" |
| | case pe.IMAGE_FILE_MACHINE_AMD64: |
| | return "amd64" |
| | case pe.IMAGE_FILE_MACHINE_ARM64: |
| | return "arm64" |
| | default: |
| | return "" |
| | } |
| | } |
| |
|
| | func (f *peFile) loadAddress() (uint64, error) { |
| | return f.imageBase() |
| | } |
| |
|
| | func (f *peFile) imageBase() (uint64, error) { |
| | switch oh := f.pe.OptionalHeader.(type) { |
| | case *pe.OptionalHeader32: |
| | return uint64(oh.ImageBase), nil |
| | case *pe.OptionalHeader64: |
| | return oh.ImageBase, nil |
| | default: |
| | return 0, fmt.Errorf("pe file format not recognized") |
| | } |
| | } |
| |
|
| | func (f *peFile) dwarf() (*dwarf.Data, error) { |
| | return f.pe.DWARF() |
| | } |
| |
|