I already provided an example how to use c functions in assembly and stated differences between 32 bit and 64 bit systems:
http://evilzone.org/other/%28asm%29-calling-c-functions-on-64bit-architecture/It is pretty much the same with command line arguments. main is just a function like every function else. You treat it the same way. The declaration is the following:
int main(int argc, char** argv)
For those who don't know C:
int argc - contains the number of command line arguments
char** argv - contains addresses to the command line argument strings
You can imagine it like that:
argv | address1 | address2 | address 3 |
/ | \
/ | \
| arg string 1 | | arg string 2 | | arg string 3 |
On a 32 bit system you can jump to the next command line argument address by adding 4 bytes (=32 bit) to the previous address (i.e. jumping from address1 to address2).
A 64 bit system needs 8 byte to jump to the next address that argv contains.
I will show how to get and print command line arguments on the screen.
The following is an example I found on
http://www.cs.lmu.edu/~ray/notes/nasmexamples/It is written for 32 bit Windows. Here you get the arguments argv and argc from the stack.
; ----------------------------------------------------------------------------
; echo.asm
;
; NASM implementation of a program that displays its commandline arguments,
; one per line.
; ----------------------------------------------------------------------------
global _main
extern _printf
section .text
_main:
mov ecx, [esp+4] ; argc
mov edx, [esp+8] ; argv
top:
push ecx ; save registers that printf wastes
push edx
push dword [edx] ; the argument string to display
push format ; the format string
call _printf
add esp, 8 ; remove the two parameters
pop edx ; restore registers printf used
pop ecx
add edx, 4 ; point to next argument
dec ecx ; count down
jnz top ; if not done counting keep going
ret
format:
db '%s', 10, 0
To use the same in 32 bit Linux you just have to remove the underscores:
; create executable this way:
; nasm -f elf -g -F stabs echo.asm
; gcc -o echo echo.o
;
global main
extern printf
section .text
main:
mov ecx, [esp+4] ; argc
mov edx, [esp+8] ; argv
top:
push ecx ; save registers that printf wastes
push edx
push dword [edx] ; the argument string to display
push format ; the format string
call printf
add esp, 8 ; remove the two parameters
pop edx ; restore registers printf used
pop ecx
add edx, 4 ; point to next argument
dec ecx ; count down
jnz top ; if not done counting keep going
ret
format:
db '%s', 10, 0
In a 64 bit system the arguments are not in the stack but in the general purpose registers. So the code above would not work. The order of the registers is the following: %rdi, %rsi, %rdx, %rcx, %r8 and %r9
That means argc is in %rdi and argv in %rsi. Here is my 64 bit version of the same program (linux). It uses the 64 bit registers and adds 8 byte to jump to the next address of the next command line argument.
; create executable this way:
; nasm -f elf64 -g -F stabs echo.asm
; gcc -o echo echo.o
;
extern printf
section .data
format: db '%s', 10, 0
section .text
global main
main:
mov rcx, rdi ; argc
mov r8, 0 ; offset
repeat:
mov rdx, qword [rsi+r8] ; argv
push rcx ; save registers that printf wastes
push rdx
push rsi
push r8
mov rdi, format ; first parameter for printf
mov rsi, rdx ; second parameter for printf
mov rax, 0 ; no floating point register used
call printf ; call to printf
pop r8 ; restore registers
pop rsi
pop rdx
pop rcx
add r8, 8 ; point to next argument
dec rcx ; count down
jnz repeat ; if not done counting keep going
mov rax, 1 ; stop the program
mov rbx, 0
int 80h
An example output:
[deque@desolate echo]$ ./echo bla la ka2
./echo
bla
la
ka2
Here is a makefile for the 64 bit version:
echo: echo.o
gcc -o echo echo.o
echo.o: echo.asm
nasm -f elf64 -g -F stabs echo.asm
Deque