Skip to content

[llvm-objcopy][ELF] Add an option to remove notes #118739

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
24308b2
[llvm-objcopy][ELF] Add an option to remove notes
igorkudrin Nov 28, 2024
6533806
Merge branch 'main' into llvm-objcopy-remove-note
igorkudrin Dec 14, 2024
3550f3a
fixup: update parsing the argument
igorkudrin Dec 14, 2024
51b05f2
fixup: update tests
igorkudrin Dec 14, 2024
1bad501
fixup: add the option to the command guide
igorkudrin Dec 14, 2024
3b17fdb
fixup: fix updating offsets
igorkudrin Dec 20, 2024
1c84760
fixup: split 'removeNote()'
igorkudrin Dec 20, 2024
f6d4330
fixup: do not allow '--remove-note' with '--(remove|add|update)-section'
igorkudrin Dec 20, 2024
fd72505
fixup: change ELF type of the test file ET_CORE -> ET_EXEC
igorkudrin Dec 21, 2024
e730cfd
fixup: also run the test for ELF32 and BE
igorkudrin Dec 21, 2024
de67454
fixup: handle SHT_NOTE sections outside of segments
igorkudrin Dec 21, 2024
95b6b94
Remove handling for segments
igorkudrin Dec 23, 2024
9baf37a
Remove DeletedRange.NewPos
igorkudrin Jan 16, 2025
1432460
"auto &RemRange" -> "const DeletedRange &RemRange"
igorkudrin Jan 16, 2025
a4c8edb
Remove a blank line at the end of an namespace
igorkudrin Jan 16, 2025
5695096
Print a warning for note segments and note sections in segments
igorkudrin Jan 16, 2025
17a3c77
Extend the test
igorkudrin Jan 17, 2025
08f7f23
Revert "Print a warning for note segments and note sections in segments"
igorkudrin Jan 18, 2025
1ee4850
removeNote() -> removeNotes()
igorkudrin Jan 18, 2025
2860c31
Print a warning for note segments and note sections in segments v2
igorkudrin Jan 18, 2025
b1957e7
std::vector<SecPtr>::iterator -> SecPtr&
igorkudrin Jan 18, 2025
9110b2c
Move tests from 'remove-note-unsupported.test' to 'remove-note.test'
igorkudrin Jan 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions llvm/docs/CommandGuide/llvm-objcopy.rst
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,11 @@ them.

Preserve access and modification timestamps in the output.

.. option:: --remove-note [<name>/]<type>

Remove notes of integer type ``<type>`` and name ``<name>`` from SHT_NOTE
sections that are not in a segment. Can be specified multiple times.

.. option:: --rename-section <old>=<new>[,<flag>,...]

