Build Settings

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.

VC Toolsets

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

Important

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.

Compiler Settings

Recommended compiler option for Recode builds (best results with this set):

Incompatible compiler options for Recode builds (ensure these are disabled):

If /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 /MD or /MDd. The static version of the library (specified via /MT or /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 /MT or /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.

  • Structured and C++ exception handling (/EHa or /EHac)
    • 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.

Incompatible exception handling compiler options that will disable Recode Protect:

  • C++ exception handling only (/EHs or /EHsc)
    • Slow performance, particularity on x86.

    • Catch only standard C++ exceptions.

    • C++ exceptions will unwind the stack and call destructors.

Linker Settings

Required linker option for Recode builds (ensure this is set):

Recommended linker option for Recode builds (best results with this set):

Incompatible linker options for Recode builds (ensure these are disabled):

Warning

Any obj files compiled with /GL prevent incremental linking causing Recode to fail when patching. Third-party lib files may also cause this problem if they contain obj files compiled with /GL. See Whole Program Optimization for more details.

Hotpatching

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.

Tip

If incremental linking is disabled, use /hotpatch and /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:

_images/debug_src_different_dialog.png

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:

  • Unmodified.cpp Main()

    • Steps into…

      • Foo.cpp MyFunc() [old version - source no longer matches]

        • Steps into…

          • Foo.cpp 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 the /FUNCTIONPADMIN:16 linker option (32-bit & 64-bit).

Note

/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.

Incremental Linking

Using Incremental Linking (/INCREMENTAL) will provide an improved debugging experience since stepping into functions will be seamless.

Tip

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:

Warning

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 see error recode : conflicting global variable due to variables from the old lib conflicting with new stubs generated by Recode.

Tip

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:

_images/vs_lib_dependencies.png

Important

Visual Studio’s 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

Any lib/obj files that were compiled with Whole Program Optimization (/GL) enabled will cause linking to restart with Link-Time Code Generation (/LTCG) enabled with the message:

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.

Tip

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.

Unity Splitting

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.