Dr. Brian Robert Callahan

academic, developer, with an eye towards a brighter techno-social life


A DMD package for OpenIndiana

A long time ago, I ported DMD, the reference compiler for the D programming language, to OpenBSD.

Today, I made a package of DMD 2.109.0 for OpenIndiana, one of the distributions of Illumos, a Solaris derivative.

Let's walk through how to do it.

Installing GDC

I could not find any packages for any Illumos distributions. The easiest way to get a D compiler on your machine, if it is supported by GCC, is to install any version of GCC from 9.1 to 11.4; this is because those versions have a version of the D compiler that is written entirely in C++, making it an ideal bootstrap compiler.

OpenIndiana's gcc packages don't include gdc, so I downloaded gcc-11.4.0 and built and installed it. This was relatively straightforward; all I did was set a custom prefix since I wasn't planning on keeping this version of GCC around, and setting --disable-shared so I could build without bootstrapping GCC. When not building a 2- or 3-stage GCC, the build is pretty quick.

You will also need the gdmd wrapper script to smooth over the differences between GDC and DMD flags when building DMD.

First issues

We could now trying building DMD with something like gmake HOST_DMD=gdmd. Unfortunately, this fails pretty quickly; the build.d build controller gets killed immediately upon execution. There were no hints here, so I thought maybe it was something to do with Sun ld(1) versus the GNU binutils. And I was right, at least right enough. I did this silly dance to force the use of GNU ld(1):

$ sudo mv /usr/bin/ld /usr/bin/ld.sun
$ sudo ln -s /usr/bin/gld /usr/bin/ld

And incredibly, this was enough to get DMD and Phobos built after a small patch to fix what I believe is a bug. I'll have the full diff at the end of this post.

Failing to link

Having the GNU linker in /usr/bin/ld is not ideal, so I switched it back to the Sun linker. This however caused problems rebuilding DMD with my stage 1 DMD. First, DMD defaults to cc as the linker name if you don't have a CC environment variable. This is easy; all I needed to do was change the default linker invocation to gcc.

Next, it complained the Sun linker did not understand the --export-dynamic flag. Which is true, but the Sun linker does this by default without the need for a flag. The solution is to find the dmd.conf file and remove that flag.

Then it complained there were undefined symbols: __start_deh and __stop_deh. These are symbols created by DMD itself; I had already written code to make sure those symbols were written on OpenBSD, so all I had to was copy it and say this should also be done for Solaris.

But that was it. After that, I could build and rebuild DMD with itself ad infinitum.

Creating a package

I figured the best thing to do now was create a package so that others can use DMD and make it easier to update in the future. OpenIndiana uses something called the Image Packaging System, IPS for short, which I could not find a lot of documentation on. All I could find was this PDF which I think it outdated, but it did the trick. I was not able to figure out how to translate that into something that could go into the OpenIndiana official package repository; if you know how to do that, it would be much appreciated! Fortunately, the PDF I found explained how to create a standalone package that could be downloaded and installed by anyone, and that is what I opted to do.

The patch

Here is the entirety of the patch I needed to get DMD building and the package to work correctly:

diff --git a/compiler/ini/solaris/bin64/dmd.conf b/compiler/ini/solaris/bin64/dmd.conf
index 04e2eae..e9ca5a1 100644
--- a/compiler/ini/solaris/bin64/dmd.conf
+++ b/compiler/ini/solaris/bin64/dmd.conf
@@ -1,5 +1,5 @@
-DFLAGS=-I%@P%/../../src/phobos -I%@P%/../../src/druntime/import -L-L%@P%/../lib32 -L--export-dynamic -L-lsocket -L-lnsl
+DFLAGS=-I/usr/include/dmd/phobos -I/usr/include/dmd/druntime/import fPIC -L-L/usr/lib -L-lsocket -L-lnsl
-DFLAGS=-I%@P%/../../src/phobos -I%@P%/../../src/druntime/import -L-L%@P%/../lib64 -L--export-dynamic -L-lsocket -L-lnsl
+DFLAGS=-I/usr/include/dmd/phobos -I/usr/include/dmd/druntime/import fPIC -L-L/usr/lib/amd64 -L-lsocket -L-lnsl
diff --git a/compiler/src/dmd/backend/elfobj.d b/compiler/src/dmd/backend/elfobj.d
index d0f288f..9216189 100644
--- a/compiler/src/dmd/backend/elfobj.d
+++ b/compiler/src/dmd/backend/elfobj.d
@@ -3331,6 +3331,17 @@ private void obj_rtinit()
         deh_end = elf_addsym(namidx4, 0, 0, STT_NOTYPE, STB_GLOBAL, SHN_UNDEF, STV_HIDDEN);
+    if (config.exe & (EX_SOLARIS | EX_SOLARIS64))
+    {
+        const namidx3 = ElfObj_addstr(symtab_strings,"__start_deh");
+        deh_beg = elf_addsym(namidx3, 0, 0, STT_NOTYPE, STB_GLOBAL, SHN_UNDEF, STV_HIDDEN);
+        ElfObj_getsegment("deh", null, SHT_PROGBITS, shf_flags, _tysize[TYnptr]);
+        const namidx4 = ElfObj_addstr(symtab_strings,"__stop_deh");
+        deh_end = elf_addsym(namidx4, 0, 0, STT_NOTYPE, STB_GLOBAL, SHN_UNDEF, STV_HIDDEN);
+    }
     const namidx = ElfObj_addstr(symtab_strings,"__start_minfo");
     minfo_beg = elf_addsym(namidx, 0, 0, STT_NOTYPE, STB_GLOBAL, SHN_UNDEF, STV_HIDDEN);
@@ -3520,6 +3531,11 @@ private void obj_rtinit()
+        if (config.exe & (EX_SOLARIS | EX_SOLARIS64))
+        {
+            writeSym(deh_end);
+            writeSym(deh_beg);
+        }
diff --git a/compiler/src/dmd/link.d b/compiler/src/dmd/link.d
index ada68b0..9ca27c5 100644
--- a/compiler/src/dmd/link.d
+++ b/compiler/src/dmd/link.d
@@ -467,7 +467,7 @@ public int runLINK(bool verbose, ErrorSink eSink)
         const(char)* cc = getenv("CC");
         if (!cc)
-            argv.push("cc");
+            argv.push("gcc");
diff --git a/compiler/src/dmd/root/rmem.d b/compiler/src/dmd/root/rmem.d
index c6986c0..90da61c 100644
--- a/compiler/src/dmd/root/rmem.d
+++ b/compiler/src/dmd/root/rmem.d
@@ -195,10 +195,7 @@ else version (LDC)
 else version (GNU)
-    version (IN_GCC)
-        enum OVERRIDE_MEMALLOC = false;
-    else
-        enum OVERRIDE_MEMALLOC = true;
+    enum OVERRIDE_MEMALLOC = false;

The package

Here is a direct link to the package for convenience.


That wasn't as difficult as I thought! Even better, now that I've written it all down, I won't forget how to do it.