裸露功能的Asm插入

时间:2018-11-29 08:26:53

标签: c++ gcc assembly x86-64 calling-convention

我有ubuntu 16.04,x86_64 arch,4.15.0-39通用内核版本。 GCC 8.1.0

我试图将此功能(从第一篇https://groups.google.com/forum/#!topic/comp.lang.c++.moderated/qHDCU73cEFc)从英特尔方言改写为AT&T。而且我没有成功。

namespace atomic {
  __declspec(naked)
  static void*
  ldptr_acq(void* volatile*) {
    _asm {
      MOV EAX, [ESP + 4]
      MOV EAX, [EAX]
      RET
    }
  }

  __declspec(naked)
  static void*
  stptr_rel(void* volatile*, void* const) {
    _asm {
      MOV ECX, [ESP + 4]
      MOV EAX, [ESP + 8]
      MOV [ECX], EAX
      RET
    }
  }
}

然后,我编写了一个简单程序,以获取相同的指针,并将其传递到内部。我安装了具有支持的裸属性(https://gcc.gnu.org/gcc-8/changes.html“ x86端口现在支持裸函数属性”)的GCC 8.1,以实现功能。 据我所记得,该属性告诉编译器不要创建函数的序言和结尾,并且我可以自己从堆栈中获取参数并返回它们。 代码:(不适用于segfault)

#include <cstdio>
#include <cstdlib>

  __attribute__ ((naked))
  int *get_num(int*) {
    __asm__  (
      "movl 4(%esp), %eax\n\t"
      "movl (%eax), %eax\n\t"
      "ret"
    );
  }

int main() {
    int *i =(int*) malloc(sizeof(int));
    *i = 5;

    int *j = get_num(i);
    printf("%d\n", *j);

    free(i);
    return 0;
}

然后我尝试使用64位寄存器:(不适用于segfault)

__asm__  (
  "movq 4(%rsp), %rax\n\t"
  "movq (%rax), %rax\n\t"
  "ret"
);

只有在我将值从rdi寄存器中取出后,它才起作用。

__asm__  (
  "movq %rdi, %rax\n\t"
  "ret"
);

为什么我无法通过堆栈寄存器进行传输?我可能弄错了。请告诉我我的失败在哪里?

1 个答案:

答案 0 :(得分:1)

因为x86-64 System V调用约定与旧的低效i386 System V调用约定不同,因此将args传递到寄存器中而不是堆栈中。

如果您要用asm编写整个函数,例如使用naked函数或独立的.S文件,则必须始终编写与调用约定匹配的asm。

GNU C扩展的asm允许您使用操作数来指定asm语句的输入,并且编译器将生成指令以实现此目的。 (不过,除非您了解asm以及编译器如何在启用优化的情况下将C转换为asm,否则我不建议您使用它。)


还要注意,movq %rdi, %rax实现的是long *foo(long*p){return p;}而不是return *p。也许您是说mov (%rdi), %rax取消对指针arg的引用?


顺便说一句,您绝对不需要,也不应该为此使用嵌入式asm。 https://gcc.gnu.org/wiki/DontUseInlineAsm,请参阅https://stackoverflow.com/tags/inline-assembly/info

在GNU C中,您可以将指针转换为volatile uint64_t*。或者,您可以使用__atomic_load_n (ptr, __ATOMIC_ACQUIRE)来获取从该asm中获得的所有内容,而无需进行函数调用的开销或在调用程序中花费所有优化调用程序的寄存器来解决优化程序的开销

您可以在任何对象上使用它们:https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html与C ++ 11不同,在C ++ 11中,您只能在std::atomic<T>上执行原子操作。