我有个特殊的问题。我有一个用VC++和MASM写的.dll
。(ml64 14.34.31942)
C部分只是一个 Package 器,因此一个函数可以由C#(.net6)应用程序调用。只有一个入口点,它传递一个指向大型状态对象的指针。该函数返回一个int 32作为结果代码。
我想让这个应用程序在Linux上工作。我认为C#应用程序可以工作,但我的问题是对非托管.dll
的调用。
如果我接受ml64.exe
创建的对象文件,我可以将其 Package 在C Package 器中,以将寄存器设置为调用约定:
- core.c*
#include <stdint.h>
extern int32_t asm_func(void* state);
int wrapper(void *state)
{
__asm__ volatile(
"mov %rdi, %rsi \t\n"
"call asm_func \t\n");
return 0;
}
int32_t fnEmulatorCode(void* state) {
return wrapper(state);
}
(返回代码待定!)
并使用以下命令编译:gcc -shared -o EmulatorCore.so -fPIC core.c core.obj -Wall -g -m64
(core.obj是MASM对象文件)
生成的.so
文件与任何其他非托管dll一样被引用:
[DllImport(@"./EmulatorCore/EmulatorCore.so")]
private static extern int fnEmulatorCode(ref CpuState state);
而且电话起作用了!有点。入口点找到了,C Package 器改变寄存器也没问题。
然而,在汇编代码中有函数指针表,在Linux上没有被重新定位,所以对表的调用是分段错误的。这在Windows上有效。
例如:
lea rax [opcode_00]
lea rax, qword ptr [rax + rbx * 8] ; rbx is the instruction
jmp qword ptr [rax] ; was original jmp qword ptr [rax + rbx * 8]
...
opcode_00 qword x00_brk ; $00
opcode_01 qword x01_ora_indx ; $01
opcode_02 qword noinstruction ; $02
...
所有其他的跳转和引用似乎都很好。
以及如何进行的建议?MASM根据objdump
生成的目标文件为pe-x86-64
。但是objcopy
似乎无法将其转换为elf64-x86-64
。
那么,为什么Linux不“重新定位”这些表呢?是通过一些gcc优化吗?或者dotnet没有正确加载库?或者只是pe-coff
文件没有正确地公开它们?如果是这样,我该如何工作?
我不能改变MASM的原始代码,但是有没有什么东西可以把它转换成NASM或类似的代码,这样我就可以交叉编译了?(不是说谷歌搜索找到任何东西…)
我可能会将表更改为适当的“跳转表”,但这会影响性能,并且可能会出现其他问题,因此在第一个示例中,我宁愿尝试保持ASM源代码不变。
编辑:
using nm -gC EmulatorCore.so | grep opcode_00
返回nothing,这意味着引用没有包含在.so
文件中,所以不能正确重定位?(表中的procs确实会出现,这就是为什么应用程序的其余部分看起来可以工作的原因)
编辑2:
为了说明这个问题,比较这两个图像。一个来自Windows,它显示了库在内存中的范围内的表,在Linux中,值都很低,表明是从库入口点的偏移量。
Windows中内存中的表(RIP:0x00007FFE043C5817)
Linux中内存中的表(RIP:0x00007f24fc59a5a8)
显示从表中跳转后的RIP,显示它正确阅读值,只是值错误。
1条答案
按热度按时间4c8rllxm1#
在Windows上,跳转表会正确地重新计算,但在Linux上则不会。
为了让它工作,需要将表转换为相对跳转,并像这样调用: