System Call Layer

Working with glibc

Introduction

Why this chapter

What is glibc

glibc is a library which has a lot of functions pre-written for you so that you do not have to write the code again and again. Also it standardizes the way you should be writing your code. It wraps a lot of system specific details and all you need to know is to how to call the particular function, and what to be expected from the function and what are the return values the function will give you.

glibc is the GNU Version of Standard C Library. All the functions supported in Standard C Library can be found there + some added by the GNU

Todo

Give an example of a function in Standard C and Not in GNU LibC

Todo

Give an example of a function in GLIBC and not in Standard C.

For example: Let us say that we have to find the length of a string. Now this is quite a small code to write and we can write the whole thing ourself, but it is a function which will be used a lot of time accross a lot of products. So the library gives you an implementation of this. As the function is present in the library you can safely assume that the function will work fine because of millions of people have used it and tested it.

For the sake of understanding it better we will now go into the code of the library function and see if its similar to our code.

Also we will make some changes to the code so that it stops working incorrectly and then use it in our programs. This exercise is just a demostration of the following.

  • We can read the code of glibc.
  • We can compile the code of glibc ourselves and use the newly compiled library.
  • We can change the code of glibc.
  • We can use the changed code of glibc.

Download, Extract and walk through glibc

Download and extract glibc

The source code of glibc is available at https://ftp.gnu.org/gnu/libc/. You can sort the list using Last Modified to get the latest tar package.

From the page I got the link as https://ftp.gnu.org/gnu/libc/glibc-2.24.tar.xz.

  • Let us download this source, see the following snippet for the exact commands.
$ wget https://ftp.gnu.org/gnu/libc/glibc-2.24.tar.xz
--2017-01-29 07:50:02--  https://ftp.gnu.org/gnu/libc/glibc-2.24.tar.xz
Resolving ftp.gnu.org (ftp.gnu.org)... 208.118.235.20, 2001:4830:134:3::b
Connecting to ftp.gnu.org (ftp.gnu.org)|208.118.235.20|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 13554048 (13M) [application/x-tar]
Saving to: ‘glibc-2.24.tar.xz’

glibc-2.24.tar.xz                     100%[==>]  12.93M   709KB/s    in 21s

2017-01-29 07:50:26 (622 KB/s) - ‘glibc-2.24.tar.xz’ saved [13554048/13554048]

Extract the code

  • The downloaded code is a compressed tar file. We need to extract it.
rishi@rishi-VirtualBox:~$ tar -xf glibc-2.24.tar.xz
  • This creates a directory names glibc-2.24 in the folder.

Walkthrough glibc

rishi@rishi-VirtualBox:~$ cd glibc-2.24/
rishi@rishi-VirtualBox:~/glibc-2.24$ ls
abi-tags          ChangeLog.3                        ChangeLog.old-ports-mips
aclocal.m4        ChangeLog.4                        ChangeLog.old-ports-powerpc
argp              ChangeLog.5                        ChangeLog.old-ports-tile
assert            ChangeLog.6                        config.h.in
benchtests        ChangeLog.7                        config.make.in
bits              ChangeLog.8                        configure
BUGS              ChangeLog.9                        configure.ac
catgets           ChangeLog.old-ports                conform
ChangeLog         ChangeLog.old-ports-aarch64        CONFORMANCE
ChangeLog.1       ChangeLog.old-ports-aix            COPYING
ChangeLog.10      ChangeLog.old-ports-alpha          COPYING.LIB
ChangeLog.11      ChangeLog.old-ports-am33           cppflags-iterator.mk
ChangeLog.12      ChangeLog.old-ports-arm            crypt
ChangeLog.13      ChangeLog.old-ports-cris           csu
ChangeLog.14      ChangeLog.old-ports-hppa           ctype
ChangeLog.15      ChangeLog.old-ports-ia64           debug
ChangeLog.16      ChangeLog.old-ports-linux-generic  dirent
ChangeLog.17      ChangeLog.old-ports-m68k           dlfcn
ChangeLog.2       ChangeLog.old-ports-microblaze     elf
extra-lib.mk      LICENSES     nscd                  stdio-common
extra-modules.mk  locale       nss                   stdlib
gen-locales.mk    localedata   o-iterator.mk         streams
gmon              login        po                    string
gnulib            mach         posix                 sunrpc
grp               Makeconfig   PROJECTS              sysdeps
gshadow           Makefile     pwd                   sysvipc
hesiod            Makefile.in  README                termios
hurd              Makerules    resolv          test-skeleton
iconv             malloc       resource        time
iconvdata         manual       rt              timezone
include           math         Rules           version.h
inet              mathvec      scripts         wcsmbs
INSTALL           misc         setjmp          wctype
intl              NAMESPACE    shadow          WUR-REPORT
io                NEWS         shlib-versions
libc-abis         nis          signal
libidn            nptl         socket
libio             nptl_db      soft-fp

