[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:
Fangrui Song 2020-07-08 10:10:43 -07:00
parent 9520b6c8ab
commit 4ce56b8122
6 changed files with 119 additions and 3 deletions

View File

@ -145,6 +145,7 @@ struct Configuration {
bool checkSections;
bool compressDebugSections;
bool cref;
std::vector<std::pair<llvm::GlobPattern, uint64_t>> deadRelocInNonAlloc;
bool defineCommon;
bool demangle = true;
bool dependentLibraries;

View File

@ -444,6 +444,7 @@ static bool isKnownZFlag(StringRef s) {
s == "rela" || s == "relro" || s == "retpolineplt" ||
s == "rodynamic" || s == "shstk" || s == "text" || s == "undefs" ||
s == "wxneeded" || s.startswith("common-page-size=") ||
s.startswith("dead-reloc-in-nonalloc=") ||
s.startswith("max-page-size=") || s.startswith("stack-size=") ||
s.startswith("start-stop-visibility=");
}
@ -1069,6 +1070,27 @@ static void readConfigs(opt::InputArgList &args) {
config->zText = getZFlag(args, "text", "notext", true);
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.
if (auto *arg = args.getLastArg(OPT_plugin_opt_mcpu_eq))
parseClangOption(saver.save("-mcpu=" + StringRef(arg->getValue())),

View File

@ -857,6 +857,12 @@ void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef<RelTy> rels) {
const bool isDebugLocOrRanges =
isDebug && (name == ".debug_loc" || name == ".debug_ranges");
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) {
RelType type = rel.getType(config->isMips64EL);
@ -907,7 +913,8 @@ void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef<RelTy> rels) {
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
// folded section symbols) to a tombstone value. Resolving to addend is
// 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);
if (!sym.getOutputSection() ||
(ds && ds->section->repl != ds->section && !isDebugLine)) {
target->relocateNoSym(bufLoc, type,
isDebugLocOrRanges ? UINT64_MAX - 1 : UINT64_MAX);
// If -z dead-reloc-in-nonalloc= is specified, respect it.
const uint64_t value =
tombstone ? SignExtend64<bits>(*tombstone)
: (isDebugLocOrRanges ? UINT64_MAX - 1 : UINT64_MAX);
target->relocateNoSym(bufLoc, type, value);
continue;
}
}

View File

@ -625,6 +625,13 @@ Use wrapper functions for symbol.
Linker option extensions.
.Bl -tag -width indent -compact
.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
Make the main stack executable.
Stack permissions are recorded in the

View File

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

View File

@ -19,6 +19,13 @@
# CHECK-NEXT: 0000 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"
.byte 0
.section .text.2,"axe"