Index: test/tools/llvm-objcopy/only-keep-debug-phdrs.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/only-keep-debug-phdrs.test @@ -0,0 +1,133 @@ +# RUN: yaml2obj %s > %t +# RUN: llvm-objcopy -only-keep-debug %t %t2 +# RUN: llvm-readobj -file-headers -sections -symbols -program-headers %t2 | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .debugfoo + Type: SHT_PROGBITS + Content: "00000000" + - Name: .blarg + Type: SHT_PROGBITS + - Name: .readonly + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Content: "00000000" + - Name: .note + Type: SHT_NOTE + Flags: [ SHF_ALLOC ] + Content: "00000000" + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: "00000000" +Symbols: + Global: + - Name: foo + Section: .text + - Name: debugfoo + Section: .debugfoo +ProgramHeaders: + - Type: PT_LOAD + Flags: [PF_X, PF_R] + VAddr: 0x1000 + PAddr: 0x1000 + Sections: + - Section: .text + - Type: PT_LOAD + Flags: [PF_R] + VAddr: 0x2000 + PAddr: 0x2000 + Sections: + - Section: .readonly + - Section: .note + - Type: PT_NOTE + Flags: [PF_R] + VAddr: 0x2000 + PAddr: 0x2000 + Sections: + - Section: .note + +# CHECK: SectionHeaderCount: 9 + +# CHECK: Name: .debugfoo +# CHECK: Name: .blarg +# CHECK: Name: .readonly +# CHECK-NEXT: Type: SHT_NOBITS +# CHECK: Name: .note +# CHECK: Name: .text +# CHECK-NEXT: Type: SHT_NOBITS +# CHECK: Name: .symtab +# CHECK: Name: .strtab +# CHECK: Name: .shstrtab + +# Check that both symbols are copied +# CHECK: Symbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: +# CHECK-NEXT: Type: +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: Undefined +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: foo +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: .text +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: debugfoo +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: .debugfoo +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# CHECK: ProgramHeader { +# CHECK-NEXT: Type: PT_LOAD +# CHECK-NEXT: Offset +# CHECK-NEXT: Virtual +# CHECK-NEXT: Physical +# CHECK-NEXT: FileSize: 0 +# CHECK-NEXT: MemSize: 4 +# CHECK-NEXT: Flags [ +# CHECK-NEXT: PF_R +# CHECK-NEXT: PF_X +# CHECK-NEXT: ] + +# CHECK: ProgramHeader { +# CHECK-NEXT: Type: PT_LOAD +# CHECK-NEXT: Offset +# CHECK-NEXT: Virtual +# CHECK-NEXT: Physical +# CHECK-NEXT: FileSize: 0 +# CHECK-NEXT: MemSize: 8 +# CHECK-NEXT: Flags [ +# CHECK-NEXT: PF_R +# CHECK-NEXT: ] + +# CHECK: ProgramHeader { +# CHECK-NEXT: Type: PT_NOTE +# CHECK-NEXT: Offset +# CHECK-NEXT: Virtual +# CHECK-NEXT: Physical +# CHECK-NEXT: FileSize: 0 +# CHECK-NEXT: MemSize: 4 +# CHECK-NEXT: Flags [ +# CHECK-NEXT: PF_R +# CHECK-NEXT: ] Index: test/tools/llvm-objcopy/only-keep-debug.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/only-keep-debug.test @@ -0,0 +1,77 @@ +# RUN: yaml2obj %s > %t +# RUN: llvm-objcopy -only-keep-debug %t %t2 +# RUN: llvm-readobj -file-headers -sections -symbols %t2 | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .debugfoo + Type: SHT_PROGBITS + Content: "00000000" + - Name: .note + Type: SHT_NOTE + - Name: .blarg + Type: SHT_PROGBITS + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Content: "00000000" + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: "00000000" +Symbols: + Global: + - Name: foo + Section: .text + - Name: debugfoo + Section: .debugfoo + +# CHECK: SectionHeaderCount: 9 + +# CHECK: Name: .debugfoo +# CHECK: Name: .note +# CHECK: Name: .blarg +# CHECK: Name: .data +# CHECK-NEXT: Type: SHT_NOBITS +# CHECK: Name: .text +# CHECK-NEXT: Type: SHT_NOBITS +# CHECK: Name: .symtab +# CHECK: Name: .strtab +# CHECK: Name: .shstrtab + +# Check that both symbols are copied +# CHECK: Symbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: +# CHECK-NEXT: Type: +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: Undefined +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: foo +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: .text +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: debugfoo +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: .debugfoo +# CHECK-NEXT: } +# CHECK-NEXT: ] Index: test/tools/llvm-objcopy/replace-strtab-ref-in-dynamic-error.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/replace-strtab-ref-in-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-ref-in-dynsym-error.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/replace-strtab-ref-in-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-ref-in-symtab.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/replace-text-ref-in-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; @@ -198,6 +200,8 @@ void addSymbolNames(); const SectionBase *getStrTab() const { return SymbolNames; } 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; @@ -253,6 +257,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; @@ -288,6 +294,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; @@ -371,6 +379,7 @@ const SymbolTableSection *getSymTab() const { return SymbolTable; } 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,11 +157,25 @@ 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 " + + 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 + " cannot be removed because it is referenced by the symbol table " + - this->Name); + Name); } auto Iter = std::remove_if(std::begin(Symbols), std::end(Symbols), @@ -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,12 +319,21 @@ 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 " + + Name); + } +} + 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); + Name); } } @@ -643,6 +679,58 @@ Section->writeSection(Out); } +template +void Object::replaceWithNoBits( + std::function ToReplace) { + if (SymbolTable != nullptr && ToReplace(*SymbolTable)) + SymbolTable = nullptr; + if (SectionNames != nullptr && ToReplace(*SectionNames)) { + if (WriteSectionHeaders) + error("Cannot replace " + SectionNames->Name + + " because it is the section header string table."); + SectionNames = nullptr; + } + // It's not necessarily safe to just change the section type so instead we + // make whole new section and delete the old way. This means removing any + // remaining references and replacing them with the new one as well. + for (auto &Sec : Sections) { + if (!ToReplace(*Sec)) + continue; + // Save the current pointer. + auto Tmp = std::move(Sec); + // Make a new empty section for the nobits section. + Sec = llvm::make_unique
(ArrayRef{}); + // Copy in all the original information. + 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; + // NOBITS sections don't affect layout and don't logically need to be in + // the segment. We remove the old section from the segment. + for (auto &Seg : Segments) + Seg->removeSection(Tmp.get()); + // Before we let Tmp go out of scope we need to remove existing references + // to it. + for (auto &KeptSection : Sections) { + if (ToReplace(*KeptSection)) + continue; + KeptSection->replaceSectionReferences(Tmp.get(), Sec.get()); + } + } + // Now all PT_LOAD segments and segments with PT_LOAD parent segments can + // have their FileSize set to zero. + for (auto &Seg : Segments) { + if (Seg->Type == PT_LOAD || + (Seg->ParentSegment && Seg->ParentSegment->Type == PT_LOAD)) + Seg->FileSize = 0; + } +} + 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 @@ -104,6 +104,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")); @@ -178,6 +182,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; }; // Removes: