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,83 @@ +## Test --add-section. This test dumps and removes 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 --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 + +# Check that the section is replaced with new content in one invocation. +# RUN: echo "123" > %t4 +# RUN: llvm-objcopy --remove-section=foo --add-section=foo=%t4 %t %t5 +# RUN: obj2yaml %t5 | FileCheck %s --check-prefix=REPLACE + +# REPLACE: - Type: CUSTOM +# REPLACE: Name: foo +# REPLACE: Payload: 3132330A + +--- !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 + - Type: CUSTOM + Name: foo + Payload: ABC123 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,38 @@ +## 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=%t.dir/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: {{.*}}/bar': {{[nN]}}o such file or directory + +## Check that dumping and removing a section works in the same invocation +# RUN: llvm-objcopy --dump-section=producers=%t.sec --remove-section=producers %t %t2 +# RUN: od -t x1 %t.sec | FileCheck %s +# RUN: obj2yaml %t2 | FileCheck --check-prefix=REMOVED %s + +# REMOVED-NOT: producers + +--- !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,26 @@ +## 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 +## Check that the producers section has been removed, but not the type section. +# CHECK: TYPE + +## Requests to remove nonexistent sections are silently ignored. +# RUN: llvm-objcopy --remove-section=nonexistent=%t.sec %t 2&>1 | count 0 + +--- !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/CMakeLists.txt b/llvm/tools/llvm-objcopy/CMakeLists.txt --- a/llvm/tools/llvm-objcopy/CMakeLists.txt +++ b/llvm/tools/llvm-objcopy/CMakeLists.txt @@ -33,6 +33,7 @@ MachO/MachOWriter.cpp MachO/MachOLayoutBuilder.cpp MachO/Object.cpp + wasm/Object.cpp wasm/Reader.cpp wasm/Writer.cpp wasm/WasmObjcopy.cpp 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 { @@ -30,6 +31,13 @@ llvm::wasm::WasmObjectHeader Header; // For now don't discriminate between kinds of sections. std::vector
Sections; + + void addSectionWithOwnedContents(Section NewSection, + std::unique_ptr &&Content); + void removeSections(function_ref ToRemove); + +private: + std::vector> OwnedContents; }; } // end namespace wasm diff --git a/llvm/tools/llvm-objcopy/wasm/Object.cpp b/llvm/tools/llvm-objcopy/wasm/Object.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-objcopy/wasm/Object.cpp @@ -0,0 +1,36 @@ +//===- Object.cpp ---------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Object.h" + +#include "llvm/Support/LEB128.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace objcopy { +namespace wasm { + +using namespace object; +using namespace llvm::wasm; + +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 @@ -21,7 +21,58 @@ using namespace object; +static Error dumpSectionToFile(StringRef SecName, StringRef Filename, + Object &Obj) { + for (const Section &Sec : Obj.Sections) { + 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 for now. + for (StringRef Flag : Config.DumpSection) { + StringRef SecName; + StringRef FileName; + std::tie(SecName, FileName) = Flag.split("="); + if (Error E = dumpSectionToFile(SecName, FileName, Obj)) + return createFileError(FileName, std::move(E)); + } + + Obj.removeSections([&Config](const Section &Sec) { + if (Config.ToRemove.matches(Sec.Name)) + return true; + return false; + }); + + for (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)); + } + if (!Config.AddGnuDebugLink.empty() || !Config.BuildIdLinkDir.empty() || Config.BuildIdLinkInput || Config.BuildIdLinkOutput || Config.ExtractPartition || !Config.SplitDWO.empty() || @@ -34,12 +85,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, only basic copying is allowed"); + "only add-section, dump-section, and remove-section are supported"); } return Error::success(); } @@ -53,7 +102,7 @@ Object *Obj = ObjOrErr->get(); assert(Obj && "Unable to deserialize Wasm object"); if (Error E = handleArgs(Config, *Obj)) - return createFileError(Config.InputFilename, std::move(E)); + return E; Writer TheWriter(*Obj, Out); if (Error E = TheWriter.write()) return createFileError(Config.OutputFilename, std::move(E)); diff --git a/llvm/utils/gn/secondary/llvm/tools/llvm-objcopy/BUILD.gn b/llvm/utils/gn/secondary/llvm/tools/llvm-objcopy/BUILD.gn --- a/llvm/utils/gn/secondary/llvm/tools/llvm-objcopy/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/tools/llvm-objcopy/BUILD.gn @@ -72,6 +72,7 @@ "MachO/MachOWriter.cpp", "MachO/Object.cpp", "llvm-objcopy.cpp", + "wasm/Object.cpp", "wasm/Reader.cpp", "wasm/WasmObjcopy.cpp", "wasm/Writer.cpp",