Index: test/tools/llvm-objcopy/dynsym-error-remove-strtab.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/dynsym-error-remove-strtab.test @@ -0,0 +1,3 @@ +# RUN: not llvm-objcopy -R .dynstr %p/Inputs/dynsym.so %t 2>&1 >/dev/null | FileCheck %s + +# CHECK: String table .dynstr cannot be removed because it is referenced by the section .dynsym Index: test/tools/llvm-objcopy/reloc-error-remove-symtab.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/reloc-error-remove-symtab.test @@ -0,0 +1,32 @@ +# RUN: yaml2obj %s > %t +# RUN: not llvm-objcopy -R .symtab %t %t2 2>&1 >/dev/null | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + AddressAlign: 0x0000000000000010 + Content: "0000000000000000" + - Name: .rel.text + Type: SHT_REL + Link: .symtab + Info: .text + Relocations: + - Offset: 0x1000 + Symbol: foo + Type: R_X86_64_PC32 + +Symbols: + Global: + - Name: foo + Type: STT_FUNC + Size: 4 + +# CHECK: Symbol table .symtab cannot be removed because it is referenced by the relocation section .rel.text. Index: test/tools/llvm-objcopy/remove-section-with-symbol.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/remove-section-with-symbol.test @@ -0,0 +1,50 @@ +# RUN: yaml2obj %s > %t +# RUN: llvm-objcopy -R .test %t %t2 +# RUN: llvm-readobj -symbols %t2 | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .test + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + - Name: .test2 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] +Symbols: + Global: + - Name: test + Type: STT_FUNC + Section: .test + Value: 0x1000 + Size: 4 + - Name: test2 + Type: STT_FUNC + Section: .test2 + Value: 0x1000 + Size: 4 + +#CHECK: Symbols [ +#CHECK-NEXT: Symbol { +#CHECK-NEXT: Name: +#CHECK-NEXT: Value: 0x0 +#CHECK-NEXT: Size: 0 +#CHECK-NEXT: Binding: Local +#CHECK-NEXT: Type: None +#CHECK-NEXT: Other: 0 +#CHECK-NEXT: Section: Undefined +#CHECK-NEXT: } +#CHECK-NEXT: Symbol { +#CHECK-NEXT: Name: test2 +#CHECK-NEXT: Value: 0x1000 +#CHECK-NEXT: Size: 4 +#CHECK-NEXT: Binding: Global +#CHECK-NEXT: Type: Function +#CHECK-NEXT: Other: 0 +#CHECK-NEXT: Section: .test2 +#CHECK-NEXT: } +#CHECK-NEXT:] Index: test/tools/llvm-objcopy/remove-section.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/remove-section.test @@ -0,0 +1,40 @@ +# RUN: yaml2obj %s > %t +# RUN: llvm-objcopy -R=.test2 %t %t2 +# RUN: llvm-readobj -sections %t2 | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .test1 + Type: SHT_PROGBITS + Flags: [ ] + - Name: .test2 + Type: SHT_PROGBITS + Flags: [ ] + - Name: .test3 + Type: SHT_PROGBITS + Flags: [ ] + +# CHECK: Type: SHT_NULL + +# CHECK: Name: .test1 +# CHECK-NEXT: Type: SHT_PROGBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: ] +# CHECK-NEXT: Address: +# CHECK-NEXT: Offset: +# CHECK-NEXT: Size: + +# CHECK-NOT: Name: .test2 + +# CHECK: Name: .test3 +# CHECK-NEXT: Type: SHT_PROGBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: ] +# CHECK-NEXT: Address: +# CHECK-NEXT: Offset: +# CHECK-NEXT: Size: Index: test/tools/llvm-objcopy/symtab-error-on-remove-strtab.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/symtab-error-on-remove-strtab.test @@ -0,0 +1,11 @@ +# RUN: yaml2obj %s > %t +# RUN: not llvm-objcopy -R .strtab %t %t2 2>&1 >/dev/null | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 + +# CHECK: String table .strtab cannot be removed because it is referenced by the symbol table Index: tools/llvm-objcopy/Object.h =================================================================== --- tools/llvm-objcopy/Object.h +++ tools/llvm-objcopy/Object.h @@ -58,6 +58,7 @@ virtual ~SectionBase() {} virtual void initialize(SectionTableRef SecTable); virtual void finalize(); + virtual void removeSectionReferences(const SectionBase *Sec); template void writeHeader(llvm::FileOutputBuffer &Out) const; virtual void writeSection(llvm::FileOutputBuffer &Out) const = 0; }; @@ -99,7 +100,8 @@ return *Sections.begin(); return nullptr; } - void addSection(const SectionBase *sec) { Sections.insert(sec); } + void removeSection(const SectionBase *Sec) { Sections.erase(Sec); } + void addSection(const SectionBase *Sec) { Sections.insert(Sec); } template void writeHeader(llvm::FileOutputBuffer &Out) const; void writeSegment(llvm::FileOutputBuffer &Out) const; }; @@ -165,6 +167,8 @@ std::vector> Symbols; StringTableSection *SymbolNames; + typedef std::unique_ptr SymPtr; + public: void setStrTab(StringTableSection *StrTab) { SymbolNames = StrTab; } void addSymbol(llvm::StringRef Name, uint8_t Bind, uint8_t Type, @@ -172,6 +176,7 @@ uint64_t Sz); void addSymbolNames(); const Symbol *getSymbolByIndex(uint32_t Index) const; + void removeSectionReferences(const SectionBase *Sec) override; void initialize(SectionTableRef SecTable) override; void finalize() override; static bool classof(const SectionBase *S) { @@ -191,14 +196,28 @@ uint32_t Type; }; -template class RelocationSectionBase : public SectionBase { +class DefinedOnRelocSection : public SectionBase { +protected: + SectionBase *SecToApplyRel; + +public: + const SectionBase *getSection() const { return SecToApplyRel; } + void setSection(SectionBase *Sec) { SecToApplyRel = Sec; } + + static bool classof(const SectionBase *S) { + return S->Type == llvm::ELF::SHT_REL || S->Type == llvm::ELF::SHT_RELA; + } +}; + +template +class RelocationSectionBase : public DefinedOnRelocSection { private: SymTabType *Symbols; - SectionBase *SecToApplyRel; public: void setSymTab(SymTabType *StrTab) { Symbols = StrTab; } - void setSection(SectionBase *Sec) { SecToApplyRel = Sec; } + + void removeSectionReferences(const SectionBase *Sec) override; void initialize(SectionTableRef SecTable) override; void finalize() override; }; @@ -226,11 +245,12 @@ class SectionWithStrTab : public Section { private: - StringTableSection *StrTab; + const SectionBase *StrTab; public: SectionWithStrTab(llvm::ArrayRef Data) : Section(Data) {} - void setStrTab(StringTableSection *StringTable) { StrTab = StringTable; } + void setStrTab(const SectionBase *StringTable) { StrTab = StringTable; } + void removeSectionReferences(const SectionBase *Sec) override; void initialize(SectionTableRef SecTable) override; void finalize() override; static bool classof(const SectionBase *S); @@ -305,6 +325,7 @@ uint32_t Flags; Object(const llvm::object::ELFObjectFile &Obj); + void removeSections(std::function ToRemove); virtual size_t totalSize() const = 0; virtual void finalize() = 0; virtual void write(llvm::FileOutputBuffer &Out) const = 0; Index: tools/llvm-objcopy/Object.cpp =================================================================== --- tools/llvm-objcopy/Object.cpp +++ tools/llvm-objcopy/Object.cpp @@ -49,6 +49,7 @@ std::copy(std::begin(Contents), std::end(Contents), Buf); } +void SectionBase::removeSectionReferences(const SectionBase *Sec) {} void SectionBase::initialize(SectionTableRef SecTable) {} void SectionBase::finalize() {} @@ -150,6 +151,18 @@ Size += this->EntrySize; } +void SymbolTableSection::removeSectionReferences(const SectionBase *Sec) { + if (SymbolNames == Sec) { + error("String table " + SymbolNames->Name + + " cannot be removed because it is referenced by the symbol table"); + } + auto Iter = + std::remove_if(std::begin(Symbols), std::end(Symbols), + [=](const SymPtr &Sym) { return Sym->DefinedIn == Sec; }); + Size -= (std::end(Symbols) - Iter) * this->EntrySize; + Symbols.erase(Iter, std::end(Symbols)); +} + void SymbolTableSection::initialize(SectionTableRef SecTable) { Size = 0; setStrTab(SecTable.getSectionOfType( @@ -206,6 +219,17 @@ } } +template +void RelocationSectionBase::removeSectionReferences( + const SectionBase *Sec) { + if (Symbols == Sec) { + error("Symbol table " + Symbols->Name + " cannot be removed because it is " + "referenced by the relocation " + "section " + + this->Name); + } +} + template void RelocationSectionBase::initialize(SectionTableRef SecTable) { setSymTab(SecTable.getSectionOfType( @@ -259,16 +283,22 @@ Out.getBufferStart() + Offset); } +void SectionWithStrTab::removeSectionReferences(const SectionBase *Sec) { + if (StrTab == Sec) { + error("String table " + StrTab->Name + " cannot be removed because it is " + "referenced by the section " + + this->Name); + } +} + bool SectionWithStrTab::classof(const SectionBase *S) { return isa(S) || isa(S); } void SectionWithStrTab::initialize(SectionTableRef SecTable) { - setStrTab(SecTable.getSectionOfType( - Link, - "Link field value " + Twine(Link) + " in section " + Name + " is invalid", - "Link field value " + Twine(Link) + " in section " + Name + - " is not a string table")); + setStrTab(SecTable.getSection(Link, + "Link field value " + Twine(Link) + + " in section " + Name + " is invalid")); } void SectionWithStrTab::finalize() { this->Link = StrTab->Index; } @@ -599,6 +629,38 @@ Section->writeSection(Out); } +template +void Object::removeSections( + std::function ToRemove) { + + auto Iter = + std::stable_partition(std::begin(Sections), std::end(Sections), + [=](const SecPtr &Sec) { return !ToRemove(*Sec); }); + // Add the things we want to remove to a set + std::set SecsToRemove; + for (auto &RemoveSec : make_range(Iter, std::end(Sections))) + SecsToRemove.insert(RemoveSec.get()); + // Now partition the relocation sections of the removed sections to the end. + Iter = + std::stable_partition(std::begin(Sections), Iter, [&](const SecPtr &Sec) { + if (auto RelSec = dyn_cast(Sec.get())) { + return SecsToRemove.count(RelSec->getSection()) == 0; + } + return true; + }); + // Now make sure there are no remaining references to the sections that will + // be removed. Sometimes it is impossible to remove a reference so we throw + // errors here. + for (auto &RemoveSec : make_range(Iter, std::end(Sections))) { + for (auto &Segment : Segments) + Segment->removeSection(RemoveSec.get()); + for (auto &KeepSec : make_range(std::begin(Sections), Iter)) + KeepSec->removeSectionReferences(RemoveSec.get()); + } + // Now finally get rid of them all togethor. + Sections.erase(Iter, std::end(Sections)); +} + template void ELFObject::sortSections() { // Put all sections in offset order. Maintain the ordering as closely as // possible while meeting that demand however. Index: tools/llvm-objcopy/llvm-objcopy.cpp =================================================================== --- tools/llvm-objcopy/llvm-objcopy.cpp +++ tools/llvm-objcopy/llvm-objcopy.cpp @@ -56,17 +56,21 @@ cl::opt OutputFormat("O", cl::desc("set output format to one of the following:" "\n\tbinary")); +cl::opt ToRemove("R", cl::desc("Remove a specific section")); void CopyBinary(const ELFObjectFile &ObjFile) { std::unique_ptr Buffer; std::unique_ptr> Obj; if (!OutputFormat.empty() && OutputFormat != "binary") error("invalid output format '" + OutputFormat + "'"); - if (!OutputFormat.empty() && OutputFormat == "binary") Obj = llvm::make_unique>(ObjFile); else Obj = llvm::make_unique>(ObjFile); + if (!ToRemove.empty()) { + Obj->removeSections( + [&](const SectionBase &Sec) -> bool { return ToRemove == Sec.Name; }); + } Obj->finalize(); ErrorOr> BufferOrErr = FileOutputBuffer::create(OutputFilename, Obj->totalSize(),