Skip to content

ruby: add DTV-based TLS access for ruby_current_ec#29

Closed
dalehamel wants to merge 7 commits intomainfrom
ruby-dtv-ec-lookup
Closed

ruby: add DTV-based TLS access for ruby_current_ec#29
dalehamel wants to merge 7 commits intomainfrom
ruby-dtv-ec-lookup

Conversation

@dalehamel
Copy link
Copy Markdown
Member

@dalehamel dalehamel commented Mar 2, 2026

superceded by open-telemetry#1226

@dalehamel
Copy link
Copy Markdown
Member Author

Coredump Test Plan

This PR needs a coredump test for a dynamically-linked Ruby on musl (Alpine Linux) to exercise the DTV-based ruby_current_ec lookup via dtv_read().

Test: ruby-3.4.x-dtv-loop (amd64 + arm64)

musl is the target because it uses DTV-based TLS (no TLSDESC), which is exactly the path this PR adds.

Setup (Alpine container):

docker run -it --privileged alpine:3.22 sh
apk add ruby gdb
echo 0x3f > /proc/self/coredump_filter

Verify musl uses DTPMOD64 (not TLSDESC):

readelf -r /usr/lib/libruby.so | grep DTPMOD
# Should show R_X86_64_DTPMOD64 or R_AARCH64_TLS_DTPMOD64
readelf -r /usr/lib/libruby.so | grep TLSDESC
# Should be EMPTY — musl doesn't use TLSDESC

Capture:

ruby /path/to/loop.rb &
PID=$!
sleep 2
./coredump new -pid $PID -name ruby-3.4.x-dtv-loop

What this exercises

  • VisitRelocations() with DTPMOD64 filter finds the TLS module ID offset in libruby.so
  • UpdateLibcInfo() receives DTVInfo extracted from musl's __tls_get_addr (via PR Extract DTV info from __tls_get_addr, add to LibcInfo open-telemetry/opentelemetry-ebpf-profiler#929's libc introspection)
  • dtv_read() shared eBPF helper traverses: tsd_base → DTV pointer (via DTVInfo.offset + optional indirection) → DTV[module_id * multiplier] → TLS block → ruby_current_ec
  • Expected frames should be identical in shape to existing ruby-3.4.x-loop tests

Important notes

  • Coredump filter must be 0x3f to capture anonymous pages containing TLS/DTV data
  • Both ld-musl-*.so and libruby.so must be included as modules in the coredump
  • musl DTV parameters: Multiplier=8, Indirect=1 (vs glibc Multiplier=16, Indirect=0 on x86)

Files to add

  • tools/coredump/testdata/amd64/ruby-3.4.x-dtv-loop.json
  • tools/coredump/testdata/arm64/ruby-3.4.x-dtv-loop.json

@dalehamel dalehamel force-pushed the ruby-dtv-ec-lookup branch 2 times, most recently from 06640ae to c5ef637 Compare March 3, 2026 15:10
@dalehamel dalehamel changed the base branch from libcinfo-tsd-tls-refactor to main March 3, 2026 15:10
@dalehamel dalehamel force-pushed the ruby-dtv-ec-lookup branch 7 times, most recently from 18d5d03 to 817a7d3 Compare March 9, 2026 13:46
@dalehamel dalehamel changed the base branch from main to yjit-wip March 9, 2026 13:47
@dalehamel dalehamel changed the base branch from yjit-wip to main March 9, 2026 13:47
@dalehamel dalehamel force-pushed the ruby-dtv-ec-lookup branch 2 times, most recently from df8e5c6 to 1b19af1 Compare March 9, 2026 13:55
When TLSDESC relocations are unavailable (e.g. some musl setups or
dynamically allocated TLS), the Ruby execution context can still be
found by traversing the Dynamic Thread Vector (DTV).

This commit:

- Adds a shared dtv_read() helper in tsd.h (alongside existing tsd_read)
  that traverses the DTV using DTVInfo from PR open-telemetry#929's libc introspection.
  This helper is available to all interpreters, not just Ruby.

- Generalizes VisitTLSRelocations into VisitRelocations with a pluggable
  relocation type filter, enabling lookup of DTPMOD64 relocations to find
  the TLS module ID offset for libruby.so.

- Adds DTVInfo, current_ec_tls_offset, and tls_module_id fields to
  RubyProcInfo so the eBPF unwinder can use DTV traversal.

- Implements UpdateLibcInfo for Ruby to receive DTVInfo when the libc
  package provides it (may arrive from a separate DSO like ld-linux.so).

- Adds a DTV fallback path in ruby_tracer.ebpf.c between the existing
  TLSDESC path and the ractor fallback.
Adds a coredump test for a shared-library Ruby 3.4.7 on amd64, where
libruby.so uses R_X86_64_DTPMOD64 relocations (the default x86_64 TLS
dialect). This exercises the DTV-based TLS lookup path: VisitRelocations
with DTPMOD64 filter, UpdateLibcInfo with DTVInfo, and dtv_read() in
the eBPF unwinder.

This test is amd64-only because aarch64 linkers aggressively relax
GD TLS to TLSDESC, making it impossible to produce DTPMOD64 relocations
on aarch64 with standard toolchains.

Captured via gdb attach + breakpoint on rb_vm_exec to ensure a clean
snapshot deep in the Ruby VM stack.
- Convert relocation type checks from chained || expressions to switch
  statements in both ruby.go Loader() and pfelf VisitTLSRelocations()
  for improved readability.
- Include module_id in DTV read failure DEBUG_PRINT for easier debugging.
Address review feedback from fabled: move arch-specific relocation type
checks into pfelf as abstract RelocType constants (RelTLSDESC, RelDTPMOD64)
with a bitmask-based filter, replacing the function argument in
VisitRelocations. This centralizes the arch-to-reloc mapping in
classifyReloc() and simplifies callers.
@dalehamel dalehamel force-pushed the ruby-dtv-ec-lookup branch 2 times, most recently from 18d5d03 to cb7528d Compare March 19, 2026 14:20
@dalehamel dalehamel changed the base branch from main to ruby-jit-frames March 19, 2026 14:21
@dalehamel dalehamel changed the base branch from ruby-jit-frames to main March 19, 2026 14:21
@dalehamel dalehamel force-pushed the ruby-dtv-ec-lookup branch from cb7528d to 29d29fd Compare March 19, 2026 15:45
Ruby 3.4.7 built from source with ruby-install on Alpine (musl libc),
linked with --enable-shared. This exercises the DTV-based TLS access
path for ruby_current_ec on musl, where TLSDESC relocations are not
used and we fall back to DTPMOD64.
@dalehamel dalehamel force-pushed the ruby-dtv-ec-lookup branch from 29d29fd to 1a9790f Compare March 19, 2026 19:16
Move the architecture dispatch (switch f.Machine) from classifyReloc,
which was called on every relocation entry, into VisitRelocations where
it runs once to select an arch-specific classifier function. This avoids
redundant machine-type checks in the inner loop.

Rename checkFunc to filterFunc for clarity.

Addresses review feedback from @fabled.
@dalehamel dalehamel force-pushed the ruby-dtv-ec-lookup branch from 1a9790f to 017dfbc Compare March 20, 2026 13:34
@dalehamel dalehamel closed this Apr 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant