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; @@ -146,6 +146,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; }; @@ -183,7 +184,7 @@ bool ignoreInterpSection(); ArrayRef getFiller(StringRef Name); - Expr getLma(StringRef Name); + bool hasLMA(StringRef Name); bool shouldKeep(InputSectionBase *S); void assignOffsets(OutputSectionCommand *Cmd); void assignAddresses(); @@ -192,6 +193,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; @@ -215,6 +217,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 @@ -388,6 +388,12 @@ Dot = alignTo(Dot, CurOutSec->getAlignment()); CurOutSec->setVA(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) { @@ -425,12 +431,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(), @@ -606,12 +613,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 {}; + if (Cmd->LMAExpr && Cmd->Name == Name) + return true; + return false; } // Returns the index of the given section name in linker script @@ -654,6 +661,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) @@ -756,6 +772,7 @@ Expr readExpr(); Expr readExpr1(Expr Lhs, int MinPrec); + StringRef readParenLiteral(); Expr readPrimary(); Expr readTernary(Expr Cond); Expr readParenExpr(); @@ -1185,7 +1202,7 @@ expect(":"); if (skip("AT")) - Cmd->LmaExpr = readParenExpr(); + Cmd->LMAExpr = readParenExpr(); if (skip("ALIGN")) Cmd->AlignExpr = readParenExpr(); if (skip("SUBALIGN")) @@ -1391,6 +1408,13 @@ return true; } +StringRef ScriptParser::readParenLiteral() { + expect("("); + StringRef Tok = next(); + expect(")"); + return Tok; +} + Expr ScriptParser::readPrimary() { if (peek() == "(") return readParenExpr(); @@ -1409,12 +1433,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") { @@ -1422,10 +1448,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("("); @@ -1461,15 +1485,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; } void setSHName(unsigned Val) { Header.sh_name = Val; } void writeHeaderTo(Elf_Shdr *SHdr); @@ -117,6 +119,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 @@ -945,7 +945,7 @@ template std::vector> Writer::createPhdrs() { std::vector Ret; - + bool HasLMA = false; auto AddHdr = [&](unsigned Type, unsigned Flags) -> Phdr * { Ret.emplace_back(Type, Flags); return &Ret.back(); @@ -983,13 +983,23 @@ if (!needsPtLoad(Sec)) continue; + // GNU objdump and objcopy are calculating section LMA by comparing + // segment virtual address with section virtual address and taking + // segment physical address if they do match. This means that in + // general every section for which LMA != VMA requires segment to + // be created for it. Here we can't be determined that section VMA + // is different from LMA because assignAddresses() has not been + // called yet, but this is very likely to happen if any previously + // seen section has AT specification. + HasLMA = HasLMA || Script::X->hasLMA(Sec->getName()); + // Segments are contiguous memory regions that has the same attributes // (e.g. executable or writable). There is one phdr for each segment. // Therefore, we need to create a new phdr when the next section has // 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) { + if (HasLMA || Flags != NewFlags) { Load = AddHdr(PT_LOAD, NewFlags); Flags = NewFlags; } @@ -1165,21 +1175,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 = Target->PageSize; 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/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