[merp] Add an exception type for managed exceptions (#18723)
[mono-project.git] / mono / utils / mono-merp.c
blob8e75680b38cf6bdf2e89d1eb22c7f01ddfaa9890
1 /**
2 * \file
3 * Support for interop with the Microsoft Error Reporting tool
5 * Author:
6 * Alexander Kyte (alkyte@microsoft.com)
8 * (C) 2018 Microsoft, Inc.
12 #include <config.h>
13 #include <glib.h>
15 #if defined(TARGET_OSX) && !defined(DISABLE_CRASH_REPORTING)
16 #include "mono-merp.h"
18 #include <unistd.h>
19 #include <spawn.h>
21 // OSX OS stuff now, for merpGUI interop
22 #include <mach/mach.h>
23 #include <mach/task_info.h>
24 #include <mach/mach_types.h>
25 #include <mach/mach_traps.h>
26 #include <servers/bootstrap.h>
28 #include <metadata/locales.h>
29 #include <mini/jit.h>
31 #if defined(HAVE_SYS_UTSNAME_H)
32 #include <sys/utsname.h>
33 #endif
35 // To get the apple machine model
36 #include <sys/param.h>
37 #include <sys/sysctl.h>
38 #include <fcntl.h>
40 #include <mono/utils/json.h>
41 #include <mono/utils/mono-state.h>
42 #include <utils/mono-threads-debug.h>
44 static const char *
45 kernel_version_string (void)
47 #ifdef HAVE_SYS_UTSNAME_H
48 static struct utsname name;
49 static const char *version_string;
51 if (!version_string) {
52 // WSL does not always nul terminate. WSL was fixed February 2018.
53 // Restore memset if variable made non-static.
54 //memset (&name, 0, sizeof (name));
56 if (uname (&name) >= 0)
57 version_string = name.release;
59 if (!version_string)
60 version_string = "";
62 return version_string;
63 #endif
64 return "";
67 static gboolean
68 starts_with (const char *pre, size_t pre_sz, const char *str)
70 return strncmp (pre, str, pre_sz) == 0;
73 static const char *
74 macos_version_string (void)
76 /* Can't allocate in here, could be called from a signal handler in a
77 * crashed process.
79 static const char *version_string;
80 static char buf[256];
81 static const size_t buf_size = sizeof (buf);
83 if (version_string)
84 return version_string;
86 /* macOS 10.13.6 or later */
87 if (!version_string) {
88 size_t size = 0;
89 if (sysctlbyname ("kern.osproductversion", NULL, &size, NULL, 0) < 0 || size >= buf_size) {
90 /* if we couldn't get the size or if it needs more space that we have in buf, leave it empty */
91 version_string = "";
92 return version_string;
95 if (sysctlbyname ("kern.osproductversion", (char*)buf, &size, NULL, 0) >= 0)
96 version_string = &buf[0];
98 /* macOS 10.13.5 or older */
99 if (!version_string) {
100 const char *kv_string = kernel_version_string ();
101 if (starts_with (G_STRING_CONSTANT_AND_LENGTH ("17"), kv_string))
102 version_string = "10.13"; // High Sierra
103 else if (starts_with (G_STRING_CONSTANT_AND_LENGTH ("16"), kv_string))
104 version_string = "10.12"; // Sierra
105 else if (starts_with (G_STRING_CONSTANT_AND_LENGTH ("15"), kv_string))
106 version_string = "10.11"; // El Capitan
107 else if (starts_with (G_STRING_CONSTANT_AND_LENGTH ("14"), kv_string))
108 version_string = "10.10"; // Yosemite
109 else if (starts_with (G_STRING_CONSTANT_AND_LENGTH ("13"), kv_string))
110 version_string = "10.9"; // Mavericks
111 else if (starts_with (G_STRING_CONSTANT_AND_LENGTH ("12"), kv_string))
112 version_string = "10.8"; // Mountain Lion
113 else if (starts_with (G_STRING_CONSTANT_AND_LENGTH ("11"), kv_string))
114 version_string = "10.7"; // Lion
116 if (!version_string)
117 version_string = "";
119 return version_string;
122 // To get the path of the running process
123 #include <libproc.h>
125 typedef enum {
126 MerpArchInvalid = 0,
128 MerpArchx86_64 = 1,
129 MerpArchx86 = 2,
130 MerpArchPPC = 3,
131 MerpArchPPC64 = 4
132 } MerpArch;
134 typedef enum
136 MERP_EXC_NONE = 0,
138 MERP_EXC_FORCE_QUIT = 1,
139 MERP_EXC_SIGSEGV = 2,
140 MERP_EXC_SIGABRT = 3,
141 MERP_EXC_SIGSYS = 4,
142 MERP_EXC_SIGILL = 5,
143 MERP_EXC_SIGBUS = 6,
144 MERP_EXC_SIGFPE = 7 ,
145 MERP_EXC_SIGTRAP = 8,
146 MERP_EXC_SIGKILL = 9,
147 MERP_EXC_HANG = 10,
148 MERP_EXC_MANAGED_EXCEPTION = 11
149 } MERPExcType;
151 typedef struct {
152 const char *merpFilePath;
153 const char *crashLogPath;
154 const char *werXmlPath;
156 const char *bundleIDArg; // App Bundle ID (required for bucketization)
157 const char *versionArg; // App Version (required for bucketization)
159 MerpArch archArg; // Arch, MacOS only, bails out if not found also required for bucketization. (required)
160 MERPExcType exceptionArg; // Exception type (refer to merpcommon.h and mach/exception_types.h for more info (optional)
162 const char *serviceNameArg; // This is the Bootstrap service name that MERP GUI will create to receive mach_task_self on a port created. Bails out if MERP GUI fails to receive mach_task_self from the crashed app. (Required for crash log generation)
163 const char *servicePathArg; // The path to the executable, used to relaunch the crashed app.
165 const char *moduleName;
166 const char *moduleVersion;
167 size_t moduleOffset;
169 const char *osVersion;
170 int uiLidArg; // MONO_LOCALE_INVARIANT 0x007F
172 char systemModel [100];
173 const char *systemManufacturer;
175 const char *eventType; /* Must be MONO_MERP_EVENT_TYPE_STR */
177 MonoStackHash hashes;
178 GSList *annotations;
179 } MERPStruct;
181 /* The event type determines the format of the fields that are reported. It
182 * must be MonoAppCrash for the rest of our report to make sense.
184 #define MONO_MERP_EVENT_TYPE_STR "MonoAppCrash"
187 typedef struct {
188 gboolean enable_merp;
190 const char *appBundleID;
191 const char *appPath;
192 const char *appSignature;
193 const char *appVersion;
194 const char *merpGUIPath;
195 const char *eventType;
196 const char *merpFilePath;
197 const char *crashLogPath;
198 const char *werXmlPath;
199 const char *moduleVersion;
201 gboolean log;
202 GSList *annotations;
203 } MerpOptions;
205 static MerpOptions config;
207 typedef struct {
208 char *key;
209 char *value;
210 } MonoMerpAnnotationEntry;
212 static const char *
213 get_merp_bitness (MerpArch arch)
215 switch (arch) {
216 case MerpArchx86_64:
217 return "x64";
218 case MerpArchx86:
219 return "x32";
220 default:
221 g_assert_not_reached ();
225 static MerpArch
226 get_merp_arch (void)
228 #ifdef TARGET_X86
229 return MerpArchx86;
230 #elif defined(TARGET_AMD64)
231 return MerpArchx86_64;
232 #elif defined(TARGET_POWERPC)
233 return MerpArchPPC;
234 #elif defined(TARGET_POWERPC64)
235 return MerpArchPPC64;
236 #else
237 g_assert_not_reached ();
238 #endif
241 static const char *
242 get_merp_exctype (MERPExcType exc)
244 switch (exc) {
245 case MERP_EXC_FORCE_QUIT:
246 return "0x10000000";
247 case MERP_EXC_SIGSEGV:
248 return "0x20000000";
249 case MERP_EXC_SIGABRT:
250 return "0x30000000";
251 case MERP_EXC_SIGSYS:
252 return "0x40000000";
253 case MERP_EXC_SIGILL:
254 return "0x50000000";
255 case MERP_EXC_SIGBUS:
256 return "0x60000000";
257 case MERP_EXC_SIGFPE:
258 return "0x70000000";
259 case MERP_EXC_SIGTRAP:
260 return "0x03000000";
261 case MERP_EXC_SIGKILL:
262 return "0x04000000";
263 case MERP_EXC_HANG:
264 return "0x02000000";
265 case MERP_EXC_MANAGED_EXCEPTION:
266 return "0x05000000";
267 case MERP_EXC_NONE:
268 // Exception type documented as optional, not optional
269 g_assert_not_reached ();
270 default:
271 g_assert_not_reached ();
275 static MERPExcType
276 parse_exception_type (const char *signal)
278 if (!strcmp (signal, "SIGSEGV"))
279 return MERP_EXC_SIGSEGV;
281 if (!strcmp (signal, "SIGFPE"))
282 return MERP_EXC_SIGFPE;
284 if (!strcmp (signal, "SIGILL"))
285 return MERP_EXC_SIGILL;
287 if (!strcmp (signal, "SIGABRT"))
288 return MERP_EXC_SIGABRT;
290 // Force quit == hang?
291 // We need a default for this
292 if (!strcmp (signal, "SIGTERM"))
293 return MERP_EXC_HANG;
295 if (!strcmp (signal, "MANAGED_EXCEPTION"))
296 return MERP_EXC_MANAGED_EXCEPTION;
298 // FIXME: There are no other such signal
299 // strings passed to mono_handle_native_crash at the
300 // time of writing this
301 g_error ("Merp doesn't know how to handle %s\n", signal);
304 static int merp_file_permissions = S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH;
306 static gboolean
307 mono_merp_write_params (MERPStruct *merp)
309 int handle = g_open (merp->merpFilePath, O_TRUNC | O_WRONLY | O_CREAT, merp_file_permissions);
310 g_assertf (handle != -1, "Could not open MERP file at %s", merp->merpFilePath);
312 g_async_safe_fprintf(handle, "ApplicationBundleId: %s\n", merp->bundleIDArg);
313 g_async_safe_fprintf(handle, "ApplicationVersion: %s\n", merp->versionArg);
314 g_async_safe_fprintf(handle, "ApplicationBitness: %s\n", get_merp_bitness (merp->archArg));
316 g_async_safe_fprintf(handle, "ApplicationName: %s\n", merp->serviceNameArg);
317 g_async_safe_fprintf(handle, "ApplicationPath: %s\n", merp->servicePathArg);
318 g_async_safe_fprintf(handle, "BlameModuleName: %s\n", merp->moduleName);
319 g_async_safe_fprintf(handle, "BlameModuleVersion: %s\n", merp->moduleVersion);
320 g_async_safe_fprintf(handle, "BlameModuleOffset: 0x%llx\n", (unsigned long long)merp->moduleOffset);
321 g_async_safe_fprintf(handle, "ExceptionType: %s\n", get_merp_exctype (merp->exceptionArg));
322 g_async_safe_fprintf(handle, "StackChecksum: 0x%llx\n", merp->hashes.offset_free_hash);
323 g_async_safe_fprintf(handle, "StackHash: 0x%llx\n", merp->hashes.offset_rich_hash);
325 // Provided by icall
326 g_async_safe_fprintf(handle, "OSVersion: %s\n", merp->osVersion);
327 g_async_safe_fprintf(handle, "LanguageID: 0x%x\n", merp->uiLidArg);
328 g_async_safe_fprintf(handle, "SystemManufacturer: %s\n", merp->systemManufacturer);
329 g_async_safe_fprintf(handle, "SystemModel: %s\n", merp->systemModel);
330 g_async_safe_fprintf(handle, "EventType: %s\n", merp->eventType);
332 close (handle);
333 return TRUE;
336 static gboolean
337 mono_merp_send (MERPStruct *merp)
339 gboolean invoke_success = FALSE;
341 #if defined(HAVE_EXECV) && defined(HAVE_FORK)
342 pid_t pid = (pid_t) fork ();
344 // Only one we define on OSX
345 if (pid == 0) {
346 const char *open_path = "/usr/bin/open";
347 const char *argvOpen[] = {open_path, "-a", config.merpGUIPath, NULL};
348 execv (open_path, (char**)argvOpen);
349 exit (-1);
350 } else {
351 int status;
352 int exit_status = FALSE;
354 while (TRUE) {
355 if (waitpid(pid, &status, WUNTRACED | WCONTINUED) == -1)
356 break;
358 if (WIFEXITED(status)) {
359 exit_status = WEXITSTATUS(status);
360 invoke_success = (exit_status == 0);
361 break;
362 } else if (WIFSIGNALED(status)) {
363 break;
368 // // Create process to launch merp gui application
369 #endif
371 return invoke_success;
374 static void
375 get_apple_model (char *buffer, size_t max_length)
377 size_t sz = 0;
379 // Get the number of bytes to copy
380 sysctlbyname("hw.model", NULL, &sz, NULL, 0);
382 if (sz > max_length) {
383 buffer[0] = '\0';
384 return;
387 sysctlbyname("hw.model", buffer, &sz, NULL, 0);
390 static void
391 mono_init_merp (const intptr_t crashed_pid, const char *signal, MonoStackHash *hashes, MERPStruct *merp)
393 mono_memory_barrier ();
394 g_assert (mono_merp_enabled ());
396 merp->merpFilePath = config.merpFilePath;
397 merp->crashLogPath = config.crashLogPath;
398 merp->werXmlPath = config.werXmlPath;
400 // If these aren't set, icall wasn't made
401 // don't do merp? / don't set the variable to use merp;
402 g_assert (config.appBundleID);
403 g_assert (config.appVersion);
404 merp->bundleIDArg = config.appSignature;
405 merp->versionArg = config.appVersion;
407 merp->archArg = get_merp_arch ();
408 merp->exceptionArg = parse_exception_type (signal);
410 merp->serviceNameArg = config.appBundleID;
411 merp->servicePathArg = config.appPath;
413 merp->moduleName = "Mono Exception";
414 merp->moduleVersion = config.moduleVersion;
416 merp->moduleOffset = 0;
418 merp->uiLidArg = MONO_LOCALE_INVARIANT;
419 #if defined (TARGET_OSX)
420 merp->osVersion = macos_version_string ();
421 #else
422 merp->osVersion = kernel_version_string ();
423 #endif
425 // FIXME: THis is apple-only for now
426 merp->systemManufacturer = "apple";
427 get_apple_model ((char *) merp->systemModel, sizeof (merp->systemModel));
429 merp->eventType = MONO_MERP_EVENT_TYPE_STR;
431 merp->hashes = *hashes;
433 merp->annotations = config.annotations;
436 static gboolean
437 mono_merp_write_fingerprint_payload (const char *non_param_data, const MERPStruct *merp)
439 int handle = g_open (merp->crashLogPath, O_TRUNC | O_WRONLY | O_CREAT, merp_file_permissions);
440 g_assertf (handle != -1, "Could not open crash log file at %s", merp->crashLogPath);
442 g_async_safe_fprintf(handle, "{\n");
443 g_async_safe_fprintf(handle, "\t\"payload\" : \n");
444 g_write (handle, non_param_data, (guint32)strlen (non_param_data));
445 g_async_safe_fprintf(handle, ",\n");
447 g_async_safe_fprintf(handle, "\t\"parameters\" : \n{\n");
448 g_async_safe_fprintf(handle, "\t\t\"ApplicationBundleId\" : \"%s\",\n", merp->bundleIDArg);
449 g_async_safe_fprintf(handle, "\t\t\"ApplicationVersion\" : \"%s\",\n", merp->versionArg);
450 g_async_safe_fprintf(handle, "\t\t\"ApplicationBitness\" : \"%s\",\n", get_merp_bitness (merp->archArg));
451 g_async_safe_fprintf(handle, "\t\t\"ApplicationName\" : \"%s\",\n", merp->serviceNameArg);
452 g_async_safe_fprintf(handle, "\t\t\"BlameModuleName\" : \"%s\",\n", merp->moduleName);
453 g_async_safe_fprintf(handle, "\t\t\"BlameModuleVersion\" : \"%s\",\n", merp->moduleVersion);
454 g_async_safe_fprintf(handle, "\t\t\"BlameModuleOffset\" : \"0x%lx\",\n", merp->moduleOffset);
455 g_async_safe_fprintf(handle, "\t\t\"ExceptionType\" : \"%s\",\n", get_merp_exctype (merp->exceptionArg));
456 g_async_safe_fprintf(handle, "\t\t\"StackChecksum\" : \"0x%llx\",\n", merp->hashes.offset_free_hash);
457 g_async_safe_fprintf(handle, "\t\t\"StackHash\" : \"0x%llx\",\n", merp->hashes.offset_rich_hash);
458 g_async_safe_fprintf(handle, "\t\t\"Extra\" : \n\t\t{\n");
460 for (GSList *cursor = merp->annotations; cursor; cursor = cursor->next) {
461 MonoMerpAnnotationEntry *iter = (MonoMerpAnnotationEntry *) cursor->data;
462 g_async_safe_fprintf(handle, "\t\t\t\"%s\" : \"%s\"\n", iter->key, iter->value);
465 g_async_safe_fprintf(handle, "\t\t},\n");
467 // Provided by icall
468 g_async_safe_fprintf(handle, "\t\t\"OSVersion\" : \"%s\",\n", merp->osVersion);
469 g_async_safe_fprintf(handle, "\t\t\"LanguageID\" : \"0x%x\",\n", merp->uiLidArg);
470 g_async_safe_fprintf(handle, "\t\t\"SystemManufacturer\" : \"%s\",\n", merp->systemManufacturer);
471 g_async_safe_fprintf(handle, "\t\t\"SystemModel\" : \"%s\",\n", merp->systemModel);
472 g_async_safe_fprintf(handle, "\t\t\"EventType\" : \"%s\"\n", merp->eventType);
474 // End of parameters
475 g_async_safe_fprintf(handle, "\t}\n");
476 g_async_safe_fprintf(handle, "}\n");
478 // End of object
479 close (handle);
481 return TRUE;
484 static gboolean
485 mono_write_wer_template (MERPStruct *merp)
487 // Note about missing ProcessInformation block: we have no PID that makes sense
488 // and when mono is embedded and used to run functions without an entry point,
489 // there is no image that would make any semantic sense to send either.
490 // It's a nuanced problem, each way we can run mono would need a separate fix.
492 int handle = g_open (merp->werXmlPath, O_WRONLY | O_CREAT | O_TRUNC, merp_file_permissions);
493 g_assertf (handle != -1, "Could not open WER XML file at %s", merp->werXmlPath);
495 // Provided by icall
496 g_async_safe_fprintf(handle, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
497 g_async_safe_fprintf(handle, "<WERReportMetadata>\n");
498 g_async_safe_fprintf(handle, "<ProblemSignatures>\n");
499 g_async_safe_fprintf(handle, "<EventType>%s</EventType>\n", merp->eventType);
501 int i=0;
502 g_async_safe_fprintf(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->bundleIDArg, i);
503 i++;
504 g_async_safe_fprintf(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->versionArg, i);
505 i++;
506 g_async_safe_fprintf(handle, "<Parameter%d>%s</Parameter%d>\n", i, get_merp_bitness (merp->archArg), i);
507 i++;
508 g_async_safe_fprintf(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->serviceNameArg, i);
509 i++;
510 g_async_safe_fprintf(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->moduleName, i);
511 i++;
512 g_async_safe_fprintf(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->moduleVersion, i);
513 i++;
514 g_async_safe_fprintf(handle, "<Parameter%d>0x%zx</Parameter%d>\n", i, merp->moduleOffset, i);
515 i++;
516 g_async_safe_fprintf(handle, "<Parameter%d>%s</Parameter%d>\n", i, get_merp_exctype (merp->exceptionArg), i);
517 i++;
518 g_async_safe_fprintf(handle, "<Parameter%d>0x%llx</Parameter%d>\n", i, merp->hashes.offset_free_hash, i);
519 i++;
520 g_async_safe_fprintf(handle, "<Parameter%d>0x%llx</Parameter%d>\n", i, merp->hashes.offset_rich_hash, i);
521 i++;
522 g_async_safe_fprintf(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->osVersion, i);
523 i++;
524 g_async_safe_fprintf(handle, "<Parameter%d>0x%x</Parameter%d>\n", i, merp->uiLidArg, i);
525 i++;
526 g_async_safe_fprintf(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->systemManufacturer, i);
527 i++;
528 g_async_safe_fprintf(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->systemModel, i);
529 i++;
531 g_async_safe_fprintf(handle, "</ProblemSignatures>\n");
532 g_async_safe_fprintf(handle, "</WERReportMetadata>\n");
534 close (handle);
536 return TRUE;
539 // Returns success
540 gboolean
541 mono_merp_invoke (const intptr_t crashed_pid, const char *signal, const char *non_param_data, MonoStackHash *hashes)
543 MonoStateMem mem;
544 int merp_tmp_file_tag = 2;
545 gboolean alloc_success = mono_state_alloc_mem (&mem, merp_tmp_file_tag, sizeof (MERPStruct));
546 if (!alloc_success)
547 return FALSE;
549 MERPStruct *merp = (MERPStruct *) mem.mem;
550 memset (merp, 0, sizeof (*merp));
552 mono_summarize_timeline_phase_log (MonoSummaryMerpWriter);
554 mono_init_merp (crashed_pid, signal, hashes, merp);
556 if (!mono_merp_write_params (merp))
557 return FALSE;
559 if (!mono_merp_write_fingerprint_payload (non_param_data, merp))
560 return FALSE;
562 if (!mono_write_wer_template (merp))
563 return FALSE;
565 // Start program
566 mono_summarize_timeline_phase_log (MonoSummaryMerpInvoke);
567 gboolean success = mono_merp_send (merp);
569 if (success)
570 mono_summarize_timeline_phase_log (MonoSummaryCleanup);
572 mono_state_free_mem (&mem);
574 return success;
577 void
578 mono_merp_add_annotation (const char *key, const char *value)
580 MonoMerpAnnotationEntry *entry = g_new0 (MonoMerpAnnotationEntry, 1);
581 entry->key = g_strdup (key);
582 entry->value = g_strdup (value);
583 config.annotations = g_slist_prepend (config.annotations, entry);
586 void
587 mono_merp_disable (void)
589 mono_memory_barrier ();
591 if (!config.enable_merp)
592 return;
594 g_free ((char*)config.appBundleID); // cast away const
595 g_free ((char*)config.appSignature);
596 g_free ((char*)config.appVersion);
597 g_free ((char*)config.merpGUIPath);
598 g_free ((char*)config.appPath);
599 g_free ((char*)config.moduleVersion);
600 g_slist_free (config.annotations);
601 memset (&config, 0, sizeof (config));
603 mono_memory_barrier ();
606 void
607 mono_merp_enable (const char *appBundleID, const char *appSignature, const char *appVersion, const char *merpGUIPath, const char *appPath, const char *configDir)
609 mono_memory_barrier ();
611 g_assert (!config.enable_merp);
613 char *prefix = NULL;
615 if (!configDir) {
616 const char *home = g_get_home_dir ();
617 prefix = g_strdup_printf ("%s/Library/Group Containers/UBF8T346G9.ms/", home);
618 } else {
619 prefix = g_strdup (configDir);
621 config.merpFilePath = g_strdup_printf ("%s%s", prefix, "MERP.uploadparams.txt");
622 config.crashLogPath = g_strdup_printf ("%s%s", prefix, "lastcrashlog.txt");
623 config.werXmlPath = g_strdup_printf ("%s%s", prefix, "CustomLogsMetadata.xml");
624 g_free (prefix);
626 config.moduleVersion = mono_get_runtime_callbacks ()->get_runtime_build_info ();
628 config.appBundleID = g_strdup (appBundleID);
629 config.appSignature = g_strdup (appSignature);
630 config.appVersion = g_strdup (appVersion);
631 config.merpGUIPath = g_strdup (merpGUIPath);
632 config.appPath = g_strdup (appPath);
634 config.log = g_getenv ("MONO_MERP_VERBOSE") != NULL;
636 config.enable_merp = TRUE;
638 mono_memory_barrier ();
641 gboolean
642 mono_merp_enabled (void)
644 return config.enable_merp;
647 #else
649 #include <mono/utils/mono-compiler.h>
651 MONO_EMPTY_SOURCE_FILE (mono_merp);
653 #endif // TARGET_OSX