aboutsummaryrefslogtreecommitdiff
path: root/tools/llvm-objcopy
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2019-10-23 17:51:42 +0000
committerDimitry Andric <dim@FreeBSD.org>2019-10-23 17:51:42 +0000
commit1d5ae1026e831016fc29fd927877c86af904481f (patch)
tree2cdfd12620fcfa5d9e4a0389f85368e8e36f63f9 /tools/llvm-objcopy
parente6d1592492a3a379186bfb02bd0f4eda0669c0d5 (diff)
Notes
Diffstat (limited to 'tools/llvm-objcopy')
-rw-r--r--tools/llvm-objcopy/COFF/COFFObjcopy.cpp88
-rw-r--r--tools/llvm-objcopy/COFF/Reader.cpp18
-rw-r--r--tools/llvm-objcopy/COFF/Writer.cpp4
-rw-r--r--tools/llvm-objcopy/CommonOpts.td123
-rw-r--r--tools/llvm-objcopy/CopyConfig.cpp370
-rw-r--r--tools/llvm-objcopy/CopyConfig.h110
-rw-r--r--tools/llvm-objcopy/ELF/ELFConfig.cpp133
-rw-r--r--tools/llvm-objcopy/ELF/ELFConfig.h44
-rw-r--r--tools/llvm-objcopy/ELF/ELFObjcopy.cpp169
-rw-r--r--tools/llvm-objcopy/ELF/Object.cpp252
-rw-r--r--tools/llvm-objcopy/ELF/Object.h56
-rw-r--r--tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp350
-rw-r--r--tools/llvm-objcopy/MachO/MachOLayoutBuilder.h50
-rw-r--r--tools/llvm-objcopy/MachO/MachOObjcopy.cpp30
-rw-r--r--tools/llvm-objcopy/MachO/MachOReader.cpp45
-rw-r--r--tools/llvm-objcopy/MachO/MachOReader.h3
-rw-r--r--tools/llvm-objcopy/MachO/MachOWriter.cpp305
-rw-r--r--tools/llvm-objcopy/MachO/MachOWriter.h19
-rw-r--r--tools/llvm-objcopy/MachO/Object.h27
-rw-r--r--tools/llvm-objcopy/ObjcopyOpts.td141
-rw-r--r--tools/llvm-objcopy/StripOpts.td103
-rw-r--r--tools/llvm-objcopy/llvm-objcopy.cpp53
22 files changed, 1587 insertions, 906 deletions
diff --git a/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
index 4ae46851a66f..2a8d816e6f3c 100644
--- a/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
+++ b/tools/llvm-objcopy/COFF/COFFObjcopy.cpp
@@ -16,8 +16,8 @@
#include "llvm/Object/Binary.h"
#include "llvm/Object/COFF.h"
+#include "llvm/Support/CRC.h"
#include "llvm/Support/Errc.h"
-#include "llvm/Support/JamCRC.h"
#include "llvm/Support/Path.h"
#include <cassert>
@@ -40,22 +40,13 @@ static uint64_t getNextRVA(const Object &Obj) {
Obj.IsPE ? Obj.PeHeader.SectionAlignment : 1);
}
-static uint32_t getCRC32(StringRef Data) {
- JamCRC CRC;
- CRC.update(ArrayRef<char>(Data.data(), Data.size()));
- // The CRC32 value needs to be complemented because the JamCRC dosn't
- // finalize the CRC32 value. It also dosn't negate the initial CRC32 value
- // but it starts by default at 0xFFFFFFFF which is the complement of zero.
- return ~CRC.getCRC();
-}
-
static std::vector<uint8_t> createGnuDebugLinkSectionContents(StringRef File) {
ErrorOr<std::unique_ptr<MemoryBuffer>> LinkTargetOrErr =
MemoryBuffer::getFile(File);
if (!LinkTargetOrErr)
error("'" + File + "': " + LinkTargetOrErr.getError().message());
auto LinkTarget = std::move(*LinkTargetOrErr);
- uint32_t CRC32 = getCRC32(LinkTarget->getBuffer());
+ uint32_t CRC32 = llvm::crc32(arrayRefFromStringRef(LinkTarget->getBuffer()));
StringRef FileName = sys::path::filename(File);
size_t CRCPos = alignTo(FileName.size() + 1, 4);
@@ -65,26 +56,37 @@ static std::vector<uint8_t> createGnuDebugLinkSectionContents(StringRef File) {
return Data;
}
-static void addGnuDebugLink(Object &Obj, StringRef DebugLinkFile) {
- uint32_t StartRVA = getNextRVA(Obj);
+// Adds named section with given contents to the object.
+static void addSection(Object &Obj, StringRef Name, ArrayRef<uint8_t> Contents,
+ uint32_t Characteristics) {
+ bool NeedVA = Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ |
+ IMAGE_SCN_MEM_WRITE);
- std::vector<Section> Sections;
Section Sec;
- Sec.setOwnedContents(createGnuDebugLinkSectionContents(DebugLinkFile));
- Sec.Name = ".gnu_debuglink";
- Sec.Header.VirtualSize = Sec.getContents().size();
- Sec.Header.VirtualAddress = StartRVA;
- Sec.Header.SizeOfRawData = alignTo(Sec.Header.VirtualSize,
- Obj.IsPE ? Obj.PeHeader.FileAlignment : 1);
+ Sec.setOwnedContents(Contents);
+ Sec.Name = Name;
+ Sec.Header.VirtualSize = NeedVA ? Sec.getContents().size() : 0u;
+ Sec.Header.VirtualAddress = NeedVA ? getNextRVA(Obj) : 0u;
+ Sec.Header.SizeOfRawData =
+ NeedVA ? alignTo(Sec.Header.VirtualSize,
+ Obj.IsPE ? Obj.PeHeader.FileAlignment : 1)
+ : Sec.getContents().size();
// Sec.Header.PointerToRawData is filled in by the writer.
Sec.Header.PointerToRelocations = 0;
Sec.Header.PointerToLinenumbers = 0;
// Sec.Header.NumberOfRelocations is filled in by the writer.
Sec.Header.NumberOfLinenumbers = 0;
- Sec.Header.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA |
- IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE;
- Sections.push_back(Sec);
- Obj.addSections(Sections);
+ Sec.Header.Characteristics = Characteristics;
+
+ Obj.addSections(Sec);
+}
+
+static void addGnuDebugLink(Object &Obj, StringRef DebugLinkFile) {
+ std::vector<uint8_t> Contents =
+ createGnuDebugLinkSectionContents(DebugLinkFile);
+ addSection(Obj, ".gnu_debuglink", Contents,
+ IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ |
+ IMAGE_SCN_MEM_DISCARDABLE);
}
static Error handleArgs(const CopyConfig &Config, Object &Obj) {
@@ -92,8 +94,7 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
Obj.removeSections([&Config](const Section &Sec) {
// Contrary to --only-keep-debug, --only-section fully removes sections that
// aren't mentioned.
- if (!Config.OnlySection.empty() &&
- !is_contained(Config.OnlySection, Sec.Name))
+ if (!Config.OnlySection.empty() && !Config.OnlySection.matches(Sec.Name))
return true;
if (Config.StripDebug || Config.StripAll || Config.StripAllGNU ||
@@ -103,7 +104,7 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
return true;
}
- if (is_contained(Config.ToRemove, Sec.Name))
+ if (Config.ToRemove.matches(Sec.Name))
return true;
return false;
@@ -137,7 +138,7 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
if (Config.StripAll || Config.StripAllGNU)
return true;
- if (is_contained(Config.SymbolsToRemove, Sym.Name)) {
+ if (Config.SymbolsToRemove.matches(Sym.Name)) {
// Explicitly removing a referenced symbol is an error.
if (Sym.Referenced)
reportError(Config.OutputFilename,
@@ -156,7 +157,7 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
if (Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC ||
Sym.Sym.SectionNumber == 0)
if (Config.StripUnneeded ||
- is_contained(Config.UnneededSymbolsToRemove, Sym.Name))
+ Config.UnneededSymbolsToRemove.matches(Sym.Name))
return true;
// GNU objcopy keeps referenced local symbols and external symbols
@@ -171,21 +172,38 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
return false;
});
+ for (const auto &Flag : Config.AddSection) {
+ StringRef SecName, FileName;
+ std::tie(SecName, FileName) = Flag.split("=");
+
+ auto BufOrErr = MemoryBuffer::getFile(FileName);
+ if (!BufOrErr)
+ return createFileError(FileName, errorCodeToError(BufOrErr.getError()));
+ auto Buf = std::move(*BufOrErr);
+
+ addSection(
+ Obj, SecName,
+ makeArrayRef(reinterpret_cast<const uint8_t *>(Buf->getBufferStart()),
+ Buf->getBufferSize()),
+ IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_1BYTES);
+ }
+
if (!Config.AddGnuDebugLink.empty())
addGnuDebugLink(Obj, Config.AddGnuDebugLink);
if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() ||
Config.BuildIdLinkInput || Config.BuildIdLinkOutput ||
!Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() ||
- !Config.AllocSectionsPrefix.empty() || !Config.AddSection.empty() ||
- !Config.DumpSection.empty() || !Config.KeepSection.empty() ||
+ !Config.AllocSectionsPrefix.empty() || !Config.DumpSection.empty() ||
+ !Config.KeepSection.empty() || Config.NewSymbolVisibility ||
!Config.SymbolsToGlobalize.empty() || !Config.SymbolsToKeep.empty() ||
!Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() ||
!Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() ||
- !Config.SetSectionFlags.empty() || !Config.SymbolsToRename.empty() ||
- Config.ExtractDWO || Config.KeepFileSymbols || Config.LocalizeHidden ||
- Config.PreserveDates || Config.StripDWO || Config.StripNonAlloc ||
- Config.StripSections || Config.Weaken || Config.DecompressDebugSections ||
+ !Config.SetSectionAlignment.empty() || !Config.SetSectionFlags.empty() ||
+ !Config.SymbolsToRename.empty() || Config.ExtractDWO ||
+ Config.KeepFileSymbols || Config.LocalizeHidden || Config.PreserveDates ||
+ Config.StripDWO || Config.StripNonAlloc || Config.StripSections ||
+ Config.Weaken || Config.DecompressDebugSections ||
Config.DiscardMode == DiscardType::Locals ||
!Config.SymbolsToAdd.empty() || Config.EntryExpr) {
return createStringError(llvm::errc::invalid_argument,
diff --git a/tools/llvm-objcopy/COFF/Reader.cpp b/tools/llvm-objcopy/COFF/Reader.cpp
index 1f0ec9fa9691..2fcec0057c03 100644
--- a/tools/llvm-objcopy/COFF/Reader.cpp
+++ b/tools/llvm-objcopy/COFF/Reader.cpp
@@ -36,14 +36,9 @@ Error COFFReader::readExecutableHeaders(Object &Obj) const {
DH->AddressOfNewExeHeader - sizeof(*DH));
if (COFFObj.is64()) {
- const pe32plus_header *PE32Plus = nullptr;
- if (auto EC = COFFObj.getPE32PlusHeader(PE32Plus))
- return errorCodeToError(EC);
- Obj.PeHeader = *PE32Plus;
+ Obj.PeHeader = *COFFObj.getPE32PlusHeader();
} else {
- const pe32_header *PE32 = nullptr;
- if (auto EC = COFFObj.getPE32Header(PE32))
- return errorCodeToError(EC);
+ const pe32_header *PE32 = COFFObj.getPE32Header();
copyPeHeader(Obj.PeHeader, *PE32);
// The pe32plus_header (stored in Object) lacks the BaseOfData field.
Obj.BaseOfData = PE32->BaseOfData;
@@ -196,16 +191,13 @@ Error COFFReader::setSymbolTargets(Object &Obj) const {
}
Expected<std::unique_ptr<Object>> COFFReader::create() const {
- auto Obj = llvm::make_unique<Object>();
+ auto Obj = std::make_unique<Object>();
- const coff_file_header *CFH = nullptr;
- const coff_bigobj_file_header *CBFH = nullptr;
- COFFObj.getCOFFHeader(CFH);
- COFFObj.getCOFFBigObjHeader(CBFH);
bool IsBigObj = false;
- if (CFH) {
+ if (const coff_file_header *CFH = COFFObj.getCOFFHeader()) {
Obj->CoffFileHeader = *CFH;
} else {
+ const coff_bigobj_file_header *CBFH = COFFObj.getCOFFBigObjHeader();
if (!CBFH)
return createStringError(object_error::parse_failed,
"no COFF file header returned");
diff --git a/tools/llvm-objcopy/COFF/Writer.cpp b/tools/llvm-objcopy/COFF/Writer.cpp
index f3bb1ce331f2..6db37435fd96 100644
--- a/tools/llvm-objcopy/COFF/Writer.cpp
+++ b/tools/llvm-objcopy/COFF/Writer.cpp
@@ -120,12 +120,12 @@ size_t COFFWriter::finalizeStringTable() {
StrTabBuilder.finalize();
for (auto &S : Obj.getMutableSections()) {
+ memset(S.Header.Name, 0, sizeof(S.Header.Name));
if (S.Name.size() > COFF::NameSize) {
- memset(S.Header.Name, 0, sizeof(S.Header.Name));
snprintf(S.Header.Name, sizeof(S.Header.Name), "/%d",
(int)StrTabBuilder.getOffset(S.Name));
} else {
- strncpy(S.Header.Name, S.Name.data(), COFF::NameSize);
+ memcpy(S.Header.Name, S.Name.data(), S.Name.size());
}
}
for (auto &S : Obj.getMutableSymbols()) {
diff --git a/tools/llvm-objcopy/CommonOpts.td b/tools/llvm-objcopy/CommonOpts.td
new file mode 100644
index 000000000000..e8c092b44431
--- /dev/null
+++ b/tools/llvm-objcopy/CommonOpts.td
@@ -0,0 +1,123 @@
+include "llvm/Option/OptParser.td"
+
+multiclass Eq<string name, string help> {
+ def NAME : Separate<["--"], name>;
+ def NAME #_eq : Joined<["--"], name #"=">,
+ Alias<!cast<Separate>(NAME)>,
+ HelpText<help>;
+}
+
+def help : Flag<["--"], "help">;
+def h : Flag<["-"], "h">, Alias<help>;
+
+def allow_broken_links
+ : Flag<["--"], "allow-broken-links">,
+ HelpText<"Allow the tool to remove sections even if it would leave "
+ "invalid section references. The appropriate sh_link fields "
+ "will be set to zero.">;
+
+def enable_deterministic_archives
+ : Flag<["--"], "enable-deterministic-archives">,
+ HelpText<"Enable deterministic mode when operating on archives (use "
+ "zero for UIDs, GIDs, and timestamps).">;
+def D : Flag<["-"], "D">,
+ Alias<enable_deterministic_archives>,
+ HelpText<"Alias for --enable-deterministic-archives">;
+
+def disable_deterministic_archives
+ : Flag<["--"], "disable-deterministic-archives">,
+ HelpText<"Disable deterministic mode when operating on archives (use "
+ "real values for UIDs, GIDs, and timestamps).">;
+def U : Flag<["-"], "U">,
+ Alias<disable_deterministic_archives>,
+ HelpText<"Alias for --disable-deterministic-archives">;
+
+def preserve_dates : Flag<["--"], "preserve-dates">,
+ HelpText<"Preserve access and modification timestamps">;
+def p : Flag<["-"], "p">,
+ Alias<preserve_dates>,
+ HelpText<"Alias for --preserve-dates">;
+
+def strip_all : Flag<["--"], "strip-all">,
+ HelpText<"Remove non-allocated sections outside segments. "
+ ".gnu.warning* sections are not removed">;
+
+def strip_all_gnu
+ : Flag<["--"], "strip-all-gnu">,
+ HelpText<"Compatible with GNU's --strip-all">;
+
+def strip_debug : Flag<["--"], "strip-debug">,
+ HelpText<"Remove all debug sections">;
+def g : Flag<["-"], "g">,
+ Alias<strip_debug>,
+ HelpText<"Alias for --strip-debug">;
+
+def strip_unneeded : Flag<["--"], "strip-unneeded">,
+ HelpText<"Remove all symbols not needed by relocations">;
+
+defm remove_section : Eq<"remove-section", "Remove <section>">,
+ MetaVarName<"section">;
+def R : JoinedOrSeparate<["-"], "R">,
+ Alias<remove_section>,
+ HelpText<"Alias for --remove-section">;
+
+def strip_sections
+ : Flag<["--"], "strip-sections">,
+ HelpText<"Remove all section headers and all sections not in segments">;
+
+defm strip_symbol : Eq<"strip-symbol", "Strip <symbol>">,
+ MetaVarName<"symbol">;
+def N : JoinedOrSeparate<["-"], "N">,
+ Alias<strip_symbol>,
+ HelpText<"Alias for --strip-symbol">;
+
+defm keep_section : Eq<"keep-section", "Keep <section>">,
+ MetaVarName<"section">;
+
+defm keep_symbol : Eq<"keep-symbol", "Do not remove symbol <symbol>">,
+ MetaVarName<"symbol">;
+def K : JoinedOrSeparate<["-"], "K">,
+ Alias<keep_symbol>,
+ HelpText<"Alias for --keep-symbol">;
+
+def keep_file_symbols : Flag<["--"], "keep-file-symbols">,
+ HelpText<"Do not remove file symbols">;
+
+def only_keep_debug
+ : Flag<["--"], "only-keep-debug">,
+ HelpText<"Clear sections that would not be stripped by --strip-debug. "
+ "Currently only implemented for COFF.">;
+
+def discard_locals : Flag<["--"], "discard-locals">,
+ HelpText<"Remove compiler-generated local symbols, (e.g. "
+ "symbols starting with .L)">;
+def X : Flag<["-"], "X">,
+ Alias<discard_locals>,
+ HelpText<"Alias for --discard-locals">;
+
+def discard_all
+ : Flag<["--"], "discard-all">,
+ HelpText<"Remove all local symbols except file and section symbols">;
+def x : Flag<["-"], "x">,
+ Alias<discard_all>,
+ HelpText<"Alias for --discard-all">;
+
+def regex
+ : Flag<["--"], "regex">,
+ HelpText<"Permit regular expressions in name comparison">;
+
+def version : Flag<["--"], "version">,
+ HelpText<"Print the version and exit.">;
+def V : Flag<["-"], "V">,
+ Alias<version>,
+ HelpText<"Alias for --version">;
+
+def wildcard
+ : Flag<["--"], "wildcard">,
+ HelpText<"Allow wildcard syntax for symbol-related flags. Incompatible "
+ "with --regex. Allows using '*' to match any number of "
+ "characters, '?' to match any single character, '\' to escape "
+ "special characters, and '[]' to define character classes. "
+ "Wildcards beginning with '!' will prevent a match, for example "
+ "\"-N '*' -N '!x'\" will strip all symbols except for \"x\".">;
+def w : Flag<["-"], "w">, Alias<wildcard>, HelpText<"Alias for --wildcard">;
diff --git a/tools/llvm-objcopy/CopyConfig.cpp b/tools/llvm-objcopy/CopyConfig.cpp
index 8d6431b3044f..d707bec20c49 100644
--- a/tools/llvm-objcopy/CopyConfig.cpp
+++ b/tools/llvm-objcopy/CopyConfig.cpp
@@ -14,10 +14,10 @@
#include "llvm/ADT/StringSet.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
+#include "llvm/Support/CRC.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/Errc.h"
-#include "llvm/Support/JamCRC.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/StringSaver.h"
#include <memory>
@@ -155,6 +155,25 @@ static Expected<SectionRename> parseRenameSectionValue(StringRef FlagValue) {
return SR;
}
+static Expected<std::pair<StringRef, uint64_t>>
+parseSetSectionAlignment(StringRef FlagValue) {
+ if (!FlagValue.contains('='))
+ return createStringError(
+ errc::invalid_argument,
+ "bad format for --set-section-alignment: missing '='");
+ auto Split = StringRef(FlagValue).split('=');
+ if (Split.first.empty())
+ return createStringError(
+ errc::invalid_argument,
+ "bad format for --set-section-alignment: missing section name");
+ uint64_t NewAlign;
+ if (Split.second.getAsInteger(0, NewAlign))
+ return createStringError(errc::invalid_argument,
+ "invalid alignment for --set-section-alignment: '%s'",
+ Split.second.str().c_str());
+ return std::make_pair(Split.first, NewAlign);
+}
+
static Expected<SectionFlagsUpdate>
parseSetSectionFlagValue(StringRef FlagValue) {
if (!StringRef(FlagValue).contains('='))
@@ -177,106 +196,6 @@ parseSetSectionFlagValue(StringRef FlagValue) {
return SFU;
}
-static Expected<NewSymbolInfo> parseNewSymbolInfo(StringRef FlagValue) {
- // Parse value given with --add-symbol option and create the
- // new symbol if possible. The value format for --add-symbol is:
- //
- // <name>=[<section>:]<value>[,<flags>]
- //
- // where:
- // <name> - symbol name, can be empty string
- // <section> - optional section name. If not given ABS symbol is created
- // <value> - symbol value, can be decimal or hexadecimal number prefixed
- // with 0x.
- // <flags> - optional flags affecting symbol type, binding or visibility:
- // The following are currently supported:
- //
- // global, local, weak, default, hidden, file, section, object,
- // indirect-function.
- //
- // The following flags are ignored and provided for GNU
- // compatibility only:
- //
- // warning, debug, constructor, indirect, synthetic,
- // unique-object, before=<symbol>.
- NewSymbolInfo SI;
- StringRef Value;
- std::tie(SI.SymbolName, Value) = FlagValue.split('=');
- if (Value.empty())
- return createStringError(
- errc::invalid_argument,
- "bad format for --add-symbol, missing '=' after '%s'",
- SI.SymbolName.str().c_str());
-
- if (Value.contains(':')) {
- std::tie(SI.SectionName, Value) = Value.split(':');
- if (SI.SectionName.empty() || Value.empty())
- return createStringError(
- errc::invalid_argument,
- "bad format for --add-symbol, missing section name or symbol value");
- }
-
- SmallVector<StringRef, 6> Flags;
- Value.split(Flags, ',');
- if (Flags[0].getAsInteger(0, SI.Value))
- return createStringError(errc::invalid_argument, "bad symbol value: '%s'",
- Flags[0].str().c_str());
-
- using Functor = std::function<void(void)>;
- SmallVector<StringRef, 6> UnsupportedFlags;
- for (size_t I = 1, NumFlags = Flags.size(); I < NumFlags; ++I)
- static_cast<Functor>(
- StringSwitch<Functor>(Flags[I])
- .CaseLower("global", [&SI] { SI.Bind = ELF::STB_GLOBAL; })
- .CaseLower("local", [&SI] { SI.Bind = ELF::STB_LOCAL; })
- .CaseLower("weak", [&SI] { SI.Bind = ELF::STB_WEAK; })
- .CaseLower("default", [&SI] { SI.Visibility = ELF::STV_DEFAULT; })
- .CaseLower("hidden", [&SI] { SI.Visibility = ELF::STV_HIDDEN; })
- .CaseLower("file", [&SI] { SI.Type = ELF::STT_FILE; })
- .CaseLower("section", [&SI] { SI.Type = ELF::STT_SECTION; })
- .CaseLower("object", [&SI] { SI.Type = ELF::STT_OBJECT; })
- .CaseLower("function", [&SI] { SI.Type = ELF::STT_FUNC; })
- .CaseLower("indirect-function",
- [&SI] { SI.Type = ELF::STT_GNU_IFUNC; })
- .CaseLower("debug", [] {})
- .CaseLower("constructor", [] {})
- .CaseLower("warning", [] {})
- .CaseLower("indirect", [] {})
- .CaseLower("synthetic", [] {})
- .CaseLower("unique-object", [] {})
- .StartsWithLower("before", [] {})
- .Default([&] { UnsupportedFlags.push_back(Flags[I]); }))();
- if (!UnsupportedFlags.empty())
- return createStringError(errc::invalid_argument,
- "unsupported flag%s for --add-symbol: '%s'",
- UnsupportedFlags.size() > 1 ? "s" : "",
- join(UnsupportedFlags, "', '").c_str());
- return SI;
-}
-
-static const StringMap<MachineInfo> ArchMap{
- // Name, {EMachine, 64bit, LittleEndian}
- {"aarch64", {ELF::EM_AARCH64, true, true}},
- {"arm", {ELF::EM_ARM, false, true}},
- {"i386", {ELF::EM_386, false, true}},
- {"i386:x86-64", {ELF::EM_X86_64, true, true}},
- {"mips", {ELF::EM_MIPS, false, false}},
- {"powerpc:common64", {ELF::EM_PPC64, true, true}},
- {"riscv:rv32", {ELF::EM_RISCV, false, true}},
- {"riscv:rv64", {ELF::EM_RISCV, true, true}},
- {"sparc", {ELF::EM_SPARC, false, false}},
- {"sparcel", {ELF::EM_SPARC, false, true}},
- {"x86-64", {ELF::EM_X86_64, true, true}},
-};
-
-static Expected<const MachineInfo &> getMachineInfo(StringRef Arch) {
- auto Iter = ArchMap.find(Arch);
- if (Iter == std::end(ArchMap))
- return createStringError(errc::invalid_argument,
- "invalid architecture: '%s'", Arch.str().c_str());
- return Iter->getValue();
-}
-
struct TargetInfo {
FileFormat Format;
MachineInfo Machine;
@@ -341,9 +260,10 @@ getOutputTargetInfoByTargetName(StringRef TargetName) {
return {TargetInfo{Format, MI}};
}
-static Error addSymbolsFromFile(std::vector<NameOrRegex> &Symbols,
- BumpPtrAllocator &Alloc, StringRef Filename,
- bool UseRegex) {
+static Error
+addSymbolsFromFile(NameMatcher &Symbols, BumpPtrAllocator &Alloc,
+ StringRef Filename, MatchStyle MS,
+ llvm::function_ref<Error(Error)> ErrorCallback) {
StringSaver Saver(Alloc);
SmallVector<StringRef, 16> Lines;
auto BufOrErr = MemoryBuffer::getFile(Filename);
@@ -356,21 +276,47 @@ static Error addSymbolsFromFile(std::vector<NameOrRegex> &Symbols,
// it's not empty.
auto TrimmedLine = Line.split('#').first.trim();
if (!TrimmedLine.empty())
- Symbols.emplace_back(Saver.save(TrimmedLine), UseRegex);
+ if (Error E = Symbols.addMatcher(NameOrPattern::create(
+ Saver.save(TrimmedLine), MS, ErrorCallback)))
+ return E;
}
return Error::success();
}
-NameOrRegex::NameOrRegex(StringRef Pattern, bool IsRegex) {
- if (!IsRegex) {
- Name = Pattern;
- return;
- }
+Expected<NameOrPattern>
+NameOrPattern::create(StringRef Pattern, MatchStyle MS,
+ llvm::function_ref<Error(Error)> ErrorCallback) {
+ switch (MS) {
+ case MatchStyle::Literal:
+ return NameOrPattern(Pattern);
+ case MatchStyle::Wildcard: {
+ SmallVector<char, 32> Data;
+ bool IsPositiveMatch = true;
+ if (Pattern[0] == '!') {
+ IsPositiveMatch = false;
+ Pattern = Pattern.drop_front();
+ }
+ Expected<GlobPattern> GlobOrErr = GlobPattern::create(Pattern);
+
+ // If we couldn't create it as a glob, report the error, but try again with
+ // a literal if the error reporting is non-fatal.
+ if (!GlobOrErr) {
+ if (Error E = ErrorCallback(GlobOrErr.takeError()))
+ return std::move(E);
+ return create(Pattern, MatchStyle::Literal, ErrorCallback);
+ }
- SmallVector<char, 32> Data;
- R = std::make_shared<Regex>(
- ("^" + Pattern.ltrim('^').rtrim('$') + "$").toStringRef(Data));
+ return NameOrPattern(std::make_shared<GlobPattern>(*GlobOrErr),
+ IsPositiveMatch);
+ }
+ case MatchStyle::Regex: {
+ SmallVector<char, 32> Data;
+ return NameOrPattern(std::make_shared<Regex>(
+ ("^" + Pattern.ltrim('^').rtrim('$') + "$").toStringRef(Data)));
+ }
+ }
+ llvm_unreachable("Unhandled llvm.objcopy.MatchStyle enum");
}
static Error addSymbolsToRenameFromFile(StringMap<StringRef> &SymbolsToRename,
@@ -407,10 +353,22 @@ template <class T> static ErrorOr<T> getAsInteger(StringRef Val) {
return Result;
}
+static void printHelp(const opt::OptTable &OptTable, raw_ostream &OS,
+ StringRef ToolName) {
+ OptTable.PrintHelp(OS, (ToolName + " input [output]").str().c_str(),
+ (ToolName + " tool").str().c_str());
+ // TODO: Replace this with libOption call once it adds extrahelp support.
+ // The CommandLine library has a cl::extrahelp class to support this,
+ // but libOption does not have that yet.
+ OS << "\nPass @FILE as argument to read options from FILE.\n";
+}
+
// ParseObjcopyOptions returns the config and sets the input arguments. If a
// help flag is set then ParseObjcopyOptions will print the help messege and
// exit.
-Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) {
+Expected<DriverConfig>
+parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
+ llvm::function_ref<Error(Error)> ErrorCallback) {
DriverConfig DC;
ObjcopyOptTable T;
unsigned MissingArgumentIndex, MissingArgumentCount;
@@ -418,12 +376,12 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) {
T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
if (InputArgs.size() == 0) {
- T.PrintHelp(errs(), "llvm-objcopy input [output]", "objcopy tool");
+ printHelp(T, errs(), "llvm-objcopy");
exit(1);
}
if (InputArgs.hasArg(OBJCOPY_help)) {
- T.PrintHelp(outs(), "llvm-objcopy input [output]", "objcopy tool");
+ printHelp(T, outs(), "llvm-objcopy");
exit(0);
}
@@ -459,7 +417,18 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) {
errc::invalid_argument,
"--target cannot be used with --input-target or --output-target");
- bool UseRegex = InputArgs.hasArg(OBJCOPY_regex);
+ if (InputArgs.hasArg(OBJCOPY_regex) && InputArgs.hasArg(OBJCOPY_wildcard))
+ return createStringError(errc::invalid_argument,
+ "--regex and --wildcard are incompatible");
+
+ MatchStyle SectionMatchStyle = InputArgs.hasArg(OBJCOPY_regex)
+ ? MatchStyle::Regex
+ : MatchStyle::Wildcard;
+ MatchStyle SymbolMatchStyle = InputArgs.hasArg(OBJCOPY_regex)
+ ? MatchStyle::Regex
+ : InputArgs.hasArg(OBJCOPY_wildcard)
+ ? MatchStyle::Wildcard
+ : MatchStyle::Literal;
StringRef InputFormat, OutputFormat;
if (InputArgs.hasArg(OBJCOPY_target)) {
InputFormat = InputArgs.getLastArgValue(OBJCOPY_target);
@@ -476,28 +445,26 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) {
.Case("binary", FileFormat::Binary)
.Case("ihex", FileFormat::IHex)
.Default(FileFormat::Unspecified);
- if (Config.InputFormat == FileFormat::Binary) {
- auto BinaryArch = InputArgs.getLastArgValue(OBJCOPY_binary_architecture);
- if (BinaryArch.empty())
- return createStringError(
- errc::invalid_argument,
- "specified binary input without specifiying an architecture");
- Expected<const MachineInfo &> MI = getMachineInfo(BinaryArch);
- if (!MI)
- return MI.takeError();
- Config.BinaryArch = *MI;
- }
+
+ if (InputArgs.hasArg(OBJCOPY_new_symbol_visibility))
+ Config.NewSymbolVisibility =
+ InputArgs.getLastArgValue(OBJCOPY_new_symbol_visibility);
Config.OutputFormat = StringSwitch<FileFormat>(OutputFormat)
.Case("binary", FileFormat::Binary)
.Case("ihex", FileFormat::IHex)
.Default(FileFormat::Unspecified);
- if (Config.OutputFormat == FileFormat::Unspecified && !OutputFormat.empty()) {
- Expected<TargetInfo> Target = getOutputTargetInfoByTargetName(OutputFormat);
- if (!Target)
- return Target.takeError();
- Config.OutputFormat = Target->Format;
- Config.OutputArch = Target->Machine;
+ if (Config.OutputFormat == FileFormat::Unspecified) {
+ if (OutputFormat.empty()) {
+ Config.OutputFormat = Config.InputFormat;
+ } else {
+ Expected<TargetInfo> Target =
+ getOutputTargetInfoByTargetName(OutputFormat);
+ if (!Target)
+ return Target.takeError();
+ Config.OutputFormat = Target->Format;
+ Config.OutputArch = Target->Machine;
+ }
}
if (auto Arg = InputArgs.getLastArg(OBJCOPY_compress_debug_sections,
@@ -535,12 +502,8 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) {
if (!DebugOrErr)
return createFileError(Config.AddGnuDebugLink, DebugOrErr.getError());
auto Debug = std::move(*DebugOrErr);
- JamCRC CRC;
- CRC.update(
- ArrayRef<char>(Debug->getBuffer().data(), Debug->getBuffer().size()));
- // The CRC32 value needs to be complemented because the JamCRC doesn't
- // finalize the CRC32 value.
- Config.GnuDebugLinkCRC32 = ~CRC.getCRC();
+ Config.GnuDebugLinkCRC32 =
+ llvm::crc32(arrayRefFromStringRef(Debug->getBuffer()));
}
Config.BuildIdLinkDir = InputArgs.getLastArgValue(OBJCOPY_build_id_link_dir);
if (InputArgs.hasArg(OBJCOPY_build_id_link_input))
@@ -582,6 +545,13 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) {
"multiple renames of section '%s'",
SR->OriginalName.str().c_str());
}
+ for (auto Arg : InputArgs.filtered(OBJCOPY_set_section_alignment)) {
+ Expected<std::pair<StringRef, uint64_t>> NameAndAlign =
+ parseSetSectionAlignment(Arg->getValue());
+ if (!NameAndAlign)
+ return NameAndAlign.takeError();
+ Config.SetSectionAlignment[NameAndAlign->first] = NameAndAlign->second;
+ }
for (auto Arg : InputArgs.filtered(OBJCOPY_set_section_flags)) {
Expected<SectionFlagsUpdate> SFU =
parseSetSectionFlagValue(Arg->getValue());
@@ -612,13 +582,28 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) {
}
for (auto Arg : InputArgs.filtered(OBJCOPY_remove_section))
- Config.ToRemove.emplace_back(Arg->getValue(), UseRegex);
+ if (Error E = Config.ToRemove.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SectionMatchStyle, ErrorCallback)))
+ return std::move(E);
for (auto Arg : InputArgs.filtered(OBJCOPY_keep_section))
- Config.KeepSection.emplace_back(Arg->getValue(), UseRegex);
+ if (Error E = Config.KeepSection.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SectionMatchStyle, ErrorCallback)))
+ return std::move(E);
for (auto Arg : InputArgs.filtered(OBJCOPY_only_section))
- Config.OnlySection.emplace_back(Arg->getValue(), UseRegex);
- for (auto Arg : InputArgs.filtered(OBJCOPY_add_section))
- Config.AddSection.push_back(Arg->getValue());
+ if (Error E = Config.OnlySection.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SectionMatchStyle, ErrorCallback)))
+ return std::move(E);
+ for (auto Arg : InputArgs.filtered(OBJCOPY_add_section)) {
+ StringRef ArgValue(Arg->getValue());
+ if (!ArgValue.contains('='))
+ return createStringError(errc::invalid_argument,
+ "bad format for --add-section: missing '='");
+ if (ArgValue.split("=").second.empty())
+ return createStringError(
+ errc::invalid_argument,
+ "bad format for --add-section: missing file name");
+ Config.AddSection.push_back(ArgValue);
+ }
for (auto Arg : InputArgs.filtered(OBJCOPY_dump_section))
Config.DumpSection.push_back(Arg->getValue());
Config.StripAll = InputArgs.hasArg(OBJCOPY_strip_all);
@@ -645,53 +630,71 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) {
if (Config.DiscardMode == DiscardType::All)
Config.StripDebug = true;
for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbol))
- Config.SymbolsToLocalize.emplace_back(Arg->getValue(), UseRegex);
+ if (Error E = Config.SymbolsToLocalize.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
+ return std::move(E);
for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbols))
if (Error E = addSymbolsFromFile(Config.SymbolsToLocalize, DC.Alloc,
- Arg->getValue(), UseRegex))
+ Arg->getValue(), SymbolMatchStyle,
+ ErrorCallback))
return std::move(E);
for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbol))
- Config.SymbolsToKeepGlobal.emplace_back(Arg->getValue(), UseRegex);
+ if (Error E = Config.SymbolsToKeepGlobal.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
+ return std::move(E);
for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbols))
if (Error E = addSymbolsFromFile(Config.SymbolsToKeepGlobal, DC.Alloc,
- Arg->getValue(), UseRegex))
+ Arg->getValue(), SymbolMatchStyle,
+ ErrorCallback))
return std::move(E);
for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbol))
- Config.SymbolsToGlobalize.emplace_back(Arg->getValue(), UseRegex);
+ if (Error E = Config.SymbolsToGlobalize.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
+ return std::move(E);
for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbols))
if (Error E = addSymbolsFromFile(Config.SymbolsToGlobalize, DC.Alloc,
- Arg->getValue(), UseRegex))
+ Arg->getValue(), SymbolMatchStyle,
+ ErrorCallback))
return std::move(E);
for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbol))
- Config.SymbolsToWeaken.emplace_back(Arg->getValue(), UseRegex);
+ if (Error E = Config.SymbolsToWeaken.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
+ return std::move(E);
for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbols))
if (Error E = addSymbolsFromFile(Config.SymbolsToWeaken, DC.Alloc,
- Arg->getValue(), UseRegex))
+ Arg->getValue(), SymbolMatchStyle,
+ ErrorCallback))
return std::move(E);
for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbol))
- Config.SymbolsToRemove.emplace_back(Arg->getValue(), UseRegex);
+ if (Error E = Config.SymbolsToRemove.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
+ return std::move(E);
for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbols))
if (Error E = addSymbolsFromFile(Config.SymbolsToRemove, DC.Alloc,
- Arg->getValue(), UseRegex))
+ Arg->getValue(), SymbolMatchStyle,
+ ErrorCallback))
return std::move(E);
for (auto Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbol))
- Config.UnneededSymbolsToRemove.emplace_back(Arg->getValue(), UseRegex);
+ if (Error E =
+ Config.UnneededSymbolsToRemove.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
+ return std::move(E);
for (auto Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbols))
if (Error E = addSymbolsFromFile(Config.UnneededSymbolsToRemove, DC.Alloc,
- Arg->getValue(), UseRegex))
+ Arg->getValue(), SymbolMatchStyle,
+ ErrorCallback))
return std::move(E);
for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbol))
- Config.SymbolsToKeep.emplace_back(Arg->getValue(), UseRegex);
+ if (Error E = Config.SymbolsToKeep.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
+ return std::move(E);
for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbols))
- if (Error E = addSymbolsFromFile(Config.SymbolsToKeep, DC.Alloc,
- Arg->getValue(), UseRegex))
+ if (Error E =
+ addSymbolsFromFile(Config.SymbolsToKeep, DC.Alloc, Arg->getValue(),
+ SymbolMatchStyle, ErrorCallback))
return std::move(E);
- for (auto Arg : InputArgs.filtered(OBJCOPY_add_symbol)) {
- Expected<NewSymbolInfo> NSI = parseNewSymbolInfo(Arg->getValue());
- if (!NSI)
- return NSI.takeError();
- Config.SymbolsToAdd.push_back(*NSI);
- }
+ for (auto Arg : InputArgs.filtered(OBJCOPY_add_symbol))
+ Config.SymbolsToAdd.push_back(Arg->getValue());
Config.AllowBrokenLinks = InputArgs.hasArg(OBJCOPY_allow_broken_links);
@@ -754,19 +757,19 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) {
// exit.
Expected<DriverConfig>
parseStripOptions(ArrayRef<const char *> ArgsArr,
- std::function<Error(Error)> ErrorCallback) {
+ llvm::function_ref<Error(Error)> ErrorCallback) {
StripOptTable T;
unsigned MissingArgumentIndex, MissingArgumentCount;
llvm::opt::InputArgList InputArgs =
T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
if (InputArgs.size() == 0) {
- T.PrintHelp(errs(), "llvm-strip [options] file...", "strip tool");
+ printHelp(T, errs(), "llvm-strip");
exit(1);
}
if (InputArgs.hasArg(STRIP_help)) {
- T.PrintHelp(outs(), "llvm-strip [options] file...", "strip tool");
+ printHelp(T, outs(), "llvm-strip");
exit(0);
}
@@ -792,7 +795,17 @@ parseStripOptions(ArrayRef<const char *> ArgsArr,
"multiple input files cannot be used in combination with -o");
CopyConfig Config;
- bool UseRegexp = InputArgs.hasArg(STRIP_regex);
+
+ if (InputArgs.hasArg(STRIP_regex) && InputArgs.hasArg(STRIP_wildcard))
+ return createStringError(errc::invalid_argument,
+ "--regex and --wildcard are incompatible");
+ MatchStyle SectionMatchStyle =
+ InputArgs.hasArg(STRIP_regex) ? MatchStyle::Regex : MatchStyle::Wildcard;
+ MatchStyle SymbolMatchStyle = InputArgs.hasArg(STRIP_regex)
+ ? MatchStyle::Regex
+ : InputArgs.hasArg(STRIP_wildcard)
+ ? MatchStyle::Wildcard
+ : MatchStyle::Literal;
Config.AllowBrokenLinks = InputArgs.hasArg(STRIP_allow_broken_links);
Config.StripDebug = InputArgs.hasArg(STRIP_strip_debug);
@@ -801,6 +814,7 @@ parseStripOptions(ArrayRef<const char *> ArgsArr,
InputArgs.hasFlag(STRIP_discard_all, STRIP_discard_locals)
? DiscardType::All
: DiscardType::Locals;
+ Config.StripSections = InputArgs.hasArg(STRIP_strip_sections);
Config.StripUnneeded = InputArgs.hasArg(STRIP_strip_unneeded);
if (auto Arg = InputArgs.getLastArg(STRIP_strip_all, STRIP_no_strip_all))
Config.StripAll = Arg->getOption().getID() == STRIP_strip_all;
@@ -809,16 +823,24 @@ parseStripOptions(ArrayRef<const char *> ArgsArr,
Config.KeepFileSymbols = InputArgs.hasArg(STRIP_keep_file_symbols);
for (auto Arg : InputArgs.filtered(STRIP_keep_section))
- Config.KeepSection.emplace_back(Arg->getValue(), UseRegexp);
+ if (Error E = Config.KeepSection.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SectionMatchStyle, ErrorCallback)))
+ return std::move(E);
for (auto Arg : InputArgs.filtered(STRIP_remove_section))
- Config.ToRemove.emplace_back(Arg->getValue(), UseRegexp);
+ if (Error E = Config.ToRemove.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SectionMatchStyle, ErrorCallback)))
+ return std::move(E);
for (auto Arg : InputArgs.filtered(STRIP_strip_symbol))
- Config.SymbolsToRemove.emplace_back(Arg->getValue(), UseRegexp);
+ if (Error E = Config.SymbolsToRemove.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
+ return std::move(E);
for (auto Arg : InputArgs.filtered(STRIP_keep_symbol))
- Config.SymbolsToKeep.emplace_back(Arg->getValue(), UseRegexp);
+ if (Error E = Config.SymbolsToKeep.addMatcher(NameOrPattern::create(
+ Arg->getValue(), SymbolMatchStyle, ErrorCallback)))
+ return std::move(E);
if (!InputArgs.hasArg(STRIP_no_strip_all) && !Config.StripDebug &&
!Config.StripUnneeded && Config.DiscardMode == DiscardType::None &&
diff --git a/tools/llvm-objcopy/CopyConfig.h b/tools/llvm-objcopy/CopyConfig.h
index aff3631a487c..55a55d3a2bc2 100644
--- a/tools/llvm-objcopy/CopyConfig.h
+++ b/tools/llvm-objcopy/CopyConfig.h
@@ -9,6 +9,7 @@
#ifndef LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H
#define LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H
+#include "ELF/ELFConfig.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/BitmaskEnum.h"
#include "llvm/ADT/Optional.h"
@@ -18,6 +19,7 @@
#include "llvm/Object/ELFTypes.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/GlobPattern.h"
#include "llvm/Support/Regex.h"
// Necessary for llvm::DebugCompressionType::None
#include "llvm/Target/TargetOptions.h"
@@ -87,36 +89,71 @@ enum class DiscardType {
Locals, // --discard-locals (-X)
};
-class NameOrRegex {
+enum class MatchStyle {
+ Literal, // Default for symbols.
+ Wildcard, // Default for sections, or enabled with --wildcard (-w).
+ Regex, // Enabled with --regex.
+};
+
+class NameOrPattern {
StringRef Name;
// Regex is shared between multiple CopyConfig instances.
std::shared_ptr<Regex> R;
+ std::shared_ptr<GlobPattern> G;
+ bool IsPositiveMatch = true;
+
+ NameOrPattern(StringRef N) : Name(N) {}
+ NameOrPattern(std::shared_ptr<Regex> R) : R(R) {}
+ NameOrPattern(std::shared_ptr<GlobPattern> G, bool IsPositiveMatch)
+ : G(G), IsPositiveMatch(IsPositiveMatch) {}
public:
- NameOrRegex(StringRef Pattern, bool IsRegex);
- bool operator==(StringRef S) const { return R ? R->match(S) : Name == S; }
+ // ErrorCallback is used to handle recoverable errors. An Error returned
+ // by the callback aborts the parsing and is then returned by this function.
+ static Expected<NameOrPattern>
+ create(StringRef Pattern, MatchStyle MS,
+ llvm::function_ref<Error(Error)> ErrorCallback);
+
+ bool isPositiveMatch() const { return IsPositiveMatch; }
+ bool operator==(StringRef S) const {
+ return R ? R->match(S) : G ? G->match(S) : Name == S;
+ }
bool operator!=(StringRef S) const { return !operator==(S); }
};
-struct NewSymbolInfo {
- StringRef SymbolName;
- StringRef SectionName;
- uint64_t Value = 0;
- uint8_t Type = ELF::STT_NOTYPE;
- uint8_t Bind = ELF::STB_GLOBAL;
- uint8_t Visibility = ELF::STV_DEFAULT;
+// Matcher that checks symbol or section names against the command line flags
+// provided for that option.
+class NameMatcher {
+ std::vector<NameOrPattern> PosMatchers;
+ std::vector<NameOrPattern> NegMatchers;
+
+public:
+ Error addMatcher(Expected<NameOrPattern> Matcher) {
+ if (!Matcher)
+ return Matcher.takeError();
+ if (Matcher->isPositiveMatch())
+ PosMatchers.push_back(std::move(*Matcher));
+ else
+ NegMatchers.push_back(std::move(*Matcher));
+ return Error::success();
+ }
+ bool matches(StringRef S) const {
+ return is_contained(PosMatchers, S) && !is_contained(NegMatchers, S);
+ }
+ bool empty() const { return PosMatchers.empty() && NegMatchers.empty(); }
};
// Configuration for copying/stripping a single file.
struct CopyConfig {
+ // Format-specific options to be initialized lazily when needed.
+ Optional<elf::ELFCopyConfig> ELF;
+
// Main input/output options
StringRef InputFilename;
FileFormat InputFormat;
StringRef OutputFilename;
FileFormat OutputFormat;
- // Only applicable for --input-format=binary
- MachineInfo BinaryArch;
// Only applicable when --output-format!=binary (e.g. elf64-x86-64).
Optional<MachineInfo> OutputArch;
@@ -132,24 +169,30 @@ struct CopyConfig {
StringRef SymbolsPrefix;
StringRef AllocSectionsPrefix;
DiscardType DiscardMode = DiscardType::None;
+ Optional<StringRef> NewSymbolVisibility;
// Repeated options
std::vector<StringRef> AddSection;
std::vector<StringRef> DumpSection;
- std::vector<NewSymbolInfo> SymbolsToAdd;
- std::vector<NameOrRegex> KeepSection;
- std::vector<NameOrRegex> OnlySection;
- std::vector<NameOrRegex> SymbolsToGlobalize;
- std::vector<NameOrRegex> SymbolsToKeep;
- std::vector<NameOrRegex> SymbolsToLocalize;
- std::vector<NameOrRegex> SymbolsToRemove;
- std::vector<NameOrRegex> UnneededSymbolsToRemove;
- std::vector<NameOrRegex> SymbolsToWeaken;
- std::vector<NameOrRegex> ToRemove;
- std::vector<NameOrRegex> SymbolsToKeepGlobal;
+ std::vector<StringRef> SymbolsToAdd;
+
+ // Section matchers
+ NameMatcher KeepSection;
+ NameMatcher OnlySection;
+ NameMatcher ToRemove;
+
+ // Symbol matchers
+ NameMatcher SymbolsToGlobalize;
+ NameMatcher SymbolsToKeep;
+ NameMatcher SymbolsToLocalize;
+ NameMatcher SymbolsToRemove;
+ NameMatcher UnneededSymbolsToRemove;
+ NameMatcher SymbolsToWeaken;
+ NameMatcher SymbolsToKeepGlobal;
// Map options
StringMap<SectionRename> SectionsToRename;
+ StringMap<uint64_t> SetSectionAlignment;
StringMap<SectionFlagsUpdate> SetSectionFlags;
StringMap<StringRef> SymbolsToRename;
@@ -178,6 +221,18 @@ struct CopyConfig {
bool Weaken = false;
bool DecompressDebugSections = false;
DebugCompressionType CompressionType = DebugCompressionType::None;
+
+ // parseELFConfig performs ELF-specific command-line parsing. Fills `ELF` on
+ // success or returns an Error otherwise.
+ Error parseELFConfig() {
+ if (!ELF) {
+ Expected<elf::ELFCopyConfig> ELFConfig = elf::parseConfig(*this);
+ if (!ELFConfig)
+ return ELFConfig.takeError();
+ ELF = *ELFConfig;
+ }
+ return Error::success();
+ }
};
// Configuration for the overall invocation of this tool. When invoked as
@@ -190,8 +245,11 @@ struct DriverConfig {
// ParseObjcopyOptions returns the config and sets the input arguments. If a
// help flag is set then ParseObjcopyOptions will print the help messege and
-// exit.
-Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr);
+// exit. ErrorCallback is used to handle recoverable errors. An Error returned
+// by the callback aborts the parsing and is then returned by this function.
+Expected<DriverConfig>
+parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
+ llvm::function_ref<Error(Error)> ErrorCallback);
// ParseStripOptions returns the config and sets the input arguments. If a
// help flag is set then ParseStripOptions will print the help messege and
@@ -199,7 +257,7 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr);
// by the callback aborts the parsing and is then returned by this function.
Expected<DriverConfig>
parseStripOptions(ArrayRef<const char *> ArgsArr,
- std::function<Error(Error)> ErrorCallback);
+ llvm::function_ref<Error(Error)> ErrorCallback);
} // namespace objcopy
} // namespace llvm
diff --git a/tools/llvm-objcopy/ELF/ELFConfig.cpp b/tools/llvm-objcopy/ELF/ELFConfig.cpp
new file mode 100644
index 000000000000..40993760add7
--- /dev/null
+++ b/tools/llvm-objcopy/ELF/ELFConfig.cpp
@@ -0,0 +1,133 @@
+//===- ELFConfig.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CopyConfig.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+namespace objcopy {
+namespace elf {
+
+static Expected<NewSymbolInfo> parseNewSymbolInfo(StringRef FlagValue,
+ uint8_t DefaultVisibility) {
+ // Parse value given with --add-symbol option and create the
+ // new symbol if possible. The value format for --add-symbol is:
+ //
+ // <name>=[<section>:]<value>[,<flags>]
+ //
+ // where:
+ // <name> - symbol name, can be empty string
+ // <section> - optional section name. If not given ABS symbol is created
+ // <value> - symbol value, can be decimal or hexadecimal number prefixed
+ // with 0x.
+ // <flags> - optional flags affecting symbol type, binding or visibility:
+ // The following are currently supported:
+ //
+ // global, local, weak, default, hidden, file, section, object,
+ // indirect-function.
+ //
+ // The following flags are ignored and provided for GNU
+ // compatibility only:
+ //
+ // warning, debug, constructor, indirect, synthetic,
+ // unique-object, before=<symbol>.
+ NewSymbolInfo SI;
+ StringRef Value;
+ std::tie(SI.SymbolName, Value) = FlagValue.split('=');
+ if (Value.empty())
+ return createStringError(
+ errc::invalid_argument,
+ "bad format for --add-symbol, missing '=' after '%s'",
+ SI.SymbolName.str().c_str());
+
+ if (Value.contains(':')) {
+ std::tie(SI.SectionName, Value) = Value.split(':');
+ if (SI.SectionName.empty() || Value.empty())
+ return createStringError(
+ errc::invalid_argument,
+ "bad format for --add-symbol, missing section name or symbol value");
+ }
+
+ SmallVector<StringRef, 6> Flags;
+ Value.split(Flags, ',');
+ if (Flags[0].getAsInteger(0, SI.Value))
+ return createStringError(errc::invalid_argument, "bad symbol value: '%s'",
+ Flags[0].str().c_str());
+
+ SI.Visibility = DefaultVisibility;
+
+ using Functor = std::function<void(void)>;
+ SmallVector<StringRef, 6> UnsupportedFlags;
+ for (size_t I = 1, NumFlags = Flags.size(); I < NumFlags; ++I)
+ static_cast<Functor>(
+ StringSwitch<Functor>(Flags[I])
+ .CaseLower("global", [&SI] { SI.Bind = ELF::STB_GLOBAL; })
+ .CaseLower("local", [&SI] { SI.Bind = ELF::STB_LOCAL; })
+ .CaseLower("weak", [&SI] { SI.Bind = ELF::STB_WEAK; })
+ .CaseLower("default", [&SI] { SI.Visibility = ELF::STV_DEFAULT; })
+ .CaseLower("hidden", [&SI] { SI.Visibility = ELF::STV_HIDDEN; })
+ .CaseLower("protected",
+ [&SI] { SI.Visibility = ELF::STV_PROTECTED; })
+ .CaseLower("file", [&SI] { SI.Type = ELF::STT_FILE; })
+ .CaseLower("section", [&SI] { SI.Type = ELF::STT_SECTION; })
+ .CaseLower("object", [&SI] { SI.Type = ELF::STT_OBJECT; })
+ .CaseLower("function", [&SI] { SI.Type = ELF::STT_FUNC; })
+ .CaseLower("indirect-function",
+ [&SI] { SI.Type = ELF::STT_GNU_IFUNC; })
+ .CaseLower("debug", [] {})
+ .CaseLower("constructor", [] {})
+ .CaseLower("warning", [] {})
+ .CaseLower("indirect", [] {})
+ .CaseLower("synthetic", [] {})
+ .CaseLower("unique-object", [] {})
+ .StartsWithLower("before", [] {})
+ .Default([&] { UnsupportedFlags.push_back(Flags[I]); }))();
+ if (!UnsupportedFlags.empty())
+ return createStringError(errc::invalid_argument,
+ "unsupported flag%s for --add-symbol: '%s'",
+ UnsupportedFlags.size() > 1 ? "s" : "",
+ join(UnsupportedFlags, "', '").c_str());
+ return SI;
+}
+
+Expected<ELFCopyConfig> parseConfig(const CopyConfig &Config) {
+ ELFCopyConfig ELFConfig;
+ if (Config.NewSymbolVisibility) {
+ const uint8_t Invalid = 0xff;
+ ELFConfig.NewSymbolVisibility =
+ StringSwitch<uint8_t>(*Config.NewSymbolVisibility)
+ .Case("default", ELF::STV_DEFAULT)
+ .Case("hidden", ELF::STV_HIDDEN)
+ .Case("internal", ELF::STV_INTERNAL)
+ .Case("protected", ELF::STV_PROTECTED)
+ .Default(Invalid);
+
+ if (ELFConfig.NewSymbolVisibility == Invalid)
+ return createStringError(errc::invalid_argument,
+ "'%s' is not a valid symbol visibility",
+ Config.NewSymbolVisibility->str().c_str());
+ }
+
+ for (StringRef Arg : Config.SymbolsToAdd) {
+ Expected<elf::NewSymbolInfo> NSI = parseNewSymbolInfo(
+ Arg,
+ ELFConfig.NewSymbolVisibility.getValueOr((uint8_t)ELF::STV_DEFAULT));
+ if (!NSI)
+ return NSI.takeError();
+ ELFConfig.SymbolsToAdd.push_back(*NSI);
+ }
+
+ return ELFConfig;
+}
+
+} // end namespace elf
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/tools/llvm-objcopy/ELF/ELFConfig.h b/tools/llvm-objcopy/ELF/ELFConfig.h
new file mode 100644
index 000000000000..977efbc4166f
--- /dev/null
+++ b/tools/llvm-objcopy/ELF/ELFConfig.h
@@ -0,0 +1,44 @@
+//===- ELFConfig.h ----------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_OBJCOPY_ELFCONFIG_H
+#define LLVM_TOOLS_OBJCOPY_ELFCONFIG_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/ELFTypes.h"
+#include "llvm/Support/Error.h"
+#include <vector>
+
+namespace llvm {
+namespace objcopy {
+struct CopyConfig;
+
+namespace elf {
+
+struct NewSymbolInfo {
+ StringRef SymbolName;
+ StringRef SectionName;
+ uint64_t Value = 0;
+ uint8_t Type = ELF::STT_NOTYPE;
+ uint8_t Bind = ELF::STB_GLOBAL;
+ uint8_t Visibility = ELF::STV_DEFAULT;
+};
+
+struct ELFCopyConfig {
+ Optional<uint8_t> NewSymbolVisibility;
+ std::vector<NewSymbolInfo> SymbolsToAdd;
+};
+
+Expected<ELFCopyConfig> parseConfig(const CopyConfig &Config);
+
+} // namespace elf
+} // namespace objcopy
+} // namespace llvm
+
+#endif
diff --git a/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
index b366c6e55987..8bf7e0f88010 100644
--- a/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
+++ b/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
@@ -136,16 +136,16 @@ static std::unique_ptr<Writer> createELFWriter(const CopyConfig &Config,
// 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,
+ return std::make_unique<ELFWriter<ELF32LE>>(Obj, Buf,
!Config.StripSections);
case ELFT_ELF64LE:
- return llvm::make_unique<ELFWriter<ELF64LE>>(Obj, Buf,
+ return std::make_unique<ELFWriter<ELF64LE>>(Obj, Buf,
!Config.StripSections);
case ELFT_ELF32BE:
- return llvm::make_unique<ELFWriter<ELF32BE>>(Obj, Buf,
+ return std::make_unique<ELFWriter<ELF32BE>>(Obj, Buf,
!Config.StripSections);
case ELFT_ELF64BE:
- return llvm::make_unique<ELFWriter<ELF64BE>>(Obj, Buf,
+ return std::make_unique<ELFWriter<ELF64BE>>(Obj, Buf,
!Config.StripSections);
}
llvm_unreachable("Invalid output format");
@@ -156,9 +156,9 @@ static std::unique_ptr<Writer> createWriter(const CopyConfig &Config,
ElfType OutputElfType) {
switch (Config.OutputFormat) {
case FileFormat::Binary:
- return llvm::make_unique<BinaryWriter>(Obj, Buf);
+ return std::make_unique<BinaryWriter>(Obj, Buf);
case FileFormat::IHex:
- return llvm::make_unique<IHexWriter>(Obj, Buf);
+ return std::make_unique<IHexWriter>(Obj, Buf);
default:
return createELFWriter(Config, Obj, Buf, OutputElfType);
}
@@ -263,7 +263,7 @@ static Error linkToBuildIdDir(const CopyConfig &Config, StringRef ToLink,
static Error splitDWOToFile(const CopyConfig &Config, const Reader &Reader,
StringRef File, ElfType OutputElfType) {
- auto DWOFile = Reader.create();
+ auto DWOFile = Reader.create(false);
auto OnlyKeepDWOPred = [&DWOFile](const SectionBase &Sec) {
return onlyKeepDWOPred(*DWOFile, Sec);
};
@@ -305,9 +305,9 @@ static Error dumpSectionToFile(StringRef SecName, StringRef Filename,
SecName.str().c_str());
}
-static bool isCompressable(const SectionBase &Section) {
- return !(Section.Flags & ELF::SHF_COMPRESSED) &&
- StringRef(Section.Name).startswith(".debug");
+static bool isCompressable(const SectionBase &Sec) {
+ return !(Sec.Flags & ELF::SHF_COMPRESSED) &&
+ StringRef(Sec.Name).startswith(".debug");
}
static void replaceDebugSections(
@@ -356,7 +356,7 @@ static Error updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) {
if (!Sym.isCommon() && Sym.getShndx() != SHN_UNDEF &&
((Config.LocalizeHidden &&
(Sym.Visibility == STV_HIDDEN || Sym.Visibility == STV_INTERNAL)) ||
- is_contained(Config.SymbolsToLocalize, Sym.Name)))
+ Config.SymbolsToLocalize.matches(Sym.Name)))
Sym.Binding = STB_LOCAL;
// Note: these two globalize flags have very similar names but different
@@ -370,16 +370,15 @@ static Error updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) {
// --keep-global-symbol. Because of that, make sure to check
// --globalize-symbol second.
if (!Config.SymbolsToKeepGlobal.empty() &&
- !is_contained(Config.SymbolsToKeepGlobal, Sym.Name) &&
+ !Config.SymbolsToKeepGlobal.matches(Sym.Name) &&
Sym.getShndx() != SHN_UNDEF)
Sym.Binding = STB_LOCAL;
- if (is_contained(Config.SymbolsToGlobalize, Sym.Name) &&
+ if (Config.SymbolsToGlobalize.matches(Sym.Name) &&
Sym.getShndx() != SHN_UNDEF)
Sym.Binding = STB_GLOBAL;
- if (is_contained(Config.SymbolsToWeaken, Sym.Name) &&
- Sym.Binding == STB_GLOBAL)
+ if (Config.SymbolsToWeaken.matches(Sym.Name) && Sym.Binding == STB_GLOBAL)
Sym.Binding = STB_WEAK;
if (Config.Weaken && Sym.Binding == STB_GLOBAL &&
@@ -399,12 +398,12 @@ static Error updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) {
// symbols are still 'needed' and which are not.
if (Config.StripUnneeded || !Config.UnneededSymbolsToRemove.empty() ||
!Config.OnlySection.empty()) {
- for (auto &Section : Obj.sections())
- Section.markSymbols();
+ for (SectionBase &Sec : Obj.sections())
+ Sec.markSymbols();
}
auto RemoveSymbolsPred = [&](const Symbol &Sym) {
- if (is_contained(Config.SymbolsToKeep, Sym.Name) ||
+ if (Config.SymbolsToKeep.matches(Sym.Name) ||
(Config.KeepFileSymbols && Sym.Type == STT_FILE))
return false;
@@ -418,12 +417,12 @@ static Error updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) {
if (Config.StripAll || Config.StripAllGNU)
return true;
- if (is_contained(Config.SymbolsToRemove, Sym.Name))
+ if (Config.SymbolsToRemove.matches(Sym.Name))
return true;
if ((Config.StripUnneeded ||
- is_contained(Config.UnneededSymbolsToRemove, Sym.Name)) &&
- isUnneededSymbol(Sym))
+ Config.UnneededSymbolsToRemove.matches(Sym.Name)) &&
+ (!Obj.isRelocatable() || isUnneededSymbol(Sym)))
return true;
// We want to remove undefined symbols if all references have been stripped.
@@ -443,7 +442,7 @@ static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) {
// Removes:
if (!Config.ToRemove.empty()) {
RemovePred = [&Config](const SectionBase &Sec) {
- return is_contained(Config.ToRemove, Sec.Name);
+ return Config.ToRemove.matches(Sec.Name);
};
}
@@ -481,7 +480,7 @@ static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) {
};
}
- if (Config.StripDebug) {
+ if (Config.StripDebug || Config.StripUnneeded) {
RemovePred = [RemovePred](const SectionBase &Sec) {
return RemovePred(Sec) || isDebugSection(Sec);
};
@@ -523,7 +522,7 @@ static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) {
if (!Config.OnlySection.empty()) {
RemovePred = [&Config, RemovePred, &Obj](const SectionBase &Sec) {
// Explicitly keep these sections regardless of previous removes.
- if (is_contained(Config.OnlySection, Sec.Name))
+ if (Config.OnlySection.matches(Sec.Name))
return false;
// Allow all implicit removes.
@@ -545,7 +544,7 @@ static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) {
if (!Config.KeepSection.empty()) {
RemovePred = [&Config, RemovePred](const SectionBase &Sec) {
// Explicitly keep these sections regardless of previous removes.
- if (is_contained(Config.KeepSection, Sec.Name))
+ if (Config.KeepSection.matches(Sec.Name))
return false;
// Otherwise defer to RemovePred.
return RemovePred(Sec);
@@ -614,9 +613,8 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj,
if (Error E = updateAndRemoveSymbols(Config, Obj))
return E;
- if (!Config.SectionsToRename.empty() || !Config.AllocSectionsPrefix.empty()) {
- DenseSet<SectionBase *> PrefixedSections;
- for (auto &Sec : Obj.sections()) {
+ if (!Config.SectionsToRename.empty()) {
+ for (SectionBase &Sec : Obj.sections()) {
const auto Iter = Config.SectionsToRename.find(Sec.Name);
if (Iter != Config.SectionsToRename.end()) {
const SectionRename &SR = Iter->second;
@@ -624,63 +622,62 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj,
if (SR.NewFlags.hasValue())
setSectionFlagsAndType(Sec, SR.NewFlags.getValue());
}
+ }
+ }
- // Add a prefix to allocated sections and their relocation sections. This
- // should be done after renaming the section by Config.SectionToRename to
- // imitate the GNU objcopy behavior.
- if (!Config.AllocSectionsPrefix.empty()) {
- if (Sec.Flags & SHF_ALLOC) {
- Sec.Name = (Config.AllocSectionsPrefix + Sec.Name).str();
- PrefixedSections.insert(&Sec);
-
- // Rename relocation sections associated to the allocated sections.
- // For example, if we rename .text to .prefix.text, we also rename
- // .rel.text to .rel.prefix.text.
- //
- // Dynamic relocation sections (SHT_REL[A] with SHF_ALLOC) are handled
- // above, e.g., .rela.plt is renamed to .prefix.rela.plt, not
- // .rela.prefix.plt since GNU objcopy does so.
- } else if (auto *RelocSec = dyn_cast<RelocationSectionBase>(&Sec)) {
- auto *TargetSec = RelocSec->getSection();
- if (TargetSec && (TargetSec->Flags & SHF_ALLOC)) {
- StringRef prefix;
- switch (Sec.Type) {
- case SHT_REL:
- prefix = ".rel";
- break;
- case SHT_RELA:
- prefix = ".rela";
- break;
- default:
- continue;
- }
-
- // If the relocation section comes *after* the target section, we
- // don't add Config.AllocSectionsPrefix because we've already added
- // the prefix to TargetSec->Name. Otherwise, if the relocation
- // section comes *before* the target section, we add the prefix.
- if (PrefixedSections.count(TargetSec)) {
- Sec.Name = (prefix + TargetSec->Name).str();
- } else {
- const auto Iter = Config.SectionsToRename.find(TargetSec->Name);
- if (Iter != Config.SectionsToRename.end()) {
- // Both `--rename-section` and `--prefix-alloc-sections` are
- // given but the target section is not yet renamed.
- Sec.Name =
- (prefix + Config.AllocSectionsPrefix + Iter->second.NewName)
- .str();
- } else {
- Sec.Name =
- (prefix + Config.AllocSectionsPrefix + TargetSec->Name)
- .str();
- }
- }
+ // Add a prefix to allocated sections and their relocation sections. This
+ // should be done after renaming the section by Config.SectionToRename to
+ // imitate the GNU objcopy behavior.
+ if (!Config.AllocSectionsPrefix.empty()) {
+ DenseSet<SectionBase *> PrefixedSections;
+ for (SectionBase &Sec : Obj.sections()) {
+ if (Sec.Flags & SHF_ALLOC) {
+ Sec.Name = (Config.AllocSectionsPrefix + Sec.Name).str();
+ PrefixedSections.insert(&Sec);
+ } else if (auto *RelocSec = dyn_cast<RelocationSectionBase>(&Sec)) {
+ // Rename relocation sections associated to the allocated sections.
+ // For example, if we rename .text to .prefix.text, we also rename
+ // .rel.text to .rel.prefix.text.
+ //
+ // Dynamic relocation sections (SHT_REL[A] with SHF_ALLOC) are handled
+ // above, e.g., .rela.plt is renamed to .prefix.rela.plt, not
+ // .rela.prefix.plt since GNU objcopy does so.
+ const SectionBase *TargetSec = RelocSec->getSection();
+ if (TargetSec && (TargetSec->Flags & SHF_ALLOC)) {
+ StringRef prefix;
+ switch (Sec.Type) {
+ case SHT_REL:
+ prefix = ".rel";
+ break;
+ case SHT_RELA:
+ prefix = ".rela";
+ break;
+ default:
+ llvm_unreachable("not a relocation section");
}
+
+ // If the relocation section comes *after* the target section, we
+ // don't add Config.AllocSectionsPrefix because we've already added
+ // the prefix to TargetSec->Name. Otherwise, if the relocation
+ // section comes *before* the target section, we add the prefix.
+ if (PrefixedSections.count(TargetSec))
+ Sec.Name = (prefix + TargetSec->Name).str();
+ else
+ Sec.Name =
+ (prefix + Config.AllocSectionsPrefix + TargetSec->Name).str();
}
}
}
}
+ if (!Config.SetSectionAlignment.empty()) {
+ for (SectionBase &Sec : Obj.sections()) {
+ auto I = Config.SetSectionAlignment.find(Sec.Name);
+ if (I != Config.SetSectionAlignment.end())
+ Sec.Align = I->second;
+ }
+ }
+
if (!Config.SetSectionFlags.empty()) {
for (auto &Sec : Obj.sections()) {
const auto Iter = Config.SetSectionFlags.find(Sec.Name);
@@ -721,7 +718,7 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj,
Obj.addSection<GnuDebugLinkSection>(Config.AddGnuDebugLink,
Config.GnuDebugLinkCRC32);
- for (const NewSymbolInfo &SI : Config.SymbolsToAdd) {
+ for (const NewSymbolInfo &SI : Config.ELF->SymbolsToAdd) {
SectionBase *Sec = Obj.findSection(SI.SectionName);
uint64_t Value = Sec ? Sec->Addr + SI.Value : SI.Value;
Obj.SymbolTable->addSymbol(
@@ -746,9 +743,9 @@ static Error writeOutput(const CopyConfig &Config, Object &Obj, Buffer &Out,
Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In,
Buffer &Out) {
IHexReader Reader(&In);
- std::unique_ptr<Object> Obj = Reader.create();
+ std::unique_ptr<Object> Obj = Reader.create(true);
const ElfType OutputElfType =
- getOutputElfType(Config.OutputArch.getValueOr(Config.BinaryArch));
+ getOutputElfType(Config.OutputArch.getValueOr(MachineInfo()));
if (Error E = handleArgs(Config, *Obj, Reader, OutputElfType))
return E;
return writeOutput(Config, *Obj, Out, OutputElfType);
@@ -756,13 +753,15 @@ Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In,
Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In,
Buffer &Out) {
- BinaryReader Reader(Config.BinaryArch, &In);
- std::unique_ptr<Object> Obj = Reader.create();
+ uint8_t NewSymbolVisibility =
+ Config.ELF->NewSymbolVisibility.getValueOr((uint8_t)ELF::STV_DEFAULT);
+ BinaryReader Reader(&In, NewSymbolVisibility);
+ std::unique_ptr<Object> Obj = Reader.create(true);
// Prefer OutputArch (-O<format>) if set, otherwise fallback to BinaryArch
// (-B<arch>).
const ElfType OutputElfType =
- getOutputElfType(Config.OutputArch.getValueOr(Config.BinaryArch));
+ getOutputElfType(Config.OutputArch.getValueOr(MachineInfo()));
if (Error E = handleArgs(Config, *Obj, Reader, OutputElfType))
return E;
return writeOutput(Config, *Obj, Out, OutputElfType);
@@ -771,7 +770,7 @@ Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In,
Error executeObjcopyOnBinary(const CopyConfig &Config,
object::ELFObjectFileBase &In, Buffer &Out) {
ELFReader Reader(&In, Config.ExtractPartition);
- std::unique_ptr<Object> Obj = Reader.create();
+ std::unique_ptr<Object> Obj = Reader.create(!Config.SymbolsToAdd.empty());
// Prefer OutputArch (-O<format>) if set, otherwise infer it from the input.
const ElfType OutputElfType =
Config.OutputArch ? getOutputElfType(Config.OutputArch.getValue())
diff --git a/tools/llvm-objcopy/ELF/Object.cpp b/tools/llvm-objcopy/ELF/Object.cpp
index fa696380e17c..74145dad6e6b 100644
--- a/tools/llvm-objcopy/ELF/Object.cpp
+++ b/tools/llvm-objcopy/ELF/Object.cpp
@@ -397,7 +397,7 @@ void SectionWriter::visit(const OwnedDataSection &Sec) {
llvm::copy(Sec.Data, Out.getBufferStart() + Sec.Offset);
}
-static const std::vector<uint8_t> ZlibGnuMagic = {'Z', 'L', 'I', 'B'};
+static constexpr std::array<uint8_t, 4> ZlibGnuMagic = {{'Z', 'L', 'I', 'B'}};
static bool isDataGnuCompressed(ArrayRef<uint8_t> Data) {
return Data.size() > ZlibGnuMagic.size() &&
@@ -665,7 +665,7 @@ void SymbolTableSection::addSymbol(Twine Name, uint8_t Bind, uint8_t Type,
Sym.Visibility = Visibility;
Sym.Size = SymbolSize;
Sym.Index = Symbols.size();
- Symbols.emplace_back(llvm::make_unique<Symbol>(Sym));
+ Symbols.emplace_back(std::make_unique<Symbol>(Sym));
Size += this->EntrySize;
}
@@ -1055,29 +1055,28 @@ void GroupSection::accept(MutableSectionVisitor &Visitor) {
}
// Returns true IFF a section is wholly inside the range of a segment
-static bool sectionWithinSegment(const SectionBase &Section,
- const Segment &Segment) {
+static bool sectionWithinSegment(const SectionBase &Sec, const Segment &Seg) {
// If a section is empty it should be treated like it has a size of 1. This is
// to clarify the case when an empty section lies on a boundary between two
// segments and ensures that the section "belongs" to the second segment and
// not the first.
- uint64_t SecSize = Section.Size ? Section.Size : 1;
+ uint64_t SecSize = Sec.Size ? Sec.Size : 1;
- if (Section.Type == SHT_NOBITS) {
- if (!(Section.Flags & SHF_ALLOC))
+ if (Sec.Type == SHT_NOBITS) {
+ if (!(Sec.Flags & SHF_ALLOC))
return false;
- bool SectionIsTLS = Section.Flags & SHF_TLS;
- bool SegmentIsTLS = Segment.Type == PT_TLS;
+ bool SectionIsTLS = Sec.Flags & SHF_TLS;
+ bool SegmentIsTLS = Seg.Type == PT_TLS;
if (SectionIsTLS != SegmentIsTLS)
return false;
- return Segment.VAddr <= Section.Addr &&
- Segment.VAddr + Segment.MemSize >= Section.Addr + SecSize;
+ return Seg.VAddr <= Sec.Addr &&
+ Seg.VAddr + Seg.MemSize >= Sec.Addr + SecSize;
}
- return Segment.Offset <= Section.OriginalOffset &&
- Segment.Offset + Segment.FileSize >= Section.OriginalOffset + SecSize;
+ return Seg.Offset <= Sec.OriginalOffset &&
+ Seg.Offset + Seg.FileSize >= Sec.OriginalOffset + SecSize;
}
// Returns true IFF a segment's original offset is inside of another segment's
@@ -1113,7 +1112,7 @@ void BasicELFBuilder::initFileHeader() {
Obj->OSABI = ELFOSABI_NONE;
Obj->ABIVersion = 0;
Obj->Entry = 0x0;
- Obj->Machine = EMachine;
+ Obj->Machine = EM_NONE;
Obj->Version = 1;
}
@@ -1141,8 +1140,8 @@ SymbolTableSection *BasicELFBuilder::addSymTab(StringTableSection *StrTab) {
}
void BasicELFBuilder::initSections() {
- for (auto &Section : Obj->sections())
- Section.initialize(Obj->sections());
+ for (SectionBase &Sec : Obj->sections())
+ Sec.initialize(Obj->sections());
}
void BinaryELFBuilder::addData(SymbolTableSection *SymTab) {
@@ -1161,11 +1160,12 @@ void BinaryELFBuilder::addData(SymbolTableSection *SymTab) {
Twine Prefix = Twine("_binary_") + SanitizedFilename;
SymTab->addSymbol(Prefix + "_start", STB_GLOBAL, STT_NOTYPE, &DataSection,
- /*Value=*/0, STV_DEFAULT, 0, 0);
+ /*Value=*/0, NewSymbolVisibility, 0, 0);
SymTab->addSymbol(Prefix + "_end", STB_GLOBAL, STT_NOTYPE, &DataSection,
- /*Value=*/DataSection.Size, STV_DEFAULT, 0, 0);
+ /*Value=*/DataSection.Size, NewSymbolVisibility, 0, 0);
SymTab->addSymbol(Prefix + "_size", STB_GLOBAL, STT_NOTYPE, nullptr,
- /*Value=*/DataSection.Size, STV_DEFAULT, SHN_ABS, 0);
+ /*Value=*/DataSection.Size, NewSymbolVisibility, SHN_ABS,
+ 0);
}
std::unique_ptr<Object> BinaryELFBuilder::build() {
@@ -1255,10 +1255,9 @@ template <class ELFT> void ELFBuilder<ELFT>::findEhdrOffset() {
if (!ExtractPartition)
return;
- for (const SectionBase &Section : Obj.sections()) {
- if (Section.Type == SHT_LLVM_PART_EHDR &&
- Section.Name == *ExtractPartition) {
- EhdrOffset = Section.Offset;
+ for (const SectionBase &Sec : Obj.sections()) {
+ if (Sec.Type == SHT_LLVM_PART_EHDR && Sec.Name == *ExtractPartition) {
+ EhdrOffset = Sec.Offset;
return;
}
}
@@ -1287,15 +1286,12 @@ void ELFBuilder<ELFT>::readProgramHeaders(const ELFFile<ELFT> &HeadersFile) {
Seg.MemSize = Phdr.p_memsz;
Seg.Align = Phdr.p_align;
Seg.Index = Index++;
- for (SectionBase &Section : Obj.sections()) {
- if (sectionWithinSegment(Section, Seg)) {
- Seg.addSection(&Section);
- if (!Section.ParentSegment ||
- Section.ParentSegment->Offset > Seg.Offset) {
- Section.ParentSegment = &Seg;
- }
+ for (SectionBase &Sec : Obj.sections())
+ if (sectionWithinSegment(Sec, Seg)) {
+ Seg.addSection(&Sec);
+ if (!Sec.ParentSegment || Sec.ParentSegment->Offset > Seg.Offset)
+ Sec.ParentSegment = &Seg;
}
- }
}
auto &ElfHdr = Obj.ElfHdrSegment;
@@ -1531,7 +1527,7 @@ template <class ELFT> void ELFBuilder<ELFT>::readSectionHeaders() {
}
}
-template <class ELFT> void ELFBuilder<ELFT>::readSections() {
+template <class ELFT> void ELFBuilder<ELFT>::readSections(bool EnsureSymtab) {
// If a section index table exists we'll need to initialize it before we
// initialize the symbol table because the symbol table might need to
// reference it.
@@ -1544,16 +1540,37 @@ template <class ELFT> void ELFBuilder<ELFT>::readSections() {
if (Obj.SymbolTable) {
Obj.SymbolTable->initialize(Obj.sections());
initSymbolTable(Obj.SymbolTable);
+ } else if (EnsureSymtab) {
+ // Reuse the existing SHT_STRTAB section if exists.
+ StringTableSection *StrTab = nullptr;
+ for (auto &Sec : Obj.sections()) {
+ if (Sec.Type == ELF::SHT_STRTAB && !(Sec.Flags & SHF_ALLOC)) {
+ StrTab = static_cast<StringTableSection *>(&Sec);
+
+ // Prefer .strtab to .shstrtab.
+ if (Obj.SectionNames != &Sec)
+ break;
+ }
+ }
+ if (!StrTab)
+ StrTab = &Obj.addSection<StringTableSection>();
+
+ SymbolTableSection &SymTab = Obj.addSection<SymbolTableSection>();
+ SymTab.Name = ".symtab";
+ SymTab.Link = StrTab->Index;
+ SymTab.initialize(Obj.sections());
+ SymTab.addSymbol("", 0, 0, nullptr, 0, 0, 0, 0);
+ Obj.SymbolTable = &SymTab;
}
// Now that all sections and symbols have been added we can add
// relocations that reference symbols and set the link and info fields for
// relocation sections.
- for (auto &Section : Obj.sections()) {
- if (&Section == Obj.SymbolTable)
+ for (auto &Sec : Obj.sections()) {
+ if (&Sec == Obj.SymbolTable)
continue;
- Section.initialize(Obj.sections());
- if (auto RelSec = dyn_cast<RelocationSection>(&Section)) {
+ Sec.initialize(Obj.sections());
+ if (auto RelSec = dyn_cast<RelocationSection>(&Sec)) {
auto Shdr = unwrapOrError(ElfFile.sections()).begin() + RelSec->Index;
if (RelSec->Type == SHT_REL)
initRelocations(RelSec, Obj.SymbolTable,
@@ -1561,7 +1578,7 @@ template <class ELFT> void ELFBuilder<ELFT>::readSections() {
else
initRelocations(RelSec, Obj.SymbolTable,
unwrapOrError(ElfFile.relas(Shdr)));
- } else if (auto GroupSec = dyn_cast<GroupSection>(&Section)) {
+ } else if (auto GroupSec = dyn_cast<GroupSection>(&Sec)) {
initGroupSection(GroupSec);
}
}
@@ -1582,7 +1599,7 @@ template <class ELFT> void ELFBuilder<ELFT>::readSections() {
" is not a string table");
}
-template <class ELFT> void ELFBuilder<ELFT>::build() {
+template <class ELFT> void ELFBuilder<ELFT>::build(bool EnsureSymtab) {
readSectionHeaders();
findEhdrOffset();
@@ -1601,7 +1618,7 @@ template <class ELFT> void ELFBuilder<ELFT>::build() {
Obj.Entry = Ehdr.e_entry;
Obj.Flags = Ehdr.e_flags;
- readSections();
+ readSections(EnsureSymtab);
readProgramHeaders(HeadersFile);
}
@@ -1609,8 +1626,8 @@ Writer::~Writer() {}
Reader::~Reader() {}
-std::unique_ptr<Object> BinaryReader::create() const {
- return BinaryELFBuilder(MInfo.EMachine, MemBuf).build();
+std::unique_ptr<Object> BinaryReader::create(bool /*EnsureSymtab*/) const {
+ return BinaryELFBuilder(MemBuf, NewSymbolVisibility).build();
}
Expected<std::vector<IHexRecord>> IHexReader::parse() const {
@@ -1639,28 +1656,28 @@ Expected<std::vector<IHexRecord>> IHexReader::parse() const {
return std::move(Records);
}
-std::unique_ptr<Object> IHexReader::create() const {
+std::unique_ptr<Object> IHexReader::create(bool /*EnsureSymtab*/) const {
std::vector<IHexRecord> Records = unwrapOrError(parse());
return IHexELFBuilder(Records).build();
}
-std::unique_ptr<Object> ELFReader::create() const {
- auto Obj = llvm::make_unique<Object>();
+std::unique_ptr<Object> ELFReader::create(bool EnsureSymtab) const {
+ auto Obj = std::make_unique<Object>();
if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(Bin)) {
ELFBuilder<ELF32LE> Builder(*O, *Obj, ExtractPartition);
- Builder.build();
+ Builder.build(EnsureSymtab);
return Obj;
} else if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(Bin)) {
ELFBuilder<ELF64LE> Builder(*O, *Obj, ExtractPartition);
- Builder.build();
+ Builder.build(EnsureSymtab);
return Obj;
} else if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(Bin)) {
ELFBuilder<ELF32BE> Builder(*O, *Obj, ExtractPartition);
- Builder.build();
+ Builder.build(EnsureSymtab);
return Obj;
} else if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) {
ELFBuilder<ELF64BE> Builder(*O, *Obj, ExtractPartition);
- Builder.build();
+ Builder.build(EnsureSymtab);
return Obj;
}
error("invalid file type");
@@ -1693,7 +1710,7 @@ template <class ELFT> void ELFWriter<ELFT>::writeEhdr() {
Ehdr.e_ehsize = sizeof(Elf_Ehdr);
if (WriteSectionHeaders && Obj.sections().size() != 0) {
Ehdr.e_shentsize = sizeof(Elf_Shdr);
- Ehdr.e_shoff = Obj.SHOffset;
+ Ehdr.e_shoff = Obj.SHOff;
// """
// If the number of sections is greater than or equal to
// SHN_LORESERVE (0xff00), this member has the value zero and the actual
@@ -1732,7 +1749,7 @@ template <class ELFT> void ELFWriter<ELFT>::writeShdrs() {
// This reference serves to write the dummy section header at the begining
// of the file. It is not used for anything else
Elf_Shdr &Shdr =
- *reinterpret_cast<Elf_Shdr *>(Buf.getBufferStart() + Obj.SHOffset);
+ *reinterpret_cast<Elf_Shdr *>(Buf.getBufferStart() + Obj.SHOff);
Shdr.sh_name = 0;
Shdr.sh_type = SHT_NULL;
Shdr.sh_flags = 0;
@@ -1862,26 +1879,13 @@ void Object::sortSections() {
});
}
-static uint64_t alignToAddr(uint64_t Offset, uint64_t Addr, uint64_t Align) {
- // Calculate Diff such that (Offset + Diff) & -Align == Addr & -Align.
- if (Align == 0)
- Align = 1;
- auto Diff =
- static_cast<int64_t>(Addr % Align) - static_cast<int64_t>(Offset % Align);
- // We only want to add to Offset, however, so if Diff < 0 we can add Align and
- // (Offset + Diff) & -Align == Addr & -Align will still hold.
- if (Diff < 0)
- Diff += Align;
- return Offset + Diff;
-}
-
// Orders segments such that if x = y->ParentSegment then y comes before x.
static void orderSegments(std::vector<Segment *> &Segments) {
llvm::stable_sort(Segments, compareSegmentsByOffset);
}
// This function finds a consistent layout for a list of segments starting from
-// an Offset. It assumes that Segments have been sorted by OrderSegments and
+// an Offset. It assumes that Segments have been sorted by orderSegments and
// returns an Offset one past the end of the last segment.
static uint64_t layoutSegments(std::vector<Segment *> &Segments,
uint64_t Offset) {
@@ -1902,8 +1906,8 @@ static uint64_t layoutSegments(std::vector<Segment *> &Segments,
Seg->Offset =
Parent->Offset + Seg->OriginalOffset - Parent->OriginalOffset;
} else {
- Offset = alignToAddr(Offset, Seg->VAddr, Seg->Align);
- Seg->Offset = Offset;
+ Seg->Offset =
+ alignTo(Offset, std::max<uint64_t>(Seg->Align, 1), Seg->VAddr);
}
Offset = std::max(Offset, Seg->Offset + Seg->FileSize);
}
@@ -1925,17 +1929,17 @@ static uint64_t layoutSections(Range Sections, uint64_t Offset) {
// of the segment we can assign a new offset to the section. For sections not
// covered by segments we can just bump Offset to the next valid location.
uint32_t Index = 1;
- for (auto &Section : Sections) {
- Section.Index = Index++;
- if (Section.ParentSegment != nullptr) {
- auto Segment = *Section.ParentSegment;
- Section.Offset =
- Segment.Offset + (Section.OriginalOffset - Segment.OriginalOffset);
+ for (auto &Sec : Sections) {
+ Sec.Index = Index++;
+ if (Sec.ParentSegment != nullptr) {
+ auto Segment = *Sec.ParentSegment;
+ Sec.Offset =
+ Segment.Offset + (Sec.OriginalOffset - Segment.OriginalOffset);
} else {
- Offset = alignTo(Offset, Section.Align == 0 ? 1 : Section.Align);
- Section.Offset = Offset;
- if (Section.Type != SHT_NOBITS)
- Offset += Section.Size;
+ Offset = alignTo(Offset, Sec.Align == 0 ? 1 : Sec.Align);
+ Sec.Offset = Offset;
+ if (Sec.Type != SHT_NOBITS)
+ Offset += Sec.Size;
}
}
return Offset;
@@ -1971,16 +1975,16 @@ template <class ELFT> void ELFWriter<ELFT>::assignOffsets() {
// Offset so that SHOffset is valid.
if (WriteSectionHeaders)
Offset = alignTo(Offset, sizeof(Elf_Addr));
- Obj.SHOffset = Offset;
+ Obj.SHOff = Offset;
}
template <class ELFT> size_t ELFWriter<ELFT>::totalSize() const {
// We already have the section header offset so we can calculate the total
// size by just adding up the size of each section header.
if (!WriteSectionHeaders)
- return Obj.SHOffset;
+ return Obj.SHOff;
size_t ShdrCount = Obj.sections().size() + 1; // Includes null shdr.
- return Obj.SHOffset + ShdrCount * sizeof(Elf_Shdr);
+ return Obj.SHOff + ShdrCount * sizeof(Elf_Shdr);
}
template <class ELFT> Error ELFWriter<ELFT>::write() {
@@ -1995,6 +1999,25 @@ template <class ELFT> Error ELFWriter<ELFT>::write() {
return Buf.commit();
}
+static Error removeUnneededSections(Object &Obj) {
+ // We can remove an empty symbol table from non-relocatable objects.
+ // Relocatable objects typically have relocation sections whose
+ // sh_link field points to .symtab, so we can't remove .symtab
+ // even if it is empty.
+ if (Obj.isRelocatable() || Obj.SymbolTable == nullptr ||
+ !Obj.SymbolTable->empty())
+ return Error::success();
+
+ // .strtab can be used for section names. In such a case we shouldn't
+ // remove it.
+ auto *StrTab = Obj.SymbolTable->getStrTab() == Obj.SectionNames
+ ? nullptr
+ : Obj.SymbolTable->getStrTab();
+ return Obj.removeSections(false, [&](const SectionBase &Sec) {
+ return &Sec == Obj.SymbolTable || &Sec == StrTab;
+ });
+}
+
template <class ELFT> Error ELFWriter<ELFT>::finalize() {
// It could happen that SectionNames has been removed and yet the user wants
// a section header table output. We need to throw an error if a user tries
@@ -2004,6 +2027,8 @@ template <class ELFT> Error ELFWriter<ELFT>::finalize() {
"cannot write section header table because "
"section header string table was removed");
+ if (Error E = removeUnneededSections(Obj))
+ return E;
Obj.sortSections();
// We need to assign indexes before we perform layout because we need to know
@@ -2045,9 +2070,8 @@ template <class ELFT> Error ELFWriter<ELFT>::finalize() {
// Make sure we add the names of all the sections. Importantly this must be
// done after we decide to add or remove SectionIndexes.
if (Obj.SectionNames != nullptr)
- for (const auto &Section : Obj.sections()) {
- Obj.SectionNames->addString(Section.Name);
- }
+ for (const SectionBase &Sec : Obj.sections())
+ Obj.SectionNames->addString(Sec.Name);
initEhdrSegment();
@@ -2055,8 +2079,8 @@ template <class ELFT> Error ELFWriter<ELFT>::finalize() {
// Also, the output arch may not be the same as the input arch, so fix up
// size-related fields before doing layout calculations.
uint64_t Index = 0;
- auto SecSizer = llvm::make_unique<ELFSectionSizer<ELFT>>();
- for (auto &Sec : Obj.sections()) {
+ auto SecSizer = std::make_unique<ELFSectionSizer<ELFT>>();
+ for (SectionBase &Sec : Obj.sections()) {
Sec.Index = Index++;
Sec.accept(*SecSizer);
}
@@ -2082,40 +2106,36 @@ template <class ELFT> Error ELFWriter<ELFT>::finalize() {
// Finally now that all offsets and indexes have been set we can finalize any
// remaining issues.
- uint64_t Offset = Obj.SHOffset + sizeof(Elf_Shdr);
- for (SectionBase &Section : Obj.sections()) {
- Section.HeaderOffset = Offset;
+ uint64_t Offset = Obj.SHOff + sizeof(Elf_Shdr);
+ for (SectionBase &Sec : Obj.sections()) {
+ Sec.HeaderOffset = Offset;
Offset += sizeof(Elf_Shdr);
if (WriteSectionHeaders)
- Section.NameIndex = Obj.SectionNames->findIndex(Section.Name);
- Section.finalize();
+ Sec.NameIndex = Obj.SectionNames->findIndex(Sec.Name);
+ Sec.finalize();
}
if (Error E = Buf.allocate(totalSize()))
return E;
- SecWriter = llvm::make_unique<ELFSectionWriter<ELFT>>(Buf);
+ SecWriter = std::make_unique<ELFSectionWriter<ELFT>>(Buf);
return Error::success();
}
Error BinaryWriter::write() {
- for (auto &Section : Obj.sections())
- if (Section.Flags & SHF_ALLOC)
- Section.accept(*SecWriter);
+ for (const SectionBase &Sec : Obj.allocSections())
+ Sec.accept(*SecWriter);
return Buf.commit();
}
Error BinaryWriter::finalize() {
- // TODO: Create a filter range to construct OrderedSegments from so that this
- // code can be deduped with assignOffsets above. This should also solve the
- // todo below for LayoutSections.
// We need a temporary list of segments that has a special order to it
// so that we know that anytime ->ParentSegment is set that segment has
// already had it's offset properly set. We only want to consider the segments
// that will affect layout of allocated sections so we only add those.
std::vector<Segment *> OrderedSegments;
- for (SectionBase &Section : Obj.sections())
- if ((Section.Flags & SHF_ALLOC) != 0 && Section.ParentSegment != nullptr)
- OrderedSegments.push_back(Section.ParentSegment);
+ for (const SectionBase &Sec : Obj.allocSections())
+ if (Sec.ParentSegment != nullptr)
+ OrderedSegments.push_back(Sec.ParentSegment);
// For binary output, we're going to use physical addresses instead of
// virtual addresses, since a binary output is used for cases like ROM
@@ -2130,7 +2150,7 @@ Error BinaryWriter::finalize() {
llvm::stable_sort(OrderedSegments, compareSegmentsByPAddr);
// Because we add a ParentSegment for each section we might have duplicate
- // segments in OrderedSegments. If there were duplicates then LayoutSegments
+ // segments in OrderedSegments. If there were duplicates then layoutSegments
// would do very strange things.
auto End =
std::unique(std::begin(OrderedSegments), std::end(OrderedSegments));
@@ -2158,28 +2178,20 @@ Error BinaryWriter::finalize() {
}
}
- // TODO: generalize LayoutSections to take a range. Pass a special range
- // constructed from an iterator that skips values for which a predicate does
- // not hold. Then pass such a range to LayoutSections instead of constructing
- // AllocatedSections here.
- std::vector<SectionBase *> AllocatedSections;
- for (SectionBase &Section : Obj.sections())
- if (Section.Flags & SHF_ALLOC)
- AllocatedSections.push_back(&Section);
- layoutSections(make_pointee_range(AllocatedSections), Offset);
+ layoutSections(Obj.allocSections(), Offset);
// Now that every section has been laid out we just need to compute the total
// file size. This might not be the same as the offset returned by
- // LayoutSections, because we want to truncate the last segment to the end of
+ // layoutSections, because we want to truncate the last segment to the end of
// its last section, to match GNU objcopy's behaviour.
TotalSize = 0;
- for (SectionBase *Section : AllocatedSections)
- if (Section->Type != SHT_NOBITS)
- TotalSize = std::max(TotalSize, Section->Offset + Section->Size);
+ for (const SectionBase &Sec : Obj.allocSections())
+ if (Sec.Type != SHT_NOBITS)
+ TotalSize = std::max(TotalSize, Sec.Offset + Sec.Size);
if (Error E = Buf.allocate(TotalSize))
return E;
- SecWriter = llvm::make_unique<BinarySectionWriter>(Buf);
+ SecWriter = std::make_unique<BinarySectionWriter>(Buf);
return Error::success();
}
@@ -2259,17 +2271,17 @@ Error IHexWriter::finalize() {
// If any section we're to write has segment then we
// switch to using physical addresses. Otherwise we
// use section virtual address.
- for (auto &Section : Obj.sections())
- if (ShouldWrite(Section) && IsInPtLoad(Section)) {
+ for (const SectionBase &Sec : Obj.sections())
+ if (ShouldWrite(Sec) && IsInPtLoad(Sec)) {
UseSegments = true;
break;
}
- for (auto &Section : Obj.sections())
- if (ShouldWrite(Section) && (!UseSegments || IsInPtLoad(Section))) {
- if (Error E = checkSection(Section))
+ for (const SectionBase &Sec : Obj.sections())
+ if (ShouldWrite(Sec) && (!UseSegments || IsInPtLoad(Sec))) {
+ if (Error E = checkSection(Sec))
return E;
- Sections.insert(&Section);
+ Sections.insert(&Sec);
}
IHexSectionWriterBase LengthCalc(Buf);
diff --git a/tools/llvm-objcopy/ELF/Object.h b/tools/llvm-objcopy/ELF/Object.h
index f3df93b9662f..eeacb014e4dc 100644
--- a/tools/llvm-objcopy/ELF/Object.h
+++ b/tools/llvm-objcopy/ELF/Object.h
@@ -57,8 +57,8 @@ public:
: Sections(Secs) {}
SectionTableRef(const SectionTableRef &) = default;
- iterator begin() { return iterator(Sections.data()); }
- iterator end() { return iterator(Sections.data() + Sections.size()); }
+ iterator begin() const { return iterator(Sections.data()); }
+ iterator end() const { return iterator(Sections.data() + Sections.size()); }
size_t size() const { return Sections.size(); }
SectionBase *getSection(uint32_t Index, Twine ErrMsg);
@@ -863,7 +863,7 @@ public:
class Reader {
public:
virtual ~Reader();
- virtual std::unique_ptr<Object> create() const = 0;
+ virtual std::unique_ptr<Object> create(bool EnsureSymtab) const = 0;
};
using object::Binary;
@@ -873,7 +873,6 @@ using object::OwningBinary;
class BasicELFBuilder {
protected:
- uint16_t EMachine;
std::unique_ptr<Object> Obj;
void initFileHeader();
@@ -883,17 +882,18 @@ protected:
void initSections();
public:
- BasicELFBuilder(uint16_t EM)
- : EMachine(EM), Obj(llvm::make_unique<Object>()) {}
+ BasicELFBuilder() : Obj(std::make_unique<Object>()) {}
};
class BinaryELFBuilder : public BasicELFBuilder {
MemoryBuffer *MemBuf;
+ uint8_t NewSymbolVisibility;
void addData(SymbolTableSection *SymTab);
public:
- BinaryELFBuilder(uint16_t EM, MemoryBuffer *MB)
- : BasicELFBuilder(EM), MemBuf(MB) {}
+ BinaryELFBuilder(MemoryBuffer *MB, uint8_t NewSymbolVisibility)
+ : BasicELFBuilder(), MemBuf(MB),
+ NewSymbolVisibility(NewSymbolVisibility) {}
std::unique_ptr<Object> build();
};
@@ -905,7 +905,7 @@ class IHexELFBuilder : public BasicELFBuilder {
public:
IHexELFBuilder(const std::vector<IHexRecord> &Records)
- : BasicELFBuilder(ELF::EM_386), Records(Records) {}
+ : BasicELFBuilder(), Records(Records) {}
std::unique_ptr<Object> build();
};
@@ -926,7 +926,7 @@ private:
void initGroupSection(GroupSection *GroupSec);
void initSymbolTable(SymbolTableSection *SymTab);
void readSectionHeaders();
- void readSections();
+ void readSections(bool EnsureSymtab);
void findEhdrOffset();
SectionBase &makeSection(const Elf_Shdr &Shdr);
@@ -936,17 +936,17 @@ public:
: ElfFile(*ElfObj.getELFFile()), Obj(Obj),
ExtractPartition(ExtractPartition) {}
- void build();
+ void build(bool EnsureSymtab);
};
class BinaryReader : public Reader {
- const MachineInfo &MInfo;
MemoryBuffer *MemBuf;
+ uint8_t NewSymbolVisibility;
public:
- BinaryReader(const MachineInfo &MI, MemoryBuffer *MB)
- : MInfo(MI), MemBuf(MB) {}
- std::unique_ptr<Object> create() const override;
+ BinaryReader(MemoryBuffer *MB, const uint8_t NewSymbolVisibility)
+ : MemBuf(MB), NewSymbolVisibility(NewSymbolVisibility) {}
+ std::unique_ptr<Object> create(bool EnsureSymtab) const override;
};
class IHexReader : public Reader {
@@ -968,7 +968,7 @@ class IHexReader : public Reader {
public:
IHexReader(MemoryBuffer *MB) : MemBuf(MB) {}
- std::unique_ptr<Object> create() const override;
+ std::unique_ptr<Object> create(bool EnsureSymtab) const override;
};
class ELFReader : public Reader {
@@ -976,7 +976,7 @@ class ELFReader : public Reader {
Optional<StringRef> ExtractPartition;
public:
- std::unique_ptr<Object> create() const override;
+ std::unique_ptr<Object> create(bool EnsureSymtab) const override;
explicit ELFReader(Binary *B, Optional<StringRef> ExtractPartition)
: Bin(B), ExtractPartition(ExtractPartition) {}
};
@@ -990,6 +990,10 @@ private:
std::vector<SegPtr> Segments;
std::vector<SecPtr> RemovedSections;
+ static bool sectionIsAlloc(const SectionBase &Sec) {
+ return Sec.Flags & ELF::SHF_ALLOC;
+ };
+
public:
template <class T>
using Range = iterator_range<
@@ -1011,13 +1015,14 @@ public:
uint8_t OSABI;
uint8_t ABIVersion;
uint64_t Entry;
- uint64_t SHOffset;
+ uint64_t SHOff;
uint32_t Type;
uint32_t Machine;
uint32_t Version;
uint32_t Flags;
bool HadShdrs = true;
+ bool MustBeRelocatable = false;
StringTableSection *SectionNames = nullptr;
SymbolTableSection *SymbolTable = nullptr;
SectionIndexSection *SectionIndexTable = nullptr;
@@ -1027,6 +1032,13 @@ public:
ConstRange<SectionBase> sections() const {
return make_pointee_range(Sections);
}
+ iterator_range<
+ filter_iterator<pointee_iterator<std::vector<SecPtr>::const_iterator>,
+ decltype(&sectionIsAlloc)>>
+ allocSections() const {
+ return make_filter_range(make_pointee_range(Sections), sectionIsAlloc);
+ }
+
SectionBase *findSection(StringRef Name) {
auto SecIt =
find_if(Sections, [&](const SecPtr &Sec) { return Sec->Name == Name; });
@@ -1041,16 +1053,20 @@ public:
std::function<bool(const SectionBase &)> ToRemove);
Error removeSymbols(function_ref<bool(const Symbol &)> ToRemove);
template <class T, class... Ts> T &addSection(Ts &&... Args) {
- auto Sec = llvm::make_unique<T>(std::forward<Ts>(Args)...);
+ auto Sec = std::make_unique<T>(std::forward<Ts>(Args)...);
auto Ptr = Sec.get();
+ MustBeRelocatable |= isa<RelocationSection>(*Ptr);
Sections.emplace_back(std::move(Sec));
Ptr->Index = Sections.size();
return *Ptr;
}
Segment &addSegment(ArrayRef<uint8_t> Data) {
- Segments.emplace_back(llvm::make_unique<Segment>(Data));
+ Segments.emplace_back(std::make_unique<Segment>(Data));
return *Segments.back();
}
+ bool isRelocatable() const {
+ return (Type != ELF::ET_DYN && Type != ELF::ET_EXEC) || MustBeRelocatable;
+ }
};
} // end namespace elf
diff --git a/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp b/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp
new file mode 100644
index 000000000000..f621f3aa09cf
--- /dev/null
+++ b/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp
@@ -0,0 +1,350 @@
+//===- MachOLayoutBuilder.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachOLayoutBuilder.h"
+#include "llvm/Support/Alignment.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/ErrorHandling.h"
+
+namespace llvm {
+namespace objcopy {
+namespace macho {
+
+uint32_t MachOLayoutBuilder::computeSizeOfCmds() const {
+ uint32_t Size = 0;
+ for (const auto &LC : O.LoadCommands) {
+ const MachO::macho_load_command &MLC = LC.MachOLoadCommand;
+ auto cmd = MLC.load_command_data.cmd;
+ switch (cmd) {
+ case MachO::LC_SEGMENT:
+ Size += sizeof(MachO::segment_command) +
+ sizeof(MachO::section) * LC.Sections.size();
+ continue;
+ case MachO::LC_SEGMENT_64:
+ Size += sizeof(MachO::segment_command_64) +
+ sizeof(MachO::section_64) * LC.Sections.size();
+ continue;
+ }
+
+ switch (cmd) {
+#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \
+ case MachO::LCName: \
+ Size += sizeof(MachO::LCStruct) + LC.Payload.size(); \
+ break;
+#include "llvm/BinaryFormat/MachO.def"
+#undef HANDLE_LOAD_COMMAND
+ }
+ }
+
+ return Size;
+}
+
+void MachOLayoutBuilder::constructStringTable() {
+ for (std::unique_ptr<SymbolEntry> &Sym : O.SymTable.Symbols)
+ StrTableBuilder.add(Sym->Name);
+ StrTableBuilder.finalize();
+}
+
+void MachOLayoutBuilder::updateSymbolIndexes() {
+ uint32_t Index = 0;
+ for (auto &Symbol : O.SymTable.Symbols)
+ Symbol->Index = Index++;
+}
+
+// Updates the index and the number of local/external/undefined symbols.
+void MachOLayoutBuilder::updateDySymTab(MachO::macho_load_command &MLC) {
+ assert(MLC.load_command_data.cmd == MachO::LC_DYSYMTAB);
+ // Make sure that nlist entries in the symbol table are sorted by the those
+ // types. The order is: local < defined external < undefined external.
+ assert(std::is_sorted(O.SymTable.Symbols.begin(), O.SymTable.Symbols.end(),
+ [](const std::unique_ptr<SymbolEntry> &A,
+ const std::unique_ptr<SymbolEntry> &B) {
+ return (A->isLocalSymbol() && !B->isLocalSymbol()) ||
+ (!A->isUndefinedSymbol() &&
+ B->isUndefinedSymbol());
+ }) &&
+ "Symbols are not sorted by their types.");
+
+ uint32_t NumLocalSymbols = 0;
+ auto Iter = O.SymTable.Symbols.begin();
+ auto End = O.SymTable.Symbols.end();
+ for (; Iter != End; ++Iter) {
+ if ((*Iter)->isExternalSymbol())
+ break;
+
+ ++NumLocalSymbols;
+ }
+
+ uint32_t NumExtDefSymbols = 0;
+ for (; Iter != End; ++Iter) {
+ if ((*Iter)->isUndefinedSymbol())
+ break;
+
+ ++NumExtDefSymbols;
+ }
+
+ MLC.dysymtab_command_data.ilocalsym = 0;
+ MLC.dysymtab_command_data.nlocalsym = NumLocalSymbols;
+ MLC.dysymtab_command_data.iextdefsym = NumLocalSymbols;
+ MLC.dysymtab_command_data.nextdefsym = NumExtDefSymbols;
+ MLC.dysymtab_command_data.iundefsym = NumLocalSymbols + NumExtDefSymbols;
+ MLC.dysymtab_command_data.nundefsym =
+ O.SymTable.Symbols.size() - (NumLocalSymbols + NumExtDefSymbols);
+}
+
+// Recomputes and updates offset and size fields in load commands and sections
+// since they could be modified.
+uint64_t MachOLayoutBuilder::layoutSegments() {
+ auto HeaderSize =
+ Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);
+ const bool IsObjectFile =
+ O.Header.FileType == MachO::HeaderFileType::MH_OBJECT;
+ uint64_t Offset = IsObjectFile ? (HeaderSize + O.Header.SizeOfCmds) : 0;
+ for (auto &LC : O.LoadCommands) {
+ auto &MLC = LC.MachOLoadCommand;
+ StringRef Segname;
+ uint64_t SegmentVmAddr;
+ uint64_t SegmentVmSize;
+ switch (MLC.load_command_data.cmd) {
+ case MachO::LC_SEGMENT:
+ SegmentVmAddr = MLC.segment_command_data.vmaddr;
+ SegmentVmSize = MLC.segment_command_data.vmsize;
+ Segname = StringRef(MLC.segment_command_data.segname,
+ strnlen(MLC.segment_command_data.segname,
+ sizeof(MLC.segment_command_data.segname)));
+ break;
+ case MachO::LC_SEGMENT_64:
+ SegmentVmAddr = MLC.segment_command_64_data.vmaddr;
+ SegmentVmSize = MLC.segment_command_64_data.vmsize;
+ Segname = StringRef(MLC.segment_command_64_data.segname,
+ strnlen(MLC.segment_command_64_data.segname,
+ sizeof(MLC.segment_command_64_data.segname)));
+ break;
+ default:
+ continue;
+ }
+
+ if (Segname == "__LINKEDIT") {
+ // We update the __LINKEDIT segment later (in layoutTail).
+ assert(LC.Sections.empty() && "__LINKEDIT segment has sections");
+ LinkEditLoadCommand = &MLC;
+ continue;
+ }
+
+ // Update file offsets and sizes of sections.
+ uint64_t SegOffset = Offset;
+ uint64_t SegFileSize = 0;
+ uint64_t VMSize = 0;
+ for (auto &Sec : LC.Sections) {
+ if (IsObjectFile) {
+ if (Sec.isVirtualSection()) {
+ Sec.Offset = 0;
+ } else {
+ uint64_t PaddingSize =
+ offsetToAlignment(SegFileSize, Align(1ull << Sec.Align));
+ Sec.Offset = SegOffset + SegFileSize + PaddingSize;
+ Sec.Size = Sec.Content.size();
+ SegFileSize += PaddingSize + Sec.Size;
+ }
+ VMSize = std::max(VMSize, Sec.Addr + Sec.Size);
+ } else {
+ if (Sec.isVirtualSection()) {
+ Sec.Offset = 0;
+ VMSize += Sec.Size;
+ } else {
+ uint32_t SectOffset = Sec.Addr - SegmentVmAddr;
+ Sec.Offset = SegOffset + SectOffset;
+ Sec.Size = Sec.Content.size();
+ SegFileSize = std::max(SegFileSize, SectOffset + Sec.Size);
+ VMSize = std::max(VMSize, SegFileSize);
+ }
+ }
+ }
+
+ if (IsObjectFile) {
+ Offset += SegFileSize;
+ } else {
+ Offset = alignTo(Offset + SegFileSize, PageSize);
+ SegFileSize = alignTo(SegFileSize, PageSize);
+ // Use the original vmsize if the segment is __PAGEZERO.
+ VMSize =
+ Segname == "__PAGEZERO" ? SegmentVmSize : alignTo(VMSize, PageSize);
+ }
+
+ switch (MLC.load_command_data.cmd) {
+ case MachO::LC_SEGMENT:
+ MLC.segment_command_data.cmdsize =
+ sizeof(MachO::segment_command) +
+ sizeof(MachO::section) * LC.Sections.size();
+ MLC.segment_command_data.nsects = LC.Sections.size();
+ MLC.segment_command_data.fileoff = SegOffset;
+ MLC.segment_command_data.vmsize = VMSize;
+ MLC.segment_command_data.filesize = SegFileSize;
+ break;
+ case MachO::LC_SEGMENT_64:
+ MLC.segment_command_64_data.cmdsize =
+ sizeof(MachO::segment_command_64) +
+ sizeof(MachO::section_64) * LC.Sections.size();
+ MLC.segment_command_64_data.nsects = LC.Sections.size();
+ MLC.segment_command_64_data.fileoff = SegOffset;
+ MLC.segment_command_64_data.vmsize = VMSize;
+ MLC.segment_command_64_data.filesize = SegFileSize;
+ break;
+ }
+ }
+
+ return Offset;
+}
+
+uint64_t MachOLayoutBuilder::layoutRelocations(uint64_t Offset) {
+ for (auto &LC : O.LoadCommands)
+ for (auto &Sec : LC.Sections) {
+ Sec.RelOff = Sec.Relocations.empty() ? 0 : Offset;
+ Sec.NReloc = Sec.Relocations.size();
+ Offset += sizeof(MachO::any_relocation_info) * Sec.NReloc;
+ }
+
+ return Offset;
+}
+
+Error MachOLayoutBuilder::layoutTail(uint64_t Offset) {
+ // The order of LINKEDIT elements is as follows:
+ // rebase info, binding info, weak binding info, lazy binding info, export
+ // trie, data-in-code, symbol table, indirect symbol table, symbol table
+ // strings.
+ uint64_t NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist);
+ uint64_t StartOfLinkEdit = Offset;
+ uint64_t StartOfRebaseInfo = StartOfLinkEdit;
+ uint64_t StartOfBindingInfo = StartOfRebaseInfo + O.Rebases.Opcodes.size();
+ uint64_t StartOfWeakBindingInfo = StartOfBindingInfo + O.Binds.Opcodes.size();
+ uint64_t StartOfLazyBindingInfo =
+ StartOfWeakBindingInfo + O.WeakBinds.Opcodes.size();
+ uint64_t StartOfExportTrie =
+ StartOfLazyBindingInfo + O.LazyBinds.Opcodes.size();
+ uint64_t StartOfFunctionStarts = StartOfExportTrie + O.Exports.Trie.size();
+ uint64_t StartOfDataInCode =
+ StartOfFunctionStarts + O.FunctionStarts.Data.size();
+ uint64_t StartOfSymbols = StartOfDataInCode + O.DataInCode.Data.size();
+ uint64_t StartOfIndirectSymbols =
+ StartOfSymbols + NListSize * O.SymTable.Symbols.size();
+ uint64_t StartOfSymbolStrings =
+ StartOfIndirectSymbols +
+ sizeof(uint32_t) * O.IndirectSymTable.Symbols.size();
+ uint64_t LinkEditSize =
+ (StartOfSymbolStrings + StrTableBuilder.getSize()) - StartOfLinkEdit;
+
+ // Now we have determined the layout of the contents of the __LINKEDIT
+ // segment. Update its load command.
+ if (LinkEditLoadCommand) {
+ MachO::macho_load_command *MLC = LinkEditLoadCommand;
+ switch (LinkEditLoadCommand->load_command_data.cmd) {
+ case MachO::LC_SEGMENT:
+ MLC->segment_command_data.cmdsize = sizeof(MachO::segment_command);
+ MLC->segment_command_data.fileoff = StartOfLinkEdit;
+ MLC->segment_command_data.vmsize = alignTo(LinkEditSize, PageSize);
+ MLC->segment_command_data.filesize = LinkEditSize;
+ break;
+ case MachO::LC_SEGMENT_64:
+ MLC->segment_command_64_data.cmdsize = sizeof(MachO::segment_command_64);
+ MLC->segment_command_64_data.fileoff = StartOfLinkEdit;
+ MLC->segment_command_64_data.vmsize = alignTo(LinkEditSize, PageSize);
+ MLC->segment_command_64_data.filesize = LinkEditSize;
+ break;
+ }
+ }
+
+ for (auto &LC : O.LoadCommands) {
+ auto &MLC = LC.MachOLoadCommand;
+ auto cmd = MLC.load_command_data.cmd;
+ switch (cmd) {
+ case MachO::LC_SYMTAB:
+ MLC.symtab_command_data.symoff = StartOfSymbols;
+ MLC.symtab_command_data.nsyms = O.SymTable.Symbols.size();
+ MLC.symtab_command_data.stroff = StartOfSymbolStrings;
+ MLC.symtab_command_data.strsize = StrTableBuilder.getSize();
+ break;
+ case MachO::LC_DYSYMTAB: {
+ if (MLC.dysymtab_command_data.ntoc != 0 ||
+ MLC.dysymtab_command_data.nmodtab != 0 ||
+ MLC.dysymtab_command_data.nextrefsyms != 0 ||
+ MLC.dysymtab_command_data.nlocrel != 0 ||
+ MLC.dysymtab_command_data.nextrel != 0)
+ return createStringError(llvm::errc::not_supported,
+ "shared library is not yet supported");
+
+ if (!O.IndirectSymTable.Symbols.empty()) {
+ MLC.dysymtab_command_data.indirectsymoff = StartOfIndirectSymbols;
+ MLC.dysymtab_command_data.nindirectsyms =
+ O.IndirectSymTable.Symbols.size();
+ }
+
+ updateDySymTab(MLC);
+ break;
+ }
+ case MachO::LC_DATA_IN_CODE:
+ MLC.linkedit_data_command_data.dataoff = StartOfDataInCode;
+ MLC.linkedit_data_command_data.datasize = O.DataInCode.Data.size();
+ break;
+ case MachO::LC_FUNCTION_STARTS:
+ MLC.linkedit_data_command_data.dataoff = StartOfFunctionStarts;
+ MLC.linkedit_data_command_data.datasize = O.FunctionStarts.Data.size();
+ break;
+ case MachO::LC_DYLD_INFO:
+ case MachO::LC_DYLD_INFO_ONLY:
+ MLC.dyld_info_command_data.rebase_off =
+ O.Rebases.Opcodes.empty() ? 0 : StartOfRebaseInfo;
+ MLC.dyld_info_command_data.rebase_size = O.Rebases.Opcodes.size();
+ MLC.dyld_info_command_data.bind_off =
+ O.Binds.Opcodes.empty() ? 0 : StartOfBindingInfo;
+ MLC.dyld_info_command_data.bind_size = O.Binds.Opcodes.size();
+ MLC.dyld_info_command_data.weak_bind_off =
+ O.WeakBinds.Opcodes.empty() ? 0 : StartOfWeakBindingInfo;
+ MLC.dyld_info_command_data.weak_bind_size = O.WeakBinds.Opcodes.size();
+ MLC.dyld_info_command_data.lazy_bind_off =
+ O.LazyBinds.Opcodes.empty() ? 0 : StartOfLazyBindingInfo;
+ MLC.dyld_info_command_data.lazy_bind_size = O.LazyBinds.Opcodes.size();
+ MLC.dyld_info_command_data.export_off =
+ O.Exports.Trie.empty() ? 0 : StartOfExportTrie;
+ MLC.dyld_info_command_data.export_size = O.Exports.Trie.size();
+ break;
+ case MachO::LC_LOAD_DYLINKER:
+ case MachO::LC_MAIN:
+ case MachO::LC_RPATH:
+ case MachO::LC_SEGMENT:
+ case MachO::LC_SEGMENT_64:
+ case MachO::LC_VERSION_MIN_MACOSX:
+ case MachO::LC_BUILD_VERSION:
+ case MachO::LC_ID_DYLIB:
+ case MachO::LC_LOAD_DYLIB:
+ case MachO::LC_UUID:
+ case MachO::LC_SOURCE_VERSION:
+ // Nothing to update.
+ break;
+ default:
+ // Abort if it's unsupported in order to prevent corrupting the object.
+ return createStringError(llvm::errc::not_supported,
+ "unsupported load command (cmd=0x%x)", cmd);
+ }
+ }
+
+ return Error::success();
+}
+
+Error MachOLayoutBuilder::layout() {
+ O.Header.NCmds = O.LoadCommands.size();
+ O.Header.SizeOfCmds = computeSizeOfCmds();
+ constructStringTable();
+ updateSymbolIndexes();
+ uint64_t Offset = layoutSegments();
+ Offset = layoutRelocations(Offset);
+ return layoutTail(Offset);
+}
+
+} // end namespace macho
+} // end namespace objcopy
+} // end namespace llvm
diff --git a/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h b/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h
new file mode 100644
index 000000000000..21cbe56605de
--- /dev/null
+++ b/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h
@@ -0,0 +1,50 @@
+//===- MachOLayoutBuilder.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_OBJCOPY_MACHO_MACHOLAYOUTBUILDER_H
+#define LLVM_OBJCOPY_MACHO_MACHOLAYOUTBUILDER_H
+
+#include "MachOObjcopy.h"
+#include "Object.h"
+
+namespace llvm {
+namespace objcopy {
+namespace macho {
+
+class MachOLayoutBuilder {
+ Object &O;
+ bool Is64Bit;
+ uint64_t PageSize;
+
+ // Points to the __LINKEDIT segment if it exists.
+ MachO::macho_load_command *LinkEditLoadCommand = nullptr;
+ StringTableBuilder StrTableBuilder{StringTableBuilder::MachO};
+
+ uint32_t computeSizeOfCmds() const;
+ void constructStringTable();
+ void updateSymbolIndexes();
+ void updateDySymTab(MachO::macho_load_command &MLC);
+ uint64_t layoutSegments();
+ uint64_t layoutRelocations(uint64_t Offset);
+ Error layoutTail(uint64_t Offset);
+
+public:
+ MachOLayoutBuilder(Object &O, bool Is64Bit, uint64_t PageSize)
+ : O(O), Is64Bit(Is64Bit), PageSize(PageSize) {}
+
+ // Recomputes and updates fields in the given object such as file offsets.
+ Error layout();
+
+ StringTableBuilder &getStringTableBuilder() { return StrTableBuilder; }
+};
+
+} // end namespace macho
+} // end namespace objcopy
+} // end namespace llvm
+
+#endif // LLVM_OBJCOPY_MACHO_MACHOLAYOUTBUILDER_H
diff --git a/tools/llvm-objcopy/MachO/MachOObjcopy.cpp b/tools/llvm-objcopy/MachO/MachOObjcopy.cpp
index 19343b65dd1e..6d586e7d73f1 100644
--- a/tools/llvm-objcopy/MachO/MachOObjcopy.cpp
+++ b/tools/llvm-objcopy/MachO/MachOObjcopy.cpp
@@ -25,18 +25,20 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
!Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() ||
!Config.AllocSectionsPrefix.empty() || !Config.AddSection.empty() ||
!Config.DumpSection.empty() || !Config.KeepSection.empty() ||
- !Config.OnlySection.empty() || !Config.SymbolsToGlobalize.empty() ||
- !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() ||
- !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() ||
- !Config.SectionsToRename.empty() || !Config.SymbolsToRename.empty() ||
+ Config.NewSymbolVisibility || !Config.OnlySection.empty() ||
+ !Config.SymbolsToGlobalize.empty() || !Config.SymbolsToKeep.empty() ||
+ !Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() ||
+ !Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() ||
+ !Config.SymbolsToRename.empty() ||
!Config.UnneededSymbolsToRemove.empty() ||
- !Config.SetSectionFlags.empty() || !Config.ToRemove.empty() ||
- Config.ExtractDWO || Config.KeepFileSymbols || Config.LocalizeHidden ||
- Config.PreserveDates || Config.StripDWO || Config.StripNonAlloc ||
- Config.StripSections || Config.Weaken || Config.DecompressDebugSections ||
- Config.StripDebug || Config.StripNonAlloc || Config.StripSections ||
- Config.StripUnneeded || Config.DiscardMode != DiscardType::None ||
- !Config.SymbolsToAdd.empty() || Config.EntryExpr) {
+ !Config.SetSectionAlignment.empty() || !Config.SetSectionFlags.empty() ||
+ !Config.ToRemove.empty() || Config.ExtractDWO || Config.KeepFileSymbols ||
+ Config.LocalizeHidden || Config.PreserveDates || Config.StripDWO ||
+ Config.StripNonAlloc || Config.StripSections || Config.Weaken ||
+ Config.DecompressDebugSections || Config.StripDebug ||
+ Config.StripNonAlloc || Config.StripSections || Config.StripUnneeded ||
+ Config.DiscardMode != DiscardType::None || !Config.SymbolsToAdd.empty() ||
+ Config.EntryExpr) {
return createStringError(llvm::errc::invalid_argument,
"option not supported by llvm-objcopy for MachO");
}
@@ -57,7 +59,11 @@ Error executeObjcopyOnBinary(const CopyConfig &Config,
if (Error E = handleArgs(Config, *O))
return createFileError(Config.InputFilename, std::move(E));
- MachOWriter Writer(*O, In.is64Bit(), In.isLittleEndian(), Out);
+ // TODO: Support 16KB pages which are employed in iOS arm64 binaries:
+ // https://github.com/llvm/llvm-project/commit/1bebb2832ee312d3b0316dacff457a7a29435edb
+ const uint64_t PageSize = 4096;
+
+ MachOWriter Writer(*O, In.is64Bit(), In.isLittleEndian(), PageSize, Out);
if (auto E = Writer.finalize())
return E;
return Writer.write();
diff --git a/tools/llvm-objcopy/MachO/MachOReader.cpp b/tools/llvm-objcopy/MachO/MachOReader.cpp
index d31293034608..b48a0d8952d0 100644
--- a/tools/llvm-objcopy/MachO/MachOReader.cpp
+++ b/tools/llvm-objcopy/MachO/MachOReader.cpp
@@ -129,10 +129,19 @@ void MachOReader::readLoadCommands(Object &O) const {
case MachO::LC_SYMTAB:
O.SymTabCommandIndex = O.LoadCommands.size();
break;
+ case MachO::LC_DYSYMTAB:
+ O.DySymTabCommandIndex = O.LoadCommands.size();
+ break;
case MachO::LC_DYLD_INFO:
case MachO::LC_DYLD_INFO_ONLY:
O.DyLdInfoCommandIndex = O.LoadCommands.size();
break;
+ case MachO::LC_DATA_IN_CODE:
+ O.DataInCodeCommandIndex = O.LoadCommands.size();
+ break;
+ case MachO::LC_FUNCTION_STARTS:
+ O.FunctionStartsCommandIndex = O.LoadCommands.size();
+ break;
}
#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \
case MachO::LCName: \
@@ -188,7 +197,7 @@ void MachOReader::readSymbolTable(Object &O) const {
StrTable,
MachOObj.getSymbolTableEntry(Symbol.getRawDataRefImpl())));
- O.SymTable.Symbols.push_back(llvm::make_unique<SymbolEntry>(SE));
+ O.SymTable.Symbols.push_back(std::make_unique<SymbolEntry>(SE));
}
}
@@ -222,8 +231,37 @@ void MachOReader::readExportInfo(Object &O) const {
O.Exports.Trie = MachOObj.getDyldInfoExportsTrie();
}
+void MachOReader::readDataInCodeData(Object &O) const {
+ if (!O.DataInCodeCommandIndex)
+ return;
+ const MachO::linkedit_data_command &LDC =
+ O.LoadCommands[*O.DataInCodeCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+
+ O.DataInCode.Data = arrayRefFromStringRef(
+ MachOObj.getData().substr(LDC.dataoff, LDC.datasize));
+}
+
+void MachOReader::readFunctionStartsData(Object &O) const {
+ if (!O.FunctionStartsCommandIndex)
+ return;
+ const MachO::linkedit_data_command &LDC =
+ O.LoadCommands[*O.FunctionStartsCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+
+ O.FunctionStarts.Data = arrayRefFromStringRef(
+ MachOObj.getData().substr(LDC.dataoff, LDC.datasize));
+}
+
+void MachOReader::readIndirectSymbolTable(Object &O) const {
+ MachO::dysymtab_command DySymTab = MachOObj.getDysymtabLoadCommand();
+ for (uint32_t i = 0; i < DySymTab.nindirectsyms; ++i)
+ O.IndirectSymTable.Symbols.push_back(
+ MachOObj.getIndirectSymbolTableEntry(DySymTab, i));
+}
+
std::unique_ptr<Object> MachOReader::create() const {
- auto Obj = llvm::make_unique<Object>();
+ auto Obj = std::make_unique<Object>();
readHeader(*Obj);
readLoadCommands(*Obj);
readSymbolTable(*Obj);
@@ -233,6 +271,9 @@ std::unique_ptr<Object> MachOReader::create() const {
readWeakBindInfo(*Obj);
readLazyBindInfo(*Obj);
readExportInfo(*Obj);
+ readDataInCodeData(*Obj);
+ readFunctionStartsData(*Obj);
+ readIndirectSymbolTable(*Obj);
return Obj;
}
diff --git a/tools/llvm-objcopy/MachO/MachOReader.h b/tools/llvm-objcopy/MachO/MachOReader.h
index 795e5cc2363d..00c8f0d55f61 100644
--- a/tools/llvm-objcopy/MachO/MachOReader.h
+++ b/tools/llvm-objcopy/MachO/MachOReader.h
@@ -36,6 +36,9 @@ class MachOReader : public Reader {
void readWeakBindInfo(Object &O) const;
void readLazyBindInfo(Object &O) const;
void readExportInfo(Object &O) const;
+ void readDataInCodeData(Object &O) const;
+ void readFunctionStartsData(Object &O) const;
+ void readIndirectSymbolTable(Object &O) const;
public:
explicit MachOReader(const object::MachOObjectFile &Obj) : MachOObj(Obj) {}
diff --git a/tools/llvm-objcopy/MachO/MachOWriter.cpp b/tools/llvm-objcopy/MachO/MachOWriter.cpp
index 74200c5aa62a..4ec91cc9eb7a 100644
--- a/tools/llvm-objcopy/MachO/MachOWriter.cpp
+++ b/tools/llvm-objcopy/MachO/MachOWriter.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "MachOWriter.h"
+#include "MachOLayoutBuilder.h"
#include "Object.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/BinaryFormat/MachO.h"
@@ -40,16 +41,10 @@ size_t MachOWriter::totalSize() const {
const MachO::symtab_command &SymTabCommand =
O.LoadCommands[*O.SymTabCommandIndex]
.MachOLoadCommand.symtab_command_data;
- if (SymTabCommand.symoff) {
- assert((SymTabCommand.nsyms == O.SymTable.Symbols.size()) &&
- "Incorrect number of symbols");
+ if (SymTabCommand.symoff)
Ends.push_back(SymTabCommand.symoff + symTableSize());
- }
- if (SymTabCommand.stroff) {
- assert((SymTabCommand.strsize == StrTableBuilder.getSize()) &&
- "Incorrect string table size");
+ if (SymTabCommand.stroff)
Ends.push_back(SymTabCommand.stroff + SymTabCommand.strsize);
- }
}
if (O.DyLdInfoCommandIndex) {
const MachO::dyld_info_command &DyLdInfoCommand =
@@ -84,6 +79,36 @@ size_t MachOWriter::totalSize() const {
}
}
+ if (O.DySymTabCommandIndex) {
+ const MachO::dysymtab_command &DySymTabCommand =
+ O.LoadCommands[*O.DySymTabCommandIndex]
+ .MachOLoadCommand.dysymtab_command_data;
+
+ if (DySymTabCommand.indirectsymoff)
+ Ends.push_back(DySymTabCommand.indirectsymoff +
+ sizeof(uint32_t) * O.IndirectSymTable.Symbols.size());
+ }
+
+ if (O.DataInCodeCommandIndex) {
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.DataInCodeCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+
+ if (LinkEditDataCommand.dataoff)
+ Ends.push_back(LinkEditDataCommand.dataoff +
+ LinkEditDataCommand.datasize);
+ }
+
+ if (O.FunctionStartsCommandIndex) {
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.FunctionStartsCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+
+ if (LinkEditDataCommand.dataoff)
+ Ends.push_back(LinkEditDataCommand.dataoff +
+ LinkEditDataCommand.datasize);
+ }
+
// Otherwise, use the last section / reloction.
for (const auto &LC : O.LoadCommands)
for (const auto &S : LC.Sections) {
@@ -120,14 +145,6 @@ void MachOWriter::writeHeader() {
memcpy(B.getBufferStart(), &Header, HeaderSize);
}
-void MachOWriter::updateSymbolIndexes() {
- uint32_t Index = 0;
- for (auto &Symbol : O.SymTable.Symbols) {
- Symbol->Index = Index;
- Index++;
- }
-}
-
void MachOWriter::writeLoadCommands() {
uint8_t *Begin = B.getBufferStart() + headerSize();
for (const auto &LC : O.LoadCommands) {
@@ -253,7 +270,7 @@ void writeNListEntry(const SymbolEntry &SE, bool IsLittleEndian, char *&Out,
Out += sizeof(NListType);
}
-void MachOWriter::writeSymbolTable() {
+void MachOWriter::writeStringTable() {
if (!O.SymTabCommandIndex)
return;
const MachO::symtab_command &SymTabCommand =
@@ -261,10 +278,10 @@ void MachOWriter::writeSymbolTable() {
.MachOLoadCommand.symtab_command_data;
uint8_t *StrTable = (uint8_t *)B.getBufferStart() + SymTabCommand.stroff;
- StrTableBuilder.write(StrTable);
+ LayoutBuilder.getStringTableBuilder().write(StrTable);
}
-void MachOWriter::writeStringTable() {
+void MachOWriter::writeSymbolTable() {
if (!O.SymTabCommandIndex)
return;
const MachO::symtab_command &SymTabCommand =
@@ -275,7 +292,7 @@ void MachOWriter::writeStringTable() {
for (auto Iter = O.SymTable.Symbols.begin(), End = O.SymTable.Symbols.end();
Iter != End; Iter++) {
SymbolEntry *Sym = Iter->get();
- auto Nstrx = StrTableBuilder.getOffset(Sym->Name);
+ uint32_t Nstrx = LayoutBuilder.getStringTableBuilder().getOffset(Sym->Name);
if (Is64Bit)
writeNListEntry<MachO::nlist_64>(*Sym, IsLittleEndian, SymTable, Nstrx);
@@ -344,6 +361,45 @@ void MachOWriter::writeExportInfo() {
memcpy(Out, O.Exports.Trie.data(), O.Exports.Trie.size());
}
+void MachOWriter::writeIndirectSymbolTable() {
+ if (!O.DySymTabCommandIndex)
+ return;
+
+ const MachO::dysymtab_command &DySymTabCommand =
+ O.LoadCommands[*O.DySymTabCommandIndex]
+ .MachOLoadCommand.dysymtab_command_data;
+
+ char *Out = (char *)B.getBufferStart() + DySymTabCommand.indirectsymoff;
+ assert((DySymTabCommand.nindirectsyms == O.IndirectSymTable.Symbols.size()) &&
+ "Incorrect indirect symbol table size");
+ memcpy(Out, O.IndirectSymTable.Symbols.data(),
+ sizeof(uint32_t) * O.IndirectSymTable.Symbols.size());
+}
+
+void MachOWriter::writeDataInCodeData() {
+ if (!O.DataInCodeCommandIndex)
+ return;
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.DataInCodeCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+ char *Out = (char *)B.getBufferStart() + LinkEditDataCommand.dataoff;
+ assert((LinkEditDataCommand.datasize == O.DataInCode.Data.size()) &&
+ "Incorrect data in code data size");
+ memcpy(Out, O.DataInCode.Data.data(), O.DataInCode.Data.size());
+}
+
+void MachOWriter::writeFunctionStartsData() {
+ if (!O.FunctionStartsCommandIndex)
+ return;
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.FunctionStartsCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
+ char *Out = (char *)B.getBufferStart() + LinkEditDataCommand.dataoff;
+ assert((LinkEditDataCommand.datasize == O.FunctionStarts.Data.size()) &&
+ "Incorrect function starts data size");
+ memcpy(Out, O.FunctionStarts.Data.data(), O.FunctionStarts.Data.size());
+}
+
void MachOWriter::writeTail() {
typedef void (MachOWriter::*WriteHandlerType)(void);
typedef std::pair<uint64_t, WriteHandlerType> WriteOperation;
@@ -379,206 +435,51 @@ void MachOWriter::writeTail() {
{DyLdInfoCommand.export_off, &MachOWriter::writeExportInfo});
}
- llvm::sort(Queue, [](const WriteOperation &LHS, const WriteOperation &RHS) {
- return LHS.first < RHS.first;
- });
-
- for (auto WriteOp : Queue)
- (this->*WriteOp.second)();
-}
-
-void MachOWriter::updateSizeOfCmds() {
- auto Size = 0;
- for (const auto &LC : O.LoadCommands) {
- auto &MLC = LC.MachOLoadCommand;
- auto cmd = MLC.load_command_data.cmd;
-
- switch (cmd) {
- case MachO::LC_SEGMENT:
- Size += sizeof(MachO::segment_command) +
- sizeof(MachO::section) * LC.Sections.size();
- continue;
- case MachO::LC_SEGMENT_64:
- Size += sizeof(MachO::segment_command_64) +
- sizeof(MachO::section_64) * LC.Sections.size();
- continue;
- }
-
- switch (cmd) {
-#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \
- case MachO::LCName: \
- Size += sizeof(MachO::LCStruct); \
- break;
-#include "llvm/BinaryFormat/MachO.def"
-#undef HANDLE_LOAD_COMMAND
- }
- }
-
- O.Header.SizeOfCmds = Size;
-}
-
-// Updates the index and the number of local/external/undefined symbols. Here we
-// assume that MLC is a LC_DYSYMTAB and the nlist entries in the symbol table
-// are already sorted by the those types.
-void MachOWriter::updateDySymTab(MachO::macho_load_command &MLC) {
- uint32_t NumLocalSymbols = 0;
- auto Iter = O.SymTable.Symbols.begin();
- auto End = O.SymTable.Symbols.end();
- for (; Iter != End; Iter++) {
- if ((*Iter)->n_type & (MachO::N_EXT | MachO::N_PEXT))
- break;
+ if (O.DySymTabCommandIndex) {
+ const MachO::dysymtab_command &DySymTabCommand =
+ O.LoadCommands[*O.DySymTabCommandIndex]
+ .MachOLoadCommand.dysymtab_command_data;
- NumLocalSymbols++;
+ if (DySymTabCommand.indirectsymoff)
+ Queue.emplace_back(DySymTabCommand.indirectsymoff,
+ &MachOWriter::writeIndirectSymbolTable);
}
- uint32_t NumExtDefSymbols = 0;
- for (; Iter != End; Iter++) {
- if (((*Iter)->n_type & MachO::N_TYPE) == MachO::N_UNDF)
- break;
-
- NumExtDefSymbols++;
- }
-
- MLC.dysymtab_command_data.ilocalsym = 0;
- MLC.dysymtab_command_data.nlocalsym = NumLocalSymbols;
- MLC.dysymtab_command_data.iextdefsym = NumLocalSymbols;
- MLC.dysymtab_command_data.nextdefsym = NumExtDefSymbols;
- MLC.dysymtab_command_data.iundefsym = NumLocalSymbols + NumExtDefSymbols;
- MLC.dysymtab_command_data.nundefsym =
- O.SymTable.Symbols.size() - (NumLocalSymbols + NumExtDefSymbols);
-}
-
-// Recomputes and updates offset and size fields in load commands and sections
-// since they could be modified.
-Error MachOWriter::layout() {
- auto SizeOfCmds = loadCommandsSize();
- auto Offset = headerSize() + SizeOfCmds;
- O.Header.NCmds = O.LoadCommands.size();
- O.Header.SizeOfCmds = SizeOfCmds;
-
- // Lay out sections.
- for (auto &LC : O.LoadCommands) {
- uint64_t FileOff = Offset;
- uint64_t VMSize = 0;
- uint64_t FileOffsetInSegment = 0;
- for (auto &Sec : LC.Sections) {
- if (!Sec.isVirtualSection()) {
- auto FilePaddingSize =
- OffsetToAlignment(FileOffsetInSegment, 1ull << Sec.Align);
- Sec.Offset = Offset + FileOffsetInSegment + FilePaddingSize;
- Sec.Size = Sec.Content.size();
- FileOffsetInSegment += FilePaddingSize + Sec.Size;
- }
-
- VMSize = std::max(VMSize, Sec.Addr + Sec.Size);
- }
-
- // TODO: Handle the __PAGEZERO segment.
- auto &MLC = LC.MachOLoadCommand;
- switch (MLC.load_command_data.cmd) {
- case MachO::LC_SEGMENT:
- MLC.segment_command_data.cmdsize =
- sizeof(MachO::segment_command) +
- sizeof(MachO::section) * LC.Sections.size();
- MLC.segment_command_data.nsects = LC.Sections.size();
- MLC.segment_command_data.fileoff = FileOff;
- MLC.segment_command_data.vmsize = VMSize;
- MLC.segment_command_data.filesize = FileOffsetInSegment;
- break;
- case MachO::LC_SEGMENT_64:
- MLC.segment_command_64_data.cmdsize =
- sizeof(MachO::segment_command_64) +
- sizeof(MachO::section_64) * LC.Sections.size();
- MLC.segment_command_64_data.nsects = LC.Sections.size();
- MLC.segment_command_64_data.fileoff = FileOff;
- MLC.segment_command_64_data.vmsize = VMSize;
- MLC.segment_command_64_data.filesize = FileOffsetInSegment;
- break;
- }
+ if (O.DataInCodeCommandIndex) {
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.DataInCodeCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
- Offset += FileOffsetInSegment;
+ if (LinkEditDataCommand.dataoff)
+ Queue.emplace_back(LinkEditDataCommand.dataoff,
+ &MachOWriter::writeDataInCodeData);
}
- // Lay out relocations.
- for (auto &LC : O.LoadCommands)
- for (auto &Sec : LC.Sections) {
- Sec.RelOff = Sec.Relocations.empty() ? 0 : Offset;
- Sec.NReloc = Sec.Relocations.size();
- Offset += sizeof(MachO::any_relocation_info) * Sec.NReloc;
- }
+ if (O.FunctionStartsCommandIndex) {
+ const MachO::linkedit_data_command &LinkEditDataCommand =
+ O.LoadCommands[*O.FunctionStartsCommandIndex]
+ .MachOLoadCommand.linkedit_data_command_data;
- // Lay out tail stuff.
- auto NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist);
- for (auto &LC : O.LoadCommands) {
- auto &MLC = LC.MachOLoadCommand;
- auto cmd = MLC.load_command_data.cmd;
- switch (cmd) {
- case MachO::LC_SYMTAB:
- MLC.symtab_command_data.nsyms = O.SymTable.Symbols.size();
- MLC.symtab_command_data.strsize = StrTableBuilder.getSize();
- MLC.symtab_command_data.symoff = Offset;
- Offset += NListSize * MLC.symtab_command_data.nsyms;
- MLC.symtab_command_data.stroff = Offset;
- Offset += MLC.symtab_command_data.strsize;
- break;
- case MachO::LC_DYSYMTAB: {
- if (MLC.dysymtab_command_data.ntoc != 0 ||
- MLC.dysymtab_command_data.nmodtab != 0 ||
- MLC.dysymtab_command_data.nextrefsyms != 0 ||
- MLC.dysymtab_command_data.nlocrel != 0 ||
- MLC.dysymtab_command_data.nextrel != 0)
- return createStringError(llvm::errc::not_supported,
- "shared library is not yet supported");
-
- if (MLC.dysymtab_command_data.nindirectsyms != 0)
- return createStringError(llvm::errc::not_supported,
- "indirect symbol table is not yet supported");
-
- updateDySymTab(MLC);
- break;
- }
- case MachO::LC_SEGMENT:
- case MachO::LC_SEGMENT_64:
- case MachO::LC_VERSION_MIN_MACOSX:
- case MachO::LC_BUILD_VERSION:
- case MachO::LC_ID_DYLIB:
- case MachO::LC_LOAD_DYLIB:
- case MachO::LC_UUID:
- case MachO::LC_SOURCE_VERSION:
- // Nothing to update.
- break;
- default:
- // Abort if it's unsupported in order to prevent corrupting the object.
- return createStringError(llvm::errc::not_supported,
- "unsupported load command (cmd=0x%x)", cmd);
- }
+ if (LinkEditDataCommand.dataoff)
+ Queue.emplace_back(LinkEditDataCommand.dataoff,
+ &MachOWriter::writeFunctionStartsData);
}
- return Error::success();
-}
+ llvm::sort(Queue, [](const WriteOperation &LHS, const WriteOperation &RHS) {
+ return LHS.first < RHS.first;
+ });
-void MachOWriter::constructStringTable() {
- for (std::unique_ptr<SymbolEntry> &Sym : O.SymTable.Symbols)
- StrTableBuilder.add(Sym->Name);
- StrTableBuilder.finalize();
+ for (auto WriteOp : Queue)
+ (this->*WriteOp.second)();
}
-Error MachOWriter::finalize() {
- updateSizeOfCmds();
- constructStringTable();
-
- if (auto E = layout())
- return E;
-
- return Error::success();
-}
+Error MachOWriter::finalize() { return LayoutBuilder.layout(); }
Error MachOWriter::write() {
if (Error E = B.allocate(totalSize()))
return E;
memset(B.getBufferStart(), 0, totalSize());
writeHeader();
- updateSymbolIndexes();
writeLoadCommands();
writeSections();
writeTail();
diff --git a/tools/llvm-objcopy/MachO/MachOWriter.h b/tools/llvm-objcopy/MachO/MachOWriter.h
index ecf12d62de2c..22abbad56f41 100644
--- a/tools/llvm-objcopy/MachO/MachOWriter.h
+++ b/tools/llvm-objcopy/MachO/MachOWriter.h
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "../Buffer.h"
+#include "MachOLayoutBuilder.h"
#include "MachOObjcopy.h"
#include "Object.h"
#include "llvm/BinaryFormat/MachO.h"
@@ -22,20 +23,15 @@ class MachOWriter {
Object &O;
bool Is64Bit;
bool IsLittleEndian;
+ uint64_t PageSize;
Buffer &B;
- StringTableBuilder StrTableBuilder{StringTableBuilder::MachO};
+ MachOLayoutBuilder LayoutBuilder;
size_t headerSize() const;
size_t loadCommandsSize() const;
size_t symTableSize() const;
size_t strTableSize() const;
- void updateDySymTab(MachO::macho_load_command &MLC);
- void updateSizeOfCmds();
- void updateSymbolIndexes();
- void constructStringTable();
- Error layout();
-
void writeHeader();
void writeLoadCommands();
template <typename StructType>
@@ -48,11 +44,16 @@ class MachOWriter {
void writeWeakBindInfo();
void writeLazyBindInfo();
void writeExportInfo();
+ void writeIndirectSymbolTable();
+ void writeDataInCodeData();
+ void writeFunctionStartsData();
void writeTail();
public:
- MachOWriter(Object &O, bool Is64Bit, bool IsLittleEndian, Buffer &B)
- : O(O), Is64Bit(Is64Bit), IsLittleEndian(IsLittleEndian), B(B) {}
+ MachOWriter(Object &O, bool Is64Bit, bool IsLittleEndian, uint64_t PageSize,
+ Buffer &B)
+ : O(O), Is64Bit(Is64Bit), IsLittleEndian(IsLittleEndian),
+ PageSize(PageSize), B(B), LayoutBuilder(O, Is64Bit, PageSize) {}
size_t totalSize() const;
Error finalize();
diff --git a/tools/llvm-objcopy/MachO/Object.h b/tools/llvm-objcopy/MachO/Object.h
index ed85fcbc47f7..1cebf8253d19 100644
--- a/tools/llvm-objcopy/MachO/Object.h
+++ b/tools/llvm-objcopy/MachO/Object.h
@@ -90,6 +90,16 @@ struct SymbolEntry {
uint8_t n_sect;
uint16_t n_desc;
uint64_t n_value;
+
+ bool isExternalSymbol() const {
+ return n_type & ((MachO::N_EXT | MachO::N_PEXT));
+ }
+
+ bool isLocalSymbol() const { return !isExternalSymbol(); }
+
+ bool isUndefinedSymbol() const {
+ return (n_type & MachO::N_TYPE) == MachO::N_UNDF;
+ }
};
/// The location of the symbol table inside the binary is described by LC_SYMTAB
@@ -100,6 +110,10 @@ struct SymbolTable {
const SymbolEntry *getSymbolByIndex(uint32_t Index) const;
};
+struct IndirectSymbolTable {
+ std::vector<uint32_t> Symbols;
+};
+
/// The location of the string table inside the binary is described by LC_SYMTAB
/// load command.
struct StringTable {
@@ -206,6 +220,10 @@ struct ExportInfo {
ArrayRef<uint8_t> Trie;
};
+struct LinkData {
+ ArrayRef<uint8_t> Data;
+};
+
struct Object {
MachHeader Header;
std::vector<LoadCommand> LoadCommands;
@@ -218,11 +236,20 @@ struct Object {
WeakBindInfo WeakBinds;
LazyBindInfo LazyBinds;
ExportInfo Exports;
+ IndirectSymbolTable IndirectSymTable;
+ LinkData DataInCode;
+ LinkData FunctionStarts;
/// The index of LC_SYMTAB load command if present.
Optional<size_t> SymTabCommandIndex;
/// The index of LC_DYLD_INFO or LC_DYLD_INFO_ONLY load command if present.
Optional<size_t> DyLdInfoCommandIndex;
+ /// The index LC_DYSYMTAB load comamnd if present.
+ Optional<size_t> DySymTabCommandIndex;
+ /// The index LC_DATA_IN_CODE load comamnd if present.
+ Optional<size_t> DataInCodeCommandIndex;
+ /// The index LC_FUNCTION_STARTS load comamnd if present.
+ Optional<size_t> FunctionStartsCommandIndex;
};
} // end namespace macho
diff --git a/tools/llvm-objcopy/ObjcopyOpts.td b/tools/llvm-objcopy/ObjcopyOpts.td
index 5fce4fbde539..9e6b6f0005cd 100644
--- a/tools/llvm-objcopy/ObjcopyOpts.td
+++ b/tools/llvm-objcopy/ObjcopyOpts.td
@@ -1,37 +1,33 @@
-include "llvm/Option/OptParser.td"
-
-multiclass Eq<string name, string help> {
- def NAME : Separate<["--"], name>;
- def NAME #_eq : Joined<["--"], name #"=">,
- Alias<!cast<Separate>(NAME)>,
- HelpText<help>;
-}
-
-def help : Flag<["--"], "help">;
-def h : Flag<["-"], "h">, Alias<help>;
-
-def allow_broken_links
- : Flag<["--"], "allow-broken-links">,
- HelpText<"Allow llvm-objcopy to remove sections even if it would leave "
- "invalid section references. The appropriate sh_link fields "
- "will be set to zero.">;
+include "CommonOpts.td"
defm binary_architecture
- : Eq<"binary-architecture", "Used when transforming an architecture-less "
- "format (such as binary) to another format">;
-def B : JoinedOrSeparate<["-"], "B">, Alias<binary_architecture>;
+ : Eq<"binary-architecture", "Ignored for compatibility">;
+def B : JoinedOrSeparate<["-"], "B">,
+ Alias<binary_architecture>,
+ HelpText<"Alias for --binary-architecture">;
defm target : Eq<"target", "Format of the input and output file">,
Values<"binary">;
-def F : JoinedOrSeparate<["-"], "F">, Alias<target>;
+def F : JoinedOrSeparate<["-"], "F">,
+ Alias<target>,
+ HelpText<"Alias for --target">;
defm input_target : Eq<"input-target", "Format of the input file">,
Values<"binary">;
-def I : JoinedOrSeparate<["-"], "I">, Alias<input_target>;
+def I : JoinedOrSeparate<["-"], "I">,
+ Alias<input_target>,
+ HelpText<"Alias for --input-target">;
defm output_target : Eq<"output-target", "Format of the output file">,
Values<"binary">;
-def O : JoinedOrSeparate<["-"], "O">, Alias<output_target>;
+def O : JoinedOrSeparate<["-"], "O">,
+ Alias<output_target>,
+ HelpText<"Alias for --output-target">;
+
+defm new_symbol_visibility : Eq<"new-symbol-visibility", "Visibility of "
+ "symbols generated for binary input or added"
+ " with --add-symbol unless otherwise"
+ " specified. The default value is 'default'.">;
def compress_debug_sections : Flag<["--"], "compress-debug-sections">;
def compress_debug_sections_eq
@@ -46,34 +42,10 @@ defm split_dwo
"<dwo-file>, then strip-dwo on the input file">,
MetaVarName<"dwo-file">;
-def enable_deterministic_archives
- : Flag<["--"], "enable-deterministic-archives">,
- HelpText<"Enable deterministic mode when copying archives (use zero for "
- "UIDs, GIDs, and timestamps).">;
-def D : Flag<["-"], "D">,
- Alias<enable_deterministic_archives>,
- HelpText<"Alias for --enable-deterministic-archives">;
-
-def disable_deterministic_archives
- : Flag<["--"], "disable-deterministic-archives">,
- HelpText<"Disable deterministic mode when copying archives (use real "
- "values for UIDs, GIDs, and timestamps).">;
-def U : Flag<["-"], "U">,
- Alias<disable_deterministic_archives>,
- HelpText<"Alias for --disable-deterministic-archives">;
-
-def preserve_dates : Flag<["--"], "preserve-dates">,
- HelpText<"Preserve access and modification timestamps">;
-def p : Flag<["-"], "p">, Alias<preserve_dates>;
-
defm add_gnu_debuglink
: Eq<"add-gnu-debuglink", "Add a .gnu_debuglink for <debug-file>">,
MetaVarName<"debug-file">;
-defm remove_section : Eq<"remove-section", "Remove <section>">,
- MetaVarName<"section">;
-def R : JoinedOrSeparate<["-"], "R">, Alias<remove_section>;
-
defm rename_section
: Eq<"rename-section",
"Renames a section from old to new, optionally with specified flags. "
@@ -93,16 +65,20 @@ defm redefine_symbols
"symbols from many files.">,
MetaVarName<"filename">;
-defm keep_section : Eq<"keep-section", "Keep <section>">,
- MetaVarName<"section">;
defm only_section : Eq<"only-section", "Remove all but <section>">,
MetaVarName<"section">;
-def j : JoinedOrSeparate<["-"], "j">, Alias<only_section>;
+def j : JoinedOrSeparate<["-"], "j">,
+ Alias<only_section>,
+ HelpText<"Alias for --only-section">;
defm add_section
: Eq<"add-section",
"Make a section named <section> with the contents of <file>.">,
MetaVarName<"section=file">;
+defm set_section_alignment
+ : Eq<"set-section-alignment", "Set alignment for a given section.">,
+ MetaVarName<"section=align">;
+
defm set_section_flags
: Eq<"set-section-flags",
"Set section flags for a given section. Flags supported for GNU "
@@ -110,26 +86,14 @@ defm set_section_flags
"rom, share, contents, merge, strings.">,
MetaVarName<"section=flag1[,flag2,...]">;
-def strip_all : Flag<["--"], "strip-all">,
- HelpText<"Remove non-allocated sections outside segments. "
- ".gnu.warning* sections are not removed">;
-def S : Flag<["-"], "S">, Alias<strip_all>;
-def strip_all_gnu : Flag<["--"], "strip-all-gnu">,
- HelpText<"Compatible with GNU objcopy's --strip-all">;
-def strip_debug : Flag<["--"], "strip-debug">,
- HelpText<"Remove all debug information">;
-def g : Flag<["-"], "g">, Alias<strip_debug>,
- HelpText<"Alias for --strip-debug">;
+def S : Flag<["-"], "S">,
+ Alias<strip_all>,
+ HelpText<"Alias for --strip-all">;
def strip_dwo : Flag<["--"], "strip-dwo">,
HelpText<"Remove all DWARF .dwo sections from file">;
-def strip_sections
- : Flag<["--"], "strip-sections">,
- HelpText<"Remove all section headers and all sections not in segments">;
def strip_non_alloc
: Flag<["--"], "strip-non-alloc">,
HelpText<"Remove all non-allocated sections outside segments">;
-def strip_unneeded : Flag<["--"], "strip-unneeded">,
- HelpText<"Remove all symbols not needed by relocations">;
defm strip_unneeded_symbol
: Eq<"strip-unneeded-symbol",
"Remove symbol <symbol> if it is not needed by relocations">,
@@ -163,7 +127,9 @@ defm localize_symbols
"Reads a list of symbols from <filename> and marks them local.">,
MetaVarName<"filename">;
-def L : JoinedOrSeparate<["-"], "L">, Alias<localize_symbol>;
+def L : JoinedOrSeparate<["-"], "L">,
+ Alias<localize_symbol>,
+ HelpText<"Alias for --localize-symbol">;
defm globalize_symbol : Eq<"globalize-symbol", "Mark <symbol> as global">,
MetaVarName<"symbol">;
@@ -178,7 +144,9 @@ defm keep_global_symbol
"Convert all symbols except <symbol> to local. May be repeated to "
"convert all except a set of symbols to local.">,
MetaVarName<"symbol">;
-def G : JoinedOrSeparate<["-"], "G">, Alias<keep_global_symbol>;
+def G : JoinedOrSeparate<["-"], "G">,
+ Alias<keep_global_symbol>,
+ HelpText<"Alias for --keep-global-symbol">;
defm keep_global_symbols
: Eq<"keep-global-symbols",
@@ -196,31 +164,17 @@ defm weaken_symbols
"Reads a list of symbols from <filename> and marks them weak.">,
MetaVarName<"filename">;
-def W : JoinedOrSeparate<["-"], "W">, Alias<weaken_symbol>;
+def W : JoinedOrSeparate<["-"], "W">,
+ Alias<weaken_symbol>,
+ HelpText<"Alias for --weaken-symbol">;
def weaken : Flag<["--"], "weaken">,
HelpText<"Mark all global symbols as weak">;
-def discard_locals : Flag<["--"], "discard-locals">,
- HelpText<"Remove compiler-generated local symbols, (e.g. "
- "symbols starting with .L)">;
-def X : Flag<["-"], "X">, Alias<discard_locals>;
-
-def discard_all
- : Flag<["--"], "discard-all">,
- HelpText<"Remove all local symbols except file and section symbols">;
-def x : Flag<["-"], "x">, Alias<discard_all>;
-defm strip_symbol : Eq<"strip-symbol", "Remove symbol <symbol>">,
- MetaVarName<"symbol">;
defm strip_symbols
: Eq<"strip-symbols",
"Reads a list of symbols from <filename> and removes them.">,
MetaVarName<"filename">;
-def N : JoinedOrSeparate<["-"], "N">, Alias<strip_symbol>;
-defm keep_symbol : Eq<"keep-symbol", "Do not remove symbol <symbol>">,
- MetaVarName<"symbol">;
-def K : JoinedOrSeparate<["-"], "K">, Alias<keep_symbol>;
-
defm keep_symbols
: Eq<"keep-symbols",
"Reads a list of symbols from <filename> and runs as if "
@@ -230,13 +184,6 @@ defm keep_symbols
"be repeated to read symbols from many files.">,
MetaVarName<"filename">;
-def only_keep_debug
- : Flag<["--"], "only-keep-debug">,
- HelpText<"Clear sections that would not be stripped by --strip-debug. "
- "Currently only implemented for COFF.">;
-
-def keep_file_symbols : Flag<["--"], "keep-file-symbols">,
- HelpText<"Do not remove file symbols">;
defm dump_section
: Eq<"dump-section",
"Dump contents of section named <section> into file <file>">,
@@ -249,9 +196,6 @@ defm prefix_alloc_sections
: Eq<"prefix-alloc-sections", "Add <prefix> to the start of every allocated section name">,
MetaVarName<"prefix">;
-def version : Flag<["--"], "version">,
- HelpText<"Print the version and exit.">;
-def V : Flag<["-"], "V">, Alias<version>;
defm build_id_link_dir
: Eq<"build-id-link-dir", "Set directory for --build-id-link-input and "
"--build-id-link-output to <dir>">,
@@ -265,10 +209,6 @@ defm build_id_link_output
"name derived from hex build ID">,
MetaVarName<"suffix">;
-def regex
- : Flag<["--"], "regex">,
- HelpText<"Permit regular expressions in name comparison">;
-
defm set_start : Eq<"set-start", "Set the start address to <addr>. Overrides "
"any previous --change-start or --adjust-start values.">,
MetaVarName<"addr">;
@@ -277,11 +217,12 @@ defm change_start : Eq<"change-start", "Add <incr> to the start address. Can be
"cumulatively.">,
MetaVarName<"incr">;
def adjust_start : JoinedOrSeparate<["--"], "adjust-start">,
- Alias<change_start>;
+ Alias<change_start>,
+ HelpText<"Alias for --change-start">;
defm add_symbol
: Eq<"add-symbol", "Add new symbol <name> to .symtab. Accepted flags: "
- "global, local, weak, default, hidden, file, section, object, "
+ "global, local, weak, default, hidden, protected, file, section, object, "
"function, indirect-function. Accepted but ignored for "
"compatibility: debug, constructor, warning, indirect, synthetic, "
"unique-object, before.">,
diff --git a/tools/llvm-objcopy/StripOpts.td b/tools/llvm-objcopy/StripOpts.td
index 1d06bb3dfb38..cd02cffae673 100644
--- a/tools/llvm-objcopy/StripOpts.td
+++ b/tools/llvm-objcopy/StripOpts.td
@@ -1,96 +1,17 @@
-include "llvm/Option/OptParser.td"
+include "CommonOpts.td"
-multiclass Eq<string name, string help> {
- def NAME : Separate<["--"], name>;
- def NAME #_eq : Joined<["--"], name #"=">,
- Alias<!cast<Separate>(NAME)>,
- HelpText<help>;
-}
+def output : JoinedOrSeparate<["-"], "o">, HelpText<"Write output to <file>">,
+ MetaVarName<"<file>">;
-def help : Flag<["--"], "help">;
-def h : Flag<["-"], "h">, Alias<help>;
-
-def allow_broken_links
- : Flag<["--"], "allow-broken-links">,
- HelpText<"Allow llvm-strip to remove sections even if it would leave "
- "invalid section references. The appropriate sh_link fields "
- "will be set to zero.">;
-
-def enable_deterministic_archives
- : Flag<["--"], "enable-deterministic-archives">,
- HelpText<"Enable deterministic mode when stripping archives (use zero "
- "for UIDs, GIDs, and timestamps).">;
-def D : Flag<["-"], "D">,
- Alias<enable_deterministic_archives>,
- HelpText<"Alias for --enable-deterministic-archives">;
-
-def disable_deterministic_archives
- : Flag<["--"], "disable-deterministic-archives">,
- HelpText<"Disable deterministic mode when stripping archives (use real "
- "values for UIDs, GIDs, and timestamps).">;
-def U : Flag<["-"], "U">,
- Alias<disable_deterministic_archives>,
- HelpText<"Alias for --disable-deterministic-archives">;
-
-def output : JoinedOrSeparate<["-"], "o">, HelpText<"Write output to <file>">;
-
-def preserve_dates : Flag<["--"], "preserve-dates">,
- HelpText<"Preserve access and modification timestamps">;
-def p : Flag<["-"], "p">, Alias<preserve_dates>;
-
-def strip_all : Flag<["--"], "strip-all">,
- HelpText<"Remove non-allocated sections outside segments. "
- ".gnu.warning* sections are not removed">;
-def s : Flag<["-"], "s">, Alias<strip_all>;
+def s : Flag<["-"], "s">,
+ Alias<strip_all>,
+ HelpText<"Alias for --strip-all">;
def no_strip_all : Flag<["--"], "no-strip-all">,
HelpText<"Disable --strip-all">;
-def strip_all_gnu : Flag<["--"], "strip-all-gnu">,
- HelpText<"Compatible with GNU strip's --strip-all">;
-def strip_debug : Flag<["--"], "strip-debug">,
- HelpText<"Remove debugging symbols only">;
-def d : Flag<["-"], "d">, Alias<strip_debug>;
-def g : Flag<["-"], "g">, Alias<strip_debug>;
-def S : Flag<["-"], "S">, Alias<strip_debug>;
-def strip_unneeded : Flag<["--"], "strip-unneeded">,
- HelpText<"Remove all symbols not needed by relocations">;
-
-defm remove_section : Eq<"remove-section", "Remove <section>">,
- MetaVarName<"section">;
-def R : JoinedOrSeparate<["-"], "R">, Alias<remove_section>;
-
-defm strip_symbol : Eq<"strip-symbol", "Strip <symbol>">,
- MetaVarName<"symbol">;
-def N : JoinedOrSeparate<["-"], "N">, Alias<strip_symbol>;
-
-defm keep_section : Eq<"keep-section", "Keep <section>">,
- MetaVarName<"section">;
-defm keep_symbol : Eq<"keep-symbol", "Do not remove symbol <symbol>">,
- MetaVarName<"symbol">;
-def keep_file_symbols : Flag<["--"], "keep-file-symbols">,
- HelpText<"Do not remove file symbols">;
-
-def K : JoinedOrSeparate<["-"], "K">, Alias<keep_symbol>;
-
-def only_keep_debug
- : Flag<["--"], "only-keep-debug">,
- HelpText<"Clear sections that would not be stripped by --strip-debug. "
- "Currently only implemented for COFF.">;
-
-def discard_locals : Flag<["--"], "discard-locals">,
- HelpText<"Remove compiler-generated local symbols, (e.g. "
- "symbols starting with .L)">;
-def X : Flag<["-"], "X">, Alias<discard_locals>;
-
-def discard_all
- : Flag<["--"], "discard-all">,
- HelpText<"Remove all local symbols except file and section symbols">;
-def x : Flag<["-"], "x">, Alias<discard_all>;
-
-def regex
- : Flag<["--"], "regex">,
- HelpText<"Permit regular expressions in name comparison">;
-
-def version : Flag<["--"], "version">,
- HelpText<"Print the version and exit.">;
-def V : Flag<["-"], "V">, Alias<version>;
+def d : Flag<["-"], "d">,
+ Alias<strip_debug>,
+ HelpText<"Alias for --strip-debug">;
+def S : Flag<["-"], "S">,
+ Alias<strip_debug>,
+ HelpText<"Alias for --strip-debug">;
diff --git a/tools/llvm-objcopy/llvm-objcopy.cpp b/tools/llvm-objcopy/llvm-objcopy.cpp
index e9372176e43b..a68210f3fdd3 100644
--- a/tools/llvm-objcopy/llvm-objcopy.cpp
+++ b/tools/llvm-objcopy/llvm-objcopy.cpp
@@ -29,6 +29,7 @@
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ErrorOr.h"
@@ -36,6 +37,7 @@
#include "llvm/Support/Memory.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
+#include "llvm/Support/StringSaver.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
@@ -84,7 +86,7 @@ LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Error E) {
ErrorSuccess reportWarning(Error E) {
assert(E);
- WithColor::warning(errs(), ToolName) << toString(std::move(E));
+ WithColor::warning(errs(), ToolName) << toString(std::move(E)) << '\n';
return Error::success();
}
@@ -130,16 +132,18 @@ static Error deepWriteArchive(StringRef ArcName,
/// The function executeObjcopyOnIHex does the dispatch based on the format
/// of the output specified by the command line options.
-static Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In,
+static Error executeObjcopyOnIHex(CopyConfig &Config, MemoryBuffer &In,
Buffer &Out) {
// TODO: support output formats other than ELF.
+ if (Error E = Config.parseELFConfig())
+ return E;
return elf::executeObjcopyOnIHex(Config, In, Out);
}
/// The function executeObjcopyOnRawBinary does the dispatch based on the format
/// of the output specified by the command line options.
-static Error executeObjcopyOnRawBinary(const CopyConfig &Config,
- MemoryBuffer &In, Buffer &Out) {
+static Error executeObjcopyOnRawBinary(CopyConfig &Config, MemoryBuffer &In,
+ Buffer &Out) {
switch (Config.OutputFormat) {
case FileFormat::ELF:
// FIXME: Currently, we call elf::executeObjcopyOnRawBinary even if the
@@ -148,6 +152,8 @@ static Error executeObjcopyOnRawBinary(const CopyConfig &Config,
case FileFormat::Binary:
case FileFormat::IHex:
case FileFormat::Unspecified:
+ if (Error E = Config.parseELFConfig())
+ return E;
return elf::executeObjcopyOnRawBinary(Config, In, Out);
}
@@ -156,11 +162,13 @@ static Error executeObjcopyOnRawBinary(const CopyConfig &Config,
/// The function executeObjcopyOnBinary does the dispatch based on the format
/// of the input binary (ELF, MachO or COFF).
-static Error executeObjcopyOnBinary(const CopyConfig &Config,
- object::Binary &In, Buffer &Out) {
- if (auto *ELFBinary = dyn_cast<object::ELFObjectFileBase>(&In))
+static Error executeObjcopyOnBinary(CopyConfig &Config, object::Binary &In,
+ Buffer &Out) {
+ if (auto *ELFBinary = dyn_cast<object::ELFObjectFileBase>(&In)) {
+ if (Error E = Config.parseELFConfig())
+ return E;
return elf::executeObjcopyOnBinary(Config, *ELFBinary, Out);
- else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In))
+ } else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In))
return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out);
else if (auto *MachOBinary = dyn_cast<object::MachOObjectFile>(&In))
return macho::executeObjcopyOnBinary(Config, *MachOBinary, Out);
@@ -169,8 +177,7 @@ static Error executeObjcopyOnBinary(const CopyConfig &Config,
"unsupported object file format");
}
-static Error executeObjcopyOnArchive(const CopyConfig &Config,
- const Archive &Ar) {
+static Error executeObjcopyOnArchive(CopyConfig &Config, const Archive &Ar) {
std::vector<NewArchiveMember> NewArchiveMembers;
Error Err = Error::success();
for (const Archive::Child &Child : Ar.children(Err)) {
@@ -246,7 +253,7 @@ static Error restoreStatOnFile(StringRef Filename,
/// The function executeObjcopy does the higher level dispatch based on the type
/// of input (raw binary, archive or single object file) and takes care of the
/// format-agnostic modifications, i.e. preserving dates.
-static Error executeObjcopy(const CopyConfig &Config) {
+static Error executeObjcopy(CopyConfig &Config) {
sys::fs::file_status Stat;
if (Config.InputFilename != "-") {
if (auto EC = sys::fs::status(Config.InputFilename, Stat))
@@ -255,7 +262,7 @@ static Error executeObjcopy(const CopyConfig &Config) {
Stat.permissions(static_cast<sys::fs::perms>(0777));
}
- typedef Error (*ProcessRawFn)(const CopyConfig &, MemoryBuffer &, Buffer &);
+ using ProcessRawFn = Error (*)(CopyConfig &, MemoryBuffer &, Buffer &);
ProcessRawFn ProcessRaw;
switch (Config.InputFormat) {
case FileFormat::Binary:
@@ -310,15 +317,31 @@ int main(int argc, char **argv) {
InitLLVM X(argc, argv);
ToolName = argv[0];
bool IsStrip = sys::path::stem(ToolName).contains("strip");
+
+ // Expand response files.
+ // TODO: Move these lines, which are copied from lib/Support/CommandLine.cpp,
+ // into a separate function in the CommandLine library and call that function
+ // here. This is duplicated code.
+ SmallVector<const char *, 20> NewArgv(argv, argv + argc);
+ BumpPtrAllocator A;
+ StringSaver Saver(A);
+ cl::ExpandResponseFiles(Saver,
+ Triple(sys::getProcessTriple()).isOSWindows()
+ ? cl::TokenizeWindowsCommandLine
+ : cl::TokenizeGNUCommandLine,
+ NewArgv);
+
+ auto Args = makeArrayRef(NewArgv).drop_front();
+
Expected<DriverConfig> DriverConfig =
- IsStrip ? parseStripOptions(makeArrayRef(argv + 1, argc), reportWarning)
- : parseObjcopyOptions(makeArrayRef(argv + 1, argc));
+ IsStrip ? parseStripOptions(Args, reportWarning)
+ : parseObjcopyOptions(Args, reportWarning);
if (!DriverConfig) {
logAllUnhandledErrors(DriverConfig.takeError(),
WithColor::error(errs(), ToolName));
return 1;
}
- for (const CopyConfig &CopyConfig : DriverConfig->CopyConfigs) {
+ for (CopyConfig &CopyConfig : DriverConfig->CopyConfigs) {
if (Error E = executeObjcopy(CopyConfig)) {
logAllUnhandledErrors(std::move(E), WithColor::error(errs(), ToolName));
return 1;