How do I link the NASM program that calls the DLL to FreePascal?

Problem

I have a "bob" function written in assembler (nasm) that uses functions in kernel32.dll. And I have a program in FreePascal that calls "bob".

I am using nasm with:

nasm -fwin32 bob.asm

      

In FreePascal, I declare:

{$link bob.obj}

function bob(s:pchar):longint; stdcall; external name 'bob';

      

But I am getting an error when compiling with fpc saying that it does not find GetStdHandle and WriteConsoleA (no @n suffix) that are declared extern in bob.asm. I would like to tell fpc to look for them in the kernel32.dll file or in the appropriate import library.

However, when I use the same function in a pure build program, it works fine with nasm and golink. And when I don't call the DLL functions, I can communicate with FreePascal without any problem.

How can I link kernel32 functions to FreePascal so that build functions "see" them?


Decision

Courtesy of Benny Bela. I change names to keep things easy to follow.

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.

      

With this in assembly, in myget.asm:

section .text

extern AliasGetStdHandle

global gethandle

gethandle:
   mov   eax, [esp+4]
   push  eax
   call  AliasGetStdHandle
   ret   4

      

WindowsGetStdHandle is another name for GetStdHandle in kernel32.dll.

WrapperGetStdHandle only go to the previous one, here for an alias or public name : we specify the name AliasGetStdHandle for external objects. This is the important part, the function becomes visible to the build program.

AsmGetStdHandle is the FreePascal name of the gethandle build function. It calls the WrapperStdHandle (named AliasGetStdHandle), which jumps to WindowsGetStdHandle, a DLL function.

And we're done, now the build program can be linked without changing anything in it. All rename machines are done in the pascal program that calls it.

The only drawback is the need for a wrapper function, but it's not overstated for fine control over names.


Another solution

If kernel32.dll is not specified in the WindowsGetStdHandle declaration, but with {$ linklib kernel32}, then the symbol is visible in the object files linked in the pascal program. Nevertheless, it seems that the $ linklib directive alone is not enough, you still need to declare some function in pascal that refers to it

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.

      

With the following build program. AliasGetStdHandle is replaced with GetStdHandle, which now points directly to kernel32.

section .text

extern GetStdHandle

global gethandle

gethandle:
         mov   eax, [esp+4]
         push  eax
         call  GetStdHandle
         ret   4

      

But this only works when using an external linker (gnu ld) with the command

fpc -Xe dlltest.pas

      

When opton '-Xe' is disabled, fpc gives the following error:

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

      

+3


source to share


2 answers


I don't know how to fix the binding problem directly, but you can declare public wrapper functions that export those functions from the Pascal source.

eg:.



{$ASMMODE INTEL}
procedure WrapperGetStdHandle; assembler; public; alias: '_GetStdHandle@4';
asm  jmp GetStdHandle end;
procedure WrapperWriteConsoleA; assembler; public; alias: '_WriteConsoleA@20';
asm  jmp WriteConsoleA end;

      

+2


source


I suspect there is some import library that automatically nasm linked them for use with nasm code, and you probably need to link the appropriate stubs to that library as well.

changed:

This could be a smart binding issue. Because FPC generates import stubs on the fly, but only when needed. Because the Windows block (which contains all the basic WINAPI calls) is so large, smart bindings are activated for it (only add what you are using). (there are other reasons)

The created NASM obj is outside the control of the FPC, so the corresponding functions are not generated for it.



If so, BeniBela's code might work because it forces the link from the FPC code by linking characters. This is an assumption, however, it could be something with embellishment too, or something with a leading underline.

Testing, which is simple, uses functions from Pascal code without Benibela's declarations.

Btw, FPC is NOT stdcall by default, so BenBela functions should probably get stdcall modifier

+1


source







All Articles