News:

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

Main Menu

Win32 API: PHYSICALDRIVE, READFILE, ERROR_INVALID_PARAMETER, memory alignment

Started by MtheK, July 21, 2013, 01:21:47 AM

Previous topic - Next topic

MtheK

  I just solved a very frustrating systems problem reading volumes.
I found mbrboot.exe on your site; a very cool program to read the MasterBootRecord, tho I have problems re-directing the O/P. So I added some of that source to my own VOLUMES program, basically a stripped-down DISKPART, now running ELEVATED, but instead of dumping hex, I just copy the records to a .log file of my own (basically for compare/backup/restore) and dump hex w/my own program. But, now I can do this for every drive in GETLOGICALDRIVES/QUERYDOSDEVICE as well as the default drive, ie:
PATHSET VOLUMES.E.Sat.07202013. 8342437
MBR*
C$\HarddiskVolume2    f                    dNTFS       7A13ECF8
E$\HarddiskVolume3    fHP_RECOVERY         dNTFS       D2BF2072
F$\HarddiskVolume4    fHP_TOOLS            dNTFS       D45B05E7
G \CdRom0                                                         NOT READY
H$\HarddiskVolume5    r021-IV               NTFS       02480666
VOLUMES Sat 07/20/2013  8:34:24.95 All OK.
        1 file(s) copied.
DUMPFILE Sat 07/20/2013  8:34:25.23 All OK.
PATHSET VOLUMES.D.Sat.07202013. 8342437

  The problem:
. occasionally, after changing my MASM program in seemingly un-related areas,
this would stop working! That is, this stupid INVALID_PARAMETER/87d would
occur for EVERY eligible drive???!!!

  After trying MANY things for MANY hours, I stumbled on something that would
work, at least until the next time I changed my program, which, in my case,
wasn't very long!

  Googling my brains out found NOTHING that worked for me!

  Being an IBM mainframe/systems programmer, there would be times when memory alignment was required in my assembler program. So I added filler before the READFILE buffer, 1 byte at a time, until/if it worked again. That's when I then
noticed the assembler listings' displacement for that buffer was QWORD-aligned;
that is, it ended in a 0 or 8h (an 8h in my working case).

  That was the key; I moved my buffer IMMEDIATELY after the .DATA section, where
the displacement is 0x00000000. Now it works even if I add labels to my .DATA,
as long as they are AFTER this buffer!

  Of course, all this ASSUMES that the .DATA beginning address is always, say,
page-aligned (4K). In my WinDbg testing, it was always 0x00404000, so I can
"guarantee" alignment, and, thus, a working program.

  In hindsight, it would have been nice if the error RC was something like,
say, ERROR_INVALID_MEMORY_ALIGNMENT, instead of INVALID_PARAMETER. Perhaps there is a URL somewhere that discusses this very problem; I just can't find it.

  In IBM-land, I have a macro to align storage:
&NAME    $ALIGN &TO=4096,&BEGIN=
.*   THIS MACRO WILL ALIGN THE ASM 'LOC' VALUE TO THE CLOSEST UPWARD
.* DESIRED BYTE BOUNDARY VIA A 'DS' STATEMENT.
         DS    ((((*-&CT-1)/&TO)*&TO)+&TO+&CT-*)X
*                                  ZEPPSPIE, 11/2/79

  This finally leads me to my MASM question; how do I align storage in the
.DATA section? I assume C++,etc doesn't have this issue; I know COBOL 01
levels align. How do I "address" the "LOCation counter" (or whatever the
PC equivalent is (ie: field1 of .LST)):
00000000  00000003 [        bsJump              db 3 dup(?)
       00
      ]


jj2007

Quote from: MtheK on July 21, 2013, 01:21:47 AMhow do I align storage in the .DATA section?

Try
.data
whatever db "hi", 0
align 16
myqwords dq 1000 dup(?)

If you really need 4096 byte alignment, use VirtualAlloc, or open a new segment:
include \masm32\include\masm32rt.inc

MySeg segment flat 'DATA?'
MyBuffer db 1000 dup(?)
MySeg ends

.code
AppName db "Masm32:", 0

start: MsgBox 0, hex$(offset MyBuffer), addr AppName, MB_OK
exit

end start


MtheK

  Hhhmmm...for some reason, 'align 16' won't work for me. However, on the plus side, I was able to make a PC equivalent of my old MainFrame macro:

00000000                 .DATA

00000000 = 00000000      DATASTART EQU  $
00000000  00                 BYTE   ?
                    ALIGN  2
00000002  00                 BYTE   ?
                    ALIGN  16
