aboutsummaryrefslogtreecommitdiff
path: root/llvm/tools/llvm-objcopy/ELF/Object.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/tools/llvm-objcopy/ELF/Object.cpp')
-rw-r--r--llvm/tools/llvm-objcopy/ELF/Object.cpp164
1 files changed, 125 insertions, 39 deletions
diff --git a/llvm/tools/llvm-objcopy/ELF/Object.cpp b/llvm/tools/llvm-objcopy/ELF/Object.cpp
index ba91d08e5540..3db5028e85f7 100644
--- a/llvm/tools/llvm-objcopy/ELF/Object.cpp
+++ b/llvm/tools/llvm-objcopy/ELF/Object.cpp
@@ -893,6 +893,17 @@ Error SymbolTableSection::accept(MutableSectionVisitor &Visitor) {
return Visitor.visit(*this);
}
+StringRef RelocationSectionBase::getNamePrefix() const {
+ switch (Type) {
+ case SHT_REL:
+ return ".rel";
+ case SHT_RELA:
+ return ".rela";
+ default:
+ llvm_unreachable("not a relocation section");
+ }
+}
+
Error RelocationSection::removeSectionReferences(
bool AllowBrokenLinks, function_ref<bool(const SectionBase *)> ToRemove) {
if (ToRemove(Symbols)) {
@@ -1342,13 +1353,16 @@ void IHexELFBuilder::addDataSections() {
if (R.HexData.empty())
continue;
RecAddr = R.Addr + SegmentAddr + BaseAddr;
- if (!Section || Section->Addr + Section->Size != RecAddr)
- // OriginalOffset field is only used to sort section properly, so
- // instead of keeping track of real offset in IHEX file, we use
- // section number.
+ if (!Section || Section->Addr + Section->Size != RecAddr) {
+ // OriginalOffset field is only used to sort sections before layout, so
+ // instead of keeping track of real offsets in IHEX file, and as
+ // layoutSections() and layoutSectionsForOnlyKeepDebug() use
+ // llvm::stable_sort(), we can just set it to a constant (zero).
Section = &Obj->addSection<OwnedDataSection>(
- ".sec" + std::to_string(SecNo++), RecAddr,
- ELF::SHF_ALLOC | ELF::SHF_WRITE, SecNo);
+ ".sec" + std::to_string(SecNo), RecAddr,
+ ELF::SHF_ALLOC | ELF::SHF_WRITE, 0);
+ SecNo++;
+ }
Section->appendHexData(R.HexData);
break;
case IHexRecord::EndOfFile:
@@ -2093,6 +2107,17 @@ template <class ELFT> void ELFWriter<ELFT>::writeSegmentData() {
Size);
}
+ for (auto it : Obj.getUpdatedSections()) {
+ SectionBase *Sec = it.first;
+ ArrayRef<uint8_t> Data = it.second;
+
+ auto *Parent = Sec->ParentSegment;
+ assert(Parent && "This section should've been part of a segment.");
+ uint64_t Offset =
+ Sec->OriginalOffset - Parent->OriginalOffset + Parent->Offset;
+ llvm::copy(Data, Buf->getBufferStart() + Offset);
+ }
+
// Iterate over removed sections and overwrite their old data with zeroes.
for (auto &Sec : Obj.removedSections()) {
Segment *Parent = Sec.ParentSegment;
@@ -2110,6 +2135,37 @@ ELFWriter<ELFT>::ELFWriter(Object &Obj, raw_ostream &Buf, bool WSH,
: Writer(Obj, Buf), WriteSectionHeaders(WSH && Obj.HadShdrs),
OnlyKeepDebug(OnlyKeepDebug) {}
+Error Object::updateSection(StringRef Name, ArrayRef<uint8_t> 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());
+
+ auto *OldSec = It->get();
+ if (!OldSec->hasContents())
+ return createStringError(
+ errc::invalid_argument,
+ "section '%s' can't be updated because it does not have contents",
+ Name.str().c_str());
+
+ if (Data.size() > OldSec->Size && OldSec->ParentSegment)
+ return createStringError(errc::invalid_argument,
+ "cannot fit data of size %zu into section '%s' "
+ "with size %zu that is part of a segment",
+ Data.size(), Name.str().c_str(), OldSec->Size);
+
+ if (!OldSec->ParentSegment) {
+ *It = std::make_unique<OwnedDataSection>(*OldSec, Data);
+ } else {
+ // The segment writer will be in charge of updating these contents.
+ OldSec->Size = Data.size();
+ UpdatedSections[OldSec] = Data;
+ }
+
+ return Error::success();
+}
+
Error Object::removeSections(
bool AllowBrokenLinks, std::function<bool(const SectionBase &)> ToRemove) {
@@ -2162,6 +2218,30 @@ Error Object::removeSections(
return Error::success();
}
+Error Object::replaceSections(
+ const DenseMap<SectionBase *, SectionBase *> &FromTo) {
+ auto SectionIndexLess = [](const SecPtr &Lhs, const SecPtr &Rhs) {
+ return Lhs->Index < Rhs->Index;
+ };
+ assert(llvm::is_sorted(Sections, SectionIndexLess) &&
+ "Sections are expected to be sorted by Index");
+ // Set indices of new sections so that they can be later sorted into positions
+ // of removed ones.
+ for (auto &I : FromTo)
+ I.second->Index = I.first->Index;
+
+ // Notify all sections about the replacement.
+ for (auto &Sec : Sections)
+ Sec->replaceSectionReferences(FromTo);
+
+ if (Error E = removeSections(
+ /*AllowBrokenLinks=*/false,
+ [=](const SectionBase &Sec) { return FromTo.count(&Sec) > 0; }))
+ return E;
+ llvm::sort(Sections, SectionIndexLess);
+ return Error::success();
+}
+
Error Object::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) {
if (SymbolTable)
for (const SecPtr &Sec : Sections)
@@ -2200,20 +2280,6 @@ Error Object::addNewSymbolTable() {
return Error::success();
}
-void Object::sortSections() {
- // Use stable_sort to maintain the original ordering as closely as possible.
- llvm::stable_sort(Sections, [](const SecPtr &A, const SecPtr &B) {
- // Put SHT_GROUP sections first, since group section headers must come
- // before the sections they contain. This also matches what GNU objcopy
- // does.
- if (A->Type != B->Type &&
- (A->Type == ELF::SHT_GROUP || B->Type == ELF::SHT_GROUP))
- return A->Type == ELF::SHT_GROUP;
- // For all other sections, sort by offset order.
- return A->OriginalOffset < B->OriginalOffset;
- });
-}
-
// Orders segments such that if x = y->ParentSegment then y comes before x.
static void orderSegments(std::vector<Segment *> &Segments) {
llvm::stable_sort(Segments, compareSegmentsByOffset);
@@ -2262,6 +2328,9 @@ static uint64_t layoutSections(Range Sections, uint64_t Offset) {
// the offset from the start of the segment. Using the offset from the start
// of the segment we can assign a new offset to the section. For sections not
// covered by segments we can just bump Offset to the next valid location.
+ // While it is not necessary, layout the sections in the order based on their
+ // original offsets to resemble the input file as close as possible.
+ std::vector<SectionBase *> OutOfSegmentSections;
uint32_t Index = 1;
for (auto &Sec : Sections) {
Sec.Index = Index++;
@@ -2269,12 +2338,19 @@ static uint64_t layoutSections(Range Sections, uint64_t Offset) {
auto Segment = *Sec.ParentSegment;
Sec.Offset =
Segment.Offset + (Sec.OriginalOffset - Segment.OriginalOffset);
- } else {
- Offset = alignTo(Offset, Sec.Align == 0 ? 1 : Sec.Align);
- Sec.Offset = Offset;
- if (Sec.Type != SHT_NOBITS)
- Offset += Sec.Size;
- }
+ } else
+ OutOfSegmentSections.push_back(&Sec);
+ }
+
+ llvm::stable_sort(OutOfSegmentSections,
+ [](const SectionBase *Lhs, const SectionBase *Rhs) {
+ return Lhs->OriginalOffset < Rhs->OriginalOffset;
+ });
+ for (auto *Sec : OutOfSegmentSections) {
+ Offset = alignTo(Offset, Sec->Align == 0 ? 1 : Sec->Align);
+ Sec->Offset = Offset;
+ if (Sec->Type != SHT_NOBITS)
+ Offset += Sec->Size;
}
return Offset;
}
@@ -2282,38 +2358,49 @@ static uint64_t layoutSections(Range Sections, uint64_t Offset) {
// Rewrite sh_offset after some sections are changed to SHT_NOBITS and thus
// occupy no space in the file.
static uint64_t layoutSectionsForOnlyKeepDebug(Object &Obj, uint64_t Off) {
+ // The layout algorithm requires the sections to be handled in the order of
+ // their offsets in the input file, at least inside segments.
+ std::vector<SectionBase *> Sections;
+ Sections.reserve(Obj.sections().size());
uint32_t Index = 1;
for (auto &Sec : Obj.sections()) {
Sec.Index = Index++;
-
- auto *FirstSec = Sec.ParentSegment && Sec.ParentSegment->Type == PT_LOAD
- ? Sec.ParentSegment->firstSection()
+ Sections.push_back(&Sec);
+ }
+ llvm::stable_sort(Sections,
+ [](const SectionBase *Lhs, const SectionBase *Rhs) {
+ return Lhs->OriginalOffset < Rhs->OriginalOffset;
+ });
+
+ for (auto *Sec : Sections) {
+ auto *FirstSec = Sec->ParentSegment && Sec->ParentSegment->Type == PT_LOAD
+ ? Sec->ParentSegment->firstSection()
: nullptr;
// The first section in a PT_LOAD has to have congruent offset and address
// modulo the alignment, which usually equals the maximum page size.
- if (FirstSec && FirstSec == &Sec)
- Off = alignTo(Off, Sec.ParentSegment->Align, Sec.Addr);
+ if (FirstSec && FirstSec == Sec)
+ Off = alignTo(Off, Sec->ParentSegment->Align, Sec->Addr);
// sh_offset is not significant for SHT_NOBITS sections, but the congruence
// rule must be followed if it is the first section in a PT_LOAD. Do not
// advance Off.
- if (Sec.Type == SHT_NOBITS) {
- Sec.Offset = Off;
+ if (Sec->Type == SHT_NOBITS) {
+ Sec->Offset = Off;
continue;
}
if (!FirstSec) {
// FirstSec being nullptr generally means that Sec does not have the
// SHF_ALLOC flag.
- Off = Sec.Align ? alignTo(Off, Sec.Align) : Off;
- } else if (FirstSec != &Sec) {
+ Off = Sec->Align ? alignTo(Off, Sec->Align) : Off;
+ } else if (FirstSec != Sec) {
// The offset is relative to the first section in the PT_LOAD segment. Use
// sh_offset for non-SHF_ALLOC sections.
- Off = Sec.OriginalOffset - FirstSec->OriginalOffset + FirstSec->Offset;
+ Off = Sec->OriginalOffset - FirstSec->OriginalOffset + FirstSec->Offset;
}
- Sec.Offset = Off;
- Off += Sec.Size;
+ Sec->Offset = Off;
+ Off += Sec->Size;
}
return Off;
}
@@ -2460,7 +2547,6 @@ template <class ELFT> Error ELFWriter<ELFT>::finalize() {
if (Error E = removeUnneededSections(Obj))
return E;
- Obj.sortSections();
// We need to assign indexes before we perform layout because we need to know
// if we need large indexes or not. We can assign indexes first and check as