-
Notifications
You must be signed in to change notification settings - Fork 76
Conversation
Hmm I should mark this as WIP. This gets us most of the way, but I am still stuck on this bizarre error from lld when linking a simple program:
I think the sections it's complaining about are actually 0 bytes. They are certainly not 1589641216 bytes. It seems like an arithmetic error somewhere. Maybe a bug in the riscv port? 1589641216 bytes is exactly 1516 MB which seems like an unusual number. It might be a clue as to what is going on. |
Another possibility is that I made a mistake in rebasing the lld patches. I had to rebase them onto an earlier commit of the 7.0 branch, to match the llvm commit that is in the rust tree. I could have messed something up in doing that. It would be worthwhile trying to use an lld built outside of Rust I guess. The other thing I can do is go back over the link.x script in cortex-m-rt with a fine-toothed comb and see if I can spot anything else that I have done wrong here. I will dig into it more over the weekend. If anyone else has any hints I am all ears :-) |
You shouldn't have a .got in there. That's for a dynamic executable |
Yeah that is one of the things I don't understand yet, there shouldn't be a |
Oookay so I spent some time messing around inside gdb, I think it is a bug in lld... specifically the nonsensical error message occurs if you start a new section with a specific start address in a different region than the section before it. The bug is that it tries to expand the previous output section to meet the new address. So one problematic bit is when we have:
Even though .bss goes into the RAM region and not the FLASH region, lld was expanding the FLASH region from 0x20400000 to the start of RAM at 0x80000000, because we told it to start the section at the specific address of the It seems like the easiest workaround is to avoid starting new sections from a fixed address, and let lld lay them out as it sees fit. In this case it will get the sizes right. So instead of the above, we would do:
That is what cortex-m-rt has. The only downside is we lose the ability for the user to pass in their own separate linker script which overrides the start of the |
Okay, I think I got it. :-) The amended series now produces working binaries with lld and I don't think I have messed anything up. I have also learnt more than I ever wanted to know about linker scripts :-P |
Ah, sigh... I did mess it up. It no longer links with GNU ld.
That address does not make much sense to me, not sure where it is getting that from... Oh well. I will have another crack at it tomorrow. |
Did you see my comment on removing the cfi stuff?
|
Oh sorry I totally you missed that comment. I didn't realise the CFI made a difference to gdb. I'm not sure how that's possible, if the I can drop that commit and instead we can leave the .cfi_* directives in there and just /DISCARD/ the .eh_frame in the linker script. Or we could even just leave it in the binary, it seems harmless and maybe we will have working panic unwinding at some point. |
|
Maybe it also fixes all the gdb crashes I was having... xD |
This is needed for lld, otherwise it will complain about section flag mismatch: ld.lld: error: incompatible section flags for .text >>> target/riscv32imac-unknown-none/debug/deps/libriscv_rt-7850ee1a6233fbe9.rlib(riscv_rt-7850ee1a6233fbe9.4tmuw4s4crjeqbm5.rcgu.o):(.trap): 0x4 >>> output section .text: 0x6
This is no longer necessary now that we can configure the target to exclude the .debug_gdb_scripts section. See: rust-lang/rust#53139
This aligns the VMA (virtual adddress at runtime): .rodata ALIGN(4) : { ... whereas this aligns the LMA (load address): .rodata : ALIGN(4) { ... If we ensure the VMA is aligned the linker will keep the corresponding LMA in sync (and it will be aligned too). Previously, by forcing the LMA to be aligned but leaving the VMA unspecified, the linker would split .text and .rodata into two separate loads because their addresses fell out of sync.
Hmm yeah I don't seem to get any usable backtraces at all, regardless whether it's built with GNU ld or lld, or with or without the .cfi_* directives, with .eh_frame discarded or included in the binary... so now I'm not sure what I'm doing wrong. 🙄 |
This came from changing (INFO) to (NOLOAD). There is a subtle difference: (INFO) not only marks the section as non-loadable, it also marks it as non-allocatable. Otherwise the program header will try to load into address 0x80000000 with size 0x80004004 (that itself is surely a bug, the real size of the stack is 0x3ffc not 0x80004000). Hence the complaint about address 0x100004004 being out of range. It turns out lld supposedly gained support for the (INFO) attribute back in February: https://bugs.llvm.org/show_bug.cgi?id=36298
So I guess that is another lld bug... |
So lld doesn't understand this:
but it understands this:
It should be redundant anyway, specifying the start address of each section to be the end address of the previous one. That will happen naturally anyway as the linker lays out each section one after the other. So I think we can just leave all the start addresses unspecified. |
The .cfi_* assembler directives cause call frame information to be emitted in a special .eh_frame section. But this is not needed in the final binary, because we are not doing panic unwinding. GNU ld already discards this section with its (default) --gc-sections behaviour. Lld doesn't do that by default though, instead it complains: rust-lld: error: no memory region specified for section '.eh_frame' So let's explicitly discard the .eh_frame section.
To work around a bug in lld, we need to avoid starting the .bss section from a fixed address (previously, the _sbss symbol). Otherwise lld incorrectly tries to extend the FLASH output region up to the start of the RAM output region, which is too large. To work around another bug in lld, we need to avoid starting sections marked with (INFO) from a specific starting address. Its parser does not accept both a start address and the (INFO) attribute. Specifying these start addresses is redundant anyway, the linker will lay them out in sequential order for us.
Okay! This is finally ready to go now I think. :-) I just pushed an amended series, changes are:
With these patches, both GNU ld and lld can successfully link and produce working binaries. For comparison, this is what you get from GNU ld before these patches (current master):
This is what you get with GNU ld with these patches applied (note the layout is the same, nothing has regressed as far as I can tell):
And this is what you get using built-in rust-lld (7.0.0 + riscv patches):
The lld version is still not perfect. You can see it treats the .stack section as allocatable in the output, and so there is an unnecessary load of size 0x00003ffc for populating the stack in the program header. It seems to be (yet another) bug in lld's implementation of (INFO). But the main thing is the linker produces a binary, and the binary actually works! So I think that is good enough to merge for now. I have requested an account on the LLVM Bugzilla so that I can file all these bugs with them. |
I'm not even sure what the |
I guess we can remove them. The idea was to be able to see the stack/heap size easily with readelf. Are you planning on submitting the lld patch to rust-lld? Or would you prefer me to do it? I'll need some time to read through all of this again. Takes some time for me to process since I'm not a linker script expert... |
Yeah take your time :-) I certainly had to learn a lot about linker stuff in the process of writing this PR! We might as well keep the I will post a PR against rust-lld for the RISCV patch too. |
r+ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
r+
@bors r+ |
@homu r+ |
If someone could tell me how to accept this pr that would be nice xD |
Ha... I might be wrong but I think we are using bors-ng here... try |
bors r+ |
10: support linking with lld r=dvc94ch a=danc86 Rust now ships an embedded copy of lld, so if we can link crates with lld it will make the getting started experience way smoother (don't need to find a separate GNU ld). Co-authored-by: Dan Callaghan <[email protected]>
Build succeeded |
Rust now ships an embedded copy of lld, so if we can link crates with lld it will make the getting started experience way smoother (don't need to find a separate GNU ld).