# From C to exec: Part 3

In Previous parts, we followed a story of a piece of code transformed into a linked executable. The center stage was taken by symbols, around which the entire process of linking happens. They are the crucial component that lets us put programs fro multiple pieces. However, we watched what happens to symbols after they are there, but we haven't seen how to control their existence, and how to solve problems we may encounter.

The third part focuses on practical problem solving related to symbols and C.

## Emitting symbols in C

You might remember the term "translation unit". It's just a more convenient name for "a thing that is compiled together", the bunch of source files that result in a single object file. This simple definition will be used to refer to the sources as opposed to the object file.

C does not support namespaces, so whenever you define a function under the same name in a different context (e.g. a logging function, an error handler or a container operation), they truly take the same name. This can lead to trouble. Let's introduce a new set of files: `floats.c`:

```
#include <stdio.h>
void print_number(float n) {
  printf("Float %f\n", n);
}

void multiply_by_tau(int number) {
  print_number(6.68 * number);
}
```

Then `float.h`:

```
void multiply_by_tau(int number);
```

Finally, `main.c`:

```
#include <stdio.h>
#include "float.h"

void print_number(int number) {
  printf("Integer %d\n", number);
}

void power_of_two(int number) {
  print_number(2 << number);
}

int main(void) {
  multiply_by_tau(100);
  power_of_two(10);
  return 0;
}
```

In this example, we end up having two different functions called `print_number`. One is in the translation unit with `main.c`, the other in `float.c`. They are only called from the relevant translation unit, so we should be fine, right?

```
# gcc -c main.c -o main.o
# gcc -c float.c -o float.o
# nm main.o | grep print_number
0000000000000000 T print_number
# nm float.o | grep print_number
0000000000000000 T print_number
# gcc float.o main.o -o main
/usr/bin/ld: main.o: in function `print_number':
main.c:(.text+0x0): multiple definition of `print_number'; float.o:float.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
```

Not really. We emitted a symbol in both of them, and the linker doesn't know which one to choose. Oops. We have to tell the compiler explicitly that each of those functions will only ever be called from the same translation unit. C defines a keyword `static` for this purpose. Let's check if it works. `float.c` becomes:

```
#include <stdio.h>
static void print_number(float n) {
  printf("Float %f\n", n);
}

void multiply_by_tau(int number) {
  print_number(6.68 * number);
}
```

And `main.c` now looks like this:

```
#include <stdio.h>
#include "float.h"

static void print_number(int number) {
  printf("Integer %d\n", number);
}

void power_of_two(int number) {
  print_number(2 << number);
}

int main(void) {
  multiply_by_tau(100);
  power_of_two(10);
  return 0;
}
```

And the compilation:

```
# gcc -c float.c -o float.o
# gcc -c main.c -o main.o
# gcc float.o main.o -o main
# ./main
Float 668.000000
Integer 2048
# nm main.o
0000000000000045 T main
                 U multiply_by_tau
0000000000000022 T power_of_two
                 U printf
0000000000000000 t print_number
# nm float.o
0000000000000024 T multiply_by_tau
                 U printf
0000000000000000 t print_number
```

Success! We turned`print_number` into a *local symbol*, (`t` is lowercase) which is ignored during linking.

## Keywords

C defines keywords for controlling linkage type. Those are:

However, functions aren't the only items that can be assigned symbols. While any kind of memory area can get a symbol, global variables are the other most common usage. Let's see it all in action in `linkage.c`:

```
int v_def = 0;
extern int v_ext = 0;
static int v_stat = 0;

void f_def(void) {};
extern void f_ext(void) {};
static void f_stat(void) {};
```

Looking at the symbols shows us:

```
# gcc -c linkage.c -o linkage.o
linkage.c:2:12: warning: ‘v_ext’ initialized and declared ‘extern’
    2 | extern int v_ext = 0;
      |            ^~~~~
# nm linkage.o
0000000000000000 T f_def
0000000000000007 T f_ext
000000000000000e t f_stat
0000000000000000 B v_def
0000000000000004 B v_ext
0000000000000008 b v_stat
```

As expected, the default is to emit a symbol, and `static` makes the symbol local. But there's an unexpected warning from the compiler, suggesting that `extern` on a variable does more than just emit a symbol for it. Let's try to apply compiler's advice and not assign any value in`variable.c`:

```
extern int v_ext;
```

Looking at symbols:

```
# gcc -c variable.c -o variable.o
# nm variable.o
#
```

…nothing? Let's try something else in `variable.c`:

```
extern int v_ext;

void use_variable(void) {
  v_ext = 0;
}
```

