diff --git a/llvm/test/tools/llvm-objcopy/MachO/remove-section-dead-symbols.test b/llvm/test/tools/llvm-objcopy/MachO/remove-section-dead-symbols.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/MachO/remove-section-dead-symbols.test @@ -0,0 +1,153 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-objcopy --remove-section __DATA,C %t %t.copy + +# RUN: llvm-readobj --sections --symbols %t.copy | FileCheck %s + +# CHECK: Sections [ +# CHECK-NEXT: Section { +# CHECK-NEXT: Index: 0 +# CHECK-NEXT: Name: __text (5F 5F 74 65 78 74 00 00 00 00 00 00 00 00 00 00) +# CHECK-NEXT: Segment: __TEXT (5F 5F 54 45 58 54 00 00 00 00 00 00 00 00 00 00) +# CHECK-NEXT: Address: 0x0 +# CHECK-NEXT: Size: 0x0 +# CHECK-NEXT: Offset: 384 +# CHECK-NEXT: Alignment: 0 +# CHECK-NEXT: RelocationOffset: 0x0 +# CHECK-NEXT: RelocationCount: 0 +# CHECK-NEXT: Type: Regular (0x0) +# CHECK-NEXT: Attributes [ (0x800000) +# CHECK-NEXT: PureInstructions (0x800000) +# CHECK-NEXT: ] +# CHECK-NEXT: Reserved1: 0x0 +# CHECK-NEXT: Reserved2: 0x0 +# CHECK-NEXT: Reserved3: 0x0 +# CHECK-NEXT: } +# CHECK-NEXT: Section { +# CHECK-NEXT: Index: 1 +# CHECK-NEXT: Name: __data (5F 5F 64 61 74 61 00 00 00 00 00 00 00 00 00 00) +# CHECK-NEXT: Segment: __DATA (5F 5F 44 41 54 41 00 00 00 00 00 00 00 00 00 00) +# CHECK-NEXT: Address: 0x0 +# CHECK-NEXT: Size: 0x4 +# CHECK-NEXT: Offset: 384 +# CHECK-NEXT: Alignment: 2 +# CHECK-NEXT: RelocationOffset: 0x0 +# CHECK-NEXT: RelocationCount: 0 +# CHECK-NEXT: Type: Regular (0x0) +# CHECK-NEXT: Attributes [ (0x0) +# CHECK-NEXT: ] +# CHECK-NEXT: Reserved1: 0x0 +# CHECK-NEXT: Reserved2: 0x0 +# CHECK-NEXT: Reserved3: 0x0 +# CHECK-NEXT: } +# CHECK-NEXT: ] + +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x00000003 + filetype: 0x00000001 + ncmds: 4 + sizeofcmds: 432 + flags: 0x00002000 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 312 + segname: '' + vmaddr: 0 + vmsize: 8 + fileoff: 464 + filesize: 8 + maxprot: 7 + initprot: 7 + nsects: 3 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x0000000000000000 + size: 0 + offset: 0x000001D0 + align: 0 + reloff: 0x00000000 + nreloc: 0 + flags: 0x80000000 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: '' + - sectname: __data + segname: __DATA + addr: 0x0000000000000000 + size: 4 + offset: 0x000001D0 + align: 2 + reloff: 0x00000000 + nreloc: 0 + flags: 0x00000000 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: '01000000' + - sectname: C + segname: __DATA + addr: 0x0000000000000004 + size: 4 + offset: 0x000001D4 + align: 2 + reloff: 0x00000000 + nreloc: 0 + flags: 0x00000000 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: '02000000' + - cmd: LC_VERSION_MIN_MACOSX + cmdsize: 16 + version: 658944 + sdk: 0 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 472 + nsyms: 2 + stroff: 504 + strsize: 8 + - cmd: LC_DYSYMTAB + cmdsize: 80 + ilocalsym: 0 + nlocalsym: 0 + iextdefsym: 0 + nextdefsym: 2 + iundefsym: 2 + nundefsym: 0 + tocoff: 0 + ntoc: 0 + modtaboff: 0 + nmodtab: 0 + extrefsymoff: 0 + nextrefsyms: 0 + indirectsymoff: 0 + nindirectsyms: 0 + extreloff: 0 + nextrel: 0 + locreloff: 0 + nlocrel: 0 +LinkEditData: + NameList: + - n_strx: 4 + n_type: 0x0F + n_sect: 2 + n_desc: 0 + n_value: 0 + - n_strx: 1 + n_type: 0x0F + n_sect: 3 + n_desc: 0 + n_value: 4 + StringTable: + - '' + - _B + - _A + - '' +... diff --git a/llvm/test/tools/llvm-objcopy/MachO/remove-section-error.test b/llvm/test/tools/llvm-objcopy/MachO/remove-section-error.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/MachO/remove-section-error.test @@ -0,0 +1,146 @@ +# RUN: yaml2obj %s -o %t +# RUN: not llvm-objcopy --remove-section __DATA,C %t %t.copy 2>&1 | FileCheck %s + +# CHECK: symbol '_a' defined in section with index '2' cannot be removed because it is referenced by a relocation in section '__TEXT,__text' + +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x00000003 + filetype: 0x00000001 + ncmds: 4 + sizeofcmds: 512 + flags: 0x00002000 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 392 + segname: '' + vmaddr: 0 + vmsize: 112 + fileoff: 544 + filesize: 112 + maxprot: 7 + initprot: 7 + nsects: 4 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x0000000000000000 + size: 12 + offset: 0x00000220 + align: 4 + reloff: 0x00000290 + nreloc: 1 + flags: 0x80000400 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: 554889E58B05000000005DC3 + relocations: + - address: 0x00000006 + symbolnum: 0 + pcrel: true + length: 2 + extern: true + type: 1 + scattered: false + value: 0 + - sectname: C + segname: __DATA + addr: 0x000000000000000C + size: 4 + offset: 0x0000022C + align: 2 + reloff: 0x00000000 + nreloc: 0 + flags: 0x00000000 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: '02000000' + - sectname: __compact_unwind + segname: __LD + addr: 0x0000000000000010 + size: 32 + offset: 0x00000230 + align: 3 + reloff: 0x00000298 + nreloc: 1 + flags: 0x02000000 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: 00000000000000000C0000000000000100000000000000000000000000000000 + relocations: + - address: 0x00000000 + symbolnum: 1 + pcrel: false + length: 3 + extern: false + type: 0 + scattered: false + value: 0 + - sectname: __eh_frame + segname: __TEXT + addr: 0x0000000000000030 + size: 64 + offset: 0x00000250 + align: 3 + reloff: 0x00000000 + nreloc: 0 + flags: 0x6800000B + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + content: 1400000000000000017A520001781001100C070890010000240000001C000000B0FFFFFFFFFFFFFF0C0000000000000000410E108602430D0600000000000000 + - cmd: LC_VERSION_MIN_MACOSX + cmdsize: 16 + version: 658944 + sdk: 0 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 672 + nsyms: 2 + stroff: 704 + strsize: 8 + - cmd: LC_DYSYMTAB + cmdsize: 80 + ilocalsym: 0 + nlocalsym: 0 + iextdefsym: 0 + nextdefsym: 2 + iundefsym: 2 + nundefsym: 0 + tocoff: 0 + ntoc: 0 + modtaboff: 0 + nmodtab: 0 + extrefsymoff: 0 + nextrefsyms: 0 + indirectsymoff: 0 + nindirectsyms: 0 + extreloff: 0 + nextrel: 0 + locreloff: 0 + nlocrel: 0 +LinkEditData: + NameList: + - n_strx: 4 + n_type: 0x0F + n_sect: 2 + n_desc: 0 + n_value: 12 + - n_strx: 1 + n_type: 0x0F + n_sect: 1 + n_desc: 0 + n_value: 0 + StringTable: + - '' + - _f + - _a + - '' +... diff --git a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp --- a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp @@ -20,7 +20,7 @@ using namespace object; using SectionPred = std::function &Sec)>; -static void removeSections(const CopyConfig &Config, Object &Obj) { +static Error removeSections(const CopyConfig &Config, Object &Obj) { SectionPred RemovePred = [](const std::unique_ptr
&) { return false; }; if (!Config.ToRemove.empty()) { @@ -181,7 +181,9 @@ return createStringError(llvm::errc::invalid_argument, "option not supported by llvm-objcopy for MachO"); } - removeSections(Config, Obj); + + if (Error E = removeSections(Config, Obj)) + return E; // Mark symbols to determine which symbols are still needed. if (Config.StripAll) diff --git a/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp b/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp --- a/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp @@ -28,10 +28,11 @@ } template -Section constructSectionCommon(SectionType Sec) { +Section constructSectionCommon(SectionType Sec, uint32_t Index) { 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.Index = Index; S.Addr = Sec.addr; S.Size = Sec.size; S.Offset = Sec.offset; @@ -45,14 +46,14 @@ return S; } -template Section constructSection(SectionType Sec); +template Section constructSection(SectionType Sec, uint32_t Index); -template <> Section constructSection(MachO::section Sec) { - return constructSectionCommon(Sec); +template <> Section constructSection(MachO::section Sec, uint32_t Index) { + return constructSectionCommon(Sec, Index); } -template <> Section constructSection(MachO::section_64 Sec) { - Section S = constructSectionCommon(Sec); +template <> Section constructSection(MachO::section_64 Sec, uint32_t Index) { + Section S = constructSectionCommon(Sec, Index); S.Reserved3 = Sec.reserved3; return S; } @@ -62,7 +63,7 @@ std::vector> extractSections(const object::MachOObjectFile::LoadCommandInfo &LoadCmd, const object::MachOObjectFile &MachOObj, - size_t &NextSectionIndex) { + uint32_t &NextSectionIndex) { auto End = LoadCmd.Ptr + LoadCmd.C.cmdsize; const SectionType *Curr = reinterpret_cast(LoadCmd.Ptr + sizeof(SegmentType)); @@ -72,9 +73,9 @@ SectionType Sec; memcpy((void *)&Sec, Curr, sizeof(SectionType)); MachO::swapStruct(Sec); - Sections.push_back(std::make_unique
(constructSection(Sec))); + Sections.push_back(std::make_unique
(constructSection(Sec, NextSectionIndex))); } else { - Sections.push_back(std::make_unique
(constructSection(*Curr))); + Sections.push_back(std::make_unique
(constructSection(*Curr, NextSectionIndex))); } Section &S = *Sections.back(); @@ -110,7 +111,7 @@ void MachOReader::readLoadCommands(Object &O) const { // For MachO sections indices start from 1. - size_t NextSectionIndex = 1; + uint32_t NextSectionIndex = 1; for (auto LoadCmd : MachOObj.load_commands()) { LoadCommand LC; switch (LoadCmd.C.cmd) { diff --git a/llvm/tools/llvm-objcopy/MachO/Object.h b/llvm/tools/llvm-objcopy/MachO/Object.h --- a/llvm/tools/llvm-objcopy/MachO/Object.h +++ b/llvm/tools/llvm-objcopy/MachO/Object.h @@ -37,6 +37,7 @@ struct RelocationInfo; struct Section { + uint32_t Index; std::string Segname; std::string Sectname; // CanonicalName is a string formatted as “,". @@ -115,6 +116,10 @@ bool isUndefinedSymbol() const { return (n_type & MachO::N_TYPE) == MachO::N_UNDF; } + + Optional section() const { + return n_sect == MachO::NO_SECT ? None : Optional(n_sect); + } }; /// The location of the symbol table inside the binary is described by LC_SYMTAB @@ -292,7 +297,7 @@ Object() : NewSectionsContents(Alloc) {} - void removeSections(function_ref &)> ToRemove); + Error removeSections(function_ref &)> ToRemove); void addLoadCommand(LoadCommand LC); /// Creates a new segment load command in the object and returns a reference diff --git a/llvm/tools/llvm-objcopy/MachO/Object.cpp b/llvm/tools/llvm-objcopy/MachO/Object.cpp --- a/llvm/tools/llvm-objcopy/MachO/Object.cpp +++ b/llvm/tools/llvm-objcopy/MachO/Object.cpp @@ -1,5 +1,6 @@ #include "Object.h" #include "../llvm-objcopy.h" +#include namespace llvm { namespace objcopy { @@ -22,11 +23,41 @@ std::end(Symbols)); } -void Object::removeSections(function_ref &)> ToRemove) { - for (LoadCommand &LC : LoadCommands) - LC.Sections.erase(std::remove_if(std::begin(LC.Sections), - std::end(LC.Sections), ToRemove), - std::end(LC.Sections)); +Error Object::removeSections( + function_ref &)> ToRemove) { + std::unordered_set RemovedSectionsIndices; + for (LoadCommand &LC : LoadCommands) { + auto It = std::stable_partition( + std::begin(LC.Sections), std::end(LC.Sections), + [&](const std::unique_ptr
&Sec) { return !ToRemove(Sec); }); + for (auto I = It, End = LC.Sections.end(); I != End; ++I) + RemovedSectionsIndices.insert((*I)->Index); + LC.Sections.erase(It, LC.Sections.end()); + } + + auto IsDead = [&](const std::unique_ptr &S) -> bool { + Optional Section = S->section(); + return (Section && RemovedSectionsIndices.count(*Section)); + }; + + std::unordered_set DeadSymbols; + for (const std::unique_ptr &Sym : SymTable.Symbols) + if (IsDead(Sym)) + DeadSymbols.insert(Sym.get()); + + for (const LoadCommand &LC : LoadCommands) + for (const std::unique_ptr
&Sec : LC.Sections) + for (const RelocationInfo &R : Sec->Relocations) + if (R.Symbol && DeadSymbols.count(R.Symbol)) + return createStringError(std::errc::invalid_argument, + "symbol '%s' defined in section with index " + "'%u' cannot be removed because it is " + "referenced by a relocation in section '%s'", + R.Symbol->Name.c_str(), + *(R.Symbol->section()), + Sec->CanonicalName.c_str()); + SymTable.removeSymbols(IsDead); + return Error::success(); } void Object::addLoadCommand(LoadCommand LC) {