ASMInline.pas 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. unit ASMInline;
  2. interface
  3. {ASM Inliner
  4. Nicholas Sherlock
  5. This is incomplete, I've only implemented enough to support InnoCallback.
  6. Instructions are stored in a TMemoryStream internally
  7. Instructions usually accept some combination of Registers, Immediates and
  8. Memory References. Memory References can either be of the simple form [EAX]
  9. (Where [EAX] is really a Delphi set), or the user can build the address with
  10. the TASMInline.addr() function. It'd be nice to have a function which builds
  11. the address from a string, too, allowing the more intuitive '[EAX+EBX*4+1000]'
  12. style.
  13. The generation of instruction-relative addresses generates Relocations, using
  14. SaveToMemory() automatically rebases using the relocations to make these correct
  15. in the final block of memory.
  16. !!!! Not all special cases have been implemented in WriteRegRef().
  17. Further reduced to just what's needed for Inno Setup by Martijn Laan
  18. }
  19. uses Sysutils, Windows, Classes, Contnrs;
  20. type
  21. TModMode = (mmNaked, mmDeref, mmDisp8, mmDisp32);
  22. TRegister32 = (EAX, EBX, ECX, EDX, ESP, EBP, ESI, EDI);
  23. TRegister32Set = set of TRegister32;
  24. TRegister16 = (AX, BX, CX, DX, SP, BP, SI, DI);
  25. TRegister16Set = set of TRegister16;
  26. TRegister8 = (AH, AL, BH, BL, CH, CL, DH, DL);
  27. TCLRegister = CL..CL;
  28. TMemSize = (ms8, ms16, ms32, ms64);
  29. TMemoryAddress = record
  30. size: TMemSize;
  31. usebase: boolean;
  32. base, index: TRegister32;
  33. offset: integer;
  34. scale: byte;
  35. end;
  36. EOperandSizeMismatch = class(exception)
  37. public
  38. constructor create;
  39. end;
  40. TRelocType = (rt32Bit);
  41. TReloc = class
  42. public
  43. position: longword;
  44. relocType: TRelocType;
  45. end;
  46. TASMInline = class
  47. private
  48. fbuffer: TMemoryStream;
  49. frelocs: TObjectList;
  50. fbase: longword;
  51. procedure AddRelocation(position: longword; relocType: TRelocType);
  52. function GetReloc(index: integer): TReloc;
  53. function RelocCount: integer;
  54. property Relocs[index: integer]: TReloc read GetReloc;
  55. procedure WriteByte(b: byte);
  56. procedure WriteInteger(i: integer);
  57. procedure WriteLongWord(l: longword);
  58. procedure WriteRegRef(reg: byte; base: TRegister32; deref: boolean; index: TRegister32; Offset: integer; Scale: byte; usebase: boolean); overload;
  59. procedure WriteRegRef(mem: TMemoryAddress; reg: TRegister32); overload;
  60. procedure WriteRegRef(reg: TRegister32; base: TRegister32; deref: boolean; index: TRegister32 = EAX; Offset: integer = 0; Scale: byte = 0; usebase: boolean = true); overload;
  61. public
  62. function Size: integer;
  63. procedure Relocate(base: pointer);
  64. function SaveAsMemory: pointer;
  65. procedure SaveToMemory(target: pointer);
  66. constructor create;
  67. destructor Destroy; override;
  68. function Addr(base: TRegister32; offset: Integer; size: TMemSize = ms32): TMemoryAddress; overload;
  69. //PUSH reg
  70. procedure Push(reg: TRegister32); overload;
  71. //POP reg
  72. procedure Pop(reg: TRegister32);
  73. //JUMP rel32
  74. procedure Jmp(target: pointer); overload;
  75. //MOV reg, imm
  76. procedure Mov(reg: TRegister32; b: longword); overload;
  77. //MOV reg, mem and MOV mem, reg
  78. procedure Mov(mem: TMemoryAddress; reg: TRegister32); overload;
  79. procedure Mov(reg: TRegister32; mem: TMemoryAddress); overload;
  80. end;
  81. implementation
  82. constructor EOperandSizeMismatch.create;
  83. begin
  84. inherited create('Operand size mismatch');
  85. end;
  86. {Throw an exception if test<>match. Poor man's assert().
  87. Could overload to add other sorts of tests}
  88. procedure require(test: TMemSize; match: TMemSize);
  89. begin
  90. if test <> match then
  91. raise EOperandSizeMismatch.create;
  92. end;
  93. function regnum(reg: TRegister32): byte; overload;
  94. begin
  95. case reg of
  96. EAX: result := 0;
  97. EBX: result := 3;
  98. ECX: result := 1;
  99. EDX: result := 2;
  100. ESP: result := 4;
  101. EBP: result := 5;
  102. ESI: result := 6;
  103. EDI: result := 7;
  104. else raise exception.create('Unknown register...');
  105. end;
  106. end;
  107. function ModModeNum(m: TModMode): byte;
  108. begin
  109. case m of
  110. mmDeref: result := 0;
  111. mmDisp8: result := 1;
  112. mmDisp32: result := 2;
  113. mmNaked: result := 3;
  114. else raise exception.create('Invalid mod mode: ' + inttostr(ord(m)));
  115. end;
  116. end;
  117. function EncodeSIB(scale, index, base: byte): byte;
  118. begin
  119. result := base or (index shl 3) or (scale shl 6);
  120. end;
  121. function EncodeModRM(aMod, aReg, aRM: byte): byte; overload;
  122. begin
  123. result := (aMod shl 6) or (areg shl 3) or aRM;
  124. end;
  125. {$IFOPT R+}
  126. {$DEFINE RESTORER}
  127. {$R-}
  128. {$ENDIF}
  129. {$IFOPT Q+}
  130. {$DEFINE RESTOREQ}
  131. {$Q-}
  132. {$ENDIF}
  133. procedure TASMInline.Relocate(base: pointer);
  134. var oldpos, diff, orig: integer;
  135. i: integer;
  136. reloc: TReloc;
  137. begin
  138. oldpos := fbuffer.Position;
  139. try
  140. diff := -(longword(base) - fbase);
  141. for i := 0 to RelocCount - 1 do begin
  142. reloc := Relocs[i];
  143. case reloc.relocType of
  144. rt32Bit: begin
  145. fbuffer.Seek(reloc.position, soBeginning);
  146. fbuffer.Read(orig, sizeof(orig));
  147. fbuffer.seek(-sizeof(orig), soCurrent);
  148. orig := LongWord(orig + diff);
  149. fbuffer.write(orig, sizeof(orig));
  150. end;
  151. end;
  152. end;
  153. fbase := longword(base);
  154. finally
  155. fbuffer.position := oldpos;
  156. end;
  157. end;
  158. {$IFDEF RESTORER}
  159. {$R+}
  160. {$ENDIF}
  161. {$IFDEF RESTOREQ}
  162. {Q+}
  163. {$ENDIF}
  164. function TASMInline.GetReloc(index: integer): TReloc;
  165. begin
  166. result := TReloc(frelocs[index]);
  167. end;
  168. function TASMInline.RelocCount: integer;
  169. begin
  170. result := frelocs.Count;
  171. end;
  172. procedure TASMInline.AddRelocation(position: longword; relocType: TRelocType);
  173. var reloc: TReloc;
  174. begin
  175. reloc := TReloc.Create;
  176. reloc.position := position;
  177. reloc.relocType := relocType;
  178. frelocs.add(reloc);
  179. end;
  180. function TASMInline.SaveAsMemory: pointer;
  181. var buf: pointer;
  182. oldprotect: Cardinal;
  183. begin
  184. GetMem(buf, size);
  185. VirtualProtect(buf, Size, PAGE_EXECUTE_READWRITE, oldprotect);
  186. SaveToMemory(buf);
  187. result := buf;
  188. end;
  189. procedure TASMInline.SaveToMemory(target: pointer);
  190. begin
  191. Relocate(target);
  192. Move(fbuffer.memory^, target^, size);
  193. end;
  194. function TASMInline.Addr(base: TRegister32; offset: Integer; size: TMemSize = ms32): TMemoryAddress;
  195. begin
  196. result.base := base;
  197. result.scale := 0; //don't use Index
  198. result.offset := offset;
  199. result.size := size;
  200. result.usebase := true;
  201. end;
  202. function TASMInline.Size: integer;
  203. begin
  204. result := fbuffer.size;
  205. end;
  206. procedure TASMInline.WriteInteger(i: integer);
  207. begin
  208. fbuffer.write(i, 4);
  209. end;
  210. procedure TASMInline.writelongword(l: longword);
  211. begin
  212. fbuffer.write(l, 4);
  213. end;
  214. procedure TASMInline.writebyte(b: byte);
  215. begin
  216. fbuffer.write(b, 1);
  217. end;
  218. procedure TASMInline.Pop(reg: TRegister32);
  219. begin
  220. writebyte($58 + regnum(reg));
  221. end;
  222. procedure TASMInline.Jmp(target: pointer);
  223. begin
  224. writebyte($E9);
  225. AddRelocation(fbuffer.position, rt32bit);
  226. WriteInteger(integer(target) - (integer(fBase) + fbuffer.Position + 4));
  227. end;
  228. procedure TASMInline.Push(reg: TRegister32);
  229. begin
  230. writebyte($50 + regnum(reg));
  231. end;
  232. procedure TASMInline.WriteRegRef(mem: TMemoryAddress; reg: TRegister32);
  233. begin
  234. writeregref(reg, mem.base, true, mem.index, mem.offset, mem.scale, mem.usebase);
  235. end;
  236. //Write the MODR/M and SIB byte for the given register or memory reference
  237. procedure TASMInline.WriteRegRef(reg: TRegister32; base: TRegister32; deref: boolean; index: TRegister32 = EAX; Offset: integer = 0; Scale: byte = 0; usebase: boolean = true);
  238. begin
  239. WriteRegRef(regnum(reg), base, deref, index, Offset, scale, usebase);
  240. end;
  241. procedure TASMInline.WriteRegRef(reg: byte; base: TRegister32; deref: boolean; index: TRegister32; Offset: integer; Scale: byte; usebase: boolean);
  242. type TOffSize = (osNone, os8, os32);
  243. var mode: TModMode;
  244. offsize: TOffSize;
  245. useSIB: boolean;
  246. areg, arm: Byte;
  247. begin
  248. if not deref then begin
  249. mode := mmNaked;
  250. offsize := osNone;
  251. end else
  252. if usebase = false then begin
  253. offsize := os32;
  254. mode := mmDeref;
  255. base := EBP; //the "no base" value
  256. end else
  257. if Offset = 0 then begin
  258. mode := mmDeref;
  259. offsize := osNone;
  260. end else
  261. if (offset >= -128) and (offset < 128) then begin //signed byte
  262. mode := mmDisp8;
  263. offsize := os8;
  264. end else begin
  265. mode := mmDisp32;
  266. offsize := os32;
  267. end;
  268. if (mode <> mmnaked) then begin
  269. usesib := (Scale > 0) or (base = ESP);
  270. end else usesib := false;
  271. if useSIB then begin //calculate scale, easiest just to use a case statement..
  272. case scale of
  273. 0: begin //dont want an index value
  274. index := ESP; //"none" value
  275. end;
  276. 1: scale := 0;
  277. 2: scale := 1;
  278. 4: scale := 2;
  279. 8: scale := 3;
  280. else raise exception.create('Invalid scale, valid values are 1,2,4,8.');
  281. end;
  282. end;
  283. if (not useSIB) and (mode = mmDeref) and (base = EBP) then begin
  284. //not available, use [EBP+0] instead
  285. mode := mmDisp8;
  286. offsize := os8;
  287. Offset := 0;
  288. end;
  289. arm := regnum(base);
  290. areg := reg;
  291. if usesib then
  292. WriteByte(EncodeModRM(ModModeNum(mode), areg, 4)) else
  293. WriteByte(EncodeModRM(ModModeNum(mode), areg, arm));
  294. if usesib then begin
  295. WriteByte(EncodeSIB(Scale, regnum(index), regnum(base)));
  296. end;
  297. //Do we have to append an offset?
  298. case offsize of
  299. os8: WriteByte(byte(offset)); //ignore sign..
  300. os32: writelongword(longword(offset));
  301. end;
  302. end;
  303. procedure TASMInline.Mov(mem: TMemoryAddress; reg: TRegister32);
  304. begin
  305. require(mem.size, ms32);
  306. WriteByte($89);
  307. WriteRegRef(mem, reg);
  308. end;
  309. procedure TASMInline.Mov(reg: TRegister32; mem: TMemoryAddress);
  310. begin
  311. require(mem.size, ms32);
  312. WriteByte($8B);
  313. WriteRegRef(mem, reg);
  314. end;
  315. procedure TASMInline.Mov(reg: TRegister32; b: longword);
  316. begin
  317. writebyte($B8 + regnum(reg));
  318. writelongword(b);
  319. end;
  320. constructor TASMInline.create;
  321. begin
  322. fbuffer := tmemorystream.create;
  323. frelocs := tobjectlist.create;
  324. end;
  325. destructor TASMInline.destroy;
  326. begin
  327. fbuffer.free;
  328. frelocs.free;
  329. inherited;
  330. end;
  331. end.