Symbols:

```
# gcc -c variable.c -o variable.o
# nm variable.o
0000000000000000 T use_variable
                 U v_ext
```

There it is, undefined! Without the body of the variable, the compiler took `extern` to mean "not defined here" instead of "emit a symbol". No wonder that we didn't see its trace when we didn't try to make use of it. This is similar to *function prototypes* we covered earlier.

Notice that `U` printed by the `nm` program does not mean "symbol of the undefined kind present". Instead, it means "the relocation table calls for this symbol, but we don't have it". Here, `nm` mixes up the symbol table and the relocations table, which can be confusing.

## Using variables

Variables with the `extern` symbol may sound confusing at first, but they work the same way functions do. Let's take an example of three files. First is `main_var.c`

```
#include <stdio.h>
#include "var.h"

int main(void) {
  set_var(10);
  printf("var is %d\n", var);
}
```

The file `var.h`:

```
extern int var;
extern void set_var(int v);
```

And finally, `var.c`:

```
int var = 5;
void set_var(int v) {
  var = v;
}
```

Compiling them shows us that both the `set_var` function and the `var` variable are needed by `main_var.c`, and provided by `var.c`:

```
# gcc -c main_var.c -o main_var.o
# gcc -c var.c -o var.o
# gcc main_var.o var.o -o main_var
# ./main_var 
var is 10
# nm main_var.o
0000000000000000 T main
                 U printf
                 U set_var
                 U var
# nm var.o
0000000000000000 T set_var
0000000000000000 D var
```

As you can see, we managed to modify a variable from a different file using a function from a different file, and then read it out directly. This mechanism is commonly used to give programs access to memory managed by a library.

## Header file trouble

There is a situation is when a function clashes with itself. It sounds silly, but this can happen if the same function creates a symbol in multiple translation units.

This usually happens when a function lives inside a header of a library we're using. Headers are not actually restricted to holding function prototypes, but instead they are fully fledged pieces of C code, verbosely included in the source file. Let's create `source.c`:

```
const int before;
#include "header.h"
const int after;
```

And `header.h`:

```
const int inside;
```

After running the C preprocessor, which happens as the first compilation step, we get:

```
$ gcc -E source.c
# 1 "source.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "source.c"
const int before;
# 1 "header.h" 1
const int inside;
# 3 "source.c" 2
const int after;
```

We can see here that the text from the header was indeed included in place of the `#include` directive.

Now, why would anyone actually *want* to place a function in the header?

As you can see, the inclusion of a full function inside header means that it will land in the translation unit of the *calling* file, instead of the *providing* file, unlike in our library examples. Here we don't even *have* a separate "providing" file. We can see that our single translation unit contains what the header provided:

```
# gcc -c source.c -o source.o
# nm source.o
0000000000000004 C after
0000000000000004 C before
0000000000000004 C inside
```

The most obvious purpose for placing a function in the same translation unit as the caller is optimization. Making a function call is slow. In broad strokes, the compiled code usually saves a generic set of working data, as part of issuing a call and restores it as part of returning from it. But this doesn't make sense if our function is relatively simple. Sometimes it's more efficient to copy the contents of the function instead of calling it. The name for that is *inlining*.

A call to a function in a dynamic library can't be inlined automatically. That would require some advanced processing at the time our program is starting. A call to a function in a static library can sometimes be inlined. That is called *link-time optimization*, and it's done at linking time as a sort of additional, slow compilation step.

On the other hand, a call to a function in the same translation unit can be inlined as part of the compilation process even before the linking, and without much additional penalty. If we place the body of our function in the header, this is exactly what we get, regardless of the kind of library we create in our other translation units.

But what does this have to do with a symbol clashing with itself? Let's introduce an example of a library that must store something for bookkeeping, but doesn't want to be a burden for the calling code. Starting with `store.c`:

```
int calls_made = 0;
```

Corresponding `store.h`:

```
extern int calls_made;

void log_call(void) {
  calls_made += 1;
}
```

Now, `user.c`:

```
#include <stdio.h>
#include "store.h"

void print_and_bump(void) {
  puts("Caller print");
  log_call();
}
```

Its `user.h`:

```
void print_and_bump(void);
```

Finally, `clash.c`:

```
#include <stdio.h>
#include "store.h"
#include "user.h"

int main(void) {
  puts("main print");
  log_call();
  print_and_bump();
  return 0;
}
```

Let's try compiling and linking this project:

