> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/angr/angrop/llms.txt
> Use this file to discover all available pages before exploring further.

# FuncCaller

> Function calling chain builder for invoking functions with automatic calling convention handling

The `FuncCaller` class builds ROP chains that call functions with arguments, automatically handling the target platform's calling convention.

## Overview

Accessed through the ROP instance as `rop.func_call()`, FuncCaller automatically:

* Detects the calling convention (System V, Windows x64, ARM AAPCS, etc.)
* Sets register arguments correctly
* Handles stack arguments when needed
* Manages return address to maintain control flow
* Supports both returning and non-returning calls

## Class Definition

```python theme={null}
class FuncCaller(Builder)
```

Located in `angrop/chain_builder/func_caller.py`

## Public Method

### func\_call

```python theme={null}
func_call(address, args, preserve_regs=None, needs_return=True) -> RopChain
```

Builds a ROP chain that calls a function with arguments.

<ParamField path="address" type="int | str" required>
  Address or symbol name of the function to call. Can be:

  * Integer address: `0x400123`
  * Symbol name: `"execve"`
  * PLT entry: `"printf"`
</ParamField>

<ParamField path="args" type="list | tuple" required>
  List of arguments to pass to the function. Arguments are mapped to registers/stack based on calling convention.
</ParamField>

<ParamField path="preserve_regs" type="set | None" default="None">
  Set of register names that should not be modified.
</ParamField>

<ParamField path="needs_return" type="bool" default="True">
  Whether the ROP chain should continue after the function returns. If False, generates a shorter chain but loses control flow.
</ParamField>

**Returns:** A `RopChain` that invokes the function.

**Raises:** `RopException` if function cannot be called.

## ROP Instance Method

```python theme={null}
rop.func_call(address, args, preserve_regs=None, needs_return=True)
```

## Implementation Details

### Calling Convention Detection

FuncCaller automatically detects the calling convention:

From source code (func\_caller.py:28-32):

```python theme={null}
self._cc = angr.default_cc(
    self.project.arch.name,
    platform=self.project.simos.name if self.project.simos is not None else None
)(self.project.arch)
```

Supported conventions:

* **x86\_64 Linux/BSD**: System V AMD64 ABI (rdi, rsi, rdx, rcx, r8, r9, stack)
* **x86\_64 Windows**: Microsoft x64 (rcx, rdx, r8, r9, stack)
* **x86 (32-bit)**: cdecl (all stack), fastcall, stdcall
* **ARM**: AAPCS (r0-r3, stack)
* **ARM64**: AAPCS64 (x0-x7, stack)
* **MIPS**: O32, N32, N64

### Argument Handling

#### Register Arguments

From source code (func\_caller.py:115-131):

```python theme={null}
# Distinguish register and stack arguments
register_arguments = args
stack_arguments = []
if len(args) > len(cc.ARG_REGS):
    register_arguments = args[:len(cc.ARG_REGS)]
    stack_arguments = args[len(cc.ARG_REGS):]

# Set register arguments
registers = {}
for arg, reg in zip(register_arguments, cc.ARG_REGS):
    registers[reg] = arg

chain = self.chain_builder.set_regs(**registers, preserve_regs=preserve_regs)
```

#### Stack Arguments

From source code (func\_caller.py:156-166):

```python theme={null}
if stack_arguments:
    shift_bytes = (len(stack_arguments)+1)*arch_bytes
    cleaner = self.chain_builder.shift(shift_bytes, next_pc_idx=-1, 
                                       preserve_regs=preserve_regs)
    chain.add_gadget(cleaner._gadgets[0])
    for arg in stack_arguments:
        chain.add_value(arg)
    next_pc = claripy.BVS("next_pc", self.project.arch.bits)
    chain.add_value(next_pc)
```

### Return Address Handling

Different calling conventions handle returns differently:

#### Stack-based Return (x86, x86\_64, ARM)

```asm theme={null}
call function  ; Return address pushed to stack
ret            ; Pop return address and jump
```

#### Register-based Return (ARM64, MIPS)

```asm theme={null}
bl function    ; Return address in link register (lr/ra)
ret            ; Jump to link register
```

From source code (func\_caller.py:168-178):

```python theme={null}
if isinstance(cc.RETURN_ADDR, SimRegArg) and cc.RETURN_ADDR.reg_name != 'ip_at_syscall':
    # Set return register before call
    reg_name = cc.RETURN_ADDR.reg_name
    shifter = self.chain_builder._shifter.shift(self.project.arch.bytes)
    next_ip = rop_utils.cast_rop_value(shifter._gadgets[0].addr, self.project)
    pre_chain = self.chain_builder.set_regs(**{reg_name: next_ip})
    chain = pre_chain + chain
```

### Symbol Resolution

From source code (func\_caller.py:189-199):

```python theme={null}
if isinstance(address, str):
    symbol = address
    symobj = self.project.loader.main_object.get_symbol(symbol)
    if hasattr(self.project.loader.main_object, 'plt') and address in plt:
        address = plt[symbol]  # Use PLT entry
    elif symobj is not None:
        address = symobj.rebased_addr  # Use actual symbol
    else:
        raise RopException("Symbol does not exist")
```

## Usage Examples

### Basic Function Call

```python theme={null}
import angr
import angrop

proj = angr.Project("/bin/bash")
rop = proj.analyses.ROP()
rop.find_gadgets()

# Call printf with one argument
chain = rop.func_call("printf", [0x400123])  # Address of format string
```

### Multiple Arguments

```python theme={null}
# Call function with 3 arguments
# Convention: rdi, rsi, rdx on x86_64
chain = rop.func_call("read", [0, 0x61b100, 0x100])
```

### Using Symbol Names

