【发布时间】:2013-02-06 10:09:13
【问题描述】:
问题
我有一个用汇编程序 (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 只跳转到前面,这里是 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
【问题讨论】:
-
@ 编辑:因为您从我的示例中删除了别名。如果添加`别名:'GetStdHandle';它应该工作。 (虽然我在帕斯卡和汇编中使用了 _GetStdHandle@4。你也可以确定两个别名)
-
傻我!我不太了解这个“别名”的东西,但现在我发现它对您的解决方案至关重要。我马上改一下。
-
我修改了我的答案。试图评论贝尼贝拉的发现。但它似乎与导入库有关。显然 FPC 链接器只为 FPC 拥有的符号生成那些存根。
-
@Marco 看起来你是对的:单独使用 $linklib 时,它不起作用,但如果我在 Pascal 中声明对 GetStdHandle 的引用,我可以在汇编中使用该符号。我想知道是否可以直接使用 Windows 单元,但我无法获得正确的名称(我认为类似于 _WINDOWS$$_GetStdHandle,但在将 $$ 替换为 PATH 时出现奇怪的错误)。另请参阅 freepascal.org/docs-html/prog/progsu125.html 了解名称修改。
标签: dll nasm freepascal