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