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 サフィックスなし) が見つからないというエラーが表示されます。それらを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
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 コードからの参照を強制し、シンボルをリンクするため、機能する可能性があります。これは憶測ですが、装飾もあるかもしれませんし、先頭にアンダースコアがあるかもしれません。

簡単なテストでは、ベニベラの宣言なしでパスカル コードの関数を使用します。

ところで、FPC のデフォルトは stdcall ではないため、BenBela の関数はおそらく stdcall 修飾子を取得する必要があります

于 2013-02-06T16:07:33.560 に答える