rust-embedded / cortex-m-quickstart

Template to develop bare metal applications for Cortex-M microcontrollers
782 stars 164 forks source link

Add --nmagic linker arg, for unaligned flash origin support. #95

Closed Dirbaio closed 3 years ago

Dirbaio commented 3 years ago

Without this, the linker places some extra program header entries that can confuse flashing tools.

Many thanks to @jonas-schievink for pointing me to this flag in Matrix.

How to reproduce the bug

Program header entries will look something like this:

Program Header:
    PHDR off    0x00000034 vaddr 0x00020034 paddr 0x00020034 align 2**2
         filesz 0x00000100 memsz 0x00000100 flags r--
    LOAD off    0x00000000 vaddr 0x00020000 paddr 0x00020000 align 2**16
         filesz 0x00000134 memsz 0x00000134 flags r--
    LOAD off    0x00007000 vaddr 0x00027000 paddr 0x00027000 align 2**16
         filesz 0x00000400 memsz 0x00000400 flags r--
    LOAD off    0x00007400 vaddr 0x00027400 paddr 0x00027400 align 2**16
         filesz 0x00000570 memsz 0x00000570 flags r-x
    LOAD off    0x00007970 vaddr 0x00027970 paddr 0x00027970 align 2**16
         filesz 0x00000180 memsz 0x00000180 flags r--
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**64
         filesz 0x00000000 memsz 0x00000000 flags rw-

The entry at 0x00020000 causes flashing tools (probe-rs at least) to flash data at 0x00020000, corrupting whatever's there. This happens whenever flash ORIGIN is not a multiple of 0x10000.

With --nmagic, program headers are correct and flashing does what it's supposed to do:

Program Header:
    LOAD off    0x000000f4 vaddr 0x00027000 paddr 0x00027000 align 2**2
         filesz 0x00000400 memsz 0x00000400 flags r--
    LOAD off    0x000004f4 vaddr 0x00027400 paddr 0x00027400 align 2**2
         filesz 0x00000570 memsz 0x00000570 flags r-x
    LOAD off    0x00000a64 vaddr 0x00027970 paddr 0x00027970 align 2**2
         filesz 0x00000180 memsz 0x00000180 flags r--
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**64
         filesz 0x00000000 memsz 0x00000000 flags rw-
rust-highfive commented 3 years ago

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @jonas-schievink (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see the contribution instructions for more information.

adamgreig commented 3 years ago

I'm trying to replicate this on any of my projects without success, any idea if there are more conditions than reported here to trigger the bug?

jonas-schievink commented 3 years ago

We were seeing this only with GNU LD, but @Dirbaio also saw it with LLD. Maybe the linker or linker version matters?

adamgreig commented 3 years ago

If I follow the instructions in the first post, I still don't reproduce this issue; the ELF looks fine in both debug and release modes, and I get the same thing in other projects too. I'm using rustc 1.46.0, but I'm sure I've generated non-aligned images for ages without issue.

@Dirbaio, could you double check your env doesn't have any stale settings like RUST_FLAGS or something, just in case? Alternatively could you possibly zip up a minimal crate which demonstrates this problem?

Dirbaio commented 3 years ago

I'm using rust nightly from rustup, and lld from Arch Linux repos. (not sure if rust uses system's lld or bundles its own though)

[dirbaio@mars nrfconnect]$ rustc --version
rustc 1.48.0-nightly (d006f5734 2020-08-28)
[dirbaio@mars nrfconnect]$ ld.lld --version
LLD 10.0.1 (compatible with GNU linkers)
[dirbaio@mars nrfconnect]$ pacman -Qo $(which ld.lld)
/usr/bin/ld.lld is owned by lld 10.0.1-1

Nothing about rust in env.

Repro is literally cargo generate and changing memory.x. Attached tarball with everything in target as is. -> repro.tar.gz

Just cargo build and llvm-objdump -x target/thumbv7m-none-eabi/debug/repro

adamgreig commented 3 years ago

Aha, I can duplicate this using nightly but not on stable. Looks like this is an LLVM regression in nightly, then? Rust uses its own copy of LLD by default, it doesn't require one on the system.

