Bug #6790
closedmingw-w64: rb_libruby_handle and libruby changes when extensions are loaded
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
        
           Updated by nobu (Nobuyoshi Nakada) over 13 years ago
          Updated by nobu (Nobuyoshi Nakada) over 13 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) over 13 years ago
          Updated by luislavena (Luis Lavena) over 13 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) over 13 years ago
          Updated by phasis68 (Heesob Park) over 13 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|
- 
 endnext if internal == 'DllMain' syms[internal] = export winapis[$1] = internal if /^_?(rb_w32_\w+)(?:@\d+)?$/ =~ internal
With the patched and rebuilt version, I confirmed this bug is solved.
        
           Updated by usa (Usaku NAKAMURA) over 13 years ago
          Updated by usa (Usaku NAKAMURA) over 13 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
        
           Updated by usa (Usaku NAKAMURA) over 13 years ago
          Updated by usa (Usaku NAKAMURA) over 13 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) over 13 years ago
          Updated by luislavena (Luis Lavena) over 13 years ago
          
          
        
        
      
      Thank you Heesob Park, Usaku Nakamura and Nobu for fixing this!