Making our own executable packer, part 13, Thread-local storage
fasterthanli.me
fasterthanli.me
I know the basics just like anyone else whose done some light debugging with gdb but it is hard to find information that goes beyond that. There’s a gazillion of half-baked gdb introductions but IMO not enough advanced tutorials.
Is this more suited for certain languages than others?
That sounds like just having scoped state in each thread that isn't global.
So, what you do in the loop:
- grab thread-local variable containing local accumulator
- if it doesn’t exist yet, create it and add it to the
global list of all local accumulators (there’s a race
here, but it will be rarely run)
- do the work for the iteration
- update accumulator
Then, after the loop, go through the global list of accumulators and merge them.Let us suppose that you wanted to store some per-thread data.
A naive way to do that could be to have a global mapping from thread ID to a pointer to your data. To look up your data, you would obtain the id of your current thread and look it up in the map. Of course, since that map is shared across threads, you'd also need to protect it with a mutex.
Operating systems themselves need to maintain per-thread data. When context-switching between threads, the OS will also switch out the current block of per-thread data. Applications can piggyback on this, so instead of needing to lock a shared map and then do a lookup to retrieve their data, it will already be available to them by virtue of the fact that it is essentially part of the current thread's context. No locking or global map lookups necessary!
- to help make thread-unsafe APIs thread-safe
E.g., errno, strtok(), and so on.
- to store thread-private mutable globals
(Thread-locals are more like thread-globals.)
- to keep a reference/copy of global immutable
data that might get replaced in the middle of
a thread's handling of a request
- to keep contextual data that you can't retrofit
an API to pass explicitly (see also first item)No, he doesn't give you an answer, but you might just find it interesting enough to no longer have the qusstion.
So when you would run a packed application, there application would essentially start to unpack an encrypted/compressed blob of memory, and then jump to it once it's unpacked.
You've found the right series of articles then!
Executables have been fascinating to me ever since I discovered, as a kid, that they were just files. If you renamed a .exe to something else, you could open it in notepad! And if you renamed something else to a .exe, you'd get a neat error dialog.
Clearly, something was different about these files. Seen from notepad, they were mostly gibberish, but there had to be order in that chaos. 12-year-old me knew that, although he didn't quite know how or where to dig to make sense of it all.
So, this series is dedicated to my past self. In it we'll attempt to understand how Linux executables are organized, how they are executed, and how to make a program that takes an executable fresh off the linker and compresses it - just because we can.