Open-Source Wikis

/

Ruby

/

C extensions

ruby/ruby

C extensions

ext/ contains the C extensions that ship with CRuby — bindings to system libraries (sockets, OpenSSL, libyaml), platform features (PTY, fcntl), or performance-critical code (the JSON parser, digest algorithms). They use the mkmf (lib/mkmf.rb) convention: each extension has an extconf.rb that probes for headers/libraries and emits a Makefile, plus C/C++ source that calls the public CRuby API in include/ruby/.

Extension layout

A typical ext/<name>/ directory contains:

ext/<name>/
├── extconf.rb          # probes + emits Makefile
├── <name>.c (or many)  # the C source
├── <name>.h            # internal header
├── depend              # generated dependency list (regenerated by `make update-deps`)
└── <name>.gemspec      # gem metadata (for default gems)

For Ruby-side glue, default-gem extensions also have lib/<name>.rb or lib/<name>/<name>.rb.

Extensions in this repo

The ext/ directory has roughly 40 extensions. Highlights:

Networking and crypto

  • ext/socket/ — Berkeley sockets, Addrinfo, getaddrinfo. ~16,000 lines of C.
  • ext/openssl/ — TLS, hashing, public-key crypto, X.509. The biggest single extension.
  • ext/digest/ — SHA1/SHA2/MD5/Bcrypt with both pure-Ruby and OpenSSL-backed implementations.

Serialisation

  • ext/json/ — JSON parser and generator, with the high-performance "ext" implementation.
  • ext/psych/ — YAML 1.1 (libyaml binding).

IO

  • ext/io/console/ — terminal control, raw mode, getch.
  • ext/io/wait/IO#wait_readable / IO#wait_writable.
  • ext/io/nonblock/IO#nonblock=.
  • ext/stringio/StringIO, an in-memory IO.
  • ext/strscan/StringScanner, incremental string parsing.

Platform glue

  • ext/etc/Etc.passwd, Etc.group, Etc.sysconf.
  • ext/fcntl/fcntl(2) constants.
  • ext/pty/ — pseudo-terminal allocation.
  • ext/syslog/ (where built) — syslog(3) interface.
  • ext/zlib/ — compression bindings.

Diagnostics

  • ext/objspace/ObjectSpace.dump_all, count_objects, memsize_of, allocation tracing.
  • ext/coverage/ — code-coverage tool used by simplecov etc.

Parsers

  • ext/ripper/ — uses parse.y to expose tokens and parse events to Ruby code.
  • ext/prism/ — the Ruby gem façade for the Prism parser.

Internals

  • ext/-test-/ — unit tests for the public C API. Built only in make test-all.
  • ext/-ext-/ — additional API tests.
  • ext/fiber/ — Fiber pool and scheduler integration.
  • ext/mmtk/ — MMTk GC binding (when built with --with-gc=mmtk).
  • ext/date/Date and DateTime (a substantial C extension).

Anatomy of an extension

ext/digest/sha1/extconf.rb is short:

require_relative "../../digest/extconf"

ext/digest/extconf.rb (the parent) does the actual work:

require "mkmf"
$defs << "-DRUBY"
have_header("openssl/sha.h")
have_library("crypto")
create_makefile("digest/sha1")

have_header, have_library, have_func, try_compile are mkmf primitives that probe the build environment and append -D flags to $defs. create_makefile emits a Makefile based on the platform's compiler settings.

When make install runs:

  1. The base build invokes each ext/<name>/extconf.rb via tool/extmk.rb.
  2. The generated Makefile builds <name>.so (or .bundle/.dll).
  3. The .so is loaded by require "name" via dln_load (see systems/loader.md).
  4. The Init_<name> function runs, registering classes and methods.

The Init_xxx convention

Every CRuby extension has an entry point named Init_<name> (after the basename of the .so):

#include <ruby.h>

void
Init_mygem(void)
{
    VALUE m = rb_define_module("MyGem");
    rb_define_singleton_method(m, "hello", mygem_hello, 0);
}