VOLUMES.ASM(82) : error A2189: invalid combination with segment alignment : 16
00000003  00                 BYTE   ?
                    ALIGN  2*2048
VOLUMES.ASM(84) : error A2189: invalid combination with segment alignment : 4096
00000004  00                 BYTE   ?

= 00001000         ALIGNSIZE  EQU 4096
00000005 = 00000005      ALIGNLOC   EQU $ - DATASTART
00000005  00000FFB [              BYTE  ((((ALIGNLOC-1)/ALIGNSIZE)*ALIGNSIZE)+ALIGNSIZE-ALIGNLOC) DUP(00h)
       00
      ]

00001000  00000003 [        bsJump              db 3 dup(?)
       00
      ]

In my case, 'bsJump' was 4K-aligned as indicated by its' 0x00001000 LOCation counter.  Thankx...

japheth

Quote from: MtheK on July 21, 2013, 06:10:40 AM
   ALIGNSIZE  EQU 4096
   ALIGNLOC   EQU $ - DATASTART
           BYTE  ((((ALIGNLOC-1)/ALIGNSIZE)*ALIGNSIZE)+ALIGNSIZE-ALIGNLOC) DUP(00h)

Yes, that's one possibility. You could instead use the AND operator:


ALIGNSIZE  EQU 4096
ALIGNLOC   EQU $ - DATASTART
BYTE  ALIGNSIZE - (ALIGNLOC and (ALIGNSIZE-1)) DUP(00h)


and replace the BYTE-DUP combination by the ORG directive:

ORG  $ + ALIGNSIZE - (ALIGNLOC and (ALIGNSIZE-1))


MtheK

  ORG looks cool; seems similar to IBM. It worked fine for me, however,
there is 1 difference btx that and mine. For mine, if I'm already aligned,
I basically become a NO-OP, yet the ORG adds an extra full boundary size:

= 00001000         ALIGNSIZE EQU  4096
0000167B = 0000167B      ALIGNLOC1 EQU  $ - DATASTART
= 00000985         ALIGNLOC1D EQU ((((ALIGNLOC1-1)/ALIGNSIZE)*ALIGNSIZE)+ALIGNSIZE-ALIGNLOC1)
0000167B  00000985 [                BYTE ALIGNLOC1D DUP(00h)           ; this aligns to 4K
       00
      ]
00002000 = 00002000      ALIGNLOC2 EQU  $ - DATASTART
= 00000000         ALIGNLOC2D EQU ((((ALIGNLOC2-1)/ALIGNSIZE)*ALIGNSIZE)+ALIGNSIZE-ALIGNLOC2)
                      BYTE ALIGNLOC2D DUP(00h)           ; this is 0 since right after 1st one
00002000 = 00002000      ALIGNLOC3 EQU  $ - DATASTART
                      ORG  $ + ALIGNSIZE - (ALIGNLOC3 and (ALIGNSIZE-1))
00003000  00                   BYTE ?
00003001 = 00003001      ALIGNLOC4 EQU  $ - DATASTART
                      ORG  $ + ALIGNSIZE - (ALIGNLOC4 and (ALIGNSIZE-1))
00004000  00000003 [        bsJump              db 3 dup(?)
       00
      ]

In my case, the jump from 0x2000 to 0x3000 by the ORG, where my LOC2 stayed at 0x2000.  Maybe only a concern for large sizes?

japheth

Quote from: MtheK on July 22, 2013, 12:30:39 AM
there is 1 difference btx that and mine. For mine, if I'm already aligned,
I basically become a NO-OP, yet the ORG adds an extra full boundary size:

You're right. I'd say the AND has to be moved a bit:


ORG  $ + ( ( ALIGNSIZE - ALIGNLOC ) and ( ALIGNSIZE-1 ) )

MtheK

  Perfect; thankx:

00002000  00                   BYTE ?
00002001 = 00002001      ALIGNLOC3 EQU  $ - DATASTART
                       ORG  $ + ( ( ALIGNSIZE - ALIGNLOC3 ) and ( ALIGNSIZE-1 ) )
00003000 = 00003000      ALIGNLOC4 EQU  $ - DATASTART
                       ORG  $ + ( ( ALIGNSIZE - ALIGNLOC4 ) and ( ALIGNSIZE-1 ) )
00003000  00000003 [        bsJump              db 3 dup(?)
       00
      ]

  As an FYI, I think the mbrboot.exe on your site only does PE1.
When I run it on another laptop, it shows 1 512-byte record, yet
my program saves 2 512-byte records (ie: DIR size). I loop thru
the 4 PARTENTRYs and, in my case, PE2 has a partition, verified
by IOCTL_STORAGE_GET_DEVICE_NUMBER.

