Fiddle 모듈

Fiddle 모듈 (Fiddle)

Fiddle은 Ruby를 위한 libffi 래퍼예요. 외부 함수 인터페이스(FFI)를 Ruby로 번역해 주는 확장이죠.

Fiddle은 널리 쓰이는 C 라이브러리인 libffi를 감싸요. libffi는 한 언어로 작성된 코드가 다른 언어로 작성된 코드를 호출할 수 있게 해주는 이식 가능한 인터페이스를 제공해요.

여기서는 Fiddle::Function으로 libm의 floor(3)를 감싸는 예시를 볼게요.

require 'fiddle'

libm = Fiddle.dlopen('/lib/libm.so.6')

floor = Fiddle::Function.new(
  libm['floor'],
  [Fiddle::TYPE_DOUBLE],
  Fiddle::TYPE_DOUBLE
)

puts floor.call(3.14159) #=> 3.0

출처: Ruby 3.3 API

본문

상수 (Constants)

Fiddle에는 타입 크기·정렬·동적 라이브러리 조작에 쓰이는 상수들이 있어요.

  • 정렬 크기(alignment) 상수: ALIGN_BOOL, ALIGN_CHAR, ALIGN_DOUBLE, ALIGN_FLOAT, ALIGN_INT, ALIGN_INT16_T, ALIGN_INT32_T, ALIGN_INT64_T, ALIGN_INT8_T, ALIGN_INTPTR_T, ALIGN_LONG, ALIGN_LONG_LONG, ALIGN_PTRDIFF_T, ALIGN_SHORT, ALIGN_SIZE_T, ALIGN_SSIZE_T, ALIGN_UINTPTR_T, ALIGN_VOIDP — 각 C 타입의 정렬 크기
  • 크기(size) 상수: SIZEOF_BOOL, SIZEOF_CHAR, SIZEOF_CONST_STRING, SIZEOF_DOUBLE, SIZEOF_FLOAT, SIZEOF_INT, SIZEOF_INT16_T, SIZEOF_INT32_T, SIZEOF_INT64_T, SIZEOF_INT8_T, SIZEOF_INTPTR_T, SIZEOF_LONG, SIZEOF_LONG_LONG, SIZEOF_PTRDIFF_T, SIZEOF_SHORT, SIZEOF_SIZE_T, SIZEOF_SSIZE_T, SIZEOF_UCHAR, SIZEOF_UINT, SIZEOF_UINT16_T, SIZEOF_UINT32_T, SIZEOF_UINT64_T, SIZEOF_UINT8_T, SIZEOF_UINTPTR_T, SIZEOF_ULONG, SIZEOF_ULONG_LONG, SIZEOF_USHORT, SIZEOF_VOIDP — 각 C 타입의 크기(바이트)
  • BUILD_RUBY_PLATFORM — 빌드된 플랫폼(예: "x86_64-linux"). RUBY_PLATFORM 참고.
  • Qfalse, Qnil, Qtrue, Qundef — Ruby 내부 Q 상수 값
  • RUBY_FREEruby_xfree() 함수의 주소
  • VERSION — 모듈 버전
  • WINDOWS — 호스트가 WIN32인지 여부를 나타내는 불리언

::dlopen (Public Class Method)

dlopen(library)Fiddle::Handlelibrary를 여는 새 핸들러를 만들고 Fiddle::Handle 인스턴스를 돌려줘요. librarynil을 주면 RTLD_DEFAULT에 해당하는 Fiddle::Handle::DEFAULT를 사용해요(man 3 dlopen 참고).

lib = Fiddle.dlopen(nil)

기본값은 OS에 따라 달라지며, 이미 로드된 모든 라이브러리에 대한 핸들을 제공해요. 대부분의 경우 libc 함수나 rb_str_new 같은 ruby 함수에 접근하는 데 쓸 수 있어요. Fiddle::Handle.new 참고.

# File ext/fiddle/lib/fiddle.rb, line 60
def dlopen library
  begin
    Fiddle::Handle.new(library)
  rescue DLError => error
    case RUBY_PLATFORM
    when /linux/
      case error.message
      when /\A(\/.+?): (?:invalid ELF header|file too short)/
        # This may be a linker script:
        # https://sourceware.org/binutils/docs/ld.html#Scripts
        path = $1
      else
        raise
      end
    else
      raise
    end

    File.open(path) do |input|
      input.each_line do |line|
        case line
        when /\A\s*(?:INPUT|GROUP)\s*\(\s*([^\s,\)]+)/
          # TODO: Should we support multiple files?
          return dlopen($1)
        end
      end
    end

    # Not found
    raise
  end
end

::dlunwrap

dlunwrap(addr) — 메모리 주소 addr에 저장된 Ruby 객체를 돌려줘요. (dlwrap의 역)

x = Object.new
Fiddle.dlwrap(x)
# => 4425504880
Fiddle.dlunwrap(_)
# => #<Object:0x...>

::dlwrap

dlwrap(val)val에 저장된 Ruby 객체의 메모리 주소를 돌려줘요.

x = Object.new
Fiddle.dlwrap(x)
# => 4425504880

val이 힙 할당 객체가 아닌 경우에는 태깅된 포인터 값을 돌려줘요.

Fiddle.dlwrap(123)
# => 247

::free

free(addr) — 주소 addr의 메모리를 해제해요.

::last_error

last_error() — 현재 실행 중인 스레드의 마지막 에러를 돌려줘요. 없으면 nil.

# File ext/fiddle/lib/fiddle.rb, line 35
def self.last_error
  Thread.current[:__FIDDLE_LAST_ERROR__]
end

::last_error=

last_error=(error) — 현재 실행 중인 스레드의 마지막 에러를 error로 설정해요.

# File ext/fiddle/lib/fiddle.rb, line 40
def self.last_error= error
  Thread.current[:__DL2_LAST_ERROR__] = error
  Thread.current[:__FIDDLE_LAST_ERROR__] = error
end

::malloc

malloc(size)size 바이트의 메모리를 할당하고, 할당된 메모리의 정수 주소를 돌려줘요.

::realloc

realloc(addr, size) — 메모리 주소 addr에 할당된 메모리의 크기를 size 바이트로 바꿔요. 재할당된 메모리의 주소를 돌려주는데, 전달한 주소와 다를 수 있어요.

::win32_last_error

win32_last_error() — 현재 실행 중인 스레드의 마지막 win32 에러를 돌려줘요. 없으면 nil.

::win32_last_error=

win32_last_error=(error) — 현재 스레드의 마지막 win32 에러를 error로 설정해요.

::win32_last_socket_error

win32_last_socket_error() — 현재 실행 중인 스레드의 마지막 win32 소켓 에러를 돌려줘요. 없으면 nil.

::win32_last_socket_error=

win32_last_socket_error=(error) — 현재 스레드의 마지막 win32 소켓 에러를 error로 설정해요.