blog

SPO600 Lab 4 - Building Emacs and Libc

This post will describe how I completed the fourth lab for the SPO600 course. For this lab, I need to choose a package from the GNU project, download it, build it, and test it (without using a package manager). After this, I have to download the GNU Standard C Library (libc), introduce a small change in it, build it, and test it. The second part of this lab (describing the multiarch system) will be in another post. Just a quick note: I did this lab on my personal computer, an x86_64 machine running Arch Linux. Before starting, I want to talk quickly about the famous recipe `configure + make + make install`. Everyone who used Linux probably used it once or twice. If you have only the source code of a program, but not the compiled binary, you will need to compile it and move the compiled binary to its final location yourself, and that is what the `make` command does: it is a recipe for how to compile and move the output in your system. This recipe is described in a file called `Makefile`. When you run the command `make`, it reads the `Makefile`, executes the set of instructions described by it to compile your code the right way. After running the `make` command, you use `make install` to move the binaries to their final destination. I like to see the `make` command as a recipe on how to compile and install a program. One thing to notice is: you will not always have a `Makefile` available; instead, you will have a file called `Makefile.in`. In order to get a `Makefile`, we will need to run another command before `make`: `configure`. If the `make` command is a recipe on how to compile and install something, the `configure` command is a recipe for a recipe for compiling and installing a program. Why do we need this? Because systems can be very different: they can have different CPU architectures, they may use different compiler versions, different dependencies, different libraries, and so on. By running the `configure` command, we will actually build a `Makefile` from our system's settings, based on the `Makefile.in`. In short, this is the general idea: we run `configure`, which is will gather data from our system, and based on it, will build a recipe for how to install the software - it is called `Makefile`. The `make` command will then read the `Makefile` and build the program. We then run the `make install` command, which also reads the recipe from the `Makefile`, and will move the program to its final destination. # Package Emacs I must tell you: I am a VIM person, not because it is a great editor, but because it makes me look cool. However, the cool kids nowadays seem to be using Emacs (in case you don't know what Emacs is, it is a great Operating System, but it has no good text editor), so I decided to install Emacs on my computer. In the GNU Project website, I found the Emacs package, which took me to a mirror to download the source code. So far so good. I downloaded the tar ball, uncompressed it, and followed the instructions on the `INSTALL` file. The `INSTALL` file recommended me to simply run the `configure` script. However, I wanted the build in another directory (it is more organized this way), so I switched to another directory and ran the configure script from there. **../emacssrc/configure** - I commanded my computer **configure: error: Emacs hasn't been ported to 'x86_64-unknown-linux-gnu' systems.** - Screamed the script **Here we go** - I mentally tweeted to myself Turns out, I downloaded the oldest source code for emacs available - one from 2005. That's how I concluded that lists are not always sorted from most to least recent. What a world we live in. I downloaded the newest version and tried again; this time, the script finished successfully. I read the lines that the `configure` script printed: it listed things such as libraries used, my operating system, architecture, etc. Everything seemed to be right. I then used the `make` command to build the application. According to the INSTALL file, it would be built in the `src` directory. It was interesting to watch the files being created there, while the `make` command was running. The command finished successfully. After the built was complete, I would have to run the command `make install` to move the binary files to their place - since I didn't actually want it installed in my system, I did not do this step. Now I only had one task left: test the program. I went to the `src` directory and ran the `emacs` executable. I have no idea how to use emacs, but it seemed to be working, so I called it a day. # LibC Now it was time to do the same with glibc. I went to the glibc package page and downloaded the latest source code. Not the one from 1994, but the one from the bottom of the list which is also the latest one (yes, I am a quick learner). I switched to another directory (so the built files would not get mixed) and ran the following commands: ``` $ ../glibc-2.26/configure --prefix=/usr ``` The `--prefix` option for the `configure` command will tell the library to use its dependent files from my `/usr` directory. And then I ran the `make` command. It takes a while to finish, so I left it working and went to make a sandwich (this detail is very important). Now, it is very important to know if my library is working, but even more important, it is good to know if my programs are actually running the library that I just compiled (an alarm is better than something failing silently, right?). So, I like ducks, I think they are great, and I think it would be nice if libC offered support for ducks. So this is what I did: I opened the `puts.c` (this file contains the definition of the `puts` function, which prints strings on the screen) file in my text editor and modified it. Now, here is something interesting: if we use the `printf` command in a C program, but we only pass one string to it (no other parameters), it will be replaced by the compiler with a call to `puts`, because it is a lot faster. Here is the original code: ``` #include "libioP.h" #include #include int _IO_puts (const char *str) { int result = EOF; _IO_size_t len = strlen (str); _IO_acquire_lock (_IO_stdout); if ((_IO_vtable_offset (_IO_stdout) != 0 || _IO_fwide (_IO_stdout, -1) == -1) && _IO_sputn (_IO_stdout, str, len) == len && _IO_putc_unlocked (' ', _IO_stdout) != EOF) result = MIN (INT_MAX, len + 1); _IO_release_lock (_IO_stdout); return result; } weak_alias (_IO_puts, puts) ``` Here is my modified code: ``` #include "libioP.h" #include #include int _IO_puts (const char *str) { int result = EOF; _IO_acquire_lock (_IO_stdout); if ((_IO_vtable_offset (_IO_stdout) != 0 || _IO_fwide (_IO_stdout, -1) == -1) && _IO_sputn (_IO_stdout, "QUACK", 5) == 5 && _IO_putc_unlocked (' ', _IO_stdout) != EOF) result = MIN (INT_MAX, 5 + 1); _IO_release_lock (_IO_stdout); return result; } weak_alias (_IO_puts, puts) ``` And here is the result of running a "Hello world" program with my library (the `printf` is replace with a `puts`): PERFECT.