---
title: 'NeuCall: Indirect Call Resolution'
url: https://www.emergentmind.com/topics/neucall
type: topic
---

# NeuCall: Indirect Call Resolution

NeuCall is an approach for resolving indirect call targets in stripped binaries by combining cross-reference augmented control-flow graphs with a relational graph convolutional network. It is designed for static binary code analysis in settings where source code is unavailable and where unresolved indirect calls would otherwise leave the inter-procedural control-flow graph incomplete. The method addresses two obstacles identified in prior ML-based work: low-quality callsite-callee training pairs and inadequate binary code representation. Its central contributions are the use of compiler-level type analysis to generate high-quality one-to-many indirect-call supervision and a heterogeneous graph representation that preserves both control flow and data/code cross-references, yielding an F1 score of 95.2% on real-world x86_64 binaries from GitHub and the Arch User Repository [2507.18801].

## 1. Indirect-call resolution as a static binary analysis problem

Indirect calls in stripped binaries arise when the target address of a call instruction, such as `call rax`, is computed at runtime rather than encoded directly in the instruction. Static analysis tools construct inter-procedural CFGs by connecting basic blocks across function calls; when an indirect callsite’s destination register or memory operand cannot be resolved statically, the CFG remains incomplete, obscuring true data-flow and control-flow relationships [2507.18801].

The underlying difficulty is not merely implementation complexity. The problem is described as undecidable in general: resolving all possible values held in a register or memory location at an indirect callsite is Turing-complete because it reduces to solving arbitrary program paths. This motivates approximations, but the paper positions existing approximations as exhibiting a characteristic precision-scalability tradeoff [2507.18801].

Prior static approaches are grouped into three broad categories. Value-set analysis over-approximates pointer ranges and may generate thousands of spurious targets, producing poor precision. Symbolic execution can enumerate actual targets but suffers path explosion, producing poor scalability. Type-based matching, including TypeArmor and TypeSqueezer, relies on argument-count and prototype heuristics, but these heuristics can under- or over-estimate types, producing low recall or low precision. Dynamic approaches such as hardware tracing, fuzzing, and concolic execution can record actual targets, yet coverage remains incomplete because rare paths may be missed, and instrumentation overhead is high [2507.18801].

NeuCall is framed as a response to limitations that persist even in recent ML-based systems such as Callee and AttnCall. Two deficiencies are emphasized. First, the training pairs used by earlier models may be incomplete or noisy. Second, binaries are often represented as linear token sequences, which discard control- and data-cross-reference semantics. The paper further notes that tokenizing addresses as special tokens such as `[addr]` or using limited loose dictionaries destroys cross-reference information. This suggests that the representational bottleneck is not only about local instruction semantics but also about preserving address-mediated program structure [2507.18801].

## 2. Cross-reference augmented CFG representation

NeuCall represents each binary as a heterogeneous graph that preserves both control flow and cross-references into and out of the code and data sections. The starting point is a base CFG obtained by disassembling the `.text` section to identify basic blocks and direct control-flow edges, including fall-through, conditional and unconditional jumps, and direct calls. Callsites are modeled as special control-flow edges `r_call` connecting a caller block to the callee’s entry block [2507.18801].

The representation is then augmented through symbolization and cross-reference extraction. The system uses angr’s symbolization to resolve immediate numeric addresses to labels in code and data sections. For each code-to-data reference, such as `mov rax, [0x401234]`, it adds an edge `r_c2d` from the referencing basic block `v_c` to a newly created data node `v_d`. For data-to-code references, such as function pointers stored in `.data`, it adds edges `r_d2c` from data node `v_d` to a basic-block node `v_c`. For code-to-code references outside the normal CFG, such as jump tables, it adds special edges `r_cc` between code nodes. Data-to-data edges `r_dd` are also included when one data item references another, although such edges are described as rare [2507.18801].

The formal graph is defined by
$$
V = V_c \cup V_d,
$$
where $V_c$ is the set of basic blocks and $V_d$ is the set of data-section objects. The relation set is
$$
R = \{r_t, r_{call}, r_{c2d}, r_{d2c}, r_{cc}, r_{dd}\},
$$
where `r_t` denotes default CFG edges for fall-through and direct jumps. Edges satisfy
$$
E \subseteq V \times R \times V,
$$
and an element $(v_i, r, v_j) \in E$ denotes a type-$r$ edge from $v_i$ to $v_j$. The augmented CFG is denoted
$$
G = (V, E, R)
$$
or, equivalently,
$$
G = (V, \{E_t, E_{call}, E_{c2d}, E_{d2c}, E_{cc}, E_{dd}\}).
$$
This construction is intended to preserve semantic information that standard CFG-only or token-sequence representations omit [2507.18801].

