Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

linker: Introduce linker-tool-lld.h #59792

Merged
merged 1 commit into from
Jul 5, 2023

Conversation

duda-patryk
Copy link
Collaborator

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         00000005  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         00000005  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         00000005  00001000  00001000  00001000  2**2
  1 .data.rel.ro  00000001  00011000  00001005  00011000  2**4
  2 .data         00000001  00011020  00001025  00011020  2**5

@dcpleung
Copy link
Member

Since linker-tool-lld.h has a lot of duplicate from linker-tool-gcc.h, please include linker-tool-gcc.h then #undef and re-define the macros needed. This way LLD would get changes in LD without needing to duplicate code.

@duda-patryk
Copy link
Collaborator Author

Since linker-tool-lld.h has a lot of duplicate from linker-tool-gcc.h, please include linker-tool-gcc.h then #undef and re-define the macros needed. This way LLD would get changes in LD without needing to duplicate code.

Good suggestion! Fixed.

dcpleung
dcpleung previously approved these changes Jun 28, 2023
Copy link
Collaborator

@tejlmand tejlmand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this needs to be done for other architectures than just cortex M.

@duda-patryk
Copy link
Collaborator Author

I assume this needs to be done for other architectures than just cortex M.

Yes. Unfortunately, I don't have access to other architectures to make tests. Without this patch, LLD linker fails, because it doesn't know what to do with ALIGN_WITH_INPUT.

I was thinking about fixing that in linker-tool-lld.h. We can just replace ALIGN_WITH_INPUT with ALIGN(16) and that would work, but it horribly wastes flash.

@carlocaione
Copy link
Collaborator

I was thinking about fixing that in linker-tool-lld.h. We can just replace ALIGN_WITH_INPUT with ALIGN(16) and that would work, but it horribly wastes flash.

Wouldn't we waste at most 16 bytes?

@duda-patryk
Copy link
Collaborator Author

duda-patryk commented Jun 29, 2023

I was thinking about fixing that in linker-tool-lld.h. We can just replace ALIGN_WITH_INPUT with ALIGN(16) and that would work, but it horribly wastes flash.

Wouldn't we waste at most 16 bytes?

If there are more sections, every section will be aligned to 16 bytes, so we can waste more. In the worst case we can waste 15 bytes * number of sections.

Side note (after reading #59667 ): .ramfunc section is also copied in kernel/xip.c, but it's only one output section, so we don't need to care about any gaps.

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);
```

By default, LLVM LLD aligns both VMA and LMA to the same value, but
when --omagic (-N) option is provided then only the first output section
of given region has aligned LMA and the difference between VMA addresses
(0 is this is the first section) is added.

As a result the difference between LMA and VMA is constant for every
section, so this emulates ALIGN_WITH_INPUT option present in GNU LD
(required by XIP systems).

The --omagic flag is defined in cmake/linker/lld/target_baremetal.cmake

Example:
```
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 -N -T script.ld \
     test.o -o lld_out
```

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

In this example the first section has lower alignment than the following
section, but with -N option the difference between VMA and LMA is the
same for .data.rel.ro and .data sections.

For comparison, using BFD linker with --omagic option results in the
following:
```
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         0000000  00001000  00001000  00000094  2**2
  1 .data.rel.ro  00000001  00011000  00001005  000000a0  2**4
  2 .data         00000001  00011020  00001006  000000c0  2**5

```

with ALIGN_WITH_INPUT added, GNU LD adds the difference between VMA to
LMA, but doesn't align LMA of .data.rel.ro section:
```
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         0000000  00001000  00001000  00000074  2**2
  1 .data.rel.ro  00000001  00011000  00001005  00000080  2**4
  2 .data         00000001  00011020  00001025  000000a0  2**5
```

Signed-off-by: Patryk Duda <[email protected]>
@duda-patryk
Copy link
Collaborator Author

I found that linker flag --omagic, -N (introduced in D61688) changes LMA calculation rules:

  • First section of different LMA and VMA regions has LMA aligned
  • LMA of the next section is just LMA of previous section + difference between VMA

The flag is enabled in cmake/linker/lld/target_baremetal.cmake

As a result, LLVM LLD emulates behavior of GNU LD with ALIGN_WITH_INPUT. Please check commit message and comments in linker-tool-lld.h for more information.

@duda-patryk duda-patryk requested a review from tejlmand July 4, 2023 11:56
@fabiobaltieri fabiobaltieri merged commit fad8ae4 into zephyrproject-rtos:main Jul 5, 2023
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: ARM ARM (32-bit) Architecture area: Build System area: Toolchains Toolchains
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants