seh32.inc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2013 by Free Pascal development team
  4. Support for 32-bit Windows exception handling
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. const
  12. EXCEPTION_UNWIND = EXCEPTION_UNWINDING or EXCEPTION_EXIT_UNWIND;
  13. type
  14. TDispatcherContext=record
  15. end;
  16. PSEHFrame=^TSEHFrame;
  17. TSEHFrame=record
  18. Next: PSEHFrame;
  19. Addr: Pointer;
  20. _EBP: PtrUint;
  21. HandlerArg: Pointer;
  22. end;
  23. procedure RtlUnwind(
  24. TargetFrame: Pointer;
  25. TargetIp: Pointer;
  26. ExceptionRecord: PExceptionRecord;
  27. ReturnValue: Pointer);
  28. stdcall; external 'kernel32.dll' name 'RtlUnwind';
  29. {$ifdef FPC_USE_WIN32_SEH}
  30. function NullHandler(
  31. var rec: TExceptionRecord;
  32. var frame: TSEHFrame;
  33. var context: TContext;
  34. var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl;
  35. begin
  36. result:=ExceptionContinueSearch;
  37. end;
  38. function GetBacktrace(Context: TContext; StartingFrame: Pointer; out Frames: PPointer): Longint;
  39. var
  40. FrameCount: Longint;
  41. oldebp: Cardinal;
  42. begin
  43. Frames:=AllocMem(RaiseMaxFrameCount*sizeof(pointer));
  44. FrameCount:=0;
  45. repeat
  46. oldebp:=context.ebp;
  47. { get_caller_stackinfo checks against StackTop on i386 }
  48. get_caller_stackinfo(pointer(Context.Ebp),codepointer(Context.Eip));
  49. if (Context.ebp<=oldebp) or (FrameCount>=RaiseMaxFrameCount) then
  50. break;
  51. if (Pointer(Context.ebp)>StartingFrame) or (FrameCount>0) then
  52. begin
  53. Frames[FrameCount]:=Pointer(Context.eip);
  54. Inc(FrameCount);
  55. end;
  56. until False;
  57. result:=FrameCount;
  58. end;
  59. function RunErrorCode386(const rec: TExceptionRecord; const context: TContext): Longint;
  60. begin
  61. result:=RunErrorCode(rec);
  62. { deal with SSE exceptions }
  63. if (result=-255) and ((context.ContextFlags and CONTEXT_EXTENDED_REGISTERS)<>0) then
  64. TranslateMxcsr(PLongword(@context.ExtendedRegisters[24])^,result);
  65. end;
  66. procedure fpc_RaiseException(Obj: TObject; AnAddr,AFrame: Pointer); [public,alias: 'FPC_RAISEEXCEPTION']; compilerproc;
  67. var
  68. ctx: TContext;
  69. args: array[0..4] of PtrUint;
  70. begin
  71. ctx.Ebp:=Cardinal(AFrame);
  72. ctx.Eip:=Cardinal(AnAddr);
  73. args[0]:=PtrUint(AnAddr);
  74. args[1]:=PtrUint(Obj);
  75. args[2]:=GetBacktrace(ctx,AFrame,PPointer(args[3]));
  76. args[4]:=PtrUInt(AFrame);
  77. RaiseException(FPC_EXCEPTION_CODE,EXCEPTION_NONCONTINUABLE,5,@args[0]);
  78. end;
  79. procedure fpc_reraise; [public,alias:'FPC_RERAISE']; compilerproc;
  80. var
  81. hp: PExceptObject;
  82. begin
  83. hp:=ExceptObjectStack;
  84. ExceptObjectStack:=hp^.next;
  85. { Since we're going to 'reraise' the original OS exception (or, more exactly, pretend
  86. it wasn't handled), we must revert action of CommonHandler. }
  87. if TExceptionRecord(hp^.ExceptRec^).ExceptionCode<>FPC_EXCEPTION_CODE then
  88. begin
  89. if assigned(hp^.frames) then
  90. freemem(hp^.frames);
  91. if hp^.refcount=0 then
  92. hp^.FObject.Free;
  93. end;
  94. TSEHFrame(hp^.SEHFrame^).Addr:=@NullHandler;
  95. longjmp(hp^.ReraiseBuf,1);
  96. end;
  97. { Parameters are dummy and used to force "ret 16" at the end;
  98. this removes a TSEHFrame record from the stack }
  99. procedure _fpc_leave(a1,a2,a3,a4:pointer); [public,alias:'_FPC_leave']; stdcall; compilerproc; assembler; nostackframe;
  100. asm
  101. movl 4(%esp),%eax
  102. movl %eax,%fs:(0)
  103. movl %ebp,%eax
  104. call 16(%esp)
  105. end;
  106. function PopObjectStack: PExceptObject;
  107. var
  108. hp: PExceptObject;
  109. begin
  110. hp:=ExceptObjectStack;
  111. if hp=nil then
  112. halt(255)
  113. else
  114. begin
  115. ExceptObjectStack:=hp^.next;
  116. if assigned(hp^.frames) then
  117. freemem(hp^.frames);
  118. end;
  119. result:=hp;
  120. end;
  121. function __FPC_finally_handler(
  122. var rec: TExceptionRecord;
  123. var frame: TSEHFrame;
  124. var context: TContext;
  125. var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl; [public,alias:'__FPC_finally_handler'];
  126. begin
  127. if (rec.ExceptionFlags and EXCEPTION_UNWIND)<>0 then
  128. begin
  129. { prevent endless loop if things go bad in user routine }
  130. frame.Addr:=@NullHandler;
  131. TUnwindProc(frame.HandlerArg)(frame._EBP);
  132. end;
  133. result:=ExceptionContinueSearch;
  134. end;
  135. function __FPC_default_handler(
  136. var rec: TExceptionRecord;
  137. var frame: TSEHFrame;
  138. var context: TContext;
  139. var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl; [public,alias:'__FPC_DEFAULT_HANDLER'];
  140. var
  141. Exc: TExceptObject;
  142. code: longint;
  143. begin
  144. if (rec.ExceptionFlags and EXCEPTION_UNWIND)=0 then
  145. begin
  146. { Athlon prefetch bug? }
  147. if (rec.ExceptionCode=STATUS_ACCESS_VIOLATION) and is_prefetch(pointer(context.eip)) then
  148. begin
  149. result:=ExceptionContinueExecution;
  150. exit;
  151. end
  152. else if (rec.ExceptionCode=STATUS_ILLEGAL_INSTRUCTION) and sse_check then
  153. begin
  154. os_supports_sse:=False;
  155. { skip the offending movaps %xmm7,%xmm6 instruction }
  156. inc(context.eip,3);
  157. result:=ExceptionContinueExecution;
  158. exit;
  159. end;
  160. RtlUnwind(@frame,nil,@rec,nil);
  161. asm
  162. { RtlUnwind destroys nonvolatile registers, this assembler block prevents
  163. regvar optimizations. }
  164. end ['ebx','esi','edi'];
  165. if rec.ExceptionCode<>FPC_EXCEPTION_CODE then
  166. begin
  167. code:=RunErrorCode386(rec,context);
  168. if code<0 then
  169. SysResetFPU;
  170. code:=abs(code);
  171. Exc.Addr:=rec.ExceptionAddress;
  172. Exc.FObject:=nil;
  173. if Assigned(ExceptObjProc) then
  174. Exc.FObject:=TObject(TExceptObjProc(ExceptObjProc)(code,rec));
  175. if Exc.FObject=nil then
  176. begin
  177. { This works because RtlUnwind does not actually unwind the stack on i386
  178. (and only on i386) }
  179. errorcode:=word(code);
  180. errorbase:=pointer(context.Ebp);
  181. erroraddr:=pointer(context.Eip);
  182. Halt(code);
  183. end;
  184. Exc.FrameCount:=GetBacktrace(context,nil,Exc.Frames);
  185. end
  186. else
  187. begin
  188. Exc.FObject:=TObject(rec.ExceptionInformation[1]);
  189. Exc.Addr:=rec.ExceptionInformation[0];
  190. Exc.Frames:=PCodePointer(rec.ExceptionInformation[3]);
  191. Exc.FrameCount:=ptruint(rec.ExceptionInformation[2]);
  192. code:=217;
  193. end;
  194. Exc.Refcount:=0;
  195. Exc.SEHFrame:=@frame;
  196. Exc.ExceptRec:=@rec;
  197. { link to ExceptObjectStack }
  198. Exc.Next:=ExceptObjectStack;
  199. ExceptObjectStack:=@Exc;
  200. if Assigned(ExceptProc) then
  201. begin
  202. ExceptProc(Exc.FObject,Exc.Addr,Exc.FrameCount,Exc.Frames);
  203. Halt(217);
  204. end
  205. else
  206. begin
  207. errorcode:=word(code);
  208. errorbase:=pointer(rec.ExceptionInformation[4]);
  209. erroraddr:=pointer(Exc.Addr);
  210. Halt(code);
  211. end;
  212. end;
  213. result:=ExceptionContinueExecution;
  214. end;
  215. function NestedHandler(
  216. var rec: TExceptionRecord;
  217. var frame: TSEHFrame;
  218. var context: TContext;
  219. var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl;
  220. var
  221. hp: PExceptObject;
  222. begin
  223. if (rec.ExceptionFlags and EXCEPTION_UNWIND)<>0 then
  224. begin
  225. hp:=PopObjectStack;
  226. if hp^.refcount=0 then
  227. hp^.FObject.Free;
  228. end;
  229. result:=ExceptionContinueSearch;
  230. end;
  231. function __FPC_except_safecall(
  232. var rec: TExceptionRecord;
  233. var frame: TSEHFrame;
  234. var context: TContext;
  235. var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl; forward;
  236. procedure CommonHandler(
  237. var rec: TExceptionRecord;
  238. var frame: TSEHFrame;
  239. var context: TContext;
  240. TargetAddr: Pointer);
  241. var
  242. Exc: TExceptObject;
  243. code: Longint;
  244. begin
  245. if rec.ExceptionCode<>FPC_EXCEPTION_CODE then
  246. begin
  247. Exc.FObject:=nil;
  248. code:=RunErrorCode386(rec,context);
  249. if Assigned(ExceptObjProc) then
  250. Exc.FObject:=TObject(TExceptObjProc(ExceptObjProc)(abs(code),rec));
  251. if (Exc.FObject=nil) and (frame.Addr<>Pointer(@__FPC_except_safecall)) then
  252. Exit;
  253. Exc.Addr:=rec.ExceptionAddress;
  254. Exc.FrameCount:=GetBacktrace(context,nil,Exc.Frames);
  255. if code<0 then
  256. SysResetFPU;
  257. end
  258. else
  259. begin
  260. Exc.Addr:=rec.ExceptionInformation[0];
  261. Exc.FObject:=TObject(rec.ExceptionInformation[1]);
  262. Exc.Framecount:=Longint(PtrUInt(rec.ExceptionInformation[2]));
  263. Exc.Frames:=rec.ExceptionInformation[3];
  264. end;
  265. RtlUnwind(@frame,nil,@rec,nil);
  266. Exc.Refcount:=0;
  267. Exc.SEHFrame:=@frame;
  268. Exc.ExceptRec:=@rec;
  269. { link to ExceptObjectStack }
  270. Exc.Next:=ExceptObjectStack;
  271. ExceptObjectStack:=@Exc;
  272. frame.Addr:=@NestedHandler;
  273. if setjmp(Exc.ReraiseBuf)=0 then
  274. asm
  275. movl Exc.FObject,%eax
  276. movl frame,%edx
  277. movl TargetAddr,%ecx // load ebp-based var before changing ebp
  278. movl TSEHFrame._EBP(%edx),%ebp
  279. jmpl *%ecx
  280. end;
  281. { control comes here if exception is re-raised }
  282. rec.ExceptionFlags:=rec.ExceptionFlags and (not EXCEPTION_UNWINDING);
  283. end;
  284. function __FPC_except_handler(
  285. var rec: TExceptionRecord;
  286. var frame: TSEHFrame;
  287. var context: TContext;
  288. var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl; [public,alias:'__FPC_except_handler'];
  289. begin
  290. if (rec.ExceptionFlags and EXCEPTION_UNWIND)=0 then
  291. begin
  292. { Athlon prefetch bug? }
  293. if (rec.ExceptionCode=STATUS_ACCESS_VIOLATION) and
  294. is_prefetch(pointer(Context.eip)) then
  295. begin
  296. result:=ExceptionContinueExecution;
  297. exit;
  298. end;
  299. CommonHandler(rec,frame,context,frame.HandlerArg);
  300. end;
  301. result:=ExceptionContinueSearch;
  302. end;
  303. { Safecall procedures are expected to handle OS exceptions even if they cannot be
  304. converted to language exceptions. This is indicated by distinct handler address. }
  305. function __FPC_except_safecall(
  306. var rec: TExceptionRecord;
  307. var frame: TSEHFrame;
  308. var context: TContext;
  309. var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl; [public,alias:'__FPC_except_safecall']; assembler; nostackframe;
  310. asm
  311. jmp __FPC_except_handler
  312. end;
  313. function __FPC_on_handler(
  314. var rec: TExceptionRecord;
  315. var frame: TSEHFrame;
  316. var context: TContext;
  317. var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl; [public,alias:'__FPC_on_handler'];
  318. var
  319. TargetAddr: Pointer;
  320. begin
  321. if (rec.ExceptionFlags and EXCEPTION_UNWIND)=0 then
  322. begin
  323. { Athlon prefetch bug? }
  324. if (rec.ExceptionCode=STATUS_ACCESS_VIOLATION) and
  325. is_prefetch(pointer(Context.eip)) then
  326. begin
  327. result:=ExceptionContinueExecution;
  328. exit;
  329. end;
  330. { Are we going to catch it? }
  331. TargetAddr:=FilterException(rec,0,PtrUInt(frame.HandlerArg),abs(RunErrorCode386(rec,context)));
  332. if assigned(TargetAddr) then
  333. CommonHandler(rec,frame,context,TargetAddr);
  334. end;
  335. result:=ExceptionContinueSearch;
  336. end;
  337. function fpc_safecallhandler(obj: TObject): HResult; [public,alias:'FPC_SAFECALLHANDLER']; compilerproc;
  338. var
  339. hp: PExceptObject;
  340. exc: TObject;
  341. begin
  342. hp:=PopObjectStack;
  343. exc:=hp^.FObject;
  344. if Assigned(obj) and Assigned(exc) then
  345. result:=obj.SafeCallException(exc,hp^.Addr)
  346. else
  347. result:=E_UNEXPECTED;
  348. if hp^.refcount=0 then
  349. exc.Free;
  350. asm
  351. movl %ebp,%edx // save current frame
  352. movl hp,%ecx
  353. movl TExceptObject.SEHFrame(%ecx),%ecx // target ESP minus sizeof(TSEHFrame)
  354. movl (%ecx),%eax
  355. movl %eax,%fs:(0) // restore SEH chain
  356. movl __RESULT,%eax
  357. movl TSEHFrame._EBP(%ecx),%ebp // restore EBP
  358. leal 16(%ecx),%esp // restore ESP past the SEH frame
  359. jmpl 4(%edx) // jump to caller
  360. end;
  361. end;
  362. procedure fpc_doneexception;[public,alias:'FPC_DONEEXCEPTION'] compilerproc;
  363. var
  364. hp: PExceptObject;
  365. begin
  366. hp:=PopObjectStack;
  367. if hp^.refcount=0 then
  368. hp^.FObject.Free;
  369. erroraddr:=nil;
  370. asm
  371. movl %ebp,%edx // save current frame
  372. movl hp,%eax
  373. movl TExceptObject.SEHFrame(%eax),%eax // target ESP minus sizeof(TSEHFrame)
  374. movl (%eax),%ecx
  375. movl %ecx,%fs:(0) // restore SEH chain
  376. movl TSEHFrame._EBP(%eax),%ebp // restore EBP
  377. leal 16(%eax),%esp // restore ESP, removing SEH frame
  378. jmpl 4(%edx) // jump to caller
  379. end;
  380. end;
  381. function main_wrapper(arg: Pointer; proc: Pointer): ptrint; assembler; nostackframe;
  382. asm
  383. xorl %ecx,%ecx
  384. pushl $__FPC_default_handler
  385. pushl %fs:(%ecx)
  386. movl %esp,%fs:(%ecx)
  387. call *%edx
  388. xorl %ecx,%ecx
  389. popl %edx
  390. movl %edx,%fs:(%ecx)
  391. popl %ecx
  392. end;
  393. {$endif FPC_USE_WIN32_SEH}