我有 ubuntu 16.04、x86_64 arch、4.15.0-39-generic 内核版本。 海湾合作委员会8.1.0
我尝试将这个函数(从第一篇文章 https://groups.google.com/forum/#!topic/comp.lang.c++.moderated/qHDCU73cEFc 开始)从 Intel 方言重写为 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
}
}
}
然后我编写了一个简单的程序,以获得相同的指针,并将其传递到内部。我安装了 GCC 版本 8.1,并支持函数的裸属性(https://gcc.gnu.org/gcc-8/changes.html“x86 端口现在支持裸函数属性”)。 据我记得,这个属性告诉编译器不要创建函数的序言和结尾,我可以自己从堆栈中获取参数并返回它们。 代码:(不适用于段错误)
#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 位寄存器:(不适用于段错误)
__asm__ (
"movq 4(%rsp), %rax\n\t"
"movq (%rax), %rax\n\t"
"ret"
);
只有在我从 rdi 寄存器中取出值后,一切才起作用。
__asm__ (
"movq %rdi, %rax\n\t"
"ret"
);
为什么我无法通过堆栈寄存器进行传输?我可能犯了一个错误。请告诉我我的失败在哪里?
最佳答案
因为 x86-64 System V 调用约定在寄存器中传递参数,而不是在堆栈上,这与旧的低效 i386 System V 调用约定不同。
如果您在 asm 中编写整个函数,则始终必须编写与调用约定相匹配的 asm,例如 naked
功能或独立.S
文件。
GNU C 扩展 asm 允许您使用操作数来指定 asm 语句的输入,编译器将生成指令来实现这一点。 (不过,在您了解 asm 以及编译器如何在启用优化的情况下将 C 转换为 asm 之前,我不建议您使用它。)
另请注意 movq %rdi, %rax
实现long *foo(long*p){return p;}
不是return *p
。也许你的意思是 mov (%rdi), %rax
取消引用指针arg?
顺便说一句,您绝对不需要也不应该为此使用内联汇编。 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 不同,您只能在 std::atomic<T>
上执行原子操作.
关于c++ - 在裸函数中插入 Asm,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53534649/