diff --git a/llvm/test/tools/llvm-objcopy/ELF/strip-unneeded-aarch64.test b/llvm/test/tools/llvm-objcopy/ELF/strip-unneeded-aarch64.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/ELF/strip-unneeded-aarch64.test @@ -0,0 +1,53 @@ +## Check that llvm-objcopy preserves AArch64 mapping symbols for relocatable files. +# RUN: yaml2obj %s -o %t.o -DTYPE=REL +# RUN: llvm-objcopy --strip-unneeded %t.o %t1.o +# RUN: llvm-nm -j --special-syms %t1.o | FileCheck %s --match-full-lines +# RUN: llvm-objcopy --discard-all %t.o %t2.o +# RUN: llvm-nm -j --special-syms %t2.o | FileCheck %s --match-full-lines + +# CHECK: $d +# CHECK-NEXT: $d.d +# CHECK-NEXT: $x +# CHECK-NEXT: $x.x + +## A mapping symbol can be deleted if specified explicitly. +# RUN: llvm-objcopy -w -N '$d*' %t.o %t3.o +# RUN: llvm-nm --special-syms %t3.o | FileCheck /dev/null --implicit-check-not='$d' + +## Mapping symbols are not preserved for executable files +# RUN: yaml2obj %s -o %t.exec -DTYPE=EXEC +# RUN: llvm-objcopy --strip-unneeded %t.exec %t1.exec +# RUN: llvm-nm --special-syms %t1.exec | count 0 + +# RUN: yaml2obj %s -o %t.so -DTYPE=DYN +# RUN: llvm-objcopy --strip-unneeded %t.so %t1.so +# RUN: llvm-nm --special-syms %t1.so | count 0 + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_[[TYPE]] + Machine: EM_AARCH64 +Sections: + - Name: .text + Type: SHT_PROGBITS +Symbols: + - Name: $a + Section: .text + - Name: $d + Section: .text + - Name: $dd + Section: .text + - Name: $d.d + Section: .text + - Name: $m + Section: .text + - Name: $t.t + Section: .text + - Name: $x + Section: .text + - Name: $xx + Section: .text + - Name: $x.x + Section: .text diff --git a/llvm/test/tools/llvm-objcopy/ELF/strip-unneeded-arm.test b/llvm/test/tools/llvm-objcopy/ELF/strip-unneeded-arm.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/ELF/strip-unneeded-arm.test @@ -0,0 +1,60 @@ +## Check that llvm-objcopy preserves ARM mapping symbols for relocatable files. +# RUN: yaml2obj %s -o %t.o -DTYPE=REL +# RUN: llvm-objcopy --strip-unneeded %t.o %t1.o +# RUN: llvm-nm -j --special-syms %t1.o | FileCheck %s --match-full-lines +# RUN: llvm-objcopy --discard-all %t.o %t2.o +# RUN: llvm-nm -j --special-syms %t2.o | FileCheck %s --match-full-lines + +# CHECK: $a +# CHECK-NEXT: $a.a +# CHECK-NEXT: $d +# CHECK-NEXT: $d.d +# CHECK-NEXT: $t +# CHECK-NEXT: $t.t +# CHECK-NOT: $x + +## A mapping symbol can be deleted if specified explicitly. +# RUN: llvm-objcopy -w -N '$d*' %t.o %t3.o +# RUN: llvm-nm --special-syms %t3.o | FileCheck /dev/null --implicit-check-not='$d' + +## Mapping symbols are not preserved for executable files +# RUN: yaml2obj %s -o %t.exec -DTYPE=EXEC +# RUN: llvm-objcopy --strip-unneeded %t.exec %t1.exec +# RUN: llvm-nm --special-syms %t1.exec | count 0 + +# RUN: yaml2obj %s -o %t.so -DTYPE=DYN +# RUN: llvm-objcopy --strip-unneeded %t.so %t1.so +# RUN: llvm-nm --special-syms %t1.so | count 0 + +!ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_[[TYPE]] + Machine: EM_ARM +Sections: + - Name: .text + Type: SHT_PROGBITS +Symbols: + - Name: $a + Section: .text + - Name: $aa + Section: .text + - Name: $a.a + Section: .text + - Name: $d + Section: .text + - Name: $dd + Section: .text + - Name: $d.d + Section: .text + - Name: $m + Section: .text + - Name: $t + Section: .text + - Name: $tt + Section: .text + - Name: $t.t + Section: .text + - Name: $x + Section: .text diff --git a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp --- a/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -227,6 +227,41 @@ return Obj.replaceSections(FromTo); } +static bool isAArch64MappingSymbol(const Symbol &Sym) { + if (Sym.Binding != STB_LOCAL || Sym.Type != STT_NOTYPE || + Sym.getShndx() == SHN_UNDEF) + return false; + StringRef Name = Sym.Name; + if (!Name.consume_front("$x") && !Name.consume_front("$d")) + return false; + return Name.empty() || Name.startswith("."); +} + +static bool isArmMappingSymbol(const Symbol &Sym) { + if (Sym.Binding != STB_LOCAL || Sym.Type != STT_NOTYPE || + Sym.getShndx() == SHN_UNDEF) + return false; + StringRef Name = Sym.Name; + if (!Name.consume_front("$a") && !Name.consume_front("$d") && + !Name.consume_front("$t")) + return false; + return Name.empty() || Name.startswith("."); +} + +// Check if the symbol should be preserved because it is required by ABI. +static bool isRequiredByABISymbol(const Object &Obj, const Symbol &Sym) { + switch (Obj.Machine) { + case EM_AARCH64: + // Mapping symbols should be preserved for a relocatable object file. + return Obj.isRelocatable() && isAArch64MappingSymbol(Sym); + case EM_ARM: + // Mapping symbols should be preserved for a relocatable object file. + return Obj.isRelocatable() && isArmMappingSymbol(Sym); + default: + return false; + } +} + static bool isUnneededSymbol(const Symbol &Sym) { return !Sym.Referenced && (Sym.Binding == STB_LOCAL || Sym.getShndx() == SHN_UNDEF) && @@ -297,20 +332,23 @@ (ELFConfig.KeepFileSymbols && Sym.Type == STT_FILE)) return false; - if ((Config.DiscardMode == DiscardType::All || - (Config.DiscardMode == DiscardType::Locals && - StringRef(Sym.Name).startswith(".L"))) && - Sym.Binding == STB_LOCAL && Sym.getShndx() != SHN_UNDEF && - Sym.Type != STT_FILE && Sym.Type != STT_SECTION) + if (Config.SymbolsToRemove.matches(Sym.Name)) return true; if (Config.StripAll || Config.StripAllGNU) return true; + if (isRequiredByABISymbol(Obj, Sym)) + return false; + if (Config.StripDebug && Sym.Type == STT_FILE) return true; - if (Config.SymbolsToRemove.matches(Sym.Name)) + if ((Config.DiscardMode == DiscardType::All || + (Config.DiscardMode == DiscardType::Locals && + StringRef(Sym.Name).startswith(".L"))) && + Sym.Binding == STB_LOCAL && Sym.getShndx() != SHN_UNDEF && + Sym.Type != STT_FILE && Sym.Type != STT_SECTION) return true; if ((Config.StripUnneeded ||