Index: test/tools/llvm-objcopy/ELF/update-section.test =================================================================== --- test/tools/llvm-objcopy/ELF/update-section.test +++ test/tools/llvm-objcopy/ELF/update-section.test @@ -0,0 +1,163 @@ +# RUN: yaml2obj %s -o %t + +# Update segment section with a larger chunk of data. We should recalculate offsets of +# all following sections, including ones residing in another segments. We also should +# update segment offset when neeeded. We don't touch section addresses, because this +# doesn't make sense neither for relocatable object nor for final image. +# RUN: echo -n 1111222233334444 > %t.text +# RUN: llvm-objcopy --update-section=.text=%t.text %t %t2 +# RUN: llvm-readobj --section-headers --section-data --program-headers %t2 | FileCheck %s --check-prefix=LONG + +# Update to original contents after another update should give us the +# original result. +# RUN: echo -n 00000000 > %t2.text +# RUN: llvm-objcopy %t %t3 +# RUN: llvm-objcopy --update-section=.text=%t.text --update-section=.text=%t2.text %t %t4 +# RUN: cmp %t3 %t4 + +# Update segment section with a smaller chunk of data. Offsets are not recalculated in this case. +# RUN: echo -n 00 > %t3.text +# RUN: llvm-objcopy --update-section=.text=%t3.text %t %t5 +# RUN: llvm-readobj --section-headers --section-data --program-headers %t5 | FileCheck %s --check-prefix=SHORT + +# We can't update sections which don't exist +# RUN: not llvm-objcopy --update-section=.nosection=%t.text %t3 %t-err1 2>&1 | FileCheck %s --check-prefix=ERR1 + +# We can't update certain types of sections +# RUN: not llvm-objcopy --update-section=.symtab=%t.text %t3 %t-err2 2>&1 | FileCheck %s --check-prefix=ERR2 +# RUN: not llvm-objcopy --update-section=.bss=%t.text %t3 %t-err2 2>&1 | FileCheck %s --check-prefix=ERR3 + +!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: 0x8 + Content: "3030303030303030" + - Name: .init + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Content: "00000000" + Address: 0x1008 + AddressAlign: 0x8 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Content: "3030303030303030" + Address: 0x1010 + AddressAlign: 0x8 + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_ALLOC ] + Size: 0x8 + Address: 0x1018 + AddressAlign: 0x8 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X, PF_R ] + VAddr: 0x1000 + PAddr: 0x1000 + Align: 0x1000 + Sections: + - Section: .text + - Section: .init + - Type: PT_LOAD + Flags: [ PF_R ] + VAddr: 0x2010 + PAddr: 0x2010 + Align: 0x1000 + Sections: + - Section: .data + - Section: .bss + +# LONG: Name: .text +# LONG: Address: 0x1000 +# LONG: Offset: 0x1000 +# LONG: Size: 16 +# LONG: |1111222233334444| +# LONG: Name: .init +# LONG: Address: 0x1008 +# LONG: Offset: 0x1010 +# LONG: Size: 4 +# LONG: Name: .data +# LONG: Address: 0x1010 +# LONG: Offset: 0x2010 +# LONG: Size: 8 +# LONG: ProgramHeaders [ +# LONG-NEXT: ProgramHeader { +# LONG-NEXT: Type: PT_LOAD (0x1) +# LONG-NEXT: Offset: 0x1000 +# LONG-NEXT: VirtualAddress: 0x1000 +# LONG-NEXT: PhysicalAddress: 0x1000 +# LONG-NEXT: FileSize: 20 +# LONG-NEXT: MemSize: 20 +# LONG-NEXT: Flags [ (0x5) +# LONG-NEXT: PF_R (0x4) +# LONG-NEXT: PF_X (0x1) +# LONG-NEXT: ] +# LONG-NEXT: Alignment: 4096 +# LONG-NEXT: } +# LONG-NEXT: ProgramHeader { +# LONG-NEXT: Type: PT_LOAD (0x1) +# LONG-NEXT: Offset: 0x2010 +# LONG-NEXT: VirtualAddress: 0x2010 +# LONG-NEXT: PhysicalAddress: 0x2010 +# LONG-NEXT: FileSize: 8 +# LONG-NEXT: MemSize: 16 +# LONG-NEXT: Flags [ (0x4) +# LONG-NEXT: PF_R (0x4) +# LONG-NEXT: ] +# LONG-NEXT: Alignment: 4096 +# LONG-NEXT: } +# LONG-NEXT: ] + +# SHORT: Name: .text +# SHORT: Address: 0x1000 +# SHORT: Offset: 0x1000 +# SHORT: Size: 2 +# SHORT: |00| +# SHORT: Name: .init +# SHORT: Address: 0x1008 +# SHORT: Offset: 0x1008 +# SHORT: Size: 4 +# SHORT: Name: .data +# SHORT: Address: 0x1010 +# SHORT: Offset: 0x1010 +# SHORT: Size: 8 +# SHORT: ProgramHeaders [ +# SHORT-NEXT: ProgramHeader { +# SHORT-NEXT: Type: PT_LOAD (0x1) +# SHORT-NEXT: Offset: 0x1000 +# SHORT-NEXT: VirtualAddress: 0x1000 +# SHORT-NEXT: PhysicalAddress: 0x1000 +# SHORT-NEXT: FileSize: 12 +# SHORT-NEXT: MemSize: 12 +# SHORT-NEXT: Flags [ (0x5) +# SHORT-NEXT: PF_R (0x4) +# SHORT-NEXT: PF_X (0x1) +# SHORT-NEXT: ] +# SHORT-NEXT: Alignment: 4096 +# SHORT-NEXT: } +# SHORT-NEXT: ProgramHeader { +# SHORT-NEXT: Type: PT_LOAD (0x1) +# SHORT-NEXT: Offset: 0x1010 +# SHORT-NEXT: VirtualAddress: 0x2010 +# SHORT-NEXT: PhysicalAddress: 0x2010 +# SHORT-NEXT: FileSize: 8 +# SHORT-NEXT: MemSize: 16 +# SHORT-NEXT: Flags [ (0x4) +# SHORT-NEXT: PF_R (0x4) +# SHORT-NEXT: ] +# SHORT-NEXT: Alignment: 4096 +# SHORT-NEXT: } +# SHORT-NEXT: ] + +# ERR1: error: Section '.nosection' not found +# ERR2: error: Section '.symtab' can't be updated +# ERR3: error: Section '.bss' can't be updated Index: tools/llvm-objcopy/CopyConfig.h =================================================================== --- tools/llvm-objcopy/CopyConfig.h +++ tools/llvm-objcopy/CopyConfig.h @@ -95,6 +95,7 @@ // Repeated options std::vector AddSection; + std::vector UpdateSection; std::vector DumpSection; std::vector SymbolsToAdd; std::vector KeepSection; Index: tools/llvm-objcopy/CopyConfig.cpp =================================================================== --- tools/llvm-objcopy/CopyConfig.cpp +++ tools/llvm-objcopy/CopyConfig.cpp @@ -557,6 +557,8 @@ Config.OnlySection.emplace_back(Arg->getValue(), UseRegex); for (auto Arg : InputArgs.filtered(OBJCOPY_add_section)) Config.AddSection.push_back(Arg->getValue()); + for (auto Arg : InputArgs.filtered(OBJCOPY_update_section)) + Config.UpdateSection.push_back(Arg->getValue()); for (auto Arg : InputArgs.filtered(OBJCOPY_dump_section)) Config.DumpSection.push_back(Arg->getValue()); Config.StripAll = InputArgs.hasArg(OBJCOPY_strip_all); Index: tools/llvm-objcopy/ELF/ELFObjcopy.cpp =================================================================== --- tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -262,6 +262,22 @@ Sym.Type != STT_FILE && Sym.Type != STT_SECTION; } +static Error +handleUserSection(StringRef Flag, + std::function)> F) { + std::pair SecPair = Flag.split("="); + StringRef SecName = SecPair.first; + StringRef File = SecPair.second; + ErrorOr> BufOrErr = MemoryBuffer::getFile(File); + if (!BufOrErr) + return createFileError(File, errorCodeToError(BufOrErr.getError())); + std::unique_ptr Buf = std::move(*BufOrErr); + ArrayRef Data( + reinterpret_cast(Buf->getBufferStart()), + Buf->getBufferSize()); + return F(SecName, Data); +} + // This function handles the high level operations of GNU objcopy including // handling command line options. It's important to outline certain properties // we expect to hold of the command line operations. Any operation that "keeps" @@ -522,21 +538,23 @@ } for (const auto &Flag : Config.AddSection) { - std::pair SecPair = Flag.split("="); - StringRef SecName = SecPair.first; - StringRef File = SecPair.second; - ErrorOr> BufOrErr = - MemoryBuffer::getFile(File); - if (!BufOrErr) - return createFileError(File, errorCodeToError(BufOrErr.getError())); - std::unique_ptr Buf = std::move(*BufOrErr); - ArrayRef Data( - reinterpret_cast(Buf->getBufferStart()), - Buf->getBufferSize()); - OwnedDataSection &NewSection = - Obj.addSection(SecName, Data); - if (SecName.startswith(".note") && SecName != ".note.GNU-stack") - NewSection.Type = SHT_NOTE; + auto AddSection = [&](StringRef Name, ArrayRef Data) { + OwnedDataSection &NewSection = + Obj.addSection(Name, Data); + if (Name.startswith(".note") && Name != ".note.GNU-stack") + NewSection.Type = SHT_NOTE; + return Error::success(); + }; + if (Error E = handleUserSection(Flag, AddSection)) + return E; + } + + for (const auto &Flag : Config.UpdateSection) { + auto UpdateSection = [&](StringRef Name, ArrayRef Data) { + return Obj.updateSection(Name, Data); + }; + if (Error E = handleUserSection(Flag, UpdateSection)) + return E; } for (const auto &Flag : Config.DumpSection) { Index: tools/llvm-objcopy/ELF/Object.h =================================================================== --- tools/llvm-objcopy/ELF/Object.h +++ tools/llvm-objcopy/ELF/Object.h @@ -282,6 +282,7 @@ virtual void markSymbols(); virtual void replaceSectionReferences(const DenseMap &); + virtual bool isReplaceable() const { return false; } }; class Segment { @@ -297,7 +298,7 @@ } }; - std::set Sections; + std::set Sections; public: uint32_t Type; @@ -314,13 +315,16 @@ Segment *ParentSegment = nullptr; const SectionBase *firstSection() const { - if (!Sections.empty()) - return *Sections.begin(); - return nullptr; + return Sections.empty() ? nullptr : *Sections.begin(); } - void removeSection(const SectionBase *Sec) { Sections.erase(Sec); } - void addSection(const SectionBase *Sec) { Sections.insert(Sec); } + const SectionBase *lastSection() const { + return Sections.empty() ? nullptr : *(--Sections.end()); + } + + void fixupSectionOffsets(); + void removeSection(SectionBase *Sec) { Sections.erase(Sec); } + void addSection(SectionBase *Sec) { Sections.insert(Sec); } }; class Section : public SectionBase { @@ -338,6 +342,7 @@ function_ref ToRemove) override; void initialize(SectionTableRef SecTable) override; void finalize() override; + bool isReplaceable() const override { return Type != ELF::SHT_NOBITS; } }; class OwnedDataSection : public SectionBase { @@ -354,8 +359,14 @@ OriginalOffset = std::numeric_limits::max(); } + OwnedDataSection(SectionBase &S, ArrayRef Data) + : SectionBase(S), Data(std::begin(Data), std::end(Data)) { + Size = Data.size(); + } + void accept(SectionVisitor &Sec) const override; void accept(MutableSectionVisitor &Visitor) override; + bool isReplaceable() const override { return true; } }; class CompressedSection : public SectionBase { @@ -827,6 +838,7 @@ Segments.emplace_back(llvm::make_unique()); return *Segments.back(); } + Error updateSection(StringRef Name, ArrayRef Data); }; } // end namespace elf Index: tools/llvm-objcopy/ELF/Object.cpp =================================================================== --- tools/llvm-objcopy/ELF/Object.cpp +++ tools/llvm-objcopy/ELF/Object.cpp @@ -778,8 +778,9 @@ // segments and ensures that the section "belongs" to the second segment and // not the first. uint64_t SecSize = Section.Size ? Section.Size : 1; - return Segment.Offset <= Section.OriginalOffset && - Segment.Offset + Segment.FileSize >= Section.OriginalOffset + SecSize; + return (Section.Flags & ELF::SHF_ALLOC) && + Segment.Offset <= Section.OriginalOffset && + Segment.Offset + Segment.MemSize >= Section.OriginalOffset + SecSize; } // Returns true IFF a segment's original offset is inside of another segment's @@ -1411,6 +1412,27 @@ CompareSections); } +Error Object::updateSection(StringRef Name, ArrayRef Data) { + auto It = llvm::find_if(Sections, + [&](const SecPtr &Sec) { return Sec->Name == Name; }); + if (It == Sections.end()) + return createStringError(errc::invalid_argument, "Section '%s' not found.", + Name.str().c_str()); + if (!(*It)->isReplaceable()) + return createStringError(errc::invalid_argument, + "Section '%s' can't be updated.", + Name.str().c_str()); + + auto *OldSec = It->get(); + *It = llvm::make_unique(*OldSec, Data); + // Update segment section list if needed. + if (auto *Seg = (*It)->ParentSegment) { + Seg->removeSection(OldSec); + Seg->addSection(It->get()); + } + return Error::success(); +} + static uint64_t alignToAddr(uint64_t Offset, uint64_t Addr, uint64_t Align) { // Calculate Diff such that (Offset + Diff) & -Align == Addr & -Align. if (Align == 0) @@ -1460,6 +1482,40 @@ return Offset; } +// We could have inserted a section of larger size when processing +// --update-sections. In such case we should update section offsets within a +// segment. Offset of first section in a segment will be calculated later in +// layoutSections based on segment virtual address and previous segment size. +void Segment::fixupSectionOffsets() { + SectionBase *Prev = nullptr; + for (auto *Sec : Sections) { + if (Prev == nullptr) { + Prev = Sec; + continue; + } + uint64_t PrevSize = Prev->Type == ELF::SHT_NOBITS ? 0 : Prev->Size; + Sec->Offset = std::max(Sec->Offset, alignTo(Prev->Offset + PrevSize, + Sec->Align ? Sec->Align : 1)); + Prev = Sec; + } +} + +// Fixup segments in case --update-section caused section to be replaced with +// a section of larger size. In such case we have to update both section offsets +// and segment sizes. +template static void fixupSegments(Range &Segments) { + for (auto &Seg : Segments) { + Seg->fixupSectionOffsets(); + if (const SectionBase *LastSec = Seg->lastSection()) { + Seg->MemSize = + std::max(Seg->MemSize, LastSec->Offset + LastSec->Size - Seg->Offset); + Seg->FileSize = std::max(Seg->FileSize, LastSec->Type == SHT_NOBITS + ? Seg->MemSize - LastSec->Size + : Seg->MemSize); + } + } +} + // This function finds a consistent layout for a list of sections. It assumes // that the ->ParentSegment of each section has already been laid out. The // supplied starting Offset is used for the starting offset of any section that @@ -1480,7 +1536,7 @@ if (Section.ParentSegment != nullptr) { auto Segment = *Section.ParentSegment; Section.Offset = - Segment.Offset + (Section.OriginalOffset - Segment.OriginalOffset); + Segment.Offset + (Section.Offset - Segment.OriginalOffset); } else { Offset = alignTo(Offset, Section.Align == 0 ? 1 : Section.Align); Section.Offset = Offset; @@ -1512,6 +1568,8 @@ OrderedSegments.push_back(&Obj.ElfHdrSegment); OrderedSegments.push_back(&Obj.ProgramHdrSegment); orderSegments(OrderedSegments); + // Fix segment sizes and section offsets within segment. + fixupSegments(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. Index: tools/llvm-objcopy/ObjcopyOpts.td =================================================================== --- tools/llvm-objcopy/ObjcopyOpts.td +++ tools/llvm-objcopy/ObjcopyOpts.td @@ -255,3 +255,7 @@ "compatibility: debug, constructor, warning, indirect, synthetic, " "unique-object, before.">, MetaVarName<"name=[section:]value[,flags]">; + +defm update_section + : Eq<"update-section", "Add section with contents from a file .">, + MetaVarName<"name=file">;