Skip to content

Commit

Permalink
Add a helper method for reading/writing memory (#123)
Browse files Browse the repository at this point in the history
This commit adds two new methods, `Fiddle::Pointer.read` and
`Fiddle::Pointer.write`. Both methods take an address, and will read or
write bytes at that address respectively.

For example we can read from an address without making a Pointer object:

```ruby
Fiddle::Pointer.read(address, 5) # read 5 bytes
```

We can also write to an address without allocating a Pointer object:

```ruby
Fiddle::Pointer.write(address, "bytes") # write 5 bytes
```

This allows us to read / write memory at arbitrary addresses without
instantiating a new `Fiddle::Pointer` object.

Examples where this API would be useful
[1](https://github.com/tenderlove/tenderjit/blob/f03481d28bff4d248746e596929b0841de65f181/lib/tenderjit/fiddle_hacks.rb#L26-L28)
[2](https://github.com/tenderlove/ruby/blob/77c8daa2d40dd58eeb3785ce17dea2ee38f308d1/lib/ruby_vm/rjit/c_pointer.rb#L193)
[3](https://github.com/tenderlove/ruby/blob/77c8daa2d40dd58eeb3785ce17dea2ee38f308d1/lib/ruby_vm/rjit/c_pointer.rb#L284)

I also added a writer method for the same reasons as the reader.

---------

Co-authored-by: Sutou Kouhei <kou@clear-code.com>
  • Loading branch information
tenderlove and kou authored Mar 16, 2023
1 parent b83f03d commit 04238ce
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 0 deletions.
29 changes: 29 additions & 0 deletions ext/fiddle/pointer.c
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,33 @@ rb_fiddle_ptr_s_to_ptr(VALUE self, VALUE val)
return ptr;
}

/*
* call-seq:
* Fiddle::Pointer.read(address, len) => string
*
* Or read the memory at address +address+ with length +len+ and return a
* string with that memory
*/

static VALUE
rb_fiddle_ptr_read_mem(VALUE klass, VALUE address, VALUE len)
{
return rb_str_new((char *)NUM2PTR(address), NUM2ULONG(len));
}

/*
* call-seq:
* Fiddle::Pointer.write(address, str)
*
* Write bytes in +str+ to the location pointed to by +address+.
*/
static VALUE
rb_fiddle_ptr_write_mem(VALUE klass, VALUE addr, VALUE str)
{
memcpy(NUM2PTR(addr), StringValuePtr(str), RSTRING_LEN(str));
return str;
}

void
Init_fiddle_pointer(void)
{
Expand All @@ -815,6 +842,8 @@ Init_fiddle_pointer(void)
rb_define_singleton_method(rb_cPointer, "malloc", rb_fiddle_ptr_s_malloc, -1);
rb_define_singleton_method(rb_cPointer, "to_ptr", rb_fiddle_ptr_s_to_ptr, 1);
rb_define_singleton_method(rb_cPointer, "[]", rb_fiddle_ptr_s_to_ptr, 1);
rb_define_singleton_method(rb_cPointer, "read", rb_fiddle_ptr_read_mem, 2);
rb_define_singleton_method(rb_cPointer, "write", rb_fiddle_ptr_write_mem, 2);
rb_define_method(rb_cPointer, "initialize", rb_fiddle_ptr_initialize, -1);
rb_define_method(rb_cPointer, "free=", rb_fiddle_ptr_free_set, 1);
rb_define_method(rb_cPointer, "free", rb_fiddle_ptr_free_get, 0);
Expand Down
16 changes: 16 additions & 0 deletions test/fiddle/test_pointer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,22 @@ def dlwrap arg
Fiddle.dlwrap arg
end

def test_can_read_write_memory
# Allocate some memory
address = Fiddle.malloc(Fiddle::SIZEOF_VOIDP)

bytes_to_write = Fiddle::SIZEOF_VOIDP.times.to_a.pack("C*")

# Write to the memory
Fiddle::Pointer.write(address, bytes_to_write)

# Read the bytes out again
bytes = Fiddle::Pointer.read(address, Fiddle::SIZEOF_VOIDP)
assert_equal bytes_to_write, bytes
ensure
Fiddle.free address
end

def test_cptr_to_int
null = Fiddle::NULL
assert_equal(null.to_i, null.to_int)
Expand Down

0 comments on commit 04238ce

Please sign in to comment.