www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Writing a Linux driver in the D ecosystem

reply Eduard Staniloiu <edi33416 gmail.com> writes:
Hello, everyone!

We, here at UPB, were thinking if it would be possible to write a 
simple Linux driver.
The long term goal would be to port an existing driver in D.

We were wondering if anyone attempted to do this.
There are several questions:
  * how can we build a driver using dmd/gdc/ldc?
    * how should the makefiles be changed
    * Is gdc the best option to create an object file that can 
link with the existing objects (that were generated by using gcc)?
  * if everything goes well, how can we insert the generated 
module? Would/Should it just work with insmod?

We believe this is an interesting bachelors thesis project.

Cheers,
Edi
Oct 23 2018
next sibling parent rikki cattermole <rikki cattermole.co.nz> writes:
I haven't attempted to do it just yet, but I would like to write up a 
Windows driver for my keyboard (yay Windows not being totally USB HID 
compliant). My current solution on D's side is -betterC with dmd (since 
I am only interested in x86).

I'm not sure what the current state of gdc is for -betterC, but for ldc 
it may work for this purpose, no guarantees.
Oct 23 2018
prev sibling next sibling parent welkam <wwwelkam gmail.com> writes:
On Tuesday, 23 October 2018 at 12:06:48 UTC, Eduard Staniloiu 
wrote:
 Hello, everyone!

 We, here at UPB, were thinking if it would be possible to write 
 a simple Linux driver.
Is it possible to write Linux driver in D? Yes. If you want to call C code from D then its easy. https://dlang.org/spec/interfaceToC.html If you want to call D code from C then it gets more tricky. Before C can use D code D runtime needs to be initialized by calling this function: https://dlang.org/library/core/runtime/runtime.initialize.html I am not familiar with driver architectures and dont know how convenient it is to add that init call but if its easy then what is left to do is to sprinkle your code with extern (c) and it should link and work. And if its not convenient then there is another option. Writing D code without its runtime. There is a compiler flag -betterC that removes D runtime but you also loose all functionality that depends on runtimes. Now you can write C type libraries with subset of D. Here is article on that. https://dlang.org/spec/betterc.html
Oct 23 2018
prev sibling next sibling parent welkam <wwwelkam gmail.com> writes:
On Tuesday, 23 October 2018 at 12:06:48 UTC, Eduard Staniloiu 
wrote:
    * how should the makefiles be changed
    * Is gdc the best option to create an object file that can 
 link with the existing objects (that were generated by using 
 gcc)?
  * if everything goes well, how can we insert the generated 
 module? Would/Should it just work with insmod?
The only experience I have with C and D linking is DMD compiler. Parts of it are still in C and when I tried to compile DMD with clang/ldc and clang/dmd everything linked and worked without any change because both emit C ABI and ELF object files. If you write your code with -betterC and mark your functions with extern (C) the linker should treat your object file as it was compiled from C code
Oct 23 2018
prev sibling parent reply sarn <sarn theartofmachinery.com> writes:
On Tuesday, 23 October 2018 at 12:06:48 UTC, Eduard Staniloiu 
wrote:
 Hello, everyone!

 We, here at UPB, were thinking if it would be possible to write 
 a simple Linux driver.
Hi, I gave this a casual try once. The difficulty is the same difficulty you get trying to write Linux kernel modules in C++: the Linux build system is a mess of make files that are only designed for C, and don't represent a stable API between kernel source versions. If I had to write a Linux module in D, I'd write an interface layer in C that gets built using the normal Linux build system and can be linked to my D code. The interface would have to provide alternatives to the various Linux preprocessor macros. (I think trying to refactor the build system to work with dpp would be a total waste of time.)
Oct 23 2018
parent reply Eduard Staniloiu <edi33416 gmail.com> writes:
On Wednesday, 24 October 2018 at 06:36:51 UTC, sarn wrote:
 On Tuesday, 23 October 2018 at 12:06:48 UTC, Eduard Staniloiu 
 wrote:
 Hello, everyone!

 We, here at UPB, were thinking if it would be possible to 
 write a simple Linux driver.
Hi, I gave this a casual try once. The difficulty is the same difficulty you get trying to write Linux kernel modules in C++: the Linux build system is a mess of make files that are only designed for C, and don't represent a stable API between kernel source versions. If I had to write a Linux module in D, I'd write an interface layer in C that gets built using the normal Linux build system and can be linked to my D code. The interface would have to provide alternatives to the various Linux preprocessor macros. (I think trying to refactor the build system to work with dpp would be a total waste of time.)
Thank you all for your replies! We too, had the same feeling that it should be possible to do, based on the ABI and `-betterC`. Our concern was the build system, and how everything can be glued together. Thank you for the thin-layer C interface suggestion; I'll try to give it a go ("Hello, insmod") in the weekend. I'll give an update as soon as I have one. Cheers, Edi
Oct 25 2018
parent reply Eduard Staniloiu <edi33416 gmail.com> writes:
On Thursday, 25 October 2018 at 12:35:56 UTC, Eduard Staniloiu 
wrote:
 On Wednesday, 24 October 2018 at 06:36:51 UTC, sarn wrote:
 On Tuesday, 23 October 2018 at 12:06:48 UTC, Eduard Staniloiu 
 wrote:
 Hello, everyone!

 We, here at UPB, were thinking if it would be possible to 
 write a simple Linux driver.
