seh64.inc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2011 by Free Pascal development team
  4. Support for 64-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. { exception flags }
  12. const
  13. EXCEPTION_UNWIND = EXCEPTION_UNWINDING or EXCEPTION_EXIT_UNWIND or
  14. EXCEPTION_TARGET_UNWIND or EXCEPTION_COLLIDED_UNWIND;
  15. UNWIND_HISTORY_TABLE_SIZE = 12;
  16. UNW_FLAG_NHANDLER = 0;
  17. type
  18. PM128A=^M128A;
  19. M128A = record
  20. Low : QWord;
  21. High : Int64;
  22. end;
  23. PContext = ^TContext;
  24. TContext = record
  25. P1Home : QWord;
  26. P2Home : QWord;
  27. P3Home : QWord;
  28. P4Home : QWord;
  29. P5Home : QWord;
  30. P6Home : QWord;
  31. ContextFlags : DWord;
  32. MxCsr : DWord;
  33. SegCs : word;
  34. SegDs : word;
  35. SegEs : word;
  36. SegFs : word;
  37. SegGs : word;
  38. SegSs : word;
  39. EFlags : DWord;
  40. Dr0 : QWord;
  41. Dr1 : QWord;
  42. Dr2 : QWord;
  43. Dr3 : QWord;
  44. Dr6 : QWord;
  45. Dr7 : QWord;
  46. Rax : QWord;
  47. Rcx : QWord;
  48. Rdx : QWord;
  49. Rbx : QWord;
  50. Rsp : QWord;
  51. Rbp : QWord;
  52. Rsi : QWord;
  53. Rdi : QWord;
  54. R8 : QWord;
  55. R9 : QWord;
  56. R10 : QWord;
  57. R11 : QWord;
  58. R12 : QWord;
  59. R13 : QWord;
  60. R14 : QWord;
  61. R15 : QWord;
  62. Rip : QWord;
  63. Header : array[0..1] of M128A;
  64. Legacy : array[0..7] of M128A;
  65. Xmm0 : M128A;
  66. Xmm1 : M128A;
  67. Xmm2 : M128A;
  68. Xmm3 : M128A;
  69. Xmm4 : M128A;
  70. Xmm5 : M128A;
  71. Xmm6 : M128A;
  72. Xmm7 : M128A;
  73. Xmm8 : M128A;
  74. Xmm9 : M128A;
  75. Xmm10 : M128A;
  76. Xmm11 : M128A;
  77. Xmm12 : M128A;
  78. Xmm13 : M128A;
  79. Xmm14 : M128A;
  80. Xmm15 : M128A;
  81. VectorRegister : array[0..25] of M128A;
  82. VectorControl : QWord;
  83. DebugControl : QWord;
  84. LastBranchToRip : QWord;
  85. LastBranchFromRip : QWord;
  86. LastExceptionToRip : QWord;
  87. LastExceptionFromRip : QWord;
  88. end;
  89. { This is a simplified definition, only array part of unions }
  90. PKNONVOLATILE_CONTEXT_POINTERS=^KNONVOLATILE_CONTEXT_POINTERS;
  91. KNONVOLATILE_CONTEXT_POINTERS=record
  92. FloatingContext: array[0..15] of PM128A;
  93. IntegerContext: array[0..15] of PQWord;
  94. end;
  95. PExceptionPointers = ^TExceptionPointers;
  96. TExceptionPointers = record
  97. ExceptionRecord : PExceptionRecord;
  98. ContextRecord : PContext;
  99. end;
  100. EXCEPTION_ROUTINE = function(
  101. var ExceptionRecord: TExceptionRecord;
  102. EstablisherFrame: Pointer;
  103. var ContextRecord: TContext;
  104. DispatcherContext: Pointer ): EXCEPTION_DISPOSITION;
  105. PRUNTIME_FUNCTION=^RUNTIME_FUNCTION;
  106. RUNTIME_FUNCTION=record
  107. BeginAddress: DWORD;
  108. EndAddress: DWORD;
  109. UnwindData: DWORD;
  110. end;
  111. UNWIND_HISTORY_TABLE_ENTRY=record
  112. ImageBase: QWord;
  113. FunctionEntry: PRUNTIME_FUNCTION;
  114. end;
  115. PUNWIND_HISTORY_TABLE=^UNWIND_HISTORY_TABLE;
  116. UNWIND_HISTORY_TABLE=record
  117. Count: DWORD;
  118. Search: Byte;
  119. RaiseStatusIndex: Byte;
  120. Unwind: Byte;
  121. Exception: Byte;
  122. LowAddress: QWord;
  123. HighAddress: QWord;
  124. Entry: array[0..UNWIND_HISTORY_TABLE_SIZE-1] of UNWIND_HISTORY_TABLE_ENTRY;
  125. end;
  126. PDispatcherContext = ^TDispatcherContext;
  127. TDispatcherContext = record
  128. ControlPc: QWord;
  129. ImageBase: QWord;
  130. FunctionEntry: PRUNTIME_FUNCTION;
  131. EstablisherFrame: QWord;
  132. TargetIp: QWord;
  133. ContextRecord: PContext;
  134. LanguageHandler: EXCEPTION_ROUTINE;
  135. HandlerData: Pointer;
  136. HistoryTable: PUNWIND_HISTORY_TABLE;
  137. ScopeIndex: DWord;
  138. Fill0: DWord;
  139. end;
  140. procedure RtlCaptureContext(var ctx: TContext); stdcall;
  141. external 'kernel32.dll' name 'RtlCaptureContext';
  142. function RtlCaptureStackBackTrace(
  143. FramesToSkip: DWORD;
  144. FramesToCapture: DWORD;
  145. var BackTrace: Pointer;
  146. BackTraceHash: PDWORD): Word; stdcall;
  147. external 'kernel32.dll' name 'RtlCaptureStackBackTrace';
  148. function RtlLookupFunctionEntry(
  149. ControlPC: QWord;
  150. out ImageBase: QWord;
  151. HistoryTable: PUNWIND_HISTORY_TABLE): PRUNTIME_FUNCTION;
  152. external 'kernel32.dll' name 'RtlLookupFunctionEntry';
  153. function RtlVirtualUnwind(
  154. HandlerType: DWORD;
  155. ImageBase: QWord;
  156. ControlPc: QWord;
  157. FunctionEntry: PRUNTIME_FUNCTION;
  158. var ContextRecord: TContext;
  159. HandlerData: PPointer;
  160. EstablisherFrame: PQWord;
  161. ContextPointers: PKNONVOLATILE_CONTEXT_POINTERS): EXCEPTION_ROUTINE;
  162. external 'kernel32.dll' name 'RtlVirtualUnwind';
  163. procedure RtlUnwindEx(
  164. TargetFrame: Pointer;
  165. TargetIp: Pointer;
  166. ExceptionRecord: PExceptionRecord;
  167. ReturnValue: Pointer;
  168. OriginalContext: PContext; { scratch space, initial contents ignored }
  169. HistoryTable: PUNWIND_HISTORY_TABLE);
  170. external 'kernel32.dll' name 'RtlUnwindEx';
  171. { FPC specific stuff }
  172. {$ifdef FPC_USE_WIN64_SEH}
  173. const
  174. SCOPE_FINALLY=0;
  175. SCOPE_CATCHALL=1;
  176. SCOPE_IMPLICIT=2;
  177. type
  178. PScopeRec=^TScopeRec;
  179. TScopeRec=record
  180. Typ: DWord; { SCOPE_FINALLY: finally code in RvaHandler
  181. SCOPE_CATCHALL: unwinds to RvaEnd, RvaHandler is the end of except block
  182. SCOPE_IMPLICIT: finally code in RvaHandler, unwinds to RvaEnd
  183. otherwise: except with 'on' stmts, value is RVA of filter data }
  184. RvaStart: DWord;
  185. RvaEnd: DWord;
  186. RvaHandler: DWord;
  187. end;
  188. function CaptureBacktrace(skipframes,count:sizeint;frames:PCodePointer):sizeint;
  189. begin
  190. { skipframes is increased because this function adds a call level }
  191. Result:=RtlCaptureStackBackTrace(skipframes+1,count,frames^,nil);
  192. end;
  193. { note: context must be passed by value, so modifications are made to a local copy }
  194. function GetBacktrace(Context: TContext; StartingFrame: Pointer; out Frames: PPointer): Longint;
  195. var
  196. UnwindHistory: UNWIND_HISTORY_TABLE;
  197. RuntimeFunction: PRUNTIME_FUNCTION;
  198. HandlerData: Pointer;
  199. EstablisherFrame: QWord;
  200. ImageBase: QWord;
  201. FrameCount,FrameBufSize: Longint;
  202. begin
  203. FillChar(UnwindHistory,sizeof(UNWIND_HISTORY_TABLE),0);
  204. UnwindHistory.Unwind:=1;
  205. FrameCount:=0;
  206. FrameBufSize:=0;
  207. Frames:=nil;
  208. repeat
  209. RuntimeFunction:=RtlLookupFunctionEntry(Context.Rip, ImageBase, @UnwindHistory);
  210. if Assigned(RuntimeFunction) then
  211. RtlVirtualUnwind(UNW_FLAG_NHANDLER, ImageBase, Context.Rip,
  212. RuntimeFunction, Context, @HandlerData, @EstablisherFrame, nil)
  213. else { a leaf function }
  214. begin
  215. Context.Rip:=PQWord(Context.Rsp)^;
  216. Inc(Context.Rsp, sizeof(Pointer));
  217. end;
  218. if (Context.Rip=0) or (FrameCount>=RaiseMaxFrameCount) then
  219. break;
  220. { The StartingFrame provides a way to skip several initial calls.
  221. It's better to specify the number of skipped calls directly,
  222. because the very purpose of this function is to retrieve stacktrace
  223. even in optimized code (i.e. without rbp-based frames). But that's
  224. limited by factors such as 'raise' syntax. }
  225. if (Pointer(Context.Rbp)>StartingFrame) or (FrameCount>0) then
  226. begin
  227. if (FrameCount>=FrameBufSize) then
  228. begin
  229. Inc(FrameBufSize,16);
  230. ReallocMem(Frames,FrameBufSize*sizeof(Pointer));
  231. end;
  232. Frames[FrameCount]:=Pointer(Context.Rip);
  233. Inc(FrameCount);
  234. end;
  235. until False;
  236. Result:=FrameCount;
  237. end;
  238. function RunErrorCodex64(const rec: TExceptionRecord; const context: TContext): Longint;
  239. begin
  240. result:=RunErrorCode(rec);
  241. if (result=-255) then
  242. TranslateMxcsr(context.MxCsr,result);
  243. end;
  244. {$push}
  245. {$codealign localmin=16} { TContext record requires this }
  246. procedure fpc_RaiseException(Obj: TObject; AnAddr,AFrame: Pointer); [public,alias: 'FPC_RAISEEXCEPTION']; compilerproc;
  247. var
  248. ctx: TContext;
  249. args: array[0..3] of PtrUint;
  250. begin
  251. RtlCaptureContext(ctx);
  252. args[0]:=PtrUint(AnAddr);
  253. args[1]:=PtrUint(Obj);
  254. args[2]:=GetBacktrace(ctx,AFrame,PPointer(args[3]));
  255. RaiseException(FPC_EXCEPTION_CODE,EXCEPTION_NONCONTINUABLE,4,@args[0]);
  256. end;
  257. procedure _fpc_local_unwind(frame,target: Pointer);[public,alias:'_FPC_local_unwind'];compilerproc;
  258. var
  259. ctx: TContext;
  260. begin
  261. RtlUnwindEx(frame,target,nil,nil,@ctx,nil);
  262. end;
  263. {$pop}
  264. procedure fpc_reraise; [public,alias:'FPC_RERAISE']; compilerproc;
  265. var
  266. hp : PExceptObject;
  267. args: array[0..3] of PtrUint;
  268. begin
  269. hp:=ExceptObjectStack;
  270. args[0]:=PtrUint(hp^.addr); { copy and clear the exception stack top }
  271. args[1]:=PtrUint(hp^.FObject);
  272. args[2]:=hp^.FrameCount;
  273. args[3]:=PtrUint(hp^.Frames);
  274. hp^.refcount:=0;
  275. hp^.FObject:=nil;
  276. hp^.Frames:=nil;
  277. RaiseException(FPC_EXCEPTION_CODE,EXCEPTION_NONCONTINUABLE,4,@args[0]);
  278. end;
  279. {$ifdef DEBUG_SEH}
  280. procedure PrintScope(idx: integer; scope: PScopeRec);
  281. begin
  282. if IsConsole then
  283. begin
  284. write(stderr,'Scope #',idx,' ',hexstr(Scope^.RvaStart,8),' - ',hexStr(Scope^.RvaEnd,8));
  285. writeln(stderr,' type=',Scope^.Typ);
  286. end;
  287. end;
  288. {$endif DEBUG_SEH}
  289. function PushException(var rec: TExceptionRecord; var context: TContext;
  290. out obj: TObject; AcceptNull: Boolean): Boolean;
  291. var
  292. adr: Pointer;
  293. Exc: PExceptObject;
  294. Frames: PPointer;
  295. FrameCount: Longint;
  296. code: Longint;
  297. begin
  298. Adr:=rec.ExceptionInformation[0];
  299. Obj:=TObject(rec.ExceptionInformation[1]);
  300. Framecount:=Longint(PtrUInt(rec.ExceptionInformation[2]));
  301. Frames:=rec.ExceptionInformation[3];
  302. if rec.ExceptionCode<>FPC_EXCEPTION_CODE then
  303. begin
  304. Obj:=nil;
  305. Result:=False;
  306. code:=RunErrorCodex64(rec,context);
  307. if Assigned(ExceptObjProc) then
  308. Obj:=TObject(TExceptObjProc(ExceptObjProc)(abs(code),rec));
  309. if (Obj=nil) and (not AcceptNull) then
  310. Exit;
  311. adr:=rec.ExceptionAddress;
  312. FrameCount:=GetBacktrace(context,nil,Frames);
  313. if code<0 then
  314. SysResetFPU;
  315. end;
  316. New(Exc);
  317. Exc^.FObject:=Obj;
  318. Exc^.Addr:=adr;
  319. Exc^.Frames:=Frames;
  320. Exc^.FrameCount:=FrameCount;
  321. Exc^.Refcount:=0;
  322. { link to RaiseList }
  323. Exc^.Next:=ExceptObjectStack;
  324. ExceptObjectStack:=Exc;
  325. Result:=True;
  326. end;
  327. { This is an outermost exception handler, installed using assembler around the
  328. entrypoint and thread procedures. Its sole purpose is to provide sensible exitcode. }
  329. function __FPC_default_handler(
  330. var rec: TExceptionRecord;
  331. frame: Pointer;
  332. var context: TCONTEXT;
  333. var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; [public,alias:'__FPC_DEFAULT_HANDLER'];
  334. label L1;
  335. var
  336. exc: PExceptObject;
  337. obj: TObject;
  338. hstdout: ^text;
  339. i,code: Longint;
  340. begin
  341. if (rec.ExceptionFlags and EXCEPTION_UNWIND)=0 then
  342. begin
  343. { Athlon prefetch bug? }
  344. if (rec.ExceptionCode=STATUS_ACCESS_VIOLATION) and is_prefetch(pointer(Context.rip)) then
  345. begin
  346. result:=ExceptionContinueExecution;
  347. exit;
  348. end;
  349. PushException(rec,context,obj,True);
  350. RtlUnwindEx(frame, @L1, @rec, nil, dispatch.ContextRecord, dispatch.HistoryTable);
  351. end
  352. else if (rec.ExceptionFlags and EXCEPTION_TARGET_UNWIND)<>0 then
  353. begin
  354. Exc:=ExceptObjectStack;
  355. if Exc^.FObject=nil then
  356. begin
  357. hstdout:=@stdout;
  358. code:=abs(RunErrorCodex64(rec,context));
  359. Writeln(hstdout^,'Runtime error ',code,' at $',hexstr(Exc^.addr));
  360. Writeln(hstdout^,BackTraceStrFunc(Exc^.Addr));
  361. if (Exc^.FrameCount>0) then
  362. begin
  363. for i:=0 to Exc^.FrameCount-1 do
  364. Writeln(hstdout^,BackTraceStrFunc(Exc^.Frames[i]));
  365. end;
  366. Writeln(hstdout^,'');
  367. ErrorCode:=word(code);
  368. Halt(code);
  369. end
  370. else
  371. begin
  372. { if ExceptObjProc=nil, ExceptProc is typically also nil,
  373. so we cannot make much use of this backtrace }
  374. if Assigned(ExceptProc) then
  375. begin
  376. ExceptProc(Exc^.FObject,Exc^.Addr,Exc^.FrameCount,Exc^.Frames);
  377. Halt(217);
  378. end;
  379. L1:
  380. { RtlUnwindEx above resets execution context to the point where the handler
  381. was installed, i.e. main_wrapper. It makes exiting this procedure no longer
  382. possible. Halting is the only possible action here.
  383. Furthermore, this is not expected to execute at all, because the above block
  384. definitely halts. }
  385. Halt(217);
  386. end;
  387. end;
  388. result:=ExceptionContinueSearch;
  389. end;
  390. { This handler is installed by compiler for every try..finally and try..except statement,
  391. including implicit ones. }
  392. function __FPC_specific_handler(
  393. var rec: TExceptionRecord;
  394. frame: Pointer;
  395. var context: TCONTEXT;
  396. var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; [public,alias:'__FPC_specific_handler'];
  397. var
  398. TargetRva,ControlRva: DWord;
  399. scope: PScopeRec;
  400. scopeIdx: DWord;
  401. TargetAddr: Pointer;
  402. obj:TObject;
  403. begin
  404. {$ifdef DEBUG_SEH}
  405. if IsConsole then
  406. begin
  407. writeln(stderr,'Exception handler for ',BacktraceStrFunc(Pointer(dispatch.FunctionEntry^.BeginAddress+dispatch.ImageBase)));
  408. writeln(stderr,'Code=', hexstr(rec.ExceptionCode,8),' Flags=',hexstr(rec.ExceptionFlags,2), ' CtrlPc=',hexstr(dispatch.ControlPc,16));
  409. end;
  410. {$endif DEBUG_SEH}
  411. result:=ExceptionContinueSearch;
  412. ControlRva:=dispatch.ControlPc-dispatch.ImageBase;
  413. ScopeIdx:=dispatch.ScopeIndex;
  414. if (rec.ExceptionFlags and EXCEPTION_UNWIND)=0 then
  415. begin
  416. while ScopeIdx<PDWord(dispatch.HandlerData)^ do
  417. begin
  418. scope:=@PScopeRec(dispatch.HandlerData+sizeof(Dword))[ScopeIdx];
  419. {$ifdef DEBUG_SEH}
  420. PrintScope(ScopeIdx, scope);
  421. {$endif DEBUG_SEH}
  422. { Check if the exception was raised in the 'except' block,
  423. and dispose the existing exception object if so. }
  424. if (ControlRva>=scope^.RvaEnd) and (ControlRva<scope^.RvaHandler) and
  425. ((scope^.Typ=SCOPE_CATCHALL) or (scope^.Typ>SCOPE_IMPLICIT)) then
  426. Internal_PopObjectStack.Free
  427. else if (ControlRva>=scope^.RvaStart) and (ControlRva<scope^.RvaEnd) and
  428. (scope^.Typ<>SCOPE_FINALLY)then
  429. begin
  430. { Athlon prefetch bug? }
  431. if (rec.ExceptionCode=STATUS_ACCESS_VIOLATION) and is_prefetch(pointer(Context.rip)) then
  432. begin
  433. result:=ExceptionContinueExecution;
  434. exit;
  435. end;
  436. if scope^.Typ>SCOPE_IMPLICIT then // filtering needed
  437. begin
  438. TargetAddr:=FilterException(rec,dispatch.ImageBase,scope^.Typ,abs(RunErrorCodex64(rec,context)));
  439. if TargetAddr=nil then
  440. begin
  441. Inc(ScopeIdx);
  442. Continue;
  443. end;
  444. end
  445. else
  446. TargetAddr:=Pointer(scope^.RvaEnd+dispatch.ImageBase);
  447. {$ifdef DEBUG_SEH}
  448. if IsConsole then
  449. writeln(stderr,'match at scope #',scopeIdx,', unwind target=',hexstr(TargetAddr));
  450. {$endif DEBUG_SEH}
  451. if not PushException(rec,context,obj,Scope^.Typ=SCOPE_IMPLICIT) then
  452. Exit;
  453. { Does not return, control is transferred to TargetAddr,
  454. obj is placed into RAX. }
  455. RtlUnwindEx(frame, TargetAddr, @rec, obj, dispatch.ContextRecord, dispatch.HistoryTable);
  456. end;
  457. Inc(ScopeIdx);
  458. end;
  459. end
  460. else
  461. begin
  462. TargetRva:=dispatch.TargetIp-dispatch.ImageBase;
  463. {$ifdef DEBUG_SEH}
  464. if IsConsole then
  465. writeln(stderr,'Unwind, TargetRva=',hexstr(TargetRva,8),' CtrlRva=',hexstr(ControlRva,8),' idx=',ScopeIdx);
  466. {$endif DEBUG_SEH}
  467. while ScopeIdx<PDword(dispatch.HandlerData)^ do
  468. begin
  469. scope:=@PScopeRec(dispatch.HandlerData+sizeof(Dword))[ScopeIdx];
  470. {$ifdef DEBUG_SEH}
  471. PrintScope(scopeIdx, scope);
  472. {$endif DEBUG_SEH}
  473. if (ControlRva>=scope^.RvaStart) and (ControlRva<scope^.RvaEnd) and
  474. ((scope^.Typ=SCOPE_FINALLY) or (scope^.Typ=SCOPE_IMPLICIT)) then
  475. begin
  476. if (TargetRva>=scope^.RvaStart) and (TargetRva<scope^.RvaEnd) and
  477. ((rec.ExceptionFlags and EXCEPTION_TARGET_UNWIND)<>0) then
  478. begin
  479. Exit;
  480. end;
  481. dispatch.ScopeIndex:=ScopeIdx+1;
  482. {$ifdef DEBUG_SEH}
  483. if IsConsole then
  484. writeln(stderr,'calling handler @',hexstr(dispatch.imagebase+scope^.RvaHandler,16));
  485. {$endif DEBUG_SEH}
  486. TUnwindProc(dispatch.ImageBase+scope^.RvaHandler)(context.rbp);
  487. end;
  488. Inc(ScopeIdx);
  489. end;
  490. end;
  491. end;
  492. {$endif FPC_USE_WIN64_SEH}