Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -102,6 +102,7 @@ bool Demangle = true; bool DisableVerify; bool EhFrameHdr; + bool EmitRelocs; bool EnableNewDtags; bool ExportDynamic; bool FatalWarnings; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -163,6 +163,10 @@ error("attempted static link of dynamic object " + Path); return; } + if (Config->EmitRelocs) { + error("--emit-reloc does not allow link against dynamic object " + Path); + return; + } Files.push_back(createSharedFile(MBRef)); return; default: @@ -220,6 +224,9 @@ if (Config->Pie) error("-r and -pie may not be used together"); } + + if (Config->EmitRelocs && Config->Pic) + error("-pie/-shared and --emit-relocs may not be used together"); } static StringRef getString(opt::InputArgList &Args, unsigned Key, @@ -482,6 +489,7 @@ 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 = Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -399,6 +399,10 @@ } assert(isUInt<31>(NumRelocations)); Target->NumRelocations = NumRelocations; + + if (Config->EmitRelocs) + return make>(this, &Sec, Name); + return nullptr; } } Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -246,7 +246,8 @@ if (Config->Rela) P->r_addend = getAddend(Rel); - P->r_offset = RelocatedSection->getOffset(Rel.r_offset); + P->r_offset = RelocatedSection->OutSec->Addr + + RelocatedSection->getOffset(Rel.r_offset); P->setSymbolAndType(Body.DynsymIndex, Type, Config->Mips64EL); } } @@ -529,7 +530,8 @@ 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; Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -262,6 +262,20 @@ for (InputSectionBase *S : V) { S->Live = false; reportDiscarded(S); + + if (!Config->EmitRelocs) + continue; + + ArrayRef *> Sections = S->getFile()->getSections(); + for (InputSectionBase *IS : Sections) { + if (!IS || !IS->Live || (IS->Type != SHT_REL && IS->Type != SHT_RELA)) + continue; + InputSection *Sec = cast>(IS); + if (Sec->getRelocatedSection() != S) + continue; + Sec->Live = false; + reportDiscarded(Sec); + } } } Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -67,6 +67,8 @@ 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">; @@ -268,6 +270,7 @@ 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; Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -116,7 +116,8 @@ } uint32_t Type = this->Type; - if (!Config->Relocatable || (Type != SHT_RELA && Type != SHT_REL)) + if ((!Config->Relocatable && !Config->EmitRelocs) || + (Type != SHT_RELA && Type != SHT_REL)) return; this->Link = In::SymTab->OutSec->SectionIndex; Index: ELF/SyntheticSections.cpp =================================================================== --- ELF/SyntheticSections.cpp +++ ELF/SyntheticSections.cpp @@ -1065,11 +1065,15 @@ this->OutSec->Info = this->Info = NumLocals + 1; this->OutSec->Entsize = this->Entsize; - if (Config->Relocatable) { + // When -r or --emit-relocs is specified, we use DynsymIndex + // to store symbol index in .symtab section. It is safe because + // .dynsym is not exist in that cases. + if (Config->Relocatable || Config->EmitRelocs) { size_t I = NumLocals; for (const SymbolTableEntry &S : Symbols) S.Symbol->DynsymIndex = ++I; - return; + if (Config->Relocatable) + return; } if (!StrTabSec.isDynamic()) { Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -396,9 +396,9 @@ 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->Relocatable || Config->EmitRelocs; // If sym references a section in a discarded group, don't keep it. if (Sec == &InputSection::Discarded) @@ -467,7 +467,7 @@ if (!shouldKeepInSymtab(Sec, B->getName(), *B)) continue; ++In::SymTab->NumLocals; - if (Config->Relocatable) + if (Config->Relocatable || Config->EmitRelocs) B->DynsymIndex = In::SymTab->NumLocals; F->KeptLocalSyms.push_back(std::make_pair( DR, In::SymTab->StrTabSec.addString(B->getName()))); Index: test/ELF/Inputs/emit-relocs.s =================================================================== --- test/ELF/Inputs/emit-relocs.s +++ test/ELF/Inputs/emit-relocs.s @@ -0,0 +1,10 @@ +.section .text +.globl fn2 +.type fn2,@function +fn2: + nop + +foo: + movl $foo, %edx + callq fn2@PLT + nop Index: test/ELF/emit-relocs.s =================================================================== --- test/ELF/emit-relocs.s +++ test/ELF/emit-relocs.s @@ -0,0 +1,98 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/emit-relocs.s -o %t2.o +# RUN: ld.lld --emit-relocs %t1.o %t2.o -o %t +# RUN: llvm-readobj -t -r %t | FileCheck %s + +## Check single dash form. +# RUN: ld.lld -emit-relocs %t1.o %t2.o -o %t1 +# RUN: llvm-readobj -t -r %t1 | FileCheck %s + +## Check alias. +# RUN: ld.lld -q %t1.o %t2.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: +# 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: 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: 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 +.globl fn +.type fn,@function +fn: + nop + +bar: + movl $bar, %edx + callq fn@PLT + nop Index: test/ELF/linkerscript/emit-reloc-discard.s =================================================================== --- test/ELF/linkerscript/emit-reloc-discard.s +++ test/ELF/linkerscript/emit-reloc-discard.s @@ -0,0 +1,15 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: echo "SECTIONS { /DISCARD/ : { *(.debug*) } }" > %t.script +# RUN: ld.lld --emit-relocs --script %t.script %t.o -o %t1 +# RUN: llvm-readobj -r %t1 | FileCheck %s + +# CHECK: Relocations [ +# CHECK-NEXT: ] + +.section .debug_str,"MS",@progbits,1 +.Linfo_string0: + .asciz "AAA" + +.section .debug_info,"",@progbits + .long .Linfo_string0