1
2
3
4
5
6
7 package objfile
8
9 import (
10 "debug/dwarf"
11 "debug/elf"
12 "encoding/binary"
13 "fmt"
14 "io"
15 )
16
17 type elfFile struct {
18 elf *elf.File
19 }
20
21 func openElf(r io.ReaderAt) (rawFile, error) {
22 f, err := elf.NewFile(r)
23 if err != nil {
24 return nil, err
25 }
26 return &elfFile{f}, nil
27 }
28
29 func (f *elfFile) symbols() ([]Sym, error) {
30 elfSyms, err := f.elf.Symbols()
31 if err != nil {
32 return nil, err
33 }
34
35 var syms []Sym
36 for _, s := range elfSyms {
37 sym := Sym{Addr: s.Value, Name: s.Name, Size: int64(s.Size), Code: '?'}
38 switch s.Section {
39 case elf.SHN_UNDEF:
40 sym.Code = 'U'
41 case elf.SHN_COMMON:
42 sym.Code = 'B'
43 default:
44 i := int(s.Section)
45 if i < 0 || i >= len(f.elf.Sections) {
46 break
47 }
48 sect := f.elf.Sections[i]
49 switch sect.Flags & (elf.SHF_WRITE | elf.SHF_ALLOC | elf.SHF_EXECINSTR) {
50 case elf.SHF_ALLOC | elf.SHF_EXECINSTR:
51 sym.Code = 'T'
52 case elf.SHF_ALLOC:
53 sym.Code = 'R'
54 case elf.SHF_ALLOC | elf.SHF_WRITE:
55 if sect.Type == elf.SHT_NOBITS {
56 sym.Code = 'B'
57 } else {
58 sym.Code = 'D'
59 }
60 }
61 }
62 if elf.ST_BIND(s.Info) == elf.STB_LOCAL {
63 sym.Code += 'a' - 'A'
64 }
65 syms = append(syms, sym)
66 }
67
68 return syms, nil
69 }
70
71 func (f *elfFile) pcln() (textStart uint64, pclntab []byte, err error) {
72 if sect := f.elf.Section(".text"); sect != nil {
73 textStart = sect.Addr
74 }
75
76 sect := f.elf.Section(".gopclntab")
77 if sect == nil {
78
79 sect = f.elf.Section(".data.rel.ro.gopclntab")
80 }
81 if sect != nil {
82 if pclntab, err = sect.Data(); err != nil {
83 return 0, nil, err
84 }
85 } else {
86
87 pclntab = f.symbolData("runtime.pclntab", "runtime.epclntab")
88 }
89
90 return textStart, pclntab, nil
91 }
92
93 func (f *elfFile) text() (textStart uint64, text []byte, err error) {
94 sect := f.elf.Section(".text")
95 if sect == nil {
96 return 0, nil, fmt.Errorf("text section not found")
97 }
98 textStart = sect.Addr
99 text, err = sect.Data()
100 return
101 }
102
103 func (f *elfFile) goarch() string {
104 switch f.elf.Machine {
105 case elf.EM_386:
106 return "386"
107 case elf.EM_X86_64:
108 return "amd64"
109 case elf.EM_ARM:
110 return "arm"
111 case elf.EM_AARCH64:
112 return "arm64"
113 case elf.EM_LOONGARCH:
114 return "loong64"
115 case elf.EM_PPC64:
116 if f.elf.ByteOrder == binary.LittleEndian {
117 return "ppc64le"
118 }
119 return "ppc64"
120 case elf.EM_RISCV:
121 if f.elf.Class == elf.ELFCLASS64 {
122 return "riscv64"
123 }
124 case elf.EM_S390:
125 return "s390x"
126 }
127 return ""
128 }
129
130 func (f *elfFile) loadAddress() (uint64, error) {
131 for _, p := range f.elf.Progs {
132 if p.Type == elf.PT_LOAD && p.Flags&elf.PF_X != 0 {
133
134
135
136
137
138 return p.Vaddr - p.Vaddr%p.Align, nil
139 }
140 }
141 return 0, fmt.Errorf("unknown load address")
142 }
143
144 func (f *elfFile) dwarf() (*dwarf.Data, error) {
145 return f.elf.DWARF()
146 }
147
148 func (f *elfFile) symbolData(start, end string) []byte {
149 elfSyms, err := f.elf.Symbols()
150 if err != nil {
151 return nil
152 }
153 var addr, eaddr uint64
154 for _, s := range elfSyms {
155 if s.Name == start {
156 addr = s.Value
157 } else if s.Name == end {
158 eaddr = s.Value
159 }
160 if addr != 0 && eaddr != 0 {
161 break
162 }
163 }
164 if addr == 0 || eaddr < addr {
165 return nil
166 }
167 size := eaddr - addr
168 data := make([]byte, size)
169 for _, prog := range f.elf.Progs {
170 if prog.Vaddr <= addr && addr+size-1 <= prog.Vaddr+prog.Filesz-1 {
171 if _, err := prog.ReadAt(data, int64(addr-prog.Vaddr)); err != nil {
172 return nil
173 }
174 return data
175 }
176 }
177 return nil
178 }
179
View as plain text