Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -74,6 +74,7 @@ StringRef Name; Expr AddrExpr; Expr AlignExpr; + Expr LmaExpr; std::vector> Commands; std::vector Phdrs; std::vector Filler; @@ -126,6 +127,8 @@ createPhdrs(ArrayRef *> S); ArrayRef getFiller(StringRef Name); + uintX_t getLma(StringRef Name, uintX_t VA); + bool hasLma(StringRef Name); bool shouldKeep(InputSectionBase *S); void assignAddresses(ArrayRef *> S); int compareSections(StringRef A, StringRef B); Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -331,6 +331,24 @@ return {}; } +template bool LinkerScript::hasLma(StringRef Name) { + for (const std::unique_ptr &Base : Opt.Commands) + if (auto *Cmd = dyn_cast(Base.get())) + if (Cmd->Name == Name) + return (bool)Cmd->LmaExpr; + return nullptr; +} + +template +typename ELFT::uint LinkerScript::getLma(StringRef Name, uintX_t VA) { + for (const std::unique_ptr &Base : Opt.Commands) + if (auto *Cmd = dyn_cast(Base.get())) + if (Cmd->Name == Name && Cmd->LmaExpr) + return Cmd->LmaExpr(VA); + llvm_unreachable("no LMA for section"); + return (uintX_t)-1; +} + // Returns the index of the given section name in linker script // SECTIONS commands. Sections are laid out as the same order as they // were in the script. If a given name did not appear in the script, @@ -442,6 +460,7 @@ unsigned readPhdrType(); void readProvide(bool Hidden); void readAlign(OutputSectionCommand *Cmd); + void readAt(OutputSectionCommand *Cmd); Expr readExpr(); Expr readExpr1(Expr Lhs, int MinPrec); @@ -701,6 +720,12 @@ expect(")"); } +void ScriptParser::readAt(OutputSectionCommand *Cmd) { + expect("("); + Cmd->LmaExpr = readExpr(); + expect(")"); +} + void ScriptParser::readOutputSectionDescription(StringRef OutSec) { OutputSectionCommand *Cmd = new OutputSectionCommand(OutSec); Opt.Commands.emplace_back(Cmd); @@ -712,6 +737,9 @@ expect(":"); + if (skip("AT")) + readAt(Cmd); + if (skip("ALIGN")) readAlign(Cmd); Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -985,9 +985,12 @@ if (!needsPtLoad(Sec)) continue; - // If flags changed then we want new load segment. + // 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 = toPhdrFlags(Sec->getFlags()); - if (Flags != NewFlags) { + if (Script::X->hasLma(Sec->getName()) || Flags != NewFlags) { Load = AddHdr(PT_LOAD, NewFlags); Flags = NewFlags; } @@ -1151,7 +1154,13 @@ H.p_align = Target->PageSize; else if (H.p_type == PT_GNU_RELRO) H.p_align = 1; - H.p_paddr = H.p_vaddr; + + // Now set the segment's physical address. + if (H.p_type == PT_LOAD && First && + Script::X->hasLma(First->getName())) + H.p_paddr = Script::X->getLma(First->getName(), H.p_vaddr); + else + H.p_paddr = 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.