Blogpost about the build process

Found a blogpost about ELF Linking and Symbol Resolution :: Noratrieb's blog

Simplified summary with questions

cargo build uses rustc which compiles each crate separately. Dependencies are also a compilation units (CUs).

Each CU —including our own crate— is compiled to an object file (with.o suffix). The .os have undefined symbols since the deps are not included yet.

Linking is the process of resolving those undefined symbols in the crate's object file.

Static Libraries (.a) are bundles of object files. They are included in the final binary.

  • Why is this extra level needed and not just separate object files?
  • Also the non-bundled .o files are included right?

For Dynamic Libraries (*.so) the content is instead searched at Runtime, it's not in the binary. This is done by ld-linux before our program's main runs.

In summary, linking static libraries (and object files) resolves the symbol and puts content in; linking dynamic libraries skip the last step (don't put content in).


That's just the intro of the blogpost, the deeper part read in the link.

For historical reasons. Originally, half-century ago, linker was only able to either include full object file or include nothing at all. Static libraries is the solution.

Later linkers have become more advanced, but, of course, it's almost impossible to change things that were established over half-century of usage.

I was confused about:

  • Is ld a.o -lb same as ld -lb a.o ?

But no, it's not for libraries:

To get around this, we always need to ensure that we provide each library and object file before its dependencies,

a.o must be before it's dependencies; order matters, whereas for ld a.o b.o order does not, and is same as ld b.o a.o.

I think some practical knowledge could be easily added.

I've never done the process, but seems that:

  • Binaries and Examples are always bin
  • But libraries can have multiple targets of compilation: bin , lib , rlib , dylib , cdylib , staticlib , and proc-macro; in fact it can do many of those at once, by specifying an array.

I do wonder what exactly is the use, and where. So for example a rust code can use a dylib to be included only if used? Why would you use a bundle of .o files i.e a staticlib instead of simply importing the library?

If you want to statically link a library such that you don't have to bundle a separate dylib file with your executable you have to use the staticlib rather than cdylib crate type.

By the way the staticlib and cdylib crate types are meant to be used when you want to expose a C ABI for use by other languages.

For convenience. Instead of adding a long series of object files and risking to forget some of them, you can simply add one or a few static libraries (.a on Linux and .lib on Windows), which are typically organized by the type of content so that you know whether you need them or not.

The linker should keep only the translation units that include the references used in the executable.

If I understand the question correctly (why use static libraries and not always dynamic libraries), on top of what was answered above, there's another difference: the linker cherry-picks the required parts of the static library that you need, as explained above, but the whole dynamic library has to be loaded in memory at runtime. On the other hand, the dynamic libraries can be shared when several processes need them.

So it's indeed a question of whether you want your executable to be independent of any other deliverables or not (see how many questions float in the Internet about missing .dll and .so files and where to get them), and of optimization if you use only a tiny part of a lib that is not likely to be shared with other programs in its dynamic instance. If there's a choice between the two, of course.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.