dln.c::dln_load calls Init_mygem after dlopen returns. The function may register classes, modules, methods, constants, and global variables.

The public C API

Headers in include/ruby/ are the contract between CRuby and extensions:

Header Purpose
ruby.h Convenience top-level header
ruby/intern.h Function declarations (the bulk of the API)
ruby/internal/... Macros, type definitions, low-level details
ruby/encoding.h M17N API
ruby/io.h IO struct + IO API
ruby/thread.h rb_thread_call_without_gvl, rb_thread_check_ints
ruby/io/buffer.h IO::Buffer API
ruby/onigmo.h Embedded regex engine API
ruby/random.h rb_random_t API
ruby/util.h Utility functions (ruby_qsort, ruby_strdup)
ruby/version.h Version macros
ruby/vm.h Limited VM-level helpers

The headers are documented with Doxygen; the extension.rdoc document under doc/extension.rdoc is the authoritative tutorial for writing one.

Static linking

For builds where dynamic loading isn't available (the WASM port, some embedded scenarios), extensions are statically linked. dmydln.c, dmyenc.c, and dmyext.c ("dummy") provide the static analogues:

  • dmydln.c provides dln_load that returns from a registry instead of calling dlopen.
  • dmyenc.c provides the encoding initialisations statically.
  • dmyext.c provides the extension Init_* registry.

make selects between dynamic and static by configure flags.

Building only some extensions

./configure --with-ext=socket,openssl --without-ext=psych

--with-ext and --without-ext accept comma-separated lists. Extensions not listed are not built — useful for slimming down builds for embedded scenarios.

CRuby C API stability

The public API in include/ruby/ is mostly stable across minor versions. The maintainers go to lengths to avoid breaking C extensions on minor bumps (e.g., 3.2 → 3.3). Major bumps (e.g., 3.x → 4.0) may include breaking changes; they're listed in NEWS.md.

The internal/ headers under include/ruby/internal/ are not part of the public API even though they're shipped — extensions that include them risk breaking on any release.

How extensions interact with the GC

C extensions must:

  • Mark held VALUEs during GC: provide a dmark callback in rb_data_type_t.
  • Update references during compaction: provide an update_references callback.
  • Use write barriers on assignment: RB_OBJ_WRITE / RB_OBJ_WRITTEN.
  • Free non-VALUE memory in dfree.
  • Report memory size via dsize for ObjectSpace.memsize_of.

TypedData_Make_Struct is the recommended helper for wrapping a C pointer as a Ruby object:

typedef struct { int *buf; size_t len; } my_data_t;

static void my_free(void *p) { my_data_t *d = p; free(d->buf); free(d); }
static size_t my_size(const void *p) { const my_data_t *d = p; return sizeof(*d) + d->len * sizeof(int); }

static const rb_data_type_t my_type = {
    "MyData",
    {NULL, my_free, my_size},
    0, 0,
    RUBY_TYPED_FREE_IMMEDIATELY,
};

VALUE my_alloc(VALUE klass) {
    my_data_t *d;
    return TypedData_Make_Struct(klass, my_data_t, &my_type, d);
}

Common pitfalls in extension code

  • Forgetting to release the GVL during a blocking syscall.
  • Not marking VALUEs held in C structs — leads to use-after-free.
  • Not calling rb_check_arity — silent argument mismatch.
  • Using RSTRING_PTR after a Ruby allocation — the underlying buffer might have moved.
  • Confusing rb_eval_string with rb_funcall — the former evals Ruby source; the latter calls a method on a known receiver.

Entry points for modification

  • Add a new method to an extension: edit the relevant C file, add rb_define_method in Init_xxx. Re-run make to pick up the change.
  • Probe for a new system feature: edit the extension's extconf.rb with have_func/have_macro/try_compile.
  • Sync from upstream gem: most C extensions also have upstream gem repos. Use tool/sync_default_gems.rb to pull updates.

See doc/extension.rdoc for the canonical extension tutorial.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

C extensions – Ruby wiki | Factory