The MASM Forum

General => The Campus => Topic started by: markallyn on December 31, 2017, 06:17:06 AM

Title: how to access an integer variable in dll
Post by: markallyn on December 31, 2017, 06:17:06 AM
Hello everyone,

I have written a simple dll in which there is an integer data item along with several arithmetic functions.  I'm trying to access the integer variable in the dll from the calling program.  The linker complains that the integer variable is undefined.  I use a PUBLIC/EXTERN pair for the integer in the dll and caller respectively.  The .def file EXPORTS section explicitly defines the exported data item and is identified as such with dumpbin /exports command.  The arithmetic functions work correctly when they are called. 

What do I need to do to get correct linkage to the .dll data item? 

Thanks,
Mark Allyn
Title: Re: how to access an integer variable in dll
Post by: hutch-- on December 31, 2017, 06:30:16 AM
Mark,

Post at least some code or its guess work otherwise.
Title: Re: how to access an integer variable in dll
Post by: jj2007 on December 31, 2017, 07:01:44 AM
There are two options in the DLL:

a) Externdef:
MyQwords QWORD 123456789123456789, 223456789123456789
EXTERNDEF MyQwords:QWORD


b) a fake proc in the .data section (tested in 32-bit land, not sure if it works with ML64):
.data
MyDwords proc syscall export
dd 111111111, 222222222, 333333333
MyDwords endp


In the calling code, use LoadLibrary + GetProcAddress.
Title: Re: how to access an integer variable in dll
Post by: markallyn on December 31, 2017, 09:01:17 AM
Hutch, JJ,

Hutch: I should have mentioned at the end of my post that I would post code if folks thought it useful.  I forgot to put that line into the text.  Apologies.  Will do so once my grandson has gone home.  Difficult at the moment.

JJ:  After putting the post up, I stopped trying to access the data item implicitly.  I did an explicit LoadLibrary call and all worked perfectly.  So, I drew (the probablly incorrect) conclusion that one cannot load data items from a dll using an implicit load.  I'll look more closely at what you have done after aforementioned grandson vacates.

Regards,
Marrk
Title: Re: how to access an integer variable in dll
Post by: hutch-- on January 01, 2018, 01:21:24 PM
Mark,

Here is one technique to get values from a DLL. A DLL procedure is called passing an array to it and the array is filled with the values that are set in the DLL code. A simple dialog interface where I have marked the code that loads and calls the DLL procedure and the DLL is simple enough. There are other ways to get this data but this one works fine and you can get as many items as you like.

Sounds like the young fella is a genuine demon, means he is a good one.  :biggrin:
Title: Re: how to access an integer variable in dll
Post by: sinsi on January 01, 2018, 04:53:37 PM
Like this maybe?

;in the DLL
.data
public MyVar
MyVar dd 12345678h


;in the caller
externdef _imp__MyVar:dword

.code
  mov eax,_imp__MyVar
  mov eax,[eax]


Or have a dll exported function called GetMyVar

  mov eax,MyVar
  ret

Title: Re: how to access an integer variable in dll
Post by: markallyn on January 04, 2018, 07:21:15 AM
Hello Sinsi and everyone,

I tried your first method and couldn't get it to work.  In the attached .dll and caller I tried also to use EXTERNDEF in both the .dll and caller.  That didn't work either.  Here's the code for the .dll

Quote
include \masm32\include64\masm64rt.inc
externdef myint:QWORD

.data
myint   qword   040h

.code
DllEntry   PROC   hInstDLL:HINSTANCE, reason:QWORD, reserved:QWORD
mov   rax, TRUE
ret
DllEntry   ENDP

add2ints   PROC    var1:QWORD, var2:QWORD            
   mov   rax, var1
   mov   rdx, var2
   add   rax, rdx
   ret
add2ints   ENDP


mult2ints   PROC   var1:QWORD, var2:QWORD
   mov    rax, var1
   mov    rdx, var2
   imul    rax, rdx
   ret
mult2ints   ENDP

END   

...And here is the code for the caller:

Quote
include \masm32\include64\masm64rt.inc
;includelib   mymathfuncs.lib