A notable implication of this design is that addresses are not treated merely as lexical items. They become graph structure, enabling the model to exploit relationships among callsites, code blocks, jump tables, and data-resident function pointers. The ablation results reported later in the paper are consistent with the view that these cross-references contribute materially to prediction quality.

## 3. Compiler-level supervision and training-pair generation

NeuCall avoids noisy dynamic traces by extracting ground-truth indirect-callsite-to-callee pairs at compile time using TyPro, an LLVM-CFI plugin that propagates type information forward. TyPro performs inter-procedural type propagation on function pointers and associates each indirect callsite with a refined set of callees whose argument and return types match the propagated types [2507.18801].

The training-pair generation workflow relies on source-level instrumentation and controlled linkage. Unique labels are inserted at each indirect callsite and function entry, after which the program is compiled and linked with a custom linker script that resolves labels to final binary addresses. The result is a one-to-many mapping from indirect callsite address to callee addresses. The paper characterizes this mapping as having high precision, with few false positives, and high recall, with few false negatives, because it leverages full IR-level type information rather than approximate heuristics [2507.18801].

The dataset assembled through this process comprises 2680 stripped binaries drawn from GitHub and the Arch User Repository, compiled at optimization levels `O0–O3` for the `x86_64` architecture. From these binaries, the authors extract 350K functions, 36K indirect calls, and validate 704K indirect-callsite–callee pairs [2507.18801].

The significance of this supervision pipeline is methodological. Earlier ML systems are criticized for low-quality training pairs; NeuCall instead constructs labels using compiler-level provenance before stripping and deployment. A plausible implication is that the reported performance gains depend not only on the graph encoder but also on the quality of the supervisory signal. The paper’s framing presents both components as necessary: clean labels for learning and a representation expressive enough to exploit them.

## 4. Relational graph neural architecture

NeuCall embeds the augmented CFG $G = (V, E, R)$ with a relational graph convolutional network. Each node $v \in V$ is assigned an initial feature $h_v^{(0)}$ assembled from several components. For basic-block nodes, instruction embeddings are obtained with PalmTree, described as BERT-based, which converts each basic block’s instructions into a fixed-length vector, with truncation at 70 instructions. Two scalar features—the normalized block address and the normalized function address—are appended to that instruction embedding. For data nodes, the initial feature is the normalized address of the data item. In addition, the model computes Laplacian positional encoding by taking the top-$k$ eigenvectors of the graph Laplacian, treating $G$ as undirected, and appending these vectors to $h_v^{(0)}$ so that each node carries positional information within the graph [2507.18801].

The layer-wise relational message-passing update for layer $\ell = 0 \ldots L-1$ is
$$
h_v^{(\ell+1)} = \sigma \left(
\sum_{r \in R} \sum_{u \in N_r(v)} \frac{1}{c_{v,r}} W_r^{(\ell)} h_u^{(\ell)}
+ W_0^{(\ell)} h_v^{(\ell)}
\right),
$$
where $R$ is the set of edge-relation types, $N_r(v)$ is the set of neighbors of $v$ connected via relation $r$, $c_{v,r} = |N_r(v)|$ is the normalization constant, $W_r^{(\ell)}$ are relation-specific weight matrices, $W_0^{(\ell)}$ is a self-loop weight, and $\sigma$ is ReLU or another nonlinear activation. Hyperparameter tuning found $L=3$ R-GCN layers to be optimal, after which each node has final embedding $h_v^{(L)}$ [2507.18801].

The design directly reflects the graph schema. Relation-specific parameters allow the model to distinguish, for example, ordinary control-flow edges from code-to-data references or jump-table-like code-to-code links. This suggests that NeuCall treats cross-reference semantics not as incidental context but as typed evidence relevant to call-target inference.

## 5. Pair scoring, loss, and inference procedure

