[ELF] Propagate LMA offset to sections with neither AT() nor AT>

Fixes https://bugs.llvm.org/show_bug.cgi?id=45313
Also fixes linkerscript/{at4.s,overlay.test} LMA address issues exposed by
011b785505.
Related: D74297

This patch improves emulation of GNU ld's heuristics on the difference
between the LMA and the VMA:
https://sourceware.org/binutils/docs/ld/Output-Section-LMA.html#Output-Section-LMA

New test linkerscript/lma-offset.s (based on at4.s) demonstrates some behaviors.

Reviewed By: psmith

Differential Revision: https://reviews.llvm.org/D76995
This commit is contained in:
Fangrui Song
2020-03-28 11:01:37 -07:00
parent 3500cc8d89
commit bb4a36ea28
7 changed files with 79 additions and 43 deletions

View File

@@ -833,6 +833,7 @@ void LinkerScript::assignOffsets(OutputSection *sec) {
if (!(sec->flags & SHF_ALLOC))
dot = 0;
bool prevLMARegionIsDefault = ctx->lmaRegion == nullptr;
ctx->memRegion = sec->memRegion;
ctx->lmaRegion = sec->lmaRegion;
if (ctx->memRegion)
@@ -851,19 +852,19 @@ void LinkerScript::assignOffsets(OutputSection *sec) {
switchTo(sec);
ctx->lmaOffset = 0;
// ctx->lmaOffset is LMA minus VMA. If LMA is explicitly specified via AT() or
// AT>, recompute ctx->lmaOffset; otherwise, if both previous/current LMA
// region is the default, reuse previous lmaOffset; otherwise, reset lmaOffset
// to 0. This emulates heuristics described in
// https://sourceware.org/binutils/docs/ld/Output-Section-LMA.html
if (sec->lmaExpr)
ctx->lmaOffset = sec->lmaExpr().getValue() - dot;
if (MemoryRegion *mr = sec->lmaRegion)
else if (MemoryRegion *mr = sec->lmaRegion)
ctx->lmaOffset = alignTo(mr->curPos, sec->alignment) - dot;
else if (!prevLMARegionIsDefault)
ctx->lmaOffset = 0;
// If neither AT nor AT> is specified for an allocatable section, the linker
// will set the LMA such that the difference between VMA and LMA for the
// section is the same as the preceding output section in the same region
// https://sourceware.org/binutils/docs-2.20/ld/Output-Section-LMA.html
// This, however, should only be done by the first "non-header" section
// in the segment.
// Propagate ctx->lmaOffset to the first "non-header" section.
if (PhdrEntry *l = ctx->outSec->ptLoad)
if (sec == findFirstSection(l))
l->lmaOffset = ctx->lmaOffset;