我正在编写一个加密程序,并且核心(一个宽的乘法例程)是用x86-64汇编编写的,两者都是为了速度而且因为它广泛使用adc那些不容易从C中访问的指令.我不想内联这个函数,因为它很大,并且在内循环中被调用了好几次.
理想情况下,我还想为此函数定义一个自定义调用约定,因为它在内部使用所有寄存器(除外rsp),不破坏其参数,并在寄存器中返回.现在,它适应了C调用约定,但当然这使它变慢(大约10%).
为了避免这种情况,我可以调用它,asm("call %Pn" : ... : my_function... : "cc", all the registers);但有没有办法告诉GCC调用指令与堆栈混淆?否则GCC会将所有这些寄存器放在红色区域中,而顶部的寄存器将被破坏.我可以使用-mno-red-zone编译整个模块,但是我更喜欢告诉GCC,比方说,红色区域的前8个字节将被破坏,以便它不会放任何东西.
来自维基百科:
在计算中,红色区域是函数堆栈帧中超出返回地址的固定大小区域,该区域不被该函数保留.被调用函数可以使用红色区域来存储局部变量,而无需修改堆栈指针的额外开销.中断/异常/信号处理程序不会修改此内存区域.System V使用的x86-64 ABI要求一个128字节的红色区域,它直接在返回地址之后开始并包含函数的参数.OpenRISC工具链假设一个128字节的红色区域.
超出%rsp指向的位置的128字节区域被认为是保留的,不应被信号或中断处理程序修改.因此,函数可以将此区域用于函数调用不需要的临时数据.特别是,叶子函数可以将这个区域用于它们的整个堆栈帧,而不是调整序言和尾声中的堆栈指针.这个区域被称为红区.
鉴于这两个引号,堆叠的返回地址上方或堆叠的返回地址下方的红色区域 是?
由于这个红色区域是相对的RSP,它是否向下push移动并且每个区域向上移动pop?
强烈建议在创建64位内核(对于x86_64平台)时,指示编译器不要使用用户空间ABI所执行的128字节红区.(对于GCC,编译器标志是-mno-red-zone).
如果启用了内核,则内核不会是中断安全的.
但那是为什么呢?
我试图弄清楚在Java中可能导致此错误的原因:
Invalid access of stack red zone 0x115ee0ed0 rip=0x114973900
Run Code Online (Sandbox Code Playgroud)
有没有人遇到过这个错误信息?它实际上是在杀死JVM,一切都停在那里.
我目前正在使用这个版本的Java :(在OS X 10.6上)
java version "1.6.0_15"
Java(TM) SE Runtime Environment (build 1.6.0_15-b03-219)
Java HotSpot(TM) 64-Bit Server VM (build 14.1-b02-90, mixed mode)
Run Code Online (Sandbox Code Playgroud)
我正在寻找的是关于如何避免再次发生这种情况的某种解释和提示.
提前致谢!
我希望能够%rbp在内联asm中使用基指针寄存器().这样的玩具示例是这样的:
void Foo(int &x)
{
asm volatile ("pushq %%rbp;" // 'prologue'
"movq %%rsp, %%rbp;" // 'prologue'
"subq $12, %%rsp;" // make room
"movl $5, -12(%%rbp);" // some asm instruction
"movq %%rbp, %%rsp;" // 'epilogue'
"popq %%rbp;" // 'epilogue'
: : : );
x = 5;
}
int main()
{
int x;
Foo(x);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我希望,因为我使用通常的序幕/结尾函数调用方法来推送和弹出旧的%rbp,这样就可以了.但是,当我尝试在内x联asm之后访问时,它会出现故障.
GCC生成的汇编代码(略微剥离)是:
_Foo:
pushq %rbp
movq %rsp, %rbp
movq %rdi, -8(%rbp)
# INLINEASM
pushq %rbp; // prologue
movq %rsp, …Run Code Online (Sandbox Code Playgroud) 真奇怪的gcc怪癖.看一下这个:
main() { int a[100]; a[0]=1; }
Run Code Online (Sandbox Code Playgroud)
产生这个组件:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 81 ec 18 01 00 00 sub $0x118,%rsp
b: c7 85 70 fe ff ff 01 movl $0x1,-0x190(%rbp)
12: 00 00 00
15: c9 leaveq
16: c3 retq
Run Code Online (Sandbox Code Playgroud)
堆栈的顶部显然是400,因为它是一个100*4阵列.因此,当它写入第一个条目时,它会执行rbp - 400(行'b').好.但是为什么它从堆栈(第4行)指针中减去280?那不指向数组的中间吗?
如果我们之后添加一个函数调用,gcc会做正确的事情:
b() {}
main() { int a[100]; a[0]=1; b(); }
Run Code Online (Sandbox Code Playgroud)
产生这个组件:
0000000000000000 <b>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: c9 leaveq
5: c3 retq …Run Code Online (Sandbox Code Playgroud) 考虑这样的内联汇编:
uint64_t flags;
asm ("pushf\n\tpop %0" : "=rm"(flags) : : /* ??? */);
Run Code Online (Sandbox Code Playgroud)
尽管可能存在某种内在函数来获取 RFLAGS 的内容,但我如何向编译器表明我的内联汇编破坏了堆栈顶部的一个四字内存?
我有以下疑惑:
正如我们所知,System V x86-64 ABI为我们提供了堆栈帧中固定大小的区域(128字节),即所谓的redzone.因此,结果我们不需要使用,例如,sub rsp, 12.只是做mov [rsp-12], X,这就是全部.
但我无法理解这一点.为什么这有关系?sub rsp, 12没有redzone 是否有必要?毕竟,堆栈大小在开始时是有限的,为什么sub rsp, 12重要?我知道这使得我们可以跟随堆栈的顶部但是让我们忽略它.
我知道一些指令使用了什么rsp价值(比如ret)但在那一刻并不关心它.
问题的症结在于:我们没有红区,我们已经完成了:
function:
mov [rsp-16], rcx
mov [rsp-32], rcx
mov [rsp-128], rcx
mov [rsp-1024], rcx
ret
Run Code Online (Sandbox Code Playgroud)
它有区别吗?
function:
sub rsp, 1024
mov [rsp-16], rcx
mov [rsp-32], rcx
mov [rsp-128], rcx
mov [rsp-1024], rcx
add rsp, 1024
ret
Run Code Online (Sandbox Code Playgroud) 在x86汇编语言中:
我假设我有一个正常的功能序言,请阅读
push ebp
mov ebp,esp
Run Code Online (Sandbox Code Playgroud)
我知道我可以通过访问内存目标操作数来读取或写入寄存器,假设我想要第一个参数。我会做
mov eax,[ebp +8]
Run Code Online (Sandbox Code Playgroud)
从堆栈中获取一个整数参数。
那为什么我不直接使用stackpointer呢?
add esp,8 ; point ESP at the data we want
pop eax
sub esp,12 ; restore ESP to its original position
Run Code Online (Sandbox Code Playgroud)
这会导致错误吗?在任何情况下都使用吗?
我当然知道第一个操作的大小较小,因为它只是一个操作码,mov而不是三个,但这不是问题的重点。
(编者注:mov eax, [ebp+8]。在x86机器代码的3字节的指令 add/ subESP,imm8指定为3个字节的每个,pop eax是1个字节。
mov eax, [esp+8]是一个4字节的指令:不像在16位寻址模式,ESP可以是一个基址寄存器。但是它确实需要一个SIB字节来对其进行编码。
这些都是现代CPU上的单联指令,不包括额外的堆栈同步联指令。)
为什么这样做是错误的做法?
这就是我通过阅读一些内存分段文档所理解的:当调用一个函数时,有一些指令(称为函数序言)将帧指针保存在堆栈上,将堆栈指针的值复制到基本指针中并保存一些局部变量的内存.
这是我尝试使用GDB调试的一个简单代码:
void test_function(int a, int b, int c, int d) {
int flag;
char buffer[10];
flag = 31337;
buffer[0] = 'A';
}
int main() {
test_function(1, 2, 3, 4);
}
Run Code Online (Sandbox Code Playgroud)
调试此代码的目的是了解调用函数时堆栈中发生的情况:因此我必须在执行程序的各个步骤(在调用函数之前和执行期间)检查内存.虽然我设法通过检查基指针来查看返回地址和保存的帧指针之类的东西,但我真的无法理解在反汇编代码之后我要写的内容.
拆解:
(gdb) disassemble main
Dump of assembler code for function main:
0x0000000000400509 <+0>: push rbp
0x000000000040050a <+1>: mov rbp,rsp
0x000000000040050d <+4>: mov ecx,0x4
0x0000000000400512 <+9>: mov edx,0x3
0x0000000000400517 <+14>: mov esi,0x2
0x000000000040051c <+19>: mov edi,0x1
0x0000000000400521 <+24>: call 0x4004ec <test_function>
0x0000000000400526 <+29>: pop rbp
0x0000000000400527 <+30>: ret
End …Run Code Online (Sandbox Code Playgroud)