win64unw.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. {
  2. Copyright (c) 2011 by Free Pascal development team
  3. Support for win64 unwind data
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. ****************************************************************************
  16. }
  17. unit win64unw;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. cclasses,globtype,aasmbase,aasmdata,aasmtai,cgbase,ogbase;
  22. type
  23. TWin64CFI=class
  24. private
  25. FFrameOffs, FFrameReg: Integer;
  26. FFlags: Integer;
  27. FHandler: TObjSymbol;
  28. FCount: Integer;
  29. FElements:TLinkedList;
  30. FFrameStartSym:TObjSymbol;
  31. FFrameStartSec:TObjSection;
  32. FXdataSym:TObjSymbol;
  33. FXdataSec:TObjSection;
  34. FPrologueEndPos:aword;
  35. FPrologueEndSeen:Boolean;
  36. FName: pshortstring;
  37. procedure AddElement(objdata:TObjData;aCode,aInfo:Integer;aOffs:dword);
  38. public
  39. constructor create;
  40. destructor destroy;override;
  41. procedure generate_prologue_data(objdata:TObjData);
  42. procedure start_frame(objdata:TObjData; const name: string);
  43. procedure end_frame(objdata:TObjData);
  44. procedure end_prologue(objdata:TObjData);
  45. procedure push_reg(objdata:TObjData;reg:tregister);
  46. procedure save_reg(objdata:TObjData;reg:tregister;ofs:dword);
  47. procedure save_xmm(objdata:TObjData;reg:tregister;ofs:dword);
  48. procedure set_frame(objdata:TObjData; reg:tregister;ofs:dword);
  49. procedure stack_alloc(objdata:TObjData;ofs:dword);
  50. procedure switch_to_handlerdata(objdata:TObjData);
  51. end;
  52. implementation
  53. uses
  54. cutils,globals,verbose,cpubase;
  55. const
  56. UWOP_PUSH_NONVOL = 0; { info = register number }
  57. UWOP_ALLOC_LARGE = 1; { no info, alloc size in next 2 slots }
  58. UWOP_ALLOC_SMALL = 2; { info = size of allocation / 8 - 1 }
  59. UWOP_SET_FPREG = 3; { no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 }
  60. UWOP_SAVE_NONVOL = 4; { info = register number, offset in next slot }
  61. UWOP_SAVE_NONVOL_FAR = 5; { info = register number, offset in next 2 slots }
  62. UWOP_SAVE_XMM = 6;
  63. UWOP_SAVE_XMM_FAR = 7;
  64. UWOP_SAVE_XMM128 = 8; { info = XMM reg number, offset in next slot }
  65. UWOP_SAVE_XMM128_FAR = 9; { info = XMM reg number, offset in next 2 slots }
  66. UWOP_PUSH_MACHFRAME = 10; { info = 0: no error-code, 1: error-code }
  67. UNW_FLAG_EHANDLER = $01; { exceptiion handler }
  68. UNW_FLAG_UHANDLER = $02; { termination handler }
  69. UNW_FLAG_FHANDLER = UNW_FLAG_EHANDLER or UNW_FLAG_UHANDLER;
  70. UNW_FLAG_CHAININFO = $04; { mutually exclusive with the above }
  71. type
  72. tai_seh_directive_x64=class(tai_seh_directive)
  73. procedure generate_code(objdata:TObjData);override;
  74. end;
  75. TPrologueElement=class(TLinkedListItem)
  76. public
  77. opcode: Integer; { =(info shl 4) or code }
  78. ofs: dword;
  79. addr: aword;
  80. end;
  81. var
  82. current_unw: TWin64Cfi;
  83. function EncodeReg(r: TRegister): integer;
  84. begin
  85. case r of
  86. NR_RAX: result:=0;
  87. NR_RCX: result:=1;
  88. NR_RDX: result:=2;
  89. NR_RBX: result:=3;
  90. NR_RSP: result:=4;
  91. NR_RBP: result:=5;
  92. NR_RSI: result:=6;
  93. NR_RDI: result:=7;
  94. NR_R8: result:=8;
  95. NR_R9: result:=9;
  96. NR_R10: result:=10;
  97. NR_R11: result:=11;
  98. NR_R12: result:=12;
  99. NR_R13: result:=13;
  100. NR_R14: result:=14;
  101. NR_R15: result:=15;
  102. else
  103. InternalError(2011072305);
  104. end;
  105. end;
  106. function EncodeXMM(r: TRegister): integer;
  107. begin
  108. if getregtype(r)=R_MMREGISTER then
  109. result:=getsupreg(r)
  110. else
  111. InternalError(2011072308);
  112. end;
  113. { TWin64CFI }
  114. constructor TWin64CFI.create;
  115. begin
  116. inherited create;
  117. FElements:=TLinkedList.Create;
  118. end;
  119. destructor TWin64CFI.destroy;
  120. begin
  121. FElements.Free;
  122. stringdispose(FName);
  123. inherited destroy;
  124. end;
  125. procedure TWin64CFI.AddElement(objdata:TObjData;aCode,aInfo:Integer;aOffs:dword);
  126. var
  127. el:TPrologueElement;
  128. begin
  129. el:=TPrologueElement.Create;
  130. FElements.concat(el);
  131. el.opcode:=(aInfo shl 4) or aCode;
  132. el.ofs:=aOffs;
  133. el.addr:=objdata.CurrObjSec.Size;
  134. { a single element may occupy 1,2 or 3 word-sized slots }
  135. case aCode of
  136. UWOP_ALLOC_LARGE:
  137. Inc(FCount,2+ord(aInfo<>0));
  138. UWOP_SAVE_NONVOL_FAR,
  139. UWOP_SAVE_XMM128_FAR:
  140. Inc(FCount,3);
  141. UWOP_SAVE_NONVOL,
  142. UWOP_SAVE_XMM128:
  143. Inc(FCount,2);
  144. else
  145. inc(FCount);
  146. end;
  147. end;
  148. { Changes objdata.CurrObjSec to .xdata, so generation of
  149. handler data may continue }
  150. procedure TWin64CFI.generate_prologue_data(objdata:TObjData);
  151. var
  152. hp: TPrologueElement;
  153. uwcode: array [0..1] of byte;
  154. uwdata: array [0..3] of byte;
  155. zero: word;
  156. begin
  157. if FCount>255 then
  158. InternalError(2011072301);
  159. if not FPrologueEndSeen then
  160. CGMessage(asmw_e_missing_endprologue);
  161. if (FPrologueEndPos-FFrameStartSym.address) > 255 then
  162. CGMessage(asmw_e_prologue_too_large);
  163. if codegenerror then
  164. exit;
  165. FXdataSec:=objdata.createsection('.xdata.n_'+lower(FName^),4,[oso_data,oso_load]);
  166. FXdataSym:=objdata.symboldefine('$unwind$'+FName^,AB_GLOBAL,AT_DATA);
  167. uwdata[0]:=(FFlags shl 3) or 1;
  168. uwdata[1]:=FPrologueEndPos-FFrameStartSym.address;
  169. uwdata[2]:=FCount;
  170. { Offset is multiple of 16, so it is already shifted into correct position }
  171. uwdata[3]:=FFrameOffs or FFrameReg;
  172. objdata.writebytes(uwdata,4);
  173. { write elements in reverse order (offset descending) }
  174. hp:=TPrologueElement(FElements.Last);
  175. while Assigned(hp) do
  176. begin
  177. uwcode[0]:=hp.addr-FFrameStartSym.address;
  178. uwcode[1]:=hp.opcode;
  179. objdata.writebytes(uwcode,2);
  180. case hp.opcode and $0F of
  181. UWOP_PUSH_NONVOL,
  182. UWOP_ALLOC_SMALL,
  183. UWOP_SET_FPREG,
  184. UWOP_PUSH_MACHFRAME: ; { These have no extra data }
  185. UWOP_ALLOC_LARGE:
  186. if (hp.opcode and $F0)<>0 then
  187. objdata.writebytes(hp.ofs,4)
  188. else
  189. objdata.writebytes(hp.ofs,2);
  190. UWOP_SAVE_NONVOL_FAR,
  191. UWOP_SAVE_XMM128_FAR:
  192. objdata.writebytes(hp.ofs,4);
  193. UWOP_SAVE_NONVOL,
  194. UWOP_SAVE_XMM128:
  195. objdata.writebytes(hp.ofs,2);
  196. else
  197. InternalError(2011072302);
  198. end;
  199. hp:=TPrologueElement(hp.Previous);
  200. end;
  201. { pad with zeros to dword boundary }
  202. zero:=0;
  203. if odd(FCount) then
  204. objdata.writebytes(zero,2);
  205. if Assigned(FHandler) then
  206. objdata.writereloc(0,sizeof(longint),FHandler,RELOC_RVA);
  207. end;
  208. procedure TWin64CFI.start_frame(objdata:TObjData;const name:string);
  209. begin
  210. if assigned(FName) then
  211. internalerror(2011072306);
  212. FName:=stringdup(name);
  213. FFrameStartSym:=objdata.symbolref(name);
  214. FFrameStartSec:=objdata.CurrObjSec;
  215. FCount:=0;
  216. FFrameReg:=0;
  217. FFrameOffs:=0;
  218. FPrologueEndPos:=0;
  219. FPrologueEndSeen:=false;
  220. FHandler:=nil;
  221. FXdataSec:=nil;
  222. FXdataSym:=nil;
  223. FFlags:=0;
  224. end;
  225. procedure TWin64CFI.switch_to_handlerdata(objdata:TObjData);
  226. begin
  227. if not assigned(FName) then
  228. internalerror(2011072310);
  229. if FHandler=nil then
  230. CGMessage(asmw_e_handlerdata_no_handler);
  231. if FXdataSec=nil then
  232. generate_prologue_data(objdata)
  233. else
  234. objdata.SetSection(FXdataSec);
  235. end;
  236. procedure TWin64CFI.end_frame(objdata:TObjData);
  237. var
  238. pdatasym:TObjSymbol;
  239. begin
  240. if not assigned(FName) then
  241. internalerror(2011072307);
  242. if FXdataSec=nil then
  243. generate_prologue_data(objdata);
  244. if not codegenerror then
  245. begin
  246. objdata.createsection(sec_pdata,lower(FName^));
  247. pdatasym:=objdata.symboldefine('$pdata$'+FName^,AB_LOCAL,AT_DATA);
  248. objdata.writereloc(0,4,FFrameStartSym,RELOC_RVA);
  249. objdata.writereloc(FFrameStartSec.Size,4,FFrameStartSym,RELOC_RVA);
  250. objdata.writereloc(0,4,FXdataSym,RELOC_RVA);
  251. { restore previous state }
  252. objdata.SetSection(FFrameStartSec);
  253. { create a dummy relocation, so pdata is not smartlinked away }
  254. objdata.writereloc(0,0,pdatasym,RELOC_NONE);
  255. end;
  256. FElements.Clear;
  257. FFrameStartSym:=nil;
  258. FHandler:=nil;
  259. FXdataSec:=nil;
  260. FXdataSym:=nil;
  261. FFlags:=0;
  262. stringdispose(FName);
  263. end;
  264. procedure TWin64CFI.end_prologue(objdata:TObjData);
  265. begin
  266. if not assigned(FName) then
  267. internalerror(2011072312);
  268. FPrologueEndPos:=objdata.CurrObjSec.Size;
  269. FPrologueEndSeen:=true;
  270. end;
  271. procedure TWin64CFI.push_reg(objdata:TObjData;reg:tregister);
  272. begin
  273. AddElement(objdata,UWOP_PUSH_NONVOL,EncodeReg(reg),0);
  274. end;
  275. procedure TWin64CFI.save_reg(objdata:TObjData;reg:tregister;ofs:dword);
  276. var
  277. info: Integer;
  278. begin
  279. info:=EncodeReg(reg);
  280. if ((ofs and 7) = 0) and (ofs<=$ffff*8) then
  281. AddElement(objdata,UWOP_SAVE_NONVOL,info,ofs shr 3)
  282. else
  283. AddElement(objdata,UWOP_SAVE_NONVOL_FAR,info,ofs);
  284. end;
  285. procedure TWin64CFI.save_xmm(objdata:TObjData;reg:tregister;ofs:dword);
  286. var
  287. info: Integer;
  288. begin
  289. info:=EncodeXMM(reg);
  290. if ((ofs and 15)=0) and (ofs<=$ffff*16) then
  291. AddElement(objdata,UWOP_SAVE_XMM128, info, ofs shr 4)
  292. else
  293. AddElement(objdata,UWOP_SAVE_XMM128_FAR, info, ofs);
  294. end;
  295. procedure TWin64CFI.set_frame(objdata:TObjData;reg:tregister;ofs:dword);
  296. var
  297. info: Integer;
  298. begin
  299. info:=EncodeReg(reg);
  300. if FFrameReg<>0 then
  301. InternalError(2011072303);
  302. if info=0 then { frame register cannot be RAX }
  303. InternalError(2011072304);
  304. if (ofs>240) or ((ofs and 15)<>0) then
  305. InternalError(2011072310);
  306. FFrameReg:=info;
  307. FFrameOffs:=ofs;
  308. { !! looks like docs aren't correct and info should be set to register }
  309. AddElement(objdata,UWOP_SET_FPREG,0,0);
  310. end;
  311. procedure TWin64CFI.stack_alloc(objdata:TObjData;ofs:dword);
  312. begin
  313. if ((ofs and 7)=0) and (ofs<=128) then
  314. AddElement(objdata,UWOP_ALLOC_SMALL,(ofs-8) shr 3,0)
  315. else if ((ofs and 7) = 0) and (ofs<=$ffff * 8) then
  316. AddElement(objdata,UWOP_ALLOC_LARGE,0,ofs shr 3)
  317. else
  318. AddElement(objdata,UWOP_ALLOC_LARGE,1,ofs);
  319. end;
  320. procedure tai_seh_directive_x64.generate_code(objdata:TObjData);
  321. begin
  322. case kind of
  323. ash_proc:
  324. current_unw.start_frame(objdata,data.name^);
  325. ash_endproc:
  326. current_unw.end_frame(objdata);
  327. ash_endprologue:
  328. current_unw.end_prologue(objdata);
  329. ash_handler:
  330. begin
  331. current_unw.FHandler:=objdata.symbolref(data.name^);
  332. current_unw.FFlags:=data.flags;
  333. end;
  334. ash_handlerdata:
  335. current_unw.switch_to_handlerdata(objdata);
  336. ash_eh,ash_32,ash_no32: ; { these are not for x86_64 }
  337. ash_setframe:
  338. current_unw.set_frame(objdata,data.reg,data.offset);
  339. ash_stackalloc:
  340. current_unw.stack_alloc(objdata,data.offset);
  341. ash_pushreg:
  342. current_unw.push_reg(objdata,data.reg);
  343. ash_savereg:
  344. current_unw.save_reg(objdata,data.reg,data.offset);
  345. ash_savexmm:
  346. current_unw.save_xmm(objdata,data.reg,data.offset);
  347. ash_pushframe: {TBD};
  348. end;
  349. end;
  350. initialization
  351. cai_seh_directive:=tai_seh_directive_x64;
  352. current_unw:=TWin64CFI.Create;
  353. finalization
  354. current_unw.Free;
  355. end.