Hi, I gave this a casual try once. The difficulty is the same difficulty you get trying to write Linux kernel modules in C++: the Linux build system is a mess of make files that are only designed for C, and don't represent a stable API between kernel source versions. If I had to write a Linux module in D, I'd write an interface layer in C that gets built using the normal Linux build system and can be linked to my D code. The interface would have to provide alternatives to the various Linux preprocessor macros. (I think trying to refactor the build system to work with dpp would be a total waste of time.)
Thank you all for your replies! We too, had the same feeling that it should be possible to do, based on the ABI and `-betterC`. Our concern was the build system, and how everything can be glued together. Thank you for the thin-layer C interface suggestion; I'll try to give it a go ("Hello, insmod") in the weekend. I'll give an update as soon as I have one. Cheers, Edi
Hello, everyone! I am trying to get a simple dummy module to work. I have a simple function written in D, named `call_d`, that I want to call from the `module_init` C function. I had the following plan: * build a .o from the D source file, using `dmd -c -betterC dsrc.d` * add `dsrc.o` to the list of objects in Kbuild * let make do it's magic Based on this stackoverflow Q/A [0], in order to tell Kbuild to use a prebuilt object, the object needs to end in `.o_shipped`; so, I named my `dsrc.o` accordingly -> dsrc.o_shipped Here are my files: // C Module ``` // hellomod.c #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> MODULE_DESCRIPTION("My kernel module"); MODULE_AUTHOR("Me"); MODULE_LICENSE("GPL"); static int dummy_init(void) { int r = call_d(); printk( KERN_ALERT "Hi. got %d from D\n", r); return 0; } static void dummy_exit(void) { printk( KERN_ALERT "Bye\n" ); } module_init(dummy_init); module_exit(dummy_exit); ``` // D Source ``` // dsrc.d extern(C) int call_d() { return 10; } ``` // Kbuild ``` EXTRA_CFLAGS = -Wall -g obj-m = hellomod.o hellomod-y = dsrc.o_shipped ``` // Makefile ``` KDIR = /lib/modules/`uname -r`/build kbuild: » make -C $(KDIR) M=`pwd` clean: » make -C $(KDIR) M=`pwd` clean ``` I'm building my dsrc.o_shipped with `dmd -c -betterC dsrc.d -of=dsrc.o_shipped` When I `make` the build, I get the following warning: ``` WARNING: could not find /home/fawkes/ws/dlang/hello/.dsrc.o_shipped.cmd for /home/fawkes/ws/dlang/hello/dsrc.o_shipped ``` The hellomod.ko get's built, but when I `insmod` it, it doesn't print anything at `dmesg`. I don't get a Kernel panic/oops either. I know it's a long post, but any suggestions? Cheers, Edi
Oct 29 2018
next sibling parent Eduard Staniloiu <edi33416 gmail.com> writes:
On Monday, 29 October 2018 at 15:51:49 UTC, Eduard Staniloiu 
wrote:
 On Thursday, 25 October 2018 at 12:35:56 UTC, Eduard Staniloiu 
 wrote:

 /* snip */
 Based on this stackoverflow Q/A [0], in order to tell Kbuild to 
 use a prebuilt object,
 the object needs to end in `.o_shipped`; so, I named my 
 `dsrc.o` accordingly -> dsrc.o_shipped
Forgot to add link [0] - https://stackoverflow.com/questions/6507631/linking-to-a-kernel-module-a-precompiled-object-file
Oct 29 2018
prev sibling parent reply sarn <sarn theartofmachinery.com> writes:
On Monday, 29 October 2018 at 15:51:49 UTC, Eduard Staniloiu 
wrote:
 I know it's a long post, but any suggestions?
Do you have the basic hello world module working? (Basically your C code minus the call to D code.) If that works, I'd confirm that the module contains what I expect using some basic binary analysis. This will do a disassembly of your module, resolving relocations: objdump -dr hellomod.ko All the functions you've defined should be in the .text section. You should see a call to call_d() in the disassembly of dummy_init().
Oct 30 2018
parent reply Eduard Staniloiu <edi33416 gmail.com> writes:
On Tuesday, 30 October 2018 at 07:24:51 UTC, sarn wrote:
 On Monday, 29 October 2018 at 15:51:49 UTC, Eduard Staniloiu 
 wrote:
 I know it's a long post, but any suggestions?
