| 1 | //===- llvm-readobj.cpp - Dump contents of an Object File -----------------===// |
| 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 | // This is a tool similar to readelf, except it works on multiple object file |
| 10 | // formats. The main purpose of this tool is to provide detailed output suitable |
| 11 | // for FileCheck. |
| 12 | // |
| 13 | // Flags should be similar to readelf where supported, but the output format |
| 14 | // does not need to be identical. The point is to not make users learn yet |
| 15 | // another set of flags. |
| 16 | // |
| 17 | // Output should be specialized for each format where appropriate. |
| 18 | // |
| 19 | //===----------------------------------------------------------------------===// |
| 20 | |
| 21 | #include "llvm-readobj.h" |
| 22 | #include "ObjDumper.h" |
| 23 | #include "WindowsResourceDumper.h" |
| 24 | #include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h" |
| 25 | #include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h" |
| 26 | #include "llvm/MC/TargetRegistry.h" |
| 27 | #include "llvm/Object/Archive.h" |
| 28 | #include "llvm/Object/COFFImportFile.h" |
| 29 | #include "llvm/Object/ELFObjectFile.h" |
| 30 | #include "llvm/Object/MachOUniversal.h" |
| 31 | #include "llvm/Object/ObjectFile.h" |
| 32 | #include "llvm/Object/Wasm.h" |
| 33 | #include "llvm/Object/WindowsResource.h" |
| 34 | #include "llvm/Object/XCOFFObjectFile.h" |
| 35 | #include "llvm/Option/Arg.h" |
| 36 | #include "llvm/Option/ArgList.h" |
| 37 | #include "llvm/Option/Option.h" |
| 38 | #include "llvm/Support/Casting.h" |
| 39 | #include "llvm/Support/CommandLine.h" |
| 40 | #include "llvm/Support/DataTypes.h" |
| 41 | #include "llvm/Support/Debug.h" |
| 42 | #include "llvm/Support/Errc.h" |
| 43 | #include "llvm/Support/FileSystem.h" |
| 44 | #include "llvm/Support/FormatVariadic.h" |
| 45 | #include "llvm/Support/LLVMDriver.h" |
| 46 | #include "llvm/Support/Path.h" |
| 47 | #include "llvm/Support/ScopedPrinter.h" |
| 48 | #include "llvm/Support/WithColor.h" |
| 49 | |
| 50 | using namespace llvm; |
| 51 | using namespace llvm::object; |
| 52 | |
| 53 | namespace { |
| 54 | using namespace llvm::opt; // for HelpHidden in Opts.inc |
| 55 | enum ID { |
| 56 | OPT_INVALID = 0, // This is not an option ID. |
| 57 | #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), |
| 58 | #include "Opts.inc" |
| 59 | #undef OPTION |
| 60 | }; |
| 61 | |
| 62 | #define OPTTABLE_STR_TABLE_CODE |
| 63 | #include "Opts.inc" |
| 64 | #undef OPTTABLE_STR_TABLE_CODE |
| 65 | |
| 66 | #define OPTTABLE_PREFIXES_TABLE_CODE |
| 67 | #include "Opts.inc" |
| 68 | #undef OPTTABLE_PREFIXES_TABLE_CODE |
| 69 | |
| 70 | static constexpr opt::OptTable::Info InfoTable[] = { |
| 71 | #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), |
| 72 | #include "Opts.inc" |
| 73 | #undef OPTION |
| 74 | }; |
| 75 | |
| 76 | class ReadobjOptTable : public opt::GenericOptTable { |
| 77 | public: |
| 78 | ReadobjOptTable() |
| 79 | : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) { |
| 80 | setGroupedShortOptions(true); |
| 81 | } |
| 82 | }; |
| 83 | |
| 84 | enum OutputFormatTy { bsd, sysv, posix, darwin, just_symbols }; |
| 85 | |
| 86 | enum SortSymbolKeyTy { |
| 87 | NAME = 0, |
| 88 | TYPE = 1, |
| 89 | UNKNOWN = 100, |
| 90 | // TODO: add ADDRESS, SIZE as needed. |
| 91 | }; |
| 92 | |
| 93 | } // namespace |
| 94 | |
| 95 | namespace opts { |
| 96 | static bool Addrsig; |
| 97 | static bool All; |
| 98 | static bool ArchSpecificInfo; |
| 99 | static bool BBAddrMap; |
| 100 | static bool PrettyPGOAnalysisMap; |
| 101 | bool ExpandRelocs; |
| 102 | static bool CGProfile; |
| 103 | static bool Decompress; |
| 104 | bool Demangle; |
| 105 | static bool DependentLibraries; |
| 106 | static bool DynRelocs; |
| 107 | static bool DynamicSymbols; |
| 108 | static bool ; |
| 109 | static bool ; |
| 110 | static bool ; |
| 111 | static std::vector<std::string> HexDump; |
| 112 | static bool PrettyPrint; |
| 113 | static bool PrintStackMap; |
| 114 | static bool PrintStackSizes; |
| 115 | static bool Relocations; |
| 116 | bool SectionData; |
| 117 | static bool SectionDetails; |
| 118 | static bool ; |
| 119 | bool SectionRelocations; |
| 120 | bool SectionSymbols; |
| 121 | static std::vector<std::string> StringDump; |
| 122 | static bool StringTable; |
| 123 | static bool Symbols; |
| 124 | static bool UnwindInfo; |
| 125 | static cl::boolOrDefault SectionMapping; |
| 126 | static SmallVector<SortSymbolKeyTy> SortKeys; |
| 127 | |
| 128 | // ELF specific options. |
| 129 | static bool DynamicTable; |
| 130 | static bool ELFLinkerOptions; |
| 131 | static bool GnuHashTable; |
| 132 | static bool HashSymbols; |
| 133 | static bool HashTable; |
| 134 | static bool HashHistogram; |
| 135 | static bool Memtag; |
| 136 | static bool NeededLibraries; |
| 137 | static bool Notes; |
| 138 | static bool ; |
| 139 | static bool SectionGroups; |
| 140 | static bool VersionInfo; |
| 141 | |
| 142 | // Mach-O specific options. |
| 143 | static bool MachODataInCode; |
| 144 | static bool MachODysymtab; |
| 145 | static bool MachOIndirectSymbols; |
| 146 | static bool MachOLinkerOptions; |
| 147 | static bool MachOSegment; |
| 148 | static bool MachOVersionMin; |
| 149 | |
| 150 | // PE/COFF specific options. |
| 151 | static bool CodeView; |
| 152 | static bool CodeViewEnableGHash; |
| 153 | static bool CodeViewMergedTypes; |
| 154 | bool CodeViewSubsectionBytes; |
| 155 | static bool COFFBaseRelocs; |
| 156 | static bool COFFDebugDirectory; |
| 157 | static bool COFFDirectives; |
| 158 | static bool COFFExports; |
| 159 | static bool COFFImports; |
| 160 | static bool COFFLoadConfig; |
| 161 | static bool COFFResources; |
| 162 | static bool COFFTLSDirectory; |
| 163 | |
| 164 | // XCOFF specific options. |
| 165 | static bool ; |
| 166 | static bool ; |
| 167 | static bool XCOFFLoaderSectionSymbol; |
| 168 | static bool XCOFFLoaderSectionRelocation; |
| 169 | static bool XCOFFExceptionSection; |
| 170 | |
| 171 | OutputStyleTy Output = OutputStyleTy::LLVM; |
| 172 | static std::vector<std::string> InputFilenames; |
| 173 | } // namespace opts |
| 174 | |
| 175 | static StringRef ToolName; |
| 176 | |
| 177 | namespace llvm { |
| 178 | |
| 179 | [[noreturn]] static void error(Twine Msg) { |
| 180 | // Flush the standard output to print the error at a |
| 181 | // proper place. |
| 182 | fouts().flush(); |
| 183 | WithColor::error(OS&: errs(), Prefix: ToolName) << Msg << "\n" ; |
| 184 | exit(status: 1); |
| 185 | } |
| 186 | |
| 187 | [[noreturn]] void reportError(Error Err, StringRef Input) { |
| 188 | assert(Err); |
| 189 | if (Input == "-" ) |
| 190 | Input = "<stdin>" ; |
| 191 | handleAllErrors(E: createFileError(F: Input, E: std::move(Err)), |
| 192 | Handlers: [&](const ErrorInfoBase &EI) { error(Msg: EI.message()); }); |
| 193 | llvm_unreachable("error() call should never return" ); |
| 194 | } |
| 195 | |
| 196 | void reportWarning(Error Err, StringRef Input) { |
| 197 | assert(Err); |
| 198 | if (Input == "-" ) |
| 199 | Input = "<stdin>" ; |
| 200 | |
| 201 | // Flush the standard output to print the warning at a |
| 202 | // proper place. |
| 203 | fouts().flush(); |
| 204 | handleAllErrors( |
| 205 | E: createFileError(F: Input, E: std::move(Err)), Handlers: [&](const ErrorInfoBase &EI) { |
| 206 | WithColor::warning(OS&: errs(), Prefix: ToolName) << EI.message() << "\n" ; |
| 207 | }); |
| 208 | } |
| 209 | |
| 210 | } // namespace llvm |
| 211 | |
| 212 | static void parseOptions(const opt::InputArgList &Args) { |
| 213 | opts::Addrsig = Args.hasArg(Ids: OPT_addrsig); |
| 214 | opts::All = Args.hasArg(Ids: OPT_all); |
| 215 | opts::ArchSpecificInfo = Args.hasArg(Ids: OPT_arch_specific); |
| 216 | opts::BBAddrMap = Args.hasArg(Ids: OPT_bb_addr_map); |
| 217 | opts::PrettyPGOAnalysisMap = Args.hasArg(Ids: OPT_pretty_pgo_analysis_map); |
| 218 | if (opts::PrettyPGOAnalysisMap && !opts::BBAddrMap) |
| 219 | WithColor::warning(OS&: errs(), Prefix: ToolName) |
| 220 | << "--bb-addr-map must be enabled for --pretty-pgo-analysis-map to " |
| 221 | "have an effect\n" ; |
| 222 | opts::CGProfile = Args.hasArg(Ids: OPT_cg_profile); |
| 223 | opts::Decompress = Args.hasArg(Ids: OPT_decompress); |
| 224 | opts::Demangle = Args.hasFlag(Pos: OPT_demangle, Neg: OPT_no_demangle, Default: false); |
| 225 | opts::DependentLibraries = Args.hasArg(Ids: OPT_dependent_libraries); |
| 226 | opts::DynRelocs = Args.hasArg(Ids: OPT_dyn_relocations); |
| 227 | opts::DynamicSymbols = Args.hasArg(Ids: OPT_dyn_syms); |
| 228 | opts::ExpandRelocs = Args.hasArg(Ids: OPT_expand_relocs); |
| 229 | opts::ExtraSymInfo = Args.hasArg(Ids: OPT_extra_sym_info); |
| 230 | opts::FileHeaders = Args.hasArg(Ids: OPT_file_header); |
| 231 | opts::Headers = Args.hasArg(Ids: OPT_headers); |
| 232 | opts::HexDump = Args.getAllArgValues(Id: OPT_hex_dump_EQ); |
| 233 | opts::Relocations = Args.hasArg(Ids: OPT_relocs); |
| 234 | opts::SectionData = Args.hasArg(Ids: OPT_section_data); |
| 235 | opts::SectionDetails = Args.hasArg(Ids: OPT_section_details); |
| 236 | opts::SectionHeaders = Args.hasArg(Ids: OPT_section_headers); |
| 237 | opts::SectionRelocations = Args.hasArg(Ids: OPT_section_relocations); |
| 238 | opts::SectionSymbols = Args.hasArg(Ids: OPT_section_symbols); |
| 239 | if (Args.hasArg(Ids: OPT_section_mapping)) |
| 240 | opts::SectionMapping = cl::BOU_TRUE; |
| 241 | else if (Args.hasArg(Ids: OPT_section_mapping_EQ_false)) |
| 242 | opts::SectionMapping = cl::BOU_FALSE; |
| 243 | else |
| 244 | opts::SectionMapping = cl::BOU_UNSET; |
| 245 | opts::PrintStackSizes = Args.hasArg(Ids: OPT_stack_sizes); |
| 246 | opts::PrintStackMap = Args.hasArg(Ids: OPT_stackmap); |
| 247 | opts::StringDump = Args.getAllArgValues(Id: OPT_string_dump_EQ); |
| 248 | opts::StringTable = Args.hasArg(Ids: OPT_string_table); |
| 249 | opts::Symbols = Args.hasArg(Ids: OPT_symbols); |
| 250 | opts::UnwindInfo = Args.hasArg(Ids: OPT_unwind); |
| 251 | |
| 252 | // ELF specific options. |
| 253 | opts::DynamicTable = Args.hasArg(Ids: OPT_dynamic_table); |
| 254 | opts::ELFLinkerOptions = Args.hasArg(Ids: OPT_elf_linker_options); |
| 255 | if (Arg *A = Args.getLastArg(Ids: OPT_elf_output_style_EQ)) { |
| 256 | std::string OutputStyleChoice = A->getValue(); |
| 257 | opts::Output = StringSwitch<opts::OutputStyleTy>(OutputStyleChoice) |
| 258 | .Case(S: "LLVM" , Value: opts::OutputStyleTy::LLVM) |
| 259 | .Case(S: "GNU" , Value: opts::OutputStyleTy::GNU) |
| 260 | .Case(S: "JSON" , Value: opts::OutputStyleTy::JSON) |
| 261 | .Default(Value: opts::OutputStyleTy::UNKNOWN); |
| 262 | if (opts::Output == opts::OutputStyleTy::UNKNOWN) { |
| 263 | error(Msg: "--elf-output-style value should be either 'LLVM', 'GNU', or " |
| 264 | "'JSON', but was '" + |
| 265 | OutputStyleChoice + "'" ); |
| 266 | } |
| 267 | } |
| 268 | opts::GnuHashTable = Args.hasArg(Ids: OPT_gnu_hash_table); |
| 269 | opts::HashSymbols = Args.hasArg(Ids: OPT_hash_symbols); |
| 270 | opts::HashTable = Args.hasArg(Ids: OPT_hash_table); |
| 271 | opts::HashHistogram = Args.hasArg(Ids: OPT_histogram); |
| 272 | opts::Memtag = Args.hasArg(Ids: OPT_memtag); |
| 273 | opts::NeededLibraries = Args.hasArg(Ids: OPT_needed_libs); |
| 274 | opts::Notes = Args.hasArg(Ids: OPT_notes); |
| 275 | opts::PrettyPrint = Args.hasArg(Ids: OPT_pretty_print); |
| 276 | opts::ProgramHeaders = Args.hasArg(Ids: OPT_program_headers); |
| 277 | opts::SectionGroups = Args.hasArg(Ids: OPT_section_groups); |
| 278 | if (Arg *A = Args.getLastArg(Ids: OPT_sort_symbols_EQ)) { |
| 279 | for (StringRef KeyStr : llvm::split(Str: A->getValue(), Separator: "," )) { |
| 280 | SortSymbolKeyTy KeyType = StringSwitch<SortSymbolKeyTy>(KeyStr) |
| 281 | .Case(S: "name" , Value: SortSymbolKeyTy::NAME) |
| 282 | .Case(S: "type" , Value: SortSymbolKeyTy::TYPE) |
| 283 | .Default(Value: SortSymbolKeyTy::UNKNOWN); |
| 284 | if (KeyType == SortSymbolKeyTy::UNKNOWN) |
| 285 | error(Msg: "--sort-symbols value should be 'name' or 'type', but was '" + |
| 286 | Twine(KeyStr) + "'" ); |
| 287 | opts::SortKeys.push_back(Elt: KeyType); |
| 288 | } |
| 289 | } |
| 290 | opts::VersionInfo = Args.hasArg(Ids: OPT_version_info); |
| 291 | |
| 292 | // Mach-O specific options. |
| 293 | opts::MachODataInCode = Args.hasArg(Ids: OPT_macho_data_in_code); |
| 294 | opts::MachODysymtab = Args.hasArg(Ids: OPT_macho_dysymtab); |
| 295 | opts::MachOIndirectSymbols = Args.hasArg(Ids: OPT_macho_indirect_symbols); |
| 296 | opts::MachOLinkerOptions = Args.hasArg(Ids: OPT_macho_linker_options); |
| 297 | opts::MachOSegment = Args.hasArg(Ids: OPT_macho_segment); |
| 298 | opts::MachOVersionMin = Args.hasArg(Ids: OPT_macho_version_min); |
| 299 | |
| 300 | // PE/COFF specific options. |
| 301 | opts::CodeView = Args.hasArg(Ids: OPT_codeview); |
| 302 | opts::CodeViewEnableGHash = Args.hasArg(Ids: OPT_codeview_ghash); |
| 303 | opts::CodeViewMergedTypes = Args.hasArg(Ids: OPT_codeview_merged_types); |
| 304 | opts::CodeViewSubsectionBytes = Args.hasArg(Ids: OPT_codeview_subsection_bytes); |
| 305 | opts::COFFBaseRelocs = Args.hasArg(Ids: OPT_coff_basereloc); |
| 306 | opts::COFFDebugDirectory = Args.hasArg(Ids: OPT_coff_debug_directory); |
| 307 | opts::COFFDirectives = Args.hasArg(Ids: OPT_coff_directives); |
| 308 | opts::COFFExports = Args.hasArg(Ids: OPT_coff_exports); |
| 309 | opts::COFFImports = Args.hasArg(Ids: OPT_coff_imports); |
| 310 | opts::COFFLoadConfig = Args.hasArg(Ids: OPT_coff_load_config); |
| 311 | opts::COFFResources = Args.hasArg(Ids: OPT_coff_resources); |
| 312 | opts::COFFTLSDirectory = Args.hasArg(Ids: OPT_coff_tls_directory); |
| 313 | |
| 314 | // XCOFF specific options. |
| 315 | opts::XCOFFAuxiliaryHeader = Args.hasArg(Ids: OPT_auxiliary_header); |
| 316 | opts::XCOFFLoaderSectionHeader = Args.hasArg(Ids: OPT_loader_section_header); |
| 317 | opts::XCOFFLoaderSectionSymbol = Args.hasArg(Ids: OPT_loader_section_symbols); |
| 318 | opts::XCOFFLoaderSectionRelocation = |
| 319 | Args.hasArg(Ids: OPT_loader_section_relocations); |
| 320 | opts::XCOFFExceptionSection = Args.hasArg(Ids: OPT_exception_section); |
| 321 | |
| 322 | opts::InputFilenames = Args.getAllArgValues(Id: OPT_INPUT); |
| 323 | } |
| 324 | |
| 325 | namespace { |
| 326 | struct ReadObjTypeTableBuilder { |
| 327 | ReadObjTypeTableBuilder() |
| 328 | : IDTable(Allocator), TypeTable(Allocator), GlobalIDTable(Allocator), |
| 329 | GlobalTypeTable(Allocator) {} |
| 330 | |
| 331 | llvm::BumpPtrAllocator Allocator; |
| 332 | llvm::codeview::MergingTypeTableBuilder IDTable; |
| 333 | llvm::codeview::MergingTypeTableBuilder TypeTable; |
| 334 | llvm::codeview::GlobalTypeTableBuilder GlobalIDTable; |
| 335 | llvm::codeview::GlobalTypeTableBuilder GlobalTypeTable; |
| 336 | std::vector<OwningBinary<Binary>> Binaries; |
| 337 | }; |
| 338 | } // namespace |
| 339 | static ReadObjTypeTableBuilder CVTypes; |
| 340 | |
| 341 | /// Creates an format-specific object file dumper. |
| 342 | static Expected<std::unique_ptr<ObjDumper>> |
| 343 | createDumper(const ObjectFile &Obj, ScopedPrinter &Writer) { |
| 344 | if (const COFFObjectFile *COFFObj = dyn_cast<COFFObjectFile>(Val: &Obj)) |
| 345 | return createCOFFDumper(Obj: *COFFObj, Writer); |
| 346 | |
| 347 | if (const ELFObjectFileBase *ELFObj = dyn_cast<ELFObjectFileBase>(Val: &Obj)) |
| 348 | return createELFDumper(Obj: *ELFObj, Writer); |
| 349 | |
| 350 | if (const MachOObjectFile *MachOObj = dyn_cast<MachOObjectFile>(Val: &Obj)) |
| 351 | return createMachODumper(Obj: *MachOObj, Writer); |
| 352 | |
| 353 | if (const WasmObjectFile *WasmObj = dyn_cast<WasmObjectFile>(Val: &Obj)) |
| 354 | return createWasmDumper(Obj: *WasmObj, Writer); |
| 355 | |
| 356 | if (const XCOFFObjectFile *XObj = dyn_cast<XCOFFObjectFile>(Val: &Obj)) |
| 357 | return createXCOFFDumper(Obj: *XObj, Writer); |
| 358 | |
| 359 | return createStringError(EC: errc::invalid_argument, |
| 360 | S: "unsupported object file format" ); |
| 361 | } |
| 362 | |
| 363 | /// Dumps the specified object file. |
| 364 | static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer, |
| 365 | const Archive *A = nullptr) { |
| 366 | std::string FileStr = |
| 367 | A ? Twine(A->getFileName() + "(" + Obj.getFileName() + ")" ).str() |
| 368 | : Obj.getFileName().str(); |
| 369 | |
| 370 | std::string ContentErrString; |
| 371 | if (Error ContentErr = Obj.initContent()) |
| 372 | ContentErrString = "unable to continue dumping, the file is corrupt: " + |
| 373 | toString(E: std::move(ContentErr)); |
| 374 | |
| 375 | ObjDumper *Dumper; |
| 376 | std::optional<SymbolComparator> SymComp; |
| 377 | Expected<std::unique_ptr<ObjDumper>> DumperOrErr = createDumper(Obj, Writer); |
| 378 | if (!DumperOrErr) |
| 379 | reportError(Err: DumperOrErr.takeError(), Input: FileStr); |
| 380 | Dumper = (*DumperOrErr).get(); |
| 381 | |
| 382 | if (!opts::SortKeys.empty()) { |
| 383 | if (Dumper->canCompareSymbols()) { |
| 384 | SymComp = SymbolComparator(); |
| 385 | for (SortSymbolKeyTy Key : opts::SortKeys) { |
| 386 | switch (Key) { |
| 387 | case NAME: |
| 388 | SymComp->addPredicate(Pred: [Dumper](SymbolRef LHS, SymbolRef RHS) { |
| 389 | return Dumper->compareSymbolsByName(LHS, RHS); |
| 390 | }); |
| 391 | break; |
| 392 | case TYPE: |
| 393 | SymComp->addPredicate(Pred: [Dumper](SymbolRef LHS, SymbolRef RHS) { |
| 394 | return Dumper->compareSymbolsByType(LHS, RHS); |
| 395 | }); |
| 396 | break; |
| 397 | case UNKNOWN: |
| 398 | llvm_unreachable("Unsupported sort key" ); |
| 399 | } |
| 400 | } |
| 401 | |
| 402 | } else { |
| 403 | reportWarning(Err: createStringError( |
| 404 | EC: errc::invalid_argument, |
| 405 | S: "--sort-symbols is not supported yet for this format" ), |
| 406 | Input: FileStr); |
| 407 | } |
| 408 | } |
| 409 | Dumper->printFileSummary(FileStr, Obj, InputFilenames: opts::InputFilenames, A); |
| 410 | |
| 411 | if (opts::FileHeaders) |
| 412 | Dumper->printFileHeaders(); |
| 413 | |
| 414 | // Auxiliary header in XOCFF is right after the file header, so print the data |
| 415 | // here. |
| 416 | if (Obj.isXCOFF() && opts::XCOFFAuxiliaryHeader) |
| 417 | Dumper->printAuxiliaryHeader(); |
| 418 | |
| 419 | // This is only used for ELF currently. In some cases, when an object is |
| 420 | // corrupt (e.g. truncated), we can't dump anything except the file header. |
| 421 | if (!ContentErrString.empty()) |
| 422 | reportError(Err: createError(Err: ContentErrString), Input: FileStr); |
| 423 | |
| 424 | if (opts::SectionDetails || opts::SectionHeaders) { |
| 425 | if (opts::Output == opts::GNU && opts::SectionDetails) |
| 426 | Dumper->printSectionDetails(); |
| 427 | else |
| 428 | Dumper->printSectionHeaders(); |
| 429 | } |
| 430 | |
| 431 | if (opts::HashSymbols) |
| 432 | Dumper->printHashSymbols(); |
| 433 | if (opts::ProgramHeaders || opts::SectionMapping == cl::BOU_TRUE) |
| 434 | Dumper->printProgramHeaders(PrintProgramHeaders: opts::ProgramHeaders, PrintSectionMapping: opts::SectionMapping); |
| 435 | if (opts::DynamicTable) |
| 436 | Dumper->printDynamicTable(); |
| 437 | if (opts::NeededLibraries) |
| 438 | Dumper->printNeededLibraries(); |
| 439 | if (opts::Relocations) |
| 440 | Dumper->printRelocations(); |
| 441 | if (opts::DynRelocs) |
| 442 | Dumper->printDynamicRelocations(); |
| 443 | if (opts::UnwindInfo) |
| 444 | Dumper->printUnwindInfo(); |
| 445 | if (opts::Symbols || opts::DynamicSymbols) |
| 446 | Dumper->printSymbols(PrintSymbols: opts::Symbols, PrintDynamicSymbols: opts::DynamicSymbols, |
| 447 | ExtraSymInfo: opts::ExtraSymInfo, SymComp); |
| 448 | if (!opts::StringDump.empty()) |
| 449 | Dumper->printSectionsAsString(Obj, Sections: opts::StringDump, Decompress: opts::Decompress); |
| 450 | if (!opts::HexDump.empty()) |
| 451 | Dumper->printSectionsAsHex(Obj, Sections: opts::HexDump, Decompress: opts::Decompress); |
| 452 | if (opts::HashTable) |
| 453 | Dumper->printHashTable(); |
| 454 | if (opts::GnuHashTable) |
| 455 | Dumper->printGnuHashTable(); |
| 456 | if (opts::VersionInfo) |
| 457 | Dumper->printVersionInfo(); |
| 458 | if (opts::StringTable) |
| 459 | Dumper->printStringTable(); |
| 460 | if (Obj.isELF()) { |
| 461 | if (opts::DependentLibraries) |
| 462 | Dumper->printDependentLibs(); |
| 463 | if (opts::ELFLinkerOptions) |
| 464 | Dumper->printELFLinkerOptions(); |
| 465 | if (opts::ArchSpecificInfo) |
| 466 | Dumper->printArchSpecificInfo(); |
| 467 | if (opts::SectionGroups) |
| 468 | Dumper->printGroupSections(); |
| 469 | if (opts::HashHistogram) |
| 470 | Dumper->printHashHistograms(); |
| 471 | if (opts::CGProfile) |
| 472 | Dumper->printCGProfile(); |
| 473 | if (opts::BBAddrMap) |
| 474 | Dumper->printBBAddrMaps(PrettyPGOAnalysis: opts::PrettyPGOAnalysisMap); |
| 475 | if (opts::Addrsig) |
| 476 | Dumper->printAddrsig(); |
| 477 | if (opts::Notes) |
| 478 | Dumper->printNotes(); |
| 479 | if (opts::Memtag) |
| 480 | Dumper->printMemtag(); |
| 481 | } |
| 482 | if (Obj.isCOFF()) { |
| 483 | if (opts::COFFImports) |
| 484 | Dumper->printCOFFImports(); |
| 485 | if (opts::COFFExports) |
| 486 | Dumper->printCOFFExports(); |
| 487 | if (opts::COFFDirectives) |
| 488 | Dumper->printCOFFDirectives(); |
| 489 | if (opts::COFFBaseRelocs) |
| 490 | Dumper->printCOFFBaseReloc(); |
| 491 | if (opts::COFFDebugDirectory) |
| 492 | Dumper->printCOFFDebugDirectory(); |
| 493 | if (opts::COFFTLSDirectory) |
| 494 | Dumper->printCOFFTLSDirectory(); |
| 495 | if (opts::COFFResources) |
| 496 | Dumper->printCOFFResources(); |
| 497 | if (opts::COFFLoadConfig) |
| 498 | Dumper->printCOFFLoadConfig(); |
| 499 | if (opts::CGProfile) |
| 500 | Dumper->printCGProfile(); |
| 501 | if (opts::Addrsig) |
| 502 | Dumper->printAddrsig(); |
| 503 | if (opts::CodeView) |
| 504 | Dumper->printCodeViewDebugInfo(); |
| 505 | if (opts::CodeViewMergedTypes) |
| 506 | Dumper->mergeCodeViewTypes(CVIDs&: CVTypes.IDTable, CVTypes&: CVTypes.TypeTable, |
| 507 | GlobalCVIDs&: CVTypes.GlobalIDTable, GlobalCVTypes&: CVTypes.GlobalTypeTable, |
| 508 | GHash: opts::CodeViewEnableGHash); |
| 509 | } |
| 510 | if (Obj.isMachO()) { |
| 511 | if (opts::MachODataInCode) |
| 512 | Dumper->printMachODataInCode(); |
| 513 | if (opts::MachOIndirectSymbols) |
| 514 | Dumper->printMachOIndirectSymbols(); |
| 515 | if (opts::MachOLinkerOptions) |
| 516 | Dumper->printMachOLinkerOptions(); |
| 517 | if (opts::MachOSegment) |
| 518 | Dumper->printMachOSegment(); |
| 519 | if (opts::MachOVersionMin) |
| 520 | Dumper->printMachOVersionMin(); |
| 521 | if (opts::MachODysymtab) |
| 522 | Dumper->printMachODysymtab(); |
| 523 | if (opts::CGProfile) |
| 524 | Dumper->printCGProfile(); |
| 525 | } |
| 526 | |
| 527 | if (Obj.isXCOFF()) { |
| 528 | if (opts::XCOFFLoaderSectionHeader || opts::XCOFFLoaderSectionSymbol || |
| 529 | opts::XCOFFLoaderSectionRelocation) |
| 530 | Dumper->printLoaderSection(PrintHeader: opts::XCOFFLoaderSectionHeader, |
| 531 | PrintSymbols: opts::XCOFFLoaderSectionSymbol, |
| 532 | PrintRelocations: opts::XCOFFLoaderSectionRelocation); |
| 533 | |
| 534 | if (opts::XCOFFExceptionSection) |
| 535 | Dumper->printExceptionSection(); |
| 536 | } |
| 537 | |
| 538 | if (opts::PrintStackMap) |
| 539 | Dumper->printStackMap(); |
| 540 | if (opts::PrintStackSizes) |
| 541 | Dumper->printStackSizes(); |
| 542 | } |
| 543 | |
| 544 | /// Dumps each object file in \a Arc; |
| 545 | static void dumpArchive(const Archive *Arc, ScopedPrinter &Writer) { |
| 546 | Error Err = Error::success(); |
| 547 | for (auto &Child : Arc->children(Err)) { |
| 548 | Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(); |
| 549 | if (!ChildOrErr) { |
| 550 | if (auto E = isNotObjectErrorInvalidFileType(Err: ChildOrErr.takeError())) |
| 551 | reportError(Err: std::move(E), Input: Arc->getFileName()); |
| 552 | continue; |
| 553 | } |
| 554 | |
| 555 | Binary *Bin = ChildOrErr->get(); |
| 556 | if (ObjectFile *Obj = dyn_cast<ObjectFile>(Val: Bin)) |
| 557 | dumpObject(Obj&: *Obj, Writer, A: Arc); |
| 558 | else if (COFFImportFile *Imp = dyn_cast<COFFImportFile>(Val: Bin)) |
| 559 | dumpCOFFImportFile(File: Imp, Writer); |
| 560 | else |
| 561 | reportWarning(Err: createStringError(EC: errc::invalid_argument, |
| 562 | S: Bin->getFileName() + |
| 563 | " has an unsupported file type" ), |
| 564 | Input: Arc->getFileName()); |
| 565 | } |
| 566 | if (Err) |
| 567 | reportError(Err: std::move(Err), Input: Arc->getFileName()); |
| 568 | } |
| 569 | |
| 570 | /// Dumps each object file in \a MachO Universal Binary; |
| 571 | static void dumpMachOUniversalBinary(const MachOUniversalBinary *UBinary, |
| 572 | ScopedPrinter &Writer) { |
| 573 | for (const MachOUniversalBinary::ObjectForArch &Obj : UBinary->objects()) { |
| 574 | Expected<std::unique_ptr<MachOObjectFile>> ObjOrErr = Obj.getAsObjectFile(); |
| 575 | if (ObjOrErr) |
| 576 | dumpObject(Obj&: *ObjOrErr.get(), Writer); |
| 577 | else if (auto E = isNotObjectErrorInvalidFileType(Err: ObjOrErr.takeError())) |
| 578 | reportError(Err: ObjOrErr.takeError(), Input: UBinary->getFileName()); |
| 579 | else if (Expected<std::unique_ptr<Archive>> AOrErr = Obj.getAsArchive()) |
| 580 | dumpArchive(Arc: &*AOrErr.get(), Writer); |
| 581 | } |
| 582 | } |
| 583 | |
| 584 | /// Dumps \a COFF file; |
| 585 | static void dumpCOFFObject(COFFObjectFile *Obj, ScopedPrinter &Writer) { |
| 586 | dumpObject(Obj&: *Obj, Writer); |
| 587 | |
| 588 | // Dump a hybrid object when available. |
| 589 | std::unique_ptr<MemoryBuffer> HybridView = Obj->getHybridObjectView(); |
| 590 | if (!HybridView) |
| 591 | return; |
| 592 | Expected<std::unique_ptr<COFFObjectFile>> HybridObjOrErr = |
| 593 | COFFObjectFile::create(Object: *HybridView); |
| 594 | if (!HybridObjOrErr) |
| 595 | reportError(Err: HybridObjOrErr.takeError(), Input: Obj->getFileName().str()); |
| 596 | DictScope D(Writer, "HybridObject" ); |
| 597 | dumpObject(Obj&: **HybridObjOrErr, Writer); |
| 598 | } |
| 599 | |
| 600 | /// Dumps \a WinRes, Windows Resource (.res) file; |
| 601 | static void dumpWindowsResourceFile(WindowsResource *WinRes, |
| 602 | ScopedPrinter &Printer) { |
| 603 | WindowsRes::Dumper Dumper(WinRes, Printer); |
| 604 | if (auto Err = Dumper.printData()) |
| 605 | reportError(Err: std::move(Err), Input: WinRes->getFileName()); |
| 606 | } |
| 607 | |
| 608 | |
| 609 | /// Opens \a File and dumps it. |
| 610 | static void dumpInput(StringRef File, ScopedPrinter &Writer) { |
| 611 | ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr = |
| 612 | MemoryBuffer::getFileOrSTDIN(Filename: File, /*IsText=*/false, |
| 613 | /*RequiresNullTerminator=*/false); |
| 614 | if (std::error_code EC = FileOrErr.getError()) |
| 615 | return reportError(Err: errorCodeToError(EC), Input: File); |
| 616 | |
| 617 | std::unique_ptr<MemoryBuffer> &Buffer = FileOrErr.get(); |
| 618 | file_magic Type = identify_magic(magic: Buffer->getBuffer()); |
| 619 | if (Type == file_magic::bitcode) { |
| 620 | reportWarning(Err: createStringError(EC: errc::invalid_argument, |
| 621 | S: "bitcode files are not supported" ), |
| 622 | Input: File); |
| 623 | return; |
| 624 | } |
| 625 | |
| 626 | Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary( |
| 627 | Source: Buffer->getMemBufferRef(), /*Context=*/nullptr, /*InitContent=*/false); |
| 628 | if (!BinaryOrErr) |
| 629 | reportError(Err: BinaryOrErr.takeError(), Input: File); |
| 630 | |
| 631 | std::unique_ptr<Binary> Bin = std::move(*BinaryOrErr); |
| 632 | if (Archive *Arc = dyn_cast<Archive>(Val: Bin.get())) |
| 633 | dumpArchive(Arc, Writer); |
| 634 | else if (MachOUniversalBinary *UBinary = |
| 635 | dyn_cast<MachOUniversalBinary>(Val: Bin.get())) |
| 636 | dumpMachOUniversalBinary(UBinary, Writer); |
| 637 | else if (COFFObjectFile *Obj = dyn_cast<COFFObjectFile>(Val: Bin.get())) |
| 638 | dumpCOFFObject(Obj, Writer); |
| 639 | else if (ObjectFile *Obj = dyn_cast<ObjectFile>(Val: Bin.get())) |
| 640 | dumpObject(Obj&: *Obj, Writer); |
| 641 | else if (COFFImportFile *Import = dyn_cast<COFFImportFile>(Val: Bin.get())) |
| 642 | dumpCOFFImportFile(File: Import, Writer); |
| 643 | else if (WindowsResource *WinRes = dyn_cast<WindowsResource>(Val: Bin.get())) |
| 644 | dumpWindowsResourceFile(WinRes, Printer&: Writer); |
| 645 | else |
| 646 | llvm_unreachable("unrecognized file type" ); |
| 647 | |
| 648 | CVTypes.Binaries.push_back( |
| 649 | x: OwningBinary<Binary>(std::move(Bin), std::move(Buffer))); |
| 650 | } |
| 651 | |
| 652 | std::unique_ptr<ScopedPrinter> createWriter() { |
| 653 | if (opts::Output == opts::JSON) |
| 654 | return std::make_unique<JSONScopedPrinter>( |
| 655 | args&: fouts(), args: opts::PrettyPrint ? 2 : 0, args: std::make_unique<ListScope>()); |
| 656 | return std::make_unique<ScopedPrinter>(args&: fouts()); |
| 657 | } |
| 658 | |
| 659 | int llvm_readobj_main(int argc, char **argv, const llvm::ToolContext &) { |
| 660 | BumpPtrAllocator A; |
| 661 | StringSaver Saver(A); |
| 662 | ReadobjOptTable Tbl; |
| 663 | ToolName = argv[0]; |
| 664 | opt::InputArgList Args = |
| 665 | Tbl.parseArgs(Argc: argc, Argv: argv, Unknown: OPT_UNKNOWN, Saver, ErrorFn: [&](StringRef Msg) { |
| 666 | error(Msg); |
| 667 | exit(status: 1); |
| 668 | }); |
| 669 | if (Args.hasArg(Ids: OPT_help)) { |
| 670 | Tbl.printHelp( |
| 671 | OS&: outs(), |
| 672 | Usage: (Twine(ToolName) + " [options] <input object files>" ).str().c_str(), |
| 673 | Title: "LLVM Object Reader" ); |
| 674 | // TODO Replace this with OptTable API once it adds extrahelp support. |
| 675 | outs() << "\nPass @FILE as argument to read options from FILE.\n" ; |
| 676 | return 0; |
| 677 | } |
| 678 | if (Args.hasArg(Ids: OPT_version)) { |
| 679 | cl::PrintVersionMessage(); |
| 680 | return 0; |
| 681 | } |
| 682 | |
| 683 | if (sys::path::stem(path: argv[0]).contains(Other: "readelf" )) |
| 684 | opts::Output = opts::GNU; |
| 685 | parseOptions(Args); |
| 686 | |
| 687 | // Default to print error if no filename is specified. |
| 688 | if (opts::InputFilenames.empty()) { |
| 689 | error(Msg: "no input files specified" ); |
| 690 | } |
| 691 | |
| 692 | if (opts::All) { |
| 693 | opts::FileHeaders = true; |
| 694 | opts::XCOFFAuxiliaryHeader = true; |
| 695 | opts::ProgramHeaders = true; |
| 696 | opts::SectionHeaders = true; |
| 697 | opts::Symbols = true; |
| 698 | opts::Relocations = true; |
| 699 | opts::DynamicTable = true; |
| 700 | opts::Notes = true; |
| 701 | opts::VersionInfo = true; |
| 702 | opts::UnwindInfo = true; |
| 703 | opts::SectionGroups = true; |
| 704 | opts::HashHistogram = true; |
| 705 | if (opts::Output == opts::LLVM) { |
| 706 | opts::Addrsig = true; |
| 707 | opts::PrintStackSizes = true; |
| 708 | } |
| 709 | opts::Memtag = true; |
| 710 | } |
| 711 | |
| 712 | if (opts::Headers) { |
| 713 | opts::FileHeaders = true; |
| 714 | opts::XCOFFAuxiliaryHeader = true; |
| 715 | opts::ProgramHeaders = true; |
| 716 | opts::SectionHeaders = true; |
| 717 | } |
| 718 | |
| 719 | std::unique_ptr<ScopedPrinter> Writer = createWriter(); |
| 720 | |
| 721 | for (const std::string &I : opts::InputFilenames) |
| 722 | dumpInput(File: I, Writer&: *Writer); |
| 723 | |
| 724 | if (opts::CodeViewMergedTypes) { |
| 725 | if (opts::CodeViewEnableGHash) |
| 726 | dumpCodeViewMergedTypes(Writer&: *Writer, IpiRecords: CVTypes.GlobalIDTable.records(), |
| 727 | TpiRecords: CVTypes.GlobalTypeTable.records()); |
| 728 | else |
| 729 | dumpCodeViewMergedTypes(Writer&: *Writer, IpiRecords: CVTypes.IDTable.records(), |
| 730 | TpiRecords: CVTypes.TypeTable.records()); |
| 731 | } |
| 732 | |
| 733 | return 0; |
| 734 | } |
| 735 | |