forked from OSchip/llvm-project
[ELF] Add -z dead-reloc-in-nonalloc=<section_glob>=<value>
... to customize the tombstone value we use for an absolute relocation referencing a discarded symbol. This can be used as a workaround when some debug processing tool has trouble with current -1 tombstone value (https://bugs.chromium.org/p/chromium/issues/detail?id=1102223#c11 ) For example, to get the current built-in rules (not considering the .debug_line special case for ICF): ``` -z dead-reloc-in-nonalloc='.debug_*=0xffffffffffffffff' -z dead-reloc-in-nonalloc=.debug_loc=0xfffffffffffffffe -z dead-reloc-in-nonalloc=.debug_ranges=0xfffffffffffffffe ``` To get GNU ld (as of binutils 2.35)'s behavior: ``` -z dead-reloc-in-nonalloc='*=0' -z dead-reloc-in-nonalloc=.debug_ranges=1 ``` This option has other use cases. For example, if we want to check whether a non-SHF_ALLOC section has dead relocations. With this patch, we can run a regular LLD and run another with a special -z dead-reloc-in-nonalloc=, then compare their output. Reviewed By: thakis Differential Revision: https://reviews.llvm.org/D83264
This commit is contained in:
parent
9520b6c8ab
commit
4ce56b8122
|
@ -145,6 +145,7 @@ struct Configuration {
|
||||||
bool checkSections;
|
bool checkSections;
|
||||||
bool compressDebugSections;
|
bool compressDebugSections;
|
||||||
bool cref;
|
bool cref;
|
||||||
|
std::vector<std::pair<llvm::GlobPattern, uint64_t>> deadRelocInNonAlloc;
|
||||||
bool defineCommon;
|
bool defineCommon;
|
||||||
bool demangle = true;
|
bool demangle = true;
|
||||||
bool dependentLibraries;
|
bool dependentLibraries;
|
||||||
|
|
|
@ -444,6 +444,7 @@ static bool isKnownZFlag(StringRef s) {
|
||||||
s == "rela" || s == "relro" || s == "retpolineplt" ||
|
s == "rela" || s == "relro" || s == "retpolineplt" ||
|
||||||
s == "rodynamic" || s == "shstk" || s == "text" || s == "undefs" ||
|
s == "rodynamic" || s == "shstk" || s == "text" || s == "undefs" ||
|
||||||
s == "wxneeded" || s.startswith("common-page-size=") ||
|
s == "wxneeded" || s.startswith("common-page-size=") ||
|
||||||
|
s.startswith("dead-reloc-in-nonalloc=") ||
|
||||||
s.startswith("max-page-size=") || s.startswith("stack-size=") ||
|
s.startswith("max-page-size=") || s.startswith("stack-size=") ||
|
||||||
s.startswith("start-stop-visibility=");
|
s.startswith("start-stop-visibility=");
|
||||||
}
|
}
|
||||||
|
@ -1069,6 +1070,27 @@ static void readConfigs(opt::InputArgList &args) {
|
||||||
config->zText = getZFlag(args, "text", "notext", true);
|
config->zText = getZFlag(args, "text", "notext", true);
|
||||||
config->zWxneeded = hasZOption(args, "wxneeded");
|
config->zWxneeded = hasZOption(args, "wxneeded");
|
||||||
|
|
||||||
|
for (opt::Arg *arg : args.filtered(OPT_z)) {
|
||||||
|
std::pair<StringRef, StringRef> option =
|
||||||
|
StringRef(arg->getValue()).split('=');
|
||||||
|
if (option.first != "dead-reloc-in-nonalloc")
|
||||||
|
continue;
|
||||||
|
constexpr StringRef errPrefix = "-z dead-reloc-in-nonalloc=: ";
|
||||||
|
std::pair<StringRef, StringRef> kv = option.second.split('=');
|
||||||
|
if (kv.first.empty() || kv.second.empty()) {
|
||||||
|
error(errPrefix + "expected <section_glob>=<value>");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uint64_t v;
|
||||||
|
if (!to_integer(kv.second, v))
|
||||||
|
error(errPrefix + "expected a non-negative integer, but got '" +
|
||||||
|
kv.second + "'");
|
||||||
|
else if (Expected<GlobPattern> pat = GlobPattern::create(kv.first))
|
||||||
|
config->deadRelocInNonAlloc.emplace_back(std::move(*pat), v);
|
||||||
|
else
|
||||||
|
error(errPrefix + toString(pat.takeError()));
|
||||||
|
}
|
||||||
|
|
||||||
// Parse LTO options.
|
// Parse LTO options.
|
||||||
if (auto *arg = args.getLastArg(OPT_plugin_opt_mcpu_eq))
|
if (auto *arg = args.getLastArg(OPT_plugin_opt_mcpu_eq))
|
||||||
parseClangOption(saver.save("-mcpu=" + StringRef(arg->getValue())),
|
parseClangOption(saver.save("-mcpu=" + StringRef(arg->getValue())),
|
||||||
|
|
|
@ -857,6 +857,12 @@ void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef<RelTy> rels) {
|
||||||
const bool isDebugLocOrRanges =
|
const bool isDebugLocOrRanges =
|
||||||
isDebug && (name == ".debug_loc" || name == ".debug_ranges");
|
isDebug && (name == ".debug_loc" || name == ".debug_ranges");
|
||||||
const bool isDebugLine = isDebug && name == ".debug_line";
|
const bool isDebugLine = isDebug && name == ".debug_line";
|
||||||
|
Optional<uint64_t> tombstone;
|
||||||
|
for (const auto &patAndValue : llvm::reverse(config->deadRelocInNonAlloc))
|
||||||
|
if (patAndValue.first.match(this->name)) {
|
||||||
|
tombstone = patAndValue.second;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
for (const RelTy &rel : rels) {
|
for (const RelTy &rel : rels) {
|
||||||
RelType type = rel.getType(config->isMips64EL);
|
RelType type = rel.getType(config->isMips64EL);
|
||||||
|
@ -907,7 +913,8 @@ void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef<RelTy> rels) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDebug && (type == target->symbolicRel || expr == R_DTPREL)) {
|
if (tombstone ||
|
||||||
|
(isDebug && (type == target->symbolicRel || expr == R_DTPREL))) {
|
||||||
// Resolve relocations in .debug_* referencing (discarded symbols or ICF
|
// Resolve relocations in .debug_* referencing (discarded symbols or ICF
|
||||||
// folded section symbols) to a tombstone value. Resolving to addend is
|
// folded section symbols) to a tombstone value. Resolving to addend is
|
||||||
// unsatisfactory because the result address range may collide with a
|
// unsatisfactory because the result address range may collide with a
|
||||||
|
@ -935,8 +942,11 @@ void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef<RelTy> rels) {
|
||||||
auto *ds = dyn_cast<Defined>(&sym);
|
auto *ds = dyn_cast<Defined>(&sym);
|
||||||
if (!sym.getOutputSection() ||
|
if (!sym.getOutputSection() ||
|
||||||
(ds && ds->section->repl != ds->section && !isDebugLine)) {
|
(ds && ds->section->repl != ds->section && !isDebugLine)) {
|
||||||
target->relocateNoSym(bufLoc, type,
|
// If -z dead-reloc-in-nonalloc= is specified, respect it.
|
||||||
isDebugLocOrRanges ? UINT64_MAX - 1 : UINT64_MAX);
|
const uint64_t value =
|
||||||
|
tombstone ? SignExtend64<bits>(*tombstone)
|
||||||
|
: (isDebugLocOrRanges ? UINT64_MAX - 1 : UINT64_MAX);
|
||||||
|
target->relocateNoSym(bufLoc, type, value);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -625,6 +625,13 @@ Use wrapper functions for symbol.
|
||||||
Linker option extensions.
|
Linker option extensions.
|
||||||
.Bl -tag -width indent -compact
|
.Bl -tag -width indent -compact
|
||||||
.Pp
|
.Pp
|
||||||
|
.It Cm dead-reloc-in-nonalloc Ns = Ns Ar section_glob=value
|
||||||
|
Resolve a relocation in a matched non-SHF_ALLOC section referencing a discarded symbol to
|
||||||
|
.Ar value
|
||||||
|
Accepts globs, in the event of a section matching more than one option, the last
|
||||||
|
option takes precedence. An order of least specific to most specific match is
|
||||||
|
recommended.
|
||||||
|
.Pp
|
||||||
.It Cm execstack
|
.It Cm execstack
|
||||||
Make the main stack executable.
|
Make the main stack executable.
|
||||||
Stack permissions are recorded in the
|
Stack permissions are recorded in the
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
# REQUIRES: x86
|
||||||
|
## Test that -z dead-reloc-in-nonalloc= can customize the tombstone value we
|
||||||
|
## use for an absolute relocation referencing a discarded symbol.
|
||||||
|
|
||||||
|
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
|
||||||
|
# RUN: ld.lld --icf=all -z dead-reloc-in-nonalloc=.debug_info=0xaaaaaaaa \
|
||||||
|
# RUN: -z dead-reloc-in-nonalloc=.not_debug=0xbbbbbbbb %t.o -o %t
|
||||||
|
# RUN: llvm-objdump -s %t | FileCheck %s --check-prefixes=COMMON,AA
|
||||||
|
## 0xaaaaaaaa == 2863311530
|
||||||
|
# RUN: ld.lld --icf=all -z dead-reloc-in-nonalloc=.debug_info=2863311530 \
|
||||||
|
# RUN: -z dead-reloc-in-nonalloc=.not_debug=0xbbbbbbbb %t.o -o - | cmp %t -
|
||||||
|
|
||||||
|
# COMMON: Contents of section .debug_addr:
|
||||||
|
# COMMON-NEXT: 0000 [[ADDR:[0-9a-f]+]] 00000000 ffffffff ffffffff
|
||||||
|
|
||||||
|
# AA: Contents of section .debug_info:
|
||||||
|
# AA-NEXT: 0000 [[ADDR]] 00000000 aaaaaaaa 00000000
|
||||||
|
# AA: Contents of section .not_debug:
|
||||||
|
# AA-NEXT: 0000 bbbbbbbb
|
||||||
|
|
||||||
|
## Specifying zero can get a behavior similar to GNU ld.
|
||||||
|
# RUN: ld.lld --icf=all -z dead-reloc-in-nonalloc=.debug_info=0 %t.o -o %tzero
|
||||||
|
# RUN: llvm-objdump -s %tzero | FileCheck %s --check-prefixes=COMMON,ZERO
|
||||||
|
|
||||||
|
# ZERO: Contents of section .debug_info:
|
||||||
|
# ZERO-NEXT: 0000 {{[0-9a-f]+}}000 00000000 00000000 00000000
|
||||||
|
|
||||||
|
## Glob works.
|
||||||
|
# RUN: ld.lld --icf=all -z dead-reloc-in-nonalloc='.debug_i*=0xaaaaaaaa' \
|
||||||
|
# RUN: -z dead-reloc-in-nonalloc='[.]not_debug=0xbbbbbbbb' %t.o -o - | cmp %t -
|
||||||
|
|
||||||
|
## If a section matches multiple option. The last option wins.
|
||||||
|
# RUN: ld.lld --icf=all -z dead-reloc-in-nonalloc='.debug_info=1' \
|
||||||
|
# RUN: -z dead-reloc-in-nonalloc='.debug_i*=0' %t.o -o - | cmp %tzero -
|
||||||
|
|
||||||
|
## Test all possible invalid cases.
|
||||||
|
# RUN: not ld.lld -z dead-reloc-in-nonalloc= 2>&1 | FileCheck %s --check-prefix=USAGE
|
||||||
|
# RUN: not ld.lld -z dead-reloc-in-nonalloc=a= 2>&1 | FileCheck %s --check-prefix=USAGE
|
||||||
|
# RUN: not ld.lld -z dead-reloc-in-nonalloc==0 2>&1 | FileCheck %s --check-prefix=USAGE
|
||||||
|
|
||||||
|
# USAGE: error: -z dead-reloc-in-nonalloc=: expected <section_glob>=<value>
|
||||||
|
|
||||||
|
# RUN: not ld.lld -z dead-reloc-in-nonalloc=a=-1 2>&1 | FileCheck %s --check-prefix=NON-INTEGER
|
||||||
|
|
||||||
|
# NON-INTEGER: error: -z dead-reloc-in-nonalloc=: expected a non-negative integer, but got '-1'
|
||||||
|
|
||||||
|
# RUN: not ld.lld -z dead-reloc-in-nonalloc='['=0 2>&1 | FileCheck %s --check-prefix=INVALID
|
||||||
|
|
||||||
|
# INVALID: error: -z dead-reloc-in-nonalloc=: invalid glob pattern: [
|
||||||
|
|
||||||
|
.globl _start
|
||||||
|
_start:
|
||||||
|
ret
|
||||||
|
|
||||||
|
## .text.1 will be folded by ICF.
|
||||||
|
.section .text.1,"ax"
|
||||||
|
ret
|
||||||
|
|
||||||
|
.section .debug_addr
|
||||||
|
.quad .text+8
|
||||||
|
.quad .text.1+8
|
||||||
|
|
||||||
|
.section .debug_info
|
||||||
|
.quad .text+8
|
||||||
|
.quad .text.1+8
|
||||||
|
|
||||||
|
## Test a non-.debug_ section.
|
||||||
|
.section .not_debug
|
||||||
|
.long .text.1+8
|
|
@ -19,6 +19,13 @@
|
||||||
# CHECK-NEXT: 0000 ffffffff ffffffff 08000000 00000000
|
# CHECK-NEXT: 0000 ffffffff ffffffff 08000000 00000000
|
||||||
# CHECK-NEXT: 0010 ffffffff ffffffff 08000000 00000000
|
# CHECK-NEXT: 0010 ffffffff ffffffff 08000000 00000000
|
||||||
|
|
||||||
|
## -z dead-reloc-in-nonalloc= can override the tombstone value.
|
||||||
|
# RUN: ld.lld --gc-sections -z dead-reloc-in-nonalloc=.debug_loc=42 %t.o %t1.o %t1.o -o %t42
|
||||||
|
# RUN: llvm-objdump -s %t42 | FileCheck %s --check-prefix=OVERRIDE
|
||||||
|
|
||||||
|
# OVERRIDE: Contents of section .debug_loc:
|
||||||
|
# OVERRIDE-NEXT: 0000 2a000000 00000000 2a000000 00000000
|
||||||
|
|
||||||
.section .text.1,"ax"
|
.section .text.1,"ax"
|
||||||
.byte 0
|
.byte 0
|
||||||
.section .text.2,"axe"
|
.section .text.2,"axe"
|
||||||
|
|
Loading…
Reference in New Issue