地址清理程序说存在段错误,但 valgrind 和 gdb 说没有?

Ani*_*ili 3 assembly gcc x86-64 segmentation-fault address-sanitizer

我目前正在学习一门课程,要求我编写汇编代码,准确地说是 x86-64 AT&T 语法汇编代码。下面是 ac 文件,其中包含我必须编写其汇编代码的函数“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);
}
Run Code Online (Sandbox Code Playgroud)

下面是函数 bubble 的汇编代码,

.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

Run Code Online (Sandbox Code Playgroud)

我只是用命令编译,

gcc bubble.c func.s
Run Code Online (Sandbox Code Playgroud)

现在,按照上面的方式编译并运行,没有错误,程序按预期运行(注意 - 我正在 Ubuntu 上编译和运行)。

但是,在编译时使用

gcc bubble.c func.s -g -fsanitize=address
Run Code Online (Sandbox Code Playgroud)

并运行,我收到以下错误,

=================================================================
==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
Run Code Online (Sandbox Code Playgroud)

将其放入 gcc 中后,它运行良好并退出

[Inferior 1 (process 685) exited normally]
Run Code Online (Sandbox Code Playgroud)

并在使用 valgrind 命令时,

valgrind --leak-check=full ./a.out
Run Code Online (Sandbox Code Playgroud)

它运行良好并退出

==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)
Run Code Online (Sandbox Code Playgroud)

那么为什么地址清理程序会出现段错误呢?如果我的问题或格式有任何问题,请道歉,这是我的第一个问题。

Nat*_*dge 5

您的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 的严格检查能够找到您的错误;只是启用它会以碰巧触发它的方式更改生成的代码。

(附带错误:bubble如果传递长度为 0 或 1 的数组,函数将崩溃。)