Clang 11和GCC 8 O2中断直插式装配

问题描述 投票:1回答:1

我有一小段代码,有些内联程序集可以在O0中正确打印argv [0],但不能在O2中打印任何内容(另一方面,使用Clang时,GCC将打印存储在envp中的字符串[打印argv [0]时为0]。此问题也仅限于argv(在启用或不启用优化的情况下,可以按预期使用其他两个函数参数)。我在GCC和Clang上都对此进行了测试,并且两个编译器都存在此问题。

这里是代码:

void exit(unsigned long long status) {
    asm volatile("movq $60, %%rax;" //system call 60 is exit
        "movq %0, %%rdi;" //return code 0
        "syscall"
        : //no outputs
        :"r"(status)
        :"rax", "rdi");
}

int open(const char *pathname, unsigned long long flags) {
    asm volatile("movq $2, %%rax;" //system call 2 is open
        "movq %0, %%rdi;"
        "movq %1, %%rsi;"
        "syscall"
        : //no outputs
        :"r"(pathname), "r"(flags)
        :"rax", "rdi", "rsi");
        return 1;
}

int write(unsigned long long fd, const void *buf, size_t count) {
    asm volatile("movq $1, %%rax;" //system call 1 is write
        "movq %0, %%rdi;"
        "movq %1, %%rsi;"
        "movq %2, %%rdx;"
        "syscall"
        : //no outputs
        :"r"(fd), "r"(buf), "r"(count)
        :"rax", "rdi", "rsi", "rdx");
        return 1;
}

static void entry(unsigned long long argc, char** argv, char** envp);

/*https://www.systutorials.com/x86-64-calling-convention-by-gcc/: "The calling convention of the System V AMD64 ABI is followed on GNU/Linux. The registers RDI, RSI, RDX, RCX, R8, and R9 are used for integer and memory address arguments
and XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6 and XMM7 are used for floating point arguments.
For system calls, R10 is used instead of RCX. Additional arguments are passed on the stack and the return value is stored in RAX."*/

//__attribute__((naked)) defines a pure-assembly function
__attribute__((naked)) void _start() {
    asm volatile("xor %%rbp,%%rbp;" //http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html: "%ebp,%ebp sets %ebp to zero. This is suggested by the ABI (Application Binary Interface specification), to mark the outermost frame."
    "pop %%rdi;" //rdi: arg1: argc -- can be popped off the stack because it is copied onto register
    "mov %%rsp, %%rsi;" //rsi: arg2: argv
    "mov %%rdi, %%rdx;"
    "shl $3, %%rdx;" //each argv pointer takes up 8 bytes (so multiply argc by 8)
    "add $8, %%rdx;" //add size of null word at end of argv-pointer array (8 bytes)
    "add %%rsp, %%rdx;" //rdx: arg3: envp
    "andq $-16, %%rsp;" //align stack to 16-bits (which is required on x86-64)
    "jmp %P0" //https://stackoverflow.com/questions/3467180/direct-c-function-call-using-gccs-inline-assembly: "After looking at the GCC source code, it's not exactly clear what the code P in front of a constraint means. But, among other things, it prevents GCC from putting a $ in front of constant values. Which is exactly what I need in this case."
    :
    :"i"(entry)
    :"rdi", "rsp", "rsi", "rdx", "rbp", "memory");
}

//Function cannot be optimized-away, since it is passed-in as an argument to asm-block above
//Compiler Options: -fno-asynchronous-unwind-tables;-O2;-Wall;-nostdlibinc;-nobuiltininc;-fno-builtin;-nostdlib; -nodefaultlibs;--no-standard-libraries;-nostartfiles;-nostdinc++
//Linker Options: -nostdlib; -nodefaultlibs
static void entry(unsigned long long argc, char** argv, char** envp) {
    int ttyfd = open("/dev/tty", O_WRONLY);

    write(ttyfd, argv[0], 9);
    write(ttyfd, "\n", 1);

    exit(0);
}

编辑:添加了syscall定义。编辑:将rcx和r11添加到syscall的clobber列表中,已解决了clang的问题,但gcc出现了错误。

c linux assembly x86-64 inline-assembly
1个回答
1
投票
  1. 根据naked,您不能在the gcc manual功能中使用扩展的asm,只能使用基本的asm。您无需通知编译器有关寄存器的问题(因为它无论如何不会做任何事情;在naked函数中,您负责所有寄存器的管理)。并且不需要在扩展操作数中传递entry的地址。只需执行jmp entry

    ((在我的测试中,您的代码根本无法编译,因此我认为您没有向我们展示您的确切代码-下次请这样做,以免浪费时间。)]] >>

  2. Linux x86-64 syscall系统调用被允许破坏rcxr11寄存器,因此您需要将它们添加到系统调用的破坏者列表中。

  3. 您将堆栈对齐到16个字节的边界,然后跳转到entry。但是,16字节对齐规则基于以下假设:您将使用call调用该函数,这会将另外8个字节压入堆栈。因此,被调用函数实际上期望堆栈最初不是16的倍数,而是大于或小于16的8倍。因此,您实际上是错误地对齐了堆栈,这可能是各种原因造成的。神秘的麻烦。

    因此,将jmp替换为call,或者从rsp中减去另外8个字节(或者只是push您选择的某些64位寄存器)。

© www.soinside.com 2019 - 2024. All rights reserved.