6

I'm learning assembly language programming on FreeBSD. I'm using FreeBSD 9.0 i386 release and nasm assembler.

When I wrote a simple syscall function, I found that I had to push a useless value into the stack to make the code run correctly.

For example:

; File:test.asm
section .text
  global _start
_start:
  xor  eax,eax
  ; Argument of exit()
  push 0x0
  ; Syscall of exit()
  mov  al,1
  int  0x80

I used the following command to assemble and link the above code:

%nasm -f elf test.asm -o test.o
%ld test.o -o test.bin

I used ktrace to inspect the program and found:

%ktrace ./test.bin
%kdump -d -f ./ktrace.out 
2059 ktrace   RET   ktrace 0
2059 ktrace   CALL  execve(-1077940941,-1077941260,-1077941252)
2059 ktrace   NAMI  "./test.bin"
2059 test.bin RET   execve 0
2059 test.bin CALL  exit(1)

So the code didn't run correctly, cause I provided 0 as the only argument of exit() but the program actually run exit(1).

Then I changed my code.

; File:test.asm
section .text
  global _start
_start:
  xor  eax,eax
  push 0x0
  ; Whatever digits,0x1,0x2...0xFFFFFFFF, ect.
  push 0xFFFFFFFF
  mov  al,1
  int  0x80

Then the code was executed correctly.

At first, I though it was because of something like "stack padding" or "stack alignment", like Stack allocation, padding, and alignment. So it might respect 16-bit alignment. But I found it not. For example, This following code:

; File:test.asm
section .text
  global _start
_start:
  xor  eax,eax
  push 0x0
  ; Actual argument of exit()
  push 0x3
  push 0xFFFFFFFF
  ; Syscall of exit()
  mov  al,1
  int  0x80

actually executed exit(3). It seemed that it didn't align bytes. I debug the above code with gdb, when the last line was about to be executed, the stack was something like this:

0xFFFFFFFF  -> esp
0x00000003
0x00000000

So here's my question: why there's always a useless argument or is there a method to work around?

Community
  • 1
  • 1
Lion
  • 965
  • 10
  • 21

1 Answers1

7

It's a dummy argument to increase performance slightly by preventing a call/ret instruction pair.

See $2.1 in the below link:

http://www.int80h.org/bsdasm/#default-calling-convention

Josh Greifer
  • 3,151
  • 24
  • 25
  • This link is very useful. Thanks! – Lion Jul 11 '12 at 13:42
  • 1
    I read that, but I don't get how using an extra argument eliminates `call`/`ret`. – sharptooth Jul 12 '12 at 12:08
  • 1
    The answer given in http://unix.derkeiler.com/Mailing-Lists/FreeBSD/hackers/2003-08/0081.html might help. trap.c in the kernel actually offsets the parameter start on the stack by sizeof(int), assuming that the last pushed value is a return address. – Josh Greifer Jul 12 '12 at 12:23
  • So a better description would be that it makes libc system call wrappers more efficient. i.e. the stack args to the wrapper are already in the right place to be args to the system call. So it's not a call/ret that's being prevented, it's a `pop ecx` / `push ecx`, or copying all the args again to the wrapper's stack frame. – Peter Cordes Apr 11 '18 at 02:58