File: Seh.cpp

package info (click to toggle)
storm-lang 0.7.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 52,004 kB
  • sloc: ansic: 261,462; cpp: 140,405; sh: 14,891; perl: 9,846; python: 2,525; lisp: 2,504; asm: 860; makefile: 678; pascal: 70; java: 52; xml: 37; awk: 12
file content (271 lines) | stat: -rw-r--r-- 6,809 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
#include "stdafx.h"
#include "Seh.h"

#ifdef WINDOWS

#include "SafeSeh.h"
#include "Seh64.h"
#include "Code/Binary.h"
#include "Core/Str.h"
#include "Gc/Gc.h"
#include "Gc/CodeTable.h"
#include "Utils/Memory.h"
#include "Utils/StackInfoSet.h"

namespace storm {
	namespace runtime {
		storm::Gc &engineGc(Engine &e);
	}
}

namespace code {
	namespace eh {

		class SehInfo : public StackInfo {
		public:
			virtual bool translate(void *ip, void *&fnBase, int &offset) const {
				storm::CodeTable &table = storm::codeTable();
				void *code = table.find(ip);
				if (!code)
					return false;

				fnBase = code;
				offset = int((byte *)ip - (byte *)code);
				return true;
			}

			virtual void format(GenericOutput &to, void *fnBase, int offset) const {
				Binary *owner = codeBinary(fnBase);
				Str *name = owner->ownerName();
				if (name) {
					to.put(name->c_str());
				} else {
					to.put(S("<unnamed Storm function>"));
				}
			}
		};

		void activateWindowsInfo(Engine &e) {
			static RegisterInfo<SehInfo> info;
#ifdef X64
			storm::runtime::engineGc(e).setEhCallback(&exceptionCallback);
#else
			(void)&e;
#endif
		}



		class Frame : public StackFrame {
		public:
			Frame(const SehFrame &frame)
				: StackFrame(frame.part, frame.activation),
				  framePtr((byte *)frame.stackPtr + frame.frameOffset) {}

			virtual void *toPtr(int offset) {
				return framePtr + offset;
			}

		private:
			byte *framePtr;
		};

		void cleanupFrame(SehFrame &frame) {
			if (frame.binary) {
				Frame f(frame);
				frame.binary->cleanup(f);
			} else {
				WARNING(L"Using SEH, but no link to the metadata provided!");
			}
		}

		Nat cleanupPartialFrame(SehFrame &frame, Nat cleanUntil) {
			if (frame.binary) {
				Frame f(frame);
				return frame.binary->cleanup(f, cleanUntil);
			} else {
				WARNING(L"Using SEH, but no link to the metadata provided!");
				return cleanUntil;
			}
		}


		/**
		 * Low-level stuff:
		 */


		// The low-level things here are inspired from the code in OS/Future.cpp, which is in turn
		// inspired by boost::exception_ptr.

		const nat cppExceptionCode = 0xE06D7363;
		const nat cppExceptionMagic = 0x19930520;
#if defined(X86)
		const nat cppExceptionParams = 3;

#if _MSC_VER == 1310
		const nat exceptionInfoOffset = 0x74;
#elif (_MSC_VER == 1400 || _MSC_VER == 1500)
		const nat exceptionInfoOffset = 0x80;
#else
// No special treatment of the re-throw mechanism.
#define MSC_NO_SPECIAL_RETHROW
#endif

#elif defined(X64)
		const nat cppExceptionParams = 4;

#if _MSC_VER==1310
		const nat exceptionInfoOffset = /* probably 0xE0 - 0xC*2, but untested */;
#elif (_MSC_VER == 1400 || _MSC_VER == 1500)
		const nat exceptionInfoOffset = 0xE0;
#else
// No special treatment of the re-throw mechanism.
#define MSC_NO_SPECIAL_RETHROW
#endif

#else
#error "Unsupported architecture!"
#endif

		struct CppTypeInfo {
			unsigned flags;
			DWORD typeInfoOffset;
			int thisOffset;
			int vbaseDescr;
			int vbaseOffset;
			unsigned int size;
			DWORD copyCtorOffset;

