📄 dealing-with-libraries.html
字号:
></TR></TABLE><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING">Listing 3.6 Makefile.am for grumpalotbin_PROGRAMS=grumpalotgrumpalot_SOURCES=main.cgrumpalot_LDADD=libgrump.lagrumpalot_LDFLAGS=include_HEADERS=grump.hlib_LTLIBRARIES=libgrump.lalibgrump_la_SOURCES=grump.clibgrump_la_LDFLAGS=-version-info 0:0:0 </PRE></TD></TR></TABLE><P> We'll have to run several commands to set things up and then compile it all into the target grumpalot executable. All of these commands should look familiar by now. If not, you should probably go back and read this chapter again. It may seem like a lot of typing, but think of all the wonderful magic that goes on behind the scenes. It would take you weeks of extra work to duplicate all that. To make things even easier, GNOME projects often wrap all these commands up into a single shell script for us, called autogen.sh. We'll learn more about that in Section 3.5.4. Here are the commands you need to compile the example, with the output of the build tools snipped for clarity: </P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING">$ libtoolize$ aclocal$ touch NEWS README AUTHORS ChangeLog$ automake --add-missing --gnu$ autoconf$ ./configure$ make$ ./grumpalotOh, bother!...Go away!Be gone!Not again!Not again!Go away! </PRE></TD></TR></TABLE></DIV><DIVCLASS="SECT2"><H2CLASS="SECT2"><ANAME="AEN543">Exploring the Results</A></H2><P> Let's see what libtool has done for us. First, it looks as if libtool has created a .libs subdirectory, filled with every possible incarnation of our libgrump library, including libgrump.a, libgrump.la, and libgrump.so. Another curious fact surfaces when we look at the .libs directory: It also contains a grumpalot file! We have two executables-one in the main directory, and one hidden away with the library files. Let's snoop around and see if we can figure out what's going on. We'll start with the file command, a handy little utility that cracks open a file, examines it, and prints out what it finds. </P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING">$ file grumpalotgrumpalot: Bourne shell script text$ file .libs/grumpalot.libs/grumpalot: ELF 32-bit LSB executable, Intel 80386, version 1, dynamically linked,not stripped </PRE></TD></TR></TABLE><P> It appears that libtool has generated some sort of wrapper script around the real executable, which is in the .libs directory. It does this to make sure the executable can properly find and load the shared libraries, even though the shared libraries haven't been installed yet. The wrapper script performs a little fancy juggling of paths that wouldn't normally be necessary with installed libraries; it then invokes the executable in .libs for us. In most cases,2 we can simply invoke the wrapper script as if it were the real executable, passing all the normal command line parameters to it. </P><P> Let's find out where the object code for our various grump_* functions ended up. We can make use of another common UNIX tool, nm, a utility for dumping the symbol tables of an object file or executable into a legible ASCII format. The output of nm can be voluminous, especially on larger binary files, so we'll pipe the results through grep to filter out the symbols we don't care about. </P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING">$ nm .libs/grumpalot | grep grump U grump_a_lot_more U grump_some$ nm .libs/libgrump.so | grep grump000008a4 T grump_a_lot_more00000880 T grump_some </PRE></TD></TR></TABLE><P> The nm utility uses a handful of single-letter codes to characterize each symbol. The documentation for nm contains a pretty good description of what they all mean. In our case, the libgrump symbols in the binary executable grumpalot are undefined (U). This is normal for any symbols imported from a library into an executable or another library, where the symbols are referenced but not implemented. In libgrump itself, the symbols are marked with a T, which means that nm found their implementations inside the text (or code) section of the library file. The addresses indicate exactly where in the code section the symbols reside. You can find out more about nm by typing info binutils at the command line of a GNU-equipped system and then going into the nm documentation. </P><P> Next we'll see how things change with static libraries. We can do this with the --disable-shared flag. We'll have to clean out the old object files to make sure everything recompiles correctly. </P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING">$ ./configure --disable-shared$ make clean && make all$ file grumpalotgrumpalot: ELF 32-bit LSB executable, Intel 80386, version 1,dynamically linked, not stripped$ nm grumpalot | grep grump08048634 T grump_a_lot_more08048620 T grump_some </PRE></TD></TR></TABLE><P> Things are a lot simpler this time. The .libs directory contains only static libraries. The .so files are gone, as is the .libs/grumpalot executable. As we see by the file command, the top-level grumpalot is now the real executable. libtool puts the executable in the .libs directory only when it's creating shared libraries. </P><P> Finally, to ease our minds we verify that the libgrump functions are linked directly into the executable. Notice how much larger the symbol addresses are when they are statically linked into the executable. The reason is that the symbols have absolute addresses when they reside in the executable but relative addresses when inside the shared library. The relative addresses make it possible to load shared libraries into different parts of an executable's memory. If a shared library tries to load itself into an area of memory that's already taken, the library loader can dynamically relocate it to another area. </P></DIV><DIVCLASS="SECT2"><H2CLASS="SECT2"><ANAME="AEN555">A Note about Version Numbers</A></H2><P> The path to creating new versions of shared libraries can be a twisted, precarious maze. You have to juggle distribution versions with library versions. The main software package might go through major alterations without a single change to the supporting libraries, and vice versa. It's dangerous to mix package version numbers with library versions because the two are not synony- mous. Package versions are fairly arbitrary, and mostly for the end user's benefit. Shared-library versions, however, refer to very specific changes in functionality. The dynamic library loader uses this version number to determine which implementation of the library to load. If you have only one libgrump.so file, the choice is easy. On UNIX, however, it's possible to have many versions of the same library in the same directory at the same time. The library version number is the loader's only hope for finding the correct library for a given application. </P><P> You can set the version for a library with an _LDFLAGS primary in your Makefile.am file, with the -version-info flag: </P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING">libgrump_la_LDFLAGS=-version-info 5:1:2 </PRE></TD></TR></TABLE><P> The three numbers stand for CURRENT:REVISION:AGE, or C:R:A for short. The libtool script typically tacks these three numbers onto the end of the name of the .so file it creates. The formula for calculating the file numbers on Linux and Solaris is (C - A).(A).(R), so the example given here would create the file libgrump.so.3.2.1. Other operating systems might use a different library file name convention; libtool takes care of the details. </P><P> As you release new versions of your library, you will update the library's C:R:A. Although the rules for changing these version numbers can quickly become confusing, a few simple tips should help keep you on track. The libtool documentation goes into greater depth. </P><P> In essence, every time you make a change to the library and release it, the C:R:A should change. A new library should start with 0:0:0. Each time you change the public interface (i.e., your installed header files), you should increment the CURRENT number. This is called your interface number. The main use of this interface number is to tag successive revisions of your API. </P><P> The AGE number is how many consecutive versions of the API the current implementation supports. Thus if the CURRENT library API is the sixth published version of the interface and it is also binary compatible with the fourth and fifth versions (i.e., the last two), the C:R:A might be 6:0:2. When you break binary compatibility, you need to set AGE to 0 and of course increment CURRENT. </P><P> The REVISION marks a change in the source code of the library that doesn't affect the interface-for example, a minor bug fix. Anytime you increment CURRENT, you should set REVISION back to 0. </P></DIV></DIV><DIVCLASS="NAVFOOTER"><HRALIGN="LEFT"WIDTH="100%"><TABLEWIDTH="100%"BORDER="0"CELLPADDING="0"CELLSPACING="0"><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top"><AHREF="generating-makefiles.html">Prev</A></TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="index.html">Home</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top"><AHREF="adding-gnome.html">Next</A></TD></TR><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top">Generating Makefiles</TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="gnome-build.html">Up</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top">Adding GNOME</TD></TR></TABLE></DIV></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -