如何在_start函数中读取参数?

时间:2016-02-11 10:47:14

标签: c linux assembly 64-bit

我正在linux 64bit中构建没有glibc的简单应用程序。 但我不知道如何得到论据。

我用Google搜索,我发现RDI是argc,RSI是argv。但它没有奏效。

当_start函数使用gdb启动时我看到了寄存器,但RDI和RSI都是0x0。 我也用最简单的装配应用进行了测试,但结果是一样的。 RDI和RSI是0x0。我相信argc不应该是0x0,即使我没有传递任何参数来编程。

_start:
jmp $

这是我尝试过的C代码:

//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();
}

结果是:

Arguments:
Test argument: Hello, world!

我使用gdb检查了堆栈(RBP~RSP中的内存值),但似乎什么都没有。

我尝试过更改

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

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

但我仍然看不到任何参数输出。

__ Print打印消息(使用sys_write系统调用),__ Exit退出程序(使用sys_exit系统调用)。

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:我用这个命令链接:

gcc -o sysHello Exit.o Print.o sysHello.o -nostdlib -nodefaultlibs -g

1 个答案:

答案 0 :(得分:1)

最简单的方法是在asm中编写_start,然后使用标准调用约定调用C函数。

请参阅 wiki以获取ABI文档的链接,该文档描述了在流程启动时查找所有内容的位置。 (或者使用Ian评论中的链接)。

_start写为C函数需要内联asm,因为没有标准方法告诉编译器argc是返回地址通常所在的位置。所以直接在asm中编写它并且在将args放入寄存器中以获得正常的调用约定之后就更容易了{.1}}。

我用于使用perf计数器测试事物的asm程序的片段:

call main

通过测试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) ,它会跳转到三个循环中的一个,具体取决于我是以2,1还是没有args运行它。它使用NASM语法。