Skip to content

Commit fb5a38b

Browse files
[llvm-objcopy] Add verification of added .note section format
Also add a --no-verify-note-sections flag to make it possible to add invalid sections if needs be. Pull Request: #90458
1 parent 0431c61 commit fb5a38b

File tree

7 files changed

+142
-6
lines changed

7 files changed

+142
-6
lines changed

llvm/docs/CommandGuide/llvm-objcopy.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,10 @@ them.
426426

427427
The default is `default`.
428428

429+
.. option:: --no-verify-note-sections
430+
431+
When adding note sections, do not verify if the section format is valid.
432+
429433
.. option:: --output-target <format>, -O
430434

431435
Write the output as the specified format. See `SUPPORTED FORMATS`_ for a list
@@ -516,6 +520,11 @@ them.
516520
specified format. See `SUPPORTED FORMATS`_ for a list of valid ``<format>``
517521
values.
518522

523+
.. option:: --verify-note-sections
524+
525+
When adding note sections, verify if the section format is valid. On by
526+
default.
527+
519528
.. option:: --weaken-symbol <symbol>, -W
520529

521530
Mark global symbols named ``<symbol>`` as weak symbols in the output. Can

llvm/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,10 @@ Changes to the LLVM tools
379379
jumping in reverse direction with shift+L/R/B). (`#95662
380380
<https://github.com/llvm/llvm-project/pull/95662>`_).
381381

382+
* llvm-objcopy now verifies format of ``.note`` sections for ELF input. This can
383+
be disabled by ``--no-verify-note-sections``. (`#90458
384+
<https://github.com/llvm/llvm-project/pull/90458>`).
385+
382386
Changes to LLDB
383387
---------------------------------
384388

llvm/include/llvm/ObjCopy/ELF/ELFConfig.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ struct ELFConfig {
3030
bool AllowBrokenLinks = false;
3131
bool KeepFileSymbols = false;
3232
bool LocalizeHidden = false;
33+
bool VerifyNoteSections = true;
3334
};
3435

3536
} // namespace objcopy

llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,54 @@ handleUserSection(const NewSectionInfo &NewSection,
623623
return F(NewSection.SectionName, Data);
624624
}
625625

