Share vs static
We know that glibc is designed for dynamic link (dso) because share libs are very efficient about the size (code space is share btw many files), flexible and easy to upgrade. These features are good for PC, general purpose environment but not for embedded system. Embedded system needs small footprint, fast and stable so static link is a good choice which system does not need to resolve symbol like dynamic. All code is link together to a big binary image the compiler has choice to optimize code with direct jump vs indirect jump in share lib and we do not face the problem with mis-match version of library.
Come back to glibc, fortunately that glibc still has a option to compile add-on libs as static. This is nss lib (name service switch). I took many days to google how to make my image static link with glibc support functions as gethostbyname, gethostbyaddr, getaddinfo … There is no clear answer but I got some hints. After downloading glibc source code, running configure tool then I found that glibc support building static lib for nss so we must cross-compile a new glibc because binary glibc image from DENX does not do that.
I am so busy on these days to continue this post but now I found the time to update this.
Back to nss-switch static link library: Cross-compile glibc is not a touch job but there is something you need to be cared. GLIBC use autoconf tool and some checks such as sizeof(unsigned long long) … does not success on host platform so you need to set these value on cache file. One more thing is about TLS (thread local storage). We must build glibc with TLS supported.
Talking about TLS: TLS as its name meaning, is a “private” storage for each thread. It is not really a private because it could be access from other threads if they know the address. The private means that the compiler will generate code to create that storage dynamically whenever you create a new thread by calling pthread_create(). If you do not call pthread_create() then you lost this feature. Following is the code using keyword __thread to declare a variable is a thread private:
static __thread int g_private_val;
void * thread_handler(void * args)
printf(“address of private_val = %p \n”, &g_thread_val);
void main(int argc, char *argv)
pthread thread1, thread2;
pthread_create(&thread1, thread_handler, NULL, NULL);
pthread_create(&thread2, thread_handler, NULL, NULL);
Look at the result; we will notice that the address for each thread will be difference. Removing keyword __thread, both thread will print the same address.
The TLS is architecture dependence. On PowerPC architecture, they allocate this storage on stack. GPR2 (general purpose register #2) points to address of private data structure (ABI standard). That data structure will store information for TLS.
I am working on a project that TLS is a good solution. My job is building platform layer. This layer must support API for creating tasks. Like thread, tasks are sharing address space, file system name space but they do not share signal handler, file descriptor. Why tasks need these features? If they sharing signal handler, when a task got SIGSEGV, all other tasks will be killed too so sharing signal handler is not a good idea. My system can’t stand with that solution. Sharing file descriptor also has some problem because we use many shell libraries which need access to standard in, standard out files. Fortunate that I could satisfy these requirement thanks by clone system call. We could ask kernel to share what we need (for example: CLONE_VM: share address space, CLONE_FILE: share file descriptor). That looks good as we could share what we want. However, a new problem occurs when I integrate other packages to my system such as Quagga and NetSnmp. These packages support for real-process (sharing nothing). Our tasks are btw thread and separated process. The problem with real-process is that global variables are private for each process but that is not true for thread. And you could guest that is the place for TLS. With TLS, my job becomes so easy that I only add keyword __thread to global variables so they are private for each thread. So, the final solution is that we use pthread_create() so that glibc will build the task context for us (base on stack). From that context, we use clone system call to build our own tasks. Until now, I satisfy with that solution :D.