C/C++ library programming on Linux - Part one: Static libraries

Background

One of the most important aspects of modern programming is concept of reuse of code. Even C programming language allows us to reuse our code using concepts like simple functions and structures. C++ programming language goes one step further and allows us to group related variables and functions into classes with the same purpose - the reuse of our valuable code. By using libraries we can go even further from sharing code inside one process - we can share code between completely different programs.

What changes when using libraries? Answer to that question is: "link phase" of your program. In this phase GNU linker links all code modules in fully functional program. When it comes to libraries on Linux operating system we have two basic concepts: static and dynamic (often called "shared"). In this article series I will do my best to explain both Linux libraries concepts using simple C language examples.

GNU Linker (ld)

GNU Linker is Linux implementation of Unix ld command made my GNU as a part of the GNU Binutils package for manipulation of object code. GNU Linker is crucial for creating executable files and it is typically called automatically by GNU GCC compiler at the end of the compilation process. It can be called manually by using the ld command. For more information consult built-in Linux manual with man ld.

Linux dynamic loader

Dynamic loader loads dynamic libraries that program being started demands, and after that starts the program. Dynamic libraries are expected to be found in the following locations (in the order of appearance):

  1. On the path defined inside the environment variable LD_LIBRARY_PATH.
  2. On the path defined inside the /etc/ld.so.cache file made from the /etc/ld.so.conf file where user can list his own library paths. The act of creating /etc/ld.so.cache is initiated by issuing ldconfig command after every modification of /etc/ld.so.conf file.
  3. On the /lib/ and /usr/lib paths.

List dynamic dependencies ldd

List dynamic dependencies ldd command is very important during library programming and debugging other peoples libraries. This command purpose is to list all necessary libraries for given executable file. I will present output of this command for our test executable files (not applicable to statically linked executable files).

Soname

Before getting into the details of library programming on Linux we must explain what is soname. Understanding the shared object name (soname) is very important aspect of Linux library programming and Linux operating system in general. This name is inside the control part of every library, and every executable file contains sonames of libraries it's linked with. Job of the Linux dynamic loader is to find those libraries. How can we add library to our programs list? It's simple, we do that by giving the -lsoname argument to the gcc where soname is the soname of the library this program is linked with (without the "lib", ".so" and major version parts). For our example where soname of the ctest library is "libctest.so.1", we will specify this with the -lctest during the compilation phase.

As I've already mentioned, full soname contains library name and major version of the library it represents. This is necessary because it is common practice that major version should be increased when ABI (Application binary interface) is broken. This is why Linux dynamic loader needs this version information when searching for the appropriate library for given executable file. We must name our ctest library with the "libctest.so.1.x" filename where "1" is the major version and the "x" is the minor version. This library will have "libctest.so.1" soname because soname contains the major version of the library it represents. To make things more flexible we need to give two symlinks with our library. First symlink is the full soname of our library and this is what dynamic loader looks for when it is searching for appropriate libraries for your program. Second symlink will have only the name part of the soname, for our example this will be "libctest.so" and this is what GNU Linker searches for when it is linking your program with your library. What's the point of this symlinks someone might ask? The purpose of this symlinks is that by adjusting those we can decide which version of library we want dynamic loader to find, as well as version of the library GNU Linker will find. This is up to us users but it is generally managed by the operating system so that when you upgrade libraries, their symlinks are automatically adjusted.

Example code

Here are the sources of our simple test C language programs. We will use two libraries and one program that uses that libraries. Following code is very simple: In our program we have three variables x, y, z. First variable "x" is given value by the first library function ctest1() and second library variable "y" is given its value by the second library function ctest2(). Variable "z" holds quotient of "x" and "y".

Here's the code for the first library source file defined inside ctest1.c file:

1
2
3
void ctest1(int *i){
   *i=100;
}

Our library consists of two library source code files so here is the code for the second file named ctest2.c:

1
2
3
void ctest2(int *i){
   *i=5;
}

We could define our library inside only one source code file but that wouldn't be so much fun? Now, here is the source code for our program contained inside cprog.c file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
void ctest1(int *);
void ctest2(int *);
 
int main(){
    int x;
    int y;
    int z;
    ctest1(&x);
    ctest2(&y);
    z = (x / y);
    printf("%d / %d = %d\n", x, y, z);
    return 0;
}

One thing to mention, common practice is putting all function prototypes for this "ctest" library into single header file called "ctest.h" and then including that into every program that links with that library. I've kept it all in one place to keep things simple.

Static libraries

Static libraries are older and they represent nothing more than packed collection of named sequences in machine code. They are linked with your program during compilation and your program caries them wherever it goes. When linking against static library you can delete this library after compilation and your program will function just fine because static library is merged with your program code during compilation. Here's how to create static librariy "libctest.a", and then link our "ctest" program with this library.

