FreeBSD/amd64 assembly - how to read "ARGC" from _start?
Asked Answered
S

2

6

Sometimes the correct argc value is returned, sometimes 0, and sometimes (seemingly)random numbers... all from the same executable.

.section .text
.global _start
_start:
    movq    $1, %rax
    popq    %rdi
    syscall

For example:

%as -o this.o this.s ; ld -o this this.o

%./this; echo $?
1

%./this 1; echo $?
0

%./this 1 2; echo $?
3

%./this 1 2 a; echo $?
4

%./this 1 2 a f; echo $?
0

%_

I'm kind of new to assembly, but I was pretty confident that getting the argument count was as easy as popping it off the stack like in Linux, where the System V ABI documents that RSP points at argc in a freshly execve'd process.

Am I doing something wrong, or is this just really messed up?

Seamark answered 18/11, 2011 at 4:21 Comment(2)
Try running it in a debugger to see if the stack layout matches your expectations.Grenade
Thanks, I've been gdb'ing it for a little while and usually 8(%esp) has the argc, but sometimes not, I guess I'll keep workin' at it! :)Seamark
H
5

I was confused in the same case on FreeBSD 9.0/amd64. What I did is (I used nasm for assembler):

$ cat foo.asm
global _start
_start:
        mov     rax, 4          ; write
        mov     rdi, 1          ; stdout
        mov     rsi, rsp        ; address
        mov     rdx, 16         ; 16bytes
        syscall

        mov     rax, 1          ; exit
        syscall
$ nasm -f elf64 foo.asm && ld -o foo foo.o
$ ./foo | hd
00000000  00 00 00 00 00 00 00 00  01 00 00 00 00 00 00 00  |................|
00000010
$ ./foo 2 | hd
00000000  02 00 00 00 00 00 00 00  b8 dc ff ff ff 7f 00 00  |................|
00000010
$ ./foo 2 3 | hd
00000000  00 00 00 00 00 00 00 00  03 00 00 00 00 00 00 00  |................|
00000010
$ ./foo 2 3 4 | hd
00000000  00 00 00 00 00 00 00 00  04 00 00 00 00 00 00 00  |................|
00000010
$ ./foo 2 3 4 5 | hd
00000000  05 00 00 00 00 00 00 00  b0 dc ff ff ff 7f 00 00  |................|
00000010

I expected that argc was at rsp, but it was not.

I guessed that the kernel (the image activator) sets registers. I searched the source tree, I found the following code in /usr/src/sys/amd64/amd64/machdep.c (exec_setregs).

        regs->tf_rsp = ((stack - 8) & ~0xFul) + 8;
        regs->tf_rdi = stack;           /* argv */

EDIT: machdep.c has been split up and this function can now be found in exec_machdep.c

These lines look saying that rsp is aligned, actual data are at rdi. I changed my code, and I got expected results.

$ cat foo.asm
global _start
_start:
        push    rdi
        mov     rax, 4          ; write
        mov     rdi, 1          ; stdout
        pop     rsi
        mov     rdx, 16         ; 16bytes
        syscall

        mov     rax, 1          ; exit
        syscall
$ nasm -f elf64 foo.asm && ld -o foo foo.o
$ ./foo | hd
00000000  01 00 00 00 00 00 00 00  b0 dc ff ff ff 7f 00 00  |................|
00000010
$ ./foo 2 | hd
00000000  02 00 00 00 00 00 00 00  a8 dc ff ff ff 7f 00 00  |................|
00000010
$ ./foo 2 3 | hd
00000000  03 00 00 00 00 00 00 00  a8 dc ff ff ff 7f 00 00  |................|
00000010
$ ./foo 2 3 4 | hd
00000000  04 00 00 00 00 00 00 00  a8 dc ff ff ff 7f 00 00  |................|
00000010
$ ./foo 2 3 4 5 | hd
00000000  05 00 00 00 00 00 00 00  a8 dc ff ff ff 7f 00 00  |................|
00000010

Can you try rdi?

Harmonyharmotome answered 14/9, 2012 at 7:38 Comment(0)
S
0

For FreeBSD's standard calling convention, you want:

movl %edi, %eax

The shortest complete program whose return state will be equal to the number of parameters it is passed is:

movl    %edi, %eax
ret
Symposium answered 18/11, 2011 at 4:38 Comment(4)
hmmm... that seg faulted on me. I'm using 64 bit not 32, so I don't know whether or not that makes a difference.Seamark
This would be for a main function, which is called by and returns to _start. He is trying to write a _start function, which receives its parameters on the stack and cannot return.Reckless
Thanks, it works fine in a main function, but not with start. I've reread the amd64 abi and tried debugging a couple times (though I'm not experienced using a debugger). If anyone can assist, I would greatly appreciate. Thanks! :)Seamark
ret will segfault in _start. The code in the question confirms that RSP points to a small integer, not a return address. (But only sometimes is that small integer actually argc on FreeBSD.) The process startup ABI is different from the function-calling convention: _start is not a function.Yoghurt

© 2022 - 2024 — McMap. All rights reserved.