summaryrefslogtreecommitdiffstats
path: root/llvm/tools/llvm-objcopy
diff options
context:
space:
mode:
authorFangrui Song <maskray@google.com>2019-10-24 15:48:32 -0700
committerFangrui Song <maskray@google.com>2019-11-05 08:56:15 -0800
commit5ad0103d8a04cb066dfae4fc20b0dfcd9413f4d4 (patch)
tree65dcbcfccb35bf5353a1bfac0b665bbf7421e1e4 /llvm/tools/llvm-objcopy
parentade55d07871040d0e75b94e3d3a1eaecbd704d36 (diff)
downloadbcm5719-llvm-5ad0103d8a04cb066dfae4fc20b0dfcd9413f4d4.tar.gz
bcm5719-llvm-5ad0103d8a04cb066dfae4fc20b0dfcd9413f4d4.zip
[llvm-objcopy][ELF] Implement --only-keep-debug
--only-keep-debug produces a debug file as the output that only preserves contents of sections useful for debugging purposes (the binutils implementation preserves SHT_NOTE and non-SHF_ALLOC sections), by changing their section types to SHT_NOBITS and rewritting file offsets. See https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html The intended use case is: ``` llvm-objcopy --only-keep-debug a a.dbg llvm-objcopy --strip-debug a b llvm-objcopy --add-gnu-debuglink=a.dbg b ``` The current layout algorithm is incapable of deleting contents and shrinking segments, so it is not suitable for implementing the functionality. This patch adds a new algorithm which assigns sh_offset to sections first, then modifies p_offset/p_filesz of program headers. It bears a resemblance to lld/ELF/Writer.cpp. Reviewed By: jhenderson, jakehehrlich Differential Revision: https://reviews.llvm.org/D67137
Diffstat (limited to 'llvm/tools/llvm-objcopy')
-rw-r--r--llvm/tools/llvm-objcopy/CommonOpts.td5
-rw-r--r--llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp21
-rw-r--r--llvm/tools/llvm-objcopy/ELF/Object.cpp109
-rw-r--r--llvm/tools/llvm-objcopy/ELF/Object.h9
4 files changed, 119 insertions, 25 deletions
diff --git a/llvm/tools/llvm-objcopy/CommonOpts.td b/llvm/tools/llvm-objcopy/CommonOpts.td
index ea3616589f5..6481d1d1df0 100644
--- a/llvm/tools/llvm-objcopy/CommonOpts.td
+++ b/llvm/tools/llvm-objcopy/CommonOpts.td
@@ -86,8 +86,9 @@ def keep_file_symbols : Flag<["--"], "keep-file-symbols">,
def only_keep_debug
: Flag<["--"], "only-keep-debug">,
- HelpText<"Clear sections that would not be stripped by --strip-debug. "
- "Currently only implemented for COFF.">;
+ HelpText<
+ "Produce a debug file as the output that only preserves contents of "
+ "sections useful for debugging purposes">;
def discard_locals : Flag<["--"], "discard-locals">,
HelpText<"Remove compiler-generated local symbols, (e.g. "
diff --git a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
index 1bfabda4a1d..e5892448033 100644
--- a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
+++ b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp
@@ -136,17 +136,17 @@ static std::unique_ptr<Writer> createELFWriter(const CopyConfig &Config,
// Depending on the initial ELFT and OutputFormat we need a different Writer.
switch (OutputElfType) {
case ELFT_ELF32LE:
- return std::make_unique<ELFWriter<ELF32LE>>(Obj, Buf,
- !Config.StripSections);
+ return std::make_unique<ELFWriter<ELF32LE>>(Obj, Buf, !Config.StripSections,
+ Config.OnlyKeepDebug);
case ELFT_ELF64LE:
- return std::make_unique<ELFWriter<ELF64LE>>(Obj, Buf,
- !Config.StripSections);
+ return std::make_unique<ELFWriter<ELF64LE>>(Obj, Buf, !Config.StripSections,
+ Config.OnlyKeepDebug);
case ELFT_ELF32BE:
- return std::make_unique<ELFWriter<ELF32BE>>(Obj, Buf,
- !Config.StripSections);
+ return std::make_unique<ELFWriter<ELF32BE>>(Obj, Buf, !Config.StripSections,
+ Config.OnlyKeepDebug);
case ELFT_ELF64BE:
- return std::make_unique<ELFWriter<ELF64BE>>(Obj, Buf,
- !Config.StripSections);
+ return std::make_unique<ELFWriter<ELF64BE>>(Obj, Buf, !Config.StripSections,
+ Config.OnlyKeepDebug);
}
llvm_unreachable("Invalid output format");
}
@@ -694,6 +694,11 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj,
}
}
+ if (Config.OnlyKeepDebug)
+ for (auto &Sec : Obj.sections())
+ if (Sec.Flags & SHF_ALLOC && Sec.Type != SHT_NOTE)
+ Sec.Type = SHT_NOBITS;
+
for (const auto &Flag : Config.AddSection) {
std::pair<StringRef, StringRef> SecPair = Flag.split("=");
StringRef SecName = SecPair.first;
diff --git a/llvm/tools/llvm-objcopy/ELF/Object.cpp b/llvm/tools/llvm-objcopy/ELF/Object.cpp
index 4798c8fed6e..dc2c7c8fa8b 100644
--- a/llvm/tools/llvm-objcopy/ELF/Object.cpp
+++ b/llvm/tools/llvm-objcopy/ELF/Object.cpp
@@ -1797,10 +1797,9 @@ template <class ELFT> void ELFWriter<ELFT>::writeSectionData() {
template <class ELFT> void ELFWriter<ELFT>::writeSegmentData() {
for (Segment &Seg : Obj.segments()) {
- uint8_t *B = Buf.getBufferStart() + Seg.Offset;
- assert(Seg.FileSize == Seg.getContents().size() &&
- "Segment size must match contents size");
- std::memcpy(B, Seg.getContents().data(), Seg.FileSize);
+ size_t Size = std::min<size_t>(Seg.FileSize, Seg.getContents().size());
+ std::memcpy(Buf.getBufferStart() + Seg.Offset, Seg.getContents().data(),
+ Size);
}
// Iterate over removed sections and overwrite their old data with zeroes.
@@ -1815,8 +1814,10 @@ template <class ELFT> void ELFWriter<ELFT>::writeSegmentData() {
}
template <class ELFT>
-ELFWriter<ELFT>::ELFWriter(Object &Obj, Buffer &Buf, bool WSH)
- : Writer(Obj, Buf), WriteSectionHeaders(WSH && Obj.HadShdrs) {}
+ELFWriter<ELFT>::ELFWriter(Object &Obj, Buffer &Buf, bool WSH,
+ bool OnlyKeepDebug)
+ : Writer(Obj, Buf), WriteSectionHeaders(WSH && Obj.HadShdrs),
+ OnlyKeepDebug(OnlyKeepDebug) {}
Error Object::removeSections(bool AllowBrokenLinks,
std::function<bool(const SectionBase &)> ToRemove) {
@@ -1957,6 +1958,78 @@ static uint64_t layoutSections(Range Sections, uint64_t Offset) {
return 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) {
+ uint32_t Index = 1;
+ for (auto &Sec : Obj.sections()) {
+ Sec.Index = Index++;
+
+ 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);
+
+ // 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;
+ 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) {
+ // 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;
+ }
+ Sec.Offset = Off;
+ Off += Sec.Size;
+ }
+ return Off;
+}
+
+// Rewrite p_offset and p_filesz of non-empty non-PT_PHDR segments after
+// sh_offset values have been updated.
+static uint64_t layoutSegmentsForOnlyKeepDebug(std::vector<Segment *> &Segments,
+ uint64_t HdrEnd) {
+ uint64_t MaxOffset = 0;
+ for (Segment *Seg : Segments) {
+ const SectionBase *Sec = Seg->firstSection();
+ if (Seg->Type == PT_PHDR || !Sec)
+ continue;
+
+ uint64_t Offset = Sec->Offset;
+ uint64_t FileSize = 0;
+ for (const SectionBase *Sec : Seg->Sections) {
+ uint64_t Size = Sec->Type == SHT_NOBITS ? 0 : Sec->Size;
+ if (Sec->Offset + Size > Offset)
+ FileSize = std::max(FileSize, Sec->Offset + Size - Offset);
+ }
+
+ // If the segment includes EHDR and program headers, don't make it smaller
+ // than the headers.
+ if (Seg->Offset < HdrEnd && HdrEnd <= Seg->Offset + Seg->FileSize) {
+ FileSize += Offset - Seg->Offset;
+ Offset = Seg->Offset;
+ FileSize = std::max(FileSize, HdrEnd - Offset);
+ }
+
+ Seg->Offset = Offset;
+ Seg->FileSize = FileSize;
+ MaxOffset = std::max(MaxOffset, Offset + FileSize);
+ }
+ return MaxOffset;
+}
+
template <class ELFT> void ELFWriter<ELFT>::initEhdrSegment() {
Segment &ElfHdr = Obj.ElfHdrSegment;
ElfHdr.Type = PT_PHDR;
@@ -1977,12 +2050,24 @@ template <class ELFT> void ELFWriter<ELFT>::assignOffsets() {
OrderedSegments.push_back(&Obj.ElfHdrSegment);
OrderedSegments.push_back(&Obj.ProgramHdrSegment);
orderSegments(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.
- uint64_t Offset = 0;
- Offset = layoutSegments(OrderedSegments, Offset);
- Offset = layoutSections(Obj.sections(), Offset);
+
+ uint64_t Offset;
+ if (OnlyKeepDebug) {
+ // For --only-keep-debug, the sections that did not preserve contents were
+ // changed to SHT_NOBITS. We now rewrite sh_offset fields of sections, and
+ // then rewrite p_offset/p_filesz of program headers.
+ uint64_t HdrEnd =
+ sizeof(Elf_Ehdr) + llvm::size(Obj.segments()) * sizeof(Elf_Phdr);
+ Offset = layoutSectionsForOnlyKeepDebug(Obj, HdrEnd);
+ Offset = std::max(Offset,
+ layoutSegmentsForOnlyKeepDebug(OrderedSegments, HdrEnd));
+ } else {
+ // 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.
+ Offset = layoutSegments(OrderedSegments, 0);
+ Offset = layoutSections(Obj.sections(), Offset);
+ }
// If we need to write the section header table out then we need to align the
// Offset so that SHOffset is valid.
if (WriteSectionHeaders)
diff --git a/llvm/tools/llvm-objcopy/ELF/Object.h b/llvm/tools/llvm-objcopy/ELF/Object.h
index 70cfc92921a..5fd58fecb7f 100644
--- a/llvm/tools/llvm-objcopy/ELF/Object.h
+++ b/llvm/tools/llvm-objcopy/ELF/Object.h
@@ -342,9 +342,13 @@ public:
virtual ~ELFWriter() {}
bool WriteSectionHeaders;
+ // For --only-keep-debug, select an alternative section/segment layout
+ // algorithm.
+ bool OnlyKeepDebug;
+
Error finalize() override;
Error write() override;
- ELFWriter(Object &Obj, Buffer &Buf, bool WSH);
+ ELFWriter(Object &Obj, Buffer &Buf, bool WSH, bool OnlyKeepDebug);
};
class BinaryWriter : public Writer {
@@ -435,8 +439,6 @@ private:
}
};
- std::set<const SectionBase *, SectionCompare> Sections;
-
public:
uint32_t Type;
uint32_t Flags;
@@ -451,6 +453,7 @@ public:
uint64_t OriginalOffset;
Segment *ParentSegment = nullptr;
ArrayRef<uint8_t> Contents;
+ std::set<const SectionBase *, SectionCompare> Sections;
explicit Segment(ArrayRef<uint8_t> Data) : Contents(Data) {}
Segment() {}
OpenPOWER on IntegriCloud