Rename sections called ``<old>`` to ``<new>`` in the output, and apply any
Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/ObjCopy/CommonConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,11 @@ struct CommonConfig {

SmallVector<std::pair<NameMatcher, llvm::DebugCompressionType>, 0>
compressSections;

// ErrorCallback is used to handle recoverable errors. An Error returned
// by the callback aborts the execution and is then returned to the caller.
// If the callback is not set, the errors are not issued.
std::function<Error(Error)> ErrorCallback;
};

} // namespace objcopy
Expand Down
9 changes: 9 additions & 0 deletions llvm/include/llvm/ObjCopy/ELF/ELFConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
namespace llvm {
namespace objcopy {

// Note to remove info specified by --remove-note option.
struct RemoveNoteInfo {
StringRef Name;
uint32_t TypeId;
};

// ELF specific configuration for copying/stripping a single file.
struct ELFConfig {
uint8_t NewSymbolVisibility = (uint8_t)ELF::STV_DEFAULT;
Expand All @@ -31,6 +37,9 @@ struct ELFConfig {
bool KeepFileSymbols = false;
bool LocalizeHidden = false;
bool VerifyNoteSections = true;

// Notes specified by --remove-note option.
SmallVector<RemoveNoteInfo, 0> NotesToRemove;
};

} // namespace objcopy
Expand Down
112 changes: 112 additions & 0 deletions llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,112 @@ static void addSymbol(Object &Obj, const NewSymbolInfo &SymInfo,
Sec ? (uint16_t)SYMBOL_SIMPLE_INDEX : (uint16_t)SHN_ABS, 0);
}

namespace {
struct RemoveNoteDetail {
struct DeletedRange {
uint64_t OldFrom;
uint64_t OldTo;
};

template <class ELFT>
static std::vector<DeletedRange>
findNotesToRemove(ArrayRef<uint8_t> Data, size_t Align,
ArrayRef<RemoveNoteInfo> NotesToRemove);
static std::vector<uint8_t> updateData(ArrayRef<uint8_t> OldData,
ArrayRef<DeletedRange> ToRemove);
};
} // namespace

template <class ELFT>
std::vector<RemoveNoteDetail::DeletedRange>
RemoveNoteDetail::findNotesToRemove(ArrayRef<uint8_t> Data, size_t Align,
ArrayRef<RemoveNoteInfo> NotesToRemove) {
LLVM_ELF_IMPORT_TYPES_ELFT(ELFT);
std::vector<DeletedRange> ToRemove;
uint64_t CurPos = 0;
while (CurPos + sizeof(Elf_Nhdr) <= Data.size()) {
auto Nhdr = reinterpret_cast<const Elf_Nhdr *>(Data.data() + CurPos);
size_t FullSize = Nhdr->getSize(Align);
if (CurPos + FullSize > Data.size())
break;
Elf_Note Note(*Nhdr);
bool ShouldRemove =
llvm::any_of(NotesToRemove, [&Note](const RemoveNoteInfo &NoteInfo) {
return NoteInfo.TypeId == Note.getType() &&
(NoteInfo.Name.empty() || NoteInfo.Name == Note.getName());
});
if (ShouldRemove)
ToRemove.push_back({CurPos, CurPos + FullSize});
CurPos += FullSize;
}
return ToRemove;
}

std::vector<uint8_t>
RemoveNoteDetail::updateData(ArrayRef<uint8_t> OldData,
ArrayRef<DeletedRange> ToRemove) {
std::vector<uint8_t> NewData;
NewData.reserve(OldData.size());
uint64_t CurPos = 0;
for (const DeletedRange &RemRange : ToRemove) {
if (CurPos < RemRange.OldFrom) {
auto Slice = OldData.slice(CurPos, RemRange.OldFrom - CurPos);
NewData.insert(NewData.end(), Slice.begin(), Slice.end());
}
CurPos = RemRange.OldTo;
}
if (CurPos < OldData.size()) {
auto Slice = OldData.slice(CurPos);
NewData.insert(NewData.end(), Slice.begin(), Slice.end());
}
return NewData;
}

static Error removeNotes(Object &Obj, endianness Endianness,
ArrayRef<RemoveNoteInfo> NotesToRemove,
function_ref<Error(Error)> ErrorCallback) {
// TODO: Support note segments.
if (ErrorCallback) {
for (Segment &Seg : Obj.segments()) {
if (Seg.Type == PT_NOTE) {
if (Error E = ErrorCallback(createStringError(
errc::not_supported, "note segments are not supported")))
return E;
break;
}
}
}
for (auto &Sec : Obj.sections()) {
if (Sec.Type != SHT_NOTE || !Sec.hasContents())
continue;
// TODO: Support note sections in segments.
if (Sec.ParentSegment) {
if (ErrorCallback)
if (Error E = ErrorCallback(createStringError(
errc::not_supported,
"cannot remove note(s) from " + Sec.Name +
": sections in segments are not supported")))
return E;
continue;
}
ArrayRef<uint8_t> OldData = Sec.getContents();
size_t Align = std::max<size_t>(4, Sec.Align);
// Note: notes for both 32-bit and 64-bit ELF files use 4-byte words in the
// header, so the parsers are the same.
auto ToRemove = (Endianness == endianness::little)
? RemoveNoteDetail::findNotesToRemove<ELF64LE>(
OldData, Align, NotesToRemove)
: RemoveNoteDetail::findNotesToRemove<ELF64BE>(
OldData, Align, NotesToRemove);
if (!ToRemove.empty()) {
if (Error E = Obj.updateSectionData(
Sec, RemoveNoteDetail::updateData(OldData, ToRemove)))
return E;
}
}
return Error::success();
}

static Error
handleUserSection(const NewSectionInfo &NewSection,
function_ref<Error(StringRef, ArrayRef<uint8_t>)> F) {
Expand Down Expand Up @@ -799,6 +905,12 @@ static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig,
? endianness::little
: endianness::big;

if (!ELFConfig.NotesToRemove.empty()) {
if (Error Err =
removeNotes(Obj, E, ELFConfig.NotesToRemove, Config.ErrorCallback))
return Err;
}

for (const NewSectionInfo &AddedSection : Config.AddSection) {
auto AddSection = [&](StringRef Name, ArrayRef<uint8_t> Data) -> Error {
OwnedDataSection &NewSection =
Expand Down
41 changes: 25 additions & 16 deletions llvm/lib/ObjCopy/ELF/ELFObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2154,37 +2154,46 @@ 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())
Error Object::updateSectionData(SecPtr &Sec, ArrayRef<uint8_t> Data) {
if (!Sec->hasContents())
return createStringError(
errc::invalid_argument,
"section '%s' cannot be updated because it does not have contents",
Name.str().c_str());
Sec->Name.c_str());

if (Data.size() > OldSec->Size && OldSec->ParentSegment)
if (Data.size() > Sec->Size && Sec->ParentSegment)
return createStringError(errc::invalid_argument,
"cannot fit data of size %zu into section '%s' "
"with size %" PRIu64 " that is part of a segment",
Data.size(), Name.str().c_str(), OldSec->Size);
Data.size(), Sec->Name.c_str(), Sec->Size);

if (!OldSec->ParentSegment) {
*It = std::make_unique<OwnedDataSection>(*OldSec, Data);
if (!Sec->ParentSegment) {
Sec = std::make_unique<OwnedDataSection>(*Sec, Data);
} else {
// The segment writer will be in charge of updating these contents.
OldSec->Size = Data.size();
UpdatedSections[OldSec] = Data;
Sec->Size = Data.size();
UpdatedSections[Sec.get()] = Data;
}

return Error::success();
}

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());
return updateSectionData(*It, Data);
}

