我有一个用汇编程序(nasm)编写的函数“bob”,它使用了kernel32.dll中的函数。我在FreePascal中有一个程序,叫做“bob”。
我使用nasm:
nasm -fwin32 bob.asm
在FreePascal中,我声明:
{$link bob.obj}
function bob(s:pchar):longint; stdcall; external name 'bob';
但是当我使用fpc编译时出现错误,告诉它没有找到GetStdHandle和WriteConsoleA(没有@n后缀),它们在bob.asm中被声明为extern。我想告诉fpc在kernel32.dll或适当的导入库中查找它们。
然而,当我在纯装配程序中使用相同的功能时,它可以与nasm和golink一起使用。当我不调用DLL函数时,我可以毫无困难地与FreePascal链接。
如何将kernel32函数与FreePascal链接起来,以便汇编函数“看到”它们?
BeniBela给出。我更改名称以便易于理解。
program dlltest;
function WindowsGetStdHandle(n: longint): longint; stdcall;
external 'kernel32.dll' name 'GetStdHandle';
{$asmmode intel}
procedure WrapperGetStdHandle; assembler; public name 'AliasGetStdHandle';
asm
jmp WindowsGetStdHandle
end;
{$link myget.obj}
function AsmGetStdHandle(n: longint): longint; stdcall;
external name 'gethandle';
const STDOUT = -11;
begin
writeln(AsmGetStdHandle(STDOUT));
writeln(WindowsGetStdHandle(STDOUT));
end.
在汇编中使用myget.asm:
section .text
extern AliasGetStdHandle
global gethandle
gethandle:
mov eax, [esp+4]
push eax
call AliasGetStdHandle
ret 4
WindowsGetStdHandle 是kernel32.dll中GetStdHandle的另一个名称。
WrapperGetStdHandle 只跳转到前面的内容,这里是 alias 或 public name 的功能:我们提供它为外部对象命名为AliasGetStdHandle。这是重要的部分,该功能对装配程序可见。
AsmGetStdHandle 是汇编函数 gethandle 的FreePascal中的名称。它调用WrapperStdHandle(昵称为AliasGetStdHandle),它跳转到DLL函数WindowsGetStdHandle。
我们已经完成了,现在装配程序可以链接,而不会改变其中的任何内容。所有重命名机制都在pascal程序中调用它。
唯一的缺点是:需要一个包装函数,但对于精确控制名称并不高估。
如果未在WindowsGetStdHandle的声明中指定kernel32.dll,但使用{$ linklib kernel32},则该符号在pascal程序中链接的目标文件中可见。但是,似乎单独的$ linklib指令是不够的,还是要在pascal中声明一些引用它的函数
program dlltest;
{$linklib kernel32}
function WindowsGetStdHandle(n: longint): longint; stdcall;
external name 'GetStdHandle';
{$link myget.obj}
function AsmGetStdHandle(n: longint): longint; stdcall;
external name 'gethandle';
const STDOUT = -11;
begin
writeln(AsmGetStdHandle(STDOUT));
writeln(WindowsGetStdHandle(STDOUT));
end.
使用以下汇编程序。 AliasGetStdHandle替换为GetStdHandle,它现在直接指向kernel32函数。
section .text
extern GetStdHandle
global gethandle
gethandle:
mov eax, [esp+4]
push eax
call GetStdHandle
ret 4
但这只适用于使用外部链接器(gnu ld)和命令
fpc -Xe dlltest.pas
当省略opton'-Xe'时,fpc会出现以下错误
Free Pascal Compiler version 2.6.0 [2011/12/25] for i386
Copyright (c) 1993-2011 by Florian Klaempfl and others
Target OS: Win32 for i386
Compiling dlltest.pas
Linking dlltest.exe
dlltest.pas(17,1) Error: Asm: Duplicate label __imp_dir_kernel32.dll
dlltest.pas(17,1) Error: Asm: Duplicate label __imp_names_kernel32.dll
dlltest.pas(17,1) Error: Asm: Duplicate label __imp_fixup_kernel32.dll
dlltest.pas(17,1) Error: Asm: Duplicate label __imp_dll_kernel32.dll
dlltest.pas(17,1) Error: Asm: Duplicate label __imp_names_end_kernel32.dll
dlltest.pas(17,1) Error: Asm: Duplicate label __imp_fixup_end_kernel32.dll
dlltest.pas(17,1) Fatal: There were 6 errors compiling module, stopping
Fatal: Compilation aborted
答案 0 :(得分:2)
我不知道如何直接修复链接问题,但您可以声明从Pascal源导出这些函数的公共包装函数。
E.g:
{$ASMMODE INTEL}
procedure WrapperGetStdHandle; assembler; public; alias: '_GetStdHandle@4';
asm jmp GetStdHandle end;
procedure WrapperWriteConsoleA; assembler; public; alias: '_WriteConsoleA@20';
asm jmp WriteConsoleA end;
答案 1 :(得分:1)
我怀疑有一些导入库由nasm自动链接,以便与nasm代码一起使用,并且您可能需要链接该库中的相关存根。
<强>修正:强>
智能链接可能存在问题。正如所说的FPC动态生成导入存根,但仅在需要时生成。由于Windows单元(包含所有核心WINAPI调用)非常大,因此为其激活智能链接(仅添加您使用的内容)。 (还有其他原因)
NASM发起的obj不在FPC的控制之内,因此不会为它生成相关的功能。
如果是这种情况,BeniBela的代码可能会起作用,因为它会强制引用FPC代码,并在符号中进行链接。这是猜测,它也可能是装饰的东西,或带有前导下划线的东西。
测试很简单,使用pascal代码中的函数,而不使用Benibela的声明。
顺便说一句,FPC的默认值不是stdcall,所以BenBela的函数可能应该得到一个stdcall修饰符