diff --git a/llvm/docs/CommandGuide/llvm-objcopy.rst b/llvm/docs/CommandGuide/llvm-objcopy.rst index e113a82b6eea..64ba094c9ee4 100644 --- a/llvm/docs/CommandGuide/llvm-objcopy.rst +++ b/llvm/docs/CommandGuide/llvm-objcopy.rst @@ -36,6 +36,13 @@ multiple file formats. Add a .gnu_debuglink section for ```` to the output. +.. option:: --add-section + + Add a section named ``
`` with the contents of ```` to the + output. For ELF objects the section will be of type `SHT_NOTE`, if the name + starts with ".note". Otherwise, it will have type `SHT_PROGBITS`. Can be + specified multiple times to add multiple sections. + .. option:: --disable-deterministic-archives, -U Use real values for UIDs, GIDs and timestamps when updating archive member @@ -141,13 +148,6 @@ The following options are implemented only for ELF objects. If used with other objects, :program:`llvm-objcopy` will either emit an error or silently ignore them. -.. option:: --add-section - - Add a section named ``
`` with the contents of ```` to the - output. The section will be of type `SHT_NOTE`, if the name starts with - ".note". Otherwise, it will have type `SHT_PROGBITS`. Can be specified multiple - times to add multiple sections. - .. option:: --add-symbol =[
:][,] Add a new symbol called ```` to the output symbol table, in the section diff --git a/llvm/test/tools/llvm-objcopy/COFF/add-section.test b/llvm/test/tools/llvm-objcopy/COFF/add-section.test new file mode 100644 index 000000000000..f2aa5ef093dc --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/COFF/add-section.test @@ -0,0 +1,56 @@ +# RUN: yaml2obj %s > %t + +## Test that llvm-objcopy adds a section to the given object with expected +## contents. +# RUN: echo DEADBEEF > %t.sec +# RUN: llvm-objcopy --add-section=.test.section=%t.sec %t %t1 +# RUN: llvm-readobj --file-headers --sections --section-data %t1 | FileCheck %s --check-prefixes=CHECK-ADD + +# CHECK-ADD: SectionCount: 2 +# CHECK-ADD: Name: .text +# CHECK-ADD: Name: .test.section +# CHECK-ADD: Characteristics [ +# CHECK-ADD-NEXT: IMAGE_SCN_ALIGN_1BYTES +# CHECK-ADD-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA +# CHECK-ADD-NEXT: ] +# CHECK-ADD: SectionData ( +# CHECK-ADD-NEXT: 0000: {{.+}}|DEADBEEF{{.+}}| +# CHECK-ADD-NEXT: ) + +## Test that llvm-objcopy can add a section with an empty name. +# RUN: llvm-objcopy --add-section==%t.sec %t %t1.empty.name +# RUN: llvm-readobj --file-headers --sections --section-data %t1.empty.name | FileCheck %s --check-prefixes=CHECK-ADD-EMPTY-NAME + +# CHECK-ADD-EMPTY-NAME: SectionCount: 2 +# CHECK-ADD-EMPTY-NAME: Name: .text +# CHECK-ADD-EMPTY-NAME: Name: (00 00 00 00 00 00 00 00) +# CHECK-ADD-EMPTY-NAME: Characteristics [ +# CHECK-ADD-EMPTY-NAME-NEXT: IMAGE_SCN_ALIGN_1BYTES +# CHECK-ADD-EMPTY-NAME-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA +# CHECK-ADD-EMPTY-NAME-NEXT: ] +# CHECK-ADD-EMPTY-NAME: SectionData ( +# CHECK-ADD-EMPTY-NAME-NEXT: 0000: {{.+}}|DEADBEEF{{.+}}| +# CHECK-ADD-EMPTY-NAME-NEXT: ) + +## Test that llvm-objcopy produces an error if the file with section contents +## to be added does not exist. +# RUN: not llvm-objcopy --add-section=.another.section=%t2 %t %t3 2>&1 | FileCheck -DFILE=%t -DFILE1=%t2 %s --check-prefixes=CHECK-ERR1 + +# CHECK-ERR1: llvm-objcopy{{(.exe)?}}: error: '[[FILE]]': '[[FILE1]]': {{[Nn]}}o such file or directory + +## Another negative test for invalid --add-sections command line argument. +# RUN: not llvm-objcopy --add-section=.another.section %t %t3 2>&1 | FileCheck -DFILE=%t %s --check-prefixes=CHECK-ERR2 + +# CHECK-ERR2: llvm-objcopy{{(.exe)?}}: error: '[[FILE]]': bad format for --add-section + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ ] + Alignment: 4 + SectionData: 488B0500000000C3 +symbols: +... diff --git a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp index 4ae46851a66f..b0474a0f0f4e 100644 --- a/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp @@ -65,26 +65,37 @@ static std::vector createGnuDebugLinkSectionContents(StringRef File) { return Data; } -static void addGnuDebugLink(Object &Obj, StringRef DebugLinkFile) { - uint32_t StartRVA = getNextRVA(Obj); +// Adds named section with given contents to the object. +static void addSection(Object &Obj, StringRef Name, ArrayRef Contents, + uint32_t Characteristics) { + bool NeedVA = Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | + IMAGE_SCN_MEM_WRITE); - std::vector
Sections; Section Sec; - Sec.setOwnedContents(createGnuDebugLinkSectionContents(DebugLinkFile)); - Sec.Name = ".gnu_debuglink"; - Sec.Header.VirtualSize = Sec.getContents().size(); - Sec.Header.VirtualAddress = StartRVA; - Sec.Header.SizeOfRawData = alignTo(Sec.Header.VirtualSize, - Obj.IsPE ? Obj.PeHeader.FileAlignment : 1); + Sec.setOwnedContents(Contents); + Sec.Name = Name; + Sec.Header.VirtualSize = NeedVA ? Sec.getContents().size() : 0u; + Sec.Header.VirtualAddress = NeedVA ? getNextRVA(Obj) : 0u; + Sec.Header.SizeOfRawData = + NeedVA ? alignTo(Sec.Header.VirtualSize, + Obj.IsPE ? Obj.PeHeader.FileAlignment : 1) + : Sec.getContents().size(); // Sec.Header.PointerToRawData is filled in by the writer. Sec.Header.PointerToRelocations = 0; Sec.Header.PointerToLinenumbers = 0; // Sec.Header.NumberOfRelocations is filled in by the writer. Sec.Header.NumberOfLinenumbers = 0; - Sec.Header.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | - IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE; - Sections.push_back(Sec); - Obj.addSections(Sections); + Sec.Header.Characteristics = Characteristics; + + Obj.addSections(Sec); +} + +static void addGnuDebugLink(Object &Obj, StringRef DebugLinkFile) { + std::vector Contents = + createGnuDebugLinkSectionContents(DebugLinkFile); + addSection(Obj, ".gnu_debuglink", Contents, + IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | + IMAGE_SCN_MEM_DISCARDABLE); } static Error handleArgs(const CopyConfig &Config, Object &Obj) { @@ -171,21 +182,40 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) { return false; }); + for (const auto &Flag : Config.AddSection) { + StringRef SecName, FileName; + std::tie(SecName, FileName) = Flag.split("="); + + if (FileName.empty()) + return createStringError(llvm::errc::invalid_argument, + "bad format for --add-section"); + auto BufOrErr = MemoryBuffer::getFile(FileName); + if (!BufOrErr) + return createFileError(FileName, errorCodeToError(BufOrErr.getError())); + auto Buf = std::move(*BufOrErr); + + addSection( + Obj, SecName, + makeArrayRef(reinterpret_cast(Buf->getBufferStart()), + Buf->getBufferSize()), + IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_1BYTES); + } + if (!Config.AddGnuDebugLink.empty()) addGnuDebugLink(Obj, Config.AddGnuDebugLink); if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() || Config.BuildIdLinkInput || Config.BuildIdLinkOutput || !Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() || - !Config.AllocSectionsPrefix.empty() || !Config.AddSection.empty() || - !Config.DumpSection.empty() || !Config.KeepSection.empty() || - !Config.SymbolsToGlobalize.empty() || !Config.SymbolsToKeep.empty() || - !Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() || - !Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() || - !Config.SetSectionFlags.empty() || !Config.SymbolsToRename.empty() || - Config.ExtractDWO || Config.KeepFileSymbols || Config.LocalizeHidden || - Config.PreserveDates || Config.StripDWO || Config.StripNonAlloc || - Config.StripSections || Config.Weaken || Config.DecompressDebugSections || + !Config.AllocSectionsPrefix.empty() || !Config.DumpSection.empty() || + !Config.KeepSection.empty() || !Config.SymbolsToGlobalize.empty() || + !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() || + !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() || + !Config.SectionsToRename.empty() || !Config.SetSectionFlags.empty() || + !Config.SymbolsToRename.empty() || Config.ExtractDWO || + Config.KeepFileSymbols || Config.LocalizeHidden || Config.PreserveDates || + Config.StripDWO || Config.StripNonAlloc || Config.StripSections || + Config.Weaken || Config.DecompressDebugSections || Config.DiscardMode == DiscardType::Locals || !Config.SymbolsToAdd.empty() || Config.EntryExpr) { return createStringError(llvm::errc::invalid_argument, diff --git a/llvm/tools/llvm-objcopy/COFF/Writer.cpp b/llvm/tools/llvm-objcopy/COFF/Writer.cpp index f3bb1ce331f2..6db37435fd96 100644 --- a/llvm/tools/llvm-objcopy/COFF/Writer.cpp +++ b/llvm/tools/llvm-objcopy/COFF/Writer.cpp @@ -120,12 +120,12 @@ size_t COFFWriter::finalizeStringTable() { StrTabBuilder.finalize(); for (auto &S : Obj.getMutableSections()) { + memset(S.Header.Name, 0, sizeof(S.Header.Name)); if (S.Name.size() > COFF::NameSize) { - memset(S.Header.Name, 0, sizeof(S.Header.Name)); snprintf(S.Header.Name, sizeof(S.Header.Name), "/%d", (int)StrTabBuilder.getOffset(S.Name)); } else { - strncpy(S.Header.Name, S.Name.data(), COFF::NameSize); + memcpy(S.Header.Name, S.Name.data(), S.Name.size()); } } for (auto &S : Obj.getMutableSymbols()) {