News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

I have noticed something cute about OPATTR.

Started by hutch--, August 03, 2016, 04:50:14 PM

Previous topic - Next topic

Relvinian

Hey all,

I am glad threads like this are still around for reference and knowledge. They help a lot for my understanding my massive "fun" project that I do when I have spare time.

Anyway, I have spent a while trying to solve a MACRO problem based on ideas and code snippets from this thread.  Overall, I have the general macros written but having problems getting one to work. It is the new one I just wrote tonight.

Anyway, my macro code is below with a few lines of ASM code. These are the full macros, comments and all.  :icon_eek:

The macro I am having trouble with is called:  IsTypeOf.   It ALWAYS returns 0, according to the disassembly view through VS17 debugger.  No matter what I do, I cannot seem to get it to return a 1 when the condition is true. 

What am I missing.   :icon_redface:



;; ============================================================================
;; TTLIB_ASM fundamental MACROS
;; ============================================================================
OPC_MEM   equ < 34 >
OPC_IMM   equ < 36 >
OPC_STRUC equ < 36 >
OPC_GVAR  equ < 42 >
OPC_REG   equ < 48 >
OPC_LVAR  equ < 98 >
OPC_SVAR  equ < 98 >
OPC_FN    equ < 165 >

IsTypeOf MACRO OpCodeUnknown:REQ, OpCodes:VARARG
   ; possible OPCODEs that are valid parameters to macros that create/destory
   ; memory or objects will be the following for 64-bit compilation:
   ;
   ;  34: memory opcode value (ie: [r15+10h])
   ;  36: immediate opcode value (ie: 127, sizeof)
   ;  42: global address opcode value (ie: .data or .data? variable)
   ;  48: register opcode value (ie: rax, rsi, r11)
   ;  98: stack (local) opcode value (ie: [rsp+10h])
   ; 165: function address opcode value (ie: HeapAlloc)
   FOR arg, <OpCodes>
      IF .TYPE(OpCodeUnknown) EQ .TYPE(arg)
         EXITM <TRUE>
      ENDIF
   ENDM
   EXITM <FALSE>
ENDM


;; ----------------------------------------------------------------------------
;; Two macros to create an object and call the Constructor and Destructor. To
;; alloc an array or another type of memory, just use 'new' and 'del' macros.
;; ============================================================================
TTCreateObject MACRO objName:REQ, ctor:REQ
   IF IsTypeOf(dtor, OPC_FN) EQ TRUE
      IF IsTypeOf(objName, OPC_IMM) EQ TRUE
         new objName       ;; allocate memory of specified size
      ELSE
         .ERR <"1st parameter (obj) is not an immediate.">
      ENDIF
      mov rcx, rax      ;; move instance object to 1st parameter
      call ctor         ;; call the constructor function
      EXITM <rax>       ;; return this$ pointer to instance
   ELSE
      .ERR <"2nd parameter (ctor) is not correct.">
   ENDIF
   EXITM <NULL>
ENDM

TTDestroyObject MACRO memPtr:REQ, dtor:REQ
   IF IsTypeOf(dtor, OPC_FN) EQ TRUE
      mov rcx, memPtr   ;; set parameter for destructor
      call dtor         ;; call the destructor function
   ELSE
      .ERR <"Second parameter must be a function address">
   ENDIF
   IF IsTypeOf(memPtr, OPC_MEM,OPC_GVAR,OPC_REG,OPC_LVAR,OPC_SVAR) EQ TRUE
      del memPtr        ;; free the memory pointer
   ELSE
      .ERR < "First parameter must be a register or memory type." >
   ENDIF
   EXITM <>
ENDM



