From 1f3a427da8fc486726ed160996a366f657944e99 Mon Sep 17 00:00:00 2001 From: Djordje Todorovic Date: Fri, 6 Sep 2024 12:29:08 +0200 Subject: [PATCH] [llvm-objcopy][ELF] Disable huge section offset By default, GNU objcopy issues a warning for large offsets in ELF files. To align with this behavior, we introduce an option to define a maximum file offset for a section. This allows us to skip creating excessively large files by specifying a threshold beyond which the file offset will trigger an error. --- llvm/docs/CommandGuide/llvm-objcopy.rst | 5 ++++ llvm/include/llvm/ObjCopy/ELF/ELFConfig.h | 1 + llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp | 15 ++++++------ llvm/lib/ObjCopy/ELF/ELFObject.cpp | 9 ++++++++ llvm/lib/ObjCopy/ELF/ELFObject.h | 9 ++++++-- .../llvm-objcopy/ELF/max-section-offset.yaml | 23 +++++++++++++++++++ llvm/tools/llvm-objcopy/ObjcopyOptions.cpp | 5 ++++ llvm/tools/llvm-objcopy/ObjcopyOpts.td | 6 +++++ 8 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 llvm/test/tools/llvm-objcopy/ELF/max-section-offset.yaml diff --git a/llvm/docs/CommandGuide/llvm-objcopy.rst b/llvm/docs/CommandGuide/llvm-objcopy.rst index 8894a7fea6bb75..967a116d629530 100644 --- a/llvm/docs/CommandGuide/llvm-objcopy.rst +++ b/llvm/docs/CommandGuide/llvm-objcopy.rst @@ -389,6 +389,11 @@ them. anything following a '#'. Can be specified multiple times to read names from multiple files. +.. option:: --max-section-offset + + Emit an error if input section has a file offset greater than the specified + ````. + .. option:: --new-symbol-visibility Specify the visibility of the symbols automatically created when using binary diff --git a/llvm/include/llvm/ObjCopy/ELF/ELFConfig.h b/llvm/include/llvm/ObjCopy/ELF/ELFConfig.h index 1e98b10919af11..824e8ba6143764 100644 --- a/llvm/include/llvm/ObjCopy/ELF/ELFConfig.h +++ b/llvm/include/llvm/ObjCopy/ELF/ELFConfig.h @@ -29,6 +29,7 @@ struct ELFConfig { bool AllowBrokenLinks = false; bool KeepFileSymbols = false; bool LocalizeHidden = false; + std::optional MaxSectionOffset; }; } // namespace objcopy diff --git a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp index 70bf97b1b8482c..65eefabde9c192 100644 --- a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp +++ b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp @@ -160,11 +160,12 @@ static std::unique_ptr createELFWriter(const CommonConfig &Config, } static std::unique_ptr createWriter(const CommonConfig &Config, + const ELFConfig &ELFConfig, Object &Obj, raw_ostream &Out, ElfType OutputElfType) { switch (Config.OutputFormat) { case FileFormat::Binary: - return std::make_unique(Obj, Out); + return std::make_unique(Obj, Out, ELFConfig); case FileFormat::IHex: return std::make_unique(Obj, Out); default: @@ -765,10 +766,10 @@ static Error handleArgs(const CommonConfig &Config, const ELFConfig &ELFConfig, return Error::success(); } -static Error writeOutput(const CommonConfig &Config, Object &Obj, - raw_ostream &Out, ElfType OutputElfType) { +static Error writeOutput(const CommonConfig &Config, const ELFConfig &ELFConfig, + Object &Obj, raw_ostream &Out, ElfType OutputElfType) { std::unique_ptr Writer = - createWriter(Config, Obj, Out, OutputElfType); + createWriter(Config, ELFConfig, Obj, Out, OutputElfType); if (Error E = Writer->finalize()) return E; return Writer->write(); @@ -786,7 +787,7 @@ Error objcopy::elf::executeObjcopyOnIHex(const CommonConfig &Config, getOutputElfType(Config.OutputArch.value_or(MachineInfo())); if (Error E = handleArgs(Config, ELFConfig, **Obj)) return E; - return writeOutput(Config, **Obj, Out, OutputElfType); + return writeOutput(Config, ELFConfig, **Obj, Out, OutputElfType); } Error objcopy::elf::executeObjcopyOnRawBinary(const CommonConfig &Config, @@ -804,7 +805,7 @@ Error objcopy::elf::executeObjcopyOnRawBinary(const CommonConfig &Config, getOutputElfType(Config.OutputArch.value_or(MachineInfo())); if (Error E = handleArgs(Config, ELFConfig, **Obj)) return E; - return writeOutput(Config, **Obj, Out, OutputElfType); + return writeOutput(Config, ELFConfig, **Obj, Out, OutputElfType); } Error objcopy::elf::executeObjcopyOnBinary(const CommonConfig &Config, @@ -824,7 +825,7 @@ Error objcopy::elf::executeObjcopyOnBinary(const CommonConfig &Config, if (Error E = handleArgs(Config, ELFConfig, **Obj)) return createFileError(Config.InputFilename, std::move(E)); - if (Error E = writeOutput(Config, **Obj, Out, OutputElfType)) + if (Error E = writeOutput(Config, ELFConfig, **Obj, Out, OutputElfType)) return createFileError(Config.InputFilename, std::move(E)); return Error::success(); diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.cpp b/llvm/lib/ObjCopy/ELF/ELFObject.cpp index 2c4821c18c713d..cc8237c9d336c6 100644 --- a/llvm/lib/ObjCopy/ELF/ELFObject.cpp +++ b/llvm/lib/ObjCopy/ELF/ELFObject.cpp @@ -2643,6 +2643,15 @@ Error BinaryWriter::finalize() { if (Sec.Type != SHT_NOBITS && Sec.Size > 0) { Sec.Offset = Sec.Addr - MinAddr; TotalSize = std::max(TotalSize, Sec.Offset + Sec.Size); + + if (MaxSectionOffset && Sec.Offset > *MaxSectionOffset) { + return createStringError( + errc::file_too_large, + "writing section " + Sec.Name + " at offset 0x" + + Twine::utohexstr(Sec.Offset) + " greater than max offset 0x" + + Twine::utohexstr(*MaxSectionOffset) + + " specified by --max-section-offset"); + } } Buf = WritableMemoryBuffer::getNewMemBuffer(TotalSize); diff --git a/llvm/lib/ObjCopy/ELF/ELFObject.h b/llvm/lib/ObjCopy/ELF/ELFObject.h index 94b5afe7df89d1..ac940f3ff97278 100644 --- a/llvm/lib/ObjCopy/ELF/ELFObject.h +++ b/llvm/lib/ObjCopy/ELF/ELFObject.h @@ -15,6 +15,7 @@ #include "llvm/BinaryFormat/ELF.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/ObjCopy/CommonConfig.h" +#include "llvm/ObjCopy/ELF/ELFConfig.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FileOutputBuffer.h" @@ -361,11 +362,15 @@ class BinaryWriter : public Writer { uint64_t TotalSize = 0; + std::optional MaxSectionOffset; + public: ~BinaryWriter() {} Error finalize() override; Error write() override; - BinaryWriter(Object &Obj, raw_ostream &Out) : Writer(Obj, Out) {} + BinaryWriter(Object &Obj, raw_ostream &Out, const ELFConfig &ELFConfig) + : Writer(Obj, Out), + MaxSectionOffset(ELFConfig.MaxSectionOffset) {} }; class IHexWriter : public Writer { @@ -544,7 +549,7 @@ class CompressedSection : public SectionBase { public: CompressedSection(const SectionBase &Sec, - DebugCompressionType CompressionType, bool Is64Bits); + DebugCompressionType CompressionType, bool Is64Bits); CompressedSection(ArrayRef CompressedData, uint32_t ChType, uint64_t DecompressedSize, uint64_t DecompressedAlign); diff --git a/llvm/test/tools/llvm-objcopy/ELF/max-section-offset.yaml b/llvm/test/tools/llvm-objcopy/ELF/max-section-offset.yaml new file mode 100644 index 00000000000000..ce431aeac6c01f --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/ELF/max-section-offset.yaml @@ -0,0 +1,23 @@ +# RUN: yaml2obj %s --docnum=1 -o %t +# RUN: not llvm-objcopy -O binary %t %t2 --max-section-offset=15 2>&1 | FileCheck -DFILE=%t %s --check-prefix=INVALID-COPY +# RUN: llvm-objcopy -O binary %t %t2 --max-section-offset=16 + +# INVALID-COPY: error: '[[FILE]]': writing section .high_addr at offset 0x10 greater than max offset 0xf specified by --max-section-offset + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x0 + Content: "10" + - Name: .high_addr + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x10 + Content: "10" diff --git a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp index 577b837320ae25..ae76e48245986a 100644 --- a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp +++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp @@ -879,6 +879,11 @@ objcopy::parseObjcopyOptions(ArrayRef RawArgsArr, Config.ExtractMainPartition = InputArgs.hasArg(OBJCOPY_extract_main_partition); ELFConfig.LocalizeHidden = InputArgs.hasArg(OBJCOPY_localize_hidden); + + if (auto *Arg = InputArgs.getLastArg(OBJCOPY_max_section_offset)) { + ELFConfig.MaxSectionOffset = std::stoull(Arg->getValue()); + } + Config.Weaken = InputArgs.hasArg(OBJCOPY_weaken); if (auto *Arg = InputArgs.getLastArg(OBJCOPY_discard_all, OBJCOPY_discard_locals)) { diff --git a/llvm/tools/llvm-objcopy/ObjcopyOpts.td b/llvm/tools/llvm-objcopy/ObjcopyOpts.td index 0fddd443a4cc78..c50a70993c8c68 100644 --- a/llvm/tools/llvm-objcopy/ObjcopyOpts.td +++ b/llvm/tools/llvm-objcopy/ObjcopyOpts.td @@ -128,6 +128,12 @@ def extract_main_partition : Flag<["--"], "extract-main-partition">, HelpText<"Extract main partition from the input file">; +defm max_section_offset + : Eq<"max-section-offset", + "Emit an error if input section has a file offset greater than the " + "specified `offset`">, + MetaVarName<"offset">; + def localize_hidden : Flag<["--"], "localize-hidden">, HelpText<