2
0

seh32.inc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  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. _oldebx,_oldedi,_oldesi,
  245. _ebx,_edi,_esi: dword;
  246. begin
  247. if rec.ExceptionCode<>FPC_EXCEPTION_CODE then
  248. begin
  249. Exc.FObject:=nil;
  250. code:=RunErrorCode386(rec,context);
  251. if Assigned(ExceptObjProc) then
  252. Exc.FObject:=TObject(TExceptObjProc(ExceptObjProc)(abs(code),rec));
  253. if (Exc.FObject=nil) and (frame.Addr<>Pointer(@__FPC_except_safecall)) then
  254. Exit;
  255. Exc.Addr:=rec.ExceptionAddress;
  256. Exc.FrameCount:=GetBacktrace(context,nil,Exc.Frames);
  257. if code<0 then
  258. SysResetFPU;
  259. end
  260. else
  261. begin
  262. Exc.Addr:=rec.ExceptionInformation[0];
  263. Exc.FObject:=TObject(rec.ExceptionInformation[1]);
  264. Exc.Framecount:=Longint(PtrUInt(rec.ExceptionInformation[2]));
  265. Exc.Frames:=rec.ExceptionInformation[3];
  266. end;
  267. asm
  268. movl %ebx,_oldebx
  269. movl %esi,_oldesi
  270. movl %edi,_oldedi
  271. end;
  272. RtlUnwind(@frame,nil,@rec,nil);
  273. asm
  274. movl %ebx,_ebx
  275. movl %esi,_esi
  276. movl %edi,_edi
  277. movl _oldebx,%ebx
  278. movl _oldesi,%esi
  279. movl _oldedi,%edi
  280. end;
  281. Exc.Refcount:=0;
  282. Exc.SEHFrame:=@frame;
  283. Exc.ExceptRec:=@rec;
  284. { link to ExceptObjectStack }
  285. Exc.Next:=ExceptObjectStack;
  286. ExceptObjectStack:=@Exc;
  287. frame.Addr:=@NestedHandler;
  288. if setjmp(Exc.ReraiseBuf)=0 then
  289. asm
  290. movl Exc.FObject,%eax
  291. movl frame,%edx
  292. movl TargetAddr,%ecx // load ebp-based var before changing ebp
  293. movl _ebx,%ebx
  294. movl _esi,%esi
  295. movl _edi,%edi
  296. movl TSEHFrame._EBP(%edx),%ebp
  297. jmpl *%ecx
  298. end;
  299. { control comes here if exception is re-raised }
  300. rec.ExceptionFlags:=rec.ExceptionFlags and (not EXCEPTION_UNWINDING);
  301. end;
  302. function __FPC_except_handler(
  303. var rec: TExceptionRecord;
  304. var frame: TSEHFrame;
  305. var context: TContext;
  306. var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl; [public,alias:'__FPC_except_handler'];
  307. begin
  308. if (rec.ExceptionFlags and EXCEPTION_UNWIND)=0 then
  309. begin
  310. { Athlon prefetch bug? }
  311. if (rec.ExceptionCode=STATUS_ACCESS_VIOLATION) and
  312. is_prefetch(pointer(Context.eip)) then
  313. begin
  314. result:=ExceptionContinueExecution;
  315. exit;
  316. end;
  317. CommonHandler(rec,frame,context,frame.HandlerArg);
  318. end;
  319. result:=ExceptionContinueSearch;
  320. end;
  321. { Safecall procedures are expected to handle OS exceptions even if they cannot be
  322. converted to language exceptions. This is indicated by distinct handler address. }
  323. function __FPC_except_safecall(
  324. var rec: TExceptionRecord;
  325. var frame: TSEHFrame;
  326. var context: TContext;
  327. var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl; [public,alias:'__FPC_except_safecall']; assembler; nostackframe;
  328. asm
  329. jmp __FPC_except_handler
  330. end;
  331. function __FPC_on_handler(
  332. var rec: TExceptionRecord;
  333. var frame: TSEHFrame;
  334. var context: TContext;
  335. var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl; [public,alias:'__FPC_on_handler'];
  336. var
  337. TargetAddr: Pointer;
  338. begin
  339. if (rec.ExceptionFlags and EXCEPTION_UNWIND)=0 then
  340. begin
  341. { Athlon prefetch bug? }
  342. if (rec.ExceptionCode=STATUS_ACCESS_VIOLATION) and
  343. is_prefetch(pointer(Context.eip)) then
  344. begin
  345. result:=ExceptionContinueExecution;
  346. exit;
  347. end;
  348. { Are we going to catch it? }
  349. TargetAddr:=FilterException(rec,0,PtrUInt(frame.HandlerArg),abs(RunErrorCode386(rec,context)));
  350. if assigned(TargetAddr) then
  351. CommonHandler(rec,frame,context,TargetAddr);
  352. end;
  353. result:=ExceptionContinueSearch;
  354. end;
  355. function fpc_safecallhandler(obj: TObject): HResult; [public,alias:'FPC_SAFECALLHANDLER']; compilerproc;
  356. var
  357. hp: PExceptObject;
  358. exc: TObject;
  359. begin
  360. hp:=PopObjectStack;
  361. exc:=hp^.FObject;
  362. if Assigned(obj) and Assigned(exc) then
  363. result:=obj.SafeCallException(exc,hp^.Addr)
  364. else
  365. result:=E_UNEXPECTED;
  366. if hp^.refcount=0 then
  367. exc.Free;
  368. asm
  369. movl %ebp,%edx // save current frame
  370. movl hp,%ecx
  371. movl TExceptObject.SEHFrame(%ecx),%ecx // target ESP minus sizeof(TSEHFrame)
  372. movl (%ecx),%eax
  373. movl %eax,%fs:(0) // restore SEH chain
  374. movl __RESULT,%eax
  375. movl TSEHFrame._EBP(%ecx),%ebp // restore EBP
  376. leal 16(%ecx),%esp // restore ESP past the SEH frame
  377. jmpl 4(%edx) // jump to caller
  378. end;
  379. end;
  380. procedure fpc_doneexception;[public,alias:'FPC_DONEEXCEPTION'] compilerproc;
  381. var
  382. hp: PExceptObject;
  383. begin
  384. hp:=PopObjectStack;
  385. if hp^.refcount=0 then
  386. hp^.FObject.Free;
  387. erroraddr:=nil;
  388. asm
  389. movl %ebp,%edx // save current frame
  390. movl hp,%eax
  391. movl TExceptObject.SEHFrame(%eax),%eax // target ESP minus sizeof(TSEHFrame)
  392. movl (%eax),%ecx
  393. movl %ecx,%fs:(0) // restore SEH chain
  394. movl TSEHFrame._EBP(%eax),%ebp // restore EBP
  395. leal 16(%eax),%esp // restore ESP, removing SEH frame
  396. jmpl 4(%edx) // jump to caller
  397. end;
  398. end;
  399. function main_wrapper(arg: Pointer; proc: Pointer): ptrint; assembler; nostackframe;
  400. asm
  401. xorl %ecx,%ecx
  402. pushl $__FPC_default_handler
  403. pushl %fs:(%ecx)
  404. movl %esp,%fs:(%ecx)
  405. call *%edx
  406. xorl %ecx,%ecx
  407. popl %edx
  408. movl %edx,%fs:(%ecx)
  409. popl %ecx
  410. end;
  411. {$endif FPC_USE_WIN32_SEH}