1 //===------ utils/obj2yaml.cpp - obj2yaml conversion tool -------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "obj2yaml.h" 10 #include "llvm/ADT/StringMap.h" 11 #include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" 12 #include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" 13 #include "llvm/DebugInfo/CodeView/StringsAndChecksums.h" 14 #include "llvm/Object/COFF.h" 15 #include "llvm/ObjectYAML/COFFYAML.h" 16 #include "llvm/ObjectYAML/CodeViewYAMLTypes.h" 17 #include "llvm/Support/ErrorHandling.h" 18 #include "llvm/Support/YAMLTraits.h" 19 20 using namespace llvm; 21 22 namespace { 23 24 class COFFDumper { 25 const object::COFFObjectFile &Obj; 26 COFFYAML::Object YAMLObj; 27 template <typename T> 28 void dumpOptionalHeader(T OptionalHeader); 29 void dumpHeader(); 30 void dumpSections(unsigned numSections); 31 void dumpSymbols(unsigned numSymbols); 32 33 public: 34 COFFDumper(const object::COFFObjectFile &Obj); 35 COFFYAML::Object &getYAMLObj(); 36 }; 37 38 } 39 40 COFFDumper::COFFDumper(const object::COFFObjectFile &Obj) : Obj(Obj) { 41 const object::pe32_header *PE32Header = nullptr; 42 Obj.getPE32Header(PE32Header); 43 if (PE32Header) { 44 dumpOptionalHeader(PE32Header); 45 } else { 46 const object::pe32plus_header *PE32PlusHeader = nullptr; 47 Obj.getPE32PlusHeader(PE32PlusHeader); 48 if (PE32PlusHeader) { 49 dumpOptionalHeader(PE32PlusHeader); 50 } 51 } 52 dumpHeader(); 53 dumpSections(Obj.getNumberOfSections()); 54 dumpSymbols(Obj.getNumberOfSymbols()); 55 } 56 57 template <typename T> void COFFDumper::dumpOptionalHeader(T OptionalHeader) { 58 YAMLObj.OptionalHeader = COFFYAML::PEHeader(); 59 YAMLObj.OptionalHeader->Header.AddressOfEntryPoint = 60 OptionalHeader->AddressOfEntryPoint; 61 YAMLObj.OptionalHeader->Header.ImageBase = OptionalHeader->ImageBase; 62 YAMLObj.OptionalHeader->Header.SectionAlignment = 63 OptionalHeader->SectionAlignment; 64 YAMLObj.OptionalHeader->Header.FileAlignment = OptionalHeader->FileAlignment; 65 YAMLObj.OptionalHeader->Header.MajorOperatingSystemVersion = 66 OptionalHeader->MajorOperatingSystemVersion; 67 YAMLObj.OptionalHeader->Header.MinorOperatingSystemVersion = 68 OptionalHeader->MinorOperatingSystemVersion; 69 YAMLObj.OptionalHeader->Header.MajorImageVersion = 70 OptionalHeader->MajorImageVersion; 71 YAMLObj.OptionalHeader->Header.MinorImageVersion = 72 OptionalHeader->MinorImageVersion; 73 YAMLObj.OptionalHeader->Header.MajorSubsystemVersion = 74 OptionalHeader->MajorSubsystemVersion; 75 YAMLObj.OptionalHeader->Header.MinorSubsystemVersion = 76 OptionalHeader->MinorSubsystemVersion; 77 YAMLObj.OptionalHeader->Header.Subsystem = OptionalHeader->Subsystem; 78 YAMLObj.OptionalHeader->Header.DLLCharacteristics = 79 OptionalHeader->DLLCharacteristics; 80 YAMLObj.OptionalHeader->Header.SizeOfStackReserve = 81 OptionalHeader->SizeOfStackReserve; 82 YAMLObj.OptionalHeader->Header.SizeOfStackCommit = 83 OptionalHeader->SizeOfStackCommit; 84 YAMLObj.OptionalHeader->Header.SizeOfHeapReserve = 85 OptionalHeader->SizeOfHeapReserve; 86 YAMLObj.OptionalHeader->Header.SizeOfHeapCommit = 87 OptionalHeader->SizeOfHeapCommit; 88 unsigned I = 0; 89 for (auto &DestDD : YAMLObj.OptionalHeader->DataDirectories) { 90 const object::data_directory *DD; 91 if (Obj.getDataDirectory(I++, DD)) 92 continue; 93 DestDD = COFF::DataDirectory(); 94 DestDD->RelativeVirtualAddress = DD->RelativeVirtualAddress; 95 DestDD->Size = DD->Size; 96 } 97 } 98 99 void COFFDumper::dumpHeader() { 100 YAMLObj.Header.Machine = Obj.getMachine(); 101 YAMLObj.Header.Characteristics = Obj.getCharacteristics(); 102 } 103 104 static void 105 initializeFileAndStringTable(const llvm::object::COFFObjectFile &Obj, 106 codeview::StringsAndChecksumsRef &SC) { 107 108 ExitOnError Err("Invalid .debug$S section!"); 109 // Iterate all .debug$S sections looking for the checksums and string table. 110 // Exit as soon as both sections are found. 111 for (const auto &S : Obj.sections()) { 112 if (SC.hasStrings() && SC.hasChecksums()) 113 break; 114 115 Expected<StringRef> SectionNameOrErr = S.getName(); 116 if (!SectionNameOrErr) { 117 consumeError(SectionNameOrErr.takeError()); 118 continue; 119 } 120 121 ArrayRef<uint8_t> sectionData; 122 if ((*SectionNameOrErr) != ".debug$S") 123 continue; 124 125 const object::coff_section *COFFSection = Obj.getCOFFSection(S); 126 127 cantFail(Obj.getSectionContents(COFFSection, sectionData)); 128 129 BinaryStreamReader Reader(sectionData, support::little); 130 uint32_t Magic; 131 132 Err(Reader.readInteger(Magic)); 133 assert(Magic == COFF::DEBUG_SECTION_MAGIC && "Invalid .debug$S section!"); 134 135 codeview::DebugSubsectionArray Subsections; 136 Err(Reader.readArray(Subsections, Reader.bytesRemaining())); 137 138 SC.initialize(Subsections); 139 } 140 } 141 142 void COFFDumper::dumpSections(unsigned NumSections) { 143 std::vector<COFFYAML::Section> &YAMLSections = YAMLObj.Sections; 144 codeview::StringsAndChecksumsRef SC; 145 initializeFileAndStringTable(Obj, SC); 146 147 StringMap<bool> SymbolUnique; 148 for (const auto &S : Obj.symbols()) { 149 object::COFFSymbolRef Symbol = Obj.getCOFFSymbol(S); 150 StringRef Name; 151 Obj.getSymbolName(Symbol, Name); 152 StringMap<bool>::iterator It; 153 bool Inserted; 154 std::tie(It, Inserted) = SymbolUnique.insert(std::make_pair(Name, true)); 155 if (!Inserted) 156 It->second = false; 157 } 158 159 for (const auto &ObjSection : Obj.sections()) { 160 const object::coff_section *COFFSection = Obj.getCOFFSection(ObjSection); 161 COFFYAML::Section NewYAMLSection; 162 163 if (Expected<StringRef> NameOrErr = ObjSection.getName()) 164 NewYAMLSection.Name = *NameOrErr; 165 else 166 consumeError(NameOrErr.takeError()); 167 168 NewYAMLSection.Header.Characteristics = COFFSection->Characteristics; 169 NewYAMLSection.Header.VirtualAddress = COFFSection->VirtualAddress; 170 NewYAMLSection.Header.VirtualSize = COFFSection->VirtualSize; 171 NewYAMLSection.Header.NumberOfLineNumbers = 172 COFFSection->NumberOfLinenumbers; 173 NewYAMLSection.Header.NumberOfRelocations = 174 COFFSection->NumberOfRelocations; 175 NewYAMLSection.Header.PointerToLineNumbers = 176 COFFSection->PointerToLinenumbers; 177 NewYAMLSection.Header.PointerToRawData = COFFSection->PointerToRawData; 178 NewYAMLSection.Header.PointerToRelocations = 179 COFFSection->PointerToRelocations; 180 NewYAMLSection.Header.SizeOfRawData = COFFSection->SizeOfRawData; 181 uint32_t Shift = (COFFSection->Characteristics >> 20) & 0xF; 182 NewYAMLSection.Alignment = (1U << Shift) >> 1; 183 assert(NewYAMLSection.Alignment <= 8192); 184 185 ArrayRef<uint8_t> sectionData; 186 if (!ObjSection.isBSS()) 187 cantFail(Obj.getSectionContents(COFFSection, sectionData)); 188 NewYAMLSection.SectionData = yaml::BinaryRef(sectionData); 189 190 if (NewYAMLSection.Name == ".debug$S") 191 NewYAMLSection.DebugS = CodeViewYAML::fromDebugS(sectionData, SC); 192 else if (NewYAMLSection.Name == ".debug$T") 193 NewYAMLSection.DebugT = CodeViewYAML::fromDebugT(sectionData, 194 NewYAMLSection.Name); 195 else if (NewYAMLSection.Name == ".debug$P") 196 NewYAMLSection.DebugP = CodeViewYAML::fromDebugT(sectionData, 197 NewYAMLSection.Name); 198 else if (NewYAMLSection.Name == ".debug$H") 199 NewYAMLSection.DebugH = CodeViewYAML::fromDebugH(sectionData); 200 201 std::vector<COFFYAML::Relocation> Relocations; 202 for (const auto &Reloc : ObjSection.relocations()) { 203 const object::coff_relocation *reloc = Obj.getCOFFRelocation(Reloc); 204 COFFYAML::Relocation Rel; 205 object::symbol_iterator Sym = Reloc.getSymbol(); 206 Expected<StringRef> SymbolNameOrErr = Sym->getName(); 207 if (!SymbolNameOrErr) { 208 std::string Buf; 209 raw_string_ostream OS(Buf); 210 logAllUnhandledErrors(SymbolNameOrErr.takeError(), OS); 211 OS.flush(); 212 report_fatal_error(Buf); 213 } 214 if (SymbolUnique.lookup(*SymbolNameOrErr)) 215 Rel.SymbolName = *SymbolNameOrErr; 216 else 217 Rel.SymbolTableIndex = reloc->SymbolTableIndex; 218 Rel.VirtualAddress = reloc->VirtualAddress; 219 Rel.Type = reloc->Type; 220 Relocations.push_back(Rel); 221 } 222 NewYAMLSection.Relocations = Relocations; 223 YAMLSections.push_back(NewYAMLSection); 224 } 225 } 226 227 static void 228 dumpFunctionDefinition(COFFYAML::Symbol *Sym, 229 const object::coff_aux_function_definition *ObjFD) { 230 COFF::AuxiliaryFunctionDefinition YAMLFD; 231 YAMLFD.TagIndex = ObjFD->TagIndex; 232 YAMLFD.TotalSize = ObjFD->TotalSize; 233 YAMLFD.PointerToLinenumber = ObjFD->PointerToLinenumber; 234 YAMLFD.PointerToNextFunction = ObjFD->PointerToNextFunction; 235 236 Sym->FunctionDefinition = YAMLFD; 237 } 238 239 static void 240 dumpbfAndEfLineInfo(COFFYAML::Symbol *Sym, 241 const object::coff_aux_bf_and_ef_symbol *ObjBES) { 242 COFF::AuxiliarybfAndefSymbol YAMLAAS; 243 YAMLAAS.Linenumber = ObjBES->Linenumber; 244 YAMLAAS.PointerToNextFunction = ObjBES->PointerToNextFunction; 245 246 Sym->bfAndefSymbol = YAMLAAS; 247 } 248 249 static void dumpWeakExternal(COFFYAML::Symbol *Sym, 250 const object::coff_aux_weak_external *ObjWE) { 251 COFF::AuxiliaryWeakExternal YAMLWE; 252 YAMLWE.TagIndex = ObjWE->TagIndex; 253 YAMLWE.Characteristics = ObjWE->Characteristics; 254 255 Sym->WeakExternal = YAMLWE; 256 } 257 258 static void 259 dumpSectionDefinition(COFFYAML::Symbol *Sym, 260 const object::coff_aux_section_definition *ObjSD, 261 bool IsBigObj) { 262 COFF::AuxiliarySectionDefinition YAMLASD; 263 int32_t AuxNumber = ObjSD->getNumber(IsBigObj); 264 YAMLASD.Length = ObjSD->Length; 265 YAMLASD.NumberOfRelocations = ObjSD->NumberOfRelocations; 266 YAMLASD.NumberOfLinenumbers = ObjSD->NumberOfLinenumbers; 267 YAMLASD.CheckSum = ObjSD->CheckSum; 268 YAMLASD.Number = AuxNumber; 269 YAMLASD.Selection = ObjSD->Selection; 270 271 Sym->SectionDefinition = YAMLASD; 272 } 273 274 static void 275 dumpCLRTokenDefinition(COFFYAML::Symbol *Sym, 276 const object::coff_aux_clr_token *ObjCLRToken) { 277 COFF::AuxiliaryCLRToken YAMLCLRToken; 278 YAMLCLRToken.AuxType = ObjCLRToken->AuxType; 279 YAMLCLRToken.SymbolTableIndex = ObjCLRToken->SymbolTableIndex; 280 281 Sym->CLRToken = YAMLCLRToken; 282 } 283 284 void COFFDumper::dumpSymbols(unsigned NumSymbols) { 285 std::vector<COFFYAML::Symbol> &Symbols = YAMLObj.Symbols; 286 for (const auto &S : Obj.symbols()) { 287 object::COFFSymbolRef Symbol = Obj.getCOFFSymbol(S); 288 COFFYAML::Symbol Sym; 289 Obj.getSymbolName(Symbol, Sym.Name); 290 Sym.SimpleType = COFF::SymbolBaseType(Symbol.getBaseType()); 291 Sym.ComplexType = COFF::SymbolComplexType(Symbol.getComplexType()); 292 Sym.Header.StorageClass = Symbol.getStorageClass(); 293 Sym.Header.Value = Symbol.getValue(); 294 Sym.Header.SectionNumber = Symbol.getSectionNumber(); 295 Sym.Header.NumberOfAuxSymbols = Symbol.getNumberOfAuxSymbols(); 296 297 if (Symbol.getNumberOfAuxSymbols() > 0) { 298 ArrayRef<uint8_t> AuxData = Obj.getSymbolAuxData(Symbol); 299 if (Symbol.isFunctionDefinition()) { 300 // This symbol represents a function definition. 301 assert(Symbol.getNumberOfAuxSymbols() == 1 && 302 "Expected a single aux symbol to describe this function!"); 303 304 const object::coff_aux_function_definition *ObjFD = 305 reinterpret_cast<const object::coff_aux_function_definition *>( 306 AuxData.data()); 307 dumpFunctionDefinition(&Sym, ObjFD); 308 } else if (Symbol.isFunctionLineInfo()) { 309 // This symbol describes function line number information. 310 assert(Symbol.getNumberOfAuxSymbols() == 1 && 311 "Expected a single aux symbol to describe this function!"); 312 313 const object::coff_aux_bf_and_ef_symbol *ObjBES = 314 reinterpret_cast<const object::coff_aux_bf_and_ef_symbol *>( 315 AuxData.data()); 316 dumpbfAndEfLineInfo(&Sym, ObjBES); 317 } else if (Symbol.isAnyUndefined()) { 318 // This symbol represents a weak external definition. 319 assert(Symbol.getNumberOfAuxSymbols() == 1 && 320 "Expected a single aux symbol to describe this weak symbol!"); 321 322 const object::coff_aux_weak_external *ObjWE = 323 reinterpret_cast<const object::coff_aux_weak_external *>( 324 AuxData.data()); 325 dumpWeakExternal(&Sym, ObjWE); 326 } else if (Symbol.isFileRecord()) { 327 // This symbol represents a file record. 328 Sym.File = StringRef(reinterpret_cast<const char *>(AuxData.data()), 329 Symbol.getNumberOfAuxSymbols() * 330 Obj.getSymbolTableEntrySize()) 331 .rtrim(StringRef("\0", /*length=*/1)); 332 } else if (Symbol.isSectionDefinition()) { 333 // This symbol represents a section definition. 334 assert(Symbol.getNumberOfAuxSymbols() == 1 && 335 "Expected a single aux symbol to describe this section!"); 336 337 const object::coff_aux_section_definition *ObjSD = 338 reinterpret_cast<const object::coff_aux_section_definition *>( 339 AuxData.data()); 340 dumpSectionDefinition(&Sym, ObjSD, Symbol.isBigObj()); 341 } else if (Symbol.isCLRToken()) { 342 // This symbol represents a CLR token definition. 343 assert(Symbol.getNumberOfAuxSymbols() == 1 && 344 "Expected a single aux symbol to describe this CLR Token!"); 345 346 const object::coff_aux_clr_token *ObjCLRToken = 347 reinterpret_cast<const object::coff_aux_clr_token *>( 348 AuxData.data()); 349 dumpCLRTokenDefinition(&Sym, ObjCLRToken); 350 } else { 351 llvm_unreachable("Unhandled auxiliary symbol!"); 352 } 353 } 354 Symbols.push_back(Sym); 355 } 356 } 357 358 COFFYAML::Object &COFFDumper::getYAMLObj() { 359 return YAMLObj; 360 } 361 362 std::error_code coff2yaml(raw_ostream &Out, const object::COFFObjectFile &Obj) { 363 COFFDumper Dumper(Obj); 364 365 yaml::Output Yout(Out); 366 Yout << Dumper.getYAMLObj(); 367 368 return std::error_code(); 369 } 370