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); +}