The MASM Forum

General => The Workshop => Topic started by: markB on June 25, 2017, 05:40:16 AM

Title: Length Error when calling DLL from high level language
Post by: markB on June 25, 2017, 05:40:16 AM
I have written a DLL in MASM to call from a high-level language.  The proc assembles correctly and the DLL is created and placed in the proper directory. 

When I call this DLL, I get the following error:  Length Error. 

Here is the PROC header for the MASM code.  It returns a pointer to the variable BlueLineConfig. 

_____

TM460_Pvtx_Values_Sub1 Proc USES ebp Last20EBands1:PTR DWord

mov ebp,Last20EBands1
BlueLineConfig dd 4 dup (0)
mov edi,OFFSET BlueLineConfig

mov eax,DWORD PTR[ebp+114]
mov ebx,DWORD PTR[ebp+108]

[remaining code]

* * *
mov eax,[edi]

RET

TM460_Pvtx_Values_Sub1 EndP

end LibMain
_____

This Proc takes only one input, an array of 140 elements, in DWord form.  I can't see why I would get a Length Error when I am passing exactly what I specified. 

Thanks very much for any help. 

Title: Re: Length Error when calling DLL from high level language
Post by: aw27 on June 25, 2017, 05:54:16 AM
Although I don't see more than a procedure of your "DLL", what you sport is amazing - it is the first time in my life I see someone save the ebp register in a USES clause. On the other hand I don't see you save any of the other registers that should be preserved across function calls.  :icon_rolleyes:

QuoteIt returns a pointer to the variable BlueLineConfig
You spelled correctly, but I still don't see what is BlueLineConfig doing in the middle of the code?

Title: Re: Length Error when calling DLL from high level language
Post by: markB on June 25, 2017, 06:21:29 AM
Thanks for your reply.  Here is the entire code.  There is only one Proc.  I have changed it to push edi as well as ebp, but I still get the same Length Error when calling from the HLL. 


LibMain proc instance:DWORD,reason:DWORD,unused:DWORD

    .if reason == DLL_PROCESS_ATTACH
      mrm hInstance, instance       ; copy local to global
      mov eax, TRUE                 ; return TRUE so DLL will start

    .elseif reason == DLL_PROCESS_DETACH

    .elseif reason == DLL_THREAD_ATTACH

    .elseif reason == DLL_THREAD_DETACH

    .endif

    ret

LibMain endp

;***********

TM460_Pvtx_Values_Sub1 Proc USES edi ebp Last20EBands1:PTR DWord

mov ebp,Last20EBands1
BlueLineConfig dd 4 dup (0)
mov edi,OFFSET BlueLineConfig

;__________

mov eax,DWORD PTR[ebp+114]
mov ebx,DWORD PTR[ebp+108]

cmp eax,ebx

.IF eax > ebx
mov eax,1
mov [edi+0],ebx
.ENDIF
;__________

mov eax,DWORD PTR[ebp+114]
mov ebx,DWORD PTR[ebp+108]

cmp eax,ebx

.IF eax < ebx
mov eax,2
mov [edi+0],ebx
.ENDIF
;__________

mov eax,DWORD PTR[ebp+114]
mov ebx,DWORD PTR[ebp+108]

cmp eax,ebx

.IF eax == ebx
mov eax,0
mov [edi+0],ebx
.ENDIF

;__________

mov eax,DWORD PTR[ebp+120]
mov ebx,DWORD PTR[ebp+114]

cmp eax,ebx

.IF eax > ebx
mov eax,1
mov [edi+4],ebx
.ENDIF

;__________

mov eax,DWORD PTR[ebp+120]
mov ebx,DWORD PTR[ebp+114]

cmp eax,ebx

.IF eax < ebx
mov eax,2
mov [edi+4],ebx
.ENDIF

;__________

mov eax,DWORD PTR[ebp+120]
mov ebx,DWORD PTR[ebp+114]

cmp eax,ebx

.IF eax == ebx
mov eax,0
mov [edi+4],ebx
.ENDIF

;__________

mov eax,[edi]

RET

TM460_Pvtx_Values_Sub1 EndP

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