;; ----------------------------------------------------------------------------
;; Macro to call a function. This is an enhance call macro that will
;; call WinAPI functions, TTAPI functions, TTAPI virtual functions, and
;; finally, user defined functions - virtual or not.
;;
;; Note:  When calling a function that is part of an object and has "instance"
;;        data associated to the function, the first parameter (rcx) must have
;;        the instance pointer.
externdef TTObject_CallVirtualFN :proc
jsr MACRO fnORid:REQ, Virtual:=<FALSE>
   IFIDNI <Virtual>,<TRUE>
      ;; transfer the ID over to the function without destroying
      ;; any of the possible parameters passed through the registers.
      mov rax, fnORid   ; id of virtual function
      call TTObject_CallVirtualFN
      ;; now call the virtual mapped function
      call rax
   ELSE
      ;; no virtual function to call, just call normally
      call fnORid
   ENDIF
ENDM


;; ----------------------------------------------------------------------------
;; Macros to allocate and destroy a memory object from the Windows Heap.
;; The memory allocation is also zeroed out upon allocation.
;; If the ctor is defined, will call the constructor for the allocated object.
;; ============================================================================
externdef G_hWinHeap :HANDLE
new MACRO s:REQ
   IF IsTypeOf(s, OPC_MEM, OPC_GVAR, OPC_REG, OPC_LVAR, OPC_SVAR) EQ TRUE
   mov r8, s ;; size of memory to allocate
   mov rdx, (HEAP_GENERATE_EXCEPTIONS or HEAP_ZERO_MEMORY)
   mov rcx, G_hWinHeap ;; handle to the process' heap
   jsr HeapAlloc ;; Win32 API call
   ELSE
      .ERR <"Parameter is not correct type.">
   ENDIF
ENDM

del MACRO m:REQ
   IF IsTypeOf(s, OPC_MEM, OPC_GVAR, OPC_REG, OPC_LVAR, OPC_SVAR) EQ TRUE
   mov r8, m ;; pointer to memory
   xor rdx, rdx ;; no flags for HeapFree
   mov rcx, G_hWinHeap ;; handle to the process' heap
   jsr HeapFree ;; Win32 API call (includes space for shadow registers)
   ELSE
      .ERR <"Parameter is not correct type.">
   ENDIF
ENDM



The ASM lines I am using for testing are the following:


   mov rax, IsTypeOf(TTWinApp_Constructor, OPCODE_FUNCTION)   ; should return TRUE (1)
   mov rax, IsTypeOf(ttAplication.bHiColorIcons, OPC_MEM, OPC_GVAR, OPC_REG, OPC_LVAR, OPC_SVAR)   ; should return TRUE (1)
   mov rax, IsTypeOf(this$, OPC_IMM, OPC_FN) ; should return FALSE (0)

   ; actual line of code in my source file.
   mov oObject.vtable, TTCreateObject(TTMap, TTMap_ctor)



Thank you all for any help with debugging/solving my mistakes.

Relvinian

jj2007

Why don't you post a complete example, ready for copy & paste? Right now I have no time to add the headers etc myself.

Relvinian

Quote from: jj2007 on May 10, 2018, 06:32:08 PM
Why don't you post a complete example, ready for copy & paste? Right now I have no time to add the headers etc myself.

I stripped out the extra MACROs that work (other than the code for the new Macro), so it is as simple example I can get to illustrate the problem.


includelib kernel32.lib


OPC_MEM   equ < 34 >
OPC_IMM   equ < 36 >
OPC_STRUC equ < 36 >
OPC_GVAR  equ < 42 >
OPC_REG   equ < 48 >
OPC_LVAR  equ < 98 >
OPC_SVAR  equ < 98 >
OPC_FN    equ < 165 >

IsTypeOf MACRO OpCodeUnknown:REQ, OpCodes:VARARG
   ; possible OPCODEs that are valid parameters to macros that create/destory
   ; memory or objects will be the following for 64-bit compilation:
   ;
   ;  34: memory opcode value (ie: [r15+10h])
   ;  36: immediate opcode value (ie: 127, sizeof)
   ;  42: global address opcode value (ie: .data or .data? variable)
   ;  48: register opcode value (ie: rax, rsi, r11)
   ;  98: stack (local) opcode value (ie: [rsp+10h])
   ; 165: function address opcode value (ie: HeapAlloc)
   FOR arg, <OpCodes>
      IF .TYPE(OpCodeUnknown) EQ .TYPE(arg)
         EXITM <1>
      ENDIF
   ENDM
   EXITM <0>
