从 amd64 可执行文件调用 aarch64 共享库,可能使用二进制 translation/QEMU

Calling aarch64 shared library from amd64 executable, maybe using binary translation/QEMU

我有一个用于 Linux 的 aarch64 库,我想在 amd64 Linux 安装中使用它。目前,我知道一种让它工作的方法,即使用 qemu-arm-static 二进制模拟器和我自己编译的 aarch64 可执行文件,它在 aarch64 库上调用 dlopen 并使用它。

烦人的是,将 aarch64 可执行文件 与我的 amd64 环境集成很烦人(例如,假设这个 arm64 库来自 IoT 设备并解码特殊的实时视频文件——我应该如何使用计算机上的本地库来播放它?)。我最终使用了 UNIX 管道,但我真的不喜欢这个解决方案。

有没有办法可以将 qemu-arm-static 东西 与库一起使用,这样我就可以获得直接调用库的 amd64 可执行文件?如果不是,那么在两种架构之间进行交互的最佳方式是什么?是管道吗?

我为此实现的解决方案是使用共享内存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);,从中获取return值,然后获取指向共享内存的指针。然后将初始化的结构复制到共享内存中。

你然后 运行 shmget(3)shmat(3) 在 ARM 二进制端,也得到一个指向共享内存的指针。 ARM 二进制文件 运行 是一个无限循环,等待它的“轮到”。当 turn 设置为 1 时,amd64 二进制文件将阻塞在一个永远的循环中,直到 turn0。 ARM 二进制文件将执行该函数,使用共享结构详细信息作为参数并使用 return 值更新共享内存结构。然后 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;  
}

同样,不能保证此代码甚至可以编译(但),但这只是一个大概的想法。