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

如何与调用DLL的NASM程序FreePascal链接? 问题,dll,nasm,freepascal,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和WriteC

我有一个用汇编语言(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仅跳到前面,它是用于or功能的:我们为外部对象命名为AliasGetStdHandle。这是一个重要的部分,函数对汇编程序是可见的

AsmGetStdHandle是汇编函数gethandle在FreePascal中的名称。它调用WrapperStdHandle(昵称为AliasGetStdHandle),它跳转到WindowsGetStdHandle,即DLL函数

section .text

extern GetStdHandle

global gethandle

gethandle:
         mov   eax, [esp+4]
         push  eax
         call  GetStdHandle
         ret   4
我们完成了,现在汇编程序可以链接了,而不需要改变其中的任何内容。所有重命名机器都是在调用它的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,它现在直接指向内核32函数

section .text

extern GetStdHandle

global gethandle

gethandle:
         mov   eax, [esp+4]
         push  eax
         call  GetStdHandle
         ret   4
但这仅在使用外部链接器(GNULD)时起作用,命令为

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

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

修订:

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

源自NASM的obj不在FPC的控制范围内,因此不会为其生成相关功能

如果是这样的话,BeniBela的代码可能会起作用,因为它强制引用FPC代码,并在符号中链接。但这只是猜测,可能是装饰的原因,也可能是主下划线的原因

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


顺便说一句,FPC的默认值不是stdcall,因此BenBela的函数可能需要一个stdcall修饰符

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

例如:


Nasm不是链接器。我与nasm一起使用的链接器Golink可以直接读取DLL文件,而无需导入库(命令为:Golink/console bob.obj kernel32.DLL)。所以,我的问题是,向freepascal显示我的链接对象文件对dll有外部依赖性(golink会自动看到),并解决这些依赖性。这就是我的观点。FPC的内部链接器也可以做到这一点,但可能只对其本机代码,而不是外部的.objs。我以前试过,它不起作用,除非使用public再次导出函数。在导入的函数本身上设置public也不起作用。我没有找到一种方法来指定链接器的内容,以强制将对象文件链接到给定的库。我不太清楚它是否需要导入库,或者它是否可以将ld直接用于dll,无论如何,它似乎不起作用(可能我错过了一个决定性的命令行选项或其他内容)。@edit:因为您从我的示例中删除了别名。如果添加'alias:'GetStdHandle';它应该会起作用。(虽然我用过_GetStdHandle@4在pascal和assembly中。您也可以同时使用这两个别名)愚蠢的我!我不太理解这个“别名”的东西,但现在我看到它对您的解决方案至关重要。我现在就修改这个,我修改了我的答案。试图对贝尼贝拉的发现发表评论。但这似乎与进口LIB有关。显然,FPC链接器只为FPC拥有的符号生成那些存根。@Marco看起来你是对的:单独使用$linklib时,它不起作用,但如果我在Pascal中声明对GetStdHandle的引用,我可以在汇编中使用该符号。我想知道是否可以直接使用Windows单元,但我找不到正确的名称(我想是像_Windows$$\u GetStdHandle这样的名称,但我会遇到奇怪的错误,其中$$被替换为路径)。另请参见名称损坏。现在,我正确地使用了alias指令,但我不太理解它。我将再等一段时间,看看是否有人提出了不涉及包装器的解决方案,但这个已经很好了。谢谢!顺便说一下,根据FP文档,“alias”已被弃用,应替换为“public name”(参见问题中的链接)。这并没有减损你答案的价值。我甚至没有意识到这一点(别名不赞成,所以不要反对BeniBela;-)顺便说一句:这些不是stdcall吗