Some string related code is here

rishi@rishi-VirtualBox:~/glibc-2.24$ ls string/str*
string/stratcliff.c    string/strcmp.c     string/strerror_l.c      string/strncase_l.c  string/strrchr.c    string/str-two-way.h
string/strcasecmp.c    string/strcoll.c    string/strfry.c          string/strncat.c     string/strsep.c     string/strverscmp.c
string/strcasecmp_l.c  string/strcoll_l.c  string/string.h          string/strncmp.c     string/strsignal.c  string/strxfrm.c
string/strcasestr.c    string/strcpy.c     string/string-inlines.c  string/strncpy.c     string/strspn.c     string/strxfrm_l.c
string/strcat.c        string/strcspn.c    string/strings.h         string/strndup.c     string/strstr.c
string/strchr.c        string/strdup.c     string/strlen.c          string/strnlen.c     string/strtok.c
string/strchrnul.c     string/strerror.c   string/strncase.c        string/strpbrk.c     string/strtok_r.c

Some math related code is here

$ ls math/w_*
math/w_acos.c    math/w_atanhf.c  math/w_fmodf.c   math/w_j1l.c             math/w_lgammal_r.c    math/w_logf.c        math/w_scalblnl.c
math/w_acosf.c   math/w_atanhl.c  math/w_fmodl.c   math/w_jn.c              math/w_lgamma_main.c  math/w_logl.c        math/w_sinh.c
math/w_acosh.c   math/w_cosh.c    math/w_hypot.c   math/w_jnf.c             math/w_lgamma_r.c     math/w_pow.c         math/w_sinhf.c
math/w_acoshf.c  math/w_coshf.c   math/w_hypotf.c  math/w_jnl.c             math/w_log10.c        math/w_powf.c        math/w_sinhl.c
math/w_acoshl.c  math/w_coshl.c   math/w_hypotl.c  math/w_lgamma.c          math/w_log10f.c       math/w_powl.c        math/w_sqrt.c
math/w_acosl.c   math/w_exp10.c   math/w_ilogb.c   math/w_lgamma_compat.c   math/w_log10l.c       math/w_remainder.c   math/w_sqrtf.c
math/w_asin.c    math/w_exp10f.c  math/w_ilogbf.c  math/w_lgamma_compatf.c  math/w_log1p.c        math/w_remainderf.c  math/w_sqrtl.c
math/w_asinf.c   math/w_exp10l.c  math/w_ilogbl.c  math/w_lgamma_compatl.c  math/w_log1pf.c       math/w_remainderl.c  math/w_tgamma.c
math/w_asinl.c   math/w_exp2.c    math/w_j0.c      math/w_lgammaf.c         math/w_log1pl.c       math/w_scalb.c       math/w_tgammaf.c
math/w_atan2.c   math/w_exp2f.c   math/w_j0f.c     math/w_lgammaf_main.c    math/w_log2.c         math/w_scalbf.c      math/w_tgammal.c
math/w_atan2f.c  math/w_exp2l.c   math/w_j0l.c     math/w_lgammaf_r.c       math/w_log2f.c        math/w_scalbl.c
math/w_atan2l.c  math/w_expl.c    math/w_j1.c      math/w_lgammal.c         math/w_log2l.c        math/w_scalbln.c
math/w_atanh.c   math/w_fmod.c    math/w_j1f.c     math/w_lgammal_main.c    math/w_log.c          math/w_scalblnf.c

The header files for the library is here.

