Index: llvm/docs/CommandGuide/llvm-objcopy.rst =================================================================== --- llvm/docs/CommandGuide/llvm-objcopy.rst +++ llvm/docs/CommandGuide/llvm-objcopy.rst @@ -137,8 +137,9 @@ sections. Supported flag names are `alloc`, `load`, `noload`, `readonly`, `exclude`, - `debug`, `code`, `data`, `rom`, `share`, `contents`, `merge` and `strings`. Not - all flags are meaningful for all object file formats. + `debug`, `code`, `data`, `rom`, `share`, `contents`, `merge`, `strings`, and + `large`. Not all flags are meaningful for all object file formats or target + architectures. For ELF objects, the flags have the following effects: @@ -152,6 +153,8 @@ - `strings` = add the `SHF_STRINGS` flag. - `contents` = if the section has `SHT_NOBITS` type, mark it as a `SHT_PROGBITS` section. + - `large` = add the `SHF_X86_64_LARGE` on x86_64; rejected if the target + architecture is not x86_64. For COFF objects, the flags have the following effects: Index: llvm/include/llvm/ObjCopy/CommonConfig.h =================================================================== --- llvm/include/llvm/ObjCopy/CommonConfig.h +++ llvm/include/llvm/ObjCopy/CommonConfig.h @@ -69,7 +69,8 @@ SecContents = 1 << 10, SecShare = 1 << 11, SecExclude = 1 << 12, - LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/SecExclude) + SecLarge = 1 << 13, + LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/SecLarge) }; struct SectionRename { Index: llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp =================================================================== --- llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp +++ llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp @@ -68,7 +68,8 @@ return !isDWOSection(Sec); } -static uint64_t getNewShfFlags(SectionFlag AllFlags) { +static Expected getNewShfFlags(SectionFlag AllFlags, + uint16_t EMachine) { uint64_t NewFlags = 0; if (AllFlags & SectionFlag::SecAlloc) NewFlags |= ELF::SHF_ALLOC; @@ -82,18 +83,26 @@ NewFlags |= ELF::SHF_STRINGS; if (AllFlags & SectionFlag::SecExclude) NewFlags |= ELF::SHF_EXCLUDE; + if (AllFlags & SectionFlag::SecLarge) { + if (EMachine != EM_X86_64) + return createStringError(errc::invalid_argument, + "section flag SHF_X86_64_LARGE can only be used " + "with x86_64 architecture"); + NewFlags |= ELF::SHF_X86_64_LARGE; + } return NewFlags; } -static uint64_t getSectionFlagsPreserveMask(uint64_t OldFlags, - uint64_t NewFlags) { +static uint64_t getSectionFlagsPreserveMask( + uint64_t OldFlags, uint64_t NewFlags, uint16_t EMachine) { // 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_GROUP | ELF::SHF_LINK_ORDER | ELF::SHF_MASKOS | ELF::SHF_MASKPROC | ELF::SHF_TLS | ELF::SHF_INFO_LINK) & - ~ELF::SHF_EXCLUDE; + ~ELF::SHF_EXCLUDE & + ~(EMachine == EM_X86_64 ? ELF::SHF_X86_64_LARGE : 0UL); return (OldFlags & PreserveMask) | (NewFlags & ~PreserveMask); } @@ -105,8 +114,12 @@ Sec.Type = Type; } -static void setSectionFlagsAndType(SectionBase &Sec, SectionFlag Flags) { - Sec.Flags = getSectionFlagsPreserveMask(Sec.Flags, getNewShfFlags(Flags)); +static Error setSectionFlagsAndType(SectionBase &Sec, SectionFlag Flags, + uint16_t EMachine) { + Expected NewFlags = getNewShfFlags(Flags, EMachine); + if (!NewFlags) + return NewFlags.takeError(); + Sec.Flags = getSectionFlagsPreserveMask(Sec.Flags, *NewFlags, EMachine); // In GNU objcopy, certain flags promote SHT_NOBITS to SHT_PROGBITS. This rule // may promote more non-ALLOC sections than GNU objcopy, but it is fine as @@ -115,6 +128,8 @@ (!(Sec.Flags & ELF::SHF_ALLOC) || Flags & (SectionFlag::SecContents | SectionFlag::SecLoad))) setSectionType(Sec, ELF::SHT_PROGBITS); + + return Error::success(); } static ElfType getOutputElfType(const Binary &Bin) { @@ -681,7 +696,8 @@ const auto Iter = Config.SetSectionFlags.find(Sec.Name); if (Iter != Config.SetSectionFlags.end()) { const SectionFlagsUpdate &SFU = Iter->second; - setSectionFlagsAndType(Sec, SFU.NewFlags); + if (Error E = setSectionFlagsAndType(Sec, SFU.NewFlags, Obj.Machine)) + return E; } auto It2 = Config.SetSectionType.find(Sec.Name); if (It2 != Config.SetSectionType.end()) @@ -698,8 +714,10 @@ if (Iter != Config.SectionsToRename.end()) { const SectionRename &SR = Iter->second; Sec.Name = std::string(SR.NewName); - if (SR.NewFlags) - setSectionFlagsAndType(Sec, *SR.NewFlags); + if (SR.NewFlags) { + if (Error E = setSectionFlagsAndType(Sec, *SR.NewFlags, Obj.Machine)) + return E; + } RenamedSections.insert(&Sec); } else if (RelocSec && !(Sec.Flags & SHF_ALLOC)) // Postpone processing relocation sections which are not specified in Index: llvm/test/tools/llvm-objcopy/ELF/rename-section-flag-osproc-mask.test =================================================================== --- llvm/test/tools/llvm-objcopy/ELF/rename-section-flag-osproc-mask.test +++ llvm/test/tools/llvm-objcopy/ELF/rename-section-flag-osproc-mask.test @@ -1,10 +1,14 @@ -# Test that cpu/processor-specific SHF_* flags are preserved. +# Test that cpu/processor-specific SHF_* flags are preserved, +# except SHF_X86_64_LARGE on x86_64 (which is controlled with the +# "large" flag). # ===== x86_64 ===== # RUN: yaml2obj --docnum=1 %s -o %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 +# RUN: llvm-objcopy --rename-section=.bar=.quz,alloc,large %t-x86_64.o +# RUN: llvm-readobj --sections %t-x86_64.o | FileCheck %s --check-prefix=X86_64-LARGE --- !ELF FileHeader: @@ -22,9 +26,16 @@ # 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: ] +# X86_64-LARGE: Name: .quz +# X86_64-LARGE-NEXT: Type: SHT_PROGBITS +# X86_64-LARGE-NEXT: Flags [ +# X86_64-LARGE-NEXT: SHF_ALLOC (0x2) +# X86_64-LARGE-NEXT: SHF_WRITE (0x1) +# X86_64-LARGE-NEXT: SHF_X86_64_LARGE (0x10000000) +# X86_64-LARGE-NEXT: ] + # ===== hex ===== # RUN: yaml2obj --docnum=2 %s -o %t-hex.o Index: llvm/test/tools/llvm-objcopy/ELF/set-section-flags-large-multiarch.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/ELF/set-section-flags-large-multiarch.test @@ -0,0 +1,61 @@ +# RUN: yaml2obj %s -o %t + +## When targetting non-x86_64, the "large" flag is not allowed: +# RUN: not llvm-objcopy -O elf64-tradbigmips --set-section-flags=.foo=large %t %t.mips.large 2>&1 | FileCheck %s --check-prefixes=BAD-LARGE +# BAD-LARGE: error: {{.*}}: section flag SHF_X86_64_LARGE can only be used with x86_64 architecture + +## Converting x86_64 to non-x86_64 preserves the flag equivalent to SHF_X86_64_LARGE. +## (This is not a deliberate feature, but it reflects how preservation/setting of flags +## works for arch-specific flags.) +# RUN: llvm-objcopy -O elf64-tradbigmips --set-section-flags=.foo=alloc --set-section-flags=.bar=alloc %t %t.mips +# RUN: llvm-readobj --sections %t.mips | FileCheck %s --check-prefixes=CHECK,REINTERPRET-GPREL + +## Converting non-x86_64 to x86_64 clears the flag equivalent to SHF_X86_64_LARGE, +## and SHF_X86_64_LARGE is set according to the presence of the "large" flag, +## as long as --set-section-flags is used. If --set-section-flag is _not_ used, then +## the section flag is retained. (This latter behaviour is also not deliberate.) +# RUN: llvm-objcopy -O elf64-x86-64 --set-section-flags=.foo=alloc --set-section-flags=.bar=alloc %t.mips %t.x86_64.no-large +# RUN: llvm-readobj --sections %t.x86_64.no-large | FileCheck %s --check-prefixes=CHECK,REINTERPRET-LARGE +# RUN: llvm-objcopy -O elf64-x86-64 --set-section-flags=.foo=alloc,large --set-section-flags=.bar=alloc,large %t.mips %t.x86_64.large +# RUN: llvm-readobj --sections %t.x86_64.large | FileCheck %s --check-prefixes=CHECK,LARGE,REINTERPRET-LARGE + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .foo + Type: SHT_PROGBITS + Flags: [ ] + - Name: .bar + Type: SHT_PROGBITS + Flags: [ SHF_X86_64_LARGE ] + - Name: .untouched + Type: SHT_PROGBITS + Flags: [ SHF_X86_64_LARGE ] + +# CHECK: Name: .foo +# CHECK-NEXT: Type: SHT_PROGBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: SHF_ALLOC (0x2) +# CHECK-NEXT: SHF_WRITE (0x1) +# LARGE-NEXT: SHF_X86_64_LARGE (0x10000000) +# CHECK-NEXT: ] + +# CHECK: Name: .bar +# CHECK-NEXT: Type: SHT_PROGBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: SHF_ALLOC (0x2) +# REINTERPRET-GPREL-NEXT: SHF_MIPS_GPREL (0x10000000) +# CHECK-NEXT: SHF_WRITE (0x1) +# LARGE-NEXT: SHF_X86_64_LARGE (0x10000000) +# CHECK-NEXT: ] + +# CHECK: Name: .untouched +# CHECK-NEXT: Type: SHT_PROGBITS +# CHECK-NEXT: Flags [ +# REINTERPRET-GPREL-NEXT: SHF_MIPS_GPREL (0x10000000) +# REINTERPRET-LARGE-NEXT: SHF_X86_64_LARGE (0x10000000) +# CHECK-NEXT: ] Index: llvm/test/tools/llvm-objcopy/ELF/set-section-flags.test =================================================================== --- llvm/test/tools/llvm-objcopy/ELF/set-section-flags.test +++ llvm/test/tools/llvm-objcopy/ELF/set-section-flags.test @@ -40,6 +40,12 @@ # RUN: llvm-objcopy --set-section-flags=.foo=share \ # RUN: --set-section-flags=.baz=share --set-section-flags=.rela.baz=share %t %t.share # RUN: llvm-readobj --sections %t.share | FileCheck %s --check-prefixes=CHECK,PROGBITS,WRITE +# RUN: llvm-objcopy --set-section-flags=.foo=large \ +# RUN: --set-section-flags=.baz=large --set-section-flags=.rela.baz=large %t %t.large +# RUN: llvm-readobj --sections %t.large | FileCheck %s --check-prefixes=CHECK,LARGE,PROGBITS,WRITE +# RUN: llvm-objcopy --set-section-flags=.foo=data \ +# RUN: --set-section-flags=.baz=data --set-section-flags=.rela.baz=data %t %t.no-large +# RUN: llvm-readobj --sections %t.no-large | FileCheck %s --check-prefixes=CHECK,PROGBITS,WRITE # Multiple flags: # RUN: llvm-objcopy --set-section-flags=.foo=alloc,readonly,strings \ @@ -102,6 +108,7 @@ # STRINGS-NEXT: SHF_STRINGS (0x20) # EXCLUDE-NEXT: SHF_EXCLUDE (0x80000000) # WRITE-NEXT: SHF_WRITE (0x1) +# LARGE-NEXT: SHF_X86_64_LARGE (0x10000000) # CHECK-NEXT: ] # CHECK: Name: .baz @@ -114,6 +121,7 @@ # STRINGS-NEXT: SHF_STRINGS (0x20) # EXCLUDE-NEXT: SHF_EXCLUDE (0x80000000) # WRITE-NEXT: SHF_WRITE (0x1) +# LARGE-NEXT: SHF_X86_64_LARGE (0x10000000) # CHECK-NEXT: ] # CHECK: Name: .rela.baz @@ -125,6 +133,7 @@ # STRINGS-NEXT: SHF_STRINGS (0x20) # EXCLUDE-NEXT: SHF_EXCLUDE (0x80000000) # WRITE-NEXT: SHF_WRITE (0x1) +# LARGE-NEXT: SHF_X86_64_LARGE (0x10000000) # CHECK-NEXT: ] # BAD-FORMAT: bad format for --set-section-flags: missing '=' Index: llvm/tools/llvm-objcopy/ObjcopyOptions.cpp =================================================================== --- llvm/tools/llvm-objcopy/ObjcopyOptions.cpp +++ llvm/tools/llvm-objcopy/ObjcopyOptions.cpp @@ -205,6 +205,7 @@ .CaseLower("contents", SectionFlag::SecContents) .CaseLower("share", SectionFlag::SecShare) .CaseLower("exclude", SectionFlag::SecExclude) + .CaseLower("large", SectionFlag::SecLarge) .Default(SectionFlag::SecNone); } @@ -218,7 +219,7 @@ errc::invalid_argument, "unrecognized section flag '%s'. Flags supported for GNU " "compatibility: alloc, load, noload, readonly, exclude, debug, " - "code, data, rom, share, contents, merge, strings", + "code, data, rom, share, contents, merge, strings, large", Flag.str().c_str()); ParsedFlags |= ParsedFlag; } Index: llvm/tools/llvm-objcopy/ObjcopyOpts.td =================================================================== --- llvm/tools/llvm-objcopy/ObjcopyOpts.td +++ llvm/tools/llvm-objcopy/ObjcopyOpts.td @@ -52,7 +52,7 @@ "Renames a section from old to new, optionally with specified flags. " "Flags supported for GNU compatibility: alloc, load, noload, " "readonly, exclude, debug, code, data, rom, share, contents, merge, " - "strings">, + "strings, large">, MetaVarName<"old=new[,flag1,...]">; defm redefine_symbol : Eq<"redefine-sym", "Change the name of a symbol old to new">, @@ -85,7 +85,7 @@ : Eq<"set-section-flags", "Set section flags for a given section. Flags supported for GNU " "compatibility: alloc, load, noload, readonly, exclude, debug, code, " - "data, rom, share, contents, merge, strings">, + "data, rom, share, contents, merge, strings, large">, MetaVarName<"section=flag1[,flag2,...]">; defm set_section_type