0

How can I let GCC check if a shared object library provides definitions to all functions that have been declared in a header file? Given the example below, how can I verify during compilation steps that 'mylib.h' and 'mylib.c' are "compatible" without having an executable that (kind of) tests the library and header in the way that it then really links both?

mylib.h:

#ifndef MYLIB_H
#define MYLIB_H

int test(int);

#endif

mylib.c:

#include "mylib.h"

int test_(int arg) { // Note: The definition here, does not fit the declaration!
    return arg * 2;
}

main.c:

#include <stdio.h>
#include "mylib.h"

int main(int argc, char* argv[]) {
    printf("%d\n", test(42));
    return 0;
}

I would like to have some warnings that the header file can result in undefined references with the shared object file if used by some application (edited wording for clarification), but it's as follows:

gcc -c -fPIC -o mylib.o mylib.c 
gcc -shared -Wl,--no-undefined,--no-allow-shlib-undefined -o libmylib.so mylib.o
# no error/warning here!
gcc -L ./ -o main main.o -lmylib
main.o: In function `main':
main.c:(.text+0x15): undefined reference to `test'
collect2: error: ld returned 1 exit status

As shown, I tried to use the correct linker options '-Wl,--no-undefined,--no-allow-shlib-undefined' but it doesn't help, even if use the same options as shown here: Force GCC to notify about undefined references in shared libraries

gcc -fPIC -shared -Wl,-soname,libmylib.so -Wl,--no-undefined -o libmylib.so mylib.c

-This means, I cannot reproduce the answer to the linked question!-

EDIT:

From discussions, I understand that the options like --no-allow-shlib-undefined aim for a different case, i.e., give warnings if a shared object calls/references some other library and references are undefined. Anyways, the aforementioned work-around is to have a little test that warns about undefined references:

test-ref.c:

#include "mylib.h"

int main(int argc, char* argv[]) {
    test(0);
}

which, if compiled and linked, gives the desired warning:

gcc -L . -o test-ref-noexec test-ref.c -lmylib
/tmp/ccF4ob56.o: In function `main':
test-ref.c:(.text+0x15): undefined reference to `test'
collect2: error: ld returned 1 exit status

Note: The point is that it's not necessary to execute the binary and, therefore, arguments provided to the function don't matter at all. In that sense, I assume(d) GCC/linker could do this kind of check for a some defined header file or warn about any undefined reference etc.

For the sake of completeness, I use the following setting:

