Thread overview
Building for ARM 32-bit embedded
4 days ago
Bonsaipanda
4 days ago
kinke
4 days ago
Bonsaipanda
4 days ago
Bonsaipanda
1 day ago
f
4 days ago

Hey guys, thought I'd throw some notes from testing out D for the first time. I'm a complete n00b when it comes to cross-platform buildchains and barely understand what crosslinking means and I thought I'd just create a small hello world test app and try to build it for amrhf to see if D would work for an app idea I have had for quite a while.

Now, for comparison, same hello world test app (when running Ubuntu 24.04) build using C is >>

arm-linux-gnueabihf-gcc test.c test_armhf

.. and out pops a 32-bit ARM executable.

So, in my head, I thought that there would be some kind of shorthand like this >>

ldc2 -armhf test.d

.. and that would give me a standalone executable, ready to be deployed on the ARM box and run.

The documentation says that you have to jump a bit through hoops to get LLVM working if you're not building on the platform that you're targeting, but the section in the documentation was almost incomprehensible to me - not that I'm stupid, I'm just not familiar with all that's required when linking stuff together, never done anything like that, never heard of any of these technologies before.

Apparently, if I want a static build (the target platform will not have anything available, optimal end-result is a standalone executable), I would need a pre-built armhf binary of the D runtime and Phobos, but I didn't understand why would I need those. When working with Vala, this was the thing that put me off - no matter how I would build the executable, on the embedded platform it would always nag about not finding all it's libraries and I couldn't figure out a way to make stuff static (there is a way, folks use it for embedded dev).

So I hopped on over to Raspberry Pi and made the test build there. Out pops a test.o which I have no idea what to do with. I know it's a 'shared object' but why would I want something like this. Testing out different build options I managed to get an executable out. Which then nags that it's in wrong format, it doesn't understand what to do, it wants to run for president etc., head scratching x 9000.

Diving into this forum, I found out that there's a problem with the linker - it connects stuff in the wrong order.

So the final build command, after ~5 hours of headscrathing (and trying ChatGPT to help out on understanding everything) is in the form of >>

ldc2 -mtriple=armv6-linux-gnueabihf -c test.d && arm-linux-gnueabihf-gcc test.o /usr/lib/arm-linux-gnueabihf/libphobos2-ldc.a /usr/lib/arm-linux-gnueabihf/libdruntime-ldc.a -lm -lz -o test_armhf

Yipe.

I mean, when you break it down, it's completely logical, but holy skagsack minion, that's verbose and I have a feeling I'm doing this completely wrong. Also, doing this on the Raspberry Pi is super cumbersome and slow.

And I don't mean this post to be any sort of rant, I'm just surprised on how the whole toolchain was so tricky to understand and set up. Was really hoping for the shorthand of sorts that would 'just work' on Ubuntu (or Windows), since LLVM itself is pretty agnostic about the platforms, or so I've understood.

The D language itself seems to fit perfectly for the stuff I had in mind, so I'm excited to dive deeper into it, but I have no idea if I'm able to build more complex code (like drawing directly into the framebuffer, some DSP stuff for ALSA).

4 days ago

Hey! Seems like you (and ChatGPT) unfortunately haven't found https://wiki.dlang.org/Cross-compiling_with_LDC, it would hopefully have made the journey a bit easier. But I'm sure you learned a lot along the way! :)