Error Object::updateSectionData(SectionBase &S, ArrayRef<uint8_t> Data) {
auto It = llvm::find_if(Sections,
[&](const SecPtr &Sec) { return Sec.get() == &S; });
assert(It != Sections.end() && "The section should belong to the object");
return updateSectionData(*It, Data);
}

Error Object::removeSections(
bool AllowBrokenLinks, std::function<bool(const SectionBase &)> ToRemove) {

Expand Down
7 changes: 7 additions & 0 deletions llvm/lib/ObjCopy/ELF/ELFObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,7 @@ class SectionBase {
virtual void
replaceSectionReferences(const DenseMap<SectionBase *, SectionBase *> &);
virtual bool hasContents() const { return false; }
virtual ArrayRef<uint8_t> getContents() const { return {}; }
// Notify the section that it is subject to removal.
virtual void onRemove();

Expand Down Expand Up @@ -619,6 +620,8 @@ class Section : public SectionBase {
bool hasContents() const override {
return Type != ELF::SHT_NOBITS && Type != ELF::SHT_NULL;
}
ArrayRef<uint8_t> getContents() const override { return Contents; }

void restoreSymTabLink(SymbolTableSection &SymTab) override;
};

Expand Down Expand Up @@ -654,6 +657,7 @@ class OwnedDataSection : public SectionBase {
Error accept(SectionVisitor &Sec) const override;
Error accept(MutableSectionVisitor &Visitor) override;
bool hasContents() const override { return true; }
ArrayRef<uint8_t> getContents() const override { return Data; }
};

class CompressedSection : public SectionBase {
Expand Down Expand Up @@ -1164,6 +1168,8 @@ class Object {
return Sec.Flags & ELF::SHF_ALLOC;
};

Error updateSectionData(SecPtr &Sec, ArrayRef<uint8_t> Data);

public:
template <class T>
using ConstRange = iterator_range<pointee_iterator<
Expand Down Expand Up @@ -1206,6 +1212,7 @@ class Object {

const auto &getUpdatedSections() const { return UpdatedSections; }
Error updateSection(StringRef Name, ArrayRef<uint8_t> Data);
Error updateSectionData(SectionBase &S, ArrayRef<uint8_t> Data);

SectionBase *findSection(StringRef Name) {
auto SecIt =
Expand Down
Loading
Loading