2
0

seh32.inc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  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. code: longint;
  142. Obj: TObject;
  143. Adr: Pointer;
  144. Frames: PCodePointer;
  145. FrameCount: Longint;
  146. begin
  147. if (rec.ExceptionFlags and EXCEPTION_UNWIND)=0 then
  148. begin
  149. { Athlon prefetch bug? }
  150. if (rec.ExceptionCode=STATUS_ACCESS_VIOLATION) and is_prefetch(pointer(context.eip)) then
  151. begin
  152. result:=ExceptionContinueExecution;
  153. exit;
  154. end
  155. else if (rec.ExceptionCode=STATUS_ILLEGAL_INSTRUCTION) and sse_check then
  156. begin
  157. os_supports_sse:=False;
  158. { skip the offending movaps %xmm7,%xmm6 instruction }
  159. inc(context.eip,3);
  160. result:=ExceptionContinueExecution;
  161. exit;
  162. end;
  163. RtlUnwind(@frame,nil,@rec,nil);
  164. asm
  165. { RtlUnwind destroys nonvolatile registers, this assembler block prevents
  166. regvar optimizations. }
  167. end ['ebx','esi','edi'];
  168. if rec.ExceptionCode<>FPC_EXCEPTION_CODE then
  169. begin
  170. code:=RunErrorCode386(rec,context);
  171. if code<0 then
  172. SysResetFPU;
  173. code:=abs(code);
  174. Adr:=rec.ExceptionAddress;
  175. Obj:=nil;
  176. if Assigned(ExceptObjProc) then
  177. Obj:=TObject(TExceptObjProc(ExceptObjProc)(code,rec));
  178. if Obj=nil then
  179. begin
  180. { This works because RtlUnwind does not actually unwind the stack on i386
  181. (and only on i386) }
  182. errorcode:=word(code);
  183. errorbase:=pointer(context.Ebp);
  184. erroraddr:=pointer(context.Eip);
  185. Halt(code);
  186. end;
  187. FrameCount:=GetBacktrace(context,nil,Frames);
  188. end
  189. else
  190. begin
  191. Obj:=TObject(rec.ExceptionInformation[1]);
  192. Adr:=rec.ExceptionInformation[0];
  193. Frames:=PCodePointer(rec.ExceptionInformation[3]);
  194. FrameCount:=ptruint(rec.ExceptionInformation[2]);
  195. code:=217;
  196. end;
  197. if Assigned(ExceptProc) then
  198. begin
  199. ExceptProc(Obj,Adr,FrameCount,Frames);
  200. Halt(217);
  201. end
  202. else
  203. begin
  204. errorcode:=word(code);
  205. errorbase:=pointer(rec.ExceptionInformation[4]);
  206. erroraddr:=pointer(Adr);
  207. Halt(code);
  208. end;
  209. end;
  210. result:=ExceptionContinueExecution;
  211. end;
  212. function NestedHandler(
  213. var rec: TExceptionRecord;
  214. var frame: TSEHFrame;
  215. var context: TContext;
  216. var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl;
  217. var
  218. hp: PExceptObject;
  219. begin
  220. if (rec.ExceptionFlags and EXCEPTION_UNWIND)<>0 then
  221. begin
  222. hp:=PopObjectStack;
  223. if hp^.refcount=0 then
  224. hp^.FObject.Free;
  225. end;
  226. result:=ExceptionContinueSearch;
  227. end;
  228. function __FPC_except_safecall(
  229. var rec: TExceptionRecord;
  230. var frame: TSEHFrame;
  231. var context: TContext;
  232. var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl; forward;
  233. procedure CommonHandler(
  234. var rec: TExceptionRecord;
  235. var frame: TSEHFrame;
  236. var context: TContext;
  237. TargetAddr: Pointer);
  238. var
  239. Exc: TExceptObject;
  240. code: Longint;
  241. begin
  242. if rec.ExceptionCode<>FPC_EXCEPTION_CODE then
  243. begin
  244. Exc.FObject:=nil;
  245. code:=RunErrorCode386(rec,context);
  246. if Assigned(ExceptObjProc) then
  247. Exc.FObject:=TObject(TExceptObjProc(ExceptObjProc)(abs(code),rec));
  248. if (Exc.FObject=nil) and (frame.Addr<>Pointer(@__FPC_except_safecall)) then
  249. Exit;
  250. Exc.Addr:=rec.ExceptionAddress;
  251. Exc.FrameCount:=GetBacktrace(context,nil,Exc.Frames);
  252. if code<0 then
  253. SysResetFPU;
  254. end
  255. else
  256. begin
  257. Exc.Addr:=rec.ExceptionInformation[0];
  258. Exc.FObject:=TObject(rec.ExceptionInformation[1]);
  259. Exc.Framecount:=Longint(PtrUInt(rec.ExceptionInformation[2]));
  260. Exc.Frames:=rec.ExceptionInformation[3];
  261. end;
  262. RtlUnwind(@frame,nil,@rec,nil);
  263. Exc.Refcount:=0;
  264. Exc.SEHFrame:=@frame;
  265. Exc.ExceptRec:=@rec;
  266. { link to ExceptObjectStack }
  267. Exc.Next:=ExceptObjectStack;
  268. ExceptObjectStack:=@Exc;
  269. frame.Addr:=@NestedHandler;
  270. if setjmp(Exc.ReraiseBuf)=0 then
  271. asm
  272. movl Exc.FObject,%eax
  273. movl frame,%edx
  274. movl TargetAddr,%ecx // load ebp-based var before changing ebp
  275. movl TSEHFrame._EBP(%edx),%ebp
  276. jmpl *%ecx
  277. end;
  278. { control comes here if exception is re-raised }
  279. rec.ExceptionFlags:=rec.ExceptionFlags and (not EXCEPTION_UNWINDING);
  280. end;
  281. function __FPC_except_handler(
  282. var rec: TExceptionRecord;
  283. var frame: TSEHFrame;
  284. var context: TContext;
  285. var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl; [public,alias:'__FPC_except_handler'];
  286. begin
  287. if (rec.ExceptionFlags and EXCEPTION_UNWIND)=0 then
  288. begin
  289. { Athlon prefetch bug? }
  290. if (rec.ExceptionCode=STATUS_ACCESS_VIOLATION) and
  291. is_prefetch(pointer(Context.eip)) then
  292. begin
  293. result:=ExceptionContinueExecution;
  294. exit;
  295. end;
  296. CommonHandler(rec,frame,context,frame.HandlerArg);
  297. end;
  298. result:=ExceptionContinueSearch;
  299. end;
  300. { Safecall procedures are expected to handle OS exceptions even if they cannot be
  301. converted to language exceptions. This is indicated by distinct handler address. }
  302. function __FPC_except_safecall(
  303. var rec: TExceptionRecord;
  304. var frame: TSEHFrame;
  305. var context: TContext;
  306. var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl; [public,alias:'__FPC_except_safecall']; assembler; nostackframe;
  307. asm
  308. jmp __FPC_except_handler
  309. end;
  310. function __FPC_on_handler(
  311. var rec: TExceptionRecord;
  312. var frame: TSEHFrame;
  313. var context: TContext;
  314. var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; cdecl; [public,alias:'__FPC_on_handler'];
  315. var
  316. TargetAddr: Pointer;
  317. begin
  318. if (rec.ExceptionFlags and EXCEPTION_UNWIND)=0 then
  319. begin
  320. { Athlon prefetch bug? }
  321. if (rec.ExceptionCode=STATUS_ACCESS_VIOLATION) and
  322. is_prefetch(pointer(Context.eip)) then
  323. begin
  324. result:=ExceptionContinueExecution;
  325. exit;
  326. end;
  327. { Are we going to catch it? }
  328. TargetAddr:=FilterException(rec,0,PtrUInt(frame.HandlerArg),abs(RunErrorCode386(rec,context)));
  329. if assigned(TargetAddr) then
  330. CommonHandler(rec,frame,context,TargetAddr);
  331. end;
  332. result:=ExceptionContinueSearch;
  333. end;
  334. function fpc_safecallhandler(obj: TObject): HResult; [public,alias:'FPC_SAFECALLHANDLER']; compilerproc;
  335. var
  336. hp: PExceptObject;
  337. exc: TObject;
  338. begin
  339. hp:=PopObjectStack;
  340. exc:=hp^.FObject;
  341. if Assigned(obj) and Assigned(exc) then
  342. result:=obj.SafeCallException(exc,hp^.Addr)
  343. else
  344. result:=E_UNEXPECTED;
  345. if hp^.refcount=0 then
  346. exc.Free;
  347. asm
  348. movl %ebp,%edx // save current frame
  349. movl hp,%ecx
  350. movl TExceptObject.SEHFrame(%ecx),%ecx // target ESP minus sizeof(TSEHFrame)
  351. movl (%ecx),%eax
  352. movl %eax,%fs:(0) // restore SEH chain
  353. movl __RESULT,%eax
  354. movl TSEHFrame._EBP(%ecx),%ebp // restore EBP
  355. leal 16(%ecx),%esp // restore ESP past the SEH frame
  356. jmpl 4(%edx) // jump to caller
  357. end;
  358. end;
  359. procedure fpc_doneexception;[public,alias:'FPC_DONEEXCEPTION'] compilerproc;
  360. var
  361. hp: PExceptObject;
  362. begin
  363. hp:=PopObjectStack;
  364. if hp^.refcount=0 then
  365. hp^.FObject.Free;
  366. erroraddr:=nil;
  367. asm
  368. movl %ebp,%edx // save current frame
  369. movl hp,%eax
  370. movl TExceptObject.SEHFrame(%eax),%eax // target ESP minus sizeof(TSEHFrame)
  371. movl (%eax),%ecx
  372. movl %ecx,%fs:(0) // restore SEH chain
  373. movl TSEHFrame._EBP(%eax),%ebp // restore EBP
  374. leal 16(%eax),%esp // restore ESP, removing SEH frame
  375. jmpl 4(%edx) // jump to caller
  376. end;
  377. end;
  378. function main_wrapper(arg: Pointer; proc: Pointer): ptrint; assembler; nostackframe;
  379. asm
  380. xorl %ecx,%ecx
  381. pushl $__FPC_default_handler
  382. pushl %fs:(%ecx)
  383. movl %esp,%fs:(%ecx)
  384. call *%edx
  385. xorl %ecx,%ecx
  386. popl %edx
  387. movl %edx,%fs:(%ecx)
  388. popl %ecx
  389. end;
  390. {$endif FPC_USE_WIN32_SEH}