```python theme={null}
# Call by symbol name
chain = rop.func_call("execve", [0x61b100, 0, 0])

# Call PLT entry
chain = rop.func_call("printf", [format_str_addr])
```

### Calling with Return Value

```python theme={null}
# Call function and use return value (in rax)
chain = rop.func_call("malloc", [0x100])
chain += rop.move_regs(rdi='rax')  # Move return to rdi
chain += rop.func_call("free", [], preserve_regs={'rdi'})
```

### Non-Returning Calls

```python theme={null}
# Shorter chain, doesn't return
chain = rop.func_call("exit", [0], needs_return=False)

# Or execve - never returns anyway
chain = rop.func_call("execve", [0x61b100, 0, 0], needs_return=False)
```

### Preserving Registers

```python theme={null}
# Don't modify rax during setup
chain = rop.func_call("some_func", [0x123, 0x456], 
                     preserve_regs={'rax'})
```

### File Operations Example

```python theme={null}
import os

# Write "/etc/passwd" to memory
chain = rop.write_to_mem(0x61b100, b"/etc/passwd\x00")

# Open file
chain += rop.func_call("open", [0x61b100, os.O_RDONLY])

# Read into buffer (fd in rax)
chain += rop.move_regs(rdi='rax')
chain += rop.func_call("read", [], preserve_regs={'rdi'})
chain += rop.set_regs(rsi=0x61b200, rdx=0x100, preserve_regs={'rdi'})

# Write to stdout
chain += rop.func_call("write", [1, 0x61b200, 0x100])
```

### Stack Arguments (7+ args on x86\_64)

```python theme={null}
# Function with 10 arguments
# First 6 in registers (rdi, rsi, rdx, rcx, r8, r9)
# Last 4 on stack
args = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
chain = rop.func_call("big_function", args)
```

### Kernel Function Calls

From angrop's kernel test suite:

```python theme={null}
proj = angr.Project("./vmlinux_sym")
rop = proj.analyses.ROP(kernel_mode=True)
rop.find_gadgets()

init_cred = 0xffffffff8368b220
init_nsproxy = 0xffffffff8368ad00

# Call kernel function
chain = rop.func_call("find_task_by_vpid", [1])

# Use return value
chain += rop.move_regs(rdi='rax')
chain += rop.set_regs(rsi=init_nsproxy, preserve_regs={'rdi'})
chain += rop.func_call("switch_task_namespaces", [], 
                       preserve_regs={'rdi', 'rsi'})
```

## Advanced Features

### GOT/PLT Resolution

FuncCaller searches for function pointers:

From source code (func\_caller.py:48-72):

```python theme={null}
def _find_function_pointer_in_got_plt(self, func_addr):
    # Search symbols
    for sym in self.project.loader.main_object.symbols:
        if sym.rebased_addr == func_addr:
            func_name = sym.name
    
    # Search PLT
    for sym, val in self.project.loader.main_object.plt.items():
        if val == func_addr:
            func_name = sym
    
    # Return GOT entry if found
    if func_name:
        func_got = self.project.loader.main_object.imports.get(func_name)
        if func_got:
            return func_got.rebased_addr
```

### Indirect Calls (jmp\_mem)

When direct calls aren't possible, FuncCaller uses indirect jumps:

```asm theme={null}
; Instead of: call rax
; Use: jmp qword ptr [address_of_function_pointer]
```

From source code (func\_caller.py:223-258):

```python theme={null}
ptr_to_func = self._find_function_pointer(address)
if ptr_to_func is not None:
    for g in self._func_jmp_gadgets:
        if g.transit_type != 'jmp_mem':
            continue
        # Build registers and use jmp_mem_target
        return self._func_call(g, self._cc, [], 
                              extra_regs=registers,
                              jmp_mem_target=ptr_to_func, **kwargs)
```

## Calling Convention Details

### x86\_64 System V

```python theme={null}
# Arguments: rdi, rsi, rdx, rcx, r8, r9, stack
# Return: rax
# Caller-saved: rax, rcx, rdx, rsi, rdi, r8-r11
# Callee-saved: rbx, rsp, rbp, r12-r15

chain = rop.func_call("func", [arg1, arg2, arg3])
# rdi = arg1, rsi = arg2, rdx = arg3
```

### x86 cdecl

```python theme={null}
# Arguments: all on stack (right-to-left)
# Return: eax
# Caller cleans stack

chain = rop.func_call("func", [arg1, arg2])
# Stack: [ret_addr][arg1][arg2]
```

### ARM AAPCS

```python theme={null}
# Arguments: r0, r1, r2, r3, stack
# Return: r0
# Link register: lr

chain = rop.func_call("func", [arg1, arg2])
# r0 = arg1, r1 = arg2
```

## Error Handling

### "fail to invoke function: {symbol}"

Raised when function cannot be called.

**Solutions:**

1. Ensure `find_gadgets(optimize=True)` was called
2. Check if symbol exists: `proj.loader.find_symbol(name)`
3. Try `needs_return=False` for simpler chain
4. Verify function address is correct

### "Symbol does not exist in the binary"

Raised when symbol name is invalid.

**Solution:** Use correct symbol name or address.

### "fail to invoke function and return"

Raised when return mechanism cannot be built.

**Solution:** Use `needs_return=False` or ensure ROP has necessary gadgets.

## Performance Considerations

* Simple calls (self-contained) are faster
* Complex calls may need multiple gadgets
* Stack arguments add overhead
* Return handling adds complexity

## Architecture Support

* **x86/x86\_64**: Full support
* **ARM/ARM64**: Full support
* **MIPS**: Full support
* **PowerPC**: Basic support

## See Also

* [SysCaller](/api/sys-caller) - System call invocation
* [Function Calls Guide](/guides/function-calls) - Usage examples and patterns
* [RegSetter](/api/reg-setter) - Setting function arguments
