Index: test/tools/llvm-objcopy/replace-strtab-dynamic-error.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/replace-strtab-dynamic-error.test @@ -0,0 +1,26 @@ +# RUN: yaml2obj %s > %t +# RUN: not llvm-objcopy -only-keep-debug %t %t2 2>&1 >/dev/null | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .mystrtab + Type: SHT_STRTAB + Flags: [ SHF_ALLOC ] + Size: 64 + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ ] + Link: .mystrtab + Info: 1 + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: "00000000" + +# CHECK: String table .mystrtab cannot be replaced because it is referenced by the section .dynamic. Index: test/tools/llvm-objcopy/replace-strtab-dynsym-error.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/replace-strtab-dynsym-error.test @@ -0,0 +1,26 @@ +# RUN: yaml2obj %s > %t +# RUN: not llvm-objcopy -only-keep-debug %t %t2 2>&1 >/dev/null | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .mystrtab + Type: SHT_STRTAB + Flags: [ SHF_ALLOC ] + Size: 64 + - Name: .dynsym + Type: SHT_DYNSYM + Flags: [ ] + Link: .mystrtab + Info: 1 + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: "00000000" + +# CHECK: String table .mystrtab cannot be replaced because it is referenced by the section .dynsym. Index: test/tools/llvm-objcopy/replace-text-symtab.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/replace-text-symtab.test @@ -0,0 +1,51 @@ +# RUN: yaml2obj %s > %t +# RUN: llvm-objcopy -only-keep-debug %t %t2 +# RUN: llvm-readobj -symbols -file-headers -sections %t2 | 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: 0x1234 + AddressAlign: 0x0000000000000010 + Content: "00000000" +Symbols: + Global: + - Name: _start + Type: STT_FUNC + Value: 0x1000 + Size: 4 + Section: .text + +# CHECK: SectionHeaderCount: 5 + +# CHECK: Name: .text +# CHECK-NEXT: Type: SHT_NOBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: SHF_ALLOC +# CHECK-NEXT: SHF_EXECINSTR +# CHECK-NEXT: ] +# CHECK-NEXT: Address: 0x1234 +# CHECK-NEXT: Offset: +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Link: +# CHECK-NEXT: Info: +# CHECK-NEXT: AddressAlignment: 16 + +# CHECK: .symtab +# CHECK: .strtab +# CHECK: .shstrtab + +#CHECK: Name: _start +#CHECK-NEXT: Value: 0x1000 +#CHECK-NEXT: Size: 4 +#CHECK-NEXT: Binding: Global +#CHECK-NEXT: Type: Function (0x2) +#CHECK-NEXT: Other: 0 +#CHECK-NEXT: Section: .text Index: tools/llvm-objcopy/Object.h =================================================================== --- tools/llvm-objcopy/Object.h +++ tools/llvm-objcopy/Object.h @@ -67,6 +67,8 @@ virtual void initialize(SectionTableRef SecTable); virtual void finalize(); + virtual void replaceSectionReferences(const SectionBase *Old, + const SectionBase *New); virtual void removeSectionReferences(const SectionBase *Sec); template void writeHeader(FileOutputBuffer &Out) const; virtual void writeSection(FileOutputBuffer &Out) const = 0; @@ -171,7 +173,7 @@ struct Symbol { uint8_t Binding; - SectionBase *DefinedIn = nullptr; + const SectionBase *DefinedIn = nullptr; SymbolShndxType ShndxType; uint32_t Index; StringRef Name; @@ -197,6 +199,8 @@ uint64_t Sz); void addSymbolNames(); const Symbol *getSymbolByIndex(uint32_t Index) const; + void replaceSectionReferences(const SectionBase *Old, + const SectionBase *New) override; void removeSectionReferences(const SectionBase *Sec) override; void initialize(SectionTableRef SecTable) override; void finalize() override; @@ -252,6 +256,8 @@ public: void setSymTab(SymTabType *StrTab) { Symbols = StrTab; } + void replaceSectionReferences(const SectionBase *Old, + const SectionBase *New) override; void removeSectionReferences(const SectionBase *Sec) override; void initialize(SectionTableRef SecTable) override; void finalize() override; @@ -287,6 +293,8 @@ SectionWithStrTab(ArrayRef Data) : Section(Data) {} void setStrTab(const SectionBase *StringTable) { StrTab = StringTable; } + void replaceSectionReferences(const SectionBase *Old, + const SectionBase *New) override; void removeSectionReferences(const SectionBase *Sec) override; void initialize(SectionTableRef SecTable) override; void finalize() override; @@ -369,6 +377,7 @@ virtual ~Object() = default; const SectionBase *getSectionHeaderStrTab() const { return SectionNames; } + void replaceWithNoBits(std::function ToReplace); void removeSections(std::function ToRemove); virtual size_t totalSize() const = 0; virtual void finalize() = 0; Index: tools/llvm-objcopy/Object.cpp =================================================================== --- tools/llvm-objcopy/Object.cpp +++ tools/llvm-objcopy/Object.cpp @@ -54,6 +54,8 @@ } void SectionBase::removeSectionReferences(const SectionBase *Sec) {} +void SectionBase::replaceSectionReferences(const SectionBase *Old, + const SectionBase *New) {} void SectionBase::initialize(SectionTableRef SecTable) {} void SectionBase::finalize() {} @@ -155,6 +157,20 @@ Size += this->EntrySize; } +void SymbolTableSection::replaceSectionReferences(const SectionBase *Old, + const SectionBase *New) { + if (SymbolNames == Old) { + error("String table " + SymbolNames->Name + + " cannot be replaced because it is referenced by " + "the symbol table " + + this->Name); + } + for (auto &Sym : Symbols) { + if (Sym->DefinedIn == Old) + Sym->DefinedIn = New; + } +} + void SymbolTableSection::removeSectionReferences(const SectionBase *Sec) { if (SymbolNames == Sec) { error("String table " + SymbolNames->Name + @@ -223,6 +239,17 @@ } } +template +void RelocSectionWithSymtabBase::replaceSectionReferences( + const SectionBase *Old, const SectionBase *New) { + if (Symbols == Old) { + error("Symbol table " + Symbols->Name + " cannot be replaced because it is " + "referenced by the relocation " + "section " + + this->Name); + } +} + template void RelocSectionWithSymtabBase::removeSectionReferences( const SectionBase *Sec) { @@ -292,6 +319,15 @@ Out.getBufferStart() + Offset); } +void SectionWithStrTab::replaceSectionReferences(const SectionBase *Old, + const SectionBase *New) { + if (StrTab == Old) { + error("String table " + StrTab->Name + " cannot be replaced because it is " + "referenced by the section " + + this->Name); + } +} + void SectionWithStrTab::removeSectionReferences(const SectionBase *Sec) { if (StrTab == Sec) { error("String table " + StrTab->Name + @@ -643,6 +679,39 @@ Section->writeSection(Out); } +template +void Object::replaceWithNoBits( + std::function ToRemove) { + auto Iter = + std::stable_partition(std::begin(Sections), std::end(Sections), + [=](const SecPtr &Sec) { return !ToRemove(*Sec); }); + + if (SymbolTable != nullptr && ToRemove(*SymbolTable)) + SymbolTable = nullptr; + if (ToRemove(*SectionNames)) { + if (WriteSectionHeaders) + error("Cannot replace " + SectionNames->Name + + " because it is the section header string table."); + SectionNames = nullptr; + } + for (auto &Sec : make_range(Iter, std::end(Sections))) { + auto Tmp = std::move(Sec); + Sec = llvm::make_unique
(ArrayRef{}); + Sec->Name = Tmp->Name; + Sec->Type = SHT_NOBITS; + Sec->Flags = Tmp->Flags; + Sec->Addr = Tmp->Addr; + Sec->Offset = Tmp->Offset; + Sec->OriginalOffset = Tmp->OriginalOffset; + Sec->Size = Tmp->Size; + Sec->Align = Tmp->Align; + Sec->EntrySize = Tmp->EntrySize; + for (auto &KeptSection : make_range(std::begin(Sections), Iter)) { + KeptSection->replaceSectionReferences(Tmp.get(), Sec.get()); + } + } +} + template void Object::removeSections( std::function ToRemove) { Index: tools/llvm-objcopy/llvm-objcopy.cpp =================================================================== --- tools/llvm-objcopy/llvm-objcopy.cpp +++ tools/llvm-objcopy/llvm-objcopy.cpp @@ -97,6 +97,10 @@ cl::desc("Remove all non-allocated sections")); static cl::opt StripDWO("strip-dwo", cl::desc("Remove all DWARF .dwo sections from file")); +static cl::opt OnlyKeepDebug( + "only-keep-debug", + cl::desc("Removes most of what is not removed by strip-debug. Section " + "headers of removed sections are kept as SHT_NOBITS sections")); static cl::opt ExtractDWO( "extract-dwo", cl::desc("Remove all sections that are not DWARF .dwo sections from file")); @@ -164,6 +168,12 @@ if (!SplitDWO.empty()) SplitDWOToFile(ObjFile, SplitDWO.getValue()); + // Replace sections before removing sections + if (OnlyKeepDebug) { + Obj->replaceWithNoBits( + [](const SectionBase &Sec) { return (Sec.Flags & SHF_ALLOC) != 0; }); + } + SectionPred RemovePred = [](const SectionBase &) { return false; }; if (!ToRemove.empty()) {