Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -158,12 +158,13 @@ bool ignoreInterpSection(); ArrayRef getFiller(StringRef Name); - Expr getLma(StringRef Name); + bool hasLma(StringRef Name); bool shouldKeep(InputSectionBase *S); void assignAddresses(); int compareSections(StringRef A, StringRef B); bool hasPhdrsCommands(); uintX_t getOutputSectionAddress(StringRef Name); + uintX_t getOutputSectionLMA(StringRef Name); uintX_t getOutputSectionSize(StringRef Name); uintX_t getHeaderSize(); Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -396,6 +396,15 @@ Dot = getHeaderSize(); uintX_t MinVA = std::numeric_limits::max(); uintX_t ThreadBssOffset = 0; + uintX_t LMAOff = 0; + + auto SetAddrs = [&](OutputSectionCommand *Cmd, OutputSectionBase *Sec, + uintX_t VA) { + Sec->setVA(VA); + if (Cmd->LmaExpr) + LMAOff = Cmd->LmaExpr(VA) - VA; + Sec->setLMAOffset(LMAOff); + }; for (const std::unique_ptr &Base : Opt.Commands) { if (auto *Cmd = dyn_cast(Base.get())) { @@ -423,10 +432,11 @@ if (Cmd->AlignExpr) Sec->updateAlignment(Cmd->AlignExpr(Dot)); + Sec->setLMAOffset(LMAOff); if ((Sec->getFlags() & SHF_TLS) && Sec->getType() == SHT_NOBITS) { uintX_t TVA = Dot + ThreadBssOffset; TVA = alignTo(TVA, Sec->getAlignment()); - Sec->setVA(TVA); + SetAddrs(Cmd, Sec, Dot); assignOffsets(Cmd, Sec); ThreadBssOffset = TVA - Dot + Sec->getSize(); continue; @@ -438,7 +448,7 @@ } Dot = alignTo(Dot, Sec->getAlignment()); - Sec->setVA(Dot); + SetAddrs(Cmd, Sec, Dot); assignOffsets(Cmd, Sec); MinVA = std::min(MinVA, Dot); Dot += Sec->getSize(); @@ -517,12 +527,12 @@ return {}; } -template Expr LinkerScript::getLma(StringRef Name) { +template bool LinkerScript::hasLma(StringRef Name) { for (const std::unique_ptr &Base : Opt.Commands) if (auto *Cmd = dyn_cast(Base.get())) if (Cmd->LmaExpr && Cmd->Name == Name) - return Cmd->LmaExpr; - return {}; + return true; + return false; } // Returns the index of the given section name in linker script @@ -566,6 +576,15 @@ } template +typename ELFT::uint LinkerScript::getOutputSectionLMA(StringRef Name) { + for (OutputSectionBase *Sec : *OutputSections) + if (Sec->getName() == Name) + return Sec->getLMA(); + error("undefined section " + Name); + return 0; +} + +template typename ELFT::uint LinkerScript::getOutputSectionSize(StringRef Name) { for (OutputSectionBase *Sec : *OutputSections) if (Sec->getName() == Name) @@ -652,6 +671,7 @@ Expr readExpr(); Expr readExpr1(Expr Lhs, int MinPrec); + StringRef readParenLiteral(); Expr readPrimary(); Expr readTernary(Expr Cond); Expr readParenExpr(); @@ -1154,6 +1174,21 @@ } } +static uint64_t getSectionLoadAddress(StringRef Name) { + switch (Config->EKind) { + case ELF32LEKind: + return Script::X->getOutputSectionLMA(Name); + case ELF32BEKind: + return Script::X->getOutputSectionLMA(Name); + case ELF64LEKind: + return Script::X->getOutputSectionLMA(Name); + case ELF64BEKind: + return Script::X->getOutputSectionLMA(Name); + default: + llvm_unreachable("unsupported target"); + } +} + static uint64_t getHeaderSize() { switch (Config->EKind) { case ELF32LEKind: @@ -1293,6 +1328,13 @@ return true; } +StringRef ScriptParser::readParenLiteral() { + expect("("); + StringRef Name = next(); + expect(")"); + return Name; +} + Expr ScriptParser::readPrimary() { if (peek() == "(") return readParenExpr(); @@ -1311,11 +1353,13 @@ // Built-in functions are parsed here. // https://sourceware.org/binutils/docs/ld/Builtin-Functions.html. if (Tok == "ADDR") { - expect("("); - StringRef Name = next(); - expect(")"); + StringRef Name = readParenLiteral(); return [=](uint64_t Dot) { return getSectionAddress(Name); }; } + if (Tok == "LOADADDR") { + StringRef Name = readParenLiteral(); + return [=](uint64_t Dot) { return getSectionLoadAddress(Name); }; + } if (Tok == "ASSERT") return readAssert(); if (Tok == "ALIGN") { @@ -1323,10 +1367,8 @@ return [=](uint64_t Dot) { return alignTo(Dot, E(Dot)); }; } if (Tok == "CONSTANT") { - expect("("); - StringRef Tok = next(); - expect(")"); - return [=](uint64_t Dot) { return getConstant(Tok); }; + StringRef Name = readParenLiteral(); + return [=](uint64_t Dot) { return getConstant(Name); }; } if (Tok == "SEGMENT_START") { expect("("); @@ -1363,9 +1405,7 @@ return [](uint64_t Dot) { return alignTo(Dot, Target->PageSize); }; } if (Tok == "SIZEOF") { - expect("("); - StringRef Name = next(); - expect(")"); + StringRef Name = readParenLiteral(); return [=](uint64_t Dot) { return getSectionSize(Name); }; } if (Tok == "SIZEOF_HEADERS") Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -76,6 +76,8 @@ OutputSectionBase(StringRef Name, uint32_t Type, uintX_t Flags); void setVA(uintX_t VA) { Header.sh_addr = VA; } uintX_t getVA() const { return Header.sh_addr; } + void setLMAOffset(uintX_t LMAOff) { LMAOffset = LMAOff; } + uintX_t getLMA() const { return Header.sh_addr + LMAOffset; } void setFileOffset(uintX_t Off) { Header.sh_offset = Off; } void setSHName(unsigned Val) { Header.sh_name = Val; } void writeHeaderTo(Elf_Shdr *SHdr); @@ -116,6 +118,7 @@ protected: StringRef Name; Elf_Shdr Header; + uintX_t LMAOffset = 0; }; template class GotSection final : public OutputSectionBase { Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -946,7 +946,7 @@ template std::vector> Writer::createPhdrs() { std::vector Ret; - + bool bHasLma = false; auto AddHdr = [&](unsigned Type, unsigned Flags) -> Phdr * { Ret.emplace_back(Type, Flags); return &Ret.back(); @@ -990,7 +990,8 @@ // different flags or is loaded at a discontiguous address using AT linker // script command. uintX_t NewFlags = Sec->getPhdrFlags(); - if (Script::X->getLma(Sec->getName()) || Flags != NewFlags) { + bHasLma = bHasLma || Script::X->hasLma(Sec->getName()); + if (bHasLma || Flags != NewFlags) { Load = AddHdr(PT_LOAD, NewFlags); Flags = NewFlags; } @@ -1162,17 +1163,13 @@ H.p_memsz = Last->getVA() + Last->getSize() - First->getVA(); H.p_offset = First->getFileOff(); H.p_vaddr = First->getVA(); + H.p_paddr = First->getLMA(); } if (H.p_type == PT_LOAD) H.p_align = Target->PageSize; 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); - // 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. if (H.p_type == PT_TLS) { Index: test/ELF/linkerscript/at.s =================================================================== --- test/ELF/linkerscript/at.s +++ test/ELF/linkerscript/at.s @@ -52,14 +52,26 @@ # CHECK-NEXT: Offset: 0x1000 # CHECK-NEXT: VirtualAddress: 0x1000 # CHECK-NEXT: PhysicalAddress: 0x2000 -# CHECK-NEXT: FileSize: 16 -# CHECK-NEXT: MemSize: 16 +# CHECK-NEXT: FileSize: 8 +# CHECK-NEXT: MemSize: 8 # CHECK-NEXT: Flags [ # CHECK-NEXT: PF_R # CHECK-NEXT: ] # CHECK-NEXT: Alignment: # CHECK-NEXT: } # CHECK-NEXT: ProgramHeader { +# CHECK-NEXT: Type: PT_LOAD (0x1) +# CHECK-NEXT: Offset: 0x1008 +# CHECK-NEXT: VirtualAddress: 0x1008 +# CHECK-NEXT: PhysicalAddress: 0x2008 +# CHECK-NEXT: FileSize: 8 +# CHECK-NEXT: MemSize: 8 +# CHECK-NEXT: Flags [ (0x4) +# CHECK-NEXT: PF_R (0x4) +# CHECK-NEXT: ] +# CHECK-NEXT: Alignment: +# CHECK-NEXT: } +# CHECK-NEXT: ProgramHeader { # CHECK-NEXT: Type: PT_LOAD # CHECK-NEXT: Offset: 0x1010 # CHECK-NEXT: VirtualAddress: 0x1010 @@ -87,7 +99,7 @@ # CHECK-NEXT: Type: PT_LOAD # CHECK-NEXT: Offset: 0x1020 # CHECK-NEXT: VirtualAddress: 0x1020 -# CHECK-NEXT: PhysicalAddress: 0x1020 +# CHECK-NEXT: PhysicalAddress: 0x4008 # CHECK-NEXT: FileSize: 1 # CHECK-NEXT: MemSize: 1 # CHECK-NEXT: Flags [ Index: test/ELF/linkerscript/loadaddr.s =================================================================== --- test/ELF/linkerscript/loadaddr.s +++ test/ELF/linkerscript/loadaddr.s @@ -0,0 +1,39 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: echo "SECTIONS { \ +# RUN: . = 0x1000; \ +# RUN: .aaa : AT(0x2000) { *(.aaa) } \ +# RUN: .bbb : { *(.bbb) } \ +# RUN: .ccc : AT(0x3000) { *(.ccc) } \ +# RUN: .ddd : AT(0x4000) { *(.ddd) } \ +# RUN: .text : { *(.text) } \ +# RUN: aaa_lma = LOADADDR(.aaa); \ +# RUN: bbb_lma = LOADADDR(.bbb); \ +# RUN: ccc_lma = LOADADDR(.ccc); \ +# RUN: ddd_lma = LOADADDR(.ddd); \ +# RUN: txt_lma = LOADADDR(.text); \ +# RUN: }" > %t.script +# RUN: ld.lld %t --script %t.script -o %t2 +# RUN: llvm-objdump -t %t2 | FileCheck %s + +# CHECK: 0000000000002000 *ABS* 00000000 aaa_lma +# CHECK-NEXT: 0000000000002008 *ABS* 00000000 bbb_lma +# CHECK-NEXT: 0000000000003000 *ABS* 00000000 ccc_lma +# CHECK-NEXT: 0000000000004000 *ABS* 00000000 ddd_lma +# CHECK-NEXT: 0000000000004008 *ABS* 00000000 txt_lma + +.global _start +_start: + nop + +.section .aaa, "a" +.quad 0 + +.section .bbb, "a" +.quad 0 + +.section .ccc, "a" +.quad 0 + +.section .ddd, "a" +.quad 0