The Slacker

On March 20, 2012, in Uncategorized, by erik

Well, it has been quite awhile since I posted on here. If only this thing was automated! I have been very busy in my current work, life, kids, attempting to move etc. My goal was to try and write a few posts per week, and at first that seemed manageable and there were even a few months where I was writing almost everyday. That crazy pace wore me out a little bit which is why the break for so long. I am going to try and get back into the swing of things and post at least once a week if not more. I find myself writing about things I have come across in my work or on my own projects at home. I will attempt to broaden my horizons here into other languages and different problems. If you have suggestions please post in the comments or use the suggestion form.

I want to thank all my readers who come and go and those of you who have spent some time to comment on my posts!

Share
Tagged with:  

As a programmer I often find myself having to decide not only how to organize my code within a set of files, but also deciding if this code may be used by another program for some inter-process communication or as a generic piece of code that I could see many other programs using, a utilities library for example. This leads to the idea of shared components or shared libraries. These libraries may be referenced (linked) by your program during compilation or loaded during runtime with a set of tools. In fact, if you have done any threaded work on a Linux system you have probably come across a reference to -lpthread. This tells the compiler to link to that library during compilation. A library is also a way of releasing an API to interface with an application instead of having to compile in those functions. Libraries also reduce the size of a program because the code is in one single place and may be referenced by many applications at once.

Types of Linux Libraries

In Linux there are two types of C/C++ libraries that can be made.

  • Dynamically linked shared object libraries (*.so) files.
  • Static libraries (*.a) files, this is linked in and becomes part of the application.

The dynamic shared object files can be linked at runtime, and must be already compiled and reachable during the compilation and linking phase, and are not included within the executable. The other option is to load and unload using the dynamic linking system.

The naming schemes include the prefix of lib, if you look in the /usr/lib/ directory you will see a few hundred libraries used by your running Linux system. When linking against your program you simply use -l then the name, so for p-threads as shown above you link with -lpthread.

How Do You Generate A Static Library?

To create a static library (ending in *.a) you compile your program as you would normally:

gcc -Wall -c temp_test.c

This will create a library_data.o file, or object file. To create the library the next step is to use the ar utility.

ar -cvq libtemp_test.a temp_test.o

Voila, you now have a static library that can be used during the compilation phase. For example:

gcc -o eriks_program eriks_program.c libtemp_test.a
--- OR ---
gcc -o eriks_program eriks_program.c -ltemp_test

How Do You Generate A Dynamic Library?

When creating the dynamic library a few more flags during compilation are required. The creation of the object code or object file is required in the same way it is for the static library, however the -fPIC flag is required. It states that position independent code needs to be outputted for a shared library. Here are the steps required:

1
2
3
4
5
gcc -Wall -fPIC -c temp_test.c
gcc -shared -Wl,-soname,libtemp_test.so.1 -o libtemp_test.so.1.0 temp_test.o
mv libtemp_test.so.1.0 /lib
ln -sf /lib/libtemp_test.so.1.0 /lib/libtemp_test.so
ln -sf /lib/libtemp_test.so.1.0 /lib/libtemp_test.so.1

These steps create the libtemp_test.so.1.0 library as well as the symbolic links that allow compilation against -ltemp_test and run time binding. So what do these compiler options mean? I will save you the trouble from having to look them up:

  1. -Wall: include warnings.
  2. -fPIC: Compiler directive to output position independent code, a characteristic required by shared libraries.
  3. -shared: Produce a shared object which can then be linked with other objects to form an executable.
  4. -W1: Pass options to linker.

Now to compile a program using the libtemp_test.so.1.0 library you simply:

gcc -Wall -I/path/to/include-files -L/path/to/libraries eriks_program.c -ltemp_test -o eriks_program
--- OR ---
gcc -Wall -L/lib eriks_program.c -ltemp_test -o eriks_program

For information about dynamically loading this library from a C program refer to dynamic linking system.

Share

I have run into the Endian (not Indian) debate on numerous occasions. I even wrote an article explaining how to detect the Endianness of your operating system earlier. Simply detecting the endianness of your system is a good starting point in understand Endianess and what it means. In some cases you may need to flip your bytes from big Endian to little Endian, this is especially relevant when dealing with network packets and network programming in C. In network programming sometimes fields in a network packet will have fields that are in little Endian when most of your network fields are in big Endian, and to verify these fields you may need to swap Endianess.

For more information regarding Endianness head to the wiki article. There are also built-in C functions that do this including ntohs, ntohl, htons, htonl, however these functions are not always transferable to another architecture (MIPS, ARM) which also differ in Endianness. Therefore, I have written a few functions to byte swap 16-bit, 32-bit and 64-bit unsigned integers, if you are dealing with signed integers simply modify the functions to be signed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
u_int16_t swap_u16(u_int16_t i) {
    u_int8_t c1, c2;
 
    c1 = i & 255;
    c2 = (i >> 8) & 255;
 
    return (c1 << 8) + c2;
}
 
u_int32_t swap_u32(u_int32_t i) {
    u_int8_t c1, c2, c3, c4;    
 
    c1 = i & 255;
    c2 = (i >> 8) & 255;
    c3 = (i >> 16) & 255;
    c4 = (i >> 24) & 255;
 
    return ((u_int32_t)c1 << 24) + ((u_int32_t)c2 << 16) + ((u_int32_t)c3 << 8) + c4;
}
 
u_int64_t swap_u64(u_int64_t i) {
    u_int8_t c1, c2, c3, c4, c5, c6, c7, c8; 
 
    c1 = i & 255;
    c2 = (i >> 8) & 255;
    c3 = (i >> 16) & 255;
    c4 = (i >> 24) & 255;
    c5 = (i >> 32) & 255;
    c6 = (i >> 40) & 255;
    c7 = (i >> 48) & 255;
    c8 = (i >> 56) & 255;
 
    return ((u_int64_t)c1 << 56) + 
            ((u_int64_t)c2 << 48) + 
            ((u_int64_t)c3 << 40) + 
            ((u_int64_t)c4 << 32) + 
            ((u_int64_t)c5 << 24) + 
            ((u_int64_t)c6 << 16) + 
            ((u_int64_t)c7 << 8) + 
            c8;
}

As you can see these functions simply use a logical & with 0xFF (or 255 in decimal) then bit shift the value to its appropriate location depending on the size of the integer. If you apply the function again it will swap the values to their initial position.

Share