diff --git a/llvm/test/tools/llvm-objcopy/wasm/add-section.test b/llvm/test/tools/llvm-objcopy/wasm/add-section.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/wasm/add-section.test @@ -0,0 +1,75 @@ +## Test --add-section. This test dumps the section first and checks that adding +## it back doesn't change the result. +# RUN: yaml2obj %s -o %t +# RUN: llvm-objcopy --dump-section=producers=%t.sec %t +# RUN: llvm-objcopy --remove-section=producers %t %t2 +# RUN: llvm-objcopy --add-section=producers=%t.sec %t2 %t3 +# RUN: obj2yaml %t3 | FileCheck %s + +## Check that the producers section has been added back unchanged. +# CHECK: Name: producers +# CHECK-NEXT: Tools: +# CHECK-NEXT: - Name: clang +# CHECK-NEXT: Version: 9.0.0 + +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: TYPE + Signatures: + - Index: 0 + ParamTypes: + - I32 + ReturnTypes: + - F32 + - Index: 1 + ParamTypes: + - I32 + - I64 + ReturnTypes: [] + - Type: FUNCTION + FunctionTypes: + - 0 + - 1 + - Type: CODE + Relocations: + - Type: R_WASM_TABLE_INDEX_SLEB + Index: 0 + Offset: 0x00000000 + - Type: R_WASM_FUNCTION_INDEX_LEB + Index: 1 + Offset: 0x0000000 + Functions: + - Index: 0 + Locals: + - Type: I32 + Count: 3 + Body: 010101010B + - Index: 1 + Locals: + - Type: I32 + Count: 1 + Body: 010101010B + - Type: CUSTOM + Name: linking + Version: 2 + SymbolTable: + - Index: 0 + Kind: FUNCTION + Name: func1 + Flags: [ ] + Function: 0 + - Index: 1 + Kind: FUNCTION + Name: func2 + Flags: [ ] + Function: 1 + - Type: CUSTOM + Name: producers + Tools: + - Name: clang + Version: 9.0.0 + +... + diff --git a/llvm/test/tools/llvm-objcopy/wasm/dump-section.test b/llvm/test/tools/llvm-objcopy/wasm/dump-section.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/wasm/dump-section.test @@ -0,0 +1,33 @@ +## Test the contents of a custom section dumped from a binary. +# RUN: yaml2obj %s -o %t +# RUN: llvm-objcopy --dump-section=producers=%t.sec %t +# RUN: od -t x1 %t.sec | FileCheck %s + +# RUN: not llvm-objcopy --dump-section=nonexistent=%t.sec %t 2>&1 | FileCheck --check-prefix=NONEXISTENT %s +# RUN: not llvm-objcopy --dump-section=producers=foo/bar %t 2>&1 | FileCheck --check-prefix=DIROUT %s + +## Raw contents of the producers section. +# CHECK: 0000000 01 0c 70 72 6f 63 65 73 73 65 64 2d 62 79 01 05 +# CHECK: 0000020 63 6c 61 6e 67 05 39 2e 30 2e 30 + +# NONEXISTENT: section 'nonexistent' not found +# DIROUT: error: {{.*}}: {{[nN]}}o such file or directory + +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: TYPE + Signatures: + - Index: 0 + ParamTypes: + - I32 + ReturnTypes: + - F32 + - Type: CUSTOM + Name: producers + Tools: + - Name: clang + Version: 9.0.0 +... + diff --git a/llvm/test/tools/llvm-objcopy/wasm/remove-section.test b/llvm/test/tools/llvm-objcopy/wasm/remove-section.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/wasm/remove-section.test @@ -0,0 +1,30 @@ +## Test the --remove-section flag. +# RUN: yaml2obj %s -o %t +# RUN: llvm-objcopy -R producers %t %t2 +# RUN: obj2yaml %t2 | FileCheck --implicit-check-not=producers %s + +## Requests to remove nonexistent sections are silently ignored. +# RUN: llvm-objcopy --remove-section=nonexistent=%t.sec %t + +## Check that the producers section has been removed, but not the type section. +# CHECK: TYPE + +# NONEXISTENT: section 'nonexistent' not found + +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: TYPE + Signatures: + - Index: 0 + ParamTypes: + - I32 + ReturnTypes: + - F32 + - Type: CUSTOM + Name: producers + Tools: + - Name: clang + Version: 9.0.0 +... diff --git a/llvm/tools/llvm-objcopy/wasm/Object.h b/llvm/tools/llvm-objcopy/wasm/Object.h --- a/llvm/tools/llvm-objcopy/wasm/Object.h +++ b/llvm/tools/llvm-objcopy/wasm/Object.h @@ -12,6 +12,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/Object/Wasm.h" +#include "llvm/Support/MemoryBuffer.h" #include namespace llvm { @@ -33,10 +34,15 @@ // TODO: Support symbols, relocations, debug info, etc ArrayRef
getSections() { return Sections; }; void addSections(ArrayRef
NewSections); + // Add a section + void addSectionWithOwnedContents(Section NewSection, + std::unique_ptr &&Content); + void removeSections(function_ref ToRemove); private: // For now don't discriminate between kinds of sections. std::vector
Sections; + std::vector> OwnedContents; }; } // end namespace wasm diff --git a/llvm/tools/llvm-objcopy/wasm/Object.cpp b/llvm/tools/llvm-objcopy/wasm/Object.cpp --- a/llvm/tools/llvm-objcopy/wasm/Object.cpp +++ b/llvm/tools/llvm-objcopy/wasm/Object.cpp @@ -20,10 +20,23 @@ void Object::addSections(ArrayRef
NewSections) { for (const Section &S : NewSections) { - Sections.emplace_back(S); + Sections.push_back(S); } } +void Object::addSectionWithOwnedContents( + Section NewSection, std::unique_ptr &&Content) { + Sections.push_back(NewSection); + OwnedContents.emplace_back(std::move(Content)); +} + +void Object::removeSections(function_ref ToRemove) { + // TODO: remove reloc sections for the removed section, handle symbols, etc. + Sections.erase( + std::remove_if(std::begin(Sections), std::end(Sections), ToRemove), + std::end(Sections)); +} + } // end namespace wasm } // end namespace objcopy } // end namespace llvm diff --git a/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp --- a/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/wasm/WasmObjcopy.cpp @@ -22,7 +22,59 @@ using namespace object; using namespace wasm; +static Error dumpSectionToFile(StringRef SecName, StringRef Filename, + Object &Obj) { + for (const Section &Sec : Obj.getSections()) { + if (Sec.Name == SecName) { + ArrayRef Contents = Sec.Contents; + Expected> BufferOrErr = + FileOutputBuffer::create(Filename, Contents.size()); + if (!BufferOrErr) + return BufferOrErr.takeError(); + std::unique_ptr Buf = std::move(*BufferOrErr); + std::copy(Contents.begin(), Contents.end(), Buf->getBufferStart()); + if (Error E = Buf->commit()) + return E; + return Error::success(); + } + } + return createStringError(errc::invalid_argument, "section '%s' not found", + SecName.str().c_str()); +} static Error handleArgs(const CopyConfig &Config, Object &Obj) { + // Only support AddSection, DumpSection, RemoveSection. + // Don't support OnlySection yet because we only operate on custom sections. + Obj.removeSections([&Config](const Section &Sec) { + if (Config.ToRemove.matches(Sec.Name)) + return true; + return false; + }); + + for (const StringRef &Flag : Config.AddSection) { + StringRef SecName, FileName; + std::tie(SecName, FileName) = Flag.split("="); + ErrorOr> BufOrErr = + MemoryBuffer::getFile(FileName); + if (!BufOrErr) + return createFileError(FileName, errorCodeToError(BufOrErr.getError())); + Section Sec; + Sec.SectionType = llvm::wasm::WASM_SEC_CUSTOM; + Sec.Name = SecName; + std::unique_ptr Buf = std::move(*BufOrErr); + Sec.Contents = makeArrayRef( + reinterpret_cast(Buf->getBufferStart()), + Buf->getBufferSize()); + Obj.addSectionWithOwnedContents(Sec, std::move(Buf)); + } + + for (const StringRef &Flag : Config.DumpSection) { + StringRef SecName; + StringRef FileName; + std::tie(SecName, FileName) = Flag.split("="); + if (Error E = dumpSectionToFile(SecName, FileName, Obj)) + return E; + } + if (!Config.AddGnuDebugLink.empty() || !Config.BuildIdLinkDir.empty() || Config.BuildIdLinkInput || Config.BuildIdLinkOutput || Config.ExtractPartition || !Config.SplitDWO.empty() || @@ -35,12 +87,10 @@ !Config.UnneededSymbolsToRemove.empty() || !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() || !Config.SetSectionAlignment.empty() || - !Config.SetSectionFlags.empty() || !Config.SymbolsToRename.empty() || - !Config.ToRemove.empty() || !Config.DumpSection.empty() || - !Config.AddSection.empty()) { + !Config.SetSectionFlags.empty() || !Config.SymbolsToRename.empty()) { return createStringError( llvm::errc::invalid_argument, - "no flags are supported yet other than basic copying"); + "only add-section, dump-section, and remove-section are supported"); } return Error::success(); }