1//===-- llvm-dwp.cpp - Split DWARF merging tool for llvm ------------------===//
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// A utility for merging DWARF 5 Split DWARF .dwo files into .dwp (DWARF
10// package files).
11//
12//===----------------------------------------------------------------------===//
13#include "llvm/DWP/DWP.h"
14#include "llvm/DWP/DWPError.h"
15#include "llvm/Object/ObjectFile.h"
16#include "llvm/Option/ArgList.h"
17#include "llvm/Option/Option.h"
18#include "llvm/Support/CommandLine.h"
19#include "llvm/Support/Driver.h"
20#include "llvm/Support/FileSystem.h"
21#include "llvm/Support/MemoryBuffer.h"
22#include "llvm/Support/ToolOutputFile.h"
23#include <optional>
24
25using namespace llvm;
26using namespace llvm::object;
27
28// Command-line option boilerplate.
29namespace {
30enum ID {
31 OPT_INVALID = 0, // This is not an option ID.
32#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
33#include "Opts.inc"
34#undef OPTION
35};
36
37using namespace llvm::opt;
38#define OPTTABLE_CODE
39#include "Opts.inc"
40
41class DwpOptTable : public opt::OptTable {
42public:
43 DwpOptTable() : OptTable(optionTables()) {}
44};
45} // end anonymous namespace
46
47// Options
48static std::vector<std::string> ExecFilenames;
49static std::string OutputFilename;
50static std::string ContinueOption;
51
52static Expected<SmallVector<std::string, 16>>
53getDWOFilenames(StringRef ExecFilename) {
54 auto ErrOrObj = object::ObjectFile::createObjectFile(ObjectPath: ExecFilename);
55 if (!ErrOrObj)
56 return ErrOrObj.takeError();
57
58 const ObjectFile &Obj = *ErrOrObj.get().getBinary();
59 std::unique_ptr<DWARFContext> DWARFCtx = DWARFContext::create(Obj);
60
61 SmallVector<std::string, 16> DWOPaths;
62 for (const auto &CU : DWARFCtx->compile_units()) {
63 const DWARFDie &Die = CU->getUnitDIE();
64 std::string DWOName = dwarf::toString(
65 V: Die.find(Attrs: {dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), Default: "");
66 if (DWOName.empty())
67 continue;
68 std::string DWOCompDir =
69 dwarf::toString(V: Die.find(Attr: dwarf::DW_AT_comp_dir), Default: "");
70 if (!DWOCompDir.empty()) {
71 SmallString<16> DWOPath(DWOName);
72 sys::path::make_absolute(current_directory: DWOCompDir, path&: DWOPath);
73 if (!sys::fs::exists(Path: DWOPath) && sys::fs::exists(Path: DWOName))
74 DWOPaths.push_back(Elt: std::move(DWOName));
75 else
76 DWOPaths.emplace_back(Args: DWOPath.data(), Args: DWOPath.size());
77 } else {
78 DWOPaths.push_back(Elt: std::move(DWOName));
79 }
80 }
81 return std::move(DWOPaths);
82}
83
84static int error(const Twine &Error, const Twine &Context) {
85 errs() << Twine("while processing ") + Context + ":\n";
86 errs() << Twine("error: ") + Error + "\n";
87 return 1;
88}
89
90int llvm_dwp_main(int argc, char **argv, const llvm::ToolContext &) {
91 DwpOptTable Tbl;
92 llvm::BumpPtrAllocator A;
93 llvm::StringSaver Saver{A};
94 OnCuIndexOverflow OverflowOptValue = OnCuIndexOverflow::HardStop;
95 Dwarf64StrOffsetsPromotion Dwarf64StrOffsetsValue =
96 Dwarf64StrOffsetsPromotion::Disabled;
97
98 opt::InputArgList Args =
99 Tbl.parseArgs(Argc: argc, Argv: argv, Unknown: OPT_UNKNOWN, Saver, ErrorFn: [&](StringRef Msg) {
100 llvm::errs() << Msg << '\n';
101 std::exit(status: 1);
102 });
103
104 if (Args.hasArg(Ids: OPT_help)) {
105 Tbl.printHelp(OS&: llvm::outs(), Usage: "llvm-dwp [options] <input files>",
106 Title: "merge split dwarf (.dwo) files");
107 std::exit(status: 0);
108 }
109
110 if (Args.hasArg(Ids: OPT_version)) {
111 llvm::cl::PrintVersionMessage();
112 std::exit(status: 0);
113 }
114
115 OutputFilename = Args.getLastArgValue(Id: OPT_outputFileName, Default: "");
116 if (Arg *Arg = Args.getLastArg(Ids: OPT_continueOnCuIndexOverflow,
117 Ids: OPT_continueOnCuIndexOverflow_EQ)) {
118 if (Arg->getOption().matches(ID: OPT_continueOnCuIndexOverflow)) {
119 OverflowOptValue = OnCuIndexOverflow::Continue;
120 } else {
121 ContinueOption = Arg->getValue();
122 if (ContinueOption == "soft-stop") {
123 OverflowOptValue = OnCuIndexOverflow::SoftStop;
124 } else if (ContinueOption == "continue") {
125 OverflowOptValue = OnCuIndexOverflow::Continue;
126 } else {
127 llvm::errs() << "invalid value for --continue-on-cu-index-overflow"
128 << ContinueOption << '\n';
129 exit(status: 1);
130 }
131 }
132 }
133
134 if (Arg *Arg = Args.getLastArg(Ids: OPT_dwarf64StringOffsets,
135 Ids: OPT_dwarf64StringOffsets_EQ)) {
136 if (Arg->getOption().matches(ID: OPT_dwarf64StringOffsets)) {
137 Dwarf64StrOffsetsValue = Dwarf64StrOffsetsPromotion::Enabled;
138 } else {
139 std::string OptValue = Arg->getValue();
140 if (OptValue == "disabled") {
141 Dwarf64StrOffsetsValue = Dwarf64StrOffsetsPromotion::Disabled;
142 } else if (OptValue == "enabled") {
143 Dwarf64StrOffsetsValue = Dwarf64StrOffsetsPromotion::Enabled;
144 } else if (OptValue == "always") {
145 Dwarf64StrOffsetsValue = Dwarf64StrOffsetsPromotion::Always;
146 } else {
147 llvm::errs()
148 << "invalid value for --dwarf64-str-offsets-promotion. Valid "
149 "values are one of: \"enabled\", \"disabled\" or \"always\".\n";
150 exit(status: 1);
151 }
152 }
153 }
154
155 for (const llvm::opt::Arg *A : Args.filtered(Ids: OPT_execFileNames))
156 ExecFilenames.emplace_back(args: A->getValue());
157
158 std::vector<std::string> DWOFilenames;
159 for (const llvm::opt::Arg *A : Args.filtered(Ids: OPT_INPUT))
160 DWOFilenames.emplace_back(args: A->getValue());
161
162 for (const auto &ExecFilename : ExecFilenames) {
163 auto DWOs = getDWOFilenames(ExecFilename);
164 if (!DWOs) {
165 logAllUnhandledErrors(
166 E: handleErrors(E: DWOs.takeError(),
167 Hs: [&](std::unique_ptr<ECError> EC) -> Error {
168 return createFileError(F: ExecFilename,
169 E: Error(std::move(EC)));
170 }),
171 OS&: WithColor::error());
172 return 1;
173 }
174 DWOFilenames.insert(position: DWOFilenames.end(),
175 first: std::make_move_iterator(i: DWOs->begin()),
176 last: std::make_move_iterator(i: DWOs->end()));
177 }
178
179 if (DWOFilenames.empty()) {
180 WithColor::defaultWarningHandler(Warning: make_error<DWPError>(
181 Args: "executable file does not contain any references to dwo files"));
182 return 0;
183 }
184
185 StringRef DiscardPrefix = Args.getLastArgValue(Id: OPT_prioritizeDiscardPath, Default: "");
186 if (OverflowOptValue == OnCuIndexOverflow::SoftStop &&
187 !DiscardPrefix.empty()) {
188 SmallString<256> CanonicalDiscardPrefix(DiscardPrefix);
189 if (std::error_code EC =
190 sys::fs::real_path(path: DiscardPrefix, output&: CanonicalDiscardPrefix)) {
191 WithColor::warning() << "invalid --prioritize-discard-path '"
192 << DiscardPrefix << "': " << EC.message()
193 << "; ignoring option.\n";
194 } else {
195 StringRef PrefixRef(CanonicalDiscardPrefix);
196 auto IsNonDiscarded = [&](const std::string &Name) {
197 SmallString<256> CanonicalDWO;
198 if (sys::fs::real_path(path: Name, output&: CanonicalDWO))
199 return true;
200 StringRef DWORef(CanonicalDWO);
201 if (!DWORef.starts_with(Prefix: PrefixRef))
202 return true;
203 if (DWORef.size() == PrefixRef.size())
204 return false;
205 if (sys::path::is_separator(value: DWORef[PrefixRef.size()]))
206 return false;
207 return true;
208 };
209 std::stable_partition(first: DWOFilenames.begin(), last: DWOFilenames.end(),
210 pred: IsNonDiscarded);
211 }
212 }
213
214 // Create the output file.
215 std::error_code EC;
216 ToolOutputFile OutFile(OutputFilename, EC, sys::fs::OF_None);
217 std::optional<buffer_ostream> BOS;
218 raw_pwrite_stream *OS;
219 if (EC)
220 return error(Error: Twine(OutputFilename) + ": " + EC.message(),
221 Context: "dwp output init");
222 if (OutFile.os().supportsSeeking()) {
223 OS = &OutFile.os();
224 } else {
225 BOS.emplace(args&: OutFile.os());
226 OS = &*BOS;
227 }
228
229 // Use DWPWriter for direct ELF output
230 DWPWriter Writer;
231
232 if (auto Err = write(Out&: Writer, Inputs: DWOFilenames, OverflowOptValue,
233 StrOffsetsOptValue: Dwarf64StrOffsetsValue, OS)) {
234 logAllUnhandledErrors(E: std::move(Err), OS&: WithColor::error());
235 return 1;
236 }
237
238 OutFile.keep();
239 return 0;
240}
241