如何与FreePascal链接调用DLL的NASM程序?

时间:2013-02-06 10:09:14

标签: dll nasm freepascal

问题

我有一个用汇编程序(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

2 个答案:

答案 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修饰符