blob: 24f0e29d4926f37f07a8b6ba1816d2a6d8f54292 [file] [log] [blame]
Alexander Shaposhnikov8d0b74c2018-10-11 22:33:50 +00001//===- CopyConfig.cpp -----------------------------------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "CopyConfig.h"
11#include "llvm-objcopy.h"
12
13#include "llvm/ADT/BitmaskEnum.h"
14#include "llvm/ADT/Optional.h"
15#include "llvm/ADT/SmallVector.h"
16#include "llvm/ADT/StringRef.h"
17#include "llvm/Object/ELFTypes.h"
18#include "llvm/Option/Arg.h"
19#include "llvm/Option/ArgList.h"
20#include "llvm/Support/CommandLine.h"
21#include "llvm/Support/Compression.h"
22#include "llvm/Support/MemoryBuffer.h"
23#include <memory>
24#include <string>
25
26namespace llvm {
27namespace objcopy {
28
29namespace {
30enum ObjcopyID {
31 OBJCOPY_INVALID = 0, // This is not an option ID.
32#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
33 HELPTEXT, METAVAR, VALUES) \
34 OBJCOPY_##ID,
35#include "ObjcopyOpts.inc"
36#undef OPTION
37};
38
39#define PREFIX(NAME, VALUE) const char *const OBJCOPY_##NAME[] = VALUE;
40#include "ObjcopyOpts.inc"
41#undef PREFIX
42
43static const opt::OptTable::Info ObjcopyInfoTable[] = {
44#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
45 HELPTEXT, METAVAR, VALUES) \
46 {OBJCOPY_##PREFIX, \
47 NAME, \
48 HELPTEXT, \
49 METAVAR, \
50 OBJCOPY_##ID, \
51 opt::Option::KIND##Class, \
52 PARAM, \
53 FLAGS, \
54 OBJCOPY_##GROUP, \
55 OBJCOPY_##ALIAS, \
56 ALIASARGS, \
57 VALUES},
58#include "ObjcopyOpts.inc"
59#undef OPTION
60};
61
62class ObjcopyOptTable : public opt::OptTable {
63public:
Jordan Rupprechtaaeaa0a2018-10-23 18:46:33 +000064 ObjcopyOptTable() : OptTable(ObjcopyInfoTable) {}
Alexander Shaposhnikov8d0b74c2018-10-11 22:33:50 +000065};
66
67enum StripID {
68 STRIP_INVALID = 0, // This is not an option ID.
69#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
70 HELPTEXT, METAVAR, VALUES) \
71 STRIP_##ID,
72#include "StripOpts.inc"
73#undef OPTION
74};
75
76#define PREFIX(NAME, VALUE) const char *const STRIP_##NAME[] = VALUE;
77#include "StripOpts.inc"
78#undef PREFIX
79
80static const opt::OptTable::Info StripInfoTable[] = {
81#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
82 HELPTEXT, METAVAR, VALUES) \
83 {STRIP_##PREFIX, NAME, HELPTEXT, \
84 METAVAR, STRIP_##ID, opt::Option::KIND##Class, \
85 PARAM, FLAGS, STRIP_##GROUP, \
86 STRIP_##ALIAS, ALIASARGS, VALUES},
87#include "StripOpts.inc"
88#undef OPTION
89};
90
91class StripOptTable : public opt::OptTable {
92public:
Jordan Rupprechtaaeaa0a2018-10-23 18:46:33 +000093 StripOptTable() : OptTable(StripInfoTable) {}
Alexander Shaposhnikov8d0b74c2018-10-11 22:33:50 +000094};
95
96enum SectionFlag {
97 SecNone = 0,
98 SecAlloc = 1 << 0,
99 SecLoad = 1 << 1,
100 SecNoload = 1 << 2,
101 SecReadonly = 1 << 3,
102 SecDebug = 1 << 4,
103 SecCode = 1 << 5,
104 SecData = 1 << 6,
105 SecRom = 1 << 7,
106 SecMerge = 1 << 8,
107 SecStrings = 1 << 9,
108 SecContents = 1 << 10,
109 SecShare = 1 << 11,
110 LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ SecShare)
111};
112
113} // namespace
114
115static SectionFlag parseSectionRenameFlag(StringRef SectionName) {
116 return llvm::StringSwitch<SectionFlag>(SectionName)
117 .Case("alloc", SectionFlag::SecAlloc)
118 .Case("load", SectionFlag::SecLoad)
119 .Case("noload", SectionFlag::SecNoload)
120 .Case("readonly", SectionFlag::SecReadonly)
121 .Case("debug", SectionFlag::SecDebug)
122 .Case("code", SectionFlag::SecCode)
123 .Case("data", SectionFlag::SecData)
124 .Case("rom", SectionFlag::SecRom)
125 .Case("merge", SectionFlag::SecMerge)
126 .Case("strings", SectionFlag::SecStrings)
127 .Case("contents", SectionFlag::SecContents)
128 .Case("share", SectionFlag::SecShare)
129 .Default(SectionFlag::SecNone);
130}
131
132static SectionRename parseRenameSectionValue(StringRef FlagValue) {
133 if (!FlagValue.contains('='))
134 error("Bad format for --rename-section: missing '='");
135
136 // Initial split: ".foo" = ".bar,f1,f2,..."
137 auto Old2New = FlagValue.split('=');
138 SectionRename SR;
139 SR.OriginalName = Old2New.first;
140
141 // Flags split: ".bar" "f1" "f2" ...
142 SmallVector<StringRef, 6> NameAndFlags;
143 Old2New.second.split(NameAndFlags, ',');
144 SR.NewName = NameAndFlags[0];
145
146 if (NameAndFlags.size() > 1) {
147 SectionFlag Flags = SectionFlag::SecNone;
148 for (size_t I = 1, Size = NameAndFlags.size(); I < Size; ++I) {
149 SectionFlag Flag = parseSectionRenameFlag(NameAndFlags[I]);
150 if (Flag == SectionFlag::SecNone)
151 error("Unrecognized section flag '" + NameAndFlags[I] +
152 "'. Flags supported for GNU compatibility: alloc, load, noload, "
153 "readonly, debug, code, data, rom, share, contents, merge, "
154 "strings.");
155 Flags |= Flag;
156 }
157
158 SR.NewFlags = 0;
159 if (Flags & SectionFlag::SecAlloc)
160 *SR.NewFlags |= ELF::SHF_ALLOC;
161 if (!(Flags & SectionFlag::SecReadonly))
162 *SR.NewFlags |= ELF::SHF_WRITE;
163 if (Flags & SectionFlag::SecCode)
164 *SR.NewFlags |= ELF::SHF_EXECINSTR;
165 if (Flags & SectionFlag::SecMerge)
166 *SR.NewFlags |= ELF::SHF_MERGE;
167 if (Flags & SectionFlag::SecStrings)
168 *SR.NewFlags |= ELF::SHF_STRINGS;
169 }
170
171 return SR;
172}
173
174static const StringMap<MachineInfo> ArchMap{
175 // Name, {EMachine, 64bit, LittleEndian}
176 {"aarch64", {ELF::EM_AARCH64, true, true}},
177 {"arm", {ELF::EM_ARM, false, true}},
178 {"i386", {ELF::EM_386, false, true}},
179 {"i386:x86-64", {ELF::EM_X86_64, true, true}},
180 {"powerpc:common64", {ELF::EM_PPC64, true, true}},
181 {"sparc", {ELF::EM_SPARC, false, true}},
182 {"x86-64", {ELF::EM_X86_64, true, true}},
183};
184
185static const MachineInfo &getMachineInfo(StringRef Arch) {
186 auto Iter = ArchMap.find(Arch);
187 if (Iter == std::end(ArchMap))
188 error("Invalid architecture: '" + Arch + "'");
189 return Iter->getValue();
190}
191
192static void addGlobalSymbolsFromFile(std::vector<std::string> &Symbols,
193 StringRef Filename) {
194 SmallVector<StringRef, 16> Lines;
195 auto BufOrErr = MemoryBuffer::getFile(Filename);
196 if (!BufOrErr)
197 reportError(Filename, BufOrErr.getError());
198
199 BufOrErr.get()->getBuffer().split(Lines, '\n');
200 for (StringRef Line : Lines) {
201 // Ignore everything after '#', trim whitespace, and only add the symbol if
202 // it's not empty.
203 auto TrimmedLine = Line.split('#').first.trim();
204 if (!TrimmedLine.empty())
205 Symbols.push_back(TrimmedLine.str());
206 }
207}
208
209// ParseObjcopyOptions returns the config and sets the input arguments. If a
210// help flag is set then ParseObjcopyOptions will print the help messege and
211// exit.
212DriverConfig parseObjcopyOptions(ArrayRef<const char *> ArgsArr) {
213 ObjcopyOptTable T;
214 unsigned MissingArgumentIndex, MissingArgumentCount;
215 llvm::opt::InputArgList InputArgs =
216 T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
217
218 if (InputArgs.size() == 0) {
219 T.PrintHelp(errs(), "llvm-objcopy input [output]", "objcopy tool");
220 exit(1);
221 }
222
223 if (InputArgs.hasArg(OBJCOPY_help)) {
224 T.PrintHelp(outs(), "llvm-objcopy input [output]", "objcopy tool");
225 exit(0);
226 }
227
228 if (InputArgs.hasArg(OBJCOPY_version)) {
Martin Storsjoe9af7152018-11-28 06:51:50 +0000229 outs() << "llvm-objcopy, compatible with GNU objcopy\n";
Alexander Shaposhnikov8d0b74c2018-10-11 22:33:50 +0000230 cl::PrintVersionMessage();
231 exit(0);
232 }
233
234 SmallVector<const char *, 2> Positional;
235
236 for (auto Arg : InputArgs.filtered(OBJCOPY_UNKNOWN))
237 error("unknown argument '" + Arg->getAsString(InputArgs) + "'");
238
239 for (auto Arg : InputArgs.filtered(OBJCOPY_INPUT))
240 Positional.push_back(Arg->getValue());
241
242 if (Positional.empty())
243 error("No input file specified");
244
245 if (Positional.size() > 2)
246 error("Too many positional arguments");
247
248 CopyConfig Config;
249 Config.InputFilename = Positional[0];
250 Config.OutputFilename = Positional[Positional.size() == 1 ? 0 : 1];
Jordan Rupprechtbb4588e2018-10-12 00:36:01 +0000251 if (InputArgs.hasArg(OBJCOPY_target) &&
252 (InputArgs.hasArg(OBJCOPY_input_target) ||
253 InputArgs.hasArg(OBJCOPY_output_target)))
254 error("--target cannot be used with --input-target or --output-target");
255
256 if (InputArgs.hasArg(OBJCOPY_target)) {
257 Config.InputFormat = InputArgs.getLastArgValue(OBJCOPY_target);
258 Config.OutputFormat = InputArgs.getLastArgValue(OBJCOPY_target);
259 } else {
260 Config.InputFormat = InputArgs.getLastArgValue(OBJCOPY_input_target);
261 Config.OutputFormat = InputArgs.getLastArgValue(OBJCOPY_output_target);
262 }
Alexander Shaposhnikov8d0b74c2018-10-11 22:33:50 +0000263 if (Config.InputFormat == "binary") {
264 auto BinaryArch = InputArgs.getLastArgValue(OBJCOPY_binary_architecture);
265 if (BinaryArch.empty())
266 error("Specified binary input without specifiying an architecture");
267 Config.BinaryArch = getMachineInfo(BinaryArch);
268 }
269
270 if (auto Arg = InputArgs.getLastArg(OBJCOPY_compress_debug_sections,
271 OBJCOPY_compress_debug_sections_eq)) {
272 Config.CompressionType = DebugCompressionType::Z;
273
274 if (Arg->getOption().getID() == OBJCOPY_compress_debug_sections_eq) {
275 Config.CompressionType =
276 StringSwitch<DebugCompressionType>(
277 InputArgs.getLastArgValue(OBJCOPY_compress_debug_sections_eq))
278 .Case("zlib-gnu", DebugCompressionType::GNU)
279 .Case("zlib", DebugCompressionType::Z)
280 .Default(DebugCompressionType::None);
281 if (Config.CompressionType == DebugCompressionType::None)
282 error("Invalid or unsupported --compress-debug-sections format: " +
283 InputArgs.getLastArgValue(OBJCOPY_compress_debug_sections_eq));
284 if (!zlib::isAvailable())
285 error("LLVM was not compiled with LLVM_ENABLE_ZLIB: can not compress.");
286 }
287 }
288
Alexander Shaposhnikov8d0b74c2018-10-11 22:33:50 +0000289 Config.AddGnuDebugLink = InputArgs.getLastArgValue(OBJCOPY_add_gnu_debuglink);
Jake Ehrlich8ad77792018-12-03 19:49:23 +0000290 Config.BuildIdLinkDir = InputArgs.getLastArgValue(OBJCOPY_build_id_link_dir);
291 if (InputArgs.hasArg(OBJCOPY_build_id_link_input))
292 Config.BuildIdLinkInput =
293 InputArgs.getLastArgValue(OBJCOPY_build_id_link_input);
294 if (InputArgs.hasArg(OBJCOPY_build_id_link_output))
295 Config.BuildIdLinkOutput =
296 InputArgs.getLastArgValue(OBJCOPY_build_id_link_output);
297 Config.SplitDWO = InputArgs.getLastArgValue(OBJCOPY_split_dwo);
Alexander Shaposhnikov8d0b74c2018-10-11 22:33:50 +0000298 Config.SymbolsPrefix = InputArgs.getLastArgValue(OBJCOPY_prefix_symbols);
299
300 for (auto Arg : InputArgs.filtered(OBJCOPY_redefine_symbol)) {
301 if (!StringRef(Arg->getValue()).contains('='))
302 error("Bad format for --redefine-sym");
303 auto Old2New = StringRef(Arg->getValue()).split('=');
304 if (!Config.SymbolsToRename.insert(Old2New).second)
305 error("Multiple redefinition of symbol " + Old2New.first);
306 }
307
308 for (auto Arg : InputArgs.filtered(OBJCOPY_rename_section)) {
309 SectionRename SR = parseRenameSectionValue(StringRef(Arg->getValue()));
310 if (!Config.SectionsToRename.try_emplace(SR.OriginalName, SR).second)
311 error("Multiple renames of section " + SR.OriginalName);
312 }
313
314 for (auto Arg : InputArgs.filtered(OBJCOPY_remove_section))
315 Config.ToRemove.push_back(Arg->getValue());
Jordan Rupprechtc5bae782018-11-13 19:32:27 +0000316 for (auto Arg : InputArgs.filtered(OBJCOPY_keep_section))
317 Config.KeepSection.push_back(Arg->getValue());
Jake Ehrlich85985ed2018-12-06 02:03:53 +0000318 for (auto Arg : InputArgs.filtered(OBJCOPY_only_section))
319 Config.OnlySection.push_back(Arg->getValue());
Alexander Shaposhnikov8d0b74c2018-10-11 22:33:50 +0000320 for (auto Arg : InputArgs.filtered(OBJCOPY_add_section))
321 Config.AddSection.push_back(Arg->getValue());
322 for (auto Arg : InputArgs.filtered(OBJCOPY_dump_section))
323 Config.DumpSection.push_back(Arg->getValue());
324 Config.StripAll = InputArgs.hasArg(OBJCOPY_strip_all);
325 Config.StripAllGNU = InputArgs.hasArg(OBJCOPY_strip_all_gnu);
326 Config.StripDebug = InputArgs.hasArg(OBJCOPY_strip_debug);
327 Config.StripDWO = InputArgs.hasArg(OBJCOPY_strip_dwo);
328 Config.StripSections = InputArgs.hasArg(OBJCOPY_strip_sections);
329 Config.StripNonAlloc = InputArgs.hasArg(OBJCOPY_strip_non_alloc);
330 Config.StripUnneeded = InputArgs.hasArg(OBJCOPY_strip_unneeded);
331 Config.ExtractDWO = InputArgs.hasArg(OBJCOPY_extract_dwo);
332 Config.LocalizeHidden = InputArgs.hasArg(OBJCOPY_localize_hidden);
333 Config.Weaken = InputArgs.hasArg(OBJCOPY_weaken);
334 Config.DiscardAll = InputArgs.hasArg(OBJCOPY_discard_all);
335 Config.OnlyKeepDebug = InputArgs.hasArg(OBJCOPY_only_keep_debug);
336 Config.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols);
337 Config.DecompressDebugSections =
338 InputArgs.hasArg(OBJCOPY_decompress_debug_sections);
339 for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbol))
340 Config.SymbolsToLocalize.push_back(Arg->getValue());
341 for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbol))
342 Config.SymbolsToKeepGlobal.push_back(Arg->getValue());
343 for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbols))
344 addGlobalSymbolsFromFile(Config.SymbolsToKeepGlobal, Arg->getValue());
345 for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbol))
346 Config.SymbolsToGlobalize.push_back(Arg->getValue());
347 for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbol))
348 Config.SymbolsToWeaken.push_back(Arg->getValue());
349 for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbol))
350 Config.SymbolsToRemove.push_back(Arg->getValue());
351 for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbol))
352 Config.SymbolsToKeep.push_back(Arg->getValue());
353
Jordan Rupprechtfc780bb2018-11-01 17:36:37 +0000354 Config.DeterministicArchives = InputArgs.hasFlag(
355 OBJCOPY_enable_deterministic_archives,
356 OBJCOPY_disable_deterministic_archives, /*default=*/true);
357
Alexander Shaposhnikov8d0b74c2018-10-11 22:33:50 +0000358 Config.PreserveDates = InputArgs.hasArg(OBJCOPY_preserve_dates);
359
Alexander Shaposhnikov8d0b74c2018-10-11 22:33:50 +0000360 if (Config.DecompressDebugSections &&
361 Config.CompressionType != DebugCompressionType::None) {
362 error("Cannot specify --compress-debug-sections at the same time as "
363 "--decompress-debug-sections at the same time");
364 }
365
366 if (Config.DecompressDebugSections && !zlib::isAvailable())
367 error("LLVM was not compiled with LLVM_ENABLE_ZLIB: cannot decompress.");
368
Jordan Rupprechtab9f6622018-10-23 20:54:51 +0000369 DriverConfig DC;
370 DC.CopyConfigs.push_back(std::move(Config));
Alexander Shaposhnikov8d0b74c2018-10-11 22:33:50 +0000371 return DC;
372}
373
374// ParseStripOptions returns the config and sets the input arguments. If a
375// help flag is set then ParseStripOptions will print the help messege and
376// exit.
377DriverConfig parseStripOptions(ArrayRef<const char *> ArgsArr) {
378 StripOptTable T;
379 unsigned MissingArgumentIndex, MissingArgumentCount;
380 llvm::opt::InputArgList InputArgs =
381 T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
382
383 if (InputArgs.size() == 0) {
384 T.PrintHelp(errs(), "llvm-strip [options] file...", "strip tool");
385 exit(1);
386 }
387
388 if (InputArgs.hasArg(STRIP_help)) {
389 T.PrintHelp(outs(), "llvm-strip [options] file...", "strip tool");
390 exit(0);
391 }
392
393 if (InputArgs.hasArg(STRIP_version)) {
Martin Storsjoe9af7152018-11-28 06:51:50 +0000394 outs() << "llvm-strip, compatible with GNU strip\n";
Alexander Shaposhnikov8d0b74c2018-10-11 22:33:50 +0000395 cl::PrintVersionMessage();
396 exit(0);
397 }
398
399 SmallVector<const char *, 2> Positional;
400 for (auto Arg : InputArgs.filtered(STRIP_UNKNOWN))
401 error("unknown argument '" + Arg->getAsString(InputArgs) + "'");
402 for (auto Arg : InputArgs.filtered(STRIP_INPUT))
403 Positional.push_back(Arg->getValue());
404
405 if (Positional.empty())
406 error("No input file specified");
407
408 if (Positional.size() > 1 && InputArgs.hasArg(STRIP_output))
409 error("Multiple input files cannot be used in combination with -o");
410
411 CopyConfig Config;
412 Config.StripDebug = InputArgs.hasArg(STRIP_strip_debug);
413
414 Config.DiscardAll = InputArgs.hasArg(STRIP_discard_all);
415 Config.StripUnneeded = InputArgs.hasArg(STRIP_strip_unneeded);
416 Config.StripAll = InputArgs.hasArg(STRIP_strip_all);
Jordan Rupprecht30d1b192018-11-01 17:48:46 +0000417 Config.StripAllGNU = InputArgs.hasArg(STRIP_strip_all_gnu);
Alexander Shaposhnikov8d0b74c2018-10-11 22:33:50 +0000418
Jordan Rupprecht30d1b192018-11-01 17:48:46 +0000419 if (!Config.StripDebug && !Config.StripUnneeded && !Config.DiscardAll &&
420 !Config.StripAllGNU)
Alexander Shaposhnikov8d0b74c2018-10-11 22:33:50 +0000421 Config.StripAll = true;
422
Jordan Rupprechtc5bae782018-11-13 19:32:27 +0000423 for (auto Arg : InputArgs.filtered(STRIP_keep_section))
424 Config.KeepSection.push_back(Arg->getValue());
Jordan Rupprecht30d1b192018-11-01 17:48:46 +0000425
Alexander Shaposhnikov8d0b74c2018-10-11 22:33:50 +0000426 for (auto Arg : InputArgs.filtered(STRIP_remove_section))
427 Config.ToRemove.push_back(Arg->getValue());
428
429 for (auto Arg : InputArgs.filtered(STRIP_keep_symbol))
430 Config.SymbolsToKeep.push_back(Arg->getValue());
431
Jordan Rupprechtfc780bb2018-11-01 17:36:37 +0000432 Config.DeterministicArchives =
433 InputArgs.hasFlag(STRIP_enable_deterministic_archives,
434 STRIP_disable_deterministic_archives, /*default=*/true);
435
Alexander Shaposhnikov8d0b74c2018-10-11 22:33:50 +0000436 Config.PreserveDates = InputArgs.hasArg(STRIP_preserve_dates);
437
438 DriverConfig DC;
439 if (Positional.size() == 1) {
440 Config.InputFilename = Positional[0];
441 Config.OutputFilename =
442 InputArgs.getLastArgValue(STRIP_output, Positional[0]);
443 DC.CopyConfigs.push_back(std::move(Config));
444 } else {
445 for (const char *Filename : Positional) {
446 Config.InputFilename = Filename;
447 Config.OutputFilename = Filename;
448 DC.CopyConfigs.push_back(Config);
449 }
450 }
451
452 return DC;
453}
454
455} // namespace objcopy
456} // namespace llvm