Just to show that cross-compiling and -linking can work without too much effort too, with a vanilla LDC config and an already available C cross-toolchain (admittedly cheating via -betterC, which doesn't require druntime+Phobos library builds - but we have an ldc-build-runtime helper to make that easier, see the linked Wiki page), also on Ubuntu 24 (but targeting AArch64, as I had that cross-toolchain on disk already):

$ cat hello.d
import core.stdc.stdio;

extern(C) void main() {
    puts("Hello world");
}
$ ldc2 -mtriple=aarch64-linux-gnu -gcc=aarch64-linux-gnu-gcc -betterC hello.d
$ file hello
hello: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=d78ca80f36c54f0b83565d7adcf29b6161567a2f, for GNU/Linux 3.7.0, not stripped
>

The documentation says that you have to jump a bit through hoops to get LLVM working if you're not building on the platform that you're targeting

Which documentation are you referring to?

4 days ago

On Friday, 11 October 2024 at 22:31:21 UTC, kinke wrote:

>

Which documentation are you referring to?
https://wiki.dlang.org/Cross-compiling_with_LDC

That exact page, section "Cross-linking executables and shared libraries". Coming from completely outside D language (and never having done any cross-compiling), that whole section is very confusing and took a while to digest. I was just so used to everything being super straightforward with C. Was scratching my head as to why it was done how it was done with D. I would probably use the kind of shorthand I was talking about as the default and if the user needs to tweak their chain, then point to a conf.

CG was pretty good at explaining what is happening under the hood and what is expected from me when setting up the whole toolchain, which is why I fired up my Raspberry Pi just to get a build out of the oven, seemed much more straightforward.

And yes, it's not a lot of effort, once it's up and running. But you really really need to have a hyperdeep understanding of buildsystems and toolchains to get it running. For a beginner like me, it was an interesting experience.

4 days ago

As a quick example, this is the output when building for armhf from Ubuntu >>

$ ldc2 -mtriple=armv6-linux-gnueabihf -gcc=arm-linux-gnueabihf-gcc -betterC test.d
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/stdio.d(4362): Error: none of the overloads of template `std.stdio.File.LockingTextWriter.put` are callable using argument types `!()(string)`
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/stdio.d(3287):        Candidates are: `put(A)(scope A writeme)`
  with `A = string`
  must satisfy one of the following constraints:
`       isSomeChar!(ElementType!A)
       is(ElementType!A : const(ubyte))`
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/stdio.d(3317):                        `put(C)(scope C c)`
  with `C = string`
  must satisfy one of the following constraints:
`       isSomeChar!C
       is(C : const(ubyte))`
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/stdio.d(3338): Error: none of the overloads of template `std.utf.decodeFront` are callable using argument types `!()(char[])`
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/utf.d(1245):        Candidates are: `decodeFront(Flag useReplacementDchar = No.useReplacementDchar, S)(ref S str, out size_t numCodeUnits)`
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/utf.d(1285):                        `decodeFront(Flag useReplacementDchar = No.useReplacementDchar, S)(ref scope S str, out size_t numCodeUnits)`
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/utf.d(1314):                        `decodeFront(Flag useReplacementDchar = No.useReplacementDchar, S)(ref S str)`
  with `useReplacementDchar = Flag.no,
       S = char[]`
  must satisfy the following constraint:
`       isInputRange!S`
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/utf.d(2311): Error: cannot use `throw` statements with -betterC
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/utf.d(2514): Error: template instance `std.utf._utfException!Flag.no` error instantiating
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/stdio.d(3340):        instantiated from here: `encode!Flag.no`
/usr/lib/ldc/x86_64-linux-gnu/include/d/std/stdio.d(4364):        instantiated from here: `put!char`
test.d(3):        instantiated from here: `writeln!string`

If I didn't have CG to ask from, I wouldn't have had any idea what was happening and why are x86-64 components being called. I would even argue that it's LDC's job to make sure stuff, that the program is dependent on, gets built at the same time for the target. And I still don't understand why (core components?) like D runtime and Phobos are external libraries. But, such is life.

1 day ago

On Friday, 11 October 2024 at 21:02:04 UTC, Bonsaipanda wrote:

>

Hey guys, thought I'd throw some notes from testing out D for the first time. I'm a complete n00b when it comes to cross-platform buildchains and barely understand what crosslinking means and I thought I'd just create a small hello world test app and try to build it for amrhf to see if D would work for an app idea I have had for quite a while.

[...]

maybe this can help, an alternative,

https://github.com/trikko/ldc2-rpi-build/releases/tag/v1.31.0

modify as you need, compile the ldc using buildkit, compile your project using docker.

i am a n00b too. just may be..