add2ints PROTO :QWORD, :QWORD
mult2ints PROTO :QWORD, :QWORD
printf   PROTO :QWORD, :VARARG\

EXTERNDEF   myint:QWORD

.data
int1   QWORD   10h
int2    QWORD   30h
frmt1   BYTE   "The result of the addition is %d",13,10,0
strname BYTE   "mymathfuncs",0
dataname BYTE   "myint",0

.data?
libname   QWORD   ?

.code
main   PROC


   ;invoke   LoadLibrary, addr strname
   ;mov   rcx, rax
   ;invoke   GetProcAddress, rcx, addr dataname   
   ;mov   rcx, rax
   ;mov   libname, rax
   ;mov   rcx, qword ptr[rax]
   lea   rcx, myint
   mov   rcx, qword ptr[rcx]
   mov   rdx, int1
   invoke   add2ints, rcx, rdx
   mov   rdx, rax
   invoke   printf, ADDR frmt1, rdx
   invoke   FreeLibrary, libname

   ret
main   ENDP
END

As I say this doesn't work.  Note the commented-out section of code in the caller.  If one un-comments this the resulting explicit LoadLibrary call finds MYINT successfully.  I have included .zip files for both of these programs along with a little bat file to make them.

Your second method is very clever indeed.  I wish I had though of it.  If I recall my smattering of C++ there is a similar technique used in classes for data declared private.

Regards,
Mark

Title: Re: how to access an integer variable in dll
Post by: markallyn on January 04, 2018, 07:23:45 AM
For some reason the two .asm files did not get included in the previous post.  Trying again.

Mark
Title: Re: how to access an integer variable in dll
Post by: sinsi on January 04, 2018, 10:15:14 AM
Here is a working? copy of accessing myint
Title: Re: how to access an integer variable in dll
Post by: markallyn on January 05, 2018, 09:14:40 AM
Good evening, Sinsi,

After posting the code on the forum I wentt back and discovered that I had messed up the caller with two bad instructions.  Don't know how it happened, but it did.  So,, the correct caller is this:
Quote
include \masm32\include64\masm64rt.inc
includelib   mymathfuncs.lib

add2ints PROTO :QWORD, :QWORD
mult2ints PROTO :QWORD, :QWORD
printf   PROTO :QWORD, :VARARG\



.data
int1   QWORD   10h
int2    QWORD   30h
frmt1   BYTE   "The result of the addition is %d",13,10,0
strname BYTE   "mymathfuncs",0
dataname BYTE   "myint",0

.data?
libname   QWORD   ?

.code
main   PROC


   invoke   LoadLibrary, addr strname
   mov   rcx, rax
   invoke   GetProcAddress, rcx, addr dataname   
   mov   rcx, rax
   mov   libname, rax
   mov   rcx, qword ptr[rcx]
   mov   rdx, int1
   invoke   add2ints, rcx, rdx
   mov   rdx, rax
   invoke   printf, ADDR frmt1, rdx
   invoke   FreeLibrary, libname

   ret
main   ENDP
END

I will have a look at your .zip file to see what you did.  I also want to duplicate your use of a .dll function to access the .data section.

Thanks,
Mark Allyn
Title: Re: how to access an integer variable in dll
Post by: markallyn on January 05, 2018, 10:10:40 AM
Good evening, again, Sinsi,

OK, I see what you did to make EXTERNDEF work.  Now I have a beginner's question.  How did you know to prefix myint with __imp_?  Is this standard for all exported data and functions in a DLL? 

Thanks again,
Mark Allyn
Title: Re: how to access an integer variable in dll
Post by: sinsi on January 05, 2018, 10:39:08 AM
Good morning Mark,

I didn't like the usual way the linker usually does imports - a call to a jump to an address, I preferred just a call to an address.
To do that you use the _imp__ version from the lib
Title: Re: how to access an integer variable in dll
Post by: six_L on January 06, 2018, 08:21:04 PM
hi,markallyn
here is another mothod
option casemap:none
option win64:7

include \masm\asm64\UASM\UASM64\include\windows.inc

includelib \masm\asm64\UASM\UASM64\Lib\user32.lib
includelib \masm\asm64\UASM\UASM64\Lib\kernel32.lib

