Skip to content

Commit 4a0b4f4

Browse files
lewurmmarek-safar
authored andcommitted
[mini] publish global patches after JitInfo has been added
Fixes #14080 Consider the following example: ```csharp static void CommonCallTarget () { } static void TailA () { tailcall CommonCallTarget (); } static void TailB () { tailcall CommonCallTarget (); } ``` Since `TailA` and `TailB` are tailcalling into `CommonCallTarget`, the resolution at patch-time is a bit tricky, that is, since it's a jump-like instruction the patching machinery won't know where it was called from. That's why we maintain a global hashtable `jump_target_hash` where each jump-site is signed up to be patched. At patch-time we know the target method (in the example `CommonCallTarget`), but since we don't know where we are coming from, we will just apply all patches for that target. This works since ages, so why did it crash on arm64 sometimes? When the patching happens, we check if the displacement between jump-site and target fits into it (24bit). If not, which happens not very often, we have to allocate a _thunk_: https://github.com/mono/mono/blob/36296ce291f8a7b19de3eccb7a32c7e4ed9df8f2/mono/mini/mini-arm64.c#L928-L942 So instead of jumping to the target directly, we'll branch to the thunk. This is a little trampoline that loads the full address of the target and then finally branches to it. This one will live close-by the jump-site, because during compilation we will reserve specifically for that scenario some space after the generated code. For this, however, we need the JitInfo of the jump-site. And that's where the origin of the race is. Let's say: * Thread A compiles `TailA`, and then jumps into it. Thus one patch point is in the `jump_target_hash`. * Now Thread B compiles `TailB`, registers the patch point but has _not_ yet registered its JitInfo. * Then Thread A continues, does the tailcall into `CommonCallTarget`, enters the patching machinery, which sees two patches. Now assume when applying the patch for `TailB` the displacement is too large, thus it tries to allocate a thunk but can't find the relevant JitInfo for it that it needs to emit the thunk. So it crashes as reported in #14080 As far as I can tell this only affects ARM64, ARM and PPC.
1 parent 7a1f63f commit 4a0b4f4

File tree

1 file changed

+18
-0
lines changed

1 file changed

+18
-0
lines changed

mono/mini/mini.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2122,6 +2122,21 @@ mono_postprocess_patches (MonoCompile *cfg)
21222122
patch_info->data.table->table = (MonoBasicBlock**)table;
21232123
break;
21242124
}
2125+
default:
2126+
/* do nothing */
2127+
break;
2128+
}
2129+
}
2130+
}
2131+
2132+
/* Those patches require the JitInfo of the compiled method already be in place when used */
2133+
static void
2134+
mono_postprocess_patches_after_ji_publish (MonoCompile *cfg)
2135+
{
2136+
MonoJumpInfo *patch_info;
2137+
2138+
for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
2139+
switch (patch_info->type) {
21252140
case MONO_PATCH_INFO_METHOD_JUMP: {
21262141
unsigned char *ip = cfg->native_code + patch_info->ip.i;
21272142

@@ -3865,6 +3880,9 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, JitFl
38653880

38663881
if (cfg->method->dynamic)
38673882
mono_dynamic_code_hash_lookup (cfg->domain, cfg->method)->ji = cfg->jit_info;
3883+
3884+
mono_postprocess_patches_after_ji_publish (cfg);
3885+
38683886
mono_domain_unlock (cfg->domain);
38693887
}
38703888

0 commit comments

Comments
 (0)