From 27b20eb4a78c52e9bd393260ad72cdc12e87ea37 Mon Sep 17 00:00:00 2001 From: Parth Arora Date: Mon, 5 Jan 2026 08:21:08 -0800 Subject: [PATCH] Reiterate layout step until section addresses converge This commit modifies the layout step to reiterate until the section addresses converge. The reiteration has an upper limit of 4. On reaching this limit, a note is emitted and the layout reiteration is stopped. It is not warning as of now because it can cause link failure when --fatal-warnings is used. This note will later be converted to a warning once the feature is complete. Please note that this reiteration limit is for layout iterations before relaxations take place. After a layout relaxation pass, the reiteration count is reset to 0. Resolves #687 Signed-off-by: Parth Arora --- include/eld/Diagnostics/DiagLayouts.inc | 4 +- include/eld/Readers/ELFSection.h | 4 +- include/eld/Target/GNULDBackend.h | 7 + lib/Target/CreateProgramHeaders.hpp | 925 +++++++++--------- lib/Target/CreateScriptProgramHeaders.hpp | 552 ++++++----- lib/Target/GNULDBackend.cpp | 21 + .../DotForwReference/DotForwReference.test | 32 + .../standalone/DotForwReference/Inputs/1.c | 4 + .../DotForwReference/Inputs/script.1.phdr.t | 12 + .../DotForwReference/Inputs/script.1.t | 7 + .../DotForwReference/Inputs/script.2.phdr.t | 15 + .../DotForwReference/Inputs/script.2.t | 10 + .../DotForwReference/Inputs/script.3.phdr.t | 15 + .../DotForwReference/Inputs/script.3.t | 10 + 14 files changed, 890 insertions(+), 728 deletions(-) create mode 100644 test/Common/standalone/DotForwReference/DotForwReference.test create mode 100644 test/Common/standalone/DotForwReference/Inputs/1.c create mode 100644 test/Common/standalone/DotForwReference/Inputs/script.1.phdr.t create mode 100644 test/Common/standalone/DotForwReference/Inputs/script.1.t create mode 100644 test/Common/standalone/DotForwReference/Inputs/script.2.phdr.t create mode 100644 test/Common/standalone/DotForwReference/Inputs/script.2.t create mode 100644 test/Common/standalone/DotForwReference/Inputs/script.3.phdr.t create mode 100644 test/Common/standalone/DotForwReference/Inputs/script.3.t diff --git a/include/eld/Diagnostics/DiagLayouts.inc b/include/eld/Diagnostics/DiagLayouts.inc index 6c3713336..603b57d0c 100644 --- a/include/eld/Diagnostics/DiagLayouts.inc +++ b/include/eld/Diagnostics/DiagLayouts.inc @@ -62,4 +62,6 @@ DIAG(warning_zero_sized_fragment_for_non_zero_symbol, DiagnosticEngine::Warning, DIAG(error_offset_not_assigned_for_output_section, DiagnosticEngine::Error, "Requested offset for output section %0 has not yet been assigned") DIAG(warn_empty_segment, DiagnosticEngine::Warning, - "Empty segment: '%0'") \ No newline at end of file + "Empty segment: '%0'") +DIAG(note_section_address_not_converging, DiagnosticEngine::Note, + "Section '%0' address did not converge after %1 layout passes") diff --git a/include/eld/Readers/ELFSection.h b/include/eld/Readers/ELFSection.h index a3fbdbdb2..e9fbacea6 100644 --- a/include/eld/Readers/ELFSection.h +++ b/include/eld/Readers/ELFSection.h @@ -179,6 +179,8 @@ class ELFSection : public ELFSectionBase { uint64_t pAddr() const { return PAddr; } + bool hasLMA() const { return PAddr != InvalidAddr; } + void setOffsetAndAddr(uint64_t Off); void setOffset(uint64_t Off) { Offset = Off; } @@ -301,7 +303,7 @@ class ELFSection : public ELFSectionBase { uint64_t Offset = ~uint64_t(0); uint64_t Addr = InvalidAddr; /// FIXME: only relevant for output sections. - uint64_t PAddr; + uint64_t PAddr = InvalidAddr; LDSymbol *Symbol = nullptr; /// FIXME: Only relevant for LTO. This should be moved out. diff --git a/include/eld/Target/GNULDBackend.h b/include/eld/Target/GNULDBackend.h index 5bc4fa39f..1f87957b4 100644 --- a/include/eld/Target/GNULDBackend.h +++ b/include/eld/Target/GNULDBackend.h @@ -18,6 +18,7 @@ #include "eld/Input/ELFDynObjectFile.h" #endif #include "eld/Object/ObjectBuilder.h" +#include "eld/Object/OutputSectionEntry.h" #include "eld/Readers/CommonELFSection.h" #include "eld/Readers/ELFExecObjParser.h" #include "eld/Readers/ELFSection.h" @@ -1046,6 +1047,10 @@ class GNULDBackend { } #endif + void setVMA(ELFSection &S, uint64_t vma, bool ignoreChangedSection); + + void setLMA(ELFSection &S, uint64_t lma); + protected: Module &m_Module; @@ -1184,6 +1189,8 @@ class GNULDBackend { GNUVerDefFragment *GNUVerDefFrag = nullptr; std::unordered_map OutputVersionIDs; #endif + OutputSectionEntry * changedOutputSection = nullptr; + const int maxPreRelaxationPasses = 4; }; } // namespace eld diff --git a/lib/Target/CreateProgramHeaders.hpp b/lib/Target/CreateProgramHeaders.hpp index 1e0b2b200..df9608739 100644 --- a/lib/Target/CreateProgramHeaders.hpp +++ b/lib/Target/CreateProgramHeaders.hpp @@ -163,7 +163,6 @@ bool GNULDBackend::createProgramHdrs() { startvma = getImageBase(hasInterp, load_ehdr); m_ImageStartVMA = startvma; dotSymbol->setValue(startvma); - dotSymbol->setValue(startvma); disconnect_lma_vma = false; noLoadSections.clear(); resetNewSectionsAddedToLayout(); @@ -174,525 +173,537 @@ bool GNULDBackend::createProgramHdrs() { m_Module.getConfig().options().printTimingStats()); evaluateScriptAssignments(/*afterLayout=*/false); } + changedOutputSection = nullptr; }; - reset_state(); - if (load_ehdr) setNeedEhdr(); if (wantPhdr) setNeedPhdr(); - while (out != outEnd) { - bool createPT_LOAD = false; - bool createNOTE_segment = false; - // Handle AT - bool useSetLMA = false; - // Handle MEMORY with VMA region - bool hasVMARegion = false; - // Handle explicit LMA specified with - // LMA region - bool hasLMARegion = false; - - bool curIsDebugSection = false; - - ELFSection *cur = (*out)->getSection(); - - bool isCurAlloc = cur->isAlloc(); - - // - // Add file header to layout, if it is not present. - // - if (!isEhdrInLayout()) - addFileHeaderToLayout(); - - // - // Add program header to layout, if it is not present. - // - if (!isPhdrInLayout()) - addProgramHeaderToLayout(); - - // - // If there were new sections added, reset and resume - // - if (isNewSectionsAddedToLayout()) { - reset_state(); - continue; - } + int preRelaxPassIter = 0; + do { + reset_state(); + while (out != outEnd) { + bool createPT_LOAD = false; + bool createNOTE_segment = false; + // Handle AT + bool useSetLMA = false; + // Handle MEMORY with VMA region + bool hasVMARegion = false; + // Handle explicit LMA specified with + // LMA region + bool hasLMARegion = false; + + bool curIsDebugSection = false; + + ELFSection *cur = (*out)->getSection(); + + bool isCurAlloc = cur->isAlloc(); + + // + // Add file header to layout, if it is not present. + // + if (!isEhdrInLayout()) + addFileHeaderToLayout(); + + // + // Add program header to layout, if it is not present. + // + if (!isPhdrInLayout()) + addProgramHeaderToLayout(); + + // + // If there were new sections added, reset and resume + // + if (isNewSectionsAddedToLayout()) { + reset_state(); + continue; + } - bool isPrevTBSS = prev && prev->isTBSS(); + bool isPrevTBSS = prev && prev->isTBSS(); - bool isPrevRelRO = - config().options().hasRelro() && prev && isRelROSection(prev); + bool isPrevRelRO = + config().options().hasRelro() && prev && isRelROSection(prev); - bool isCurRelRO = - config().options().hasRelro() && cur && isRelROSection(cur); + bool isCurRelRO = + config().options().hasRelro() && cur && isRelROSection(cur); - bool isNoLoad = ((*out)->prolog().type() == OutputSectDesc::NOLOAD); + bool isNoLoad = ((*out)->prolog().type() == OutputSectDesc::NOLOAD); - if (isNoLoad) - cur->setType(llvm::ELF::SHT_NOBITS); + if (isNoLoad) + cur->setType(llvm::ELF::SHT_NOBITS); - // Skip ehdr and phdr if the linker configuration does not - // need file header and program header to be loaded - if (!isEhdrNeeded() && (cur == m_ehdr)) { - ++out; - continue; - } + // Skip ehdr and phdr if the linker configuration does not + // need file header and program header to be loaded + if (!isEhdrNeeded() && (cur == m_ehdr)) { + ++out; + continue; + } - cur->setWanted(cur->wantedInOutput() || cur->size()); + cur->setWanted(cur->wantedInOutput() || cur->size()); - if (!isPhdrNeeded() && (cur == m_phdr)) { - ++out; - continue; - } + if (!isPhdrNeeded() && (cur == m_phdr)) { + ++out; + continue; + } - if ((!isCurAlloc) && cur->isWanted()) - curIsDebugSection = true; - - // Linker script overriding below. - std::optional scriptvma; - bool doAlign = true; - // If the output section specified a VMA value. - if ((*out)->prolog().hasVMA()) { - (*out)->prolog().vma().evaluateAndRaiseError(); - // If the output section descriptor has an alignment specified - // honor the alignment specified, the alignment would have been - // reflected in the section alignment. - // The linker doesn't align the section if there was no alignment - // specified for the output section but a "VMA" was specified. - if (!(*out)->prolog().hasAlign()) - doAlign = false; - scriptvma = (*out)->prolog().vma().result(); - if (isCurAlloc) - dotSymbol->setValue(*scriptvma); - if ((*out)->epilog().hasRegion() && - (*out)->epilog().region().containsVMA(scriptvma.value())) { - hasVMARegion = true; + if ((!isCurAlloc) && cur->isWanted()) + curIsDebugSection = true; + + // Linker script overriding below. + std::optional scriptvma; + bool doAlign = true; + // If the output section specified a VMA value. + if ((*out)->prolog().hasVMA()) { + (*out)->prolog().vma().evaluateAndRaiseError(); + // If the output section descriptor has an alignment specified + // honor the alignment specified, the alignment would have been + // reflected in the section alignment. + // The linker doesn't align the section if there was no alignment + // specified for the output section but a "VMA" was specified. + if (!(*out)->prolog().hasAlign()) + doAlign = false; + scriptvma = (*out)->prolog().vma().result(); + if (isCurAlloc) + dotSymbol->setValue(*scriptvma); + if ((*out)->epilog().hasRegion() && + (*out)->epilog().region().containsVMA(scriptvma.value())) { + hasVMARegion = true; + } } - } - // If we find that a section is specified with an address - addr = config().options().addressMap().find(cur->name()); - if (addr != addrEnd) { - vma = addr->getValue(); - if (isCurAlloc) - createPT_LOAD = true; - if (isCurAlloc) - dotSymbol->setValue(vma); - } + // If we find that a section is specified with an address + addr = config().options().addressMap().find(cur->name()); + if (addr != addrEnd) { + vma = addr->getValue(); + if (isCurAlloc) + createPT_LOAD = true; + if (isCurAlloc) + dotSymbol->setValue(vma); + } - // Check if the user specified MEMORY - if ((*out)->epilog().hasRegion() && !scriptvma) { - hasVMARegion = true; - ScriptMemoryRegion &R = (*out)->epilog().region(); - vma = R.getVirtualAddr(*out); - if (isCurAlloc) - dotSymbol->setValue(vma); - } + // Check if the user specified MEMORY + if ((*out)->epilog().hasRegion() && !scriptvma) { + hasVMARegion = true; + ScriptMemoryRegion &R = (*out)->epilog().region(); + vma = R.getVirtualAddr(*out); + if (isCurAlloc) + dotSymbol->setValue(vma); + } - // If there is an AT Table. Lets try to check the dot value with the - // section specified in the AT Table. - if (atTable.size() && m_AtTableIndex < atTable.size()) { - ELFSection *atSection = atTable[m_AtTableIndex]; - if (curIsDebugSection || - ((atSection->addr() < dotSymbol->value()) || - (scriptvma && (*scriptvma > atSection->addr())))) { - out = script.sectionMap().insert(out, atSection); - SectionMap::iterator outPrev = --out; - SectionMap::iterator outCur = ++out; - (*outCur)->setOrder((*outPrev)->order()); - if (m_Module.getPrinter()->isVerbose()) - config().raise(Diag::verbose_inserting_section_at_fixed_addr) - << atSection->name() << cur->addr() - << atSection->getInputFile()->getInput()->decoratedPath() - << (*out)->name(); - if (out != script.sectionMap().begin()) - (*outCur)->moveSectionAssignments(*outPrev); - if (atSection->hasSectionData()) { - for (auto &f : atSection->getFragmentList()) - f->getOwningSection()->setOutputSection(*outCur); + // If there is an AT Table. Lets try to check the dot value with the + // section specified in the AT Table. + if (atTable.size() && m_AtTableIndex < atTable.size()) { + ELFSection *atSection = atTable[m_AtTableIndex]; + if (curIsDebugSection || + ((atSection->addr() < dotSymbol->value()) || + (scriptvma && (*scriptvma > atSection->addr())))) { + out = script.sectionMap().insert(out, atSection); + SectionMap::iterator outPrev = --out; + SectionMap::iterator outCur = ++out; + (*outCur)->setOrder((*outPrev)->order()); + if (m_Module.getPrinter()->isVerbose()) + config().raise(Diag::verbose_inserting_section_at_fixed_addr) + << atSection->name() << cur->addr() + << atSection->getInputFile()->getInput()->decoratedPath() + << (*out)->name(); + if (out != script.sectionMap().begin()) + (*outCur)->moveSectionAssignments(*outPrev); + if (atSection->hasSectionData()) { + for (auto &f : atSection->getFragmentList()) + f->getOwningSection()->setOutputSection(*outCur); + } + m_AtTableIndex++; + reset_state(); + continue; } - m_AtTableIndex++; - reset_state(); - continue; } - } - - if (curIsDebugSection || (*out)->isDiscard()) { - cur->setAddr(dotSymbol->value()); - evaluateAssignments(*out, m_AtTableIndex); - evaluateAssignmentsAtEndOfOutputSection(*out); - cur->setWanted(cur->wantedInOutput() || cur->size()); - ++out; - cur->setAddr(0); - cur->setPaddr(0); - continue; - } - // Whatever the linker wants to set would go here. - if (cur->isFixedAddr()) { - vma = cur->addr(); - if (isCurAlloc) - dotSymbol->setValue(vma); - } + if (curIsDebugSection || (*out)->isDiscard()) { + setVMA(*cur, dotSymbol->value(), /*ignoreChangedSection=*/true); + evaluateAssignments(*out, m_AtTableIndex); + evaluateAssignmentsAtEndOfOutputSection(*out); + cur->setWanted(cur->wantedInOutput() || cur->size()); + ++out; + setVMA(*cur, 0, /*ignoreChangedSection=*/true); + setLMA(*cur, 0); + continue; + } - // Get the value from the dot value. - vma = dotSymbol->value(); - if (doAlign) - alignAddress(vma, cur->getAddrAlign()); - - // check if the physical address is being set, otherwise if - // if this is the first section and the VMA was forced then - // set PMA = VMA. For all sections following the first section - // PMA will be calculated separately below - if ((*out)->prolog().hasLMA()) { - useSetLMA = true; - disconnect_lma_vma = true; - } + // Whatever the linker wants to set would go here. + if (cur->isFixedAddr()) { + vma = cur->addr(); + if (isCurAlloc) + dotSymbol->setValue(vma); + } - // Check if the user specified MEMORY for LMA - if ((*out)->epilog().hasLMARegion()) { - hasLMARegion = true; - disconnect_lma_vma = true; - } + // Get the value from the dot value. + vma = dotSymbol->value(); + if (doAlign) + alignAddress(vma, cur->getAddrAlign()); - cur_flag = getSegmentFlag(cur->getFlags()); + // check if the physical address is being set, otherwise if + // if this is the first section and the VMA was forced then + // set PMA = VMA. For all sections following the first section + // PMA will be calculated separately below + if ((*out)->prolog().hasLMA()) { + useSetLMA = true; + disconnect_lma_vma = true; + } - if (linkerScriptHasMemoryCommand && (*out)->epilog().hasRegion()) - cur_mem_region = (*out) - ->epilog() - .region() - .getMemoryDesc() - ->getMemorySpec() - ->getMemoryDescriptor(); + // Check if the user specified MEMORY for LMA + if ((*out)->epilog().hasLMARegion()) { + hasLMARegion = true; + disconnect_lma_vma = true; + } - // If the user specifies the linker to create a separate rosegment, do that. - if (!config().options().rosegment() || config().options().isOMagic()) - cur_flag = (cur_flag & ~llvm::ELF::PF_X); + cur_flag = getSegmentFlag(cur->getFlags()); - if (config().options().isOMagic()) - cur_flag = (cur_flag & ~llvm::ELF::PF_W); + if (linkerScriptHasMemoryCommand && (*out)->epilog().hasRegion()) + cur_mem_region = (*out) + ->epilog() + .region() + .getMemoryDesc() + ->getMemorySpec() + ->getMemoryDescriptor(); - // getSegmentFlag returns 0 if the section is not allocatable. - if ((cur_flag != prev_flag) && (isCurAlloc)) - createPT_LOAD = true; + // If the user specifies the linker to create a separate rosegment, do + // that. + if (!config().options().rosegment() || config().options().isOMagic()) + cur_flag = (cur_flag & ~llvm::ELF::PF_X); - if (linkerScriptHasMemoryCommand && (cur_mem_region != prev_mem_region)) - createPT_LOAD = true; + if (config().options().isOMagic()) + cur_flag = (cur_flag & ~llvm::ELF::PF_W); - if (linkerScriptHasMemoryCommand && (cur_mem_region != prev_mem_region)) - createPT_LOAD = true; + // getSegmentFlag returns 0 if the section is not allocatable. + if ((cur_flag != prev_flag) && (isCurAlloc)) + createPT_LOAD = true; - // If the current section is alloc section and if the previous section is - // NOBITS and current is PROGBITS, we need to create a new segment. - if (isCurAlloc && cur->isWanted() && !isPrevTBSS) { - if ((cur_flag == prev_flag) && handleBSS(prev, cur)) + if (linkerScriptHasMemoryCommand && (cur_mem_region != prev_mem_region)) createPT_LOAD = true; - } - // Calculate VMA offset - int64_t vmaoffset = 0; - // Adjust the offset with VMA. - if (prev && !isPrevTBSS) { - // Calculate VMA only if section is allocatable - if (!createPT_LOAD && isCurAlloc) { - // it's possible that the location counter was set with an address - // which is less than the previous output section address + size, in - // which case the vmaoffset should 0x0 - if (vma >= (prev->addr() + prev->size())) - vmaoffset = vma - (prev->addr() + prev->size()); - else - // Without program headers, we always create a new segment if the - // previous address is larger than the current address. Calculating - // the offsets is taken care automatically. The gnu linker may choose - // to place the sections in decreasing order of addresses and later - // decide to sort it as is done in CreateScriptProgramheaders. - // TODO: raise an error to see LMA needs to be assigned. - createPT_LOAD = true; + if (linkerScriptHasMemoryCommand && (cur_mem_region != prev_mem_region)) + createPT_LOAD = true; - // If Program headers are not specified and the vma difference is big - // lets create a PT_LOAD to adjust the offset. - if (std::abs(vmaoffset) > (int64_t)segAlign) + // If the current section is alloc section and if the previous section is + // NOBITS and current is PROGBITS, we need to create a new segment. + if (isCurAlloc && cur->isWanted() && !isPrevTBSS) { + if ((cur_flag == prev_flag) && handleBSS(prev, cur)) createPT_LOAD = true; } - } - // create a PT_LOAD if the pma and vma are decoupled - bool createNewSegmentDueToLMADifference = false; - if ((useSetLMA || hasLMARegion) && (pma != vma) && (isCurAlloc) && - !createPT_LOAD) { - // This will allow us to enter the condition that a PT_LOAD would need to - // be created, since pma is not equal vma. - createPT_LOAD = true; - // Lets set a boolean flag to indicate that a new segment is being - // requested because of VMA and PMA are not equal. Linker is going to - // later decide, if this new segment was really needed. - createNewSegmentDueToLMADifference = true; - } - // If we dont have a load segment created, create a PT_LOAD. - if (!load_seg) - createPT_LOAD = true; + // Calculate VMA offset + int64_t vmaoffset = 0; + // Adjust the offset with VMA. + if (prev && !isPrevTBSS) { + // Calculate VMA only if section is allocatable + if (!createPT_LOAD && isCurAlloc) { + // it's possible that the location counter was set with an address + // which is less than the previous output section address + size, in + // which case the vmaoffset should 0x0 + if (vma >= (prev->addr() + prev->size())) + vmaoffset = vma - (prev->addr() + prev->size()); + else + // Without program headers, we always create a new segment if the + // previous address is larger than the current address. Calculating + // the offsets is taken care automatically. The gnu linker may + // choose to place the sections in decreasing order of addresses and + // later decide to sort it as is done in CreateScriptProgramheaders. + // TODO: raise an error to see LMA needs to be assigned. + createPT_LOAD = true; + + // If Program headers are not specified and the vma difference is big + // lets create a PT_LOAD to adjust the offset. + if (std::abs(vmaoffset) > (int64_t)segAlign) + createPT_LOAD = true; + } + } + // create a PT_LOAD if the pma and vma are decoupled + bool createNewSegmentDueToLMADifference = false; + if ((useSetLMA || hasLMARegion) && (pma != vma) && (isCurAlloc) && + !createPT_LOAD) { + // This will allow us to enter the condition that a PT_LOAD would need + // to be created, since pma is not equal vma. + createPT_LOAD = true; + // Lets set a boolean flag to indicate that a new segment is being + // requested because of VMA and PMA are not equal. Linker is going to + // later decide, if this new segment was really needed. + createNewSegmentDueToLMADifference = true; + } - bool sectionHasLoadSeg = false; - bool sectionHasNoteSeg = false; - bool newSegmentCreated = false; + // If we dont have a load segment created, create a PT_LOAD. + if (!load_seg) + createPT_LOAD = true; - if (_segmentsForSection.find(*out) != _segmentsForSection.end()) { - load_seg = _segmentsForSection[*out].front(); - sectionHasLoadSeg = true; - } + bool sectionHasLoadSeg = false; + bool sectionHasNoteSeg = false; + bool newSegmentCreated = false; - if (isCurAlloc && (createPT_LOAD || last_section_needs_new_segment)) { - if (config().options().alignSegmentsToPage()) - alignAddress(vma, segAlign); - if (cur->isFixedAddr() && (vma != cur->addr())) { - config().raise(Diag::cannot_set_at_address) << cur->name(); - hasError = true; + if (_segmentsForSection.find(*out) != _segmentsForSection.end()) { + load_seg = _segmentsForSection[*out].front(); + sectionHasLoadSeg = true; } - cur->setAddr(vma); - // Handle setting LMA and alignment of LMA - // Explicitly align LMA to make the code easy to read - if (useSetLMA) { - (*out)->prolog().lma().evaluateAndRaiseError(); - pma = (*out)->prolog().lma().result(); - } else if (hasVMARegion || hasLMARegion) { - ScriptMemoryRegion &R = (*out)->epilog().lmaRegion(); - pma = R.getPhysicalAddr(*out); - // If LMA region has been explicitly specified - // do not align LMA. A LMA region exists for the VMA - // region just for making it easy for us to handle assigning - // physical addresses - if (!(*out)->prolog().hasAlignWithInput() && !hasLMARegion) + + if (isCurAlloc && (createPT_LOAD || last_section_needs_new_segment)) { + if (config().options().alignSegmentsToPage()) + alignAddress(vma, segAlign); + if (cur->isFixedAddr() && (vma != cur->addr())) { + config().raise(Diag::cannot_set_at_address) << cur->name(); + hasError = true; + } + setVMA(*cur, vma, /*ignoreChangedSection=*/false); + // Handle setting LMA and alignment of LMA + // Explicitly align LMA to make the code easy to read + if (useSetLMA) { + (*out)->prolog().lma().evaluateAndRaiseError(); + pma = (*out)->prolog().lma().result(); + } else if (hasVMARegion || hasLMARegion) { + ScriptMemoryRegion &R = (*out)->epilog().lmaRegion(); + pma = R.getPhysicalAddr(*out); + // If LMA region has been explicitly specified + // do not align LMA. A LMA region exists for the VMA + // region just for making it easy for us to handle assigning + // physical addresses + if (!(*out)->prolog().hasAlignWithInput() && !hasLMARegion) + alignAddress(pma, cur->getAddrAlign()); + } else if (!prev || !disconnect_lma_vma) { + pma = vma; alignAddress(pma, cur->getAddrAlign()); - } else if (!prev || !disconnect_lma_vma) { - pma = vma; - alignAddress(pma, cur->getAddrAlign()); - } else if ((last_section_needs_new_segment) && (!disconnect_lma_vma)) { - // If the LMA and VMA are disconnected, the physical address should - // always be taken as the difference of the current virtual address - // minus the previous virtual address. - pma = prev->pAddr() + prev->size(); - alignAddress(pma, cur->getAddrAlign()); - } else { - vmaoffset = vma - (prev->addr() + prev->size()); - pma = prev->pAddr() + prev->size() + vmaoffset; - alignAddress(pma, cur->getAddrAlign()); - } + } else if ((last_section_needs_new_segment) && (!disconnect_lma_vma)) { + // If the LMA and VMA are disconnected, the physical address should + // always be taken as the difference of the current virtual address + // minus the previous virtual address. + pma = prev->pAddr() + prev->size(); + alignAddress(pma, cur->getAddrAlign()); + } else { + vmaoffset = vma - (prev->addr() + prev->size()); + pma = prev->pAddr() + prev->size() + vmaoffset; + alignAddress(pma, cur->getAddrAlign()); + } - // FIXME : remove this option alignSegmentsToPage - // Handle the case without any linker scripts, - if (config().options().alignSegmentsToPage()) - alignAddress(pma, segAlign); - - cur->setPaddr(pma); - if (createNewSegmentDueToLMADifference) { - if (pma == vma) - createPT_LOAD = false; - else { - int64_t pmaoffset = 0; - if (pma >= (prev->pAddr() + prev->size())) { - pmaoffset = pma - (prev->pAddr() + prev->size()); - if (vma >= (prev->addr() + prev->size())) - vmaoffset = vma - (prev->addr() + prev->size()); - if (pmaoffset == vmaoffset) - createPT_LOAD = false; + // FIXME : remove this option alignSegmentsToPage + // Handle the case without any linker scripts, + if (config().options().alignSegmentsToPage()) + alignAddress(pma, segAlign); + + setLMA(*cur, pma); + if (createNewSegmentDueToLMADifference) { + if (pma == vma) + createPT_LOAD = false; + else { + int64_t pmaoffset = 0; + if (pma >= (prev->pAddr() + prev->size())) { + pmaoffset = pma - (prev->pAddr() + prev->size()); + if (vma >= (prev->addr() + prev->size())) + vmaoffset = vma - (prev->addr() + prev->size()); + if (pmaoffset == vmaoffset) + createPT_LOAD = false; + } } } - } - evaluateAssignments(*out, m_AtTableIndex); - cur->setWanted(cur->wantedInOutput() || cur->size()); - if (hasVMARegion) - (*out)->epilog().region().addOutputSectionVMA(*out); - if (!useSetLMA && (hasVMARegion || hasLMARegion)) - (*out)->epilog().lmaRegion().addOutputSectionLMA(*out); - if (!config().getDiagEngine()->diagnose()) { - return false; - } + evaluateAssignments(*out, m_AtTableIndex); + cur->setWanted(cur->wantedInOutput() || cur->size()); + if (hasVMARegion) + (*out)->epilog().region().addOutputSectionVMA(*out); + if (!useSetLMA && (hasVMARegion || hasLMARegion)) + (*out)->epilog().lmaRegion().addOutputSectionLMA(*out); + if (!config().getDiagEngine()->diagnose()) { + return false; + } - if (m_AtTableIndex < atTable.size() && - (atTable[m_AtTableIndex]->addr() < (cur->addr() + cur->size()))) { - config().raise(Diag::cannot_place_at_section) - << atTable[m_AtTableIndex]->name() << cur->name(); - ++m_AtTableIndex; - hasError = true; - } - // The flag last_section_needs_new_segment controls that if there was a - // need to create a PT_LOAD segment but we determined that the size of the - // section is 0, we set the flag and move on. - if (!cur->isWanted()) - last_section_needs_new_segment = true; - else if (!sectionHasLoadSeg && - (createPT_LOAD || (cur->addr() < prev->addr()))) { - load_seg = make(llvm::ELF::PT_LOAD, - getSegmentFlag(cur->getFlags())); - elfSegmentTable().addSegment(load_seg); - last_section_needs_new_segment = false; - newSegmentCreated = true; - } else - // If there was no segment created and the section is in the same load - // segment. - last_section_needs_new_segment = false; - } else { - // If the previous section is a RELRO section and the current section is - // not a RELRO section, move the non RELRO section to a new page, as the - // dynamic linker will mprotect the page after dynamic relocation. - // If this doesn't move to the next page, then any writes to the section - // will incur a pagefault and crash!. If there is a linker script dont do - // any of this, as the user may want to configure sections to be in the - // same page. This is a difference between ELD and GNU linker. This is - // done only if the previous section and the new section fall in the same - // segment. - if (enable_RELRO && isPrevRelRO && !isCurRelRO && - !linkerScriptHasSectionsCommand) - vma += abiPageSize(); - if (cur->isFixedAddr() && (vma != cur->addr())) { - config().raise(Diag::cannot_set_at_address) << cur->name(); - hasError = true; - } - if (doAlign) - alignAddress(vma, cur->getAddrAlign()); - cur->setAddr(vma); - // FIXME : de-duplicate this case. - if (useSetLMA) { - (*out)->prolog().lma().evaluateAndRaiseError(); - pma = (*out)->prolog().lma().result(); - } else if (hasVMARegion || hasLMARegion) { - ScriptMemoryRegion &R = (*out)->epilog().lmaRegion(); - pma = R.getPhysicalAddr(*out); - if (!(*out)->prolog().hasAlignWithInput() && !hasLMARegion) - alignAddress(pma, cur->getAddrAlign()); - } else if (!prev || !disconnect_lma_vma) { - pma = vma; - alignAddress(pma, cur->getAddrAlign()); + if (m_AtTableIndex < atTable.size() && + (atTable[m_AtTableIndex]->addr() < (cur->addr() + cur->size()))) { + config().raise(Diag::cannot_place_at_section) + << atTable[m_AtTableIndex]->name() << cur->name(); + ++m_AtTableIndex; + hasError = true; + } + // The flag last_section_needs_new_segment controls that if there was a + // need to create a PT_LOAD segment but we determined that the size of + // the section is 0, we set the flag and move on. + if (!cur->isWanted()) + last_section_needs_new_segment = true; + else if (!sectionHasLoadSeg && + (createPT_LOAD || (cur->addr() < prev->addr()))) { + load_seg = make(llvm::ELF::PT_LOAD, + getSegmentFlag(cur->getFlags())); + elfSegmentTable().addSegment(load_seg); + last_section_needs_new_segment = false; + newSegmentCreated = true; + } else + // If there was no segment created and the section is in the same load + // segment. + last_section_needs_new_segment = false; } else { - vmaoffset = vma - (prev->addr() + prev->size()); - pma = prev->pAddr() + prev->size() + vmaoffset; - alignAddress(pma, cur->getAddrAlign()); + // If the previous section is a RELRO section and the current section is + // not a RELRO section, move the non RELRO section to a new page, as the + // dynamic linker will mprotect the page after dynamic relocation. + // If this doesn't move to the next page, then any writes to the section + // will incur a pagefault and crash!. If there is a linker script dont + // do any of this, as the user may want to configure sections to be in + // the same page. This is a difference between ELD and GNU linker. This + // is done only if the previous section and the new section fall in the + // same segment. + if (enable_RELRO && isPrevRelRO && !isCurRelRO && + !linkerScriptHasSectionsCommand) + vma += abiPageSize(); + if (cur->isFixedAddr() && (vma != cur->addr())) { + config().raise(Diag::cannot_set_at_address) << cur->name(); + hasError = true; + } + if (doAlign) + alignAddress(vma, cur->getAddrAlign()); + setVMA(*cur, vma, /*ignoreChangedSection=*/false); + // FIXME : de-duplicate this case. + if (useSetLMA) { + (*out)->prolog().lma().evaluateAndRaiseError(); + pma = (*out)->prolog().lma().result(); + } else if (hasVMARegion || hasLMARegion) { + ScriptMemoryRegion &R = (*out)->epilog().lmaRegion(); + pma = R.getPhysicalAddr(*out); + if (!(*out)->prolog().hasAlignWithInput() && !hasLMARegion) + alignAddress(pma, cur->getAddrAlign()); + } else if (!prev || !disconnect_lma_vma) { + pma = vma; + alignAddress(pma, cur->getAddrAlign()); + } else { + vmaoffset = vma - (prev->addr() + prev->size()); + pma = prev->pAddr() + prev->size() + vmaoffset; + alignAddress(pma, cur->getAddrAlign()); + } + setLMA(*cur, pma); + evaluateAssignments(*out, m_AtTableIndex); + if (hasVMARegion) + (*out)->epilog().region().addOutputSectionVMA(*out); + if (!useSetLMA && (hasVMARegion || hasLMARegion)) + (*out)->epilog().lmaRegion().addOutputSectionLMA(*out); + if (!config().getDiagEngine()->diagnose()) { + return false; + } + if (m_AtTableIndex < atTable.size() && + (atTable[m_AtTableIndex]->addr() < (cur->addr() + cur->size()))) { + config().raise(Diag::cannot_place_at_section) + << atTable[m_AtTableIndex]->name() << cur->name(); + ++m_AtTableIndex; + hasError = true; + } } - cur->setPaddr(pma); - evaluateAssignments(*out, m_AtTableIndex); - if (hasVMARegion) - (*out)->epilog().region().addOutputSectionVMA(*out); - if (!useSetLMA && (hasVMARegion || hasLMARegion)) - (*out)->epilog().lmaRegion().addOutputSectionLMA(*out); + + // Evaluate Assignments at end of output section. + evaluateAssignmentsAtEndOfOutputSection(*out); + cur->setWanted(cur->wantedInOutput() || cur->size()); + if (!config().getDiagEngine()->diagnose()) { return false; } - if (m_AtTableIndex < atTable.size() && - (atTable[m_AtTableIndex]->addr() < (cur->addr() + cur->size()))) { - config().raise(Diag::cannot_place_at_section) - << atTable[m_AtTableIndex]->name() << cur->name(); - ++m_AtTableIndex; - hasError = true; - } - } - - // Evaluate Assignments at end of output section. - evaluateAssignmentsAtEndOfOutputSection(*out); - cur->setWanted(cur->wantedInOutput() || cur->size()); - - if (!config().getDiagEngine()->diagnose()) { - return false; - } - // Append the section to the segment. - if (load_seg && isCurAlloc) { - if (!last_section_needs_new_segment && - (createPT_LOAD || cur->isWanted())) { - if (isPrevRelRO && !isCurRelRO) - enable_RELRO = false; - // Check if the current section is a NOTE section and create appropriate - // NOTE segments. - if (cur->getType() == llvm::ELF::SHT_NOTE) { - note_seg = nullptr; - if (_noteSegmentsForSection.find(*out) != - _noteSegmentsForSection.end()) { - note_seg = _noteSegmentsForSection[*out]; - sectionHasNoteSeg = true; - } - // If the current section has no NOTE segment, check if the previous - // section had a note segment and check if we can reuse it. - if (!sectionHasNoteSeg) { - if (prev) { - if (prev->getType() == llvm::ELF::SHT_NOTE && - (cur->getFlags() == prev->getFlags())) - note_seg = _noteSegmentsForSection[prevOut]; + // Append the section to the segment. + if (load_seg && isCurAlloc) { + if (!last_section_needs_new_segment && + (createPT_LOAD || cur->isWanted())) { + if (isPrevRelRO && !isCurRelRO) + enable_RELRO = false; + // Check if the current section is a NOTE section and create + // appropriate NOTE segments. + if (cur->getType() == llvm::ELF::SHT_NOTE) { + note_seg = nullptr; + if (_noteSegmentsForSection.find(*out) != + _noteSegmentsForSection.end()) { + note_seg = _noteSegmentsForSection[*out]; + sectionHasNoteSeg = true; } - // If there was no note segment that we can reuse, lets create one. - if (!note_seg) + // If the current section has no NOTE segment, check if the previous + // section had a note segment and check if we can reuse it. + if (!sectionHasNoteSeg) { + if (prev) { + if (prev->getType() == llvm::ELF::SHT_NOTE && + (cur->getFlags() == prev->getFlags())) + note_seg = _noteSegmentsForSection[prevOut]; + } + // If there was no note segment that we can reuse, lets create + // one. + if (!note_seg) + createNOTE_segment = true; + } + // If for some reason, we create a new load segment, create a + // separate segment for NOTE too. + if (newSegmentCreated) createNOTE_segment = true; + if (createNOTE_segment) { + note_seg = make(llvm::ELF::PT_NOTE, + getSegmentFlag(cur->getFlags())); + elfSegmentTable().addSegment(note_seg); + newSegmentCreated = true; + } + // Append the section to the NOTE segment. + if (!sectionHasNoteSeg) { + note_seg->append(*out); + note_seg->updateFlag(getSegmentFlag(cur->getFlags())); + _noteSegmentsForSection[*out] = note_seg; + note_seg->setAlign(note_seg->getMaxSectionAlign()); + } } - // If for some reason, we create a new load segment, create a separate - // segment for NOTE too. - if (newSegmentCreated) - createNOTE_segment = true; - if (createNOTE_segment) { - note_seg = make(llvm::ELF::PT_NOTE, - getSegmentFlag(cur->getFlags())); - elfSegmentTable().addSegment(note_seg); - newSegmentCreated = true; - } - // Append the section to the NOTE segment. - if (!sectionHasNoteSeg) { - note_seg->append(*out); - note_seg->updateFlag(getSegmentFlag(cur->getFlags())); - _noteSegmentsForSection[*out] = note_seg; - note_seg->setAlign(note_seg->getMaxSectionAlign()); + // Handle GNU RELRO sections. + if (isRelROSection(cur) && enable_RELRO) { + bool sectionHasRelROSegment = true; + bool needRelRoSegment = false; + if (_relRoSegmentSections.find(*out) == _relRoSegmentSections.end()) + sectionHasRelROSegment = false; + if (!sectionHasRelROSegment && !pt_gnu_relro) + needRelRoSegment = true; + if (needRelRoSegment) { + pt_gnu_relro = make(llvm::ELF::PT_GNU_RELRO, + getSegmentFlag(cur->getFlags())); + elfSegmentTable().addSegment(pt_gnu_relro); + newSegmentCreated = true; + } + // Append the section to the RELRO segment, if + // this is the first section for the RELRO segment (or) + // the previous section also was a RELRO section. + if (!sectionHasRelROSegment && + (!pt_gnu_relro->size() || isRelROSection(prev))) { + pt_gnu_relro->append(*out); + pt_gnu_relro->updateFlag(getSegmentFlag(cur->getFlags())); + _relRoSegmentSections[*out] = pt_gnu_relro; + pt_gnu_relro->setAlign(pt_gnu_relro->getMaxSectionAlign()); + } } - } - // Handle GNU RELRO sections. - if (isRelROSection(cur) && enable_RELRO) { - bool sectionHasRelROSegment = true; - bool needRelRoSegment = false; - if (_relRoSegmentSections.find(*out) == _relRoSegmentSections.end()) - sectionHasRelROSegment = false; - if (!sectionHasRelROSegment && !pt_gnu_relro) - needRelRoSegment = true; - if (needRelRoSegment) { - pt_gnu_relro = make(llvm::ELF::PT_GNU_RELRO, - getSegmentFlag(cur->getFlags())); - elfSegmentTable().addSegment(pt_gnu_relro); - newSegmentCreated = true; + if (!cur->isTBSS()) + prev = cur; + if (isNoLoad) + noLoadSections.push_back(cur); + prevOut = (*out); + prev_flag = cur_flag; + if (!cur_mem_region.empty()) + prev_mem_region = cur_mem_region; + load_seg->updateFlag(getSegmentFlag(cur->getFlags())); + changeSymbolsFromAbsoluteToGlobal(*out); + last_section_needs_new_segment = false; + if (!sectionHasLoadSeg) { + _segmentsForSection[*out].push_back(load_seg); + load_seg->append(*out); + // Set the PT_LOAD alignment. + load_seg->setAlign(config().options().isOMagic() + ? load_seg->getMaxSectionAlign() + : segAlign); } - // Append the section to the RELRO segment, if - // this is the first section for the RELRO segment (or) - // the previous section also was a RELRO section. - if (!sectionHasRelROSegment && - (!pt_gnu_relro->size() || isRelROSection(prev))) { - pt_gnu_relro->append(*out); - pt_gnu_relro->updateFlag(getSegmentFlag(cur->getFlags())); - _relRoSegmentSections[*out] = pt_gnu_relro; - pt_gnu_relro->setAlign(pt_gnu_relro->getMaxSectionAlign()); + // Continue from beginning since we added a segment. + if (newSegmentCreated) { + reset_state(); + continue; } } - if (!cur->isTBSS()) - prev = cur; - if (isNoLoad) - noLoadSections.push_back(cur); - prevOut = (*out); - prev_flag = cur_flag; - if (!cur_mem_region.empty()) - prev_mem_region = cur_mem_region; - load_seg->updateFlag(getSegmentFlag(cur->getFlags())); - changeSymbolsFromAbsoluteToGlobal(*out); - last_section_needs_new_segment = false; - if (!sectionHasLoadSeg) { - _segmentsForSection[*out].push_back(load_seg); - load_seg->append(*out); - // Set the PT_LOAD alignment. - load_seg->setAlign(config().options().isOMagic() - ? load_seg->getMaxSectionAlign() - : segAlign); - } - // Continue from beginning since we added a segment. - if (newSegmentCreated) { - reset_state(); - continue; - } } + ++out; } - ++out; + } while (++preRelaxPassIter < maxPreRelaxationPasses && changedOutputSection); + + // If layout did not converge within the limit, emit a warning for the + // changed section. + if (changedOutputSection) { + config().raise(Diag::note_section_address_not_converging) + << changedOutputSection->name() << maxPreRelaxationPasses; } if (dynamic && dynamic->size()) { diff --git a/lib/Target/CreateScriptProgramHeaders.hpp b/lib/Target/CreateScriptProgramHeaders.hpp index 933634db6..4e29b614a 100644 --- a/lib/Target/CreateScriptProgramHeaders.hpp +++ b/lib/Target/CreateScriptProgramHeaders.hpp @@ -58,7 +58,7 @@ bool GNULDBackend::createScriptProgramHdrs() { _segmentsForSection[m_phdr->getOutputSection()].push_back( _segments[EhdrInLoadSegment->getSpec()->name()]); }; - + bool linkerScriptHasMemoryCommand = m_Module.getScript().hasMemoryCommand(); auto reset_state = [&](OutputSectionEntry *OS = nullptr) { // Reset the state, and start from beginning. outBegin = script.sectionMap().begin(); @@ -87,319 +87,333 @@ bool GNULDBackend::createScriptProgramHdrs() { m_Module.getConfig().options().printTimingStats()); evaluateScriptAssignments(/*afterLayout=*/false); } - }; - reset_state(); + changedOutputSection = nullptr; + if (linkerScriptHasMemoryCommand) + clearMemoryRegions(); + }; - while (out != outEnd) { - bool useSetLMA = false; + int preRelaxPassIter = 0; + do { + reset_state(); + while (out != outEnd) { + bool useSetLMA = false; - bool isStartOfSegment = false; + bool isStartOfSegment = false; - bool curIsDebugSection = false; + bool curIsDebugSection = false; - ELFSection *cur = (*out)->getSection(); + ELFSection *cur = (*out)->getSection(); - bool isCurAllocSection = cur->isAlloc(); + bool isCurAllocSection = cur->isAlloc(); - // - // Add file header to layout, if it is not present. - // - if (isEhdrNeeded() && !isEhdrInLayout()) { - addFileHeaderToLayout(); - assign_filehdr(); - } + // + // Add file header to layout, if it is not present. + // + if (isEhdrNeeded() && !isEhdrInLayout()) { + addFileHeaderToLayout(); + assign_filehdr(); + } - // - // Add program header to layout, if it is not present. - // - if (isPhdrNeeded() && !isPhdrInLayout()) { - addProgramHeaderToLayout(); - assign_phdr(); - } + // + // Add program header to layout, if it is not present. + // + if (isPhdrNeeded() && !isPhdrInLayout()) { + addProgramHeaderToLayout(); + assign_phdr(); + } - // - // If there were new sections added, reset and resume - // - if (isNewSectionsAddedToLayout()) { - reset_state(); - continue; - } + // + // If there were new sections added, reset and resume + // + if (isNewSectionsAddedToLayout()) { + reset_state(); + continue; + } - bool isNoLoad = ((*out)->prolog().type() == OutputSectDesc::NOLOAD); + bool isNoLoad = ((*out)->prolog().type() == OutputSectDesc::NOLOAD); - bool hasVMARegion = false; - bool hasLMARegion = false; - bool hasFixedLMA = false; + bool hasVMARegion = false; + bool hasLMARegion = false; + bool hasFixedLMA = false; - if (isNoLoad) - cur->setType(llvm::ELF::SHT_NOBITS); + if (isNoLoad) + cur->setType(llvm::ELF::SHT_NOBITS); - // Skip ehdr and phdr if the linker configuration does not - // need file header and program header to be loaded - if (!isEhdrNeeded() && (cur == m_ehdr)) { - ++out; - continue; - } + // Skip ehdr and phdr if the linker configuration does not + // need file header and program header to be loaded + if (!isEhdrNeeded() && (cur == m_ehdr)) { + ++out; + continue; + } - if (!isPhdrNeeded() && (cur == m_phdr)) { - ++out; - continue; - } + if (!isPhdrNeeded() && (cur == m_phdr)) { + ++out; + continue; + } - bool isRelocationSection = false; - if (cur->isRelocationSection()) - isRelocationSection = true; + bool isRelocationSection = false; + if (cur->isRelocationSection()) + isRelocationSection = true; - if (!isCurAllocSection && isRelocationSection) { - ++out; - continue; - } + if (!isCurAllocSection && isRelocationSection) { + ++out; + continue; + } - for (auto &seg : _segmentsForSection[*out]) { - if (seg->empty() && seg->isLoadSegment()) { - curLoadSegment = seg; - isStartOfSegment = true; + for (auto &seg : _segmentsForSection[*out]) { + if (seg->empty() && seg->isLoadSegment()) { + curLoadSegment = seg; + isStartOfSegment = true; + } } - } - if ((!isCurAllocSection) && cur->size()) - curIsDebugSection = true; - - std::optional scriptvma; - bool doAlign = true; - // If the output section specified a VMA value. - if ((*out)->prolog().hasVMA()) { - (*out)->prolog().vma().evaluateAndRaiseError(); - scriptvma = (*out)->prolog().vma().result(); - if (isCurAllocSection) - dotSymbol->setValue(*scriptvma); - if ((*out)->epilog().hasRegion() && - (*out)->epilog().region().containsVMA(scriptvma.value())) { - hasVMARegion = true; + if ((!isCurAllocSection) && cur->size()) + curIsDebugSection = true; + + std::optional scriptvma; + bool doAlign = true; + // If the output section specified a VMA value. + if ((*out)->prolog().hasVMA()) { + (*out)->prolog().vma().evaluateAndRaiseError(); + scriptvma = (*out)->prolog().vma().result(); + if (isCurAllocSection) + dotSymbol->setValue(*scriptvma); + if ((*out)->epilog().hasRegion() && + (*out)->epilog().region().containsVMA(scriptvma.value())) { + hasVMARegion = true; + } + // If the output section descriptor has an alignment specified + // honor the alignment specified, the alignment would have been + // reflected in the section alignment. + // The linker doesn't align the section if there was no alignment + // specified for the output section but a "VMA" was specified. + if (!(*out)->prolog().hasAlign()) + doAlign = false; } - // If the output section descriptor has an alignment specified - // honor the alignment specified, the alignment would have been - // reflected in the section alignment. - // The linker doesn't align the section if there was no alignment - // specified for the output section but a "VMA" was specified. - if (!(*out)->prolog().hasAlign()) - doAlign = false; - } - // If we find that a section is specified with an address - addr = config().options().addressMap().find(cur->name()); - if (addr != addrEnd) { - vma = addr->getValue(); - scriptvma = vma; - if (isCurAllocSection) - dotSymbol->setValue(vma); - } + // If we find that a section is specified with an address + addr = config().options().addressMap().find(cur->name()); + if (addr != addrEnd) { + vma = addr->getValue(); + scriptvma = vma; + if (isCurAllocSection) + dotSymbol->setValue(vma); + } - // Check if the user specified MEMORY - if ((*out)->epilog().hasRegion() && !scriptvma) { - ScriptMemoryRegion &R = (*out)->epilog().region(); - vma = R.getVirtualAddr(*out); - if (isCurAllocSection) - dotSymbol->setValue(vma); - hasVMARegion = true; - } - // If there is an AT Table. Lets try to check the dot value with the - // section specified in the AT Table. - if (atTable.size() && m_AtTableIndex < atTable.size()) { - ELFSection *atSection = atTable[m_AtTableIndex]; - if (curIsDebugSection || - ((atSection->addr() < dotSymbol->value()) || - (scriptvma && (*scriptvma > atSection->addr())))) { - // If the AT section falls within the bounds of the current address and - // the size of the current section, try to place the AT section inside - // the output section while traversing through the assignments. - auto &segmentsForSection = _segmentsForSection[*out]; - out = script.sectionMap().insert(out, atSection); - SectionMap::iterator outPrev = --out; - SectionMap::iterator outCur = ++out; - (*outCur)->setOrder((*outPrev)->order()); - if (out != script.sectionMap().begin()) { - (*outCur)->moveSectionAssignments(*outPrev); - _segmentsForSection[*outCur]; - } - if (m_Module.getPrinter()->isVerbose()) - config().raise(Diag::verbose_inserting_section_at_fixed_addr) - << atSection->name() << cur->addr() - << atSection->getInputFile()->getInput()->decoratedPath() - << (*out)->name(); - if (atSection->hasSectionData()) { - for (auto &f : atSection->getFragmentList()) - f->getOwningSection()->setOutputSection(*outCur); + // Check if the user specified MEMORY + if ((*out)->epilog().hasRegion() && !scriptvma) { + ScriptMemoryRegion &R = (*out)->epilog().region(); + vma = R.getVirtualAddr(*out); + if (isCurAllocSection) + dotSymbol->setValue(vma); + hasVMARegion = true; + } + // If there is an AT Table. Lets try to check the dot value with the + // section specified in the AT Table. + if (atTable.size() && m_AtTableIndex < atTable.size()) { + ELFSection *atSection = atTable[m_AtTableIndex]; + if (curIsDebugSection || + ((atSection->addr() < dotSymbol->value()) || + (scriptvma && (*scriptvma > atSection->addr())))) { + // If the AT section falls within the bounds of the current address + // and the size of the current section, try to place the AT section + // inside the output section while traversing through the assignments. + auto &segmentsForSection = _segmentsForSection[*out]; + out = script.sectionMap().insert(out, atSection); + SectionMap::iterator outPrev = --out; + SectionMap::iterator outCur = ++out; + (*outCur)->setOrder((*outPrev)->order()); + if (out != script.sectionMap().begin()) { + (*outCur)->moveSectionAssignments(*outPrev); + _segmentsForSection[*outCur]; + } + if (m_Module.getPrinter()->isVerbose()) + config().raise(Diag::verbose_inserting_section_at_fixed_addr) + << atSection->name() << cur->addr() + << atSection->getInputFile()->getInput()->decoratedPath() + << (*out)->name(); + if (atSection->hasSectionData()) { + for (auto &f : atSection->getFragmentList()) + f->getOwningSection()->setOutputSection(*outCur); + } + // Pick the PHDR's from the current section if the previous section is + // a + // nullptr section. + if ((*outPrev)->getSection()->isNullKind()) + _segmentsForSection[*outCur] = segmentsForSection; + else + _segmentsForSection[*outCur] = _segmentsForSection[*outPrev]; + m_AtTableIndex++; + reset_state(*outCur); + continue; } - // Pick the PHDR's from the current section if the previous section is - // a - // nullptr section. - if ((*outPrev)->getSection()->isNullKind()) - _segmentsForSection[*outCur] = segmentsForSection; - else - _segmentsForSection[*outCur] = _segmentsForSection[*outPrev]; - m_AtTableIndex++; - reset_state(*outCur); + } + + if (curIsDebugSection || (*out)->isDiscard()) { + setVMA(*cur, dotSymbol->value(), /*ignoreChangedSection=*/true); + evaluateAssignments(*out, m_AtTableIndex); + evaluateAssignmentsAtEndOfOutputSection(*out); + setVMA(*cur, 0, /*ignoreChangedSection=*/true); + setLMA(*cur, 0); + ++out; continue; } - } - if (curIsDebugSection || (*out)->isDiscard()) { - cur->setAddr(dotSymbol->value()); - evaluateAssignments(*out, m_AtTableIndex); - evaluateAssignmentsAtEndOfOutputSection(*out); - cur->setAddr(0); - cur->setPaddr(0); - ++out; - continue; - } + if (cur->isFixedAddr()) { + vma = cur->addr(); + if (isCurAllocSection) + dotSymbol->setValue(vma); + } - if (cur->isFixedAddr()) { - vma = cur->addr(); - if (isCurAllocSection) - dotSymbol->setValue(vma); - } + // Get the value from the dot value. + vma = dotSymbol->value(); - // Get the value from the dot value. - vma = dotSymbol->value(); + if (doAlign) + alignAddress(vma, cur->getAddrAlign()); - if (doAlign) - alignAddress(vma, cur->getAddrAlign()); + // Check if the segment has a LMA address that is set. + if (isStartOfSegment) { + if (curLoadSegment->hasFixedLMA()) { + hasFixedLMA = true; + disconnect_lma_vma = true; + } + } - // Check if the segment has a LMA address that is set. - if (isStartOfSegment) { - if (curLoadSegment->hasFixedLMA()) { - hasFixedLMA = true; + // check if the physical address is being set, otherwise if + // if this is the first section and the VMA was forced then + // set PMA = VMA. For all sections following the first section + // PMA will be calculated separately below + if ((*out)->prolog().hasLMA()) { + useSetLMA = true; disconnect_lma_vma = true; } - } - // check if the physical address is being set, otherwise if - // if this is the first section and the VMA was forced then - // set PMA = VMA. For all sections following the first section - // PMA will be calculated separately below - if ((*out)->prolog().hasLMA()) { - useSetLMA = true; - disconnect_lma_vma = true; - } + // Check if the user specified MEMORY for LMA + if ((*out)->epilog().hasLMARegion()) { + hasLMARegion = true; + disconnect_lma_vma = true; + } - // Check if the user specified MEMORY for LMA - if ((*out)->epilog().hasLMARegion()) { - hasLMARegion = true; - disconnect_lma_vma = true; - } + // Adjust the offset with VMA. + if (prev && (!useSetLMA || !hasLMARegion || !hasFixedLMA) && + disconnect_lma_vma) { + // if pma was previously defined but not for this section then increment + // this pma by the prev->pAddr + size and include the vmaoffset if this + // not the start of a new segment. + if (config().options().isCompact()) + pma = prev->pAddr() + + ((prev->isBSS() || prev->isTBSS()) ? 0 : prev->size()); + else if (!is_previous_start_of_segment) + pma = prev->pAddr() + (vma - prev->addr()); + } else { + pma = vma; + } - // Adjust the offset with VMA. - if (prev && (!useSetLMA || !hasLMARegion || !hasFixedLMA) && - disconnect_lma_vma) { - // if pma was previously defined but not for this section then increment - // this pma by the prev->pAddr + size and include the vmaoffset if this - // not the start of a new segment. - if (config().options().isCompact()) - pma = prev->pAddr() + - ((prev->isBSS() || prev->isTBSS()) ? 0 : prev->size()); - else if (!is_previous_start_of_segment) - pma = prev->pAddr() + (vma - prev->addr()); - } else { - pma = vma; - } + if (cur->isFixedAddr() && (vma != cur->addr())) { + config().raise(Diag::cannot_set_at_address) << cur->name(); + hasError = true; + } - if (cur->isFixedAddr() && (vma != cur->addr())) { - config().raise(Diag::cannot_set_at_address) << cur->name(); - hasError = true; - } + setVMA(*cur, vma, /*ignoreChangedSection=*/false); + cur->setAddr(vma); + + if (useSetLMA) { + (*out)->prolog().lma().evaluateAndRaiseError(); + pma = (*out)->prolog().lma().result(); + } else if (hasVMARegion || hasLMARegion) { + ScriptMemoryRegion &R = (*out)->epilog().lmaRegion(); + pma = R.getPhysicalAddr(*out); + if (!(*out)->prolog().hasAlignWithInput() && !hasLMARegion) + alignAddress(pma, cur->getAddrAlign()); + } else if (hasFixedLMA) { + // If the current segment has a fixed LMA address, then + curLoadSegment->fixedLMA()->evaluateAndRaiseError(); + pma = curLoadSegment->fixedLMA()->result(); + } else { + alignAddress(pma, cur->getAddrAlign()); + } - cur->setAddr(vma); + setLMA(*cur, pma); + evaluateAssignments(*out, m_AtTableIndex); - if (useSetLMA) { - (*out)->prolog().lma().evaluateAndRaiseError(); - pma = (*out)->prolog().lma().result(); - } else if (hasVMARegion || hasLMARegion) { - ScriptMemoryRegion &R = (*out)->epilog().lmaRegion(); - pma = R.getPhysicalAddr(*out); - if (!(*out)->prolog().hasAlignWithInput() && !hasLMARegion) - alignAddress(pma, cur->getAddrAlign()); - } else if (hasFixedLMA) { - // If the current segment has a fixed LMA address, then - curLoadSegment->fixedLMA()->evaluateAndRaiseError(); - pma = curLoadSegment->fixedLMA()->result(); - } else { - alignAddress(pma, cur->getAddrAlign()); - } + if (hasVMARegion) + (*out)->epilog().region().addOutputSectionVMA(*out); + // If LMA is set explicitly then there is no LMA region for this + // section. + if (!useSetLMA && (hasVMARegion || hasLMARegion)) + (*out)->epilog().lmaRegion().addOutputSectionLMA(*out); + if (!config().getDiagEngine()->diagnose()) { + return false; + } + if (m_AtTableIndex < atTable.size() && + (atTable[m_AtTableIndex]->addr() < (cur->addr() + cur->size()))) { + config().raise(Diag::cannot_place_at_section) + << atTable[m_AtTableIndex]->name() << cur->name(); + ++m_AtTableIndex; + hasError = true; + } + cur->setWanted(cur->wantedInOutput() || cur->size()); + if (cur->isWanted()) { + changeSymbolsFromAbsoluteToGlobal(*out); + if (is_previous_start_of_segment) + is_previous_start_of_segment = false; + } else if (isStartOfSegment) + is_previous_start_of_segment = true; + + if (cur->isAlloc()) + last_seen_alloc_section = cur; + prev = cur; + + if (isNoLoad) + noLoadSections.push_back(cur); + + // Add sections for the segment. + for (auto &seg : _segmentsForSection[*out]) { + // If there is a VMA assigned for the section, + // lets just add the section. + if (cur->isWanted() || cur->wantedInOutput() || + (scriptvma && scriptvma.value())) { + seg->append(*out); + seg->updateFlagPhdr(getSegmentFlag(cur->getFlags())); + } + if (seg->isLoadSegment() && !config().options().isOMagic()) + seg->setAlign(abiPageSize()); + else + seg->setAlign(seg->getMaxSectionAlign()); + } - cur->setPaddr(pma); - evaluateAssignments(*out, m_AtTableIndex); - - if (hasVMARegion) - (*out)->epilog().region().addOutputSectionVMA(*out); - // If LMA is set explicitly then there is no LMA region for this - // section. - if (!useSetLMA && (hasVMARegion || hasLMARegion)) - (*out)->epilog().lmaRegion().addOutputSectionLMA(*out); - if (!config().getDiagEngine()->diagnose()) { - return false; - } - if (m_AtTableIndex < atTable.size() && - (atTable[m_AtTableIndex]->addr() < (cur->addr() + cur->size()))) { - config().raise(Diag::cannot_place_at_section) - << atTable[m_AtTableIndex]->name() << cur->name(); - ++m_AtTableIndex; - hasError = true; - } - cur->setWanted(cur->wantedInOutput() || cur->size()); - if (cur->isWanted()) { - changeSymbolsFromAbsoluteToGlobal(*out); - if (is_previous_start_of_segment) - is_previous_start_of_segment = false; - } else if (isStartOfSegment) - is_previous_start_of_segment = true; - - if (cur->isAlloc()) - last_seen_alloc_section = cur; - prev = cur; - - if (isNoLoad) - noLoadSections.push_back(cur); - - // Add sections for the segment. - for (auto &seg : _segmentsForSection[*out]) { - // If there is a VMA assigned for the section, - // lets just add the section. - if (cur->isWanted() || cur->wantedInOutput() || - (scriptvma && scriptvma.value())) { - seg->append(*out); - seg->updateFlagPhdr(getSegmentFlag(cur->getFlags())); + // Evaluate Assignments at the end of the output section. + evaluateAssignmentsAtEndOfOutputSection(*out); + cur->setWanted(cur->wantedInOutput() || cur->size()); + if (!config().getDiagEngine()->diagnose()) { + return false; } - if (seg->isLoadSegment() && !config().options().isOMagic()) - seg->setAlign(abiPageSize()); - else - seg->setAlign(seg->getMaxSectionAlign()); - } - // Evaluate Assignments at the end of the output section. - evaluateAssignmentsAtEndOfOutputSection(*out); - cur->setWanted(cur->wantedInOutput() || cur->size()); - if (!config().getDiagEngine()->diagnose()) { - return false; + ++out; } + } while (++preRelaxPassIter < maxPreRelaxationPasses && changedOutputSection); - ++out; - } + // If layout did not converge within the limit, emit a warning for the + // changed section. + if (changedOutputSection) { + config().raise(Diag::note_section_address_not_converging) + << changedOutputSection->name() << maxPreRelaxationPasses; + } - for (auto &seg : elfSegmentTable()) { - // Handle empty segments - if (!seg->size()) { - if (seg->isLoadSegment() && !config().options().isOMagic()) - seg->setAlign(abiPageSize()); - else - seg->setAlign(config().targets().is32Bits() ? 4 : 8); + for (auto &seg : elfSegmentTable()) { + // Handle empty segments + if (!seg->size()) { + if (seg->isLoadSegment() && !config().options().isOMagic()) + seg->setAlign(abiPageSize()); + else + seg->setAlign(config().targets().is32Bits() ? 4 : 8); + } } - } - evaluateTargetSymbolsBeforeRelaxation(); + evaluateTargetSymbolsBeforeRelaxation(); - m_Module.getAtTable().clear(); + m_Module.getAtTable().clear(); - return hasError; -} + return hasError; + } diff --git a/lib/Target/GNULDBackend.cpp b/lib/Target/GNULDBackend.cpp index 13358b2a9..e5d2d29b4 100644 --- a/lib/Target/GNULDBackend.cpp +++ b/lib/Target/GNULDBackend.cpp @@ -5348,3 +5348,24 @@ void GNULDBackend::assignOutputVersionIDs() { } } #endif + +void GNULDBackend::setVMA(ELFSection &S, uint64_t vma, + bool ignoreChangedSection) { + if (S.hasVMA() && !ignoreChangedSection && !changedOutputSection) { + uint64_t prevVMA = S.addr(); + if (prevVMA != vma) { + changedOutputSection = S.getOutputSection(); + } + } + S.setAddr(vma); +} + +void GNULDBackend::setLMA(ELFSection &S, uint64_t lma) { + if (S.hasLMA() && !changedOutputSection) { + uint64_t prevLMA = S.pAddr(); + if (prevLMA != lma) { + changedOutputSection = S.getOutputSection(); + } + } + S.setPaddr(lma); +} diff --git a/test/Common/standalone/DotForwReference/DotForwReference.test b/test/Common/standalone/DotForwReference/DotForwReference.test new file mode 100644 index 000000000..3ecd1c9bb --- /dev/null +++ b/test/Common/standalone/DotForwReference/DotForwReference.test @@ -0,0 +1,32 @@ +#---DotForwReference.test--------------------- Executable,LS ------------------# +#BEGIN_COMMENT +# Validates that a forward reference to a symbol in dot assignments within +# SECTIONS is handled correctly. +#END_COMMENT +#START_TEST +RUN: %clang %clangopts -o %t1.1.o %p/Inputs/1.c -c -ffunction-sections +RUN: %link %linkopts -o %t1.1.out %t1.1.o -T %p/Inputs/script.1.t +RUN: %readelf -S %t1.1.out | %filecheck %s +RUN: %link %linkopts -o %t1.1.phdr.out %t1.1.o -T %p/Inputs/script.1.phdr.t +RUN: %readelf -S %t1.1.phdr.out | %filecheck %s +RUN: %link %linkopts -o %t1.2.out %t1.1.o -T %p/Inputs/script.2.t 2>&1 \ +RUN: --trace=assignments | %filecheck %s --allow-empty --check-prefix TRACE +RUN: %readelf -S %t1.2.out | %filecheck %s --check-prefix CHECK2 +RUN: %link %linkopts -o %t1.2.phdr.out %t1.1.o -T %p/Inputs/script.2.phdr.t 2>&1 \ +RUN: --trace=assignments | %filecheck %s --allow-empty --check-prefix TRACE +RUN: %readelf -S %t1.2.phdr.out | %filecheck %s --check-prefix CHECK2 +RUN: %link %linkopts -o %t1.1.3.out %t1.1.o -T %p/Inputs/script.3.t 2>&1 | %filecheck %s --check-prefix=NON_CONVERGENCE_NOTE +#END_TEST + +CHECK: .text PROGBITS {{0+}}3000 + +TRACE: Trace: OUTPUT_SECTION(EVALUATE) >> .text VMA : 0x0 LMA : 0x0 +TRACE: Trace: OUTPUT_SECTION(EVALUATE) >> .text VMA : 0x4000 LMA : 0x4000 +TRACE: Trace: OUTPUT_SECTION(EVALUATE) >> .text VMA : 0x5000 LMA : 0x5000 +TRACE: Trace: OUTPUT_SECTION(EVALUATE) >> .text VMA : 0x5000 LMA : 0x5000 +TRACE-NOT: address did not converge + +CHECK2: .text PROGBITS {{0+}}5000 + +NON_CONVERGENCE_NOTE: Note: Section '.text' address did not converge after 4 layout passes + diff --git a/test/Common/standalone/DotForwReference/Inputs/1.c b/test/Common/standalone/DotForwReference/Inputs/1.c new file mode 100644 index 000000000..7d1db46c7 --- /dev/null +++ b/test/Common/standalone/DotForwReference/Inputs/1.c @@ -0,0 +1,4 @@ +int foo() { return 1; } + +int val = 3; + diff --git a/test/Common/standalone/DotForwReference/Inputs/script.1.phdr.t b/test/Common/standalone/DotForwReference/Inputs/script.1.phdr.t new file mode 100644 index 000000000..ac6049590 --- /dev/null +++ b/test/Common/standalone/DotForwReference/Inputs/script.1.phdr.t @@ -0,0 +1,12 @@ +PHDRS { + TEXT PT_LOAD; + DATA PT_LOAD; + COMMENT PT_NOTE; +} +SECTIONS { + . = u; + .text : { *(.text*) } :TEXT + .data : { *(.data*) } :DATA + .comment : { *(.comment*) } :COMMENT + u = 0x3000; +} diff --git a/test/Common/standalone/DotForwReference/Inputs/script.1.t b/test/Common/standalone/DotForwReference/Inputs/script.1.t new file mode 100644 index 000000000..f32ebb14c --- /dev/null +++ b/test/Common/standalone/DotForwReference/Inputs/script.1.t @@ -0,0 +1,7 @@ +SECTIONS { + . = u; + .text : { *(.text*) } + .data : { *(.data*) } + .comment : { *(.comment*) } + u = 0x3000; +} diff --git a/test/Common/standalone/DotForwReference/Inputs/script.2.phdr.t b/test/Common/standalone/DotForwReference/Inputs/script.2.phdr.t new file mode 100644 index 000000000..bcf849bed --- /dev/null +++ b/test/Common/standalone/DotForwReference/Inputs/script.2.phdr.t @@ -0,0 +1,15 @@ +PHDRS { + TEXT PT_LOAD; + DATA PT_LOAD; + COMMENT PT_NOTE; +} +SECTIONS { + TEXT_START = u; + . = TEXT_START; + .text : { *(.text*) } :TEXT + .data : { *(.data*) } :DATA + .comment : { *(.comment*) } :COMMENT + u = 0x4000 + v; + w = MIN(0x1000, w + 0xf00); + v = ALIGN(w, 0x1000); +} diff --git a/test/Common/standalone/DotForwReference/Inputs/script.2.t b/test/Common/standalone/DotForwReference/Inputs/script.2.t new file mode 100644 index 000000000..822e401a6 --- /dev/null +++ b/test/Common/standalone/DotForwReference/Inputs/script.2.t @@ -0,0 +1,10 @@ +SECTIONS { + TEXT_START = u; + . = TEXT_START; + .text : { *(.text*) } + .data : { *(.data*) } + .comment : { *(.comment*) } + u = 0x4000 + v; + w = MIN(0x1000, w + 0xf00); + v = ALIGN(w, 0x1000); +} diff --git a/test/Common/standalone/DotForwReference/Inputs/script.3.phdr.t b/test/Common/standalone/DotForwReference/Inputs/script.3.phdr.t new file mode 100644 index 000000000..9532fffcd --- /dev/null +++ b/test/Common/standalone/DotForwReference/Inputs/script.3.phdr.t @@ -0,0 +1,15 @@ +PHDRS { + TEXT PT_LOAD; + DATA PT_LOAD; + COMMENT PT_NOTE; +} +SECTIONS { + TEXT_START = u; + . = TEXT_START; + .text : { *(.text*) } :TEXT + .data : { *(.data*) } :DATA + .comment : { *(.comment*) } :COMMENT + u = 0x4000 + v; + w = MIN(0x3000, w + 0xf00); + v = ALIGN(w, 0x1000); +} diff --git a/test/Common/standalone/DotForwReference/Inputs/script.3.t b/test/Common/standalone/DotForwReference/Inputs/script.3.t new file mode 100644 index 000000000..bd5c2ef74 --- /dev/null +++ b/test/Common/standalone/DotForwReference/Inputs/script.3.t @@ -0,0 +1,10 @@ +SECTIONS { + TEXT_START = u; + . = TEXT_START; + .text : { *(.text*) } + .data : { *(.data*) } + .comment : { *(.comment*) } + u = 0x4000 + v; + w = MIN(0x3000, w + 0xf00); + v = ALIGN(w, 0x1000); +}