Subject: cygwin, libtool, dlpreopen, and .rdata
From: Charles Wilson <[email-masked]>
To: [email-masked]
Cc: [email-masked]
Date: Wed, 22 Sep 2004 02:31:27 -0400

[cygwin list: I'm CC'ing this so that it goes into the archives, but 
it's pretty technical about the innards of libtool...]

With newer gcc's (cygwin version numbers 3.3.3-3, 3.4.1-1, but not 
3.3.1-3), const variables are placed in an .rdata section.  This causes 
problems when those variables contain references to OTHER vars that are 
imported from a dll -- because the runtime relocation machinery can't 
fixup the address, since the variable holding the address is const -- 
and is in .rdata!

Concrete example: the demo test program from libtool itself.  When 
building helldl, libtool creates the following virtual lookup table:

helldl.exeS.c (important bits only):
*************************************
/* External symbol declarations for the compiler. */
extern int foo;
extern int hello;
extern int nothing;

/* The mapping between symbol names and symbols. */
const struct {
   const char *name;
   lt_ptr address;
}
lt_preloaded_symbols[] =
{
   {"libhello.a", (lt_ptr) 0},
   {"hello", (lt_ptr) &hello},
   {"foo", (lt_ptr) &foo},
   {"nothing", (lt_ptr) &nothing},
   {0, (lt_ptr) 0}
};
*************************************

Because lt_preloaded_symbols[] is an array of const structs, it is 
placed in .rdata.  However, the "nothing" symbol is a DATA export from 
cyghello-2.dll -- so the runtime psuedo-reloc machinery needs to fixup 
the address stored in "lt_preloaded_symbols[3].address" to point to the 
actual, relocated memory location of the "nothing" variable.

But it can't, because .rdata is non-writable.  However, this is a 
*runtime* error; the *link* succeeds.  But when you run the app, you get 
a popup window declaring:

"The application failed to initialize properly (0xc0000005). Click on OK 
to terminate the application."

Note that
  (1) the "nothing" relocation works fine for "normal" links; hell.exe 
works ok, and imports "nothing" correctly.

  (2) the lt_preloaded_symbol[] object works fine, as long as it doesn't 
contain references to DATA items imported from DLL's: staticly linked 
helldl.exe works ok, and dynamically linked helldl.exe works IF 
"nothing" is expunged from the application code (manually removed from 
helldl.exeS.c, and from dlmain.c)

  (3) even with newer compilers, if lt_preloaded_symbol[] is in .data 
(by changing its definition from "const struct" to "struct") then 
everything works fine.  Note that you only need to do this in 
helldl.exeS.c; the application (dlmain.c, in this case) can still 
declare 'extern const struct ....').

*************************************
I see two solutions: a short term libtool-focused workaround, and a 
longer term general solution.

The short term workaround is to simply change lt_preloaded_symbol[]'s 
constness in the *.exeS.c file, so that it goes in the .data section. 
Mabye this could be done for cygwin/mingw only (but application code can 
remain unchanged, declaring 'extern const struct ...').  But that won't 
fix any OTHER problems out there which put the address of DLL DATA 
imports into const variables.

The long term solution is...er...hard.  Somehow, ld/(gcc?) should be 
modified to detect that a dllreloc is being put into an .rdata, and move 
the entire enclosing variable into .data.  This is difficult: 
lt_preloaded_symbol[3].address can be flagged easily, but how can the 
linker figure out that the whole lt_preloaded_symbol variable should be 
moved from .rdata to .data?  And didn't the compiler put the symbol into 
.rdata in the first place -- so maybe gcc is the one who should detect 
this?  But gcc doesn't know how the link will be performed: it doesn't 
know that "nothing" is a DATA import from a DLL, but "foo" is a function 
import from a DLL!  (Worse, you might link to a STATIC library and not a 
DLL at all, so lt_preloaded_symbol could STAY in .rdata in that case.)

This might be one of those "doctor, it hurts when I do this/OK, don't do 
that" situations.

Note, I found this problem in libtool-1.5.x, but it exists in HEAD, too. 
  The following patch is against HEAD...

--
Chuck

2004-09-21  Charles Wilson  <[email-masked]>

	* config/ltmain.in (func_generate_dlsyms): addresses
	in _preloaded_symbols[] cannot go into .rdata section
	if symbols are DATA imported from DLL, on windows,
	because runtime relocations must happen.
	* m4/libtool.m4 (_LT_LINKER_SHLIBS([TAGNAME])):
	build exports for symbols in .rdata sections

Index: config/ltmain.in
===================================================================
RCS file: /cvsroot/libtool/libtool/config/ltmain.in,v
retrieving revision 1.24
diff -u -r1.24 ltmain.in
--- config/ltmain.in	17 Sep 2004 17:12:17 -0000	1.24
+++ config/ltmain.in	22 Sep 2004 05:23:32 -0000
@@ -1064,7 +1064,26 @@
 	  $echo >> "$output_objdir/$my_dlsyms" "\
 
 /* The mapping between symbol names and symbols.  */
+"
+
+	    case $host in
+	    *cygwin* | *mingw* )
+	  $echo >> "$output_objdir/$my_dlsyms" "\
+/* DATA imports from DLLs on WIN32 can't be const, because
+   runtime relocations are performed -- see ld's documentation
+   on pseudo-relocs */
+struct {
+"
+	      ;;
+	    * )
+	  $echo >> "$output_objdir/$my_dlsyms" "\
 const struct {
+"
+	      ;;
+	    esac
+
+
+	  $echo >> "$output_objdir/$my_dlsyms" "\
    const char *name;
    void *address;
 }
Index: m4/libtool.m4
===================================================================
RCS file: /cvsroot/libtool/libtool/m4/libtool.m4,v
retrieving revision 1.106
diff -u -r1.106 libtool.m4
--- m4/libtool.m4	17 Sep 2004 13:54:05 -0000	1.106
+++ m4/libtool.m4	22 Sep 2004 05:23:37 -0000
@@ -3414,7 +3414,7 @@
     _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds"
   ;;
   cygwin* | mingw*)
-    _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGS]] /s/.* \([[^ ]]*\)/\1 DATA/;/^.* __nm__/s/^.* __nm__\([[^ ]]*\) [[^ ]]*/\1 DATA/;/^I /d;/^[[AITW]] /s/.* //'\'' | sort | uniq > $export_symbols'
+    _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]] /s/.* \([[^ ]]*\)/\1 DATA/;/^.* __nm__/s/^.* __nm__\([[^ ]]*\) [[^ ]]*/\1 DATA/;/^I /d;/^[[AITW]] /s/.* //'\'' | sort | uniq > $export_symbols'
   ;;
   *)
     _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
@@ -3530,7 +3530,7 @@
       _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
       _LT_TAGVAR(always_export_symbols, $1)=no
       _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
-      _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGS]] /s/.* \([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]] /s/.* //'\'' | sort | uniq > $export_symbols'
+      _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]] /s/.* \([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]] /s/.* //'\'' | sort | uniq > $export_symbols'
 
       if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
         _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--image-base=0x10000000 ${wl}--out-implib,$lib'

--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Problem reports:       http://cygwin.com/problems.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/