Hi all.

I am having some very strange problems linking what
I think should be a very simple dll. I am using
libtool to cross compile with the mingw compiler
to generate a windows dll from Linux.

I have created a very simple "bare minimium"
example that reproduces the linker error.
I am going to show the more important
file here and attach the tar file
in case anyone wants to try it
(Note: it would also work with a cross
or native cygwin, with the -mno-cygwin swtich).

I am using autoconf, automake, and libtool
out of the CVS.


Ok here goes:

% cat configure.in
AC_INIT(foo.c)

AM_INIT_AUTOMAKE(foo, 0.1)
AM_MAINTAINER_MODE

AC_LANG_C
AC_PROG_CC

dnl     This is a hack to work around a bug in autoconf/libtool
dnl     We need to export CC and CC_FOR_BUILD so that libtool
dnl     uses the compiler found by ./configure and we can cross.

export CC

if test "x$cross_compiling" = "xyes" ; then
  AC_CHECK_PROG(CC_FOR_BUILD, gcc, gcc, cc)
  export CC_FOR_BUILD
fi

AC_LIBTOOL_WIN32_DLL
AC_PROG_LIBTOOL

AC_OUTPUT([Makefile other/Makefile])



% cat Makefile.am 
SUBDIRS = other

lib_LTLIBRARIES = libfoo.la

libfoo_la_LIBADD = other/libbar.la

libfoo_la_SOURCES = foo.c

libfoo_la_LDFLAGS = -no-undefined -avoid-version


% cat foo.c 
void foo() {
  bar();
}

% cat other/Makefile.am
lib_LTLIBRARIES = libbar.la

libbar_la_SOURCES = bar.c

% cat other/bar.c 
void bar() {}


Looks good so far, libtool should compile
a static lib named other/libbar.a and
then link it along with foo.o to
create libfoo.dll.

Here is the output I get:



.../configure --host=i386-mingw32msvc

checking for i386-mingw32msvc-gcc... i386-mingw32msvc-gcc
checking whether the C compiler works... yes
checking whether we are cross compiling... yes
...

checking for Cygwin environment... no
checking for mingw32 environment... yes
checking for EMX OS/2 environment... no
checking for executable suffix... .exe
checking for object suffix... o
...
checking build system type... i686-pc-linux-gnu
checking host system type... i386-pc-mingw32msvc
...
checking for ld used by GCC... 
/usr/local/project/install/Xmingwin/i386-mingw32m
svc/bin/ld
checking if the linker 
(/usr/local/project/install/Xmingwin/i386-mingw32msvc/bin
/ld) is GNU ld... yes
checking for BSD-compatible nm... 
/usr/local/project/install/Xmingwin/bin/i386-m
ingw32msvc-nm -B
checking for i386-mingw32msvc-ranlib... i386-mingw32msvc-ranlib
checking for i386-mingw32msvc-strip... i386-mingw32msvc-strip
checking for i386-mingw32msvc-dlltool... i386-mingw32msvc-dlltool
checking for i386-mingw32msvc-as... i386-mingw32msvc-as
checking for i386-mingw32msvc-objdump... i386-mingw32msvc-objdump
...
checking if libtool should supply DllMain function... no
checking how to link DLLs... -mdll
checking command to parse 
/usr/local/project/install/Xmingwin/bin/i386-mingw32ms
vc-nm -B output... ok
checking if libtool supports shared libraries... yes
checking if package supports dlls... yes
checking whether to build shared libraries... yes
checking whether to build static libraries... yes
creating libtool
...



