From f996389adf3c5a4e674ff55b7bd2aeb5fb631cd4 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Mon, 12 Aug 2024 13:53:33 -0700 Subject: [PATCH] [llvm-objcopy][WebAssembly] Allow --strip-debug to operate on relocatable files. This change is enough to allow `--strip-debug` to work on object files, without breaking the relocation information or symbol table. A more complete version of this change would instead reconstruct the symbol table and relocation sections, but that is much larger change. Bug: #102002 --- lld/wasm/Writer.cpp | 5 ++ llvm/lib/ObjCopy/wasm/WasmObjcopy.cpp | 2 +- llvm/lib/ObjCopy/wasm/WasmObject.cpp | 18 ++++- llvm/lib/ObjCopy/wasm/WasmObject.h | 1 + llvm/lib/ObjCopy/wasm/WasmReader.cpp | 1 + llvm/lib/ObjectYAML/WasmEmitter.cpp | 3 +- .../wasm/invalid_section_order.yaml | 2 +- .../llvm-objcopy/wasm/basic-only-section.test | 16 +++-- .../llvm-objcopy/wasm/only-keep-debug.test | 38 +++++----- .../tools/llvm-objcopy/wasm/strip-all.test | 6 ++ .../tools/llvm-objcopy/wasm/strip-debug.test | 71 +++++++++++++++++-- .../tools/llvm-objcopy/wasm/strip-reloc.test | 4 +- 12 files changed, 132 insertions(+), 35 deletions(-) diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp index 6a66a29d2498c53..6beef81d39a3818 100644 --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -138,6 +138,11 @@ void Writer::calculateCustomSections() { // Exclude COMDAT sections that are not selected for inclusion if (section->discarded) continue; + // Ignore empty custom sections. In particular objcopy/strip will + // sometimes replace stripped sections with empty custom sections to + // avoid section re-numbering. + if (section->getSize() == 0) + continue; StringRef name = section->name; // These custom sections are known the linker and synthesized rather than // blindly copied. diff --git a/llvm/lib/ObjCopy/wasm/WasmObjcopy.cpp b/llvm/lib/ObjCopy/wasm/WasmObjcopy.cpp index 5bba1dea9adf0e3..cf3d884bee3bd8e 100644 --- a/llvm/lib/ObjCopy/wasm/WasmObjcopy.cpp +++ b/llvm/lib/ObjCopy/wasm/WasmObjcopy.cpp @@ -22,7 +22,7 @@ using namespace object; using SectionPred = std::function; static bool isDebugSection(const Section &Sec) { - return Sec.Name.starts_with(".debug"); + return Sec.Name.starts_with(".debug") || Sec.Name.starts_with("reloc..debug"); } static bool isLinkerSection(const Section &Sec) { diff --git a/llvm/lib/ObjCopy/wasm/WasmObject.cpp b/llvm/lib/ObjCopy/wasm/WasmObject.cpp index 28a2de6e6e4f124..0b543b793f049ed 100644 --- a/llvm/lib/ObjCopy/wasm/WasmObject.cpp +++ b/llvm/lib/ObjCopy/wasm/WasmObject.cpp @@ -25,8 +25,22 @@ void Object::addSectionWithOwnedContents( } void Object::removeSections(function_ref ToRemove) { - // TODO: remove reloc sections for the removed section, handle symbols, etc. - llvm::erase_if(Sections, ToRemove); + if (isRelocatableObject) { + // For relocatable objects, avoid actually removing any sections, + // since that can invalidate the symbol table and relocation sections. + // TODO: Allow removal of sections by re-generating symbol table and + // relocation sections here instead. + for (auto &Sec : Sections) { + if (ToRemove(Sec)) { + Sec.Name = ".objcopy.removed"; + Sec.SectionType = wasm::WASM_SEC_CUSTOM; + Sec.Contents = {}; + Sec.HeaderSecSizeEncodingLen = std::nullopt; + } + } + } else { + llvm::erase_if(Sections, ToRemove); + } } } // end namespace wasm diff --git a/llvm/lib/ObjCopy/wasm/WasmObject.h b/llvm/lib/ObjCopy/wasm/WasmObject.h index f860ec697e5684d..374e523cd879b65 100644 --- a/llvm/lib/ObjCopy/wasm/WasmObject.h +++ b/llvm/lib/ObjCopy/wasm/WasmObject.h @@ -32,6 +32,7 @@ struct Object { llvm::wasm::WasmObjectHeader Header; // For now don't discriminate between kinds of sections. std::vector
Sections; + bool isRelocatableObject = false; void addSectionWithOwnedContents(Section NewSection, std::unique_ptr &&Content); diff --git a/llvm/lib/ObjCopy/wasm/WasmReader.cpp b/llvm/lib/ObjCopy/wasm/WasmReader.cpp index 578e78955af3e45..420d17f9864323f 100644 --- a/llvm/lib/ObjCopy/wasm/WasmReader.cpp +++ b/llvm/lib/ObjCopy/wasm/WasmReader.cpp @@ -18,6 +18,7 @@ using namespace llvm::wasm; Expected> Reader::create() const { auto Obj = std::make_unique(); Obj->Header = WasmObj.getHeader(); + Obj->isRelocatableObject = WasmObj.isRelocatableObject(); std::vector
Sections; Obj->Sections.reserve(WasmObj.getNumSections()); for (const SectionRef &Sec : WasmObj.sections()) { diff --git a/llvm/lib/ObjectYAML/WasmEmitter.cpp b/llvm/lib/ObjectYAML/WasmEmitter.cpp index 9b8fd11f85437e9..4f384928b89f1df 100644 --- a/llvm/lib/ObjectYAML/WasmEmitter.cpp +++ b/llvm/lib/ObjectYAML/WasmEmitter.cpp @@ -604,7 +604,8 @@ bool WasmWriter::writeWasm(raw_ostream &OS) { if (auto S = dyn_cast(Sec.get())) SecName = S->Name; if (!Checker.isValidSectionOrder(Sec->Type, SecName)) { - reportError("out of order section type: " + Twine(Sec->Type)); + reportError("out of order section type: " + + wasm::sectionTypeToString(Sec->Type)); return false; } encodeULEB128(Sec->Type, OS); diff --git a/llvm/test/ObjectYAML/wasm/invalid_section_order.yaml b/llvm/test/ObjectYAML/wasm/invalid_section_order.yaml index 71767576979b0a3..08319e500e11158 100644 --- a/llvm/test/ObjectYAML/wasm/invalid_section_order.yaml +++ b/llvm/test/ObjectYAML/wasm/invalid_section_order.yaml @@ -14,7 +14,7 @@ Sections: - Index: 0 Locals: [] Body: 0B - # CHECK: yaml2obj: error: out of order section type: 3 + # CHECK: yaml2obj: error: out of order section type: FUNCTION - Type: FUNCTION FunctionTypes: [ 0 ] ... diff --git a/llvm/test/tools/llvm-objcopy/wasm/basic-only-section.test b/llvm/test/tools/llvm-objcopy/wasm/basic-only-section.test index fe47d5c027733a8..f30743c632bc863 100644 --- a/llvm/test/tools/llvm-objcopy/wasm/basic-only-section.test +++ b/llvm/test/tools/llvm-objcopy/wasm/basic-only-section.test @@ -1,15 +1,15 @@ ## Test --only-section. # RUN: yaml2obj %s -o %t # RUN: llvm-objcopy --only-section=foo %t %t2 -# RUN: obj2yaml %t2 | FileCheck --implicit-check-not TYPE --implicit-check-not linking %s +# RUN: obj2yaml %t2 | FileCheck --implicit-check-not TYPE --implicit-check-not producers %s ## Test that it's the same with only-section + keep-section (for the same section). # RUN: llvm-objcopy --only-section=foo --keep-section=foo %t %t2 -# RUN: obj2yaml %t2 | FileCheck --implicit-check-not TYPE --implicit-check-not linking %s +# RUN: obj2yaml %t2 | FileCheck --implicit-check-not TYPE --implicit-check-not producers %s ## Also test that only-section overrides remove-section. # RUN: llvm-objcopy --only-section=foo --remove-section=foo %t %t2 -# RUN: obj2yaml %t2 | FileCheck --implicit-check-not linking %s +# RUN: obj2yaml %t2 | FileCheck --implicit-check-not producers %s ## This file has both known and custom sections. Check that only the foo section is left. # CHECK: Sections: @@ -19,10 +19,10 @@ # CHECK-NEXT: ... ## Test that only-section + keep-section keeps both sections. -# RUN: llvm-objcopy --only-section=foo --keep-section=linking %t %t2 +# RUN: llvm-objcopy --only-section=foo --keep-section=producers %t %t2 # RUN: obj2yaml %t2 | FileCheck --implicit-check-not=TYPE --check-prefix=KEEP %s # KEEP: Name: foo -# KEEP: Name: linking +# KEEP: Name: producers --- !WASM FileHeader: @@ -39,5 +39,7 @@ Sections: ReturnTypes: - F32 - Type: CUSTOM - Name: linking - Version: 2 + Name: producers + Tools: + - Name: clang + Version: 9.0.0 diff --git a/llvm/test/tools/llvm-objcopy/wasm/only-keep-debug.test b/llvm/test/tools/llvm-objcopy/wasm/only-keep-debug.test index fc57329bdd74c2f..c7a5da99b1edf70 100644 --- a/llvm/test/tools/llvm-objcopy/wasm/only-keep-debug.test +++ b/llvm/test/tools/llvm-objcopy/wasm/only-keep-debug.test @@ -1,26 +1,32 @@ ## Test that only debug sections are kept with --only-keep-debug. -# RUN: yaml2obj %s -o %t -# RUN: llvm-strip --only-keep-debug %t -# RUN: obj2yaml %t | FileCheck %s +# RUN: yaml2obj %s -o %t.wasm +# RUN: llvm-strip --only-keep-debug %t.wasm -o %t2.wasm +# RUN: obj2yaml %t2.wasm | FileCheck %s -check-prefixes=CHECK,STRIPTYPE,STRIPFOO ## Test that keep-section overrides only-keep-debug. -# RUN: yaml2obj %s -o %t -# RUN: llvm-strip --only-keep-debug --keep-section=foo %t -# RUN: obj2yaml %t | FileCheck --implicit-check-not=Name --check-prefix=CHECK --check-prefix=KEEP %s +# RUN: llvm-strip --only-keep-debug --keep-section=foo %t.wasm -o %t3.wasm +# RUN: obj2yaml %t3.wasm | FileCheck --implicit-check-not=Name --check-prefixes=CHECK,STRIPTYPE,KEEPFOO %s ## Test that keep-section overrides only-keep-debug, even for known sections. -# RUN: yaml2obj %s -o %t -# RUN: llvm-strip --only-keep-debug --keep-section=TYPE %t -# RUN: obj2yaml %t | FileCheck --implicit-check-not=Name --check-prefix=CHECK --check-prefix=KEEPTYPE %s +# RUN: llvm-strip --only-keep-debug --keep-section=TYPE %t.wasm -o %t4.wasm +# RUN: obj2yaml %t4.wasm | FileCheck --implicit-check-not=Name --check-prefixes=CHECK,KEEPTYPE,STRIPFOO %s # CHECK: Sections: -# KEEPTYPE: - Type: TYPE -# CHECK-NOT: - Type: TYPE -# CHECK: - Type: CUSTOM -# CHECK-NEXT: Name: .debug_info -# CHECK: - Type: CUSTOM -# CHECK-NEXT: Name: .debug_line -# KEEP: Name: foo +# KEEPTYPE: - Type: TYPE +# STRIPTYPE-NOT: - Type: TYPE +# STRIPTYPE: - Type: CUSTOM +# STRIPTYPE-NEXT: Name: .objcopy.removed +# CHECK: - Type: CUSTOM +# CHECK-NEXT: Name: .debug_info +# CHECK: - Type: CUSTOM +# CHECK-NEXT: Name: .objcopy.removed +# CHECK: - Type: CUSTOM +# CHECK-NEXT: Name: .objcopy.removed +# CHECK: - Type: CUSTOM +# CHECK-NEXT: Name: .debug_line +# CHECK: - Type: CUSTOM +# KEEPFOO-NEXT: Name: foo +# STRIPFOO-NEXT: Name: .objcopy.removed ## Test that remove-section overrides only-keep-debug. # RUN: yaml2obj %s -o %t diff --git a/llvm/test/tools/llvm-objcopy/wasm/strip-all.test b/llvm/test/tools/llvm-objcopy/wasm/strip-all.test index 9a9015713242c81..ef7c1a90e610752 100644 --- a/llvm/test/tools/llvm-objcopy/wasm/strip-all.test +++ b/llvm/test/tools/llvm-objcopy/wasm/strip-all.test @@ -7,6 +7,12 @@ # CHECK: Sections: # CHECK-NEXT: - Type: TYPE # CHECK: - Type: CUSTOM +# CHECK-NEXT: Name: .objcopy.removed +# CHECK: - Type: CUSTOM +# CHECK-NEXT: Name: .objcopy.removed +# CHECK: - Type: CUSTOM +# CHECK-NEXT: Name: .objcopy.removed +# CHECK: - Type: CUSTOM # CHECK-NEXT: Name: foo --- !WASM diff --git a/llvm/test/tools/llvm-objcopy/wasm/strip-debug.test b/llvm/test/tools/llvm-objcopy/wasm/strip-debug.test index 2747c3bab742ea4..53e8153dd3539ab 100644 --- a/llvm/test/tools/llvm-objcopy/wasm/strip-debug.test +++ b/llvm/test/tools/llvm-objcopy/wasm/strip-debug.test @@ -1,14 +1,65 @@ -## Test that debug sections (but not linking or names) are stripped with --strip-debug # RUN: yaml2obj %s -o %t -# RUN: llvm-strip --strip-debug %t -# RUN: obj2yaml %t | FileCheck --implicit-check-not=.debug %s +# RUN: cp %t %t3 +# RUN: llvm-objcopy --strip-debug %t %t2 +## Test that debug sections (but not linking or names) are stripped with --strip-debug +# RUN: obj2yaml %t2 | FileCheck --implicit-check-not=.debug %s +# +# RUN: llvm-objcopy -g %t %t2g +# Verify that --strip-debug and -g produce the same output +# RUN: cmp %t2 %t2g + +# RUN: llvm-strip --strip-debug %t3 +# RUN: cmp %t2 %t3 + +# RUN: cp %t %t4 +# RUN: llvm-strip -d %t4 +# RUN: cmp %t2 %t4 + +# RUN: cp %t %t5 +# RUN: llvm-strip -g %t5 +# RUN: cmp %t2 %t5 + +# RUN: cp %t %t6 +# RUN: llvm-strip -S %t6 +# RUN: cmp %t2 %t6 + +# Verify that an archive with multiple object files is handled correctly. +# RUN: cp %t %t.duplicate +# RUN: cp %t2 %t.duplicate.stripped +# RUN: rm -f %t.multiple-stripped-obj.a +# RUN: llvm-ar crs %t.multiple-stripped-obj.a %t2 %t.duplicate.stripped +# RUN: rm -f %t.multiple-obj.a +# RUN: llvm-ar crs %t.multiple-obj.a %t %t.duplicate +# RUN: llvm-objcopy --strip-debug %t.multiple-obj.a %t.multiple-obj.stripped.a +# RUN: llvm-ar p %t.multiple-stripped-obj.a > %t.multiple-stripped-obj.a.dump +# RUN: llvm-ar p %t.multiple-obj.stripped.a > %t.multiple-obj.stripped.a.dump +# RUN: cmp %t.multiple-stripped-obj.a.dump %t.multiple-obj.stripped.a.dump # CHECK: Sections: # CHECK-NEXT: - Type: TYPE -# CHECK: Name: linking +# CHECK: - Type: CUSTOM +## We expect the linking section to be preceeded by the removed `.debug_info` +## section. +# CHECK-NEXT: Name: .objcopy.removed +# CHECK-NEXT: Payload: '' +# CHECK-NEXT: - Type: CUSTOM +# CHECK-NEXT: Name: linking # CHECK: Name: name # CHECK-NEXT: FunctionNames: # CHECK: Name: producers +## Following the producers section we expect to find three removed sections. +## The `.debug_line` section that two reloction section corresponding to the +## two debug sections. +# CHECK: - Type: CUSTOM +# CHECK-NEXT: Name: .objcopy.removed +# CHECK-NEXT: Payload: '' +# CHECK-NEXT: - Type: CUSTOM +# CHECK-NEXT: Name: .objcopy.removed +# CHECK-NEXT: Payload: '' +# CHECK-NEXT: - Type: CUSTOM +# CHECK-NEXT: Name: .objcopy.removed +# CHECK-NEXT: Payload: '' + --- !WASM FileHeader: @@ -28,7 +79,11 @@ Sections: Body: 0B - Type: CUSTOM Name: .debug_info - Payload: CAFE1234 + Payload: 'CAFE123456' + Relocations: + - Type: R_WASM_FUNCTION_INDEX_LEB + Index: 0 + Offset: 0x0000000 - Type: CUSTOM Name: linking Version: 2 @@ -50,4 +105,8 @@ Sections: Version: 9.0.0 - Type: CUSTOM Name: .debug_line - Payload: DEADBEEF + Payload: 'DEADBEEF01' + Relocations: + - Type: R_WASM_FUNCTION_INDEX_LEB + Index: 0 + Offset: 0x0000000 diff --git a/llvm/test/tools/llvm-objcopy/wasm/strip-reloc.test b/llvm/test/tools/llvm-objcopy/wasm/strip-reloc.test index 4c8d1ba670447f8..d072bf70c9db76b 100644 --- a/llvm/test/tools/llvm-objcopy/wasm/strip-reloc.test +++ b/llvm/test/tools/llvm-objcopy/wasm/strip-reloc.test @@ -5,13 +5,15 @@ # RUN: yaml2obj %s -o %t # RUN: llvm-objcopy --strip-all %t %t2 -# RUN: obj2yaml %t2 | FileCheck --implicit-check-not=Type: %s +# RUN: obj2yaml %t2 | FileCheck %s ## Check that the known sections are still present. # CHECK: Sections: # CHECK: - Type: TYPE # CHECK: - Type: FUNCTION # CHECK: - Type: CODE +# CHECK-NOT: Relocations +# CHECK-NOT: linking ## Check that there are still functions in the code section. # CHECK: Functions: