Linux system calls are typically performed via a C interface. D can call these just like any other C library functions (see the Using external libraries recipe in Chapter 1, Core Tasks). Here, we'll write a "Hello World" program using the standard C function for the system call, and say "hello" using inline assembly to demonstrate how that can be done as well.
Making Linux system calls with inline assembly is quite different than making them with the C interface. First, we will look at how it is done with the function, then we'll translate it to the assembly.
Let's make Linux system calls by executing the following steps:
core.sys.posix
package, or if it is not present, write the prototype with extern(C)
.The code is as follows:
import core.sys.posix.unistd; // analogous to #include <unistd.h> string hello = "Hello, world!"; write(1 /* stdout file descriptor */, hello.ptr, hello.length);
static assert(0)
to trigger a compile error on unsupported systems.The code is follows:
string hello = "Hello, world!"; auto hptr = hello.ptr; auto hlength = hello.length; version(linux) { version(D_InlineAsm_X86) asm { mov ECX, hptr; mov EDX, hlength; mov EBX, 1; // stdout mov EAX, 4; // sys_write int 0x80; // perform the operation } else version(D_InlineAsm_X86_64) asm { mov RSI, hptr; mov RDX, hlength; mov RDI, 1; // stdout mov RAX, 1; // sys_write syscall; // perform the operation } else static assert(0, "Unsupported Processor for inline asm"); } else static assert(0, "Unsupported OS for this function");
When you run the program, it will print Hello, world!
.
Standardized C headers are available in druntime
under the core package. The core.stdc.*
package has the standard C library headers. The core.sys
module has standard headers for the operating system.
The following table has some illustrative examples of a header file and its corresponding D module names:
C header |
D module |
---|---|
|
|
|
|
|
|
|
|
|
|
If you are unsure whether a header is standardized in Posix or is specific to a particular system such as Linux, you can check the man
page for a Conforming to section, or simply try to import the module from core.sys.posix
first, and if that doesn't work, try again with core.sys.linux
.
If you need a function that is not provided in any core module, you can still call it by copying the function and struct prototypes into your D program, marking them extern(C)
, as shown in the following code:
/* import the basic type definitions */ import core.sys.posix.sys.types; /* Copy the function prototype from the system documentation */ extern(C) ssize_t write(int fd, const void* buf, size_t count); // we can now call write!
D also has built-in support for the inline assembly code. While the main compilers currently have differing support for inline assembly, here, we're using the syntax for the DMD compiler, which is based on the Intel assembly syntax.
A block of assembly code is denoted by the asm { }
structure. Inside, the syntax remains similar to D—comments remain in the same D forms, instructions must be separated by semicolons, and D identifiers are still usable. Unlike most assemblers, D's inline asm
syntax is case sensitive. Instruction names must be in lowercase and register names must be in uppercase.
The assembly language code is rarely portable, so it is also advisable to always put asm
blocks inside version
blocks, and be as specific as you can be, at least based on the processor family. Here, we broke it down by the operating system, then by 32-bit and 64-bit asm
on the x86 processor because the code needs to be different in each case. The else static assert
function at the end of each group of versions ensures that an unimplemented configuration results in a compilation error rather than the silently buggy or incomplete code. Any time you use version statements, consider terminating the group with else static assert(0, "message")
for future maintenance.
3.14.141.115