Dirbaio commented 3 years ago

I did some more investigation.

  1. ld.lld aligns to the "page size" by design, and --nmagic is the intended way to disable that:
[dirbaio@mars repro]$ ld.lld --help | grep magic
  --nmagic                Do not page align sections, link against static libraries.
  --no-nmagic             Page align sections (default)
  --no-omagic             Do not set the text data sections to be writable, page align sections (default)
  -N                      Alias for --omagic
  -n                      Alias for --nmagic
  --omagic                Set the text and data sections to be readable and writable, do not page align sections, link against static libraries
  1. It works on stable because stable is using a page size of 0x1000 by default, and nightly is using 0x10000.
STABLE

rustc 1.46.0 (04488afe3 2020-08-24):
    LOAD off    0x00001000 vaddr 0x00027000 paddr 0x00027000 align 2**12
         filesz 0x00000acc memsz 0x00000acc flags r-x
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**64
         filesz 0x00000000 memsz 0x00000000 flags rw-

rustc 1.46.0 (04488afe3 2020-08-24) with --nmagic:
    LOAD off    0x000000b4 vaddr 0x00027000 paddr 0x00027000 align 2**2
         filesz 0x00000a7c memsz 0x00000a7c flags r-x
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**64
         filesz 0x00000000 memsz 0x00000000 flags rw-

rustc 1.46.0 (04488afe3 2020-08-24) with -zmax-page-size=0x1000
    LOAD off    0x00001000 vaddr 0x00027000 paddr 0x00027000 align 2**12
         filesz 0x00000acc memsz 0x00000acc flags r-x
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**64
         filesz 0x00000000 memsz 0x00000000 flags rw-

rustc 1.46.0 (04488afe3 2020-08-24) with -zmax-page-size=0x10000 ===> BROKEN!!
    PHDR off    0x00000034 vaddr 0x00020034 paddr 0x00020034 align 2**2
         filesz 0x000000c0 memsz 0x000000c0 flags r--
    LOAD off    0x00000000 vaddr 0x00020000 paddr 0x00020000 align 2**16
         filesz 0x000000f4 memsz 0x000000f4 flags r-x
    LOAD off    0x00007000 vaddr 0x00027000 paddr 0x00027000 align 2**16
         filesz 0x00000acc memsz 0x00000acc flags r-x
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**64
         filesz 0x00000000 memsz 0x00000000 flags rw-

NIGHTLY

rustc 1.48.0-nightly (d006f5734 2020-08-28) ===> BROKEN!!
    PHDR off    0x00000034 vaddr 0x00020034 paddr 0x00020034 align 2**2
         filesz 0x00000100 memsz 0x00000100 flags r--
    LOAD off    0x00000000 vaddr 0x00020000 paddr 0x00020000 align 2**16
         filesz 0x00000134 memsz 0x00000134 flags r--
    LOAD off    0x00007000 vaddr 0x00027000 paddr 0x00027000 align 2**16
         filesz 0x00000400 memsz 0x00000400 flags r--
    LOAD off    0x00007400 vaddr 0x00027400 paddr 0x00027400 align 2**16
         filesz 0x00000550 memsz 0x00000550 flags r-x
    LOAD off    0x00007950 vaddr 0x00027950 paddr 0x00027950 align 2**16
         filesz 0x00000180 memsz 0x00000180 flags r--
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**64
         filesz 0x00000000 memsz 0x00000000 flags rw-

rustc 1.48.0-nightly (d006f5734 2020-08-28) with --nmagic
    LOAD off    0x000000f4 vaddr 0x00027000 paddr 0x00027000 align 2**2
         filesz 0x00000400 memsz 0x00000400 flags r--
    LOAD off    0x000004f4 vaddr 0x00027400 paddr 0x00027400 align 2**2
         filesz 0x00000550 memsz 0x00000550 flags r-x
    LOAD off    0x00000a44 vaddr 0x00027950 paddr 0x00027950 align 2**2
         filesz 0x00000180 memsz 0x00000180 flags r--
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**64
         filesz 0x00000000 memsz 0x00000000 flags rw-

