binfmt-misc(Miscellaneous Binary Формат) — это функция, предоставляемая ядром Linux, похожая на ассоциацию файлов в Windows, но более мощная, чем ассоциация файлов, заключается в том, что она может судить не только по имени суффикса файла, но и по содержимому файла (Magic Bytes)использовать разныепрограмма Открыть。Типичный сценарий использования::использоватьqemu
Запустить другое Архитектура Бинарные файлы на платформе。
В этой статье этот сценарий рассматривается в качестве примера для анализа его конкретного принципа работы.
Чтобы временно включить его, используйте следующую команду:
$ sudo mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc
Этот метод станет недействительным после перезапуска.,Если вы хотите быть эффективными в течение длительного времени,Можно найти в/etc/fstab
Добавьте строку в файл:
none /proc/sys/fs/binfmt_misc binfmt_misc defaults 0 0
Вы можете использовать следующую команду, чтобы проверить успешность открытия:
$ mount | grep binfmt_misc
binfmt_misc on /proc/sys/fs/binfmt_misc type binfmt_misc (rw,relatime)
$ ls -l /proc/sys/fs/binfmt_misc
Общая дозировка 0
--w------- 1 root root 0 февраль 5 22:55 register
-rw-r--r-- 1 root root 0 февраль 5 22:55 status
Сначала подготовьте программу с архитектурой Arm64 (для ее создания можно использовать кросс-платформенную компиляцию Go). После выполнения выдается сообщение об ошибке:
bash: ./go-test: Невозможно выполнить двоичный файл: Ошибка формата исполняемого файла
Сейчас,Давайте выполним этоapt install qemu-user-binfmt
Заказ,Затем запустите указанную выше программу Arm64.,Обнаружил, что работает нормально。Установитьqemu-user-binfmt
назад,будет внутри/proc/sys/fs/binfmt_misc
Создайте несколько файлов в каталоге,Есть среди них одинqemu-aarch64
。Давайте посмотрим на содержимое этого файла:
$ cat /proc/sys/fs/binfmt_misc/qemu-aarch64
enabled
interpreter /usr/libexec/qemu-binfmt/aarch64-binfmt-P
flags: POC
offset 0
magic 7f454c460201010000000000000000000200b700
mask ffffffffffffff00fffffffffffffffffeffffff
Этот файл описывает файл правил,первая линияenabled
Указывает, что правило включено;вторая линияinterpreter /usr/libexec/qemu-binfmt/aarch64-binfmt-P
выражатьиспользовать/usr/libexec/qemu-binfmt/aarch64-binfmt-P
выполнить двоичный файл;Третья линияflags: POC
Флаг, указывающий на бег,Конкретный смысл заключается в следующем:
P
: означает persist-argv,Это означает, что при вызове симулятора,Исходные аргументы (argv) будут сохранены. Это полезно в тех случаях, когда некоторым программам необходимо знать свои имена во время выполнения (например, argv[0]).O
: Представляет смещение. Это означает, что перед запуском симулятора необходимо считать смещение из двоичного файла. Это смещение будет использоваться как параметр симулятора.C
: Представляет учетные данные, что означает, что эмулятор будет работать с тем же идентификатором пользователя и идентификатором группы, что и исходная программа. Это помогает гарантировать, что эмулятор работает с теми же разрешениями, что и исходная программа.четвертая строкаoffset 0
Выражает из0
Значение смещения для начала чтения файла;пятая линияmagic 7f454c460201010000000000000000000200b700
Представляет магический байт, который соответствует;mask ffffffffffffff00fffffffffffffffffeffffff
Представляет байтовую маску,Используется для игнорирования некоторых неважных байтов в файле.
Это можно увидеть,Это правило будетиспользовать/usr/libexec/qemu-binfmt/aarch64-binfmt-P
выполнитьarm64Архитектурадвоичный файл,И этот файл на самом деле является мягкой ссылкой,На что на самом деле указывает:/usr/bin/qemu-aarch64
。
В приведенном выше примере,/proc/sys/fs/binfmt_misc/qemu-aarch64
документнаходится в УстановитьqemuБиблиотекаизчасавтоматически Установитьвходитьиз。Если вы хотите создать правило вручную,Как это сделать?
Давайте сначала сохраним следующий код в файлmain.go
середина:
package main
import (
"fmt"
"os"
)
func main() {
fmt.Println("Program name:", os.Args[0])
if len(os.Args) > 1 {
fmt.Println("Arguments:")
for i, arg := range os.Args[1:] {
fmt.Printf("Arg %d: %s\n", i+1, arg)
}
} else {
fmt.Println("No arguments provided.")
}
}
использовать Заказ:go build -o fake-runner ./main.go
руководитькомпилировать,и будеткомпилироватьвышелfake-runner
скопировать в/usr/local/bin
в каталоге。
в это время,мы должны/proc/sys/fs/binfmt_misc/register
серединав соответствии с:name:type:offset:magic:mask:interpreter:flags
правила написания формата。
E
(Сопоставить по расширению)илиM
(Сопоставление по магическим байтам файла)№1type
дляM
эффективен, когда,Представляет значение смещения магического байта.type
дляE
час,выражать要匹配изназадфамилия;когдаtype
дляM
час,Представляет магический байт в шестнадцатеричном формате.type
дляM
эффективен, когда,Маска, представляющая магические байты,Аналогично маскировке IP-адресаflags
一致假设我们想用fake-runner
Открытьк12344578
файлы, начинающиеся с,Вы можете выполнить следующие команды:
# echo ':binfmt-test:M::12345678::/usr/local/bin/fake-runner:P' > /proc/sys/fs/binfmt_misc/register
# cat /proc/sys/fs/binfmt_misc/binfmt-test enabled
interpreter /usr/local/bin/fake-runner
flags: P
offset 0
magic 3132333435363738
Эту команду необходимо запускать с правами root.
Затем используйте команду для создания целевого файла:
$ echo 12345678 > /tmp/test.txt
$ chmod 755 /tmp/test.txt
$ /tmp/test.txt hello
Program name: /usr/local/bin/fake-runner
Arguments:
Arg 1: /tmp/test.txt
Arg 2: /tmp/test.txt
Arg 3: hello
удалить规则可киспользовать Заказ:echo -1 > /proc/sys/fs/binfmt_misc/binfmt-test
Теперь мы используем команду docker для запуска образа Arm64:
$ docker run -it arm64v8/ubuntu bash
Unable to find image 'arm64v8/ubuntu:latest' locally
latest: Pulling from arm64v8/ubuntu
005e2837585d: Pull complete
Digest: sha256:ba545858745d6307f0d1064d0d25365466f78d02f866cf4efb9e1326a4c196ca
Status: Downloaded newer image for arm64v8/ubuntu:latest
standard_init_linux.go:207: exec user process caused "no such file or directory"
После некоторых исследований,Я обнаружил, что пока я выполняю Заказ:apt install qemu-user-static
,перезапускdockerКонтейнер нормальный。выполнить это Заказ Изменю/usr/libexec/qemu-binfmt/aarch64-binfmt-P
Мягкая ссылка на файл/usr/bin/qemu-aarch64-static
。Давайте посмотримqemu-aarch64
иqemu-aarch64-static
разница:
$ readelf -d /usr/bin/qemu-aarch64
Dynamic section at offset 0x3aee38 contains 37 entries:
отметка тип имя/значение
0x0000000000000001 (NEEDED) Общая библиотека: [libz.so.1]
0x0000000000000001 (NEEDED) Общая библиотека: [librt.so.1]
0x0000000000000001 (NEEDED) Общая библиотека: [libcapstone.so.4]
0x0000000000000001 (NEEDED) Общая библиотека: [libglib-2.0.so.0]
0x0000000000000001 (NEEDED) Общая библиотека: [libgnutls.so.30]
0x0000000000000001 (NEEDED) Общая библиотека: [libgmodule-2.0.so.0]
0x0000000000000001 (NEEDED) Общая библиотека: [libstdc++.so.6]
0x0000000000000001 (NEEDED) Общая библиотека: [libm.so.6]
0x0000000000000001 (NEEDED) Общая библиотека: [libgcc_s.so.1]
0x0000000000000001 (NEEDED) Общая библиотека: [libpthread.so.0]
0x0000000000000001 (NEEDED) Общая библиотека: [libc.so.6]
0x000000000000000c (INIT) 0xab000
0x000000000000000d (FINI) 0x2a83ec
0x0000000000000019 (INIT_ARRAY) 0x35b8e0
0x000000000000001b (INIT_ARRAYSZ) 248 (bytes)
0x000000000000001a (FINI_ARRAY) 0x35b9d8
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
0x000000006ffffef5 (GNU_HASH) 0x340
0x0000000000000005 (STRTAB) 0x2a608
0x0000000000000006 (SYMTAB) 0xa1f0
0x000000000000000a (STRSZ) 122726 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000015 (DEBUG) 0x0
0x0000000000000003 (PLTGOT) 0x3b00c8
0x0000000000000002 (PLTRELSZ) 11136 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0xa7f68
0x0000000000000007 (RELA) 0x4b2e0
0x0000000000000008 (RELASZ) 380040 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000000000001e (FLAGS) BIND_NOW
0x000000006ffffffb (FLAGS_1) Логотип: NOW PIE
0x000000006ffffffe (VERNEED) 0x4b070
0x000000006fffffff (VERNEEDNUM) 7
0x000000006ffffff0 (VERSYM) 0x4856e
0x000000006ffffff9 (RELACOUNT) 15807
0x0000000000000000 (NULL) 0x0
$ readelf -d /usr/bin/qemu-aarch64-static
There is no dynamic section in this file.
Это можно увидеть,qemu-aarch64-static не имеет зависимостей от динамических библиотек,То есть,dockerдолжениспользоватьстатическийкомпилироватьизqemu
работать。таким образом,Может быть реализован Сейчасx86_64на машинекомпилироватьчерез Архитектура Цель зеркального отображения。
Для поддержки нескольких архитектур вам необходимо включить экспериментальную функцию Docker. Способ ее включения следующий:
в файле/etc/docker/daemon.json
середина Добавьте следующую конфигурацию
{
"experimental": true
}
Ранназадиспользоватьsysemcrtl restart docker
Заказ ПерезапускDockerСлужить。
$ docker info | grep -i 'experimental'
Experimental: true
Когда вы видите приведенный выше вывод, это означает, что экспериментальная функция включена.
Напишите следующий Dockerfile:
FROM ubuntu:20.04
RUN set -ex && apt update
Затем используйте следующую команду для компиляции образа Arm64.
$ sudo docker build --platform linux/arm64 -t ubuntu .
$ sudo docker run -it ubuntu bash
root@616a3dd3a915:/# uname -a
Linux 616a3dd3a915 5.15.34-amd64-desktop #2 SMP Mon May 16 16:31:30 CST 2022 aarch64 aarch64 aarch64 GNU/Linux
поэтому,использовать--platform linux/arm64
Просто параметрыкомпилироватьвнеarm64Архитектураиззеркало。
использоватьbinfmt-misc
механизм может поддерживать прямуюLinuxбеги дальшеWindowsизexeдокумент,Это достигается с помощью вина.
$ cat /proc/sys/fs/binfmt_misc/DOSWin
enabled
interpreter /usr/bin/wine
flags:
offset 0
magic 4d5a
$ ls -l /usr/bin/wine
lrwxrwxrwx 1 root root 19 Октябрь 8 18:09 /usr/bin/wine -> deepin-wine6-stable
deepin-wine6-stable на самом деле является bash-скриптом:
#!/bin/bash
name=${0##*/}
bindir=/usr/lib/$name
wine32=/opt/$name/bin/wine
wine64=/opt/$name/bin/wine64
if test -x $wine32 -a "$WINEARCH" != "win64"; then
wine=$wine32
elif test -x $wine64; then
wine=$wine64
if [ "$(dpkg --print-architecture)" = "amd64" -a "$(dpkg --print-foreign-architectures | grep -cx "i386")" -ne 1 ]; then
echo "it looks like multiarch needs to be enabled. as root, please"
echo "execute \"dpkg --add-architecture i386 && apt-get update &&"
echo "apt-get install $(echo $name | sed s/wine/wine32/)\""
fi
else
echo "error: unable to find wine executable. this shouldn't happen."
exit 1
fi
if test -z "$WINEPREFIX"; then
if test "$wine" = "$wine64"; then
wineprefix=$HOME/.wine64
else
wineprefix=$HOME/.wine
fi
else
wineprefix=$WINEPREFIX
fi
if test -z "$WINELOADER"; then
wineloader=$wine
else
wineloader=$WINELOADER
fi
if test -z "$WINEDEBUG"; then
winedebug=-all
else
winedebug=$WINEDEBUG
fi
runtime_path=/opt/deepinwine/runtime-i386
export LD_LIBRARY_PATH="/opt/$name/lib:/opt/$name/lib64:$LD_LIBRARY_PATH"
export WINEDLLPATH=/opt/$name/lib:/opt/$name/lib64
# 32-битному Wine необходимо указать путь к 32-битной среде выполнения.
if [ -f "$runtime_path/init_runtime.sh" -a "$wine" = "$wine32" ];then
source "$runtime_path/init_runtime.sh"
PE_FILE="$1"
if [[ "$1" == *".exe" ]]; then
PE_FILE=${PE_FILE//\\/\/}
drive=${PE_FILE:0:2}
if [[ ${drive} == "c:"* || ${drive} == "C:"* ]]; then
PE_FILE=${wineprefix}/drive_c${PE_FILE:2}
fi
fi
init_runtime
if [ -f "$PE_FILE" ];then
#only 32 bit application need config this envs
if file "$PE_FILE" | grep -q -e "PE32 "; then
init_32bit_config
fi
fi
export WINELOADERNOEXEC=1
winepreloader=/opt/$name/bin/wine-preloader
WINEPREFIX=$wineprefix WINELOADER=$wineloader WINEDEBUG=$winedebug $winepreloader $wine "$@"
else
WINEPREFIX=$wineprefix WINELOADER=$wineloader WINEDEBUG=$winedebug $wine "$@"
fi
Поэтому, если вы напрямую введете путь к exe-файлу в командной строке, например, в игре «Сапёр», вы увидите, что система открывает интерфейс игры «Сапер».
binfmt-misc предоставляет гибкий механизм ассоциации файлов, так что некоторые программы, которые не могут быть запущены напрямую, могут запускаться напрямую, как обычные программы Linux (например, кросс-архитектурные программы, Windows exe и т. д.).