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?
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
export RUSTFLAGS="-C linker=lld -C link-arg=-Tlink.x"
"-C", "linker=lld"
as a rust flag in the .cargo/config
file with the other optionsarm-none-eabi-ld
) which is more easily retrievable by uncommenting the following line in .cargo/config
: "-C", "linker=arm-none-eabi-gcc"