diff options
Diffstat (limited to 'llvm/tools/llvm-objcopy')
-rw-r--r-- | llvm/tools/llvm-objcopy/CMakeLists.txt | 3 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp | 503 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h | 34 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/ELF/Object.cpp (renamed from llvm/tools/llvm-objcopy/Object.cpp) | 0 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/ELF/Object.h (renamed from llvm/tools/llvm-objcopy/Object.h) | 0 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/llvm-objcopy.cpp | 472 |
6 files changed, 540 insertions, 472 deletions
diff --git a/llvm/tools/llvm-objcopy/CMakeLists.txt b/llvm/tools/llvm-objcopy/CMakeLists.txt index 9ac7d0eb4c2..afbf7879176 100644 --- a/llvm/tools/llvm-objcopy/CMakeLists.txt +++ b/llvm/tools/llvm-objcopy/CMakeLists.txt @@ -17,7 +17,8 @@ add_llvm_tool(llvm-objcopy Buffer.cpp CopyConfig.cpp llvm-objcopy.cpp - Object.cpp + ELF/ELFObjcopy.cpp + ELF/Object.cpp DEPENDS ObjcopyOptsTableGen StripOptsTableGen diff --git a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp new file mode 100644 index 00000000000..76379788205 --- /dev/null +++ b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -0,0 +1,503 @@ +//===- ELFObjcopy.cpp -----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ELFObjcopy.h" +#include "Buffer.h" +#include "CopyConfig.h" +#include "llvm-objcopy.h" +#include "Object.h" + +#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCTargetOptions.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Object/Error.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compression.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/Memory.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <cstdlib> +#include <functional> +#include <iterator> +#include <memory> +#include <string> +#include <system_error> +#include <utility> + +namespace llvm { +namespace objcopy { +namespace elf { + +using namespace object; +using namespace ELF; +using SectionPred = std::function<bool(const SectionBase &Sec)>; + +static bool isDebugSection(const SectionBase &Sec) { + return StringRef(Sec.Name).startswith(".debug") || + StringRef(Sec.Name).startswith(".zdebug") || Sec.Name == ".gdb_index"; +} + +static bool isDWOSection(const SectionBase &Sec) { + return StringRef(Sec.Name).endswith(".dwo"); +} + +static bool onlyKeepDWOPred(const Object &Obj, const SectionBase &Sec) { + // We can't remove the section header string table. + if (&Sec == Obj.SectionNames) + return false; + // Short of keeping the string table we want to keep everything that is a DWO + // section and remove everything else. + return !isDWOSection(Sec); +} + +static ElfType getOutputElfType(const Binary &Bin) { + // Infer output ELF type from the input ELF object + if (isa<ELFObjectFile<ELF32LE>>(Bin)) + return ELFT_ELF32LE; + if (isa<ELFObjectFile<ELF64LE>>(Bin)) + return ELFT_ELF64LE; + if (isa<ELFObjectFile<ELF32BE>>(Bin)) + return ELFT_ELF32BE; + if (isa<ELFObjectFile<ELF64BE>>(Bin)) + return ELFT_ELF64BE; + llvm_unreachable("Invalid ELFType"); +} + +static ElfType getOutputElfType(const MachineInfo &MI) { + // Infer output ELF type from the binary arch specified + if (MI.Is64Bit) + return MI.IsLittleEndian ? ELFT_ELF64LE : ELFT_ELF64BE; + else + return MI.IsLittleEndian ? ELFT_ELF32LE : ELFT_ELF32BE; +} + +static std::unique_ptr<Writer> createWriter(const CopyConfig &Config, + Object &Obj, Buffer &Buf, + ElfType OutputElfType) { + if (Config.OutputFormat == "binary") { + return llvm::make_unique<BinaryWriter>(Obj, Buf); + } + // Depending on the initial ELFT and OutputFormat we need a different Writer. + switch (OutputElfType) { + case ELFT_ELF32LE: + return llvm::make_unique<ELFWriter<ELF32LE>>(Obj, Buf, + !Config.StripSections); + case ELFT_ELF64LE: + return llvm::make_unique<ELFWriter<ELF64LE>>(Obj, Buf, + !Config.StripSections); + case ELFT_ELF32BE: + return llvm::make_unique<ELFWriter<ELF32BE>>(Obj, Buf, + !Config.StripSections); + case ELFT_ELF64BE: + return llvm::make_unique<ELFWriter<ELF64BE>>(Obj, Buf, + !Config.StripSections); + } + llvm_unreachable("Invalid output format"); +} + +static void splitDWOToFile(const CopyConfig &Config, const Reader &Reader, + StringRef File, ElfType OutputElfType) { + auto DWOFile = Reader.create(); + DWOFile->removeSections( + [&](const SectionBase &Sec) { return onlyKeepDWOPred(*DWOFile, Sec); }); + FileBuffer FB(File); + auto Writer = createWriter(Config, *DWOFile, FB, OutputElfType); + Writer->finalize(); + Writer->write(); +} + +static Error dumpSectionToFile(StringRef SecName, StringRef Filename, + Object &Obj) { + for (auto &Sec : Obj.sections()) { + if (Sec.Name == SecName) { + if (Sec.OriginalData.size() == 0) + return make_error<StringError>("Can't dump section \"" + SecName + + "\": it has no contents", + object_error::parse_failed); + Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = + FileOutputBuffer::create(Filename, Sec.OriginalData.size()); + if (!BufferOrErr) + return BufferOrErr.takeError(); + std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr); + std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(), + Buf->getBufferStart()); + if (Error E = Buf->commit()) + return E; + return Error::success(); + } + } + return make_error<StringError>("Section not found", + object_error::parse_failed); +} + +static bool isCompressed(const SectionBase &Section) { + const char *Magic = "ZLIB"; + return StringRef(Section.Name).startswith(".zdebug") || + (Section.OriginalData.size() > strlen(Magic) && + !strncmp(reinterpret_cast<const char *>(Section.OriginalData.data()), + Magic, strlen(Magic))) || + (Section.Flags & ELF::SHF_COMPRESSED); +} + +static bool isCompressable(const SectionBase &Section) { + return !isCompressed(Section) && isDebugSection(Section) && + Section.Name != ".gdb_index"; +} + +static void replaceDebugSections( + const CopyConfig &Config, Object &Obj, SectionPred &RemovePred, + function_ref<bool(const SectionBase &)> shouldReplace, + function_ref<SectionBase *(const SectionBase *)> addSection) { + SmallVector<SectionBase *, 13> ToReplace; + SmallVector<RelocationSection *, 13> RelocationSections; + for (auto &Sec : Obj.sections()) { + if (RelocationSection *R = dyn_cast<RelocationSection>(&Sec)) { + if (shouldReplace(*R->getSection())) + RelocationSections.push_back(R); + continue; + } + + if (shouldReplace(Sec)) + ToReplace.push_back(&Sec); + } + + for (SectionBase *S : ToReplace) { + SectionBase *NewSection = addSection(S); + + for (RelocationSection *RS : RelocationSections) { + if (RS->getSection() == S) + RS->setSection(NewSection); + } + } + + RemovePred = [shouldReplace, RemovePred](const SectionBase &Sec) { + return shouldReplace(Sec) || RemovePred(Sec); + }; +} + +// This function handles the high level operations of GNU objcopy including +// handling command line options. It's important to outline certain properties +// we expect to hold of the command line operations. Any operation that "keeps" +// should keep regardless of a remove. Additionally any removal should respect +// any previous removals. Lastly whether or not something is removed shouldn't +// depend a) on the order the options occur in or b) on some opaque priority +// system. The only priority is that keeps/copies overrule removes. +static void handleArgs(const CopyConfig &Config, Object &Obj, + const Reader &Reader, ElfType OutputElfType) { + + if (!Config.SplitDWO.empty()) { + splitDWOToFile(Config, Reader, Config.SplitDWO, OutputElfType); + } + + // TODO: update or remove symbols only if there is an option that affects + // them. + if (Obj.SymbolTable) { + Obj.SymbolTable->updateSymbols([&](Symbol &Sym) { + if ((Config.LocalizeHidden && + (Sym.Visibility == STV_HIDDEN || Sym.Visibility == STV_INTERNAL)) || + (!Config.SymbolsToLocalize.empty() && + is_contained(Config.SymbolsToLocalize, Sym.Name))) + Sym.Binding = STB_LOCAL; + + // Note: these two globalize flags have very similar names but different + // meanings: + // + // --globalize-symbol: promote a symbol to global + // --keep-global-symbol: all symbols except for these should be made local + // + // If --globalize-symbol is specified for a given symbol, it will be + // global in the output file even if it is not included via + // --keep-global-symbol. Because of that, make sure to check + // --globalize-symbol second. + if (!Config.SymbolsToKeepGlobal.empty() && + !is_contained(Config.SymbolsToKeepGlobal, Sym.Name)) + Sym.Binding = STB_LOCAL; + + if (!Config.SymbolsToGlobalize.empty() && + is_contained(Config.SymbolsToGlobalize, Sym.Name)) + Sym.Binding = STB_GLOBAL; + + if (!Config.SymbolsToWeaken.empty() && + is_contained(Config.SymbolsToWeaken, Sym.Name) && + Sym.Binding == STB_GLOBAL) + Sym.Binding = STB_WEAK; + + if (Config.Weaken && Sym.Binding == STB_GLOBAL && + Sym.getShndx() != SHN_UNDEF) + Sym.Binding = STB_WEAK; + + const auto I = Config.SymbolsToRename.find(Sym.Name); + if (I != Config.SymbolsToRename.end()) + Sym.Name = I->getValue(); + + if (!Config.SymbolsPrefix.empty() && Sym.Type != STT_SECTION) + Sym.Name = (Config.SymbolsPrefix + Sym.Name).str(); + }); + + // The purpose of this loop is to mark symbols referenced by sections + // (like GroupSection or RelocationSection). This way, we know which + // symbols are still 'needed' and which are not. + if (Config.StripUnneeded) { + for (auto &Section : Obj.sections()) + Section.markSymbols(); + } + + Obj.removeSymbols([&](const Symbol &Sym) { + if ((!Config.SymbolsToKeep.empty() && + is_contained(Config.SymbolsToKeep, Sym.Name)) || + (Config.KeepFileSymbols && Sym.Type == STT_FILE)) + return false; + + if (Config.DiscardAll && Sym.Binding == STB_LOCAL && + Sym.getShndx() != SHN_UNDEF && Sym.Type != STT_FILE && + Sym.Type != STT_SECTION) + return true; + + if (Config.StripAll || Config.StripAllGNU) + return true; + + if (!Config.SymbolsToRemove.empty() && + is_contained(Config.SymbolsToRemove, Sym.Name)) { + return true; + } + + if (Config.StripUnneeded && !Sym.Referenced && + (Sym.Binding == STB_LOCAL || Sym.getShndx() == SHN_UNDEF) && + Sym.Type != STT_FILE && Sym.Type != STT_SECTION) + return true; + + return false; + }); + } + + SectionPred RemovePred = [](const SectionBase &) { return false; }; + + // Removes: + if (!Config.ToRemove.empty()) { + RemovePred = [&Config](const SectionBase &Sec) { + return is_contained(Config.ToRemove, Sec.Name); + }; + } + + if (Config.StripDWO || !Config.SplitDWO.empty()) + RemovePred = [RemovePred](const SectionBase &Sec) { + return isDWOSection(Sec) || RemovePred(Sec); + }; + + if (Config.ExtractDWO) + RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { + return onlyKeepDWOPred(Obj, Sec) || RemovePred(Sec); + }; + + if (Config.StripAllGNU) + RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { + if (RemovePred(Sec)) + return true; + if ((Sec.Flags & SHF_ALLOC) != 0) + return false; + if (&Sec == Obj.SectionNames) + return false; + switch (Sec.Type) { + case SHT_SYMTAB: + case SHT_REL: + case SHT_RELA: + case SHT_STRTAB: + return true; + } + return isDebugSection(Sec); + }; + + if (Config.StripSections) { + RemovePred = [RemovePred](const SectionBase &Sec) { + return RemovePred(Sec) || (Sec.Flags & SHF_ALLOC) == 0; + }; + } + + if (Config.StripDebug) { + RemovePred = [RemovePred](const SectionBase &Sec) { + return RemovePred(Sec) || isDebugSection(Sec); + }; + } + + if (Config.StripNonAlloc) + RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { + if (RemovePred(Sec)) + return true; + if (&Sec == Obj.SectionNames) + return false; + return (Sec.Flags & SHF_ALLOC) == 0; + }; + + if (Config.StripAll) + RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { + if (RemovePred(Sec)) + return true; + if (&Sec == Obj.SectionNames) + return false; + if (StringRef(Sec.Name).startswith(".gnu.warning")) + return false; + return (Sec.Flags & SHF_ALLOC) == 0; + }; + + // Explicit copies: + if (!Config.OnlyKeep.empty()) { + RemovePred = [&Config, RemovePred, &Obj](const SectionBase &Sec) { + // Explicitly keep these sections regardless of previous removes. + if (is_contained(Config.OnlyKeep, Sec.Name)) + return false; + + // Allow all implicit removes. + if (RemovePred(Sec)) + return true; + + // Keep special sections. + if (Obj.SectionNames == &Sec) + return false; + if (Obj.SymbolTable == &Sec || + (Obj.SymbolTable && Obj.SymbolTable->getStrTab() == &Sec)) + return false; + + // Remove everything else. + return true; + }; + } + + if (!Config.Keep.empty()) { + RemovePred = [Config, RemovePred](const SectionBase &Sec) { + // Explicitly keep these sections regardless of previous removes. + if (is_contained(Config.Keep, Sec.Name)) + return false; + // Otherwise defer to RemovePred. + return RemovePred(Sec); + }; + } + + // This has to be the last predicate assignment. + // If the option --keep-symbol has been specified + // and at least one of those symbols is present + // (equivalently, the updated symbol table is not empty) + // the symbol table and the string table should not be removed. + if ((!Config.SymbolsToKeep.empty() || Config.KeepFileSymbols) && + Obj.SymbolTable && !Obj.SymbolTable->empty()) { + RemovePred = [&Obj, RemovePred](const SectionBase &Sec) { + if (&Sec == Obj.SymbolTable || &Sec == Obj.SymbolTable->getStrTab()) + return false; + return RemovePred(Sec); + }; + } + + if (Config.CompressionType != DebugCompressionType::None) + replaceDebugSections(Config, Obj, RemovePred, isCompressable, + [&Config, &Obj](const SectionBase *S) { + return &Obj.addSection<CompressedSection>( + *S, Config.CompressionType); + }); + else if (Config.DecompressDebugSections) + replaceDebugSections( + Config, Obj, RemovePred, + [](const SectionBase &S) { return isa<CompressedSection>(&S); }, + [&Obj](const SectionBase *S) { + auto CS = cast<CompressedSection>(S); + return &Obj.addSection<DecompressedSection>(*CS); + }); + + Obj.removeSections(RemovePred); + + if (!Config.SectionsToRename.empty()) { + for (auto &Sec : Obj.sections()) { + const auto Iter = Config.SectionsToRename.find(Sec.Name); + if (Iter != Config.SectionsToRename.end()) { + const SectionRename &SR = Iter->second; + Sec.Name = SR.NewName; + if (SR.NewFlags.hasValue()) { + // Preserve some flags which should not be dropped when setting flags. + // Also, preserve anything OS/processor dependant. + const uint64_t PreserveMask = ELF::SHF_COMPRESSED | ELF::SHF_EXCLUDE | + ELF::SHF_GROUP | ELF::SHF_LINK_ORDER | + ELF::SHF_MASKOS | ELF::SHF_MASKPROC | + ELF::SHF_TLS | ELF::SHF_INFO_LINK; + Sec.Flags = (Sec.Flags & PreserveMask) | + (SR.NewFlags.getValue() & ~PreserveMask); + } + } + } + } + + if (!Config.AddSection.empty()) { + for (const auto &Flag : Config.AddSection) { + auto SecPair = Flag.split("="); + auto SecName = SecPair.first; + auto File = SecPair.second; + auto BufOrErr = MemoryBuffer::getFile(File); + if (!BufOrErr) + reportError(File, BufOrErr.getError()); + auto Buf = std::move(*BufOrErr); + auto BufPtr = reinterpret_cast<const uint8_t *>(Buf->getBufferStart()); + auto BufSize = Buf->getBufferSize(); + Obj.addSection<OwnedDataSection>(SecName, + ArrayRef<uint8_t>(BufPtr, BufSize)); + } + } + + if (!Config.DumpSection.empty()) { + for (const auto &Flag : Config.DumpSection) { + std::pair<StringRef, StringRef> SecPair = Flag.split("="); + StringRef SecName = SecPair.first; + StringRef File = SecPair.second; + if (Error E = dumpSectionToFile(SecName, File, Obj)) + reportError(Config.InputFilename, std::move(E)); + } + } + + if (!Config.AddGnuDebugLink.empty()) + Obj.addSection<GnuDebugLinkSection>(Config.AddGnuDebugLink); +} + +void executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, + Buffer &Out) { + BinaryReader Reader(Config.BinaryArch, &In); + std::unique_ptr<Object> Obj = Reader.create(); + + const ElfType OutputElfType = getOutputElfType(Config.BinaryArch); + handleArgs(Config, *Obj, Reader, OutputElfType); + std::unique_ptr<Writer> Writer = + createWriter(Config, *Obj, Out, OutputElfType); + Writer->finalize(); + Writer->write(); +} + +void executeObjcopyOnBinary(const CopyConfig &Config, + object::ELFObjectFileBase &In, Buffer &Out) { + ELFReader Reader(&In); + std::unique_ptr<Object> Obj = Reader.create(); + const ElfType OutputElfType = getOutputElfType(In); + handleArgs(Config, *Obj, Reader, OutputElfType); + std::unique_ptr<Writer> Writer = + createWriter(Config, *Obj, Out, OutputElfType); + Writer->finalize(); + Writer->write(); +} + +} // end namespace elf +} // end namespace objcopy +} // end namespace llvm diff --git a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h new file mode 100644 index 00000000000..43f41c00ce5 --- /dev/null +++ b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.h @@ -0,0 +1,34 @@ +//===- ELFObjcopy.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_OBJCOPY_ELFOBJCOPY_H +#define LLVM_TOOLS_OBJCOPY_ELFOBJCOPY_H + +namespace llvm { +class MemoryBuffer; + +namespace object { +class ELFObjectFileBase; +} // end namespace object + +namespace objcopy { +struct CopyConfig; +class Buffer; + +namespace elf { +void executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, + Buffer &Out); +void executeObjcopyOnBinary(const CopyConfig &Config, + object::ELFObjectFileBase &In, Buffer &Out); + +} // end namespace elf +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_ELFOBJCOPY_H diff --git a/llvm/tools/llvm-objcopy/Object.cpp b/llvm/tools/llvm-objcopy/ELF/Object.cpp index 5b2138436d5..5b2138436d5 100644 --- a/llvm/tools/llvm-objcopy/Object.cpp +++ b/llvm/tools/llvm-objcopy/ELF/Object.cpp diff --git a/llvm/tools/llvm-objcopy/Object.h b/llvm/tools/llvm-objcopy/ELF/Object.h index 38ef21ffec9..38ef21ffec9 100644 --- a/llvm/tools/llvm-objcopy/Object.h +++ b/llvm/tools/llvm-objcopy/ELF/Object.h diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp index b7e2361cc01..deaea5eff85 100644 --- a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp +++ b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp @@ -10,16 +10,12 @@ #include "llvm-objcopy.h" #include "Buffer.h" #include "CopyConfig.h" -#include "Object.h" +#include "ELF/ELFObjcopy.h" -#include "llvm/ADT/BitmaskEnum.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" -#include "llvm/BinaryFormat/ELF.h" -#include "llvm/MC/MCTargetOptions.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/Binary.h" @@ -30,13 +26,9 @@ #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/Casting.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Compiler.h" -#include "llvm/Support/Compression.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" -#include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/Memory.h" #include "llvm/Support/Path.h" @@ -46,8 +38,6 @@ #include <algorithm> #include <cassert> #include <cstdlib> -#include <functional> -#include <iterator> #include <memory> #include <string> #include <system_error> @@ -85,466 +75,6 @@ LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Error E) { } // end namespace objcopy } // end namespace llvm -// TODO: move everything enclosed in the namespace llvm::objcopy::elf -// into separate header+cpp files. -namespace llvm { -namespace objcopy { -namespace elf { - -using namespace object; -using namespace ELF; -using SectionPred = std::function<bool(const SectionBase &Sec)>; - -static bool isDebugSection(const SectionBase &Sec) { - return StringRef(Sec.Name).startswith(".debug") || - StringRef(Sec.Name).startswith(".zdebug") || Sec.Name == ".gdb_index"; -} - -static bool isDWOSection(const SectionBase &Sec) { - return StringRef(Sec.Name).endswith(".dwo"); -} - -static bool onlyKeepDWOPred(const Object &Obj, const SectionBase &Sec) { - // We can't remove the section header string table. - if (&Sec == Obj.SectionNames) - return false; - // Short of keeping the string table we want to keep everything that is a DWO - // section and remove everything else. - return !isDWOSection(Sec); -} - -static ElfType getOutputElfType(const Binary &Bin) { - // Infer output ELF type from the input ELF object - if (isa<ELFObjectFile<ELF32LE>>(Bin)) - return ELFT_ELF32LE; - if (isa<ELFObjectFile<ELF64LE>>(Bin)) - return ELFT_ELF64LE; - if (isa<ELFObjectFile<ELF32BE>>(Bin)) - return ELFT_ELF32BE; - if (isa<ELFObjectFile<ELF64BE>>(Bin)) - return ELFT_ELF64BE; - llvm_unreachable("Invalid ELFType"); -} - -static ElfType getOutputElfType(const MachineInfo &MI) { - // Infer output ELF type from the binary arch specified - if (MI.Is64Bit) - return MI.IsLittleEndian ? ELFT_ELF64LE : ELFT_ELF64BE; - else - return MI.IsLittleEndian ? ELFT_ELF32LE : ELFT_ELF32BE; -} - -static std::unique_ptr<Writer> createWriter(const CopyConfig &Config, - Object &Obj, Buffer &Buf, - ElfType OutputElfType) { - if (Config.OutputFormat == "binary") { - return llvm::make_unique<BinaryWriter>(Obj, Buf); - } - // Depending on the initial ELFT and OutputFormat we need a different Writer. - switch (OutputElfType) { - case ELFT_ELF32LE: - return llvm::make_unique<ELFWriter<ELF32LE>>(Obj, Buf, - !Config.StripSections); - case ELFT_ELF64LE: - return llvm::make_unique<ELFWriter<ELF64LE>>(Obj, Buf, - !Config.StripSections); - case ELFT_ELF32BE: - return llvm::make_unique<ELFWriter<ELF32BE>>(Obj, Buf, - !Config.StripSections); - case ELFT_ELF64BE: - return llvm::make_unique<ELFWriter<ELF64BE>>(Obj, Buf, - !Config.StripSections); - } - llvm_unreachable("Invalid output format"); -} - -static void splitDWOToFile(const CopyConfig &Config, const Reader &Reader, - StringRef File, ElfType OutputElfType) { - auto DWOFile = Reader.create(); - DWOFile->removeSections( - [&](const SectionBase &Sec) { return onlyKeepDWOPred(*DWOFile, Sec); }); - FileBuffer FB(File); - auto Writer = createWriter(Config, *DWOFile, FB, OutputElfType); - Writer->finalize(); - Writer->write(); -} - -static Error dumpSectionToFile(StringRef SecName, StringRef Filename, - Object &Obj) { - for (auto &Sec : Obj.sections()) { - if (Sec.Name == SecName) { - if (Sec.OriginalData.size() == 0) - return make_error<StringError>("Can't dump section \"" + SecName + - "\": it has no contents", - object_error::parse_failed); - Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = - FileOutputBuffer::create(Filename, Sec.OriginalData.size()); - if (!BufferOrErr) - return BufferOrErr.takeError(); - std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr); - std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(), - Buf->getBufferStart()); - if (Error E = Buf->commit()) - return E; - return Error::success(); - } - } - return make_error<StringError>("Section not found", - object_error::parse_failed); -} - -static bool isCompressed(const SectionBase &Section) { - const char *Magic = "ZLIB"; - return StringRef(Section.Name).startswith(".zdebug") || - (Section.OriginalData.size() > strlen(Magic) && - !strncmp(reinterpret_cast<const char *>(Section.OriginalData.data()), - Magic, strlen(Magic))) || - (Section.Flags & ELF::SHF_COMPRESSED); -} - -static bool isCompressable(const SectionBase &Section) { - return !isCompressed(Section) && isDebugSection(Section) && - Section.Name != ".gdb_index"; -} - -static void replaceDebugSections( - const CopyConfig &Config, Object &Obj, SectionPred &RemovePred, - function_ref<bool(const SectionBase &)> shouldReplace, - function_ref<SectionBase *(const SectionBase *)> addSection) { - SmallVector<SectionBase *, 13> ToReplace; - SmallVector<RelocationSection *, 13> RelocationSections; - for (auto &Sec : Obj.sections()) { - if (RelocationSection *R = dyn_cast<RelocationSection>(&Sec)) { - if (shouldReplace(*R->getSection())) - RelocationSections.push_back(R); - continue; - } - - if (shouldReplace(Sec)) - ToReplace.push_back(&Sec); - } - - for (SectionBase *S : ToReplace) { - SectionBase *NewSection = addSection(S); - - for (RelocationSection *RS : RelocationSections) { - if (RS->getSection() == S) - RS->setSection(NewSection); - } - } - - RemovePred = [shouldReplace, RemovePred](const SectionBase &Sec) { - return shouldReplace(Sec) || RemovePred(Sec); - }; -} - -// This function handles the high level operations of GNU objcopy including -// handling command line options. It's important to outline certain properties -// we expect to hold of the command line operations. Any operation that "keeps" -// should keep regardless of a remove. Additionally any removal should respect -// any previous removals. Lastly whether or not something is removed shouldn't -// depend a) on the order the options occur in or b) on some opaque priority -// system. The only priority is that keeps/copies overrule removes. -static void handleArgs(const CopyConfig &Config, Object &Obj, - const Reader &Reader, ElfType OutputElfType) { - - if (!Config.SplitDWO.empty()) { - splitDWOToFile(Config, Reader, Config.SplitDWO, OutputElfType); - } - - // TODO: update or remove symbols only if there is an option that affects - // them. - if (Obj.SymbolTable) { - Obj.SymbolTable->updateSymbols([&](Symbol &Sym) { - if ((Config.LocalizeHidden && - (Sym.Visibility == STV_HIDDEN || Sym.Visibility == STV_INTERNAL)) || - (!Config.SymbolsToLocalize.empty() && - is_contained(Config.SymbolsToLocalize, Sym.Name))) - Sym.Binding = STB_LOCAL; - - // Note: these two globalize flags have very similar names but different - // meanings: - // - // --globalize-symbol: promote a symbol to global - // --keep-global-symbol: all symbols except for these should be made local - // - // If --globalize-symbol is specified for a given symbol, it will be - // global in the output file even if it is not included via - // --keep-global-symbol. Because of that, make sure to check - // --globalize-symbol second. - if (!Config.SymbolsToKeepGlobal.empty() && - !is_contained(Config.SymbolsToKeepGlobal, Sym.Name)) - Sym.Binding = STB_LOCAL; - - if (!Config.SymbolsToGlobalize.empty() && - is_contained(Config.SymbolsToGlobalize, Sym.Name)) - Sym.Binding = STB_GLOBAL; - - if (!Config.SymbolsToWeaken.empty() && - is_contained(Config.SymbolsToWeaken, Sym.Name) && - Sym.Binding == STB_GLOBAL) - Sym.Binding = STB_WEAK; - - if (Config.Weaken && Sym.Binding == STB_GLOBAL && - Sym.getShndx() != SHN_UNDEF) - Sym.Binding = STB_WEAK; - - const auto I = Config.SymbolsToRename.find(Sym.Name); - if (I != Config.SymbolsToRename.end()) - Sym.Name = I->getValue(); - - if (!Config.SymbolsPrefix.empty() && Sym.Type != STT_SECTION) - Sym.Name = (Config.SymbolsPrefix + Sym.Name).str(); - }); - - // The purpose of this loop is to mark symbols referenced by sections - // (like GroupSection or RelocationSection). This way, we know which - // symbols are still 'needed' and which are not. - if (Config.StripUnneeded) { - for (auto &Section : Obj.sections()) - Section.markSymbols(); - } - - Obj.removeSymbols([&](const Symbol &Sym) { - if ((!Config.SymbolsToKeep.empty() && - is_contained(Config.SymbolsToKeep, Sym.Name)) || - (Config.KeepFileSymbols && Sym.Type == STT_FILE)) - return false; - - if (Config.DiscardAll && Sym.Binding == STB_LOCAL && - Sym.getShndx() != SHN_UNDEF && Sym.Type != STT_FILE && - Sym.Type != STT_SECTION) - return true; - - if (Config.StripAll || Config.StripAllGNU) - return true; - - if (!Config.SymbolsToRemove.empty() && - is_contained(Config.SymbolsToRemove, Sym.Name)) { - return true; - } - - if (Config.StripUnneeded && !Sym.Referenced && - (Sym.Binding == STB_LOCAL || Sym.getShndx() == SHN_UNDEF) && - Sym.Type != STT_FILE && Sym.Type != STT_SECTION) - return true; - - return false; - }); - } - - SectionPred RemovePred = [](const SectionBase &) { return false; }; - - // Removes: - if (!Config.ToRemove.empty()) { - RemovePred = [&Config](const SectionBase &Sec) { - return is_contained(Config.ToRemove, Sec.Name); - }; - } - - if (Config.StripDWO || !Config.SplitDWO.empty()) - RemovePred = [RemovePred](const SectionBase &Sec) { - return isDWOSection(Sec) || RemovePred(Sec); - }; - - if (Config.ExtractDWO) - RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { - return onlyKeepDWOPred(Obj, Sec) || RemovePred(Sec); - }; - - if (Config.StripAllGNU) - RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { - if (RemovePred(Sec)) - return true; - if ((Sec.Flags & SHF_ALLOC) != 0) - return false; - if (&Sec == Obj.SectionNames) - return false; - switch (Sec.Type) { - case SHT_SYMTAB: - case SHT_REL: - case SHT_RELA: - case SHT_STRTAB: - return true; - } - return isDebugSection(Sec); - }; - - if (Config.StripSections) { - RemovePred = [RemovePred](const SectionBase &Sec) { - return RemovePred(Sec) || (Sec.Flags & SHF_ALLOC) == 0; - }; - } - - if (Config.StripDebug) { - RemovePred = [RemovePred](const SectionBase &Sec) { - return RemovePred(Sec) || isDebugSection(Sec); - }; - } - - if (Config.StripNonAlloc) - RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { - if (RemovePred(Sec)) - return true; - if (&Sec == Obj.SectionNames) - return false; - return (Sec.Flags & SHF_ALLOC) == 0; - }; - - if (Config.StripAll) - RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { - if (RemovePred(Sec)) - return true; - if (&Sec == Obj.SectionNames) - return false; - if (StringRef(Sec.Name).startswith(".gnu.warning")) - return false; - return (Sec.Flags & SHF_ALLOC) == 0; - }; - - // Explicit copies: - if (!Config.OnlyKeep.empty()) { - RemovePred = [&Config, RemovePred, &Obj](const SectionBase &Sec) { - // Explicitly keep these sections regardless of previous removes. - if (is_contained(Config.OnlyKeep, Sec.Name)) - return false; - - // Allow all implicit removes. - if (RemovePred(Sec)) - return true; - - // Keep special sections. - if (Obj.SectionNames == &Sec) - return false; - if (Obj.SymbolTable == &Sec || - (Obj.SymbolTable && Obj.SymbolTable->getStrTab() == &Sec)) - return false; - - // Remove everything else. - return true; - }; - } - - if (!Config.Keep.empty()) { - RemovePred = [Config, RemovePred](const SectionBase &Sec) { - // Explicitly keep these sections regardless of previous removes. - if (is_contained(Config.Keep, Sec.Name)) - return false; - // Otherwise defer to RemovePred. - return RemovePred(Sec); - }; - } - - // This has to be the last predicate assignment. - // If the option --keep-symbol has been specified - // and at least one of those symbols is present - // (equivalently, the updated symbol table is not empty) - // the symbol table and the string table should not be removed. - if ((!Config.SymbolsToKeep.empty() || Config.KeepFileSymbols) && - Obj.SymbolTable && !Obj.SymbolTable->empty()) { - RemovePred = [&Obj, RemovePred](const SectionBase &Sec) { - if (&Sec == Obj.SymbolTable || &Sec == Obj.SymbolTable->getStrTab()) - return false; - return RemovePred(Sec); - }; - } - - if (Config.CompressionType != DebugCompressionType::None) - replaceDebugSections(Config, Obj, RemovePred, isCompressable, - [&Config, &Obj](const SectionBase *S) { - return &Obj.addSection<CompressedSection>( - *S, Config.CompressionType); - }); - else if (Config.DecompressDebugSections) - replaceDebugSections( - Config, Obj, RemovePred, - [](const SectionBase &S) { return isa<CompressedSection>(&S); }, - [&Obj](const SectionBase *S) { - auto CS = cast<CompressedSection>(S); - return &Obj.addSection<DecompressedSection>(*CS); - }); - - Obj.removeSections(RemovePred); - - if (!Config.SectionsToRename.empty()) { - for (auto &Sec : Obj.sections()) { - const auto Iter = Config.SectionsToRename.find(Sec.Name); - if (Iter != Config.SectionsToRename.end()) { - const SectionRename &SR = Iter->second; - Sec.Name = SR.NewName; - if (SR.NewFlags.hasValue()) { - // Preserve some flags which should not be dropped when setting flags. - // Also, preserve anything OS/processor dependant. - const uint64_t PreserveMask = ELF::SHF_COMPRESSED | ELF::SHF_EXCLUDE | - ELF::SHF_GROUP | ELF::SHF_LINK_ORDER | - ELF::SHF_MASKOS | ELF::SHF_MASKPROC | - ELF::SHF_TLS | ELF::SHF_INFO_LINK; - Sec.Flags = (Sec.Flags & PreserveMask) | - (SR.NewFlags.getValue() & ~PreserveMask); - } - } - } - } - - if (!Config.AddSection.empty()) { - for (const auto &Flag : Config.AddSection) { - auto SecPair = Flag.split("="); - auto SecName = SecPair.first; - auto File = SecPair.second; - auto BufOrErr = MemoryBuffer::getFile(File); - if (!BufOrErr) - reportError(File, BufOrErr.getError()); - auto Buf = std::move(*BufOrErr); - auto BufPtr = reinterpret_cast<const uint8_t *>(Buf->getBufferStart()); - auto BufSize = Buf->getBufferSize(); - Obj.addSection<OwnedDataSection>(SecName, - ArrayRef<uint8_t>(BufPtr, BufSize)); - } - } - - if (!Config.DumpSection.empty()) { - for (const auto &Flag : Config.DumpSection) { - std::pair<StringRef, StringRef> SecPair = Flag.split("="); - StringRef SecName = SecPair.first; - StringRef File = SecPair.second; - if (Error E = dumpSectionToFile(SecName, File, Obj)) - reportError(Config.InputFilename, std::move(E)); - } - } - - if (!Config.AddGnuDebugLink.empty()) - Obj.addSection<GnuDebugLinkSection>(Config.AddGnuDebugLink); -} - -void executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, - Buffer &Out) { - BinaryReader Reader(Config.BinaryArch, &In); - std::unique_ptr<Object> Obj = Reader.create(); - - const ElfType OutputElfType = getOutputElfType(Config.BinaryArch); - handleArgs(Config, *Obj, Reader, OutputElfType); - std::unique_ptr<Writer> Writer = - createWriter(Config, *Obj, Out, OutputElfType); - Writer->finalize(); - Writer->write(); -} - -void executeObjcopyOnBinary(const CopyConfig &Config, - object::ELFObjectFileBase &In, Buffer &Out) { - ELFReader Reader(&In); - std::unique_ptr<Object> Obj = Reader.create(); - const ElfType OutputElfType = getOutputElfType(In); - handleArgs(Config, *Obj, Reader, OutputElfType); - std::unique_ptr<Writer> Writer = - createWriter(Config, *Obj, Out, OutputElfType); - Writer->finalize(); - Writer->write(); -} - -} // end namespace elf -} // end namespace objcopy -} // end namespace llvm - using namespace llvm; using namespace llvm::object; using namespace llvm::objcopy; |