Do you have the basic hello world module working? (Basically your C code minus the call to D code.)
Yes, this is working as expected.
 If that works, I'd confirm that the module contains what I 
 expect using some basic binary analysis.

 This will do a disassembly of your module, resolving 
 relocations:

 objdump -dr hellomod.ko

 All the functions you've defined should be in the .text 
 section.  You should see a call to call_d() in the disassembly 
 of dummy_init().
Here is the result of running `make` ``` make -C /lib/modules/`uname -r`/build M=`pwd` make[1]: Entering directory '/usr/src/linux-headers-4.10.0-28-generic' LD /home/fawkes/ws/dlang/hello/built-in.o LD [M] /home/fawkes/ws/dlang/hello/hellomod.o Building modules, stage 2. MODPOST 1 modules WARNING: could not find /home/fawkes/ws/dlang/hello/.dsrc.o_shipped.cmd for /home/fawkes/ws/dlang/hello/dsrc.o_shipped CC /home/fawkes/ws/dlang/hello/hellomod.mod.o LD [M] /home/fawkes/ws/dlang/hello/hellomod.ko make[1]: Leaving directory '/usr/src/linux-headers-4.10.0-28-generic' ``` And the result of the disassembly, result of `objdump -dr hellomod.ko` ``` hellomod.ko: file format elf64-x86-64 Disassembly of section .text.call_d: 0000000000000000 <call_d>: 0: 55 push %rbp 1: 48 8b ec mov %rsp,%rbp 4: b8 0a 00 00 00 mov $0xa,%eax 9: 5d pop %rbp a: c3 retq ... ```
Oct 30 2018
parent reply Radu <void null.pt> writes:
On Tuesday, 30 October 2018 at 09:10:02 UTC, Eduard Staniloiu 
wrote:
 On Tuesday, 30 October 2018 at 07:24:51 UTC, sarn wrote:
 [...]
Yes, this is working as expected.
 [...]
Here is the result of running `make` ``` make -C /lib/modules/`uname -r`/build M=`pwd` make[1]: Entering directory '/usr/src/linux-headers-4.10.0-28-generic' LD /home/fawkes/ws/dlang/hello/built-in.o LD [M] /home/fawkes/ws/dlang/hello/hellomod.o Building modules, stage 2. MODPOST 1 modules WARNING: could not find /home/fawkes/ws/dlang/hello/.dsrc.o_shipped.cmd for /home/fawkes/ws/dlang/hello/dsrc.o_shipped CC /home/fawkes/ws/dlang/hello/hellomod.mod.o LD [M] /home/fawkes/ws/dlang/hello/hellomod.ko make[1]: Leaving directory '/usr/src/linux-headers-4.10.0-28-generic' ``` [...]
Have you tried to build it with LDC? You might need to tweak the code generation, for example `-betterC -m64 -nodefaultlib --disable-red-zone -output-o -code-model=large -relocation-model=static`
Oct 30 2018
parent Eduard Staniloiu <edi33416 gmail.com> writes:
On Tuesday, 30 October 2018 at 13:22:16 UTC, Radu wrote:
 On Tuesday, 30 October 2018 at 09:10:02 UTC, Eduard Staniloiu 
 wrote:
 On Tuesday, 30 October 2018 at 07:24:51 UTC, sarn wrote:
 [...]
Yes, this is working as expected.
 [...]
Here is the result of running `make` ``` make -C /lib/modules/`uname -r`/build M=`pwd` make[1]: Entering directory '/usr/src/linux-headers-4.10.0-28-generic' LD /home/fawkes/ws/dlang/hello/built-in.o LD [M] /home/fawkes/ws/dlang/hello/hellomod.o Building modules, stage 2. MODPOST 1 modules WARNING: could not find /home/fawkes/ws/dlang/hello/.dsrc.o_shipped.cmd for /home/fawkes/ws/dlang/hello/dsrc.o_shipped CC /home/fawkes/ws/dlang/hello/hellomod.mod.o LD [M] /home/fawkes/ws/dlang/hello/hellomod.ko make[1]: Leaving directory '/usr/src/linux-headers-4.10.0-28-generic' ``` [...]
Have you tried to build it with LDC? You might need to tweak the code generation, for example `-betterC -m64 -nodefaultlib --disable-red-zone -output-o -code-model=large -relocation-model=static`
I've come back with an update. I've managed to make it work with both `dmd` and `ldc`. The issue was how I wrote the `Kbuild` file // Bad Kbuild ``` EXTRA_CFLAGS = -Wall -g obj-m = hellomod.o hellomod-y = dsrc.o_shipped ``` I was under the false impression that `hellomod-y = dsrc.o_shipped` will add the dsrc.o object to the `hellomod.o` object and create the .ko. It doesn't do that. I believe it will only link the dsrc.o and omit the `hellomod.o`. The correct `Kbuild` is // Bad Kbuild ``` EXTRA_CFLAGS = -Wall -g obj-m = mydriver.o hellomod-y = hellomod.o dsrc.o_shipped ``` Now everything works as expected. Thank you all for your support! Edi
Nov 22 2018