JECXZ - "error: jump out of range" !?

Everything else that doesn't fall into one of the other PB categories.
Wayne Diamond
User
User
Posts: 38
Joined: Tue Dec 30, 2003 1:37 pm
Location: Australia

JECXZ - "error: jump out of range" !?

Post by Wayne Diamond »

I've 99% ported a compression/decompression algorithm known as SuperTiny over from Powerbasic to Purebasic (im slowly but surely porting all my crypto code over as my recent posts show!), but I'm encountering a couple of ASM problems that I wasnt having with Powerbasic.

If you try to compile the code below you'll get an error regarding the JECXZ line saying that the jump is out of range, yet virtually the same code compiles fine under Powerbasic. So is this a bug in the assembler, or ... ? Does anyone know any workarounds?
Under Powerbasic it compiles to this:
00401206 E3 28 JECXZ SHORT 00401230

Code: Select all

Procedure.l Compress(ptrInData.l, LenInData.l, ptrOutData.l, ptrTmpData.l)
PUSHAD
   PUSH    ptrTmpData
   PUSH    LenInData
   PUSH    ptrInData
   PUSH    ptrOutData
   CALL    l_cstart
   JMP     l_cend
  CStart:
   SUB     edx, edx
   XCHG    eax, edx
   PUSHAD
   MOV     ebp, esp
   AND     ecx, eax
   MOV     edi, [ebp+0x30]
   CLD
   MOV     ch, 0x40
   PUSH    edi
   REP     stosd
   SUB     edx, 0x2864E25C
   MOV     esi, [ebp+0x28]
   JNZ     l_pack0
   DEC     edx
  pack0:
   PUSH    ecx
   SUB     ax, 0x0AEB6
   MOV     edi, [ebp+0x24]
   POP     ebx
   !stosw
   XCHG    eax, edx
   POP     ebp
   !stosd
   PUSH    edi
   XCHG    eax, edx
   PUSH    esp
  pack1:
   TEST    cl, 7
   !lodsb
   JNZ     l_pack3
   XCHG    edx, [esp]
   ADC     ah, dl
   POP     edx
   XCHG    edi, [esp]
   ROR     edx, 1
   MOV     [edi], ah
   JC      l_pack2
   XOR     edx, 0x2C047C3E
  pack2:
   POP     edi
   MOV     ah, 0x0FF
   PUSH    edi
   XOR     edx, 0x76C52B8D
   INC     edi
   PUSH    edx
  pack3:
   CMP     al, [ebx+ebp]
   JZ      l_pack5
   ROR     edx, 1
   MOV     [ebx+ebp], al
   JNC     l_pack4
   XOR     edx, 0x2C047C3E
  pack4:
   MOV     bh, al
   XOR     edx, 0x5AC157B3
   ADC     al, dl
   !stosb
   MOV     al, bh
   STC
  pack5:
   INC     ecx
   MOV     bh, bl
   RCL     ah, 1
   CMP     ecx, [esp+0x34]
   MOV     bl, al
   JC      l_pack1
   ROR     ah, cl
   POP     ebx
   ADD     ah, bl
   POP     esi
   MOV     ebp, esp
   SUB     edi, [ebp+0x24]
   MOV     [ebp+0x14], edx
   XCHG    ah, [esi]
   ADD     [ebp+0x1C], edi
   POPAD
   RET 0x10
CEnd:
POPAD
EndProcedure
 
Procedure.l Decompress(ptrInData.l, LenInData.l, ptrOutData.l, ptrTmpData.l)
PUSHAD
   PUSH    ptrTmpData
   PUSH    LenInData
   PUSH    ptrInData
   PUSH    ptrOutData
   CALL    l_decstart
   JMP     l_decend
  DecStart:
   SUB     eax, eax
   PUSHAD
   MOV     ebp, esp
   AND     ecx, eax
   MOV     edi, [ebp+0x30]
   CLD
   MOV     ch, 0x40
   PUSH    edi
   REP     stosd
   MOV     esi, [ebp+0x28]
   XCHG    ebx, eax
   ADD     ecx, [ebp+0x2C]
   !lodsw
   MOV     edi, [ebp+0x24]
   ADD     ecx,-6
   POP     ebp
   !lodsd
   XCHG    eax, edx
  unpack0:
   DB 0xF6 ;TEST BYTE PTR [ESP+1C], 7
   DB 0x44
   DB 0x24
   DB 0x1C
   DB 0x07
   JNZ     l_unpack2
   ROR     edx, 1
   JECXZ   l_unpack5
   JNC     l_unpack1
   XOR     edx, 0x2C047C3E
  unpack1:
   !lodsb
   DEC     ecx
   XOR     edx, 0x5AC157B3
   SBB     al, dl
   MOV     ah, al
  unpack2:
   SHL     ah, 1
   DB 0xFE ;INC BYTE PTR [ESP+1C]
   DB 0x44
   DB 0x24
   DB 0x1C
   JNC     l_unpack4
   ROR     edx, 1
   JECXZ   l_unpack5
   JC      l_unpack3
   XOR     edx, 0x2C047C3E
  unpack3:
   !lodsb
   DEC     ecx
   XOR     edx, 0x76C52B8D
   SBB     al, dl
   MOV     [ebx+ebp], al
  unpack4:
   MOV     al, [ebx+ebp]
   MOV     bh, bl
   !stosb
   MOV     bl, al
   JMP     l_unpack0
   DEC     edx
   PUSH    ecx
  unpack5:
   SUB     edi, [esp+0x24]
   MOV     [esp+0x1C], edi
   POPAD
   RET     0x10
