I was re-reading Ian Lance Taylor's series of articles on Linkers [0], and one thing I didn't realise is that using dynamically linked libraries almost requires self-modifying code unless you want to resolve all function calls are start-time (which would make startup slower).
I'm still working my way through it so it's possible that I've misunderstood this section, though, and one question I haven't answered is how they get around the typical restriction on w+x pages.
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.
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.
It was maybe cool 50 years ago or so. Nowadays it's no longer needed. Possible performance gains of such code are marginal and modern programming languages allow generating many specialized and optimized code pieces using the same template, so that self-modification is no longer needed.
There is also JIT (like in regexp engines), but it's different story.
It's still beneficial on some x86-64 implementations to rewrite indirect jumps (as used in PLT stubs) to direct jumps when feasible. For example, AMD says this about the Zen 4 architecture:
> Only a limited number of indirect targets that cross a 64MB aligned boundary relative to the branch address can be tracked in the indirect target predictor. Software should limit the number of indirect branch targets that cross such a boundary.
And one way doing this is to replace the indirect branch with a direct branch, which supports a 32-bit signed displacement.
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.
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.
A nested function requires an extra parameter for the nested stack pointer, which is passed in a dedicated register on most ABIs. You can't spell this kind of function type in C. To make a C-callable function pointer, the compiler needs to generate a little bit of code (a trampoline) that stuffs the appropriate stack pointer in the appropriate register.
That trampoline needs to live somewhere. Since the function is inherently noncallable after the stack returns, and C programmers hate it when their compiler sneaks in extra malloc calls under the hood, the compiler decides to stick the trampoline on the stack instead of heap-allocating it.
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.
C# does closures by creating a class. When you run the function, captured local variables actually live inside the object instead of in the stack. This makes use of an object, but no memory pages need to become read/write/executable to make C#-style closures happen. You just have a combination function/object pointer (a delegate) instead of a single function pointer.
Have always wondered, why can’t nested functions just be normal functions inside a namespace?
It seems silly to expose a tiny helper to the entire compilation unit when it is only meant to be used inside one function…
> why can’t nested functions just be normal functions inside a namespace
Because GCC's nested functions are closures - they can access local variables within the function.
Self-modifying code is cool. It's a shame we had to disable it for security.
I was re-reading Ian Lance Taylor's series of articles on Linkers [0], and one thing I didn't realise is that using dynamically linked libraries almost requires self-modifying code unless you want to resolve all function calls are start-time (which would make startup slower).
I'm still working my way through it so it's possible that I've misunderstood this section, though, and one question I haven't answered is how they get around the typical restriction on w+x pages.
[0] Particularly this one https://www.airs.com/blog/archives/41
Not just security. Instruction caches must also be aware of self-modifying code.
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.
15 replies →
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.
6 replies →
It was maybe cool 50 years ago or so. Nowadays it's no longer needed. Possible performance gains of such code are marginal and modern programming languages allow generating many specialized and optimized code pieces using the same template, so that self-modification is no longer needed.
There is also JIT (like in regexp engines), but it's different story.
It's still beneficial on some x86-64 implementations to rewrite indirect jumps (as used in PLT stubs) to direct jumps when feasible. For example, AMD says this about the Zen 4 architecture:
> Only a limited number of indirect targets that cross a 64MB aligned boundary relative to the branch address can be tracked in the indirect target predictor. Software should limit the number of indirect branch targets that cross such a boundary.
And one way doing this is to replace the indirect branch with a direct branch, which supports a 32-bit signed displacement.
1 reply →
Related: previous article in the series, https://news.ycombinator.com/item?id=49308685 (103 points, 47 comments)
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.
Thanks. Sigils fixed (may take a couple of minutes).
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.
A nested function requires an extra parameter for the nested stack pointer, which is passed in a dedicated register on most ABIs. You can't spell this kind of function type in C. To make a C-callable function pointer, the compiler needs to generate a little bit of code (a trampoline) that stuffs the appropriate stack pointer in the appropriate register.
That trampoline needs to live somewhere. Since the function is inherently noncallable after the stack returns, and C programmers hate it when their compiler sneaks in extra malloc calls under the hood, the compiler decides to stick the trampoline on the stack instead of heap-allocating it.
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.
Doesn't x86 actually support nested call pointers using enter to natively support this in Pascal?
9 replies →
need to represent the "captured-variables"/closure
C# does closures by creating a class. When you run the function, captured local variables actually live inside the object instead of in the stack. This makes use of an object, but no memory pages need to become read/write/executable to make C#-style closures happen. You just have a combination function/object pointer (a delegate) instead of a single function pointer.