626+
static Error verifyNoteSection(StringRef Name, endianness Endianness,
627+
ArrayRef<uint8_t> Data) {
628+
// An ELF note has the following structure:
629+
// Name Size: 4 bytes (integer)
630+
// Desc Size: 4 bytes (integer)
631+
// Type : 4 bytes
632+
// Name : variable size, padded to a 4 byte boundary
633+
// Desc : variable size, padded to a 4 byte boundary
634+
635+
if (Data.empty())
636+
return Error::success();
637+
638+
if (Data.size() < 12) {
639+
std::string msg;
640+
raw_string_ostream(msg)
641+
<< Name << " data must be either empty or at least 12 bytes long";
642+
return createStringError(errc::invalid_argument, msg);
643+
}
644+
if (Data.size() % 4 != 0) {
645+
std::string msg;
646+
raw_string_ostream(msg)
647+
<< Name << " data size must be a multiple of 4 bytes";
648+
return createStringError(errc::invalid_argument, msg);
649+
}
650+
ArrayRef<uint8_t> NameSize = Data.slice(0, 4);
651+
ArrayRef<uint8_t> DescSize = Data.slice(4, 4);
652+
653+
uint32_t NameSizeValue = support::endian::read32(NameSize.data(), Endianness);
654+
uint32_t DescSizeValue = support::endian::read32(DescSize.data(), Endianness);
655+
656+
uint64_t ExpectedDataSize =
657+
/*NameSize=*/4 + /*DescSize=*/4 + /*Type=*/4 +
658+
/*Name=*/alignTo(NameSizeValue, 4) +
659+
/*Desc=*/alignTo(DescSizeValue, 4);
660+
uint64_t ActualDataSize = Data.size();
661+
if (ActualDataSize != ExpectedDataSize) {
662+
std::string msg;
663+
raw_string_ostream(msg)
664+
<< Name
665+
<< " data size is incompatible with the content of "
666+
"the name and description size fields:"
667+
<< " expecting " << ExpectedDataSize << ", found " << ActualDataSize;
668+
return createStringError(errc::invalid_argument, msg);
669+
}
670+
671+
return Error::success();
672+
}
673+
626674
// This function handles the high level operations of GNU objcopy including
627675
// handling command line options. It's important to outline certain properties
628676
// we expect to hold of the command line operations. Any operation that "keeps"
@@ -631,7 +679,7 @@ handleUserSection(const NewSectionInfo &NewSection,
631679
// depend a) on the order the options occur in or b) on some opaque priority
632680
// system. The only priority is that keeps/copies overrule removes.
633681
static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig,
634-
Object &Obj) {
682+
ElfType OutputElfType, Object &Obj) {
635683
if (Config.OutputArch) {
636684
Obj.Machine = Config.OutputArch->EMachine;
637685
Obj.OSABI = Config.OutputArch->OSABI;
@@ -702,12 +750,19 @@ static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig,
702750
if (Sec.Flags & SHF_ALLOC && Sec.Type != SHT_NOTE)
703751
Sec.Type = SHT_NOBITS;
704752

753+
endianness E = OutputElfType == ELFT_ELF32LE || OutputElfType == ELFT_ELF64LE
754+
? endianness::little
755+
: endianness::big;
756+
705757
for (const NewSectionInfo &AddedSection : Config.AddSection) {
706-
auto AddSection = [&](StringRef Name, ArrayRef<uint8_t> Data) {
758+
auto AddSection = [&](StringRef Name, ArrayRef<uint8_t> Data) -> Error {
707759
OwnedDataSection &NewSection =
708760
Obj.addSection<OwnedDataSection>(Name, Data);
709-
if (Name.starts_with(".note") && Name != ".note.GNU-stack")
761+
if (Name.starts_with(".note") && Name != ".note.GNU-stack") {
710762
NewSection.Type = SHT_NOTE;
763+
if (ELFConfig.VerifyNoteSections)
764+
return verifyNoteSection(Name, E, Data);
765+
}
711766
return Error::success();
712767
};
713768
if (Error E = handleUserSection(AddedSection, AddSection))
@@ -840,7 +895,7 @@ Error objcopy::elf::executeObjcopyOnIHex(const CommonConfig &Config,
840895

841896
const ElfType OutputElfType =
842897
getOutputElfType(Config.OutputArch.value_or(MachineInfo()));
843-
if (Error E = handleArgs(Config, ELFConfig, **Obj))
898+
if (Error E = handleArgs(Config, ELFConfig, OutputElfType, **Obj))
844899
return E;
845900
return writeOutput(Config, **Obj, Out, OutputElfType);
846901
}
@@ -858,7 +913,7 @@ Error objcopy::elf::executeObjcopyOnRawBinary(const CommonConfig &Config,
858913
// (-B<arch>).
859914
const ElfType OutputElfType =
860915
getOutputElfType(Config.OutputArch.value_or(MachineInfo()));
861-
if (Error E = handleArgs(Config, ELFConfig, **Obj))
916+
if (Error E = handleArgs(Config, ELFConfig, OutputElfType, **Obj))
862917
return E;
863918
return writeOutput(Config, **Obj, Out, OutputElfType);
864919
}
@@ -877,7 +932,7 @@ Error objcopy::elf::executeObjcopyOnBinary(const CommonConfig &Config,
877932
? getOutputElfType(*Config.OutputArch)
878933
: getOutputElfType(In);
879934

880-
if (Error E = handleArgs(Config, ELFConfig, **Obj))
935+
if (Error E = handleArgs(Config, ELFConfig, OutputElfType, **Obj))
881936
return createFileError(Config.InputFilename, std::move(E));
882937

883938
if (Error E = writeOutput(Config, **Obj, Out, OutputElfType))
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
## Verify that --add-section warns on invalid note sections.
2+
3+
## Add [namesz, descsz, type, name, desc] for a build id.
4+
5+
## Notes should be padded to 8 bytes.
6+
# RUN: printf "\x04\x00\x00\x00" > %t-miss-padding-note.bin
7+
# RUN: printf "\x07\x00\x00\x00" >> %t-miss-padding-note.bin
8+
# RUN: printf "\x03\x00\x00\x00" >> %t-miss-padding-note.bin
9+
# RUN: printf "GNU\x00" >> %t-miss-padding-note.bin
10+
# RUN: printf "\x0c\x0d\x0e" >> %t-miss-padding-note.bin
11+
12+
## The namesz field bit is incorrect.
13+
# RUN: printf "\x08\x00\x00\x00" > %t-invalid-size-note.bin
14+
# RUN: printf "\x07\x00\x00\x00" >> %t-invalid-size-note.bin
15+
# RUN: printf "\x03\x00\x00\x00" >> %t-invalid-size-note.bin
16+
# RUN: printf "GNU\x00" >> %t-invalid-size-note.bin
17+
# RUN: printf "\x0c\x0d\x0e\x00" >> %t-invalid-size-note.bin
18+
19+
## Missing type field.
20+
# RUN: printf "\x08\x00\x00\x00" > %t-short-note.bin
21+
# RUN: printf "\x07\x00\x00\x00" >> %t-short-note.bin
22+
23+
# RUN: yaml2obj %s -o %t.o
24+
25+
## Test each invalid note.
26+
# RUN: not llvm-objcopy --add-section=.note.miss.padding=%t-miss-padding-note.bin %t.o %t-with-note.o 2>&1 | FileCheck --check-prefix=CHECK-MISS-PADDING %s
27+
# CHECK-MISS-PADDING: .note.miss.padding data size must be a multiple of 4 bytes
28+
29+
# RUN: not llvm-objcopy --add-section=.note.invalid.size=%t-invalid-size-note.bin %t.o %t-with-note.o 2>&1 | FileCheck --check-prefix=CHECK-INVALID-SIZE %s
30+
# CHECK-INVALID-SIZE: .note.invalid.size data size is incompatible with the content of the name and description size fields: expecting 28, found 20
31+
32+
# RUN: not llvm-objcopy --add-section=.note.short=%t-short-note.bin %t.o %t-with-note.o 2>&1 | FileCheck --check-prefix=CHECK-SHORT %s
33+
# CHECK-SHORT: .note.short data must be either empty or at least 12 bytes long
34+
35+
## Test compatibility with .note.gnu.property, which has 8-byte alignment.
36+
# RUN: printf "\x04\x00\x00\x00\x40\x00\x00\x00\x05\x00\x00\x00\x47\x4e\x55\x00" > %t-note-gnu-property.bin
37+
# RUN: printf "\x02\x00\x00\xc0\x04\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00" >> %t-note-gnu-property.bin
38+
# RUN: printf "\x01\x80\x01\xc0\x04\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" >> %t-note-gnu-property.bin
39+
# RUN: printf "\x01\x00\x01\xc0\x04\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00" >> %t-note-gnu-property.bin
40+
# RUN: printf "\x02\x00\x01\xc0\x04\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" >> %t-note-gnu-property.bin
41+
# RUN: llvm-objcopy --add-section=.note.gnu.property=%t-note-gnu-property.bin %t.o %t-with-note-gnu-property.o
42+
43+
## Test that verification can be disabled.
44+
# RUN: llvm-objcopy --add-section=.note.short=%t-short-note.bin --no-verify-note-sections %t.o %t-with-note.o
45+
46+
## Test that last argument has precedence.
47+
# RUN: llvm-objcopy --add-section=.note.short=%t-short-note.bin --verify-note-sections --no-verify-note-sections %t.o %t-with-note.o
48+
# RUN: not llvm-objcopy --add-section=.note.short=%t-short-note.bin --no-verify-note-sections --verify-note-sections %t.o %t-with-note.o 2>&1 | FileCheck --check-prefix=CHECK-SHORT %s
49+
50+
!ELF
51+
FileHeader:
52+
Class: ELFCLASS64
53+
Data: ELFDATA2LSB
54+
Type: ET_REL

llvm/tools/llvm-objcopy/ObjcopyOptions.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -990,6 +990,10 @@ objcopy::parseObjcopyOptions(ArrayRef<const char *> RawArgsArr,
990990
? DiscardType::All
991991
: DiscardType::Locals;
992992
}
993+
994+
ELFConfig.VerifyNoteSections = InputArgs.hasFlag(
995+
OBJCOPY_verify_note_sections, OBJCOPY_no_verify_note_sections, true);
996+
993997
Config.OnlyKeepDebug = InputArgs.hasArg(OBJCOPY_only_keep_debug);
994998
ELFConfig.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols);
995999
MachOConfig.KeepUndefined = InputArgs.hasArg(OBJCOPY_keep_undefined);

llvm/tools/llvm-objcopy/ObjcopyOpts.td

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
include "CommonOpts.td"
22

3+
multiclass B<string name, string help1, string help2> {
4+
def NAME: Flag<["--"], name>, HelpText<help1>;
5+
def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>;
6+
}
7+
38
defm binary_architecture
49
: Eq<"binary-architecture", "Ignored for compatibility">;
510
def B : JoinedOrSeparate<["-"], "B">,
@@ -176,6 +181,10 @@ def G : JoinedOrSeparate<["-"], "G">,
176181
Alias<keep_global_symbol>,
177182
HelpText<"Alias for --keep-global-symbol">;
178183

184+
defm verify_note_sections : B<"verify-note-sections",
185+
"Validate note sections",
186+
"Don't validate note sections">;
187+
179188
defm keep_global_symbols
180189
: Eq<"keep-global-symbols",
181190
"Read a list of symbols from <filename> and run as if "

0 commit comments

Comments
 (0)