ENDM


PUBLIC start

externdef ExitProcess: proc

this$ TEXTEQU < r15 > ; define a "this" pointer for instances of objects

.data
   bHiColorIcons  byte 1

.code

myProc PROC

   xor rax, rax

myProc ENDP

start:
   int 3 ; for WinDbg

   mov rax, IsTypeOf(myProc, OPC_FN)
   mov rax, IsTypeOf(bHiColorIcons, OPC_MEM, OPC_GVAR, OPC_REG, OPC_LVAR, OPC_SVAR)
   mov rax, IsTypeOf(this$, OPC_IMM, OPC_FN)


   mov eax, 1
   call ExitProcess
END


Just cut and paste into your own file and compile in 64-bit mode.  If you compile in 32-bit, I am not sure if the OPCODES I have defined will match.  If not, change them.  ;)

Just to repeat my problem, the "rax' values on the first two should have a 1.  The third should be 0, since it should not match.

Relvinian

jj2007

Hi Relvinian,

This is odd indeed. Where did you get the info on 165: function address opcode value?

Here are some little helper lines for your macro, see tmp$:
OPC_FN=165

IsTypeOf MACRO OpCodeUnknown:REQ, OpCodes:VARARG
Local tmp$, tx
   ; possible OPCODEs that are valid parameters to macros that create/destory
   ; memory or objects will be the following for 64-bit compilation:
   ;
   ;  34: memory opcode value (ie: [r15+10h])
   ;  36: immediate opcode value (ie: 127, sizeof)
   ;  42: global address opcode value (ie: .data or .data? variable)
   ;  48: register opcode value (ie: rax, rsi, r11)
   ;  98: stack (local) opcode value (ie: [rsp+10h]) ; OxPT_Assembler ML
   ; 165: function address opcode value (ie: HeapAlloc) ; OPT_Errlines 0
   FOR arg, <OpCodes>
tx=type(OpCodeUnknown) and 255
tmp$ CATSTR <_TYPE(>, <OpCodeUnknown>, <)=>, %tx
% echo tmp$
tx=.TYPE(OpCodeUnknown) and 255
tmp$ CATSTR <.TYPE(>, <OpCodeUnknown>, <)=>, %tx
% echo tmp$
tx=type(arg) and 255
tmp$ CATSTR <_type(>, <arg>, <)=>, %tx
% echo tmp$
tx=.type(arg) and 255
tmp$ CATSTR <.type(>, <arg>, <)=>, %tx
% echo tmp$
      IF .TYPE(OpCodeUnknown) EQ .TYPE(arg)
         EXITM <1>
      ENDIF
   ENDM
   EXITM <0>
ENDM


For mov rax, IsTypeOf(myProc, OPC_FN) I get these...

... results for ML64 (no error thrown):
_TYPE(myProc)=8
.TYPE(myProc)=0
_type(OPC_FN)=0
.type(OPC_FN)=36


... results for UAsm (which actually throws an error):
_TYPE(myProc)=8
.TYPE(myProc)=37
_type(OPC_FN)=0
.type(OPC_FN)=36


The "_" is just to align the results obtained by TYPE without the dot. Not to be confused with .TYPE, which yields the lobyte of opattr, as you certainly know.

8 is not defined anywhere.
37 is a label (OK for a proc)
36 means immediate value (OK for OPC_FN, which is an immediate)

Relvinian

Quote from: jj2007 on May 10, 2018, 08:05:41 PM
This is odd indeed. Where did you get the info on 165: function address opcode value?

By just doing a mov rax, .TYPE(myFunc).    Once the line was executed, I had a 165 value.  One thing to note about the 165 value on my own system.  I was using a function that had a full frame, stack and exception code in the prologue for stack unwinding.  Do not know if that makes a difference. 

Btw:  All the defines at the top where generated using that method.  I just did a whole bunch of mov rax, ????  to find the value.

I am running Windows 10, latest version with updates.  Do not know if that makes a difference.