ICO_MAIN equ 1000
DLG_MAIN equ 100
IDC_CLEAR equ 101
IDC_ADD equ 102
IDC_SUB equ 103
IDC_MUL equ 104
IDC_DIV equ 105
IDC_INPUT1 equ 106
IDC_INPUT2 equ 107
IDC_RESULT1 equ 108
IDC_RESULT2 equ 109
IDC_RESULT3 equ 110
IDC_RESULT4 equ 111
IDC_RESULT0 equ 112

_Padd2ints typedef proto :qword,:qword
_Psub2ints typedef proto :qword,:qword
_Pmult2ints typedef proto :qword,:qword
_Pdiv2ints typedef proto :qword,:qword

Padd2ints typedef ptr _Padd2ints
Psub2ints typedef ptr _Psub2ints
Pmult2ints typedef ptr _Pmult2ints
Pdiv2ints typedef ptr _Pdiv2ints

.data?
hInstance dq ?
hWinMain dq ?
hCalc_1dll dq ?
lPadd2ints Padd2ints ?
lPsub2ints Psub2ints ?
lPmult2ints Pmult2ints ?
lPdiv2ints Pdiv2ints ?
Addr_myint dq ?
myint dq ?

.code

atodq proc uses rsi rdi String:QWORD
; ----------------------------------------
; Convert decimal string into qword value
; return value in rax
; ----------------------------------------

xor rax, rax
mov rsi, [String]
xor rcx, rcx
xor rdx, rdx
mov al, [rsi]
inc rsi
cmp al, "-"
jne proceed
mov al, [rsi]
not rdx
inc rsi
jmp proceed
@@:
sub al, 30h
lea rcx, qword ptr [rcx+4*rcx]
lea rcx, qword ptr [rax+2*rcx]
mov al, [rsi]
inc rsi
proceed:
or al, al
jne @B
lea rax, qword ptr [rdx+rcx]
xor rax, rdx
ret

atodq endp

_GetArithmeticResult proc uses rbx AriFlag:QWORD
local Idc_result:dword
local num1Buf[64]:byte
local num2Buf[64]:byte
local OutBuf[128]:byte

invoke RtlZeroMemory,ADDR num1Buf, sizeof num1Buf
invoke RtlZeroMemory,ADDR num2Buf, sizeof num2Buf
invoke RtlZeroMemory,ADDR OutBuf, sizeof OutBuf
invoke GetDlgItemText,hWinMain,IDC_INPUT1,ADDR num1Buf,sizeof num1Buf
invoke GetDlgItemText,hWinMain,IDC_INPUT2,ADDR num2Buf,sizeof num2Buf

invoke atodq,addr num1Buf
mov rbx,rax
invoke atodq,addr num2Buf
.if AriFlag==1
invoke lPadd2ints,rbx,rax
mov Idc_result,IDC_RESULT1
.elseif AriFlag==2
invoke lPsub2ints,rbx,rax
mov Idc_result,IDC_RESULT2
.elseif AriFlag==3
invoke lPmult2ints,rbx,rax
mov Idc_result,IDC_RESULT3
.elseif AriFlag==4
invoke lPdiv2ints,rbx,rax
mov Idc_result,IDC_RESULT4
.else
invoke MessageBox,NULL,CStr("AriFlag Failed"),CStr("_GetArithmeticResult"),MB_OK
jmp @Exit
.endif

invoke wsprintf,addr OutBuf,CStr("%d"),rax
invoke SetDlgItemText,hWinMain,Idc_result,addr OutBuf

mov rax,Addr_myint
mov rcx,[rax]
mov myint,rcx
invoke RtlZeroMemory,ADDR OutBuf, sizeof OutBuf
invoke wsprintf,addr OutBuf,CStr("%d"),myint
invoke SetDlgItemText,hWinMain,IDC_RESULT0,addr OutBuf
@Exit:
ret

_GetArithmeticResult endp

_ProcDlgMain proc hWnd:qword,wMsg:dword,wParam:qword,lParam:qword

