github.com/bir3/gocompiler@v0.9.2202/src/internal/xcoff/file.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package xcoff implements access to XCOFF (Extended Common Object File Format) files. 6 package xcoff 7 8 import ( 9 "debug/dwarf" 10 "encoding/binary" 11 "fmt" 12 "github.com/bir3/gocompiler/src/internal/saferio" 13 "io" 14 "os" 15 "strings" 16 ) 17 18 // SectionHeader holds information about an XCOFF section header. 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 // AuxiliaryCSect holds information about an XCOFF symbol in an AUX_CSECT entry. 36 type AuxiliaryCSect struct { 37 Length int64 38 StorageMappingClass int 39 SymbolType int 40 } 41 42 // AuxiliaryFcn holds information about an XCOFF symbol in an AUX_FCN entry. 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 // ImportedSymbol holds information about an imported XCOFF symbol. 66 type ImportedSymbol struct { 67 Name string 68 Library string 69 } 70 71 // FileHeader holds information about an XCOFF file header. 72 type FileHeader struct { 73 TargetMachine uint16 74 } 75 76 // A File represents an open XCOFF file. 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 // Open opens the named file using os.Open and prepares it for use as an XCOFF binary. 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 // Close closes the File. 103 // If the File was created using NewFile directly instead of Open, 104 // Close has no effect. 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 // Section returns the first section with the given name, or nil if no such 115 // section exists. 116 // Xcoff have section's name limited to 8 bytes. Some sections like .gosymtab 117 // can be trunked but this method will still find them. 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 // SectionByType returns the first section in f with the 128 // given type, or nil if there is no such section. 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 // cstring converts ASCII byte sequence b to string. 139 // It stops once it finds 0 or reaches end of b. 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 // getString extracts a string from an XCOFF string table. 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 // NewFile creates a new File for accessing an XCOFF binary in an underlying reader. 156 func NewFile(r io.ReaderAt) (*File, error) { 157 sr := io.NewSectionReader(r, 0, 1<<63-1) 158 // Read XCOFF target machine 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 // Read XCOFF file header 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 // Read string table (located right after symbol table). 207 offset := symptr + uint64(nsyms)*SYMESZ 208 if _, err := sr.Seek(int64(offset), io.SeekStart); err != nil { 209 return nil, err 210 } 211 // The first 4 bytes contain the length (in bytes). 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 // Read section headers 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 { // .bss must have all 0s 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 // Symbol map needed by relocation 272 var idxToSym = make(map[int]*Symbol) 273 274 // Read symbol table 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 // Must have at least one csect auxiliary entry. 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 // If this symbol is a function, it must retrieve its size from 339 // its AUX_FCN entry. 340 // It can happen that a function symbol doesn't have any AUX_FCN. 341 // In this case, needAuxFcn is false and their size will be set to 0. 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 // Read csect auxiliary entry (by convention, it is the last). 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 // Skip auxiliary entries 388 if _, err := sr.Seek(int64(numaux)*SYMESZ, io.SeekCurrent); err != nil { 389 return nil, err 390 } 391 } 392 393 // Read relocations 394 // Only for .data or .text section 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 // zeroReaderAt is ReaderAt that reads 0s. 455 type zeroReaderAt struct{} 456 457 // ReadAt writes len(p) 0s into p. 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 // Data reads and returns the contents of the XCOFF section s. 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 // CSect reads and returns the contents of a csect. 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 // There are many other DWARF sections, but these 498 // are the ones the debug/dwarf package uses. 499 // Don't bother loading others. 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 // readImportID returns the import file IDs stored inside the .loader section. 518 // Library name pattern is either path/base/member or base/member 519 func (f *File) readImportIDs(s *Section) ([]string, error) { 520 // Read loader header 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 // Read loader import file ID table 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 // First import file ID is the default LIBPATH value 557 libpath := cstring(table[offset:]) 558 f.LibraryPaths = strings.Split(libpath, ":") 559 offset += len(libpath) + 3 // 3 null bytes 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 // ImportedSymbols returns the names of all symbols 581 // referred to by the binary f that are expected to be 582 // satisfied by other libraries at dynamic load time. 583 // It does not return weak symbols. 584 func (f *File) ImportedSymbols() ([]ImportedSymbol, error) { 585 s := f.SectionByType(STYP_LOADER) 586 if s == nil { 587 return nil, nil 588 } 589 // Read loader header 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 // Read loader section string table 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 // Read imported libraries 628 libs, err := f.readImportIDs(s) 629 if err != nil { 630 return nil, err 631 } 632 633 // Read loader symbol table 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 // Imported symbols only 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 // Imported symbols only 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 // ImportedLibraries returns the names of all libraries 688 // referred to by the binary f that are expected to be 689 // linked with the binary at dynamic link time. 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 }