Recode works directly with the MSVC compiler to generate code for Recode patching. This requires that your build system is correctly configured for Recode and generates the correct compiler and linker options for Recoding.
Recode requires access to a full VC toolset for compilation and linking. The VC toolset can either be installed with Visual Studio, or it may be located in a custom folder relative to the codebase you are building.
Local VC Toolsets¶
If you use a local VC toolset directory with Recode then you need to make sure it conforms to this basic layout:
/bin One dir per architecture as needed (or same as standard VC installation) /include Same layout as standard VC installation /lib Same layout as standard VC installation
It is highly recommended that your installed version of Visual Studio’s Visual C++ is the same or higher than the version of any local toolset directory. Or in other words: Keep your Visual Studio installation up-to-date.
Recommended compiler option for Recode builds (best results with this set):
- Hotpatch (
/hotpatch) for 32-bit only
Incompatible compiler options for Recode builds (ensure these are disabled):
- Whole Program Optimization (
- Common Language Runtime support (
- Static Run-Time Library (
/GL present when Recoding the following error is emitted:
recode error: cl.exe /GL cannot be used for Recode builds. Ensure Whole Program Optimization is disabled.
Compilation for Recode requires use of the DLL based runtime library using
The static version of the library (specified via
/MTd) can’t be supported as it would result in
duplicated runtime library state and could lead to heap conflicts and worse.
If you attempt to compile for Recode with
/MTd you will see the following error:
recode error: /MD or /MDd required for Recode compilation. /MT or /MTd are unsupported.
Managed (C++/CLI) binaries (compiled with
/clr) are not supported by Recode.
Exception Settings For Recode Protect¶
Recode Protect requires the compiler to be configured to disable exception handling or to explicitly support structured exceptions. Exception handling settings are not overridden automatically by Recode as it could unexpectedly alter program behavior if you rely on exception handling.
Required exception handling compiler options for compatibility with Recode Protect:
- No exception handling (no
/EH*options are set)
- The default option used by most high-performance code.
- Catch blocks will catch both structured exceptions and C++ exceptions.
- No stack unwind will occur and any objects in scope will not be destructed.
- No exception handling (no
- Structured and C++ exception handling (
- The slowest option for performance, particularity on x86.
- Catch blocks catch both structured exceptions and C++ exceptions.
- Only C++ exceptions will unwind the stack and call destructors.
- Structured and C++ exception handling (
Incompatible exception handling compiler options that will disable Recode Protect:
- C++ exception handling only (
- Slow performance, particularity on x86.
- Catch only standard C++ exceptions.
- C++ exceptions will unwind the stack and call destructors.
- C++ exception handling only (
If a module is not linked with
/INCREMENTAL or something prevents incremental linking (see below)
then Recode will attempt to use the hotpatching technique to inject code changes.
If incremental linking is disabled, use
/FUNCTIONPADMIN:16 for reliable Recoding.
While hotpatching works well most of the time there are a few side-effects to consider. When stepping into Recoded files from unmodified code you may receive the following warning:
This is a result of the debugger having to jump through the old function to get to the new function. Since the changed code file only matches the new version of the function, Visual Studio warns about the unmatched source code. This is a side-effect of the trampoline jump that Recode inserts to move to the new function. Stepping in a second time will enter the new code from the modified file:
MyFunc()[old version - source no longer matches]
MyFunc()[new version - matches modified source]
Visual Studio will remember you choice, so if you step into a modified file a second time you shouldn’t see the warning dialog again.
If incremental linking is disabled, enable VC++ hotpatching to help ensure Recode has enough space available
in small functions to be able to redirect them properly.
To enable VC++ hotpatching for Recode, set the
/hotpatch compiler option (32-bit only) and
/FUNCTIONPADMIN:16 linker option (32-bit & 64-bit).
/FUNCTIONPADMIN:16 adds padding between functions for hotpatching. This will increase the size of your
modules although it should have little impact on performance. Disable it for final production builds.
Using Incremental Linking (
will provide an improved debugging experience since stepping into functions will be seamless.
Use incremental linking (if possible) to ensure reliable Recoding and avoid debugging issues.
Lib Project Dependencies¶
Until VS2015, the Microsoft linker did not support incremental linking for any code linked from within a lib. For a smoother debugging experience incremental linking is recommended, so if you’re using VS2012/VS2013, you should try to ensure that that any user code normally stored in a lib file is directly linked to the executable and DLL projects.
In MSBuild this is achieved by enabling both of these options on the executable and DLL projects:
Care must be taken to ensure any libs produced by projects within the solution are not passed to the
linker on the command-line (via
Additional Dependencies in the project settings). If they are you may
error recode : conflicting global variable due to variables from the old lib conflicting with new
stubs generated by Recode.
Even without Recode enabled, if you are frequently modifying code within a lib it is best to enable
Use Library Dependency Inputs to keep your link times as short as possible.
These options require that any lib projects be added as a Project References on any exe and DLL projects that use them. For example, here the BasicLib project has been added as a Project Reference for the BasicTests project:
Project -> Project Dependencies dialog cannot be used to setup Project References to lib
projects - it is merely use to determine build order for projects built within Visual Studio.
Ensure you setup Project References via the
References heading under the Project Properties dialog.
Whole Program Optimization¶
foo.lib(bar.obj) : MSIL .netmodule or module compiled with /GL found; restarting link with /LTCG; add /LTCG to the link command line to improve linker performance LINK : warning LNK4075: ignoring '/INCREMENTAL' due to '/LTCG' specification
In this case please ensure /INCREMENTAL, /OPT:NOREF and /OPT:NOICF are set for the linker. Also remember that external libs may implicitly disable /INCREMENTAL - check linker logs for LNK4075.
As lib files supplied by third parties may contain obj files built with /GL this problem can be hard to diagnose until Recode patching fails. The best approach is to check the linker logs for each project to ensure LNK4074 doesn’t occur.
Enable linker.exe Treat Warnings As Errors
/WX) to help identify LNK4075 more easily.
Module Base Address Requirements¶
The address that a module (exe or DLL) is loaded at can be important for successful Recoding. Recode has to find space to inject patches somewhere ±2GB from the original module base address. If Recode cannot find enough contiguous free space in the 4GB region around the base module to accommodate the patched code it will fail with:
Failed to find an unreserved memory region for patching c:\path\to\module.exe load address: 0x0 VA dump: [... dump of address space around module ...]
This can occur due to one or more factors reducing the available virtual address space:
- Module loaded too close to the start of the address space (0x0) reducing the search window.
- Modules and allocations clustered together due to ASLR and default OS allocation strategy.
- Mapping large files to memory will often consume large amounts of address space.
Windows has a tendency of clustering loaded modules towards the bottom of the address space. This is especially true of modules
that fail to load at their preferred base addresses (this can be seen by checking in the Visual Studio
Modules window during a debug session).
This will also occur if
Address Space Layout Randomization (/DYNAMICBASE) is enabled
(which it is by default). For Recode it can make finding suitable address space hard, especially for a multi-module game engine
with a very busy address space.
Since 64-bit processes have
vast amounts of usable virtual address space,
the easiest workaround is to force the module to load at a higher base address. You can try setting an explicit base address
via the linker option
/BASE (and also make sure you set
/DYNAMICBASE:NO). Something like
/BASE:0x400000000 will usually
shift the module somewhere less crowded in the address space.
Finally, check to see if the base modules are actually being loaded at their assigned base addresses in the Modules debug window -
there will be a
x symbol next to the module icon and a
* to the right of the address range for any module that failed to load
at the assigned base address.
Recode will attempt to optimize compilation for unity files when it detects them. These
#include multiple C/C++ source files into a single unified
file that can then be compiled in a single compiler invocation. This technique reduces overall compilation time by reducing
preprocessing and IO overhead by sharing the overhead between formally separate files. They also allows the compiler to inline
more and reduce the load on the linker (and thus link times).
The downside to unity files is they increase compilation times for small changes and often lead to implicit dependencies appearing between files. As Recode has to analyse and compile the entire compiland, naively compiling the original unity file can lead to much longer Recode times. Recode supports splitting unity files to help reduce this overhead.