digitalmars.D - Porting gdc/dmd to Hurd
- yelninei (48/48) Mar 19 Hi everyone,
- Dejan Lekic (6/9) Mar 19 That is great! I hope we can help you in your endeavour.
- HNBas (35/83) Mar 20 The crash you’re seeing on 32-bit smells very much like a calling
- Walter Bright (11/11) Mar 20 The struct call/return ABI is always badly/erroneously documented, and e...
- yelninei (16/51) Mar 21 Thanks a lot for the responses.
- Walter Bright (4/6) Mar 20 Pretty much everyone who has tried to wrangle it has given up on it, whi...
Hi everyone,
Over the last couple of weeks I have been trying to make gdc/dmd
work on GNU Hurd.
This mostly involved porting druntime, wiring things up in the
gcc/dmd build system and telling dmd about the new OS.
With 3 different versions of the same patch I am now able to
bootstrap dmd with
gdc-11 -> gdc-14 -> gdmd -> dmd-2.112 -> dmd-2.112
I have not looked yet at what would be needed for ldc but I hope
it is only minimal additional changes.
It does not work 100% correctly yet as some dmd/phobos/druntime
tests still segfault.
One thing that I am currently a bit stuck on is the following
which is a simplification of `runnable_cxx/cabi.d` test.
It currently crashes either in the c code or when D tries to use
the value returned from C on the 32bit Hurd variant, the 64bit
one does not seem to have the problem.
The same code compiled with my gdc works as expected.
For the codegen i have not done anything special other than
adding hurd to the existing linux/bsd cases
```d
import core.stdc.stdio;
struct Foo1 { char c; }
extern (C) Foo1 ctest1();
void main()
{
Foo1 f1 = ctest1();
printf("%d\n", f1.c);
assert(f1.c == 3);
}
```
where ctest1 is
``` c
struct Foo1 { char c; };
struct Foo1 ctest1()
{
struct Foo1 f;
f.c = 3;
return f;
}
```
I must admit I am a bit lost in the codegen part in dmd, if
anyone has some hints where I should look to fix this, that would
be great.
Id love to upstream my changes but I would need to clean them up
a bit as so far I have prioritized functionality over style, etc.
For the same reason I dont want to link to my changes yet but
they are public if one knows where to look.
Mar 19
On Thursday, 19 March 2026 at 16:41:41 UTC, yelninei wrote:Hi everyone, Over the last couple of weeks I have been trying to make gdc/dmd work on GNU Hurd.That is great! I hope we can help you in your endeavour. PS. Some 30 years ago I was very much interested in GNU/Hurd. Tried to become part of the community but there were countless number of insanely rude people there that made me turn my back to the project. I wonder is this still the case?
Mar 19
On Thursday, 19 March 2026 at 16:41:41 UTC, yelninei wrote:Hi everyone, Over the last couple of weeks I have been trying to make gdc/dmd work on GNU Hurd. This mostly involved porting druntime, wiring things up in the gcc/dmd build system and telling dmd about the new OS. With 3 different versions of the same patch I am now able to https://gamer-choice.com/ boost dmd with gdc-11 -> gdc-14 -> gdmd -> dmd-2.112 -> dmd-2.112 I have not looked yet at what would be needed for ldc but I hope it is only minimal additional changes. It does not work 100% correctly yet as some dmd/phobos/druntime tests still segfault. One thing that I am currently a bit stuck on is the following which is a simplification of `runnable_cxx/cabi.d` test. It currently crashes either in the c code or when D tries to use the value returned from C on the 32bit Hurd variant, the 64bit one does not seem to have the problem. The same code compiled with my gdc works as expected. For the codegen i have not done anything special other than adding hurd to the existing linux/bsd cases ```d import core.stdc.stdio; struct Foo1 { char c; } extern (C) Foo1 ctest1(); void main() { Foo1 f1 = ctest1(); printf("%d\n", f1.c); assert(f1.c == 3); } ``` where ctest1 is ``` c struct Foo1 { char c; }; struct Foo1 ctest1() { struct Foo1 f; f.c = 3; return f; } ``` I must admit I am a bit lost in the codegen part in dmd, if anyone has some hints where I should look to fix this, that would be great. Id love to upstream my changes but I would need to clean them up a bit as so far I have prioritized functionality over style, etc. For the same reason I dont want to link to my changes yet but they are public if one knows where to look.The crash you’re seeing on 32-bit smells very much like a calling convention / ABI mismatch for small structs returned by value. Your example (struct Foo1 { char c; }) is exactly the kind of case where different ABIs handle returns differently (register vs. hidden pointer). A few things you might want to check: First, compare what gdc emits vs. dmd for that function. Since gdc works, it’s a good reference for the correct ABI on Hurd/i386. Look at the generated assembly for ctest1 and the call site – specifically how the return value is passed back. If gdc uses a hidden sret pointer but dmd expects a register (or vice versa), that would explain the crash. In dmd, the relevant logic is in the backend where struct return conventions are decided. Files around toir.d, target.d, and the C ABI handling are worth digging into. On x86 32-bit, there’s usually special handling for “small structs” – often ≤ 8 bytes – but that depends on the platform ABI. Since you added Hurd under linux/bsd, it might be inheriting the wrong assumption. Also worth checking: alignment and packing. A char-only struct might still be treated differently depending on default alignment rules. Try slightly modifying the struct (e.g. add another char or an int) and see if behavior changes – that can help confirm it’s an ABI classification issue. Another quick sanity check: declare the C function with pragma(mangle) or inspect the symbol to ensure there’s no mismatch there, though this looks more like a return-value issue than name mangling. Given that 64-bit works, it further points to 32-bit System V i386 ABI quirks. Hurd might follow a slightly different convention than Linux there, so reusing the linux path blindly could be the root cause. If you narrow down how Hurd expects small structs to be returned, you’ll likely just need to tweak the classification logic in dmd’s backend for that target.
Mar 20
The struct call/return ABI is always badly/erroneously documented, and every
platform has to reinvent it in an undocumented, maddening way.
The way I deal with it is to write trivial C programs:
```C
struct S { int a; }
struct S test(struct S *p) { return *p; }
```
Compile it with the compiler that comes with the system, and disassemble the
object file. Then figure out what the instructions are doing.
I've found out that asking grok or google or chatgpt for an AI description of
it
can help a lot.
Mar 20
On Friday, 20 March 2026 at 21:22:22 UTC, HNBas wrote:
The crash you’re seeing on 32-bit smells very much like a
calling convention / ABI mismatch for small structs returned by
value. Your example (struct Foo1 { char c; }) is exactly the
kind of case where different ABIs handle returns differently
(register vs. hidden pointer).
A few things you might want to check:
First, compare what gdc emits vs. dmd for that function. Since
gdc works, it’s a good reference for the correct ABI on
Hurd/i386. Look at the generated assembly for ctest1 and the
call site – specifically how the return value is passed back.
If gdc uses a hidden sret pointer but dmd expects a register
(or vice versa), that would explain the crash.
In dmd, the relevant logic is in the backend where struct
return conventions are decided. Files around toir.d, target.d,
and the C ABI handling are worth digging into. On x86 32-bit,
there’s usually special handling for “small structs” – often ≤
8 bytes – but that depends on the platform ABI. Since you added
Hurd under linux/bsd, it might be inheriting the wrong
assumption.
Also worth checking: alignment and packing. A char-only struct
might still be treated differently depending on default
alignment rules. Try slightly modifying the struct (e.g. add
another char or an int) and see if behavior changes – that can
help confirm it’s an ABI classification issue.
Another quick sanity check: declare the C function with
pragma(mangle) or inspect the symbol to ensure there’s no
mismatch there, though this looks more like a return-value
issue than name mangling.
Given that 64-bit works, it further points to 32-bit System V
i386 ABI quirks. Hurd might follow a slightly different
convention than Linux there, so reusing the linux path blindly
could be the root cause.
If you narrow down how Hurd expects small structs to be
returned, you’ll likely just need to tweak the classification
logic in dmd’s backend for that target.
Thanks a lot for the responses.
I think i overlooked/missed a lot of linux cases when I initially
did this.
I found the i686 linux struct case in target.d and adding hurd to
that seems to work.
then i also found lot more unhandled cases for the os.
Almost all non windows dmd tests now pass on i686 (he remaining
failures seem expected), on x86_64 the test runner gets killed
with the gc suspend signal after a while, not yet sure whats up
with that.
there are still some issues with druntime which i think is
because CRuntime_Glibc is used for linux+glibc in several places.
I also already found a lot of things that are not needed
anymore/can be simplified significantly as some of my druntime
changes are initially against gdc-11.
Mar 21
On 3/19/2026 9:41 AM, yelninei wrote:I must admit I am a bit lost in the codegen part in dmd, if anyone has some hints where I should look to fix this, that would be great.Pretty much everyone who has tried to wrangle it has given up on it, which baffles me because it is trivially obvious to the most casual observer. Anyhow, feel free to ask questions about it!
Mar 20









Dejan Lekic <dejan.lekic gmail.com> 