uname -a
Linux ubuntu 4.4.0-96-generic #119-Ubuntu SMP Tue Sep 12 14:59:54 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.4' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)
sema
  • 532
  • 7
  • 23
  • 1
    What you want might be solvable with some external tool, but it's not really how translation units or C compilers and linker work. You should probably learn the difference between a *declaration* (what you have in the header file) and a *definition* (what you have in `mylib.c`). – Some programmer dude Oct 04 '17 at 13:23
  • Why does it matter whether all the declarations in a given header match definitions in a given library? The relevant question is usually whether some or all of the functions *that are actually called* are defined in a given library. Compilers can more readily be persuaded to answer the latter question. – John Bollinger Oct 04 '17 at 13:35
  • Additionally, your title casts the question in terms of header and shared object, but the text instead describes it as about header and source files. Which do you really want to know about? – John Bollinger Oct 04 '17 at 13:39
  • @Someprogrammerdude Thanks, I think my comment "Note: The definition here, does not fit the declaration!" indicates what I have understood. Can you elaborate more on what I may have not understood about declaration/definition from your perspective? – sema Oct 04 '17 at 13:39
  • "I would like to have some warnings that the header file has undefined references with the shared object file" The header file have not definitions, and so can't have any undefined references. Maybe I'm misreading it? – Some programmer dude Oct 04 '17 at 13:43
  • And also, there's really no connection between the header file and the translation unit built from `mylib.c`. How would the compiler ever know that a specific translation unit should define a specific declaration? Without compiler extensions it can't. And the linker have no knowledge of anything about the issue either, since there are no undefined references to `test` in any of the translation units used to create the library. Perhaps the problem is your understanding of [translation units](https://en.wikipedia.org/wiki/Translation_unit_(programming))? – Some programmer dude Oct 04 '17 at 13:44
  • @JohnBollinger Thanks, I have indeed thought about a test file that calls all the functions of the header, which presumes that my test file has a coverage that includes all function calls. That moves the problem to the test coverage analysis tool + compilation/linkage of the test code. This is exactly what my question is about, and so far I approached it with the compiler/linker and thought it should be feasible this way. To your second question: Well, I compile a shared object from a source file, I don't see how this is decoupled. Why do you distinguish it so strictly source and .so file? – sema Oct 04 '17 at 13:45
  • @Someprogrammerdude I got you, "has" is the wrong wording here. I know and understand, the compiler does not NEED to check the header for some declarations, but I could do it (kind of) manually with 'nm' and the header file, so why shouldn't it be possible to let GCC/linker check that for me? (rhetoric question) Is your answer then, that it's definitely not possible with either of them? I could accept that answer, if it's really the case BUT what is the ld option '--no-allow-shlib-undefined' then all about? – sema Oct 04 '17 at 13:52
  • @sema - If this was a *real* library, you would likely have to write some test cases to verify that `test(i)` always returns `2 * i` for several different values of `i`. And you would then quickly notice if `test` was missing altogether. – Bo Persson Oct 04 '17 at 13:57
  • @BoPersson Thanks, but this is not the question. What i'm doing with my _real_ library is a JNI wrapper for some HW analysis system calls. Unfortunately, JNI header generation changes the function definitions whenever I change something in the Java side code. Writing tests for simply wrapper system calls is kind of pointless because it would only check my function definitions, which is useless if GCC checks that all functions have been defined. – sema Oct 04 '17 at 14:02
  • That's not really what the `--no-allow-shlib-undefined` option do. I recommend you read [the documentation](https://sourceware.org/binutils/docs/ld/Options.html) about it. – Some programmer dude Oct 04 '17 at 14:02
  • @sema why do you *not* distinguish between a shared object and the source (not necessarily just one file) from which it was built? The information they contain is related, to be sure, but different. Moreover, having one of the two does not necessarily mean you have or can obtain the other. Overall, the answer to your question depends greatly on what exactly it is that you're trying to compare to the header. – John Bollinger Oct 04 '17 at 14:06
  • I have read the documentation several times, but honestly I don't get why it's not doing what I want to do (Please note that I'm not a native English speaker.): "The default behaviour is to report errors for any undefined symbols referenced in shared libraries if the linker is being used to create an executable, but to allow them if the linker is being used to create a shared library." sounds to me like default is to check for undefined references when creating an executable and this option switches to "when creating a shared library". – sema Oct 04 '17 at 14:09
  • The magic word is "referenced". The only way to *reference* a library symbol is **to use it**. – tofro Oct 04 '17 at 14:14
  • @tofro You're right, that's kind of the magic word because it points to the problem, BUT you see it in the opposite way as I do because the linker references the functions without using it! That means, it should be able to reference function declarations of a header file without using it, which is good because using it requires knowledge of the arguments to be provided and linkage doesn't. – sema Oct 04 '17 at 14:20
  • Nah. You have a fatal mis-understanding what linking does - It doesn't *check* anything - It simply resolves symbolic references with numerical references. And if something is *not referenced*, it will not even touch it. – tofro Oct 04 '17 at 14:25
  • @tofro Okay, that's now a bit about wording: If it CAN'T resolve a symbolic reference, it could do something like giving a warning. That's what I mean with a check. The header provides all the information that is necessary to resolve a symbolic reference, right? So it's not impossible. - Nevertheless, I understand that at the state of linking objects to a shared library compiler/linker have no notion of the header file to be "checked", but I thought it could be possible with some features of GCC/linkers that I have never heard of. That's the question. – sema Oct 04 '17 at 14:37

0 Answers0