diff options
Diffstat (limited to 'llvm/tools/llvm-objcopy')
-rw-r--r-- | llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp | 13 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/COFF/Object.h | 6 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/COFF/Reader.cpp | 4 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/COFF/Writer.cpp | 22 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/CommonOpts.td | 8 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/CopyConfig.cpp | 89 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/CopyConfig.h | 12 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp | 29 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/ELF/Object.cpp | 207 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/ELF/Object.h | 84 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/InstallNameToolOpts.td | 22 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp | 11 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp | 200 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/MachO/MachOReader.cpp | 40 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp | 19 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/MachO/Object.cpp | 64 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/MachO/Object.h | 82 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/llvm-objcopy.cpp | 20 |
18 files changed, 748 insertions, 184 deletions
diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp index 2a8d816e6f3c..b172fae527eb 100644 --- a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp @@ -131,6 +131,12 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) { if (Error E = Obj.markSymbols()) return E; + for (Symbol &Sym : Obj.getMutableSymbols()) { + auto I = Config.SymbolsToRename.find(Sym.Name); + if (I != Config.SymbolsToRename.end()) + Sym.Name = I->getValue(); + } + // Actually do removals of symbols. Obj.removeSymbols([&](const Symbol &Sym) { // For StripAll, all relocations have been stripped and we remove all @@ -200,10 +206,9 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) { !Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() || !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.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/llvm/tools/llvm-objcopy/COFF/Object.h b/llvm/tools/llvm-objcopy/COFF/Object.h index 21475b068629..78f8da00b8cd 100644 --- a/llvm/tools/llvm-objcopy/COFF/Object.h +++ b/llvm/tools/llvm-objcopy/COFF/Object.h @@ -25,11 +25,11 @@ namespace objcopy { namespace coff { struct Relocation { - Relocation() {} + Relocation() = default; Relocation(const object::coff_relocation& R) : Reloc(R) {} object::coff_relocation Reloc; - size_t Target; + size_t Target = 0; StringRef TargetName; // Used for diagnostics only }; @@ -124,7 +124,7 @@ struct Object { ArrayRef<Section> getSections() const { return Sections; } // This allows mutating individual Sections, but not mutating the list - // of symbols itself. + // of sections itself. iterator_range<std::vector<Section>::iterator> getMutableSections() { return make_range(Sections.begin(), Sections.end()); } diff --git a/llvm/tools/llvm-objcopy/COFF/Reader.cpp b/llvm/tools/llvm-objcopy/COFF/Reader.cpp index 2fcec0057c03..7be9cce2be3d 100644 --- a/llvm/tools/llvm-objcopy/COFF/Reader.cpp +++ b/llvm/tools/llvm-objcopy/COFF/Reader.cpp @@ -63,6 +63,7 @@ Error COFFReader::readSections(Object &Obj) const { Sections.push_back(Section()); Section &S = Sections.back(); S.Header = *Sec; + S.Header.Characteristics &= ~COFF::IMAGE_SCN_LNK_NRELOC_OVFL; ArrayRef<uint8_t> Contents; if (Error E = COFFObj.getSectionContents(Sec, Contents)) return E; @@ -74,9 +75,6 @@ Error COFFReader::readSections(Object &Obj) const { S.Name = *NameOrErr; else return NameOrErr.takeError(); - if (Sec->hasExtendedRelocations()) - return createStringError(object_error::parse_failed, - "extended relocations not supported yet"); } Obj.addSections(Sections); return Error::success(); diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.cpp b/llvm/tools/llvm-objcopy/COFF/Writer.cpp index 6db37435fd96..e35e0474a36d 100644 --- a/llvm/tools/llvm-objcopy/COFF/Writer.cpp +++ b/llvm/tools/llvm-objcopy/COFF/Writer.cpp @@ -97,9 +97,16 @@ void COFFWriter::layoutSections() { S.Header.PointerToRawData = FileSize; FileSize += S.Header.SizeOfRawData; // For executables, this is already // aligned to FileAlignment. - S.Header.NumberOfRelocations = S.Relocs.size(); - S.Header.PointerToRelocations = - S.Header.NumberOfRelocations > 0 ? FileSize : 0; + if (S.Relocs.size() >= 0xffff) { + S.Header.Characteristics |= COFF::IMAGE_SCN_LNK_NRELOC_OVFL; + S.Header.NumberOfRelocations = 0xffff; + S.Header.PointerToRelocations = FileSize; + FileSize += sizeof(coff_relocation); + } else { + S.Header.NumberOfRelocations = S.Relocs.size(); + S.Header.PointerToRelocations = S.Relocs.size() ? FileSize : 0; + } + FileSize += S.Relocs.size() * sizeof(coff_relocation); FileSize = alignTo(FileSize, FileAlignment); @@ -307,6 +314,15 @@ void COFFWriter::writeSections() { S.Header.SizeOfRawData - Contents.size()); Ptr += S.Header.SizeOfRawData; + + if (S.Relocs.size() >= 0xffff) { + object::coff_relocation R; + R.VirtualAddress = S.Relocs.size() + 1; + R.SymbolTableIndex = 0; + R.Type = 0; + memcpy(Ptr, &R, sizeof(R)); + Ptr += sizeof(R); + } for (const auto &R : S.Relocs) { memcpy(Ptr, &R.Reloc, sizeof(R.Reloc)); Ptr += sizeof(R.Reloc); diff --git a/llvm/tools/llvm-objcopy/CommonOpts.td b/llvm/tools/llvm-objcopy/CommonOpts.td index e8c092b44431..6481d1d1df05 100644 --- a/llvm/tools/llvm-objcopy/CommonOpts.td +++ b/llvm/tools/llvm-objcopy/CommonOpts.td @@ -40,7 +40,8 @@ def p : Flag<["-"], "p">, def strip_all : Flag<["--"], "strip-all">, HelpText<"Remove non-allocated sections outside segments. " - ".gnu.warning* sections are not removed">; + ".gnu.warning* and .ARM.attribute sections are not " + "removed">; def strip_all_gnu : Flag<["--"], "strip-all-gnu">, @@ -85,8 +86,9 @@ def keep_file_symbols : Flag<["--"], "keep-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.">; + HelpText< + "Produce a debug file as the output that only preserves contents of " + "sections useful for debugging purposes">; def discard_locals : Flag<["--"], "discard-locals">, HelpText<"Remove compiler-generated local symbols, (e.g. " diff --git a/llvm/tools/llvm-objcopy/CopyConfig.cpp b/llvm/tools/llvm-objcopy/CopyConfig.cpp index d707bec20c49..73ed00b5cb2a 100644 --- a/llvm/tools/llvm-objcopy/CopyConfig.cpp +++ b/llvm/tools/llvm-objcopy/CopyConfig.cpp @@ -63,6 +63,44 @@ public: ObjcopyOptTable() : OptTable(ObjcopyInfoTable) {} }; +enum InstallNameToolID { + INSTALL_NAME_TOOL_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + INSTALL_NAME_TOOL_##ID, +#include "InstallNameToolOpts.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) \ + const char *const INSTALL_NAME_TOOL_##NAME[] = VALUE; +#include "InstallNameToolOpts.inc" +#undef PREFIX + +static const opt::OptTable::Info InstallNameToolInfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + {INSTALL_NAME_TOOL_##PREFIX, \ + NAME, \ + HELPTEXT, \ + METAVAR, \ + INSTALL_NAME_TOOL_##ID, \ + opt::Option::KIND##Class, \ + PARAM, \ + FLAGS, \ + INSTALL_NAME_TOOL_##GROUP, \ + INSTALL_NAME_TOOL_##ALIAS, \ + ALIASARGS, \ + VALUES}, +#include "InstallNameToolOpts.inc" +#undef OPTION +}; + +class InstallNameToolOptTable : public opt::OptTable { +public: + InstallNameToolOptTable() : OptTable(InstallNameToolInfoTable) {} +}; + enum StripID { STRIP_INVALID = 0, // This is not an option ID. #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ @@ -752,6 +790,57 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr, return std::move(DC); } +// ParseInstallNameToolOptions returns the config and sets the input arguments. +// If a help flag is set then ParseInstallNameToolOptions will print the help +// messege and exit. +Expected<DriverConfig> +parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr) { + DriverConfig DC; + CopyConfig Config; + InstallNameToolOptTable T; + unsigned MissingArgumentIndex, MissingArgumentCount; + llvm::opt::InputArgList InputArgs = + T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount); + + if (InputArgs.size() == 0) { + printHelp(T, errs(), "llvm-install-name-tool"); + exit(1); + } + + if (InputArgs.hasArg(INSTALL_NAME_TOOL_help)) { + printHelp(T, outs(), "llvm-install-name-tool"); + exit(0); + } + + if (InputArgs.hasArg(INSTALL_NAME_TOOL_version)) { + outs() << "llvm-install-name-tool, compatible with cctools " + "install_name_tool\n"; + cl::PrintVersionMessage(); + exit(0); + } + + for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_add_rpath)) + Config.RPathToAdd.push_back(Arg->getValue()); + + SmallVector<StringRef, 2> Positional; + for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_UNKNOWN)) + return createStringError(errc::invalid_argument, "unknown argument '%s'", + Arg->getAsString(InputArgs).c_str()); + for (auto Arg : InputArgs.filtered(INSTALL_NAME_TOOL_INPUT)) + Positional.push_back(Arg->getValue()); + if (Positional.empty()) + return createStringError(errc::invalid_argument, "no input file specified"); + if (Positional.size() > 1) + return createStringError( + errc::invalid_argument, + "llvm-install-name-tool expects a single input file"); + Config.InputFilename = Positional[0]; + Config.OutputFilename = Positional[0]; + + DC.CopyConfigs.push_back(std::move(Config)); + return std::move(DC); +} + // ParseStripOptions returns the config and sets the input arguments. If a // help flag is set then ParseStripOptions will print the help messege and // exit. diff --git a/llvm/tools/llvm-objcopy/CopyConfig.h b/llvm/tools/llvm-objcopy/CopyConfig.h index 55a55d3a2bc2..c262934b4a41 100644 --- a/llvm/tools/llvm-objcopy/CopyConfig.h +++ b/llvm/tools/llvm-objcopy/CopyConfig.h @@ -150,9 +150,9 @@ struct CopyConfig { // Main input/output options StringRef InputFilename; - FileFormat InputFormat; + FileFormat InputFormat = FileFormat::Unspecified; StringRef OutputFilename; - FileFormat OutputFormat; + FileFormat OutputFormat = FileFormat::Unspecified; // Only applicable when --output-format!=binary (e.g. elf64-x86-64). Optional<MachineInfo> OutputArch; @@ -175,6 +175,7 @@ struct CopyConfig { std::vector<StringRef> AddSection; std::vector<StringRef> DumpSection; std::vector<StringRef> SymbolsToAdd; + std::vector<StringRef> RPathToAdd; // Section matchers NameMatcher KeepSection; @@ -251,6 +252,12 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr, llvm::function_ref<Error(Error)> ErrorCallback); +// ParseInstallNameToolOptions returns the config and sets the input arguments. +// If a help flag is set then ParseInstallNameToolOptions will print the help +// messege and exit. +Expected<DriverConfig> +parseInstallNameToolOptions(ArrayRef<const char *> ArgsArr); + // ParseStripOptions returns the config and sets the input arguments. If a // help flag is set then ParseStripOptions will print the help messege and // exit. ErrorCallback is used to handle recoverable errors. An Error returned @@ -258,7 +265,6 @@ parseObjcopyOptions(ArrayRef<const char *> ArgsArr, Expected<DriverConfig> parseStripOptions(ArrayRef<const char *> ArgsArr, llvm::function_ref<Error(Error)> ErrorCallback); - } // namespace objcopy } // namespace llvm diff --git a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp index 8bf7e0f88010..a0cfd9a5ff86 100644 --- a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -136,17 +136,17 @@ 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 std::make_unique<ELFWriter<ELF32LE>>(Obj, Buf, - !Config.StripSections); + return std::make_unique<ELFWriter<ELF32LE>>(Obj, Buf, !Config.StripSections, + Config.OnlyKeepDebug); case ELFT_ELF64LE: - return std::make_unique<ELFWriter<ELF64LE>>(Obj, Buf, - !Config.StripSections); + return std::make_unique<ELFWriter<ELF64LE>>(Obj, Buf, !Config.StripSections, + Config.OnlyKeepDebug); case ELFT_ELF32BE: - return std::make_unique<ELFWriter<ELF32BE>>(Obj, Buf, - !Config.StripSections); + return std::make_unique<ELFWriter<ELF32BE>>(Obj, Buf, !Config.StripSections, + Config.OnlyKeepDebug); case ELFT_ELF64BE: - return std::make_unique<ELFWriter<ELF64BE>>(Obj, Buf, - !Config.StripSections); + return std::make_unique<ELFWriter<ELF64BE>>(Obj, Buf, !Config.StripSections, + Config.OnlyKeepDebug); } llvm_unreachable("Invalid output format"); } @@ -175,7 +175,7 @@ findBuildID(const CopyConfig &Config, const object::ELFFile<ELFT> &In) { if (Phdr.p_type != PT_NOTE) continue; Error Err = Error::success(); - for (const auto &Note : In.notes(Phdr, Err)) + for (auto Note : In.notes(Phdr, Err)) if (Note.getType() == NT_GNU_BUILD_ID && Note.getName() == ELF_NOTE_GNU) return Note.getDesc(); if (Err) @@ -503,6 +503,12 @@ static Error replaceAndRemoveSections(const CopyConfig &Config, Object &Obj) { return false; if (StringRef(Sec.Name).startswith(".gnu.warning")) return false; + // We keep the .ARM.attribute section to maintain compatibility + // with Debian derived distributions. This is a bug in their + // patchset as documented here: + // https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=943798 + if (Sec.Type == SHT_ARM_ATTRIBUTES) + return false; if (Sec.ParentSegment != nullptr) return false; return (Sec.Flags & SHF_ALLOC) == 0; @@ -688,6 +694,11 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj, } } + if (Config.OnlyKeepDebug) + for (auto &Sec : Obj.sections()) + if (Sec.Flags & SHF_ALLOC && Sec.Type != SHT_NOTE) + Sec.Type = SHT_NOBITS; + for (const auto &Flag : Config.AddSection) { std::pair<StringRef, StringRef> SecPair = Flag.split("="); StringRef SecName = SecPair.first; diff --git a/llvm/tools/llvm-objcopy/ELF/Object.cpp b/llvm/tools/llvm-objcopy/ELF/Object.cpp index 74145dad6e6b..ad53c75663ec 100644 --- a/llvm/tools/llvm-objcopy/ELF/Object.cpp +++ b/llvm/tools/llvm-objcopy/ELF/Object.cpp @@ -815,7 +815,8 @@ Error RelocationSection::removeSectionReferences( } for (const Relocation &R : Relocations) { - if (!R.RelocSymbol->DefinedIn || !ToRemove(R.RelocSymbol->DefinedIn)) + if (!R.RelocSymbol || !R.RelocSymbol->DefinedIn || + !ToRemove(R.RelocSymbol->DefinedIn)) continue; return createStringError(llvm::errc::invalid_argument, "section '%s' cannot be removed: (%s+0x%" PRIx64 @@ -868,7 +869,8 @@ static void writeRel(const RelRange &Relocations, T *Buf) { for (const auto &Reloc : Relocations) { Buf->r_offset = Reloc.Offset; setAddend(*Buf, Reloc.Addend); - Buf->setSymbolAndType(Reloc.RelocSymbol->Index, Reloc.Type, false); + Buf->setSymbolAndType(Reloc.RelocSymbol ? Reloc.RelocSymbol->Index : 0, + Reloc.Type, false); ++Buf; } } @@ -893,7 +895,7 @@ void RelocationSection::accept(MutableSectionVisitor &Visitor) { Error RelocationSection::removeSymbols( function_ref<bool(const Symbol &)> ToRemove) { for (const Relocation &Reloc : Relocations) - if (ToRemove(*Reloc.RelocSymbol)) + if (Reloc.RelocSymbol && ToRemove(*Reloc.RelocSymbol)) return createStringError( llvm::errc::invalid_argument, "not stripping symbol '%s' because it is named in a relocation", @@ -903,7 +905,8 @@ Error RelocationSection::removeSymbols( void RelocationSection::markSymbols() { for (const Relocation &Reloc : Relocations) - Reloc.RelocSymbol->Referenced = true; + if (Reloc.RelocSymbol) + Reloc.RelocSymbol->Referenced = true; } void RelocationSection::replaceSectionReferences( @@ -1006,7 +1009,7 @@ void GnuDebugLinkSection::init(StringRef File) { Size = alignTo(FileName.size() + 1, 4) + 4; // The CRC32 will only be aligned if we align the whole section. Align = 4; - Type = ELF::SHT_PROGBITS; + Type = OriginalType = ELF::SHT_PROGBITS; Name = ".gnu_debuglink"; // For sections not found in segments, OriginalOffset is only used to // establish the order that sections should go in. By using the maximum @@ -1418,7 +1421,15 @@ static void initRelocations(RelocationSection *Relocs, ToAdd.Offset = Rel.r_offset; getAddend(ToAdd.Addend, Rel); ToAdd.Type = Rel.getType(false); - ToAdd.RelocSymbol = SymbolTable->getSymbolByIndex(Rel.getSymbol(false)); + + if (uint32_t Sym = Rel.getSymbol(false)) { + if (!SymbolTable) + error("'" + Relocs->Name + + "': relocation references symbol with index " + Twine(Sym) + + ", but there is no symbol table"); + ToAdd.RelocSymbol = SymbolTable->getSymbolByIndex(Sym); + } + Relocs->addRelocation(ToAdd); } } @@ -1510,8 +1521,8 @@ template <class ELFT> void ELFBuilder<ELFT>::readSectionHeaders() { } auto &Sec = makeSection(Shdr); Sec.Name = unwrapOrError(ElfFile.getSectionName(&Shdr)); - Sec.Type = Shdr.sh_type; - Sec.Flags = Shdr.sh_flags; + Sec.Type = Sec.OriginalType = Shdr.sh_type; + Sec.Flags = Sec.OriginalFlags = Shdr.sh_flags; Sec.Addr = Shdr.sh_addr; Sec.Offset = Shdr.sh_offset; Sec.OriginalOffset = Shdr.sh_offset; @@ -1528,6 +1539,21 @@ template <class ELFT> void ELFBuilder<ELFT>::readSectionHeaders() { } template <class ELFT> void ELFBuilder<ELFT>::readSections(bool EnsureSymtab) { + uint32_t ShstrIndex = ElfFile.getHeader()->e_shstrndx; + if (ShstrIndex == SHN_XINDEX) + ShstrIndex = unwrapOrError(ElfFile.getSection(0))->sh_link; + + if (ShstrIndex == SHN_UNDEF) + Obj.HadShdrs = false; + else + Obj.SectionNames = + Obj.sections().template getSectionOfType<StringTableSection>( + ShstrIndex, + "e_shstrndx field value " + Twine(ShstrIndex) + " in elf header " + + " is invalid", + "e_shstrndx field value " + Twine(ShstrIndex) + " in elf header " + + " does not reference a string table"); + // 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. @@ -1541,13 +1567,14 @@ template <class ELFT> void ELFBuilder<ELFT>::readSections(bool EnsureSymtab) { Obj.SymbolTable->initialize(Obj.sections()); initSymbolTable(Obj.SymbolTable); } else if (EnsureSymtab) { - // Reuse the existing SHT_STRTAB section if exists. + // Reuse an existing SHT_STRTAB section if it 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. + // Prefer a string table that is not the section header string table, if + // such a table exists. if (Obj.SectionNames != &Sec) break; } @@ -1582,21 +1609,6 @@ template <class ELFT> void ELFBuilder<ELFT>::readSections(bool EnsureSymtab) { initGroupSection(GroupSec); } } - - uint32_t ShstrIndex = ElfFile.getHeader()->e_shstrndx; - if (ShstrIndex == SHN_XINDEX) - ShstrIndex = unwrapOrError(ElfFile.getSection(0))->sh_link; - - if (ShstrIndex == SHN_UNDEF) - Obj.HadShdrs = false; - else - Obj.SectionNames = - Obj.sections().template getSectionOfType<StringTableSection>( - ShstrIndex, - "e_shstrndx field value " + Twine(ShstrIndex) + " in elf header " + - " is invalid", - "e_shstrndx field value " + Twine(ShstrIndex) + " in elf header " + - " is not a string table"); } template <class ELFT> void ELFBuilder<ELFT>::build(bool EnsureSymtab) { @@ -1785,10 +1797,9 @@ template <class ELFT> void ELFWriter<ELFT>::writeSectionData() { template <class ELFT> void ELFWriter<ELFT>::writeSegmentData() { for (Segment &Seg : Obj.segments()) { - uint8_t *B = Buf.getBufferStart() + Seg.Offset; - assert(Seg.FileSize == Seg.getContents().size() && - "Segment size must match contents size"); - std::memcpy(B, Seg.getContents().data(), Seg.FileSize); + size_t Size = std::min<size_t>(Seg.FileSize, Seg.getContents().size()); + std::memcpy(Buf.getBufferStart() + Seg.Offset, Seg.getContents().data(), + Size); } // Iterate over removed sections and overwrite their old data with zeroes. @@ -1803,8 +1814,10 @@ template <class ELFT> void ELFWriter<ELFT>::writeSegmentData() { } template <class ELFT> -ELFWriter<ELFT>::ELFWriter(Object &Obj, Buffer &Buf, bool WSH) - : Writer(Obj, Buf), WriteSectionHeaders(WSH && Obj.HadShdrs) {} +ELFWriter<ELFT>::ELFWriter(Object &Obj, Buffer &Buf, bool WSH, + bool OnlyKeepDebug) + : Writer(Obj, Buf), WriteSectionHeaders(WSH && Obj.HadShdrs), + OnlyKeepDebug(OnlyKeepDebug) {} Error Object::removeSections(bool AllowBrokenLinks, std::function<bool(const SectionBase &)> ToRemove) { @@ -1945,6 +1958,78 @@ static uint64_t layoutSections(Range Sections, uint64_t Offset) { return Offset; } +// Rewrite sh_offset after some sections are changed to SHT_NOBITS and thus +// occupy no space in the file. +static uint64_t layoutSectionsForOnlyKeepDebug(Object &Obj, uint64_t Off) { + uint32_t Index = 1; + for (auto &Sec : Obj.sections()) { + Sec.Index = Index++; + + auto *FirstSec = Sec.ParentSegment && Sec.ParentSegment->Type == PT_LOAD + ? Sec.ParentSegment->firstSection() + : nullptr; + + // The first section in a PT_LOAD has to have congruent offset and address + // modulo the alignment, which usually equals the maximum page size. + if (FirstSec && FirstSec == &Sec) + Off = alignTo(Off, Sec.ParentSegment->Align, Sec.Addr); + + // sh_offset is not significant for SHT_NOBITS sections, but the congruence + // rule must be followed if it is the first section in a PT_LOAD. Do not + // advance Off. + if (Sec.Type == SHT_NOBITS) { + Sec.Offset = Off; + continue; + } + + if (!FirstSec) { + // FirstSec being nullptr generally means that Sec does not have the + // SHF_ALLOC flag. + Off = Sec.Align ? alignTo(Off, Sec.Align) : Off; + } else if (FirstSec != &Sec) { + // The offset is relative to the first section in the PT_LOAD segment. Use + // sh_offset for non-SHF_ALLOC sections. + Off = Sec.OriginalOffset - FirstSec->OriginalOffset + FirstSec->Offset; + } + Sec.Offset = Off; + Off += Sec.Size; + } + return Off; +} + +// Rewrite p_offset and p_filesz of non-empty non-PT_PHDR segments after +// sh_offset values have been updated. +static uint64_t layoutSegmentsForOnlyKeepDebug(std::vector<Segment *> &Segments, + uint64_t HdrEnd) { + uint64_t MaxOffset = 0; + for (Segment *Seg : Segments) { + const SectionBase *FirstSec = Seg->firstSection(); + if (Seg->Type == PT_PHDR || !FirstSec) + continue; + + uint64_t Offset = FirstSec->Offset; + uint64_t FileSize = 0; + for (const SectionBase *Sec : Seg->Sections) { + uint64_t Size = Sec->Type == SHT_NOBITS ? 0 : Sec->Size; + if (Sec->Offset + Size > Offset) + FileSize = std::max(FileSize, Sec->Offset + Size - Offset); + } + + // If the segment includes EHDR and program headers, don't make it smaller + // than the headers. + if (Seg->Offset < HdrEnd && HdrEnd <= Seg->Offset + Seg->FileSize) { + FileSize += Offset - Seg->Offset; + Offset = Seg->Offset; + FileSize = std::max(FileSize, HdrEnd - Offset); + } + + Seg->Offset = Offset; + Seg->FileSize = FileSize; + MaxOffset = std::max(MaxOffset, Offset + FileSize); + } + return MaxOffset; +} + template <class ELFT> void ELFWriter<ELFT>::initEhdrSegment() { Segment &ElfHdr = Obj.ElfHdrSegment; ElfHdr.Type = PT_PHDR; @@ -1965,12 +2050,24 @@ template <class ELFT> void ELFWriter<ELFT>::assignOffsets() { OrderedSegments.push_back(&Obj.ElfHdrSegment); OrderedSegments.push_back(&Obj.ProgramHdrSegment); orderSegments(OrderedSegments); - // Offset is used as the start offset of the first segment to be laid out. - // Since the ELF Header (ElfHdrSegment) must be at the start of the file, - // we start at offset 0. - uint64_t Offset = 0; - Offset = layoutSegments(OrderedSegments, Offset); - Offset = layoutSections(Obj.sections(), Offset); + + uint64_t Offset; + if (OnlyKeepDebug) { + // For --only-keep-debug, the sections that did not preserve contents were + // changed to SHT_NOBITS. We now rewrite sh_offset fields of sections, and + // then rewrite p_offset/p_filesz of program headers. + uint64_t HdrEnd = + sizeof(Elf_Ehdr) + llvm::size(Obj.segments()) * sizeof(Elf_Phdr); + Offset = layoutSectionsForOnlyKeepDebug(Obj, HdrEnd); + Offset = std::max(Offset, + layoutSegmentsForOnlyKeepDebug(OrderedSegments, HdrEnd)); + } else { + // Offset is used as the start offset of the first segment to be laid out. + // Since the ELF Header (ElfHdrSegment) must be at the start of the file, + // we start at offset 0. + Offset = layoutSegments(OrderedSegments, 0); + Offset = layoutSections(Obj.sections(), Offset); + } // If we need to write the section header table out then we need to align the // Offset so that SHOffset is valid. if (WriteSectionHeaders) @@ -2156,38 +2253,28 @@ Error BinaryWriter::finalize() { std::unique(std::begin(OrderedSegments), std::end(OrderedSegments)); OrderedSegments.erase(End, std::end(OrderedSegments)); - uint64_t Offset = 0; - - // Modify the first segment so that there is no gap at the start. This allows - // our layout algorithm to proceed as expected while not writing out the gap - // at the start. - if (!OrderedSegments.empty()) { - Segment *Seg = OrderedSegments[0]; - const SectionBase *Sec = Seg->firstSection(); - auto Diff = Sec->OriginalOffset - Seg->OriginalOffset; - Seg->OriginalOffset += Diff; - // The size needs to be shrunk as well. - Seg->FileSize -= Diff; - // The PAddr needs to be increased to remove the gap before the first - // section. - Seg->PAddr += Diff; - uint64_t LowestPAddr = Seg->PAddr; - for (Segment *Segment : OrderedSegments) { - Segment->Offset = Segment->PAddr - LowestPAddr; - Offset = std::max(Offset, Segment->Offset + Segment->FileSize); - } + // Compute the section LMA based on its sh_offset and the containing segment's + // p_offset and p_paddr. Also compute the minimum LMA of all sections as + // MinAddr. In the output, the contents between address 0 and MinAddr will be + // skipped. + uint64_t MinAddr = UINT64_MAX; + for (SectionBase &Sec : Obj.allocSections()) { + if (Sec.ParentSegment != nullptr) + Sec.Addr = + Sec.Offset - Sec.ParentSegment->Offset + Sec.ParentSegment->PAddr; + MinAddr = std::min(MinAddr, Sec.Addr); } - 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 // its last section, to match GNU objcopy's behaviour. TotalSize = 0; - for (const SectionBase &Sec : Obj.allocSections()) + for (SectionBase &Sec : Obj.allocSections()) { + Sec.Offset = Sec.Addr - MinAddr; if (Sec.Type != SHT_NOBITS) TotalSize = std::max(TotalSize, Sec.Offset + Sec.Size); + } if (Error E = Buf.allocate(TotalSize)) return E; diff --git a/llvm/tools/llvm-objcopy/ELF/Object.h b/llvm/tools/llvm-objcopy/ELF/Object.h index eeacb014e4dc..97702a66bc47 100644 --- a/llvm/tools/llvm-objcopy/ELF/Object.h +++ b/llvm/tools/llvm-objcopy/ELF/Object.h @@ -342,16 +342,20 @@ public: virtual ~ELFWriter() {} bool WriteSectionHeaders; + // For --only-keep-debug, select an alternative section/segment layout + // algorithm. + bool OnlyKeepDebug; + Error finalize() override; Error write() override; - ELFWriter(Object &Obj, Buffer &Buf, bool WSH); + ELFWriter(Object &Obj, Buffer &Buf, bool WSH, bool OnlyKeepDebug); }; class BinaryWriter : public Writer { private: std::unique_ptr<BinarySectionWriter> SecWriter; - uint64_t TotalSize; + uint64_t TotalSize = 0; public: ~BinaryWriter() {} @@ -366,7 +370,7 @@ class IHexWriter : public Writer { }; std::set<const SectionBase *, SectionCompare> Sections; - size_t TotalSize; + size_t TotalSize = 0; Error checkSection(const SectionBase &Sec); uint64_t writeEntryPointRecord(uint8_t *Buf); @@ -383,11 +387,14 @@ class SectionBase { public: std::string Name; Segment *ParentSegment = nullptr; - uint64_t HeaderOffset; - uint64_t OriginalOffset = std::numeric_limits<uint64_t>::max(); - uint32_t Index; + uint64_t HeaderOffset = 0; + uint32_t Index = 0; bool HasSymbol = false; + uint64_t OriginalFlags = 0; + uint64_t OriginalType = ELF::SHT_NULL; + uint64_t OriginalOffset = std::numeric_limits<uint64_t>::max(); + uint64_t Addr = 0; uint64_t Align = 1; uint32_t EntrySize = 0; @@ -432,25 +439,24 @@ private: } }; - std::set<const SectionBase *, SectionCompare> Sections; - public: - uint32_t Type; - uint32_t Flags; - uint64_t Offset; - uint64_t VAddr; - uint64_t PAddr; - uint64_t FileSize; - uint64_t MemSize; - uint64_t Align; - - uint32_t Index; - uint64_t OriginalOffset; + uint32_t Type = 0; + uint32_t Flags = 0; + uint64_t Offset = 0; + uint64_t VAddr = 0; + uint64_t PAddr = 0; + uint64_t FileSize = 0; + uint64_t MemSize = 0; + uint64_t Align = 0; + + uint32_t Index = 0; + uint64_t OriginalOffset = 0; Segment *ParentSegment = nullptr; ArrayRef<uint8_t> Contents; + std::set<const SectionBase *, SectionCompare> Sections; explicit Segment(ArrayRef<uint8_t> Data) : Contents(Data) {} - Segment() {} + Segment() = default; const SectionBase *firstSection() const { if (!Sections.empty()) @@ -490,7 +496,7 @@ public: OwnedDataSection(StringRef SecName, ArrayRef<uint8_t> Data) : Data(std::begin(Data), std::end(Data)) { Name = SecName.str(); - Type = ELF::SHT_PROGBITS; + Type = OriginalType = ELF::SHT_PROGBITS; Size = Data.size(); OriginalOffset = std::numeric_limits<uint64_t>::max(); } @@ -498,9 +504,9 @@ public: OwnedDataSection(const Twine &SecName, uint64_t SecAddr, uint64_t SecFlags, uint64_t SecOff) { Name = SecName.str(); - Type = ELF::SHT_PROGBITS; + Type = OriginalType = ELF::SHT_PROGBITS; Addr = SecAddr; - Flags = SecFlags; + Flags = OriginalFlags = SecFlags; OriginalOffset = SecOff; } @@ -530,7 +536,7 @@ public: void accept(MutableSectionVisitor &Visitor) override; static bool classof(const SectionBase *S) { - return (S->Flags & ELF::SHF_COMPRESSED) || + return (S->OriginalFlags & ELF::SHF_COMPRESSED) || (StringRef(S->Name).startswith(".zdebug")); } }; @@ -543,7 +549,7 @@ public: : SectionBase(Sec) { Size = Sec.getDecompressedSize(); Align = Sec.getDecompressedAlign(); - Flags = (Flags & ~ELF::SHF_COMPRESSED); + Flags = OriginalFlags = (Flags & ~ELF::SHF_COMPRESSED); if (StringRef(Name).startswith(".zdebug")) Name = "." + Name.substr(2); } @@ -567,7 +573,7 @@ class StringTableSection : public SectionBase { public: StringTableSection() : StrTabBuilder(StringTableBuilder::ELF) { - Type = ELF::SHT_STRTAB; + Type = OriginalType = ELF::SHT_STRTAB; } void addString(StringRef Name); @@ -577,9 +583,9 @@ public: void accept(MutableSectionVisitor &Visitor) override; static bool classof(const SectionBase *S) { - if (S->Flags & ELF::SHF_ALLOC) + if (S->OriginalFlags & ELF::SHF_ALLOC) return false; - return S->Type == ELF::SHT_STRTAB; + return S->OriginalType == ELF::SHT_STRTAB; } }; @@ -648,7 +654,7 @@ public: Name = ".symtab_shndx"; Align = 4; EntrySize = 4; - Type = ELF::SHT_SYMTAB_SHNDX; + Type = OriginalType = ELF::SHT_SYMTAB_SHNDX; } }; @@ -666,7 +672,7 @@ protected: using SymPtr = std::unique_ptr<Symbol>; public: - SymbolTableSection() { Type = ELF::SHT_SYMTAB; } + SymbolTableSection() { Type = OriginalType = ELF::SHT_SYMTAB; } void addSymbol(Twine Name, uint8_t Bind, uint8_t Type, SectionBase *DefinedIn, uint64_t Value, uint8_t Visibility, uint16_t Shndx, @@ -695,7 +701,7 @@ public: const DenseMap<SectionBase *, SectionBase *> &FromTo) override; static bool classof(const SectionBase *S) { - return S->Type == ELF::SHT_SYMTAB; + return S->OriginalType == ELF::SHT_SYMTAB; } }; @@ -724,7 +730,7 @@ public: void setSection(SectionBase *Sec) { SecToApplyRel = Sec; } static bool classof(const SectionBase *S) { - return S->Type == ELF::SHT_REL || S->Type == ELF::SHT_RELA; + return S->OriginalType == ELF::SHT_REL || S->OriginalType == ELF::SHT_RELA; } }; @@ -762,9 +768,9 @@ public: const DenseMap<SectionBase *, SectionBase *> &FromTo) override; static bool classof(const SectionBase *S) { - if (S->Flags & ELF::SHF_ALLOC) + if (S->OriginalFlags & ELF::SHF_ALLOC) return false; - return S->Type == ELF::SHT_REL || S->Type == ELF::SHT_RELA; + return S->OriginalType == ELF::SHT_REL || S->OriginalType == ELF::SHT_RELA; } }; @@ -799,7 +805,7 @@ public: const DenseMap<SectionBase *, SectionBase *> &FromTo) override; static bool classof(const SectionBase *S) { - return S->Type == ELF::SHT_GROUP; + return S->OriginalType == ELF::SHT_GROUP; } }; @@ -808,7 +814,7 @@ public: explicit DynamicSymbolTableSection(ArrayRef<uint8_t> Data) : Section(Data) {} static bool classof(const SectionBase *S) { - return S->Type == ELF::SHT_DYNSYM; + return S->OriginalType == ELF::SHT_DYNSYM; } }; @@ -817,7 +823,7 @@ public: explicit DynamicSection(ArrayRef<uint8_t> Data) : Section(Data) {} static bool classof(const SectionBase *S) { - return S->Type == ELF::SHT_DYNAMIC; + return S->OriginalType == ELF::SHT_DYNAMIC; } }; @@ -838,9 +844,9 @@ public: function_ref<bool(const SectionBase *)> ToRemove) override; static bool classof(const SectionBase *S) { - if (!(S->Flags & ELF::SHF_ALLOC)) + if (!(S->OriginalFlags & ELF::SHF_ALLOC)) return false; - return S->Type == ELF::SHT_REL || S->Type == ELF::SHT_RELA; + return S->OriginalType == ELF::SHT_REL || S->OriginalType == ELF::SHT_RELA; } }; diff --git a/llvm/tools/llvm-objcopy/InstallNameToolOpts.td b/llvm/tools/llvm-objcopy/InstallNameToolOpts.td new file mode 100644 index 000000000000..35047a57994c --- /dev/null +++ b/llvm/tools/llvm-objcopy/InstallNameToolOpts.td @@ -0,0 +1,22 @@ +//===-- InstallNameToolOpts.td - llvm-install-name-tool options --------*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file describes the command line options of llvm-install-name. +// +//===----------------------------------------------------------------------===// + +include "llvm/Option/OptParser.td" + +def help : Flag<["--"], "help">; +def h : Flag<["-"], "h">, Alias<help>; + +def add_rpath : Option<["-", "--"], "add_rpath", KIND_SEPARATE>, + HelpText<"Add new rpath">; + +def version : Flag<["--"], "version">, + HelpText<"Print the version and exit.">; diff --git a/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp b/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp index f621f3aa09cf..380f2e989fe4 100644 --- a/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp @@ -64,9 +64,11 @@ void MachOLayoutBuilder::updateDySymTab(MachO::macho_load_command &MLC) { 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()); + bool AL = A->isLocalSymbol(), BL = B->isLocalSymbol(); + if (AL != BL) + return AL; + return !AL && !A->isUndefinedSymbol() && + B->isUndefinedSymbol(); }) && "Symbols are not sorted by their types."); @@ -318,6 +320,9 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { case MachO::LC_SEGMENT: case MachO::LC_SEGMENT_64: case MachO::LC_VERSION_MIN_MACOSX: + case MachO::LC_VERSION_MIN_IPHONEOS: + case MachO::LC_VERSION_MIN_TVOS: + case MachO::LC_VERSION_MIN_WATCHOS: case MachO::LC_BUILD_VERSION: case MachO::LC_ID_DYLIB: case MachO::LC_LOAD_DYLIB: diff --git a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp index 6d586e7d73f1..4578d0bb75d4 100644 --- a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp @@ -18,31 +18,209 @@ namespace objcopy { namespace macho { using namespace object; +using SectionPred = std::function<bool(const Section &Sec)>; + +static void removeSections(const CopyConfig &Config, Object &Obj) { + SectionPred RemovePred = [](const Section &) { return false; }; + + if (!Config.ToRemove.empty()) { + RemovePred = [&Config, RemovePred](const Section &Sec) { + return Config.ToRemove.matches(Sec.CanonicalName); + }; + } + + if (Config.StripAll || Config.StripDebug) { + // Remove all debug sections. + RemovePred = [RemovePred](const Section &Sec) { + if (Sec.Segname == "__DWARF") + return true; + + return RemovePred(Sec); + }; + } + + if (!Config.OnlySection.empty()) { + // Overwrite RemovePred because --only-section takes priority. + RemovePred = [&Config](const Section &Sec) { + return !Config.OnlySection.matches(Sec.CanonicalName); + }; + } + + return Obj.removeSections(RemovePred); +} + +static void markSymbols(const CopyConfig &Config, Object &Obj) { + // Symbols referenced from the indirect symbol table must not be removed. + for (IndirectSymbolEntry &ISE : Obj.IndirectSymTable.Symbols) + if (ISE.Symbol) + (*ISE.Symbol)->Referenced = true; +} + +static void updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) { + for (SymbolEntry &Sym : Obj.SymTable) { + auto I = Config.SymbolsToRename.find(Sym.Name); + if (I != Config.SymbolsToRename.end()) + Sym.Name = I->getValue(); + } + + auto RemovePred = [Config](const std::unique_ptr<SymbolEntry> &N) { + if (N->Referenced) + return false; + return Config.StripAll; + }; + + Obj.SymTable.removeSymbols(RemovePred); +} + +static LoadCommand buildRPathLoadCommand(StringRef Path) { + LoadCommand LC; + MachO::rpath_command RPathLC; + RPathLC.cmd = MachO::LC_RPATH; + RPathLC.path = sizeof(MachO::rpath_command); + RPathLC.cmdsize = alignTo(sizeof(MachO::rpath_command) + Path.size(), 8); + LC.MachOLoadCommand.rpath_command_data = RPathLC; + LC.Payload.assign(RPathLC.cmdsize - sizeof(MachO::rpath_command), 0); + std::copy(Path.begin(), Path.end(), LC.Payload.begin()); + return LC; +} + +static Error dumpSectionToFile(StringRef SecName, StringRef Filename, + Object &Obj) { + for (LoadCommand &LC : Obj.LoadCommands) + for (Section &Sec : LC.Sections) { + if (Sec.CanonicalName == SecName) { + Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = + FileOutputBuffer::create(Filename, Sec.Content.size()); + if (!BufferOrErr) + return BufferOrErr.takeError(); + std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr); + llvm::copy(Sec.Content, Buf->getBufferStart()); + + if (Error E = Buf->commit()) + return E; + return Error::success(); + } + } + + return createStringError(object_error::parse_failed, "section '%s' not found", + SecName.str().c_str()); +} + +static Error addSection(StringRef SecName, StringRef Filename, Object &Obj) { + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = + MemoryBuffer::getFile(Filename); + if (!BufOrErr) + return createFileError(Filename, errorCodeToError(BufOrErr.getError())); + std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr); + + std::pair<StringRef, StringRef> Pair = SecName.split(','); + StringRef TargetSegName = Pair.first; + Section Sec(TargetSegName, Pair.second); + Sec.Content = Obj.NewSectionsContents.save(Buf->getBuffer()); + + // Add the a section into an existing segment. + for (LoadCommand &LC : Obj.LoadCommands) { + Optional<StringRef> SegName = LC.getSegmentName(); + if (SegName && SegName == TargetSegName) { + LC.Sections.push_back(Sec); + return Error::success(); + } + } + + // There's no segment named TargetSegName. Create a new load command and + // Insert a new section into it. + LoadCommand &NewSegment = Obj.addSegment(TargetSegName); + NewSegment.Sections.push_back(Sec); + return Error::success(); +} + +// isValidMachOCannonicalName returns success if Name is a MachO cannonical name +// ("<segment>,<section>") and lengths of both segment and section names are +// valid. +Error isValidMachOCannonicalName(StringRef Name) { + if (Name.count(',') != 1) + return createStringError(errc::invalid_argument, + "invalid section name '%s' (should be formatted " + "as '<segment name>,<section name>')", + Name.str().c_str()); + + std::pair<StringRef, StringRef> Pair = Name.split(','); + if (Pair.first.size() > 16) + return createStringError(errc::invalid_argument, + "too long segment name: '%s'", + Pair.first.str().c_str()); + if (Pair.second.size() > 16) + return createStringError(errc::invalid_argument, + "too long section name: '%s'", + Pair.second.str().c_str()); + return Error::success(); +} static Error handleArgs(const CopyConfig &Config, Object &Obj) { 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.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.AllocSectionsPrefix.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.UnneededSymbolsToRemove.empty() || !Config.SetSectionAlignment.empty() || !Config.SetSectionFlags.empty() || - !Config.ToRemove.empty() || Config.ExtractDWO || Config.KeepFileSymbols || - Config.LocalizeHidden || Config.PreserveDates || Config.StripDWO || + Config.ExtractDWO || Config.KeepFileSymbols || Config.LocalizeHidden || + Config.PreserveDates || Config.StripAllGNU || Config.StripDWO || Config.StripNonAlloc || Config.StripSections || Config.Weaken || - Config.DecompressDebugSections || Config.StripDebug || - Config.StripNonAlloc || Config.StripSections || Config.StripUnneeded || + Config.DecompressDebugSections || 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"); } + removeSections(Config, Obj); + + // Mark symbols to determine which symbols are still needed. + if (Config.StripAll) + markSymbols(Config, Obj); + + updateAndRemoveSymbols(Config, Obj); + + if (Config.StripAll) + for (LoadCommand &LC : Obj.LoadCommands) + for (Section &Sec : LC.Sections) + Sec.Relocations.clear(); + + for (const StringRef &Flag : Config.DumpSection) { + std::pair<StringRef, StringRef> SecPair = Flag.split("="); + StringRef SecName = SecPair.first; + StringRef File = SecPair.second; + if (Error E = dumpSectionToFile(SecName, File, Obj)) + return E; + } + for (const auto &Flag : Config.AddSection) { + std::pair<StringRef, StringRef> SecPair = Flag.split("="); + StringRef SecName = SecPair.first; + StringRef File = SecPair.second; + if (Error E = isValidMachOCannonicalName(SecName)) + return E; + if (Error E = addSection(SecName, File, Obj)) + return E; + } + + for (StringRef RPath : Config.RPathToAdd) { + for (LoadCommand &LC : Obj.LoadCommands) { + if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH && + RPath == StringRef(reinterpret_cast<char *>(LC.Payload.data()), + LC.Payload.size()) + .trim(0)) { + return createStringError(errc::invalid_argument, + "rpath " + RPath + + " would create a duplicate load command"); + } + } + Obj.addLoadCommand(buildRPathLoadCommand(RPath)); + } return Error::success(); } diff --git a/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp b/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp index b48a0d8952d0..46bb11727322 100644 --- a/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp @@ -29,12 +29,9 @@ void MachOReader::readHeader(Object &O) const { template <typename SectionType> Section constructSectionCommon(SectionType Sec) { - Section S; - S.Sectname = - StringRef(Sec.sectname, strnlen(Sec.sectname, sizeof(Sec.sectname))) - .str(); - S.Segname = - StringRef(Sec.segname, strnlen(Sec.segname, sizeof(Sec.sectname))).str(); + StringRef SegName(Sec.segname, strnlen(Sec.segname, sizeof(Sec.segname))); + StringRef SectName(Sec.sectname, strnlen(Sec.sectname, sizeof(Sec.sectname))); + Section S(SegName, SectName); S.Addr = Sec.addr; S.Size = Sec.size; S.Offset = Sec.offset; @@ -149,10 +146,11 @@ void MachOReader::readLoadCommands(Object &O) const { sizeof(MachO::LCStruct)); \ if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) \ MachO::swapStruct(LC.MachOLoadCommand.LCStruct##_data); \ - LC.Payload = ArrayRef<uint8_t>( \ - reinterpret_cast<uint8_t *>(const_cast<char *>(LoadCmd.Ptr)) + \ - sizeof(MachO::LCStruct), \ - LoadCmd.C.cmdsize - sizeof(MachO::LCStruct)); \ + if (LoadCmd.C.cmdsize > sizeof(MachO::LCStruct)) \ + LC.Payload = ArrayRef<uint8_t>( \ + reinterpret_cast<uint8_t *>(const_cast<char *>(LoadCmd.Ptr)) + \ + sizeof(MachO::LCStruct), \ + LoadCmd.C.cmdsize - sizeof(MachO::LCStruct)); \ break; switch (LoadCmd.C.cmd) { @@ -161,10 +159,11 @@ void MachOReader::readLoadCommands(Object &O) const { sizeof(MachO::load_command)); if (MachOObj.isLittleEndian() != sys::IsLittleEndianHost) MachO::swapStruct(LC.MachOLoadCommand.load_command_data); - LC.Payload = ArrayRef<uint8_t>( - reinterpret_cast<uint8_t *>(const_cast<char *>(LoadCmd.Ptr)) + - sizeof(MachO::load_command), - LoadCmd.C.cmdsize - sizeof(MachO::load_command)); + if (LoadCmd.C.cmdsize > sizeof(MachO::load_command)) + LC.Payload = ArrayRef<uint8_t>( + reinterpret_cast<uint8_t *>(const_cast<char *>(LoadCmd.Ptr)) + + sizeof(MachO::load_command), + LoadCmd.C.cmdsize - sizeof(MachO::load_command)); break; #include "llvm/BinaryFormat/MachO.def" } @@ -255,9 +254,16 @@ void MachOReader::readFunctionStartsData(Object &O) const { 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)); + constexpr uint32_t AbsOrLocalMask = + MachO::INDIRECT_SYMBOL_LOCAL | MachO::INDIRECT_SYMBOL_ABS; + for (uint32_t i = 0; i < DySymTab.nindirectsyms; ++i) { + uint32_t Index = MachOObj.getIndirectSymbolTableEntry(DySymTab, i); + if ((Index & AbsOrLocalMask) != 0) + O.IndirectSymTable.Symbols.emplace_back(Index, None); + else + O.IndirectSymTable.Symbols.emplace_back( + Index, O.SymTable.getSymbolByIndex(Index)); + } } std::unique_ptr<Object> MachOReader::create() const { diff --git a/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp b/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp index 4ec91cc9eb7a..0d9590612eca 100644 --- a/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp @@ -180,7 +180,8 @@ void MachOWriter::writeLoadCommands() { MachO::swapStruct(MLC.LCStruct##_data); \ memcpy(Begin, &MLC.LCStruct##_data, sizeof(MachO::LCStruct)); \ Begin += sizeof(MachO::LCStruct); \ - memcpy(Begin, LC.Payload.data(), LC.Payload.size()); \ + if (!LC.Payload.empty()) \ + memcpy(Begin, LC.Payload.data(), LC.Payload.size()); \ Begin += LC.Payload.size(); \ break; @@ -193,7 +194,8 @@ void MachOWriter::writeLoadCommands() { MachO::swapStruct(MLC.load_command_data); memcpy(Begin, &MLC.load_command_data, sizeof(MachO::load_command)); Begin += sizeof(MachO::load_command); - memcpy(Begin, LC.Payload.data(), LC.Payload.size()); + if (!LC.Payload.empty()) + memcpy(Begin, LC.Payload.data(), LC.Payload.size()); Begin += LC.Payload.size(); break; #include "llvm/BinaryFormat/MachO.def" @@ -369,11 +371,14 @@ void MachOWriter::writeIndirectSymbolTable() { 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()); + uint32_t *Out = + (uint32_t *)(B.getBufferStart() + DySymTabCommand.indirectsymoff); + for (const IndirectSymbolEntry &Sym : O.IndirectSymTable.Symbols) { + uint32_t Entry = (Sym.Symbol) ? (*Sym.Symbol)->Index : Sym.OriginalIndex; + if (IsLittleEndian != sys::IsLittleEndianHost) + sys::swapByteOrder(Entry); + *Out++ = Entry; + } } void MachOWriter::writeDataInCodeData() { diff --git a/llvm/tools/llvm-objcopy/MachO/Object.cpp b/llvm/tools/llvm-objcopy/MachO/Object.cpp index 264f39c28ed2..d3b4fdc2f633 100644 --- a/llvm/tools/llvm-objcopy/MachO/Object.cpp +++ b/llvm/tools/llvm-objcopy/MachO/Object.cpp @@ -10,6 +10,70 @@ const SymbolEntry *SymbolTable::getSymbolByIndex(uint32_t Index) const { return Symbols[Index].get(); } +SymbolEntry *SymbolTable::getSymbolByIndex(uint32_t Index) { + return const_cast<SymbolEntry *>( + static_cast<const SymbolTable *>(this)->getSymbolByIndex(Index)); +} + +void SymbolTable::removeSymbols( + function_ref<bool(const std::unique_ptr<SymbolEntry> &)> ToRemove) { + Symbols.erase( + std::remove_if(std::begin(Symbols), std::end(Symbols), ToRemove), + std::end(Symbols)); +} + +void Object::removeSections(function_ref<bool(const Section &)> ToRemove) { + for (LoadCommand &LC : LoadCommands) + LC.Sections.erase(std::remove_if(std::begin(LC.Sections), + std::end(LC.Sections), ToRemove), + std::end(LC.Sections)); +} + +void Object::addLoadCommand(LoadCommand LC) { + LoadCommands.push_back(std::move(LC)); +} + +template <typename SegmentType> +static void constructSegment(SegmentType &Seg, + llvm::MachO::LoadCommandType CmdType, + StringRef SegName) { + assert(SegName.size() <= sizeof(Seg.segname) && "too long segment name"); + memset(&Seg, 0, sizeof(SegmentType)); + Seg.cmd = CmdType; + strncpy(Seg.segname, SegName.data(), SegName.size()); +} + +LoadCommand &Object::addSegment(StringRef SegName) { + LoadCommand LC; + if (is64Bit()) + constructSegment(LC.MachOLoadCommand.segment_command_64_data, + MachO::LC_SEGMENT_64, SegName); + else + constructSegment(LC.MachOLoadCommand.segment_command_data, + MachO::LC_SEGMENT, SegName); + + LoadCommands.push_back(LC); + return LoadCommands.back(); +} + +/// Extracts a segment name from a string which is possibly non-null-terminated. +static StringRef extractSegmentName(const char *SegName) { + return StringRef(SegName, + strnlen(SegName, sizeof(MachO::segment_command::segname))); +} + +Optional<StringRef> LoadCommand::getSegmentName() const { + const MachO::macho_load_command &MLC = MachOLoadCommand; + switch (MLC.load_command_data.cmd) { + case MachO::LC_SEGMENT: + return extractSegmentName(MLC.segment_command_data.segname); + case MachO::LC_SEGMENT_64: + return extractSegmentName(MLC.segment_command_64_data.segname); + default: + return None; + } +} + } // end namespace macho } // end namespace objcopy } // end namespace llvm diff --git a/llvm/tools/llvm-objcopy/MachO/Object.h b/llvm/tools/llvm-objcopy/MachO/Object.h index 1cebf8253d19..dc2606eefa4a 100644 --- a/llvm/tools/llvm-objcopy/MachO/Object.h +++ b/llvm/tools/llvm-objcopy/MachO/Object.h @@ -14,6 +14,7 @@ #include "llvm/BinaryFormat/MachO.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/ObjectYAML/DWARFYAML.h" +#include "llvm/Support/StringSaver.h" #include "llvm/Support/YAMLTraits.h" #include <cstdint> #include <string> @@ -36,22 +37,32 @@ struct MachHeader { struct RelocationInfo; struct Section { - std::string Sectname; std::string Segname; - uint64_t Addr; - uint64_t Size; - uint32_t Offset; - uint32_t Align; - uint32_t RelOff; - uint32_t NReloc; - uint32_t Flags; - uint32_t Reserved1; - uint32_t Reserved2; - uint32_t Reserved3; - + std::string Sectname; + // CanonicalName is a string formatted as “<Segname>,<Sectname>". + std::string CanonicalName; + uint64_t Addr = 0; + uint64_t Size = 0; + uint32_t Offset = 0; + uint32_t Align = 0; + uint32_t RelOff = 0; + uint32_t NReloc = 0; + uint32_t Flags = 0; + uint32_t Reserved1 = 0; + uint32_t Reserved2 = 0; + uint32_t Reserved3 = 0; StringRef Content; std::vector<RelocationInfo> Relocations; + Section(StringRef SegName, StringRef SectName) + : Segname(SegName), Sectname(SectName), + CanonicalName((Twine(SegName) + Twine(',') + SectName).str()) {} + + Section(StringRef SegName, StringRef SectName, StringRef Content) + : Segname(SegName), Sectname(SectName), + CanonicalName((Twine(SegName) + Twine(',') + SectName).str()), + Content(Content) {} + MachO::SectionType getType() const { return static_cast<MachO::SectionType>(Flags & MachO::SECTION_TYPE); } @@ -72,19 +83,23 @@ struct LoadCommand { // The raw content of the payload of the load command (located right after the // corresponding struct). In some cases it is either empty or can be // copied-over without digging into its structure. - ArrayRef<uint8_t> Payload; + std::vector<uint8_t> Payload; // Some load commands can contain (inside the payload) an array of sections, // though the contents of the sections are stored separately. The struct // Section describes only sections' metadata and where to find the // corresponding content inside the binary. std::vector<Section> Sections; + + // Returns the segment name if the load command is a segment command. + Optional<StringRef> getSegmentName() const; }; // A symbol information. Fields which starts with "n_" are same as them in the // nlist. struct SymbolEntry { std::string Name; + bool Referenced = false; uint32_t Index; uint8_t n_type; uint8_t n_sect; @@ -107,11 +122,32 @@ struct SymbolEntry { struct SymbolTable { std::vector<std::unique_ptr<SymbolEntry>> Symbols; + using iterator = pointee_iterator< + std::vector<std::unique_ptr<SymbolEntry>>::const_iterator>; + + iterator begin() const { return iterator(Symbols.begin()); } + iterator end() const { return iterator(Symbols.end()); } + const SymbolEntry *getSymbolByIndex(uint32_t Index) const; + SymbolEntry *getSymbolByIndex(uint32_t Index); + void removeSymbols( + function_ref<bool(const std::unique_ptr<SymbolEntry> &)> ToRemove); +}; + +struct IndirectSymbolEntry { + // The original value in an indirect symbol table. Higher bits encode extra + // information (INDIRECT_SYMBOL_LOCAL and INDIRECT_SYMBOL_ABS). + uint32_t OriginalIndex; + /// The Symbol referenced by this entry. It's None if the index is + /// INDIRECT_SYMBOL_LOCAL or INDIRECT_SYMBOL_ABS. + Optional<SymbolEntry *> Symbol; + + IndirectSymbolEntry(uint32_t OriginalIndex, Optional<SymbolEntry *> Symbol) + : OriginalIndex(OriginalIndex), Symbol(Symbol) {} }; struct IndirectSymbolTable { - std::vector<uint32_t> Symbols; + std::vector<IndirectSymbolEntry> Symbols; }; /// The location of the string table inside the binary is described by LC_SYMTAB @@ -250,6 +286,24 @@ struct Object { Optional<size_t> DataInCodeCommandIndex; /// The index LC_FUNCTION_STARTS load comamnd if present. Optional<size_t> FunctionStartsCommandIndex; + + BumpPtrAllocator Alloc; + StringSaver NewSectionsContents; + + Object() : NewSectionsContents(Alloc) {} + + void removeSections(function_ref<bool(const Section &)> ToRemove); + void addLoadCommand(LoadCommand LC); + + /// Creates a new segment load command in the object and returns a reference + /// to the newly created load command. The caller should verify that SegName + /// is not too long (SegName.size() should be less than or equal to 16). + LoadCommand &addSegment(StringRef SegName); + + bool is64Bit() const { + return Header.Magic == MachO::MH_MAGIC_64 || + Header.Magic == MachO::MH_CIGAM_64; + } }; } // end namespace macho diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp index a68210f3fdd3..e662f35f4b08 100644 --- a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp +++ b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp @@ -313,11 +313,20 @@ static Error executeObjcopy(CopyConfig &Config) { return Error::success(); } +namespace { + +enum class ToolType { Objcopy, Strip, InstallNameTool }; + +} // anonymous namespace + int main(int argc, char **argv) { InitLLVM X(argc, argv); ToolName = argv[0]; - bool IsStrip = sys::path::stem(ToolName).contains("strip"); - + ToolType Tool = StringSwitch<ToolType>(sys::path::stem(ToolName)) + .EndsWith("strip", ToolType::Strip) + .EndsWith("install-name-tool", ToolType::InstallNameTool) + .EndsWith("install_name_tool", ToolType::InstallNameTool) + .Default(ToolType::Objcopy); // 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 @@ -332,10 +341,11 @@ int main(int argc, char **argv) { NewArgv); auto Args = makeArrayRef(NewArgv).drop_front(); - Expected<DriverConfig> DriverConfig = - IsStrip ? parseStripOptions(Args, reportWarning) - : parseObjcopyOptions(Args, reportWarning); + (Tool == ToolType::Strip) ? parseStripOptions(Args, reportWarning) + : ((Tool == ToolType::InstallNameTool) + ? parseInstallNameToolOptions(Args) + : parseObjcopyOptions(Args, reportWarning)); if (!DriverConfig) { logAllUnhandledErrors(DriverConfig.takeError(), WithColor::error(errs(), ToolName)); |