問題
アセンブラ (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 サフィックスなし) が見つからないというエラーが表示されます。それらをkernel32.dllまたは適切なインポートライブラリで探すようにfpcに指示したいと思います。
しかし、純粋なアセンブリ プログラムで同じ関数を使用すると、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 での名前です。これは、WindowsGetStdHandle という DLL 関数にジャンプする WrapperStdHandle (ニックネーム AliasGetStdHandle) を呼び出します。
これで、何も変更せずにアセンブリ プログラムをリンクできるようになりました。すべての名前変更機構は、それを呼び出すパスカル プログラムで行われます。
唯一の欠点は、ラッパー関数が必要なことですが、名前を細かく制御するのに高価ではありません。
別の解決策
WindowsGetStdHandle の宣言で kernel32.dll が指定されていないが、{$linklib kernel32} が指定されている場合、パスカル プログラムでリンクされたオブジェクト ファイルでシンボルが表示されます。ただし、 $linklib ディレクティブだけでは不十分なようです。それを参照する関数をパスカルで宣言する必要があります
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
オプション「-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