MtheK

  I FINALLY found the PC equivalent of the IBM &SYSECT, which is the
beginning of the name of the currect section (ie: program segment).
Now, as in my old macro, I no longer need to add the "DATASTART" label
after .DATA, and, more importantly, I no longer need to add a unique
label before every alignment:

           ORG  $ + ( ( ALIGNSIZE - ($ - _DATA) ) and ( ALIGNSIZE-1 ) )

Perhaps this is obvious to you all, but it wasn't for me.

jj2007

Quote from: MtheK on July 24, 2013, 01:21:57 AM
Perhaps this is obvious to you all, but it wasn't for me.

Definitely not to all (I didn't know it). Here is a snippet for testing the built-in @CurSeg macro:

include \masm32\include\masm32rt.inc
.data?
   tmp$ CATSTR <The current segment =>, @CurSeg
   % echo tmp$
.const
   tmp$ CATSTR <The current segment =>, @CurSeg
   % echo tmp$
.data
   tmp$ CATSTR <The current segment =>, @CurSeg
   % echo tmp$

MyPersonalSegment SEGMENT "whatever"
MyCount   dd ?
   tmp$ CATSTR <The current segment =>, @CurSeg
   % echo tmp$
MyPersonalSegment ENDS

.code
   tmp$ CATSTR <The current segment =>, @CurSeg
   % echo tmp$
start:
   .err   ; force an error to stop assembly
   exit

end start

MtheK

  I like @CurSeg:
The current segment = _BSS
The current segment = CONST
The current segment = _DATA
The current segment = MyPersonalSegment
The current segment = _TEXT

MtheK

  And so, hopefully, the last and only statement needed to align storage is:

           ORG     $ + ( ( ALIGNSIZE - ($ - @CurSeg) ) and ( ALIGNSIZE-1 ) )

I cheat 'cause ALIGNSIZE could just be replaced w/an actual number instead of an EQUATE.

jj2007

I thought, so, too - but now I am no longer sure:
004060D0        v1
004070D0        v1a
00403010        v2
00404010        v2a
00405000        v3
00406000        v3a
00408000        v4
00409000        v4a
00401000        v5
00402000        v5a


include \masm32\include\masm32rt.inc

MySize=1000h

MyAlign MACRO ALIGNSIZE
LOCAL neworg, tmp$
  ORG     $ + ( ( ALIGNSIZE - ($ - @CurSeg) ) and ( ALIGNSIZE-1 ) )
ENDM

.data?
v1 dd ?
tmp$ CATSTR <The current segment =>, @CurSeg
% echo tmp$
MyAlign MySize
v1a dd ?

.const
v2 dd 333
tmp$ CATSTR <The current segment =>, @CurSeg
% echo tmp$
MyAlign MySize
v2a dd 4444

.data
v3 dd 555
tmp$ CATSTR <The current segment =>, @CurSeg
% echo tmp$
MyAlign MySize
v3a dd 666


MyPersonalSegment SEGMENT "whatever"
v4 dd 777
tmp$ CATSTR <The current segment =>, @CurSeg
% echo tmp$
MyAlign MySize
v4a dd 888
MyPersonalSegment ENDS

.code
v5 dd 999
tmp$ CATSTR <The current segment =>, @CurSeg
% echo tmp$
MyAlign MySize
v5a dd 888

start:
print hex$(offset v1), 9, "v1", 13, 10
print hex$(offset v1a), 9, "v1a", 13, 10
print hex$(offset v2), 9, "v2", 13, 10
print hex$(offset v2a), 9, "v2a", 13, 10
print hex$(offset v3), 9, "v3", 13, 10
print hex$(offset v3a), 9, "v3a", 13, 10
print hex$(offset v4), 9, "v4", 13, 10
print hex$(offset v4a), 9, "v4a", 13, 10
print hex$(offset v5), 9, "v5", 13, 10
print hex$(offset v5a), 9, "v5a", 13, 10
; .err ; force an error to stop assembly
exit

end start

MtheK

  Hhhmmm...in truth, I only know about .DATA:
00002000  00                   BYTE ?
                       ORG  $ + ( ( ALIGNSIZE - ($ - @CurSeg) ) and ( ALIGNSIZE-1 ) )
                       ORG  $ + ( ( ALIGNSIZE - ($ - @CurSeg) ) and ( ALIGNSIZE-1 ) )
00003000  00000003 [        bsJump              db 3 dup(?)
       00
      ]

  I tried .CODE and that failed like yours:
00000038  E8 00000FAA               CALL  PREP
                       ORG  $ + ( ( ALIGNSIZE - ($ - @CurSeg) ) and ( ALIGNSIZE-1 ) )
00000FD1  E8 00000836               CALL  PHYSICAL0                         ; read MBR/drive0

  I went back to naming it manually, and that also failed the same way:
00000038  E8 00000FAA               CALL  PREP
                       ORG  $ + ( ( ALIGNSIZE - ($ - _TEXT) ) and ( ALIGNSIZE-1 ) )
00000FD1  E8 00000836               CALL  PHYSICAL0                         ; read MBR/drive0

  HOWEVER, if I go back to my old IBM macro, it works fine:
00000038  E8 00000FD9               CALL  PREP
0000003D 0000003D = 00000FC3   ALIGNLOC5D EQU ((((($ - @CurSeg)-1)/ALIGNSIZE)*ALIGNSIZE)+ALIGNSIZE-($ - @CurSeg))
0000003D  00000FC3 [                BYTE ALIGNLOC5D DUP(00h)           ; this aligns to 4K
       00
      ]
00001000  E8 00000836               CALL  PHYSICAL0                         ; read MBR/drive0
Of course, none are runnable this way, but it does indeed align it???






jj2007

Either there is something seriously wrong with ORG, or I miss the point completely ::)

