PatchMaker troubleshooting guide
The PatchMaker is a powerful tool that can be used to inject code in an existing binary, using assembly or C source code. The PatchMaker uses a selected toolchain to compile/assemble source code/assembly into bytes, to be injected in the target binary.
During this process, it is possible to encounter assembler/compiler/linker errors in the corresponding OFRAK log. This troubleshooting guide describes common errors and how to address them.
Missing function definition and symbol
Error:
/path/to/linker-ld: /path/to/obj_file.c.o: in function `function_name':
/path/to/source.c:39: undefined reference to `printf'
- find the address of that function in the binary, for example through reverse engineering
- define the function's prototype, for example:
extern void printf(const char *restrict format, ...);
- define a symbol for the function, then pass it to the PatchMaker in the
base_symbols
field, for example like this:patch_maker = PatchMaker([...], base_symbols=["printf": 0x1234],)
Missing compiler builtins or libraries
Users might want to use math operators in C code, like divide or modulo operators, which may rely on compiler builtins. Users might also want to use commonly used standard functions which come from libraries.
In this section we take the example of math operators.
Error:
/path/to/linker-ld: /path/to/obj_file.c.o: in function `function_name':
/path/to/source.c:14: undefined reference to `__aeabi_uidivmod'
__aeabi_uidivmod
or __aeabi_idivmod
if compiling
with GCC or __umodsi3
if compiling with Clang.
To address this, find the function in the binary and define a symbol for it, then pass it to the PatchMaker in the base_symbols
field. Note that in the math operators case, there is no need to define the function's prototype like in the above section, as those types of functions are compiler builtins.
Note that the PatchMaker implementation doesn't yet allow linking against libraries.
Multiple symbol definitions
Error:
/path/to/linker-ld: /path/to/obj_file.c.o: in function `function_name':
(.text+0x0): multiple definition of `function_name'; /path/to/obj_file.c.o:/path/to/source.c:26: first defined here
base_symbols
field. In case such as these, the symbol should only be defined in the source, and
not passed to the PatchMaker.
Patch doesn't fit in patch range
Error:
/path/to/linker-ld: address 0xabcd of /path/to/FEM_executable section `.rbs_function_name_text' is not within region `.rbs_function_name_text_mem'
# or:
/path/to/linker-ld: /path/to/FEM_executable section `.text' will not fit in region `.rbs_name_text_mem'
# or:
/path/to/linker-ld: region `.region_name_text_mem' overflowed by 2 bytes
segments
field of
the PatchRegionConfig
).
There are three approaches for addressing this:
- if it's not already the case, use
CompilerOptimizationLevel.SPACE,
for thecompiler_optimization_level
field of theToolchainConfig
. - create a bigger segment for the patch, if additional space is available
- consider extending the binary to make as much space as needed for the patch. An example is OFRAK example 7.
Use of read-only data
Error:
/path/to/linker-ld: /path/to/FEM_executable section `.rodata' will not fit in region `.rbs_region_name_text_mem'
A common case for this is using strings. If the target string is already available in the binary,
consider using a pointer to it instead of re-defining the string. The string can be defined like
this: extern char string1;
, and then used like this: printf (&string1);
. Finally, a symbol must be
defined for it and passed to the PatchMaker
in the base_symbols
field.
Alternatively, a read-only segment part of the segments
field of the PatchRegionConfig
can be
defined. Note that additional space will be required for that segment.
Use of global variables
Error:
ofrak_patch_maker.model.PatchMakerException: .bss found but `no_bss_section` is set in the provided ToolchainConfig!
no_bss_section=True
is set in the ToolchainConfig
, but the code still
employs uninitialized data. This is usually a result of one or more uninitialized global variable.
If that global variable was already present in the binary and needs to be reused it, a user can
find its address, provide it as a symbol to the PatchMaker
in the base_symbols
field, and define
it in the C code as extern: extern uint8_t variable_name;
.
Alternatively, a .bss
segment can be defined, and passed to make_fem
in the unsafe_bss_segment
argument. Note that additional space will be required for that segment.
Another error can be:
/path/to/linker-ld: warning: start of section .igot.plt changed by 1
/path/to/linker-ld: /path/to/FEM_executable section `.data' will not fit in region `.rbs_region_name_data_mem'
.data
readable and writable (RW)
section. There are two approaches for addressing this:
- reuse the variable in the binary, if present, as described above.
- define a read-write segment part of the
segments
field of thePatchRegionConfig
. Note that additional space will be required for that segment.
Linking to existing global variables in the target binary
Error:
/path/to/linker-ld: error: no memory region specified for section '.rel.dyn'
.got
section, and the offsets of those pointers are stored in the .rel.dyn
section.
The code using the variable is expected to load an address from an offset in this table, and the dynamic linker is expected to fix up this table at runtime to have accurate pointers.
When injecting a patch, we usually don't want to have to deal with a Global Offset Table since, in the best-case scenario, it would mean finding and appending to the table already in the target binary, which is complicated.
A good workaround is to use __attribute__((weak))
instead of extern
when declaring the global variable in the patch source code, and defining a strong symbol for the variable at the address in the target binary.
Contrary to extern
, declaring a symbol as "weak" will count as a definition so that the BOM can be built without a Global Offset Table to resolve the pointer to some outside data.
But, since a "weak" definition can still be overruled by a "strong" definition of the variable elsewhere, you can define the variable at the correct address when building the FEM and the linker will go fix the usage of that variable to point at the correct address.
If using the symbol stub generation provided by OFRAK's LinkableBinary
(indirectly in PatchFromSourceModifier
or FunctionReplacementModifier
), the stub symbols are already strong definitions, so just use __attribute__((weak))
in your patch source.
Linker errors
In the case of linker errors, helpful troubleshooting approaches are:
- find the linker command line in the OFRAK log, then:
- inspect the generated linker script (
-T/path/to/linker_script.ld
in the command line). Check if the addresses, sizes, alignments match the expected values. - inspect the map file (
-Map /path/to/map_file.map
in the command line). Check if things land where they're expected to, compared to the linker script. - inspect the linker options to check if they are the expected ones
- inspect the generated linker script (
If anything strange stands up during the above inspection, use the toolchains directly to diagnose what the PatchMaker needs (fixups in the linker script generation, change in the command line arguments, etc.), and then fix it in the PatchMaker setup/usage.
General troubleshooting tips:
- the standalone assembler/compiler/linker commands can be run on the command line manually while experimenting, avoiding running the full OFRAK script
- different toolchains will handle compilation differently, and unless prohibited by the use case, it may be helpful to change the employed toolchain to further triage the error (or even get past it)