fiddle 1.1.5 → 1.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Rakefile +9 -0
- data/ext/fiddle/pointer.c +2 -1
- data/lib/fiddle/cparser.rb +1 -1
- data/lib/fiddle/ffi_backend.rb +63 -27
- data/lib/fiddle/version.rb +1 -1
- data/lib/fiddle.rb +81 -76
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b031b86e4ccc2f94430cb9322a38e7d06932800eb597452f130f01329ecd78ed
|
4
|
+
data.tar.gz: 0b6ccf708b048792e7e6afec9f3f7114289b0d9755353c37d95857085f1fadef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e98b34f7c1deed926635fa0a99c24beadf19d4f5804acc87dec5ceab87a364a27538d5e715b1c7064b34cce8966de0b594a0a4c4131a3f9dfa5952935b2d1d5
|
7
|
+
data.tar.gz: 52782b8d2ab08ef3ac8cdecf78271b7e6fdcbc62e0e27bdad576034dfe0eabf410ba57f4809b14e0f26e24180c80f89234a812ead950088e1b56b80444e3ad47
|
data/Rakefile
CHANGED
@@ -5,6 +5,15 @@ task :test do
|
|
5
5
|
ruby("test/run.rb")
|
6
6
|
end
|
7
7
|
|
8
|
+
release_task = Rake.application["release"]
|
9
|
+
release_task.prerequisites.delete("build")
|
10
|
+
release_task.prerequisites.delete("release:rubygem_push")
|
11
|
+
release_task_comment = release_task.comment
|
12
|
+
if release_task_comment
|
13
|
+
release_task.clear_comments
|
14
|
+
release_task.comment = release_task_comment.gsub(/ and build.*$/, "")
|
15
|
+
end
|
16
|
+
|
8
17
|
namespace :version do
|
9
18
|
desc "Bump version"
|
10
19
|
task :bump do
|
data/ext/fiddle/pointer.c
CHANGED
@@ -827,7 +827,8 @@ rb_fiddle_ptr_read_mem(VALUE klass, VALUE address, VALUE len)
|
|
827
827
|
static VALUE
|
828
828
|
rb_fiddle_ptr_write_mem(VALUE klass, VALUE addr, VALUE str)
|
829
829
|
{
|
830
|
-
|
830
|
+
const char *ptr = StringValuePtr(str);
|
831
|
+
memcpy(NUM2PTR(addr), ptr, RSTRING_LEN(str));
|
831
832
|
return str;
|
832
833
|
}
|
833
834
|
|
data/lib/fiddle/cparser.rb
CHANGED
data/lib/fiddle/ffi_backend.rb
CHANGED
@@ -159,15 +159,16 @@ module Fiddle
|
|
159
159
|
args[i] = Fiddle::FFIBackend.to_ffi_type(args[i])
|
160
160
|
end
|
161
161
|
else
|
162
|
-
args.
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
162
|
+
@args.each_with_index do |arg_type, i|
|
163
|
+
next unless arg_type == Types::VOIDP
|
164
|
+
|
165
|
+
src = args[i]
|
166
|
+
next if src.nil?
|
167
|
+
next if src.is_a?(String)
|
168
|
+
next if src.is_a?(FFI::AbstractMemory)
|
169
|
+
next if src.is_a?(FFI::Struct)
|
170
|
+
|
171
|
+
args[i] = Pointer[src]
|
171
172
|
end
|
172
173
|
end
|
173
174
|
result = @function.call(*args, &block)
|
@@ -187,8 +188,10 @@ module Fiddle
|
|
187
188
|
end
|
188
189
|
return_type = Fiddle::FFIBackend.to_ffi_type(@ctype)
|
189
190
|
raise "#{self.class} must implement #call" unless respond_to?(:call)
|
190
|
-
|
191
|
-
|
191
|
+
wrapper = lambda do |*args|
|
192
|
+
call(*args.map { |v| v.is_a?(FFI::Pointer) ? Pointer.new(v) : v })
|
193
|
+
end
|
194
|
+
@function = FFI::Function.new(return_type, ffi_args, wrapper, convention: abi)
|
192
195
|
@freed = false
|
193
196
|
end
|
194
197
|
|
@@ -215,8 +218,8 @@ module Fiddle
|
|
215
218
|
class DLError < Error; end
|
216
219
|
class ClearedReferenceError < Error; end
|
217
220
|
|
221
|
+
# Pointer isn't thread safe for now
|
218
222
|
class Pointer
|
219
|
-
attr_reader :ffi_ptr
|
220
223
|
extend FFI::DataConverter
|
221
224
|
native_type FFI::Type::Builtin::POINTER
|
222
225
|
|
@@ -239,7 +242,7 @@ module Fiddle
|
|
239
242
|
def self.to_ptr(value)
|
240
243
|
if value.is_a?(String)
|
241
244
|
cptr = Pointer.malloc(value.bytesize)
|
242
|
-
cptr.ffi_ptr.
|
245
|
+
cptr.ffi_ptr.put_bytes(0, value)
|
243
246
|
cptr
|
244
247
|
|
245
248
|
elsif value.is_a?(Array)
|
@@ -261,6 +264,14 @@ module Fiddle
|
|
261
264
|
end
|
262
265
|
end
|
263
266
|
|
267
|
+
def self.write(addr, bytes)
|
268
|
+
FFI::Pointer.new(addr).write_bytes(bytes)
|
269
|
+
end
|
270
|
+
|
271
|
+
def self.read(addr, len)
|
272
|
+
FFI::Pointer.new(addr).read_bytes(len)
|
273
|
+
end
|
274
|
+
|
264
275
|
class << self
|
265
276
|
alias [] to_ptr
|
266
277
|
end
|
@@ -274,7 +285,7 @@ module Fiddle
|
|
274
285
|
value = value.to_str(args[1])
|
275
286
|
end
|
276
287
|
|
277
|
-
|
288
|
+
ffi_ptr.put_bytes(args[0], value, 0, args[1])
|
278
289
|
elsif args.size == 1
|
279
290
|
if value.is_a?(Fiddle::Pointer)
|
280
291
|
value = value.to_str(args[0] + 1)
|
@@ -282,7 +293,7 @@ module Fiddle
|
|
282
293
|
value = value.chr
|
283
294
|
end
|
284
295
|
|
285
|
-
|
296
|
+
ffi_ptr.put_bytes(args[0], value, 0, 1)
|
286
297
|
end
|
287
298
|
rescue FFI::NullPointerError
|
288
299
|
raise DLError.new("NULL pointer access")
|
@@ -308,14 +319,27 @@ module Fiddle
|
|
308
319
|
end
|
309
320
|
elsif addr.is_a?(IO)
|
310
321
|
raise NotImplementedError, "IO ptr isn't supported"
|
322
|
+
else
|
323
|
+
FFI::Pointer.new(Integer(addr))
|
311
324
|
end
|
312
325
|
|
313
|
-
|
326
|
+
if size
|
327
|
+
@size = size
|
328
|
+
elsif ptr.size_limit?
|
329
|
+
@size = ptr.size
|
330
|
+
else
|
331
|
+
@size = 0
|
332
|
+
end
|
314
333
|
@free = free
|
315
334
|
@ffi_ptr = ptr
|
335
|
+
@addr_ptr = nil
|
316
336
|
@freed = false
|
317
337
|
end
|
318
338
|
|
339
|
+
def ffi_ptr
|
340
|
+
@addr_ptr ? @addr_ptr.get_pointer(0) : @ffi_ptr
|
341
|
+
end
|
342
|
+
|
319
343
|
module LibC
|
320
344
|
extend FFI::Library
|
321
345
|
ffi_lib FFI::Library::LIBC
|
@@ -344,11 +368,11 @@ module Fiddle
|
|
344
368
|
end
|
345
369
|
|
346
370
|
def null?
|
347
|
-
|
371
|
+
ffi_ptr.null?
|
348
372
|
end
|
349
373
|
|
350
374
|
def to_ptr
|
351
|
-
|
375
|
+
ffi_ptr
|
352
376
|
end
|
353
377
|
|
354
378
|
def size
|
@@ -367,9 +391,9 @@ module Fiddle
|
|
367
391
|
return if @free.nil?
|
368
392
|
return if @freed
|
369
393
|
if @free == RUBY_FREE
|
370
|
-
LibC::FREE.call(
|
394
|
+
LibC::FREE.call(ffi_ptr)
|
371
395
|
else
|
372
|
-
@free.call(
|
396
|
+
@free.call(ffi_ptr)
|
373
397
|
end
|
374
398
|
@freed = true
|
375
399
|
end
|
@@ -397,12 +421,13 @@ module Fiddle
|
|
397
421
|
end
|
398
422
|
alias to_int to_i
|
399
423
|
|
400
|
-
# without \0
|
401
424
|
def to_s(len = nil)
|
402
425
|
if len
|
403
|
-
ffi_ptr.
|
426
|
+
ffi_ptr.read_string(len)
|
427
|
+
elsif @size == 0
|
428
|
+
ffi_ptr.read_string
|
404
429
|
else
|
405
|
-
ffi_ptr.get_string(0)
|
430
|
+
ffi_ptr.get_string(0, @size)
|
406
431
|
end
|
407
432
|
rescue FFI::NullPointerError
|
408
433
|
raise DLError.new("NULL pointer access")
|
@@ -412,6 +437,9 @@ module Fiddle
|
|
412
437
|
if len
|
413
438
|
ffi_ptr.read_string(len)
|
414
439
|
else
|
440
|
+
if @size < 0
|
441
|
+
raise ArgumentError.new("negative string size (or size too big)")
|
442
|
+
end
|
415
443
|
ffi_ptr.read_string(@size)
|
416
444
|
end
|
417
445
|
rescue FFI::NullPointerError
|
@@ -423,7 +451,16 @@ module Fiddle
|
|
423
451
|
end
|
424
452
|
|
425
453
|
def inspect
|
426
|
-
|
454
|
+
# SIZEOF_VOIDP * 2 == Math.log(2 ** (SIZEOF_VOIDP * 8), 16)
|
455
|
+
pointer_inspect_width = SIZEOF_VOIDP * 2
|
456
|
+
"#<%s ptr=0x%0.*x size=%d free=0x%0.*x>" % [
|
457
|
+
self.class.name,
|
458
|
+
pointer_inspect_width,
|
459
|
+
to_i,
|
460
|
+
size,
|
461
|
+
pointer_inspect_width,
|
462
|
+
@free || 0,
|
463
|
+
]
|
427
464
|
end
|
428
465
|
|
429
466
|
def +(delta)
|
@@ -463,9 +500,8 @@ module Fiddle
|
|
463
500
|
end
|
464
501
|
|
465
502
|
def ref
|
466
|
-
|
467
|
-
|
468
|
-
cptr
|
503
|
+
@addr_ptr ||= FFI::MemoryPointer.new(:pointer).put_pointer(0, @ffi_ptr)
|
504
|
+
Pointer.new(@addr_ptr, 0)
|
469
505
|
end
|
470
506
|
end
|
471
507
|
|
data/lib/fiddle/version.rb
CHANGED
data/lib/fiddle.rb
CHANGED
@@ -10,98 +10,80 @@ require 'fiddle/function'
|
|
10
10
|
require 'fiddle/version'
|
11
11
|
|
12
12
|
module Fiddle
|
13
|
-
if
|
14
|
-
|
15
|
-
|
16
|
-
def self.win32_last_error
|
17
|
-
if RUBY_ENGINE == 'jruby'
|
18
|
-
errno = FFI.errno
|
19
|
-
errno == 0 ? nil : errno
|
20
|
-
else
|
21
|
-
Thread.current[:__FIDDLE_WIN32_LAST_ERROR__]
|
22
|
-
end
|
13
|
+
if RUBY_ENGINE != 'ruby' # FFI backend
|
14
|
+
def self.last_error
|
15
|
+
FFI.errno
|
23
16
|
end
|
24
17
|
|
25
|
-
|
26
|
-
|
27
|
-
if RUBY_ENGINE == 'jruby'
|
28
|
-
FFI.errno = error || 0
|
29
|
-
else
|
30
|
-
Thread.current[:__FIDDLE_WIN32_LAST_ERROR__] = error
|
31
|
-
end
|
18
|
+
def self.last_error=(error)
|
19
|
+
FFI.errno = error || 0
|
32
20
|
end
|
33
21
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
end
|
43
|
-
end
|
22
|
+
if WINDOWS
|
23
|
+
class << self
|
24
|
+
def win32_last_error
|
25
|
+
FFI.errno.nonzero?
|
26
|
+
end
|
27
|
+
def win32_last_error=(error)
|
28
|
+
FFI.errno = error || 0
|
29
|
+
end
|
44
30
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
Thread.current[:__FIDDLE_WIN32_LAST_SOCKET_ERROR__] = error
|
31
|
+
def win32_last_socket_error
|
32
|
+
FFI.errno.nonzero?
|
33
|
+
end
|
34
|
+
def win32_last_socket_error=(error)
|
35
|
+
FFI.errno = error || 0
|
36
|
+
end
|
52
37
|
end
|
53
38
|
end
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
def self.last_error
|
58
|
-
if RUBY_ENGINE == 'jruby'
|
59
|
-
errno = FFI.errno
|
60
|
-
errno == 0 ? nil : errno
|
61
|
-
else
|
39
|
+
else
|
40
|
+
# Returns the last +Error+ of the current executing +Thread+ or nil if none
|
41
|
+
def self.last_error
|
62
42
|
Thread.current[:__FIDDLE_LAST_ERROR__]
|
63
43
|
end
|
64
|
-
end
|
65
44
|
|
66
|
-
|
67
|
-
|
68
|
-
if RUBY_ENGINE == 'jruby'
|
69
|
-
FFI.errno = error || 0
|
70
|
-
else
|
45
|
+
# Sets the last +Error+ of the current executing +Thread+ to +error+
|
46
|
+
def self.last_error= error
|
71
47
|
Thread.current[:__DL2_LAST_ERROR__] = error
|
72
48
|
Thread.current[:__FIDDLE_LAST_ERROR__] = error
|
73
49
|
end
|
50
|
+
|
51
|
+
if WINDOWS
|
52
|
+
# Returns the last win32 +Error+ of the current executing +Thread+ or nil
|
53
|
+
# if none
|
54
|
+
def self.win32_last_error
|
55
|
+
Thread.current[:__FIDDLE_WIN32_LAST_ERROR__]
|
56
|
+
end
|
57
|
+
|
58
|
+
# Sets the last win32 +Error+ of the current executing +Thread+ to +error+
|
59
|
+
def self.win32_last_error= error
|
60
|
+
Thread.current[:__FIDDLE_WIN32_LAST_ERROR__] = error
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns the last win32 socket +Error+ of the current executing
|
64
|
+
# +Thread+ or nil if none
|
65
|
+
def self.win32_last_socket_error
|
66
|
+
Thread.current[:__FIDDLE_WIN32_LAST_SOCKET_ERROR__]
|
67
|
+
end
|
68
|
+
|
69
|
+
# Sets the last win32 socket +Error+ of the current executing
|
70
|
+
# +Thread+ to +error+
|
71
|
+
def self.win32_last_socket_error= error
|
72
|
+
Thread.current[:__FIDDLE_WIN32_LAST_SOCKET_ERROR__] = error
|
73
|
+
end
|
74
|
+
end
|
74
75
|
end
|
75
76
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
# Fiddle::Handle.
|
80
|
-
#
|
81
|
-
# If +nil+ is given for the +library+, Fiddle::Handle::DEFAULT is used, which
|
82
|
-
# is the equivalent to RTLD_DEFAULT. See <code>man 3 dlopen</code> for more.
|
83
|
-
#
|
84
|
-
# lib = Fiddle.dlopen(nil)
|
85
|
-
#
|
86
|
-
# The default is dependent on OS, and provide a handle for all libraries
|
87
|
-
# already loaded. For example, in most cases you can use this to access
|
88
|
-
# +libc+ functions, or ruby functions like +rb_str_new+.
|
89
|
-
#
|
90
|
-
# See Fiddle::Handle.new for more.
|
91
|
-
def dlopen library
|
92
|
-
begin
|
77
|
+
case RUBY_PLATFORM
|
78
|
+
when /linux/
|
79
|
+
def dlopen library
|
93
80
|
Fiddle::Handle.new(library)
|
94
81
|
rescue DLError => error
|
95
|
-
case
|
96
|
-
when /
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
# https://sourceware.org/binutils/docs/ld.html#Scripts
|
101
|
-
path = $1
|
102
|
-
else
|
103
|
-
raise
|
104
|
-
end
|
82
|
+
case error.message
|
83
|
+
when /\A(\/.+?): (?:invalid ELF header|file too short)/
|
84
|
+
# This may be a linker script:
|
85
|
+
# https://sourceware.org/binutils/docs/ld.html#Scripts
|
86
|
+
path = $1
|
105
87
|
else
|
106
88
|
raise
|
107
89
|
end
|
@@ -111,7 +93,11 @@ module Fiddle
|
|
111
93
|
case line
|
112
94
|
when /\A\s*(?:INPUT|GROUP)\s*\(\s*([^\s,\)]+)/
|
113
95
|
# TODO: Should we support multiple files?
|
114
|
-
|
96
|
+
first_input = $1
|
97
|
+
if first_input.start_with?("-l")
|
98
|
+
first_input = "lib#{first_input[2..-1]}.so"
|
99
|
+
end
|
100
|
+
return dlopen(first_input)
|
115
101
|
end
|
116
102
|
end
|
117
103
|
end
|
@@ -119,6 +105,25 @@ module Fiddle
|
|
119
105
|
# Not found
|
120
106
|
raise
|
121
107
|
end
|
108
|
+
else
|
109
|
+
# call-seq: dlopen(library) => Fiddle::Handle
|
110
|
+
#
|
111
|
+
# Creates a new handler that opens +library+, and returns an instance of
|
112
|
+
# Fiddle::Handle.
|
113
|
+
#
|
114
|
+
# If +nil+ is given for the +library+, Fiddle::Handle::DEFAULT is used, which
|
115
|
+
# is the equivalent to RTLD_DEFAULT. See <code>man 3 dlopen</code> for more.
|
116
|
+
#
|
117
|
+
# lib = Fiddle.dlopen(nil)
|
118
|
+
#
|
119
|
+
# The default is dependent on OS, and provide a handle for all libraries
|
120
|
+
# already loaded. For example, in most cases you can use this to access
|
121
|
+
# +libc+ functions, or ruby functions like +rb_str_new+.
|
122
|
+
#
|
123
|
+
# See Fiddle::Handle.new for more.
|
124
|
+
def dlopen library
|
125
|
+
Fiddle::Handle.new(library)
|
126
|
+
end
|
122
127
|
end
|
123
128
|
module_function :dlopen
|
124
129
|
|
metadata
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fiddle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.7
|
5
5
|
platform: ruby
|
6
|
-
original_platform: ''
|
7
6
|
authors:
|
8
7
|
- Aaron Patterson
|
9
8
|
- SHIBATA Hiroshi
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
13
12
|
dependencies: []
|
14
13
|
description: A libffi wrapper for Ruby.
|
15
14
|
email:
|
@@ -70,7 +69,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
69
|
- !ruby/object:Gem::Version
|
71
70
|
version: '0'
|
72
71
|
requirements: []
|
73
|
-
rubygems_version: 3.6.
|
72
|
+
rubygems_version: 3.6.7
|
74
73
|
specification_version: 4
|
75
74
|
summary: A libffi wrapper for Ruby.
|
76
75
|
test_files: []
|