123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- unit ASMInline;
- interface
- {ASM Inliner
- Nicholas Sherlock
- This is incomplete, I've only implemented enough to support InnoCallback.
- Instructions are stored in a TMemoryStream internally
- Instructions usually accept some combination of Registers, Immediates and
- Memory References. Memory References can either be of the simple form [EAX]
- (Where [EAX] is really a Delphi set), or the user can build the address with
- the TASMInline.addr() function. It'd be nice to have a function which builds
- the address from a string, too, allowing the more intuitive '[EAX+EBX*4+1000]'
- style.
- The generation of instruction-relative addresses generates Relocations, using
- SaveToMemory() automatically rebases using the relocations to make these correct
- in the final block of memory.
- !!!! Not all special cases have been implemented in WriteRegRef().
- Further reduced to just what's needed for Inno Setup by Martijn Laan
- }
- uses Sysutils, Windows, Classes, Contnrs;
- type
- TModMode = (mmNaked, mmDeref, mmDisp8, mmDisp32);
- TRegister32 = (EAX, EBX, ECX, EDX, ESP, EBP, ESI, EDI);
- TRegister32Set = set of TRegister32;
- TRegister16 = (AX, BX, CX, DX, SP, BP, SI, DI);
- TRegister16Set = set of TRegister16;
- TRegister8 = (AH, AL, BH, BL, CH, CL, DH, DL);
- TCLRegister = CL..CL;
- TMemSize = (ms8, ms16, ms32, ms64);
- TMemoryAddress = record
- size: TMemSize;
- usebase: boolean;
- base, index: TRegister32;
- offset: integer;
- scale: byte;
- end;
- EOperandSizeMismatch = class(exception)
- public
- constructor create;
- end;
- TRelocType = (rt32Bit);
- TReloc = class
- public
- position: longword;
- relocType: TRelocType;
- end;
- TASMInline = class
- private
- fbuffer: TMemoryStream;
- frelocs: TObjectList;
- fbase: longword;
- procedure AddRelocation(position: longword; relocType: TRelocType);
- function GetReloc(index: integer): TReloc;
- function RelocCount: integer;
- property Relocs[index: integer]: TReloc read GetReloc;
- procedure WriteByte(b: byte);
- procedure WriteInteger(i: integer);
- procedure WriteLongWord(l: longword);
- procedure WriteRegRef(reg: byte; base: TRegister32; deref: boolean; index: TRegister32; Offset: integer; Scale: byte; usebase: boolean); overload;
- procedure WriteRegRef(mem: TMemoryAddress; reg: TRegister32); overload;
- procedure WriteRegRef(reg: TRegister32; base: TRegister32; deref: boolean; index: TRegister32 = EAX; Offset: integer = 0; Scale: byte = 0; usebase: boolean = true); overload;
- public
- function Size: integer;
- procedure Relocate(base: pointer);
- function SaveAsMemory: pointer;
- procedure SaveToMemory(target: pointer);
- constructor create;
- destructor Destroy; override;
- function Addr(base: TRegister32; offset: Integer; size: TMemSize = ms32): TMemoryAddress; overload;
- //PUSH reg
- procedure Push(reg: TRegister32); overload;
- //POP reg
- procedure Pop(reg: TRegister32);
- //JUMP rel32
- procedure Jmp(target: pointer); overload;
- //MOV reg, imm
- procedure Mov(reg: TRegister32; b: longword); overload;
- //MOV reg, mem and MOV mem, reg
- procedure Mov(mem: TMemoryAddress; reg: TRegister32); overload;
- procedure Mov(reg: TRegister32; mem: TMemoryAddress); overload;
- end;
- implementation
- constructor EOperandSizeMismatch.create;
- begin
- inherited create('Operand size mismatch');
- end;
- {Throw an exception if test<>match. Poor man's assert().
- Could overload to add other sorts of tests}
- procedure require(test: TMemSize; match: TMemSize);
- begin
- if test <> match then
- raise EOperandSizeMismatch.create;
- end;
- function regnum(reg: TRegister32): byte; overload;
- begin
- case reg of
- EAX: result := 0;
- EBX: result := 3;
- ECX: result := 1;
- EDX: result := 2;
- ESP: result := 4;
- EBP: result := 5;
- ESI: result := 6;
- EDI: result := 7;
- else raise exception.create('Unknown register...');
- end;
- end;
- function ModModeNum(m: TModMode): byte;
- begin
- case m of
- mmDeref: result := 0;
- mmDisp8: result := 1;
- mmDisp32: result := 2;
- mmNaked: result := 3;
- else raise exception.create('Invalid mod mode: ' + inttostr(ord(m)));
- end;
- end;
- function EncodeSIB(scale, index, base: byte): byte;
- begin
- result := base or (index shl 3) or (scale shl 6);
- end;
- function EncodeModRM(aMod, aReg, aRM: byte): byte; overload;
- begin
- result := (aMod shl 6) or (areg shl 3) or aRM;
- end;
- {$IFOPT R+}
- {$DEFINE RESTORER}
- {$R-}
- {$ENDIF}
- {$IFOPT Q+}
- {$DEFINE RESTOREQ}
- {$Q-}
- {$ENDIF}
- procedure TASMInline.Relocate(base: pointer);
- var oldpos, diff, orig: integer;
- i: integer;
- reloc: TReloc;
- begin
- oldpos := fbuffer.Position;
- try
- diff := -(longword(base) - fbase);
- for i := 0 to RelocCount - 1 do begin
- reloc := Relocs[i];
- case reloc.relocType of
- rt32Bit: begin
- fbuffer.Seek(reloc.position, soBeginning);
- fbuffer.Read(orig, sizeof(orig));
- fbuffer.seek(-sizeof(orig), soCurrent);
- orig := LongWord(orig + diff);
- fbuffer.write(orig, sizeof(orig));
- end;
- end;
- end;
- fbase := longword(base);
- finally
- fbuffer.position := oldpos;
- end;
- end;
- {$IFDEF RESTORER}
- {$R+}
- {$ENDIF}
- {$IFDEF RESTOREQ}
- {Q+}
- {$ENDIF}
- function TASMInline.GetReloc(index: integer): TReloc;
- begin
- result := TReloc(frelocs[index]);
- end;
- function TASMInline.RelocCount: integer;
- begin
- result := frelocs.Count;
- end;
- procedure TASMInline.AddRelocation(position: longword; relocType: TRelocType);
- var reloc: TReloc;
- begin
- reloc := TReloc.Create;
- reloc.position := position;
- reloc.relocType := relocType;
- frelocs.add(reloc);
- end;
- function TASMInline.SaveAsMemory: pointer;
- var buf: pointer;
- oldprotect: Cardinal;
- begin
- GetMem(buf, size);
- VirtualProtect(buf, Size, PAGE_EXECUTE_READWRITE, oldprotect);
- SaveToMemory(buf);
- result := buf;
- end;
- procedure TASMInline.SaveToMemory(target: pointer);
- begin
- Relocate(target);
- Move(fbuffer.memory^, target^, size);
- end;
- function TASMInline.Addr(base: TRegister32; offset: Integer; size: TMemSize = ms32): TMemoryAddress;
- begin
- result.base := base;
- result.scale := 0; //don't use Index
- result.offset := offset;
- result.size := size;
- result.usebase := true;
- end;
- function TASMInline.Size: integer;
- begin
- result := fbuffer.size;
- end;
- procedure TASMInline.WriteInteger(i: integer);
- begin
- fbuffer.write(i, 4);
- end;
- procedure TASMInline.writelongword(l: longword);
- begin
- fbuffer.write(l, 4);
- end;
- procedure TASMInline.writebyte(b: byte);
- begin
- fbuffer.write(b, 1);
- end;
- procedure TASMInline.Pop(reg: TRegister32);
- begin
- writebyte($58 + regnum(reg));
- end;
- procedure TASMInline.Jmp(target: pointer);
- begin
- writebyte($E9);
- AddRelocation(fbuffer.position, rt32bit);
- WriteInteger(integer(target) - (integer(fBase) + fbuffer.Position + 4));
- end;
- procedure TASMInline.Push(reg: TRegister32);
- begin
- writebyte($50 + regnum(reg));
- end;
- procedure TASMInline.WriteRegRef(mem: TMemoryAddress; reg: TRegister32);
- begin
- writeregref(reg, mem.base, true, mem.index, mem.offset, mem.scale, mem.usebase);
- end;
- //Write the MODR/M and SIB byte for the given register or memory reference
- procedure TASMInline.WriteRegRef(reg: TRegister32; base: TRegister32; deref: boolean; index: TRegister32 = EAX; Offset: integer = 0; Scale: byte = 0; usebase: boolean = true);
- begin
- WriteRegRef(regnum(reg), base, deref, index, Offset, scale, usebase);
- end;
- procedure TASMInline.WriteRegRef(reg: byte; base: TRegister32; deref: boolean; index: TRegister32; Offset: integer; Scale: byte; usebase: boolean);
- type TOffSize = (osNone, os8, os32);
- var mode: TModMode;
- offsize: TOffSize;
- useSIB: boolean;
- areg, arm: Byte;
- begin
- if not deref then begin
- mode := mmNaked;
- offsize := osNone;
- end else
- if usebase = false then begin
- offsize := os32;
- mode := mmDeref;
- base := EBP; //the "no base" value
- end else
- if Offset = 0 then begin
- mode := mmDeref;
- offsize := osNone;
- end else
- if (offset >= -128) and (offset < 128) then begin //signed byte
- mode := mmDisp8;
- offsize := os8;
- end else begin
- mode := mmDisp32;
- offsize := os32;
- end;
- if (mode <> mmnaked) then begin
- usesib := (Scale > 0) or (base = ESP);
- end else usesib := false;
- if useSIB then begin //calculate scale, easiest just to use a case statement..
- case scale of
- 0: begin //dont want an index value
- index := ESP; //"none" value
- end;
- 1: scale := 0;
- 2: scale := 1;
- 4: scale := 2;
- 8: scale := 3;
- else raise exception.create('Invalid scale, valid values are 1,2,4,8.');
- end;
- end;
- if (not useSIB) and (mode = mmDeref) and (base = EBP) then begin
- //not available, use [EBP+0] instead
- mode := mmDisp8;
- offsize := os8;
- Offset := 0;
- end;
- arm := regnum(base);
- areg := reg;
- if usesib then
- WriteByte(EncodeModRM(ModModeNum(mode), areg, 4)) else
- WriteByte(EncodeModRM(ModModeNum(mode), areg, arm));
- if usesib then begin
- WriteByte(EncodeSIB(Scale, regnum(index), regnum(base)));
- end;
- //Do we have to append an offset?
- case offsize of
- os8: WriteByte(byte(offset)); //ignore sign..
- os32: writelongword(longword(offset));
- end;
- end;
- procedure TASMInline.Mov(mem: TMemoryAddress; reg: TRegister32);
- begin
- require(mem.size, ms32);
- WriteByte($89);
- WriteRegRef(mem, reg);
- end;
- procedure TASMInline.Mov(reg: TRegister32; mem: TMemoryAddress);
- begin
- require(mem.size, ms32);
- WriteByte($8B);
- WriteRegRef(mem, reg);
- end;
- procedure TASMInline.Mov(reg: TRegister32; b: longword);
- begin
- writebyte($B8 + regnum(reg));
- writelongword(b);
- end;
- constructor TASMInline.create;
- begin
- fbuffer := tmemorystream.create;
- frelocs := tobjectlist.create;
- end;
- destructor TASMInline.destroy;
- begin
- fbuffer.free;
- frelocs.free;
- inherited;
- end;
- end.
|