diff --git a/llvm/docs/CommandGuide/llvm-objcopy.rst b/llvm/docs/CommandGuide/llvm-objcopy.rst --- a/llvm/docs/CommandGuide/llvm-objcopy.rst +++ b/llvm/docs/CommandGuide/llvm-objcopy.rst @@ -428,6 +428,11 @@ specified ```` values. See :option:`--set-section-flags` for a list of supported flags. Can be specified multiple times to rename multiple sections. +.. option:: --set-section-type
= + + Set the type of section ``
`` to the integer ````. Can be + specified multiple times to update multiple sections. + .. option:: --set-start-addr Set the start address of the output to ````. Overrides any previously diff --git a/llvm/include/llvm/ObjCopy/CommonConfig.h b/llvm/include/llvm/ObjCopy/CommonConfig.h --- a/llvm/include/llvm/ObjCopy/CommonConfig.h +++ b/llvm/include/llvm/ObjCopy/CommonConfig.h @@ -241,6 +241,7 @@ StringMap SectionsToRename; StringMap SetSectionAlignment; StringMap SetSectionFlags; + StringMap SetSectionType; StringMap SymbolsToRename; // Symbol info specified by --add-symbol option. diff --git a/llvm/lib/ObjCopy/ConfigManager.cpp b/llvm/lib/ObjCopy/ConfigManager.cpp --- a/llvm/lib/ObjCopy/ConfigManager.cpp +++ b/llvm/lib/ObjCopy/ConfigManager.cpp @@ -20,9 +20,9 @@ !Common.SymbolsToKeep.empty() || !Common.SymbolsToLocalize.empty() || !Common.SymbolsToWeaken.empty() || !Common.SymbolsToKeepGlobal.empty() || !Common.SectionsToRename.empty() || !Common.SetSectionAlignment.empty() || - Common.ExtractDWO || Common.PreserveDates || Common.StripDWO || - Common.StripNonAlloc || Common.StripSections || Common.Weaken || - Common.DecompressDebugSections || + !Common.SetSectionType.empty() || Common.ExtractDWO || + Common.PreserveDates || Common.StripDWO || Common.StripNonAlloc || + Common.StripSections || Common.Weaken || Common.DecompressDebugSections || Common.DiscardMode == DiscardType::Locals || !Common.SymbolsToAdd.empty()) return createStringError(llvm::errc::invalid_argument, "option is not supported for COFF"); @@ -38,9 +38,10 @@ !Common.SymbolsToKeepGlobal.empty() || !Common.SectionsToRename.empty() || !Common.UnneededSymbolsToRemove.empty() || !Common.SetSectionAlignment.empty() || !Common.SetSectionFlags.empty() || - Common.ExtractDWO || Common.PreserveDates || Common.StripAllGNU || - Common.StripDWO || Common.StripNonAlloc || Common.StripSections || - Common.Weaken || Common.DecompressDebugSections || Common.StripUnneeded || + !Common.SetSectionType.empty() || Common.ExtractDWO || + Common.PreserveDates || Common.StripAllGNU || Common.StripDWO || + Common.StripNonAlloc || Common.StripSections || Common.Weaken || + Common.DecompressDebugSections || Common.StripUnneeded || Common.DiscardMode == DiscardType::Locals || !Common.SymbolsToAdd.empty()) return createStringError(llvm::errc::invalid_argument, "option is not supported for MachO"); @@ -58,7 +59,8 @@ !Common.UnneededSymbolsToRemove.empty() || !Common.SymbolsToWeaken.empty() || !Common.SymbolsToKeepGlobal.empty() || !Common.SectionsToRename.empty() || !Common.SetSectionAlignment.empty() || - !Common.SetSectionFlags.empty() || !Common.SymbolsToRename.empty()) + !Common.SetSectionFlags.empty() || !Common.SetSectionType.empty() || + !Common.SymbolsToRename.empty()) return createStringError(llvm::errc::invalid_argument, "only flags for section dumping, removal, and " "addition are supported"); @@ -79,12 +81,12 @@ !Common.UnneededSymbolsToRemove.empty() || !Common.SymbolsToWeaken.empty() || !Common.SymbolsToKeepGlobal.empty() || !Common.SectionsToRename.empty() || !Common.SetSectionAlignment.empty() || - !Common.SetSectionFlags.empty() || !Common.SymbolsToRename.empty() || - Common.ExtractDWO || Common.ExtractMainPartition || - Common.OnlyKeepDebug || Common.PreserveDates || Common.StripAllGNU || - Common.StripDWO || Common.StripDebug || Common.StripNonAlloc || - Common.StripSections || Common.Weaken || Common.StripUnneeded || - Common.DecompressDebugSections) { + !Common.SetSectionFlags.empty() || !Common.SetSectionType.empty() || + !Common.SymbolsToRename.empty() || Common.ExtractDWO || + Common.ExtractMainPartition || Common.OnlyKeepDebug || + Common.PreserveDates || Common.StripAllGNU || Common.StripDWO || + Common.StripDebug || Common.StripNonAlloc || Common.StripSections || + Common.Weaken || Common.StripUnneeded || Common.DecompressDebugSections) { return createStringError( llvm::errc::invalid_argument, "no flags are supported yet, only basic copying is allowed"); diff --git a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp --- a/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp +++ b/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp @@ -675,14 +675,17 @@ for (const NewSymbolInfo &SI : Config.SymbolsToAdd) addSymbol(Obj, SI, ELFConfig.NewSymbolVisibility); - // --set-section-flags works with sections added by --add-section. - if (!Config.SetSectionFlags.empty()) { + // --set-section-{flags,type} work with sections added by --add-section. + if (!Config.SetSectionFlags.empty() || !Config.SetSectionType.empty()) { for (auto &Sec : Obj.sections()) { const auto Iter = Config.SetSectionFlags.find(Sec.Name); if (Iter != Config.SetSectionFlags.end()) { const SectionFlagsUpdate &SFU = Iter->second; setSectionFlagsAndType(Sec, SFU.NewFlags); } + auto It2 = Config.SetSectionType.find(Sec.Name); + if (It2 != Config.SetSectionType.end()) + Sec.Type = It2->second; } } diff --git a/llvm/test/tools/llvm-objcopy/ELF/add-section-and-set-attr.test b/llvm/test/tools/llvm-objcopy/ELF/add-section-and-set-attr.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/ELF/add-section-and-set-attr.test @@ -0,0 +1,28 @@ +## Check --set-section-flags/--set-section-types work with sections added by --add-section. + +# RUN: yaml2obj %s -o %t +# RUN: llvm-objcopy --add-section=foo=/dev/null --set-section-flags=foo=alloc,exclude %t %t.1 +# RUN: llvm-readobj -S %t.1 | FileCheck %s + +# CHECK: Name: foo +# CHECK-NEXT: Type: SHT_PROGBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: SHF_ALLOC +# CHECK-NEXT: SHF_EXCLUDE +# CHECK-NEXT: SHF_WRITE +# CHECK-NEXT: ] + +# RUN: llvm-objcopy --add-section=foo=/dev/null --set-section-type=foo=7 %t %t.2 +# RUN: llvm-readobj -S %t.2 | FileCheck %s --check-prefix=CHECK2 + +# CHECK2: Name: foo +# CHECK2-NEXT: Type: SHT_NOTE (0x7) +# CHECK2-NEXT: Flags [ +# CHECK2-NEXT: ] + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 diff --git a/llvm/test/tools/llvm-objcopy/ELF/add-section-and-set-flags.test b/llvm/test/tools/llvm-objcopy/ELF/add-section-and-set-flags.test deleted file mode 100644 --- a/llvm/test/tools/llvm-objcopy/ELF/add-section-and-set-flags.test +++ /dev/null @@ -1,20 +0,0 @@ -## Check --set-section-flags works with sections added by --add-section. - -# RUN: yaml2obj %s -o %t -# RUN: llvm-objcopy --add-section=foo=/dev/null --set-section-flags=foo=alloc,exclude %t %t.out -# RUN: llvm-readobj -S %t.out | FileCheck %s - -# CHECK: Name: foo -# CHECK-NEXT: Type: SHT_PROGBITS -# CHECK-NEXT: Flags [ -# CHECK-NEXT: SHF_ALLOC -# CHECK-NEXT: SHF_EXCLUDE -# CHECK-NEXT: SHF_WRITE -# CHECK-NEXT: ] - ---- !ELF -FileHeader: - Class: ELFCLASS64 - Data: ELFDATA2LSB - Type: ET_EXEC - Machine: EM_X86_64 diff --git a/llvm/test/tools/llvm-objcopy/ELF/set-section-alignment.test b/llvm/test/tools/llvm-objcopy/ELF/set-section-alignment.test --- a/llvm/test/tools/llvm-objcopy/ELF/set-section-alignment.test +++ b/llvm/test/tools/llvm-objcopy/ELF/set-section-alignment.test @@ -43,7 +43,7 @@ # RUN: not llvm-objcopy --set-section-alignment=.foo=bar %t /dev/null 2>&1 | \ # RUN: FileCheck --check-prefix=INVALID-ALIGN %s -# INVALID-ALIGN: error: invalid alignment for --set-section-alignment: 'bar' +# INVALID-ALIGN: error: invalid value for --set-section-alignment: 'bar' !ELF FileHeader: diff --git a/llvm/test/tools/llvm-objcopy/ELF/set-section-attr-and-rename.test b/llvm/test/tools/llvm-objcopy/ELF/set-section-attr-and-rename.test --- a/llvm/test/tools/llvm-objcopy/ELF/set-section-attr-and-rename.test +++ b/llvm/test/tools/llvm-objcopy/ELF/set-section-attr-and-rename.test @@ -1,10 +1,10 @@ # RUN: yaml2obj %s -o %t -# RUN: llvm-objcopy --rename-section=.foo=.bar --set-section-alignment=.foo=16 --set-section-flags=.foo=alloc %t %t.1 +# RUN: llvm-objcopy --rename-section=.foo=.bar --set-section-alignment=.foo=16 --set-section-flags=.foo=alloc --set-section-type=.foo=5 %t %t.1 # RUN: llvm-readobj -S %t.1 | FileCheck %s # CHECK: Name: .bar -# CHECK-NEXT: Type: SHT_PROGBITS +# CHECK-NEXT: Type: SHT_HASH (0x5) # CHECK-NEXT: Flags [ # CHECK-NEXT: SHF_ALLOC # CHECK-NEXT: SHF_WRITE @@ -13,7 +13,12 @@ # CHECK-SAME: {{^}} 16 # RUN: not llvm-objcopy --rename-section=.foo=.bar --set-section-flags=.bar=alloc %t %t.2 2>&1 | \ -# RUN: FileCheck %s --check-prefix=SET-BAR +# RUN: FileCheck %s --check-prefix=SET-BAR1 +# SET-BAR1: --set-section-flags=.bar conflicts with --rename-section=.foo=.bar + +# RUN: not llvm-objcopy --rename-section=.foo=.bar --set-section-type=.bar=1 %t %t.2 2>&1 | \ +# RUN: FileCheck %s --check-prefix=SET-BAR2 +# SET-BAR2: --set-section-type=.bar conflicts with --rename-section=.foo=.bar !ELF FileHeader: @@ -25,5 +30,3 @@ - Name: .foo Type: SHT_PROGBITS Flags: [ SHF_ALLOC ] - -# SET-BAR: --set-section-flags=.bar conflicts with --rename-section=.foo=.bar diff --git a/llvm/test/tools/llvm-objcopy/ELF/set-section-type.test b/llvm/test/tools/llvm-objcopy/ELF/set-section-type.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/ELF/set-section-type.test @@ -0,0 +1,54 @@ +# RUN: yaml2obj %s -o %t + +# RUN: llvm-objcopy --set-section-type=.foo=14 --set-section-type .bar=0xf %t %t.1 +# RUN: llvm-readobj --sections %t.1 | FileCheck %s + +# RUN: llvm-objcopy --set-section-type=.foo=13 --set-section-type=.foo=14 --set-section-type .bar=0xf %t %t.1 +# RUN: llvm-readobj --sections %t.1 | FileCheck %s + +# CHECK: Name: .foo +# CHECK-NEXT: Type: SHT_INIT_ARRAY (0xE) +# CHECK-NEXT: Flags [ +# CHECK-NEXT: SHF_ALLOC +# CHECK-NEXT: ] + +# CHECK: Name: .bar +# CHECK-NEXT: Type: SHT_FINI_ARRAY (0xF) +# CHECK-NEXT: Flags [ +# CHECK-NEXT: ] + +## --set-section-flags does not specify "readonly", so the output gets SHF_WRITE. +## "contents" changes SHT_NOBITS to SHT_PROGBITS, but this is overridden by --set-section-type. +## sh_type is a uint32_t. There is no diagnostic for an overflow value. +# RUN: llvm-objcopy --set-section-flags=.foo=alloc,contents --set-section-type=.foo=0x10000000a %t %t.2 2>&1 | count 0 +# RUN: llvm-readobj --sections %t.2 | FileCheck %s --check-prefix=CHECK2 + +# CHECK2: Name: .foo +# CHECK2-NEXT: Type: SHT_SHLIB +# CHECK2-NEXT: Flags [ +# CHECK2-NEXT: SHF_ALLOC +# CHECK2-NEXT: SHF_WRITE +# CHECK2-NEXT: ] + +# RUN: not llvm-objcopy --set-section-type=.foo %t /dev/null 2>&1 | FileCheck %s --check-prefix=BAD-FORMAT +# BAD-FORMAT: bad format for --set-section-type: missing '=' + +# RUN: not llvm-objcopy --set-section-type==4 %t /dev/null 2>&1 | FileCheck %s --check-prefix=MISSING-SECTION +# MISSING-SECTION: error: bad format for --set-section-type: missing section name + +# RUN: not llvm-objcopy --set-section-type=.foo=aaa %t /dev/null 2>&1 | FileCheck %s --check-prefix=INVALID-TYPE +# INVALID-TYPE: error: invalid value for --set-section-type: 'aaa' + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .foo + Type: SHT_NOBITS + Flags: [ SHF_ALLOC ] + - Name: .bar + Type: SHT_PROGBITS + Flags: [ ] diff --git a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp --- a/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp +++ b/llvm/tools/llvm-objcopy/ObjcopyOptions.cpp @@ -236,23 +236,21 @@ } static Expected> -parseSetSectionAlignment(StringRef FlagValue) { +parseSetSectionAttribute(StringRef Option, StringRef FlagValue) { if (!FlagValue.contains('=')) - return createStringError( - errc::invalid_argument, - "bad format for --set-section-alignment: missing '='"); + return make_error("bad format for " + Option + ": missing '='", + errc::invalid_argument); auto Split = StringRef(FlagValue).split('='); if (Split.first.empty()) - return createStringError( - errc::invalid_argument, - "bad format for --set-section-alignment: missing section name"); - uint64_t NewAlign; - if (Split.second.getAsInteger(0, NewAlign)) - return createStringError( - errc::invalid_argument, - "invalid alignment for --set-section-alignment: '%s'", - Split.second.str().c_str()); - return std::make_pair(Split.first, NewAlign); + return make_error("bad format for " + Option + + ": missing section name", + errc::invalid_argument); + uint64_t Value; + if (Split.second.getAsInteger(0, Value)) + return make_error("invalid value for " + Option + ": '" + + Split.second + "'", + errc::invalid_argument); + return std::make_pair(Split.first, Value); } static Expected @@ -793,7 +791,7 @@ } for (auto Arg : InputArgs.filtered(OBJCOPY_set_section_alignment)) { Expected> NameAndAlign = - parseSetSectionAlignment(Arg->getValue()); + parseSetSectionAttribute("--set-section-alignment", Arg->getValue()); if (!NameAndAlign) return NameAndAlign.takeError(); Config.SetSectionAlignment[NameAndAlign->first] = NameAndAlign->second; @@ -809,16 +807,28 @@ "--set-section-flags set multiple times for section '%s'", SFU->Name.str().c_str()); } - // Prohibit combinations of --set-section-flags when the section name is used - // as the destination of a --rename-section. + for (auto Arg : InputArgs.filtered(OBJCOPY_set_section_type)) { + Expected> NameAndType = + parseSetSectionAttribute("--set-section-type", Arg->getValue()); + if (!NameAndType) + return NameAndType.takeError(); + Config.SetSectionType[NameAndType->first] = NameAndType->second; + } + // Prohibit combinations of --set-section-{flags,type} when the section name + // is used as the destination of a --rename-section. for (const auto &E : Config.SectionsToRename) { const SectionRename &SR = E.second; - if (Config.SetSectionFlags.count(SR.NewName)) + auto Err = [&](const char *Option) { return createStringError( errc::invalid_argument, - "--set-section-flags=%s conflicts with --rename-section=%s=%s", + "--set-section-%s=%s conflicts with --rename-section=%s=%s", Option, SR.NewName.str().c_str(), SR.OriginalName.str().c_str(), SR.NewName.str().c_str()); + }; + if (Config.SetSectionFlags.count(SR.NewName)) + return Err("flags"); + if (Config.SetSectionType.count(SR.NewName)) + return Err("type"); } for (auto Arg : InputArgs.filtered(OBJCOPY_remove_section)) diff --git a/llvm/tools/llvm-objcopy/ObjcopyOpts.td b/llvm/tools/llvm-objcopy/ObjcopyOpts.td --- a/llvm/tools/llvm-objcopy/ObjcopyOpts.td +++ b/llvm/tools/llvm-objcopy/ObjcopyOpts.td @@ -87,6 +87,11 @@ "data, rom, share, contents, merge, strings.">, MetaVarName<"section=flag1[,flag2,...]">; +defm set_section_type + : Eq<"set-section-type", + "Set the type of section
to the integer ">, + MetaVarName<"section=type">; + def S : Flag<["-"], "S">, Alias, HelpText<"Alias for --strip-all">;