As for your output, interesting that the case sensitivity and underscore on the built-in 'type' pre-processor define makes a difference in the values you are getting back.  I wonder if using OPATTR would have the same effect?

Anyway, I will examine your code and the extra "helpers",  as you called it, and hopefully with those, they will help me try and figure out what is going on with my system.

Thank you.

Relvinian

jj2007

case sensitivity doesn't play a role there, and the underscore, as written above, is just there to visualise better (a space gets swallowed by echo).

Google for Microsoft MASM Programmer's Guide, and check the "using macros" chapter. Or search MasmBasic.inc for atLabel

One important thing there is that type(something) is also a numeric value but not only. Test this:
include \masm32\include\masm32rt.inc

.data
WhichType MACRO arg
Local tmp$
  if (type(arg) eq REAL4)
echo arg is a REAL4
  elseif type(arg) eq DWORD
echo arg is a DWORD
  else
echo arg is something different
  endif
  tmp$ CATSTR <... but the numerical value of type(x) is >, %type(arg)
  % echo tmp$
ENDM

SomeDword dd ?
SomeReal4 REAL4 ?

.code
start:
  WhichType SomeDword
  WhichType SomeReal4
  .err  ; we want to see the assembler output
  exit

end start


Output:SomeDword is a DWORD
... but the numerical value of type(x) is 4
SomeReal4 is a REAL4
... but the numerical value of type(x) is 4

Relvinian

Quote from: jj2007 on May 10, 2018, 08:20:29 PM
case sensitivity doesn't play a role there, and the underscore, as written above, is just there to visualise better (a space gets swallowed by echo).

Google for Microsoft MASM Programmer's Guide, and check the "using macros" chapter. Or search MasmBasic.inc for atLabel

One important thing there is that type(something) is also a numeric value but not only. Test this:

Gotcha.

I changed the .TYPE to OPATTR in your macro that output the ECHO to the console window.  I did NOT change any of the text portion in the macro so it still has the strings of "_TYPE", etc.  Here are my result for the same code.


Microsoft (R) Incremental Linker Version 14.13.26128.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/DEBUG
/OUT:test.exe
test.obj
/subsystem:windows
/defaultlib:kernel32.lib
/entry:start
/largeaddressaware
/machine:x64
/debug
Assembling: test.asm
_TYPE(myProc)=8
.TYPE(myProc)=0
_type( 165 )=0
.type( 165 )=36
_TYPE(bHiColorIcons)=1
.TYPE(bHiColorIcons)=0
_type( 34 )=0
.type( 34 )=36
_TYPE(bHiColorIcons)=1
.TYPE(bHiColorIcons)=0
_type( 42 )=0
.type( 42 )=36
_TYPE(bHiColorIcons)=1
.TYPE(bHiColorIcons)=0
_type( 48 )=0
.type( 48 )=36
_TYPE(bHiColorIcons)=1
.TYPE(bHiColorIcons)=0
_type( 98 )=0
.type( 98 )=36
_TYPE(bHiColorIcons)=1
.TYPE(bHiColorIcons)=0
_type( 98 )=0
.type( 98 )=36
_TYPE( r15 )=8
.TYPE( r15 )=0
_type( 36 )=0
.type( 36 )=36
_TYPE( r15 )=8
.TYPE( r15 )=0
_type( 165 )=0
.type( 165 )=36


The following code is what generated the above data:

   mov rax, IsTypeOf(myProc, OPC_FN)
   mov rax, IsTypeOf(bHiColorIcons, OPC_MEM, OPC_GVAR, OPC_REG, OPC_LVAR, OPC_SVAR)
   mov rax, IsTypeOf(this$, OPC_IMM, OPC_FN)


Relvinian

sinsi

Not sure if this works properly, but...

OPC_FN    = 37

IsTypeOf MACRO OpCodeUnknown:REQ, OpCodes:VARARG
local result,s,t
   result = 0
   FOR arg, <OpCodes>
      s = opattr(OpCodeUnknown)
      t = arg
      IF s EQ t
         result = 1
         exitm
      ENDIF
   ENDM
   EXITM <result>
ENDM

