(最新版はこちらをご覧ください: https://zenn.dev/orimanabu/articles/rosetta-libkrun)
Appleは、Linux用のRosettaバイナリを提供しています。 これを使うと、Apple Silicon上のmacOS上で稼働するaarch64 Linux仮想マシン上で、x86_64バイナリを実行できるようになります1。
Rosettaは、実行環境がVirtualization.framework(Apple純正のVMM用フレームワーク)を使った仮想環境かをチェックしており、そうでなければ実行できないらしく、 そのチェックは「rosettaバイナリに謎のioctl(2)を発行して、特定の文字列が返ってくること」を確認しているようです2。
以下、その様子を再現してみたメモです。
- ホスト: Macbook Air M2, macOS, UTM
- ゲスト: Fedora 40
ori@myfedora:~/work$ sudo dmidecode | head -n 15
# dmidecode 3.6
Getting SMBIOS data from sysfs.
SMBIOS 3.3.0 present.
Table at 0x16FCB3000.
Handle 0x0000, DMI type 1, 27 bytes
System Information
Manufacturer: Apple Inc.
Product Name: Apple Virtualization Generic Platform
Version: 1
Serial Number: Virtualization-c4e505cf-ced1-493f-9d6f-f5e46434e9d0
UUID: cf05e5c4-d1ce-3f49-9d6f-f5e46434e9d0
Wake-up Type: Power Switch
SKU Number: Not Specified
Family: Not Specified
ori@myfedora:~/work$ uname -r
6.8.5-301.fc40.aarch64
ori@myfedora:~/work$ sudo mount -t virtiofs rosetta /mnt/rosetta
ori@myfedora:~/work$ findmnt /mnt/rosetta
TARGET SOURCE FSTYPE OPTIONS
/mnt/rosetta rosetta virtiofs rw,relatime,seclabel
ori@myfedora:~/work$ ls -l /mnt/rosetta
total 436
-rwxr-xr-x. 1 ori ori 1660888 May 22 08:09 rosetta
-rwxr-xr-x. 1 ori ori 298680 May 22 08:09 rosettad
ori@myfedora:~/work$ strace /mnt/rosetta/rosetta
execve("/mnt/rosetta/rosetta", ["/mnt/rosetta/rosetta"], 0xfffff242ddf0 /* 31 vars */) = 0
openat(AT_FDCWD, "/proc/self/exe", O_RDONLY) = 3
ioctl(3, _IOC(_IOC_READ, 0x61, 0x22, 0x45), 0xffffc32340e8) = 1
close(3) = 0
gettid() = 29760
write(2, "Usage: rosetta <x86_64 ELF to ru"..., 165Usage: rosetta <x86_64 ELF to run>
Optional environment variables:
ROSETTA_DEBUGSERVER_PORT wait for a debugger connection on given port
version: Rosetta-318.8
) = 165
exit(1) = ?
+++ exited with 1 +++
/proc/self/exe
に謎のioctl(2)を発行しているのがわかる。
straceの出力と[2]に載っているコードを参考に、下記のような a.c
を作成する。
/* usage: `./a.out PATH_TO_ROSETTA_BINARY` */
/* see also: https://threedots.ovh/blog/2022/06/quick-look-at-rosetta-on-linux/ */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#define KEY_SIZE 0x45
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "usage: %s path\n", argv[0]);
exit(1);
}
int fd = openat(AT_FDCWD, argv[1], 0);
if (fd < 0) {
perror("openat");
exit(1);
}
char key[KEY_SIZE];
int result = ioctl(fd, _IOC(_IOC_READ, 0x61, 0x22, KEY_SIZE), key);
if (result < 0) {
perror("ioctl");
exit(1);
}
printf("%s\n", key);
exit(0);
}
ori@myfedora:~/work$ gcc a.c
実行すると...謎の文字列が返ってきた!
ori@myfedora:~/work$ ./a.out /mnt/rosetta/rosetta
Our hard work
by these words guarded
please don't steal
© Apple Inc
libkrunはVirtualization.frameworkを使わないVMMだけど、このioctlの動きをエミュレート?することでRosettaサポートを導入した3。すごい。
libkrunの場合は、仮想マシンが上記のioctl(2)を発行したことを補足すると、ハンドラ内で ${HOME}/.krunvm-rosetta
に書いた内容を返します。
したがって、あらかじめこのファイルを手で作って、ioctl(2)で返すべき文字列を書いておく必要があります。
...と思いきや、いろいろあって(virtiofs周りで)、いつのまにか使えなくなっていた4。残念。