1 | //===- ObjcopyOptions.cpp -------------------------------------------------===// |
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 "ObjcopyOptions.h" |
10 | #include "llvm/ADT/SmallVector.h" |
11 | #include "llvm/ADT/StringExtras.h" |
12 | #include "llvm/ADT/StringRef.h" |
13 | #include "llvm/ADT/StringSwitch.h" |
14 | #include "llvm/BinaryFormat/COFF.h" |
15 | #include "llvm/ObjCopy/CommonConfig.h" |
16 | #include "llvm/ObjCopy/ConfigManager.h" |
17 | #include "llvm/ObjCopy/MachO/MachOConfig.h" |
18 | #include "llvm/Option/Arg.h" |
19 | #include "llvm/Option/ArgList.h" |
20 | #include "llvm/Support/CRC.h" |
21 | #include "llvm/Support/CommandLine.h" |
22 | #include "llvm/Support/Compression.h" |
23 | #include "llvm/Support/Errc.h" |
24 | #include "llvm/Support/Error.h" |
25 | #include "llvm/Support/MemoryBuffer.h" |
26 | |
27 | using namespace llvm; |
28 | using namespace llvm::objcopy; |
29 | using namespace llvm::opt; |
30 | |
31 | namespace { |
32 | enum ObjcopyID { |
33 | OBJCOPY_INVALID = 0, // This is not an option ID. |
34 | #define OPTION(...) LLVM_MAKE_OPT_ID_WITH_ID_PREFIX(OBJCOPY_, __VA_ARGS__), |
35 | #include "ObjcopyOpts.inc" |
36 | #undef OPTION |
37 | }; |
38 | |
39 | namespace objcopy_opt { |
40 | #define PREFIX(NAME, VALUE) \ |
41 | static constexpr StringLiteral NAME##_init[] = VALUE; \ |
42 | static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \ |
43 | std::size(NAME##_init) - 1); |
44 | #include "ObjcopyOpts.inc" |
45 | #undef PREFIX |
46 | |
47 | static constexpr opt::OptTable::Info ObjcopyInfoTable[] = { |
48 | #define OPTION(...) \ |
49 | LLVM_CONSTRUCT_OPT_INFO_WITH_ID_PREFIX(OBJCOPY_, __VA_ARGS__), |
50 | #include "ObjcopyOpts.inc" |
51 | #undef OPTION |
52 | }; |
53 | } // namespace objcopy_opt |
54 | |
55 | class ObjcopyOptTable : public opt::GenericOptTable { |
56 | public: |
57 | ObjcopyOptTable() : opt::GenericOptTable(objcopy_opt::ObjcopyInfoTable) { |
58 | setGroupedShortOptions(true); |
59 | } |
60 | }; |
61 | |
62 | enum InstallNameToolID { |
63 | INSTALL_NAME_TOOL_INVALID = 0, // This is not an option ID. |
64 | #define OPTION(...) \ |
65 | LLVM_MAKE_OPT_ID_WITH_ID_PREFIX(INSTALL_NAME_TOOL_, __VA_ARGS__), |
66 | #include "InstallNameToolOpts.inc" |
67 | #undef OPTION |
68 | }; |
69 | |
70 | namespace install_name_tool { |
71 | |
72 | #define PREFIX(NAME, VALUE) \ |
73 | static constexpr StringLiteral NAME##_init[] = VALUE; \ |
74 | static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \ |
75 | std::size(NAME##_init) - 1); |
76 | #include "InstallNameToolOpts.inc" |
77 | #undef PREFIX |
78 | |
79 | static constexpr opt::OptTable::Info InstallNameToolInfoTable[] = { |
80 | #define OPTION(...) \ |
81 | LLVM_CONSTRUCT_OPT_INFO_WITH_ID_PREFIX(INSTALL_NAME_TOOL_, __VA_ARGS__), |
82 | #include "InstallNameToolOpts.inc" |
83 | #undef OPTION |
84 | }; |
85 | } // namespace install_name_tool |
86 | |
87 | class InstallNameToolOptTable : public opt::GenericOptTable { |
88 | public: |
89 | InstallNameToolOptTable() |
90 | : GenericOptTable(install_name_tool::InstallNameToolInfoTable) {} |
91 | }; |
92 | |
93 | enum BitcodeStripID { |
94 | BITCODE_STRIP_INVALID = 0, // This is not an option ID. |
95 | #define OPTION(...) \ |
96 | LLVM_MAKE_OPT_ID_WITH_ID_PREFIX(BITCODE_STRIP_, __VA_ARGS__), |
97 | #include "BitcodeStripOpts.inc" |
98 | #undef OPTION |
99 | }; |
100 | |
101 | namespace bitcode_strip { |
102 | |
103 | #define PREFIX(NAME, VALUE) \ |
104 | static constexpr StringLiteral NAME##_init[] = VALUE; \ |
105 | static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \ |
106 | std::size(NAME##_init) - 1); |
107 | #include "BitcodeStripOpts.inc" |
108 | #undef PREFIX |
109 | |
110 | static constexpr opt::OptTable::Info BitcodeStripInfoTable[] = { |
111 | #define OPTION(...) \ |
112 | LLVM_CONSTRUCT_OPT_INFO_WITH_ID_PREFIX(BITCODE_STRIP_, __VA_ARGS__), |
113 | #include "BitcodeStripOpts.inc" |
114 | #undef OPTION |
115 | }; |
116 | } // namespace bitcode_strip |
117 | |
118 | class BitcodeStripOptTable : public opt::GenericOptTable { |
119 | public: |
120 | BitcodeStripOptTable() |
121 | : opt::GenericOptTable(bitcode_strip::BitcodeStripInfoTable) {} |
122 | }; |
123 | |
124 | enum StripID { |
125 | STRIP_INVALID = 0, // This is not an option ID. |
126 | #define OPTION(...) LLVM_MAKE_OPT_ID_WITH_ID_PREFIX(STRIP_, __VA_ARGS__), |
127 | #include "StripOpts.inc" |
128 | #undef OPTION |
129 | }; |
130 | |
131 | namespace strip { |
132 | #define PREFIX(NAME, VALUE) \ |
133 | static constexpr StringLiteral NAME##_init[] = VALUE; \ |
134 | static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \ |
135 | std::size(NAME##_init) - 1); |
136 | #include "StripOpts.inc" |
137 | #undef PREFIX |
138 | |
139 | static constexpr opt::OptTable::Info StripInfoTable[] = { |
140 | #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO_WITH_ID_PREFIX(STRIP_, __VA_ARGS__), |
141 | #include "StripOpts.inc" |
142 | #undef OPTION |
143 | }; |
144 | } // namespace strip |
145 | |
146 | class StripOptTable : public opt::GenericOptTable { |
147 | public: |
148 | StripOptTable() : GenericOptTable(strip::StripInfoTable) { |
149 | setGroupedShortOptions(true); |
150 | } |
151 | }; |
152 | |
153 | } // namespace |
154 | |
155 | static SectionFlag parseSectionRenameFlag(StringRef SectionName) { |
156 | return llvm::StringSwitch<SectionFlag>(SectionName) |
157 | .CaseLower(S: "alloc" , Value: SectionFlag::SecAlloc) |
158 | .CaseLower(S: "load" , Value: SectionFlag::SecLoad) |
159 | .CaseLower(S: "noload" , Value: SectionFlag::SecNoload) |
160 | .CaseLower(S: "readonly" , Value: SectionFlag::SecReadonly) |
161 | .CaseLower(S: "debug" , Value: SectionFlag::SecDebug) |
162 | .CaseLower(S: "code" , Value: SectionFlag::SecCode) |
163 | .CaseLower(S: "data" , Value: SectionFlag::SecData) |
164 | .CaseLower(S: "rom" , Value: SectionFlag::SecRom) |
165 | .CaseLower(S: "merge" , Value: SectionFlag::SecMerge) |
166 | .CaseLower(S: "strings" , Value: SectionFlag::SecStrings) |
167 | .CaseLower(S: "contents" , Value: SectionFlag::SecContents) |
168 | .CaseLower(S: "share" , Value: SectionFlag::SecShare) |
169 | .CaseLower(S: "exclude" , Value: SectionFlag::SecExclude) |
170 | .CaseLower(S: "large" , Value: SectionFlag::SecLarge) |
171 | .Default(Value: SectionFlag::SecNone); |
172 | } |
173 | |
174 | static Expected<SectionFlag> |
175 | parseSectionFlagSet(ArrayRef<StringRef> SectionFlags) { |
176 | SectionFlag ParsedFlags = SectionFlag::SecNone; |
177 | for (StringRef Flag : SectionFlags) { |
178 | SectionFlag ParsedFlag = parseSectionRenameFlag(SectionName: Flag); |
179 | if (ParsedFlag == SectionFlag::SecNone) |
180 | return createStringError( |
181 | EC: errc::invalid_argument, |
182 | Fmt: "unrecognized section flag '%s'. Flags supported for GNU " |
183 | "compatibility: alloc, load, noload, readonly, exclude, debug, " |
184 | "code, data, rom, share, contents, merge, strings, large" , |
185 | Vals: Flag.str().c_str()); |
186 | ParsedFlags |= ParsedFlag; |
187 | } |
188 | |
189 | return ParsedFlags; |
190 | } |
191 | |
192 | static Expected<SectionRename> parseRenameSectionValue(StringRef FlagValue) { |
193 | if (!FlagValue.contains(C: '=')) |
194 | return createStringError(EC: errc::invalid_argument, |
195 | Msg: "bad format for --rename-section: missing '='" ); |
196 | |
197 | // Initial split: ".foo" = ".bar,f1,f2,..." |
198 | auto Old2New = FlagValue.split(Separator: '='); |
199 | SectionRename SR; |
200 | SR.OriginalName = Old2New.first; |
201 | |
202 | // Flags split: ".bar" "f1" "f2" ... |
203 | SmallVector<StringRef, 6> NameAndFlags; |
204 | Old2New.second.split(A&: NameAndFlags, Separator: ','); |
205 | SR.NewName = NameAndFlags[0]; |
206 | |
207 | if (NameAndFlags.size() > 1) { |
208 | Expected<SectionFlag> ParsedFlagSet = |
209 | parseSectionFlagSet(SectionFlags: ArrayRef(NameAndFlags).drop_front()); |
210 | if (!ParsedFlagSet) |
211 | return ParsedFlagSet.takeError(); |
212 | SR.NewFlags = *ParsedFlagSet; |
213 | } |
214 | |
215 | return SR; |
216 | } |
217 | |
218 | static Expected<std::pair<StringRef, uint64_t>> |
219 | parseSetSectionAttribute(StringRef Option, StringRef FlagValue) { |
220 | if (!FlagValue.contains(C: '=')) |
221 | return make_error<StringError>(Args: "bad format for " + Option + ": missing '='" , |
222 | Args: errc::invalid_argument); |
223 | auto Split = StringRef(FlagValue).split(Separator: '='); |
224 | if (Split.first.empty()) |
225 | return make_error<StringError>(Args: "bad format for " + Option + |
226 | ": missing section name" , |
227 | Args: errc::invalid_argument); |
228 | uint64_t Value; |
229 | if (Split.second.getAsInteger(Radix: 0, Result&: Value)) |
230 | return make_error<StringError>(Args: "invalid value for " + Option + ": '" + |
231 | Split.second + "'" , |
232 | Args: errc::invalid_argument); |
233 | return std::make_pair(x&: Split.first, y&: Value); |
234 | } |
235 | |
236 | static Expected<SectionFlagsUpdate> |
237 | parseSetSectionFlagValue(StringRef FlagValue) { |
238 | if (!StringRef(FlagValue).contains(C: '=')) |
239 | return createStringError(EC: errc::invalid_argument, |
240 | Msg: "bad format for --set-section-flags: missing '='" ); |
241 | |
242 | // Initial split: ".foo" = "f1,f2,..." |
243 | auto Section2Flags = StringRef(FlagValue).split(Separator: '='); |
244 | SectionFlagsUpdate SFU; |
245 | SFU.Name = Section2Flags.first; |
246 | |
247 | // Flags split: "f1" "f2" ... |
248 | SmallVector<StringRef, 6> SectionFlags; |
249 | Section2Flags.second.split(A&: SectionFlags, Separator: ','); |
250 | Expected<SectionFlag> ParsedFlagSet = parseSectionFlagSet(SectionFlags); |
251 | if (!ParsedFlagSet) |
252 | return ParsedFlagSet.takeError(); |
253 | SFU.NewFlags = *ParsedFlagSet; |
254 | |
255 | return SFU; |
256 | } |
257 | |
258 | static Expected<uint8_t> parseVisibilityType(StringRef VisType) { |
259 | const uint8_t Invalid = 0xff; |
260 | uint8_t type = StringSwitch<uint8_t>(VisType) |
261 | .Case(S: "default" , Value: ELF::STV_DEFAULT) |
262 | .Case(S: "hidden" , Value: ELF::STV_HIDDEN) |
263 | .Case(S: "internal" , Value: ELF::STV_INTERNAL) |
264 | .Case(S: "protected" , Value: ELF::STV_PROTECTED) |
265 | .Default(Value: Invalid); |
266 | if (type == Invalid) |
267 | return createStringError(EC: errc::invalid_argument, |
268 | Fmt: "'%s' is not a valid symbol visibility" , |
269 | Vals: VisType.str().c_str()); |
270 | return type; |
271 | } |
272 | |
273 | namespace { |
274 | struct TargetInfo { |
275 | FileFormat Format; |
276 | MachineInfo Machine; |
277 | }; |
278 | } // namespace |
279 | |
280 | // FIXME: consolidate with the bfd parsing used by lld. |
281 | static const StringMap<MachineInfo> TargetMap{ |
282 | // Name, {EMachine, 64bit, LittleEndian} |
283 | // x86 |
284 | {"elf32-i386" , {ELF::EM_386, false, true}}, |
285 | {"elf32-x86-64" , {ELF::EM_X86_64, false, true}}, |
286 | {"elf64-x86-64" , {ELF::EM_X86_64, true, true}}, |
287 | // Intel MCU |
288 | {"elf32-iamcu" , {ELF::EM_IAMCU, false, true}}, |
289 | // ARM |
290 | {"elf32-littlearm" , {ELF::EM_ARM, false, true}}, |
291 | // ARM AArch64 |
292 | {"elf64-aarch64" , {ELF::EM_AARCH64, true, true}}, |
293 | {"elf64-littleaarch64" , {ELF::EM_AARCH64, true, true}}, |
294 | // RISC-V |
295 | {"elf32-littleriscv" , {ELF::EM_RISCV, false, true}}, |
296 | {"elf64-littleriscv" , {ELF::EM_RISCV, true, true}}, |
297 | // PowerPC |
298 | {"elf32-powerpc" , {ELF::EM_PPC, false, false}}, |
299 | {"elf32-powerpcle" , {ELF::EM_PPC, false, true}}, |
300 | {"elf64-powerpc" , {ELF::EM_PPC64, true, false}}, |
301 | {"elf64-powerpcle" , {ELF::EM_PPC64, true, true}}, |
302 | // MIPS |
303 | {"elf32-bigmips" , {ELF::EM_MIPS, false, false}}, |
304 | {"elf32-ntradbigmips" , {ELF::EM_MIPS, false, false}}, |
305 | {"elf32-ntradlittlemips" , {ELF::EM_MIPS, false, true}}, |
306 | {"elf32-tradbigmips" , {ELF::EM_MIPS, false, false}}, |
307 | {"elf32-tradlittlemips" , {ELF::EM_MIPS, false, true}}, |
308 | {"elf64-tradbigmips" , {ELF::EM_MIPS, true, false}}, |
309 | {"elf64-tradlittlemips" , {ELF::EM_MIPS, true, true}}, |
310 | // SPARC |
311 | {"elf32-sparc" , {ELF::EM_SPARC, false, false}}, |
312 | {"elf32-sparcel" , {ELF::EM_SPARC, false, true}}, |
313 | // Hexagon |
314 | {"elf32-hexagon" , {ELF::EM_HEXAGON, false, true}}, |
315 | // LoongArch |
316 | {"elf32-loongarch" , {ELF::EM_LOONGARCH, false, true}}, |
317 | {"elf64-loongarch" , {ELF::EM_LOONGARCH, true, true}}, |
318 | // SystemZ |
319 | {"elf64-s390" , {ELF::EM_S390, true, false}}, |
320 | }; |
321 | |
322 | static Expected<TargetInfo> |
323 | getOutputTargetInfoByTargetName(StringRef TargetName) { |
324 | StringRef OriginalTargetName = TargetName; |
325 | bool IsFreeBSD = TargetName.consume_back(Suffix: "-freebsd" ); |
326 | auto Iter = TargetMap.find(Key: TargetName); |
327 | if (Iter == std::end(cont: TargetMap)) |
328 | return createStringError(EC: errc::invalid_argument, |
329 | Fmt: "invalid output format: '%s'" , |
330 | Vals: OriginalTargetName.str().c_str()); |
331 | MachineInfo MI = Iter->getValue(); |
332 | if (IsFreeBSD) |
333 | MI.OSABI = ELF::ELFOSABI_FREEBSD; |
334 | |
335 | FileFormat Format; |
336 | if (TargetName.starts_with(Prefix: "elf" )) |
337 | Format = FileFormat::ELF; |
338 | else |
339 | // This should never happen because `TargetName` is valid (it certainly |
340 | // exists in the TargetMap). |
341 | llvm_unreachable("unknown target prefix" ); |
342 | |
343 | return {TargetInfo{.Format: Format, .Machine: MI}}; |
344 | } |
345 | |
346 | static Error addSymbolsFromFile(NameMatcher &Symbols, BumpPtrAllocator &Alloc, |
347 | StringRef Filename, MatchStyle MS, |
348 | function_ref<Error(Error)> ErrorCallback) { |
349 | StringSaver Saver(Alloc); |
350 | SmallVector<StringRef, 16> Lines; |
351 | auto BufOrErr = MemoryBuffer::getFile(Filename); |
352 | if (!BufOrErr) |
353 | return createFileError(F: Filename, EC: BufOrErr.getError()); |
354 | |
355 | BufOrErr.get()->getBuffer().split(A&: Lines, Separator: '\n'); |
356 | for (StringRef Line : Lines) { |
357 | // Ignore everything after '#', trim whitespace, and only add the symbol if |
358 | // it's not empty. |
359 | auto TrimmedLine = Line.split(Separator: '#').first.trim(); |
360 | if (!TrimmedLine.empty()) |
361 | if (Error E = Symbols.addMatcher(Matcher: NameOrPattern::create( |
362 | Pattern: Saver.save(S: TrimmedLine), MS, ErrorCallback))) |
363 | return E; |
364 | } |
365 | |
366 | return Error::success(); |
367 | } |
368 | |
369 | static Error addSymbolsToRenameFromFile(StringMap<StringRef> &SymbolsToRename, |
370 | BumpPtrAllocator &Alloc, |
371 | StringRef Filename) { |
372 | StringSaver Saver(Alloc); |
373 | SmallVector<StringRef, 16> Lines; |
374 | auto BufOrErr = MemoryBuffer::getFile(Filename); |
375 | if (!BufOrErr) |
376 | return createFileError(F: Filename, EC: BufOrErr.getError()); |
377 | |
378 | BufOrErr.get()->getBuffer().split(A&: Lines, Separator: '\n'); |
379 | size_t NumLines = Lines.size(); |
380 | for (size_t LineNo = 0; LineNo < NumLines; ++LineNo) { |
381 | StringRef TrimmedLine = Lines[LineNo].split(Separator: '#').first.trim(); |
382 | if (TrimmedLine.empty()) |
383 | continue; |
384 | |
385 | std::pair<StringRef, StringRef> Pair = Saver.save(S: TrimmedLine).split(Separator: ' '); |
386 | StringRef NewName = Pair.second.trim(); |
387 | if (NewName.empty()) |
388 | return createStringError(EC: errc::invalid_argument, |
389 | Fmt: "%s:%zu: missing new symbol name" , |
390 | Vals: Filename.str().c_str(), Vals: LineNo + 1); |
391 | SymbolsToRename.insert(KV: {Pair.first, NewName}); |
392 | } |
393 | return Error::success(); |
394 | } |
395 | |
396 | template <class T> static ErrorOr<T> getAsInteger(StringRef Val) { |
397 | T Result; |
398 | if (Val.getAsInteger(0, Result)) |
399 | return errc::invalid_argument; |
400 | return Result; |
401 | } |
402 | |
403 | namespace { |
404 | |
405 | enum class ToolType { Objcopy, Strip, InstallNameTool, BitcodeStrip }; |
406 | |
407 | } // anonymous namespace |
408 | |
409 | static void printHelp(const opt::OptTable &OptTable, raw_ostream &OS, |
410 | ToolType Tool) { |
411 | StringRef HelpText, ToolName; |
412 | switch (Tool) { |
413 | case ToolType::Objcopy: |
414 | ToolName = "llvm-objcopy" ; |
415 | HelpText = " [options] input [output]" ; |
416 | break; |
417 | case ToolType::Strip: |
418 | ToolName = "llvm-strip" ; |
419 | HelpText = " [options] inputs..." ; |
420 | break; |
421 | case ToolType::InstallNameTool: |
422 | ToolName = "llvm-install-name-tool" ; |
423 | HelpText = " [options] input" ; |
424 | break; |
425 | case ToolType::BitcodeStrip: |
426 | ToolName = "llvm-bitcode-strip" ; |
427 | HelpText = " [options] input" ; |
428 | break; |
429 | } |
430 | OptTable.printHelp(OS, Usage: (ToolName + HelpText).str().c_str(), |
431 | Title: (ToolName + " tool" ).str().c_str()); |
432 | // TODO: Replace this with libOption call once it adds extrahelp support. |
433 | // The CommandLine library has a cl::extrahelp class to support this, |
434 | // but libOption does not have that yet. |
435 | OS << "\nPass @FILE as argument to read options from FILE.\n" ; |
436 | } |
437 | |
438 | static Expected<NewSymbolInfo> parseNewSymbolInfo(StringRef FlagValue) { |
439 | // Parse value given with --add-symbol option and create the |
440 | // new symbol if possible. The value format for --add-symbol is: |
441 | // |
442 | // <name>=[<section>:]<value>[,<flags>] |
443 | // |
444 | // where: |
445 | // <name> - symbol name, can be empty string |
446 | // <section> - optional section name. If not given ABS symbol is created |
447 | // <value> - symbol value, can be decimal or hexadecimal number prefixed |
448 | // with 0x. |
449 | // <flags> - optional flags affecting symbol type, binding or visibility. |
450 | NewSymbolInfo SI; |
451 | StringRef Value; |
452 | std::tie(args&: SI.SymbolName, args&: Value) = FlagValue.split(Separator: '='); |
453 | if (Value.empty()) |
454 | return createStringError( |
455 | EC: errc::invalid_argument, |
456 | Fmt: "bad format for --add-symbol, missing '=' after '%s'" , |
457 | Vals: SI.SymbolName.str().c_str()); |
458 | |
459 | if (Value.contains(C: ':')) { |
460 | std::tie(args&: SI.SectionName, args&: Value) = Value.split(Separator: ':'); |
461 | if (SI.SectionName.empty() || Value.empty()) |
462 | return createStringError( |
463 | EC: errc::invalid_argument, |
464 | Msg: "bad format for --add-symbol, missing section name or symbol value" ); |
465 | } |
466 | |
467 | SmallVector<StringRef, 6> Flags; |
468 | Value.split(A&: Flags, Separator: ','); |
469 | if (Flags[0].getAsInteger(Radix: 0, Result&: SI.Value)) |
470 | return createStringError(EC: errc::invalid_argument, Fmt: "bad symbol value: '%s'" , |
471 | Vals: Flags[0].str().c_str()); |
472 | |
473 | using Functor = std::function<void()>; |
474 | SmallVector<StringRef, 6> UnsupportedFlags; |
475 | for (size_t I = 1, NumFlags = Flags.size(); I < NumFlags; ++I) |
476 | static_cast<Functor>( |
477 | StringSwitch<Functor>(Flags[I]) |
478 | .CaseLower(S: "global" , |
479 | Value: [&] { SI.Flags.push_back(Elt: SymbolFlag::Global); }) |
480 | .CaseLower(S: "local" , Value: [&] { SI.Flags.push_back(Elt: SymbolFlag::Local); }) |
481 | .CaseLower(S: "weak" , Value: [&] { SI.Flags.push_back(Elt: SymbolFlag::Weak); }) |
482 | .CaseLower(S: "default" , |
483 | Value: [&] { SI.Flags.push_back(Elt: SymbolFlag::Default); }) |
484 | .CaseLower(S: "hidden" , |
485 | Value: [&] { SI.Flags.push_back(Elt: SymbolFlag::Hidden); }) |
486 | .CaseLower(S: "protected" , |
487 | Value: [&] { SI.Flags.push_back(Elt: SymbolFlag::Protected); }) |
488 | .CaseLower(S: "file" , Value: [&] { SI.Flags.push_back(Elt: SymbolFlag::File); }) |
489 | .CaseLower(S: "section" , |
490 | Value: [&] { SI.Flags.push_back(Elt: SymbolFlag::Section); }) |
491 | .CaseLower(S: "object" , |
492 | Value: [&] { SI.Flags.push_back(Elt: SymbolFlag::Object); }) |
493 | .CaseLower(S: "function" , |
494 | Value: [&] { SI.Flags.push_back(Elt: SymbolFlag::Function); }) |
495 | .CaseLower( |
496 | S: "indirect-function" , |
497 | Value: [&] { SI.Flags.push_back(Elt: SymbolFlag::IndirectFunction); }) |
498 | .CaseLower(S: "debug" , Value: [&] { SI.Flags.push_back(Elt: SymbolFlag::Debug); }) |
499 | .CaseLower(S: "constructor" , |
500 | Value: [&] { SI.Flags.push_back(Elt: SymbolFlag::Constructor); }) |
501 | .CaseLower(S: "warning" , |
502 | Value: [&] { SI.Flags.push_back(Elt: SymbolFlag::Warning); }) |
503 | .CaseLower(S: "indirect" , |
504 | Value: [&] { SI.Flags.push_back(Elt: SymbolFlag::Indirect); }) |
505 | .CaseLower(S: "synthetic" , |
506 | Value: [&] { SI.Flags.push_back(Elt: SymbolFlag::Synthetic); }) |
507 | .CaseLower(S: "unique-object" , |
508 | Value: [&] { SI.Flags.push_back(Elt: SymbolFlag::UniqueObject); }) |
509 | .StartsWithLower(S: "before=" , |
510 | Value: [&] { |
511 | StringRef SymNamePart = |
512 | Flags[I].split(Separator: '=').second; |
513 | |
514 | if (!SymNamePart.empty()) |
515 | SI.BeforeSyms.push_back(Elt: SymNamePart); |
516 | }) |
517 | .Default(Value: [&] { UnsupportedFlags.push_back(Elt: Flags[I]); }))(); |
518 | if (!UnsupportedFlags.empty()) |
519 | return createStringError(EC: errc::invalid_argument, |
520 | Fmt: "unsupported flag%s for --add-symbol: '%s'" , |
521 | Vals: UnsupportedFlags.size() > 1 ? "s" : "" , |
522 | Vals: join(R&: UnsupportedFlags, Separator: "', '" ).c_str()); |
523 | |
524 | return SI; |
525 | } |
526 | |
527 | // Parse input option \p ArgValue and load section data. This function |
528 | // extracts section name and name of the file keeping section data from |
529 | // ArgValue, loads data from the file, and stores section name and data |
530 | // into the vector of new sections \p NewSections. |
531 | static Error loadNewSectionData(StringRef ArgValue, StringRef OptionName, |
532 | SmallVector<NewSectionInfo, 0> &NewSections) { |
533 | if (!ArgValue.contains(C: '=')) |
534 | return createStringError(EC: errc::invalid_argument, |
535 | S: "bad format for " + OptionName + ": missing '='" ); |
536 | |
537 | std::pair<StringRef, StringRef> SecPair = ArgValue.split(Separator: "=" ); |
538 | if (SecPair.second.empty()) |
539 | return createStringError(EC: errc::invalid_argument, S: "bad format for " + |
540 | OptionName + |
541 | ": missing file name" ); |
542 | |
543 | ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = |
544 | MemoryBuffer::getFile(Filename: SecPair.second); |
545 | if (!BufOrErr) |
546 | return createFileError(F: SecPair.second, |
547 | E: errorCodeToError(EC: BufOrErr.getError())); |
548 | |
549 | NewSections.push_back(Elt: {SecPair.first, std::move(*BufOrErr)}); |
550 | return Error::success(); |
551 | } |
552 | |
553 | // parseObjcopyOptions returns the config and sets the input arguments. If a |
554 | // help flag is set then parseObjcopyOptions will print the help messege and |
555 | // exit. |
556 | Expected<DriverConfig> |
557 | objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr, |
558 | function_ref<Error(Error)> ErrorCallback) { |
559 | DriverConfig DC; |
560 | ObjcopyOptTable T; |
561 | |
562 | const char *const *DashDash = |
563 | llvm::find_if(Range&: RawArgsArr, P: [](StringRef Str) { return Str == "--" ; }); |
564 | ArrayRef<const char *> ArgsArr = ArrayRef(RawArgsArr.begin(), DashDash); |
565 | if (DashDash != RawArgsArr.end()) |
566 | DashDash = std::next(x: DashDash); |
567 | |
568 | unsigned MissingArgumentIndex, MissingArgumentCount; |
569 | llvm::opt::InputArgList InputArgs = |
570 | T.ParseArgs(Args: ArgsArr, MissingArgIndex&: MissingArgumentIndex, MissingArgCount&: MissingArgumentCount); |
571 | |
572 | if (InputArgs.size() == 0 && DashDash == RawArgsArr.end()) { |
573 | printHelp(OptTable: T, OS&: errs(), Tool: ToolType::Objcopy); |
574 | exit(status: 1); |
575 | } |
576 | |
577 | if (InputArgs.hasArg(OBJCOPY_help)) { |
578 | printHelp(OptTable: T, OS&: outs(), Tool: ToolType::Objcopy); |
579 | exit(status: 0); |
580 | } |
581 | |
582 | if (InputArgs.hasArg(OBJCOPY_version)) { |
583 | outs() << "llvm-objcopy, compatible with GNU objcopy\n" ; |
584 | cl::PrintVersionMessage(); |
585 | exit(status: 0); |
586 | } |
587 | |
588 | SmallVector<const char *, 2> Positional; |
589 | |
590 | for (auto *Arg : InputArgs.filtered(OBJCOPY_UNKNOWN)) |
591 | return createStringError(errc::invalid_argument, "unknown argument '%s'" , |
592 | Arg->getAsString(InputArgs).c_str()); |
593 | |
594 | for (auto *Arg : InputArgs.filtered(Ids: OBJCOPY_INPUT)) |
595 | Positional.push_back(Elt: Arg->getValue()); |
596 | std::copy(first: DashDash, last: RawArgsArr.end(), result: std::back_inserter(x&: Positional)); |
597 | |
598 | if (Positional.empty()) |
599 | return createStringError(EC: errc::invalid_argument, Msg: "no input file specified" ); |
600 | |
601 | if (Positional.size() > 2) |
602 | return createStringError(EC: errc::invalid_argument, |
603 | Msg: "too many positional arguments" ); |
604 | |
605 | ConfigManager ConfigMgr; |
606 | CommonConfig &Config = ConfigMgr.Common; |
607 | COFFConfig &COFFConfig = ConfigMgr.COFF; |
608 | ELFConfig &ELFConfig = ConfigMgr.ELF; |
609 | MachOConfig &MachOConfig = ConfigMgr.MachO; |
610 | Config.InputFilename = Positional[0]; |
611 | Config.OutputFilename = Positional[Positional.size() == 1 ? 0 : 1]; |
612 | if (InputArgs.hasArg(OBJCOPY_target) && |
613 | (InputArgs.hasArg(OBJCOPY_input_target) || |
614 | InputArgs.hasArg(OBJCOPY_output_target))) |
615 | return createStringError( |
616 | EC: errc::invalid_argument, |
617 | Msg: "--target cannot be used with --input-target or --output-target" ); |
618 | |
619 | if (InputArgs.hasArg(OBJCOPY_regex) && InputArgs.hasArg(OBJCOPY_wildcard)) |
620 | return createStringError(EC: errc::invalid_argument, |
621 | Msg: "--regex and --wildcard are incompatible" ); |
622 | |
623 | MatchStyle SectionMatchStyle = InputArgs.hasArg(OBJCOPY_regex) |
624 | ? MatchStyle::Regex |
625 | : MatchStyle::Wildcard; |
626 | MatchStyle SymbolMatchStyle |
627 | = InputArgs.hasArg(OBJCOPY_regex) ? MatchStyle::Regex |
628 | : InputArgs.hasArg(OBJCOPY_wildcard) ? MatchStyle::Wildcard |
629 | : MatchStyle::Literal; |
630 | StringRef InputFormat, OutputFormat; |
631 | if (InputArgs.hasArg(OBJCOPY_target)) { |
632 | InputFormat = InputArgs.getLastArgValue(Id: OBJCOPY_target); |
633 | OutputFormat = InputArgs.getLastArgValue(Id: OBJCOPY_target); |
634 | } else { |
635 | InputFormat = InputArgs.getLastArgValue(Id: OBJCOPY_input_target); |
636 | OutputFormat = InputArgs.getLastArgValue(Id: OBJCOPY_output_target); |
637 | } |
638 | |
639 | // FIXME: Currently, we ignore the target for non-binary/ihex formats |
640 | // explicitly specified by -I option (e.g. -Ielf32-x86-64) and guess the |
641 | // format by llvm::object::createBinary regardless of the option value. |
642 | Config.InputFormat = StringSwitch<FileFormat>(InputFormat) |
643 | .Case(S: "binary" , Value: FileFormat::Binary) |
644 | .Case(S: "ihex" , Value: FileFormat::IHex) |
645 | .Default(Value: FileFormat::Unspecified); |
646 | |
647 | if (InputArgs.hasArg(OBJCOPY_new_symbol_visibility)) { |
648 | const uint8_t Invalid = 0xff; |
649 | StringRef VisibilityStr = |
650 | InputArgs.getLastArgValue(Id: OBJCOPY_new_symbol_visibility); |
651 | |
652 | ELFConfig.NewSymbolVisibility = StringSwitch<uint8_t>(VisibilityStr) |
653 | .Case(S: "default" , Value: ELF::STV_DEFAULT) |
654 | .Case(S: "hidden" , Value: ELF::STV_HIDDEN) |
655 | .Case(S: "internal" , Value: ELF::STV_INTERNAL) |
656 | .Case(S: "protected" , Value: ELF::STV_PROTECTED) |
657 | .Default(Value: Invalid); |
658 | |
659 | if (ELFConfig.NewSymbolVisibility == Invalid) |
660 | return createStringError(EC: errc::invalid_argument, |
661 | Fmt: "'%s' is not a valid symbol visibility" , |
662 | Vals: VisibilityStr.str().c_str()); |
663 | } |
664 | |
665 | for (const auto *Arg : InputArgs.filtered(OBJCOPY_subsystem)) { |
666 | StringRef Subsystem, Version; |
667 | std::tie(Subsystem, Version) = StringRef(Arg->getValue()).split(':'); |
668 | COFFConfig.Subsystem = |
669 | StringSwitch<unsigned>(Subsystem.lower()) |
670 | .Case("boot_application" , |
671 | COFF::IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION) |
672 | .Case("console" , COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI) |
673 | .Case("efi_application" , COFF::IMAGE_SUBSYSTEM_EFI_APPLICATION) |
674 | .Case("efi_boot_service_driver" , |
675 | COFF::IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) |
676 | .Case("efi_rom" , COFF::IMAGE_SUBSYSTEM_EFI_ROM) |
677 | .Case("efi_runtime_driver" , |
678 | COFF::IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) |
679 | .Case("native" , COFF::IMAGE_SUBSYSTEM_NATIVE) |
680 | .Case("posix" , COFF::IMAGE_SUBSYSTEM_POSIX_CUI) |
681 | .Case("windows" , COFF::IMAGE_SUBSYSTEM_WINDOWS_GUI) |
682 | .Default(COFF::IMAGE_SUBSYSTEM_UNKNOWN); |
683 | if (*COFFConfig.Subsystem == COFF::IMAGE_SUBSYSTEM_UNKNOWN) |
684 | return createStringError(errc::invalid_argument, |
685 | "'%s' is not a valid subsystem" , |
686 | Subsystem.str().c_str()); |
687 | if (!Version.empty()) { |
688 | StringRef Major, Minor; |
689 | std::tie(Major, Minor) = Version.split('.'); |
690 | unsigned Number; |
691 | if (Major.getAsInteger(10, Number)) |
692 | return createStringError(errc::invalid_argument, |
693 | "'%s' is not a valid subsystem major version" , |
694 | Major.str().c_str()); |
695 | COFFConfig.MajorSubsystemVersion = Number; |
696 | Number = 0; |
697 | if (!Minor.empty() && Minor.getAsInteger(10, Number)) |
698 | return createStringError(errc::invalid_argument, |
699 | "'%s' is not a valid subsystem minor version" , |
700 | Minor.str().c_str()); |
701 | COFFConfig.MinorSubsystemVersion = Number; |
702 | } |
703 | } |
704 | |
705 | Config.OutputFormat = StringSwitch<FileFormat>(OutputFormat) |
706 | .Case(S: "binary" , Value: FileFormat::Binary) |
707 | .Case(S: "ihex" , Value: FileFormat::IHex) |
708 | .Case(S: "srec" , Value: FileFormat::SREC) |
709 | .Default(Value: FileFormat::Unspecified); |
710 | if (Config.OutputFormat == FileFormat::Unspecified) { |
711 | if (OutputFormat.empty()) { |
712 | Config.OutputFormat = Config.InputFormat; |
713 | } else { |
714 | Expected<TargetInfo> Target = |
715 | getOutputTargetInfoByTargetName(TargetName: OutputFormat); |
716 | if (!Target) |
717 | return Target.takeError(); |
718 | Config.OutputFormat = Target->Format; |
719 | Config.OutputArch = Target->Machine; |
720 | } |
721 | } |
722 | |
723 | if (const auto *A = InputArgs.getLastArg(OBJCOPY_compress_debug_sections)) { |
724 | Config.CompressionType = StringSwitch<DebugCompressionType>(A->getValue()) |
725 | .Case(S: "zlib" , Value: DebugCompressionType::Zlib) |
726 | .Case(S: "zstd" , Value: DebugCompressionType::Zstd) |
727 | .Default(Value: DebugCompressionType::None); |
728 | if (Config.CompressionType == DebugCompressionType::None) { |
729 | return createStringError( |
730 | errc::invalid_argument, |
731 | "invalid or unsupported --compress-debug-sections format: %s" , |
732 | A->getValue()); |
733 | } |
734 | if (const char *Reason = compression::getReasonIfUnsupported( |
735 | F: compression::formatFor(Type: Config.CompressionType))) |
736 | return createStringError(EC: errc::invalid_argument, Msg: Reason); |
737 | } |
738 | |
739 | for (const auto *A : InputArgs.filtered(OBJCOPY_compress_sections)) { |
740 | SmallVector<StringRef, 0> Fields; |
741 | StringRef(A->getValue()).split(Fields, '='); |
742 | if (Fields.size() != 2 || Fields[1].empty()) { |
743 | return createStringError( |
744 | errc::invalid_argument, |
745 | A->getSpelling() + |
746 | ": parse error, not 'section-glob=[none|zlib|zstd]'" ); |
747 | } |
748 | |
749 | auto Type = StringSwitch<DebugCompressionType>(Fields[1]) |
750 | .Case("zlib" , DebugCompressionType::Zlib) |
751 | .Case("zstd" , DebugCompressionType::Zstd) |
752 | .Default(DebugCompressionType::None); |
753 | if (Type == DebugCompressionType::None && Fields[1] != "none" ) { |
754 | return createStringError( |
755 | errc::invalid_argument, |
756 | "invalid or unsupported --compress-sections format: %s" , |
757 | A->getValue()); |
758 | } |
759 | |
760 | auto &P = Config.compressSections.emplace_back(); |
761 | P.second = Type; |
762 | auto Matcher = |
763 | NameOrPattern::create(Fields[0], SectionMatchStyle, ErrorCallback); |
764 | // =none allows overriding a previous =zlib or =zstd. Reject negative |
765 | // patterns, which would be confusing. |
766 | if (Matcher && !Matcher->isPositiveMatch()) { |
767 | return createStringError( |
768 | errc::invalid_argument, |
769 | "--compress-sections: negative pattern is unsupported" ); |
770 | } |
771 | if (Error E = P.first.addMatcher(std::move(Matcher))) |
772 | return std::move(E); |
773 | } |
774 | |
775 | Config.AddGnuDebugLink = InputArgs.getLastArgValue(Id: OBJCOPY_add_gnu_debuglink); |
776 | // The gnu_debuglink's target is expected to not change or else its CRC would |
777 | // become invalidated and get rejected. We can avoid recalculating the |
778 | // checksum for every target file inside an archive by precomputing the CRC |
779 | // here. This prevents a significant amount of I/O. |
780 | if (!Config.AddGnuDebugLink.empty()) { |
781 | auto DebugOrErr = MemoryBuffer::getFile(Filename: Config.AddGnuDebugLink); |
782 | if (!DebugOrErr) |
783 | return createFileError(F: Config.AddGnuDebugLink, EC: DebugOrErr.getError()); |
784 | auto Debug = std::move(*DebugOrErr); |
785 | Config.GnuDebugLinkCRC32 = |
786 | llvm::crc32(Data: arrayRefFromStringRef(Input: Debug->getBuffer())); |
787 | } |
788 | Config.SplitDWO = InputArgs.getLastArgValue(Id: OBJCOPY_split_dwo); |
789 | |
790 | Config.SymbolsPrefix = InputArgs.getLastArgValue(Id: OBJCOPY_prefix_symbols); |
791 | Config.SymbolsPrefixRemove = |
792 | InputArgs.getLastArgValue(Id: OBJCOPY_remove_symbol_prefix); |
793 | |
794 | Config.AllocSectionsPrefix = |
795 | InputArgs.getLastArgValue(Id: OBJCOPY_prefix_alloc_sections); |
796 | if (auto Arg = InputArgs.getLastArg(OBJCOPY_extract_partition)) |
797 | Config.ExtractPartition = Arg->getValue(); |
798 | |
799 | if (const auto *A = InputArgs.getLastArg(OBJCOPY_gap_fill)) { |
800 | if (Config.OutputFormat != FileFormat::Binary) |
801 | return createStringError( |
802 | EC: errc::invalid_argument, |
803 | Msg: "'--gap-fill' is only supported for binary output" ); |
804 | ErrorOr<uint64_t> Val = getAsInteger<uint64_t>(A->getValue()); |
805 | if (!Val) |
806 | return createStringError(Val.getError(), "--gap-fill: bad number: %s" , |
807 | A->getValue()); |
808 | uint8_t ByteVal = Val.get(); |
809 | if (ByteVal != Val.get()) |
810 | return createStringError(std::errc::value_too_large, |
811 | "gap-fill value %s is out of range (0 to 0xff)" , |
812 | A->getValue()); |
813 | Config.GapFill = ByteVal; |
814 | } |
815 | |
816 | if (const auto *A = InputArgs.getLastArg(OBJCOPY_pad_to)) { |
817 | if (Config.OutputFormat != FileFormat::Binary) |
818 | return createStringError( |
819 | EC: errc::invalid_argument, |
820 | Msg: "'--pad-to' is only supported for binary output" ); |
821 | ErrorOr<uint64_t> Addr = getAsInteger<uint64_t>(A->getValue()); |
822 | if (!Addr) |
823 | return createStringError(Addr.getError(), "--pad-to: bad number: %s" , |
824 | A->getValue()); |
825 | Config.PadTo = *Addr; |
826 | } |
827 | |
828 | for (auto *Arg : InputArgs.filtered(OBJCOPY_redefine_symbol)) { |
829 | if (!StringRef(Arg->getValue()).contains('=')) |
830 | return createStringError(errc::invalid_argument, |
831 | "bad format for --redefine-sym" ); |
832 | auto Old2New = StringRef(Arg->getValue()).split('='); |
833 | if (!Config.SymbolsToRename.insert(Old2New).second) |
834 | return createStringError(errc::invalid_argument, |
835 | "multiple redefinition of symbol '%s'" , |
836 | Old2New.first.str().c_str()); |
837 | } |
838 | |
839 | for (auto *Arg : InputArgs.filtered(OBJCOPY_redefine_symbols)) |
840 | if (Error E = addSymbolsToRenameFromFile(Config.SymbolsToRename, DC.Alloc, |
841 | Arg->getValue())) |
842 | return std::move(E); |
843 | |
844 | for (auto *Arg : InputArgs.filtered(OBJCOPY_rename_section)) { |
845 | Expected<SectionRename> SR = |
846 | parseRenameSectionValue(StringRef(Arg->getValue())); |
847 | if (!SR) |
848 | return SR.takeError(); |
849 | if (!Config.SectionsToRename.try_emplace(SR->OriginalName, *SR).second) |
850 | return createStringError(errc::invalid_argument, |
851 | "multiple renames of section '%s'" , |
852 | SR->OriginalName.str().c_str()); |
853 | } |
854 | for (auto *Arg : InputArgs.filtered(OBJCOPY_set_section_alignment)) { |
855 | Expected<std::pair<StringRef, uint64_t>> NameAndAlign = |
856 | parseSetSectionAttribute("--set-section-alignment" , Arg->getValue()); |
857 | if (!NameAndAlign) |
858 | return NameAndAlign.takeError(); |
859 | Config.SetSectionAlignment[NameAndAlign->first] = NameAndAlign->second; |
860 | } |
861 | for (auto *Arg : InputArgs.filtered(OBJCOPY_set_section_flags)) { |
862 | Expected<SectionFlagsUpdate> SFU = |
863 | parseSetSectionFlagValue(Arg->getValue()); |
864 | if (!SFU) |
865 | return SFU.takeError(); |
866 | if (!Config.SetSectionFlags.try_emplace(SFU->Name, *SFU).second) |
867 | return createStringError( |
868 | errc::invalid_argument, |
869 | "--set-section-flags set multiple times for section '%s'" , |
870 | SFU->Name.str().c_str()); |
871 | } |
872 | for (auto *Arg : InputArgs.filtered(OBJCOPY_set_section_type)) { |
873 | Expected<std::pair<StringRef, uint64_t>> NameAndType = |
874 | parseSetSectionAttribute("--set-section-type" , Arg->getValue()); |
875 | if (!NameAndType) |
876 | return NameAndType.takeError(); |
877 | Config.SetSectionType[NameAndType->first] = NameAndType->second; |
878 | } |
879 | // Prohibit combinations of --set-section-{flags,type} when the section name |
880 | // is used as the destination of a --rename-section. |
881 | for (const auto &E : Config.SectionsToRename) { |
882 | const SectionRename &SR = E.second; |
883 | auto Err = [&](const char *Option) { |
884 | return createStringError( |
885 | EC: errc::invalid_argument, |
886 | Fmt: "--set-section-%s=%s conflicts with --rename-section=%s=%s" , Vals: Option, |
887 | Vals: SR.NewName.str().c_str(), Vals: SR.OriginalName.str().c_str(), |
888 | Vals: SR.NewName.str().c_str()); |
889 | }; |
890 | if (Config.SetSectionFlags.count(Key: SR.NewName)) |
891 | return Err("flags" ); |
892 | if (Config.SetSectionType.count(Key: SR.NewName)) |
893 | return Err("type" ); |
894 | } |
895 | |
896 | for (auto *Arg : InputArgs.filtered(OBJCOPY_remove_section)) |
897 | if (Error E = Config.ToRemove.addMatcher(NameOrPattern::create( |
898 | Arg->getValue(), SectionMatchStyle, ErrorCallback))) |
899 | return std::move(E); |
900 | for (auto *Arg : InputArgs.filtered(OBJCOPY_keep_section)) |
901 | if (Error E = Config.KeepSection.addMatcher(NameOrPattern::create( |
902 | Arg->getValue(), SectionMatchStyle, ErrorCallback))) |
903 | return std::move(E); |
904 | for (auto *Arg : InputArgs.filtered(OBJCOPY_only_section)) |
905 | if (Error E = Config.OnlySection.addMatcher(NameOrPattern::create( |
906 | Arg->getValue(), SectionMatchStyle, ErrorCallback))) |
907 | return std::move(E); |
908 | for (auto *Arg : InputArgs.filtered(OBJCOPY_add_section)) { |
909 | if (Error Err = loadNewSectionData(Arg->getValue(), "--add-section" , |
910 | Config.AddSection)) |
911 | return std::move(Err); |
912 | } |
913 | for (auto *Arg : InputArgs.filtered(OBJCOPY_update_section)) { |
914 | if (Error Err = loadNewSectionData(Arg->getValue(), "--update-section" , |
915 | Config.UpdateSection)) |
916 | return std::move(Err); |
917 | } |
918 | for (auto *Arg : InputArgs.filtered(OBJCOPY_dump_section)) { |
919 | StringRef Value(Arg->getValue()); |
920 | if (Value.split('=').second.empty()) |
921 | return createStringError( |
922 | errc::invalid_argument, |
923 | "bad format for --dump-section, expected section=file" ); |
924 | Config.DumpSection.push_back(Value); |
925 | } |
926 | Config.StripAll = InputArgs.hasArg(OBJCOPY_strip_all); |
927 | Config.StripAllGNU = InputArgs.hasArg(OBJCOPY_strip_all_gnu); |
928 | Config.StripDebug = InputArgs.hasArg(OBJCOPY_strip_debug); |
929 | Config.StripDWO = InputArgs.hasArg(OBJCOPY_strip_dwo); |
930 | Config.StripSections = InputArgs.hasArg(OBJCOPY_strip_sections); |
931 | Config.StripNonAlloc = InputArgs.hasArg(OBJCOPY_strip_non_alloc); |
932 | Config.StripUnneeded = InputArgs.hasArg(OBJCOPY_strip_unneeded); |
933 | Config.ExtractDWO = InputArgs.hasArg(OBJCOPY_extract_dwo); |
934 | Config.ExtractMainPartition = |
935 | InputArgs.hasArg(OBJCOPY_extract_main_partition); |
936 | ELFConfig.LocalizeHidden = InputArgs.hasArg(OBJCOPY_localize_hidden); |
937 | Config.Weaken = InputArgs.hasArg(OBJCOPY_weaken); |
938 | if (auto *Arg = |
939 | InputArgs.getLastArg(OBJCOPY_discard_all, OBJCOPY_discard_locals)) { |
940 | Config.DiscardMode = Arg->getOption().matches(OBJCOPY_discard_all) |
941 | ? DiscardType::All |
942 | : DiscardType::Locals; |
943 | } |
944 | Config.OnlyKeepDebug = InputArgs.hasArg(OBJCOPY_only_keep_debug); |
945 | ELFConfig.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols); |
946 | MachOConfig.KeepUndefined = InputArgs.hasArg(OBJCOPY_keep_undefined); |
947 | Config.DecompressDebugSections = |
948 | InputArgs.hasArg(OBJCOPY_decompress_debug_sections); |
949 | if (Config.DiscardMode == DiscardType::All) { |
950 | Config.StripDebug = true; |
951 | ELFConfig.KeepFileSymbols = true; |
952 | } |
953 | for (auto *Arg : InputArgs.filtered(OBJCOPY_localize_symbol)) |
954 | if (Error E = Config.SymbolsToLocalize.addMatcher(NameOrPattern::create( |
955 | Arg->getValue(), SymbolMatchStyle, ErrorCallback))) |
956 | return std::move(E); |
957 | for (auto *Arg : InputArgs.filtered(OBJCOPY_localize_symbols)) |
958 | if (Error E = addSymbolsFromFile(Config.SymbolsToLocalize, DC.Alloc, |
959 | Arg->getValue(), SymbolMatchStyle, |
960 | ErrorCallback)) |
961 | return std::move(E); |
962 | for (auto *Arg : InputArgs.filtered(OBJCOPY_keep_global_symbol)) |
963 | if (Error E = Config.SymbolsToKeepGlobal.addMatcher(NameOrPattern::create( |
964 | Arg->getValue(), SymbolMatchStyle, ErrorCallback))) |
965 | return std::move(E); |
966 | for (auto *Arg : InputArgs.filtered(OBJCOPY_keep_global_symbols)) |
967 | if (Error E = addSymbolsFromFile(Config.SymbolsToKeepGlobal, DC.Alloc, |
968 | Arg->getValue(), SymbolMatchStyle, |
969 | ErrorCallback)) |
970 | return std::move(E); |
971 | for (auto *Arg : InputArgs.filtered(OBJCOPY_globalize_symbol)) |
972 | if (Error E = Config.SymbolsToGlobalize.addMatcher(NameOrPattern::create( |
973 | Arg->getValue(), SymbolMatchStyle, ErrorCallback))) |
974 | return std::move(E); |
975 | for (auto *Arg : InputArgs.filtered(OBJCOPY_globalize_symbols)) |
976 | if (Error E = addSymbolsFromFile(Config.SymbolsToGlobalize, DC.Alloc, |
977 | Arg->getValue(), SymbolMatchStyle, |
978 | ErrorCallback)) |
979 | return std::move(E); |
980 | for (auto *Arg : InputArgs.filtered(OBJCOPY_weaken_symbol)) |
981 | if (Error E = Config.SymbolsToWeaken.addMatcher(NameOrPattern::create( |
982 | Arg->getValue(), SymbolMatchStyle, ErrorCallback))) |
983 | return std::move(E); |
984 | for (auto *Arg : InputArgs.filtered(OBJCOPY_weaken_symbols)) |
985 | if (Error E = addSymbolsFromFile(Config.SymbolsToWeaken, DC.Alloc, |
986 | Arg->getValue(), SymbolMatchStyle, |
987 | ErrorCallback)) |
988 | return std::move(E); |
989 | for (auto *Arg : InputArgs.filtered(OBJCOPY_strip_symbol)) |
990 | if (Error E = Config.SymbolsToRemove.addMatcher(NameOrPattern::create( |
991 | Arg->getValue(), SymbolMatchStyle, ErrorCallback))) |
992 | return std::move(E); |
993 | for (auto *Arg : InputArgs.filtered(OBJCOPY_strip_symbols)) |
994 | if (Error E = addSymbolsFromFile(Config.SymbolsToRemove, DC.Alloc, |
995 | Arg->getValue(), SymbolMatchStyle, |
996 | ErrorCallback)) |
997 | return std::move(E); |
998 | for (auto *Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbol)) |
999 | if (Error E = |
1000 | Config.UnneededSymbolsToRemove.addMatcher(NameOrPattern::create( |
1001 | Arg->getValue(), SymbolMatchStyle, ErrorCallback))) |
1002 | return std::move(E); |
1003 | for (auto *Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbols)) |
1004 | if (Error E = addSymbolsFromFile(Config.UnneededSymbolsToRemove, DC.Alloc, |
1005 | Arg->getValue(), SymbolMatchStyle, |
1006 | ErrorCallback)) |
1007 | return std::move(E); |
1008 | for (auto *Arg : InputArgs.filtered(OBJCOPY_keep_symbol)) |
1009 | if (Error E = Config.SymbolsToKeep.addMatcher(NameOrPattern::create( |
1010 | Arg->getValue(), SymbolMatchStyle, ErrorCallback))) |
1011 | return std::move(E); |
1012 | for (auto *Arg : InputArgs.filtered(OBJCOPY_keep_symbols)) |
1013 | if (Error E = |
1014 | addSymbolsFromFile(Config.SymbolsToKeep, DC.Alloc, Arg->getValue(), |
1015 | SymbolMatchStyle, ErrorCallback)) |
1016 | return std::move(E); |
1017 | for (auto *Arg : InputArgs.filtered(OBJCOPY_skip_symbol)) |
1018 | if (Error E = Config.SymbolsToSkip.addMatcher(NameOrPattern::create( |
1019 | Arg->getValue(), SymbolMatchStyle, ErrorCallback))) |
1020 | return std::move(E); |
1021 | for (auto *Arg : InputArgs.filtered(OBJCOPY_skip_symbols)) |
1022 | if (Error E = |
1023 | addSymbolsFromFile(Config.SymbolsToSkip, DC.Alloc, Arg->getValue(), |
1024 | SymbolMatchStyle, ErrorCallback)) |
1025 | return std::move(E); |
1026 | for (auto *Arg : InputArgs.filtered(OBJCOPY_add_symbol)) { |
1027 | Expected<NewSymbolInfo> SymInfo = parseNewSymbolInfo(Arg->getValue()); |
1028 | if (!SymInfo) |
1029 | return SymInfo.takeError(); |
1030 | |
1031 | Config.SymbolsToAdd.push_back(*SymInfo); |
1032 | } |
1033 | for (auto *Arg : InputArgs.filtered(OBJCOPY_set_symbol_visibility)) { |
1034 | if (!StringRef(Arg->getValue()).contains('=')) |
1035 | return createStringError(errc::invalid_argument, |
1036 | "bad format for --set-symbol-visibility" ); |
1037 | auto [Sym, Visibility] = StringRef(Arg->getValue()).split('='); |
1038 | Expected<uint8_t> Type = parseVisibilityType(Visibility); |
1039 | if (!Type) |
1040 | return Type.takeError(); |
1041 | ELFConfig.SymbolsToSetVisibility.emplace_back(NameMatcher(), *Type); |
1042 | if (Error E = ELFConfig.SymbolsToSetVisibility.back().first.addMatcher( |
1043 | NameOrPattern::create(Sym, SymbolMatchStyle, ErrorCallback))) |
1044 | return std::move(E); |
1045 | } |
1046 | for (auto *Arg : InputArgs.filtered(OBJCOPY_set_symbols_visibility)) { |
1047 | if (!StringRef(Arg->getValue()).contains('=')) |
1048 | return createStringError(errc::invalid_argument, |
1049 | "bad format for --set-symbols-visibility" ); |
1050 | auto [File, Visibility] = StringRef(Arg->getValue()).split('='); |
1051 | Expected<uint8_t> Type = parseVisibilityType(Visibility); |
1052 | if (!Type) |
1053 | return Type.takeError(); |
1054 | ELFConfig.SymbolsToSetVisibility.emplace_back(NameMatcher(), *Type); |
1055 | if (Error E = |
1056 | addSymbolsFromFile(ELFConfig.SymbolsToSetVisibility.back().first, |
1057 | DC.Alloc, File, SymbolMatchStyle, ErrorCallback)) |
1058 | return std::move(E); |
1059 | } |
1060 | |
1061 | ELFConfig.AllowBrokenLinks = InputArgs.hasArg(OBJCOPY_allow_broken_links); |
1062 | |
1063 | Config.DeterministicArchives = InputArgs.hasFlag( |
1064 | OBJCOPY_enable_deterministic_archives, |
1065 | OBJCOPY_disable_deterministic_archives, /*default=*/true); |
1066 | |
1067 | Config.PreserveDates = InputArgs.hasArg(OBJCOPY_preserve_dates); |
1068 | |
1069 | if (Config.PreserveDates && |
1070 | (Config.OutputFilename == "-" || Config.InputFilename == "-" )) |
1071 | return createStringError(EC: errc::invalid_argument, |
1072 | Msg: "--preserve-dates requires a file" ); |
1073 | |
1074 | for (auto *Arg : InputArgs) |
1075 | if (Arg->getOption().matches(OBJCOPY_set_start)) { |
1076 | auto EAddr = getAsInteger<uint64_t>(Val: Arg->getValue()); |
1077 | if (!EAddr) |
1078 | return createStringError( |
1079 | EC: EAddr.getError(), Fmt: "bad entry point address: '%s'" , Vals: Arg->getValue()); |
1080 | |
1081 | ELFConfig.EntryExpr = [EAddr](uint64_t) { return *EAddr; }; |
1082 | } else if (Arg->getOption().matches(OBJCOPY_change_start)) { |
1083 | auto EIncr = getAsInteger<int64_t>(Val: Arg->getValue()); |
1084 | if (!EIncr) |
1085 | return createStringError(EC: EIncr.getError(), |
1086 | Fmt: "bad entry point increment: '%s'" , |
1087 | Vals: Arg->getValue()); |
1088 | auto Expr = ELFConfig.EntryExpr ? std::move(ELFConfig.EntryExpr) |
1089 | : [](uint64_t A) { return A; }; |
1090 | ELFConfig.EntryExpr = [Expr, EIncr](uint64_t EAddr) { |
1091 | return Expr(EAddr) + *EIncr; |
1092 | }; |
1093 | } |
1094 | |
1095 | if (Config.DecompressDebugSections && |
1096 | Config.CompressionType != DebugCompressionType::None) { |
1097 | return createStringError( |
1098 | EC: errc::invalid_argument, |
1099 | Msg: "cannot specify both --compress-debug-sections and " |
1100 | "--decompress-debug-sections" ); |
1101 | } |
1102 | |
1103 | if (Config.ExtractPartition && Config.ExtractMainPartition) |
1104 | return createStringError(EC: errc::invalid_argument, |
1105 | Msg: "cannot specify --extract-partition together with " |
1106 | "--extract-main-partition" ); |
1107 | |
1108 | DC.CopyConfigs.push_back(Elt: std::move(ConfigMgr)); |
1109 | return std::move(DC); |
1110 | } |
1111 | |
1112 | // parseInstallNameToolOptions returns the config and sets the input arguments. |
1113 | // If a help flag is set then parseInstallNameToolOptions will print the help |
1114 | // messege and exit. |
1115 | Expected<DriverConfig> |
1116 | objcopy::parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr) { |
1117 | DriverConfig DC; |
1118 | ConfigManager ConfigMgr; |
1119 | CommonConfig &Config = ConfigMgr.Common; |
1120 | MachOConfig &MachOConfig = ConfigMgr.MachO; |
1121 | InstallNameToolOptTable T; |
1122 | unsigned MissingArgumentIndex, MissingArgumentCount; |
1123 | llvm::opt::InputArgList InputArgs = |
1124 | T.ParseArgs(Args: ArgsArr, MissingArgIndex&: MissingArgumentIndex, MissingArgCount&: MissingArgumentCount); |
1125 | |
1126 | if (MissingArgumentCount) |
1127 | return createStringError( |
1128 | EC: errc::invalid_argument, |
1129 | S: "missing argument to " + |
1130 | StringRef(InputArgs.getArgString(Index: MissingArgumentIndex)) + |
1131 | " option" ); |
1132 | |
1133 | if (InputArgs.size() == 0) { |
1134 | printHelp(OptTable: T, OS&: errs(), Tool: ToolType::InstallNameTool); |
1135 | exit(status: 1); |
1136 | } |
1137 | |
1138 | if (InputArgs.hasArg(INSTALL_NAME_TOOL_help)) { |
1139 | printHelp(OptTable: T, OS&: outs(), Tool: ToolType::InstallNameTool); |
1140 | exit(status: 0); |
1141 | } |
1142 | |
1143 | if (InputArgs.hasArg(INSTALL_NAME_TOOL_version)) { |
1144 | outs() << "llvm-install-name-tool, compatible with cctools " |
1145 | "install_name_tool\n" ; |
1146 | cl::PrintVersionMessage(); |
1147 | exit(status: 0); |
1148 | } |
1149 | |
1150 | for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_add_rpath)) |
1151 | MachOConfig.RPathToAdd.push_back(Arg->getValue()); |
1152 | |
1153 | for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_prepend_rpath)) |
1154 | MachOConfig.RPathToPrepend.push_back(Arg->getValue()); |
1155 | |
1156 | for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_delete_rpath)) { |
1157 | StringRef RPath = Arg->getValue(); |
1158 | |
1159 | // Cannot add and delete the same rpath at the same time. |
1160 | if (is_contained(MachOConfig.RPathToAdd, RPath)) |
1161 | return createStringError( |
1162 | errc::invalid_argument, |
1163 | "cannot specify both -add_rpath '%s' and -delete_rpath '%s'" , |
1164 | RPath.str().c_str(), RPath.str().c_str()); |
1165 | if (is_contained(MachOConfig.RPathToPrepend, RPath)) |
1166 | return createStringError( |
1167 | errc::invalid_argument, |
1168 | "cannot specify both -prepend_rpath '%s' and -delete_rpath '%s'" , |
1169 | RPath.str().c_str(), RPath.str().c_str()); |
1170 | |
1171 | MachOConfig.RPathsToRemove.insert(RPath); |
1172 | } |
1173 | |
1174 | for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_rpath)) { |
1175 | StringRef Old = Arg->getValue(0); |
1176 | StringRef New = Arg->getValue(1); |
1177 | |
1178 | auto Match = [=](StringRef RPath) { return RPath == Old || RPath == New; }; |
1179 | |
1180 | // Cannot specify duplicate -rpath entries |
1181 | auto It1 = find_if( |
1182 | MachOConfig.RPathsToUpdate, |
1183 | [&Match](const DenseMap<StringRef, StringRef>::value_type &OldNew) { |
1184 | return Match(OldNew.getFirst()) || Match(OldNew.getSecond()); |
1185 | }); |
1186 | if (It1 != MachOConfig.RPathsToUpdate.end()) |
1187 | return createStringError(errc::invalid_argument, |
1188 | "cannot specify both -rpath '" + |
1189 | It1->getFirst() + "' '" + It1->getSecond() + |
1190 | "' and -rpath '" + Old + "' '" + New + "'" ); |
1191 | |
1192 | // Cannot specify the same rpath under both -delete_rpath and -rpath |
1193 | auto It2 = find_if(MachOConfig.RPathsToRemove, Match); |
1194 | if (It2 != MachOConfig.RPathsToRemove.end()) |
1195 | return createStringError(errc::invalid_argument, |
1196 | "cannot specify both -delete_rpath '" + *It2 + |
1197 | "' and -rpath '" + Old + "' '" + New + "'" ); |
1198 | |
1199 | // Cannot specify the same rpath under both -add_rpath and -rpath |
1200 | auto It3 = find_if(MachOConfig.RPathToAdd, Match); |
1201 | if (It3 != MachOConfig.RPathToAdd.end()) |
1202 | return createStringError(errc::invalid_argument, |
1203 | "cannot specify both -add_rpath '" + *It3 + |
1204 | "' and -rpath '" + Old + "' '" + New + "'" ); |
1205 | |
1206 | // Cannot specify the same rpath under both -prepend_rpath and -rpath. |
1207 | auto It4 = find_if(MachOConfig.RPathToPrepend, Match); |
1208 | if (It4 != MachOConfig.RPathToPrepend.end()) |
1209 | return createStringError(errc::invalid_argument, |
1210 | "cannot specify both -prepend_rpath '" + *It4 + |
1211 | "' and -rpath '" + Old + "' '" + New + "'" ); |
1212 | |
1213 | MachOConfig.RPathsToUpdate.insert({Old, New}); |
1214 | } |
1215 | |
1216 | if (auto *Arg = InputArgs.getLastArg(INSTALL_NAME_TOOL_id)) { |
1217 | MachOConfig.SharedLibId = Arg->getValue(); |
1218 | if (MachOConfig.SharedLibId->empty()) |
1219 | return createStringError(EC: errc::invalid_argument, |
1220 | Msg: "cannot specify an empty id" ); |
1221 | } |
1222 | |
1223 | for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_change)) |
1224 | MachOConfig.InstallNamesToUpdate.insert( |
1225 | {Arg->getValue(0), Arg->getValue(1)}); |
1226 | |
1227 | MachOConfig.RemoveAllRpaths = |
1228 | InputArgs.hasArg(INSTALL_NAME_TOOL_delete_all_rpaths); |
1229 | |
1230 | SmallVector<StringRef, 2> Positional; |
1231 | for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_UNKNOWN)) |
1232 | return createStringError(errc::invalid_argument, "unknown argument '%s'" , |
1233 | Arg->getAsString(InputArgs).c_str()); |
1234 | for (auto *Arg : InputArgs.filtered(INSTALL_NAME_TOOL_INPUT)) |
1235 | Positional.push_back(Arg->getValue()); |
1236 | if (Positional.empty()) |
1237 | return createStringError(EC: errc::invalid_argument, Msg: "no input file specified" ); |
1238 | if (Positional.size() > 1) |
1239 | return createStringError( |
1240 | EC: errc::invalid_argument, |
1241 | Msg: "llvm-install-name-tool expects a single input file" ); |
1242 | Config.InputFilename = Positional[0]; |
1243 | Config.OutputFilename = Positional[0]; |
1244 | |
1245 | DC.CopyConfigs.push_back(Elt: std::move(ConfigMgr)); |
1246 | return std::move(DC); |
1247 | } |
1248 | |
1249 | Expected<DriverConfig> |
1250 | objcopy::parseBitcodeStripOptions(ArrayRef<const char *> ArgsArr, |
1251 | function_ref<Error(Error)> ErrorCallback) { |
1252 | DriverConfig DC; |
1253 | ConfigManager ConfigMgr; |
1254 | CommonConfig &Config = ConfigMgr.Common; |
1255 | MachOConfig &MachOConfig = ConfigMgr.MachO; |
1256 | BitcodeStripOptTable T; |
1257 | unsigned MissingArgumentIndex, MissingArgumentCount; |
1258 | opt::InputArgList InputArgs = |
1259 | T.ParseArgs(Args: ArgsArr, MissingArgIndex&: MissingArgumentIndex, MissingArgCount&: MissingArgumentCount); |
1260 | |
1261 | if (InputArgs.size() == 0) { |
1262 | printHelp(OptTable: T, OS&: errs(), Tool: ToolType::BitcodeStrip); |
1263 | exit(status: 1); |
1264 | } |
1265 | |
1266 | if (InputArgs.hasArg(BITCODE_STRIP_help)) { |
1267 | printHelp(OptTable: T, OS&: outs(), Tool: ToolType::BitcodeStrip); |
1268 | exit(status: 0); |
1269 | } |
1270 | |
1271 | if (InputArgs.hasArg(BITCODE_STRIP_version)) { |
1272 | outs() << "llvm-bitcode-strip, compatible with cctools " |
1273 | "bitcode_strip\n" ; |
1274 | cl::PrintVersionMessage(); |
1275 | exit(status: 0); |
1276 | } |
1277 | |
1278 | for (auto *Arg : InputArgs.filtered(BITCODE_STRIP_UNKNOWN)) |
1279 | return createStringError(errc::invalid_argument, "unknown argument '%s'" , |
1280 | Arg->getAsString(InputArgs).c_str()); |
1281 | |
1282 | SmallVector<StringRef, 2> Positional; |
1283 | for (auto *Arg : InputArgs.filtered(BITCODE_STRIP_INPUT)) |
1284 | Positional.push_back(Arg->getValue()); |
1285 | if (Positional.size() > 1) |
1286 | return createStringError(EC: errc::invalid_argument, |
1287 | Msg: "llvm-bitcode-strip expects a single input file" ); |
1288 | assert(!Positional.empty()); |
1289 | Config.InputFilename = Positional[0]; |
1290 | |
1291 | if (!InputArgs.hasArg(BITCODE_STRIP_output)) { |
1292 | return createStringError(EC: errc::invalid_argument, |
1293 | Msg: "-o is a required argument" ); |
1294 | } |
1295 | Config.OutputFilename = InputArgs.getLastArgValue(BITCODE_STRIP_output); |
1296 | |
1297 | if (!InputArgs.hasArg(BITCODE_STRIP_remove)) |
1298 | return createStringError(EC: errc::invalid_argument, Msg: "no action specified" ); |
1299 | |
1300 | // We only support -r for now, which removes all bitcode sections and |
1301 | // the __LLVM segment if it's now empty. |
1302 | cantFail(Err: Config.ToRemove.addMatcher(Matcher: NameOrPattern::create( |
1303 | Pattern: "__LLVM,__asm" , MS: MatchStyle::Literal, ErrorCallback))); |
1304 | cantFail(Err: Config.ToRemove.addMatcher(Matcher: NameOrPattern::create( |
1305 | Pattern: "__LLVM,__bitcode" , MS: MatchStyle::Literal, ErrorCallback))); |
1306 | cantFail(Err: Config.ToRemove.addMatcher(Matcher: NameOrPattern::create( |
1307 | Pattern: "__LLVM,__bundle" , MS: MatchStyle::Literal, ErrorCallback))); |
1308 | cantFail(Err: Config.ToRemove.addMatcher(Matcher: NameOrPattern::create( |
1309 | Pattern: "__LLVM,__cmdline" , MS: MatchStyle::Literal, ErrorCallback))); |
1310 | cantFail(Err: Config.ToRemove.addMatcher(Matcher: NameOrPattern::create( |
1311 | Pattern: "__LLVM,__swift_cmdline" , MS: MatchStyle::Literal, ErrorCallback))); |
1312 | MachOConfig.EmptySegmentsToRemove.insert(V: "__LLVM" ); |
1313 | |
1314 | DC.CopyConfigs.push_back(Elt: std::move(ConfigMgr)); |
1315 | return std::move(DC); |
1316 | } |
1317 | |
1318 | // parseStripOptions returns the config and sets the input arguments. If a |
1319 | // help flag is set then parseStripOptions will print the help messege and |
1320 | // exit. |
1321 | Expected<DriverConfig> |
1322 | objcopy::parseStripOptions(ArrayRef<const char *> RawArgsArr, |
1323 | function_ref<Error(Error)> ErrorCallback) { |
1324 | const char *const *DashDash = |
1325 | llvm::find_if(Range&: RawArgsArr, P: [](StringRef Str) { return Str == "--" ; }); |
1326 | ArrayRef<const char *> ArgsArr = ArrayRef(RawArgsArr.begin(), DashDash); |
1327 | if (DashDash != RawArgsArr.end()) |
1328 | DashDash = std::next(x: DashDash); |
1329 | |
1330 | StripOptTable T; |
1331 | unsigned MissingArgumentIndex, MissingArgumentCount; |
1332 | llvm::opt::InputArgList InputArgs = |
1333 | T.ParseArgs(Args: ArgsArr, MissingArgIndex&: MissingArgumentIndex, MissingArgCount&: MissingArgumentCount); |
1334 | |
1335 | if (InputArgs.size() == 0 && DashDash == RawArgsArr.end()) { |
1336 | printHelp(OptTable: T, OS&: errs(), Tool: ToolType::Strip); |
1337 | exit(status: 1); |
1338 | } |
1339 | |
1340 | if (InputArgs.hasArg(STRIP_help)) { |
1341 | printHelp(OptTable: T, OS&: outs(), Tool: ToolType::Strip); |
1342 | exit(status: 0); |
1343 | } |
1344 | |
1345 | if (InputArgs.hasArg(STRIP_version)) { |
1346 | outs() << "llvm-strip, compatible with GNU strip\n" ; |
1347 | cl::PrintVersionMessage(); |
1348 | exit(status: 0); |
1349 | } |
1350 | |
1351 | SmallVector<StringRef, 2> Positional; |
1352 | for (auto *Arg : InputArgs.filtered(STRIP_UNKNOWN)) |
1353 | return createStringError(errc::invalid_argument, "unknown argument '%s'" , |
1354 | Arg->getAsString(InputArgs).c_str()); |
1355 | for (auto *Arg : InputArgs.filtered(STRIP_INPUT)) |
1356 | Positional.push_back(Arg->getValue()); |
1357 | std::copy(first: DashDash, last: RawArgsArr.end(), result: std::back_inserter(x&: Positional)); |
1358 | |
1359 | if (Positional.empty()) |
1360 | return createStringError(EC: errc::invalid_argument, Msg: "no input file specified" ); |
1361 | |
1362 | if (Positional.size() > 1 && InputArgs.hasArg(STRIP_output)) |
1363 | return createStringError( |
1364 | EC: errc::invalid_argument, |
1365 | Msg: "multiple input files cannot be used in combination with -o" ); |
1366 | |
1367 | ConfigManager ConfigMgr; |
1368 | CommonConfig &Config = ConfigMgr.Common; |
1369 | ELFConfig &ELFConfig = ConfigMgr.ELF; |
1370 | MachOConfig &MachOConfig = ConfigMgr.MachO; |
1371 | |
1372 | if (InputArgs.hasArg(STRIP_regex) && InputArgs.hasArg(STRIP_wildcard)) |
1373 | return createStringError(EC: errc::invalid_argument, |
1374 | Msg: "--regex and --wildcard are incompatible" ); |
1375 | MatchStyle SectionMatchStyle = |
1376 | InputArgs.hasArg(STRIP_regex) ? MatchStyle::Regex : MatchStyle::Wildcard; |
1377 | MatchStyle SymbolMatchStyle |
1378 | = InputArgs.hasArg(STRIP_regex) ? MatchStyle::Regex |
1379 | : InputArgs.hasArg(STRIP_wildcard) ? MatchStyle::Wildcard |
1380 | : MatchStyle::Literal; |
1381 | ELFConfig.AllowBrokenLinks = InputArgs.hasArg(STRIP_allow_broken_links); |
1382 | Config.StripDebug = InputArgs.hasArg(STRIP_strip_debug); |
1383 | |
1384 | if (auto *Arg = InputArgs.getLastArg(STRIP_discard_all, STRIP_discard_locals)) |
1385 | Config.DiscardMode = Arg->getOption().matches(STRIP_discard_all) |
1386 | ? DiscardType::All |
1387 | : DiscardType::Locals; |
1388 | Config.StripSections = InputArgs.hasArg(STRIP_strip_sections); |
1389 | Config.StripUnneeded = InputArgs.hasArg(STRIP_strip_unneeded); |
1390 | if (auto Arg = InputArgs.getLastArg(STRIP_strip_all, STRIP_no_strip_all)) |
1391 | Config.StripAll = Arg->getOption().getID() == STRIP_strip_all; |
1392 | Config.StripAllGNU = InputArgs.hasArg(STRIP_strip_all_gnu); |
1393 | MachOConfig.StripSwiftSymbols = InputArgs.hasArg(STRIP_strip_swift_symbols); |
1394 | Config.OnlyKeepDebug = InputArgs.hasArg(STRIP_only_keep_debug); |
1395 | ELFConfig.KeepFileSymbols = InputArgs.hasArg(STRIP_keep_file_symbols); |
1396 | MachOConfig.KeepUndefined = InputArgs.hasArg(STRIP_keep_undefined); |
1397 | |
1398 | for (auto *Arg : InputArgs.filtered(STRIP_keep_section)) |
1399 | if (Error E = Config.KeepSection.addMatcher(NameOrPattern::create( |
1400 | Arg->getValue(), SectionMatchStyle, ErrorCallback))) |
1401 | return std::move(E); |
1402 | |
1403 | for (auto *Arg : InputArgs.filtered(STRIP_remove_section)) |
1404 | if (Error E = Config.ToRemove.addMatcher(NameOrPattern::create( |
1405 | Arg->getValue(), SectionMatchStyle, ErrorCallback))) |
1406 | return std::move(E); |
1407 | |
1408 | for (auto *Arg : InputArgs.filtered(STRIP_strip_symbol)) |
1409 | if (Error E = Config.SymbolsToRemove.addMatcher(NameOrPattern::create( |
1410 | Arg->getValue(), SymbolMatchStyle, ErrorCallback))) |
1411 | return std::move(E); |
1412 | |
1413 | for (auto *Arg : InputArgs.filtered(STRIP_keep_symbol)) |
1414 | if (Error E = Config.SymbolsToKeep.addMatcher(NameOrPattern::create( |
1415 | Arg->getValue(), SymbolMatchStyle, ErrorCallback))) |
1416 | return std::move(E); |
1417 | |
1418 | if (!InputArgs.hasArg(STRIP_no_strip_all) && !Config.StripDebug && |
1419 | !Config.OnlyKeepDebug && !Config.StripUnneeded && |
1420 | Config.DiscardMode == DiscardType::None && !Config.StripAllGNU && |
1421 | Config.SymbolsToRemove.empty()) |
1422 | Config.StripAll = true; |
1423 | |
1424 | if (Config.DiscardMode == DiscardType::All) { |
1425 | Config.StripDebug = true; |
1426 | ELFConfig.KeepFileSymbols = true; |
1427 | } |
1428 | |
1429 | Config.DeterministicArchives = |
1430 | InputArgs.hasFlag(STRIP_enable_deterministic_archives, |
1431 | STRIP_disable_deterministic_archives, /*default=*/true); |
1432 | |
1433 | Config.PreserveDates = InputArgs.hasArg(STRIP_preserve_dates); |
1434 | Config.InputFormat = FileFormat::Unspecified; |
1435 | Config.OutputFormat = FileFormat::Unspecified; |
1436 | |
1437 | DriverConfig DC; |
1438 | if (Positional.size() == 1) { |
1439 | Config.InputFilename = Positional[0]; |
1440 | Config.OutputFilename = |
1441 | InputArgs.getLastArgValue(STRIP_output, Positional[0]); |
1442 | DC.CopyConfigs.push_back(Elt: std::move(ConfigMgr)); |
1443 | } else { |
1444 | StringMap<unsigned> InputFiles; |
1445 | for (StringRef Filename : Positional) { |
1446 | if (InputFiles[Filename]++ == 1) { |
1447 | if (Filename == "-" ) |
1448 | return createStringError( |
1449 | EC: errc::invalid_argument, |
1450 | Msg: "cannot specify '-' as an input file more than once" ); |
1451 | if (Error E = ErrorCallback(createStringError( |
1452 | EC: errc::invalid_argument, Fmt: "'%s' was already specified" , |
1453 | Vals: Filename.str().c_str()))) |
1454 | return std::move(E); |
1455 | } |
1456 | Config.InputFilename = Filename; |
1457 | Config.OutputFilename = Filename; |
1458 | DC.CopyConfigs.push_back(Elt: ConfigMgr); |
1459 | } |
1460 | } |
1461 | |
1462 | if (Config.PreserveDates && (is_contained(Positional, "-" ) || |
1463 | InputArgs.getLastArgValue(STRIP_output) == "-" )) |
1464 | return createStringError(EC: errc::invalid_argument, |
1465 | Msg: "--preserve-dates requires a file" ); |
1466 | |
1467 | return std::move(DC); |
1468 | } |
1469 | |