Problems with FreeBSD assembly
Giorgos Keramidas
keramida at ceid.upatras.gr
Thu Nov 12 17:45:15 UTC 2009
On Wed, 11 Nov 2009 14:43:21 -0500, David Jackson <norstar39 at gmail.com> wrote:
> I am having great difficulty running a very simple assembler program
> on FreeBSD on x86 in my efforts to learn some assembly programming on
> FreeBSD. I have tried to compile the following with nasm, however i
> get nothing in response when I attempt to run this program:
>
> section .data
> hello db 'Hello, World!', 0xa
> hbytes equ $ - hello
>
> section .text
> global _start
> _start:
> push dword hbytes
> push dword hello
> push dword 1
> mov eax,0x4
> int 0x80
> add esp,12
>
> push dword 0
> mov eax,0x1
> int 0x80
>
> nasm -f elf -o hello1s.o hello1.s
> ld -s -o hello1s hello1s.o
>
> ./hello1s prints nothing.
>
> What is wrong here? It should print "hello world".
> Thanks in advance for your help, it is greatly appreciated.
Hi David. The truss utility is your friend when you are trying to
decipher system call problems. It can translate system call arguments
to human-readable output; a very useful property when debugging issues
like this. For example here's the output for your original code:
$ truss ./hello
write(134516904,0xe,1) ERR#9 'Bad file descriptor'
process exit, rval = 1
Note how the arguments of write() are 'misplaced'? The answer is that
you are not calling the system call with C-like conventions (including a
function call return address). The calling conventions of system calls
are described in the Developer's Handbook at:
http://www.freebsd.org/doc/en/books/developers-handbook/x86-system-calls.html
You are missing a dword push before interrupting. As the dev handbook
says, you have to use C calling conventions or push an extra (ignorable)
dword before interrupting:
An assembly language program can do that as well. For example, we could
open a file:
| kernel:
| int 80h ; Call kernel
| ret
|
| open:
| push dword mode
| push dword flags
| push dword path
| mov eax, 5
| call kernel
| add esp, byte 12
| ret
This is a very clean and portable way of coding. If you need to port
the code to a UNIX system which uses a different interrupt, or a
different way of passing parameters, all you need to change is the
kernel procedure.
But assembly language programmers like to shave off cycles. The
above example requires a call/ret combination. We can eliminate it
by pushing an extra dword:
| open:
| push dword mode
| push dword flags
| push dword path
| mov eax, 5
| push eax ; Or any other dword
| int 80h
| add esp, byte 16
So by pushing *one* more dword before you interrupt should work fine
(and it does, from a small test I ran just now):
: keramida at kobe:/home/keramida$ cat hello.s
: section .data
: hello db 'Hello, World!', 0xa
: hbytes equ $ - hello
:
: section .text
: global _start
: _start:
: push dword hbytes
: push dword hello
: push dword 1
: push dword 0 ;or any other dword
: mov eax, 4
: int 0x80
: add esp, byte 16
:
: push dword 0
: push dword 0 ;ignored dword
: mov eax, 1
: int 0x80
: add esp, byte 8 ;NOT REACHED
: keramida at kobe:/home/keramida$ nasm -f elf -o hello.o hello.s
: keramida at kobe:/home/keramida$ ld -s -o hello hello.o
: keramida at kobe:/home/keramida$ truss ./hello
: Hello, World!
: write(1,"Hello, World!\n",14) = 14 (0xe)
: process exit, rval = 0
: keramida at kobe:/home/keramida$
HTH,
Giorgos
More information about the freebsd-questions
mailing list