$ ls include/
aio.h       ctype.h     fenv.h          grp-merge.h        link.h      netinet     resolv.h          spawn.h           syscall.h   utmp.h
aliases.h   des.h       fmtmsg.h        gshadow.h          list.h      nl_types.h  rounding-mode.h   stab.h            sysexits.h  values.h
alloca.h    dirent.h    fnmatch.h       iconv.h            locale.h    nss.h       rpc               stackinfo.h       syslog.h    wchar.h
argp.h      dlfcn.h     fpu_control.h   ifaddrs.h          malloc.h    nsswitch.h  rpcsvc            stap-probe.h      tar.h       wctype.h
argz.h      elf.h       ftw.h           ifunc-impl-list.h  math.h      obstack.h   sched.h           stdc-predef.h     termios.h   wordexp.h
arpa        endian.h    gconv.h         inline-hashtab.h   mcheck.h    poll.h      scratch_buffer.h  stdio_ext.h       tgmath.h    xlocale.h
assert.h    envz.h      getopt.h        langinfo.h         memory.h    printf.h    search.h          stdio.h           time.h
atomic.h    err.h       getopt_int.h    libc-internal.h    mntent.h    programs    set-hooks.h       stdlib.h          ttyent.h
bits        errno.h     glob.h          libc-symbols.h     monetary.h  protocols   setjmp.h          string.h          uchar.h
byteswap.h  error.h     gmp.h           libgen.h           mqueue.h    pthread.h   sgtty.h           strings.h         ucontext.h
caller.h    execinfo.h  gnu             libintl.h          net         pty.h       shadow.h          stropts.h         ulimit.h
complex.h   fcntl.h     gnu-versions.h  libio.h            netdb.h     pwd.h       shlib-compat.h    stubs-prologue.h  unistd.h
cpio.h      features.h  grp.h           limits.h           netgroup.h  regex.h     signal.h          sys               utime.h

Walkthrough strlen

Todo

write this section.

Walkthrough div

Todo

write this section.

Walkthrough open

Todo

write this section.

Compiling the code of glibc

Generally compling and installing code on Linux system involves the following stages

  1. Configuring - running configure with right options.
  2. Compiling - running make with right options.
  3. Install - running make install.

Configuring

We will get into the glibc-2.24 source directory and run the configure script. I have intentionally shown the mistakes which happened so that you also understand the small things which needs to be taken care while configuring and compling.

rishi@rishi-VirtualBox:~/glibc-2.24$ ./configure
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking for gcc... gcc
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for readelf... readelf
checking for g++... g++
checking whether we are using the GNU C++ compiler... yes
checking whether g++ accepts -g... yes
checking whether g++ can link programs... yes
configure: error: you must configure in a separate build directory
  • We got an error that we should use a separate directory for running configure
rishi@rishi-VirtualBox:~/glibc-2.24$ mkdir ../build_glibc

rishi@rishi-VirtualBox:~/glibc-2.24$ cd ../build_glibc/
  • Let us now run the configure command.
rishi@rishi-VirtualBox:~/build_glibc$ ../glibc-2.24/configure
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking for gcc... gcc
checking for suffix of object files... o
checking version of sed... 4.2.2, ok
checking for gawk... no

>>>>>>>>>>>>>>>>>SNIP<<<<<<<<<<<<<<<<<<<<<<

checking if gcc is sufficient to build libc... yes
checking for nm... nm
configure: error:
*** These critical programs are missing or too old: gawk
*** Check the INSTALL file for required versions.
  • The configure step gave errors - let us install gawk now.
rishi@rishi-VirtualBox:~/build_glibc$ sudo apt-get install gawk
[sudo] password for rishi:
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
libsigsegv2
Suggested packages:
gawk-doc
The following NEW packages will be installed:
gawk libsigsegv2

>>>>>>>>>>>>>SNIP<<<<<<<<<<<<<<

Setting up gawk (1:4.1.3+dfsg-0.1) ...
  • Check if the command is present.
rishi@rishi-office:~/mydev/publications/system_calls$ which gawk
/usr/bin/gawk
  • Let us run configure again
rishi@rishi-VirtualBox:~/build_glibc$ ../glibc-2.24/configure
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking for gcc... gcc
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes

>>>>>>>>>>SNIP<<<<<<<<<<<<<<<<<<<<<<

running configure fragment for sysdeps/unix/sysv/linux/x86_64
running configure fragment for sysdeps/unix/sysv/linux
checking installed Linux kernel header files... 3.2.0 or later
checking for kernel header at least 2.6.32... ok
*** On GNU/Linux systems the GNU C Library should not be installed into
*** /usr/local since this might make your system totally unusable.
*** We strongly advise to use a different prefix.  For details read the FAQ.
*** If you really mean to do this, run configure again using the extra
*** parameter `--disable-sanity-checks`.
  • Configure does not want to overwrite the default library and hence we need to give another directory to install the library.
  • Let us make a directory and run the configure script.
rishi@rishi-VirtualBox:~/build_glibc$ mkdir ../install_glibc
rishi@rishi-VirtualBox:~/build_glibc$ ../glibc-2.24/configure --prefix=/home/rishi/install_glibc/
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking for gcc... gcc
checking for suffix of object files... o
configure: creating ./config.status

>>>>>>>SNIP<<<<<<<<<<<<

config.status: creating config.make
config.status: creating Makefile
config.status: creating config.h
config.status: executing default commands
  • Configure completed