			std::type_info *typeInfo(HINSTANCE instance) const {
				return (std::type_info *)(size_t(instance) + size_t(typeInfoOffset));
			}
		};

		struct CppTypeInfoTable {
			unsigned count;
			DWORD infoOffset[1];

			const CppTypeInfo *info(unsigned id, HINSTANCE instance) const {
				return (const CppTypeInfo *)(size_t(instance) + size_t(infoOffset[id]));
			}
		};

		struct CppExceptionType {
			unsigned flags;
			DWORD dtorOffset;
			DWORD handlerOffset; // void (*handler)()
			DWORD tableOffset;

			const CppTypeInfoTable *table(HINSTANCE instance) const {
				return (const CppTypeInfoTable *)(size_t(instance) + size_t(tableOffset));
			}
		};

		static bool isObjPtr(const CppTypeInfoTable *table, HINSTANCE instance) {
			for (unsigned i = 0; i < table->count; i++) {
				const CppTypeInfo *info = table->info(i, instance);

				if (*info->typeInfo(instance) == typeid(storm::RootObject *))
					return true;
			}

			return false;
		}


		static bool isCppException(EXCEPTION_RECORD *record) {
			return record
				&& record->ExceptionCode == cppExceptionCode
				&& record->NumberParameters == cppExceptionParams
				&& record->ExceptionInformation[0] == cppExceptionMagic;
		}

		extern "C"
		EXCEPTION_DISPOSITION windowsHandler(_EXCEPTION_RECORD *er, void *frame, _CONTEXT *ctx, void *dispatch) {
			SehFrame f = extractFrame(er, frame, ctx, dispatch);

			if (er->ExceptionFlags & EXCEPTION_UNWINDING) {
				// Only need to do cleanup!
				if (er->ExceptionFlags & EXCEPTION_TARGET_UNWIND) {
					cleanupPartialFrame(f, er);
				} else {
					cleanupFrame(f);
				}
				return ExceptionContinueSearch;
			}

			// Early out if we have no catch clauses:
			if (!f.binary || !f.binary->hasCatch())
				return ExceptionContinueSearch;

			// Only catch C++ exceptions.
			if (!isCppException(er))
				return ExceptionContinueSearch;

#ifdef MSC_NO_SPECIAL_RETHROW
			// It seems we don't have to handle re-throws as a special case on newer MSC versions.
			// Check if this is true...
			if (!er->ExceptionInformation[2]) {
				WARNING(L"It seems like we need to handle re-throws like in older MSC versions!");
				return ExceptionContinueSearch;
			}
#else
			if (!er->ExceptionInformation[2]) {
				byte *t = (byte *)_errno();
				er = *(_EXCEPTION_RECORD **)(t + exceptionInfoOffset);

				if (!isCppException(er))
					return ExceptionContinueSearch;
			}
#endif

			HINSTANCE instance = cppExceptionParams >= 4
				? HINSTANCE(er->ExceptionInformation[3])
				: HINSTANCE(NULL);

			const CppExceptionType *type = (const CppExceptionType *)er->ExceptionInformation[2];

			// Apparently, the 'simple type' flag is set when we're working with pointers to const objects.
			// If it's not a simple type, don't bother looking for a void *.
			// if ((type->flags & simpleType) != 0)
			// 	return ExceptionContinueSearch;

			// The table seems to be a table of possible types that can catch the exception. We look for
			// 'RootObject *' in that, then we know if it's a pointer or not!
			if (!isObjPtr(type->table(instance), instance))
				return ExceptionContinueSearch;

			// It seems like we don't have to worry about extra padding etc for simple types at least.
			storm::RootObject **object = (storm::RootObject **)er->ExceptionInformation[1];
			if (!object)
				return ExceptionContinueSearch;

			Binary::Resume resume;
			if (f.binary->hasCatch(f.part, *object, resume)) {
				// We found a matching catch block! Unwind the stack and resume!
				resumeFrame(f, resume, *object, ctx, er, dispatch);

				// Note: Might not return to here, but if we do we should continue execution.
				return ExceptionContinueExecution;
			}

			return ExceptionContinueSearch;
		}


	}
}

#else

namespace code {
	namespace eh {

		extern "C"
		void windowsHandler() {}

	}
}

#endif