1
2
3
4
gcc -Wall -c ctest1.c ctest2.c
ar rcs libctest.a ctest1.o ctest2.o
gcc -static cprog.c -L. -lctest -o cprog
./cprog

Now lets explain this a little. First line does actual translation of the libraries source code to the object code. Second line makes static library archive named "libctest.a" using GNU ar. Using the third line we compile our "cprog" program and link it statically with the "libctest.a" library. You can notice that we don't have any symlinks as described in "Soname" section. This is because static libraries are not used system wide and Linux dynamic loader is not used when you start program that is statically liked with some library. The whole static library is simply "glued" to the executable file that links with this library. Output of these commands will be 100/5 = 20. last time I've checked 100/5 really equals 20 so this means that everything is working fine and that our program got it's code from the library.

If you have any questions you can ask here and I'll do my best to explain. In my next article i will explain concept of dynamic (shared) libraries. Cheers!

DevGenii

A quality focused Magento specialized web development agency. Get in touch!

18 thoughts on “C/C++ library programming on Linux - Part one: Static libraries

  1. rohini chandra

    Thanks Marko, I learnt about soname from your article.

    Could you please help by letting me know how to build a static library which links to two dynamic libraries. For example, how to build a static library libstat.a(a.obj,b.obj) which links to two dynamic libraries libdyn1.so.1.0 and libdyn2.so.2.0. I found the below reply to similar query in the below forum where I found the link to your blog.

    gcc -shared -Wl,-soname,LIBNAME -o LIBNAME OBJFILES -lOTHERLIBRARY

    http://www.linuxquestions.org/questions/programming-9/linux-linking-archive-static-library-with-shared-dynamic-library-467565/

    But would it not build a shared object instead of a static library?

    Reply
  2. Marko Martinović Author

    @rohini chandra

    Sorry my friend but what you need is unfortunately not possible. Dynamic libraries are done deal and can’t be “converted” to static one or used to link statically. You need sources to your shared libs, recompile them as static libraries as I’ve described here and then use that static library to link statically. Hope I’ve helped you.

    Reply
  3. rohini chandra

    @Marko Martinović
    Thank you Marko. Now, I at least know that it is not possible to do so. I could not get this clear when tried to find this information on the web. Then If I understand it correct, the user applications of the static library have to link to necessary shared objects for the external symbols used by the static library.

    Reply
  4. Marko Martinović Author

    @rohini chandra

    It really isn’t problem in the Linux world that you can’t go from shared to static cause there’s always source code available. In the other closed source worlds that would be a problem.

    Library is on its own, you can’t link it with other dynamic library. Libraries are final product so to speak and can be used only by other programs not by other libraries. You can’t do “ldd” on library to see what it is linked to. When you run “ldd libsomething.a” you will get an answer “not a dynamic executable”. See “man ldd” for info on ldd 🙂

    Reply
  5. Jimmy

    >ldd /usr/local/lib/libncurses.so
    libc.so.1 => /usr/lib/libc.so.1
    libdl.so.1 => /usr/lib/libdl.so.1
    /usr/platform/SUNW,Sun-Fire-V440/lib/libc_psr.so.1

    I think you mean you can’t do ldd on a static library/archive….

    Reply
  6. yfarouk

    very interesting article Marko. I have a question here.. how can I create a shared library if my code uses other static libraries ? How can I remove paths of header files to these other static libraries from my code to generate my .so?

    Reply
    1. Mahesh

      You’ll need the headers of the static libs you want to use. You can build a .so “embedding” other static libs if you compile the object files that go into the static libs using the -fPIC option.

      Reply
  7. Omar

    I’ve executed these steps flawlessly on CentOS 6.2 and get the following error. Being new to all this, I’m at a loss. Any insights?
    $ gcc -static cprog.c -L. -lctest -o cprog
    /usr/bin/ld: cannot find -lc
    collect2: ld returned 1 exit status

    Reply
    1. Omar

      resolved. CentOS’ regular glibc and glibc-devel packages don’t include the static libraries. Anyone else trying this needs to install glibc-static before it’ll work.
      $ sudo yum install glibc-static

      Reply
  8. swapnil

    Getting error on my ubuntu please help
    swapnil@swapnil-Lenovo-G560:~/Documents/CPP/ctest$ gcc -static cprog.c -L -libtest -o cprog
    /tmp/ccGbOhUR.o: In function `main’:
    cprog.c:(.text+0x10): undefined reference to `ctest1′
    cprog.c:(.text+0x1c): undefined reference to `ctest2′

    Reply
  9. John

    I am getting the following error, what could be the problem when I run:

    $ gcc -static cprog.c -L. lctest -o cprog
    gcc: error: lctest: No such file or directory

    Reply
  10. YogaV

    Hi Marko,
    I am new to linux environment. Your article help me to start development in linux. thanks lot.

    Regards,
    YogaV

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *