www.digitalmars.com         C & C++   DMDScript  

digitalmars.dip.ideas - watch Attribute for Hotpatchable Functions

reply xoxorwr <noreply gmail.com> writes:
Hey,

I'm working on a dynamic hot reloading / binary patching 
framework in D, and I've hit an issue with hooking very small 
functions.

On x86_64, a safe absolute jump hook requires about 14 bytes:

```asm
jmp [rip+0]
dq target
```

But some functions compile to fewer than 14 bytes, so if I 
overwrite their prologue, I can corrupt the `ret` instruction / 
adjacent code.

Right now, the only workaround is manually padding them with 
inline assembly, which is very inconvenient.

I'd like to propose a builtin function attribute, for example:

```d
 watch
void foo() { }
```

This attribute would tell the compiler to make the function 
hotpatch friendly by:

- disabling inlining
- emitting enough NOP padding at the function entry for a runtime 
jump patch

On x86_64, this would mean reserving at least 14 bytes at the 
start of the function.

The goal is to provide a standard, compiler supported way to mark 
functions as safe targets for runtime patching or hot reloading, 
without requiring manual assembly padding.


Example:

```diff
--- a/compiler/src/dmd/glue/package.d
+++ b/compiler/src/dmd/glue/package.d
   -507,2 +507,6   
          Symbol* s = toSymbol(fd); // may set skipCodegen
          func_t* f = s.Sfunc;
+
+        if (isHotpatchable(fd))
+            f.Fflags3 |= Fwatch;
+
```

```diff
--- a/compiler/src/dmd/backend/x86/cgcod.d
+++ b/compiler/src/dmd/backend/x86/cgcod.d
   -628,2 +628,12   
          if (config.flags3 & CFG3ibt && !I16)
              cdb.gen1(I32 ? ENDBR32 : ENDBR64);
+
+        if (funcsym_p.Sfunc.Fflags3 & Fwatch)
+        {
+            if (I64)
+                // 14 byte multibyte NOP for 64bit absolute JMP
+                cdb.genasm([0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00]);
+            else
+                // 8 byte multibyte NOP for 32bit relative JMP
+                cdb.genasm([0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 
0x00, 0x00]);
+        }
```
Jun 12
next sibling parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
This can be done using a core.attribute attribute.

It is not something that should affect normal compilation, nor should it 
be common place.

For llvm, it looks like the attribute is: patchable-function-entry set 
to a number like 5.
Jun 12
parent reply xoxorwr <noreply gmail.com> writes:
On Friday, 12 June 2026 at 13:05:03 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 This can be done using a core.attribute attribute.

 It is not something that should affect normal compilation, nor 
 should it be common place.

 For llvm, it looks like the attribute is: 
 patchable-function-entry set to a number like 5.
This sounds like a better idea, least intrusive option, a compiler flag.
Jun 12
parent Guillaume Piolat <first.name gmail.com> writes:
On Friday, 12 June 2026 at 13:11:59 UTC, xoxorwr wrote:
 On Friday, 12 June 2026 at 13:05:03 UTC, Richard (Rikki) Andrew 
 Cattermole wrote:
 This can be done using a core.attribute attribute.

 It is not something that should affect normal compilation, nor 
 should it be common place.

 For llvm, it looks like the attribute is: 
 patchable-function-entry set to a number like 5.
This sounds like a better idea, least intrusive option, a compiler flag.
If we supported arm64ec (x86 code compiled into the ARM64X linker format), then each x86 function would have a similar prefix so that the JIT can do its work. IIRC "arm64ec" requires a similar function prologue as your case.
Jun 14
prev sibling parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Friday, 12 June 2026 at 12:58:53 UTC, xoxorwr wrote:
 I'm working on a dynamic hot reloading / binary patching 
 framework in D, and I've hit an issue with hooking very small 
 functions.

 […]

 I'd like to propose a builtin function attribute, for example:
This seems to be compiler-specific. It should probably be a `pragma`, essentially for the same reason as inlining is controlled by a `pragma` and not an attribute.
Jun 19