flat assembler
Message board for the users of flat assembler.

Index > Macroinstructions > constant always a constant in my procedure

Author
Thread Post new topic Reply to topic
alwaysnub



Joined: 30 Mar 2013
Posts: 26
alwaysnub
Hi, i have been working many hours on my own procedure macros, and i have a problem with defining the offsets for ebp witch im using to access the stack.
As you'll see in my code im using a constant to calculate each parameter's offset and then defining it, but constants get calculated before hand and results in all my parameter's having the same offset.

Code:
macro PROC ProcName,[arg] ;Define procedure.
{
    ProcBase = $
    ParamOFFSET = 8

    reverse
    match argName:argType, arg
    \{
    match =BYTE, argType \\{ ParamOFFSET = ParamOFFSET + 1
                             define argName#ItsOffset ParamOFFSET
                             argName equ [EBP+argName#ItsOffset] \\}

    match =WORD, argType \\{ ParamOFFSET = ParamOFFSET + 2
                             define argName#ItsOffset ParamOFFSET
                             argName equ [EBP+argName#ItsOffset] \\}

    match =DWORD, argType \\{ ParamOFFSET = ParamOFFSET + 4
                              define argName#ItsOffset ParamOFFSET
                              argName equ [EBP+argName#ItsOffset] \\}

    match =QWORD, argType \\{ ParamOFFSET = ParamOFFSET + 8
                              define argName#ItsOffset ParamOFFSET
                              argName equ [EBP+argName#ItsOffset] \\}
    \}

    common define ProcName ProcBase

    PUSH EBP
    MOV EBP,ESP
}

PROC testfunction, lkiop:WORD, ihujwef:DWORD
MOV AX,ihujwef    


Not sure how to fix the problem, i have already tried by using define argName#ItsOffset ParamOFFSET - But the rules for ParamOFFSET remain, and so does the problem.
Post 30 Mar 2013, 20:28
View user's profile Send private message Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 2917
Location: [RSP+8*5]
bitRAKE
I know that you would need to count with the preprocessor. Look at baldr's post here. There was another post with specific explanation of this technique, but some time searching - I was unable to find it.

IIRC, FASM macros use a different method at assemble-time. Maybe use the listing tools to RE that process?

_________________
¯\(°_o)/¯ unlicense.org
Post 30 Mar 2013, 22:52
View user's profile Send private message Visit poster's website Reply with quote
hopcode



Joined: 04 Mar 2008
Posts: 563
Location: Germany
hopcode
this is 64bit, ABI is more complex than 32 bit. but may give you an hint. that is very raw and without fanciness
http://code.google.com/p/x64lab/source/browse/develop/macro/mrk_macrow.inc
line 114 and 190
HTH,
Very Happy

_________________
⠓⠕⠏⠉⠕⠙⠑
Post 31 Mar 2013, 08:00
View user's profile Send private message Visit poster's website Reply with quote
alwaysnub



Joined: 30 Mar 2013
Posts: 26
alwaysnub
Ok i fixed it. I simply need to declare the offset as equ then add to it the size of each parameter. See:

Code:
macro PROC ProcName,[arg] ;Define procedure.
{
    define ProcName $
    BytesAtProc = 0
    ParamOffset equ 8

    reverse
    match argName:argType, arg
    \{
    match =BYTE, argType \\{ BytesAtProc = BytesAtProc + 1
                             argName#ItsOffset equ ParamOffset
                             argName equ [EBP+argName#ItsOffset]
                             ParamOffset equ ParamOffset+1 \\}
    match =WORD, argType \\{ BytesAtProc = BytesAtProc + 2
                             argName#ItsOffset equ ParamOffset
                             argName equ [EBP+argName#ItsOffset]
                             ParamOffset equ ParamOffset+2 \\}
    match =DWORD, argType \\{ BytesAtProc = BytesAtProc + 4
                              argName#ItsOffset equ ParamOffset
                              argName equ [EBP+argName#ItsOffset]
                              ParamOffset equ ParamOffset+4 \\}
    match =QWORD, argType \\{ BytesAtProc = BytesAtProc + 8
                              argName#ItsOffset equ ParamOffset
                              argName equ [EBP+argName#ItsOffset]
                              ParamOffset equ ParamOffset+8 \\}
    \}

    common
    define BytesAt#ProcName BytesAtProc
    PUSH EBP
    MOV EBP,ESP
}    


Note that BytesAt#ProcName is used to fix the stack after the call to the procedure. Im da man Very Happy
Post 31 Mar 2013, 18:29
View user's profile Send private message Reply with quote
baldr



Joined: 19 Mar 2008
Posts: 1651
baldr
alwaysnub wrote:
Ok i fixed it.
No, you didn't. Wink

Perhaps define ProcName $ isn't doing what you're expecting (define is a preprocessor directive, and $ is a compiler symbol). Same for define BytesAt#ProcName BytesAtProc.

Win32 ABI uses 32-bit stack values for BYTE and WORD values too (so you might be quite surprized).

Arguments are pushed in reverse before call, yet on stack they're in natural order (i.e. first of them is at ebp+8 anyway).

Difference between define and equ is that latter expands symbolic constants in assigned value prior to definition, while former defers expansion until symbolic constant usage.

Is ItsOffset defined anywhere?
Post 31 Mar 2013, 19:54
View user's profile Send private message Reply with quote
alwaysnub



Joined: 30 Mar 2013
Posts: 26
alwaysnub
its not for windows, its for my own OS, hence the difference in how addressing the stack is working... define ProcName $ should work, though untester as of right now (I know BytesAt#ProcName works correctly because im looking at the bytes in the bin file). Also ItsOffset becomes apart of arg Name. Here is my complete source for your understanding:

Code:
macro PROC ProcName,[arg] ;Define procedure.
{
    define ProcName $
    BytesAtProc = 0
    ParamOffset equ 8

    reverse
    match argName:argType, arg
    \{
    match =BYTE, argType \\{ BytesAtProc = BytesAtProc + 1
                             argName#ItsOffset equ ParamOffset
                             argName equ [EBP+argName#ItsOffset]
                             ParamOffset equ ParamOffset+1 \\}
    match =WORD, argType \\{ BytesAtProc = BytesAtProc + 2
                             argName#ItsOffset equ ParamOffset
                             argName equ [EBP+argName#ItsOffset]
                             ParamOffset equ ParamOffset+2 \\}
    match =DWORD, argType \\{ BytesAtProc = BytesAtProc + 4
                              argName#ItsOffset equ ParamOffset
                              argName equ [EBP+argName#ItsOffset]
                              ParamOffset equ ParamOffset+4 \\}
    match =QWORD, argType \\{ BytesAtProc = BytesAtProc + 8
                              argName#ItsOffset equ ParamOffset
                              argName equ [EBP+argName#ItsOffset]
                              ParamOffset equ ParamOffset+8 \\}
    \}

    common
    define BytesAt#ProcName BytesAtProc
    PUSH EBP
    MOV EBP,ESP
}

macro ENDP ;End procedure.
{
    POP EBP
    RETD
}

macro PCALL ProcName,[arg] ;Directly call procedure.
{
    forward PUSH arg
    common CALL NEAR DWORD ProcName
    ADD ESP,BytesAt#ProcName
}

macro PICALL ProcName,[arg] ;Indirectly call procedure.
{
    forward PUSH arg
    common CALL NEAR DWORD [ProcName]
    ADD ESP,BytesAt#ProcName
}    
Post 31 Mar 2013, 20:08
View user's profile Send private message Reply with quote
baldr



Joined: 19 Mar 2008
Posts: 1651
baldr
alwaysnub,

Well, custom calling conventions are always fun to explore. Let's see:
Code:
        include "proc.finc"

        use32
        org     0x10000

        PROC    abs, x: DWORD
        mov     eax, x
.again: neg     eax
        jo      .done
        js      .again
.done:
        ENDP

        PROC    max, y: DWORD, z: DWORD
        mov     eax, y
        mov     ecx, z
        cmp     eax, ecx
        cmovl   eax, ecx
        ENDP

        align   16

        PCALL   abs, -10
        PCALL   max, eax, 11    
Below is a consolidated and annotated output of PREPSRC and LISTING:
Code:
        include "proc.finc"
        use32
        org     0x10000

;       PROC    abs, x: DWORD
        PUSH EBP                        ;00010000: 55
        MOV EBP,ESP                     ;00010001: 89 E5
;       mov     eax, x
        mov     eax, [EBP+8]            ;00010003: 8B 45 08
.again: neg     eax                     ;00010006: F7 D8
;       jo      .done
        jo      0x1000C                 ;00010008: 70 02
;       js      .again
        js      0x10006                 ;0001000A: 78 FA
.done:
;       ENDP
        POP EBP                         ;0001000C: 5D
        RETD                            ;0001000D: C3

;       PROC    max, y: DWORD, z: DWORD
        PUSH EBP                        ;0001000E: 55
        MOV EBP, ESP                    ;0001000F: 89 E5
;       mov     eax, y
        mov     eax, [EBP+8+4]          ;00010011: 8B 45 0C
;       mov     ecx, z
        mov     ecx, [EBP+8]            ;00010014: 8B 4D 08
        cmp     eax, ecx                ;00010017: 39 C8
        cmovl   eax, ecx                ;00010019: 0F 4C C1
;       ENDP
        POP EBP                         ;0001001C: 5D
        RETD                            ;0001001D: C3

;;; So far, so good. Now we'll try to call them

        align   16                      ;0001001E: 90 90

;       PCALL   abs, -10
        PUSH    -10                     ;00010020: 6A F6
        CALL    0x10022                 ;00010022: E8 FB FF FF FF

;;; WTF?! CALL instruction refers to itself as a target!

        ADD     ESP, 8                  ;00010027: 83 C4 08

;;; Huh? There were only 4 bytes pushed!

;       PCALL   max, eax, 11
        PUSH    eax                     ;0001002A: 50
        PUSH    11                      ;0001002B: 6A 0B
        CALL    0x1002D                 ;0001002D: E8 FB FF FF FF

;;; Now it becomes clearer, I think.

        ADD     ESP, 8                  ;00010032: 83 C4 08

;;; Probably everything is clear now.    
It breaks down to following:
  • formal parameters should have unique names in entire program (or else you'll get erroneous [EBP+x] equ [EBP+y] statement); moreover, parameters are global (in sense that anywhere after function definition parameter name is replaced with [EBP+x]);
  • function can't be called using its name (because that name is defined to be $, i.e. address of current instruction, e.g. call);
  • stack adjustment for any defined function is equal to that of last defined function.
Is this enough? Wink

Anyway, your calling convention looks like weird mix of pascal and cdecl. Parameters pushing order makes it almost unusable for variadic functions, then caller being responsible for stack cleanup seems not very size-optimal. While unaligned stack is almost OK, your PCALL/PICALL macros simply push actual parameters (well, push ax is perfectly valid, does push al?)

And last, but not least, embedded brackets in formal parameters' definition prevent their proper usage. Consider this:
Code:
        PROC bswap8, qw:QWORD
        mov     edx, qw; start with low dword
        bswap   edx; byte-swap it
        mov     eax, ???; we're stuck, high dword seems to be inaccessible    
Without brackets mov eax, [qw+4] works just fine.
Post 01 Apr 2013, 08:29
View user's profile Send private message Reply with quote
alwaysnub



Joined: 30 Mar 2013
Posts: 26
alwaysnub
I have found a solution to most of the problems except defining the address of the function... I tried defining ProcName as a label in the PROC macro (ProcName:) but it did not work. any ideals ??? preferably one that works so that i can define the procedure under the call?

And one last question, how do i/can i add a period to an equ constant?
For example:
ProcName.something equ something
I have tried without success.

Your help much appreciated!
Post 02 Apr 2013, 03:24
View user's profile Send private message Reply with quote
baldr



Joined: 19 Mar 2008
Posts: 1651
baldr
alwaysnub wrote:
I have found a solution to most of the problems except defining the address of the function... I tried defining ProcName as a label in the PROC macro (ProcName:) but it did not work. any ideals ??? preferably one that works so that i can define the procedure under the call?
I commented out define ProcName $ and added ProcName: just before PUSH EBP in PROC macro, now call in PCALL macro refers it correctly.
alwaysnub wrote:
And one last question, how do i/can i add a period to an equ constant?
For example:
ProcName.something equ something
I have tried without success.
You've already used that: ProcName#.something equ something will do the trick. Here # macrooperator serves two purposes: it 1) splits ProcName.something into two separate symbols (namely, ProcName and .something) to allow substitution of former, and 2) joins them afterward.
Post 02 Apr 2013, 06:09
View user's profile Send private message Reply with quote
alwaysnub



Joined: 30 Mar 2013
Posts: 26
alwaysnub
Yes, ProcName: does seem to work, when testing it last night i was thinking the call op used a direct address in its code when its using an offset (which i new), i must have been sleeping Rolling Eyes

however:
argName#.something equ Blah does not work correctly. argName does not become the argName, but instead it is argName... lol

Code:
macro PROC ProcName,[arg] ;Define procedure.
{
    common
    ProcName:
    PUSH EBP
    MOV EBP,ESP

    BytesAtProc = 0
    ParamOffset equ 8

    reverse
    match argName:argType, arg
    \{
    match =WORD, argType \\{ BytesAtProc = BytesAtProc + 2
                             argName equ [EBP+ParamOffset]
                             ParamOffset equ ParamOffset+1
                             argName#.HiByte equ [EBP+ParamOffset]
                             ParamOffset equ ParamOffset+1 \\}

    match =DWORD, argType \\{ BytesAtProc = BytesAtProc + 4
                              argName equ [EBP+ParamOffset]
                              ParamOffset equ ParamOffset+4 \\}

    match =QWORD, argType \\{ BytesAtProc = BytesAtProc + 8
                              argName equ [EBP+ParamOffset]
                              ParamOffset equ ParamOffset+8 \\}
    \}
}

macro ENDP ;End procedure.
{
    POP EBP
    RETD BytesAtProc
}

macro PCALL ProcName,[arg] ;Directly call procedure.
{
    forward PUSH arg
    common CALL NEAR DWORD ProcName
}

macro PICALL ProcName,[arg] ;Indirectly call procedure.
{
    forward PUSH arg
    common CALL NEAR DWORD [ProcName]
}

USE16
ORG 0

PCALL MyFunction, AX, DWORD 249

someData: DD 0

PROC MyFunction, Lkiop:WORD, Ihujwef:DWORD
MOV AX,Lkiop
MOV EAX,Ihujwef
;MOV AL,Lkiop.HiByte   ;not working!!!
MOV AL,argName.HiByte  ;works !!! ???
;ENDP    


What you think?
Post 03 Apr 2013, 00:48
View user's profile Send private message Reply with quote
baldr



Joined: 19 Mar 2008
Posts: 1651
baldr
alwaysnub wrote:
argName#.something equ Blah does not work correctly. argName does not become the argName, but instead it is argName.
Concatenation occurs too early, i.e. when entire macro expands, not its match argName:argType, arg block (which defines argName). To prevent this, you must protect # operator with proper number of backslashes: argName\#.HiByte equ [EBP+ParamOffset]. As a rule of thumb, match number of \'s before operator/directive with that of corresponding { (though latter doesn't have to be backslashed, it's just a matter of style).

Your notation is somewhat inconsistent with general fasm rule: name is an address, [name] is a value at that address. If you remove brackets in argName equ [EBP+ParamOffset], no additional equs needed: simple mov al, [Lkiop+1] will do.

BytesAtProc is always 8 less than ParamOffset, isn't it? You may use RETD ParamOffset-8 in ENDP with the very same effect (and drop BytesAtProc altogether).

There is a trick to perform calculations with preprocessor: rept evaluates base value for counter symbol (it does so for number of repetitions too, but now it's insignificant). So, assuming ParamOffset is 8, rept 1 x:ParamOffset+4 { ParamOffset equ x } results in ParamOffset being 12, not 8+4 as ParamOffset equ ParamOffset+4 does.
Post 03 Apr 2013, 08:00
View user's profile Send private message Reply with quote
alwaysnub



Joined: 30 Mar 2013
Posts: 26
alwaysnub
Quote:
Your notation is somewhat inconsistent with general fasm rule: name is an address, [name] is a value at that address. If you remove brackets in argName equ [EBP+ParamOffset], no additional equs needed: simple mov al, [Lkiop+1] will do.


More like inconsistent with asm in general... am i right? lol
I think it comes down to how much of the assembly the programmer wants to hide (make it look like a higher language).
I personally think MOV AX,Lkiop & MOV AL,Lkiop.HiByte is nice and clean.

Quote:
BytesAtProc is always 8 less than ParamOffset, isn't it? You may use RETD ParamOffset-8 in ENDP with the very same effect (and drop BytesAtProc altogether).


Nice thinking!

Here it is everyone, free example:
Code:
macro PROC ProcName,[arg] ;Define procedure.
{
    common
    ProcName:
    PUSH EBP
    MOV EBP,ESP

    ParamOffset equ 8

    reverse
    match argName:argType, arg
    \{
    match =WORD, argType \\{ argName equ [EBP+ParamOffset]
                             rept 1 x:ParamOffset+1 \\\{ ParamOffset equ x \\\}
                             argName\#.HiByte equ [EBP+ParamOffset]
                             rept 1 x:ParamOffset+1 \\\{ ParamOffset equ x \\\} \\}

    match =DWORD, argType \\{ argName equ [EBP+ParamOffset]
                              rept 1 x:ParamOffset+2 \\\{ ParamOffset equ x \\\}
                              argName\#.HiWord equ [EBP+ParamOffset]
                              rept 1 x:ParamOffset+2 \\\{ ParamOffset equ x \\\} \\}
    \}
}

macro ENDP ;End procedure.
{
    POP EBP
    RETD ParamOffset-8
}

macro PCALL ProcName,[arg] ;Directly call procedure.
{
    common
    if ~ arg eq
    forward
    PUSH arg
    common
    end if
    CALL NEAR DWORD ProcName
}

macro PICALL ProcName,[arg] ;Indirectly call procedure.
{
    common
    if ~ arg eq
    forward
    PUSH arg
    common
    end if
    CALL NEAR DWORD [ProcName]
}

USE16
ORG 0

PCALL MyFunction, AX, DWORD 249
PCALL MyFunction2

PROC MyFunction, Lkiop:WORD, Ihujwef:DWORD
MOV AX,Lkiop
MOV EAX,Ihujwef
MOV AL,Lkiop.HiByte
MOV AX,Ihujwef.HiWord
ENDP

PROC MyFunction2
;No parameter proc.
ENDP    


If you find any problems or have any ideals let us know baldr.
Thanks!
Post 04 Apr 2013, 02:55
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 17352
Location: In your JS exploiting you and your system
revolution
It is unfortunate that Intel decided to use mov for register copy, memory load and memory store. So constructs like 'MOV AX,Lkiop' are ambiguous. Does it load a memory value or a constant? MASM has those unfortunate HLL lookalike constructs also.

To me it would be much clearer to determine that 'MOV AX,[Lkiop]' is a memory read. Unambiguous. And 'MOV AX,Lkiop' is a constant load. Also unambiguous if one adopts the syntax of all memory references must use the square brackets [].

Mixing memory reads and constants loads with the same syntax creates not only a problem of ambiguity, but also how to extract the constant value (if one exists)? And thus the keyword 'offset' was born. A kludge upon a kludge IMO.
Post 04 Apr 2013, 03:10
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2020, Tomasz Grysztar. Also on YouTube, Twitter.

Website powered by rwasa.