/* compute the address and check it. This code is only called by your
    program. Not portable...
   Copyright 1993 Tristan Gingold
		  Written September 1993 Tristan Gingold

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; see the file COPYING.LIB.  If
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
Cambridge, MA 02139, USA.

   The author may be reached (Email) at the address marc@david.saclay.cea.fr,
   or (US/French mail) as Tristan Gingold 
   			  8 rue Parmentier
   			  F91120 PALAISEAU
   			  FRANCE 
*/

#include "config.h"

#ifdef CHKR_USE_BITMAP
/* infos: on Linux, ss = ds = es = fs = gs
	  esi points to the encoded address (i.e. Disp or Modrm)
   Line comment is hard: neither "/*", nor "/" works. So I use the good and
	very old '#'. 
   Binary constants must begin with "0B", not with "0b" (backward local
	label)
*/
	.file "codecheck.s"
/* Registers saved: */
	.data
	.align 4

/* Keep this order */
registers:
Reax:	.long 0
Recx:	.long 0
Redx:	.long 0
Rebx:	.long 0
Resp:	.long 0
Rebp:	.long 0
Resi:	.long 0
Redi:	.long 0	
Reip:	.long 0
Rflag:	.byte 0

size:	.long 0
right:	.long 0
accmod:	.long 0

	.text
	.align 4
#ifdef CHKR_DEBUG
	.globl save_register, restore_register, Chkr_Modrm
	.globl Sib, Check_addr
	.globl Reax, Recx, Redx, Rebx, Resp, Rebp, Resi, Redi, Reip
	.globl size, right, accmod
#endif

/* function to save register, so that, we can directly access to them */
save_register:
	movl %eax, Reax
	movl %ebx, Rebx
	movl %ecx, Recx
	movl %edx, Redx
	movl %esi, Resi
	movl %edi, Redi
	movl %ebp, Rebp
	lahf			# /* save flag */
	movb %ah, Rflag
	movl %esp, %eax		# /* compute esp */
	addl $8, %eax
	movl %eax, Resp
	movl 4(%esp), %eax
	movl %eax, Reip		# /* compute eip */
	movl %eax, %esi
	ret

	.align 4
restore_register:
	movb Rflag, %ah		# /* restore flags */
	sahf
	movl Reax, %eax
	movl Rebx, %ebx
	movl Recx, %ecx
	movl Redx, %edx
	movl Resi, %esi
	movl Redi, %edi
	movl Rebp, %ebp
/* eip and esp will be automatically restored */
/*	movl Resp, %esp
	jump (Reip) */
	ret

	.align 4
Chkr_Modrm:
	movb (%esi), %al	# /* load the ModRm byte into al */ 
	andb $0B11000111, %al	# /* We use only the Mod and Rm fields */
	movb %al, %ah		# /* Is it a register ? */
	andb $0B11000000, %ah
	cmpb $0B11000000, %ah
	jne L0
	jmp abort		# /* This is a register. Must never happen */
L0:	movb %al, %ah		# /* Test for s-i-b byte */
	andb $0B00000111, %ah
	cmpb $0B00000100, %ah
	je Sib
/* s-i-b byte doesn't follow */
	cmpb $0B00000101, %al	# /* 0b00???101 is special. test it */
	jne L1
/* A 32 disp follows */
	movl 1(%esi),%edi
	jmp Check_addr
/* Load the register */
L1:	movl %eax, %edi
	andl $0B00000111, %edi
	movl registers(,%edi,4), %edi
/* Is there a disp ? */
	movb %al, %ah
	andb $0B11000000, %ah
	cmp  $0B00000000, %ah
	je Check_addr 		# /* no disp, so finish */
	cmp  $0B01000000, %ah	# /* test for mod == 01 */
	jne L2
/* just add a disp8 */
	movsbl 1(%esi), %edx	# /* signed convertion 8 bits to 32 bits */
	addl %edx, %edi
	jmp Check_addr
/* Just add a disp32 */
L2:	addl 1(%esi), %edi
	jmp Check_addr

/* Handle the sib byte 
   In Sib: al is Modrm, ah is s-i-b */
Sib:	andb $0B11000000, %al	# /* ignore unuseful bits */
	movb 1(%esi), %ah	# /* load sib byte into ah */
/* handle scale and index */
	movb %ah, %cl
	andb $0B00111000, %cl	# /* mask scale and base */
	cmpb $0B00100000, %cl	# /* test for no index */
	jne L3
	xor  %edi, %edi		# /* clean the address */
	jmp L4			# /* skipf scale, handle directlry the disp */
