DynLdr ====== DynLdr is a class that uses metasm to dynamically add native methods, or native method wrappers, available to the running ruby interpreter. It leverages the built-in C parser / compiler. It is implemented in `metasm/dynldr.rb`. Currently only supported for and under Windows and Linux. Basics ------ Native library wrapper ###################### The main usage is to generate interfaces to native libraries. This is done through the `#new_api_c` method. The following exemple will read the specified C header fragment, define ruby constants for all `#define`/`enum`, and define ruby method wrappers to call the native functions whose prototype is present in the header. All referenced native functions must be exported by the given library file. class MyInterface < DynLdr c_header = < 4) return 1; else return 0; } EOS end References to external functions are allowed, and resolved automatically. The ruby objects used as arguments to the wrapper method are automatically converted to the right C type. You can also write native functions in assembly, but you must specify a C prototype, used for argument and return value conversion. class MyInterface < DynLdr new_func_asm "int increment(int i);", < :size, :s_value => 42) # we can access fields using Hash-style access s_obj['s_UniOn'] = 0xa8 # or Struct-style access puts '0x%x' % s_obj.s_BiTS2 # => '0xa' This object can be directly passed as argument to a wrapped function, and the native function will receive a pointer to this structure (that it can freely modify). This object is a `C::AllocStruct`, defined in `metasm/parse_c.rb`. Internally, it is based on a ruby `String`, and has a reference to the parser's `Struct` to find the mapping membername -> offsets/length. See for more details. Callbacks --------- `DynLdr` handles C callbacks, with arbitrary ABI. Any number of callbacks can be defined at any time. C callbacks are backed by a ruby `Proc`, eg `lambda {}`. class MyInterface < DynLdr new_api_c < memory_read(p2, 1) } qsort(str, str.length, 1, cmp) p str end Argument conversion ------------------- Ruby objects passed to a wrapper method are converted to the corresponding C type * `Strings` are converted to a C pointer to the byte buffer (also directly accessible from the ruby through `DynLdr.str_ptr(obj)` * `Integers` are converted to their C equivalent, according to the prototype (`char`, `unsigned long long`, ...) * `Procs` are converted to a C callback * `Floats` are not supported for now. Working with memory ------------------- DynLdr provides different ways to allocate memory. * `alloc_c_struct` to allocate a C structure * `alloc_c_ary` to allocate C array of some type * `alloc_c_ptr`, which is just an ary of size 1 * `memory_alloc` allocates memory from a new memory page `memory_alloc` works by calling `mmap` under linux and `VirtualAlloc` under windows, and is suitable for allocating memory where you want to control the memory permissions (read, write, execute). This is done through `memory_perm`. `memory_perm` takes for argument the start address, the length, and the new permission, specified as a String (e.g. 'r', 'rwx') To work with memory that may be returned by an API (e.g. `malloc`), DynLdr provides ways to read and write arbitrary pointers from the ruby interpreter memory. Take care, those may generate faults when called with invalid addresses that will crash the ruby interpreter. * `memory_read` takes a pointer and a length, and returns a String * `memory_read_int` takes a pointer, and returns an Integer (of pointer size, e.g. 64 bit in a 64-bit interpreter) * `memory_write` takes a pointer and a String, and writes it to memory * `memory_write_int` Hacking ------- Internally, DynLdr relies on a number of features that are not directly available from the ruby interpreter. So the first thing done by the script is to generate a binary native module that will act as a C extension to the ruby interpreter. This binary is necessarily different depending on the interpreter. The binary name includes the target architecture, in the format dynldr-*arch*-*cpu*-*19*.so, e.g. * dynldr-linux-ia32.so * dynldr-windows-x64-19.so This native module is (re)generated if it does not exist, or is older than the `dynldr.rb` script. A special trick is used in this module, as it does not know the actual name of the ruby library used by the interpreter. So on linux, the `libruby` is removed from the `DT_NEEDED` library list, and on windows a special stub is assembled to manually resolve the ruby imports needed by the module from any instance of `libruby` present in the running process. The native file is written to a directory writeably by the current user. The following list of directories are tried, until a suitable one is found: * the `metasm` directory itself * the `$HOME`/`$APPDATA`/`$USERPROFILE` directory * the `$TMP`/`$TEMP`/current directory