Try this with ML and JWasm (with very different results):
include \masm32\include\masm32rt.inc

MySize=1000h ; try 100h - looks better...

MyAlign MACRO ALIGNSIZE
  addbytes=($ - @CurSeg) ; and (ALIGNSIZE-1)
  addbytes=ALIGNSIZE-addbytes
  % echo @CatStr(<adding >, %addbytes, < in seg >, @CurSeg)
  ORG   $ + addbytes ;; same as ORG     $ + ( ( ALIGNSIZE - ($ - @CurSeg) ) and ( ALIGNSIZE-1 ) )
ENDM

.data?
v1 dd ?
MyAlign MySize
v1a dd ?

.const
v2 db 111
MyAlign MySize
v2a dd 222

.data
v3 RECT <12, 34, 56, 78>
MyAlign MySize
v3a dd 666

MyPersonalSegment SEGMENT "whatever"
v4 dd 777
MyAlign MySize
v4a dd 888
MyPersonalSegment ENDS

.code
v5 dd 999
MyAlign MySize
v5a dd 888

start:
print hex$(offset v1), 9, "v1 (.data?)", 13, 10
print hex$(offset v1a), 9, "v1a", 13, 10, 10
print hex$(offset v2), 9, "v2 (.const)", 13, 10
print hex$(offset v2a), 9, "v2a", 13, 10, 10
print hex$(offset v3), 9, "v3 (.data)", 13, 10
print hex$(offset v3a), 9, "v3a", 13, 10, 10
print hex$(offset v4), 9, "v4 (MyPersonalSegment)", 13, 10
print hex$(offset v4a), 9, "v4a", 13, 10, 10
print hex$(offset v5), 9, "v5 (.code)", 13, 10
print hex$(offset v5a), 9, "v5a", 13, 10, 10
exit

end start


The echoes are OK, the rest is not:
adding 4092 in seg _BSS
adding 4095 in seg CONST
adding 4080 in seg _DATA
adding 4092 in seg MyPersonalSegment
adding 4092 in seg _TEXT

:(

MtheK

  It looks like the 1st ORG, using the formula in my old macro, comes up w/the same value as EQU, but it doesn't ADD to the loc ctr as BYTE does. But then, the 2nd ORG comes up with F94h, but shouldn't it be the same value as the 1st ORG since both ORGs start at 3Dh?

00000038  E8 00000FAA               CALL  PREP

0000003D         setloc1 byte 0 dup(00h)

0000003D 0000003D = 00000FC3   ALIGNLOC5D EQU ((((($ - @CurSeg)-1)/ALIGNSIZE)*ALIGNSIZE)+ALIGNSIZE-($ - @CurSeg))

                       ORG   ((((($ - @CurSeg)-1)/ALIGNSIZE)*ALIGNSIZE)+ALIGNSIZE-($ - @CurSeg))
00000FC3         setloc2 byte 0 dup(00h)

                     org   setloc1
0000003D         setloc3 byte 0 dup(00h)
                     ORG  $ + ( ( ALIGNSIZE - ($ - @CurSeg) ) and ( ALIGNSIZE-1 ) )

00000FD1  E8 00000836               CALL  PHYSICAL0                         ; read MBR/drive0