% make
Making all in other
make[1]: Entering directory `/home/mo/build_cross_libtool/other'
source='../../cross_libtool/other/bar.c' object='bar.lo' libtool=yes \
depfile='.deps/bar.Plo' tmpdepfile='.deps/bar.TPlo' \
depmode=gcc /bin/sh ../../cross_libtool/depcomp \
/bin/sh ../libtool --mode=compile i386-mingw32msvc-gcc -DPACKAGE=\"foo\" 
-DVERSION=\"0.1\"  -I. -I../../cross_libtool/other     -mno-cygwin -c -o 
bar.lo `test -f ../../cross_libtool/other/bar.c || echo 
'../../cross_libtool/other/'`../../cross_libtool/other/bar.c
mkdir .libs
i386-mingw32msvc-gcc -DPACKAGE=\"foo\" -DVERSION=\"0.1\" -I. 
-I../../cross_libtool/other -mno-cygwin -c 
../../cross_libtool/other/bar.c -Wp,-MD,.deps/bar.TPlo  
-DDLL_EXPORT -DPIC -o .libs/bar.o
i386-mingw32msvc-gcc -DPACKAGE=\"foo\" -DVERSION=\"0.1\" -I. 
-I../../cross_libtool/other -mno-cygwin -c 
../../cross_libtool/other/bar.c -Wp,-MD,.deps/bar.TPlo -o bar.o 
>/dev/null 2>&1
/bin/sh ../libtool --mode=link i386-mingw32msvc-gcc  -mno-cygwin   -o 
libbar.la -rpath /usr/local/lib  bar.lo  
libtool: link: warning: undefined symbols not allowed in 
i386-pc-mingw32msvc shared libraries
ar cru .libs/libbar.a  bar.o
creating libbar.la
(cd .libs && rm -f libbar.la && ln -s ../libbar.la libbar.la)
make[1]: Leaving directory `/home/mo/build_cross_libtool/other'
make[1]: Entering directory `/home/mo/build_cross_libtool'
source='../cross_libtool/foo.c' object='foo.lo' libtool=yes \
depfile='.deps/foo.Plo' tmpdepfile='.deps/foo.TPlo' \
depmode=gcc /bin/sh ../cross_libtool/depcomp \
/bin/sh ./libtool --mode=compile i386-mingw32msvc-gcc -DPACKAGE=\"foo\" 
-DVERSION=\"0.1\"  -I. -I../cross_libtool     -mno-cygwin -c -o foo.lo 
`test -f ../cross_libtool/foo.c || echo 
'../cross_libtool/'`../cross_libtool/foo.c
i386-mingw32msvc-gcc -DPACKAGE=\"foo\" -DVERSION=\"0.1\" -I. 
-I../cross_libtool 
-mno-cygwin -c ../cross_libtool/foo.c -Wp,-MD,.deps/foo.TPlo  
-DDLL_EXPORT -DPIC
 -o .libs/foo.o
i386-mingw32msvc-gcc -DPACKAGE=\"foo\" -DVERSION=\"0.1\" -I. 
-I../cross_libtool 
-mno-cygwin -c ../cross_libtool/foo.c -Wp,-MD,.deps/foo.TPlo -o foo.o 
>/dev/null
 2>&1
/bin/sh ./libtool --mode=link i386-mingw32msvc-gcc  -mno-cygwin   -o 
libfoo.la -rpath /usr/local/lib -no-undefined -avoid-version foo.lo 
other/libbar.la 

*** Warning: This library needs some functionality provided by 
other/libbar.la.
*** I have the capability to make that library automatically link in when
*** you link to this library.  But I can only do this if you have a
*** shared version of the library, which you do not appear to have.
rm -fr  .libs/libfoo.dll-base .libs/libfoo.dll-def .libs/libfoo.exp
generating symbol list for `libfoo.la'
      i386-mingw32msvc-dlltool --export-all --exclude-symbols 
DllMain@12,_cygwin
_dll_entry@12,_cygwin_noncygwin_dll_entry@12,DllMainCRTStartup@12,DllEntryPoint@12 
--output-def .libs/libfoo.dll-def  .libs/foo.o 

      sed -e "1,/EXPORTS/d" -e "s/ @ [0-9]*//" -e "s/ *;.*$//" < 
.libs/libfoo.dl
l-def > .libs/libfoo.exp
echo EXPORTS > .libs/libfoo.dll-def

      _lt_hint=1;
      cat .libs/libfoo.exp | while read symbol; do
        set dummy $symbol;
        case $# in
          2) echo "     $2 @ $_lt_hint ; " >> .libs/libfoo.dll-def;;
          *) echo "     $2 @ $_lt_hint $3 ; " >> .libs/libfoo.dll-def;;
        esac;
        _lt_hint=`expr 1 + $_lt_hint`;
      done
      i386-mingw32msvc-gcc -Wl,--base-file,.libs/libfoo.dll-base  
