c++ BUS_ADRALN错误(当Apple Silicon上预期有BUS_ADRERR时)

sigwle7e  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(274)

在将一个大型C++代码库移植到Apple Silicon时,我观察到信号处理在本机构建上的行为不同。具体来说,我正在写入一个mmaped内存地址,并期望出现BUS_ADRERR总线错误。这是Windows,Linux以及运行x86代码的Apple Silicon Mac的情况,下面的代码使用Rosetta仿真。
然而,在本机构建中,BUS_ADRALN是我观察到的错误代码。这似乎完全出乎意料,因为地址是对齐的。下面我粘贴了一个min repro,用于这个问题和我在M1 Pro机器上观察到的输出

/*
Reproducing the issue on M1 Pro laptop:

Clang version:
Apple clang version 15.0.0 (clang-1500.0.40.1)
Target: arm64-apple-darwin23.1.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

Observed output on M1 Mac:
clang++ repro.cpp && ./a.out
Obtained mmapped address: 0x102590000
Bus error (SIGBUS) occurred. Signal: 10
Siginfo code BUS_ADRALN -> Invalid address alignment
---------------------------------------------------------
On a Amazon Linux system on the other hand:

Compiler version:
clang version 11.1.0 (Amazon Linux 2 11.1.0-1.amzn2.0.2)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

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() {
   // Install sighandler for 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;
   }  

   // mmaped file
   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;
   }

   // Write int to mmaped memory
   // (triggers bus error with signal code BUS_ADRALN, even though address seems to be properly aligned)
   int value = 42;
   // Make sure alignment is fine to write 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;

   // Unmap the file
   if (munmap(mapped, filesize) == -1) {
      perror("munmap");
      close(fd);
      return EXIT_FAILURE;
   }

   // Close the file descriptor
   close(fd);

   return EXIT_SUCCESS;
}

字符串

093gszye

093gszye1#

XNU似乎无条件地在x86上使用BUS_ADRERR,在ARM上使用BUS_ADRALN
参见bsd/dev/i386/unix_signal.c

case SIGBUS:
    sinfo64.si_code = BUS_ADRERR;
    sinfo64.si_addr = ua_cr2;
    break;

字符串
bsd/dev/arm/unix_signal.c

case SIGBUS:
    if (proc_is64bit_data(p)) {
#if defined(__arm64__)
        sinfo.si_addr = user_frame.uf64.mctx.es.far;
#else
#error Unsupported architecture
#endif
    } else {
        sinfo.si_addr = user_frame.uf32.mctx.es.far;
    }

    sinfo.si_code = BUS_ADRALN;
    break;


Rosetta几乎可以肯定只是镜像了x86_64内核的功能,如果它没有完全包含相同的代码的话。
但由于这些都是硬编码的,我认为忽略它们是安全的。

相关问题