gcc functions with constructor attribute are not being linked
Asked Answered
S

2

9

I have a bunch of static libraries and they are interdependent. I faced problems while linking those libraries for my target because of dependencies. As a workaround I created one single archive file from all the libraries.

One of the static library has constructor and destructor functions so does the combined archive (examined the archive using nm and objdump) But when I used the combined archive for my target the final binary does not contain the constructor and destructor functions.

I also tried with --whole-archive but this option doesn't seem to work for me (the binary size didn't increase).

Any ideas what might have gone wrong. Thank you

Syreetasyria answered 5/7, 2011 at 22:56 Comment(1)
What is your linker? what is its version? (if you use gcc, please say version of gcc, of ld and binutils)Gourmand
S
8

The linker only loads the modules it must (i.e. what you actually reference implicitly or explicitly), unless you force it.

That is both a good thing, and unfortunate. It makes the link process faster and prevents code bloat. It normally does not cause problems either, because normally you reference all modules that are needed in some way, and what you don't reference isn't needed. Normally.
But it is also the cause for this silent failure: The linker never loads the module containing your constructor/destructor functions and never even looks at it. That is because you never actually call these functions.

You can force the linker to include the module by explicitly linking with the object file that corresponds to the source containing the constructor/destructor functions.

In other words, if the constructor/destructor functions are in foo.c, add -l/path/to/foo.o to your linker's options, or simply pass foo.o on the commandline to the linker (as an extra argument).

In either case, by doing so, you explicitly tell the linker to consider this module, and doing so will make it find the constructor functions and properly call them.

An alternative (which does not require you to play with the commandline) may be putting a variable or a function (which does not necessarily do anything) into the same source file as your constructor/destructor functions and calling that one from any source file in the main program.
This will also force the linker to load the containing module (where it will find the constructor functions).

Update:
I tested that alternative, it works fine:

/* libcode.c */
void enable_constructors() { /* do nothing */ }
void __attribute__ ((constructor)) con() { __builtin_puts("construct"); }
void __attribute__ ((destructor))  des() { __builtin_puts("destruct"); }


/* main.c */
extern void enable_constructors();

int main()
{
    enable_constructors();
    __builtin_puts("main");
    return 0;
}

Output is:

construct
main
destruct

It also works with a global variable which you touch from a source file in the main program:

/* libcode.c */
int enable_constructors; /* not used for anything */

void __attribute__ ((constructor)) cons() { __builtin_puts("construct"); }
void __attribute__ ((destructor)) des() { __builtin_puts("destruct"); }


/* main.c */
extern int enable_constructors;

int main()
{
    ++enable_constructors; /* touch the other module */
    __builtin_puts("main");
    return 0;
}
Stringfellow answered 6/7, 2011 at 13:41 Comment(6)
I did not try this method yet but I noticed that I did not use --Whole-archive properly. Now with this option I'm able to use constructors but I would be happy if I could make constructors linked without doing anything special.Syreetasyria
Calling a function that does nothing or incrementing an extern variable is an admittedly a somewhat ugly hack, but it boils down to a single superfluous INC instruction or a single CALL (or nothing at all if you're lucky) at any point of your program (for example at the beginning of main), which I think is still acceptable. Note that any code that compiler optimizations don't dead-strip will do, it needs not be in main or occur at a particular time. As long as something references something in the other module, the linker will stumble across the constructor attribute.Stringfellow
It should probably even suffice (though I've not tried!) to initialize variable from your main module with the address of the constructor function. That alone should already make the linker touch the module, too.Stringfellow
@Syreetasyria Now I faced with the same problem as yours. I tried add "-Wl,--whole-archive" flag to g++, but it didn't work. Could you give an example about how it works?Anisotropic
@Anisotropic Sorry, I don't have exact commands now, it's been so long. Could you tell which version of toolchain you are using?Syreetasyria
This worked well for me. I resorted to passing the object file containing the init code to gcc and it was successfully picked up by the linker and everything just worked. Also thanks for __builtin_puts, it's a nice touchSemiliquid
H
2

From the other answer:

You can force the linker to include the module by explicitly linking with the object file that corresponds to the source containing the constructor/destructor functions.

With modern CMake, this is as easy as an INTERFACE source on the library. Just use a dedicated source file for the constructor.

cmake_minimum_required(VERSION 3.13)

add_library(mylib STATIC libsource.c)
target_sources(mylib INTERFACE lib_ctor.c)
# CMake prior 3.13 requires an absolute path
# target_sources(mylib INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/lib_ctor.c)

add_executable(prog app.c)
target_link_libraries(prog mylib)

Here, lib_ctor.c would contain the constructor. INTERFACE sources are added to the list of source files of every target linking against mylib. So they get explicitly linked and the constructor function is read by the linker.

I wasted an hour or two on --whole-archive and other hacks before realizing my build system can take care of this in a much cleaner way. As CMake is pretty popular nowadays and this is the top search result for terms like "gcc force constructor" and "gcc constructor not linked", I'd like to share it.

Heptane answered 12/11, 2019 at 21:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.