I don't know how many other people get this issue, but it comes up at my work a bit: Some co-worker asks me if I know of a tool to do such-and-such, and invariably I think “well, that's easy to do on Unix, but on Windows…”.
Then I remember Cygwin, and quickly find a Cygwin utility that does it, or can be scripted to do it with a small amount of work. So then co-worker asks if they can have a copy of this utility, and of course Cygwin is Free, so I say “sure, go download from www.cygwin.com.” Then they say, “yeah, but I don't want to install all of Cygwin, can't you just give me that one program?”
Well, the Cygwin command-line tools can be run from a Windows
CMD.EXE shell, so this is quite possible to do. However, they all require the Cygwin POSIX layer, which at a minimum means I should also give them
cygwin1.dll. But what other DLLs might the program use?
MJL2008-09-10T14:37+1000 Update: since this page gets a lot of hits, here's the quick answer: use cygcheck, i.e:
Find it under Happy hacker discovery #2. Keep reading if you're bored...
In the past, I used to just give the person the
cygwin1.dll DLL and a program (
SPLIT.EXE,say), let the person run it on their computer, explaining that it will crash with an error about a missing DLL. Then they would come back to me and say that they also need
cygintl-8.dll and try again. It will crash again because
cygintl-8.dll just happens to link to
cygiconv-2.dll. After supplying this third DLL file, my co-worker can finally run
SPLIT.EXE and break their huge data set into manageable chunks. Everyone's happy.
The whole process is a bit embarrassing for me and annoying for them (though they could just download the base Cygwin install like I asked…) So, to solve this problem somewhat, I set about thinking “I wonder if there's a Cygwin tool that can find DLLs in a program?”.
Well, a quick look at
info binutils reveals that there is nothing that directly solves my problem of listing which DLLs a program links to. There is this thing called
dlltool, but it is for creating PE dynamic libraries, not for listing the libraries used by a PE executable. Bummer.
Well, there is also a tool called
strings, which will list text strings in binary files. Now, Windows PE executables happen to contain the name of their linked DLLs as text strings, yay! So all I need to do, is pass
strings over a given program, filter for DLL names (to strip out error messages and such) and I have my list.
Oh, but then I'll need to repeat for each DLL as well. So I need a script to do it:
#!/bin/bash # # finds all the DLL files linked by a Win32 PE executable. # Useful for finding needed DLLs to supply with a Cygwin program when # sharing just that program with other people. # if [ x"$1" = x ]; then PROG=`basename $0` printf "Usage: $PROG <command to list DLLs for>\\n" printf " (you should not add '.exe' to the end).\\n" exit 1 fi SEARCH_FILE=`which $1`.exe # This is not perfect, as it's too permissive, but should be good enough DLL_REGEXP='^[[:alnum:][:punct:]]*\\.[Dd][Ll][Ll]$' # We don't care about the Win32 kernel API library... KERNEL_REGEXP='[Kk][Ee][Rr][Nn][Ee][Ll]32\\.[Dd][Ll][Ll]' # Get list of program's DLLs: DLL_LIST=`strings -n 5 $SEARCH_FILE \\ |grep $DLL_REGEXP \\ |grep -v $KERNEL_REGEXP` # Print the DLLs used directly by the program printf "$SEARCH_FILE:\\n" for I in $DLL_LIST; do printf "\\t$I\\n" done printf "\\t(KERNEL32.DLL)\\n\\n" #Now find each DLL's dependencies (only one level deep though) for CURR_DLL in $DLL_LIST; do SEARCH_DLL=`which $CURR_DLL` DLL_DLL_LIST=`strings -n 5 $SEARCH_DLL \\ |grep $DLL_REGEXP \\ |grep -v $KERNEL_REGEXP \\ |grep -v $CURR_DLL` printf " -->\\t$CURR_DLL:\\n" for I in $DLL_DLL_LIST; do printf "\\t\\t$I\\n" done printf "\\t\\t(KERNEL32.DLL)\\n\\n" done
There we are, a list of DLLs used by a given program, and the DLLs that those DLLs use.
It's not perfect: it is not recursive, so only goes one level deep, but this should cover most cases. One day I might come back to this, when I figure out how to write a recursive function in bash.
So now I know which DLLs to give to my co-worker along with the nifty tool they want. But where on my system are they? Cygwin installs most DLLs into
/usr/bin. But sometimes they can be in
/usr/lib. So do I have to make a search?
Well, no I don't. Cygwin's
which command also works for DLLs, yay! You just have to go:
$ which cygwin1.dll /usr/bin/cygwin1.dll $ which cygintl-8.dll /usr/bin/cygintl-8.dll $ which cygiconv-2.dll /usr/bin/cygiconv-2.dll
There, problem solved.
Update: 2008-01-21 09:56+1100: There's an awesome tool that comes with Cygwin, called
cygcheck(1), that does exactly what I need. Thanks for the tip, Leni! Here's a sample output for the SPLIT.EXE example:
$ cygcheck split Found: d:\cygwin\bin\split.exe d:\cygwin\bin\split.exe d:\cygwin\bin\cygwin1.dll C:\WINDOWS\system32\ADVAPI32.DLL C:\WINDOWS\system32\ntdll.dll C:\WINDOWS\system32\KERNEL32.dll C:\WINDOWS\system32\RPCRT4.dll C:\WINDOWS\system32\Secur32.dll d:\cygwin\bin\cygintl-8.dll d:\cygwin\bin\cygiconv-2.dll