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 typ := p[0] & 0x7F
220 s.typ = typ
221 p = p[1:]
222
223
224 var i int
225 var nnul int
226 for i = 0; i < len(p); i++ {
227 if p[i] == 0 {
228 nnul = 1
229 break
230 }
231 }
232 switch typ {
233 case 'z', 'Z':
234 p = p[i+nnul:]
235 for i = 0; i+2 <= len(p); i += 2 {
236 if p[i] == 0 && p[i+1] == 0 {
237 nnul = 2
238 break
239 }
240 }
241 }
242 if len(p) < i+nnul {
243 return &formatError{len(data), "unexpected EOF", nil}
244 }
245 s.name = p[0:i]
246 i += nnul
247 p = p[i:]
248
249 fn(s)
250 }
251 return nil
252 }
253
254
255
256 func newTable(symtab []byte, ptrsz int) ([]Sym, error) {
257 var n int
258 err := walksymtab(symtab, ptrsz, func(s sym) error {
259 n++
260 return nil
261 })
262 if err != nil {
263 return nil, err
264 }
265
266 fname := make(map[uint16]string)
267 syms := make([]Sym, 0, n)
268 err = walksymtab(symtab, ptrsz, func(s sym) error {
269 n := len(syms)
270 syms = syms[0 : n+1]
271 ts := &syms[n]
272 ts.Type = rune(s.typ)
273 ts.Value = s.value
274 switch s.typ {
275 default:
276 ts.Name = string(s.name)
277 case 'z', 'Z':
278 for i := 0; i < len(s.name); i += 2 {
279 eltIdx := binary.BigEndian.Uint16(s.name[i : i+2])
280 elt, ok := fname[eltIdx]
281 if !ok {
282 return &formatError{-1, "bad filename code", eltIdx}
283 }
284 if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' {
285 ts.Name += "/"
286 }
287 ts.Name += elt
288 }
289 }
290 switch s.typ {
291 case 'f':
292 fname[uint16(s.value)] = ts.Name
293 }
294 return nil
295 })
296 if err != nil {
297 return nil, err
298 }
299
300 return syms, nil
301 }
302
303
304
305 var ErrNoSymbols = errors.New("no symbol section")
306
307
308 func (f *File) Symbols() ([]Sym, error) {
309 symtabSection := f.Section("syms")
310 if symtabSection == nil {
311 return nil, ErrNoSymbols
312 }
313
314 symtab, err := symtabSection.Data()
315 if err != nil {
316 return nil, errors.New("cannot load symbol section")
317 }
318
319 return newTable(symtab, f.PtrSize)
320 }
321
322
323
324 func (f *File) Section(name string) *Section {
325 for _, s := range f.Sections {
326 if s.Name == name {
327 return s
328 }
329 }
330 return nil
331 }
332
View as plain text