Finding DLLs used by a Cygwin program
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:
cygcheck grep
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.
Happy hacker discovery #0
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?
Happy hacker discovery #1
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.
Happy hacker discovery #2
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