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.
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?
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
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?
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.
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.
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.
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.
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?
It's called from Python. ebp+114 contains useful data -- the array passed has 140 integers (560 bytes).
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?
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
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)
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.
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?
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.
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
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.
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.
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
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.
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.
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:
deleted
Error A2210: Syntax error: printf ::)
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...
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.