```
# gcc -c store.c -o store.o
# gcc -c user.c -o user.o
# gcc -c clash.c -o clash.o
# gcc store.o user.o clash.o -o clash
/usr/bin/ld: clash.o: in function `log_call':
clash.c:(.text+0x0): multiple definition of `log_call'; user.o:user.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
```

That's an error! Remember that we placed the `log_call` function into the header file. That means that every translation unit including the header file will get a copy. We can see that this is what happened:

```
# nm store.o | grep log_call
# nm user.o | grep log_call
0000000000000000 T log_call
# nm clash.o | grep log_call
0000000000000000 T log_call
# gcc -E user.c
[...]
# 1 "store.h"
extern int calls_made;

void log_call(void) {
  calls_made += 1;
}
# 3 "user.c" 2
[...]
# gcc -E clash.c
[...]
# 1 "store.h"
extern int calls_made;

void log_call(void) {
  calls_made += 1;
}
# 3 "clash.c" 2
[...]
```

The solution is straightforward. Let's change `store.h` to show this:

```
extern int calls_made;

static void log_call(void) {
  calls_made += 1;
}
```

Linking test:

```
# gcc -c user.c -o user.o
# gcc -c clash.c -o clash.o
# gcc store.o user.o clash.o -o clash
# nm clash.o | grep log_call
0000000000000000 t log_call
# nm user.o | grep log_call
0000000000000000 t log_call
```

That did it! Our static function's symbol turned local, and stopped being a duplicate.

## Inline woes

You might know the C99 keyword `inline` already. It's meant to be a suggestion to the compiler that the function should be copied instead of called. It turns out that it affects linking.

Taking the example from above and modifying it slightly, we get a new `store.h`:

```
extern int calls_made;

inline void log_call(void) {
  calls_made += 1;
}
```

If you think that `inline` implies no need for any sort of linking, you may be surprised:

```
# gcc -std=c99 -c user.c -o user.o
# gcc -std=c99 -c clash.c -o clash.o
# gcc store.o user.o clash.o -o clash
/usr/bin/ld: user.o: in function `print_and_bump':
user.c:(.text+0xf): undefined reference to `log_call'
/usr/bin/ld: clash.o: in function `main':
clash.c:(.text+0xf): undefined reference to `log_call'
collect2: error: ld returned 1 exit status
# nm user.o | grep log_call
                 U log_call
```

I'm not going to dive into what exactly happened here, but it boils down to C99 designers wanting to avoid too many copies when inlining isn't possible. A symbol may still be needed for the function, as explained by Jens Gustedt. That explanation offers a solution in the form of placing the following line into exactly one translation unit:

```
extern inline void log_call(void);
```

Alternatively, use the `static` version, although it may offer a different rate of success in inlining or emit too many copies.

## Bad declaration

Remember that symbols don't carry type information? One of the most confusing problems is using something believing it's one type when in reality it's another. For a quick example, see a botched version check in file `version.c`:

```
const char version[] = "10.1";
```

The header `version.h` has a mistake! The variable is declared as `int` instead of `char`:

```
extern const int version;
```

The file `check.c` is using the declaration from the header:

```
#include <stdio.h>
#include "version.h"

int main(void) {
  printf("Version %d\n", version);
  return 0;
}
```

And when it tries to read the `version` variable…

```
# gcc -c version.c -o version.o
# gcc -c check.c -o check.o
# gcc version.o check.o -o check
# ./check
Version 825110577
```

That doesn't look right… Nowhere during the entire process was the type of the actual variable compared to the one declared in the header. The compiler believed what was the header file, the linker matched the symbol simply by name, and the mistake only appeared when we tried to execute the program.

This time we got off easily, with an obvious problem. But the bugs can crash your code outright – if the type in the mistaken definition is larger than the actual type, or be subtle, if the wrong type is only slightly bigger and causes unrelated memory corruption, or when an `int` is taken for a `float`.

Your C++ compiler can catch some of those errors:

```
# g++ -c check.c -o check.o
# g++ -c version.c -o version.o
# g++ version.o check.o -o check
/usr/bin/ld: check.o: in function `main':
check.c:(.text+0x6): undefined reference to `version'
collect2: error: ld returned 1 exit status
# nm version.o
0000000000000000 r _ZL7version
# nm check.o
0000000000000000 T main
                 U printf
                 U version
```

Unfortunately, C offers no solution but to always keep the headers correct!

## Summary

Those are the most common but annoying problems you may encounter which stem from the way C and symbols interact. If you're intrigued about some other linking problem, let me know about it. You can find me on the "about me" page.

## Glossary

Important terms this episode:

Written on .

Comments

dcz's projects

Thoughts on software and society.

Atom feed