mov eax,wMsg
.if eax == WM_INITDIALOG
push hWnd
pop hWinMain
invoke LoadIcon,hInstance,ICO_MAIN
invoke SendMessage,hWnd,WM_SETICON,ICON_BIG,eax
invoke LoadLibrary,CStr("calc_1.dll")
.if eax
mov hCalc_1dll,rax
invoke GetProcAddress,hCalc_1dll,CStr("add2ints")
mov lPadd2ints,rax
invoke GetProcAddress,hCalc_1dll,CStr("sub2ints")
mov lPsub2ints,rax
invoke GetProcAddress,hCalc_1dll,CStr("mult2ints")
mov lPmult2ints,rax
invoke GetProcAddress,hCalc_1dll,CStr("div2ints")
mov lPdiv2ints,rax

invoke GetProcAddress,hCalc_1dll,CStr("myint")
mov Addr_myint,rax
.else
invoke MessageBox,NULL,CStr("calc_1.dll load Failed"),CStr("LoadLibrary"),MB_OK
.endif

.elseif eax == WM_COMMAND
mov rax,wParam
.if ax == IDC_CLEAR
invoke SetDlgItemText,hWnd,IDC_INPUT1,NULL
invoke SetDlgItemText,hWnd,IDC_INPUT2,NULL
invoke SetDlgItemText,hWnd,IDC_RESULT0,NULL
invoke SetDlgItemText,hWnd,IDC_RESULT1,NULL
invoke SetDlgItemText,hWnd,IDC_RESULT2,NULL
invoke SetDlgItemText,hWnd,IDC_RESULT3,NULL
invoke SetDlgItemText,hWnd,IDC_RESULT4,NULL
.elseif ax == IDC_ADD
invoke _GetArithmeticResult,1
.elseif ax == IDC_SUB
invoke _GetArithmeticResult,2
.elseif ax == IDC_MUL
invoke _GetArithmeticResult,3
.elseif ax == IDC_DIV
invoke _GetArithmeticResult,4
.endif
.elseif eax == WM_CLOSE
invoke FreeLibrary,hCalc_1dll
invoke EndDialog,hWnd,NULL
.else
mov rax,FALSE
ret
.endif
mov rax,TRUE
ret

_ProcDlgMain endp

start Proc
invoke GetModuleHandle,NULL
mov hInstance,rax

invoke DialogBoxParam,hInstance,DLG_MAIN,NULL,offset _ProcDlgMain,NULL

invoke ExitProcess,NULL
start Endp


end



    \UASM64\bin\uasm64 -c -win64 %name%.bat
    \UASM64\bin\rc %name%.rc
    \UASM64\bin\link /ENTRY:start /SUBSYSTEM:windows /MACHINE:X64 %name%.obj %name%.res


option casemap:none
option win64:7

include \UASM64\include\windows.inc

includelib \UASM64\Lib\user32.lib
includelib \UASM64\Lib\kernel32.lib

.data?
public  myint
myint   qword   ?

.code

DllEntry PROC hInstDLL:HINSTANCE, reason:QWORD, @reserved:QWORD
mov myint,0h
mov rax, TRUE
ret
DllEntry ENDP

add2ints PROC var1:QWORD, var2:QWORD           
mov myint,0fh
mov rax, var1
mov rdx, var2
add rax, rdx
ret
add2ints ENDP

sub2ints PROC var1:QWORD, var2:QWORD           
mov myint,0ah
mov rax, var1
mov rdx, var2
sub rax, rdx
ret
sub2ints ENDP

mult2ints PROC var1:QWORD, var2:QWORD
mov myint,08h
mov rax, var1
mov rcx, var2
xor rdx, rdx
imul    rcx
ret
mult2ints ENDP

div2ints PROC var1:QWORD, var2:QWORD
mov myint,0ffffh
mov rax, var1
mov rcx, var2
xor rdx,rdx
idiv    rcx
ret
div2ints ENDP


end DllEntry


    \UASM64\bin\uasm64 -c -win64 %name%.bat
    \UASM64\bin\link /subsystem:windows /section:.bss,S /Def:%name%.def /Dll %name%.obj



LIBRARY calc
EXPORTS myint
EXPORTS add2ints
EXPORTS sub2ints
EXPORTS mult2ints
EXPORTS div2ints