rishi@rishi-VirtualBox:~/build_glibc$ ls
bits  config.h  config.log  config.make  config.status  Makefile

Compliing

  • Let us run the make command now.
rishi@rishi-VirtualBox:~/build_glibc$ make -j 16
make -r PARALLELMFLAGS="" -C ../glibc-2.24 objdir=`pwd` all
make[1]: Entering directory '/home/rishi/glibc-2.24'
LC_ALL=C gawk -f scripts/sysd-rules.awk > /home/rishi/build_glibc/sysd-rulesT \



rishi@rishi-VirtualBox:~/build_glibc$ ls
bits  config.h  config.log  config.make  config.status  Makefile
rishi@rishi-VirtualBox:~/build_glibc$
rishi@rishi-VirtualBox:~/build_glibc$
rishi@rishi-VirtualBox:~/build_glibc$



rishi@rishi-VirtualBox:~/build_glibc$ make -j 16
make -r PARALLELMFLAGS="" -C ../glibc-2.24 objdir=`pwd` all
make[1]: Entering directory '/home/rishi/glibc-2.24'
LC_ALL=C gawk -f scripts/sysd-rules.awk > /home/rishi/build_glibc/sysd-rulesT \

    >>>>>>>>>>>>>>>>>>>>>SNIP<<<<<<<<<<<<<<<<<<<
gcc -nostdlib -nostartfiles -o /home/rishi/build_glibc/elf/pldd    -Wl,-z,combreloc -Wl,-z,relro -Wl,--hash-style=both /home/rishi/build_glibc/csu/crt1.o /home/rishi/build_glibc/csu/crti.o `gcc  --print-file-name=crtbegin.o` /home/rishi/build_glibc/elf/pldd.o /home/rishi/build_glibc/elf/xmalloc.o  -Wl,-dynamic-linker=/home/rishi/install_glibc/lib/ld-linux-x86-64.so.2 -Wl,-rpath-link=/home/rishi/build_glibc:/home/rishi/build_glibc/math:/home/rishi/build_glibc/elf:/home/rishi/build_glibc/dlfcn:/home/rishi/build_glibc/nss:/home/rishi/build_glibc/nis:/home/rishi/build_glibc/rt:/home/rishi/build_glibc/resolv:/home/rishi/build_glibc/crypt:/home/rishi/build_glibc/mathvec:/home/rishi/build_glibc/nptl /home/rishi/build_glibc/libc.so.6 /home/rishi/build_glibc/libc_nonshared.a -Wl,--as-needed /home/rishi/build_glibc/elf/ld.so -Wl,--no-as-needed -lgcc  `gcc  --print-file-name=crtend.o` /home/rishi/build_glibc/csu/crtn.o
make[2]: Leaving directory '/home/rishi/glibc-2.24/elf'
make[1]: Leaving directory '/home/rishi/glibc-2.24'
  • Make runs successfully.
  • Let us check the install_glibc directory. It has nothing in it.
$ ls ../install_glibc/
  • Let us run the make install command.
$ make install
LC_ALL=C; export LC_ALL; \
make -r PARALLELMFLAGS="" -C ../glibc-2.24 objdir=`pwd` install
make[1]: Entering directory '/home/rishi/glibc-2.24'
make  subdir=csu -C csu ..=../ subdir_lib
make[2]: Entering directory '/home/rishi/glibc-2.24/csu'
make[2]: Leaving directory '/home/rishi/glibc-

>>>>>>>>>>>SNIP<<<<<<<<<<<<<<<

-f /home/rishi/build_glibc/elf/symlink.list
test ! -x /home/rishi/build_glibc/elf/ldconfig || LC_ALL=C \
/home/rishi/build_glibc/elf/ldconfig  \
/home/rishi/install_glibc/lib /home/rishi/install_glibc/lib
/home/rishi/build_glibc/elf/ldconfig: Warning: ignoring configuration file that cannot be opened: /home/rishi/install_glibc/etc/ld.so.conf: No such file or directory
make[1]: Leaving directory '/home/rishi/glibc-2.24'

Install

  • Let us now check the install_glibc directory. It has the required files of the new compiled library.
rishi@rishi-VirtualBox:~/build_glibc$ ls ../install_glibc/
bin  etc  include  lib  libexec  sbin

Using the new library

Let us now use the above library to link and run our code. We will add a new function to the glibc, change the behaviour of a function in glibc and use the new function and call the changed function.

This will give us a good understanding of how to compile and link with the new library.

Here is the code for adding some changes to the glibc code. See the file glibc-2.24/stdlib/div.c and glibc-2.24/include/stdlib.h.

Here is the diff

