Open inconstante opened 3 years ago
4 - compile specifically to emit a preamble and nops at function starts
on x86-64 that is GCCs -fpatchable-function-entry=N,M option. Using libpulp on other architecture means that this flag needs to be supported on that architecture.
More items:
S390x port is desired.
Currently, Libpulp only supports x86_64. Adding support for more architectures requires a few changes and a couple of new files. This issue tries to summarize what's needed:
1- Calling libpulp functions from ptrace:
Libpulp is like GDB. It attaches to running processes with ptrace and does stuff, such as calling functions in the target process. Calling functions from ptraced processes is not like calling functions in its own memory space, so Libpulp must hijack a running thread, stealing its context and diverting execution to wherever it wants. However, to regain control, it can't rely on regular
ret
instructions; instead, it must cause a software trap. On x86, that is achieved with theint3
instruction, such as the following snippet, from lib/ulp_interface.S:where
__ulp_trigger
is the function Libpulp wants to call. Notice how it ends withint3
, notret
.A new port must provide a new lib/ulp_interface.S implementation.
2- Selecting between versions of a live patched function:
After a live patch has been applied, live patched functions have their prologues altered (see item 3, below), so that, instead of running the actual code of the function, execution gets diverted to a small block of code in its preamble that: saves the contents of
%rdi
(a caller-saved register) onto the stack; writes an index value to it; calls a live patch version selection routine, which performs its computation, then returns the result in%r11
(x86's scratch register on AMD64's ABI supplement for SysV).Since the version selection routine executes between calls, it must preserve all registers, even caller-saved ones, specially those used in parameter passing. Otherwise, when the target function gets finally executed, the parameters would be wrong. In Libpulp, the saving and restoring of registers happens in
lib/ulp_prologue.S
.Step-by-step, this is what happens in a call to a live patched function:
A new port must provide a similar mechanism for its target architecture.
3- Patching function prologues
As mentioned above, live patched functions have their prologues altered, so that, instead of execution the function, as they normally would, they call a version selection routine. This function selection routine takes a single argument, an index into Libpulp's data structures, which contain references to all versions of the live patched functions.
However, when a live patchable process starts, the prologues of live patchable functions contain
nop
instructions, so that the regular functions execute normally. When a live patch is applied, these nops get replaced with a small block of code that diverts execution to the version selection routine. On x86_64, these blocks of code look like the following snippet:In the current implementation, Libpulp only knows how to write x64_64 code. The snippet above is written by ulp_patch_prologue_layout (also check ulp_prologue and ulp_patch_addr_absolute).
A new port must implement a mechanism that similarly patches function prologues.
4- compile specifically to emit a preamble and nops at function starts
on x86-64 that is GCCs -fpatchable-function-entry=N,M option. Using libpulp on other architecture means that this flag needs to be supported on that architecture.