Training is formulated as link prediction between indirect callsites and candidate function-entry nodes. For each binary, the set of indirect-callsite blocks is denoted
$$
S = \{v_i : \text{icallsite blocks}\},
$$
and the set of function-entry blocks is
$$
T = \{v_j : \text{function entry blocks}\}.
$$
Positive pairs $(v_i, v_t)$ are constructed for each true target $v_t$ obtained from TyPro, while an equal number of negative pairs $(v_i, v_f)$ are sampled from $T \setminus \text{true-targets}$ to balance the classes [2507.18801].

For each pair $(v_i, v_j)$, a two-layer MLP takes the concatenation $[h_{v_i} \parallel h_{v_j}]$ and outputs
$$
f_{call} \in [0,1],
$$
interpreted as the probability that $v_j$ is a callee of $v_i$. The loss is binary cross-entropy over positive and negative pairs:
$$
\ell = - \log f_{call}(h_{v_i}, h_{v_t})
      - \log \big(1 - f_{call}(h_{v_i}, h_{v_f})\big).
$$
The R-GCN and MLP are trained end-to-end with Adam, learning rate $1e^{-3}$, dropout $0.2$, and balanced sampling [2507.18801].

Inference proceeds by constructing the new binary’s augmented graph $G$, computing node embeddings for all nodes, and then scoring all address-taken function entries $v_j$ for a given indirect callsite $v_i$. Targets can be predicted either by ranking candidates according to $f_{call}(v_i, v_j)$ or by thresholding at $0.5$ [2507.18801].

A common misconception in indirect-call resolution is that once a good graph encoder is available, candidate generation and training supervision are secondary. NeuCall’s formulation does not support that view. The method combines a graph encoder, a callsite-callee pair classifier, and a compiler-derived labeling pipeline; the reported system is the composition of all three.

## 6. Empirical results, ablations, and downstream significance

Evaluation is performed on the 2680 stripped `x86_64` binaries described above, with train/validation/test partitioning at project granularity so that no project appears in more than one split. The split ratio is `80%/10%/10%`. The reported metrics are Precision, Recall, F1, and AUROC [2507.18801].

NeuCall achieves `F1 = 95.2%`, `Precision = 97.1%`, `Recall = 93.3%`, and `AUROC = 98.3%`. The state-of-the-art baseline is Callee, re-implemented and fine-tuned on the same data, which achieves `F1 = 89.9%`. The paper therefore presents NeuCall as outperforming prior ML-based indirect-call resolution on this benchmark [2507.18801].

The ablation studies isolate the role of graph augmentation and positional encoding. Using reverse edges only yields `F1 = 91.5%`. Using data nodes with `c2d+d2d` edges yields `F1 = 85.1%`. Using code-cross-reference edges only yields `F1 = 85.2%`. Using all cross-reference features but no positional encoding yields `F1 = 93.7%`. Adding Laplacian positional encoding restores performance to `F1 = 95.2%`, and the paper notes that this improves precision at slight recall cost [2507.18801]. These results suggest that neither code references nor data references alone suffice; the best performance arises from the full cross-reference augmentation together with positional structure.

The method is also reported to be robust across optimization levels, with F1 remaining at least `93.6%` across `O0–O3`. In a case study on binary-only CFI using the Average Indirect Call Targets metric, NeuCall’s predicted target sets are described as on average `80%` closer to LLVM-CFI’s precision than Callee’s sets, thereby reducing over-approximation [2507.18801].

The downstream applications enumerated in the paper follow from producing more complete inter-procedural CFGs. With high-precision indirect-call resolution, static analyses such as taint analysis, vulnerability scanning, and program slicing can become more sound because fewer edges are missing and more precise because fewer spurious edges are introduced. Correct call targets also support binary rewriting and recompilation tasks, including safe instrumentation, inline caching, and re-optimization of stripped binaries. In control-flow integrity, refined target sets enable stronger binary-only CFI policies with lower overhead and higher security. In malware analysis and reverse engineering, more accurate call graphs can accelerate deobfuscation, function matching, and vulnerability discovery [2507.18801].

Taken together, the reported evidence supports a specific interpretation of NeuCall’s contribution. It does not treat indirect-call resolution as a purely sequence-modeling problem or as a purely type-matching problem. Instead, it combines compiler-level type-propagated labels with a graph representation that explicitly encodes control flow and cross-references, and trains a relation-aware GNN to perform call-target prediction. Within the scope of the reported dataset and evaluation protocol, this combination is what yields the stated improvement over prior ML-based approaches [2507.18801].

Source: https://www.emergentmind.com/topics/neucall