Can dmd compile with my own runtime library?

Frits van Bommel fvbommel at REMwOVExCAPSs.nl
Wed Aug 22 01:45:51 PDT 2007


Huang F Guan wrote:
> Hi, I'm an operating system developer. I've written a win32-compatible and POSIX operating system, just for fun, but it can run many windows api programs. My system is written in C, it is not readable and diffcult to manage. Now I am looking for the new techniques to rewrite this os.

I'm writing a toy "OS kernel" for fun (in D), so I might be able to help.

> I've ever thought for an own compiler and linker, it is so complicated to develop an advanced one. But DMD did it, the D language almost satisfied what I need. Yes, you see, I am trying to use it to develop an operating system in D language, and maybe it will be the first one written in D language. 

As Chad mentioned, definitely not the first. But I don't know if 
anything actually usable has been written yet...

> While I was using C++ developing an os, the compiler can output the platform-independent code. But DMD compiler can not do this, because it needs a runtime library based on the present platform. 
> 
> I have tried gdc, but I failed too. It prints the error below when I link:
> 
> ld: warning: cannot find entry symbol _mainCRTStartup; defaulting to 00401000

This first line isn't D-specific; a warning like this will likely pop up 
for any code that doesn't link in a C library and doesn't specify an 
alternative starting point for the program.
(Use ENTRY(<symbol>) in your linker script or add -e <symbol> or 
--entry=<symbol> to your ld command line to set the entry point to some 
function, or use a number instead to set it to a raw address)

> hello.o(.text+0x18):hello.d: undefined reference to `_Dmodule_ref'
> hello.o(.text+0x23):hello.d: undefined reference to `_Dmodule_ref'
> hello.o(.data+0x0):hello.d: undefined reference to `_D10ModuleInfo6__vtblZ'
> hello.o(.data+0x30):hello.d: undefined reference to `_D6object12__ModuleInfoZ'

These are indeed defined in the runtime library. They're used to 
construct an import graph at runtime, in order to correctly handle 
static constructors. Look at phobos/std/moduleinit.d or 
tango/lib/compiler/{dmd,gdc}/genobj.d to see how this works.

If you're using DMD/Windows (Probably not if you're using ld), you may 
also want to look at phobos/internal/minit.* (I haven't actually done 
this, I use Linux).
Otherwise, you'll also need to run the constructors. They just 
initialize a global symbol ('_Dmodule_ref' above) to link to a linked 
list of module descriptors, so they don't need anything special as long 
as the executable is loaded at the address it's linked at.
To run them on a Linux compiler, just call the function pointers stored 
in the .ctors section of your executable.
If you're using native GDC on Windows, figure out how the Windows 
runtime library is supposed to do this (It might be the same as on Linux).
If you're using GDC, you could also try to cross-compile it for 
i386-elf, i586-elf, or similar. Again, I haven't done this myself so I 
can't help much. The procedure for constructors will probably be much 
the same as the one for Linux though.

> Now I am wondering how can I link a platform-independent executable file? 

(Assuming by "platform" you mean "operating system")
How about linking a your-os-dependent executable? :).

> Do I need to rewrite the runtime library? 
> Or just I need to write a tiny runtime library like gc, moduleinit, object?

Whatever you do, you will need to include some of the functionality 
provided by the runtime library yourself. However you don't need to 
rewrite the entire runtime library, just the parts you actually use :).
Also, you can copy large parts of Phobos or Tango since much of the code 
is OS-independent. Low-level routines for things like memory allocation 
and anything else you use that normally requires system calls may need 
to be rewritten to work for your OS.

I haven't actually ported the garbage collector myself, but it might not 
be that hard. From the looks of it, it just uses a few routines that 
depend on the OS and CPU architecture (to locate stacks and halt other 
threads).
Note that you don't even necessarily need a GC. As long as you're 
careful to delete any memory allocated manually you should be fine. 
However, going this route means you'll probably want to stay away from 
things that *might* reallocate[1], and check every library routine you 
use to see whether it allocates anything and how to make sure it gets 
deleted.
Like I said, I haven't gotten to porting the GC yet. Currently I just 
allocate memory with no way to deallocate. Since I don't allocate very 
much I haven't run out of memory yet :).


[1] For example: operator '~=' appends elements and other arrays to 
arrays; it normally only reallocates when the (over-allocated) memory 
for the array fills up. However it doesn't delete the original, since 
other array references may still point to it (or parts of it).
Of course, this is technically just the behavior with Phobos and Tango. 
If you're rewriting this anyway (as you might, since it allocates 
memory) you can change this behavior any way you like.
If you don't want to use this operator for above reasons, you can delete 
or comment out the relevant runtime routines (IIRC they're named 
_d_arrayappend*) to turn use of it into a link-time error so you don't 
accidentally use them (directly or in a library routine).

Something else that has similar behavior is the use of copy-on-write in 
Phobos' std.string.


> I hope you can help me solve these puzzles, thanks!

I hope the above helps.



More information about the Digitalmars-d mailing list