Author Topic: Calling C-functions from assembly  (Read 24804 times)

Offline Flybro

  • Jr. Member
  • *
  • Posts: 6
  • Country: au
Calling C-functions from assembly
« on: February 20, 2010, 09:50:59 AM »
Hi, all

I'm trying to compile (MacOSX 10.6 Leopard) very simple and very small assembly routine which print a message to the console using the puts c-function.
Unfortunately every time I try to run it, I get "segementation fault". What's wrong with this?
Code: [Select]
flybro$ nasm -f macho eatclib.asm
flybro$ gcc -m32 -mmacosx-version-min=10.5 -isysroot /Developer/SDKs/MacOSX10.5.sdk -o eatclib eatclib.o
flybro$ ./eatclib
flybro$ Segmentation fault

Code: [Select]
flybro$ gcc -v
Using built-in specs.
Target: i686-apple-darwin10
Configured with: /var/tmp/gcc/gcc-5646.1~2/src/configure --disable-checking --enable-werror --prefix=/usr --mandir=/share/man --enable-languages=c,objc,c++,obj-c++ --program-transform-name=/^[cg][^.-]*$/s/$/-4.2/ --with-slibdir=/usr/lib --build=i686-apple-darwin10 --with-gxx-include-dir=/include/c++/4.2.1 --program-prefix=i686-apple-darwin10- --host=x86_64-apple-darwin10 --target=i686-apple-darwin10
Thread model: posix
gcc version 4.2.1 (Apple Inc. build 5646) (dot 1)

Code: [Select]
SECTION .data

EatMsg            db "Eat at Joe's", 0

SECTION .text

GLOBAL _main
extern _puts

_main:
push ebp
mov ebp,esp
push ebx
push esi
push edi

push EatMsg
call _puts
add esp,4

pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret

Cheers,
Flybro

Offline Keith Kanios

  • Full Member
  • **
  • Posts: 383
  • Country: us
    • Personal Homepage
Re: Calling C-functions from assembly (in Mac OS X)
« Reply #1 on: February 20, 2010, 05:03:32 PM »
I have Snow Leopard (10.6) as well, and I get the same error.

I know that Mac can get cranky unless the stack is aligned to a 16-byte boundary, but adding that didn't help.

I've tried using YASM instead, with no luck, so it doesn't seem like a NASM bug at the moment.

It's even more awkward as the following 64-bit equivalent works:

Code: [Select]
;nasm -f macho64 -o eatclib.o eatclib.asm
;gcc -m64 -mmacosx-version-min=10.5 -isysroot /Developer/SDKs/MacOSX10.5.sdk -o eatclib eatclib.o
;./eatclib

SECTION .data

EatMsg            db "Eat at Joe's", 0

SECTION .text

GLOBAL _main
extern _puts

_main:
push rbp
mov rbp,rsp
push rbx

mov rbx,rsp
and spl,0xF0

mov rdi,EatMsg
call _puts

mov rsp,rbx

pop rbx
mov rsp,rbp
pop rbp
ret

I'll look into it further, later on today.
« Last Edit: February 20, 2010, 05:09:51 PM by Keith Kanios »

Offline Keith Kanios

  • Full Member
  • **
  • Posts: 383
  • Country: us
    • Personal Homepage
Re: Calling C-functions from assembly
« Reply #2 on: February 21, 2010, 07:03:13 AM »
Try the following replacement:

Code: [Select]
;nasm -f macho -o eatclib.o eatclib.asm
;ld -e _main -lc -framework ApplicationServices -o eatclib eatclib.o

SECTION .data

EatMsg            db "Eat at Joe's", 0

SECTION .text

global _main
extern _puts
extern _exit

_main:
push ebp
mov ebp,esp
push ebx

mov ebx,esp
and esp,0xFFFFFFF0
sub esp,12
push EatMsg
call _puts
mov esp,ebx

mov ebx,esp
and esp,0xFFFFFFF0
sub esp,12
push DWORD 0
call _exit
mov esp,ebx