L3:	xor %ecx, %ecx		# /* clean all 32bits of ecx */
	movb %ah, %cl		
	andb $0B00111000, %cl	# /* keep only index */
	shrb $1, %cl		# /* shift -> the index is good */
	movl registers(%ecx), %edi	# /* load the register */
	movb %ah, %cl		
	andb $0B11000000, %cl	# /* mask index and base */
	shrb $6, %cl		# /* move the index into the first 2 bits */
	shll %cl, %edi		# /* scale now */
L4:	andb $0B00000111, %ah	# /* keep only base */
	cmpw $0x0004, %ax	# /* special case: mod=0, base=100. */
	jne L5
	addl 2(%esi), %edi	# /* add the disp32 */
	jmp Check_addr		# /* end */
L5:	xor %ecx, %ecx		# /* clean ecx, for base register */
	movb %ah, %cl		# /* load base into cl */
	addl registers(,%ecx,4), %edi	# /* add the base register */
	cmpb $0B01000000, %al	# /* Is this a disp8 */
	jne L6			# /* not, handle no disp/disp32 */
	movsbl 2(%esi), %edx	# /* convert disp8 to 32 bits */
	addl %edx, %edi		# /* add the disp */
	jmp Check_addr
L6:	cmpb $0B00000000, %al	# /* test for no disp */
	je Check_addr
	addl 2(%esi), %edi	# /* add disp32 */
	jmp Check_addr
	
Check_addr:
/* make a dummy frame, in order to be used by chkr_show_frames */
	movl Reip, %eax		# /* return address */
	pushl %eax
	pushl %ebp		# /* push the frame pointer */
	movl %esp, %ebp
	movl accmod, %eax	# /* push the "right" arg */
	pushl %eax
	movl size, %eax		# /* push the "len" arg */
	pushl %eax
	pushl %edi		# /* push the "ptr" arg */
	call _chkr_check_addr	# /* call the chkr_check_addr */
	addl $20, %esp		# /* normalize the stack */
	ret

abort:	
/* make a dummy frame, in order to be used by chkr_show_frames */
	movl Reip, %eax		# /* return address */
	pushl %eax
	pushl %ebp		# /* push the frame pointer */
	movl %esp, %ebp
	movl $11, _chkr_errno
	call _chkr_perror
	call _chkr_abort
here:	jmp here		# /* infinite loop */

/* chkr functions have this prototype:
        chkr_?_?_?_?_chkr
             | | | |
             | | | +------------------ size of the memory zone
             | | +-------------------- right demanded
             | +---------------------- addressing mode 1:disp, 2:Modrm, 3:str
             +------------------------ bytes to skipf
   str mode is special: accmode: 1 is repne, 2 is repe/rep, 0 means no prefix
*/

#define chkr_1_2_x_x_chkr(accm,s) \
	.globl chkr_1_2_##accm##_##s##_chkr; \
chkr_1_2_##accm##_##s##_chkr:; \
	call save_register;\
	movl $##s##,size; \
	movl $##accm##,accmod; \
	incl %esi; \
	call Chkr_Modrm; \
	call restore_register; \
	ret

#define chkr_x_2_x_x_chkr(l,accm,s) \
	.globl chkr_##l##_2_##accm##_##s##_chkr; \
chkr_##l##_2_##accm##_##s##_chkr:; \
	call save_register;\
	movl $##s##,size; \
	movl $##accm##,accmod; \
	addl $##l##, %esi; \
	call Chkr_Modrm; \
	call restore_register; \
	ret

#define chkr_1_1_x_x_chkr(accm,s) \
	.globl chkr_1_1_##accm##_##s##_chkr; \
chkr_1_1_##accm##_##s##_chkr:; \
	call save_register;\
	movl $##s##,size; \
	movl $##accm##,accmod; \
	movl 1(%esi), %edi; \
	call Check_addr; \
	call restore_register; \
	ret

chkr_1_2_x_x_chkr(1,1);		# /* size = 1, accmod = read */
chkr_1_2_x_x_chkr(2,1);		# /* size = 1, accmod = write */
chkr_1_2_x_x_chkr(3,1);		# /* size = 1, accmod = read & write */

chkr_1_2_x_x_chkr(1,2);		# /* size = 2, accmod = read */
chkr_1_2_x_x_chkr(2,2);		# /* size = 2, accmod = write */

chkr_1_2_x_x_chkr(1,4);		# /* size = 4, accmod = read */
chkr_1_2_x_x_chkr(2,4);		# /* size = 4, accmod = write */
chkr_1_2_x_x_chkr(3,4);		# /* size = 4, accmod = read & write */

chkr_1_2_x_x_chkr(1,8);		# /* size = 8, accmod = read */
chkr_1_2_x_x_chkr(2,8);		# /* size = 8, accmod = write */

