1
2
3
4
5
16 package plan9obj
17
18 import (
19 "encoding/binary"
20 "errors"
21 "fmt"
22 "internal/saferio"
23 "io"
24 "os"
25 )
26
27
28 type FileHeader struct {
29 Magic uint32
30 Bss uint32
31 Entry uint64
32 PtrSize int
33 LoadAddress uint64
34 HdrSize uint64
35 }
36
37
38 type File struct {
39 FileHeader
40 Sections []*Section
41 closer io.Closer
42 }
43
44
45
46
47 type SectionHeader struct {
48 Name string
49 Size uint32
50 Offset uint32
51 }
52
53
54 type Section struct {
55 SectionHeader
56
57
58
59
60
61
62
63 io.ReaderAt
64 sr *io.SectionReader
65 }
66
67
68 func (s *Section) Data() ([]byte, error) {
69 return saferio.ReadDataAt(s.sr, uint64(s.Size), 0)
70 }
71
72
73 func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
74
75
76 type Sym struct {
77 Value uint64
78 Type rune
79 Name string
80 }
81
82
85
86
87
88 type formatError struct {
89 off int
90 msg string
91 val any
92 }
93
94 func (e *formatError) Error() string {
95 msg := e.msg
96 if e.val != nil {
97 msg += fmt.Sprintf(" '%v'", e.val)
98 }
99 msg += fmt.Sprintf(" in record at byte %#x", e.off)
100 return msg
101 }
102
103
104 func Open(name string) (*File, error) {
105 f, err := os.Open(name)
106 if err != nil {
107 return nil, err
108 }
109 ff, err := NewFile(f)
110 if err != nil {
111 f.Close()
112 return nil, err
113 }
114 ff.closer = f
115 return ff, nil
116 }
117
118
119
120
121 func (f *File) Close() error {
122 var err error
123 if f.closer != nil {
124 err = f.closer.Close()
125 f.closer = nil
126 }
127 return err
128 }
129
130 func parseMagic(magic []byte) (uint32, error) {
131 m := binary.BigEndian.Uint32(magic)
132 switch m {
133 case Magic386, MagicAMD64, MagicARM:
134 return m, nil
135 }
136 return 0, &formatError{0, "bad magic number", magic}
137 }
138
139
140
141 func NewFile(r io.ReaderAt) (*File, error) {
142 sr := io.NewSectionReader(r, 0, 1<<63-1)
143
144 var magic [4]byte
145 if _, err := r.ReadAt(magic[:], 0); err != nil {
146 return nil, err
147 }
148 _, err := parseMagic(magic[:])
149 if err != nil {
150 return nil, err
151 }
152
153 ph := new(prog)
154 if err := binary.Read(sr, binary.BigEndian, ph); err != nil {
155 return nil, err
156 }
157
158 f := &File{FileHeader: FileHeader{
159 Magic: ph.Magic,
160 Bss: ph.Bss,
161 Entry: uint64(ph.Entry),
162 PtrSize: 4,
163 LoadAddress: 0x1000,
164 HdrSize: 4 * 8,
165 }}
166
167 if ph.Magic&Magic64 != 0 {
168 if err := binary.Read(sr, binary.BigEndian, &f.Entry); err != nil {
169 return nil, err
170 }
171 f.PtrSize = 8
172 f.LoadAddress = 0x200000
173 f.HdrSize += 8
174 }
175
176 var sects = []struct {
177 name string
178 size uint32
179 }{
180 {"text", ph.Text},
181 {"data", ph.Data},
182 {"syms", ph.Syms},
183 {"spsz", ph.Spsz},
184 {"pcsz", ph.Pcsz},
185 }
186
187 f.Sections = make([]*Section, 5)
188
189 off := uint32(f.HdrSize)
190
191 for i, sect := range sects {
192 s := new(Section)
193 s.SectionHeader = SectionHeader{
194 Name: sect.name,
195 Size: sect.size,
196 Offset: off,
197 }
198 off += sect.size
199 s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Size))
200 s.ReaderAt = s.sr
201 f.Sections[i] = s
202 }
203
204 return f, nil
205 }
206
207 func walksymtab(data []byte, ptrsz int, fn func(sym) error) error {
208 var order binary.ByteOrder = binary.BigEndian
209 var s sym
210 p := data
211 for len(p) >= 4 {
212
213 if len(p) < ptrsz {
214 return &formatError{len(data), "unexpected EOF", nil}
215 }
216
217 if ptrsz == 8 {
218 s.value = order.Uint64(p[0:8])
219 p = p[8:]
220 } else {
221 s.value = uint64(order.Uint32(p[0:4]))
222 p = p[4:]
223 }
224
225 if len(p) < 1 {
226 return &formatError{len(data), "unexpected EOF", nil}
227 }
228 typ := p[0] & 0x7F
229 s.typ = typ
230 p = p[1:]
231
232
233 var i int
234 var nnul int
235 for i = 0; i < len(p); i++ {
236 if p[i] == 0 {
237 nnul = 1
238 break
239 }
240 }
241 switch typ {
242 case 'z', 'Z':
243 p = p[i+nnul:]
244 for i = 0; i+2 <= len(p); i += 2 {
245 if p[i] == 0 && p[i+1] == 0 {
246 nnul = 2
247 break
248 }
249 }
250 }
251 if len(p) < i+nnul {
252 return &formatError{len(data), "unexpected EOF", nil}
253 }
254 s.name = p[0:i]
255 i += nnul
256 p = p[i:]
257
258 fn(s)
259 }
260 return nil
261 }
262
263
264
265 func newTable(symtab []byte, ptrsz int) ([]Sym, error) {
266 var n int
267 err := walksymtab(symtab, ptrsz, func(s sym) error {
268 n++
269 return nil
270 })
271 if err != nil {
272 return nil, err
273 }
274
275 fname := make(map[uint16]string)
276 syms := make([]Sym, 0, n)
277 err = walksymtab(symtab, ptrsz, func(s sym) error {
278 n := len(syms)
279 syms = syms[0 : n+1]
280 ts := &syms[n]
281 ts.Type = rune(s.typ)
282 ts.Value = s.value
283 switch s.typ {
284 default:
285 ts.Name = string(s.name)
286 case 'z', 'Z':
287 for i := 0; i < len(s.name); i += 2 {
288 eltIdx := binary.BigEndian.Uint16(s.name[i : i+2])
289 elt, ok := fname[eltIdx]
290 if !ok {
291 return &formatError{-1, "bad filename code", eltIdx}
292 }
293 if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' {
294 ts.Name += "/"
295 }
296 ts.Name += elt
297 }
298 }
299 switch s.typ {
300 case 'f':
301 fname[uint16(s.value)] = ts.Name
302 }
303 return nil
304 })
305 if err != nil {
306 return nil, err
307 }
308
309 return syms, nil
310 }
311
312
313
314 var ErrNoSymbols = errors.New("no symbol section")
315
316
317 func (f *File) Symbols() ([]Sym, error) {
318 symtabSection := f.Section("syms")
319 if symtabSection == nil {
320 return nil, ErrNoSymbols
321 }
322
323 symtab, err := symtabSection.Data()
324 if err != nil {
325 return nil, errors.New("cannot load symbol section")
326 }
327
328 return newTable(symtab, f.PtrSize)
329 }
330
331
332
333 func (f *File) Section(name string) *Section {
334 for _, s := range f.Sections {
335 if s.Name == name {
336 return s
337 }
338 }
339 return nil
340 }
341
View as plain text