pop ebx
mov esp,ebp
pop ebp
ret

For some reason, linking to ApplicationServices (as opposed to say -ldylib1.o) seems to placate the Bus Error issue.

The original Segmentation Fault problem you were incurring was due to a misaligned stack, as previously mentioned.

Offline Flybro

  • Jr. Member
  • *
  • Posts: 6
  • Country: au
Re: Calling C-functions from assembly
« Reply #3 on: February 21, 2010, 08:36:21 AM »
Thanks a lot but could you, please, give me a general rules for calling external functions? Probably I do not fully understood what you did because when I replace _puts with, for example, _TickCount I got, again, segmentation fault.

Cheers,
F.

Offline Keith Kanios

  • Full Member
  • **
  • Posts: 383
  • Country: us
    • Personal Homepage
Re: Calling C-functions from assembly
« Reply #4 on: February 21, 2010, 09:11:22 AM »
The following example grabs and displays the TickCount:

Code: [Select]
;nasm -f macho -o tick.o tick.asm
;ld -e _main -lc -framework ApplicationServices -o tick tick.o

;tick.asm
[section .data]
Ticks db 'Tick Count: %i',10,0

[SECTION .text]
global _main
extern _TickCount
extern _printf
extern _exit

_main:
push ebp
mov ebp,esp
push ebx

mov ebx,esp
and esp,0xFFFFFFF0
call _TickCount
mov esp,ebx

mov ebx,esp
and esp,0xFFFFFFF0
sub esp,8
push eax
push Ticks
call _printf
mov esp,ebx

mov ebx,esp
and esp,0xFFFFFFF0
sub esp,12
push DWORD 0
call _exit
mov esp,ebx

pop ebx
mov esp,ebp
pop ebp
ret

In the above code, you have three different examples of stack alignment prior to a function call.

The first one, TickCount, has no arguments and thus the initial 16-byte stack alignment (and esp,0xFFFFFFF0) is sufficient.

The second one, printf, takes at least one argument. In this case, %i in Tick indicates that a second argument should be on the stack. Each of these arguments is 4 bytes in size, totaling 8 bytes for this example. After the initial 16-byte stack alignment, we subtract 8 more bytes from esp prior to pushing the two 4-byte arguments, thus realigning the stack to a known safe 16-byte boundary prior to executing call.

The third one, exit, takes one argument. After the initial 16-byte stack alignment, we subtract 12 more bytes from esp prior to pushing the 4-byte exit code argument, thus realigning the stack to a known safe 16-byte boundary prior to executing call.

Here's one alternative way of looking at the above:

Code: [Select]
;nasm -f macho -o tick.o tick.asm
;ld -e _main -lc -framework ApplicationServices -o tick tick.o

;tick.asm
[section .data]
Ticks db 'Tick Count: %i',10,0

[SECTION .text]
global _main
extern _TickCount
extern _printf
extern _exit

_main:
push ebp
mov ebp,esp
push ebx

mov ebx,esp
and esp,0xFFFFFFF0
call _TickCount
mov esp,ebx

mov ebx,esp
and esp,0xFFFFFFF0
sub esp,16
mov DWORD[esp+4],eax
mov DWORD[esp],Ticks
call _printf
mov esp,ebx

mov ebx,esp
and esp,0xFFFFFFF0
sub esp,16
mov DWORD[esp],0
call _exit
mov esp,ebx

pop ebx
mov esp,ebp
pop ebp
ret

Notice we align the stack first to a known good 16-byte boundary and then simply use esp directly with mov instead of push.

Key Concept: Ensure the stack is aligned to a 16-byte boundary on Mac OS X immediately prior to executing a function call.

Offline Flybro

  • Jr. Member
  • *
  • Posts: 6
  • Country: au
Re: Calling C-functions from assembly
« Reply #5 on: February 21, 2010, 09:30:29 AM »
Beautiful, all is clear now. Thanks a lot Keith.

Have a good night,
F.