rustc 1.48.0-nightly (d006f5734 2020-08-28) with -zmax-page-size=0x1000
    LOAD off    0x00001000 vaddr 0x00027000 paddr 0x00027000 align 2**12
         filesz 0x00000400 memsz 0x00000400 flags r--
    LOAD off    0x00001400 vaddr 0x00027400 paddr 0x00027400 align 2**12
         filesz 0x00000550 memsz 0x00000550 flags r-x
    LOAD off    0x00001950 vaddr 0x00027950 paddr 0x00027950 align 2**12
         filesz 0x00000180 memsz 0x00000180 flags r--
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**64
         filesz 0x00000000 memsz 0x00000000 flags rw-

rustc 1.48.0-nightly (d006f5734 2020-08-28) with -zmax-page-size=0x10000 ===> BROKEN!!
    PHDR off    0x00000034 vaddr 0x00020034 paddr 0x00020034 align 2**2
         filesz 0x00000100 memsz 0x00000100 flags r--
    LOAD off    0x00000000 vaddr 0x00020000 paddr 0x00020000 align 2**16
         filesz 0x00000134 memsz 0x00000134 flags r--
    LOAD off    0x00007000 vaddr 0x00027000 paddr 0x00027000 align 2**16
         filesz 0x00000400 memsz 0x00000400 flags r--
    LOAD off    0x00007400 vaddr 0x00027400 paddr 0x00027400 align 2**16
         filesz 0x00000550 memsz 0x00000550 flags r-x
    LOAD off    0x00007950 vaddr 0x00027950 paddr 0x00027950 align 2**16
         filesz 0x00000180 memsz 0x00000180 flags r--
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**64
         filesz 0x00000000 memsz 0x00000000 flags rw-

The exact commit that changed this is https://github.com/llvm/llvm-project/commit/87383e408d41623ada41e2bbc371b037fa29e894 . Apparently it's for compat with GCC linker and with systems with bigger pages. It's not a nightly regression.

IMO the right fix is --nmagic.

The alternative is adding -zmax-page-size=0x1000 to restore the old smaller alignment, but someone might need even lower alignment. There are chips out there with flash pages smaller than 0x1000. eg nrf51 is 0x400.

bors[bot] commented 3 years ago

Configuration problem: bors.toml: not found

therealprof commented 3 years ago

Yeah, no bors in here. Let's just merge it then, shall we? ;)

hydra commented 2 years ago

If one switches to using the gnu linker (ld) instead of llvm because of this bug: https://github.com/rust-lang/rust/issues/88704 then you get this:

 = note: arm-none-eabi-gcc.exe: error: unrecognized command line option '--nmagic'

linker switched using this .cargo/config.toml snippet.

[target.thumbv7m-none-eabi]
rustflags = [
  "-C", "linker=arm-none-eabi-gcc",
 ...

Is there a solution that also works for gnu ld ?

newAM commented 2 years ago

Try replacing arm-none-eabi-gcc with arm-none-eabi-ld:

[target.thumbv7m-none-eabi]
rustflags = [
  "-C", "linker=arm-none-eabi-ld",

--nmagic is a flag in the GNU linker[1], so it should work.

hydra commented 2 years ago

Try replacing arm-none-eabi-gcc with arm-none-eabi-ld:

doh! yes, overlooked that... I'll give it a whirl and report back as time permits.

hydra commented 2 years ago

I have to use 'gcc' because other arguments need to be passed to it, for map file and for keeping generated assembly files.

[target.thumbv7m-none-eabi]
rustflags = [
  "-C", "linker=arm-none-eabi-gcc",
  "-C", "link-arg=-nostartfiles",
  "-C", "link-arg=-Wl,--print-memory-usage",
  "-C", "link-arg=-Wl,-Map=out.map",
  "-C", "link-arg=-Wl,--verbose",
  "-C", "save-temps",
  "--emit", "asm",
]

a solution that works is to use a build.rs file:

    // for GNU LD
    println!("cargo:rustc-link-arg-bins=-Wl,--nmagic");
    // for LLVM
    //println!("cargo:rustc-link-arg-bins=--nmagic");

but you have to know which linker you're using to uncomment the right line. Some automatic way of doing it would be ideal.