github.com/JimmyHuang454/JLS-go@v0.0.0-20230831150107-90d536585ba0/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 "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 int32 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 f.Sections = make([]*Section, nscns) 229 for i := 0; i < int(nscns); i++ { 230 var scnptr uint64 231 s := new(Section) 232 switch f.TargetMachine { 233 case U802TOCMAGIC: 234 shdr := new(SectionHeader32) 235 if err := binary.Read(sr, binary.BigEndian, shdr); err != nil { 236 return nil, err 237 } 238 s.Name = cstring(shdr.Sname[:]) 239 s.VirtualAddress = uint64(shdr.Svaddr) 240 s.Size = uint64(shdr.Ssize) 241 scnptr = uint64(shdr.Sscnptr) 242 s.Type = shdr.Sflags 243 s.Relptr = uint64(shdr.Srelptr) 244 s.Nreloc = uint32(shdr.Snreloc) 245 case U64_TOCMAGIC: 246 shdr := new(SectionHeader64) 247 if err := binary.Read(sr, binary.BigEndian, shdr); err != nil { 248 return nil, err 249 } 250 s.Name = cstring(shdr.Sname[:]) 251 s.VirtualAddress = shdr.Svaddr 252 s.Size = shdr.Ssize 253 scnptr = shdr.Sscnptr 254 s.Type = shdr.Sflags 255 s.Relptr = shdr.Srelptr 256 s.Nreloc = shdr.Snreloc 257 } 258 r2 := r 259 if scnptr == 0 { // .bss must have all 0s 260 r2 = zeroReaderAt{} 261 } 262 s.sr = io.NewSectionReader(r2, int64(scnptr), int64(s.Size)) 263 s.ReaderAt = s.sr 264 f.Sections[i] = s 265 } 266 267 // Symbol map needed by relocation 268 var idxToSym = make(map[int]*Symbol) 269 270 // Read symbol table 271 if _, err := sr.Seek(int64(symptr), io.SeekStart); err != nil { 272 return nil, err 273 } 274 f.Symbols = make([]*Symbol, 0) 275 for i := 0; i < int(nsyms); i++ { 276 var numaux int 277 var ok, needAuxFcn bool 278 sym := new(Symbol) 279 switch f.TargetMachine { 280 case U802TOCMAGIC: 281 se := new(SymEnt32) 282 if err := binary.Read(sr, binary.BigEndian, se); err != nil { 283 return nil, err 284 } 285 numaux = int(se.Nnumaux) 286 if numaux < 0 { 287 return nil, fmt.Errorf("malformed symbol table, invalid number of aux symbols") 288 } 289 sym.SectionNumber = int(se.Nscnum) 290 sym.StorageClass = int(se.Nsclass) 291 sym.Value = uint64(se.Nvalue) 292 needAuxFcn = se.Ntype&SYM_TYPE_FUNC != 0 && numaux > 1 293 zeroes := binary.BigEndian.Uint32(se.Nname[:4]) 294 if zeroes != 0 { 295 sym.Name = cstring(se.Nname[:]) 296 } else { 297 offset := binary.BigEndian.Uint32(se.Nname[4:]) 298 sym.Name, ok = getString(f.StringTable, offset) 299 if !ok { 300 goto skip 301 } 302 } 303 case U64_TOCMAGIC: 304 se := new(SymEnt64) 305 if err := binary.Read(sr, binary.BigEndian, se); err != nil { 306 return nil, err 307 } 308 numaux = int(se.Nnumaux) 309 if numaux < 0 { 310 return nil, fmt.Errorf("malformed symbol table, invalid number of aux symbols") 311 } 312 sym.SectionNumber = int(se.Nscnum) 313 sym.StorageClass = int(se.Nsclass) 314 sym.Value = se.Nvalue 315 needAuxFcn = se.Ntype&SYM_TYPE_FUNC != 0 && numaux > 1 316 sym.Name, ok = getString(f.StringTable, se.Noffset) 317 if !ok { 318 goto skip 319 } 320 } 321 if sym.StorageClass != C_EXT && sym.StorageClass != C_WEAKEXT && sym.StorageClass != C_HIDEXT { 322 goto skip 323 } 324 // Must have at least one csect auxiliary entry. 325 if numaux < 1 || i+numaux >= int(nsyms) { 326 goto skip 327 } 328 329 if sym.SectionNumber > int(nscns) { 330 goto skip 331 } 332 if sym.SectionNumber == 0 { 333 sym.Value = 0 334 } else { 335 sym.Value -= f.Sections[sym.SectionNumber-1].VirtualAddress 336 } 337 338 idxToSym[i] = sym 339 340 // If this symbol is a function, it must retrieve its size from 341 // its AUX_FCN entry. 342 // It can happen that a function symbol doesn't have any AUX_FCN. 343 // In this case, needAuxFcn is false and their size will be set to 0. 344 if needAuxFcn { 345 switch f.TargetMachine { 346 case U802TOCMAGIC: 347 aux := new(AuxFcn32) 348 if err := binary.Read(sr, binary.BigEndian, aux); err != nil { 349 return nil, err 350 } 351 sym.AuxFcn.Size = int64(aux.Xfsize) 352 case U64_TOCMAGIC: 353 aux := new(AuxFcn64) 354 if err := binary.Read(sr, binary.BigEndian, aux); err != nil { 355 return nil, err 356 } 357 sym.AuxFcn.Size = int64(aux.Xfsize) 358 } 359 } 360 361 // Read csect auxiliary entry (by convention, it is the last). 362 if !needAuxFcn { 363 if _, err := sr.Seek(int64(numaux-1)*SYMESZ, io.SeekCurrent); err != nil { 364 return nil, err 365 } 366 } 367 i += numaux 368 numaux = 0 369 switch f.TargetMachine { 370 case U802TOCMAGIC: 371 aux := new(AuxCSect32) 372 if err := binary.Read(sr, binary.BigEndian, aux); err != nil { 373 return nil, err 374 } 375 sym.AuxCSect.SymbolType = int(aux.Xsmtyp & 0x7) 376 sym.AuxCSect.StorageMappingClass = int(aux.Xsmclas) 377 sym.AuxCSect.Length = int64(aux.Xscnlen) 378 case U64_TOCMAGIC: 379 aux := new(AuxCSect64) 380 if err := binary.Read(sr, binary.BigEndian, aux); err != nil { 381 return nil, err 382 } 383 sym.AuxCSect.SymbolType = int(aux.Xsmtyp & 0x7) 384 sym.AuxCSect.StorageMappingClass = int(aux.Xsmclas) 385 sym.AuxCSect.Length = int64(aux.Xscnlenhi)<<32 | int64(aux.Xscnlenlo) 386 } 387 f.Symbols = append(f.Symbols, sym) 388 skip: 389 i += numaux // Skip auxiliary entries 390 if _, err := sr.Seek(int64(numaux)*SYMESZ, io.SeekCurrent); err != nil { 391 return nil, err 392 } 393 } 394 395 // Read relocations 396 // Only for .data or .text section 397 for _, sect := range f.Sections { 398 if sect.Type != STYP_TEXT && sect.Type != STYP_DATA { 399 continue 400 } 401 sect.Relocs = make([]Reloc, sect.Nreloc) 402 if sect.Relptr == 0 { 403 continue 404 } 405 if _, err := sr.Seek(int64(sect.Relptr), io.SeekStart); err != nil { 406 return nil, err 407 } 408 for i := uint32(0); i < sect.Nreloc; i++ { 409 switch f.TargetMachine { 410 case U802TOCMAGIC: 411 rel := new(Reloc32) 412 if err := binary.Read(sr, binary.BigEndian, rel); err != nil { 413 return nil, err 414 } 415 sect.Relocs[i].VirtualAddress = uint64(rel.Rvaddr) 416 sect.Relocs[i].Symbol = idxToSym[int(rel.Rsymndx)] 417 sect.Relocs[i].Type = rel.Rtype 418 sect.Relocs[i].Length = rel.Rsize&0x3F + 1 419 420 if rel.Rsize&0x80 != 0 { 421 sect.Relocs[i].Signed = true 422 } 423 if rel.Rsize&0x40 != 0 { 424 sect.Relocs[i].InstructionFixed = true 425 } 426 427 case U64_TOCMAGIC: 428 rel := new(Reloc64) 429 if err := binary.Read(sr, binary.BigEndian, rel); err != nil { 430 return nil, err 431 } 432 sect.Relocs[i].VirtualAddress = rel.Rvaddr 433 sect.Relocs[i].Symbol = idxToSym[int(rel.Rsymndx)] 434 sect.Relocs[i].Type = rel.Rtype 435 sect.Relocs[i].Length = rel.Rsize&0x3F + 1 436 if rel.Rsize&0x80 != 0 { 437 sect.Relocs[i].Signed = true 438 } 439 if rel.Rsize&0x40 != 0 { 440 sect.Relocs[i].InstructionFixed = true 441 } 442 } 443 } 444 } 445 446 return f, nil 447 } 448 449 // zeroReaderAt is ReaderAt that reads 0s. 450 type zeroReaderAt struct{} 451 452 // ReadAt writes len(p) 0s into p. 453 func (w zeroReaderAt) ReadAt(p []byte, off int64) (n int, err error) { 454 for i := range p { 455 p[i] = 0 456 } 457 return len(p), nil 458 } 459 460 // Data reads and returns the contents of the XCOFF section s. 461 func (s *Section) Data() ([]byte, error) { 462 dat := make([]byte, s.sr.Size()) 463 n, err := s.sr.ReadAt(dat, 0) 464 if n == len(dat) { 465 err = nil 466 } 467 return dat[:n], err 468 } 469 470 // CSect reads and returns the contents of a csect. 471 func (f *File) CSect(name string) []byte { 472 for _, sym := range f.Symbols { 473 if sym.Name == name && sym.AuxCSect.SymbolType == XTY_SD { 474 if i := sym.SectionNumber - 1; 0 <= i && i < len(f.Sections) { 475 s := f.Sections[i] 476 if sym.Value+uint64(sym.AuxCSect.Length) <= s.Size { 477 dat := make([]byte, sym.AuxCSect.Length) 478 _, err := s.sr.ReadAt(dat, int64(sym.Value)) 479 if err != nil { 480 return nil 481 } 482 return dat 483 } 484 } 485 break 486 } 487 } 488 return nil 489 } 490 491 func (f *File) DWARF() (*dwarf.Data, error) { 492 // There are many other DWARF sections, but these 493 // are the ones the debug/dwarf package uses. 494 // Don't bother loading others. 495 var subtypes = [...]uint32{SSUBTYP_DWABREV, SSUBTYP_DWINFO, SSUBTYP_DWLINE, SSUBTYP_DWRNGES, SSUBTYP_DWSTR} 496 var dat [len(subtypes)][]byte 497 for i, subtype := range subtypes { 498 s := f.SectionByType(STYP_DWARF | subtype) 499 if s != nil { 500 b, err := s.Data() 501 if err != nil && uint64(len(b)) < s.Size { 502 return nil, err 503 } 504 dat[i] = b 505 } 506 } 507 508 abbrev, info, line, ranges, str := dat[0], dat[1], dat[2], dat[3], dat[4] 509 return dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str) 510 } 511 512 // readImportID returns the import file IDs stored inside the .loader section. 513 // Library name pattern is either path/base/member or base/member 514 func (f *File) readImportIDs(s *Section) ([]string, error) { 515 // Read loader header 516 if _, err := s.sr.Seek(0, io.SeekStart); err != nil { 517 return nil, err 518 } 519 var istlen uint32 520 var nimpid int32 521 var impoff uint64 522 switch f.TargetMachine { 523 case U802TOCMAGIC: 524 lhdr := new(LoaderHeader32) 525 if err := binary.Read(s.sr, binary.BigEndian, lhdr); err != nil { 526 return nil, err 527 } 528 istlen = lhdr.Listlen 529 nimpid = lhdr.Lnimpid 530 impoff = uint64(lhdr.Limpoff) 531 case U64_TOCMAGIC: 532 lhdr := new(LoaderHeader64) 533 if err := binary.Read(s.sr, binary.BigEndian, lhdr); err != nil { 534 return nil, err 535 } 536 istlen = lhdr.Listlen 537 nimpid = lhdr.Lnimpid 538 impoff = lhdr.Limpoff 539 } 540 541 // Read loader import file ID table 542 if _, err := s.sr.Seek(int64(impoff), io.SeekStart); err != nil { 543 return nil, err 544 } 545 table := make([]byte, istlen) 546 if _, err := io.ReadFull(s.sr, table); err != nil { 547 return nil, err 548 } 549 550 offset := 0 551 // First import file ID is the default LIBPATH value 552 libpath := cstring(table[offset:]) 553 f.LibraryPaths = strings.Split(libpath, ":") 554 offset += len(libpath) + 3 // 3 null bytes 555 all := make([]string, 0) 556 for i := 1; i < int(nimpid); i++ { 557 impidpath := cstring(table[offset:]) 558 offset += len(impidpath) + 1 559 impidbase := cstring(table[offset:]) 560 offset += len(impidbase) + 1 561 impidmem := cstring(table[offset:]) 562 offset += len(impidmem) + 1 563 var path string 564 if len(impidpath) > 0 { 565 path = impidpath + "/" + impidbase + "/" + impidmem 566 } else { 567 path = impidbase + "/" + impidmem 568 } 569 all = append(all, path) 570 } 571 572 return all, nil 573 } 574 575 // ImportedSymbols returns the names of all symbols 576 // referred to by the binary f that are expected to be 577 // satisfied by other libraries at dynamic load time. 578 // It does not return weak symbols. 579 func (f *File) ImportedSymbols() ([]ImportedSymbol, error) { 580 s := f.SectionByType(STYP_LOADER) 581 if s == nil { 582 return nil, nil 583 } 584 // Read loader header 585 if _, err := s.sr.Seek(0, io.SeekStart); err != nil { 586 return nil, err 587 } 588 var stlen uint32 589 var stoff uint64 590 var nsyms int32 591 var symoff uint64 592 switch f.TargetMachine { 593 case U802TOCMAGIC: 594 lhdr := new(LoaderHeader32) 595 if err := binary.Read(s.sr, binary.BigEndian, lhdr); err != nil { 596 return nil, err 597 } 598 stlen = lhdr.Lstlen 599 stoff = uint64(lhdr.Lstoff) 600 nsyms = lhdr.Lnsyms 601 symoff = LDHDRSZ_32 602 case U64_TOCMAGIC: 603 lhdr := new(LoaderHeader64) 604 if err := binary.Read(s.sr, binary.BigEndian, lhdr); err != nil { 605 return nil, err 606 } 607 stlen = lhdr.Lstlen 608 stoff = lhdr.Lstoff 609 nsyms = lhdr.Lnsyms 610 symoff = lhdr.Lsymoff 611 } 612 613 // Read loader section string table 614 if _, err := s.sr.Seek(int64(stoff), io.SeekStart); err != nil { 615 return nil, err 616 } 617 st := make([]byte, stlen) 618 if _, err := io.ReadFull(s.sr, st); err != nil { 619 return nil, err 620 } 621 622 // Read imported libraries 623 libs, err := f.readImportIDs(s) 624 if err != nil { 625 return nil, err 626 } 627 628 // Read loader symbol table 629 if _, err := s.sr.Seek(int64(symoff), io.SeekStart); err != nil { 630 return nil, err 631 } 632 all := make([]ImportedSymbol, 0) 633 for i := 0; i < int(nsyms); i++ { 634 var name string 635 var ifile int32 636 var ok bool 637 switch f.TargetMachine { 638 case U802TOCMAGIC: 639 ldsym := new(LoaderSymbol32) 640 if err := binary.Read(s.sr, binary.BigEndian, ldsym); err != nil { 641 return nil, err 642 } 643 if ldsym.Lsmtype&0x40 == 0 { 644 continue // Imported symbols only 645 } 646 zeroes := binary.BigEndian.Uint32(ldsym.Lname[:4]) 647 if zeroes != 0 { 648 name = cstring(ldsym.Lname[:]) 649 } else { 650 offset := binary.BigEndian.Uint32(ldsym.Lname[4:]) 651 name, ok = getString(st, offset) 652 if !ok { 653 continue 654 } 655 } 656 ifile = ldsym.Lifile 657 case U64_TOCMAGIC: 658 ldsym := new(LoaderSymbol64) 659 if err := binary.Read(s.sr, binary.BigEndian, ldsym); err != nil { 660 return nil, err 661 } 662 if ldsym.Lsmtype&0x40 == 0 { 663 continue // Imported symbols only 664 } 665 name, ok = getString(st, ldsym.Loffset) 666 if !ok { 667 continue 668 } 669 ifile = ldsym.Lifile 670 } 671 var sym ImportedSymbol 672 sym.Name = name 673 if ifile >= 1 && int(ifile) <= len(libs) { 674 sym.Library = libs[ifile-1] 675 } 676 all = append(all, sym) 677 } 678 679 return all, nil 680 } 681 682 // ImportedLibraries returns the names of all libraries 683 // referred to by the binary f that are expected to be 684 // linked with the binary at dynamic link time. 685 func (f *File) ImportedLibraries() ([]string, error) { 686 s := f.SectionByType(STYP_LOADER) 687 if s == nil { 688 return nil, nil 689 } 690 all, err := f.readImportIDs(s) 691 return all, err 692 }