This chapter explains the principles and peculiarities of creating patches manually with the new API.
The previous API drew a strict line between two patch creation modes: automatic with the fullest possible support for binding symbols in patch and manual that only allowed binding symbols with limited scope in patch and target in the simplest of cases. The new API expands the manual mode to the level of automatic, essentially removing the strict division between modes. In particular, the new API allows you to unambiguosly define the following in patches:
- links to target symbols for all types of symbols and scopes,
- mutable functions depending on scope.
Patches must be built as follows:
- Without compiler optimizations i.e. with the parameter
-O0
. This is required to correctly and unambiguously bind symbols between the patch and target. - With the parameter
-g
to create debug info required by the generator to map the patch and target. - With the parameter
-Wimplicit-function-declaration
that requires defining a prototype of external function or variable to access. This will eliminate attempts to use instrumentation without including required header files.
The generator allows using two classes of symbols:
- links (to target symbols, e.g., functions or variables),
- functions (e.g., fixed or new code).
Symbols, in turn, can be:
- global,
- with a limited scope.
To correctly bind fixed functions and links in patch with the target, it is generally required to define a binding
context so that a symbol can be found. The binding context is defined by patch instrumentation i.e. macros
defined in the header file nsb/vzp.h
.
The generator only needs binding context for static symbols as the context describes which target source file defines the code and links used in the patch. Otherwise binding context is unnecessary.
The following macro allows you to define binding context:
VZP_FILE("path_to_file");
where path_to_file is the path to target file with mappings to patch, starting from project root.
Have in mind that:
- The patch generator requires a path (string), not the file itself. In other words, project source files are not required.
- The macro defines binding context for code that follows context definition.
- Binding context must be defined for patches that fix functions with a limited scope.
- Prototype of the fixed function must match that of the target function. This helps avoid errors during patch creation. If the prototypes do not match, the patch generator returns an error.
Global links do not require any special instrumentation. If you need to link to a target’s global variable in a patch, follow the programming language semantics. For example:
extern int x;
int printf(const char *format, ...);
Have in mind that:
- Instrumentation methods described in this guide (including binding context) do not affect global links.
- Global links are bound when patch loads.
Links to symbols with limited scope require instrumentation for these symbols to be found. There are several types of symbols with limited scope:
- Static symbols.
- Symbols with deliberately limited scope:
- hidden, e.g.,
(__attribute__ ((visibility ("hidden"))))
, - protected, e.g.,
(__attribute__ ((visibility ("protected"))))
, - internal, e.g.,
(__attribute__ ((visibility ("internal"))))
.
- hidden, e.g.,
In general, file context is not required. It will be necessary, for example, if there are two static variables of the same name in different target source files.
You can define file context in one of two ways:
- in general, by means of binding context described above, or
- for a specific symbol, in which case the file context will only affect binding of said symbol and have priority over the binding context.
For each type of link to variables with limited scope described earlier, two macros exist: for linking to functions and variables. Macros for links to variables:
- VZP_STATIC_VAR_REF, link to a static variable,
- VZP_HIDDEN_VAR_REF, link to a hidden variable,
- VZP_PROTECTED_VAR_REF, link to a protected variable,
- VZP_INTERNAL_VAR_REF, link to an internal variable.
Macros for links to functions:
- VZP_STATIC_FUNC_REF, link to a static function,
- VZP_HIDDEN_FUNC_REF, link to a hidden function,
- VZP_PROTECTED_FUNC_REF, link to a protected function,
- VZP_INTERNAL_FUNC_REF, link to an internal function.
Macro prototypes are the same for all link types (except for the parameter file that is used only for static symbols).
A macro prototype for a link to a variable is as follows:
VZP_{VISIBILITY}_VAR_REF(type, name[, file])
where
- type is variable type,
- name is variable name,
- file is the name of file to search for the variable in the target. This parameter is only used for static symbols and is optional if you have already defined VZP_FILE or symbol name is unique in the target.
A macro prototype for a link to a function is as follows:
VZP_{VISIBILITY}_FUNC_REF(type, name, (args...), [, file])
where
- type is the function return type,
- name is the function name,
- (args...) is the list of function arguments in parenthesis,
- file is the name of file to search for the function in the target. This parameter is only used for static symbols and is optional if you have already defined VZP_FILE or symbol name is unique in the target. If file is present in link definition, it has priority over VZP_FILE (if defined).
When you create a patch, you may need to modify two functions that need to link to different static variables that have the same name. For example, a target may contain code like this:
int f(void)
{
static int x;
return x++;
}
int g(void)
{
static int x;
return x--;
}
Attempting to patch it as shown below will result in name conflict during compilation:
int f(void)
{
VZP_STATIC_VAR_REF(int, x);
return x++;
}
int g(void)
{
VZP_STATIC_VAR_REF(int, x);
return x--;
}
The reason for conflict of names is the architectural limitation of links to variables defined by the macros VZP_STATIC_VAR_REF and VZP_STATIC_FUNC_REF. When either macro is used, a special service variable is created with a name derived from the name of the original static variable. This results in a compilation error.
The name conflict may also be a result of changing multiple static functions of the same name. However, in this case you can work around the problem by defining same-name functions in different source files of the patch. However, this workaround will not work with variables, because, an external link with the name of the variable is created in addition to a service variable. And an external link to a symbol must be unique. As a result, after patch is built, the code will feature a single link to two different variables, preventing the patch from being bound.
To resolve a name conflict, make variable names unique any way you like.
For example, if you need two links to different variables with the same name var, rename one of them var_2 and use this name wherever the variable is used in the patch.
Such an approach will let you build the patch. However, after a renaming alone, the patch generator may fail to bind the renamed variable to the target or, even worse, may bind it to a wrong variable of the same name.
To avoid this problem, the patch generator must be made aware that the variable var_2 is the link to the variable var. This can be done withe the following macro:
VZP_STATIC_SYM_ALIAS(patch_name, target_name);
where patch_name is renamed symbol in the patch and target_name is the symbol in the target.
Have in mind that the macro that creates a link to a symbol in the target always creates a global link and a static service variable. It means that an external global variable and a link to a static variable of the same name cannot be defined in the same file.
Thie chapter gives examples of using the new API to build patches manually.
All examples demonstrate cases whem symbols with a limited scope are defined in the target in the file
foo/bar.c
. Have in mind that the path to the file is relative to target’s built root (usually, the project root).
#include <nsb/vzp.h>
VZP_FILE("foo/bar.c");
static int static_function(int x)
{
return x + 5;
}
#include <nsb/vzp.h>
VZP_FILE("foo/bar.c");
VZP_STATIC_VAR_REF(int, static_var);
int some_function(int a)
{
return a + static_var;
}
Alternatively:
#include <nsb/vzp.h>
VZP_STATIC_VAR_REF(int, static_var, "foo/bar.c");
int some_function(int a)
{
return a + static_var;
}
#include <nsb/vzp.h>
VZP_FILE("foo/bar.c");
VZP_STATIC_FUNC_REF(int, static_func, (int));
int some_function(int a)
{
return a + static_func(a);
}
Alternatively:
#include <nsb/vzp.h>
VZP_STATIC_FUNC_REF(int, static_func, (int), "foo/bar.c");
int some_function(int a)
{
return a + static_func(a);
}
2.2 Example of a Link to a Hidden Symbol
2.2.1 Defining context for the fixed version of function hidden_function:
__attribute__ ((visibility ("hidden"))) int hidden_function(int x)
{
return x + 5;
}
2.2.2 Link to the variable hidden_var:
#include <nsb/vzp.h>
VZP_HIDDEN_VAR_REF(int, hidden_var);
int some_function(int a)
{
return a + hidden_var;
}
2.2.3 Link to the function hidden_func:
#include <nsb/vzp.h>
VZP_HIDDEN_FUNC_REF(int, hidden_func, (int));
int some_function(int a)
{
return a + hidden_func(a);
}
__attribute__ ((visibility ("protected"))) int protected_function(int x)
{
return x + 5;
}
#include <nsb/vzp.h>
VZP_PROTECTED_VAR_REF(int, protected_var);
int some_function(int a)
{
return a + protected_var;
}
#include <nsb/vzp.h>
VZP_PROTECTED_FUNC_REF(int, protected_func, (int));
int some_function(int a)
{
return a + protected_func(a);
}
__attribute__ ((visibility ("internal"))) int internal_function(int x)
{
return x + 5;
}
#include <nsb/vzp.h>
VZP_INTERNAL_VAR_REF(int, internal_var);
int some_function(int a)
{
return a + internal_var;
}
#include <nsb/vzp.h>
VZP_INTERNAL_FUNC_REF(int, internal_func, (int));
int some_function(int a)
{
return a + internal_func(a);
}
#include <nsb/vzp.h>
int f(void)
{
VZP_STATIC_VAR_REF(int, var);
return var++;
}
int g(void)
{
VZP_STATIC_VAR_REF(int, var_2);
VZP_STATIC_SYM_ALIAS(var_2, var);
return var_2--;
}