frida-pharo

Description

Frida bindings for Pharo/Smalltalk

Details

Source
GitHub
Dialect
pharo (25% confidence)
License
MIT
Stars
1
Created
July 16, 2026
Updated
July 20, 2026

README excerpt

# frida-pharo

Pharo/Smalltalk bindings for [Frida](https://frida.re), the dynamic
instrumentation toolkit.

The binding is **auto-generated** from Frida's GObject-Introspection metadata
(`Frida-1.0.gir`) by the shared `frida_bindgen_core` generator, and vendored as
[Tonel](https://github.com/pharo-vcs/tonel) source under `src/FridaPharo/`. At
runtime it talks to a hand-linked `libfrida-core` shared library through Pharo's
uFFI. There is no C glue: Frida is initialised with the GLib runtime and its
GMainContext is driven directly from Smalltalk (`FridaMainLoop`), so async
completions and signals dispatch on the Pharo thread.

## Loading the bindings

The generated Tonel sources load into any Pharo 11+ image via Metacello,
straight from GitHub:

```smalltalk
Metacello new
	baseline: 'FridaPharo';
	repository: 'github://frida/frida-pharo:main';
	load.
```

Nothing to compile: on first use, `FridaLibrary` downloads the prebuilt
`libfrida-core` for the current platform and pinned frida version from the
GitHub release, verifies it against `SHA256SUMS`, and caches it next to the
image. So `Frida localDevice` just works.

To use a local build instead (contributors, or an unreleased frida version),
set the `FRIDA_CORE_LIB` environment variable to its path — it takes
precedence over the download.

## Usage

```smalltalk
"Enumerate the processes running on the local device."
device := Frida localDevice.
(device enumerateProcesses: nil) do: [ :process |
	Transcript showLn: process pid printString, ' ', process name ].

"Attach, inject a script, and receive its messages."
session := device attach: (device getProcessByName: 'Twitter') pid.
script := session createScript: 'Interceptor.attach(Module.getExportByName(null, "open"), {
	onEnter(args) { send(args[0].readUtf8String()); }
});'.
script onMessage: [ :message | Transcript showLn: message ].
script load.

"Call RPC exports exposed by the script via rpc.exports."
result := script exports add: 3 to: 4.

"Subscribe to any GObject signal; the block runs on the Pharo thread."
session on: 'detached' do: [ :args | Transcript showLn: 'detached: ', args printString ].

"Spawn, instrument at start-up, then resume."
pid := device spawn: '/bin/ls'.
session := device attach: pid.

"Options are set with a configuration block."
pid := device spawn: '/bin/sh' options: [ :o |
	o argv: #('/bin/sh' '-c' 'echo hi').
	o cwd: '/tmp' ].
"... createScript / load ..."
device resume: pid.
```

Async Frida operations (`attach:`, `spawn:`, `enumerateProcesses:`, ...) are
exposed as ordinary synchronous-looking Pharo methods: the call is scheduled on
the GMainContext that `FridaMainLoop` drives, and the calling Pharo process
blocks on a semaphore until the loop's dispatch delivers the result — on the
Pharo thread — or raises a `FridaError`.

## Building from source

Only needed for development — end users get a prebuilt library automatically
(see [Loading the bindings](#loading-the-bindings)). The prebuilts themselves are
produced by the `Release prebuilt libraries` workflow, which links the same
`tools/build-lib.sh` against frida-core's published devkit for each platform.

`make all` performs the full loop end to end:

1. **lib** — `tools/build-lib.sh` links a frida-core devkit's single
   self-contained archive into one uFFI-loadable shared library (whole-archive +
   the glib symbol aliasing, limited to the exports the binding imports, then
   dead-stripped). Pass `DEVKIT=<extracted devkit dir>` — a published devkit, or
   one built from a frida-core tree with `--with-devkits=core` when testing
   unreleased changes.
2. **generate** — runs the Python generator over the `.gir` files, refreshing the
   Tonel sources under `src/FridaPharo/` (needs a frida-core build tree for the
   GObject `.gir` set; `FRIDA_CORE`/`FRIDA_MACHINE`).
3. **image** — loads the baseline into a fresh Pharo image.
4. **test** — runs the SUnit suite headless.

Override the defaults on the command line as needed, e.g.:

```sh
make lib DEVKIT=/path/to/frida-core-devkit
make generate FRIDA_CORE=/path/to/frida-core FRIDA_MACHINE=linux-x86_64
```

## Layout

| Path                     | What it is                                             |
| ------------------------ | ------------------------------------------------------ |
| `frida_pharo/`           | The generator: thin model subclasses, data-only `customization.py`, type-agnostic `codegen.py`. |
| `frida-bindgen/`         | The shared `frida_bindgen_core` generator (submodule). |
| `src/FridaPharo/`        | Vendored generated Tonel sources + the hand-written runtime base classes (`FridaObject`, `FridaMainLoop`, `FridaSignalSubscription`, `FridaVariant`, ...). |
| `src/FridaPharo-Tests/`  | The SUnit suite.                                        |
| `tools/build-lib.sh`   | Reproducible frida-core static-to-shared link step.    |
← Back to results