Syscalls and Operating System Calls from Assembly
CS 301 Lecture, Dr. Lawlor
When writing real code in assembly, you often need to interface with other programs.
Communication Method 0: Pass everything you need into assembly as function parameters
The easiest way to get information from C++ is just to pass it in as a parameter to an assembly language function.
Communication Method 1: Call the C standard library
Sometimes, assembly has to get its own work done. One way to get
information out of a machine is to call other C functions, like the
builtin "printf" function in the C standard library header
<stdio.h>. Here's how to call it in assembly:
;Assembly equivalent of C: printf("The integer is %d\n",17);
push 17 ; integer to print
push myPrintString ; string to print it with
extern printf
call printf
add esp,8 ; clean up the stack
ret
myPrintString:
db "The integer is %d",0xa,0 ; 0xa = newline, 0 = end of a C string.
(Try this in NetRun now!)
Note that the C standard library (including fopen, rand, srand, time,
etc) are just C functions, so you can just call them like ordinary C
functions.
Communication Method 2: Make a direct syscall
Sometimes, such as when you're implementing
a C library, or when there is no C library call to access the
functionality you need, you want to talk to the OS kernel
directly. There's a special x86 "interrupt" instruction to do
this, called "int". On Linux, you talk to the OS by loading up
values into registers then calling "int 0x80".
Konstantin Boldyshev has a good writeup and examples of Linux, BSD, and BeOS x86 syscalls, and a list of common Linux syscalls. He uses NASM for the examples. Here's a slightly cleaned up version of his Linux example:
mov edx,8 ;message length
mov ecx,myString ;message to write
mov ebx,1 ;file descriptor (fd 1 is stdout)
mov eax,4 ;system call number (number 4 is sys_write)
int 0x80 ;call kernel
; Kernel call return value is in eax-- it'll do as a function return code.
ret
myString:
db 'Wazzup?',0xa ; our little string, followed by a newline
(Try this in NetRun now!)
Windows system call numbers keep changing, so direct system calls aren't at all easy to use on Windows.
Linker Interfacing
To define a new assembly function that can be called from outside, you need to do two things:
- Declare the assembly function in the ".text" section, and make the symbol "global", like so:
section .text
global foo ; Make "foo" visible from outside
foo:
mov eax,23
ret
-
(Try this in NetRun now!)
- Declare a C++ prototype for the assembly function "extern "C"", like so:
extern "C" int foo(void); // Code is written in assembly
- From C++ in Windows, you may also need a "__cdecl" calling
convention specifier:
extern "C" int __cdecl foo(void); // Code is written in assembly (called from Windows)
On Windows and several other platforms, C functions normally get
linked
with an underscore in front of their name, so your assembly function
"foo" (in C/C++) must be declared "_foo" in assembly, and similarly the
C function "printf" can only be accessed as "_printf" in assembly.
In Linux, you can even write a whole main program in assembly by
just writing a
"main" function:
global main
main:
push myString
extern printf
call printf
pop eax
ret
myString:
db "Hey!",0xa,0
(Try this in NetRun now!)
"extern" also works to access C/C++ global variables.
Windows Inline Interfacing
In the non-NetRun world, you have several ways to get assembly code
running. The easiest on Windows is to use inline assembly, which
we previously discussed here.
Annoyingly, the Windows inline assembler (MASM) doesn't accept "extern"
or "db" declarations, so you have to declare strings and external
functions in C. Thus the printf example above can be written like
so in Microsoft Visual C++:
#include <stdio.h>
int main() {
const char *myString="Hello!\n";
__asm{
push myString
call printf
pop eax
}
return 0;
}
See my NASM/Windows guide for how to make
this code run without crashing.
Windows NASM Interfacing
You can get the full NASM syntax on Windows by using... NASM itself. Dr. Hartman has an excellent howto on interfacing NASM with Visual C++. The other way to use NASM from Windows is by calling it on the command line directly, like this:
nasm -f win32 foo.S
cl main.cpp /EHsc /Gr foo.obj
That'll turn foo.S and main.cpp into one program, main.exe.
If you prefer the Visual C++ IDE, see my
illustrated guide to integrating NASM with Visual C++.