end LibMain
Title: Re: Length Error when calling DLL from high level language
Post by: jj2007 on June 25, 2017, 06:38:03 AM
The dd 4 dup(0) inserts a series of add [eax], al into your code section. No problem if eax points to some reasonable memory location, but that is hardly what you intended 8)

Which language calls that DLL? And how do you know that [ebp+114] contains something useful?
Title: Re: Length Error when calling DLL from high level language
Post by: aw27 on June 25, 2017, 07:07:46 AM
Quote from: markB on June 25, 2017, 06:21:29 AM
Thanks for your reply.  Here is the entire code.  There is only one Proc.  I have changed it to push edi as well as ebp, but I still get the same Length Error when calling from the HLL. 
You must preserve all registers except eax, ecx and edx wish are assumed to be trashed inside the function.
So, save ebx.
It may not be enough to make your dll work, as mentioned there are other strange things in the code, namely BlueLineConfig must be moved to the .data sectiom, it is not doing anything in the code section and can't be even written to because Windows does not allow that for security reasons.
Title: Re: Length Error when calling DLL from high level language
Post by: markB on June 25, 2017, 07:34:46 AM
I have pushed the registers eax - edx and popped them at the end, and the BlueLineConfig declaration has been moved to the .data section.  However, I am still returned a length error.  I do not know if the error refers to the data passed to the DLL or the data returned by it.  I am continuing to investigate. 


Title: Re: Length Error when calling DLL from high level language
Post by: aw27 on June 25, 2017, 08:16:26 AM
Quote from: markB on June 25, 2017, 07:34:46 AM
I have pushed the registers eax - edx and popped them at the end, and the BlueLineConfig declaration has been moved to the .data section.  However, I am still returned a length error.  I do not know if the error refers to the data passed to the DLL or the data returned by it.  I am continuing to investigate.
I told you to save ebx, why do you save the registers that don't need to be saved?
To track where the error comes from use  a debugger.
Title: Re: Length Error when calling DLL from high level language
Post by: markB on June 25, 2017, 08:24:58 AM
I saved the other registers out of caution.  They are popped back at the end.  Earlier I had pushed only ebx, but I got the same error so I pushed the others.  Will that cause any problems? 

The HLL I am calling this from does not have a debugger.  I encounter the problem upon the call from the HLL to the DLL. 

Title: Re: Length Error when calling DLL from high level language
Post by: jj2007 on June 25, 2017, 08:28:58 AM
Quote from: jj2007 on June 25, 2017, 06:38:03 AMWhich language calls that DLL? And how do you know that [ebp+114] contains something useful?
Title: Re: Length Error when calling DLL from high level language
Post by: markB on June 25, 2017, 08:46:50 AM
It's called from Python.  ebp+114 contains useful data -- the array passed has 140 integers (560 bytes). 

