从 amd64 可执行文件调用 aarch64 共享库,可能使用二进制翻译/QEMU
Posted
技术标签:
【中文标题】从 amd64 可执行文件调用 aarch64 共享库,可能使用二进制翻译/QEMU【英文标题】:Calling aarch64 shared library from amd64 executable, maybe using binary translation/QEMU 【发布时间】:2022-01-05 07:30:36 【问题描述】:我有一个适用于 Linux 的 aarch64 库,我想在 amd64 Linux 安装中使用它。目前,我知道一种让它工作的方法,即使用 qemu-arm-static
二进制模拟器和我自己编译的 aarch64 可执行文件,它在 aarch64 库上调用 dlopen
并使用它。
令人烦恼的是,将 aarch64executable 与我的 amd64 环境集成很烦人(例如,假设这个 arm64 库来自物联网设备并实时解码特殊视频文件——我应该如何使用我电脑上的原生库来播放它?)。我最终使用了 UNIX 管道,但我真的不喜欢这个解决方案。
有没有办法我可以将 qemu-arm-static
的东西 only 与库一起使用,这样我就可以拥有一个直接调用库的 amd64 可执行文件?如果不是,那么在这两种架构之间进行交互的最佳方式是什么?是管道吗?
【问题讨论】:
【参考方案1】:我为此实现的解决方案是使用共享内存 IPC。这个解决方案特别好,因为它与固定长度的 C 结构很好地集成在一起,允许您简单地在一端和另一端使用结构。
假设你有一个带有签名uint32_t so_lib_function_a(uint32_t c[2])
的函数
您可以在 amd64 库中编写包装函数:uint32_t wrapped_so_lib_function_a(uint32_t c[2])
。
然后,您创建一个共享内存结构:
typedef struct
uint32_t c[2];
uint32_t ret;
int turn; // turn = 0 means amd64 library, turn = 1 means arm library
ipc_call_struct;
像这样初始化一个结构,然后运行shmget(SOME_SHM_KEY, sizeof(ipc_call_struct), IPC_CREAT | 0777);
,从中获取返回值,然后获取指向共享内存的指针。然后将初始化的结构体复制到共享内存中。
然后您在 ARM 二进制端运行 shmget(3)
和 shmat(3)
,同时获得指向共享内存的指针。 ARM 二进制文件运行一个无限循环,等待它的“轮到”。当turn
设置为1
时,amd64 二进制文件将一直阻塞,直到turn
为0
。 ARM 二进制文件将执行该函数,使用共享结构详细信息作为参数,并使用返回值更新共享内存结构。然后 ARM 库会将 turn
设置为 0
并阻塞,直到 turn
再次变为 1
,这将允许 amd64 二进制文件执行其操作,直到它准备好再次调用 ARM 函数。
这是一个例子(它可能还没有编译,但它给了你一个大概的想法):
我们的“未知”库:shared.h
#include <stdint.h>
#define MAGIC_NUMBER 0x44E
uint32_t so_lib_function_a(uint32_t c[2])
// Add args and multiplies by MAGIC_NUMBER
uint32_t ret;
for (int i = 0; i < 2; i++)
ret += c[i];
ret *= MAGIC_NUMBER;
return ret;
挂钩到“未知”库:shared_executor.c
#include <dlfcn.h>
#include <sys/shm.h>
#include <stdint.h>
#define SHM_KEY 22828 // Some random SHM ID
uint32_t (*so_lib_function_a)(uint32_t c[2]);
typedef struct
uint32_t c[2];
uint32_t ret;
int turn; // turn = 0 means amd64 library, turn = 1 means arm library
ipc_call_struct;
int main()
ipc_call_struct *handle;
void *lib_dlopen = dlopen("./shared.so", RTLD_LAZY);
so_lib_function_a = dlsym(lib_dlopen, "so_lib_function_a");
// setup shm
int shm_id = shmget(SHM_KEY, sizeof(ipc_call_struct), IPC_CREAT | 0777);
handle = shmat(shm_id, NULL, 0);
// We expect the handle to already be initialised by the time we get here, so we don't have to do anything
while (true)
if (handle->turn == 1) // our turn
handle->ret = so_lib_function_a(handle->c);
handle->turn = 0; // hand off for later
amd64 方面:shm_shared.h
#include <stdint.h>
#include <sys/shm.h>
typedef struct
uint32_t c[2];
uint32_t ret;
int turn; // turn = 0 means amd64 library, turn = 1 means arm library
ipc_call_struct;
#define SHM_KEY 22828 // Some random SHM ID
static ipc_call_struct* handle;
void wrapper_init()
// setup shm here
int shm_id = shmget(SHM_KEY, sizeof(ipc_call_struct), IPC_CREAT | 0777);
handle = shmat(shm_id, NULL, 0);
// Initialise the handle
// Currently, we don't want to call the ARM library, so the turn is still zero
ipc_call_struct temp_handle = .c=0, .ret=0, .turn=0 ;
*handle = temp_handle;
// you should be able to fork the ARM binary using "qemu-arm-static" here
// (and add code for that if you'd like)
uint32_t wrapped_so_lib_function_a(uint32_t c[2])
handle->c = c;
handle->turn = 1; // hand off execution to the ARM librar
while (handle->turn != 0) // wait
return handle->ret;
同样,不能保证这段代码甚至可以编译(目前),但只是一个大概的想法。
【讨论】:
即使这个解决方案也不是很理想。为了更好地工作,您应该使用 futex 系统调用进行同步,这样每个程序就不会周期性地最大化 CPU 内核。以上是关于从 amd64 可执行文件调用 aarch64 共享库,可能使用二进制翻译/QEMU的主要内容,如果未能解决你的问题,请参考以下文章