[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:
Jordan Rupprecht 2019-01-29 15:05:38 +00:00
parent a1f6973ade
commit c892741e74
8 changed files with 218 additions and 38 deletions

View File

@ -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.

View File

@ -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: ]

View File

@ -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.

View File

@ -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");
}

View File

@ -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());

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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<