Index: llvm/test/tools/llvm-objcopy/ELF/set-section-flags-and-rename.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/ELF/set-section-flags-and-rename.test @@ -0,0 +1,19 @@ +# RUN: yaml2obj %s > %t + +# RUN: not llvm-objcopy --rename-section=.foo=.bar --set-section-flags=.foo=alloc %t %t.2 2>&1 | FileCheck %s --check-prefix=SET-FOO +# RUN: not llvm-objcopy --rename-section=.foo=.bar --set-section-flags=.bar=alloc %t %t.2 2>&1 | FileCheck %s --check-prefix=SET-BAR + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .foo + Type: SHT_PROGBITS + Flags: [ ] + Content: "c3c3c3c3" + +# SET-FOO: --set-section-flags=.foo conflicts with --rename-section=.foo=.bar. +# SET-BAR: --set-section-flags=.bar conflicts with --rename-section=.foo=.bar. Index: llvm/test/tools/llvm-objcopy/ELF/set-section-flags-multiple.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/ELF/set-section-flags-multiple.test @@ -0,0 +1,34 @@ +# RUN: yaml2obj %s > %t + +# RUN: llvm-objcopy --set-section-flags=.foo=alloc --set-section-flags=.bar=code %t %t.2 +# RUN: llvm-readobj --sections %t.2 | FileCheck %s --check-prefixes=CHECK,ALLOC,WRITE + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .foo + Type: SHT_PROGBITS + Flags: [ ] + Content: "c3c3c3c3" + - Name: .bar + Type: SHT_PROGBITS + Flags: [ ] + Content: "d4d4d4d4" + +# CHECK: Name: .foo +# CHECK-NEXT: Type: SHT_PROGBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: SHF_ALLOC (0x2) +# CHECK-NEXT: SHF_WRITE (0x1) +# CHECK-NEXT: ] + +# CHECK: Name: .bar +# CHECK-NEXT: Type: SHT_PROGBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: SHF_EXECINSTR (0x4) +# CHECK-NEXT: SHF_WRITE (0x1) +# CHECK-NEXT: ] Index: llvm/test/tools/llvm-objcopy/ELF/set-section-flags.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/ELF/set-section-flags.test @@ -0,0 +1,69 @@ +# RUN: yaml2obj %s > %t + +# Single flags on a section with no flags: +# RUN: llvm-objcopy --set-section-flags=.foo=alloc %t %t.alloc +# RUN: llvm-readobj --sections %t.alloc | FileCheck %s --check-prefixes=CHECK,ALLOC,WRITE +# RUN: llvm-objcopy --set-section-flags=.foo=load %t %t.load +# RUN: llvm-readobj --sections %t.load | FileCheck %s --check-prefixes=CHECK,WRITE +# RUN: llvm-objcopy --set-section-flags=.foo=noload %t %t.noload +# RUN: llvm-readobj --sections %t.noload | FileCheck %s --check-prefixes=CHECK,WRITE +# RUN: llvm-objcopy --set-section-flags=.foo=readonly %t %t.readonly +# RUN: llvm-readobj --sections %t.readonly | FileCheck %s --check-prefixes=CHECK +# RUN: llvm-objcopy --set-section-flags=.foo=debug %t %t.debug +# RUN: llvm-readobj --sections %t.debug | FileCheck %s --check-prefixes=CHECK,WRITE +# RUN: llvm-objcopy --set-section-flags=.foo=code %t %t.code +# RUN: llvm-readobj --sections %t.code | FileCheck %s --check-prefixes=CHECK,EXEC,WRITE +# RUN: llvm-objcopy --set-section-flags=.foo=data %t %t.data +# RUN: llvm-readobj --sections %t.data | FileCheck %s --check-prefixes=CHECK,WRITE +# RUN: llvm-objcopy --set-section-flags=.foo=rom %t %t.rom +# RUN: llvm-readobj --sections %t.rom | FileCheck %s --check-prefixes=CHECK,WRITE +# RUN: llvm-objcopy --set-section-flags=.foo=contents %t %t.contents +# RUN: llvm-readobj --sections %t.contents | FileCheck %s --check-prefixes=CHECK,WRITE +# RUN: llvm-objcopy --set-section-flags=.foo=merge %t %t.merge +# RUN: llvm-readobj --sections %t.merge | FileCheck %s --check-prefixes=CHECK,MERGE,WRITE +# RUN: llvm-objcopy --set-section-flags=.foo=strings %t %t.strings +# RUN: llvm-readobj --sections %t.strings | FileCheck %s --check-prefixes=CHECK,STRINGS,WRITE +# RUN: llvm-objcopy --set-section-flags=.foo=share %t %t.share +# RUN: llvm-readobj --sections %t.share | FileCheck %s --check-prefixes=CHECK,WRITE + +# Multiple flags: +# RUN: llvm-objcopy --set-section-flags=.foo=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 --set-section-flags=.foo=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 --set-section-flags=.foo=xyzzy %t %t.xyzzy 2>&1 | FileCheck %s --check-prefix=BAD-FLAG + +# Bad flag format: +# RUN: not llvm-objcopy --set-section-flags=.foo %t %t2 2>&1 | FileCheck %s --check-prefix=BAD-FORMAT + +# Setting flags for the same section multiple times: +# RUN: not llvm-objcopy --set-section-flags=.foo=alloc --set-section-flags=.foo=load %t %t2 2>&1 | FileCheck %s --check-prefix=MULTIPLE-SETS + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .foo + Type: SHT_PROGBITS + Flags: [ ] + Content: "c3c3c3c3" + +# CHECK: Name: .foo +# 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-FORMAT: Bad format for --set-section-flags: missing '=' +# MULTIPLE-SETS: --set-section-flags set multiple times for section .foo + +# BAD-FLAG: Unrecognized section flag 'xyzzy'. Flags supported for GNU compatibility: alloc, load, noload, readonly, debug, code, data, rom, share, contents, merge, strings. Index: llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp =================================================================== --- llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp +++ llvm/tools/llvm-objcopy/COFF/COFFObjcopy.cpp @@ -177,10 +177,10 @@ !Config.SymbolsToGlobalize.empty() || !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() || - !Config.SymbolsToRename.empty() || Config.ExtractDWO || - Config.KeepFileSymbols || Config.LocalizeHidden || Config.PreserveDates || - Config.StripDWO || Config.StripNonAlloc || Config.StripSections || - Config.Weaken || Config.DecompressDebugSections) { + !Config.SetSectionFlags.empty() || !Config.SymbolsToRename.empty() || + Config.ExtractDWO || Config.KeepFileSymbols || Config.LocalizeHidden || + Config.PreserveDates || Config.StripDWO || Config.StripNonAlloc || + Config.StripSections || Config.Weaken || Config.DecompressDebugSections) { return createStringError(llvm::errc::invalid_argument, "Option not supported by llvm-objcopy for COFF"); } Index: llvm/tools/llvm-objcopy/CopyConfig.h =================================================================== --- llvm/tools/llvm-objcopy/CopyConfig.h +++ llvm/tools/llvm-objcopy/CopyConfig.h @@ -73,6 +73,7 @@ // Map options StringMap SectionsToRename; + StringMap SetSectionFlags; StringMap SymbolsToRename; // Boolean options Index: llvm/tools/llvm-objcopy/CopyConfig.cpp =================================================================== --- llvm/tools/llvm-objcopy/CopyConfig.cpp +++ llvm/tools/llvm-objcopy/CopyConfig.cpp @@ -128,6 +128,32 @@ .Default(SectionFlag::SecNone); } +static uint64_t parseSectionFlagSet(ArrayRef SectionFlags) { + SectionFlag ParsedFlags = SectionFlag::SecNone; + for (StringRef Flag : SectionFlags) { + SectionFlag ParsedFlag = parseSectionRenameFlag(Flag); + if (ParsedFlag == SectionFlag::SecNone) + error("Unrecognized section flag '" + Flag + + "'. Flags supported for GNU compatibility: alloc, load, noload, " + "readonly, debug, code, data, rom, share, contents, merge, " + "strings."); + ParsedFlags |= ParsedFlag; + } + + uint64_t NewFlags = 0; + if (ParsedFlags & SectionFlag::SecAlloc) + NewFlags |= ELF::SHF_ALLOC; + if (!(ParsedFlags & SectionFlag::SecReadonly)) + NewFlags |= ELF::SHF_WRITE; + if (ParsedFlags & SectionFlag::SecCode) + NewFlags |= ELF::SHF_EXECINSTR; + if (ParsedFlags & SectionFlag::SecMerge) + NewFlags |= ELF::SHF_MERGE; + if (ParsedFlags & SectionFlag::SecStrings) + NewFlags |= ELF::SHF_STRINGS; + return NewFlags; +} + static SectionRename parseRenameSectionValue(StringRef FlagValue) { if (!FlagValue.contains('=')) error("Bad format for --rename-section: missing '='"); @@ -142,34 +168,18 @@ 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; - } + if (NameAndFlags.size() > 1) + SR.NewFlags = parseSectionFlagSet(makeArrayRef(NameAndFlags).drop_front()); return SR; } +static uint64_t parseSetSectionFlagValue(StringRef FlagValue) { + SmallVector SectionFlags; + FlagValue.split(SectionFlags, ','); + return parseSectionFlagSet(SectionFlags); +} + static const StringMap ArchMap{ // Name, {EMachine, 64bit, LittleEndian} {"aarch64", {ELF::EM_AARCH64, true, true}}, @@ -327,6 +337,29 @@ if (!Config.SectionsToRename.try_emplace(SR.OriginalName, SR).second) error("Multiple renames of section " + SR.OriginalName); } + for (auto Arg : InputArgs.filtered(OBJCOPY_set_section_flags)) { + if (!StringRef(Arg->getValue()).contains('=')) + error("Bad format for --set-section-flags: missing '='"); + auto Section2Flags = StringRef(Arg->getValue()).split('='); + uint64_t NewFlags = parseSetSectionFlagValue(Section2Flags.second); + if (!Config.SetSectionFlags.try_emplace(Section2Flags.first, NewFlags) + .second) + error("--set-section-flags set multiple times for section " + + Section2Flags.first); + } + // Prohibit combinations of --set-section-flags when the section name is used + // by --rename-section, either as a source or a destination. + for (const auto &E : Config.SectionsToRename) { + const SectionRename &SR = E.second; + if (Config.SetSectionFlags.count(SR.OriginalName)) + error("--set-section-flags=" + SR.OriginalName + + " conflicts with --rename-section=" + SR.OriginalName + "=" + + SR.NewName); + if (Config.SetSectionFlags.count(SR.NewName)) + error("--set-section-flags=" + SR.NewName + + " conflicts with --rename-section=" + SR.OriginalName + "=" + + SR.NewName); + } for (auto Arg : InputArgs.filtered(OBJCOPY_remove_section)) Config.ToRemove.push_back(Arg->getValue()); Index: llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp =================================================================== --- llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -70,6 +70,17 @@ return !isDWOSection(Sec); } +static uint64_t setSectionFlagsPreserveMask(uint64_t OldFlags, + uint64_t NewFlags) { + // 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; + return (OldFlags & PreserveMask) | (NewFlags & ~PreserveMask); +} + static ElfType getOutputElfType(const Binary &Bin) { // Infer output ELF type from the input ELF object if (isa>(Bin)) @@ -484,20 +495,21 @@ 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); - } + if (SR.NewFlags.hasValue()) + Sec.Flags = + setSectionFlagsPreserveMask(Sec.Flags, SR.NewFlags.getValue()); } } } + if (!Config.SetSectionFlags.empty()) { + for (auto &Sec : Obj.sections()) { + const auto Iter = Config.SetSectionFlags.find(Sec.Name); + if (Iter != Config.SetSectionFlags.end()) + Sec.Flags = setSectionFlagsPreserveMask(Sec.Flags, Iter->second); + } + } + if (!Config.AddSection.empty()) { for (const auto &Flag : Config.AddSection) { std::pair SecPair = Flag.split("="); Index: llvm/tools/llvm-objcopy/ObjcopyOpts.td =================================================================== --- llvm/tools/llvm-objcopy/ObjcopyOpts.td +++ llvm/tools/llvm-objcopy/ObjcopyOpts.td @@ -86,6 +86,13 @@ "Make a section named
with the contents of .">, MetaVarName<"section=file">; +defm set_section_flags + : Eq<"set-section-flags", + "Set section flags for a given section. Flags supported for GNU " + "compatibility: alloc, load, noload, readonly, debug, code, data, " + "rom, share, contents, merge, strings.">, + MetaVarName<"section=flag1[,flag2,...]">; + def strip_all : Flag<["-", "--"], "strip-all">, HelpText<