From 687f21474893d09ad44c080e5751999f72b74bc6 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 15 Mar 2023 10:36:45 -0700 Subject: [PATCH 1/3] Add a helper methods for reading and writing memory 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. Co-Authored-By: Sutou Kouhei --- ext/fiddle/pointer.c | 29 +++++++++++++++++++++++++++++ test/fiddle/test_pointer.rb | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/ext/fiddle/pointer.c b/ext/fiddle/pointer.c index 15107e38..71ae7008 100644 --- a/ext/fiddle/pointer.c +++ b/ext/fiddle/pointer.c @@ -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 *)NUM2ULONG(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) { @@ -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); diff --git a/test/fiddle/test_pointer.rb b/test/fiddle/test_pointer.rb index d6cba04b..a179849c 100644 --- a/test/fiddle/test_pointer.rb +++ b/test/fiddle/test_pointer.rb @@ -10,6 +10,38 @@ def dlwrap arg Fiddle.dlwrap arg end + def test_can_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_can_read_memory + # Allocate some memory + address = Fiddle.malloc(Fiddle::SIZEOF_VOIDP) + + # Write some bytes in to the memory + ptr = Fiddle::Pointer.new(address) + bytes_to_write = Fiddle::SIZEOF_VOIDP.times.to_a + bytes_to_write.each { |i| ptr[i] = i } + + # Read the bytes out again + bytes = Fiddle::Pointer.read(address, Fiddle::SIZEOF_VOIDP).bytes + 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) From 9ba4f1c26ff4bb02beb2f185d516f668e40a7de7 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 16 Mar 2023 08:46:42 -0700 Subject: [PATCH 2/3] Update ext/fiddle/pointer.c Co-authored-by: Sutou Kouhei --- ext/fiddle/pointer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/fiddle/pointer.c b/ext/fiddle/pointer.c index 71ae7008..5c71967a 100644 --- a/ext/fiddle/pointer.c +++ b/ext/fiddle/pointer.c @@ -810,7 +810,7 @@ rb_fiddle_ptr_s_to_ptr(VALUE self, VALUE val) static VALUE rb_fiddle_ptr_read_mem(VALUE klass, VALUE address, VALUE len) { - return rb_str_new((char *)NUM2ULONG(address), NUM2ULONG(len)); + return rb_str_new((char *)NUM2PTR(address), NUM2ULONG(len)); } /* From 324c44dc77394b0c0d5e1cdeb7564a3ff57b0707 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 16 Mar 2023 08:46:56 -0700 Subject: [PATCH 3/3] Update test/fiddle/test_pointer.rb Co-authored-by: Sutou Kouhei --- test/fiddle/test_pointer.rb | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/test/fiddle/test_pointer.rb b/test/fiddle/test_pointer.rb index a179849c..f2c1d285 100644 --- a/test/fiddle/test_pointer.rb +++ b/test/fiddle/test_pointer.rb @@ -10,7 +10,7 @@ def dlwrap arg Fiddle.dlwrap arg end - def test_can_write_memory + def test_can_read_write_memory # Allocate some memory address = Fiddle.malloc(Fiddle::SIZEOF_VOIDP) @@ -26,22 +26,6 @@ def test_can_write_memory Fiddle.free address end - def test_can_read_memory - # Allocate some memory - address = Fiddle.malloc(Fiddle::SIZEOF_VOIDP) - - # Write some bytes in to the memory - ptr = Fiddle::Pointer.new(address) - bytes_to_write = Fiddle::SIZEOF_VOIDP.times.to_a - bytes_to_write.each { |i| ptr[i] = i } - - # Read the bytes out again - bytes = Fiddle::Pointer.read(address, Fiddle::SIZEOF_VOIDP).bytes - 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)