Rendered at 18:28:42 GMT+0000 (Coordinated Universal Time) with Cloudflare Workers.
inigyou 3 hours ago [-]
Self-modifying code is cool. It's a shame we had to disable it for security.
mcculley 2 hours ago [-]
Not just security. Instruction caches must also be aware of self-modifying code.
WalterBright 39 minutes ago [-]
Back in the 1980s, text editors had configuration files. The configuration file would be read every time the editor was loaded. This was very slow on a floppy disk system.
I realized that, instead of a configuration file, I could configure the executable instead! So, any changes in configuration meant the editor would patch its own exe file!
This marvelous technique came to an end when attempts to stop malware got folded into the operating system.
weinzierl 23 minutes ago [-]
Some early systems, like TeX and I believe some Lisps, took this to the extreme. Instead of patching, they loaded their config once and then dumped the configured process image to a file, which was used in subsequent invocations.
badsectoracula 18 minutes ago [-]
IIRC early Turbo Pascal versions worked like that too, there was some "setup" program that let you configure colors, etc, by modifying the COM/EXE file itself.
inigyou 1 hours ago [-]
yes but it's easy enough to issue a cache flush when you modify the code.
The overhead of cache flushing means some old school techniques are no longer viable, like modifying a constant in the next instruction. However it is still interesting to write machine code snippets once and execute them many times, like the nested function trampolines. I had a case where I had RGB masks like R=0x00ff0000 etc (loaded at startup once) and wanted to convert 0x00rrggbb to match the mask (so no-op in the common case but not always) which could have involved setting the shift amounts in a series of shift instructions.
The Linux kernel uses self-modification to change branches depending on whether certain features are on. For example when a user-mode process starts tracing a certain function, it adds code to the beginning of that function to trace the call, otherwise it pads that space with a no-op. JIT compilers also make good use of knowing whether a class has any subclasses, which is statically unknowable in Java but dynamically knowable.
codedokode 7 minutes ago [-]
Modifying a constant in code might make sense because it saves several precious bytes of variable storage.
What do you need the executable stack for? You call using a function pointer, there's no executable read/write memory involved in using a function pointer.
tom_ 2 hours ago [-]
Nested functions may require a context pointer of some kind, which the caller can't supply. One way of doing this: create a thunk on the stack that provides the context pointer, and use that address as the pointer to the nested function.
But now the stack needs to be executable.
ok123456 1 hours ago [-]
Doesn't x86 actually support nested call pointers using enter to natively support this in Pascal?
klodolph 43 minutes ago [-]
It’s not a question of ISA support. If you call via a function pointer, how do you supply the pointer to the data? (It has to either be in a separate place from the function code, or the same place. One requires an ABI change, the other an executable, writable section of memory.)
QuadmasterXLII 6 minutes ago [-]
but that can just be executable heap?
WalterBright 30 minutes ago [-]
> If you call via a function pointer, how do you supply the pointer to the data?
D has the notion of a "delegate", which is a (function pointer) and (context pointer) pair. This is incredibly useful, because delegates can:
1. call nested functions that need a pointer to the stack frame of the nestee function
2. call member functions that need `this` pointer
3. call lambdas
4. call COM member functions
The neato thing about this is the ABI for delegates is all the same, so a function that gets a delegate parameter will work with any of 1..4. It's one of the most used features of D.
klodolph 28 minutes ago [-]
Yes, that’s the “ABI” alternative that I was referring to.
uecker 47 seconds ago [-]
This is why I want to have such a feature in C. But because ISA was mentioned, x86 does indeed even has native support for this: https://devblogs.microsoft.com/oldnewthing/20231211-00/?p=10... These instructions are not too useful though and I do not think anybody uses them.
mwkaufma 6 minutes ago [-]
need to represent the "captured-variables"/closure
2 hours ago [-]
gue5t 3 hours ago [-]
At first, I was annoyed by having to read AT&T syntax. Then I was disoriented by realizing the next snippet was in AT&T syntax without the '%' sigil for registers. But the technique is cool.
uecker 2 hours ago [-]
Thanks. Sigils fixed (may take a couple of minutes).
I realized that, instead of a configuration file, I could configure the executable instead! So, any changes in configuration meant the editor would patch its own exe file!
This marvelous technique came to an end when attempts to stop malware got folded into the operating system.
The overhead of cache flushing means some old school techniques are no longer viable, like modifying a constant in the next instruction. However it is still interesting to write machine code snippets once and execute them many times, like the nested function trampolines. I had a case where I had RGB masks like R=0x00ff0000 etc (loaded at startup once) and wanted to convert 0x00rrggbb to match the mask (so no-op in the common case but not always) which could have involved setting the shift amounts in a series of shift instructions.
The Linux kernel uses self-modification to change branches depending on whether certain features are on. For example when a user-mode process starts tracing a certain function, it adds code to the beginning of that function to trace the call, otherwise it pads that space with a no-op. JIT compilers also make good use of knowing whether a class has any subclasses, which is statically unknowable in Java but dynamically knowable.
But now the stack needs to be executable.
D has the notion of a "delegate", which is a (function pointer) and (context pointer) pair. This is incredibly useful, because delegates can:
1. call nested functions that need a pointer to the stack frame of the nestee function
2. call member functions that need `this` pointer
3. call lambdas
4. call COM member functions
The neato thing about this is the ABI for delegates is all the same, so a function that gets a delegate parameter will work with any of 1..4. It's one of the most used features of D.