裸函数中的 Asm 插入
Asm inserion in naked function
我有 ubuntu 16.04,x86_64 arch,4.15.0-39-generic 内核版本。
海湾合作委员会 8.1.0
我试图将这个函数(从第一个 post 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
}
}
}
然后我写了一个简单的程序,来获取我传递给内部的相同指针。我安装了 GCC 8.1 版,支持裸属性(https://gcc.gnu.org/gcc-8/changes.html "The x86 port now supports the naked function attribute")以实现功能。
据我所知,这个属性告诉编译器不要创建函数的序言和结尾,我可以自己从堆栈中获取参数并 return 它们。
代码:(不适用于段错误)
#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?
顺便说一句,你绝对不需要也不应该为此使用内联 asm。 https://gcc.gnu.org/wiki/DontUseInlineAsm, and see https://whosebug.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>
.
执行原子操作
我有 ubuntu 16.04,x86_64 arch,4.15.0-39-generic 内核版本。 海湾合作委员会 8.1.0
我试图将这个函数(从第一个 post 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
}
}
}
然后我写了一个简单的程序,来获取我传递给内部的相同指针。我安装了 GCC 8.1 版,支持裸属性(https://gcc.gnu.org/gcc-8/changes.html "The x86 port now supports the naked function attribute")以实现功能。 据我所知,这个属性告诉编译器不要创建函数的序言和结尾,我可以自己从堆栈中获取参数并 return 它们。 代码:(不适用于段错误)
#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?
顺便说一句,你绝对不需要也不应该为此使用内联 asm。 https://gcc.gnu.org/wiki/DontUseInlineAsm, and see https://whosebug.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>
.