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