From 8ffb2097cc1adf5a0f964006a7633ed1ee4f59d3 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Fri, 19 Jun 2020 09:07:48 -0700 Subject: [PATCH] [ELF] Refine LMA offset propagation rule in D76995 If neither AT(lma) nor AT>lma_region is specified, D76995 keeps `lmaOffset` (LMA - VMA) if the previous section is in the default LMA region. This patch additionally checks that the two sections are in the same memory region. Add a test case derived from https://bugs.llvm.org/show_bug.cgi?id=45313 .mdata : AT(0xfb01000) { *(.data); } > TCM // It is odd to make .bss inherit lmaOffset, because the two sections // are in different memory regions. .bss : { *(.bss) } > DDR With this patch, section VMA/LMA match GNU ld. Note, GNU ld supports out-of-order (w.r.t sh_offset) sections and places .text and .bss in the same PT_LOAD. We don't have that behavior. Reviewed By: grimar Differential Revision: https://reviews.llvm.org/D81986 --- lld/ELF/LinkerScript.cpp | 10 ++++++---- lld/docs/ELF/linker_script.rst | 6 +++--- lld/test/ELF/linkerscript/lma-offset2.s | 30 ++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 lld/test/ELF/linkerscript/lma-offset2.s diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp index 5f508cd..72e2ebf 100644 --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -851,7 +851,8 @@ void LinkerScript::assignOffsets(OutputSection *sec) { if (!(sec->flags & SHF_ALLOC)) dot = 0; - bool prevLMARegionIsDefault = ctx->lmaRegion == nullptr; + const bool sameMemRegion = ctx->memRegion == sec->memRegion; + const bool prevLMARegionIsDefault = ctx->lmaRegion == nullptr; ctx->memRegion = sec->memRegion; ctx->lmaRegion = sec->lmaRegion; if (ctx->memRegion) @@ -872,14 +873,15 @@ void LinkerScript::assignOffsets(OutputSection *sec) { // 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 + // region is the default, and the two sections are in the same memory region, + // 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; else if (MemoryRegion *mr = sec->lmaRegion) ctx->lmaOffset = alignTo(mr->curPos, sec->alignment) - dot; - else if (!prevLMARegionIsDefault) + else if (!sameMemRegion || !prevLMARegionIsDefault) ctx->lmaOffset = 0; // Propagate ctx->lmaOffset to the first "non-header" section. diff --git a/lld/docs/ELF/linker_script.rst b/lld/docs/ELF/linker_script.rst index c5115c1..0f409b2 100644 --- a/lld/docs/ELF/linker_script.rst +++ b/lld/docs/ELF/linker_script.rst @@ -71,7 +71,7 @@ The two keywords cannot be specified at the same time. If neither ``AT(lma)`` nor ``AT>lma_region`` is specified: -- If the previous section is also in the default LMA region, the difference - between the LMA and the VMA is computed to be the same as the previous - difference. +- If the previous section is also in the default LMA region, and the two + section have the same memory regions, the difference between the LMA and the + VMA is computed to be the same as the previous difference. - Otherwise, the LMA is set to the VMA. diff --git a/lld/test/ELF/linkerscript/lma-offset2.s b/lld/test/ELF/linkerscript/lma-offset2.s new file mode 100644 index 0000000..9342739 --- /dev/null +++ b/lld/test/ELF/linkerscript/lma-offset2.s @@ -0,0 +1,30 @@ +# REQUIRES: x86 +## If neither AT(lma) nor AT>lma_region is specified, don't propagate +## lmaOffset if the section and the previous section are in different memory +## regions. + +# RUN: echo '.globl _start; _start: ret; \ +# RUN: .data; .byte 0; \ +# RUN: .bss; .byte 0' | \ +# RUN: llvm-mc -filetype=obj -triple=x86_64 - -o %t.o +# RUN: ld.lld -T %s %t.o -o %t +# RUN: llvm-readelf -l %t | FileCheck %s + +## GNU ld places .text and .bss in the same RWX PT_LOAD. +# CHECK: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg +# CHECK-NEXT: LOAD 0x001000 0x000000000fb00000 0x000000000fb00000 0x000001 0x000001 R E +# CHECK-NEXT: LOAD 0x002000 0x000000001f400000 0x000000000fb01000 0x000001 0x000001 RW +# CHECK-NEXT: LOAD 0x002001 0x000000000fb00001 0x000000000fb00001 0x000000 0x000001 RW + +MEMORY { + DDR : o = 0xfb00000, l = 185M + TCM : o = 0x1f400000, l = 128K +} + +SECTIONS { + .text : { *(.text) } > DDR + .mdata : AT(0xfb01000) { *(.data); } > TCM + ## .mdata and .bss are in different memory regions. Start a new PT_LOAD for + ## .bss, even if .mdata does not set a LMA region. + .bss : { *(.bss) } > DDR +} -- 2.7.4