Index: test/tools/llvm-objcopy/rename-section-flag-osproc-mask.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/rename-section-flag-osproc-mask.test @@ -0,0 +1,115 @@ +# Test that cpu/processor-specific SHF_* flags are preserved. + +# ===== x86_64 ===== + +# RUN: yaml2obj -docnum 1 %s > %t-x86_64.o +# RUN: llvm-objcopy --rename-section=.foo=.bar,alloc %t-x86_64.o +# RUN: llvm-readobj -sections %t-x86_64.o | FileCheck %s --check-prefix=X86_64 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .foo + Type: SHT_PROGBITS + Flags: [ SHF_X86_64_LARGE ] + +# X86_64: Name: .bar +# X86_64-NEXT: Type: SHT_PROGBITS +# X86_64-NEXT: Flags [ +# X86_64-NEXT: SHF_ALLOC (0x2) +# X86_64-NEXT: SHF_WRITE (0x1) +# X86_64-NEXT: SHF_X86_64_LARGE (0x10000000) +# X86_64-NEXT: ] + +# ===== hex ===== + +# RUN: yaml2obj -docnum 2 %s > %t-hex.o +# RUN: llvm-objcopy --rename-section=.foo=.bar,alloc %t-hex.o +# RUN: llvm-readobj -sections %t-hex.o | FileCheck %s --check-prefix=HEX + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_HEXAGON +Sections: + - Name: .foo + Type: SHT_PROGBITS + Flags: [ SHF_HEX_GPREL ] + +# HEX: Name: .bar +# HEX-NEXT: Type: SHT_PROGBITS +# HEX-NEXT: Flags [ +# HEX-NEXT: SHF_ALLOC (0x2) +# HEX-NEXT: SHF_HEX_GPREL (0x10000000) +# HEX-NEXT: SHF_WRITE (0x1) +# HEX-NEXT: ] + +# ===== mips ===== + +# RUN: yaml2obj -docnum 3 %s > %t-mips.o +# RUN: llvm-objcopy --rename-section=.foo=.bar,alloc %t-mips.o +# RUN: llvm-readobj -sections %t-mips.o | FileCheck %s --check-prefix=MIPS + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS +Sections: + - Name: .foo + Type: SHT_PROGBITS + Flags: [ SHF_MIPS_NODUPES, SHF_MIPS_NAMES, SHF_MIPS_LOCAL, + SHF_MIPS_NOSTRIP, SHF_MIPS_GPREL, SHF_MIPS_MERGE, + SHF_MIPS_ADDR, SHF_MIPS_STRING ] + +# Note: llvm-readobj prints SHF_EXCLUDE/SHF_MASKPROC since specifying all +# SHF_MIPS_* flags covers the same bitset. + +# MIPS: Name: .bar +# MIPS-NEXT: Type: SHT_PROGBITS +# MIPS-NEXT: Flags [ +# MIPS-NEXT: SHF_ALLOC (0x2) +# MIPS-NEXT: SHF_EXCLUDE (0x80000000) +# MIPS-NEXT: SHF_MASKPROC (0xF0000000) +# MIPS-NEXT: SHF_MIPS_ADDR (0x40000000) +# MIPS-NEXT: SHF_MIPS_GPREL (0x10000000) +# MIPS-NEXT: SHF_MIPS_LOCAL (0x4000000) +# MIPS-NEXT: SHF_MIPS_MERGE (0x20000000) +# MIPS-NEXT: SHF_MIPS_NAMES (0x2000000) +# MIPS-NEXT: SHF_MIPS_NODUPES (0x1000000) +# MIPS-NEXT: SHF_MIPS_NOSTRIP (0x8000000) +# MIPS-NEXT: SHF_MIPS_STRING (0x80000000) +# MIPS-NEXT: SHF_WRITE (0x1) +# MIPS-NEXT: ] + +# ===== arm ===== + +# RUN: yaml2obj -docnum 4 %s > %t-arm.o +# RUN: llvm-objcopy --rename-section=.foo=.bar,alloc %t-arm.o +# RUN: llvm-readobj -sections %t-arm.o | FileCheck %s --check-prefix=ARM + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM +Sections: + - Name: .foo + Type: SHT_PROGBITS + Flags: [ SHF_ARM_PURECODE ] + +# ARM: Name: .bar +# ARM-NEXT: Type: SHT_PROGBITS +# ARM-NEXT: Flags [ +# ARM-NEXT: SHF_ALLOC (0x2) +# ARM-NEXT: SHF_ARM_PURECODE (0x20000000) +# ARM-NEXT: SHF_WRITE (0x1) +# ARM-NEXT: ] Index: test/tools/llvm-objcopy/rename-section-flag-preserved.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/rename-section-flag-preserved.test @@ -0,0 +1,74 @@ +# RUN: yaml2obj %s > %t + +# Single flags on a section with all flags: +# RUN: llvm-objcopy --rename-section=.foo=.bar,alloc %t %t.alloc +# RUN: llvm-readobj -sections %t.alloc | FileCheck %s --check-prefixes=CHECK,ALLOC,WRITE +# RUN: llvm-objcopy --rename-section=.foo=.bar,load %t %t.load +# RUN: llvm-readobj -sections %t.load | FileCheck %s --check-prefixes=CHECK,WRITE +# RUN: llvm-objcopy --rename-section=.foo=.bar,noload %t %t.noload +# RUN: llvm-readobj -sections %t.noload | FileCheck %s --check-prefixes=CHECK,WRITE +# RUN: llvm-objcopy --rename-section=.foo=.bar,readonly %t %t.readonly +# RUN: llvm-readobj -sections %t.readonly | FileCheck %s --check-prefixes=CHECK +# RUN: llvm-objcopy --rename-section=.foo=.bar,debug %t %t.debug +# RUN: llvm-readobj -sections %t.debug | FileCheck %s --check-prefixes=CHECK,WRITE +# RUN: llvm-objcopy --rename-section=.foo=.bar,code %t %t.code +# RUN: llvm-readobj -sections %t.code | FileCheck %s --check-prefixes=CHECK,EXEC,WRITE +# RUN: llvm-objcopy --rename-section=.foo=.bar,data %t %t.data +# RUN: llvm-readobj -sections %t.data | FileCheck %s --check-prefixes=CHECK,WRITE +# RUN: llvm-objcopy --rename-section=.foo=.bar,rom %t %t.rom +# RUN: llvm-readobj -sections %t.rom | FileCheck %s --check-prefixes=CHECK,WRITE +# RUN: llvm-objcopy --rename-section=.foo=.bar,contents %t %t.contents +# RUN: llvm-readobj -sections %t.contents | FileCheck %s --check-prefixes=CHECK,WRITE +# RUN: llvm-objcopy --rename-section=.foo=.bar,merge %t %t.merge +# RUN: llvm-readobj -sections %t.merge | FileCheck %s --check-prefixes=CHECK,MERGE,WRITE +# RUN: llvm-objcopy --rename-section=.foo=.bar,strings %t %t.strings +# RUN: llvm-readobj -sections %t.strings | FileCheck %s --check-prefixes=CHECK,STRINGS,WRITE +# RUN: llvm-objcopy --rename-section=.foo=.bar,share %t %t.share +# RUN: llvm-readobj -sections %t.share | FileCheck %s --check-prefixes=CHECK,WRITE + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .infolink + Type: SHT_NOBITS + - Name: .group + Type: SHT_GROUP + Link: .symtab + Flags: [ ] + Info: dummy + Members: + - SectionOrType: GRP_COMDAT + - SectionOrType: .foo + - Name: .foo + Type: SHT_PROGBITS + Link: .infolink + Flags: [ SHF_ALLOC, SHF_COMPRESSED, SHF_EXCLUDE, SHF_EXECINSTR, + SHF_GROUP, SHF_INFO_LINK, SHF_LINK_ORDER, SHF_MERGE, + SHF_OS_NONCONFORMING, SHF_STRINGS, SHF_TLS, SHF_WRITE ] + Content: "a4a4a4a4" +Symbols: + Local: + - Name: dummy + Section: .group + +# CHECK: Name: .bar +# CHECK-NEXT: Type: SHT_PROGBITS +# CHECK-NEXT: Flags [ +# ALLOC-NEXT: SHF_ALLOC (0x2) +# CHECK-NEXT: SHF_COMPRESSED (0x800) +# CHECK-NEXT: SHF_EXCLUDE (0x80000000) +# EXEC-NEXT: SHF_EXECINSTR (0x4) +# CHECK-NEXT: SHF_GROUP (0x200) +# CHECK-NEXT: SHF_INFO_LINK (0x40) +# CHECK-NEXT: SHF_LINK_ORDER (0x80) +# MERGE-NEXT: SHF_MERGE (0x10) +# STRINGS-NEXT: SHF_STRINGS (0x20) +# CHECK-NEXT: SHF_TLS (0x400) +# WRITE-NEXT: SHF_WRITE (0x1) +# CHECK-NEXT: ] + +# BAD-FLAG: unrecognized section flag 'xyzzy' Index: test/tools/llvm-objcopy/rename-section-flag.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/rename-section-flag.test @@ -0,0 +1,60 @@ +# RUN: yaml2obj %s > %t + +# Single flags on a section with no flags: +# RUN: llvm-objcopy --rename-section=.foo=.bar,alloc %t %t.alloc +# RUN: llvm-readobj -sections %t.alloc | FileCheck %s --check-prefixes=CHECK,ALLOC,WRITE +# RUN: llvm-objcopy --rename-section=.foo=.bar,load %t %t.load +# RUN: llvm-readobj -sections %t.load | FileCheck %s --check-prefixes=CHECK,WRITE +# RUN: llvm-objcopy --rename-section=.foo=.bar,noload %t %t.noload +# RUN: llvm-readobj -sections %t.noload | FileCheck %s --check-prefixes=CHECK,WRITE +# RUN: llvm-objcopy --rename-section=.foo=.bar,readonly %t %t.readonly +# RUN: llvm-readobj -sections %t.readonly | FileCheck %s --check-prefixes=CHECK +# RUN: llvm-objcopy --rename-section=.foo=.bar,debug %t %t.debug +# RUN: llvm-readobj -sections %t.debug | FileCheck %s --check-prefixes=CHECK,WRITE +# RUN: llvm-objcopy --rename-section=.foo=.bar,code %t %t.code +# RUN: llvm-readobj -sections %t.code | FileCheck %s --check-prefixes=CHECK,EXEC,WRITE +# RUN: llvm-objcopy --rename-section=.foo=.bar,data %t %t.data +# RUN: llvm-readobj -sections %t.data | FileCheck %s --check-prefixes=CHECK,WRITE +# RUN: llvm-objcopy --rename-section=.foo=.bar,rom %t %t.rom +# RUN: llvm-readobj -sections %t.rom | FileCheck %s --check-prefixes=CHECK,WRITE +# RUN: llvm-objcopy --rename-section=.foo=.bar,contents %t %t.contents +# RUN: llvm-readobj -sections %t.contents | FileCheck %s --check-prefixes=CHECK,WRITE +# RUN: llvm-objcopy --rename-section=.foo=.bar,merge %t %t.merge +# RUN: llvm-readobj -sections %t.merge | FileCheck %s --check-prefixes=CHECK,MERGE,WRITE +# RUN: llvm-objcopy --rename-section=.foo=.bar,strings %t %t.strings +# RUN: llvm-readobj -sections %t.strings | FileCheck %s --check-prefixes=CHECK,STRINGS,WRITE +# RUN: llvm-objcopy --rename-section=.foo=.bar,share %t %t.share +# RUN: llvm-readobj -sections %t.share | FileCheck %s --check-prefixes=CHECK,WRITE + +# Multiple flags: +# RUN: llvm-objcopy --rename-section=.foo=.bar,alloc,readonly,strings %t %t.alloc_ro_strings +# RUN: llvm-readobj -sections %t.alloc_ro_strings | FileCheck %s --check-prefixes=CHECK,ALLOC,STRINGS +# RUN: llvm-objcopy --rename-section=.foo=.bar,alloc,code %t %t.alloc_code +# RUN: llvm-readobj -sections %t.alloc_code | FileCheck %s --check-prefixes=CHECK,ALLOC,EXEC,WRITE + +# Invalid flags: +# RUN: not llvm-objcopy --rename-section=.foo=.bar,xyzzy %t %t.xyzzy 2>&1 | FileCheck %s --check-prefix=BAD-FLAG + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .foo + Type: SHT_PROGBITS + Flags: [ ] + Content: "c3c3c3c3" + +# CHECK: Name: .bar +# CHECK-NEXT: Type: SHT_PROGBITS +# CHECK-NEXT: Flags [ +# ALLOC-NEXT: SHF_ALLOC (0x2) +# EXEC-NEXT: SHF_EXECINSTR (0x4) +# MERGE-NEXT: SHF_MERGE (0x10) +# STRINGS-NEXT: SHF_STRINGS (0x20) +# WRITE-NEXT: SHF_WRITE (0x1) +# CHECK-NEXT: ] + +# BAD-FLAG: Unrecognized section flag 'xyzzy'. Flags supported for GNU compatibility: alloc, load, noload, readonly, debug, code, data, rom, share, contents, merge, strings. Index: test/tools/llvm-objcopy/rename-section.test =================================================================== --- test/tools/llvm-objcopy/rename-section.test +++ test/tools/llvm-objcopy/rename-section.test @@ -26,5 +26,5 @@ # CHECK: Name: .strtab # CHECK: Name: .shstrtab -#BAD-FORMAT: Bad format for --rename-section -#MULTIPLE-RENAMES: Already have a section rename for .foo +#BAD-FORMAT: Bad format for --rename-section: missing '=' +#MULTIPLE-RENAMES: Multiple renames of section .foo Index: tools/llvm-objcopy/ObjcopyOpts.td =================================================================== --- tools/llvm-objcopy/ObjcopyOpts.td +++ tools/llvm-objcopy/ObjcopyOpts.td @@ -27,9 +27,13 @@ defm remove_section : Eq<"remove-section">, MetaVarName<"section">, HelpText<"Remove
">; -defm rename_section : Eq<"rename-section">, - MetaVarName<"old=new">, - HelpText<"Renames a section from old to new">; +defm rename_section + : Eq<"rename-section">, + MetaVarName<"old=new[,flag1,...]">, + HelpText< + "Renames a section from old to new, optionally with specified flags. " + "Flags supported for GNU compatibility: alloc, load, noload, " + "readonly, debug, code, data, rom, share, contents, merge, strings.">; defm redefine_symbol : Eq<"redefine-sym">, MetaVarName<"old=new">, HelpText<"Change the name of a symbol old to new">; Index: tools/llvm-objcopy/llvm-objcopy.cpp =================================================================== --- tools/llvm-objcopy/llvm-objcopy.cpp +++ tools/llvm-objcopy/llvm-objcopy.cpp @@ -9,7 +9,10 @@ #include "llvm-objcopy.h" #include "Object.h" +#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/ELF.h" @@ -115,6 +118,12 @@ StripOptTable() : OptTable(StripInfoTable, true) {} }; +struct SectionRename { + StringRef OriginalName; + StringRef NewName; + Optional NewFlags; +}; + struct CopyConfig { StringRef OutputFilename; StringRef InputFilename; @@ -133,7 +142,7 @@ std::vector SymbolsToWeaken; std::vector SymbolsToRemove; std::vector SymbolsToKeep; - StringMap SectionsToRename; + StringMap SectionsToRename; StringMap SymbolsToRename; bool StripAll = false; bool StripAllGNU = false; @@ -152,6 +161,23 @@ using SectionPred = std::function; +enum SectionFlag { + SecNone = 0, + SecAlloc = 1 << 0, + SecLoad = 1 << 1, + SecNoload = 1 << 2, + SecReadonly = 1 << 3, + SecDebug = 1 << 4, + SecCode = 1 << 5, + SecData = 1 << 6, + SecRom = 1 << 7, + SecMerge = 1 << 8, + SecStrings = 1 << 9, + SecContents = 1 << 10, + SecShare = 1 << 11, + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ SecShare) +}; + } // namespace namespace llvm { @@ -185,6 +211,65 @@ } // end namespace objcopy } // end namespace llvm +static SectionFlag ParseSectionRenameFlag(StringRef SectionName) { + return llvm::StringSwitch(SectionName) + .Case("alloc", SectionFlag::SecAlloc) + .Case("load", SectionFlag::SecLoad) + .Case("noload", SectionFlag::SecNoload) + .Case("readonly", SectionFlag::SecReadonly) + .Case("debug", SectionFlag::SecDebug) + .Case("code", SectionFlag::SecCode) + .Case("data", SectionFlag::SecData) + .Case("rom", SectionFlag::SecRom) + .Case("merge", SectionFlag::SecMerge) + .Case("strings", SectionFlag::SecStrings) + .Case("contents", SectionFlag::SecContents) + .Case("share", SectionFlag::SecShare) + .Default(SectionFlag::SecNone); +} + +static SectionRename ParseRenameSectionValue(StringRef FlagValue) { + if (!FlagValue.contains('=')) + error("Bad format for --rename-section: missing '='"); + + // Initial split: ".foo" = ".bar,f1,f2,..." + auto Old2New = FlagValue.split('='); + SectionRename SR; + SR.OriginalName = Old2New.first; + + // Flags split: ".bar" "f1" "f2" ... + SmallVector NameAndFlags; + Old2New.second.split(NameAndFlags, ','); + SR.NewName = NameAndFlags[0]; + + if (NameAndFlags.size() > 1) { + SectionFlag Flags = SectionFlag::SecNone; + for (size_t I = 1, Size = NameAndFlags.size(); I < Size; ++I) { + SectionFlag Flag = ParseSectionRenameFlag(NameAndFlags[I]); + if (Flag == SectionFlag::SecNone) + error("Unrecognized section flag '" + NameAndFlags[I] + + "'. Flags supported for GNU compatibility: alloc, load, noload, " + "readonly, debug, code, data, rom, share, contents, merge, " + "strings."); + Flags |= Flag; + } + + SR.NewFlags = 0; + if (Flags & SectionFlag::SecAlloc) + *SR.NewFlags |= ELF::SHF_ALLOC; + if (!(Flags & SectionFlag::SecReadonly)) + *SR.NewFlags |= ELF::SHF_WRITE; + if (Flags & SectionFlag::SecCode) + *SR.NewFlags |= ELF::SHF_EXECINSTR; + if (Flags & SectionFlag::SecMerge) + *SR.NewFlags |= ELF::SHF_MERGE; + if (Flags & SectionFlag::SecStrings) + *SR.NewFlags |= ELF::SHF_STRINGS; + } + + return SR; +} + static bool IsDebugSection(const SectionBase &Sec) { return Sec.Name.startswith(".debug") || Sec.Name.startswith(".zdebug"); } @@ -436,8 +521,20 @@ if (!Config.SectionsToRename.empty()) { for (auto &Sec : Obj.sections()) { const auto Iter = Config.SectionsToRename.find(Sec.Name); - if (Iter != Config.SectionsToRename.end()) - Sec.Name = Iter->second; + if (Iter != Config.SectionsToRename.end()) { + const SectionRename &SR = Iter->second; + Sec.Name = SR.NewName; + if (SR.NewFlags.hasValue()) { + // Preserve some flags which should not be dropped when setting flags. + // Also, preserve anything OS/processor dependant. + const uint64_t PreserveMask = ELF::SHF_COMPRESSED | ELF::SHF_EXCLUDE | + ELF::SHF_GROUP | ELF::SHF_LINK_ORDER | + ELF::SHF_MASKOS | ELF::SHF_MASKPROC | + ELF::SHF_TLS | ELF::SHF_INFO_LINK; + Sec.Flags = (Sec.Flags & PreserveMask) | + (SR.NewFlags.getValue() & ~PreserveMask); + } + } } } @@ -599,11 +696,9 @@ } for (auto Arg : InputArgs.filtered(OBJCOPY_rename_section)) { - if (!StringRef(Arg->getValue()).contains('=')) - error("Bad format for --rename-section"); - auto Old2New = StringRef(Arg->getValue()).split('='); - if (!Config.SectionsToRename.insert(Old2New).second) - error("Already have a section rename for " + Old2New.first); + SectionRename SR = ParseRenameSectionValue(StringRef(Arg->getValue())); + if (!Config.SectionsToRename.try_emplace(SR.OriginalName, SR).second) + error("Multiple renames of section " + SR.OriginalName); } for (auto Arg : InputArgs.filtered(OBJCOPY_remove_section))