Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 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
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
96 changes: 96 additions & 0 deletions llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,97 @@ 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;
uint64_t NewPos;
};

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;
uint64_t NewPos = 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, NewPos});
else
NewPos += 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 (auto &RemRange : ToRemove) {
if (CurPos < RemRange.OldFrom) {
auto Slice = OldData.slice(CurPos, RemRange.OldFrom - CurPos);
NewData.insert(NewData.end(), Slice.begin(), Slice.end());
}
assert(RemRange.NewPos == NewData.size());
CurPos = RemRange.OldTo;
}
if (CurPos < OldData.size()) {
auto Slice = OldData.slice(CurPos);
NewData.insert(NewData.end(), Slice.begin(), Slice.end());
}
return NewData;
}

static Error removeNote(Object &Obj, endianness Endianness,
ArrayRef<RemoveNoteInfo> NotesToRemove) {
for (auto &Sec : Obj.sections()) {
// TODO: Support note sections in segments
if (Sec.Type != SHT_NOTE || Sec.ParentSegment || !Sec.hasContents())
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 +890,11 @@ static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig,
? endianness::little
: endianness::big;

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

for (const NewSectionInfo &AddedSection : Config.AddSection) {
auto AddSection = [&](StringRef Name, ArrayRef<uint8_t> Data) -> Error {
OwnedDataSection &NewSection =
Expand Down
29 changes: 20 additions & 9 deletions llvm/lib/ObjCopy/ELF/ELFObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2154,25 +2154,20 @@ 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());

Error Object::updateSectionData(std::vector<SecPtr>::iterator It,
ArrayRef<uint8_t> Data) {
auto *OldSec = It->get();
if (!OldSec->hasContents())
return createStringError(
errc::invalid_argument,
"section '%s' cannot be updated because it does not have contents",
Name.str().c_str());
OldSec->Name.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 %" PRIu64 " that is part of a segment",
Data.size(), Name.str().c_str(), OldSec->Size);
Data.size(), OldSec->Name.c_str(), OldSec->Size);

if (!OldSec->ParentSegment) {
*It = std::make_unique<OwnedDataSection>(*OldSec, Data);
Expand All @@ -2185,6 +2180,22 @@ Error Object::updateSection(StringRef Name, ArrayRef<uint8_t> 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
8 changes: 8 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,9 @@ class Object {
return Sec.Flags & ELF::SHF_ALLOC;
};

Error updateSectionData(std::vector<SecPtr>::iterator SecIt,
ArrayRef<uint8_t> Data);

public:
template <class T>
using ConstRange = iterator_range<pointee_iterator<
Expand Down Expand Up @@ -1206,6 +1213,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
135 changes: 135 additions & 0 deletions llvm/test/tools/llvm-objcopy/ELF/remove-note.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
## Check incompatible options
# RUN: not llvm-objcopy --remove-note=1 --remove-section=.test - 2>&1 | FileCheck %s --check-prefix=ERR-REMSEC
# RUN: not llvm-objcopy --remove-note=1 --add-section=.test=%s - 2>&1 | FileCheck %s --check-prefix=ERR-ADDSEC
# RUN: not llvm-objcopy --remove-note=1 --update-section=.test=%s - 2>&1 | FileCheck %s --check-prefix=ERR-UPDSEC

# ERR-REMSEC: error: cannot specify both --remove-note and --remove-section
# ERR-ADDSEC: error: cannot specify both --remove-note and --add-section
# ERR-UPDSEC: error: cannot specify both --remove-note and --update-section

## Check invalid argument formats
# RUN: not llvm-objcopy --remove-note= - 2>&1 | FileCheck %s --check-prefix=ERR-NOTYPEID
# RUN: not llvm-objcopy --remove-note=CORE/ - 2>&1 | FileCheck %s --check-prefix=ERR-NOTYPEID
# RUN: not llvm-objcopy --remove-note=/1 - 2>&1 | FileCheck %s --check-prefix=ERR-EMPTYNAME
# RUN: not llvm-objcopy --remove-note=CORE/1/2 - 2>&1 | FileCheck %s --check-prefix=ERR-INVNUM1
# RUN: not llvm-objcopy --remove-note=Notanumber - 2>&1 | FileCheck %s --check-prefix=ERR-INVNUM2
# RUN: not llvm-objcopy --remove-note=CORE/Notanumber - 2>&1 | FileCheck %s --check-prefix=ERR-INVNUM2

# ERR-NOTYPEID: error: bad format for --remove-note, missing type_id
# ERR-EMPTYNAME: error: bad format for --remove-note, note name is empty
# ERR-INVNUM1: error: bad note type_id for --remove-note: '1/2'
# ERR-INVNUM2: error: bad note type_id for --remove-note: 'Notanumber'

# RUN: yaml2obj -D ALIGN=8 -D ELFCLASS=64 -D ENDIANNESS=LSB %s -o %t8.64.lsb
# RUN: llvm-objcopy --remove-note=0x01 --remove-note=DUMMY/2 --remove-note=CORE/0x03 %t8.64.lsb %t8.64.lsb.o
# RUN: llvm-readobj --segments --sections --notes %t8.64.lsb.o | \
# RUN: FileCheck %s -D#SIZE0=32 -D#SIZE1=32

# RUN: yaml2obj -D ALIGN=4 -D ELFCLASS=64 -D ENDIANNESS=MSB %s -o %t4.64.msb
# RUN: llvm-objcopy --remove-note=0x01 --remove-note=DUMMY/0x02 --remove-note=CORE/3 %t4.64.msb %t4.64.msb.o
# RUN: llvm-readobj --segments --sections --notes %t4.64.msb.o | \
# RUN: FileCheck %s -D#SIZE0=24 -D#SIZE1=24

# RUN: yaml2obj -D ALIGN=4 -D ELFCLASS=32 -D ENDIANNESS=LSB %s -o %t4.32.lsb
# RUN: llvm-objcopy --remove-note=1 --remove-note=DUMMY/0x02 --remove-note=CORE/3 %t4.32.lsb %t4.32.lsb.o
# RUN: llvm-readobj --segments --sections --notes %t4.32.lsb.o | \
# RUN: FileCheck %s -D#SIZE0=24 -D#SIZE1=24

# CHECK: Sections [
# CHECK: Section {
# CHECK: Name: .note0
# CHECK-NEXT: Type: SHT_NOTE
# CHECK-NEXT: Flags [
# CHECK-NEXT: ]
# CHECK-NEXT: Address:
# CHECK-NEXT: Offset:
# CHECK-NEXT: Size: [[#%d,SIZE0]]
# CHECK: Name: .note1
# CHECK-NEXT: Type: SHT_NOTE
# CHECK-NEXT: Flags [
# CHECK-NEXT: ]
# CHECK-NEXT: Address:
# CHECK-NEXT: Offset:
# CHECK-NEXT: Size: [[#%d,SIZE1]]
# CHECK: Name: .note2
# CHECK-NEXT: Type: SHT_NOTE
# CHECK-NEXT: Flags [
# CHECK-NEXT: ]
# CHECK-NEXT: Address:
# CHECK-NEXT: Offset:
# CHECK-NEXT: Size: 0

# CHECK: NoteSections [
# CHECK-NEXT: NoteSection {
# CHECK-NEXT: Name: .note0
# CHECK-NEXT: Offset:
# CHECK-NEXT: Size: 0x[[#%X,SIZE0]]
# CHECK-NEXT: Notes [
# CHECK-NEXT: {
# CHECK-NEXT: Owner: CORE
# CHECK-NEXT: Data size: 0x2
# CHECK-NEXT: Type: NT_ARCH
# CHECK-NEXT: Description data (
# CHECK-NEXT: 0000: 0202
# CHECK-NEXT: )
# CHECK-NEXT: }
# CHECK-NEXT: ]
# CHECK-NEXT: }
# CHECK-NEXT: NoteSection {
# CHECK-NEXT: Name: .note1
# CHECK-NEXT: Offset:
# CHECK-NEXT: Size: 0x[[#%X,SIZE1]]
# CHECK-NEXT: Notes [
# CHECK-NEXT: {
# CHECK-NEXT: Owner: CORE
# CHECK-NEXT: Data size: 0x2
# CHECK-NEXT: Type: Unknown (0x00000004)
# CHECK-NEXT: Description data (
# CHECK-NEXT: 0000: 0404
# CHECK-NEXT: )
# CHECK-NEXT: }
# CHECK-NEXT: ]
# CHECK-NEXT: }
# CHECK-NEXT: NoteSection {
# CHECK-NEXT: Name: .note2
# CHECK-NEXT: Offset:
# CHECK-NEXT: Size: 0x0
# CHECK-NEXT: Notes [
# CHECK-NEXT: ]
# CHECK-NEXT: }

--- !ELF
FileHeader:
Class: ELFCLASS[[ELFCLASS]]
Data: ELFDATA2[[ENDIANNESS]]
Type: ET_REL
Machine: EM_X86_64
Sections:
- Name: .note0
Type: SHT_NOTE
AddressAlign: [[ALIGN]]
Notes:
- Name: CORE
Type: 0x01
Desc: 0101
- Name: CORE
Type: 0x02
Desc: 0202
- Name: .note1
Type: SHT_NOTE
AddressAlign: [[ALIGN]]
Notes:
- Name: CORE
Type: 0x03
Desc: 0303
- Name: CORE
Type: 0x04
Desc: 0404
- Name: .note2
Type: SHT_NOTE
AddressAlign: [[ALIGN]]
Notes:
- Name: LINUX
Type: 0x01
Desc: 0505
...
Loading
Loading