Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -65,6 +65,7 @@ struct Configuration { Symbol *EntrySym = nullptr; InputFile *FirstElf = nullptr; + llvm::StringMap SectionStartMap; llvm::StringRef DynamicLinker; llvm::StringRef Entry; llvm::StringRef Emulation; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -379,6 +379,33 @@ return StripPolicy::None; } +static uint64_t parseSectionAddress(StringRef S, opt::Arg *Arg) { + uint64_t VA = 0; + if (S.startswith("0x")) + S = S.drop_front(2); + if (S.getAsInteger(16, VA)) + error("invalid argument: " + stringize(Arg)); + return VA; +} + +static StringMap getSectionStartMap(opt::InputArgList &Args) { + StringMap Ret; + for (auto *Arg : Args.filtered(OPT_section_start)) { + StringRef Name; + StringRef Addr; + std::tie(Name, Addr) = StringRef(Arg->getValue()).split('='); + Ret[Name] = parseSectionAddress(Addr, Arg); + } + + if (auto *Arg = Args.getLastArg(OPT_Ttext)) + Ret[".text"] = parseSectionAddress(Arg->getValue(), Arg); + if (auto *Arg = Args.getLastArg(OPT_Tdata)) + Ret[".data"] = parseSectionAddress(Arg->getValue(), Arg); + if (auto *Arg = Args.getLastArg(OPT_Tbss)) + Ret[".bss"] = parseSectionAddress(Arg->getValue(), Arg); + return Ret; +} + // Initializes Config members by the command line options. void LinkerDriver::readConfigs(opt::InputArgList &Args) { for (auto *Arg : Args.filtered(OPT_L)) @@ -390,6 +417,8 @@ if (!RPaths.empty()) Config->RPath = llvm::join(RPaths.begin(), RPaths.end(), ":"); + Config->SectionStartMap = getSectionStartMap(Args); + if (auto *Arg = Args.getLastArg(OPT_m)) { // Parse ELF{32,64}{LE,BE} and CPU type. StringRef S = Arg->getValue(); Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -147,6 +147,9 @@ def script: S<"script">, HelpText<"Read linker script">; +def section_start: S<"section-start">, MetaVarName<"
">, + HelpText<"Set address of section">; + def shared: F<"shared">, HelpText<"Build a shared object">; def soname: J<"soname=">, HelpText<"Set DT_SONAME">; @@ -164,12 +167,18 @@ def target1_abs: F<"target1-abs">, HelpText<"Interpret R_ARM_TARGET1 as R_ARM_ABS32">; +def Tbss: J<"Tbss=">, HelpText<"Same as --section-start with .bss as the sectionname">; + +def Tdata: J<"Tdata=">, HelpText<"Same as --section-start with .data as the sectionname.">; + def threads: F<"threads">, HelpText<"Enable use of threads">; def trace: F<"trace">, HelpText<"Print the names of the input files">; def trace_symbol : J<"trace-symbol=">, HelpText<"Trace references to symbols">; +def Ttext: J<"Ttext=">, HelpText<"Same as --section-start with .text as the sectionname.">; + def undefined: S<"undefined">, HelpText<"Force undefined symbol during linking">; Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -1083,6 +1083,10 @@ if (Sec->PageAlign) Alignment = std::max(Alignment, Target->PageSize); + auto I = Config->SectionStartMap.find(Sec->getName()); + if (I != Config->SectionStartMap.end()) + VA = I->second; + // We only assign VAs to allocated sections. if (needsPtLoad(Sec)) { VA = alignTo(VA, Alignment); Index: test/ELF/sectionstart.s =================================================================== --- test/ELF/sectionstart.s +++ test/ELF/sectionstart.s @@ -0,0 +1,57 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o +# RUN: ld.lld %t.o --section-start .text=0x100000 \ +# RUN: --section-start .data=0x110000 --section-start .bss=0x200000 -o %t +# RUN: llvm-objdump -section-headers %t | FileCheck %s + +# CHECK: Sections: +# CHECK-NEXT: Idx Name Size Address Type +# CHECK-NEXT: 0 00000000 0000000000000000 +# CHECK-NEXT: 1 .text 00000001 0000000000100000 TEXT DATA +# CHECK-NEXT: 2 .data 00000004 0000000000110000 DATA +# CHECK-NEXT: 3 .bss 00000004 0000000000200000 BSS + +## The same, but dropped "0x" prefix. +# RUN: ld.lld %t.o --section-start .text=100000 \ +# RUN: --section-start .data=110000 --section-start .bss=0x200000 -o %t1 +# RUN: llvm-objdump -section-headers %t1 | FileCheck %s + +## Use -Ttext, -Tdata, -Tbss as replacement for --section-start: +# RUN: ld.lld %t.o -Ttext=0x100000 -Tdata=0x110000 -Tbss=0x200000 -o %t4 +# RUN: llvm-objdump -section-headers %t4 | FileCheck %s + +## The same, but dropped "0x" prefix. +# RUN: ld.lld %t.o -Ttext=100000 -Tdata=110000 -Tbss=200000 -o %t5 +# RUN: llvm-objdump -section-headers %t5 | FileCheck %s + +## Errors: +# RUN: not ld.lld %t.o --section-start .text100000 -o %t2 2>&1 \ +# RUN: | FileCheck -check-prefix=ERR1 %s +# ERR1: invalid argument: --section-start .text100000 + +# RUN: not ld.lld %t.o --section-start .text=1Q0000 -o %t3 2>&1 \ +# RUN: | FileCheck -check-prefix=ERR2 %s +# ERR2: invalid argument: --section-start .text=1Q0000 + +# RUN: not ld.lld %t.o -Ttext=1w0000 -o %t6 2>&1 \ +# RUN: | FileCheck -check-prefix=ERR3 %s +# ERR3: invalid argument: -Ttext=1w0000 + +# RUN: not ld.lld %t.o -Tbss=1w0000 -o %t6 2>&1 \ +# RUN: | FileCheck -check-prefix=ERR4 %s +# ERR4: invalid argument: -Tbss=1w0000 + +# RUN: not ld.lld %t.o -Tdata=1w0000 -o %t6 2>&1 \ +# RUN: | FileCheck -check-prefix=ERR5 %s +# ERR5: invalid argument: -Tdata=1w0000 + +.text +.globl _start +_start: + nop + +.data +.long 0 + +.bss +.zero 4