Skip to content
This repository has been archived by the owner on Jan 24, 2022. It is now read-only.

LLD support (AKA LLD *almost* works out of the box) #53

Closed
japaric opened this issue Jan 17, 2018 · 5 comments
Closed

LLD support (AKA LLD *almost* works out of the box) #53

japaric opened this issue Jan 17, 2018 · 5 comments

Comments

@japaric
Copy link
Member

japaric commented Jan 17, 2018

If you switch the linker from arm-none-eabi-ld to ld.lld you'll get the following error:

$ ld.lld --version
LLD 7.0.0 (https://github.com/llvm-mirror/lld 527f7fd20d23a54ae30ccdf5071312b24e28776b) (compatible with GNU linkers)

$ xargo build
error: linking with `ld.lld` failed: exit code: 1
  |
  = note: "ld.lld" (..)
  = note: ld.lld: error: $PWD/target/thumbv7em-none-eabihf/debug/build/cortex-m-rt-f07298b7bbb47f59/out/link.x:92: NOLOAD expected, but got INFO
          >>>   .debug_gdb_scripts _stext (INFO) : {
          >>>                              ^

But if you modify this crate link.x like this:

   /* a rustc hack will force the program to read the first byte of this section,
      so we'll set the (fake) start address of this section to something we're
      sure can be read at runtime: the start of the .text section */
-  .debug_gdb_scripts _stext (INFO) : {
+  .debug_gdb_scripts _stext (NOLOAD) : {
     KEEP(*(.debug_gdb_scripts))
-  }
+  } > FLASH

Programs link correctly. The resulting binaries are well formed and debug information works. 🎉

However, this results in an artifical increase of size of the text section as reported by arm-none-eabi-size:

$ # before (linked with GNU ld)
$ arm-none-eabi-size (..)
   text    data     bss     dec     hex filename
   1300       0       0    1300     514 (..)

$ arm-none-eabi-size -Ax (..)
section                  size         addr
.vector_table           0x188    0x8000000
.text                   0x384    0x8000188
.rodata                   0x8    0x800050c
.bss                      0x0   0x20000000
.data                     0x0   0x20000000
.debug_gdb_scripts       0x88    0x8000188

$ # after (linked with GNU ld)
$ arm-none-eabi-size (..)
   text    data     bss     dec     hex filename
   1436       0       0    1436     59c (..)

$ arm-none-eabi-size -Ax (..)
section                  size         addr
.vector_table           0x188    0x8000000
.text                   0x384    0x8000188
.rodata                   0x8    0x800050c
.bss                      0x0   0x20000000
.data                     0x0   0x20000000
.debug_gdb_scripts       0x88    0x8000188 <- NOTE overlaps with .text

$ # after (linked with LLVM lld)
$ arm-none-eabi-size (..)
   text    data     bss     dec     hex filename
   1436       0       0    1436     59c (..)

$ arm-none-eabi-size -Ax (..)
section                  size         addr
.vector_table           0x188    0x8000000
.text                   0x384    0x8000188
.rodata                   0x8    0x800050c
.bss                      0x0   0x20000000
.data                     0x0   0x20000000
.got                      0x0   0x20000000
.debug_gdb_scripts       0x88    0x8000514 <- NOTE comes after .text

This is just an error in the report though. The .debug_gdb_scripts doesn't end up in FLASH even with the linker script changes.

> # GDB console after flashing the binary linked using LLD
> x/12x 0x8000514
0x8000514 <__rustc_debug_gdb_scripts_section__>:        0xffffffff      0xffffffff      0xffffffff      0xffffffff
0x8000524 <__rustc_debug_gdb_scripts_section__+16>:     0xffffffff      0xffffffff      0xffffffff      0xffffffff
0x8000534 <__rustc_debug_gdb_scripts_section__+32>:     0xffffffff      0xffffffff      0xffffffff      0xffffffff

This issue is to gauge interested in adding LLD support right now (by applying the above diff).

Alternatively we can hold off until LLD lands in rustc (hopefully sometime this year) since it's only then when we'll be able to drop the dependency on a external linker (arm-none-eabi-ld).

@japaric
Copy link
Member Author

japaric commented Feb 10, 2018

update: Once PR rust-lang/rust#48125 lands a standalone LLD (version 6.0) binary will ship with the Rust distribution on some (all?) tier 1 platforms. We'll be able to use that lld to link ARM Cortex-M binaries so we should revisit this issue after that PR lands.

@japaric
Copy link
Member Author

japaric commented Mar 9, 2018

Update: LLD is now being shipped with the Rust toolchain. You'll need Xargo v0.3.11 to be able to use it though.

I was testing this again and had to patch things a bit more after #43 landed:

diff --git a/link.x b/link.x
index 6f39655..64bf73b 100644
--- a/link.x
+++ b/link.x
@@ -56,11 +56,11 @@ SECTIONS
   /* its zero sized */
   _sstack = _stack_start < ORIGIN(RAM)? _stack_start : ORIGIN(RAM);
 
-  /* fictitious region that represents the memory available for the stack */
-  .stack _sstack (INFO) : ALIGN(4)
-  {
-    . += (_estack - _sstack);
-  }
+  /* /\* fictitious region that represents the memory available for the stack *\/ */
+  /* .stack _sstack (INFO) : ALIGN(4) */
+  /* { */
+  /*   . += (_estack - _sstack); */
+  /* } */
 
   PROVIDE(_sbss = ORIGIN(RAM));
   .bss _sbss : ALIGN(4)
@@ -84,11 +84,11 @@ SECTIONS
   _sheap = _edata;
   _eheap = _sheap + _heap_size;
 
-  /* fictitious region that represents the memory available for the heap */
-  .heap _sheap (INFO) : ALIGN(4)
-  {
-    . += _heap_size;
-  }
+  /* /\* fictitious region that represents the memory available for the heap *\/ */
+  /* .heap _sheap (INFO) : ALIGN(4) */
+  /* { */
+  /*   . += _heap_size; */
+  /* } */
 
   /* fake output .got section */
   /* Dynamic relocations are unsupported. This section is only used to detect
@@ -114,9 +114,9 @@ SECTIONS
   /* a rustc hack will force the program to read the first byte of this section,
      so we'll set the (fake) start address of this section to something we're
      sure can be read at runtime: the start of the .text section */
-  .debug_gdb_scripts _stext (INFO) : {
+  .debug_gdb_scripts _stext (NOLOAD) : {
     KEEP(*(.debug_gdb_scripts))
-  }
+  } > FLASH
 
   /DISCARD/ :
   {

(This actually breaks cortex-m-rt-ld but it doesn't matter right now as I'm just testing LLD)

It seems to me that LLD is producing bad binaries in some cases: when flashing the allocator example from cortex-m-quickstart v0.2.5 I get a segfault in OpenOCD:

$ # compiled using the LLD in $(rustc --print sysroot)
$ arm-none-eabi-gdb -q target/thumbv7m-none-eabi/debug/app
Reading symbols from target/thumbv7m-none-eabi/debug/app...done.
(gdb) target remote :3333
Remote debugging using :3333
0xfffffffe in ?? ()
(gdb) load
Loading section .vector_table, size 0x400 lma 0x8000000
Loading section .text, size 0x60ea lma 0x8000400
Loading section .rodata, size 0x1804 lma 0x80064f0
Loading section .data, size 0x10 lma 0x8007cee
Remote connection closed
$ openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg
(..)
Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints
Info : accepting 'gdb' connection on tcp/3333
Info : device id = 0x20036410
Info : flash size = 64kbytes
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0xfffffffe msp: 0xfffffffc
Info : Padding image section 0 with 6 bytes
[1]    3342 segmentation fault (core dumped)  openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg

I was wondering if this was a bug in the LLD version shipped with the toolchain so I tried a more recent version and I got this error:

$ ld.lld --version
LLD 7.0.0 (git://github.com/llvm-mirror/lld.git bceb08ce5f0055cee953673a3e098fef868a18d4) (compatible with GNU linkers)

$ xargo build
error: linking with `ld.lld` failed: exit code: 1
  |
  = note: "ld.lld" "-L" (..)
  = note: ld.lld: error: section .rodata load address range overlaps with .data
          >>> .rodata range is [0x80064F0, 0x8007CF3]
          >>> .data range is [0x8007CEE, 0x8007CFD]

which I don't understand because the program does fit in Flash memory -- GNU LD can link it within the memory constraints.

So I went back to inspect the binary linked using rustc's LLD (version 6.0).

$ arm-none-eabi-size -Ax target/thumbv7m-none-eabi/debug/app | grep rodata
.rodata               0x1804    0x80064f0

$ arm-none-eabi-nm -C target/thumbv7m-none-eabi/debug/app | grep sidata
08007cee A _sidata

This shows that indeed .rodata, which spans [0x80064f0, 0x8007cf3], is overlapping with the load memory of .data, which starts at address 0x08007cee. This doesn't occur in the binary linked using GNU LD; there the LMA is equal to the end address of .rodata (no overlap).

So it seems that LLD is not ready for prime time yet. It would be great to report this issue or see if it has already been reported in LLVM's bug tracker. Unfortunately, I won't have time to follow up on that.

@japaric
Copy link
Member Author

japaric commented Apr 6, 2018

Turns out the overlap problem I mentioned above also occurs with GNU LD in some scenarios (I don't know the exact cause). However, #63 fixes the issue for both LD and LLD. Will re-test LLD in a bit.

japaric added a commit that referenced this issue Apr 6, 2018
this commit adds LLD support by removing all INFO sections. LLD kind of supports INFO in the form of
NOLOAD but when the linker script contains NOLOAD sections LLD emits a binary with false `size`
information: for example, it reported a fake increase of 20KB in .text and a fake increase of 1KB in
.bss when compiling a program that only allocates a single Box.

As the INFO sections are gone we can no longer support the stack overflow protection added in #43 so
all the other related changes, like making _stack_start overridable, have been removed as well.
If you want to continue using stack overflow protection you can stick to v0.3.x.

As the .debug_gdb_scripts output section has been removed from the linker script these changes will
only reliably support both LD and LLD if/when rust-lang/rust#49728 lands.

closes #53
@japaric japaric closed this as completed in 401416a Apr 9, 2018
@jcsoo
Copy link
Contributor

jcsoo commented Apr 23, 2018

I'm still seeing the overlap problem when there is static data. I was able to reproduce using cortex-m-quickstart with the following changes:

.cargo/config

[target.thumbv7em-none-eabihf]
runner = 'arm-none-eabi-gdb'
rustflags = [
  "-C", "link-arg=-Tlink.x",
  "-C", "linker=lld",
  "-Z", "linker-flavor=ld.lld",
  "-Z", "thinlto=no",
]

Cargo.toml

[dependencies]
cortex-m = "0.4.0"
cortex-m-semihosting = "0.2.0"
panic-abort = "0.1.1"

[dependencies.cortex-m-rt]
version = "0.4.0"

examples/minimal.rs

#![feature(used)]
#![no_std]

extern crate cortex_m;
extern crate cortex_m_rt;
extern crate cortex_m_semihosting;
extern crate panic_abort;

use cortex_m::asm;

static mut TICK: u32 = 0x1234;

fn main() {
    loop {
        unsafe { TICK = TICK.wrapping_add(1) }
    }
}

// As we are not using interrupts, we just register a dummy catch all handler
#[link_section = ".vector_table.interrupts"]
#[used]
static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240];

extern "C" fn default_handler() {
    asm::bkpt();
}
$ rustc --version
rustc 1.27.0-nightly (ac3c2288f 2018-04-18)

$ arm-none-eabi-size -Ax target/thumbv7em-none-eabihf/debug/examples/minimal | grep rodata
.rodata               0x4    0x80007a0

$ arm-none-eabi-nm -C target/thumbv7em-none-eabihf/debug/examples/minimal | grep sidata
080007a2 A _sidata

So .rodata is [0x800007a0, 0x800007a4] and .data starts at [0x080007a2].

Looking at the symbol table in detail:

$ arm-none-eabi-objdump -t target/thumbv7em-none-eabihf/debug/examples/minimal

target/thumbv7em-none-eabihf/debug/examples/minimal:     file format elf32-littlearm

SYMBOL TABLE:
08000040 l     O .vector_table	000003c0 _ZN7minimal10INTERRUPTS17h7464d2ddc3029b6cE
080004d8 l     F .text	00000006 _ZN7minimal15default_handler17h114b49b69a086ceaE
20000000 l     O .data	00000004 _ZN7minimal4TICK17ha5ad9028c8af9fbfE
080004ae l     F .text	0000002a _ZN7minimal4main17hd2384a11f0fa43e7E
08000004 l     O .vector_table	00000004 _ZN11cortex_m_rt12RESET_VECTOR17he036e15b38aad01bE
08000400 l     F .text	00000062 _ZN11cortex_m_rt13reset_handler17he9975823b9c4d19dE
0800050e l     F .text	0000001e _ZN11cortex_m_rt13reset_handler4main17hb8aca15aff884cf3E
0800052c l     F .text	0000000e _ZN11cortex_m_rt15default_handler17h529c8bd85d78d9daE
08000462 l     F .text	0000002a .hidden _ZN11cortex_m_rt10lang_items5start17h06853a5574522047E
0800048c l     F .text	00000022 .hidden _ZN4core3num21_$LT$impl$u20$u32$GT$12wrapping_add17h04659799d93a58e6E
080004de g     F .text	00000030 main
080005f6 g     F .text	00000012 _ZN4core3mem6zeroed17hb3ed4c80b6965fd6E
0800053a  w    F .text	0000000a BUS_FAULT
0800053a  w    F .text	0000000a DEBUG_MONITOR
0800053a  w    F .text	0000000a DEFAULT_HANDLER
08000008 g     O .vector_table	00000038 EXCEPTIONS
0800053a  w    F .text	0000000a HARD_FAULT
0800053a  w    F .text	0000000a MEM_MANAGE
0800053a  w    F .text	0000000a NMI
0800053a  w    F .text	0000000a PENDSV
0800053a  w    F .text	0000000a SVCALL
0800053a  w    F .text	0000000a SYS_TICK
0800053a  w    F .text	0000000a USAGE_FAULT
080006a0 g     F .text	00000024 _ZN4core3ptr31_$LT$impl$u20$$BP$mut$u20$T$GT$6offset17hcf0d1f6785eda00eE
080006c4 g     F .text	00000004 _ZN4core3ptr4null17hbdffd19ec01e4a11E
080006c8 g     F .text	00000018 _ZN4core3ptr5write17h2066dcf92f7e2ad9E
08000544 g     F .text	0000004a _ZN2r08zero_bss17h7ad2bf41c077011dE
0800058e g     F .text	00000068 _ZN2r09init_data17h861ee0448484f224E
0800076c g     F .text	00000024 _ZN8cortex_m10peripheral3SCB10enable_fpu17h80d211e9d53267a5E
080006e0 g     F .text	0000008c _ZN8cortex_m10peripheral3SCB19set_fpu_access_mode17h5a760d49cb9277eeE
08000682 g     F .text	00000010 _ZN40_$LT$bare_metal..Peripheral$LT$T$GT$$GT$3get17hb0a006b9114bda39E
08000692 g     F .text	0000000e _ZN4core3mem13uninitialized17h748acbeff95d4744E
08000790 g     F .text	0000000e _ZN40_$LT$core..cell..UnsafeCell$LT$T$GT$$GT$3get17ha98494ecb41d2659E
08000608 g     F .text	0000001a _ZN4core3ptr13read_volatile17h67d516ad79d70fe2E
08000622 g     F .text	0000001a _ZN4core3ptr14write_volatile17h2ab28f223f4d509bE
0800063c g     F .text	00000024 _ZN4core3ptr33_$LT$impl$u20$$BP$const$u20$T$GT$6offset17h3f8f4ebea9f8c4d8E
08000660 g     F .text	00000022 _ZN4core3ptr4read17h695db441e027dbf6E
20050000 g       *ABS*	00000000 _stack_start
08000400 g       .vector_table	00000000 _einterrupts
08000400 g       .vector_table	00000000 _stext
20000000 g       *ABS*	00000000 _sbss
20000004 g       .data	00000000 _edata
08000040 g       .vector_table	00000000 _eexceptions
20000004 g       .got	00000000 _sgot
20000004 g       .got	00000000 _egot
20000000 g       .bss	00000000 _ebss
20000000 g       .data	00000000 _sdata
080007a2 g       *ABS*	00000000 _sidata
08000000 g       .vector_table	00000000 _svector_table
20000004 g       .data	00000000 _sheap

The last function in .text is [0x08000790, 0x0800079e] (core::cell::UnsafeCell::get())

08000790 g     F .text	0000000e _ZN40_$LT$core..cell..UnsafeCell$LT$T$GT$$GT$3get17ha98494ecb41d2659E

The first (and only) entry in .data is

20000000 l     O .data	00000004 _ZN7minimal4TICK17ha5ad9028c8af9fbfE

which presumably is being loaded from [0x0800079e, 0x080007a2] if _sidata
is [0x080007a2,...], but this conflicts with .rodata at [0x800007a0, 0x800007a4], which is causing the problems.

So it looks like lld is not respecting the .rodata alignment when writing out the initializer.

@japaric
Copy link
Member Author

japaric commented Apr 23, 2018

This appears to be fixed in the latest LLD (LLD 7.0.0 (git://github.com/llvm-mirror/lld.git 31f297161e44ba84e5e257a9213e32dc73b540e3) (compatible with GNU linkers)). I get:

$ arm-none-eabi-size -Ax target/thumbv7em-none-eabihf/debug/app | grep rodata
.rodata               0x4    0x80007a0

$ arm-none-eabi-nm -C target/thumbv7em-none-eabihf/debug/app | grep sidata
080007a4 A _sidata

(with rustc LLD I get 080007a2 A _sidata)

We probably only have to backport the fix.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants