3

问题

我有一个用汇编程序(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 编译时出现错误,告诉它没有找到在 bob.asm 中声明为 extern 的 GetStdHandle 和 WriteConsoleA(没有 @n 后缀)。我想告诉 fpc 在 kernel32.dll 或适当的导入库中查找它们。

但是,当我在纯汇编程序中使用相同的函数时,它可以很好地与 nasm 和 golink 一起使用。当我不调用 DLL 函数时,我可以毫无问题地与 FreePascal 链接。

如何将 kernel32 函数与 FreePascal 链接,以便汇编函数“看到”它们?


一个办法

由贝尼贝拉提供。我更改名称以便事情易于理解。

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只跳到前面,这里是为别名公共名称能力:我们为外部对象命名为 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
4

2 回答 2

2

我不知道如何直接解决链接问题,但您可以声明从 Pascal 源导出这些函数的公共包装函数。

例如:

{$ASMMODE INTEL}
procedure WrapperGetStdHandle; assembler; public; alias: '_GetStdHandle@4';
asm  jmp GetStdHandle end;
procedure WrapperWriteConsoleA; assembler; public; alias: '_WriteConsoleA@20';
asm  jmp WriteConsoleA end;
于 2013-02-09T16:58:35.360 回答
1

我怀疑有一些导入库由 nasm 自动链接以与 nasm 代码一起使用,并且可能您也需要从该库链接相关的存根。

修正:

这可能是智能链接的问题。如前所述,FPC 会即时生成导入存根,但仅在需要时才会生成。因为 Windows 单元(包含所有核心 WINAPI 调用)非常大,所以为它激活了智能链接(仅添加您使用的内容)。(还有其他原因)

NASM 起源的 obj 不在 FPC 的控制范围内,因此不会为其生成相关函数。

如果是这种情况,BeniBela 的代码可能会起作用,因为它强制来自 FPC 代码的引用,并在符号中链接。不过这是推测,它也可能是带有装饰的东西,或者带有前导下划线的东西。

测试很简单,使用 pascal 代码中的函数,而不使用 Benibela 的声明。

顺便说一句,FPC 的默认值不是 stdcall,所以 BenBela 的函数可能应该得到一个 stdcall 修饰符

于 2013-02-06T16:07:33.560 回答