Rust embedded application is not linked correctly under AArch64 system

0

I'm trying to compile and debug an embedded rust application for stm32f0 using an ARM system as host. The application already compiles and works under an Intel installation.

I am running on a Pinebook Pro, powered by a Quad Cortex-A53, 64-bit CPU. The OS is a 64-bit version of Debian:

$ uname -a
Linux pinebook 4.4.196 #1 SMP Tue Oct 15 16:54:21 EDT 2019 aarch64 GNU/Linux

I installed rust and cargo with rustup for AArch64 (channel stable):

$ rustc --version
rustc 1.39.0 (4560ea788 2019-11-04)
$ cargo --version
cargo 1.39.0 (1c6ec66d5 2019-09-30)

As per this issue I found out that rust-lld is not distributed in binary form for ARM systems, so I had to compile it from sources:

$ ld.lld --version
LLD 10.0.0 (https://github.com/llvm/llvm-project.git 1c247dd028b368875bc36cd2a9ccc7fd90507776) (compatible with GNU linkers)

Now the compilation process completes without issues:

export RUSTFLAGS="-C linker=ld.lld"
cargo build

However the resulting elf file seems to be linked incorrectly: trying to load it with gdb through openocd results in some kind of silent failure:

(gdb) target remote :3333
Remote debugging using :3333
0x00000000 in ?? ()
(gdb) load
Start address 0x0, load size 0
Transfer rate: 0 bits in <1 sec.
(gdb) 

The load size is empty, so no new program is flashed. In contrast, when using the elf compiled in my Intel system (with openocd still running on the arm laptop) everything works as expected:

(gdb) target remote 192.168.1.153:3333
Remote debugging using 192.168.1.153:3333
0x00000000 in ?? ()
(gdb) load
Loading section .vector_table, size 0xc0 lma 0x8000000
Loading section .text, size 0x686e lma 0x80000c0
Loading section .rodata, size 0x4a0 lma 0x8006940
Start address 0x8005b58, load size 28110
Transfer rate: 19 KB/sec, 7027 bytes/write.
(gdb)

It would seems like the elf is not linked correctly. Running readelf -l highlights that on my ARM system the entry point set is 0x0, which is wrong for the stm32f0. This is readelf on my ARM laptop:

lf file type is EXEC (Executable file)
Entry point 0x0
There are 3 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x00010034 0x00010034 0x00060 0x00060 R   0x4
  LOAD           0x000000 0x00010000 0x00010000 0x00094 0x00094 R   0x1000
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0

 Section to Segment mapping:
  Segment Sections...
   00     
   01     
   02 

While this is from the elf that works, compiled under my Intel system:

Elf file type is EXEC (Executable file)
Entry point 0x8005b59
There are 3 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x001000 0x08000000 0x08000000 0x06de0 0x06de0 R E 0x1000
  LOAD           0x007de0 0x20000000 0x08006de0 0x00000 0x00028 RW  0x1000
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0

 Section to Segment mapping:
  Segment Sections...
   00     .vector_table .text .rodata 
   01     .data .bss 
   02     

I'm not sure if this has something to do with the target architecture being the same as the host (thus using the linux userland linking) or simple because arm systems are less supported.

Can anyone point me in the right direction?

rust
arm
embedded
rust-cargo
stm32f0
asked on Stack Overflow Nov 23, 2019 by Maldus • edited Feb 8, 2020 by Maldus

2 Answers

0

I was correct in assuming there was a problem with the linker, and there are a couple of solutions.

Since two years ago Rust uses LLD as the default linker for the ARM architecture (https://rust-embedded.github.io/blog/2018-08-2x-psa-cortex-m-breakage/). Unfortunately rust-lld itself is not distributed in binary form for the ARM platforms (ironic, isn't it?), so I had to compile it from source and specify it via command line.

Exporting the RUSTFLAGS variable works but overwrites its default value defined in .cargo/config, which would include also the directive for the linker script (-C link-arg=-Tlink.x). In short I was convinced of using the correct linker script because it was listed in .cargo/config, but the RUSTFLAGS env variable was removing it.

The solution is to either

  • include the linker script explicitly when exporting RUSTFLAGS: export RUSTFLAGS="-C linker=ldd -C link-arg=-Tlink.x"
  • specify "-C", "linker=lld" as a rust flag in the .cargo/config file with the other options
  • Enable the old linker (arm-none-eabi-ld) which is more easily retrievable by uncommenting the following line in .cargo/config: "-C", "linker=arm-none-eabi-gcc"
answered on Stack Overflow Feb 15, 2020 by Maldus
-1

You have to create raw binary image by using a linker srcipt dedicated to that board.

https://github.com/szczys/stm32f0-discovery-basic-template/tree/master/Device/ldscripts

answered on Stack Overflow Nov 23, 2019 by Gábor

User contributions licensed under CC BY-SA 3.0