-Wl,-e,_DllMainCRTStartup@12 -o .libs/libfoo.dll  .libs/foo.o  
/usr/local/project/install/Xmingwin/bin/../lib/gcc-lib/i386-mingw32msvc/2.95.2/../../../../i386-mingw32msvc/bin/ld:
 warning: cannot find entry symbol _DllMainCRTStartup@12; defaulting to 00401000
.libs/foo.o(.text+0x7):foo.c: undefined reference to `bar'
/usr/local/project/install/Xmingwin/bin/../lib/gcc-lib/i386-mingw32msvc/2.95.2/../../../../i386-mingw32msvc/lib/libmingw32.a(main.o)(.text+0x8e):
 undefined reference to `WinMain@16'
make[1]: *** [libfoo.la] Error 1




Now there are a couple of interesting things to note at this point.
Check out the line where libbar.a is created, it does not run ranlib
on the library after creating it (you will see why this matters in a sec).

ar cru .libs/libbar.a  bar.o
creating libbar.la


Now back to the linker error.

.libs/foo.o(.text+0x7):foo.c: undefined reference to `bar'

% i386-mingw32msvc-nm .libs/foo.o | grep bar
         U _bar

% i386-mingw32msvc-nm other/.libs/bar.o | grep bar
00000000 T _bar

% i386-mingw32msvc-nm other/.libs/libbar.a | grep bar
bar.o:
00000000 T _bar

So you would think that link step would have worked, but
the problem seems to be that .libs/libbar.a is not
actually getting passed to the gcc command line for
some reason. Lets try passing it by hand.


% i386-mingw32msvc-gcc -Wl,--base-file,.libs/libfoo.dll-base  
-Wl,-e,_DllMainCRTStartup@12 -o .libs/libfoo.dll  .libs/foo.o 
other/.libs/libbar.a 
other/.libs/libbar.a: could not read symbols: Archive has no index; run 
ranlib to add one

So now we get an error because ranlib was not run on other/.libs.libbar.a,
lets do that by hand and try again.

% i386-mingw32msvc-ranlib other/.libs/libbar.a 

% i386-mingw32msvc-gcc -Wl,--base-file,.libs/libfoo.dll-base 
-Wl,-e,_DllMainCRTStartup@12 -o .libs/libfoo.dll .libs/foo.o 
other/.libs/libbar.a
/usr/local/project/install/Xmingwin/bin/../lib/gcc-lib/i386-mingw32msvc/2.95.2/../../../../i386-mingw32msvc/bin/ld:
 warning: cannot find entry symbol _DllMainCRTStartup@12; defaulting to 00401000
/usr/local/project/install/Xmingwin/bin/../lib/gcc-lib/i386-mingw32msvc/2.95.2/../../../../i386-mingw32msvc/lib/libmingw32.a(main.o)(.text+0x8e):
 undefined reference to `WinMain@16'


This is an improvement at least, we still get a linker error but now it finds
_bar ok.

Now lets try to just invoke ld -shared with the same arguments and see if 
that works.

% i386-mingw32msvc-ld -shared --base-file .libs/libfoo.dll-base -e 
_DllMainCRTStartup@12 -o .libs/libfoo.dll .libs/foo.o other/.libs/libbar.a

% find . -name "*.dll"
./.libs/libfoo.dll

So after munging it my hand, I have been able to produce a .dll, but I
really need libtool to do this for me.



I decided to take a look at the generated Makefile to see if I could
figure out what was going wrong.

( Checking the missing ranlib while creating the .a )

