xref: /llvm-project/llvm/tools/llvm-objcopy/llvm-objcopy.cpp (revision 654d3a9577d9782b03edaff7be369fa13e9ba14b)
1 //===- llvm-objcopy.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 "llvm-objcopy.h"
11 #include "Buffer.h"
12 #include "CopyConfig.h"
13 #include "Object.h"
14 
15 #include "llvm/ADT/BitmaskEnum.h"
16 #include "llvm/ADT/Optional.h"
17 #include "llvm/ADT/STLExtras.h"
18 #include "llvm/ADT/SmallVector.h"
19 #include "llvm/ADT/StringRef.h"
20 #include "llvm/ADT/Twine.h"
21 #include "llvm/BinaryFormat/ELF.h"
22 #include "llvm/MC/MCTargetOptions.h"
23 #include "llvm/Object/Archive.h"
24 #include "llvm/Object/ArchiveWriter.h"
25 #include "llvm/Object/Binary.h"
26 #include "llvm/Object/ELFObjectFile.h"
27 #include "llvm/Object/ELFTypes.h"
28 #include "llvm/Object/Error.h"
29 #include "llvm/Option/Arg.h"
30 #include "llvm/Option/ArgList.h"
31 #include "llvm/Option/Option.h"
32 #include "llvm/Support/Casting.h"
33 #include "llvm/Support/CommandLine.h"
34 #include "llvm/Support/Compiler.h"
35 #include "llvm/Support/Compression.h"
36 #include "llvm/Support/Error.h"
37 #include "llvm/Support/ErrorHandling.h"
38 #include "llvm/Support/ErrorOr.h"
39 #include "llvm/Support/FileOutputBuffer.h"
40 #include "llvm/Support/InitLLVM.h"
41 #include "llvm/Support/Memory.h"
42 #include "llvm/Support/Path.h"
43 #include "llvm/Support/Process.h"
44 #include "llvm/Support/WithColor.h"
45 #include "llvm/Support/raw_ostream.h"
46 #include <algorithm>
47 #include <cassert>
48 #include <cstdlib>
49 #include <functional>
50 #include <iterator>
51 #include <memory>
52 #include <string>
53 #include <system_error>
54 #include <utility>
55 
56 namespace llvm {
57 namespace objcopy {
58 
59 // The name this program was invoked as.
60 StringRef ToolName;
61 
62 LLVM_ATTRIBUTE_NORETURN void error(Twine Message) {
63   WithColor::error(errs(), ToolName) << Message << ".\n";
64   errs().flush();
65   exit(1);
66 }
67 
68 LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, std::error_code EC) {
69   assert(EC);
70   WithColor::error(errs(), ToolName)
71       << "'" << File << "': " << EC.message() << ".\n";
72   exit(1);
73 }
74 
75 LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Error E) {
76   assert(E);
77   std::string Buf;
78   raw_string_ostream OS(Buf);
79   logAllUnhandledErrors(std::move(E), OS, "");
80   OS.flush();
81   WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf;
82   exit(1);
83 }
84 
85 } // end namespace objcopy
86 } // end namespace llvm
87 
88 // TODO: move everything enclosed in the namespace llvm::objcopy::elf
89 // into separate header+cpp files.
90 namespace llvm {
91 namespace objcopy {
92 namespace elf {
93 
94 using namespace object;
95 using namespace ELF;
96 using SectionPred = std::function<bool(const SectionBase &Sec)>;
97 
98 static bool isDebugSection(const SectionBase &Sec) {
99   return StringRef(Sec.Name).startswith(".debug") ||
100          StringRef(Sec.Name).startswith(".zdebug") || Sec.Name == ".gdb_index";
101 }
102 
103 static bool isDWOSection(const SectionBase &Sec) {
104   return StringRef(Sec.Name).endswith(".dwo");
105 }
106 
107 static bool onlyKeepDWOPred(const Object &Obj, const SectionBase &Sec) {
108   // We can't remove the section header string table.
109   if (&Sec == Obj.SectionNames)
110     return false;
111   // Short of keeping the string table we want to keep everything that is a DWO
112   // section and remove everything else.
113   return !isDWOSection(Sec);
114 }
115 
116 static ElfType getOutputElfType(const Binary &Bin) {
117   // Infer output ELF type from the input ELF object
118   if (isa<ELFObjectFile<ELF32LE>>(Bin))
119     return ELFT_ELF32LE;
120   if (isa<ELFObjectFile<ELF64LE>>(Bin))
121     return ELFT_ELF64LE;
122   if (isa<ELFObjectFile<ELF32BE>>(Bin))
123     return ELFT_ELF32BE;
124   if (isa<ELFObjectFile<ELF64BE>>(Bin))
125     return ELFT_ELF64BE;
126   llvm_unreachable("Invalid ELFType");
127 }
128 
129 static ElfType getOutputElfType(const MachineInfo &MI) {
130   // Infer output ELF type from the binary arch specified
131   if (MI.Is64Bit)
132     return MI.IsLittleEndian ? ELFT_ELF64LE : ELFT_ELF64BE;
133   else
134     return MI.IsLittleEndian ? ELFT_ELF32LE : ELFT_ELF32BE;
135 }
136 
137 static std::unique_ptr<Writer> createWriter(const CopyConfig &Config,
138                                             Object &Obj, Buffer &Buf,
139                                             ElfType OutputElfType) {
140   if (Config.OutputFormat == "binary") {
141     return llvm::make_unique<BinaryWriter>(Obj, Buf);
142   }
143   // Depending on the initial ELFT and OutputFormat we need a different Writer.
144   switch (OutputElfType) {
145   case ELFT_ELF32LE:
146     return llvm::make_unique<ELFWriter<ELF32LE>>(Obj, Buf,
147                                                  !Config.StripSections);
148   case ELFT_ELF64LE:
149     return llvm::make_unique<ELFWriter<ELF64LE>>(Obj, Buf,
150                                                  !Config.StripSections);
151   case ELFT_ELF32BE:
152     return llvm::make_unique<ELFWriter<ELF32BE>>(Obj, Buf,
153                                                  !Config.StripSections);
154   case ELFT_ELF64BE:
155     return llvm::make_unique<ELFWriter<ELF64BE>>(Obj, Buf,
156                                                  !Config.StripSections);
157   }
158   llvm_unreachable("Invalid output format");
159 }
160 
161 static void splitDWOToFile(const CopyConfig &Config, const Reader &Reader,
162                            StringRef File, ElfType OutputElfType) {
163   auto DWOFile = Reader.create();
164   DWOFile->removeSections(
165       [&](const SectionBase &Sec) { return onlyKeepDWOPred(*DWOFile, Sec); });
166   FileBuffer FB(File);
167   auto Writer = createWriter(Config, *DWOFile, FB, OutputElfType);
168   Writer->finalize();
169   Writer->write();
170 }
171 
172 static Error dumpSectionToFile(StringRef SecName, StringRef Filename,
173                                Object &Obj) {
174   for (auto &Sec : Obj.sections()) {
175     if (Sec.Name == SecName) {
176       if (Sec.OriginalData.size() == 0)
177         return make_error<StringError>("Can't dump section \"" + SecName +
178                                            "\": it has no contents",
179                                        object_error::parse_failed);
180       Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
181           FileOutputBuffer::create(Filename, Sec.OriginalData.size());
182       if (!BufferOrErr)
183         return BufferOrErr.takeError();
184       std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr);
185       std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(),
186                 Buf->getBufferStart());
187       if (Error E = Buf->commit())
188         return E;
189       return Error::success();
190     }
191   }
192   return make_error<StringError>("Section not found",
193                                  object_error::parse_failed);
194 }
195 
196 static bool isCompressed(const SectionBase &Section) {
197   const char *Magic = "ZLIB";
198   return StringRef(Section.Name).startswith(".zdebug") ||
199          (Section.OriginalData.size() > strlen(Magic) &&
200           !strncmp(reinterpret_cast<const char *>(Section.OriginalData.data()),
201                    Magic, strlen(Magic))) ||
202          (Section.Flags & ELF::SHF_COMPRESSED);
203 }
204 
205 static bool isCompressable(const SectionBase &Section) {
206   return !isCompressed(Section) && isDebugSection(Section) &&
207          Section.Name != ".gdb_index";
208 }
209 
210 static void replaceDebugSections(
211     const CopyConfig &Config, Object &Obj, SectionPred &RemovePred,
212     function_ref<bool(const SectionBase &)> shouldReplace,
213     function_ref<SectionBase *(const SectionBase *)> addSection) {
214   SmallVector<SectionBase *, 13> ToReplace;
215   SmallVector<RelocationSection *, 13> RelocationSections;
216   for (auto &Sec : Obj.sections()) {
217     if (RelocationSection *R = dyn_cast<RelocationSection>(&Sec)) {
218       if (shouldReplace(*R->getSection()))
219         RelocationSections.push_back(R);
220       continue;
221     }
222 
223     if (shouldReplace(Sec))
224       ToReplace.push_back(&Sec);
225   }
226 
227   for (SectionBase *S : ToReplace) {
228     SectionBase *NewSection = addSection(S);
229 
230     for (RelocationSection *RS : RelocationSections) {
231       if (RS->getSection() == S)
232         RS->setSection(NewSection);
233     }
234   }
235 
236   RemovePred = [shouldReplace, RemovePred](const SectionBase &Sec) {
237     return shouldReplace(Sec) || RemovePred(Sec);
238   };
239 }
240 
241 // This function handles the high level operations of GNU objcopy including
242 // handling command line options. It's important to outline certain properties
243 // we expect to hold of the command line operations. Any operation that "keeps"
244 // should keep regardless of a remove. Additionally any removal should respect
245 // any previous removals. Lastly whether or not something is removed shouldn't
246 // depend a) on the order the options occur in or b) on some opaque priority
247 // system. The only priority is that keeps/copies overrule removes.
248 static void handleArgs(const CopyConfig &Config, Object &Obj,
249                        const Reader &Reader, ElfType OutputElfType) {
250 
251   if (!Config.SplitDWO.empty()) {
252     splitDWOToFile(Config, Reader, Config.SplitDWO, OutputElfType);
253   }
254 
255   // TODO: update or remove symbols only if there is an option that affects
256   // them.
257   if (Obj.SymbolTable) {
258     Obj.SymbolTable->updateSymbols([&](Symbol &Sym) {
259       if ((Config.LocalizeHidden &&
260            (Sym.Visibility == STV_HIDDEN || Sym.Visibility == STV_INTERNAL)) ||
261           (!Config.SymbolsToLocalize.empty() &&
262            is_contained(Config.SymbolsToLocalize, Sym.Name)))
263         Sym.Binding = STB_LOCAL;
264 
265       // Note: these two globalize flags have very similar names but different
266       // meanings:
267       //
268       // --globalize-symbol: promote a symbol to global
269       // --keep-global-symbol: all symbols except for these should be made local
270       //
271       // If --globalize-symbol is specified for a given symbol, it will be
272       // global in the output file even if it is not included via
273       // --keep-global-symbol. Because of that, make sure to check
274       // --globalize-symbol second.
275       if (!Config.SymbolsToKeepGlobal.empty() &&
276           !is_contained(Config.SymbolsToKeepGlobal, Sym.Name))
277         Sym.Binding = STB_LOCAL;
278 
279       if (!Config.SymbolsToGlobalize.empty() &&
280           is_contained(Config.SymbolsToGlobalize, Sym.Name))
281         Sym.Binding = STB_GLOBAL;
282 
283       if (!Config.SymbolsToWeaken.empty() &&
284           is_contained(Config.SymbolsToWeaken, Sym.Name) &&
285           Sym.Binding == STB_GLOBAL)
286         Sym.Binding = STB_WEAK;
287 
288       if (Config.Weaken && Sym.Binding == STB_GLOBAL &&
289           Sym.getShndx() != SHN_UNDEF)
290         Sym.Binding = STB_WEAK;
291 
292       const auto I = Config.SymbolsToRename.find(Sym.Name);
293       if (I != Config.SymbolsToRename.end())
294         Sym.Name = I->getValue();
295 
296       if (!Config.SymbolsPrefix.empty() && Sym.Type != STT_SECTION)
297         Sym.Name = (Config.SymbolsPrefix + Sym.Name).str();
298     });
299 
300     // The purpose of this loop is to mark symbols referenced by sections
301     // (like GroupSection or RelocationSection). This way, we know which
302     // symbols are still 'needed' and which are not.
303     if (Config.StripUnneeded) {
304       for (auto &Section : Obj.sections())
305         Section.markSymbols();
306     }
307 
308     Obj.removeSymbols([&](const Symbol &Sym) {
309       if ((!Config.SymbolsToKeep.empty() &&
310            is_contained(Config.SymbolsToKeep, Sym.Name)) ||
311           (Config.KeepFileSymbols && Sym.Type == STT_FILE))
312         return false;
313 
314       if (Config.DiscardAll && Sym.Binding == STB_LOCAL &&
315           Sym.getShndx() != SHN_UNDEF && Sym.Type != STT_FILE &&
316           Sym.Type != STT_SECTION)
317         return true;
318 
319       if (Config.StripAll || Config.StripAllGNU)
320         return true;
321 
322       if (!Config.SymbolsToRemove.empty() &&
323           is_contained(Config.SymbolsToRemove, Sym.Name)) {
324         return true;
325       }
326 
327       if (Config.StripUnneeded && !Sym.Referenced &&
328           (Sym.Binding == STB_LOCAL || Sym.getShndx() == SHN_UNDEF) &&
329           Sym.Type != STT_FILE && Sym.Type != STT_SECTION)
330         return true;
331 
332       return false;
333     });
334   }
335 
336   SectionPred RemovePred = [](const SectionBase &) { return false; };
337 
338   // Removes:
339   if (!Config.ToRemove.empty()) {
340     RemovePred = [&Config](const SectionBase &Sec) {
341       return is_contained(Config.ToRemove, Sec.Name);
342     };
343   }
344 
345   if (Config.StripDWO || !Config.SplitDWO.empty())
346     RemovePred = [RemovePred](const SectionBase &Sec) {
347       return isDWOSection(Sec) || RemovePred(Sec);
348     };
349 
350   if (Config.ExtractDWO)
351     RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
352       return onlyKeepDWOPred(Obj, Sec) || RemovePred(Sec);
353     };
354 
355   if (Config.StripAllGNU)
356     RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
357       if (RemovePred(Sec))
358         return true;
359       if ((Sec.Flags & SHF_ALLOC) != 0)
360         return false;
361       if (&Sec == Obj.SectionNames)
362         return false;
363       switch (Sec.Type) {
364       case SHT_SYMTAB:
365       case SHT_REL:
366       case SHT_RELA:
367       case SHT_STRTAB:
368         return true;
369       }
370       return isDebugSection(Sec);
371     };
372 
373   if (Config.StripSections) {
374     RemovePred = [RemovePred](const SectionBase &Sec) {
375       return RemovePred(Sec) || (Sec.Flags & SHF_ALLOC) == 0;
376     };
377   }
378 
379   if (Config.StripDebug) {
380     RemovePred = [RemovePred](const SectionBase &Sec) {
381       return RemovePred(Sec) || isDebugSection(Sec);
382     };
383   }
384 
385   if (Config.StripNonAlloc)
386     RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
387       if (RemovePred(Sec))
388         return true;
389       if (&Sec == Obj.SectionNames)
390         return false;
391       return (Sec.Flags & SHF_ALLOC) == 0;
392     };
393 
394   if (Config.StripAll)
395     RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
396       if (RemovePred(Sec))
397         return true;
398       if (&Sec == Obj.SectionNames)
399         return false;
400       if (StringRef(Sec.Name).startswith(".gnu.warning"))
401         return false;
402       return (Sec.Flags & SHF_ALLOC) == 0;
403     };
404 
405   // Explicit copies:
406   if (!Config.OnlyKeep.empty()) {
407     RemovePred = [&Config, RemovePred, &Obj](const SectionBase &Sec) {
408       // Explicitly keep these sections regardless of previous removes.
409       if (is_contained(Config.OnlyKeep, Sec.Name))
410         return false;
411 
412       // Allow all implicit removes.
413       if (RemovePred(Sec))
414         return true;
415 
416       // Keep special sections.
417       if (Obj.SectionNames == &Sec)
418         return false;
419       if (Obj.SymbolTable == &Sec ||
420           (Obj.SymbolTable && Obj.SymbolTable->getStrTab() == &Sec))
421         return false;
422 
423       // Remove everything else.
424       return true;
425     };
426   }
427 
428   if (!Config.Keep.empty()) {
429     RemovePred = [Config, RemovePred](const SectionBase &Sec) {
430       // Explicitly keep these sections regardless of previous removes.
431       if (is_contained(Config.Keep, Sec.Name))
432         return false;
433       // Otherwise defer to RemovePred.
434       return RemovePred(Sec);
435     };
436   }
437 
438   // This has to be the last predicate assignment.
439   // If the option --keep-symbol has been specified
440   // and at least one of those symbols is present
441   // (equivalently, the updated symbol table is not empty)
442   // the symbol table and the string table should not be removed.
443   if ((!Config.SymbolsToKeep.empty() || Config.KeepFileSymbols) &&
444       Obj.SymbolTable && !Obj.SymbolTable->empty()) {
445     RemovePred = [&Obj, RemovePred](const SectionBase &Sec) {
446       if (&Sec == Obj.SymbolTable || &Sec == Obj.SymbolTable->getStrTab())
447         return false;
448       return RemovePred(Sec);
449     };
450   }
451 
452   if (Config.CompressionType != DebugCompressionType::None)
453     replaceDebugSections(Config, Obj, RemovePred, isCompressable,
454                          [&Config, &Obj](const SectionBase *S) {
455                            return &Obj.addSection<CompressedSection>(
456                                *S, Config.CompressionType);
457                          });
458   else if (Config.DecompressDebugSections)
459     replaceDebugSections(
460         Config, Obj, RemovePred,
461         [](const SectionBase &S) { return isa<CompressedSection>(&S); },
462         [&Obj](const SectionBase *S) {
463           auto CS = cast<CompressedSection>(S);
464           return &Obj.addSection<DecompressedSection>(*CS);
465         });
466 
467   Obj.removeSections(RemovePred);
468 
469   if (!Config.SectionsToRename.empty()) {
470     for (auto &Sec : Obj.sections()) {
471       const auto Iter = Config.SectionsToRename.find(Sec.Name);
472       if (Iter != Config.SectionsToRename.end()) {
473         const SectionRename &SR = Iter->second;
474         Sec.Name = SR.NewName;
475         if (SR.NewFlags.hasValue()) {
476           // Preserve some flags which should not be dropped when setting flags.
477           // Also, preserve anything OS/processor dependant.
478           const uint64_t PreserveMask = ELF::SHF_COMPRESSED | ELF::SHF_EXCLUDE |
479                                         ELF::SHF_GROUP | ELF::SHF_LINK_ORDER |
480                                         ELF::SHF_MASKOS | ELF::SHF_MASKPROC |
481                                         ELF::SHF_TLS | ELF::SHF_INFO_LINK;
482           Sec.Flags = (Sec.Flags & PreserveMask) |
483                       (SR.NewFlags.getValue() & ~PreserveMask);
484         }
485       }
486     }
487   }
488 
489   if (!Config.AddSection.empty()) {
490     for (const auto &Flag : Config.AddSection) {
491       auto SecPair = Flag.split("=");
492       auto SecName = SecPair.first;
493       auto File = SecPair.second;
494       auto BufOrErr = MemoryBuffer::getFile(File);
495       if (!BufOrErr)
496         reportError(File, BufOrErr.getError());
497       auto Buf = std::move(*BufOrErr);
498       auto BufPtr = reinterpret_cast<const uint8_t *>(Buf->getBufferStart());
499       auto BufSize = Buf->getBufferSize();
500       Obj.addSection<OwnedDataSection>(SecName,
501                                        ArrayRef<uint8_t>(BufPtr, BufSize));
502     }
503   }
504 
505   if (!Config.DumpSection.empty()) {
506     for (const auto &Flag : Config.DumpSection) {
507       std::pair<StringRef, StringRef> SecPair = Flag.split("=");
508       StringRef SecName = SecPair.first;
509       StringRef File = SecPair.second;
510       if (Error E = dumpSectionToFile(SecName, File, Obj))
511         reportError(Config.InputFilename, std::move(E));
512     }
513   }
514 
515   if (!Config.AddGnuDebugLink.empty())
516     Obj.addSection<GnuDebugLinkSection>(Config.AddGnuDebugLink);
517 }
518 
519 void executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In,
520                                Buffer &Out) {
521   BinaryReader Reader(Config.BinaryArch, &In);
522   std::unique_ptr<Object> Obj = Reader.create();
523 
524   const ElfType OutputElfType = getOutputElfType(Config.BinaryArch);
525   handleArgs(Config, *Obj, Reader, OutputElfType);
526   std::unique_ptr<Writer> Writer =
527       createWriter(Config, *Obj, Out, OutputElfType);
528   Writer->finalize();
529   Writer->write();
530 }
531 
532 void executeObjcopyOnBinary(const CopyConfig &Config,
533                             object::ELFObjectFileBase &In, Buffer &Out) {
534   ELFReader Reader(&In);
535   std::unique_ptr<Object> Obj = Reader.create();
536   const ElfType OutputElfType = getOutputElfType(In);
537   handleArgs(Config, *Obj, Reader, OutputElfType);
538   std::unique_ptr<Writer> Writer =
539       createWriter(Config, *Obj, Out, OutputElfType);
540   Writer->finalize();
541   Writer->write();
542 }
543 
544 } // end namespace elf
545 } // end namespace objcopy
546 } // end namespace llvm
547 
548 using namespace llvm;
549 using namespace llvm::object;
550 using namespace llvm::objcopy;
551 
552 // For regular archives this function simply calls llvm::writeArchive,
553 // For thin archives it writes the archive file itself as well as its members.
554 static Error deepWriteArchive(StringRef ArcName,
555                               ArrayRef<NewArchiveMember> NewMembers,
556                               bool WriteSymtab, object::Archive::Kind Kind,
557                               bool Deterministic, bool Thin) {
558   Error E =
559       writeArchive(ArcName, NewMembers, WriteSymtab, Kind, Deterministic, Thin);
560   if (!Thin || E)
561     return E;
562   for (const NewArchiveMember &Member : NewMembers) {
563     // Internally, FileBuffer will use the buffer created by
564     // FileOutputBuffer::create, for regular files (that is the case for
565     // deepWriteArchive) FileOutputBuffer::create will return OnDiskBuffer.
566     // OnDiskBuffer uses a temporary file and then renames it. So in reality
567     // there is no inefficiency / duplicated in-memory buffers in this case. For
568     // now in-memory buffers can not be completely avoided since
569     // NewArchiveMember still requires them even though writeArchive does not
570     // write them on disk.
571     FileBuffer FB(Member.MemberName);
572     FB.allocate(Member.Buf->getBufferSize());
573     std::copy(Member.Buf->getBufferStart(), Member.Buf->getBufferEnd(),
574               FB.getBufferStart());
575     if (auto E = FB.commit())
576       return E;
577   }
578   return Error::success();
579 }
580 
581 /// The function executeObjcopyOnRawBinary does the dispatch based on the format
582 /// of the output specified by the command line options.
583 static void executeObjcopyOnRawBinary(const CopyConfig &Config,
584                                       MemoryBuffer &In, Buffer &Out) {
585   // TODO: llvm-objcopy should parse CopyConfig.OutputFormat to recognize
586   // formats other than ELF / "binary" and invoke
587   // elf::executeObjcopyOnRawBinary, macho::executeObjcopyOnRawBinary or
588   // coff::executeObjcopyOnRawBinary accordingly.
589   return elf::executeObjcopyOnRawBinary(Config, In, Out);
590 }
591 
592 /// The function executeObjcopyOnBinary does the dispatch based on the format
593 /// of the input binary (ELF, MachO or COFF).
594 static void executeObjcopyOnBinary(const CopyConfig &Config, object::Binary &In,
595                                    Buffer &Out) {
596   if (auto *ELFBinary = dyn_cast<object::ELFObjectFileBase>(&In))
597     return elf::executeObjcopyOnBinary(Config, *ELFBinary, Out);
598   else
599     error("Unsupported object file format");
600 }
601 
602 static void executeObjcopyOnArchive(const CopyConfig &Config,
603                                     const Archive &Ar) {
604   std::vector<NewArchiveMember> NewArchiveMembers;
605   Error Err = Error::success();
606   for (const Archive::Child &Child : Ar.children(Err)) {
607     Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary();
608     if (!ChildOrErr)
609       reportError(Ar.getFileName(), ChildOrErr.takeError());
610     Binary *Bin = ChildOrErr->get();
611 
612     Expected<StringRef> ChildNameOrErr = Child.getName();
613     if (!ChildNameOrErr)
614       reportError(Ar.getFileName(), ChildNameOrErr.takeError());
615 
616     MemBuffer MB(ChildNameOrErr.get());
617     executeObjcopyOnBinary(Config, *Bin, MB);
618 
619     Expected<NewArchiveMember> Member =
620         NewArchiveMember::getOldMember(Child, true);
621     if (!Member)
622       reportError(Ar.getFileName(), Member.takeError());
623     Member->Buf = MB.releaseMemoryBuffer();
624     Member->MemberName = Member->Buf->getBufferIdentifier();
625     NewArchiveMembers.push_back(std::move(*Member));
626   }
627 
628   if (Err)
629     reportError(Config.InputFilename, std::move(Err));
630   if (Error E =
631           deepWriteArchive(Config.OutputFilename, NewArchiveMembers,
632                            Ar.hasSymbolTable(), Ar.kind(), true, Ar.isThin()))
633     reportError(Config.OutputFilename, std::move(E));
634 }
635 
636 static void restoreDateOnFile(StringRef Filename,
637                               const sys::fs::file_status &Stat) {
638   int FD;
639 
640   if (auto EC =
641           sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting))
642     reportError(Filename, EC);
643 
644   if (auto EC = sys::fs::setLastAccessAndModificationTime(
645           FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime()))
646     reportError(Filename, EC);
647 
648   if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD))
649     reportError(Filename, EC);
650 }
651 
652 /// The function executeObjcopy does the higher level dispatch based on the type
653 /// of input (raw binary, archive or single object file) and takes care of the
654 /// format-agnostic modifications, i.e. preserving dates.
655 static void executeObjcopy(const CopyConfig &Config) {
656   sys::fs::file_status Stat;
657   if (Config.PreserveDates)
658     if (auto EC = sys::fs::status(Config.InputFilename, Stat))
659       reportError(Config.InputFilename, EC);
660 
661   if (Config.InputFormat == "binary") {
662     auto BufOrErr = MemoryBuffer::getFile(Config.InputFilename);
663     if (!BufOrErr)
664       reportError(Config.InputFilename, BufOrErr.getError());
665     FileBuffer FB(Config.OutputFilename);
666     executeObjcopyOnRawBinary(Config, *BufOrErr->get(), FB);
667   } else {
668     Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr =
669         createBinary(Config.InputFilename);
670     if (!BinaryOrErr)
671       reportError(Config.InputFilename, BinaryOrErr.takeError());
672 
673     if (Archive *Ar = dyn_cast<Archive>(BinaryOrErr.get().getBinary())) {
674       executeObjcopyOnArchive(Config, *Ar);
675     } else {
676       FileBuffer FB(Config.OutputFilename);
677       executeObjcopyOnBinary(Config, *BinaryOrErr.get().getBinary(), FB);
678     }
679   }
680 
681   if (Config.PreserveDates) {
682     restoreDateOnFile(Config.OutputFilename, Stat);
683     if (!Config.SplitDWO.empty())
684       restoreDateOnFile(Config.SplitDWO, Stat);
685   }
686 }
687 
688 int main(int argc, char **argv) {
689   InitLLVM X(argc, argv);
690   ToolName = argv[0];
691   DriverConfig DriverConfig;
692   if (sys::path::stem(ToolName).endswith_lower("strip"))
693     DriverConfig = parseStripOptions(makeArrayRef(argv + 1, argc));
694   else
695     DriverConfig = parseObjcopyOptions(makeArrayRef(argv + 1, argc));
696   for (const CopyConfig &CopyConfig : DriverConfig.CopyConfigs)
697     executeObjcopy(CopyConfig);
698 }
699