glibc-2.24/stdlib/div.c

  • Here we have added a function my_div which just returns -1 on invokation and have changed the way the function div behaves. Now when we will pass 99 and 99 to div it will return 100 and 100. Read the default behaviour in the man pages.
$ diff glibc-2.24/stdlib/div.c temp/glibc-2.24/stdlib/div.c
51d50
< #include <stdio.h>
59,64d57
<   if (numer == 99 && denom == 99) {
<   printf ("\nValues are 99 and 99");
<   result.quot = 100;
<   result.rem = 100;
<   return result;
<   }
69,74d61
< }
<
<
< int my_div(void) {
<   printf("\n\nCalling my_div() function.");
<   return -1;
  • Here is the declaration of the new function.

glibc-2.24/stdlib/stdlib.h

$ diff glibc-2.24/stdlib/stdlib.h temp/glibc-2.24/stdlib/stdlib.h
753,754d752
<
< extern int my_div(void);
  • Here is the code which calls the functions.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include <stdio.h>
#include <stdlib.h>


int main () {

	div_t result = div(99, 99);
	int x = my_div();

	printf ("\n\nQuotient %d Remainder %d", result.quot, result.rem);
	return 0;
}
  • Here is the Makefile which will be used to compile the program.
 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
TARGET = div
OBJ = $(TARGET).o
SRC = $(TARGET).c
CC = gcc
CFLAGS = -g
LDFLAGS = -nostdlib -nostartfiles -static
GLIBCDIR = /home/rishi/install_glibc/lib/
STARTFILES = $(GLIBCDIR)/crt1.o $(GLIBCDIR)/crti.o `gcc --print-file-name=crtbegin.o`
ENDFILES = `gcc --print-file-name=crtend.o` $(GLIBCDIR)/crtn.o
LIBGROUP = -Wl,--start-group $(GLIBCDIR)/libc.a -lgcc -lgcc_eh -Wl,--end-group
INCDIR = /home/rishi/install_glibc/include

$(TARGET): $(OBJ)
	$(CC) $(LDFLAGS) -o $@ $(STARTFILES) $^ $(LIBGROUP) $(ENDFILES) 

$(OBJ): $(SRC)
	$(CC) $(CFLAGS) -c $^ -I `gcc --print-file-name=include` -I $(INCDIR)

clean:
	rm -f *.o *.~ $(TARGET)
	rm test.c.*
	rm a.out


# https://stackoverflow.com/questions/10763394/how-to-build-a-c-program-using-a-custom-version-of-glibc-and-static-linking/10772056#10772056
  • Run the make command.
$ make
gcc -g -c div.c -I `gcc --print-file-name=include` -I /home/rishi/install_glibc/include
gcc -nostdlib -nostartfiles -static -o div /home/rishi/install_glibc/lib//crt1.o /home/rishi/install_glibc/lib//crti.o `gcc --print-file-name=crtbegin.o` div.o -Wl,--start-group /home/rishi/install_glibc/lib//libc.a -lgcc -lgcc_eh -Wl,--end-group `gcc --print-file-name=crtend.o` /home/rishi/install_glibc/lib//crtn.o
  • Run the statically linked code
$ ./div

Values are 99 and 99

Calling my_div() function.

Quotient 100 Remainder 100
  • See the size of the staticically linked code. The huge size is due to static linking. We will now link it dynamically and then see the size.
$ ls -lh div
-rwxrwxr-x 1 rishi rishi 3.3M Jan 29 20:00 div
  • Run the dynamically linked code.

Todo

Link it dynamically.

Error

Unable to do it dynamically.

  • See the sizes of the files
rishi@rishi-VirtualBox:~/test_code$ ls -l dynamic-test static-test
-rwxrwxr-x  1 rishi rishi   8600 Jan 29 12:13 dynamic-test
-rwxrwxr-x  1 rishi rishi 909048 Jan 29 12:13 static-test

Todo

Link it dynamically.

  • Check the file type of the executables.

Todo

correct the following.

rishi@rishi-VirtualBox:~/test_code$ file static-test
static-test: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=866f4fe367915159ae62cc80a0ae614059d67153, not stripped
rishi@rishi-VirtualBox:~/test_code$ file dynamic-test
dynamic-test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /home/rishi/install_glibc/lib/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=c0f8ac9a77a879e6adc855333d6bc88c5078ffd3, not stripped

Conclusion

In this chapter we have see pretty important things with respect to using glibc. We have see where to find glibc, how to download, extract, make changes and compile the glibc library in your system.

Doing all the steps hands-on will enable you understand the whole workflow more clearly and will thus improve your understanding of systems.

Indices and tables