% cd other
% ../libtool --debug --mode=link i386-mingw32msvc-gcc  -mno-cygwin   -o 
libbar.la -rpath /usr/local/lib  bar.lo
libtool: enabling shell trace mode
...
+ modename=libtool: link
+ allow_undefined=yes
+ libtool_args=i386-mingw32msvc-gcc
+ compile_command=i386-mingw32msvc-gcc
+ finalize_command=i386-mingw32msvc-gcc
...
+ prefer_static_libs=no
...
+ echo libtool: link: warning: undefined symbols not allowed in 
i386-pc-mingw32m
svc shared libraries
+ build_libtool_libs=no
+ build_old_libs=yes
+ oldlibs= .libs/libbar.a
...
+ eval cmds="$AR $AR_FLAGS $oldlib$oldobjs$old_deplibs"
++ cmds=ar cru .libs/libbar.a  bar.o
...
+ relink_command=cd /home/mo/build_cross_libtool/other; /bin/sh 
../libtool --mod
e=relink i386-mingw32msvc-gcc -mno-cygwin -o libbar.la -rpath 
/usr/local/lib bar
.lo
...
++ cd .libs
++ rm -f libbar.la
++ ln -s ../libbar.la libbar.la


So it looks like ranlib is never even considered. When I look at the
src for libtool the only mention of RANLIB that I can find is the
following.


# Commands used to build and install an old-style archive.
RANLIB="i386-mingw32msvc-ranlib"
old_archive_cmds="\$AR \$AR_FLAGS \$oldlib\$oldobjs\$old_deplibs"
old_postinstall_cmds="chmod 644 \$oldlib"
old_postuninstall_cmds=""


The only place where old_archive_cmds is used is:

eval cmds=\"$old_archive_cmds\"

So, from this quick look, I can only assume that this
is a bug in the libtool script and that old_archive_cmds
should really run RANLIB too.


I tried defining old_archive_cmds like so:

old_archive_cmds="\$AR \$AR_FLAGS \$oldlib\$oldobjs\$old_deplibs && 
\$RANLIB \$oldlib"


That seemed to fix the ranlib problem.

% make clean ; make

libtool: link: warning: undefined symbols not allowed in 
i386-pc-mingw32msvc shared libraries
ar cru .libs/libbar.a  bar.o && i386-mingw32msvc-ranlib .libs/libbar.a
creating libbar.la
(cd .libs && rm -f libbar.la && ln -s ../libbar.la libbar.la)



Now if I go up a dir and run the ld -shared step, it does not
complain about ranlib not being run.

% cd ..

% i386-mingw32msvc-ld -shared --base-file .libs/libfoo.dll-base -e 
_DllMainCRTStartup@12 -o .libs/libfoo.dll .libs/foo.o other/.libs/libbar.a




So far so good, now to figure out why the other/libbar.lo is not
getting passed down by the libtool script.



( Checking the .dll link step )

% make -n libfoo.la
/bin/sh ./libtool --mode=link i386-mingw32msvc-gcc  -mno-cygwin   -o 
libfoo.la -rpath /usr/local/lib -no-undefined -avoid-version foo.lo 
other/libbar.la 


% ./libtool --debug --mode=link i386-mingw32msvc-gcc  -mno-cygwin   -o 
libfoo.la -rpath /usr/local/lib -no-undefined -avoid-version foo.lo 
other/libbar.la 

...

+ libtool_args=i386-mingw32msvc-gcc -mno-cygwin -o libfoo.la -rpath 
/usr/local/l
ib -no-undefined -avoid-version foo.lo
+ . ./foo.lo
++ pic_object=.libs/foo.o
++ non_pic_object=foo.o
...
+ compile_command=i386-mingw32msvc-gcc -mno-cygwin -o @OUTPUT@ .libs/foo.o
...
deplibs= other/libbar.la
...
lib=other/libbar.la
test -f other/libbar.la
...
+ linklib=libbar.a
+ test -z libbar.a
+ test link = dlopen
++ cd other
++ pwd
+ abs_ladir=/home/mo/build_cross_libtool/other
+ test -z /home/mo/build_cross_libtool/other
++ echo Xother/libbar.la
++ sed -e 1s/^X// -e s%^.*/%%
+ laname=libbar.la
+ test Xno = Xyes
+ dir=other/.libs
+ absdir=/home/mo/build_cross_libtool/other/.libs
+ uninst_path= /home/mo/build_cross_libtool/other
++ echo Xlibbar.la
++ sed -e 1s/^X// -e s/\.la$// -e s/^lib//
+ name=bar
+ echo *** Warning: This library needs some functionality provided by 
other/libbar.la.
...
dependency_libs=/home/mo/build_cross_libtool/other/libbar.la
...
++ library_names=libfoo.dll
...
+ lib=.libs/libfoo.dll
...
      i386-mingw32msvc-dlltool --export-all --exclude-symbols 
