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