1 //===- ELFObjcopy.cpp -----------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "ELFObjcopy.h"
11 #include "CopyConfig.h"
13 #include "llvm-objcopy.h"
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/Binary.h"
24 #include "llvm/Object/ELFObjectFile.h"
25 #include "llvm/Object/ELFTypes.h"
26 #include "llvm/Object/Error.h"
27 #include "llvm/Option/Option.h"
28 #include "llvm/Support/Casting.h"
29 #include "llvm/Support/Compression.h"
30 #include "llvm/Support/Errc.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"
44 #include <system_error>
51 using namespace object
;
53 using SectionPred
= std::function
<bool(const SectionBase
&Sec
)>;
55 static bool isDebugSection(const SectionBase
&Sec
) {
56 return StringRef(Sec
.Name
).startswith(".debug") ||
57 StringRef(Sec
.Name
).startswith(".zdebug") || Sec
.Name
== ".gdb_index";
60 static bool isDWOSection(const SectionBase
&Sec
) {
61 return StringRef(Sec
.Name
).endswith(".dwo");
64 static bool onlyKeepDWOPred(const Object
&Obj
, const SectionBase
&Sec
) {
65 // We can't remove the section header string table.
66 if (&Sec
== Obj
.SectionNames
)
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
);
73 static uint64_t setSectionFlagsPreserveMask(uint64_t OldFlags
,
75 // Preserve some flags which should not be dropped when setting flags.
76 // Also, preserve anything OS/processor dependant.
77 const uint64_t PreserveMask
= ELF::SHF_COMPRESSED
| ELF::SHF_EXCLUDE
|
78 ELF::SHF_GROUP
| ELF::SHF_LINK_ORDER
|
79 ELF::SHF_MASKOS
| ELF::SHF_MASKPROC
|
80 ELF::SHF_TLS
| ELF::SHF_INFO_LINK
;
81 return (OldFlags
& PreserveMask
) | (NewFlags
& ~PreserveMask
);
84 static ElfType
getOutputElfType(const Binary
&Bin
) {
85 // Infer output ELF type from the input ELF object
86 if (isa
<ELFObjectFile
<ELF32LE
>>(Bin
))
88 if (isa
<ELFObjectFile
<ELF64LE
>>(Bin
))
90 if (isa
<ELFObjectFile
<ELF32BE
>>(Bin
))
92 if (isa
<ELFObjectFile
<ELF64BE
>>(Bin
))
94 llvm_unreachable("Invalid ELFType");
97 static ElfType
getOutputElfType(const MachineInfo
&MI
) {
98 // Infer output ELF type from the binary arch specified
100 return MI
.IsLittleEndian
? ELFT_ELF64LE
: ELFT_ELF64BE
;
102 return MI
.IsLittleEndian
? ELFT_ELF32LE
: ELFT_ELF32BE
;
105 static std::unique_ptr
<Writer
> createWriter(const CopyConfig
&Config
,
106 Object
&Obj
, Buffer
&Buf
,
107 ElfType OutputElfType
) {
108 if (Config
.OutputFormat
== "binary") {
109 return llvm::make_unique
<BinaryWriter
>(Obj
, Buf
);
111 // Depending on the initial ELFT and OutputFormat we need a different Writer.
112 switch (OutputElfType
) {
114 return llvm::make_unique
<ELFWriter
<ELF32LE
>>(Obj
, Buf
,
115 !Config
.StripSections
);
117 return llvm::make_unique
<ELFWriter
<ELF64LE
>>(Obj
, Buf
,
118 !Config
.StripSections
);
120 return llvm::make_unique
<ELFWriter
<ELF32BE
>>(Obj
, Buf
,
121 !Config
.StripSections
);
123 return llvm::make_unique
<ELFWriter
<ELF64BE
>>(Obj
, Buf
,
124 !Config
.StripSections
);
126 llvm_unreachable("Invalid output format");
129 template <class ELFT
>
130 static Expected
<ArrayRef
<uint8_t>>
131 findBuildID(const object::ELFFile
<ELFT
> &In
) {
132 for (const auto &Phdr
: unwrapOrError(In
.program_headers())) {
133 if (Phdr
.p_type
!= PT_NOTE
)
135 Error Err
= Error::success();
136 for (const auto &Note
: In
.notes(Phdr
, Err
))
137 if (Note
.getType() == NT_GNU_BUILD_ID
&& Note
.getName() == ELF_NOTE_GNU
)
138 return Note
.getDesc();
140 return std::move(Err
);
142 return createStringError(llvm::errc::invalid_argument
,
143 "Could not find build ID.");
146 static Expected
<ArrayRef
<uint8_t>>
147 findBuildID(const object::ELFObjectFileBase
&In
) {
148 if (auto *O
= dyn_cast
<ELFObjectFile
<ELF32LE
>>(&In
))
149 return findBuildID(*O
->getELFFile());
150 else if (auto *O
= dyn_cast
<ELFObjectFile
<ELF64LE
>>(&In
))
151 return findBuildID(*O
->getELFFile());
152 else if (auto *O
= dyn_cast
<ELFObjectFile
<ELF32BE
>>(&In
))
153 return findBuildID(*O
->getELFFile());
154 else if (auto *O
= dyn_cast
<ELFObjectFile
<ELF64BE
>>(&In
))
155 return findBuildID(*O
->getELFFile());
157 llvm_unreachable("Bad file format");
160 static Error
linkToBuildIdDir(const CopyConfig
&Config
, StringRef ToLink
,
162 ArrayRef
<uint8_t> BuildIdBytes
) {
163 SmallString
<128> Path
= Config
.BuildIdLinkDir
;
164 sys::path::append(Path
, llvm::toHex(BuildIdBytes
[0], /*LowerCase*/ true));
165 if (auto EC
= sys::fs::create_directories(Path
))
166 return createFileError(
168 createStringError(EC
, "cannot create build ID link directory"));
170 sys::path::append(Path
,
171 llvm::toHex(BuildIdBytes
.slice(1), /*LowerCase*/ true));
173 if (auto EC
= sys::fs::create_hard_link(ToLink
, Path
)) {
174 // Hard linking failed, try to remove the file first if it exists.
175 if (sys::fs::exists(Path
))
176 sys::fs::remove(Path
);
177 EC
= sys::fs::create_hard_link(ToLink
, Path
);
179 return createStringError(EC
, "cannot link %s to %s", ToLink
.data(),
182 return Error::success();
185 static Error
splitDWOToFile(const CopyConfig
&Config
, const Reader
&Reader
,
186 StringRef File
, ElfType OutputElfType
) {
187 auto DWOFile
= Reader
.create();
188 auto OnlyKeepDWOPred
= [&DWOFile
](const SectionBase
&Sec
) {
189 return onlyKeepDWOPred(*DWOFile
, Sec
);
191 if (Error E
= DWOFile
->removeSections(OnlyKeepDWOPred
))
193 if (Config
.OutputArch
)
194 DWOFile
->Machine
= Config
.OutputArch
.getValue().EMachine
;
196 auto Writer
= createWriter(Config
, *DWOFile
, FB
, OutputElfType
);
197 if (Error E
= Writer
->finalize())
199 return Writer
->write();
202 static Error
dumpSectionToFile(StringRef SecName
, StringRef Filename
,
204 for (auto &Sec
: Obj
.sections()) {
205 if (Sec
.Name
== SecName
) {
206 if (Sec
.OriginalData
.empty())
207 return createStringError(
208 object_error::parse_failed
,
209 "Can't dump section \"%s\": it has no contents",
210 SecName
.str().c_str());
211 Expected
<std::unique_ptr
<FileOutputBuffer
>> BufferOrErr
=
212 FileOutputBuffer::create(Filename
, Sec
.OriginalData
.size());
214 return BufferOrErr
.takeError();
215 std::unique_ptr
<FileOutputBuffer
> Buf
= std::move(*BufferOrErr
);
216 std::copy(Sec
.OriginalData
.begin(), Sec
.OriginalData
.end(),
217 Buf
->getBufferStart());
218 if (Error E
= Buf
->commit())
220 return Error::success();
223 return createStringError(object_error::parse_failed
, "Section not found");
226 static bool isCompressed(const SectionBase
&Section
) {
227 const char *Magic
= "ZLIB";
228 return StringRef(Section
.Name
).startswith(".zdebug") ||
229 (Section
.OriginalData
.size() > strlen(Magic
) &&
230 !strncmp(reinterpret_cast<const char *>(Section
.OriginalData
.data()),
231 Magic
, strlen(Magic
))) ||
232 (Section
.Flags
& ELF::SHF_COMPRESSED
);
235 static bool isCompressable(const SectionBase
&Section
) {
236 return !isCompressed(Section
) && isDebugSection(Section
) &&
237 Section
.Name
!= ".gdb_index";
240 static void replaceDebugSections(
241 const CopyConfig
&Config
, Object
&Obj
, SectionPred
&RemovePred
,
242 function_ref
<bool(const SectionBase
&)> shouldReplace
,
243 function_ref
<SectionBase
*(const SectionBase
*)> addSection
) {
244 SmallVector
<SectionBase
*, 13> ToReplace
;
245 SmallVector
<RelocationSection
*, 13> RelocationSections
;
246 for (auto &Sec
: Obj
.sections()) {
247 if (RelocationSection
*R
= dyn_cast
<RelocationSection
>(&Sec
)) {
248 if (shouldReplace(*R
->getSection()))
249 RelocationSections
.push_back(R
);
253 if (shouldReplace(Sec
))
254 ToReplace
.push_back(&Sec
);
257 for (SectionBase
*S
: ToReplace
) {
258 SectionBase
*NewSection
= addSection(S
);
260 for (RelocationSection
*RS
: RelocationSections
) {
261 if (RS
->getSection() == S
)
262 RS
->setSection(NewSection
);
266 RemovePred
= [shouldReplace
, RemovePred
](const SectionBase
&Sec
) {
267 return shouldReplace(Sec
) || RemovePred(Sec
);
271 static bool isUnneededSymbol(const Symbol
&Sym
) {
272 return !Sym
.Referenced
&&
273 (Sym
.Binding
== STB_LOCAL
|| Sym
.getShndx() == SHN_UNDEF
) &&
274 Sym
.Type
!= STT_FILE
&& Sym
.Type
!= STT_SECTION
;
277 // This function handles the high level operations of GNU objcopy including
278 // handling command line options. It's important to outline certain properties
279 // we expect to hold of the command line operations. Any operation that "keeps"
280 // should keep regardless of a remove. Additionally any removal should respect
281 // any previous removals. Lastly whether or not something is removed shouldn't
282 // depend a) on the order the options occur in or b) on some opaque priority
283 // system. The only priority is that keeps/copies overrule removes.
284 static Error
handleArgs(const CopyConfig
&Config
, Object
&Obj
,
285 const Reader
&Reader
, ElfType OutputElfType
) {
287 if (!Config
.SplitDWO
.empty())
289 splitDWOToFile(Config
, Reader
, Config
.SplitDWO
, OutputElfType
))
292 if (Config
.OutputArch
)
293 Obj
.Machine
= Config
.OutputArch
.getValue().EMachine
;
295 // TODO: update or remove symbols only if there is an option that affects
297 if (Obj
.SymbolTable
) {
298 Obj
.SymbolTable
->updateSymbols([&](Symbol
&Sym
) {
299 // Common and undefined symbols don't make sense as local symbols, and can
300 // even cause crashes if we localize those, so skip them.
301 if (!Sym
.isCommon() && Sym
.getShndx() != SHN_UNDEF
&&
302 ((Config
.LocalizeHidden
&&
303 (Sym
.Visibility
== STV_HIDDEN
|| Sym
.Visibility
== STV_INTERNAL
)) ||
304 is_contained(Config
.SymbolsToLocalize
, Sym
.Name
)))
305 Sym
.Binding
= STB_LOCAL
;
307 // Note: these two globalize flags have very similar names but different
310 // --globalize-symbol: promote a symbol to global
311 // --keep-global-symbol: all symbols except for these should be made local
313 // If --globalize-symbol is specified for a given symbol, it will be
314 // global in the output file even if it is not included via
315 // --keep-global-symbol. Because of that, make sure to check
316 // --globalize-symbol second.
317 if (!Config
.SymbolsToKeepGlobal
.empty() &&
318 !is_contained(Config
.SymbolsToKeepGlobal
, Sym
.Name
) &&
319 Sym
.getShndx() != SHN_UNDEF
)
320 Sym
.Binding
= STB_LOCAL
;
322 if (is_contained(Config
.SymbolsToGlobalize
, Sym
.Name
) &&
323 Sym
.getShndx() != SHN_UNDEF
)
324 Sym
.Binding
= STB_GLOBAL
;
326 if (is_contained(Config
.SymbolsToWeaken
, Sym
.Name
) &&
327 Sym
.Binding
== STB_GLOBAL
)
328 Sym
.Binding
= STB_WEAK
;
330 if (Config
.Weaken
&& Sym
.Binding
== STB_GLOBAL
&&
331 Sym
.getShndx() != SHN_UNDEF
)
332 Sym
.Binding
= STB_WEAK
;
334 const auto I
= Config
.SymbolsToRename
.find(Sym
.Name
);
335 if (I
!= Config
.SymbolsToRename
.end())
336 Sym
.Name
= I
->getValue();
338 if (!Config
.SymbolsPrefix
.empty() && Sym
.Type
!= STT_SECTION
)
339 Sym
.Name
= (Config
.SymbolsPrefix
+ Sym
.Name
).str();
342 // The purpose of this loop is to mark symbols referenced by sections
343 // (like GroupSection or RelocationSection). This way, we know which
344 // symbols are still 'needed' and which are not.
345 if (Config
.StripUnneeded
|| !Config
.UnneededSymbolsToRemove
.empty()) {
346 for (auto &Section
: Obj
.sections())
347 Section
.markSymbols();
350 auto RemoveSymbolsPred
= [&](const Symbol
&Sym
) {
351 if (is_contained(Config
.SymbolsToKeep
, Sym
.Name
) ||
352 (Config
.KeepFileSymbols
&& Sym
.Type
== STT_FILE
))
355 if ((Config
.DiscardMode
== DiscardType::All
||
356 (Config
.DiscardMode
== DiscardType::Locals
&&
357 StringRef(Sym
.Name
).startswith(".L"))) &&
358 Sym
.Binding
== STB_LOCAL
&& Sym
.getShndx() != SHN_UNDEF
&&
359 Sym
.Type
!= STT_FILE
&& Sym
.Type
!= STT_SECTION
)
362 if (Config
.StripAll
|| Config
.StripAllGNU
)
365 if (is_contained(Config
.SymbolsToRemove
, Sym
.Name
))
368 if ((Config
.StripUnneeded
||
369 is_contained(Config
.UnneededSymbolsToRemove
, Sym
.Name
)) &&
370 isUnneededSymbol(Sym
))
375 if (Error E
= Obj
.removeSymbols(RemoveSymbolsPred
))
379 SectionPred RemovePred
= [](const SectionBase
&) { return false; };
382 if (!Config
.ToRemove
.empty()) {
383 RemovePred
= [&Config
](const SectionBase
&Sec
) {
384 return is_contained(Config
.ToRemove
, Sec
.Name
);
388 if (Config
.StripDWO
|| !Config
.SplitDWO
.empty())
389 RemovePred
= [RemovePred
](const SectionBase
&Sec
) {
390 return isDWOSection(Sec
) || RemovePred(Sec
);
393 if (Config
.ExtractDWO
)
394 RemovePred
= [RemovePred
, &Obj
](const SectionBase
&Sec
) {
395 return onlyKeepDWOPred(Obj
, Sec
) || RemovePred(Sec
);
398 if (Config
.StripAllGNU
)
399 RemovePred
= [RemovePred
, &Obj
](const SectionBase
&Sec
) {
402 if ((Sec
.Flags
& SHF_ALLOC
) != 0)
404 if (&Sec
== Obj
.SectionNames
)
413 return isDebugSection(Sec
);
416 if (Config
.StripSections
) {
417 RemovePred
= [RemovePred
](const SectionBase
&Sec
) {
418 return RemovePred(Sec
) || (Sec
.Flags
& SHF_ALLOC
) == 0;
422 if (Config
.StripDebug
) {
423 RemovePred
= [RemovePred
](const SectionBase
&Sec
) {
424 return RemovePred(Sec
) || isDebugSection(Sec
);
428 if (Config
.StripNonAlloc
)
429 RemovePred
= [RemovePred
, &Obj
](const SectionBase
&Sec
) {
432 if (&Sec
== Obj
.SectionNames
)
434 return (Sec
.Flags
& SHF_ALLOC
) == 0;
438 RemovePred
= [RemovePred
, &Obj
](const SectionBase
&Sec
) {
441 if (&Sec
== Obj
.SectionNames
)
443 if (StringRef(Sec
.Name
).startswith(".gnu.warning"))
445 return (Sec
.Flags
& SHF_ALLOC
) == 0;
449 if (!Config
.OnlySection
.empty()) {
450 RemovePred
= [&Config
, RemovePred
, &Obj
](const SectionBase
&Sec
) {
451 // Explicitly keep these sections regardless of previous removes.
452 if (is_contained(Config
.OnlySection
, Sec
.Name
))
455 // Allow all implicit removes.
459 // Keep special sections.
460 if (Obj
.SectionNames
== &Sec
)
462 if (Obj
.SymbolTable
== &Sec
||
463 (Obj
.SymbolTable
&& Obj
.SymbolTable
->getStrTab() == &Sec
))
466 // Remove everything else.
471 if (!Config
.KeepSection
.empty()) {
472 RemovePred
= [&Config
, RemovePred
](const SectionBase
&Sec
) {
473 // Explicitly keep these sections regardless of previous removes.
474 if (is_contained(Config
.KeepSection
, Sec
.Name
))
476 // Otherwise defer to RemovePred.
477 return RemovePred(Sec
);
481 // This has to be the last predicate assignment.
482 // If the option --keep-symbol has been specified
483 // and at least one of those symbols is present
484 // (equivalently, the updated symbol table is not empty)
485 // the symbol table and the string table should not be removed.
486 if ((!Config
.SymbolsToKeep
.empty() || Config
.KeepFileSymbols
) &&
487 Obj
.SymbolTable
&& !Obj
.SymbolTable
->empty()) {
488 RemovePred
= [&Obj
, RemovePred
](const SectionBase
&Sec
) {
489 if (&Sec
== Obj
.SymbolTable
|| &Sec
== Obj
.SymbolTable
->getStrTab())
491 return RemovePred(Sec
);
495 if (Config
.CompressionType
!= DebugCompressionType::None
)
496 replaceDebugSections(Config
, Obj
, RemovePred
, isCompressable
,
497 [&Config
, &Obj
](const SectionBase
*S
) {
498 return &Obj
.addSection
<CompressedSection
>(
499 *S
, Config
.CompressionType
);
501 else if (Config
.DecompressDebugSections
)
502 replaceDebugSections(
503 Config
, Obj
, RemovePred
,
504 [](const SectionBase
&S
) { return isa
<CompressedSection
>(&S
); },
505 [&Obj
](const SectionBase
*S
) {
506 auto CS
= cast
<CompressedSection
>(S
);
507 return &Obj
.addSection
<DecompressedSection
>(*CS
);
510 if (Error E
= Obj
.removeSections(RemovePred
))
513 if (!Config
.SectionsToRename
.empty()) {
514 for (auto &Sec
: Obj
.sections()) {
515 const auto Iter
= Config
.SectionsToRename
.find(Sec
.Name
);
516 if (Iter
!= Config
.SectionsToRename
.end()) {
517 const SectionRename
&SR
= Iter
->second
;
518 Sec
.Name
= SR
.NewName
;
519 if (SR
.NewFlags
.hasValue())
521 setSectionFlagsPreserveMask(Sec
.Flags
, SR
.NewFlags
.getValue());
526 if (!Config
.SetSectionFlags
.empty()) {
527 for (auto &Sec
: Obj
.sections()) {
528 const auto Iter
= Config
.SetSectionFlags
.find(Sec
.Name
);
529 if (Iter
!= Config
.SetSectionFlags
.end()) {
530 const SectionFlagsUpdate
&SFU
= Iter
->second
;
531 Sec
.Flags
= setSectionFlagsPreserveMask(Sec
.Flags
, SFU
.NewFlags
);
536 if (!Config
.AddSection
.empty()) {
537 for (const auto &Flag
: Config
.AddSection
) {
538 std::pair
<StringRef
, StringRef
> SecPair
= Flag
.split("=");
539 StringRef SecName
= SecPair
.first
;
540 StringRef File
= SecPair
.second
;
541 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> BufOrErr
=
542 MemoryBuffer::getFile(File
);
544 return createFileError(File
, errorCodeToError(BufOrErr
.getError()));
545 std::unique_ptr
<MemoryBuffer
> Buf
= std::move(*BufOrErr
);
546 ArrayRef
<uint8_t> Data(
547 reinterpret_cast<const uint8_t *>(Buf
->getBufferStart()),
548 Buf
->getBufferSize());
549 OwnedDataSection
&NewSection
=
550 Obj
.addSection
<OwnedDataSection
>(SecName
, Data
);
551 if (SecName
.startswith(".note") && SecName
!= ".note.GNU-stack")
552 NewSection
.Type
= SHT_NOTE
;
556 if (!Config
.DumpSection
.empty()) {
557 for (const auto &Flag
: Config
.DumpSection
) {
558 std::pair
<StringRef
, StringRef
> SecPair
= Flag
.split("=");
559 StringRef SecName
= SecPair
.first
;
560 StringRef File
= SecPair
.second
;
561 if (Error E
= dumpSectionToFile(SecName
, File
, Obj
))
562 return createFileError(Config
.InputFilename
, std::move(E
));
566 if (!Config
.AddGnuDebugLink
.empty())
567 Obj
.addSection
<GnuDebugLinkSection
>(Config
.AddGnuDebugLink
);
569 return Error::success();
572 Error
executeObjcopyOnRawBinary(const CopyConfig
&Config
, MemoryBuffer
&In
,
574 BinaryReader
Reader(Config
.BinaryArch
, &In
);
575 std::unique_ptr
<Object
> Obj
= Reader
.create();
577 // Prefer OutputArch (-O<format>) if set, otherwise fallback to BinaryArch
579 const ElfType OutputElfType
= getOutputElfType(
580 Config
.OutputArch
? Config
.OutputArch
.getValue() : Config
.BinaryArch
);
581 if (Error E
= handleArgs(Config
, *Obj
, Reader
, OutputElfType
))
583 std::unique_ptr
<Writer
> Writer
=
584 createWriter(Config
, *Obj
, Out
, OutputElfType
);
585 if (Error E
= Writer
->finalize())
587 return Writer
->write();
590 Error
executeObjcopyOnBinary(const CopyConfig
&Config
,
591 object::ELFObjectFileBase
&In
, Buffer
&Out
) {
592 ELFReader
Reader(&In
);
593 std::unique_ptr
<Object
> Obj
= Reader
.create();
594 // Prefer OutputArch (-O<format>) if set, otherwise infer it from the input.
595 const ElfType OutputElfType
=
596 Config
.OutputArch
? getOutputElfType(Config
.OutputArch
.getValue())
597 : getOutputElfType(In
);
598 ArrayRef
<uint8_t> BuildIdBytes
;
600 if (!Config
.BuildIdLinkDir
.empty()) {
601 BuildIdBytes
= unwrapOrError(findBuildID(In
));
602 if (BuildIdBytes
.size() < 2)
603 return createFileError(
604 Config
.InputFilename
,
605 createStringError(object_error::parse_failed
,
606 "build ID is smaller than two bytes."));
609 if (!Config
.BuildIdLinkDir
.empty() && Config
.BuildIdLinkInput
)
611 linkToBuildIdDir(Config
, Config
.InputFilename
,
612 Config
.BuildIdLinkInput
.getValue(), BuildIdBytes
))
615 if (Error E
= handleArgs(Config
, *Obj
, Reader
, OutputElfType
))
617 std::unique_ptr
<Writer
> Writer
=
618 createWriter(Config
, *Obj
, Out
, OutputElfType
);
619 if (Error E
= Writer
->finalize())
621 if (Error E
= Writer
->write())
623 if (!Config
.BuildIdLinkDir
.empty() && Config
.BuildIdLinkOutput
)
625 linkToBuildIdDir(Config
, Config
.OutputFilename
,
626 Config
.BuildIdLinkOutput
.getValue(), BuildIdBytes
))
629 return Error::success();
632 } // end namespace elf
633 } // end namespace objcopy
634 } // end namespace llvm