News:

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

AAAAARGH! Pointer references to structure members

Started by NoCforMe, November 09, 2011, 07:16:28 PM

Previous topic - Next topic

NoCforMe

OK, this is really pissing me off. Either I'm making a total rookie mistake, or ML (Micro$oft's assembler) is really STOOPID.

I'm about to write some code that references lots of structure members through passed pointers. Surely one of the most common things done in programming. Here's a little testbed to illustrate my problem:



.486
.MODEL FLAT, STDCALL
OPTION CASEMAP :NONE

MyStruct STRUCT
  X0 DWORD ?
  Y0 DWORD ?
MyStruct ENDS

$myPtr TYPEDEF PTR MyStruct

.code

TestIt PROC testPtr:$myPtr

MOV EAX, testPtr.X0
MOV EDX, MyStruct PTR testPtr.Y0
RET

TestIt ENDP

END



I tried both ways of referencing members of the structure through the pointer, one using the explicit TYPEDEF, which I pulled straight out of the Microsoft manual for MASM 6.1). But when I assemble it, I get errors on both pointer references:


test.asm(16) : error A2006: undefined symbol : X0
test.asm(17) : error A2006: undefined symbol : Y0


How the hell do you do this really SIMPLE thing? (Note: I'm trying to do what would be done like so in C:  testPtr->X0.)

qWord

FPU in a trice: SmplMath
It's that simple!

NoCforMe

OK, after taking a couple deep breaths, I think I solved my own problem.

I thought about what it would take for the assembler to be able to resolve those pointer references. Since they are using a base address + offset, it wouldn't be possible to do this in a single MOV or LEA operation (since the X86 only allows offsets to be used with registers, not with memory addresses directly):



MOV   EAX, [testPtr +  MyStruct.Xo]



This works:



MOV EAX, testPtr
MOV EDX, [EAX + MyStruct.X0]
MOV EAX, [EAX + MyStruct.Y0]



However, it still raises the question: Why bother using TYPEDEFs (like $myPtr if you can't really use them to deference items? All you're buying is a (slightly) more clear parameter definition. (I could just as easily defined my parameter simply as a DWORD, after all, and it would work the same.)

Less confused, but still not totally clear on the concept ...

MichaelW

The pointer must be in a register, as part of an indirect-memory operand. Two possibilities are:

TestIt  PROC  testPtr:$myPtr

  push ebx
  mov ebx, testPtr

  mov eax, [ebx].MyStruct.X0
  mov edx, [ebx].MyStruct.Y0

  ASSUME ebx:PTR MyStruct
  mov eax, [ebx].X0
  mov edx, [ebx].Y0
  ASSUME ebx:NOTHING

  pop ebx
  ret

TestIt  ENDP


See Indirect Memory Operands here.
eschew obfuscation

qWord

Quote from: NoCforMe on November 09, 2011, 07:26:13 PM
However, it still raises the question: Why bother using TYPEDEFs (like $myPtr if you can't really use them to deference items? All you're buying is a (slightly) more clear parameter definition. (I could just as easily defined my parameter simply as a DWORD, after all, and it would work the same.)
typedef's are useful when using function pointers with INVOKE and/or for parameter typechecking. Also it may be useful for macros, which analyse the datatype.
FPU in a trice: SmplMath
It's that simple!

NoCforMe

Quote from: qWord on November 09, 2011, 07:32:48 PM
Quote from: NoCforMe on November 09, 2011, 07:26:13 PM
However, it still raises the question: Why bother using TYPEDEFs (like $myPtr if you can't really use them to deference items? All you're buying is a (slightly) more clear parameter definition. (I could just as easily defined my parameter simply as a DWORD, after all, and it would work the same.)
typedef's are useful when using function pointers with INVOKE and/or for parameter typechecking. Also it may be useful for macros, which analyse the datatype.

I can see how they're useful for parameter type checking, but I don't see at all how they're otherwise useful with INVOKE. I still can't use them (the TYPEDEFs) at all within the function to dereference pointers; I end up having to use the original name of the structure.member name to reference a field. How does this help?

qWord

Quote from: NoCforMe on November 09, 2011, 07:36:50 PMbut I don't see at all how they're otherwise useful with INVOKE.
FOO typedef proto :DWORD,:QWORD
PFOO typedef ptr FOO

xyz proc pFoo:PFOO

invoke pFoo,123,edx::eax
ret

xyz endp


Quote from: NoCforMe on November 09, 2011, 07:36:50 PMI still can't use them (the TYPEDEFs) at all within the function to dereference pointers; I end up having to use the original name of the structure.member name to reference a field. How does this help?
Well, the x86 Architecture force you to use a register, thus the type definition get lost - in this case you can either access the structure as shown, or use MASM's ASSUME-directive:
assume eax: ptr MYSTRUCT
mov eax,p
mov edx,[eax].member
...
assume eax: nothing
FPU in a trice: SmplMath
It's that simple!

jj2007

Try this one:
include \masm32\include\masm32rt.inc

MyStruct STRUCT
 X0 DWORD ?
 Y0 DWORD ?
MyStruct ENDS

$myPtr TYPEDEF MyStruct

.data
ms MyStruct <123, 456>

.code
TestIt PROC testPtr:$myPtr
print str$(testPtr.X0), 13, 10
print str$(testPtr.Y0), 13, 10
RET
TestIt ENDP

start: invoke TestIt, ms
inkey "ok"
exit
END start

MichaelW


.code
TestIt PROC testPtr:$myPtr
print str$(testPtr.X0), 13, 10
print str$(testPtr.Y0), 13, 10
RET
TestIt ENDP

start: invoke TestIt, ms


The structure members are being passed to the procedure by placing their values directly on the stack, instead of through passed pointers, but even so they are still accessed as indirect-memory operands.

00401000  /$ 55             PUSH EBP
00401001  |. 8BEC           MOV EBP,ESP
00401003  |. 68 08304000    PUSH testptrj.00403008
00401008  |. FF75 08        PUSH DWORD PTR SS:[EBP+8]  ;<========
0040100B  |. E8 70000000    CALL testptrj.00401080
00401010  |. 68 08304000    PUSH testptrj.00403008                   ; /Arg1 = 00403008
00401015  |. E8 CE000000    CALL testptrj.004010E8                   ; \testptrj.004010E8
0040101A  |. 68 1C304000    PUSH testptrj.0040301C                   ; /Arg1 = 0040301C ASCII "
"
0040101F  |. E8 C4000000    CALL testptrj.004010E8                   ; \testptrj.004010E8
00401024  |. 68 1F304000    PUSH testptrj.0040301F
00401029  |. FF75 0C        PUSH DWORD PTR SS:[EBP+C]  ;<========
0040102C  |. E8 4F000000    CALL testptrj.00401080
00401031  |. 68 1F304000    PUSH testptrj.0040301F                   ; /Arg1 = 0040301F
00401036  |. E8 AD000000    CALL testptrj.004010E8                   ; \testptrj.004010E8
0040103B  |. 68 34304000    PUSH testptrj.00403034                   ; /Arg1 = 00403034 ASCII "
"
00401040  |. E8 A3000000    CALL testptrj.004010E8                   ; \testptrj.004010E8
00401045  |. C9             LEAVE
00401046  \. C2 0800        RETN 8


And somehow I doubt that this will help with the AAAAARGH! problem  :bg
eschew obfuscation

jj2007

Quote from: MichaelW on November 10, 2011, 03:44:32 AM

.code
TestIt PROC testPtr:$myPtr
print str$(testPtr.X0), 13, 10
print str$(testPtr.Y0), 13, 10
RET
TestIt ENDP

start: invoke TestIt, ms


The structure members are being passed to the procedure by placing their values directly on the stack, instead of through passed pointers, but even so they are still accessed as indirect-memory operands.


I find it more relevant how they are being used:

mov eax, testPtr.X0
mov ecx, testPtr.Y0

00401004  |.  8B45 08       mov eax, [ebp+8]
00401007  |.  8B4D 0C       mov ecx, [ebp+0C]


The passed pointer is ebp+8.