Project

General

Profile

Actions

Bug #6790

closed

mingw-w64: rb_libruby_handle and libruby changes when extensions are loaded

Added by luislavena (Luis Lavena) almost 10 years ago. Updated almost 10 years ago.

Status:
Closed
Priority:
Normal
Target version:
ruby -v:
ruby 2.0.0dev (2012-07-25 trunk 36527) [x64-mingw32]
Backport:
[ruby-core:46743]

Description

=begin
Hello,

I started to look into this failure when running dl/handle tests:

test_initialize_noargs(DL::TestHandle):
DL::DLError: unknown symbol "rb_str_new"
C:/Users/Worker/Jenkins/workspace/git-ruby-trunk/test/dl/test_handle.rb:110:in []' C:/Users/Worker/Jenkins/workspace/git-ruby-trunk/test/dl/test_handle.rb:110:in test_initialize_noargs'

Above error only happens with compiled 64bits ruby.

After looking a bit more closer, it seems DllMain from libruby gets called several times when (({x64-msvcrt-ruby200.dll})) is loaded but also when extensions are loaded.

Adding some debug strings into DllMain like the following:

diff --git a/ruby.c b/ruby.c
index ab4b674..6d1a715 100644
--- a/ruby.c
+++ b/ruby.c
@@ -315,8 +315,22 @@ static HMODULE libruby;
BOOL WINAPI
DllMain(HINSTANCE dll, DWORD reason, LPVOID reserved)
{

  • HMODULE self;
  • char filename[MAX_PATH];
  • if (reason == DLL_PROCESS_ATTACH)
  •   libruby = dll;
    
  • {
  •    printf("DLL_PROCESS_ATTACH\n");
    
  •    self = GetModuleHandle(NULL);
    
  •    GetModuleFileName(self, filename, MAX_PATH);
    
  •    printf("self: %#x (%s)\n", self, filename);
    
  •    GetModuleFileName(dll, filename, MAX_PATH);
    
  •    printf("dll: %#x (%s)\n", dll, filename);
    
  •    libruby = dll;
    
  • }
    return TRUE;
    }

Produces the following:

32bits compilation:

C:\Users\Luis\Code\ruby\ruby\x86-installed>ruby -v -rdl -e "puts :ok"
DLL_PROCESS_ATTACH
self: 0x400000 (C:\Users\Luis\Code\ruby\ruby\x86-installed\bin\ruby.exe)
dll: 0x66780000 (C:\Users\Luis\Code\ruby\ruby\x86-installed\bin\msvcrt-ruby200.dll)
ruby 2.0.0dev (2012-07-25 trunk 36527) [i386-mingw32]
ok

64bits (mingw-w64):

C:\Users\Luis\Code\ruby\ruby\x64-installed>ruby -v -rdl -e "puts :ok"
DLL_PROCESS_ATTACH
self: 0x400000 (C:\Users\Luis\Code\ruby\ruby\x64-installed\bin\ruby.exe)
dll: 0x6f100000 (C:\Users\Luis\Code\ruby\ruby\x64-installed\bin\x64-msvcrt-ruby200.dll)
ruby 2.0.0dev (2012-07-25 trunk 36527) [x64-mingw32]
DLL_PROCESS_ATTACH
self: 0x400000 (C:\Users\Luis\Code\ruby\ruby\x64-installed\bin\ruby.exe)
dll: 0x1ee0000 (C:\Users\Luis\Code\ruby\ruby\x64-installed\lib\ruby\2.0.0\x64-mingw32\enc\encdb.so)
DLL_PROCESS_ATTACH
self: 0x400000 (C:\Users\Luis\Code\ruby\ruby\x64-installed\bin\ruby.exe)
dll: 0x6aa00000 (C:\Users\Luis\Code\ruby\ruby\x64-installed\lib\ruby\2.0.0\x64-mingw32\enc\iso_8859_1.so)
DLL_PROCESS_ATTACH
self: 0x400000 (C:\Users\Luis\Code\ruby\ruby\x64-installed\bin\ruby.exe)
dll: 0x68080000 (C:\Users\Luis\Code\ruby\ruby\x64-installed\lib\ruby\2.0.0\x64-mingw32\enc\trans\transdb.so)
DLL_PROCESS_ATTACH
self: 0x400000 (C:\Users\Luis\Code\ruby\ruby\x64-installed\bin\ruby.exe)
dll: 0x623c0000 (C:\Users\Luis\Code\ruby\ruby\x64-installed\lib\ruby\2.0.0\x64-mingw32\dl.so)
DLL_PROCESS_ATTACH
self: 0x400000 (C:\Users\Luis\Code\ruby\ruby\x64-installed\bin\ruby.exe)
dll: 0x63d80000 (C:\Users\Luis\Code\ruby\ruby\x64-installed\lib\ruby\2.0.0\x64-mingw32\fiddle.so)
DLL_PROCESS_ATTACH
self: 0x400000 (C:\Users\Luis\Code\ruby\ruby\x64-installed\bin\ruby.exe)
dll: 0x6e6c0000 (C:\Users\Luis\Code\ruby\ruby\x64-installed\lib\ruby\2.0.0\x64-mingw32\enc\utf_16le.so)
DLL_PROCESS_ATTACH
self: 0x400000 (C:\Users\Luis\Code\ruby\ruby\x64-installed\bin\ruby.exe)
dll: 0x6a340000 (C:\Users\Luis\Code\ruby\ruby\x64-installed\lib\ruby\2.0.0\x64-mingw32\enc\trans\single_byte.so)
DLL_PROCESS_ATTACH
self: 0x400000 (C:\Users\Luis\Code\ruby\ruby\x64-installed\bin\ruby.exe)
dll: 0x1f10000 (C:\Users\Luis\Code\ruby\ruby\x64-installed\lib\ruby\2.0.0\x64-mingw32\enc\trans\utf_16_32.so)
ok

