| //===------ xcoff2yaml.cpp - XCOFF YAMLIO implementation --------*- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "obj2yaml.h" | |
| #include "llvm/Object/XCOFFObjectFile.h" | |
| #include "llvm/ObjectYAML/XCOFFYAML.h" | |
| #include "llvm/Support/Errc.h" | |
| #include "llvm/Support/YAMLTraits.h" | |
| using namespace llvm; | |
| using namespace llvm::object; | |
| namespace { | |
| class XCOFFDumper { | |
| const object::XCOFFObjectFile &Obj; | |
| XCOFFYAML::Object YAMLObj; | |
| void dumpHeader(); | |
| Error dumpSections(); | |
| Error dumpSymbols(); | |
| template <typename Shdr, typename Reloc> | |
| Error dumpSections(ArrayRef<Shdr> Sections); | |
| // Dump auxiliary symbols. | |
| Error dumpFileAuxSym(XCOFFYAML::Symbol &Sym, | |
| const XCOFFSymbolRef &SymbolEntRef); | |
| Error dumpStatAuxSym(XCOFFYAML::Symbol &Sym, | |
| const XCOFFSymbolRef &SymbolEntRef); | |
| Error dumpBlockAuxSym(XCOFFYAML::Symbol &Sym, | |
| const XCOFFSymbolRef &SymbolEntRef); | |
| Error dumpDwarfAuxSym(XCOFFYAML::Symbol &Sym, | |
| const XCOFFSymbolRef &SymbolEntRef); | |
| Error dumpAuxSyms(XCOFFYAML::Symbol &Sym, const XCOFFSymbolRef &SymbolEntRef); | |
| void dumpFuncAuxSym(XCOFFYAML::Symbol &Sym, const uintptr_t AuxAddress); | |
| void dumpExpAuxSym(XCOFFYAML::Symbol &Sym, const uintptr_t AuxAddress); | |
| void dumpCsectAuxSym(XCOFFYAML::Symbol &Sym, | |
| const object::XCOFFCsectAuxRef &AuxEntPtr); | |
| public: | |
| XCOFFDumper(const object::XCOFFObjectFile &obj) : Obj(obj) {} | |
| Error dump(); | |
| XCOFFYAML::Object &getYAMLObj() { return YAMLObj; } | |
| template <typename T> const T *getAuxEntPtr(uintptr_t AuxAddress) { | |
| Obj.checkSymbolEntryPointer(AuxAddress); | |
| return reinterpret_cast<const T *>(AuxAddress); | |
| } | |
| }; | |
| } // namespace | |
| Error XCOFFDumper::dump() { | |
| dumpHeader(); | |
| if (Error E = dumpSections()) | |
| return E; | |
| return dumpSymbols(); | |
| } | |
| void XCOFFDumper::dumpHeader() { | |
| YAMLObj.Header.Magic = Obj.getMagic(); | |
| YAMLObj.Header.NumberOfSections = Obj.getNumberOfSections(); | |
| YAMLObj.Header.TimeStamp = Obj.getTimeStamp(); | |
| YAMLObj.Header.SymbolTableOffset = Obj.is64Bit() | |
| ? Obj.getSymbolTableOffset64() | |
| : Obj.getSymbolTableOffset32(); | |
| YAMLObj.Header.NumberOfSymTableEntries = | |
| Obj.is64Bit() ? Obj.getNumberOfSymbolTableEntries64() | |
| : Obj.getRawNumberOfSymbolTableEntries32(); | |
| YAMLObj.Header.AuxHeaderSize = Obj.getOptionalHeaderSize(); | |
| YAMLObj.Header.Flags = Obj.getFlags(); | |
| } | |
| Error XCOFFDumper::dumpSections() { | |
| if (Obj.is64Bit()) | |
| return dumpSections<XCOFFSectionHeader64, XCOFFRelocation64>( | |
| Obj.sections64()); | |
| return dumpSections<XCOFFSectionHeader32, XCOFFRelocation32>( | |
| Obj.sections32()); | |
| } | |
| template <typename Shdr, typename Reloc> | |
| Error XCOFFDumper::dumpSections(ArrayRef<Shdr> Sections) { | |
| std::vector<XCOFFYAML::Section> &YamlSections = YAMLObj.Sections; | |
| for (const Shdr &S : Sections) { | |
| XCOFFYAML::Section YamlSec; | |
| YamlSec.SectionName = S.getName(); | |
| YamlSec.Address = S.PhysicalAddress; | |
| YamlSec.Size = S.SectionSize; | |
| YamlSec.NumberOfRelocations = S.NumberOfRelocations; | |
| YamlSec.NumberOfLineNumbers = S.NumberOfLineNumbers; | |
| YamlSec.FileOffsetToData = S.FileOffsetToRawData; | |
| YamlSec.FileOffsetToRelocations = S.FileOffsetToRelocationInfo; | |
| YamlSec.FileOffsetToLineNumbers = S.FileOffsetToLineNumberInfo; | |
| YamlSec.Flags = S.Flags; | |
| if (YamlSec.Flags & XCOFF::STYP_DWARF) { | |
| unsigned Mask = Obj.is64Bit() | |
| ? XCOFFSectionHeader64::SectionFlagsTypeMask | |
| : XCOFFSectionHeader32::SectionFlagsTypeMask; | |
| YamlSec.SectionSubtype = | |
| static_cast<XCOFF::DwarfSectionSubtypeFlags>(S.Flags & ~Mask); | |
| } | |
| // Dump section data. | |
| if (S.FileOffsetToRawData) { | |
| DataRefImpl SectionDRI; | |
| SectionDRI.p = reinterpret_cast<uintptr_t>(&S); | |
| Expected<ArrayRef<uint8_t>> SecDataRefOrErr = | |
| Obj.getSectionContents(SectionDRI); | |
| if (!SecDataRefOrErr) | |
| return SecDataRefOrErr.takeError(); | |
| YamlSec.SectionData = SecDataRefOrErr.get(); | |
| } | |
| // Dump relocations. | |
| if (S.NumberOfRelocations) { | |
| auto RelRefOrErr = Obj.relocations<Shdr, Reloc>(S); | |
| if (!RelRefOrErr) | |
| return RelRefOrErr.takeError(); | |
| for (const Reloc &R : RelRefOrErr.get()) { | |
| XCOFFYAML::Relocation YamlRel; | |
| YamlRel.Type = R.Type; | |
| YamlRel.Info = R.Info; | |
| YamlRel.SymbolIndex = R.SymbolIndex; | |
| YamlRel.VirtualAddress = R.VirtualAddress; | |
| YamlSec.Relocations.push_back(YamlRel); | |
| } | |
| } | |
| YamlSections.push_back(YamlSec); | |
| } | |
| return Error::success(); | |
| } | |
| Error XCOFFDumper::dumpFileAuxSym(XCOFFYAML::Symbol &Sym, | |
| const XCOFFSymbolRef &SymbolEntRef) { | |
| for (uint8_t I = 1; I <= Sym.NumberOfAuxEntries; ++I) { | |
| uintptr_t AuxAddress = XCOFFObjectFile::getAdvancedSymbolEntryAddress( | |
| SymbolEntRef.getEntryAddress(), I); | |
| const XCOFFFileAuxEnt *FileAuxEntPtr = | |
| getAuxEntPtr<XCOFFFileAuxEnt>(AuxAddress); | |
| auto FileNameOrError = Obj.getCFileName(FileAuxEntPtr); | |
| if (!FileNameOrError) | |
| return FileNameOrError.takeError(); | |
| XCOFFYAML::FileAuxEnt FileAuxSym; | |
| FileAuxSym.FileNameOrString = FileNameOrError.get(); | |
| FileAuxSym.FileStringType = FileAuxEntPtr->Type; | |
| Sym.AuxEntries.push_back( | |
| std::make_unique<XCOFFYAML::FileAuxEnt>(FileAuxSym)); | |
| } | |
| return Error::success(); | |
| } | |
| Error XCOFFDumper::dumpStatAuxSym(XCOFFYAML::Symbol &Sym, | |
| const XCOFFSymbolRef &SymbolEntRef) { | |
| if (Sym.NumberOfAuxEntries != 1) { | |
| uint32_t SymbolIndex = Obj.getSymbolIndex(SymbolEntRef.getEntryAddress()); | |
| return createError("failed to parse symbol \"" + Sym.SymbolName + | |
| "\" with index of " + Twine(SymbolIndex) + | |
| ": expected 1 aux symbol for C_STAT, while got " + | |
| Twine(static_cast<uint32_t>(*Sym.NumberOfAuxEntries))); | |
| } | |
| const XCOFFSectAuxEntForStat *AuxEntPtr = | |
| getAuxEntPtr<XCOFFSectAuxEntForStat>( | |
| XCOFFObjectFile::getAdvancedSymbolEntryAddress( | |
| SymbolEntRef.getEntryAddress(), 1)); | |
| XCOFFYAML::SectAuxEntForStat StatAuxSym; | |
| StatAuxSym.SectionLength = AuxEntPtr->SectionLength; | |
| StatAuxSym.NumberOfLineNum = AuxEntPtr->NumberOfLineNum; | |
| StatAuxSym.NumberOfRelocEnt = AuxEntPtr->NumberOfRelocEnt; | |
| Sym.AuxEntries.push_back( | |
| std::make_unique<XCOFFYAML::SectAuxEntForStat>(StatAuxSym)); | |
| return Error::success(); | |
| } | |
| void XCOFFDumper::dumpFuncAuxSym(XCOFFYAML::Symbol &Sym, | |
| const uintptr_t AuxAddress) { | |
| XCOFFYAML::FunctionAuxEnt FunAuxSym; | |
| if (Obj.is64Bit()) { | |
| const XCOFFFunctionAuxEnt64 *AuxEntPtr = | |
| getAuxEntPtr<XCOFFFunctionAuxEnt64>(AuxAddress); | |
| FunAuxSym.PtrToLineNum = AuxEntPtr->PtrToLineNum; | |
| FunAuxSym.SizeOfFunction = AuxEntPtr->SizeOfFunction; | |
| FunAuxSym.SymIdxOfNextBeyond = AuxEntPtr->SymIdxOfNextBeyond; | |
| } else { | |
| const XCOFFFunctionAuxEnt32 *AuxEntPtr = | |
| getAuxEntPtr<XCOFFFunctionAuxEnt32>(AuxAddress); | |
| FunAuxSym.OffsetToExceptionTbl = AuxEntPtr->OffsetToExceptionTbl; | |
| FunAuxSym.PtrToLineNum = AuxEntPtr->PtrToLineNum; | |
| FunAuxSym.SizeOfFunction = AuxEntPtr->SizeOfFunction; | |
| FunAuxSym.SymIdxOfNextBeyond = AuxEntPtr->SymIdxOfNextBeyond; | |
| } | |
| Sym.AuxEntries.push_back( | |
| std::make_unique<XCOFFYAML::FunctionAuxEnt>(FunAuxSym)); | |
| } | |
| void XCOFFDumper::dumpExpAuxSym(XCOFFYAML::Symbol &Sym, | |
| const uintptr_t AuxAddress) { | |
| const XCOFFExceptionAuxEnt *AuxEntPtr = | |
| getAuxEntPtr<XCOFFExceptionAuxEnt>(AuxAddress); | |
| XCOFFYAML::ExcpetionAuxEnt ExceptAuxSym; | |
| ExceptAuxSym.OffsetToExceptionTbl = AuxEntPtr->OffsetToExceptionTbl; | |
| ExceptAuxSym.SizeOfFunction = AuxEntPtr->SizeOfFunction; | |
| ExceptAuxSym.SymIdxOfNextBeyond = AuxEntPtr->SymIdxOfNextBeyond; | |
| Sym.AuxEntries.push_back( | |
| std::make_unique<XCOFFYAML::ExcpetionAuxEnt>(ExceptAuxSym)); | |
| } | |
| void XCOFFDumper::dumpCsectAuxSym(XCOFFYAML::Symbol &Sym, | |
| const object::XCOFFCsectAuxRef &AuxEntPtr) { | |
| XCOFFYAML::CsectAuxEnt CsectAuxSym; | |
| CsectAuxSym.ParameterHashIndex = AuxEntPtr.getParameterHashIndex(); | |
| CsectAuxSym.TypeChkSectNum = AuxEntPtr.getTypeChkSectNum(); | |
| CsectAuxSym.SymbolAlignment = AuxEntPtr.getAlignmentLog2(); | |
| CsectAuxSym.SymbolType = | |
| static_cast<XCOFF::SymbolType>(AuxEntPtr.getSymbolType()); | |
| CsectAuxSym.StorageMappingClass = AuxEntPtr.getStorageMappingClass(); | |
| if (Obj.is64Bit()) { | |
| CsectAuxSym.SectionOrLengthLo = | |
| static_cast<uint32_t>(AuxEntPtr.getSectionOrLength64()); | |
| CsectAuxSym.SectionOrLengthHi = | |
| static_cast<uint32_t>(AuxEntPtr.getSectionOrLength64() >> 32); | |
| } else { | |
| CsectAuxSym.SectionOrLength = AuxEntPtr.getSectionOrLength32(); | |
| CsectAuxSym.StabInfoIndex = AuxEntPtr.getStabInfoIndex32(); | |
| CsectAuxSym.StabSectNum = AuxEntPtr.getStabSectNum32(); | |
| } | |
| Sym.AuxEntries.push_back( | |
| std::make_unique<XCOFFYAML::CsectAuxEnt>(CsectAuxSym)); | |
| } | |
| Error XCOFFDumper::dumpAuxSyms(XCOFFYAML::Symbol &Sym, | |
| const XCOFFSymbolRef &SymbolEntRef) { | |
| auto ErrOrCsectAuxRef = SymbolEntRef.getXCOFFCsectAuxRef(); | |
| if (!ErrOrCsectAuxRef) | |
| return ErrOrCsectAuxRef.takeError(); | |
| XCOFFCsectAuxRef CsectAuxRef = ErrOrCsectAuxRef.get(); | |
| for (uint8_t I = 1; I <= Sym.NumberOfAuxEntries; ++I) { | |
| if (I == Sym.NumberOfAuxEntries && !Obj.is64Bit()) { | |
| dumpCsectAuxSym(Sym, CsectAuxRef); | |
| return Error::success(); | |
| } | |
| uintptr_t AuxAddress = XCOFFObjectFile::getAdvancedSymbolEntryAddress( | |
| SymbolEntRef.getEntryAddress(), I); | |
| if (Obj.is64Bit()) { | |
| XCOFF::SymbolAuxType Type = *Obj.getSymbolAuxType(AuxAddress); | |
| if (Type == XCOFF::SymbolAuxType::AUX_CSECT) | |
| dumpCsectAuxSym(Sym, CsectAuxRef); | |
| else if (Type == XCOFF::SymbolAuxType::AUX_FCN) | |
| dumpFuncAuxSym(Sym, AuxAddress); | |
| else if (Type == XCOFF::SymbolAuxType::AUX_EXCEPT) | |
| dumpExpAuxSym(Sym, AuxAddress); | |
| else { | |
| uint32_t SymbolIndex = | |
| Obj.getSymbolIndex(SymbolEntRef.getEntryAddress()); | |
| return createError("failed to parse symbol \"" + Sym.SymbolName + | |
| "\" with index of " + Twine(SymbolIndex) + | |
| ": invalid auxiliary symbol type: " + | |
| Twine(static_cast<uint32_t>(Type))); | |
| } | |
| } else | |
| dumpFuncAuxSym(Sym, AuxAddress); | |
| } | |
| return Error::success(); | |
| } | |
| Error XCOFFDumper::dumpBlockAuxSym(XCOFFYAML::Symbol &Sym, | |
| const XCOFFSymbolRef &SymbolEntRef) { | |
| if (Sym.NumberOfAuxEntries != 1) { | |
| uint32_t SymbolIndex = Obj.getSymbolIndex(SymbolEntRef.getEntryAddress()); | |
| return createError( | |
| "failed to parse symbol \"" + Sym.SymbolName + "\" with index of " + | |
| Twine(SymbolIndex) + | |
| ": expected 1 aux symbol for C_BLOCK or C_FCN, while got " + | |
| Twine(static_cast<uint32_t>(*Sym.NumberOfAuxEntries))); | |
| } | |
| uintptr_t AuxAddress = XCOFFObjectFile::getAdvancedSymbolEntryAddress( | |
| SymbolEntRef.getEntryAddress(), 1); | |
| XCOFFYAML::BlockAuxEnt BlockAuxSym; | |
| if (Obj.is64Bit()) { | |
| const XCOFFBlockAuxEnt64 *AuxEntPtr = | |
| getAuxEntPtr<XCOFFBlockAuxEnt64>(AuxAddress); | |
| BlockAuxSym.LineNum = AuxEntPtr->LineNum; | |
| } else { | |
| const XCOFFBlockAuxEnt32 *AuxEntPtr = | |
| getAuxEntPtr<XCOFFBlockAuxEnt32>(AuxAddress); | |
| BlockAuxSym.LineNumLo = AuxEntPtr->LineNumLo; | |
| BlockAuxSym.LineNumHi = AuxEntPtr->LineNumHi; | |
| } | |
| Sym.AuxEntries.push_back( | |
| std::make_unique<XCOFFYAML::BlockAuxEnt>(BlockAuxSym)); | |
| return Error::success(); | |
| } | |
| Error XCOFFDumper::dumpDwarfAuxSym(XCOFFYAML::Symbol &Sym, | |
| const XCOFFSymbolRef &SymbolEntRef) { | |
| if (Sym.NumberOfAuxEntries != 1) { | |
| uint32_t SymbolIndex = Obj.getSymbolIndex(SymbolEntRef.getEntryAddress()); | |
| return createError("failed to parse symbol \"" + Sym.SymbolName + | |
| "\" with index of " + Twine(SymbolIndex) + | |
| ": expected 1 aux symbol for C_DWARF, while got " + | |
| Twine(static_cast<uint32_t>(*Sym.NumberOfAuxEntries))); | |
| } | |
| uintptr_t AuxAddress = XCOFFObjectFile::getAdvancedSymbolEntryAddress( | |
| SymbolEntRef.getEntryAddress(), 1); | |
| XCOFFYAML::SectAuxEntForDWARF DwarfAuxSym; | |
| if (Obj.is64Bit()) { | |
| const XCOFFSectAuxEntForDWARF64 *AuxEntPtr = | |
| getAuxEntPtr<XCOFFSectAuxEntForDWARF64>(AuxAddress); | |
| DwarfAuxSym.LengthOfSectionPortion = AuxEntPtr->LengthOfSectionPortion; | |
| DwarfAuxSym.NumberOfRelocEnt = AuxEntPtr->NumberOfRelocEnt; | |
| } else { | |
| const XCOFFSectAuxEntForDWARF32 *AuxEntPtr = | |
| getAuxEntPtr<XCOFFSectAuxEntForDWARF32>(AuxAddress); | |
| DwarfAuxSym.LengthOfSectionPortion = AuxEntPtr->LengthOfSectionPortion; | |
| DwarfAuxSym.NumberOfRelocEnt = AuxEntPtr->NumberOfRelocEnt; | |
| } | |
| Sym.AuxEntries.push_back( | |
| std::make_unique<XCOFFYAML::SectAuxEntForDWARF>(DwarfAuxSym)); | |
| return Error::success(); | |
| } | |
| Error XCOFFDumper::dumpSymbols() { | |
| std::vector<XCOFFYAML::Symbol> &Symbols = YAMLObj.Symbols; | |
| for (const SymbolRef &S : Obj.symbols()) { | |
| DataRefImpl SymbolDRI = S.getRawDataRefImpl(); | |
| const XCOFFSymbolRef SymbolEntRef = Obj.toSymbolRef(SymbolDRI); | |
| XCOFFYAML::Symbol Sym; | |
| Expected<StringRef> SymNameRefOrErr = Obj.getSymbolName(SymbolDRI); | |
| if (!SymNameRefOrErr) { | |
| return SymNameRefOrErr.takeError(); | |
| } | |
| Sym.SymbolName = SymNameRefOrErr.get(); | |
| Sym.Value = SymbolEntRef.getValue(); | |
| Expected<StringRef> SectionNameRefOrErr = | |
| Obj.getSymbolSectionName(SymbolEntRef); | |
| if (!SectionNameRefOrErr) | |
| return SectionNameRefOrErr.takeError(); | |
| Sym.SectionName = SectionNameRefOrErr.get(); | |
| Sym.Type = SymbolEntRef.getSymbolType(); | |
| Sym.StorageClass = SymbolEntRef.getStorageClass(); | |
| Sym.NumberOfAuxEntries = SymbolEntRef.getNumberOfAuxEntries(); | |
| if (Sym.NumberOfAuxEntries) { | |
| switch (Sym.StorageClass) { | |
| case XCOFF::C_FILE: | |
| if (Error E = dumpFileAuxSym(Sym, SymbolEntRef)) | |
| return E; | |
| break; | |
| case XCOFF::C_STAT: | |
| if (Error E = dumpStatAuxSym(Sym, SymbolEntRef)) | |
| return E; | |
| break; | |
| case XCOFF::C_EXT: | |
| case XCOFF::C_WEAKEXT: | |
| case XCOFF::C_HIDEXT: | |
| if (Error E = dumpAuxSyms(Sym, SymbolEntRef)) | |
| return E; | |
| break; | |
| case XCOFF::C_BLOCK: | |
| case XCOFF::C_FCN: | |
| if (Error E = dumpBlockAuxSym(Sym, SymbolEntRef)) | |
| return E; | |
| break; | |
| case XCOFF::C_DWARF: | |
| if (Error E = dumpDwarfAuxSym(Sym, SymbolEntRef)) | |
| return E; | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| Symbols.push_back(std::move(Sym)); | |
| } | |
| return Error::success(); | |
| } | |
| Error xcoff2yaml(raw_ostream &Out, const object::XCOFFObjectFile &Obj) { | |
| XCOFFDumper Dumper(Obj); | |
| if (Error E = Dumper.dump()) | |
| return E; | |
| yaml::Output Yout(Out); | |
| Yout << Dumper.getYAMLObj(); | |
| return Error::success(); | |
| } |