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