Box64 正在做一个新的功能叫 Box32,用于执行 32 位的 Linux 程序。
对于一个没有 softmmu 的模拟器,这个想法能正常实现的前提就是:程序内所有的指针必须位于 32 位空间,包括 box64 自己。
所以我们使用
今天晚上发现使用 mold 编译出来的 box64 二进制
1. mold
2. lld
3. GNU ld
使用
对于一个没有 softmmu 的模拟器,这个想法能正常实现的前提就是:程序内所有的指针必须位于 32 位空间,包括 box64 自己。
所以我们使用
-Ttext-segment,0x34800000 来将 box64 自己加载到一个低位的地址空间。不过 -Ttext-segment 在 lld 和 mold 中并不存在,好在可以使用等价的 --image-base 来代替。今天晚上发现使用 mold 编译出来的 box64 二进制
--image-base 完全没生效,导致 Box32 炸了。研究了一下,发现 mold,lld 和 GNU ld 在这上面的表现各不相同:1. mold
-Wl,--image-base 必须显式和 -no-pie 一起使用,否则不生效。如果不加 -no-pie 不会有任何报错/警告,只是不生效!2. lld
-Wl,--image-base 和是否 pie 无关。3. GNU ld
使用
-Wl,-Ttext-segment 会强制隐含 -no-pie 。👍4🤔2
今天需要用 MangoHud 看一下 FPS,发现这玩意在 RISC-V 上竟然不工作!
故事有点长,但是没有 TLDR。
MangoHud 的核心工作原理并不复杂,上面的
到这就有问题了,MangoHud 要做这些事情,势必要大量用到
MangoHud 解决这个问题的办法就是用 ld.so 提供的函数去内存中找到真正的那个 libc/libdl 中的
总之最后肯定要去读 libc/libdl 的
比如当
对于
从这个表述中我们可以知道,
只可惜啊,规范是规范,实现是实现。
在 glibc 中,
只可惜啊,实现是实现,RISC-V 是 RISC-V。
glibc 在 RISC-V 中突然遵守规范了,
不过,我刚刚修复了这个问题:https://github.com/flightlessmango/MangoHud/pull/1417
问题是,我实在不知道为什么在且仅在 RISC-V 上 glibc 突然决定遵守规范了,有知道的吗?
debian@rockos-eswin:~$ mangohud --dlsym glxdemo
MANGOHUD: Can't get dlopen() and dlsym()
故事有点长,但是没有 TLDR。
MangoHud 的核心工作原理并不复杂,上面的
mangohud 命令其实只是个 shell 脚本,真正起作用的是 libMangoHud_dlsym.so ,脚本中会通过 LD_PRELOAD 机制替换掉 libc/libdl 的 dlsym() 函数,然后再在自己实现的 dlsym() 中劫持感兴趣 Vulkan/OpenGL 函数,替换成自己的实现,比如通过替换 glxSwapBuffers() 实现在原画面的左上角显示 fps 等信息。到这就有问题了,MangoHud 要做这些事情,势必要大量用到
dlsym() ,但这个函数已经被自己给替换掉了(??)MangoHud 解决这个问题的办法就是用 ld.so 提供的函数去内存中找到真正的那个 libc/libdl 中的
dlsym() ,所以这就免不了要做一些 ELF parsing 的工作。总之最后肯定要去读 libc/libdl 的
PT_DYNAMIC 表,这个表中给出了各种 section 的地址/信息。
typedef struct {
Elf64_Sxword d_tag;
union {
Elf64_Xword d_val;
Elf64_Addr d_ptr;
} d_un;
} Elf64_Dyn;
比如当
d_tag 是 DT_SYMTAB 时, d_ptr 中保存的就是符号表的地址。对于
d_ptr ,elf(5) 是这样说的:
This member represents program virtual addresses. When interpreting these addresses, the actual address should be computed based on the original file value and memory base address. Files do not contain relocation entries to fixup these addresses.
从这个表述中我们可以知道,
d_ptr 保存的并不是绝对地址,而是一个相对的地址,要获取绝对地址只需要加上 elf 的基地址即可: obj->addr + obj->dynamic[i].d_un.d_ptr 。只可惜啊,规范是规范,实现是实现。
在 glibc 中,
d_ptr 保存的是绝对地址;在 musl 中,则保存的是相对地址。MangoHud 通过 #ifdef __GLIBC__ 解决了这个不一致的问题。只可惜啊,实现是实现,RISC-V 是 RISC-V。
glibc 在 RISC-V 中突然遵守规范了,
d_ptr 里面保存的是一个相对值!所以 MangoHud 在 x86_64、AArch64、LoongArch 上都可以工作,但是在 RISC-V 上炸掉了。不过,我刚刚修复了这个问题:https://github.com/flightlessmango/MangoHud/pull/1417
问题是,我实在不知道为什么在且仅在 RISC-V 上 glibc 突然决定遵守规范了,有知道的吗?
👍3🤔1
> 问题是,我实在不知道为什么在且仅在 RISC-V 上 glibc 突然决定遵守规范了,有知道的吗?
后续:
双倍多多冰发现在 glibc 中只有 RISC-V 和 MIPS 的
在 ld.so 的
我看你们谁还不承认 RISC-V 是 MIPS 的正统续作
后续:
双倍多多冰发现在 glibc 中只有 RISC-V 和 MIPS 的
DL_RO_DYN_SECTION 宏定义为真,表示 dynamic section 是只读的。在 ld.so 的
elf_get_dynamic_info() 函数中,如果 dynamic section 可写,则会把上文提到的 d_ptr patch 为绝对地址,所以 RISC-V 和 MIPS 就没有被 patch。👍2