Index: lld/trunk/ELF/LinkerScript.h =================================================================== --- lld/trunk/ELF/LinkerScript.h +++ lld/trunk/ELF/LinkerScript.h @@ -121,6 +121,7 @@ bool HasFilehdr; bool HasPhdrs; unsigned Flags; + Expr LMAExpr; }; class LinkerScriptBase { Index: lld/trunk/ELF/LinkerScript.cpp =================================================================== --- lld/trunk/ELF/LinkerScript.cpp +++ lld/trunk/ELF/LinkerScript.cpp @@ -463,6 +463,11 @@ Phdr.add(Out::ElfHeader); if (Cmd.HasPhdrs) Phdr.add(Out::ProgramHeaders); + + if (Cmd.LMAExpr) { + Phdr.H.p_paddr = Cmd.LMAExpr(0); + Phdr.HasLMA = true; + } } // Add output sections to program headers. @@ -860,7 +865,8 @@ expect("{"); while (!Error && !skip("}")) { StringRef Tok = next(); - Opt.PhdrsCommands.push_back({Tok, PT_NULL, false, false, UINT_MAX}); + Opt.PhdrsCommands.push_back( + {Tok, PT_NULL, false, false, UINT_MAX, nullptr}); PhdrsCommand &PhdrCmd = Opt.PhdrsCommands.back(); PhdrCmd.Type = readPhdrType(); @@ -872,6 +878,8 @@ PhdrCmd.HasFilehdr = true; else if (Tok == "PHDRS") PhdrCmd.HasPhdrs = true; + else if (Tok == "AT") + PhdrCmd.LMAExpr = readParenExpr(); else if (Tok == "FLAGS") { expect("("); // Passing 0 for the value of dot is a bit of a hack. It means that Index: lld/trunk/ELF/Writer.h =================================================================== --- lld/trunk/ELF/Writer.h +++ lld/trunk/ELF/Writer.h @@ -38,6 +38,7 @@ typename ELFT::Phdr H = {}; OutputSectionBase *First = nullptr; OutputSectionBase *Last = nullptr; + bool HasLMA = false; }; template Index: lld/trunk/ELF/Writer.cpp =================================================================== --- lld/trunk/ELF/Writer.cpp +++ lld/trunk/ELF/Writer.cpp @@ -1168,10 +1168,14 @@ else if (H.p_type == PT_GNU_RELRO) H.p_align = 1; - H.p_paddr = H.p_vaddr; - if (H.p_type == PT_LOAD && First) - if (Expr LmaExpr = Script::X->getLma(First->getName())) - H.p_paddr = LmaExpr(H.p_vaddr); + if (!P.HasLMA) { + // The p_paddr field can be set using linker script AT command. + // By default, it is the same value as p_vaddr. + H.p_paddr = H.p_vaddr; + if (H.p_type == PT_LOAD && First) + if (Expr LmaExpr = Script::X->getLma(First->getName())) + H.p_paddr = LmaExpr(H.p_vaddr); + } // The TLS pointer goes after PT_TLS. At least glibc will align it, // so round up the size to make sure the offsets are correct. Index: lld/trunk/test/ELF/linkerscript/phdrs.s =================================================================== --- lld/trunk/test/ELF/linkerscript/phdrs.s +++ lld/trunk/test/ELF/linkerscript/phdrs.s @@ -6,9 +6,19 @@ # RUN: .text : {*(.text*)} :all \ # RUN: .foo : {*(.foo.*)} :all \ # RUN: .data : {*(.data.*)} :all}" > %t.script - # RUN: ld.lld -o %t1 --script %t.script %t # RUN: llvm-readobj -program-headers %t1 | FileCheck %s + +## Check the AT(expr) +# RUN: echo "PHDRS {all PT_LOAD FILEHDR PHDRS AT(0x500 + 0x500) ;} \ +# RUN: SECTIONS { \ +# RUN: . = 0x10000200; \ +# RUN: .text : {*(.text*)} :all \ +# RUN: .foo : {*(.foo.*)} :all \ +# RUN: .data : {*(.data.*)} :all}" > %t.script +# RUN: ld.lld -o %t1 --script %t.script %t +# RUN: llvm-readobj -program-headers %t1 | FileCheck --check-prefix=AT %s + # CHECK: ProgramHeaders [ # CHECK-NEXT: ProgramHeader { # CHECK-NEXT: Type: PT_LOAD (0x1) @@ -23,6 +33,20 @@ # CHECK-NEXT: PF_X (0x1) # CHECK-NEXT: ] +# AT: ProgramHeaders [ +# AT-NEXT: ProgramHeader { +# AT-NEXT: Type: PT_LOAD (0x1) +# AT-NEXT: Offset: 0x0 +# AT-NEXT: VirtualAddress: 0x10000000 +# AT-NEXT: PhysicalAddress: 0xA00 +# AT-NEXT: FileSize: 521 +# AT-NEXT: MemSize: 521 +# AT-NEXT: Flags [ (0x7) +# AT-NEXT: PF_R (0x4) +# AT-NEXT: PF_W (0x2) +# AT-NEXT: PF_X (0x1) +# AT-NEXT: ] + .global _start _start: nop