Finally, if you still want to try this crazy idea and write something in assembly (if you've reached this section -- you're real assembly fan), I'll herein provide what you will need to get started.
As you've read before, you can write for Linux in different ways; I'll show example of using pure system calls. This means that we will not use libc at all, the only thing required for our program to run is kernel. Our code will not be linked to any library, will not use ELF interpreter -- it will communicate directly with kernel.
I will show the same sample program in two assemblers, nasm
and gas
,
thus showing Intel and AT&T syntax.
You may also want to read Introduction to UNIX assembly programming tutorial, it contains sample code for other UNIX-like OSes.
First of all you need assembler (compiler): nasm
or gas
.
Second, you need linker: ld
, assembler produces only object code.
Almost all distributions include gas
and ld
, in binutils package.
As for nasm
, you may have to download and install binary packages
for Linux and docs from the
nasm webpage;
however, several distributions (Stampede, Debian, SuSe)
already include it, check first.
If you are going to dig in, you should also install kernel source. I assume that you are using at least Linux 2.0 and ELF.
Linux is 32bit and has flat memory model. A program can be divided into sections. Main sections are .text for your code, .data for your data, .bss for undefined data. Program must have at least .text section.
Now we will write our first program. Here is sample code:
section .data ;section declaration
msg db "Hello, world!",0xa ;our dear string
len equ $ - msg ;length of our dear string
section .text ;section declaration
;we must export the entry point to the ELF linker or
global _start ;loader. They conventionally recognize _start as their
;entry point. Use ld -e foo to override the default.
_start:
;write our string to stdout
mov edx,len ;third argument: message length
mov ecx,msg ;second argument: pointer to message to write
mov ebx,1 ;first argument: file handle (stdout)
mov eax,4 ;system call number (sys_write)
int 0x80 ;call kernel
;and exit
mov ebx,0 ;first syscall argument: exit code
mov eax,1 ;system call number (sys_exit)
int 0x80 ;call kernel
.data # section declaration
msg:
.string "Hello, world!\n" # our dear string
len = . - msg # length of our dear string
.text # section declaration
# we must export the entry point to the ELF linker or
.global _start # loader. They conventionally recognize _start as their
# entry point. Use ld -e foo to override the default.
_start:
# write our string to stdout
movl $len,%edx # third argument: message length
movl $msg,%ecx # second argument: pointer to message to write
movl $1,%ebx # first argument: file handle (stdout)
movl $4,%eax # system call number (sys_write)
int $0x80 # call kernel
# and exit
movl $0,%ebx # first argument: exit code
movl $1,%eax # system call number (sys_exit)
int $0x80 # call kernel
First step of building binary is producing object file from source, by invoking assembler; we must issue the following:
For nasm
example:
$ nasm -f elf hello.asm
For gas
example:
$ as -o hello.o hello.S
This will produce hello.o
object file.
Second step is producing executable file itself from object file, by invoking linker:
$ ld -s -o hello hello.o
This will finally build hello
ELF binary.
Hey, try to run it... Works? That's it. Pretty simple.
If you get interested and want to proceed further, you may want to look through Linux assembly projects, they contain PLENTY of source code and examples.