Title: Re: Length Error when calling DLL from high level language
Post by: jj2007 on June 25, 2017, 09:16:01 AM
Interesting. It is possible to call Python from Assembler (Printing a sinus via the FPU, the CRT, the GSL, and Python (http://masm32.com/board/index.php?topic=94.msg43801#msg43801)), but I never tried the other way round.

Re "ebp+114 contains useful data" - how did you test this? It is very unusual to have an array in this location, normally you would simply pass a pointer to the array on the stack (and yes, it would be somewhere at [ebp+x]).

Can you post a snippet that shows how you pass this array from Python to Masm?
Title: Re: Length Error when calling DLL from high level language
Post by: HSE on June 25, 2017, 09:38:38 AM
Theoretically not needed if I remmember well, but you can try:

Replace:mov edi,OFFSET BlueLineConfig
by:lea edi, BlueLineConfig

lea store the complete location of BlueLineConfig, not only the offset (here I think only offset is necessary).
But you have to be sure in [ebp+114] and [ebp+108] are saved complete direccions and not only offset (this is what JJ is asking).

Ok, just what is coming in Last20EBands is relevant
Title: Re: Length Error when calling DLL from high level language
Post by: aw27 on June 25, 2017, 09:39:17 AM
Quote from: markB on June 25, 2017, 07:34:46 AM
I do not know if the error refers to the data passed to the DLL or the data returned by it.  I am continuing to investigate.
I don't see any error reporting facility in the DLL, so it is Python reporting.

Quote
The HLL I am calling this from does not have a debugger.
It is always possible to debug a DLL, but is not straightforward. However, being or not being a DLL is irrelevant in this case, you can easily produce an all-masm test application.

EX:
.686
.model Flat, Stdcall
option Casemap :None 

.data
value dword[100]

.code

TM460_Pvtx_Values_Sub1 Proc USES edi ebp Last20EBands1:PTR DWord
   mov ebp, Last20EBands1
   ; etc, etc
   ret
TM460_Pvtx_Values_Sub1 endp

main PROC
   INVOKE TM460_Pvtx_Values_Sub1, addr value
   ret
main ENDP
END main

(Not tested, just to pass the idea)

Title: Re: Length Error when calling DLL from high level language
Post by: markB on June 25, 2017, 09:43:05 AM
The Python call is:

thisdll = cdll.LoadLibrary("C:\\TM460.dll")
result= thisdll.GetValues(Last20EB1)
_____

The array passed to GetValues (Last20EB1) is a 140-element array of DWords. 

"It is very unusual to have an array in this location, normally you would simply pass a pointer to the array on the stack (and yes, it would be somewhere at [ebp+x])."
ebp is assigned to Last20EBands1 at the top of the MASM code:

TM460_Pvtx_Values_Sub1 Proc USES edi ebp Last20EBands1:PTR DWord

* * *

mov ebp,Last20EBands1

So the index to ebp+114 is an index to that location in the array that was passed in and assigned to ebp. 


Title: Re: Length Error when calling DLL from high level language
Post by: jj2007 on June 25, 2017, 10:46:18 AM
Quote from: markB on June 25, 2017, 09:43:05 AM
mov ebp,Last20EBands1

So the index to ebp+114 is an index to that location in the array that was passed in and assigned to ebp.

OK, that makes sense. I made a little test with Python 3.4.3 - is this roughly the syntax you are using?

import ctypes
hAlgos = ctypes.WinDLL("StringAlgos")
# Instring proc ; StartPos, lpSource, lpPattern, sMode
hAlgos.Instring.restype = ctypes.c_char_p
print('match: ')
print(hAlgos.Instring(1,"This is a string", "string", 0))


The StringAlgos.dll is attached.

You know that you can use Olly as Just In Time debugger? It works fine, and would allow to see what goes wrong under the hood. It's a can of worms btw, here is what happens when you return from the DLL:CPU Disasm
Address           Hex dump                     Command                            Comments
1D1ADDD7          ³.  FF55 1C                  call near [ebp+1C]                 ; dive into the DLL
1D1ADDDA          ³.  8B4D 0C                  mov ecx, [ebp+0C]
1D1ADDDD          ³.  8B09                     mov ecx, [ecx]
1D1ADDDF          ³.  8B09                     mov ecx, [ecx]


> thisdll.GetValues(Last20EB1)
Can you add a little console print to the DLL to see if [ebp+0],  [ebp+4],  [ebp+8] contain the expected values?
Title: Re: Length Error when calling DLL from high level language
Post by: markB on June 25, 2017, 12:09:44 PM
Thanks very much for your reply.  Because a deadline is fast approaching, I had to re-write this differently, and now it runs and serves my needs in this case. 

I'll come back to this thread later, but I want to thank everybody who answered my question -- you all posted very helpful information. 

Title: Re: Length Error when calling DLL from high level language
Post by: aw27 on June 25, 2017, 05:07:44 PM
Quote from: markB on June 25, 2017, 12:09:44 PM
Thanks very much for your reply.  Because a deadline is fast approaching, I had to re-write this differently, and now it runs and serves my needs in this case. 

That's a cool idea to rewrite it from scratch, particularly because nobody uses "USES EBP" in this World.  :icon_rolleyes:
In spite of that, the apparent cause for the error was that you can not index arrays in MASM as you do in any other language. In MASM you must take into account the size of the element itself (in MASM they call it TYPE, so the confusion starts just here).

As an illustration:


.686
.model Flat, Stdcall
option Casemap :None 

includelib h:\masm32\lib\msvcrt.lib ; (*)
printf PROTO C :VARARG

Counter=0

.data

format db "%d",13,10,0
someStruct STRUCT
REPEAT 1024
Counter = Counter + 1
dword Counter
ENDM
someStruct ENDS

myStruct someStruct <>

.code

TM460_Pvtx_Values_Sub1 Proc USES ebx edi Last20EBands1:PTR DWord
mov edi, Last20EBands1
xor ebx, ebx
.while ebx<sizeof(someStruct)/TYPE(DWORD)
mov edx, dword ptr [edi+ebx*TYPE(DWORD)]
INVOKE printf, offset format, edx
inc ebx
.endw
ret
TM460_Pvtx_Values_Sub1 endp

main PROC
INVOKE TM460_Pvtx_Values_Sub1, offset myStruct
ret
main ENDP
END main



(*) Replace as needed. Someone may tell that you should use  \masm32\lib\msvcrt.lib because relative addresses work all the time. Of course, they do not when you scatter things all over the place,
Build with:
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\ml" /c /coff test.asm
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\link" /SUBSYSTEM:console test.obj
Title: Re: Length Error when calling DLL from high level language
Post by: coder on June 25, 2017, 08:37:46 PM
Quote from: markB on June 25, 2017, 05:40:16 AM
I have written a DLL in MASM to call from a high-level language.  The proc assembles correctly and the DLL is created and placed in the proper directory. 

When I call this DLL, I get the following error:  Length Error. 

Here is the PROC header for the MASM code.  It returns a pointer to the variable BlueLineConfig. 

_____

TM460_Pvtx_Values_Sub1 Proc USES ebp Last20EBands1:PTR DWord

mov ebp,Last20EBands1
BlueLineConfig dd 4 dup (0)
mov edi,OFFSET BlueLineConfig

mov eax,DWORD PTR[ebp+114]
mov ebx,DWORD PTR[ebp+108]

[remaining code]

* * *
mov eax,[edi]

RET

TM460_Pvtx_Values_Sub1 EndP

end LibMain
_____

This Proc takes only one input, an array of 140 elements, in DWord form.  I can't see why I would get a Length Error when I am passing exactly what I specified. 

Thanks very much for any help.

I don't understand what your code does, but my initial guess is that you do not fully understand how the real, low-level codes of a "proc" works. For example, EBP is a taboo register once high-level proc is involved, especially when you have both local variables and function arguments in the function. Look;

push ebp
mov ebp,esp
push ebp                  ;your attempt to save EBP
sub esp,12
mov eax,[ebp+8]                    ;first argument
mov ebp,something_else
mov dword ptr[ebp-8],'ABC'   ;local variable. Now EBP is corrupt due to previous line
mov ebx,[ebp+12]                  ; second argument also has corrupt EBP.


So while you think you have saved the EBP, the truth is, your "proc" uses it internally.

Title: Re: Length Error when calling DLL from high level language
Post by: jj2007 on June 25, 2017, 08:45:50 PM
Quote from: coder on June 25, 2017, 08:37:46 PMmy initial guess is that you do not fully understand how the real, low-level codes of a "proc" works.

@Mark: Ignore this, it's wrong. Test it:include \masm32\include\masm32rt.inc

.code
MyTest proc uses esi edi ebx ebp hwnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
LOCAL MyLocVar:DWORD
  print str$(hwnd), 9, "hwnd", 13, 10
  print str$(uMsg), 9, "uMsg", 13, 10
  print str$(wParam), 9, "wParam", 13, 10
  print str$(lParam), 9, "lParam", 13, 10
  ret
MyTest endp

start:
  invoke MyTest, 11111, 22222, 33333, 44444
  inkey "ok?"
  exit

end start


It is quite unusual to save ebp but the code would work. Your problem is elsewhere, probably in the way you call the DLL from Python.
Title: Re: Length Error when calling DLL from high level language
Post by: aw27 on June 25, 2017, 09:32:53 PM
Quote from: jj2007 on June 25, 2017, 08:45:50 PM
It is quite unusual to save ebp but the code would work. Your problem is elsewhere, probably in the way you call the DLL from Python.
Jochen,
In his particular case works simply because he loads only once a variable from the stack to the ebp register. mov ebp, [ebp+8]. In general it would not work when you use ebp as the frame base pointer.  :t
Title: Re: Length Error when calling DLL from high level language
Post by: hutch-- on June 25, 2017, 09:52:38 PM
The general idea would be to fix all of the register usage first, put it into a conventional procedure with a stack frame and make sure you properly observe the Intel "Application Binary Interface" for Win32. If you don't do this you get unexpected crashes as the OS expects certain registers not to be changed between procedure calls. Nor is it sensible to just experiment with one OS version because there is no garrantee that it will be the same in another version. The only safe way is to properly observe the previously mentioned ABI. I would imagine that Python properly observes the Intel ABI so you should be safe in doing the same.

Get it up and going reliably and if there is some need, you can do things like removing the stack frame to reduce the call overhead but be aware that this only matters on very short procedures, on longer ones and ones that call other procedures it is a waste of time.
Title: Re: Length Error when calling DLL from high level language
Post by: jj2007 on June 25, 2017, 09:59:04 PM
Quote from: aw27 on June 25, 2017, 09:32:53 PM
Jochen,
In his particular case works simply because he loads only once a variable from the stack to the ebp register. mov ebp, [ebp+8]. In general it would not work when you use ebp as the frame base pointer.  :t

Right: You cannot use ebp as a variable and use local variables. But the usage of ebp as such does no harm as long as you don't use any locals and/or arguments.

I posted here (http://masm32.com/board/index.php?topic=6356.0) a Python script using a simple DLL. It's messy, to say the least. Python does strange things with strings under the hood 8)

However, Mark's problem seems very easy to solve - exchanging ebp with ebx (or esi, edi) should be sufficient. The mechanism of passing arguments as such works perfectly.
Title: Re: Length Error when calling DLL from high level language
Post by: aw27 on June 25, 2017, 11:38:47 PM
Quote from: jj2007 on June 25, 2017, 09:59:04 PM
I posted here (http://masm32.com/board/index.php?topic=6356.0) a Python script using a simple DLL. It's messy, to say the least. Python does strange things with strings under the hood 8)
Teachers and people from universities are so proficient in Python, Perl and eventually F (and some in Javascript as well, grrrr) that I don' t dare to argue with them about the virtues of other stuff.  :badgrin:
Title: Re: Length Error when calling DLL from high level language
Post by: nidud on June 26, 2017, 12:08:26 AM
deleted
Title: Re: Length Error when calling DLL from high level language
Post by: jj2007 on June 26, 2017, 12:19:46 AM
Error A2210: Syntax error: printf ::)
Title: Re: Length Error when calling DLL from high level language
Post by: coder on June 26, 2017, 12:28:24 AM
Quote from: jj2007 on June 25, 2017, 08:45:50 PM
Quote from: coder on June 25, 2017, 08:37:46 PMmy initial guess is that you do not fully understand how the real, low-level codes of a "proc" works.

@Mark: Ignore this, it's wrong. Test it:include \masm32\include\masm32rt.inc

.code
MyTest proc uses esi edi ebx ebp hwnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
LOCAL MyLocVar:DWORD
  print str$(hwnd), 9, "hwnd", 13, 10
  print str$(uMsg), 9, "uMsg", 13, 10
  print str$(wParam), 9, "wParam", 13, 10
  print str$(lParam), 9, "lParam", 13, 10
  ret
MyTest endp

start:
  invoke MyTest, 11111, 22222, 33333, 44444
  inkey "ok?"
  exit

end start


It is quite unusual to save ebp but the code would work. Your problem is elsewhere, probably in the way you call the DLL from Python.

JJ

I don't quite follow which part of my advice is wrong. EBP is a base pointer register, extremely common in stack programming. I thought you knew...

Title: Re: Length Error when calling DLL from high level language
Post by: jj2007 on June 26, 2017, 12:39:33 AM
Quote from: coder on June 26, 2017, 12:28:24 AMI thought you knew...

Yes I know.
Title: Re: Length Error when calling DLL from high level language
Post by: hutch-- on June 26, 2017, 01:24:31 AM
Something I have done in the past is copy arguments to registers from the EBP based addresses then push EBP, use it then pop it but it is risky stuff. You can also do old trick from the win3 days of DLLs and store registers in global variables and while its not elegant, it does work OK.