“总线错误”消息是什么意思,它与分段错误有什么不同?


当前回答

对于我来说,由于没有声明程序集返回到.text部分,我意外触发了“总线错误”。这似乎是显而易见的,但它让我困惑了一段时间。

Eg.

.globl _myGlobal # Allocate a 64-bit global with the value 2
.data
.align 3
_myGlobal:
.quad 2
.globl _main # Main function code
_main:
push %rbp

从数据返回代码时缺少一个文本指令:

_myGlobal:
.quad 2
.text # <- This
.globl _main
_main:

希望这能对某人有所帮助

其他回答

这取决于你的操作系统、CPU、编译器以及其他因素。

一般来说,这意味着CPU总线无法完成命令或发生冲突,但这可能意味着一系列事情,具体取决于正在运行的环境和代码。

在POSIX系统上,当代码页由于某种原因无法换入时,还可以获得SIGBUS信号。

mmap最小POSIX 7的例子

“总线错误”发生在内核向进程发送SIGBUS时。

一个因为ftruncate被遗忘而产生它的最小示例:

#include <fcntl.h> /* O_ constants */
#include <unistd.h> /* ftruncate */
#include <sys/mman.h> /* mmap */

int main() {
    int fd;
    int *map;
    int size = sizeof(int);
    char *name = "/a";

    shm_unlink(name);
    fd = shm_open(name, O_RDWR | O_CREAT, (mode_t)0600);
    /* THIS is the cause of the problem. */
    /*ftruncate(fd, size);*/
    map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    /* This is what generates the SIGBUS. */
    *map = 0;
}

运行:

gcc -std=c99 main.c -lrt
./a.out

在Ubuntu 14.04中测试。

POSIX将SIGBUS描述为:

访问内存对象的未定义部分。

mmap规范说:

在从pa开始的地址范围内引用,并在对象结束后继续为len bytes到整个页面,将导致SIGBUS信号的传递。

shm_open表示它生成大小为0的对象:

共享内存对象的大小为0。

因此,在*map = 0处,我们将触及已分配对象的末端。

ARMv8 aarch64中未对齐的堆栈内存访问

这在:什么是总线错误?对于SPARC,但是在这里我将提供一个更可重复的示例。

你所需要的是一个独立的aarch64程序:

.global _start
_start:
asm_main_after_prologue:
    /* misalign the stack out of 16-bit boundary */
    add sp, sp, #-4
    /* access the stack */
    ldr w0, [sp]

    /* exit syscall in case SIGBUS does not happen */
    mov x0, 0
    mov x8, 93
    svc 0

然后,该程序在ThunderX2服务器机器上的Ubuntu 18.04 aarch64和Linux内核4.15.0上引发SIGBUS。

不幸的是,我不能在QEMU v4.0.0用户模式上重现它,我不知道为什么。

该错误似乎是可选的,由SCTLR_ELx控制。SA和SCTLR_EL1。SA0字段,我在这里进一步总结了相关文档。

总线错误现在在x86上很少见,发生在你的处理器甚至不能尝试请求的内存访问时,通常是:

使用具有不满足其对齐要求的地址的处理器指令。

分段错误发生在访问不属于你的进程的内存时。它们非常常见,通常是以下原因造成的:

使用指向已释放对象的指针。 使用未初始化的伪指针。 使用空指针。 缓冲区溢出。

PS:更准确地说,不是操作指针本身会导致问题。它正在访问它所指向的内存(解引用)。

总线错误的一个典型实例是在某些体系结构上,例如SPARC(至少在某些SPARC上,可能已经更改了),即当您执行不一致的访问时。例如:

unsigned char data[6];
(unsigned int *) (data + 2) = 0xdeadf00d;

这段代码尝试将32位整型值0xdeadf00d写入(很可能)没有正确对齐的地址,并将在“挑剔”的体系结构上生成总线错误。顺便说一下,Intel x86并不是这样的架构。它将允许访问(尽管执行速度较慢)。