From my "extensive" macro experience  :badgrin:
- you should be testing against arg, not .type(arg)
- exitm 1 exits the for loop (not the macro) then the final exitm 0 overwrites it
🍺🍺🍺

Relvinian

Sinsi and JJ207,

JJ207,

Thank you so much for your help.  The output to the console screen really help with trying to figure out what I was doing and what was wrong.


Sinsi,

That is what I basically ended up with after a lot of playing around.  Below is my macro now that fully works that way I wanted it to.


Both,
  Sinsi had the right idea by assigning a variable to the result of OPATTR, which you had done through the ECHO and tmp$, JJ2207.  By assigned a variable and then comparing the variable to the varargs (arg), the match was correct.  As for the 'and 255', I understand normally that strips any bits (sets to zero) which are above 8, but somehow that was messing up the actual return value by OPATTR.  What was your purpose on that JJ2207?

Anyway, thank you both again VERY much for the help.  I am a happy camper now after spending over two days on trying to get this macro to work. Now I can continue on with my project.  Basically, I am taking the "concept" of the Microsoft Foundation Classes (MFC) written in C++, which is Object Oriented programming and class designs, and creating my own simple versions of those in ASM.  So, writing a Windows GUI application that deal extensively with Messages (using ::sendmessage / ::postmessage) will be a much simpler task.  Still a LONG way to go but coming along.


;; ----------------------------------------------------------------------------
; These values were created in Windows 10 using ML64.EXE
; Microsoft (R) Incremental Linker Version 14.13.26128.0
;
; OPCODEs of various labels, functions, register and memory attributes
; Example on how they were defined:  mov rax, opattr(GetProcessHeap)
;; ============================================================================
OPC_MEM     equ < 34 >  ; memory address using LEA or [rcx+rdx*8]
OPC_IMM     equ < 36 >  ; Immediate operands that equate at assembly to values: ie: 1234
OPC_STRUC   equ < 36 >  ; STRUCT and UNION definitions
OPC_ENTRY   equ < 37 >  ; function label - local to ASM file
OPC_OFFSET  equ < 38 >  ; immediate and mem expression
OPC_GVAR    equ < 42 >  ; .data or .data? segment variable
OPC_REG      equ < 48 >  ; register, like RAX, ESI, RSP, etc
OPC_XMM     equ < 77 >  ; XMM registers
OPC_MVAR   equ < 98 >  ; variable in memory using the address
OPC_LVAR    equ < 98 >  ; local variable using keyword LOCAL or another method
OPC_SVAR   equ < 98 >  ; variable on the stack
OPC_FN       equ < 165 > ; function label - external to ASM file or in imported library

IsTypeOf MACRO OpCodeUnknown:REQ, OpCodes:VARARG
   ; opattr superceded the .type in ML.EXE (32-bit) and returns a WORD value
   ; for 64-bit, opattr only returns a BYTE value
   ; .type in either 32-bit or 64-bit returns only a BYTE value.
   rc = 0
   FOR arg, <OpCodes>
      ; IF opattr(OpCodeUnknown) EQ arg
      ; the above does not compute correctly - so changed to below
      opc = opattr(OpCodeUnknown)
      IF opc EQ arg
         rc = 1
         EXITM
      ENDIF
   ENDM
   EXITM <rc>
ENDM


For anyone else that needs or is playing around with OPATTR and .TYPE -- please feel free to my macro and modify it any way you want. 

Relvinian

jj2007

Quote from: Relvinian on May 10, 2018, 11:10:55 PMAs for the 'and 255', I understand normally that strips any bits (sets to zero) which are above 8, but somehow that was messing up the actual return value by OPATTR.  What was your purpose on that JJ2207?

Just testing. Normally, I use and 127 to strip bit 7 (and higher), which is irrelevant for determining the type of the variable. If bit 7 is set, opattr says "references an external label" - and I just see that label is 37. Add 128, and you have "your" 165:
165: function address opcode value (ie: HeapAlloc)

anunitu

I was a little surprised to see "cute" to describe an assembler function,but it might be my old school brain is all.