c - 如何读取 _start 函数中的参数?

标签 c linux assembly 64-bit

我正在 64 位 Linux 中构建没有 glibc 的简单应用程序。 但我不知道如何获取参数。

我用谷歌搜索,发现 RDI 是 argc,RSI 是 argv。但它没有用。

当 _start 函数开始使用 gdb 时,我看到了寄存器,但 RDI 和 RSI 都是 0x0。 我还测试了最简单的 assembly 应用程序,但结果是一样的。 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系统调用)。

打印.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

退出.asm:

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

添加:我用这个命令链接:

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

最佳答案

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

参见 wiki 获取 ABI 文档的链接,该文档描述了在流程启动时在哪里可以找到所有内容。 (或使用 Ian 评论中的链接)。

_start 编写为 C 函数需要内联汇编,因为没有标准方法告诉编译器 argc 是返回地址通常所在的位置。因此,直接用 asm 编写它并在将 args 放入寄存器以实现正常调用约定后让它调用 main 会更容易。

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

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)

通过测试 argc,它会跳转到三个循环之一,具体取决于我是使用 2、1 还是不使用 args 运行它。它使用 NASM 语法。

关于c - 如何读取 _start 函数中的参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35337196/

相关文章:

javascript - 将 Node Chat 服务器与 ChucK 链接以获得声音

c - 修改 C 字符串常量?

linux - 隐藏 bash 函数内部

memory - x86 push/pop 可以小于 4 个字节吗?

C++/Win32 - 如何迭代特定进程的线程列表并将起始地址解析为模块?

c - 将指针传递给在 C 中创建(随机大小)填充随机整数的数组的函数

linux - 知道目录是否为空的权限

linux - 在 Linux 设备驱动程序中使用 GPL 许可证的重要性

Linux 上的 C 内联汇编,将字符串从堆栈写入标准输出

c - 在 C 源代码中省略内联 ASM 代码