[llvm-objcopy] Implement --set-section-flags.
Summary: --set-section-flags is used to change the section flags (e.g. SHF_ALLOC) for given sections. The flags allowed are the same from the existing --rename-section=.old=.new[,flags] feature. Additionally, make sure that --set-section-flag cannot be used with --rename-section (either the source or destination), since --rename-section accepts flags. This avoids ambiguity for something like "--rename-section=.foo=.bar,alloc --set-section-flag=.bar,code". Reviewers: jhenderson, jakehehrlich, alexshap, espindola Reviewed By: jhenderson, jakehehrlich Subscribers: llvm-commits, emaste, arichardson Differential Revision: https://reviews.llvm.org/D57198 llvm-svn: 352505
This commit is contained in:
parent
a1f6973ade
commit
c892741e74
|
@ -0,0 +1,14 @@
|
|||
# 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
|
||||
|
||||
# SET-FOO: --set-section-flags=.foo conflicts with --rename-section=.foo=.bar.
|
||||
# SET-BAR: --set-section-flags=.bar conflicts with --rename-section=.foo=.bar.
|
|
@ -0,0 +1,32 @@
|
|||
# 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: [ ]
|
||||
- Name: .bar
|
||||
Type: SHT_PROGBITS
|
||||
Flags: [ ]
|
||||
|
||||
# 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: ]
|
|
@ -0,0 +1,68 @@
|
|||
# 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: [ ]
|
||||
|
||||
# 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.
|
|
@ -177,10 +177,10 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
|
|||
!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");
|
||||
}
|
||||
|
|
|
@ -128,6 +128,32 @@ static SectionFlag parseSectionRenameFlag(StringRef SectionName) {
|
|||
.Default(SectionFlag::SecNone);
|
||||
}
|
||||
|
||||
static uint64_t parseSectionFlagSet(ArrayRef<StringRef> 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,29 @@ static SectionRename parseRenameSectionValue(StringRef FlagValue) {
|
|||
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 SectionFlagsUpdate parseSetSectionFlagValue(StringRef FlagValue) {
|
||||
if (!StringRef(FlagValue).contains('='))
|
||||
error("Bad format for --set-section-flags: missing '='");
|
||||
|
||||
// Initial split: ".foo" = "f1,f2,..."
|
||||
auto Section2Flags = StringRef(FlagValue).split('=');
|
||||
SectionFlagsUpdate SFU;
|
||||
SFU.Name = Section2Flags.first;
|
||||
|
||||
// Flags split: "f1" "f2" ...
|
||||
SmallVector<StringRef, 6> SectionFlags;
|
||||
Section2Flags.second.split(SectionFlags, ',');
|
||||
SFU.NewFlags = parseSectionFlagSet(SectionFlags);
|
||||
|
||||
return SFU;
|
||||
}
|
||||
|
||||
static const StringMap<MachineInfo> ArchMap{
|
||||
// Name, {EMachine, 64bit, LittleEndian}
|
||||
{"aarch64", {ELF::EM_AARCH64, true, true}},
|
||||
|
@ -327,6 +348,24 @@ DriverConfig parseObjcopyOptions(ArrayRef<const char *> ArgsArr) {
|
|||
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)) {
|
||||
SectionFlagsUpdate SFU = parseSetSectionFlagValue(Arg->getValue());
|
||||
if (!Config.SetSectionFlags.try_emplace(SFU.Name, SFU).second)
|
||||
error("--set-section-flags set multiple times for section " + SFU.Name);
|
||||
}
|
||||
// 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());
|
||||
|
|
|
@ -37,6 +37,11 @@ struct SectionRename {
|
|||
Optional<uint64_t> NewFlags;
|
||||
};
|
||||
|
||||
struct SectionFlagsUpdate {
|
||||
StringRef Name;
|
||||
uint64_t NewFlags;
|
||||
};
|
||||
|
||||
// Configuration for copying/stripping a single file.
|
||||
struct CopyConfig {
|
||||
// Main input/output options
|
||||
|
@ -73,6 +78,7 @@ struct CopyConfig {
|
|||
|
||||
// Map options
|
||||
StringMap<SectionRename> SectionsToRename;
|
||||
StringMap<SectionFlagsUpdate> SetSectionFlags;
|
||||
StringMap<StringRef> SymbolsToRename;
|
||||
|
||||
// Boolean options
|
||||
|
|
|
@ -70,6 +70,17 @@ static bool onlyKeepDWOPred(const Object &Obj, const SectionBase &Sec) {
|
|||
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<ELFObjectFile<ELF32LE>>(Bin))
|
||||
|
@ -484,16 +495,19 @@ static void handleArgs(const CopyConfig &Config, Object &Obj,
|
|||
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()) {
|
||||
const SectionFlagsUpdate &SFU = Iter->second;
|
||||
Sec.Flags = setSectionFlagsPreserveMask(Sec.Flags, SFU.NewFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,6 +86,13 @@ defm add_section
|
|||
"Make a section named <section> with the contents of <file>.">,
|
||||
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<
|
||||
|
|
Loading…
Reference in New Issue