Skip to content

Commit

Permalink
linker: Introduce linker-tool-lld.h
Browse files Browse the repository at this point in the history
Until now, linker-tool-gcc.h was used when LLD linker was chosen.
This causes linking issues because for GNU LD we use ALIGN_WITH_INPUT
attribute which is not available in LLVM LLD.

When using GNU LD we have to use ALIGN_WITH_INPUT to make sure that the
difference between VMA and LMA remains the same between output sections
that have different memory regions for VMA and LMA (RAM and FLASH).
With ALIGN_WITH_INPUT it's safe to do the memcpy of sections
that needs to be copied from flash to RAM in one function call:

(from z_data_copy() in kernel/xip.c)
```
z_early_memcpy(&__data_region_start, &__data_region_load_start,
               __data_region_end - __data_region_start);
```

LLVM LLD aligns both VMA and LMA to the same value (in the following
example it's maximum of input section alignment):

```
MEMORY {
  ROM : ORIGIN = 0x1000, LENGTH = 1K
  RAM : ORIGIN = 0x11000, LENGTH = 1K
}
SECTIONS {
  .text 0x1000 : {
  	*(.text*)
  } >ROM

  .data.rel.ro : {
  	*(.data.rel.ro)
  } >RAM AT>ROM

  .data : {
  	*(.data*)
  } >RAM AT>ROM
}
```

```
echo '.globl _start; _start: nop; .byte 1;'\
     '.data.rel.ro; .balign 16; .byte 0;'\
     '.data; .balign 32; .byte 0;' | \
     llvm-mc -filetype=obj -triple=arm - -o test.o

armv7m-cros-eabi-ld.lld --sort-section=alignment -T script.ld \
     test.o -o lld_out
```

```
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         0000000  00001000  00001000  00001000  2**2
  1 .data.rel.ro  00000001  00011000  00001010  00011000  2**4
  2 .data         00000001  00011020  00001040  00011020  2**5
```

In this example the first section has lower alignment than the following
section, despite of aligning both LMA and VMA there is a different gap
size between `.data.rel.ro` and `.data` sections. To fix that, we can
set output section alignment of `.data.rel.ro` to 32:

```
  .data.rel.ro : ALIGN(32) {
  	*(.data.rel.ro)
  } >RAM AT>ROM
```

which results in the following:

```
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         0000000  00001000  00001000  00001000  2**2
  1 .data.rel.ro  00000001  00011000  00001020  00011000  2**5
  2 .data         00000001  00011020  00001040  00011020  2**5
```

For comparison, using BFD linker with ALIGN_WITH_INPUT results in the
following:
```
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         0000000  00001000  00001000  00001000  2**2
  1 .data.rel.ro  00000001  00011000  00001005  00011000  2**4
  2 .data         00000001  00011020  00001025  00011020  2**5
```

Signed-off-by: Patryk Duda <[email protected]>
  • Loading branch information
duda-patryk committed Jun 27, 2023
1 parent 6050a10 commit 2a45b86
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 3 deletions.
2 changes: 1 addition & 1 deletion cmake/linker/lld/target_base.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
macro(toolchain_ld_base)

if(NOT PROPERTY_LINKER_SCRIPT_DEFINES)
set_property(GLOBAL PROPERTY PROPERTY_LINKER_SCRIPT_DEFINES -D__GCC_LINKER_CMD__)
set_property(GLOBAL PROPERTY PROPERTY_LINKER_SCRIPT_DEFINES -D__LLD_LINKER_CMD__)
endif()

# TOOLCHAIN_LD_FLAGS comes from compiler/clang/target.cmake
Expand Down
18 changes: 17 additions & 1 deletion include/zephyr/arch/arm/aarch32/cortex_m/scripts/linker.ld
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,23 @@ SECTIONS

GROUP_START(DATA_REGION)

SECTION_DATA_PROLOGUE(_DATA_SECTION_NAME,,)
#if defined(__LLD_LINKER_CMD__) && defined(CONFIG_XIP)
/*
* Note about aligning sections where VMA != LMA.
*
* LLVM LLD always aligns LMA and VMA section addresses to the same value.
* However, it doesn't necessary mean that the gaps created after aligning are
* equal (it can happen when subsequent sections have ascending alignment).
*
* To make sure that the gaps have the same size on both LMA and VMA we need to
* align the first section to the greatest alignment of all sections.
*/
#define DATA_SECTION_ALIGN ALIGN(16)
#else
#define DATA_SECTION_ALIGN
#endif

SECTION_DATA_PROLOGUE(_DATA_SECTION_NAME,,DATA_SECTION_ALIGN)
{
__data_region_start = .;
__data_start = .;
Expand Down
202 changes: 202 additions & 0 deletions include/zephyr/linker/linker-tool-lld.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
/*
* Copyright (c) 2023, Google, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @file
* @brief LLVM LLD linker defs
*
* This header file defines the necessary macros used by the linker script for
* use with the LLD linker.
*/

#ifndef ZEPHYR_INCLUDE_LINKER_LINKER_TOOL_LLD_H_
#define ZEPHYR_INCLUDE_LINKER_LINKER_TOOL_LLD_H_

#include <zephyr/sys/mem_manage.h>

#if defined(CONFIG_ARM)
#if defined(CONFIG_BIG_ENDIAN)
#define OUTPUT_FORMAT_ "elf32-bigarm"
#else
#define OUTPUT_FORMAT_ "elf32-littlearm"
#endif
OUTPUT_FORMAT(OUTPUT_FORMAT_)
#elif defined(CONFIG_ARM64)
OUTPUT_FORMAT("elf64-littleaarch64")
#elif defined(CONFIG_ARC)
#if defined(CONFIG_ISA_ARCV3) && defined(CONFIG_64BIT)
OUTPUT_FORMAT("elf64-littlearc64")
#elif defined(CONFIG_ISA_ARCV3) && !defined(CONFIG_64BIT)
OUTPUT_FORMAT("elf32-littlearc64")
#else
OUTPUT_FORMAT("elf32-littlearc", "elf32-bigarc", "elf32-littlearc")
#endif
#elif defined(CONFIG_X86)
#if defined(CONFIG_X86_64)
OUTPUT_FORMAT("elf64-x86-64")
OUTPUT_ARCH("i386:x86-64")
#else
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH("i386")
#endif
#elif defined(CONFIG_NIOS2)
OUTPUT_FORMAT("elf32-littlenios2", "elf32-bignios2", "elf32-littlenios2")
#elif defined(CONFIG_RISCV)
OUTPUT_ARCH("riscv")
#ifdef CONFIG_64BIT
OUTPUT_FORMAT("elf64-littleriscv")
#else
OUTPUT_FORMAT("elf32-littleriscv")
#endif
#elif defined(CONFIG_XTENSA)
/* Not needed */
#elif defined(CONFIG_MIPS)
OUTPUT_ARCH("mips")
#elif defined(CONFIG_ARCH_POSIX)
/* Not needed */
#elif defined(CONFIG_SPARC)
OUTPUT_FORMAT("elf32-sparc")
#else
#error Arch not supported.
#endif

/*
* The GROUP_START() and GROUP_END() macros are used to define a group
* of sections located in one memory area, such as RAM, ROM, etc.
* The <where> parameter is the name of the memory area.
*/
#define GROUP_START(where)
#define GROUP_END(where)

/**
* @def GROUP_LINK_IN
*
* Route memory to a specified memory area
*
* The GROUP_LINK_IN() macro is located at the end of the section
* description and tells the linker that this section is located in
* the memory area specified by 'where' argument.
*
* This macro is intentionally undefined for CONFIG_MMU systems when
* CONFIG_KERNEL_VM_BASE is not the same as CONFIG_SRAM_BASE_ADDRESS,
* as both the LMA and VMA destinations must be known for all sections
* as this corresponds to physical vs. virtual location.
*
* @param where Destination memory area
*/
#if defined(CONFIG_ARCH_POSIX)
#define GROUP_LINK_IN(where)
#elif !defined(Z_VM_KERNEL)
#define GROUP_LINK_IN(where) > where
#endif

/**
* @def GROUP_ROM_LINK_IN
*
* Route memory for a read-only section
*
* The GROUP_ROM_LINK_IN() macro is located at the end of the section
* description and tells the linker that this a read-only section
* that is physically placed at the 'lregion` argument.
*
* If CONFIG_XIP is active, the 'lregion' area is flash memory.
*
* If CONFIG_MMU is active, the vregion argument will be used to
* determine where this is located in the virtual memory map, otherwise
* it is ignored.
*
* @param vregion Output VMA (only used if CONFIG_MMU where LMA != VMA)
* @param lregion Output LMA
*/
#if defined(CONFIG_ARCH_POSIX)
#define GROUP_ROM_LINK_IN(vregion, lregion)
#elif defined(Z_VM_KERNEL)
#define GROUP_ROM_LINK_IN(vregion, lregion) > vregion AT > lregion

Check warning on line 117 in include/zephyr/linker/linker-tool-lld.h

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

CONSTANT_COMPARISON

include/zephyr/linker/linker-tool-lld.h:117 Comparisons should place the constant on the right side of the test
#else
#define GROUP_ROM_LINK_IN(vregion, lregion) > lregion
#endif

/**
* @def GROUP_DATA_LINK_IN
*
* Route memory for read-write sections that are loaded.
*
* Used for initialized data sections that on XIP platforms must be copied at
* startup.
*
* @param vregion Output VMA
* @param lregion Output LMA (only used if CONFIG_MMU if VMA != LMA,
* or CONFIG_XIP)
*/
#if defined(CONFIG_ARCH_POSIX)
#define GROUP_DATA_LINK_IN(vregion, lregion)
#elif defined(CONFIG_XIP) || defined(Z_VM_KERNEL)
#define GROUP_DATA_LINK_IN(vregion, lregion) > vregion AT > lregion

Check warning on line 137 in include/zephyr/linker/linker-tool-lld.h

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

CONSTANT_COMPARISON

include/zephyr/linker/linker-tool-lld.h:137 Comparisons should place the constant on the right side of the test
#else
#define GROUP_DATA_LINK_IN(vregion, lregion) > vregion
#endif

/**
* @def GROUP_NOLOAD_LINK_IN
*
* Route memory for read-write sections that are NOT loaded; typically this
* is only used for 'BSS' and 'noinit'.
*
* @param vregion Output VMA
* @param lregion Output LMA (only used if CONFIG_MMU if VMA != LMA,
* corresponds to physical location)
*/
#if defined(CONFIG_ARCH_POSIX)
#define GROUP_NOLOAD_LINK_IN(vregion, lregion)
#elif defined(Z_VM_KERNEL)
#define GROUP_NOLOAD_LINK_IN(vregion, lregion) > vregion AT > lregion

Check warning on line 155 in include/zephyr/linker/linker-tool-lld.h

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

CONSTANT_COMPARISON

include/zephyr/linker/linker-tool-lld.h:155 Comparisons should place the constant on the right side of the test
#elif defined(CONFIG_XIP)
#define GROUP_NOLOAD_LINK_IN(vregion, lregion) > vregion AT > vregion

Check warning on line 157 in include/zephyr/linker/linker-tool-lld.h

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

CONSTANT_COMPARISON

include/zephyr/linker/linker-tool-lld.h:157 Comparisons should place the constant on the right side of the test
#else
#define GROUP_NOLOAD_LINK_IN(vregion, lregion) > vregion
#endif

/**
* @def SECTION_PROLOGUE
*
* The SECTION_PROLOGUE() macro is used to define the beginning of a section.
*
* No need to do any special alignment when virtual memory map is enabled,
* because LLD always aligns both LMA and VMA to the same value.
*
* @param name Name of the output section
* @param options Section options, such as (NOLOAD), or left blank
* @param align Alignment directives, such as SUBALIGN(). May be blank.
*/
#define SECTION_PROLOGUE(name, options, align) \
name options : align

/**
* @def SECTION_DATA_PROLOGUE
*
* Same as for SECTION_PROLOGUE(), except that this one must be used
* for data sections which on XIP platforms will have differing
* virtual and load addresses (i.e. they'll be copied into RAM at
* program startup). Such a section must also use
* GROUP_DATA_LINK_IN to specify the correct output load address.
*
* This is equivalent to SECTION_PROLOGUE() when linking using LLD.
*
* LLD aligns LMA and VMA to the same value, so on XIP systems, it's necessary
* to align the first section to the highest alignment from a group of sections
* with the same virtual and load regions to avoid different size of gaps when
* alignment of sections is ascending.
*
* @param name Name of the output section
* @param options Section options, or left blank
* @param align Alignment directives, such as SUBALIGN(). May be blank.
*/
#define SECTION_DATA_PROLOGUE(name, options, align) \
SECTION_PROLOGUE(name, options, align)

#define COMMON_SYMBOLS *(COMMON)

#endif /* ZEPHYR_INCLUDE_LINKER_LINKER_TOOL_LLD_H_ */
2 changes: 2 additions & 0 deletions include/zephyr/linker/linker-tool.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include <zephyr/linker/linker-tool-gcc.h>
#elif defined(__MWDT_LINKER_CMD__)
#include <zephyr/linker/linker-tool-mwdt.h>
#elif defined(__LLD_LINKER_CMD__)
#include <zephyr/linker/linker-tool-lld.h>
#else
#error "Unknown toolchain"
#endif
Expand Down
2 changes: 1 addition & 1 deletion include/zephyr/toolchain.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
#include <zephyr/toolchain/mwdt.h>
#elif defined(__ARMCOMPILER_VERSION)
#include <zephyr/toolchain/armclang.h>
#elif defined(__llvm__)
#elif defined(__llvm__) || (defined(_LINKER) && defined(__LLD_LINKER_CMD__))
#include <zephyr/toolchain/llvm.h>
#elif defined(__GNUC__) || (defined(_LINKER) && defined(__GCC_LINKER_CMD__))
#include <zephyr/toolchain/gcc.h>
Expand Down

0 comments on commit 2a45b86

Please sign in to comment.