我目前正在上一门课程,要求我编写汇编代码,准确地说是 x86-64 AT&T 语法汇编代码。下面是一个 c 文件,其中包含我必须编写其汇编代码的函数“bubble”的函数定义。
#include<stdio.h>
#include<stdlib.h>
void bubble(int* arr, int len);
int main(){
int n;
scanf("%d", &n);
int* arr = malloc(sizeof(int)*n);
for (int i = 0; i < n; i++)
{
scanf("%d", &arr[i]);
}
bubble(arr, n);
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
free(arr);
}
下面是函数泡泡的汇编代码,
.global bubble
.text
bubble:
movq $-1, %rcx
.L1:
incq %rcx
movl 4(%rdi,%rcx,4), %eax
cmpl (%rdi,%rcx,4), %eax
jge .T1
movl (%rdi,%rcx,4), %eax
movl 4(%rdi,%rcx,4), %ebx
movl %eax, 4(%rdi,%rcx,4)
movl %ebx, (%rdi,%rcx,4)
.T1:
movq %rsi, %rax
subq %rcx, %rax
cmpq $0x2, %rax
jne .L1
.T2:
decq %rsi
cmpq $0x1, %rsi
jne bubble
ret
我简单的用命令编译,
gcc bubble.c func.s
现在只要像上面那样编译并运行,就没有错误,程序按预期运行(注意 - 我正在 Ubuntu 上编译和运行)。
但是,在编译时使用
gcc bubble.c func.s -g -fsanitize=address
运行,出现以下错误,
=================================================================
==654==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000001 (pc 0x55d713a2944e bp 0x7ffed345b730 sp 0x7ffed345b6a0 T0)
==654==The signal is caused by a WRITE memory access.
==654==Hint: address points to the zero page.
#0 0x55d713a2944e in main /mnt/c/Users/rudy/Desktop/CSO/test/bubble.c:6
#1 0x7fe6ec4b0d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#2 0x7fe6ec4b0e3f in __libc_start_main_impl ../csu/libc-start.c:392
#3 0x55d713a29124 in _start (/mnt/c/Users/rudy/Desktop/CSO/test/a.out+0x1124)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /mnt/c/Users/rudy/Desktop/CSO/test/bubble.c:6 in main
==654==ABORTING
将其放入 gcc 后,它运行良好并退出
[Inferior 1 (process 685) exited normally]
以及在命令中使用 valgrind,
valgrind --leak-check=full ./a.out
它运行良好并退出
==694==
==694== HEAP SUMMARY:
==694== in use at exit: 0 bytes in 0 blocks
==694== total heap usage: 3 allocs, 3 frees, 2,068 bytes allocated
==694==
==694== All heap blocks were freed -- no leaks are possible
==694==
==694== For lists of detected and suppressed errors, rerun with: -s
==694== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
那么为什么地址消毒器会出现段错误?抱歉,如果我的问题或格式有任何问题,这是我的第一个问题。
您的
bubble
函数破坏了 %rbx
寄存器(修改它而不在返回之前恢复其先前的值)。这违反了 Linux/SysV x86-64 调用约定,该约定指定 %rbx
由函数调用保留。请参阅通过 linux x86-64 函数调用保留哪些寄存器,x86 程序集 - 为什么 [e]bx 在调用约定中保留?.
最简单的修复就是使用一个不同的寄存器,它被指定为调用破坏,例如
%rdx
。如果你真的想使用%rbx
,那么在你的函数顶部使用push %rbx
,然后在返回之前使用pop %rbx
。
至于为什么它只在使用 AddressSanitizer 时崩溃:似乎没有 ASAN,编译后的
main
代码实际上并没有在函数调用中的%rbx
中存储任何重要值;实际上它根本不使用%rbx
。 (对于未优化的编译来说并不奇怪。)而且我猜 main
的调用者也没有在那里存储值。所以在这种情况下不会造成任何伤害。但是当 ASAN 打开时,%rbx
被它添加的检测代码使用。所以我们实际上不能相信 ASAN 的严格检查发现了你的错误;只是启用它会以一种恰好触发它的方式更改生成的代码。
(附带错误:如果传递长度为 0 或 1 的数组,您的
bubble
函数将崩溃。)