Module: Ocran::LibraryDetector

Extended by:
Fiddle::Importer
Includes:
Fiddle::Win32Types
Defined in:
lib/ocran/library_detector.rb,
lib/ocran/library_detector_posix.rb

Constant Summary collapse

MAX_PATH =

Windows API functions for handling files may return long paths, with a maximum character limit of 32,767. “\?" prefix(4 characters) + long path(32762 characters) + NULL = 32767 characters learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation

32767
DEFAULT_HMODULE_BUFFER_SIZE =

The byte size of the buffer given as an argument to the EnumProcessModules function. This buffer is used to store the handles of the loaded modules. If the buffer size is smaller than the number of loaded modules, it will automatically increase the buffer size and call the EnumProcessModules function again. Increasing the initial buffer size can reduce the number of iterations required. learn.microsoft.com/en-us/windows/win32/psapi/enumerating-all-modules-for-a-process

1024

Class Method Summary collapse

Class Method Details

.detect_linuxObject



16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/ocran/library_detector_posix.rb', line 16

def self.detect_linux
  File.readlines("/proc/self/maps").filter_map do |line|
    # Format: address perms offset dev ino pathname
    # Example: 56206f09c000-56206f0bc000 r--p 00000000 101:02 1234567   /path/to/lib.so.1
    fields = line.split
    path = fields[5]

    # Only include absolute paths to shared libraries
    next unless path&.start_with?("/")
    next unless path.end_with?(".so") || path.match?(/\.so\.\d/)

    path
  end.uniq
end

.detect_macosObject



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/ocran/library_detector_posix.rb', line 31

def self.detect_macos
  # On macOS, we can use `vmmap` to list the memory regions, but it's slow.
  # A better way might be using ObjectSpace for loaded features, but that's for Ruby files.
  # For shared libraries (.dylib), we can use `otool -L` on the ruby binary,
  # but that only gives linked libraries, not dynamically loaded ones.
  # For now, let's use `vmmap` or similar if available, or just rely on $LOADED_FEATURES for Ruby-based extensions.

  # Using `vmmap` is one way to get mapped files:
  libs = []
  begin
    IO.popen(["vmmap", Process.pid.to_s], err: [:child, :out]) do |io|
      io.each_line do |line|
        # Look for lines with paths ending in .dylib
        if line =~ %r{ (/.*\.dylib)$}
          libs << $1
        end
      end
    end
  rescue Errno::ENOENT
    # vmmap not available
  end
  libs.uniq
end

.loaded_dllsObject

Detect loaded shared libraries on POSIX systems (Linux, macOS)



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/ocran/library_detector_posix.rb', line 6

def loaded_dlls
  dword = "L" # A DWORD is a 32-bit unsigned integer.
  bytes_needed = [0].pack(dword)
  bytes = DEFAULT_HMODULE_BUFFER_SIZE
  process_handle = GetCurrentProcess()
  handles = while true
              buffer = "\x00" * bytes
              if EnumProcessModules(process_handle, buffer, buffer.bytesize, bytes_needed) == 0
                raise "EnumProcessModules failed with error code #{GetLastError()}"
              end
              bytes = bytes_needed.unpack1(dword)
              if bytes <= buffer.bytesize
                break buffer.unpack("J#{bytes / Fiddle::SIZEOF_VOIDP}")
              end
            end
  str = "\x00".encode("UTF-16LE") * MAX_PATH
  handles.map do |handle|
    length = GetModuleFileNameW(handle, str, str.bytesize / 2)
    if length == 0
      raise "GetModuleFileNameW failed with error code #{GetLastError()}"
    end
    str[0, length].encode("UTF-8")
  end
end