blob: 05985d3d2f1a224a3b175f9d5cf84bd26d9970df [file] [log] [blame]
Alexander Shaposhnikovf4e75a52018-10-29 21:22:58 +00001//===- ELFObjcopy.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 "ELFObjcopy.h"
11#include "Buffer.h"
12#include "CopyConfig.h"
13#include "llvm-objcopy.h"
14#include "Object.h"
15
16#include "llvm/ADT/BitmaskEnum.h"
17#include "llvm/ADT/Optional.h"
18#include "llvm/ADT/STLExtras.h"
19#include "llvm/ADT/SmallVector.h"
20#include "llvm/ADT/StringRef.h"
21#include "llvm/ADT/Twine.h"
22#include "llvm/BinaryFormat/ELF.h"
23#include "llvm/MC/MCTargetOptions.h"
24#include "llvm/Object/Binary.h"
25#include "llvm/Object/ELFObjectFile.h"
26#include "llvm/Object/ELFTypes.h"
27#include "llvm/Object/Error.h"
28#include "llvm/Option/Option.h"
29#include "llvm/Support/Casting.h"
30#include "llvm/Support/Compression.h"
31#include "llvm/Support/Error.h"
32#include "llvm/Support/ErrorHandling.h"
33#include "llvm/Support/ErrorOr.h"
34#include "llvm/Support/Memory.h"
35#include "llvm/Support/Path.h"
36#include "llvm/Support/raw_ostream.h"
37#include <algorithm>
38#include <cassert>
39#include <cstdlib>
40#include <functional>
41#include <iterator>
42#include <memory>
43#include <string>
44#include <system_error>
45#include <utility>
46
47namespace llvm {
48namespace objcopy {
49namespace elf {
50
51using namespace object;
52using namespace ELF;
53using SectionPred = std::function<bool(const SectionBase &Sec)>;
54
55static bool isDebugSection(const SectionBase &Sec) {
56 return StringRef(Sec.Name).startswith(".debug") ||
57 StringRef(Sec.Name).startswith(".zdebug") || Sec.Name == ".gdb_index";
58}
59
60static bool isDWOSection(const SectionBase &Sec) {
61 return StringRef(Sec.Name).endswith(".dwo");
62}
63
64static bool onlyKeepDWOPred(const Object &Obj, const SectionBase &Sec) {
65 // We can't remove the section header string table.
66 if (&Sec == Obj.SectionNames)
67 return false;
68 // Short of keeping the string table we want to keep everything that is a DWO
69 // section and remove everything else.
70 return !isDWOSection(Sec);
71}
72
73static ElfType getOutputElfType(const Binary &Bin) {
74 // Infer output ELF type from the input ELF object
75 if (isa<ELFObjectFile<ELF32LE>>(Bin))
76 return ELFT_ELF32LE;
77 if (isa<ELFObjectFile<ELF64LE>>(Bin))
78 return ELFT_ELF64LE;
79 if (isa<ELFObjectFile<ELF32BE>>(Bin))
80 return ELFT_ELF32BE;
81 if (isa<ELFObjectFile<ELF64BE>>(Bin))
82 return ELFT_ELF64BE;
83 llvm_unreachable("Invalid ELFType");
84}
85
86static ElfType getOutputElfType(const MachineInfo &MI) {
87 // Infer output ELF type from the binary arch specified
88 if (MI.Is64Bit)
89 return MI.IsLittleEndian ? ELFT_ELF64LE : ELFT_ELF64BE;
90 else
91 return MI.IsLittleEndian ? ELFT_ELF32LE : ELFT_ELF32BE;
92}
93
94static std::unique_ptr<Writer> createWriter(const CopyConfig &Config,
95 Object &Obj, Buffer &Buf,
96 ElfType OutputElfType) {
97 if (Config.OutputFormat == "binary") {
98 return llvm::make_unique<BinaryWriter>(Obj, Buf);
99 }
100 // Depending on the initial ELFT and OutputFormat we need a different Writer.
101 switch (OutputElfType) {
102 case ELFT_ELF32LE:
103 return llvm::make_unique<ELFWriter<ELF32LE>>(Obj, Buf,
104 !Config.StripSections);
105 case ELFT_ELF64LE:
106 return llvm::make_unique<ELFWriter<ELF64LE>>(Obj, Buf,
107 !Config.StripSections);
108 case ELFT_ELF32BE:
109 return llvm::make_unique<ELFWriter<ELF32BE>>(Obj, Buf,
110 !Config.StripSections);
111 case ELFT_ELF64BE:
112 return llvm::make_unique<ELFWriter<ELF64BE>>(Obj, Buf,
113 !Config.StripSections);
114 }
115 llvm_unreachable("Invalid output format");
116}
117
118static void splitDWOToFile(const CopyConfig &Config, const Reader &Reader,
119 StringRef File, ElfType OutputElfType) {
120 auto DWOFile = Reader.create();
121 DWOFile->removeSections(
122 [&](const SectionBase &Sec) { return onlyKeepDWOPred(*DWOFile, Sec); });
123 FileBuffer FB(File);
124 auto Writer = createWriter(Config, *DWOFile, FB, OutputElfType);
125 Writer->finalize();
126 Writer->write();
127}
128
129static Error dumpSectionToFile(StringRef SecName, StringRef Filename,
130 Object &Obj) {
131 for (auto &Sec : Obj.sections()) {
132 if (Sec.Name == SecName) {
133 if (Sec.OriginalData.size() == 0)
134 return make_error<StringError>("Can't dump section \"" + SecName +
135 "\": it has no contents",
136 object_error::parse_failed);
137 Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
138 FileOutputBuffer::create(Filename, Sec.OriginalData.size());
139 if (!BufferOrErr)
140 return BufferOrErr.takeError();
141 std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr);
142 std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(),
143 Buf->getBufferStart());
144 if (Error E = Buf->commit())
145 return E;
146 return Error::success();
147 }
148 }
149 return make_error<StringError>("Section not found",
150 object_error::parse_failed);
151}
152
153static bool isCompressed(const SectionBase &Section) {
154 const char *Magic = "ZLIB";
155 return StringRef(Section.Name).startswith(".zdebug") ||
156 (Section.OriginalData.size() > strlen(Magic) &&
157 !strncmp(reinterpret_cast<const char *>(Section.OriginalData.data()),
158 Magic, strlen(Magic))) ||
159 (Section.Flags & ELF::SHF_COMPRESSED);
160}
161
162static bool isCompressable(const SectionBase &Section) {
163 return !isCompressed(Section) && isDebugSection(Section) &&
164 Section.Name != ".gdb_index";
165}
166
167static void replaceDebugSections(
168 const CopyConfig &Config, Object &Obj, SectionPred &RemovePred,
169 function_ref<bool(const SectionBase &)> shouldReplace,
170 function_ref<SectionBase *(const SectionBase *)> addSection) {
171 SmallVector<SectionBase *, 13> ToReplace;
172 SmallVector<RelocationSection *, 13> RelocationSections;
173 for (auto &Sec : Obj.sections()) {
174 if (RelocationSection *R = dyn_cast<RelocationSection>(&Sec)) {
175 if (shouldReplace(*R->getSection()))
176 RelocationSections.push_back(R);
177 continue;
178 }
179
180 if (shouldReplace(Sec))
181 ToReplace.push_back(&Sec);
182 }
183
184 for (SectionBase *S : ToReplace) {
185 SectionBase *NewSection = addSection(S);
186
187 for (RelocationSection *RS : RelocationSections) {
188 if (RS->getSection() == S)
189 RS->setSection(NewSection);
190 }
191 }
192
193 RemovePred = [shouldReplace, RemovePred](const SectionBase &Sec) {
194 return shouldReplace(Sec) || RemovePred(Sec);
195 };
196}
197
198// This function handles the high level operations of GNU objcopy including
199// handling command line options. It's important to outline certain properties
200// we expect to hold of the command line operations. Any operation that "keeps"
201// should keep regardless of a remove. Additionally any removal should respect
202// any previous removals. Lastly whether or not something is removed shouldn't
203// depend a) on the order the options occur in or b) on some opaque priority
204// system. The only priority is that keeps/copies overrule removes.
205static void handleArgs(const CopyConfig &Config, Object &Obj,
206 const Reader &Reader, ElfType OutputElfType) {
207
208 if (!Config.SplitDWO.empty()) {
209 splitDWOToFile(Config, Reader, Config.SplitDWO, OutputElfType);
210 }
211
212 // TODO: update or remove symbols only if there is an option that affects
213 // them.
214 if (Obj.SymbolTable) {
215 Obj.SymbolTable->updateSymbols([&](Symbol &Sym) {
Jordan Rupprechtb47475c2018-11-01 17:26:36 +0000216 if (!Sym.isCommon() &&
217 ((Config.LocalizeHidden &&
218 (Sym.Visibility == STV_HIDDEN || Sym.Visibility == STV_INTERNAL)) ||
219 (!Config.SymbolsToLocalize.empty() &&
220 is_contained(Config.SymbolsToLocalize, Sym.Name))))
Alexander Shaposhnikovf4e75a52018-10-29 21:22:58 +0000221 Sym.Binding = STB_LOCAL;
222
223 // Note: these two globalize flags have very similar names but different
224 // meanings:
225 //
226 // --globalize-symbol: promote a symbol to global
227 // --keep-global-symbol: all symbols except for these should be made local
228 //
229 // If --globalize-symbol is specified for a given symbol, it will be
230 // global in the output file even if it is not included via
231 // --keep-global-symbol. Because of that, make sure to check
232 // --globalize-symbol second.
233 if (!Config.SymbolsToKeepGlobal.empty() &&
Jordan Rupprecht634820d2018-10-30 16:23:38 +0000234 !is_contained(Config.SymbolsToKeepGlobal, Sym.Name) &&
235 Sym.getShndx() != SHN_UNDEF)
Alexander Shaposhnikovf4e75a52018-10-29 21:22:58 +0000236 Sym.Binding = STB_LOCAL;
237
238 if (!Config.SymbolsToGlobalize.empty() &&
Jordan Rupprecht634820d2018-10-30 16:23:38 +0000239 is_contained(Config.SymbolsToGlobalize, Sym.Name) &&
240 Sym.getShndx() != SHN_UNDEF)
Alexander Shaposhnikovf4e75a52018-10-29 21:22:58 +0000241 Sym.Binding = STB_GLOBAL;
242
243 if (!Config.SymbolsToWeaken.empty() &&
244 is_contained(Config.SymbolsToWeaken, Sym.Name) &&
245 Sym.Binding == STB_GLOBAL)
246 Sym.Binding = STB_WEAK;
247
248 if (Config.Weaken && Sym.Binding == STB_GLOBAL &&
249 Sym.getShndx() != SHN_UNDEF)
250 Sym.Binding = STB_WEAK;
251
252 const auto I = Config.SymbolsToRename.find(Sym.Name);
253 if (I != Config.SymbolsToRename.end())
254 Sym.Name = I->getValue();
255
256 if (!Config.SymbolsPrefix.empty() && Sym.Type != STT_SECTION)
257 Sym.Name = (Config.SymbolsPrefix + Sym.Name).str();
258 });
259
260 // The purpose of this loop is to mark symbols referenced by sections
261 // (like GroupSection or RelocationSection). This way, we know which
262 // symbols are still 'needed' and which are not.
263 if (Config.StripUnneeded) {
264 for (auto &Section : Obj.sections())
265 Section.markSymbols();
266 }
267
268 Obj.removeSymbols([&](const Symbol &Sym) {
269 if ((!Config.SymbolsToKeep.empty() &&
270 is_contained(Config.SymbolsToKeep, Sym.Name)) ||
271 (Config.KeepFileSymbols && Sym.Type == STT_FILE))
272 return false;
273
274 if (Config.DiscardAll && Sym.Binding == STB_LOCAL &&
275 Sym.getShndx() != SHN_UNDEF && Sym.Type != STT_FILE &&
276 Sym.Type != STT_SECTION)
277 return true;
278
279 if (Config.StripAll || Config.StripAllGNU)
280 return true;
281
282 if (!Config.SymbolsToRemove.empty() &&
283 is_contained(Config.SymbolsToRemove, Sym.Name)) {
284 return true;
285 }
286
287 if (Config.StripUnneeded && !Sym.Referenced &&
288 (Sym.Binding == STB_LOCAL || Sym.getShndx() == SHN_UNDEF) &&
289 Sym.Type != STT_FILE && Sym.Type != STT_SECTION)
290 return true;
291
292 return false;
293 });
294 }
295
296 SectionPred RemovePred = [](const SectionBase &) { return false; };
297
298 // Removes:
299 if (!Config.ToRemove.empty()) {
300 RemovePred = [&Config](const SectionBase &Sec) {
301 return is_contained(Config.ToRemove, Sec.Name);
302 };
303 }
304
305 if (Config.StripDWO || !Config.SplitDWO.empty())
306 RemovePred = [RemovePred](const SectionBase &Sec) {
307 return isDWOSection(Sec) || RemovePred(Sec);
308 };
309
310 if (Config.ExtractDWO)
311 RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
312 return onlyKeepDWOPred(Obj, Sec) || RemovePred(Sec);
313 };
314
315 if (Config.StripAllGNU)
316 RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
317 if (RemovePred(Sec))
318 return true;
319 if ((Sec.Flags & SHF_ALLOC) != 0)
320 return false;
321 if (&Sec == Obj.SectionNames)
322 return false;
323 switch (Sec.Type) {
324 case SHT_SYMTAB:
325 case SHT_REL:
326 case SHT_RELA:
327 case SHT_STRTAB:
328 return true;
329 }
330 return isDebugSection(Sec);
331 };
332
333 if (Config.StripSections) {
334 RemovePred = [RemovePred](const SectionBase &Sec) {
335 return RemovePred(Sec) || (Sec.Flags & SHF_ALLOC) == 0;
336 };
337 }
338
339 if (Config.StripDebug) {
340 RemovePred = [RemovePred](const SectionBase &Sec) {
341 return RemovePred(Sec) || isDebugSection(Sec);
342 };
343 }
344
345 if (Config.StripNonAlloc)
346 RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
347 if (RemovePred(Sec))
348 return true;
349 if (&Sec == Obj.SectionNames)
350 return false;
351 return (Sec.Flags & SHF_ALLOC) == 0;
352 };
353
354 if (Config.StripAll)
355 RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
356 if (RemovePred(Sec))
357 return true;
358 if (&Sec == Obj.SectionNames)
359 return false;
360 if (StringRef(Sec.Name).startswith(".gnu.warning"))
361 return false;
362 return (Sec.Flags & SHF_ALLOC) == 0;
363 };
364
365 // Explicit copies:
366 if (!Config.OnlyKeep.empty()) {
367 RemovePred = [&Config, RemovePred, &Obj](const SectionBase &Sec) {
368 // Explicitly keep these sections regardless of previous removes.
369 if (is_contained(Config.OnlyKeep, Sec.Name))
370 return false;
371
372 // Allow all implicit removes.
373 if (RemovePred(Sec))
374 return true;
375
376 // Keep special sections.
377 if (Obj.SectionNames == &Sec)
378 return false;
379 if (Obj.SymbolTable == &Sec ||
380 (Obj.SymbolTable && Obj.SymbolTable->getStrTab() == &Sec))
381 return false;
382
383 // Remove everything else.
384 return true;
385 };
386 }
387
388 if (!Config.Keep.empty()) {
Fangrui Songe9f34b02018-11-12 23:46:22 +0000389 RemovePred = [&Config, RemovePred](const SectionBase &Sec) {
Alexander Shaposhnikovf4e75a52018-10-29 21:22:58 +0000390 // Explicitly keep these sections regardless of previous removes.
391 if (is_contained(Config.Keep, Sec.Name))
392 return false;
393 // Otherwise defer to RemovePred.
394 return RemovePred(Sec);
395 };
396 }
397
398 // This has to be the last predicate assignment.
399 // If the option --keep-symbol has been specified
400 // and at least one of those symbols is present
401 // (equivalently, the updated symbol table is not empty)
402 // the symbol table and the string table should not be removed.
403 if ((!Config.SymbolsToKeep.empty() || Config.KeepFileSymbols) &&
404 Obj.SymbolTable && !Obj.SymbolTable->empty()) {
405 RemovePred = [&Obj, RemovePred](const SectionBase &Sec) {
406 if (&Sec == Obj.SymbolTable || &Sec == Obj.SymbolTable->getStrTab())
407 return false;
408 return RemovePred(Sec);
409 };
410 }
411
412 if (Config.CompressionType != DebugCompressionType::None)
413 replaceDebugSections(Config, Obj, RemovePred, isCompressable,
414 [&Config, &Obj](const SectionBase *S) {
415 return &Obj.addSection<CompressedSection>(
416 *S, Config.CompressionType);
417 });
418 else if (Config.DecompressDebugSections)
419 replaceDebugSections(
420 Config, Obj, RemovePred,
421 [](const SectionBase &S) { return isa<CompressedSection>(&S); },
422 [&Obj](const SectionBase *S) {
423 auto CS = cast<CompressedSection>(S);
424 return &Obj.addSection<DecompressedSection>(*CS);
425 });
426
427 Obj.removeSections(RemovePred);
428
429 if (!Config.SectionsToRename.empty()) {
430 for (auto &Sec : Obj.sections()) {
431 const auto Iter = Config.SectionsToRename.find(Sec.Name);
432 if (Iter != Config.SectionsToRename.end()) {
433 const SectionRename &SR = Iter->second;
434 Sec.Name = SR.NewName;
435 if (SR.NewFlags.hasValue()) {
436 // Preserve some flags which should not be dropped when setting flags.
437 // Also, preserve anything OS/processor dependant.
438 const uint64_t PreserveMask = ELF::SHF_COMPRESSED | ELF::SHF_EXCLUDE |
439 ELF::SHF_GROUP | ELF::SHF_LINK_ORDER |
440 ELF::SHF_MASKOS | ELF::SHF_MASKPROC |
441 ELF::SHF_TLS | ELF::SHF_INFO_LINK;
442 Sec.Flags = (Sec.Flags & PreserveMask) |
443 (SR.NewFlags.getValue() & ~PreserveMask);
444 }
445 }
446 }
447 }
448
449 if (!Config.AddSection.empty()) {
450 for (const auto &Flag : Config.AddSection) {
451 auto SecPair = Flag.split("=");
452 auto SecName = SecPair.first;
453 auto File = SecPair.second;
454 auto BufOrErr = MemoryBuffer::getFile(File);
455 if (!BufOrErr)
456 reportError(File, BufOrErr.getError());
457 auto Buf = std::move(*BufOrErr);
458 auto BufPtr = reinterpret_cast<const uint8_t *>(Buf->getBufferStart());
459 auto BufSize = Buf->getBufferSize();
460 Obj.addSection<OwnedDataSection>(SecName,
461 ArrayRef<uint8_t>(BufPtr, BufSize));
462 }
463 }
464
465 if (!Config.DumpSection.empty()) {
466 for (const auto &Flag : Config.DumpSection) {
467 std::pair<StringRef, StringRef> SecPair = Flag.split("=");
468 StringRef SecName = SecPair.first;
469 StringRef File = SecPair.second;
470 if (Error E = dumpSectionToFile(SecName, File, Obj))
471 reportError(Config.InputFilename, std::move(E));
472 }
473 }
474
475 if (!Config.AddGnuDebugLink.empty())
476 Obj.addSection<GnuDebugLinkSection>(Config.AddGnuDebugLink);
477}
478
479void executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In,
480 Buffer &Out) {
481 BinaryReader Reader(Config.BinaryArch, &In);
482 std::unique_ptr<Object> Obj = Reader.create();
483
484 const ElfType OutputElfType = getOutputElfType(Config.BinaryArch);
485 handleArgs(Config, *Obj, Reader, OutputElfType);
486 std::unique_ptr<Writer> Writer =
487 createWriter(Config, *Obj, Out, OutputElfType);
488 Writer->finalize();
489 Writer->write();
490}
491
492void executeObjcopyOnBinary(const CopyConfig &Config,
493 object::ELFObjectFileBase &In, Buffer &Out) {
494 ELFReader Reader(&In);
495 std::unique_ptr<Object> Obj = Reader.create();
496 const ElfType OutputElfType = getOutputElfType(In);
497 handleArgs(Config, *Obj, Reader, OutputElfType);
498 std::unique_ptr<Writer> Writer =
499 createWriter(Config, *Obj, Out, OutputElfType);
500 Writer->finalize();
501 Writer->write();
502}
503
504} // end namespace elf
505} // end namespace objcopy
506} // end namespace llvm