Apple Silicon原生编译下BUS_ADRALN替代BUS_ADRERR问题问询
Apple Silicon原生编译下mmap未分配文件区域写入的SIGBUS错误码差异问题
问题现象
将大型C++代码库迁移至Apple Silicon(arm64 macOS)平台时,发现原生编译版本的信号处理行为存在差异:向mmap映射但未实际分配物理空间的文件地址写入数据时,预期会触发BUS_ADRERR总线错误(表示物理地址不存在或无效),该行为在Windows、Linux及通过Rosetta模拟运行x86代码的Apple Silicon Mac上均符合预期。但在Apple Silicon原生编译环境下,实际触发的却是BUS_ADRALN错误码,而该内存地址已通过代码确认是严格对齐的。
复现代码与运行输出
最小复现代码
/* M1 Pro设备上的复现环境: Clang版本: Apple clang version 15.0.0 (clang-1500.0.40.1) Target: arm64-apple-darwin23.1.0 M1 Mac上的运行输出: clang++ repro.cpp && ./a.out Obtained mmapped address: 0x102590000 Bus error (SIGBUS) occurred. Signal: 10 Siginfo code BUS_ADRALN -> Invalid address alignment --------------------------------------------------------- 对比Amazon Linux x86_64环境: 编译器版本: clang version 11.1.0 (Amazon Linux 2 11.1.0-1.amzn2.0.2) Target: x86_64-unknown-linux-gnu 运行输出: Obtained mmapped address: 0x7facdaddf000 Bus error (SIGBUS) occurred. Signal: 7 Siginfo code BUS_ADRERR -> Non-existent physical address or invalid address */ #include <cassert> #include <csignal> #include <fcntl.h> #include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <unistd.h> using namespace std; void sigbusHandler(int sig, siginfo_t* info, void*) { cerr << "Bus error (SIGBUS) occurred. Signal: " << sig << endl; switch (info->si_code) { case BUS_ADRALN: cerr << "Siginfo code BUS_ADRALN -> Invalid address alignment" << endl; break; case BUS_ADRERR: cerr << "Siginfo code BUS_ADRERR -> Non-existent physical address or invalid address" << endl; break; default: cerr << "Unknown bus error code: " << info->si_code << endl; break; } exit(sig); } int main() { // 注册SIGBUS信号处理器 struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = sigbusHandler; sa.sa_flags = SA_SIGINFO; sigfillset(&sa.sa_mask); if (sigaction(SIGBUS, &sa, nullptr) == -1) { perror("sigaction"); return EXIT_FAILURE; } // 创建并mmap文件 int fd; off_t filesize = sizeof(int); void *mapped; fd = open("example_file.txt", O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0660); if (fd == -1) { perror("open"); return EXIT_FAILURE; } mapped = mmap(NULL, filesize, PROT_WRITE, MAP_PRIVATE, fd, 0); if (mapped == MAP_FAILED) { perror("mmap"); close(fd); return EXIT_FAILURE; } // 向mmap内存写入数据(会触发BUS_ADRALN,尽管地址已对齐) int value = 42; // 确保地址对齐符合int类型要求 assert((reinterpret_cast<uintptr_t>(mapped) & (alignment_of<decltype(value)>::value - 1)) == 0); cerr << "Obtained mmapped address: " << mapped << endl; memcpy(mapped, &value, sizeof(int)); cerr << "Did not trigger an error, repro didn't work" << endl; // 解除映射 if (munmap(mapped, filesize) == -1) { perror("munmap"); close(fd); return EXIT_FAILURE; } // 关闭文件描述符 close(fd); return EXIT_SUCCESS; }
原因分析
这是Apple Silicon(arm64)macOS内核特有的行为差异:当尝试写入mmap映射但未预先分配空间的文件区域时,内核返回的错误码被标记为BUS_ADRALN,但实际原因并非地址对齐问题,而是物理地址未分配(和其他平台的BUS_ADRERR场景本质一致)。这种错误码的映射差异是平台实现层面的区别,并非代码的对齐逻辑错误。
兼容处理方案
针对该平台差异,可通过以下方式处理:
- 统一错误处理逻辑:在信号处理器中,将
BUS_ADRALN和BUS_ADRERR视为同一类错误(无法写入目标内存区域)处理,不要依赖特定错误码区分场景。 - 预先分配文件空间:在mmap之前,使用
ftruncate(fd, filesize)扩展文件大小,确保映射区域已分配物理空间,从根源避免触发这类SIGBUS错误。 - 平台特定分支处理:如果必须区分错误场景,可通过预编译宏(如
__arm64__或__APPLE__)针对macOS arm64平台单独处理BUS_ADRALN错误码。
内容的提问来源于stack exchange,提问作者Diego H.
相关产品推荐
相关产品推荐

