Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -132,6 +132,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; }; @@ -171,14 +172,16 @@ 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(); + 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; Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -388,6 +388,20 @@ 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; + + // 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 + Sec->setLMAOffset(LMAOff); + }; for (const std::unique_ptr &Base : Opt.Commands) { if (auto *Cmd = dyn_cast(Base.get())) { @@ -417,7 +431,7 @@ 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; @@ -429,7 +443,7 @@ } Dot = alignTo(Dot, Sec->getAlignment()); - Sec->setVA(Dot); + SetAddrs(Cmd, Sec, Dot); assignOffsets(Cmd, Sec); MinVA = std::min(MinVA, Dot); Dot += Sec->getSize(); @@ -514,12 +528,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 @@ -562,6 +576,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) @@ -663,6 +686,7 @@ Expr readExpr(); Expr readExpr1(Expr Lhs, int MinPrec); + StringRef readParenLiteral(); Expr readPrimary(); Expr readTernary(Expr Cond); Expr readParenExpr(); @@ -1241,6 +1265,13 @@ return true; } +StringRef ScriptParser::readParenLiteral() { + expect("("); + StringRef Tok = next(); + expect(")"); + return Tok; +} + Expr ScriptParser::readPrimary() { if (peek() == "(") return readParenExpr(); @@ -1259,12 +1290,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") { @@ -1272,10 +1305,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("("); @@ -1312,15 +1343,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); @@ -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 HasLMA = false; auto AddHdr = [&](unsigned Type, unsigned Flags) -> Phdr * { Ret.emplace_back(Type, Flags); return &Ret.back(); @@ -984,13 +984,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; } @@ -1162,21 +1172,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