Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -309,6 +309,10 @@ // to be inserted into SECTIONS commands list. llvm::DenseMap> InsertAfterCommands; llvm::DenseMap> InsertBeforeCommands; + + // Used to implement OVERLAY. Contains all output sections + // that were seen in overlay descriptions. + llvm::DenseSet OverlaySections; }; extern LinkerScript *Script; Index: ELF/ScriptParser.cpp =================================================================== --- ELF/ScriptParser.cpp +++ ELF/ScriptParser.cpp @@ -79,7 +79,8 @@ uint32_t readFill(); uint32_t parseFill(StringRef Tok); void readSectionAddressType(OutputSection *Cmd); - OutputSection *readOutputSectionDescription(StringRef OutSec); + OutputSection *readOutputSectionDescription(StringRef OutSec, bool InOverlay); + std::vector readOverlay(); std::vector readOutputSectionPhdrs(); InputSectionDescription *readInputSectionDescription(StringRef Tok); StringMatcher readFilePatterns(); @@ -428,6 +429,49 @@ expect(")"); } +std::vector ScriptParser::readOverlay() { + // VA and LMA expressions are optional, though for simplicity of + // implementation we assume they are not. That is what OVERLAY was designed + // for first of all: to allow sections with overlapping VAs at different LMAs. + Expr AddrExpr = readExpr(); + expect(":"); + expect("AT"); + Expr LMAExpr = readParenExpr(); + expect("{"); + + std::vector V; + while (!errorCount() && !consume("}")) { + OutputSection *Prev = V.empty() ? nullptr : cast(V.back()); + OutputSection *Cmd = + readOutputSectionDescription(next(), /*InOverlay=*/true); + // VA is the same for all sections. The LMAs are consecutive in memory + // starting from the base load address specified. + Cmd->AddrExpr = AddrExpr; + if (Prev) + Cmd->LMAExpr = [=] { return Prev->LMAExpr().getValue() + Prev->Size; }; + else + Cmd->LMAExpr = LMAExpr; + + Script->OverlaySections.insert(Cmd); + V.push_back(Cmd); + } + + // According to the specification, at the end of the overlay, the location + // counter should be equal to the overlay base address plus size of the + // largest section seen in the overlay. + // Here we want to create the Dot assignment command to achieve that. + Expr MoveDotExpr = [=] { + uint64_t MaxSize = 0; + for (BaseCommand *Cmd : V) + if (MaxSize < cast(Cmd)->Size) + MaxSize = cast(Cmd)->Size; + return AddrExpr().getValue() + MaxSize; + }; + V.push_back(make(".", MoveDotExpr, getCurrentLocation(), + "" /*CommandString*/)); + return V; +} + void ScriptParser::readSections() { Script->HasSectionsCommand = true; @@ -440,12 +484,18 @@ std::vector V; while (!errorCount() && !consume("}")) { StringRef Tok = next(); + if (Tok == "OVERLAY") { + for (BaseCommand *Cmd : readOverlay()) + V.push_back(Cmd); + continue; + } + BaseCommand *Cmd = readProvideOrAssignment(Tok); if (!Cmd) { if (Tok == "ASSERT") Cmd = readAssert(); else - Cmd = readOutputSectionDescription(Tok); + Cmd = readOutputSectionDescription(Tok, /*InOverlay=*/false); } V.push_back(Cmd); } @@ -673,29 +723,32 @@ }; } -OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) { +OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec, + bool InOverlay) { OutputSection *Cmd = Script->createOutputSection(OutSec, getCurrentLocation()); size_t SymbolsReferenced = Script->ReferencedSymbols.size(); - if (peek() != ":") - readSectionAddressType(Cmd); - expect(":"); + if (!InOverlay) { + if (peek() != ":") + readSectionAddressType(Cmd); + expect(":"); - std::string Location = getCurrentLocation(); - if (consume("AT")) - Cmd->LMAExpr = readParenExpr(); - if (consume("ALIGN")) - Cmd->AlignExpr = checkAlignment(readParenExpr(), Location); - if (consume("SUBALIGN")) - Cmd->SubalignExpr = checkAlignment(readParenExpr(), Location); - - // Parse constraints. - if (consume("ONLY_IF_RO")) - Cmd->Constraint = ConstraintKind::ReadOnly; - if (consume("ONLY_IF_RW")) - Cmd->Constraint = ConstraintKind::ReadWrite; + std::string Location = getCurrentLocation(); + if (consume("AT")) + Cmd->LMAExpr = readParenExpr(); + if (consume("ALIGN")) + Cmd->AlignExpr = checkAlignment(readParenExpr(), Location); + if (consume("SUBALIGN")) + Cmd->SubalignExpr = checkAlignment(readParenExpr(), Location); + + // Parse constraints. + if (consume("ONLY_IF_RO")) + Cmd->Constraint = ConstraintKind::ReadOnly; + if (consume("ONLY_IF_RW")) + Cmd->Constraint = ConstraintKind::ReadWrite; + } expect("{"); while (!errorCount() && !consume("}")) { Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -1966,13 +1966,15 @@ } } +enum class OverlapKind : uint8_t { File, VirtualAddress, LoadAddress }; + // Check whether sections overlap for a specific address range (file offsets, // load and virtual adresses). // // This is a helper function called by Writer::checkNoOverlappingSections(). template static void checkForSectionOverlap(ArrayRef AllSections, - StringRef Kind, Getter GetStart, + OverlapKind Kind, Getter GetStart, Predicate ShouldSkip) { std::vector Sections; // By removing all zero-size sections we can simplify the check for overlap to @@ -1993,6 +1995,7 @@ [=](const OutputSection *A, const OutputSection *B) { return GetStart(A) < GetStart(B); }); + StringRef KindS[] = {"file", "virtual address", "load address"}; for (size_t I = 0; I < Sections.size(); ++I) { OutputSection *Sec = Sections[I]; uint64_t Start = GetStart(Sec); @@ -2003,7 +2006,15 @@ // will also start after the end of Sec. if (Start + Sec->Size <= GetStart(Other)) break; - errorOrWarn("section " + Sec->Name + " " + Kind + + + // If both sections are in OVERLAY we allow the overlapping of virtual + // addresses, because it is what OVERLAY was designed for. + if (Kind == OverlapKind::VirtualAddress && + Script->OverlaySections.count(Sec) && + Script->OverlaySections.count(Other)) + continue; + + errorOrWarn("section " + Sec->Name + " " + KindS[(uint8_t)Kind] + " range overlaps with " + Other->Name + "\n>>> " + Sec->Name + " range is " + rangeToString(Start, Sec->Size) + "\n>>> " + Other->Name + " range is " + @@ -2023,7 +2034,7 @@ // space in the file so Sec->Offset + Sec->Size can overlap with others. // If --oformat binary is specified only add SHF_ALLOC sections are added to // the output file so we skip any non-allocated sections in that case. - checkForSectionOverlap(OutputSections, "file", + checkForSectionOverlap(OutputSections, OverlapKind::File, [](const OutputSection *Sec) { return Sec->Offset; }, [](const OutputSection *Sec) { return Sec->Type == SHT_NOBITS || @@ -2045,14 +2056,14 @@ auto SkipNonAllocSections = [](const OutputSection *Sec) { return (Sec->Flags & SHF_ALLOC) == 0 || (Sec->Flags & SHF_TLS); }; - checkForSectionOverlap(OutputSections, "virtual address", + checkForSectionOverlap(OutputSections, OverlapKind::VirtualAddress, [](const OutputSection *Sec) { return Sec->Addr; }, SkipNonAllocSections); // Finally, check that the load addresses don't overlap. This will usually be // the same as the virtual addresses but can be different when using a linker // script with AT(). - checkForSectionOverlap(OutputSections, "load address", + checkForSectionOverlap(OutputSections, OverlapKind::LoadAddress, [](const OutputSection *Sec) { return Sec->getLMA(); }, SkipNonAllocSections); } Index: test/ELF/linkerscript/overlay.test =================================================================== --- test/ELF/linkerscript/overlay.test +++ test/ELF/linkerscript/overlay.test @@ -0,0 +1,30 @@ +# REQUIRES: x86 +# RUN: echo 'nop; .section .small, "a"; .long 0; .section .big, "a"; .quad 1;' \ +# RUN: | llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %t.o +# RUN: ld.lld %t.o --script %s -o %t + +SECTIONS { + OVERLAY 0x1000 : AT ( 0x4000 ) { + .out.big { *(.big) } + .out.small { *(.small) } + } +} + +## Here we check that can handle OVERLAY which will produce sections +## .out.big and .out.small with the same starting VAs, but different LMAs. +## Section .big is larger than .small, we check that placing of section +## .text does not cause overlapping error and that +## .text's VA is 0x1000 + max(sizeof(.out.big), sizeof(.out.small)). + +# RUN: llvm-readobj -sections -program-headers -elf-output-style=GNU %t | FileCheck %s + +# CHECK: Section Headers: +# CHECK: Name Type Address Off Size +# CHECK: .out.big PROGBITS 0000000000001000 001000 000008 +# CHECK: .out.small PROGBITS 0000000000001000 002000 000004 +# CHECK: .text PROGBITS 0000000000001008 002008 000001 + +# CHECK: Program Headers: +# CHECK: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# CHECK-NEXT: LOAD 0x001000 0x0000000000001000 0x0000000000004000 0x000008 0x000008 R E 0x1000 +# CHECK-NEXT: LOAD 0x002000 0x0000000000001000 0x0000000000004008 0x000009 0x000009 R E 0x1000