35

Say I have a C program main.c that statically links to libmine.a. Statically linking to a library causes library functions to be embedded into the main executable at compile time.

If libmine.a were to feature functions that weren't used by main.c, would the compiler (e.g. GCC) discard these functions?

This question is inspired by the "common messaging" that using static libraries make executables larger, so I'm curious if the compiler at least strips away unused code from an archive file.

4

3 Answers 3

38

By default, linkers handle object files as a whole. In your example, the executable will end up containing the code from main.c (main.o), and any object files from libmine.a (which is an archive of object files) required to provide all the functions used by main.c (transitively).

So the linker won’t necessarily include all of libmine.a, but the granularity it can use isn’t functions (by default), it’s object files (strictly speaking, sections). The reason for this is that when a given .c file is compiled to an object file, information from the source code is lost; in particular, the end of a function isn’t stored, only its start, and since multiple functions can be combined, it’s very difficult to determine from an object file what can actually be removed if a function is unused.

It is however possible for compilers and linkers to do better than this if they have access to the extra information needed. For example, the LightspeedC programming environment on ’80s Macs could use projects as libraries, and since it had the full source code in such cases, it would only include functions that were actually needed.

On more modern systems, the compiler can be told to produce object files which allow the linker to handle functions separately. With GCC, build your .o files with the -ffunction-sections -fdata-sections options enabled, and link the final program with the --gc-sections option. This does have an impact, notably by preventing certain categories of optimisation; see discard unused functions in GCC for details.

Another option you can use with modern compilers and linkers is link-time optimisation; enable this with -flto. When optimisation is enabled (e.g. -O2 when compiling the object files), the linker will not include unused functions in the resulting binary. This works even without -ffunction-sections -fdata-sections.

4
  • This answer says "modern compilers", but I'm pretty sure LightSpeed C on my Mac back in 1989 or so would only link in functions that were explicitly used.
    – larsks
    Commented Sep 2, 2022 at 22:54
  • How does this interact with link-time optimization (-flto), which defers certain code generation to link time? If that option is passed, does that allow the linker to perform the optimizations that it would ordinarily be prevented from performing by --function-sections -fdata-sections --gc-sections? Commented Sep 3, 2022 at 6:18
  • @larsks that depended on how you defined the libraries in Lightspeed — “projects as libraries” would only pull in functions that were actually used, “binary libraries” (.lib files) were coarse-grained. Commented Sep 3, 2022 at 7:28
  • 2
    @CodyGray with LTO (and optimisation, so -O2 -flto in practice) the linker will eliminate unused functions from the resulting binary, even if the object files were built without -ffunction-sections. Commented Sep 3, 2022 at 7:31
14

Yes. But it is on a module level, not on a function.

For example, you have two source files: foo_goo.c and bar.c

// foo_goo.c
int foo() { .. };
int goo() { .. };
// bar.c
int bar() { .. };

Compile them into foo_goo.o and bar.o, and put both .o into the archive libmine.a.

Now, in your main() you are calling only the foo() function.

// main.c
int main(int argc, char **argv) {
   return foo();
}

Compile this source file into main.o and link with the .a.

The linker will see the function foo() in a module foo_goo.o and will use it, the main() will reference the foo() function, and the goo() function would be added into final binary, but not refrenced. The bar.o would be ignored, since none of the names in it were referenced.

-1

This has nothing to do with the compiler. It is the linker which puts all the objects needed into a single file if you use static linking.

That will be the main() function of your C program, everything possibly used by main, everything used by something possibly used by something used by something used by main and so on. If there is a function that cannot possibly be called or an object that cannot possibly be accessed then the linker can leave it out. If an object file contains five functions that can be called and ten that cannot be called, the linker may include only the first five.

2
  • 1
    No, if a single object file in a library contains fifteen functions, and one of them is called, all fifteen functions will be included in the final binary, unless the compiler provided extra information in the object file (or split it up into one section per function), and the linker was told to use that extra information. Commented Sep 3, 2022 at 13:20
  • 1
    Technically the compiler and linker are separate operations. But practically they're always used together, and might even be the same program. So it's a semantic quibble to argue whether it's the compiler or linker that's responsible. Commented Sep 5, 2022 at 2:44

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .