News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

CeCall ARM procedure

Started by vozzie, September 27, 2010, 02:39:03 PM

Previous topic - Next topic

vozzie

Hy,

Last month i started again in assembly and i tried to write something in ARM assembly. Now i didn't know much about x86 asm and less "nothing" about ARM asm. So have a little compassion with me here :).

I wrote a library that makes it possible to call a native library dynamically on windows CE(ARM). This to use from .Net. It is possible to call LoadLibrary/GetModuleHandle and GetProcAddress, but no way to call the address. I based it on this codeproject article, http://www.codeproject.com/KB/cs/DynamicInvokeCSharp.aspx. I passed some other solutions to do it, but none were lean and mean like a assembly procedure. One was generating a wrapper class to call it in memory. Another was using a C++ proxy, but not all types are passed correctly i think. Both are here: http://www.rsdn.ru/forum/src/3308865.flat.aspx

Anyway, the code is useless on Windows Phone 7.  :'(

I post the code here and attach it together with some unit tests and a tutorial. But I've better did not write a tutorial being a noob :) so have mercy,...

Hope some enjoy the code... here it is

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; FileName CeCall.s
; Type ARM Assembler
; Description Library for the .Net CE Framework that exports procedures
; to enable dynamic invocation of procedures in native libraries
; Version 0.9
; Timestamp 19:00 5/09/2010
;
;
EXPORT invoke
EXPORT invoke2
EXPORT _DllMainCRTStartup

AREA .code, CODE, ALIGN=4

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
_DllMainCRTStartup PROC
mov r0, #1
mov pc, lr
ENDP

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; invoke pfunc,arg1,arg2,arg3,arg4,...,arg~
;
ALIGN 4
invoke PROC
ldr r12, =dwLR ; load the address of dwLR into r12
str lr, [r12] ; store link register(lr) in dwLR location

mov r12, r0 ; move function ptr in register 0 to register 12

mov r0, r1 ; move first argument from register 1 to register 0
mov r1, r2 ; move second argument from register 2 to register 1
mov r2, r3 ; move third argument from register 3 to register 2

ldr r3, [sp] ; load the fourth argument from the stack into to register 3

add sp, sp, #4 ; add 4 to the stack pointer, let it point to the fifth argument

mov lr, pc ; set return address, (add 8 to the program counter(pc)
; and store in the link register(lr))

mov pc, r12 ; mov function address from register 12 into the program counter(pc)

sub sp, sp, #4 ; subtract 4 from the stack pointer(sp)

ldr r1, =dwLR ; load the address of dwLR into r1
ldr lr, [r1] ; load the value from register 1 into the link register(lr)

mov pc, lr ; move the link register(lr) to the program counter(pc) = return

; maybe change previous 2 lines into "ldr pc, [r1]"
; i've chosen to restore the link register too,
; in case that is expected
ENDP

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; invoke pfunc,arg1,arg2,arg3
;
ALIGN 4
invoke2 PROC
mov r12, r0 ; move function ptr in register 0 to register 12

mov r0, r1 ; move first argument from register 1 to register 0
mov r1, r2 ; move second argument from register 2 to register 1
mov r2, r3 ; move third argument from register 3 to register 2

mov pc, r12 ; move register 12 to the program counter(pc)
ENDP

ALIGN 4
LTORG

AREA .data, DATA, READWRITE, ALIGN=4

dwLR DCD 0x00000000

END



Greetings

Twister


vozzie

Hy,

I wrote one and it's in the attachment of the post. But it's a bit "outdated". I didn't know what to do with my creation, but today decided to drop it here  :P.

Like the "add lr, pc, #8" in the tutorial is changed into "mov lr, pc" what seems to have the same result.

In the tutor there is a html file and links to some sources i used to create it. I think i didn't save all sources...

One strange thing is that microsoft description of the registers purpose is different then the arm manual. That is: r12 is a temporary register(what means it can be modified i think). http://msdn.microsoft.com/en-us/library/ms253983(VS.90).aspx

If you have questions i can try to answer them but i hoped more for hearing some tips or flaws myself.

Update: Watching those registers i just see that there's a bug inside it. I overwrite the called function it's return value...

In this piece i overwrite a DWORD part of a 64bit return value, if the called function returns a 64 bit value. Because r1:r0 is a 64bit return value.

ldr r1, =dwLR ; load the address of dwLR into r1
ldr lr, [r1] ; load the value from register 1 into the link register(lr)


So it should be.

ldr r2, =dwLR ; load the address of dwLR into register 2
ldr lr, [r2] ; load the value from register 2 into the link register(lr)


Greetings

clive

Well it looks reasonable, but if I were calling some arbitrary address I'd probably just cast it. If I had to implement it, I'd probably restack the arguments to make it thread safe.

Here is a 4 parameter example, but you could have routines for different parameter/stack usage. Not quite as general as your example, but more portable.

#include <windows.h>

#include <stdio.h>

// function who's address you know

DWORD __cdecl  foo4(DWORD param1, DWORD param2, DWORD param3, DWORD param4)
{
  printf("%d %d %d %d\n", param1, param2, param3, param4);

  return(param1 + param2 + param3 + param4);
}

int main(int argc, char **argv)
{
  DWORD funcaddr;
  DWORD (__cdecl * call4)(DWORD, DWORD, DWORD, DWORD);

  funcaddr = (DWORD)foo4; // Address you got from GetProcAddress, or whatever

  call4 = (void *)funcaddr;

  printf("%d\n",call4(1001,1002,1003,1004));

  return(0);
}
It could be a random act of randomness. Those happen a lot as well.

vozzie


QuoteWell it looks reasonable,

Thank you,

Quotebut if I were calling some arbitrary address I'd probably just cast it.

I guess you are talking about C/C++. But in .Net i don't think there's a way to do it, unless described like in that link to that russian forum.

QuoteIf I had to implement it, I'd probably restack the arguments to make it thread safe.

That is something i did not take into account. I did not test it for thread safety. Even never tried to pass a imported method to a thread object in .Net.

QuoteHere is a 4 parameter example, but you could have routines for different parameter/stack usage. Not quite as general as your example, but more portable.

I would say can you explain, but i guess that it is more portable is void considering it's intention is to use from .Net and not C++.

It was tested pretty good from a C project, only i must have overlooked the 64bit result value. Tomorow i'll update the attachment,... 

clive

Quote from: vozzie
That is something i did not take into account. I did not test it for thread safety.

Sticking LR into a single globally visible memory location is clearly not thread-safe. The stack is the only safe place to put it, but that would require a lot of manipulation.
It could be a random act of randomness. Those happen a lot as well.

vozzie

#6
Ok, because a second overlapping call would overwrite it, got it :)

Would it be possible to allocate memory and use something like a "lock" to load/store the return address?

I guess using the stack would be difficult because there could be arguments there and it's unknown how many. Would there be a solution to it?

Otherwise changing the arguments so not only the function to call but also the number of arguments would be passed it's known if there are and how many are on the stack... then maybe it would not be needed to store the return address because the stack would not be unbalanced, just adjusted if needed. I'll give that a try. (update: soon )

Thx,