Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I read arguments in _start function?

I am building simple application without glibc in linux 64bit. But I don't know how to get arguments.

I googled and I found that RDI is argc, RSI is argv. But it didn't worked.

I saw registers when _start function starts using gdb, but both RDI and RSI was 0x0. I also tested with simplest assembly application, but result was same. RDI and RSI was 0x0. I believe argc shouldn't be 0x0 even if I passed no arguments to program.

_start:
jmp $

Here's C code what I tried:

//Print Hello world WITHOUT standard library
//ONLY WITH SYSTEM CALL

#define ReadRdi(To) asm("movq %%rdi,%0" : "=r"(To));
#define ReadRsi(To) asm("movq %%rsi,%0" : "=r"(To));

void __Print(const char *str);
void __Exit();

void Test(const char *a){
    long aL;
    ReadRdi(aL);
    char *aC = (int) aL;
    __Print("Test argument: ");
    __Print(aC);
    __Print("\n");
}

void _start() {
    long argcL;
    long argvL;
    ReadRdi(argcL);
    ReadRsi(argvL);
    int argc = (int) argcL;
    char **argv = (char **) argvL;
    __Print("Arguments: ");
    for(int i = 0; i < argc; i ++) {
        __Print(argv[i]);
        __Print(", ");
    }
    __Print("\n");
    Test("Hello, world!");
    __Exit();
}

The result is:

Arguments:
Test argument: Hello, world!

I checked stack(Memory value in the RBP~RSP) using gdb, but it seemed there's nothing.

I tried changing

void _start() {
    long argcL;
    long argvL;
    ReadRdi(argcL);
    ReadRsi(argvL);
    int argc = (int) argcL;
    char **argv = (char **) argvL;
    __Print("Arguments: ");

to

void _start(int argc, char **argv) {
    __Print("Arguments: ");

but still I can't see any arguments output.

__Print prints message(using sys_write system call), __Exit exits program(using sys_exit system call).

Print.asm:

section     .text
global      __Print
__Print:
    mov rdx, 0
    push rdi
    jmp .count
.count:
    add rdx, 1
    add rdi, 1
    cmp byte[rdi], 0
    jne .count
.print:
    pop rdi
    mov     rcx, rdi                             ;message to write
    mov     rbx, 1                               ;file descriptor (stdout)
    mov     rax, 4                               ;system call number (sys_write)
    int     0x80                                ;call kernel
    ret

Exit.asm:

section     .text
global      __Exit
__Exit:
    mov rax,1                               ;system call number (sys_exit)
    int 0x80

ADD: I linked with this command:

gcc -o sysHello Exit.o Print.o sysHello.o -nostdlib -nodefaultlibs -g
like image 622
Gippeumi Avatar asked Sep 08 '25 15:09

Gippeumi


1 Answers

The easiest thing will be to write _start in asm, and have it call your C functions using the standard calling convention.

See the x86 wiki for links to the ABI doc that describes where to find everything at process startup. (Or use the link in Ian's comment).

Writing _start as a C function would require inline asm, because there's no standard way to tell the compiler that argc is where the return address normally goes. So it's easier just to write it directly in asm and have it call main after putting args in registers for the normal calling convention.

A fragment of an asm program I use for testing things with perf counters:

cmp dword [rsp], 2
jg  addrmode_lat_3comp  ; argc > 2  ; $(seq 2)
jge addrmode_lat_1comp  ; argc >= 2 ; $(seq 1)
jmp loadlat_1comp       ; argc < 2  ; $(seq 0)

It jumps to one of three loops, depending on whether I run it with 2, 1, or no args, by testing argc. It uses NASM syntax.

like image 147
Peter Cordes Avatar answered Sep 10 '25 05:09

Peter Cordes