#include <\UASM64\include\resource.h>

#define ICO_MAIN 1000
#define DLG_MAIN 100
#define IDC_CLEAR 101
#define IDC_ADD 102
#define IDC_SUB 103
#define IDC_MUL 104
#define IDC_DIV 105
#define IDC_INPUT1 106
#define IDC_INPUT2 107
#define IDC_RESULT1 108
#define IDC_RESULT2 109
#define IDC_RESULT3 110
#define IDC_RESULT4 111
#define IDC_RESULT0 112

ICO_MAIN ICON "Charfx.ico"

DLG_MAIN DIALOG 0, 0, 234, 95
STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Test_calc"
FONT 10, "Calibri"
{
LTEXT "Num1", -1, 42, 9, 40, 10
EDITTEXT IDC_INPUT1, 42, 18, 60, 14,ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP 
LTEXT "Num2", -1, 132, 9, 105, 10
EDITTEXT IDC_INPUT2, 132, 18, 60, 14,ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
PUSHBUTTON "Add(&a)", IDC_ADD, 14, 44, 30, 12
EDITTEXT IDC_RESULT1, 2, 58, 50, 14,ES_AUTOHSCROLL | ES_READONLY | WS_BORDER | WS_TABSTOP 
PUSHBUTTON "Sub(&b)", IDC_SUB, 73, 44, 30, 12
EDITTEXT IDC_RESULT2, 62, 58, 50, 14,ES_AUTOHSCROLL | ES_READONLY | WS_BORDER | WS_TABSTOP 
PUSHBUTTON "Mul(&m)", IDC_MUL, 133, 44, 30, 12
EDITTEXT IDC_RESULT3, 122, 58, 50, 14,ES_AUTOHSCROLL | ES_READONLY | WS_BORDER | WS_TABSTOP 
PUSHBUTTON "Div(&d)", IDC_DIV, 193, 44, 30, 12
EDITTEXT IDC_RESULT4, 182, 58, 50, 14,ES_AUTOHSCROLL | ES_READONLY | WS_BORDER | WS_TABSTOP 

LTEXT "Myint:", -1, 2, 81, 20, 10
EDITTEXT IDC_RESULT0, 24, 78, 40, 14,ES_AUTOHSCROLL | ES_READONLY | WS_BORDER | WS_TABSTOP 
PUSHBUTTON "Clear(&c)", IDC_CLEAR, 191, 78, 40, 14
}
Title: Re: how to access an integer variable in dll
Post by: markallyn on January 07, 2018, 07:38:32 AM
Sinsi and six_L,

Sinsi,

Perhaps you would be willing to clarify how the linker works.  When I use your EXTERNDEF technique and examine the object file for callmathfuncs with dumpbin /symbols command __imp_myint is shown in the output.  Interestingly, (to me anyway), the __imp prefix is not applied to the two functions in the .dll (multints, addints).  I;m guessing (literally) that this has to do with the use of the PROTO directive in the assembly language file.  I guess a test of this would be to use the EXTERNDEF technique with the two functions and see what the .obj file looks like.  I imagine that if I substitute EXTERNDEFs for the two PROTOs I can't use the INVOKE command....

six_L:  Your response was very comprehensive.  I need more time to thoroughly understand it. 

I've been out of pocket for two days and wasn't able to get back to you folks as soon as I would have liked.

Regards,
Mark
Title: Re: how to access an integer variable in dll
Post by: markallyn on January 09, 2018, 05:53:20 AM
Sinsi, six_L,

So I tested my EXTERNDEF question.  If I use EXTERNDEF to define a function -- in this case, mult2ints -- as EXTERNDEF __imp_mult2ints:PROC, then indeed in the callmathfuncs.obj file mult2ints shows up as __imp_mult2ints.  Interesting. 

Regards,
Mark Allyn
Title: Re: how to access an integer variable in dll
Post by: markallyn on January 09, 2018, 06:38:45 AM
...But, to actually link to the function (mult2ints), the EXTERNDEF mult2ints:PROC declaration is not sufficient.  One also has to explicitly link to the function in the .dll. And, it appears that the mult2ints symbol in the .dll must be declared PUBLIC in the .dll.  It's a nuisance, but it works.  Much more efficient to use the mult2ints PROTO declaration.

