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