DecEnd:
POPAD
EndProcedure
 
;Dim sInData AS STRING, sOutData AS STRING, sTmpData AS STRING * 65535, OutSize AS DWORD
sTmpData.s = Space(1000)
sTmpData = ReplaceString(sTmpData, " ", Chr(0), 1, 1)

;// Data to compress
sInData.s = "testingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtesting"
;// Compress it ...
sOutData.s = Space(Len(sInData))
sOutData.s = ReplaceString(sOutData, " ", Chr(0), 1, 1)

OutSize.l = Compress(@sInData, Len(sInData), @sOutData, @sTmpData)

If OutSize > Len(sInData)
    Debug "Unable to compress this data (probably not enough repetition)."
    ;End
EndIf
Debug "Original size     = " + StrU(Len(sInData), #LONG) + " bytes"
Debug "Compressed size   = " + StrU(OutSize,#LONG) + " bytes"

Many thanks in advance for any help
User avatar
Rings
Moderator
Moderator
Posts: 1435
Joined: Sat Apr 26, 2003 1:11 am

Post by Rings »

the fasm doc says:
Every "loop" instruction needs an operand being an
immediate value specifying target address, it can be only short jump (in the
range of 128 bytes back and 127 bytes forward from the address of instruction
following the "loop" instruction).
"jcxz" branches to the label specified in the instruction if it finds a
value of zero in CX, "jecxz" does the same, but checks the value of ECX
instead of CX. Rules for the operands are the same as for the "loop"
instruction.
and also my INTEl-Docu says:
JECXZ rel8 Jump short if ECX register is 0
so it is clear that this instruction can only be used in short ranges.

But of course it is anoying that it works under Powerbasic and not with fasm, but you can use

CMP ECX,0
JZ l_unpack5

instead of JECXZ l_unpack5 .Should work.

[Edit] i have seen that the procedures crashes while get in trouble with Stack. This depends also on the different stack-used for Functions between Power and Pure.For more details about stack using compile some code with the /Commented code[/Edit]

[Edit] also if you write more with the ! command in front oif the asm-instruction it worked also (less bytes for the assembler)[/Edit]

and btw, i like you codes. go on,
SPAMINATOR NR.1
Fred
Administrator
Administrator
Posts: 18335
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Post by Fred »

If you enable the Debugger, it will put some check instruction between each lines, so the range will explode. I suggest to put a 'DisableDebugger' just above the first Procedure and an 'EnableDebugger' right after the last EndProcedure.

Another topic, PB use 'ESP' for local variable, which means than if you use 'PUSH localvar' it will increase the stack pointer and all will be shifted.

I hope this help..
User avatar
Danilo
Addict
Addict
Posts: 3036
Joined: Sat Apr 26, 2003 8:26 am
Location: Planet Earth

Post by Danilo »

@Wayne:
The jump is out of range here because the debugger inserts
extra stuff - compiles fine with debugger disabled.
It compiles also if you use DisableDebugger+EnableDebugger
around your procedure/asm-code, so the debugger doesnt
insert extra stuff.

Another thing:
I dont think ReplaceString(A$, " ", Chr(0), 1, 1) is right. PureBasic uses
ASCIIZ-Strings, so '0' is not a valid char.
Use allocated memory for buffers/data: sTmpData = AllocateMemory(1,1000)

Still doesnt work with this modifications, app just closes in your
function Compress().

You ask why? Its easy, your code:

Code: Select all

Procedure.l Compress(ptrInData.l, LenInData.l, ptrOutData.l, ptrTmpData.l) 
PUSHAD 
   PUSH    ptrTmpData 
   PUSH    LenInData 
   PUSH    ptrInData 
   PUSH    ptrOutData 
   CALL    l_cstart 
   JMP     l_cend
is translated to:

Code: Select all

; PUSHAD
  PUSHAD
; PUSH    ptrTmpData
  PUSH   dword [esp+12]
; PUSH    LenInData
  PUSH   dword [esp+4]
; PUSH    ptrInData
  PUSH   dword [esp]
; PUSH    ptrOutData
  PUSH   dword [esp+8]
; !CALL    l_cstart 
CALL    l_cstart 
; !JMP     l_cend 
JMP     l_cend
After your PUSHAD, the stack has changed. As you can see,
PB doesnt update the internal stack calculation on PUSHAD
and still assumes that the 4 arguments are in [ESP] - [ESP+12].
What PB pushes here are the registers you pushed with PUSHAD
before.
The 2nd Problem: PB also doesnt update the internal Stack-Counter
after every 'PUSH variable' (see above).

I tried to correct this problem with directASM, but there is still
an Error in the code. I checked also your PUSH/POP pairs and it
seems to be OK:

Code: Select all

OnErrorGoto(?Error)

DisableDebugger

Procedure.l Compress(ptrInData.l, LenInData.l, ptrOutData.l, ptrTmpData.l)
!PUSHAD
; PUSH    ptrTmpData
   !PUSH   dword [esp+12 + 32    ]
; PUSH    LenInData
   !PUSH   dword [esp+4  + 32 + 4]
; PUSH    ptrInData
   !PUSH   dword [esp    + 32 + 8]
; PUSH    ptrOutData
   !PUSH   dword [esp+8  + 32 + 12]

   !CALL    l_cstart
   !JMP     l_cend
  CStart: 
   !SUB     edx, edx 
   !XCHG    eax, edx 
   !PUSHAD
     !MOV     ebp, esp 
     !AND     ecx, eax 
     !MOV     edi, [ebp+0x30] 
     !CLD
     !MOV     ch, 0x40 
     !PUSH    edi
       !REP     stosd 
       !SUB     edx, 0x2864E25C 
       !MOV     esi, [ebp+0x28] 
       !JNZ     l_pack0 
       !DEC     edx 
pack0: 
       !PUSH    ecx 
         !SUB     ax, 0x0AEB6 
         !MOV     edi, [ebp+0x24] 
       !POP     ebx 
       !stosw 
       !XCHG    eax, edx 
     !POP     ebp 
     !stosd
     !PUSH    edi 
       !XCHG    eax, edx 
       !PUSH    esp 
pack1: 
         !TEST    cl, 7 
         !lodsb 
         !JNZ     l_pack3 
         !XCHG    edx, [esp] 
         !ADC     ah, dl 
       !POP     edx 
       !XCHG    edi, [esp] 
       !ROR     edx, 1 
       !MOV     [edi], ah 
       !JC      l_pack2 
       !XOR     edx, 0x2C047C3E 
pack2:
     !POP     edi 
     !MOV     ah, 0x0FF 
     !PUSH    edi 
       !XOR     edx, 0x76C52B8D 
       !INC     edi 
       !PUSH    edx 
pack3: 
         !CMP     al, [ebx+ebp] 
         !JZ      l_pack5 
         !ROR     edx, 1 
         !MOV     [ebx+ebp], al 
         !JNC     l_pack4 
         !XOR     edx, 0x2C047C3E 
pack4: 
         !MOV     bh, al 
         !XOR     edx, 0x5AC157B3 
         !ADC     al, dl 
         !stosb 
         !MOV     al, bh 
         !STC 
pack5: 
         !INC     ecx 
         !MOV     bh, bl 
         !RCL     ah, 1 
         !CMP     ecx, [esp+0x34] 
         !MOV     bl, al 
         !JC      l_pack1 
         !ROR     ah, cl 
       !POP     ebx 
       !ADD     ah, bl 
     !POP     esi 
     !MOV     ebp, esp 
     !SUB     edi, [ebp+0x24] 
     !MOV     [ebp+0x14], edx 
     !XCHG    ah, [esi] 
     !ADD     [ebp+0x1C], edi
   !POPAD
   !RET 0x10
CEnd:
!POPAD
EndProcedure 

EnableDebugger

;Dim sInData AS STRING, sOutData AS STRING, sTmpData AS STRING * 65535, OutSize AS DWORD 
sTmpData.s = Space(1000) 
sTmpData = ReplaceString(sTmpData, " ", Chr(0), 1, 1) 
;sTmpData = AllocateMemory(1,1000)

;// Data to compress 
sInData.s = "testingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtestingtesting"
;// Compress it ... 
sOutData.s = Space(Len(sInData))
sOutData.s = ReplaceString(sOutData, " ", Chr(0), 1, 1)

OutSize.l = Compress(@sInData, Len(sInData), @sOutData, @sTmpData)

If OutSize > Len(sInData)
  Debug "Unable to compress this data (probably not enough repetition)."
  ;End 
EndIf

A$ = "Original size     = " + StrU(Len(sInData), #LONG) + " bytes" 
B$ = "Compressed size   = " + StrU(OutSize,#LONG)       + " bytes"
MessageRequester("INFO",A$+Chr(13)+B$)

End

Error:
MessageRequester("FATAL ERROR !",GetErrorDescription(),#MB_ICONERROR)
End
You should check now if all your stack calculation is correct
(!MOV esi, [ebp+0x28] and all this), there is a protection fault
somewhere (write/read to/from wrong address).
cya,
...Danilo
...:-=< http://codedan.net/work >=-:...
-= FaceBook.com/DaniloKrahn =-
User avatar
Rings
Moderator
Moderator
Posts: 1435
Joined: Sat Apr 26, 2003 1:11 am

Post by Rings »

another hint:

instead for this workaround

Code: Select all

   DB 0xF6 ;TEST BYTE PTR [ESP+1C], 7 
   DB 0x44 
   DB 0x24 
   DB 0x1C 
   DB 0x07 
   
you can also write:

! test byte [esp+$1C], 7

so sometimes using the ! command to pass asm-commands directly to the assembler is absolutly needed .
SPAMINATOR NR.1
Post Reply