I think push messages are relevant. Here is another example of a hello world program.
section .text global _start ;must be declared for linker (ld) _syscall: int 0x80 ;system call ret _start: ;tell linker entry point push dword len ;message length push dword msg ;message to write push dword 1 ;file descriptor (stdout) mov eax,0x4 ;system call number (sys_write) call _syscall ;call kernel ;the alternate way to call kernel: ;push eax ;call 7:0 add esp,12 ;clean stack (3 arguments * 4) push dword 0 ;exit code mov eax,0x1 ;system call number (sys_exit) call _syscall ;call kernel ;we do not return from sys_exit, ;there no need to clean stack section .data msg db "Hello, world!",0xa ;our dear string len equ $ - msg ;length of our dear string
Now I have not written the code above, it came from here:
http://www.cin.ufpe.br/~if817/arquivos/asmtut/index.html#helloworld
So you can see that this author can push values ββonto the stack and call the kernel program, which will take parameters from the stack. This actually makes more sense to me, as I thought how parameters were passed to c-functions. Passing parameters through specific registers does not make sense. What if the method has 30 parameters, how would you pass this through registers?
In any case, I compiled the above code, linked it and ran it on my macbook pro, and it worked fine.
In any case, as good as in the above example, itβs not enough to teach you. Here, I think, is the best example of how you will print an arbitrary value from a register.
So what I want is not a predefined string, but a real programming construct; variable.
Easy enough, you can define a 32-bit variable by doing the following:
section .bss msg: resd 2
This gives me a variable (or memory location) called msg that I can store. By declaring it with resd, I determine the number of double words that I want to reserve. The double word is 32 bits, so I declare that I want to reserve 2 double words. Why 2? I will tell you in a moment.
So far so good.
Now all I have to do is move the value to this memory location and follow the example from hello, world, right? So I coded this:
section .text global _start ;must be declared for linker (ld) _syscall: int 0x80 ;system call ret _start: ;tell linker entry point mov dword [msg], 'h' ;move the letter h into my memory location mov dword [msg+1], 0xa ;this is so important, but other author gloss over it ; the line terminator is essential in order to ; print something out push dword 2 ;message length push dword msg ;message to write push dword 1 ;file descriptor (stdout) mov eax,0x4 ;system call number (sys_write) call _syscall ;call kernel ;the alternate way to call kernel: ;push eax ;call 7:0 add esp,12 ;clean stack (3 arguments * 4) push dword 0 ;exit code mov eax,0x1 ;system call number (sys_exit) call _syscall ;call kernel ;we do not return from sys_exit, ;there no need to clean stack section .bss msg: resd 1
So not too much has changed. I added a .bss section which is for uninitialized data. Much more useful than static strings. We move the static value (letter h) to the msg memory location, but you can move the case as easily as this
mov [msg], eax
The next line is so important, but every author of "hello world" just hides it. That 0xa is the line terminator that is required, or WILL_NOT, displays your values. It made me go crazy, trying to figure out why none of this would work. You NEED this terminator. This is why we need to define our variable in order to have two values ββinstead of one. We need to save this terminator.
The rest is simple, flip the parameters onto the stack and call the kernel. You now have an example of how to print some arbitrary data from the actual location of the variables. Enjoy it!