uses Windows, Classes, Common, IPBase; type TX86Register = (rgEAX, rgECX, rgEDX, rgEBX, rgESP, rgEBP, rgESI, rgEDI, rgAX, rgCX, rgDX, rgBX, rgSP, rgBP, rgSI, rgDI, rgAL, rgCL, rgDL, rgBL, rgAH, rgCH, rgDH, rgBH); TX86CodeGen = class(TIPCustomCodeGen) private ConstAddrFixupList, DataAddrFixupList, FuncAddrFixupList: TList; public constructor Create; override; destructor Destroy; override; procedure ApplyFixups (Funcs: TList; FuncAddress: TCodeAddr; CodeVA, ConstVA, BSSVA: TVirtualAddress); override; procedure AsgRegImm (Reg: TX86Register; Value: Longint); procedure AsgVarAddrOfConst (Addr: TVarAddr; ConstAddr: TConstAddr); override; procedure AsgVarFuncResult (Addr: TVarAddr; Size: TSize); override; procedure AsgVarImm (Addr: TVarAddr; Size: TSize; Value: Longint); override; procedure AsgVarPop (Addr: TVarAddr; Size: TSize); override; procedure AsgVarVar (DestAddr: TVarAddr; DestSize: TSize; SourceAddr: TVarAddr; SourceSize: TSize); override; procedure CallFunc (const CallData: TCallData); override; procedure Expression (const Expr: TExpression); override; procedure FuncEnd; override; procedure ImportThunk (ImportAddressVA: TVirtualAddress); override; procedure PopIntoReg (Reg: TX86Register); procedure PushAddrOfConst (Addr: TConstAddr); procedure PushAddrOfVar (Addr: TVarAddr); procedure PushImm (Value: Longint); procedure PushReg (Reg: TX86Register); procedure PushVarAtAddr (Addr: TVarAddr); end; implementation type PFuncAddrFixupRec = ^TFuncAddrFixupRec; TFuncAddrFixupRec = record FuncIndex: Integer; CodeAddress: TCodeAddr; end; const opOperandSizePrefix = $66; constructor TX86CodeGen.Create; begin inherited; ConstAddrFixupList := TList.Create; DataAddrFixupList := TList.Create; FuncAddrFixupList := TList.Create; end; destructor TX86CodeGen.Destroy; begin FuncAddrFixupList.Free; {}{mem leak; needs to free individual items} ConstAddrFixupList.Free; DataAddrFixupList.Free; inherited; end; (*procedure TX86CodeGen.AsgEAXZero; const X: array[0..1] of Byte = ($31, $C0); { xor eax, eax } begin EmitCode (X, 2); end;*) procedure TX86CodeGen.AsgRegImm (Reg: TX86Register; Value: Longint); var X: array[0..4] of Byte; begin if Reg in [rgEAX..rgEDI] then begin { 32-bit reg } if Value <> 0 then begin X[0] := $B8 + Ord(Reg); { mov reg32, xxxxxxxx } Longint((@X[1])^) := Value; EmitCode (X, 5); end else begin { use a more optimized XOR instruction to assign zero } X[0] := $31; X[1] := $C0 or (Ord(Reg) shl 3) or Ord(Reg); EmitCode (X, 2); end; end else if Reg in [rgAX..rgDI] then begin { 16-bit reg } X[0] := opOperandSizePrefix; Dec (Reg, Ord(rgAX)); if Value <> 0 then begin X[1] := $B8 + Ord(Reg); { mov reg32, xxxxxxxx } Word((@X[2])^) := Value; EmitCode (X, 4); end else begin { use a more optimized XOR instruction to assign zero } X[1] := $31; X[2] := $C0 or (Ord(Reg) shl 3) or Ord(Reg); EmitCode (X, 3); end; end else begin { 8-bit reg } Dec (Reg, Ord(rgAL)); if Value <> 0 then begin X[0] := $B0 + Ord(Reg); X[1] := Byte(Value); EmitCode (X, 2); end else begin { use a more optimized XOR instruction to assign zero } X[0] := $30; X[1] := $C0 or (Ord(Reg) shl 3) or Ord(Reg); EmitCode (X, 2); end; end; end; procedure TX86CodeGen.PushReg (Reg: TX86Register); var X: Byte; begin Assert (Reg in [rgEAX..rgEDI]); {}{currently there's no support for 8/16-bit regs} X := $50 + Ord(Reg); EmitCode (X, 1); end; procedure TX86CodeGen.PopIntoReg (Reg: TX86Register); var X: Byte; begin Assert (Reg in [rgEAX..rgEDI]); {}{currently there's no support for 8/16-bit regs} X := $58 + Ord(Reg); EmitCode (X, 1); end; procedure TX86CodeGen.AsgVarImm (Addr: TVarAddr; Size: TSize; Value: Longint); { regs used: none } var X: array[0..9] of Byte; begin case Size of 4: begin X[0] := $C7; { mov [xxxxxxxx], yyyyyyyy } X[1] := $05; LongWord((@X[2])^) := Addr; Longint((@X[6])^) := Value; DataAddrFixupList.Add (Pointer(Length(Code) + 2)); EmitCode (X, 10); end; 2: begin X[0] := $66; { mov [xxxxxxxx], yyyy } X[1] := $C7; X[2] := $05; LongWord((@X[3])^) := Addr; Word((@X[7])^) := Word(Value); DataAddrFixupList.Add (Pointer(Length(Code) + 3)); EmitCode (X, 9); end; 1: begin X[0] := $C6; { mov [xxxxxxxx], yy } X[1] := $05; LongWord((@X[2])^) := Addr; Byte((@X[6])^) := Byte(Value); DataAddrFixupList.Add (Pointer(Length(Code) + 2)); EmitCode (X, 7); end; else Assert (False); end; end; procedure TX86CodeGen.AsgVarAddrOfConst (Addr: TVarAddr; ConstAddr: TConstAddr); { regs used: none } var X: array[0..9] of Byte; begin X[0] := $C7; { mov [xxxxxxxx], yyyyyyyy } X[1] := $05; LongWord((@X[2])^) := Addr; LongWord((@X[6])^) := ConstAddr; DataAddrFixupList.Add (Pointer(Length(Code) + 2)); ConstAddrFixupList.Add (Pointer(Length(Code) + 6)); EmitCode (X, 10); end; procedure TX86CodeGen.AsgVarVar (DestAddr: TVarAddr; DestSize: TSize; SourceAddr: TVarAddr; SourceSize: TSize); { regs used: eax } var X: array[0..11] of Byte; begin {}{only supports 4-byte types for now!} Assert ((DestSize = 4) and (SourceSize = 4)); X[0] := $8B; { mov eax, [xxxxxxxx] } X[1] := $05; LongWord((@X[2])^) := SourceAddr; X[6] := $89; { mov [xxxxxxxx], eax } X[7] := $05; LongWord((@X[8])^) := DestAddr; DataAddrFixupList.Add (Pointer(Length(Code) + 2)); DataAddrFixupList.Add (Pointer(Length(Code) + 8)); EmitCode (X, 12); end; procedure TX86CodeGen.AsgVarFuncResult (Addr: TVarAddr; Size: TSize); { regs used: none } var X: array[0..5] of Byte; begin {}{only supports 4-byte types for now!} Assert (Size = 4); X[0] := $89; { mov [xxxxxxxx], eax } X[1] := $05; LongWord((@X[2])^) := Addr; DataAddrFixupList.Add (Pointer(Length(Code) + 2)); EmitCode (X, 6); end; procedure TX86CodeGen.AsgVarPop (Addr: TVarAddr; Size: TSize); { pop into EAX; store EAX in Addr } begin {}{only supports 4-byte types for now!} Assert (Size = 4); PopIntoReg (rgEAX); { though the function is named AsgVarFuncResult, what it really does is assign EAX to a memory location. It will work for our purposes here. } AsgVarFuncResult (Addr, Size); end; procedure TX86CodeGen.FuncEnd; { regs used: none } const X: Byte = $C3; { ret } begin EmitCode (X, 1); end; procedure TX86CodeGen.PushImm (Value: Longint); var X: array[0..4] of Byte; begin if (Value >= -128) and (Value <= 127) then begin { If Value is in the range of a signed byte, use a smaller instruction } X[0] := $6A; { push xx (sign-extended to 32 bits) } Byte((@X[1])^) := Byte(Value); EmitCode (X, 2); end else begin X[0] := $68; { push xxxxxxxx } Longint((@X[1])^) := Value; EmitCode (X, 5); end; end; procedure TX86CodeGen.PushAddrOfConst (Addr: TConstAddr); var X: array[0..4] of Byte; begin Assert (Addr <> $FFFFFFFF); X[0] := $68; { push xxxxxxxx } LongWord((@X[1])^) := Addr; ConstAddrFixupList.Add (Pointer(Length(Code) + 1)); EmitCode (X, 5); end; procedure TX86CodeGen.PushAddrOfVar (Addr: TVarAddr); var X: array[0..4] of Byte; begin X[0] := $68; { push xxxxxxxx } LongWord((@X[1])^) := Addr; DataAddrFixupList.Add (Pointer(Length(Code) + 1)); EmitCode (X, 5); end; procedure TX86CodeGen.PushVarAtAddr (Addr: TVarAddr); var X: array[0..5] of Byte; begin X[0] := $FF; { push [xxxxxxxx] } X[1] := $35; LongWord((@X[2])^) := Addr; DataAddrFixupList.Add (Pointer(Length(Code) + 2)); EmitCode (X, 6); end; procedure TX86CodeGen.CallFunc (const CallData: TCallData); var J: Integer; X: array[0..4] of Byte; FixupRec: PFuncAddrFixupRec; begin { stdcall - push parameters from right to left } for J := CallData.ParamCount-1 downto 0 do Expression (CallData.ParamExpr[J]); X[0] := $E8; { call xxxxxxxx (relative address) } Longint((@X[1])^) := 0; { xxxxxxxx is zero for now } New (FixupRec); FixupRec.FuncIndex := CallData.FuncIndex; FixupRec.CodeAddress := Length(Code) + 1; FuncAddrFixupList.Add (FixupRec); EmitCode (X, 5); end; procedure TX86CodeGen.ImportThunk (ImportAddressVA: TVirtualAddress); var X: array[0..5] of Byte; begin X[0] := $FF; { jmp [xxxxxxxx] } X[1] := $25; LongWord((@X[2])^) := ImportAddressVA; EmitCode (X, 6); end; procedure TX86CodeGen.ApplyFixups (Funcs: TList; FuncAddress: TCodeAddr; CodeVA, ConstVA, BSSVA: TVirtualAddress); var I: Integer; Diff, Addr: Longint; FixupRec: PFuncAddrFixupRec; FixupOffset: PLongint; begin for I := 0 to FuncAddrFixupList.Count-1 do begin FixupRec := FuncAddrFixupList[I]; Addr := PFuncData(Funcs[FixupRec.FuncIndex]).Address; Assert (Addr <> -1); Diff := Addr - Longint(FuncAddress + FixupRec.CodeAddress + 4); Longint((@Code[FixupRec.CodeAddress+1])^) := Diff; end; for I := 0 to ConstAddrFixupList.Count-1 do Inc (Longint((@Code[Cardinal(ConstAddrFixupList[I])+1])^), ConstVA); for I := 0 to DataAddrFixupList.Count-1 do begin FixupOffset := @Code[Cardinal(DataAddrFixupList[I])+1]; Assert (FixupOffset^ <> -1); Inc (FixupOffset^, BSSVA); end; end; procedure TX86CodeGen.Expression (const Expr: TExpression); { regs used: eax, ecx, edx. Result of expression is currently left at the top of the stack } const AddEAXtoMESP: array[0..2] of Byte = ($01, $04, $24); { add [esp], eax } AddECXtoEAX: array[0..1] of Byte = ($01, $C8); { add eax, ecx } AddMESPtoEAX: array[0..2] of Byte = ($03, $04, $24); { add eax, [esp] } DivideEAXbyECX: array[0..2] of Byte = ($99,{;} $F7, $F9); { cdq; idiv ecx } SubtractEAXfromMESP: array[0..2] of Byte = ($29, $04, $24); { sub [esp], eax } SubtractECXfromEAX: array[0..1] of Byte = ($29, $C8); { sub eax, ecx } SubtractMESPfromEAX: array[0..2] of Byte = ($2B, $04, $24); { sub eax, [esp] } MultiplyEAXbyECX: array[0..2] of Byte = ($0F, $AF, $C1); { imul eax, ecx } MultiplyEAXbyMESP: array[0..3] of Byte = ($0F, $AF, $04, $24); { imul eax, [esp] } MultiplyECXbyEAX: array[0..2] of Byte = ($0F, $AF, $C8); { imul ecx, eax } MovEAX_MESP: array[0..2] of Byte = ($8B, $04, $24); { mov eax, [esp] } MovEAX_MESP4: array[0..3] of Byte = ($8B, $44, $24, $04); { mov eax, [esp+4] } MovECX_MESP: array[0..2] of Byte = ($8B, $0C, $24); { mov ecx, [esp] } MovMESP_EAX: array[0..2] of Byte = ($89, $04, $24); { mov [esp], eax } MovMESP_ECX: array[0..2] of Byte = ($89, $0C, $24); { mov [esp], ecx } MovMESP_EDX: array[0..2] of Byte = ($89, $14, $24); { mov [esp], edx } MovMESP4_EAX: array[0..3] of Byte = ($89, $44, $24, $04); { mov [esp+4], eax } NegEAX: array[0..1] of Byte = ($F7, $D8); { neg eax } var P: PExprRec; begin P := Expr.First; while Assigned(P) do begin case P.Op of eoPushImm: PushImm (P.Value.AsInteger); eoPushVar: begin PushVarAtAddr (P.Value.AsVarAddress); if efNegate in P.Flags then begin { quick & ugly hack: pop the var off the stack and push it back negated } PopIntoReg (rgEAX); EmitCode (NegEAX, SizeOf(NegEAX)); PushReg (rgEAX); end; end; eoPushStrConst: PushAddrOfConst (P.Value.AsConstAddress); eoPushAddrOfVar: PushAddrOfVar (P.Value.AsVarAddress); eoPushCall: begin CallFunc (P.CallData^); { result of function is in EAX } if efNegate in P.Flags then EmitCode (NegEAX, SizeOf(NegEAX)); PushReg (rgEAX); end; eoAdd: begin PopIntoReg (rgEAX); EmitCode (AddEAXtoMESP, SizeOf(AddEAXtoMESP)); end; eoSubtract: begin PopIntoReg (rgEAX); EmitCode (SubtractEAXfromMESP, SizeOf(SubtractEAXfromMESP)); end; eoMultiply: begin PopIntoReg (rgEAX); EmitCode (MovECX_MESP, SizeOf(MovECX_MESP)); EmitCode (MultiplyECXbyEAX, SizeOf(MultiplyECXbyEAX)); EmitCode (MovMESP_ECX, SizeOf(MovMESP_ECX)); end; eoDivide: begin PopIntoReg (rgECX); EmitCode (MovEAX_MESP, SizeOf(MovEAX_MESP)); EmitCode (DivideEAXbyECX, SizeOf(DivideEAXbyECX)); { DivideEAXbyECX saves quotient in EAX, and remainder in EDX } EmitCode (MovMESP_EAX, SizeOf(MovMESP_EAX)); end; eoMod: begin { eoMod is identical to eoDivide except for the last line } PopIntoReg (rgECX); EmitCode (MovEAX_MESP, SizeOf(MovEAX_MESP)); EmitCode (DivideEAXbyECX, SizeOf(DivideEAXbyECX)); { DivideEAXbyECX saves quotient in EAX, and remainder in EDX } EmitCode (MovMESP_EDX, SizeOf(MovMESP_EDX)); end; else Assert (False); end; P := P.Next; end; end; end.