10

This description is valid for Linux 32 bit: When a Linux program begins, all pointers to command-line arguments are stored on the stack. The number of arguments is stored at 0(%ebp), the name of the program is stored at 4(%ebp), and the arguments are stored from 8(%ebp).

I need the same information for 64 bit.

Edit: I have working code sample which shows how to use argc, argv[0] and argv[1]: http://cubbi.com/fibonacci/asm.html

.globl _start
_start:
    popq    %rcx        # this is argc, must be 2 for one argument
    cmpq    $2,%rcx
    jne     usage_exit
    addq    $8,%rsp     # skip argv[0]
    popq    %rsi        # get argv[1]
    call ...
...
}

It looks like parameters are on the stack. Since this code is not clear, I ask this question. My guess that I can keep rsp in rbp, and then access these parameters using 0(%rbp), 8(%rbp), 16(%rbp) etc. It this correct?

Alex F
  • 42,307
  • 41
  • 144
  • 212

3 Answers3

20

Despite the accepted answer being more than sufficient, I would like to give an explicit answer, as there are some other answers which might confuse.

Most important (for more information see examples below): in x86-64 the command line arguments are passed via stack:

(%rsp) -> number of arguments
8(%rsp) -> address of the name of the executable
16(%rsp) -> address of the first command line argument (if exists)
... so on ...

It is different from the function parameter passing in x86-64, which uses %rdi, %rsi and so on.

One more thing: one should not deduce the behavior from reverse engineering of the C main-function. C runtime provides the entry point _start, wraps the command line arguments and calls main as a common function. To see it, let's consider the following example.

No C runtime/GCC with -nostdlib

Let's check this simple x86-64 assembler program, which do nothing but returns 42:

.section .text
.globl _start
_start:   
    movq $60, %rax #60 -> exit
    movq $42, %rdi #return 42
    syscall #run kernel 

We build it with:

as --64 exit64.s -o exit64.o
ld -m elf_x86_64 exit64.o -o exit64

or with

gcc -nostdlib exit64.s -o exit64

run in gdb with

./exit64 first second third

and stop at the breakpoint at _start. Let's check the registers:

(gdb) info registers
...
rsi            0x0  0
rdi            0x0  0
...

Nothing there. What about the stack?

(gdb) x/5g $sp
0x7fffffffde40: 4   140737488347650
0x7fffffffde50: 140737488347711 140737488347717
0x7fffffffde60: 140737488347724

So the first element on the stack is 4 - the expected argc. The next 4 values look a lot like pointers. Let's look at the second pointer:

(gdb) print (char[5])*(140737488347711)
$1 = "first"

As expected it is the first command line argument.

So there is experimental evidence, that the command line arguments are passed via stack in x86-64. However only by reading the ABI (as the accepted answer suggested) we can be sure, that this is really the case.

With C runtime

We have to change the program slightly, renaming _start into main, because the entry point _start is provided by the C runtime.

.section .text
.globl main
main:   
    movq $60, %rax #60 -> exit
    movq $42, %rdi #return 42
    syscall #run kernel 

We build it with (C runtime is used per default):

gcc exit64gcc.s -o exit64gcc

run in gdb with

./exit64gcc first second third

and stop at the breakpoint at main. What is at the stack?

(gdb) x/5g $sp
0x7fffffffdd58: 0x00007ffff7a36f45  0x0000000000000000
0x7fffffffdd68: 0x00007fffffffde38  0x0000000400000000
0x7fffffffdd78: 0x00000000004004ed

It does not look familiar. And registers?

(gdb) info registers
...
rsi            0x7fffffffde38   140737488346680
rdi            0x4  4
...

We can see that rdi contains the argc value. But if we now inspect the pointer in rsi strange things happen:

(gdb) print (char[5])*($rsi)
$1 =  "\211\307???"

But wait, the second argument of the main function in C is not char *, but char ** also:

(gdb) print (unsigned long long [4])*($rsi)
$8 = {140737488347644, 140737488347708, 140737488347714, 140737488347721}
(gdb) print (char[5])*(140737488347708)
$9 = "first"

And now we found our arguments, which are passed via registers as it would be for a normal function in x86-64.

Conclusion: As we can see, the is a difference concerning passing of command line arguments between code using C runtime and code which doesn't.

ead
  • 32,758
  • 6
  • 90
  • 153
  • 1
    You can build a program that defines `_start` [using `gcc -nostdlib`](http://stackoverflow.com/questions/36861903/assembling-32-bit-binaries-on-a-64-bit-system-gnu-toolchain/36901649#36901649). If you only define `main`, not `_start`, you have to use gcc, but defining `_start` doesn't stop you from using gcc. – Peter Cordes Jul 01 '16 at 23:10
  • 1
    Looking at registers / memory is a good way to figure out how something *might* work, i.e. to get something to search on to find official docs. You shouldn't just assume that anything you find is guaranteed to be there, though. You don't want to depend on something that's actually just a leftover scratch copy, and might not be in a future version. Your conclusions are correct, but it would be much better to go on to reference how the SysV ABI guarantees that those values will be there. – Peter Cordes Jul 01 '16 at 23:13
  • You also don't mention the glibc CRT code which provides `_start` and calls your `main`. There's no magical difference between defining `main` or `_start`; it's not hard to understand what the startup boilerplate code does to collect the process-startup state and call `main` with those function args. – Peter Cordes Jul 01 '16 at 23:15
  • @Peter Cordes Thank a lot for the remarks, tried to consider them in my edit. Any further comments are highly appreciated! – ead Jul 02 '16 at 06:53
  • Those changes are exactly what I had in mind. It still reads more as a gdb tutorial, than just an answer, but if that's what you wanted to write and were looking for somewhere to put it, that's cool. You included the stack layout for a newly-`exec`ed process up front, before diving into gdb to show it by example, and that's essential. It makes the answer skim-able, so people that don't need the explanation can just find the good part. And people that do want the explanation will know where it's leading, which makes it easier to follow. – Peter Cordes Jul 02 '16 at 11:08
9

It looks like section 3.4 Process Initialization, and specifically figure 3.9, in the already mentioned System V AMD64 ABI describes precisely what you want to know.

modrobert
  • 19
  • 6
rkhayrov
  • 10,040
  • 2
  • 35
  • 40
  • 12
    Link only answers are fragile. That page doesn't work for me. *www.x86-64.org didn’t send any data. ERR_EMPTY_RESPONSE* – doug65536 Oct 30 '17 at 10:26
  • That document can be found on the internet archive's wayback machine as https://web.archive.org/web/20160706074221/http://www.x86-64.org/documentation/abi.pdf – Daniel Martin Nov 21 '22 at 17:28
0

I do believe what you need to do is check out the x86-64 ABI. Specifically, I think you need to look at section 3.2.3 Parameter Passing.

Dean Pucsek
  • 257
  • 2
  • 7
  • 2
    This describes calling convention inside of the program. Process command line parameters may have their own rules. I need this specific information for Linux 64. – Alex F Sep 10 '10 at 08:37