Sunday, November 9, 2008

Why are there so many symbolic links?

Why are there so many symbolic links? Beta

This document is still beta.
If you find anything is wrong or inaccurate, please let me know.


You can see lots of symbolic links to shared libraries on Linux. Why are there so many and how it works? I couldn't find a detailed explanation on the web so I'll try to make a very easy tut here.

In short they are there for version control of the shared libraries. The key is that there are two types of library modifications, implementation modification with no interface change of a library, and interface modification. An "interface" of a function, classes, etc. is the meaning of it. If the declaration has been changed, like

old: void foo(int bar);
new: void foo(double bar);

You can say the interface of the function has been changed. And even if there's no change of the declaration, the interface may have been changed. It gets changed if the meaning of the argument or return value has been changed.

old: void foo(int bar); //bar means your age.
new: void foo(int bar); //bar means the number of chocolate bars you have eaten today.

and an "implementation" means how the function that satisfies the interface is made. You can make a sort function using bubblesort, heapsort, quicksort, or bogosort ;) all have the same interface but implementations are different. A modification to a function implementation with no interface change is often made for bug fix and performance tuning.


I will first tell you how it works and then why it works.

How it works

[Three types of names]

There are three types of names for one shared library.

(1)libfoo.so
(2)libfoo.so.1
(3)libfoo.so.1.0

Usually (1) and (2) are symbolic links. libfoo.so -> libfoo.so.1 -> libfoo.so.1.0



(1)libfoo.so
It is called "linker name". It is used when building (linking) a shared library like

gcc -lfoo -o myapp *.o

(2)libfoo.so.1
It is called "soname". th number indicates interface version. It is incremented on every interface modification of the library. A program that uses the library doesn't always assume the latest interface, since the program may only work on old interface. Remember an interface means the meaning of the function. In a shared library, its soname can be embedded (i.e. information about the interface version of the library is held in the library itself) by building (linking) like

gcc -shared -Wl,-soname,libfoo.so.1 -o libfoo.so.1.0 *.o

(the reason is described later).


(3)libfoo.so.1.0
It is called "real name". It is (usually) not a symbolic link. The first number (in this case 1) is the interface version of the library which is correspondent to the soname, and the second number (and sometimes the third) indicates the implementation version of the library, which gets incremented on every implementation modification of the library, like libfoo.so.1.1, libfoo.so.1.2 All the programs that use the library assume the latest implementation version of the library.


[When linking]
When a program (myapp) is built, like

gcc -lfoo -o myapp *.o

libfoo.so is searched and used (unless libfoo.a is found first). libfoo.so is a symbolic link: libfoo.so -> libfoo.so.1 -> libfoo.so.1.0 so what is really used is libfoo.so.1.0 The linker finds its soname libfoo.so.1, which is embedded in the libfoo.so.1.0, and put the soname to the executable (myapp) as a library that the executable is dependent on. It will sound a roundabout way but you'll see why the linker do such a thing when you've finished reading this blog entry.

[At the start up of myapp execution]
When myapp is started, libfoo.so.1 is searched because the name (soname) is recorded in myapp as a dependent library. Because libfoo.so.1 is a symbolic link to libfoo.so.1.0, libfoo.so.1.0 is loaded and used.


Why it works
Why is it good for the library's version control? I'll explain it by taking examples.


[When libfoo has a bug]
The library's author fixes the bug and release it as libfoo.so.1.1

gcc -shared -Wl,-soname,libfoo.so.1 -o libfoo.so.1.1 *.o

Because the interface is not changed, soname is not changed. Now libfoo.so.1.1 is the latest and the libfoo.so.1 's link should be updated.
old: libfoo.so.1 -> libfoo.so.1.0
new: libfoo.so.1 -> libfoo.so.1.1
This link is done by the system automatically (When you restart linux a program called ldconfig is executed, ldconfig finds wrong links and updates them). Now myapp uses libfoo.so.1.1 because libfoo.so.1 is a symbolic link to libfoo.so.1.1 In this way my app uses the latest implementation version of the library.

[When libfoo has a interface modification]

The library's author makes a new libfoo which has a different interface and release it as libfoo.so.2.0

gcc -shared -Wl,-soname,libfoo.so.2 -o libfoo.so.2.0 *.o

Because the interface has been changed, its soname should be updated (libfoo.so.2). Now a symbolic link libfoo.so.2 -> libfoo.so.2.0 is created. If he rebuilds myapp like before

gcc -lfoo -o myapp *.o

and runs it, libfoo.so.1.1 is still used, because myapp is still told to be dependent on libfoo.so.1 (because libfoo.so -> libfoo.so.1 when building) and libfoo.so.1 is still a symbolic link to libfoo.so.1.1

[When myapp's author wants to test the new library]

He wants to test the new library. He wants to make "yourapp" which uses the new libfoo. He can do so by changing the symbolic link libfoo.so

old: libfoo.so -> libfoo.so.1
new: libfoo.so -> libfoo.so.2

When he builds yourapp

gcc -lfoo -o yourapp *.o

and runs it. Now libfoo.so is a symbolic link to libfoo.so -> libfoo.so.2 -> libfoo.so.2.0, soname libfoo.so.2 is embedded in libfoo.so.2.0, and libfoo.so.2 is recorded in yourapp as a dependent library. On yourapp application startup, libfoo.so.2 -> libfoo.so.2.0 is loaded and used. myapp still uses libfoo.so.1 unless he rebuilds it after libfoo.so -> libfoo.so.2 relinking.


Conclusion
Every executable uses the latest implementation of a fixed interface which is recorded as a "soname" in the executable.
The soname which is recorded in an executable is the one that the symbolic link which name is "linker name" links to.

1 comment:

Anonymous said...

great post. is there a way to update the soname in an existing shared library (where the -Wl,-soname=xxxx was not passed during linking)?