2
0

seh64.inc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  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. { note: context must be passed by value, so modifications are made to a local copy }
  189. function GetBacktrace(Context: TContext; StartingFrame: Pointer; out Frames: PPointer): Longint;
  190. var
  191. UnwindHistory: UNWIND_HISTORY_TABLE;
  192. RuntimeFunction: PRUNTIME_FUNCTION;
  193. HandlerData: Pointer;
  194. EstablisherFrame: QWord;
  195. ImageBase: QWord;
  196. FrameCount,FrameBufSize: Longint;
  197. begin
  198. FillChar(UnwindHistory,sizeof(UNWIND_HISTORY_TABLE),0);
  199. UnwindHistory.Unwind:=1;
  200. FrameCount:=0;
  201. FrameBufSize:=0;
  202. Frames:=nil;
  203. repeat
  204. RuntimeFunction:=RtlLookupFunctionEntry(Context.Rip, ImageBase, @UnwindHistory);
  205. if Assigned(RuntimeFunction) then
  206. RtlVirtualUnwind(UNW_FLAG_NHANDLER, ImageBase, Context.Rip,
  207. RuntimeFunction, Context, @HandlerData, @EstablisherFrame, nil)
  208. else { a leaf function }
  209. begin
  210. Context.Rip:=PQWord(Context.Rsp)^;
  211. Inc(Context.Rsp, sizeof(Pointer));
  212. end;
  213. if (Context.Rip=0) or (FrameCount>=RaiseMaxFrameCount) then
  214. break;
  215. { The StartingFrame provides a way to skip several initial calls.
  216. It's better to specify the number of skipped calls directly,
  217. because the very purpose of this function is to retrieve stacktrace
  218. even in optimized code (i.e. without rbp-based frames). But that's
  219. limited by factors such as 'raise' syntax. }
  220. if (Pointer(Context.Rbp)>StartingFrame) or (FrameCount>0) then
  221. begin
  222. if (FrameCount>=FrameBufSize) then
  223. begin
  224. Inc(FrameBufSize,16);
  225. ReallocMem(Frames,FrameBufSize*sizeof(Pointer));
  226. end;
  227. Frames[FrameCount]:=Pointer(Context.Rip);
  228. Inc(FrameCount);
  229. end;
  230. until False;
  231. Result:=FrameCount;
  232. end;
  233. function RunErrorCodex64(const rec: TExceptionRecord; const context: TContext): Longint;
  234. begin
  235. result:=RunErrorCode(rec);
  236. if (result=-255) then
  237. TranslateMxcsr(context.MxCsr,result);
  238. end;
  239. {$push}
  240. {$codealign localmin=16} { TContext record requires this }
  241. procedure fpc_RaiseException(Obj: TObject; AnAddr,AFrame: Pointer); [public,alias: 'FPC_RAISEEXCEPTION']; compilerproc;
  242. var
  243. ctx: TContext;
  244. args: array[0..3] of PtrUint;
  245. begin
  246. RtlCaptureContext(ctx);
  247. args[0]:=PtrUint(AnAddr);
  248. args[1]:=PtrUint(Obj);
  249. args[2]:=GetBacktrace(ctx,AFrame,PPointer(args[3]));
  250. RaiseException(FPC_EXCEPTION_CODE,EXCEPTION_NONCONTINUABLE,4,@args[0]);
  251. end;
  252. procedure _fpc_local_unwind(frame,target: Pointer);[public,alias:'_FPC_local_unwind'];compilerproc;
  253. var
  254. ctx: TContext;
  255. begin
  256. RtlUnwindEx(frame,target,nil,nil,@ctx,nil);
  257. end;
  258. {$pop}
  259. procedure fpc_reraise; [public,alias:'FPC_RERAISE']; compilerproc;
  260. var
  261. hp : PExceptObject;
  262. args: array[0..3] of PtrUint;
  263. begin
  264. hp:=ExceptObjectStack;
  265. args[0]:=PtrUint(hp^.addr); { copy and clear the exception stack top }
  266. args[1]:=PtrUint(hp^.FObject);
  267. args[2]:=hp^.FrameCount;
  268. args[3]:=PtrUint(hp^.Frames);
  269. hp^.refcount:=0;
  270. hp^.FObject:=nil;
  271. hp^.Frames:=nil;
  272. RaiseException(FPC_EXCEPTION_CODE,EXCEPTION_NONCONTINUABLE,4,@args[0]);
  273. end;
  274. {$ifdef DEBUG_SEH}
  275. procedure PrintScope(idx: integer; scope: PScopeRec);
  276. begin
  277. if IsConsole then
  278. begin
  279. write(stderr,'Scope #',idx,' ',hexstr(Scope^.RvaStart,8),' - ',hexStr(Scope^.RvaEnd,8));
  280. writeln(stderr,' type=',Scope^.Typ);
  281. end;
  282. end;
  283. {$endif DEBUG_SEH}
  284. function PushException(var rec: TExceptionRecord; var context: TContext;
  285. out obj: TObject; AcceptNull: Boolean): Boolean;
  286. var
  287. adr: Pointer;
  288. Exc: PExceptObject;
  289. Frames: PPointer;
  290. FrameCount: Longint;
  291. code: Longint;
  292. begin
  293. Adr:=rec.ExceptionInformation[0];
  294. Obj:=TObject(rec.ExceptionInformation[1]);
  295. Framecount:=Longint(PtrUInt(rec.ExceptionInformation[2]));
  296. Frames:=rec.ExceptionInformation[3];
  297. if rec.ExceptionCode<>FPC_EXCEPTION_CODE then
  298. begin
  299. Obj:=nil;
  300. Result:=False;
  301. code:=RunErrorCodex64(rec,context);
  302. if Assigned(ExceptObjProc) then
  303. Obj:=TObject(TExceptObjProc(ExceptObjProc)(abs(code),rec));
  304. if (Obj=nil) and (not AcceptNull) then
  305. Exit;
  306. adr:=rec.ExceptionAddress;
  307. FrameCount:=GetBacktrace(context,nil,Frames);
  308. if code<0 then
  309. SysResetFPU;
  310. end;
  311. New(Exc);
  312. Exc^.FObject:=Obj;
  313. Exc^.Addr:=adr;
  314. Exc^.Frames:=Frames;
  315. Exc^.FrameCount:=FrameCount;
  316. Exc^.Refcount:=0;
  317. { link to RaiseList }
  318. Exc^.Next:=ExceptObjectStack;
  319. ExceptObjectStack:=Exc;
  320. Result:=True;
  321. end;
  322. { This is an outermost exception handler, installed using assembler around the
  323. entrypoint and thread procedures. Its sole purpose is to provide sensible exitcode. }
  324. function __FPC_default_handler(
  325. var rec: TExceptionRecord;
  326. frame: Pointer;
  327. var context: TCONTEXT;
  328. var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; [public,alias:'__FPC_DEFAULT_HANDLER'];
  329. label L1;
  330. var
  331. exc: PExceptObject;
  332. obj: TObject;
  333. begin
  334. if (rec.ExceptionFlags and EXCEPTION_UNWIND)=0 then
  335. begin
  336. { Athlon prefetch bug? }
  337. if (rec.ExceptionCode=STATUS_ACCESS_VIOLATION) and is_prefetch(pointer(Context.rip)) then
  338. begin
  339. result:=ExceptionContinueExecution;
  340. exit;
  341. end;
  342. PushException(rec,context,obj,True);
  343. RtlUnwindEx(frame, @L1, @rec, nil, dispatch.ContextRecord, dispatch.HistoryTable);
  344. end
  345. else if (rec.ExceptionFlags and EXCEPTION_TARGET_UNWIND)<>0 then
  346. begin
  347. Exc:=ExceptObjectStack;
  348. if Exc^.FObject=nil then
  349. RunError(abs(RunErrorCodex64(rec,context))) // !!prints wrong backtrace
  350. else
  351. begin
  352. { if ExceptObjProc=nil, ExceptProc is typically also nil,
  353. so we cannot make much use of this backtrace }
  354. if Assigned(ExceptProc) then
  355. begin
  356. ExceptProc(Exc^.FObject,Exc^.Addr,Exc^.FrameCount,Exc^.Frames);
  357. Halt(217);
  358. end;
  359. L1:
  360. { RtlUnwindEx above resets execution context to the point where the handler
  361. was installed, i.e. main_wrapper. It makes exiting this procedure no longer
  362. possible, halting is the only possible action here. }
  363. RunError(217);
  364. end;
  365. end;
  366. result:=ExceptionContinueSearch;
  367. end;
  368. { This handler is installed by compiler for every try..finally and try..except statement,
  369. including implicit ones. }
  370. function __FPC_specific_handler(
  371. var rec: TExceptionRecord;
  372. frame: Pointer;
  373. var context: TCONTEXT;
  374. var dispatch: TDispatcherContext): EXCEPTION_DISPOSITION; [public,alias:'__FPC_specific_handler'];
  375. var
  376. TargetRva,ControlRva: DWord;
  377. scope: PScopeRec;
  378. scopeIdx: DWord;
  379. TargetAddr: Pointer;
  380. obj:TObject;
  381. begin
  382. {$ifdef DEBUG_SEH}
  383. if IsConsole then
  384. begin
  385. writeln(stderr,'Exception handler for ',BacktraceStrFunc(Pointer(dispatch.FunctionEntry^.BeginAddress+dispatch.ImageBase)));
  386. writeln(stderr,'Code=', hexstr(rec.ExceptionCode,8),' Flags=',hexstr(rec.ExceptionFlags,2), ' CtrlPc=',hexstr(dispatch.ControlPc,16));
  387. end;
  388. {$endif DEBUG_SEH}
  389. result:=ExceptionContinueSearch;
  390. ControlRva:=dispatch.ControlPc-dispatch.ImageBase;
  391. ScopeIdx:=dispatch.ScopeIndex;
  392. if (rec.ExceptionFlags and EXCEPTION_UNWIND)=0 then
  393. begin
  394. while ScopeIdx<PDWord(dispatch.HandlerData)^ do
  395. begin
  396. scope:=@PScopeRec(dispatch.HandlerData+sizeof(Dword))[ScopeIdx];
  397. {$ifdef DEBUG_SEH}
  398. PrintScope(ScopeIdx, scope);
  399. {$endif DEBUG_SEH}
  400. { Check if the exception was raised in the 'except' block,
  401. and dispose the existing exception object if so. }
  402. if (ControlRva>=scope^.RvaEnd) and (ControlRva<scope^.RvaHandler) and
  403. ((scope^.Typ=SCOPE_CATCHALL) or (scope^.Typ>SCOPE_IMPLICIT)) then
  404. Internal_PopObjectStack.Free
  405. else if (ControlRva>=scope^.RvaStart) and (ControlRva<scope^.RvaEnd) and
  406. (scope^.Typ<>SCOPE_FINALLY)then
  407. begin
  408. { Athlon prefetch bug? }
  409. if (rec.ExceptionCode=STATUS_ACCESS_VIOLATION) and is_prefetch(pointer(Context.rip)) then
  410. begin
  411. result:=ExceptionContinueExecution;
  412. exit;
  413. end;
  414. if scope^.Typ>SCOPE_IMPLICIT then // filtering needed
  415. begin
  416. TargetAddr:=FilterException(rec,dispatch.ImageBase,scope^.Typ,abs(RunErrorCodex64(rec,context)));
  417. if TargetAddr=nil then
  418. begin
  419. Inc(ScopeIdx);
  420. Continue;
  421. end;
  422. end
  423. else
  424. TargetAddr:=Pointer(scope^.RvaEnd+dispatch.ImageBase);
  425. {$ifdef DEBUG_SEH}
  426. if IsConsole then
  427. writeln(stderr,'match at scope #',scopeIdx,', unwind target=',hexstr(TargetAddr));
  428. {$endif DEBUG_SEH}
  429. if not PushException(rec,context,obj,Scope^.Typ=SCOPE_IMPLICIT) then
  430. Exit;
  431. { Does not return, control is transferred to TargetAddr,
  432. obj is placed into RAX. }
  433. RtlUnwindEx(frame, TargetAddr, @rec, obj, dispatch.ContextRecord, dispatch.HistoryTable);
  434. end;
  435. Inc(ScopeIdx);
  436. end;
  437. end
  438. else
  439. begin
  440. TargetRva:=dispatch.TargetIp-dispatch.ImageBase;
  441. {$ifdef DEBUG_SEH}
  442. if IsConsole then
  443. writeln(stderr,'Unwind, TargetRva=',hexstr(TargetRva,8),' CtrlRva=',hexstr(ControlRva,8),' idx=',ScopeIdx);
  444. {$endif DEBUG_SEH}
  445. while ScopeIdx<PDword(dispatch.HandlerData)^ do
  446. begin
  447. scope:=@PScopeRec(dispatch.HandlerData+sizeof(Dword))[ScopeIdx];
  448. {$ifdef DEBUG_SEH}
  449. PrintScope(scopeIdx, scope);
  450. {$endif DEBUG_SEH}
  451. if (ControlRva>=scope^.RvaStart) and (ControlRva<scope^.RvaEnd) and
  452. ((scope^.Typ=SCOPE_FINALLY) or (scope^.Typ=SCOPE_IMPLICIT)) then
  453. begin
  454. if (TargetRva>=scope^.RvaStart) and (TargetRva<scope^.RvaEnd) and
  455. ((rec.ExceptionFlags and EXCEPTION_TARGET_UNWIND)<>0) then
  456. begin
  457. Exit;
  458. end;
  459. dispatch.ScopeIndex:=ScopeIdx+1;
  460. {$ifdef DEBUG_SEH}
  461. if IsConsole then
  462. writeln(stderr,'calling handler @',hexstr(dispatch.imagebase+scope^.RvaHandler,16));
  463. {$endif DEBUG_SEH}
  464. TUnwindProc(dispatch.ImageBase+scope^.RvaHandler)(context.rbp);
  465. end;
  466. Inc(ScopeIdx);
  467. end;
  468. end;
  469. end;
  470. {$endif FPC_USE_WIN64_SEH}