[llvm-objcopy][ELF] Add an option to remove notes (#118739)
This adds an option `--remove-note=[name/]type` to selectively delete notes in ELF files, where `type` is the numeric value of the note type and `name` is the name of the originator. The name can be omitted, in which case all notes of the specified type will be removed. For now, only `SHT_NOTE` sections that are not associated with segments are handled. The implementation can be extended later as needed. RFC: https://discourse.llvm.org/t/rfc-llvm-objcopy-feature-for-editing-notes/83491
This commit is contained in:
@@ -477,6 +477,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
@@ -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 =
|
||||
|
||||
@@ -2154,35 +2154,44 @@ ELFWriter<ELFT>::ELFWriter(Object &Obj, raw_ostream &Buf, bool WSH,
|
||||
: Writer(Obj, Buf), WriteSectionHeaders(WSH && Obj.HadShdrs),
|
||||
OnlyKeepDebug(OnlyKeepDebug) {}
|
||||
|
||||
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",
|
||||
Sec->Name.c_str());
|
||||
|
||||
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(), Sec->Name.c_str(), Sec->Size);
|
||||
|
||||
if (!Sec->ParentSegment) {
|
||||
Sec = std::make_unique<OwnedDataSection>(*Sec, Data);
|
||||
} else {
|
||||
// The segment writer will be in charge of updating these contents.
|
||||
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);
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
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);
|
||||
|
||||
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::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(
|
||||
|
||||
@@ -549,6 +549,7 @@ public:
|
||||
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();
|
||||
|
||||
@@ -619,6 +620,8 @@ public:
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -654,6 +657,7 @@ public:
|
||||
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 {
|
||||
@@ -1164,6 +1168,8 @@ private:
|
||||
return Sec.Flags & ELF::SHF_ALLOC;
|
||||
};
|
||||
|
||||
Error updateSectionData(SecPtr &Sec, ArrayRef<uint8_t> Data);
|
||||
|
||||
public:
|
||||
template <class T>
|
||||
using ConstRange = iterator_range<pointee_iterator<
|
||||
@@ -1206,6 +1212,7 @@ public:
|
||||
|
||||
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 =
|
||||
|
||||
198
llvm/test/tools/llvm-objcopy/ELF/remove-note.test
Normal file
198
llvm/test/tools/llvm-objcopy/ELF/remove-note.test
Normal file
@@ -0,0 +1,198 @@
|
||||
## 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'
|
||||
|
||||
## Check deleting notes:
|
||||
## * --remove-note=1 will remove note "CORE/1" and "LINUX/1",
|
||||
## * --remove-note=DUMMY/2 will not remove any notes because there are no notes with this owner,
|
||||
## * --remove-note=CORE/3 will remove "CORE/3" but preserve "LINUX/3".
|
||||
# RUN: yaml2obj --docnum=1 -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=64
|
||||
|
||||
# RUN: yaml2obj --docnum=1 -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=48
|
||||
|
||||
# RUN: yaml2obj --docnum=1 -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=48
|
||||
|
||||
# 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: 0201
|
||||
# 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: LINUX
|
||||
# CHECK-NEXT: Data size: 0x2
|
||||
# CHECK-NEXT: Type: Unknown (0x00000003)
|
||||
# CHECK-NEXT: Description data (
|
||||
# CHECK-NEXT: 0000: 0301
|
||||
# CHECK-NEXT: )
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: Owner: CORE
|
||||
# CHECK-NEXT: Data size: 0x2
|
||||
# CHECK-NEXT: Type: Unknown (0x00000004)
|
||||
# CHECK-NEXT: Description data (
|
||||
# CHECK-NEXT: 0000: 0401
|
||||
# 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: 0201
|
||||
- Name: .note1
|
||||
Type: SHT_NOTE
|
||||
AddressAlign: [[ALIGN]]
|
||||
Notes:
|
||||
- Name: LINUX
|
||||
Type: 0x03
|
||||
Desc: 0301
|
||||
- Name: CORE
|
||||
Type: 0x03
|
||||
Desc: 0302
|
||||
- Name: CORE
|
||||
Type: 0x04
|
||||
Desc: 0401
|
||||
- Name: .note2
|
||||
Type: SHT_NOTE
|
||||
AddressAlign: [[ALIGN]]
|
||||
Notes:
|
||||
- Name: LINUX
|
||||
Type: 0x01
|
||||
Desc: 0102
|
||||
|
||||
# RUN: yaml2obj --docnum=2 %s -o %t2
|
||||
# RUN: llvm-objcopy --remove-note=1 %t2 %t2o 2>&1 | FileCheck %s --check-prefix=TEST2
|
||||
# TEST2: warning: note segments are not supported
|
||||
# TEST2-NOT: note segments are not supported
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS64
|
||||
Data: ELFDATA2LSB
|
||||
Type: ET_CORE
|
||||
Machine: EM_X86_64
|
||||
ProgramHeaders:
|
||||
- Type: PT_NOTE
|
||||
FirstSec: .data0
|
||||
LastSec: .data0
|
||||
- Type: PT_NOTE
|
||||
FirstSec: .data1
|
||||
LastSec: .data1
|
||||
Sections:
|
||||
- Name: .data0
|
||||
Type: Fill
|
||||
Size: 8
|
||||
- Name: .data1
|
||||
Type: Fill
|
||||
Size: 8
|
||||
|
||||
# RUN: yaml2obj --docnum=3 %s -o %t3
|
||||
# RUN: llvm-objcopy --remove-note=1 %t3 %t3o 2>&1 | FileCheck %s --check-prefix=TEST3
|
||||
# TEST3: warning: cannot remove note(s) from .note: sections in segments are not supported
|
||||
|
||||
--- !ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS64
|
||||
Data: ELFDATA2LSB
|
||||
Type: ET_EXEC
|
||||
Machine: EM_X86_64
|
||||
ProgramHeaders:
|
||||
- Type: PT_LOAD
|
||||
FirstSec: .note
|
||||
LastSec: .note
|
||||
Sections:
|
||||
- Name: .note
|
||||
Type: SHT_NOTE
|
||||
AddressAlign: 4
|
||||
Notes:
|
||||
- Name: ABC
|
||||
Type: 1
|
||||
Desc: 0102
|
||||
@@ -538,6 +538,38 @@ static Expected<NewSymbolInfo> parseNewSymbolInfo(StringRef FlagValue) {
|
||||
return SI;
|
||||
}
|
||||
|
||||
static Expected<RemoveNoteInfo> parseRemoveNoteInfo(StringRef FlagValue) {
|
||||
// Parse value given with --remove-note option. The format is:
|
||||
//
|
||||
// [name/]type_id
|
||||
//
|
||||
// where:
|
||||
// <name> - optional note name. If not given, all notes with the specified
|
||||
// <type_id> are removed.
|
||||
// <type_id> - note type value, can be decimal or hexadecimal number prefixed
|
||||
// with 0x.
|
||||
RemoveNoteInfo NI;
|
||||
StringRef TypeIdStr;
|
||||
if (auto Idx = FlagValue.find('/'); Idx != StringRef::npos) {
|
||||
if (Idx == 0)
|
||||
return createStringError(
|
||||
errc::invalid_argument,
|
||||
"bad format for --remove-note, note name is empty");
|
||||
NI.Name = FlagValue.slice(0, Idx);
|
||||
TypeIdStr = FlagValue.substr(Idx + 1);
|
||||
} else {
|
||||
TypeIdStr = FlagValue;
|
||||
}
|
||||
if (TypeIdStr.empty())
|
||||
return createStringError(errc::invalid_argument,
|
||||
"bad format for --remove-note, missing type_id");
|
||||
if (TypeIdStr.getAsInteger(0, NI.TypeId))
|
||||
return createStringError(errc::invalid_argument,
|
||||
"bad note type_id for --remove-note: '%s'",
|
||||
TypeIdStr.str().c_str());
|
||||
return NI;
|
||||
}
|
||||
|
||||
// Parse input option \p ArgValue and load section data. This function
|
||||
// extracts section name and name of the file keeping section data from
|
||||
// ArgValue, loads data from the file, and stores section name and data
|
||||
@@ -1221,6 +1253,29 @@ objcopy::parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
|
||||
};
|
||||
}
|
||||
|
||||
for (auto *Arg : InputArgs.filtered(OBJCOPY_remove_note)) {
|
||||
Expected<RemoveNoteInfo> NoteInfo = parseRemoveNoteInfo(Arg->getValue());
|
||||
if (!NoteInfo)
|
||||
return NoteInfo.takeError();
|
||||
|
||||
ELFConfig.NotesToRemove.push_back(*NoteInfo);
|
||||
}
|
||||
|
||||
if (!ELFConfig.NotesToRemove.empty()) {
|
||||
if (!Config.ToRemove.empty())
|
||||
return createStringError(
|
||||
errc::invalid_argument,
|
||||
"cannot specify both --remove-note and --remove-section");
|
||||
if (!Config.AddSection.empty())
|
||||
return createStringError(
|
||||
errc::invalid_argument,
|
||||
"cannot specify both --remove-note and --add-section");
|
||||
if (!Config.UpdateSection.empty())
|
||||
return createStringError(
|
||||
errc::invalid_argument,
|
||||
"cannot specify both --remove-note and --update-section");
|
||||
}
|
||||
|
||||
if (Config.DecompressDebugSections &&
|
||||
Config.CompressionType != DebugCompressionType::None) {
|
||||
return createStringError(
|
||||
|
||||
@@ -297,3 +297,7 @@ defm pad_to
|
||||
"of zero or the value specified by the --gap-fill option. "
|
||||
"This option is only supported for ELF input and binary output">,
|
||||
MetaVarName<"address">;
|
||||
|
||||
defm remove_note
|
||||
: Eq<"remove-note", "Remove note(s) with <type_id> and optional <name>">,
|
||||
MetaVarName<"[name/]type_id">;
|
||||
|
||||
@@ -248,6 +248,8 @@ int llvm_objcopy_main(int argc, char **argv, const llvm::ToolContext &) {
|
||||
return 1;
|
||||
}
|
||||
for (ConfigManager &ConfigMgr : DriverConfig->CopyConfigs) {
|
||||
assert(!ConfigMgr.Common.ErrorCallback);
|
||||
ConfigMgr.Common.ErrorCallback = reportWarning;
|
||||
if (Error E = executeObjcopy(ConfigMgr)) {
|
||||
logAllUnhandledErrors(std::move(E), WithColor::error(errs(), ToolName));
|
||||
return 1;
|
||||
|
||||
Reference in New Issue
Block a user