• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1-------------------
2Written by Ted T'so
3-------------------
4
5> https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
6>
7> I understood that, if there is no interface change but some implementation
8> changes, I need to bump revision. If new interface is added, for example, I
9> need to bump current while revision=0 and age++.
10
11So part of the problem here is that libtool is doing something really
12strange because they are trying to use some abstract concept that is
13OS-independent.  I don't use libtool because I find it horribly
14complex and doesn't add enough value to be worth the complexity.
15
16So I'll tell you how things work with respect to Linux's ELF version
17numbering system.  Translating this to libtool's wierd "current,
18revision, age" terminology is left as an exercise to the reader.  I've
19looked at the libtool documentation, and it confuses me horribly.
20Reading it, I suspect it's wrong, but I don't have the time to
21experiment to confirm that the documentation is wrong and how it
22diverges from the libtool implementation.
23
24So let me explain things using the ELF shared library terminology,
25which is "major version, minor version, patchlevel".  This shows up in
26the library name:
27
28	libudev.so.1.6.11
29
30So in this example, the major version number is 1, the minor version
31is 6, and the patchlevel is 11.  The patchlevel is entirely optional,
32and many packages don't use it at all.  The minor number is also
33mostly useless on Linux, but it's still there for historical reasons.
34The patchlevel and minor version numbers were useful back for SunOS
35(and Linux a.out shared library), back when there weren't rpm and dpkg
36as package managers.
37
38So many modern Linux shared libraries will only use the major and
39minor version numbers, e.g:
40
41	libext2fs.so.2.4
42
43The only thing you really need to worry about is the major version
44number, really.  The minor version is *supposed* to change when new
45interfaces has changed (but I and most other people don't do that any
46more).  But the big deal is that the major number *must* get bumped if
47an existing interface has *changed*.
48
49So let's talk about the major version number, and then we'll talk
50about why the minor version number isn't really a big deal for Linux.
51
52So if you change any of the library's function signatures --- and this
53includes changing a type from a 32-bit integer to a 64-bit integer,
54that's an ABI breakage, and so you must bump the major version number
55so that a program that was linked against libfoo.so.4 doesn't try to
56use libfoo.so.5.  That's really the key --- will a program linked
57against the previous version library break if it links against the
58newer version.  If it does, then you need to bump the version number.
59
60So for structures, if you change any of the existing fields, or if the
61application program allocates the structure --- either by declaring it
62on the stack, or via malloc() --- and you expand the structure,
63obviously that will cause problem, and so that's an ABI break.
64
65If however, you arrange to have structures allocated by the library,
66and struct members are always added at the end, then an older program
67won't have any problems.  You can guarantee this by simply only using
68a pointer to the struct in your public header files, and defining the
69struct in a private header file that is not available to userspace
70programs.
71
72Similarly, adding new functions never breaks the ABI.  That's because
73older program won't try to use the newer interfaces.  So if I need to
74change an interface to a function, what I'll generally do is to define
75a new function, and then implement the older function in terms of the
76newer one.  For example:
77
78extern errcode_t ext2fs_open(const char *name, int flags, int superblock,
79			     unsigned int block_size, io_manager manager,
80			     ext2_filsys *ret_fs);
81
82extern errcode_t ext2fs_open2(const char *name, const char *io_options,
83			      int flags, int superblock,
84			      unsigned int block_size, io_manager manager,
85			      ext2_filsys *hret_fs);
86
87As far as the minor version numbers are concerned, the dynamic linker
88doesn't use it.  In SunOS 4, if you have a DT_NEEDED for libfoo.so.4,
89and the dynamic linker finds in its search path:
90
91    libfoo.so.4.8
92    libfoo.so.4.9
93
94It will preferentially use libfoo.so.4.9.
95
96That's not how it works in Linux, though.  In Linux there will be a
97symlink that points libfoo.so.4 to libfoo.so.4.9, and the linker just
98looks for libfoo.so.4.  One could imagine a package manager which
99adjusts the symlink to point at the library with the highest version,
100but given that libfoo.so.4.9 is supposed to contain a superset of
101libfoo.so.4.8, there's no point.  So we just in practice handle all of
102this in the package manager, or via an ELF symbol map.  Or, we just
103assume that since vast majority of software comes from the
104distribution, the distro package manager will just update libraries to
105the newer version as a matter of course, and nothing special needs to
106be done.
107
108So in practice I don't bump the minor version number for e2fsprogs
109each time I add new interfaces, because in practice it really doesn't
110matter for Linux.  We have a much better system that gets used for
111Debian.
112
113For example in Debian there is a file that contains when each symbol
114was first introduced into a library, by its package version number.
115See:
116
117https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git/tree/debian/libext2fs2.symbols
118
119This file contains a version number for each symbol in libext2fs2, and
120it tells us what version of libext2fs you need to guarantee that a
121particular symbol is present in the library.  Then when *other*
122packages are built that depend on libext2fs2, the minimum version of
123libext2fs can be calculated based on which symbols they use.
124
125So for example the libf2fs-format4 package has a Debian dependency of:
126
127Depends: libblkid1 (>= 2.17.2), libc6 (>= 2.14), libf2fs5, libuuid1 (>= 2.16)
128
129The minimum version numbers needed for libblkid1 and libuuid1 are
130determined by figuring out all of the symbols used by the
131libf2fs-format4 package, and determining the minimum version number of
132libblkid1 that supports all of those blkid functions.
133
134This gets done automatically, so I didn't have to figure this out.
135All I have in the debian/control file is:
136
137Depends: ${misc:Depends}, ${shlibs:Depends}
138
139Sorry this got so long, but hopefully you'll find this useful.  How
140you bend libtool to your will is something you'll have to figure out,
141because I don't use libtool in my packages.[1]
142
143Cheers,
144
145					- Ted
146
147
148[1] If you are interested in how I do things in e2fsprogs, take a look
149at the Makefile.elf-lib, Makefile.solaris-lib, Makefile.darwin-lib,
150etc. here:
151
152https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git/tree/lib
153
154This these Makefile fragments are then pulled into the generated
155makefile using autoconf's substitution rules, here:
156
157https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git/tree/lib/ext2fs/Makefile.in
158
159(Search for "@MAKEFILE_ELF@" in the above Makefile.in).
160
161So when someone runs "configure --enable-elf-shlibs", they get the ELF
162shared libraries built.  On BSD and MacOS systems they just have to
163run "configure --enable-bsd-shlibs", and so on.
164
165Personally, since most people don't bother to write truly portable
166programs, as their C code is full of Linux'isms, using libtool is just
167overkill, because they probably can't build on any other OS *anyway*
168so libtool's slow and complex abstraction layer is totally wasted.
169Might as well not use autoconf, automake, and libtool at all.
170
171On the other hand, if you really *do* worry about portability on other
172OS's (e2fsprogs builds on MacOS, NetBSD, Hurd, Solaris, etc.) then
173using autoconf makes sense --- but I *still* don't think the
174complexity of libtool is worth it.
175
176= Add-on =
177If you are going to be making one less major update, this is the
178perfect time to make sure that data structures are allocated by the
179library, and are (ideally) opaque to the calling application (so they
180only manipulate structure poitners).  That is, the structure
181definition is not exposed in the public header file, and you use
182accessor functions to set and get fields in the structure.
183
184If you can't do that for all data structures, if you can do that with
185your primary data structure that's going to make your life much easier
186in the long term.  For ext2fs, that's the file systme handle.  It's
187created by ext2fs_open(), and it's passed to all other library
188functions as the first argument.
189
190The other thing you might want to consider doing is adding a magic
191number to the beginning of each structure.  That way you can tell if
192the wrong structure gets passed to a library.  It's also helpful for
193doing the equivalent of subclassing in C.
194
195This is how we do it in libext2fs --- we use com_err to define the
196magic numbers:
197
198	error_table ext2
199
200ec	EXT2_ET_BASE,
201	"EXT2FS Library version @E2FSPROGS_VERSION@"
202
203ec	EXT2_ET_MAGIC_EXT2FS_FILSYS,
204	"Wrong magic number for ext2_filsys structure"
205
206ec	EXT2_ET_MAGIC_BADBLOCKS_LIST,
207	"Wrong magic number for badblocks_list structure"
208	...
209
210And then every single structure starts like so:
211
212struct struct_ext2_filsys {
213	errcode_t			magic;
214	...
215
216struct ext2_struct_inode_scan {
217	errcode_t		magic;
218	...
219
220And then before we use any pointer we do this:
221
222	if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
223		return EXT2_ET_MAGIC_EXT2_FILE;
224