DynamoRIO
Register Management

The drreg DynamoRIO Register Management Extension is a mediator for selecting, preserving, and using registers among multiple instrumentation components. The interface is meant to be used in concert with the drmgr instrumentation pass scheme. The results are most efficient when used in the drmgr instrumentation insertion phase. Use outside of the insertion phase, such as during drmgr's instrumentation-to-instrumentation transformation phase, or use outside of drmgr entirely, is supported, but duplicate spills and restores may not be optimized away. Also, for non-insertion phases, the responsibility to restore a spilled app value before certain instructions like an app read or a label with the note DR_NOTE_ANNOTATION, and to re-spill the app value after an app write, lies with the user; in the insertion phase, this is done automatically, but it needs to be done manually in non-insertion phases. When using drreg to reserve registers in multiple phases (like app2app or instru2instru in addition to the insertion phase), it is important to call drreg_set_bb_properties() with DRREG_HANDLE_MULTI_PHASE_SLOT_RESERVATIONS to enable logic that avoids conflicts with spill slots used by previous phases. This may have some performance cost, so we do not turn it on by default. Using this option also makes spill slots used in prior phases less available in future phases; the current logic skips over a slot if there's a usage found anywhere later in the bb added by any previous phase. So it requires additional spill slots as well.

Note that DR provides other APIs to save or restore regs, like dr_save_reg() and dr_restore_reg(), and the corresponding dr_read_saved_reg() and dr_write_saved_reg(). They use DR spill slots. drreg may also use DR spill slots if not given enough dedicated slots via drreg_options_t.num_spill_slots. It is unsafe to mix usages of drreg and these direct APIs as it may cause conflicts in slot usages within the same client or with other clients/libraries. Therefore, all cooperating client components should use drreg. Also, clients should make sure to request sufficient dedicated slots from drreg. See documentation of drreg_options_t.num_spill_slots for more details.

Setup

To use drreg with your client simply include this line in your client's CMakeLists.txt file:

use_DynamoRIO_extension(clientname drreg)

That will automatically set up the include path and library dependence.

The initialization routine drreg_init() must be called prior to any of the other routines. Additional calls to drreg_init() are allowed (so long as they are paired with corresponding calls to drreg_exit()). The option fields are combined from multiple calls as described in the documentation for each field. Typically the end-user tool itself specifies these options, with most other library components not directly interacting with drreg (libraries often take in scratch registers from the caller for most of their operations).

Usage

The drreg API is based on a reservation model. Each general-purpose register may be reserved for a period of time and then unreserved. The process of reserving spills the application value, if necessary. A reservation implies exclusive access to that register. Reservations may extend across application instructions, in which case drreg will automatically restore or re-spill application values, when the intervening application instruction reads or writes that register, while still preserving the client's value in that register for use after the application instruction. Reservations may not extend beyond the end of a basic block.

Application Values

drreg assumes that only application instructions need to read application values. drreg assumes that it is safe to wait as long as possible to restore a spilled application value that is no longer reserved. If a client is inserting instrumentation that reads application values, or is using a library routine that does the same, the client needs to tell drreg to restore the needed application registers. This can be done with drreg_restore_app_values() or with drreg_get_app_value(). This essentially acts as a "barrier" to lazy restoring.

Clean calls that read the application context (via dr_get_mcontext()) can have a barrier auto-inserted by passing DR_CLEANCALL_READS_APP_CONTEXT to dr_insert_clean_call_ex() or dr_insert_clean_call_ex_varg(). Similarly, clean calls that modify the application context (via dr_set_mcontext()) should pass DR_CLEANCALL_WRITES_APP_CONTEXT to ensure that drreg updates the new application values. There can be failures to restore application values at points in an instruction list where a register is considered dead and its value is not available (if drreg_options_t.conservative is set to false), but it is assumed that the value is in fact dead and does not matter.

If a clean call is only conditionally executed due to inserted tool control flow, the flag DR_CLEANCALL_MULTIPATH can be passed in addition to the read or write flag. This results in stateless restores. However, tool values will be clobbered by those restores. If control flow and clean call context access is used with registers holding tool values across the clean call, manual restoration may be required rather than passing any of these automated flags.

Linear Control Flow

The drreg API was designed for linear control flow. The API assumes that it can safely wait to restore an unreserved scratch register across application instructions. If a client inserts internal control flow inside a basic block that crosses application instructions, and the client is not explicitly ensuring that each forward jump contains the same set of saved scratch registers at its source and target (typically done by saving all scratch registers that will be needed inside control flow prior to any forward branches), the client should tell drreg to disable its optimizations. This is done by calling drreg_set_bb_properties() and passing DRREG_CONTAINS_SPANNING_CONTROL_FLOW either prior to the drmgr insertion phase or as early as possible in the insertion phase. Setting this property causes application instructions to become barriers to spilled scratch registers that have been unreserved but have not yet been lazily restored. Such scratch registers are then restored prior to each application instruction. drreg will still collapse adjacent spill+restore pairs for the same app instr.

For control flow added during the app2app phase (such as by drutil_expand_rep_string()), drreg is able to identify and act on its own. Normally, drreg disables optimizations if it sees any kind of internal control flow (viz., a branch with an instr_t target) while examining code during the analysis phase. If the client is certain that all app2app internal control flow that it and any libraries it uses either do not cross application instructions or satisfy the property that each forward jump contains the same set of saved scratch registers at its source as at its target (typically done by saving all scratch registers needed inside control flow prior to any forward branches), then the client can call drreg_set_bb_properties() and pass DRREG_IGNORE_CONTROL_FLOW in order to enable full lazy restores by drreg.