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.
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 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/i386 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 eax,4 ;system call number (sys_write) mov ebx,1 ;first argument: file handle (stdout) mov ecx,msg ;second argument: pointer to message to write mov edx,len ;third argument: message length int 0x80 ;call kernel ;and exit mov eax,1 ;system call number (sys_exit) xor ebx,ebx ;first syscall argument: exit code int 0x80 ;call kernel
.data # section declaration msg: .string "Hello, world!\n" # our dear string msgend: .equ len, msgend - 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 $4,%eax # system call number (sys_write) movl $1,%ebx # first argument: file handle (stdout) movl $msg,%ecx # second argument: pointer to message to write movl $len,%edx # third argument: message length int $0x80 # call kernel # and exit movl $1,%eax # system call number (sys_exit) xorl %ebx,%ebx # first syscall argument: exit code 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 download my asmutils package, it contains an organized macro set and plenty of examples.