1

I have a pre-built image of a shared library, and that library implements main() with something like:

int main(...) {
    bootstrap(...);
    return 0;
}

bootstrap(...) {
    /*...*/
    sc_main(...);
}

and the implication is that the executable provide sc_main(). The shared library lists sc_main() as an undefined symbol as one would expect.

However, legacy usage has arisen where the executable implements main() instead; and historically this has not been a problem. But now I want to rebuild the library from source, and when I put my re-built version in place of the original blob the linker refuses to link the executable because it's using --no-allow-shlib-undefined and it can't find sc_main().

Sure, I knew that symbol was absent, and I knew the shared object listed it as undefined, but why was this OK before but not now that I rebuilt the library with a newer toolchain? The command to link the executable is not changing. The order of the arguments to the linker is not changing. Only the specific binary for the shared object is changing, so obviously the new shared object has a problem.

In fact, I can manually override the linker with --allow-shlib-undefined and get an executable which works just fine, so sc_main() is actually not needed and the executable's implementation of main() is being instead of the shared library's version. Manual override isn't a proper solution, though.

readelf indicates that the two versions are about as identical as they could be. Every defined symbol is the same and every undefined symbol is the same; just at new offsets. Flags are the same. Dependencies are the same. The generated code is different because of the compiler change, but only the symbols should affect linkage, right?

So, given that I have this "magic" binary file which I can't reproduce for myself; what are the queries that I can perform on each binary to determine why they lead to different requirements while linking the executable?

My theory is that there's some race in the linker to decide whether the library or the application provides the main() function. If the library provides it then we will need bootstrap(), and if we need bootstrap() then we will need sc_main(). Whereas, if the linker looks at the executable's main() first then there's no reference to sc_main() in the call graph. Does the linker look that deeply into shared libraries to reconcile dependencies and non-dependencies? Maybe not at function granularity but section or segment granularity or something?

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.