chkr_x_2_x_x_chkr(2,1,2);	# /* size = 2, accmod = read */
chkr_x_2_x_x_chkr(2,2,2);	# /* size = 2, accmod = write */
chkr_x_2_x_x_chkr(2,3,2);	# /* size = 2, accmod = read & write */

chkr_x_2_x_x_chkr(2,1,4);	# /* size = 4, accmod = read */

chkr_x_2_x_x_chkr(3,1,2);	# /* size = 4, accmod = write */
chkr_x_2_x_x_chkr(3,1,4);	# /* size = 4, accmod = read */

chkr_1_1_x_x_chkr(1,4);		# /* size = 4, accmod = read */
chkr_1_1_x_x_chkr(2,4);		# /* size = 4, accmod = write */


/* About string instruction, the direction flag is not tested BUG */
/* entry point for repne xxx instructions */
	.globl chkr_1_3_1_0_chkr
chkr_1_3_1_0_chkr:
	call save_register
	testl %ecx, %ecx
	je L20
	cmpb $0xae, 1(%esi)	# /* is repne scasb */
	je Lzscasb
	jmp abort		# /* don't know */
Lzscasb:
	movl Reax, %eax		# /* load aex */
	repne			# /* execute the instruction */
	scasb
	subl Recx, %ecx		# /* compute the lenth */
	movl %ecx, size
	movl $1, accmod		# /* access is read */
	movl Redi, %edi
	call Check_addr		# /* go... */
L20:	call restore_register
	ret

/* entry point for rep/repe xxx instructions */
	.globl chkr_1_3_2_0_chkr
chkr_1_3_2_0_chkr:
	call save_register
	testl %ecx,%ecx		# /* nothing to check */
	je L30
	cmpb $0xa5, 1(%esi)	# /* is rep movsl */
	je Lemovsl
	cmpb $0xa6, 1(%esi)	# /* is repz cmpsb */
	je Lecmpsb
	cmpb $0xaa, 1(%esi)	# /* is rep stosb */
	je Lestosb
	cmpb $0xab, 1(%esi)	# /* is rep stosl */
	je Lestosl
	cmpb $0xa4, 1(%esi)	# /* is rep movsb */
	je Lemovsb
	jmp abort		# /* don't know */
Lemovsl:
	movl $2, accmod		# /* destination: write */
	shll $2, %ecx		# /* convert nbr of dword to nbr of bytes */
	movl %ecx, size		# /* load the lenth */
	call Check_addr		# /* first check */
	movl Resi, %edi		# /* source is in Resi */
	movl $1, accmod		# /* one read the source */
	movl Recx, %ecx
	shll $2, %ecx
	movl %ecx, size
	call Check_addr
L30:	call restore_register
	ret
Lemovsb:
	movl $2, accmod		# /* destination: write */
	movl %ecx, size		# /* load the lenth */
	call Check_addr		# /* first check */
	movl Resi, %edi		# /* source is in Resi */
	movl $1, accmod		# /* one read the source */
	movl Recx, %ecx
	movl %ecx, size
	call Check_addr
	call restore_register
	ret
Lestosb:
	movl $2, accmod		# /* destination: write */
	movl %ecx, size		# /* load the lenth */
	call Check_addr		# /* first check */
	call restore_register
	ret
Lestosl:
	movl $2, accmod		# /* destination: write */
	shll $2, %ecx		# /* covert nbr of dword to nbr of bytes */
	movl %ecx, size		# /* load the lenth */
	call Check_addr		# /* first check */
	call restore_register
	ret
Lecmpsb:
	repz			# /* execute the instruction */
	cmpsb
	subl Recx, %ecx		# /* Now, we known the size */
	movl %ecx, size		
	movl $1, accmod		# /* accmod is read */
	movl Redi, %edi		# /* first operand */
	call Check_addr		# /* and first check */
	movl Resi, %edi		# /* second operand */
	call Check_addr		# /* and check */
	call restore_register
	ret

/* entry point for lodsb, scasb instructions */
	.globl chkr_1_3_0_1_chkr
chkr_1_3_0_1_chkr:
	call save_register
	cmpb $0xac, (%esi)	# /* is lodsb */
	je Llodsb
	cmpb $0xae, (%esi)	# /* is scasb */
	je Lscasb
	jmp abort		# /* don't know */
Llodsb:	movl $1, accmod		# /* source: read */
	movl $1, size		# /* load the lenth 1:char size */
	movl %esi, %edi
	call Check_addr		# /* first check */
	call restore_register
	ret
Lscasb:	movl $1, accmod		# /* source: read */
	movl $1, size		# /* load the lenth 1:char size */
	call Check_addr		# /* first check */
	call restore_register
	ret

#endif /* CHKR_USE_BITMAP */