I just tested a simple project which loads multiple DLLs and DllMain of each DLL do not get invoked several times.

Since each extension depends on (({x86-msvcrt-ruby200.dll})), perhaps that is the reason why (({DllMain})) is invoked multiple times.

Maybe there is something I'm missing? Perhaps a workaround will be set (({libruby})) only once?
=end

Actions #1

Updated by nobu (Nobuyoshi Nakada) almost 10 years ago

Seems it occurs only in x64-mingw; and does not occur when loading the DLL by x64-mswin ruby.exe.
Needs more investigation.

Updated by luislavena (Luis Lavena) almost 10 years ago

nobu (Nobuyoshi Nakada) wrote:

Seems it occurs only in x64-mingw; and does not occur when loading the DLL by x64-mswin ruby.exe.
Needs more investigation.

Do you think this simplification is good enough?

https://github.com/luislavena/experiments/tree/master/dll-handles

DllMain is only invoked once on this example, which might indicate something going on on Ruby.

After a GDB session, seems that dln.c line 1271 (LoadLibrary) is causing x64-msvcrt-ruby200.dll be loaded again.

Will keep investigating on this.

Updated by phasis68 (Heesob Park) almost 10 years ago

After some investigating, I found this bug is due to exporting "DllMain" symbol of x64-msvcrt-ruby200.dll.

You can see the following line in the x64-msvcrt-ruby200.def.

VERSION 2.0
EXPORTS
DllMain
FD_CLR=rb_w32_fdclr
FD_ISSET=rb_w32_fdisset
...

I think that DllMain should not be exported.

Here is a simple workaround for win32/mkexports.rb

diff --git a/mkexports.rb b/mkexports.rb.new
index 3a1a91a..9dc8a19 100644
--- a/mkexports.rb
+++ b/mkexports.rb.new
@@ -38,6 +38,7 @@ class Exports
winapis = {}
syms["ruby_sysinit_real"] = "ruby_sysinit"
each_export(objs) do |internal, export|

  •  next if internal == 'DllMain'
     syms[internal] = export
     winapis[$1] = internal if /^_?(rb_w32_\w+)(?:@\d+)?$/ =~ internal
    
    end

With the patched and rebuilt version, I confirmed this bug is solved.

Updated by usa (Usaku NAKAMURA) almost 10 years ago

=begin
Hmm, I see.

Is this patch OK?

Index: win32/mkexports.rb

--- win32/mkexports.rb (revision 36541)
+++ win32/mkexports.rb (working copy)
@@ -116,7 +116,7 @@ class Exports::Mswin < Exports
is_data = !$1
if noprefix or /^[@_]/ =~ l
next if /(?!^)@.*@/ =~ l || /@:xdigit:{8,16}$/ =~ l ||

  •          /^_(?:Init_|.*_threadptr_|DllMain@)/ =~ l
    
  •          /^_?(?:Init_|.*_threadptr_|DllMain\b)/ =~ l
    

l.sub!(/^[@_]/, '') if /@\d+$/ !~ l
elsif !l.sub!(/^(\S+) ([^@?`']*)$/, '\1')
next
@@ -150,7 +150,7 @@ class Exports::Cygwin < Exports
def each_export(objs)
symprefix = RbConfig::CONFIG["SYMBOL_PREFIX"]
symprefix.strip! if symprefix

  • re = /\s(?:(T)|:upper:)\s#{symprefix}((?!Init_|.threadptr|DllMain@).)$/
  • re = /\s(?:(T)|:upper:)\s#{symprefix}((?!Init_|.threadptr|DllMain\b).)$/
    objdump(objs) do |l|
    next if /@.*@/ =~ l
    yield $2, !$1 if re =~ l

=end

Actions #5

Updated by usa (Usaku NAKAMURA) almost 10 years ago

  • Status changed from Assigned to Closed
  • % Done changed from 0 to 100

This issue was solved with changeset r36544.
Luis, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.


  • win32/mkexports.rb: should not export DllMain().
    reported by luis at [ruby-core:46743] [Bug #6790], solved by
    Heesob Park, and confirmed by nobu.

Updated by luislavena (Luis Lavena) almost 10 years ago

Thank you Heesob Park, Usaku Nakamura and Nobu for fixing this!

Actions

Also available in: Atom PDF