WIP -- this is an unreviewed DRAFT. Information may be incomplete and/or misleading. This article only represents the understanding of one individual.
Shiftability is the ability to safely shift code around an executable without changing the behavior of the code at all. Shiftability allows changing the order of functions, adding or removing functions, and adding or removing TUs (source files). It is useful for modifying applications, i.e. developing mods for video games.
Programming console games is often done in C/C++, using lots of pointers. Usually, these pointers are relative to other pointers, e.g. auto *pFoo = &bar->mFoo This forms like a tree of pointers, where the root of the tree is of interest. The root is not relative to another pointer, instead it is absolute. It could come from malloc, which does not affect shiftability. Or, it could come from a static variable. Static variables do affect shiftability, causing problems: they have a fixed, static location in memory. Therefore, they have a fixed, constant address. As a result, a program will use hardcoded pointer literals (e.g. 0x80001234) to reference these static variables.
Problems arise when more code/data is inserted or removed before the static variable. This changes the location of the static variable; it shifts the variable and its data. That invalidates the hardcoded pointers. Being hard-coded, static, unchanging constants, hardcoded pointers will still point to the old location.
Compilers generate object files, more precisely called "relocatable" object files. You may recognize them from their typical .o file extension. As their name suggests, they can be relocated, or put another way, shifted. When compiling object files, references to static variables will become relocation entries (sometimes called "relocations"). If one examines an .o file using a tool like objdump, they will see things like RELOC_PPC32; these are relocation entries. When the linker combines the .o object files into an .elf executable file, the linker will perform relocation resolution. The linker will pick a specific address to store static data, and use that address to replace all the associated relocation entries. So the final .elf executable is not relocatable.
The first step of decompiling is splitting. Splitting undoes the linker's work. It converts ane executable program back into object files. However, undoing relocation resolution isn't straightforward. Short of fully decompiling and matching the entire prgram, there is no way to 100% guarantee that we have properly undone the relocation resolution step. In other words, shiftability cannot be proven without 100% OK. This is because relocation resolution is a lossy process. It erases information about the original data. If a program's code has:
const auto foo = 0x80001234;
static auto bar = "some data";
printf("%d", foo);
printf(bar);
then looking at the final executable, how do we know foo is just a literal value and does not come from a relocation entry, but &bar does come from a relocation entry? We don't! And thus, problems occur.
To fix, one must undo the relocation converting hardcoded pointers back to relocations. So first: find hardcoded pointers, and second: give them to your splitter so it can turn them back into relocation entries.
encounter's decomp-toolkit for GC/Wii includes a splitter that tries to automatically detect relocations and undo relocation resolution. Thus, it tries to achieve perfect shiftablity automatically. However, sometimes it misses some relocations. Sometimes it fakes relocations, erroneously converting normal data into relocations. So, we have to use dtk's noreloc attribute to tell it not to treat normal data as a hardcoded pointer. See the tool's documentation for further details.