Mark Allyn
Title: Re: how to access an integer variable in dll
Post by: markallyn on January 11, 2018, 06:49:47 AM
Hello everyone,

Well, it turns out you can use EXTERNDEF to define an imported function from a DLL.  In the following code I import a data item (myint) and a function (mult2ints) from a DLL (MyMathFuncs.dll):

Quote
nclude \masm32\include64\masm64rt.inc
includelib   \masm32\mystuff\mymathfuncs.lib

add2ints PROTO :QWORD, :QWORD
;mult2ints PROTO :QWORD, :QWORD
printf   PROTO :QWORD, :VARARG

EXTERNDEF   __imp_myint:QWORD
myint   TEXTEQU <__imp_myint>
EXTERNDEF   __imp_mult2ints:PROC
mult2ints TEXTEQU <__imp_mult2ints>

.data
int1   QWORD   10h
int2    QWORD   30h
frmt1   BYTE   "The result of the addition is %d",13,10,0
frmt2   BYTE   "The result of the multiplication is %d",13,10,0
strname BYTE   "mymathfuncs",0
dataname BYTE   "myint",0
funcname BYTE   "mult2ints",0

.data?
libname   QWORD   ?
;mult2ints   QWORD   ?
.code
main   PROC   public


   mov   rcx, myint
   mov   rcx, qword ptr[rcx]
   mov   rdx, int1
   invoke   add2ints, rcx, rdx
   mov   rdx, rax
   invoke   printf, ADDR frmt1, rdx
   


   
   mov   rax, mult2ints
   mov   rdx, int1
   mov   rcx, myint
   mov   rcx, qword ptr[rcx]
   call    qword ptr[rax]
   mov   rdx, rax
   invoke   printf, ADDR frmt2, rdx
   

   ret
main   ENDP
END



So, LoadLibrary isn't necessary to get the .dll function imported.  CALLing its address is sufficient.

Regards,
Mark Allyn
Title: Re: how to access an integer variable in dll
Post by: six_L on January 11, 2018, 04:32:53 PM
hi,markallyn
QuoteSo, LoadLibrary isn't necessary to get the .dll function imported.  CALLing its address is sufficient.
you'v not understood the difference of "static library" and "dynamic library".
QuoteIf you program long enough, you'll find that the programs you wrote usually have some code routines in common. It's such a waste of time to rewrite them everytime you start coding new programs.
A dynamic link library is a kind of common pool of functions. Windows will not load several copies of a DLL into memory so even if there are many instances of your program running at the same time, there'll be only one copy of the DLL that program uses in memory. And I should clarify this point a bit. In reality, all processes that use the same dll will have their own copies of that dll. It will look like there are many copies of the DLL in memory. But in reality, Windows does it magic with paging and all processes share the same DLL code.So in physical memory, there is only one copy of DLL code. However, each process will have its own unique data section of the DLL.
The program links to a DLL at runtime unlike the static library. That's why it's called dynamic link library. You can also unload a DLL at runtime as well when you don't need it. If that program is the only one that uses the DLL, it'll be unloaded from memory immediately. But if the DLL is still used by some other program, the DLL remains in memory until the last program that uses its service unloads it.
However, the linker has a more difficult job when it performs address fixups for the final executable file. Since it cannot "extract" the functions and insert them into the final executable file, somehow it must store enough information about the DLL and functions into the final execuable file for it to be able to locate and load the correct DLL at runtime.
That's where import library comes in. An import library contains the information about the DLL it represents. The linker can extract the info it needs from the import libraries and stuff it into the executable file. When Windows loader loads the program into memory, it sees that the program links to a DLL so it searches for that DLL and maps it into the address space of the process as well and performs the address fixups for the calls to the functions in the DLL.
You may choose to load the DLL yourself without relying on Windows loader.
Title: Re: how to access an integer variable in dll
Post by: markallyn on January 12, 2018, 02:48:01 AM
Good morning, six_L,

1.  What is it you think I don't understand about the difference between "static" and "dynamic"?

2.  Where did the quotation originate?

Regards,
Mark