
Merge multiple functions into one. An extra formal argument is added
to allow call sites to call any of the functions.
This transformation is useful as a precursor to virtualization or jitting:
if you want to virtualize both foo and bar,
first merge them together, then virtualize the result.
The transformation merges the argument list and the local variables of the functions, thereby tying them together.
Merging relies on the Flatten transformation, and has the same sources of diversity as it.
There are several ways to merge. In a simple merge, the function bodies
are simply put in an if-nest. This is simplistic, of course, but sufficient
if you are going to, say, virtualize or jit the merged function. If you set
--MergeFlatten=true then constituent functions are first
flattened, then the resulting blocks are merged together, and finally
a dispatch method is added (switch, goto, or indirect, selected by
--MergeFlattenDispatch).
The merged function is named
prefix ^ fun1 ^ "_" ^ fun2 ^ "_" ^ ...
where ^ is concatenation.
It is a good idea to run a --Trandform=RndArgs transformation after
this one to hide the obvious extra argument that's been added to
the function.
| Option | Arguments | Description |
|---|---|---|
| --Transform | Merge | Merge of two or more functions. Two different types of merge are supported: simple merge (if () function1 else if () function2 else ...) and flatten merge, where the functions are first flattened, and then the resulting blocks are woven together. This transformation modifies the signature of the function (an extra formal selector argument is added that selects between the constituent functions at runtime), and this cannot be done for functions whose address is taken. --Functions=\* merges together all functions in the program whose signatures can be changed, --Functions=%50 merges together about half of them, etc. It is a good idea to follow this transform by a RndArgs transform to hide the extra selector argument. |
| --MergeName | string | If set, the merged function will be named prefix_name, otherwise it will be named prefix_originalName1_originalName2. Note that it's unpredictable which function will be the first and the second, so it's better to set the merged named explicitly. |
| --MergeObfuscateSelect | BOOLSPEC | Whether the extra parameter passed to the merged function should be obfuscated with opaque expressions or not. Default=false. |
| --MergeOpaqueStructs | Deprecated spelling of --MergeOpaqueInvariantKinds Kept for compatibility. Replace --MergeOpaqueStructs=list,array with --MergeOpaqueInvariantKinds=structure_state. Replace --MergeOpaqueStructs=env with --MergeOpaqueInvariantKinds=environment. The input kind was removed in 4.1. Default=The kinds specified at InitOpaque.. | |
| --MergeFlatten | BOOLSPEC | Whether to flatten before merging or not. Default=true. |
| --MergeFlattenDispatch | switch, goto, indirect, ? | Dispatch method used for flattened merge. Default=switch.
|
| --MergeSplitBasicBlocks | BOOLSPEC | If true, then basic blocks (sequences of assignment and call statements without intervening branches) will be split up into indiviual blocks prior to merging. Default=false. |
| --MergeRandomizeBlocks | BOOLSPEC | If true, then basic block sequences will be randomized. Default=false. |
| --MergeConditionalKinds | branch, compute, flag | If merging before flattening, this option describes ways to transform conditional branches. Default=branch.
|
| --MergeOpaqueInvariantKinds | identity, modular, mba, structure_state, scalar_state, environment, plugin, * | Comma-separated list of the kinds of opaque invariant merged code may draw from, named by the hardness family they rest on. Constrained to the kinds set up by --InitOpaqueInvariantKinds.
|
| --MergeOpaqueInvariantResilience | trivial, local, global, interprocedural, inter_process, * | Comma-separated list of the resilience levels merged code may draw from -- the scope of analysis an attacker must run to decide the invariant. Constrained to the levels set up by --InitOpaqueInvariantResilience.
|
| --MergeOpaqueMaxSize | INTSPEC | The largest number of sub-opaques one opaque may be composed of. See --AddOpaqueMaxSize. |
| --MergeOpaqueSelect | (Deity mode only.) Comma-separated list of exact invariant names to draw from, bypassing the kind and resilience selection entirely. Names come from --InitOpaqueList. Silently inert unless the deity key is supplied with --DeityMode. The named invariants must have been set up by InitOpaque, so pair this with a permissive --InitOpaqueInvariantKinds/--InitOpaqueInvariantResilience. |
comp-goto-1.c torture test:
goto *(base_addr + insn.f1.offset);
--Transform=Merge --MergeFlatten=true.
--MergeConditionalKinds=flag option seems to have
multiple issues on MacOS/clang. Presumably this is due to some compiler
problem related to inline assembly.