1//===------ utils/obj2yaml.cpp - obj2yaml conversion tool -----------------===//
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/BinaryFormat/Magic.h"
11#include "llvm/Object/Archive.h"
12#include "llvm/Object/COFF.h"
13#include "llvm/Object/Minidump.h"
14#include "llvm/Option/ArgList.h"
15#include "llvm/Option/OptTable.h"
16#include "llvm/Option/Option.h"
17#include "llvm/Support/CommandLine.h"
18#include "llvm/Support/Errc.h"
19#include "llvm/Support/InitLLVM.h"
20#include "llvm/Support/StringSaver.h"
21#include "llvm/Support/ToolOutputFile.h"
22#include "llvm/Support/WithColor.h"
23
24using namespace llvm;
25using namespace llvm::object;
26
27namespace {
28enum ID {
29 OPT_INVALID = 0, // This is not an option ID.
30#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
31#include "Opts.inc"
32#undef OPTION
33};
34
35using namespace llvm::opt;
36#define OPTTABLE_CODE
37#include "Opts.inc"
38
39class Obj2YamlOptTable : public opt::OptTable {
40public:
41 Obj2YamlOptTable() : OptTable(optionTables()) { setDashDashParsing(true); }
42};
43} // namespace
44
45static Error dumpObject(const ObjectFile &Obj, raw_ostream &OS) {
46 if (Obj.isCOFF())
47 return errorCodeToError(EC: coff2yaml(Out&: OS, Obj: cast<COFFObjectFile>(Val: Obj)));
48
49 if (Obj.isXCOFF())
50 return xcoff2yaml(Out&: OS, Obj: cast<XCOFFObjectFile>(Val: Obj));
51
52 if (Obj.isELF())
53 return elf2yaml(Out&: OS, Obj);
54
55 if (Obj.isGOFF())
56 return goff2yaml(Out&: OS, Obj: cast<GOFFObjectFile>(Val: Obj));
57
58 if (Obj.isWasm())
59 return errorCodeToError(EC: wasm2yaml(Out&: OS, Obj: cast<WasmObjectFile>(Val: Obj)));
60
61 llvm_unreachable("unexpected object file format");
62}
63
64static Error dumpInput(StringRef File, unsigned RawSegment, raw_ostream &OS) {
65 ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
66 MemoryBuffer::getFileOrSTDIN(Filename: File, /*IsText=*/false,
67 /*RequiresNullTerminator=*/false);
68 if (std::error_code EC = FileOrErr.getError())
69 return errorCodeToError(EC);
70 std::unique_ptr<MemoryBuffer> &Buffer = FileOrErr.get();
71 MemoryBufferRef MemBuf = Buffer->getMemBufferRef();
72 switch (identify_magic(magic: MemBuf.getBuffer())) {
73 case file_magic::archive:
74 return archive2yaml(Out&: OS, Source: MemBuf);
75 case file_magic::dxcontainer_object:
76 return dxcontainer2yaml(Out&: OS, Source: MemBuf);
77 case file_magic::offload_binary:
78 return offload2yaml(Out&: OS, Source: MemBuf);
79 default:
80 break;
81 }
82
83 Expected<std::unique_ptr<Binary>> BinOrErr =
84 createBinary(Source: MemBuf, /*Context=*/nullptr);
85 if (!BinOrErr)
86 return BinOrErr.takeError();
87
88 Binary &Binary = *BinOrErr->get();
89 // Universal MachO is not a subclass of ObjectFile, so it needs to be handled
90 // here with the other binary types.
91 if (Binary.isMachO() || Binary.isMachOUniversalBinary())
92 return macho2yaml(Out&: OS, Obj: Binary, RawSegments: RawSegment);
93 if (ObjectFile *Obj = dyn_cast<ObjectFile>(Val: &Binary))
94 return dumpObject(Obj: *Obj, OS);
95 if (MinidumpFile *Minidump = dyn_cast<MinidumpFile>(Val: &Binary))
96 return minidump2yaml(Out&: OS, Obj: *Minidump);
97
98 return Error::success();
99}
100
101static void reportError(StringRef Input, Error Err) {
102 if (Input == "-")
103 Input = "<stdin>";
104 std::string ErrMsg;
105 raw_string_ostream OS(ErrMsg);
106 logAllUnhandledErrors(E: std::move(Err), OS);
107 errs() << "Error reading file: " << Input << ": " << ErrMsg;
108 errs().flush();
109}
110
111int main(int argc, char *argv[]) {
112 InitLLVM X(argc, argv);
113 bool HasError = false;
114 auto ErrorFn = [&](const Twine &Msg) {
115 WithColor::error(OS&: errs(), Prefix: "obj2yaml") << Msg << '\n';
116 HasError = true;
117 };
118 BumpPtrAllocator A;
119 StringSaver Saver(A);
120 Obj2YamlOptTable Tbl;
121 opt::InputArgList Args =
122 Tbl.parseArgs(Argc: argc, Argv: argv, Unknown: OPT_UNKNOWN, Saver, ErrorFn);
123 if (HasError)
124 return 1;
125 if (Args.hasArg(Ids: OPT_help)) {
126 Tbl.printHelp(OS&: outs(), Usage: "obj2yaml [options] <input file>",
127 Title: "Dump a YAML description from an object file");
128 return 0;
129 }
130 if (Args.hasArg(Ids: OPT_version)) {
131 cl::PrintVersionMessage();
132 return 0;
133 }
134
135 std::vector<std::string> Inputs = Args.getAllArgValues(Id: OPT_INPUT);
136 if (Inputs.size() > 1)
137 ErrorFn("too many input files");
138 StringRef InputFilename =
139 Inputs.empty() ? StringRef("-") : StringRef(Inputs[0]);
140 StringRef OutputFilename = Args.getLastArgValue(Id: OPT_o, Default: "-");
141 unsigned RawSegment = RawSegments::none;
142 for (StringRef S : Args.getAllArgValues(Id: OPT_raw_segment_EQ)) {
143 if (S == "data")
144 RawSegment |= RawSegments::data;
145 else if (S == "linkedit")
146 RawSegment |= RawSegments::linkedit;
147 else
148 ErrorFn("unknown segment '" + S + "' for --raw-segment");
149 }
150 if (HasError)
151 return 1;
152
153 std::error_code EC;
154 ToolOutputFile Out(OutputFilename, EC, sys::fs::OF_Text);
155 if (EC) {
156 ErrorFn("failed to open '" + OutputFilename + "': " + EC.message());
157 return 1;
158 }
159 if (Error Err = dumpInput(File: InputFilename, RawSegment, OS&: Out.os())) {
160 reportError(Input: InputFilename, Err: std::move(Err));
161 return 1;
162 }
163 Out.keep();
164
165 return 0;
166}
167