A taste of evil.rb: using DL to unfreeze objects

More people are getting interested in ruby's
internals*1, perhaps thanks to Caleb Tennis'
latestseries.
So maybe the time has come to unveil
evil.rb*2 as quite an useful learning tool,
allowing you to mess with the interpreter's internal data structures at runtime.

I will show how evil.rb manages to unfreeze an object, introducing DL along the way.
That's one of the simplest low-level manipulations one can do, just the
first step towards better understanding of the runtime structures, but this is
the sort of things happening under the hood in the interpreter all the
time.

evil.rb

The idea behind evil.rb was conceived by Florian Groß: it's
as simple as using DL (a standard extension providing an interface to the dynamic linker) to access internal objects. That is, it allows
you to manipulate them at the C-struct level. This enables you to do things like:

changing the class of an object

manipulating the inheritance chain for fun and profit

grabbing instance variables or singleton methods

swapping objects (ever heard of Object#become?)

changing the self context of a Proc (the way 1.9's instance_exec does)

messing with the flags of an object

Using DL

Ruby VALUES for non-immediate objects are pointers to RVALUE slots
inside heaps managed by Ruby. RVALUE is a union type taking 5 words (20
bytes), whose contents can be interpreted as any of the "RStructs":
it turns out that the interpreter distinguishes internally between
different object types (and other things like NODEs that correspond to
executable code, or scopes).

Getting object addresses

I can now instantiate Internal::RObject objects, which will have three attribute accessors:
flags, klass and iv_tbl. In order to do so, RObject.new must be given the address of the
chunk of memory that will be interpreted as a RObject. This is fairly easy: Object#object_id is defined as