Assembly language notes
Assembly language notes
x86 Assembly
The Netwide Assembler is an x86 and x86-64 assembler that uses syntax similar to Intel. It supports a
variety of object file formats, including:
1. ELF32/64
2. Linux a.out
3. NetBSD/FreeBSD a.out
4. MS-DOS 16-bit/32-bit object files
5. Win32/64 object files
6. COFF
7. Mach-O 32/64
8. rdf
NASM runs on both Unix and Windows/DOS.
Contents
• 1 NASM Syntax
• 1.1 Comments
• 1.2 Macros
• 2 Example I/O (Linux and BSD)
• 3 Hello World (Linux)
• 4 Hello World (Using only Win32 system calls)
• 5 Hello World (Using C libraries and Linking with gcc)
NASM Syntax
Wikipedia has related information at Netwide Assembler
The Netwide Assembler (NASM) uses a syntax "designed to be simple and easy to understand, similar
to Intel's but less complex". This means that the operand order is dest then src, as opposed to the AT&T
style used by the GNU Assembler. For example,
mov ax, 9
Macros
NASM has powerful macro functions, similar to C's preprocessor. For example,
%define newline 0xA
%define func(a, b) ((a) * (b) + 2)
After the int 0x80, eax will contain the number of bytes read. If this number is < 0, there was a
read error of some sort.
Outputting follows a similar convention:
; print a byte to stdout
mov eax, 4 ; the system interprets 4 as "write"
mov ebx, 1 ; standard output (print to terminal)
mov ecx, variable ; pointer to the value being passed
mov edx, 1 ; length of output (in bytes)
int 0x80 ; call the kernel
BSD systems (MacOS X included) use similar system calls, but convention to execute them is
different. While on Linux you pass system call arguments in different registers, on BSD systems they
are pushed onto stack (except the system call number, which is put into eax, the same way as in Linux).
BSD version of the code above:
; read a byte from stdin
mov eax, 3 ; sys_read system call
push dword 1 ; input length
push dword variable ; address to pass to
push dword 0 ; read from standard input
push eax
int 0x80 ; call the kernel
add esp, 16 ; move back the stack pointer
section .data
; Align to the nearest 2 byte boundary, must be a power of two
align 2
; String, which is just a collection of bytes, 0xA is newline
str: db 'Hello, world!',0xA
strLen: equ $-str
section .bss
section .text
_start:
;
; op dst, src
;
;
; Call write(2) syscall:
; ssize_t write(int fd, const void *buf,
size_t count)
;
mov edx, strLen ; Arg three: the length of the string
mov ecx, str ; Arg two: the address of the string
mov ebx, 1 ; Arg one: file descriptor, in this case stdout
mov eax, 4 ; Syscall number, in this case the write(2)
syscall:
int 0x80 ; Interrupt 0x80
;
; Call exit(3) syscall
; void exit(int status)
;
mov ebx, 0 ; Arg one: the status
mov eax, 1 ; Syscall number:
int 0x80
In order to assemble, link and run the program we need to do the following:
$ nasm -f elf32 -g helloWorld.asm
$ ld -g helloWorld.o
$ ./a.out
global _start
extern _GetStdHandle@4
extern _WriteConsoleA@20
extern _ExitProcess@4
section .data
str: db 'hello, world',0xA
strLen: equ $-str
section .bss
numCharsWritten: resb 1
section .text
_start:
;
; HANDLE WINAPI GetStdHandle( _In_ DWORD nStdHandle ) ;
;
push dword -11 ; Arg1: request handle for standard output
call _GetStdHandle@4 ; Result: in eax
;
; BOOL WINAPI WriteConsole(
; _In_ HANDLE hConsoleOutput,
; _In_ const VOID *lpBuffer,
; _In_ DWORD nNumberOfCharsToWrite,
; _Out_ LPDWORD lpNumberOfCharsWritten,
; _Reserved_ LPVOID lpReserved ) ;
;
push dword 0 ; Arg5: Unused so just use zero
push numCharsWritten ; Arg4: push pointer to numCharsWritten
push dword strLen ; Arg3: push length of output string
push str ; Arg2: push pointer to output string
push eax ; Arg1: push handle returned from _GetStdHandle
call _WriteConsoleA@20
;
; VOID WINAPI ExitProcess( _In_ UINT uExitCode ) ;
;
push dword 0 ; Arg1: push exit code
call _ExitProcess@4
In order to assemble, link and run the program we need to do the following. This example was run
under cygwin, in a Windows command prompt the link step would be different. In this example we use
the -e command line option when invoking ld to specify the entry point for program execution.
Otherwise we would have to use _WinMain@16 as the entry point rather than _start. One last note,
WriteConsole() does not behave well within a cygwin console, so in order to see output the final
exe should be run within a Windows command prompt:
$ nasm -f win32 -g helloWorldWin32.asm
$ ld -e _start helloWorldwin32.obj -lkernel32 -o helloWorldWin32.exe
global main
extern printf
section .data
fmtStr: db 'hello, world',0xA,0
section .text
main:
ret
In order to assemble, link and run the program we need to do the following.
$ nasm -felf32 helloWorldgcc.asm
$ gcc helloWorldgcc.o -o helloWorldgcc
section .data
fmtStr: db 'hello, world',0xA,0
section .text
_main:
ret
In order to assemble, link and run the program we need to do the following.
$ nasm -fwin32 helloWorldgcc.asm
$ gcc helloWorldgcc.o -o helloWorldgcc