When cross-compiling a #![no_std] crate for i686-pc-windows-gnu it is linked with the following flags the following flags: "-lgcc_eh" "-l:libpthread.a" "-lmsvcrt" "-lmingwex" "-lmingw32" "-lgcc" "-lmsvcrt" "-lmingwex" "-luser32" "-lkernel32".
And I have the mingw toolchain installed for other projects. (No idea when I installed or what apt packages, but I have mingw-w64 version 9.0.0-1ppa3, FWIW.)
Ok, I did a little digging. The issue is unrelated to the linker flags. Those are always passed even for the 64-bit target.
The linker needs leading underscores for some symbols. This patch will make it compile for i686-pc-windows-gnu:
diff --git a/stardust/scripts/windows.ld b/stardust/scripts/windows.ld
index 595ac24..d8ee602 100644
--- a/stardust/scripts/windows.ld
+++ b/stardust/scripts/windows.ld
@@ -43,7 +43,7 @@ SECTIONS
*/
. = ALIGN(0x1000);
- _data_offset = .;
+ __data_offset = .;
*(.global*)
*(.data*)
*(.bss*)
diff --git a/stardust/src/stcore/arch/i686/i686.asm b/stardust/src/stcore/arch/i686/i686.asm
index 8264e12..34df2a6 100644
--- a/stardust/src/stcore/arch/i686/i686.asm
+++ b/stardust/src/stcore/arch/i686/i686.asm
@@ -24,7 +24,7 @@
mov esi, esp // store current esp in esi
and esp, 0xFFFFFFF0 // align esp to 16 bytes
sub esp, 0x10 // allocate stack space for alignment
- call stmain // call the main function
+ call _stmain // call the main function
mov esp, esi // restore esp
pop esi // restore esi
ret
You may need to separate the linker script for 32-bit and 64-bit Windows builds. Because this patch as-is breaks x86_64 (__data_offset will not be found). This is a requirement for the __cdecl calling convention. Or, maybe find a way to change the calling convention, if that is compatible with what you are trying to do...
You're a wizard! Thank you! Successfully compiles, although still crashes so time time fire up xdbg XD. Annoyingly this naming convention only holds for x32 so you're right on the requirement for i686-specific linker script. What I'm trying to do? Mainly be stubborn as a donkey and prove to a colleague that it can be done while learning a bunch in the process.
For completeness, llvm has a flag to opt-out: --no-leading-underscore. (This does change the calling convention, making it incompatible with other libraries.)
I think it can be passed through the environment (haven't tried this myself):
It appears I may have been too hasty here, I'm seeing a bunch of libraries being included in the map which I've never seen linked in previous targets. This is leading to significantly larger binary.
This includes a bunch of addition unwinding code in the .text section and references to strlen, malloc etc. from libmsvcrt. I've been trying to avoid these by using compiler-builtins. An import table is also being generated.
At the moment the program is crashing as it is generating a .rdata$.refptr.__data_offset which holds at hard coded memory address leading to an exception. Unfortuantly the initial question still stands.
Hmm, it's using identical instructions to x86_64 but I tried the link optimization stuff. Amusingly the -Zlocation-detail=none results in a SEGV during compilation. Comparing the output linker maps I think this is the main difference:
You may have to use -Z build-std to disable unwinding within the standard library. (min-sized-rust also covers this.) That is probably why you are seeing those references.
Another awesome tool to help out with this kind of analysis is cargo-show-asm.
No luck with -Z build-std, still included. The crate doesn't use the standard library anyhow so I don't know how much that flag would affect things. cargo-show-asm looks cool, I've just been using radare2 as that's what I've become comfortable with.
Note that the windows-gnu tries to emulate a unix-ish target in some ways so it includes a lot of stuff to make that work. You might have more luck with windows-gnullvm which is closer to a native Windows target.
Using -Z build-std will implicitly compile the stable crates core , std , alloc , and proc_macro .
I don't know if it will help, but if you are using any of those libraries, you might want to build with -Z build-std=core,alloc,panic_abort -Z build-std-features=panic_immediate_abort.
Including these arguments still results in libgcc_eh.a and others being linked. It also gives an error regarding compiler_builtins being included twice but I worked around this by removing the dependency.
My current hypothesis is that panic=abort is being ignored leading to rsbegin.o (__register_frame_info) being linked. This then has a dependency on libgcc_eh.a(unwind-dw2-fde.o) (for unwinding).
libgcc_eh.a(unwind-dw2-fde.o) then has dependencies on libmsvcrt.a for malloc, free, abort, and so on and so forth.
Even when building with the following flags, this is still the case.