DllMain@12,_cygwin
_dll_entry@12,_cygwin_noncygwin_dll_entry@12,DllMainCRTStartup@12,DllEntryPoint@
12 --output-def .libs/libfoo.dll-def  .libs/foo.o
...

      $CC -Wl,--base-file,$output_objdir/$soname-base  
-Wl,-e,_DllMainCRTStartup
@12 -o $lib $libobjs $deplibs $compiler_flags~

...

      i386-mingw32msvc-gcc -Wl,--base-file,.libs/libfoo.dll-base  
-Wl,-e,_DllMai
nCRTStartup@12 -o .libs/libfoo.dll  .libs/foo.o  ~


The dependency_libs variable does get set to include the static lib, but
it seems libs $deplibs is just not getting set to include this .a file.
This seems to be related to this bit of code. It seems like the first
branch is getting executed in my case, and the .a file is just getting
ignored.

          if test "$deplibs_check_method" != pass_all; then
            # We're trying link a shared library against a static one
            # but the system doesn't support it.
            # Just print a warning and add the library to dependency_libs so
            # that the program can be linked against the static library.
            echo
            echo "*** Warning: This library needs some functionality 
provided by $lib."
            echo "*** I have the capability to make that library 
automatically link in when"
            echo "*** you link to this library.  But I can only do this 
if you have a"
            echo "*** shared version of the library, which you do not 
appear to have."
          else
            convenience="$convenience $dir/$old_library"
            old_convenience="$old_convenience $dir/$old_library"
            deplibs="$dir/$old_library $deplibs"
            link_static=yes
          fi


So as a quick hack, I added the following lines to the true block.

            deplibs="$dir/$old_library $deplibs"
            link_static=yes

I am not sure if that is really the right solution, but it seemed to
work for me. Note how when I reran make, the other/.libs/libbar.a
dep got added to the compile line.


      i386-mingw32msvc-gcc -Wl,--base-file,.libs/libfoo.dll-base  
-Wl,-e,_DllMainCRTStartup@12 -o .libs/libfoo.dll  .libs/foo.o  
other/.libs/libbar.a 
/usr/local/project/install/Xmingwin/bin/../lib/gcc-lib/i386-mingw32msvc/2.95.2/../../../../i386-mingw32msvc/bin/ld:
 warning: cannot find entry symbol _DllMainCRTStartup@12; defaulting to 00401000


This of course brings me to the final error, something about 
_DllMainCRTStartup@12
or whatnot.

This got me wondering why libtool is even messing around with all this
import stuff when all it really needs to do it use the ld -shared option.

% make clean
% cd other
% make
% cd ..
% make foo.lo
% i386-mingw32msvc-ld -shared -o .libs/libfoo.dll  .libs/foo.o  
other/.libs/libbar.a

% ls .libs/
foo.o  libfoo.dll



So my two questions are:

1. Is this patch to add a ranlib call to a .a lib correct?

Index: ltconfig.in
===================================================================
RCS file: /cvs/libtool/ltconfig.in,v
retrieving revision 1.246.2.21
diff -u -r1.246.2.21 ltconfig.in
--- ltconfig.in 2000/07/20 02:29:16     1.246.2.21
+++ ltconfig.in 2000/07/22 05:59:02
@@ -616,7 +616,7 @@
 esac
 
 # Determine commands to create old-style static archives.
-old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs$old_deplibs'
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs$old_deplibs && \$RANLIB 
\$oldlib'
 old_postinstall_cmds='chmod 644 $oldlib'
 old_postuninstall_cmds=
 

2. How can I tell libtool to just use ld -shared to link a .dll?


thanks
Mo DeJong
Red Hat Inc

cross_libtool.tgz

Reply via email to