From 82bd8be6d8c1927bdd107822c1d0c5a5f7e5330b Mon Sep 17 00:00:00 2001 From: George Rimar Date: Wed, 8 Feb 2017 16:18:10 +0000 Subject: [PATCH] Recommit r294464 "[ELF] - Added partial support for --emit-relocs (no --gc-section case, no /DISCARD/ support) #3" with temporarily file name fix in testcase. Original commit message: -q, --emit-relocs - Generate relocations in output Simplest implementation: * no GC case, * no "/DISCARD/" linkerscript command support. This patch is extracted from D28612 / D29636, Relative to PR31579. Differential revision: https://reviews.llvm.org/D29663 llvm-svn: 294469 --- lld/ELF/Config.h | 2 + lld/ELF/Driver.cpp | 1 + lld/ELF/InputFiles.cpp | 7 +++ lld/ELF/InputSection.cpp | 15 ++++-- lld/ELF/Options.td | 3 ++ lld/ELF/OutputSections.cpp | 2 +- lld/ELF/Writer.cpp | 4 +- lld/test/ELF/emit-relocs.s | 108 +++++++++++++++++++++++++++++++++++++ 8 files changed, 134 insertions(+), 8 deletions(-) create mode 100644 lld/test/ELF/emit-relocs.s diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 2649a96e9441..093e26aa5b9d 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -102,6 +102,7 @@ struct Configuration { bool Demangle = true; bool DisableVerify; bool EhFrameHdr; + bool EmitRelocs; bool EnableNewDtags; bool ExportDynamic; bool FatalWarnings; @@ -157,6 +158,7 @@ struct Configuration { unsigned LTOO; unsigned Optimize; unsigned ThinLTOJobs; + bool copyRelocs() { return Relocatable || EmitRelocs; }; }; // The only instance of Configuration struct. diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 74dfc790fefa..c743bebfc9a4 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -519,6 +519,7 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions); Config->Demangle = getArg(Args, OPT_demangle, OPT_no_demangle, true); Config->DisableVerify = Args.hasArg(OPT_disable_verify); + Config->EmitRelocs = Args.hasArg(OPT_emit_relocs); Config->EhFrameHdr = Args.hasArg(OPT_eh_frame_hdr); Config->EnableNewDtags = !Args.hasArg(OPT_disable_new_dtags); Config->ExportDynamic = diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 1e66f51ca8c1..69c404f74566 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -399,6 +399,13 @@ elf::ObjectFile::createInputSection(const Elf_Shdr &Sec, } assert(isUInt<31>(NumRelocations)); Target->NumRelocations = NumRelocations; + + // Relocation sections processed by the linker are usually removed + // from the output, so returning `nullptr` for the normal case. + // However, if -emit-relocs is given, we need to leave them in the output. + // (Some post link analysis tools need this information.) + if (Config->EmitRelocs) + return make>(this, &Sec, Name); return nullptr; } } diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index e8f24e14109e..235d500984bd 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -214,9 +214,9 @@ InputSectionBase *InputSection::getRelocatedSection() { return Sections[this->Info]; } -// This is used for -r. We can't use memcpy to copy relocations because we need -// to update symbol table offset and section index for each relocation. So we -// copy relocations one by one. +// This is used for -r and --emit-relocs. We can't use memcpy to copy +// relocations because we need to update symbol table offset and section index +// for each relocation. So we copy relocations one by one. template template void InputSection::copyRelocations(uint8_t *Buf, ArrayRef Rels) { @@ -235,7 +235,11 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef Rels) { if (Config->Rela) P->r_addend = getAddend(Rel); - P->r_offset = RelocatedSection->getOffset(Rel.r_offset); + + // Output section VA is zero for -r, so r_offset is an offset within the + // section, but for --emit-relocs it is an virtual address. + P->r_offset = RelocatedSection->OutSec->Addr + + RelocatedSection->getOffset(Rel.r_offset); P->setSymbolAndType(In::SymTab->getSymbolIndex(&Body), Type, Config->Mips64EL); } @@ -514,7 +518,8 @@ template void InputSection::writeTo(uint8_t *Buf) { return; } - // If -r is given, then an InputSection may be a relocation section. + // If -r or --emit-relocs is given, then an InputSection + // may be a relocation section. if (this->Type == SHT_RELA) { copyRelocations(Buf + OutSecOff, this->template getDataAs()); return; diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index 0fd16fc1b2c7..46c45e60bbc6 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -70,6 +70,8 @@ def dynamic_list: S<"dynamic-list">, def eh_frame_hdr: F<"eh-frame-hdr">, HelpText<"Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header">; +def emit_relocs: F<"emit-relocs">, HelpText<"Generate relocations in output">; + def enable_new_dtags: F<"enable-new-dtags">, HelpText<"Enable new dynamic tags">; @@ -283,6 +285,7 @@ def alias_define_common_dp: F<"dp">, Alias; def alias_discard_all_x: Flag<["-"], "x">, Alias; def alias_discard_locals_X: Flag<["-"], "X">, Alias; def alias_dynamic_list: J<"dynamic-list=">, Alias; +def alias_emit_relocs: Flag<["-"], "q">, Alias; def alias_entry_e: JoinedOrSeparate<["-"], "e">, Alias; def alias_entry_entry: J<"entry=">, Alias; def alias_error_limit: J<"error-limit=">, Alias; diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index 6de30447913c..9e638ad17dea 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -116,7 +116,7 @@ template void OutputSection::finalize() { } uint32_t Type = this->Type; - if (!Config->Relocatable || (Type != SHT_RELA && Type != SHT_REL)) + if (!Config->copyRelocs() || (Type != SHT_RELA && Type != SHT_REL)) return; this->Link = In::SymTab->OutSec->SectionIndex; diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index a3887f384df3..81c01b068241 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -449,9 +449,9 @@ static bool shouldKeepInSymtab(InputSectionBase *Sec, StringRef SymName, if (B.isFile()) return false; - // We keep sections in symtab for relocatable output. + // We keep sections in symtab for relocatable output and --emit-reloc. if (B.isSection()) - return Config->Relocatable; + return Config->copyRelocs(); // If sym references a section in a discarded group, don't keep it. if (Sec == &InputSection::Discarded) diff --git a/lld/test/ELF/emit-relocs.s b/lld/test/ELF/emit-relocs.s new file mode 100644 index 000000000000..10808fd90e59 --- /dev/null +++ b/lld/test/ELF/emit-relocs.s @@ -0,0 +1,108 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o +# RUN: ld.lld --emit-relocs %t1.o -o %t +# RUN: llvm-readobj -t -r %t | FileCheck %s + +## Check single dash form. +# RUN: ld.lld -emit-relocs %t1.o -o %t1 +# RUN: llvm-readobj -t -r %t1 | FileCheck %s + +## Check alias. +# RUN: ld.lld -q %t1.o -o %t2 +# RUN: llvm-readobj -t -r %t2 | FileCheck %s + +# CHECK: Relocations [ +# CHECK-NEXT: Section ({{.*}}) .rela.text { +# CHECK-NEXT: 0x201002 R_X86_64_32 .text 0x1 +# CHECK-NEXT: 0x201007 R_X86_64_PLT32 fn 0xFFFFFFFFFFFFFFFC +# CHECK-NEXT: 0x20100E R_X86_64_32 .text 0x1 +# CHECK-NEXT: 0x201013 R_X86_64_PLT32 fn2 0xFFFFFFFFFFFFFFFC +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: Symbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: None +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: bar +# CHECK-NEXT: Value: 0x201001 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: None +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: foo +# CHECK-NEXT: Value: 0x20100D +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: None +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: +# CHECK-NEXT: Value: 0x201000 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: Section +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: +# CHECK-NEXT: Value: 0x20100C +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local +# CHECK-NEXT: Type: Section +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: fn +# CHECK-NEXT: Value: 0x201000 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: Function +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: fn2 +# CHECK-NEXT: Value: 0x20100C +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: Function +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text +# CHECK-NEXT: } +# CHECK-NEXT: ] + +.section .text,"ax",@progbits,unique,0 +.globl fn +.type fn,@function +fn: + nop + +bar: + movl $bar, %edx + callq fn@PLT + nop + +.section .text,"ax",@progbits,unique,1 +.globl fn2 +.type fn2,@function +fn2: + nop + +foo: + movl $foo, %edx + callq fn2@PLT + nop