Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -90,7 +90,7 @@ StringRef Name; Expr AddrExpr; Expr AlignExpr; - Expr LmaExpr; + Expr LMAExpr; Expr SubalignExpr; std::vector> Commands; std::vector Phdrs; @@ -165,6 +165,7 @@ virtual uint64_t getOutputSectionAddress(StringRef Name) = 0; virtual uint64_t getOutputSectionSize(StringRef Name) = 0; virtual uint64_t getOutputSectionAlign(StringRef Name) = 0; + virtual uint64_t getOutputSectionLMA(StringRef Name) = 0; virtual uint64_t getHeaderSize() = 0; virtual uint64_t getSymbolValue(StringRef S) = 0; virtual bool isDefined(StringRef S) = 0; @@ -205,7 +206,7 @@ ArrayRef getFiller(StringRef Name); void writeDataBytes(StringRef Name, uint8_t *Buf); - Expr getLma(StringRef Name); + bool hasLMA(StringRef Name); bool shouldKeep(InputSectionBase *S); void assignOffsets(OutputSectionCommand *Cmd); void assignAddresses(std::vector> &Phdrs); @@ -213,6 +214,7 @@ uint64_t getOutputSectionAddress(StringRef Name) override; uint64_t getOutputSectionSize(StringRef Name) override; uint64_t getOutputSectionAlign(StringRef Name) override; + uint64_t getOutputSectionLMA(StringRef Name) override; uint64_t getHeaderSize() override; uint64_t getSymbolValue(StringRef S) override; bool isDefined(StringRef S) override; @@ -238,6 +240,7 @@ size_t getPhdrIndex(StringRef PhdrName); uintX_t Dot; + uintX_t LMAOffset = 0; OutputSectionBase *CurOutSec = nullptr; uintX_t ThreadBssOffset = 0; void switchTo(OutputSectionBase *Sec); Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -410,6 +410,12 @@ Dot = alignTo(Dot, CurOutSec->getAlignment()); CurOutSec->setVA(isTbss(CurOutSec) ? Dot + ThreadBssOffset : Dot); + + // If neither AT nor AT> is specified for an allocatable section, the linker + // will set the LMA such that the difference between VMA and LMA for the + // section is the same as the preceding output section in the same region + // https://sourceware.org/binutils/docs-2.20/ld/Output-Section-LMA.html + CurOutSec->setLMAOffset(LMAOffset); } template void LinkerScript::process(BaseCommand &Base) { @@ -460,12 +466,13 @@ template void LinkerScript::assignOffsets(OutputSectionCommand *Cmd) { + if (Cmd->LMAExpr) + LMAOffset = Cmd->LMAExpr(Dot) - Dot; std::vector *> Sections = findSections(*Cmd, *OutputSections); if (Sections.empty()) return; switchTo(Sections[0]); - // Find the last section output location. We will output orphan sections // there so that end symbols point to the correct location. auto E = std::find_if(Cmd->Commands.rbegin(), Cmd->Commands.rend(), @@ -749,12 +756,12 @@ writeInt(&Buf[DataCmd->Offset], DataCmd->Data, DataCmd->Size); } -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 {}; + if (Cmd->LMAExpr && Cmd->Name == Name) + return true; + return false; } // Returns the index of the given section name in linker script @@ -786,6 +793,15 @@ } template +uint64_t LinkerScript::getOutputSectionLMA(StringRef Name) { + for (OutputSectionBase *Sec : *OutputSections) + if (Sec->getName() == Name) + return Sec->getLMA(); + error("undefined section " + Name); + return 0; +} + +template uint64_t LinkerScript::getOutputSectionSize(StringRef Name) { for (OutputSectionBase *Sec : *OutputSections) if (Sec->getName() == Name) @@ -893,6 +909,7 @@ Expr readExpr(); Expr readExpr1(Expr Lhs, int MinPrec); + StringRef readParenLiteral(); Expr readPrimary(); Expr readTernary(Expr Cond); Expr readParenExpr(); @@ -1298,7 +1315,7 @@ expect(":"); if (skip("AT")) - Cmd->LmaExpr = readParenExpr(); + Cmd->LMAExpr = readParenExpr(); if (skip("ALIGN")) Cmd->AlignExpr = readParenExpr(); if (skip("SUBALIGN")) @@ -1533,6 +1550,13 @@ return new BytesDataCommand(Val, Size); } +StringRef ScriptParser::readParenLiteral() { + expect("("); + StringRef Tok = next(); + expect(")"); + return Tok; +} + Expr ScriptParser::readPrimary() { if (peek() == "(") return readParenExpr(); @@ -1551,12 +1575,14 @@ // 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 ScriptBase->getOutputSectionAddress(Name); }; } + if (Tok == "LOADADDR") { + StringRef Name = readParenLiteral(); + return [=](uint64_t Dot) { return ScriptBase->getOutputSectionLMA(Name); }; + } if (Tok == "ASSERT") return readAssert(); if (Tok == "ALIGN") { @@ -1564,10 +1590,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 == "DEFINED") { expect("("); @@ -1609,15 +1633,11 @@ 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 ScriptBase->getOutputSectionSize(Name); }; } if (Tok == "ALIGNOF") { - expect("("); - StringRef Name = next(); - expect(")"); + StringRef Name = readParenLiteral(); return [=](uint64_t Dot) { return ScriptBase->getOutputSectionAlign(Name); }; } 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; } uintX_t getFileOffset() { return Header.sh_offset; } void setSHName(unsigned Val) { Header.sh_name = Val; } @@ -126,6 +128,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 @@ -1033,7 +1033,6 @@ template std::vector> Writer::createPhdrs() { std::vector Ret; - auto AddHdr = [&](unsigned Type, unsigned Flags) -> Phdr * { Ret.emplace_back(Type, Flags); return &Ret.back(); @@ -1079,7 +1078,7 @@ // different flags or is loaded at a discontiguous address using AT linker // script command. uintX_t NewFlags = computeFlags(Sec->getPhdrFlags()); - if (Script::X->getLma(Sec->getName()) || Flags != NewFlags) { + if (Script::X->hasLMA(Sec->getName()) || Flags != NewFlags) { Load = AddHdr(PT_LOAD, NewFlags); Flags = NewFlags; } @@ -1256,21 +1255,14 @@ H.p_memsz = Last->getVA() + Last->getSize() - First->getVA(); H.p_offset = First->getFileOff(); H.p_vaddr = First->getVA(); + if (!P.HasLMA) + H.p_paddr = First->getLMA(); } if (H.p_type == PT_LOAD) H.p_align = Config->MaxPageSize; else if (H.p_type == PT_GNU_RELRO) H.p_align = 1; - 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. if (H.p_type == PT_TLS) { Index: test/ELF/linkerscript/loadaddr.s =================================================================== --- test/ELF/linkerscript/loadaddr.s +++ test/ELF/linkerscript/loadaddr.s @@ -0,0 +1,42 @@ +# 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 +# RUN: echo "SECTIONS { v = LOADADDR(.zzz); }" > %t.script +# RUN: not ld.lld %t --script %t.script -o %t2 2>&1 | FileCheck --check-prefix=ERROR %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 +# ERROR: undefined section .zzz + +.global _start +_start: + nop + +.section .aaa, "a" +.quad 0 + +.section .bbb, "a" +.quad 0 + +.section .ccc, "a" +.quad 0 + +.section .ddd, "a" +.quad 0