1
2
3
4
5
6 package xcoff
7
8 import (
9 "debug/dwarf"
10 "encoding/binary"
11 "fmt"
12 "io"
13 "os"
14 "strings"
15 )
16
17
18 type SectionHeader struct {
19 Name string
20 VirtualAddress uint64
21 Size uint64
22 Type uint32
23 Relptr uint64
24 Nreloc uint32
25 }
26
27 type Section struct {
28 SectionHeader
29 Relocs []Reloc
30 io.ReaderAt
31 sr *io.SectionReader
32 }
33
34
35 type AuxiliaryCSect struct {
36 Length int64
37 StorageMappingClass int
38 SymbolType int
39 }
40
41
42 type AuxiliaryFcn struct {
43 Size int64
44 }
45
46 type Symbol struct {
47 Name string
48 Value uint64
49 SectionNumber int
50 StorageClass int
51 AuxFcn AuxiliaryFcn
52 AuxCSect AuxiliaryCSect
53 }
54
55 type Reloc struct {
56 VirtualAddress uint64
57 Symbol *Symbol
58 Signed bool
59 InstructionFixed bool
60 Length uint8
61 Type uint8
62 }
63
64
65 type ImportedSymbol struct {
66 Name string
67 Library string
68 }
69
70
71 type FileHeader struct {
72 TargetMachine uint16
73 }
74
75
76 type File struct {
77 FileHeader
78 Sections []*Section
79 Symbols []*Symbol
80 StringTable []byte
81 LibraryPaths []string
82
83 closer io.Closer
84 }
85
86
87 func Open(name string) (*File, error) {
88 f, err := os.Open(name)
89 if err != nil {
90 return nil, err
91 }
92 ff, err := NewFile(f)
93 if err != nil {
94 f.Close()
95 return nil, err
96 }
97 ff.closer = f
98 return ff, nil
99 }
100
101
102
103
104 func (f *File) Close() error {
105 var err error
106 if f.closer != nil {
107 err = f.closer.Close()
108 f.closer = nil
109 }
110 return err
111 }
112
113
114
115
116
117 func (f *File) Section(name string) *Section {
118 for _, s := range f.Sections {
119 if s.Name == name || (len(name) > 8 && s.Name == name[:8]) {
120 return s
121 }
122 }
123 return nil
124 }
125
126
127
128 func (f *File) SectionByType(typ uint32) *Section {
129 for _, s := range f.Sections {
130 if s.Type == typ {
131 return s
132 }
133 }
134 return nil
135 }
136
137
138
139 func cstring(b []byte) string {
140 var i int
141 for i = 0; i < len(b) && b[i] != 0; i++ {
142 }
143 return string(b[:i])
144 }
145
146
147 func getString(st []byte, offset uint32) (string, bool) {
148 if offset < 4 || int(offset) >= len(st) {
149 return "", false
150 }
151 return cstring(st[offset:]), true
152 }
153
154
155 func NewFile(r io.ReaderAt) (*File, error) {
156 sr := io.NewSectionReader(r, 0, 1<<63-1)
157
158 var magic uint16
159 if err := binary.Read(sr, binary.BigEndian, &magic); err != nil {
160 return nil, err
161 }
162 if magic != U802TOCMAGIC && magic != U64_TOCMAGIC {
163 return nil, fmt.Errorf("unrecognised XCOFF magic: 0x%x", magic)
164 }
165
166 f := new(File)
167 f.TargetMachine = magic
168
169
170 if _, err := sr.Seek(0, os.SEEK_SET); err != nil {
171 return nil, err
172 }
173 var nscns uint16
174 var symptr uint64
175 var nsyms int32
176 var opthdr uint16
177 var hdrsz int
178 switch f.TargetMachine {
179 case U802TOCMAGIC:
180 fhdr := new(FileHeader32)
181 if err := binary.Read(sr, binary.BigEndian, fhdr); err != nil {
182 return nil, err
183 }
184 nscns = fhdr.Fnscns
185 symptr = uint64(fhdr.Fsymptr)
186 nsyms = fhdr.Fnsyms
187 opthdr = fhdr.Fopthdr
188 hdrsz = FILHSZ_32
189 case U64_TOCMAGIC:
190 fhdr := new(FileHeader64)
191 if err := binary.Read(sr, binary.BigEndian, fhdr); err != nil {
192 return nil, err
193 }
194 nscns = fhdr.Fnscns
195 symptr = fhdr.Fsymptr
196 nsyms = fhdr.Fnsyms
197 opthdr = fhdr.Fopthdr
198 hdrsz = FILHSZ_64
199 }
200
201 if symptr == 0 || nsyms <= 0 {
202 return nil, fmt.Errorf("no symbol table")
203 }
204
205
206 offset := symptr + uint64(nsyms)*SYMESZ
207 if _, err := sr.Seek(int64(offset), os.SEEK_SET); err != nil {
208 return nil, err
209 }
210
211 var l uint32
212 if err := binary.Read(sr, binary.BigEndian, &l); err != nil {
213 return nil, err
214 }
215 if l > 4 {
216 if _, err := sr.Seek(int64(offset), os.SEEK_SET); err != nil {
217 return nil, err
218 }
219 f.StringTable = make([]byte, l)
220 if _, err := io.ReadFull(sr, f.StringTable); err != nil {
221 return nil, err
222 }
223 }
224
225
226 if _, err := sr.Seek(int64(hdrsz)+int64(opthdr), os.SEEK_SET); err != nil {
227 return nil, err
228 }
229 f.Sections = make([]*Section, nscns)
230 for i := 0; i < int(nscns); i++ {
231 var scnptr uint64
232 s := new(Section)
233 switch f.TargetMachine {
234 case U802TOCMAGIC:
235 shdr := new(SectionHeader32)
236 if err := binary.Read(sr, binary.BigEndian, shdr); err != nil {
237 return nil, err
238 }
239 s.Name = cstring(shdr.Sname[:])
240 s.VirtualAddress = uint64(shdr.Svaddr)
241 s.Size = uint64(shdr.Ssize)
242 scnptr = uint64(shdr.Sscnptr)
243 s.Type = shdr.Sflags
244 s.Relptr = uint64(shdr.Srelptr)
245 s.Nreloc = uint32(shdr.Snreloc)
246 case U64_TOCMAGIC:
247 shdr := new(SectionHeader64)
248 if err := binary.Read(sr, binary.BigEndian, shdr); err != nil {
249 return nil, err
250 }
251 s.Name = cstring(shdr.Sname[:])
252 s.VirtualAddress = shdr.Svaddr
253 s.Size = shdr.Ssize
254 scnptr = shdr.Sscnptr
255 s.Type = shdr.Sflags
256 s.Relptr = shdr.Srelptr
257 s.Nreloc = shdr.Snreloc
258 }
259 r2 := r
260 if scnptr == 0 {
261 r2 = zeroReaderAt{}
262 }
263 s.sr = io.NewSectionReader(r2, int64(scnptr), int64(s.Size))
264 s.ReaderAt = s.sr
265 f.Sections[i] = s
266 }
267
268
269 var idxToSym = make(map[int]*Symbol)
270
271
272 if _, err := sr.Seek(int64(symptr), os.SEEK_SET); err != nil {
273 return nil, err
274 }
275 f.Symbols = make([]*Symbol, 0)
276 for i := 0; i < int(nsyms); i++ {
277 var numaux int
278 var ok, needAuxFcn bool
279 sym := new(Symbol)
280 switch f.TargetMachine {
281 case U802TOCMAGIC:
282 se := new(SymEnt32)
283 if err := binary.Read(sr, binary.BigEndian, se); err != nil {
284 return nil, err
285 }
286 numaux = int(se.Nnumaux)
287 sym.SectionNumber = int(se.Nscnum)
288 sym.StorageClass = int(se.Nsclass)
289 sym.Value = uint64(se.Nvalue)
290 needAuxFcn = se.Ntype&SYM_TYPE_FUNC != 0 && numaux > 1
291 zeroes := binary.BigEndian.Uint32(se.Nname[:4])
292 if zeroes != 0 {
293 sym.Name = cstring(se.Nname[:])
294 } else {
295 offset := binary.BigEndian.Uint32(se.Nname[4:])
296 sym.Name, ok = getString(f.StringTable, offset)
297 if !ok {
298 goto skip
299 }
300 }
301 case U64_TOCMAGIC:
302 se := new(SymEnt64)
303 if err := binary.Read(sr, binary.BigEndian, se); err != nil {
304 return nil, err
305 }
306 numaux = int(se.Nnumaux)
307 sym.SectionNumber = int(se.Nscnum)
308 sym.StorageClass = int(se.Nsclass)
309 sym.Value = se.Nvalue
310 needAuxFcn = se.Ntype&SYM_TYPE_FUNC != 0 && numaux > 1
311 sym.Name, ok = getString(f.StringTable, se.Noffset)
312 if !ok {
313 goto skip
314 }
315 }
316 if sym.StorageClass != C_EXT && sym.StorageClass != C_WEAKEXT && sym.StorageClass != C_HIDEXT {
317 goto skip
318 }
319
320 if numaux < 1 || i+numaux >= int(nsyms) {
321 goto skip
322 }
323
324 if sym.SectionNumber > int(nscns) {
325 goto skip
326 }
327 if sym.SectionNumber == 0 {
328 sym.Value = 0
329 } else {
330 sym.Value -= f.Sections[sym.SectionNumber-1].VirtualAddress
331 }
332
333 idxToSym[i] = sym
334
335
336
337
338
339 if needAuxFcn {
340 switch f.TargetMachine {
341 case U802TOCMAGIC:
342 aux := new(AuxFcn32)
343 if err := binary.Read(sr, binary.BigEndian, aux); err != nil {
344 return nil, err
345 }
346 sym.AuxFcn.Size = int64(aux.Xfsize)
347 case U64_TOCMAGIC:
348 aux := new(AuxFcn64)
349 if err := binary.Read(sr, binary.BigEndian, aux); err != nil {
350 return nil, err
351 }
352 sym.AuxFcn.Size = int64(aux.Xfsize)
353 }
354 }
355
356
357 if !needAuxFcn {
358 if _, err := sr.Seek(int64(numaux-1)*SYMESZ, os.SEEK_CUR); err != nil {
359 return nil, err
360 }
361 }
362 i += numaux
363 numaux = 0
364 switch f.TargetMachine {
365 case U802TOCMAGIC:
366 aux := new(AuxCSect32)
367 if err := binary.Read(sr, binary.BigEndian, aux); err != nil {
368 return nil, err
369 }
370 sym.AuxCSect.SymbolType = int(aux.Xsmtyp & 0x7)
371 sym.AuxCSect.StorageMappingClass = int(aux.Xsmclas)
372 sym.AuxCSect.Length = int64(aux.Xscnlen)
373 case U64_TOCMAGIC:
374 aux := new(AuxCSect64)
375 if err := binary.Read(sr, binary.BigEndian, aux); err != nil {
376 return nil, err
377 }
378 sym.AuxCSect.SymbolType = int(aux.Xsmtyp & 0x7)
379 sym.AuxCSect.StorageMappingClass = int(aux.Xsmclas)
380 sym.AuxCSect.Length = int64(aux.Xscnlenhi)<<32 | int64(aux.Xscnlenlo)
381 }
382 f.Symbols = append(f.Symbols, sym)
383 skip:
384 i += numaux
385 if _, err := sr.Seek(int64(numaux)*SYMESZ, os.SEEK_CUR); err != nil {
386 return nil, err
387 }
388 }
389
390
391
392 for _, sect := range f.Sections {
393 if sect.Type != STYP_TEXT && sect.Type != STYP_DATA {
394 continue
395 }
396 sect.Relocs = make([]Reloc, sect.Nreloc)
397 if sect.Relptr == 0 {
398 continue
399 }
400 if _, err := sr.Seek(int64(sect.Relptr), os.SEEK_SET); err != nil {
401 return nil, err
402 }
403 for i := uint32(0); i < sect.Nreloc; i++ {
404 switch f.TargetMachine {
405 case U802TOCMAGIC:
406 rel := new(Reloc32)
407 if err := binary.Read(sr, binary.BigEndian, rel); err != nil {
408 return nil, err
409 }
410 sect.Relocs[i].VirtualAddress = uint64(rel.Rvaddr)
411 sect.Relocs[i].Symbol = idxToSym[int(rel.Rsymndx)]
412 sect.Relocs[i].Type = rel.Rtype
413 sect.Relocs[i].Length = rel.Rsize&0x3F + 1
414
415 if rel.Rsize&0x80 != 0 {
416 sect.Relocs[i].Signed = true
417 }
418 if rel.Rsize&0x40 != 0 {
419 sect.Relocs[i].InstructionFixed = true
420 }
421
422 case U64_TOCMAGIC:
423 rel := new(Reloc64)
424 if err := binary.Read(sr, binary.BigEndian, rel); err != nil {
425 return nil, err
426 }
427 sect.Relocs[i].VirtualAddress = rel.Rvaddr
428 sect.Relocs[i].Symbol = idxToSym[int(rel.Rsymndx)]
429 sect.Relocs[i].Type = rel.Rtype
430 sect.Relocs[i].Length = rel.Rsize&0x3F + 1
431 if rel.Rsize&0x80 != 0 {
432 sect.Relocs[i].Signed = true
433 }
434 if rel.Rsize&0x40 != 0 {
435 sect.Relocs[i].InstructionFixed = true
436 }
437 }
438 }
439 }
440
441 return f, nil
442 }
443
444
445 type zeroReaderAt struct{}
446
447
448 func (w zeroReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
449 for i := range p {
450 p[i] = 0
451 }
452 return len(p), nil
453 }
454
455
456 func (s *Section) Data() ([]byte, error) {
457 dat := make([]byte, s.sr.Size())
458 n, err := s.sr.ReadAt(dat, 0)
459 if n == len(dat) {
460 err = nil
461 }
462 return dat[:n], err
463 }
464
465
466 func (f *File) CSect(name string) []byte {
467 for _, sym := range f.Symbols {
468 if sym.Name == name && sym.AuxCSect.SymbolType == XTY_SD {
469 if i := sym.SectionNumber - 1; 0 <= i && i < len(f.Sections) {
470 s := f.Sections[i]
471 if sym.Value+uint64(sym.AuxCSect.Length) <= s.Size {
472 dat := make([]byte, sym.AuxCSect.Length)
473 _, err := s.sr.ReadAt(dat, int64(sym.Value))
474 if err != nil {
475 return nil
476 }
477 return dat
478 }
479 }
480 break
481 }
482 }
483 return nil
484 }
485
486 func (f *File) DWARF() (*dwarf.Data, error) {
487
488
489
490 var subtypes = [...]uint32{SSUBTYP_DWABREV, SSUBTYP_DWINFO, SSUBTYP_DWLINE, SSUBTYP_DWRNGES, SSUBTYP_DWSTR}
491 var dat [len(subtypes)][]byte
492 for i, subtype := range subtypes {
493 s := f.SectionByType(STYP_DWARF | subtype)
494 if s != nil {
495 b, err := s.Data()
496 if err != nil && uint64(len(b)) < s.Size {
497 return nil, err
498 }
499 dat[i] = b
500 }
501 }
502
503 abbrev, info, line, ranges, str := dat[0], dat[1], dat[2], dat[3], dat[4]
504 return dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str)
505 }
506
507
508
509 func (f *File) readImportIDs(s *Section) ([]string, error) {
510
511 if _, err := s.sr.Seek(0, os.SEEK_SET); err != nil {
512 return nil, err
513 }
514 var istlen uint32
515 var nimpid int32
516 var impoff uint64
517 switch f.TargetMachine {
518 case U802TOCMAGIC:
519 lhdr := new(LoaderHeader32)
520 if err := binary.Read(s.sr, binary.BigEndian, lhdr); err != nil {
521 return nil, err
522 }
523 istlen = lhdr.Listlen
524 nimpid = lhdr.Lnimpid
525 impoff = uint64(lhdr.Limpoff)
526 case U64_TOCMAGIC:
527 lhdr := new(LoaderHeader64)
528 if err := binary.Read(s.sr, binary.BigEndian, lhdr); err != nil {
529 return nil, err
530 }
531 istlen = lhdr.Listlen
532 nimpid = lhdr.Lnimpid
533 impoff = lhdr.Limpoff
534 }
535
536
537 if _, err := s.sr.Seek(int64(impoff), os.SEEK_SET); err != nil {
538 return nil, err
539 }
540 table := make([]byte, istlen)
541 if _, err := io.ReadFull(s.sr, table); err != nil {
542 return nil, err
543 }
544
545 offset := 0
546
547 libpath := cstring(table[offset:])
548 f.LibraryPaths = strings.Split(libpath, ":")
549 offset += len(libpath) + 3
550 all := make([]string, 0)
551 for i := 1; i < int(nimpid); i++ {
552 impidpath := cstring(table[offset:])
553 offset += len(impidpath) + 1
554 impidbase := cstring(table[offset:])
555 offset += len(impidbase) + 1
556 impidmem := cstring(table[offset:])
557 offset += len(impidmem) + 1
558 var path string
559 if len(impidpath) > 0 {
560 path = impidpath + "/" + impidbase + "/" + impidmem
561 } else {
562 path = impidbase + "/" + impidmem
563 }
564 all = append(all, path)
565 }
566
567 return all, nil
568 }
569
570
571
572
573
574 func (f *File) ImportedSymbols() ([]ImportedSymbol, error) {
575 s := f.SectionByType(STYP_LOADER)
576 if s == nil {
577 return nil, nil
578 }
579
580 if _, err := s.sr.Seek(0, os.SEEK_SET); err != nil {
581 return nil, err
582 }
583 var stlen uint32
584 var stoff uint64
585 var nsyms int32
586 var symoff uint64
587 switch f.TargetMachine {
588 case U802TOCMAGIC:
589 lhdr := new(LoaderHeader32)
590 if err := binary.Read(s.sr, binary.BigEndian, lhdr); err != nil {
591 return nil, err
592 }
593 stlen = lhdr.Lstlen
594 stoff = uint64(lhdr.Lstoff)
595 nsyms = lhdr.Lnsyms
596 symoff = LDHDRSZ_32
597 case U64_TOCMAGIC:
598 lhdr := new(LoaderHeader64)
599 if err := binary.Read(s.sr, binary.BigEndian, lhdr); err != nil {
600 return nil, err
601 }
602 stlen = lhdr.Lstlen
603 stoff = lhdr.Lstoff
604 nsyms = lhdr.Lnsyms
605 symoff = lhdr.Lsymoff
606 }
607
608
609 if _, err := s.sr.Seek(int64(stoff), os.SEEK_SET); err != nil {
610 return nil, err
611 }
612 st := make([]byte, stlen)
613 if _, err := io.ReadFull(s.sr, st); err != nil {
614 return nil, err
615 }
616
617
618 libs, err := f.readImportIDs(s)
619 if err != nil {
620 return nil, err
621 }
622
623
624 if _, err := s.sr.Seek(int64(symoff), os.SEEK_SET); err != nil {
625 return nil, err
626 }
627 all := make([]ImportedSymbol, 0)
628 for i := 0; i < int(nsyms); i++ {
629 var name string
630 var ifile int32
631 var ok bool
632 switch f.TargetMachine {
633 case U802TOCMAGIC:
634 ldsym := new(LoaderSymbol32)
635 if err := binary.Read(s.sr, binary.BigEndian, ldsym); err != nil {
636 return nil, err
637 }
638 if ldsym.Lsmtype&0x40 == 0 {
639 continue
640 }
641 zeroes := binary.BigEndian.Uint32(ldsym.Lname[:4])
642 if zeroes != 0 {
643 name = cstring(ldsym.Lname[:])
644 } else {
645 offset := binary.BigEndian.Uint32(ldsym.Lname[4:])
646 name, ok = getString(st, offset)
647 if !ok {
648 continue
649 }
650 }
651 ifile = ldsym.Lifile
652 case U64_TOCMAGIC:
653 ldsym := new(LoaderSymbol64)
654 if err := binary.Read(s.sr, binary.BigEndian, ldsym); err != nil {
655 return nil, err
656 }
657 if ldsym.Lsmtype&0x40 == 0 {
658 continue
659 }
660 name, ok = getString(st, ldsym.Loffset)
661 if !ok {
662 continue
663 }
664 ifile = ldsym.Lifile
665 }
666 var sym ImportedSymbol
667 sym.Name = name
668 if ifile >= 1 && int(ifile) <= len(libs) {
669 sym.Library = libs[ifile-1]
670 }
671 all = append(all, sym)
672 }
673
674 return all, nil
675 }
676
677
678
679
680 func (f *File) ImportedLibraries() ([]string, error) {
681 s := f.SectionByType(STYP_LOADER)
682 if s == nil {
683 return nil, nil
684 }
685 all, err := f.readImportIDs(s)
686 return all, err
687 }
688
View as plain text