* libltdl/config/ltmain.m4sh (func_enable_tag): allow --verbose to set opt_verbose (func_win32_dllname_for_implib): New function. (func_mode_link) [cygwin|mingw]: Use linklib (that is, import lib) as dlpreopen file, rather than DLL. (func_generate_dlsyms) [cygwin|mingw]: Use func_win32_dllname_for_implib to extract DLL name from import library. Also, properly extract dlsyms from the import library. * libltdl/m4/libtool.m4 (_LT_LINKER_SHLIBS) [cygwin|mingw][C++]: Set exclude_expsyms correctly for $host. (_LT_LINKER_SHLIBS) [cygwin|mingw|pw32][C]: Set exclude_expsyms correctly for $host. Enable export_symbols_cmds to identify DATA exports by _nm_ prefix. --- I've had this patch sitting around since May, but some personal issues have prevented me from following up on it...
Corrects: old testsuite failure demo-exec (in demo-shared group). When configuring with --disable-static, dlpreopen is very confused. First, libtool tries to extract the symbols -- using $NM and $global_symbols_pipe -- from the DLL. That works...poorly: Here's a snippet from helldl.exeS.c when --disable-static: ====== /* External symbol declarations for the compiler. */ extern int _CTOR_LIST__(); extern int _DTOR_LIST__(); extern char _RUNTIME_PSEUDO_RELOC_LIST_END__; extern char _RUNTIME_PSEUDO_RELOC_LIST__; extern int __CTOR_LIST__(); extern int __DTOR_LIST__(); ... extern int printf(); extern int puts(); extern int realloc(); ... lt__PROGRAM__LTX_preloaded_symbols[] = { { "@PROGRAM@", (void *) 0 }, {"cyghello-2.dll", (void *) 0}, {"_CTOR_LIST__", (void *) &_CTOR_LIST__}, {"_DTOR_LIST__", (void *) &_DTOR_LIST__}, ... {"puts", (void *) &puts}, {"realloc", (void *) &realloc}, {0, (void *) 0} }; ====== That's bad (but, at least foo(), hello(), and nothing ARE present). So, first, I tried special-casing for cygwin|mingw, and using the import library for dlpreopen. That works a little better... ====== /* External symbol declarations for the compiler. */ extern char _head_cyghello_2_dll; extern char _imp__foo; extern char _imp__hello; extern char _imp__nothing; extern char _nm__nothing; extern char cyghello_2_dll_iname; extern int foo(); extern int hello(); ... lt__PROGRAM__LTX_preloaded_symbols[] = { { "@PROGRAM@", (void *) 0 }, {"libhello.dll.a", (void *) 0}, {"_head_cyghello_2_dll", (void *) &_head_cyghello_2_dll}, {"_imp__foo", (void *) &_imp__foo}, ... {"foo", (void *) &foo}, {"hello", (void *) &hello}, {0, (void *) 0} }; ====== But there are still four problems: 1. 'nothing' is missing 2. the _imp_* and _nm_* symbols are not removed when extracting the symbols from the library for dlpreopen 3. the _head_* / *_dll_iname symbols are not removed 4. the library name itself is wrong: the second entry in lt__PROGRAM__LTX_preloaded_symbols is used by ltdl to determine which DLL to dlopen. libhello.dll.a is not a DLL and can't be dlopened. By way of comparison, the (correct) contents of helldl.exeS.c for a static library are: ====== /* External symbol declarations for the compiler. */ extern int foo(); extern int hello(); extern char nothing; ... lt_dlsymlist lt__PROGRAM__LTX_preloaded_symbols[] = { { "@PROGRAM@", (void *) 0 }, {"libhello.a", (void *) 0}, {"hello", (void *) &hello}, {"foo", (void *) &foo}, {"nothing", (void *) ¬hing}, {0, (void *) 0} }; ======= Problem #1 is addressed by using the same "improved" export_symbols_cmds for cygwin|mingw C, as is already used for cygwin|mingw C++. Problem #2 is addressed by using the following $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' AFTER $global_symbol_pipe when inferring the exported symbols of a module from the import library. We do not modify $global_symbol_pipe itself, however (we don't want $global_symbol_pipe to strip out _nm_ symbols, because $export_symbols_cmds needs them to determine which symbols to mark as DATA). Problem #3 is addressed by providing a custom cygwin|mingw value for exclude_expsyms. Problem #4 is...tricky. We need a way to extract the name of the associated DLL from an import library. I've implemented two: a) patch for dlltool to add --identify option http://sourceware.org/ml/binutils/2008-11/msg00063.html b) fallback when dlltool is not available, or doesn't have the identify option. This workaround uses objdump and parses the textual representation. With these changes, I get helldl.exeS.c with the following contents, when --disable-static: ====== /* External symbol declarations for the compiler. */ extern int foo(); extern int hello(); extern char nothing; ... lt__PROGRAM__LTX_preloaded_symbols[] = { { "@PROGRAM@", (void *) 0 }, {"cyghello-2.dll", (void *) 0}, {"nothing", (void *) ¬hing}, {"hello", (void *) &hello}, {"foo", (void *) &foo}, {0, (void *) 0} }; ====== Finally, the attached patch also improves the following (some of which I forgot to mention in the change history; I will address that during the inevitable rewrites of this patch and/or merge to master). A) libtool --verbose does not actually set opt_verbose. In fact, nothing ever sets opt_verbose true. Should all uses of opt_verbose be replaced by !opt_silent, or should (as I have done in this patch) --verbose set both opt_silent false, and opt_verbose true? B) func_win32_libid() gives some confusing errors to users when (a) using recursive make, and (b) MAKEFLAGS does not contain $OBJDUMP. Add a diagnostic error message, rather than allowing $SED to die a horrible death. End result: demo-exec after demo-shared works (and, I'll bet, but haven't tested, that a shared-only build of m4-2.0 will work now...) I've only bootstrapped and run those specific tests from a git-based tree. However, I've done a full testsuite run with these changes on a 2.2.6a-based tree, with expected results (all old tests pass, and only 25 and 73 in the new suite fail). Comments? libltdl/config/ltmain.m4sh | 146 +++++++++++++++++++++++++++++++++++++++++--- libltdl/m4/libtool.m4 | 6 +- 2 files changed, 142 insertions(+), 10 deletions(-) diff --git a/libltdl/config/ltmain.m4sh b/libltdl/config/ltmain.m4sh index 696b6ab..da5f668 100644 --- a/libltdl/config/ltmain.m4sh +++ b/libltdl/config/ltmain.m4sh @@ -334,6 +334,7 @@ func_enable_tag () --verbose| -v) preserve_args="$preserve_args $opt" opt_silent=false + opt_verbose=true ;; --tag) test "$#" -eq 0 && func_missing_arg "$opt" && break @@ -1991,10 +1992,36 @@ extern \"C\" { func_verbose "extracting global C symbols from \`$dlprefile'" func_basename "$dlprefile" name="$func_basename_result" - $opt_dry_run || { - eval '$ECHO ": $name " >> "$nlist"' - eval "$NM $dlprefile 2>/dev/null | $global_symbol_pipe >> '$nlist'" - } + case $host in + *cygwin | *mingw* ) + # if an import library, we need to obtain dlname + if eval $file_magic_cmd \"\$dlprefile\" 2>/dev/null | + $SED -e 10q | $EGREP "import" >/dev/null; then + dllname=`func_win32_dllname_for_implib "$dlprefile"` + $opt_dry_run || { + if test -n "$dllname" ; then + eval '$ECHO ": $dllname " >> "$nlist"' + else + func_warning "Could not compute DLL name from $name" + eval '$ECHO ": $name " >> "$nlist"' + fi + eval "$NM $dlprefile 2>/dev/null | $global_symbol_pipe | + $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" + } + else + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + eval "$NM $dlprefile 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + fi + ;; + * ) + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + eval "$NM $dlprefile 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + ;; + esac done $opt_dry_run || { @@ -2166,6 +2193,11 @@ func_win32_libid () win32_libid_type="x86 archive import" ;; *ar\ archive*) # could be an import, or static + # In recursive makes, OBJDUMP is often omitted from the + # passed-down MAKEFLAGS. As a courtesy, flag an error when + # this happens (it's more humane than allowing the sed + # expression below to fail). + test -z "${OBJDUMP}" && func_fatal_error "\$OBJDUMP is not defined" if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | $EGREP 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then win32_nmres=`eval $NM -f posix -A $1 | @@ -2198,6 +2230,91 @@ func_win32_libid () } +# func_win32_dllname_for_implib implib +# Obtain the name of the DLL associated with the +# specified import library. +# +func_win32_dllname_for_implib () +{ + $opt_debug + f_win32_d_for_i_implib="$1" + f_win32_d_for_i_can_use_dlltool=no + f_win32_d_for_i_dllname= + + # In recursive makes, DLLTOOL is often omitted from the + # passed-down MAKEFLAGS. As a courtesy, warn when this + # happens but don't fail; we have a workaround. + if test -z "${DLLTOOL}"; then + func_warning "\$DLLTOOL is not defined" + else + # check for --identify option + if eval $DLLTOOL --help | $EGREP -- '--identify' >/dev/null ; then + f_win32_d_for_i_can_use_dlltool=yes + fi + fi + + if test "$f_win32_d_for_i_can_use_dlltool" = "yes"; then + f_win32_d_for_i_dllname=`$DLLTOOL --identify "$f_win32_d_for_i_implib" 2>/dev/null` + fi + + # use fallback implementation when dlltool is not available, or + # does not have the --identify option. + if test -z "$f_win32_d_for_i_dllname"; then + # make sure argument is actually an import library + if eval $file_magic_cmd \"\$f_win32_d_for_i_implib\" 2>/dev/null | + $SED -e 10q | $EGREP "import" >/dev/null; then + # gcc puts dllname in the .idata$7 section of ONE member + # of the import library -- but the name of that member is + # random. No other member contains an .idata$7 section. + # So, use objdump to print the contents. We get something + # like the following (blank lines elided): + # + # |In archive /usr/lib/libncurses++.dll.a: + # |d000253.o: file format pe-i386 + # |Contents of section .idata$7: + # | 0000 6379676e 63757273 65732b2b 2d382e64 cygncurses++-8.d + # | 0010 6c6c0000 ll..____________ + # |d000006.o: file format pe-i386 + # |d000252.o: file format pe-i386 + # |Contents of section .idata$7: + # | 0000 00000000 ....____________ + # + # where '_' represents a space character. So, we delete all + # lines that have less than 43 characters, and chomp the + # first 43 characters of the remaining lines. This gives us + # + # |cygncurses++-8.d + # |ll..____________ + # |....____________ + # + # We are not guaranteed that the name we want is first. So, + # remove all newlines, then remove all sequences of two + # or more . characters, then remove all sequences of two + # or more whitespace characters. Finally, remove leading and + # trailing whitespace. This would be simpler if we could + # assume that the dllname does not contain whitespace, but we + # DO assume the dllname doesn't contain *multiple* adjacent + # whitespace, nor *multiple* adjacent . characters. + + + # In recursive makes, OBJDUMP is often omitted from the + # passed-down MAKEFLAGS. As a courtesy, flag an error when + # this happens (it's more humane than allowing the sed + # expression below to fail). + test -z "${OBJDUMP}" && func_fatal_error "\$OBJDUMP is not defined" + + f_win32_d_for_i_dllname=`$OBJDUMP -s --section '.idata$7' $f_win32_d_for_i_implib | + $SED -e '/^.\{43\}/!d' -e 's/^.\{43\}//' | + $SED -e ':a;N;$!ba;s/\n//g' -e 's/\.\.\.*//g' \ + -e 's/[ ][ ][ ]*//g' \ + -e 's/^[ ]*//' -e 's/[ ]*$//'` + fi + fi + $ECHO "$f_win32_d_for_i_dllname" +} + + + # func_extract_an_archive dir oldlib func_extract_an_archive () @@ -5052,11 +5169,24 @@ func_mode_link () # that they are being used correctly in the link pass. test -z "$libdir" && \ dlpreconveniencelibs="$dlpreconveniencelibs $dir/$old_library" - # Otherwise, use the dlname, so that lt_dlopen finds it. - elif test -n "$dlname"; then - newdlprefiles="$newdlprefiles $dir/$dlname" + # Otherwise, use the dlname, so that lt_dlopen finds it -- + # except on mingw|cygwin, where we must use the import library + # for symbol extraction. Therefore, on those platforms, the name + # needed by lt_dlopen is extracted from the import library via + # func_win32_dllname_for_implib. else - newdlprefiles="$newdlprefiles $dir/$linklib" + case "$host" in + *cygwin* | *mingw* ) + newdlprefiles="$newdlprefiles $dir/$linklib" + ;; + * ) + if test -n "$dlname"; then + newdlprefiles="$newdlprefiles $dir/$dlname" + else + newdlprefiles="$newdlprefiles $dir/$linklib" + fi + ;; + esac fi fi # $pass = dlpreopen diff --git a/libltdl/m4/libtool.m4 b/libltdl/m4/libtool.m4 index caf88b1..0ce770c 100644 --- a/libltdl/m4/libtool.m4 +++ b/libltdl/m4/libtool.m4 @@ -4170,6 +4170,7 @@ m4_require([_LT_TAG_COMPILER])dnl AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) m4_if([$1], [CXX], [ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] case $host_os in aix[[4-9]]*) # If we're using GNU nm, then we don't want the "-C" option. @@ -4185,12 +4186,12 @@ m4_if([$1], [CXX], [ ;; cygwin* | mingw* | cegcc*) _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(exclude_expsyms, $1)=['_head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac - _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] ], [ runpath_var= _LT_TAGVAR(allow_undefined_flag, $1)= @@ -4329,7 +4330,8 @@ _LT_EOF _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 '\''/^[[BCDGRS]][[ ]]/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/;/^.*[[ ]]__nm__/s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['_head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] 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}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' -- 1.6.0.2