6

Running example C code is a painful exercise unless it comes with a makefile.

I often find myself with a C file containing code that supposedly does something very cool, but for which a first basic attempt at compilation (gcc main.c) fails with—

main.c:(.text+0x1f): undefined reference to `XListInputDevices'
clang-3.7: error: linker command failed with exit code 1 (use -v to see invocation)

—or similar.

I know this means I'm missing the right linker flags, like -lX11, -lXext or -lpthread.

But which ones?


The way I currently deal with this is to find the library header that a function was included from, use Github's search to find some other program that imports that same header, open its makefile, find the linker flags, copy them onto my compilation command, and keep deleting flags until I find a minimal set that still compiles.

This is inefficient, boring, and makes me feel like there must be a better way.

  • Can you provide an example file? A reasonable way is to look up which package the header file belongs to. From there, it's a hop, skip and jump to finding the correct library flag. But to provide an illustration, an example file would help. – Faheem Mitha Apr 20 '16 at 15:48
  • @FaheemMitha I don't see how an example file helps the general question. – Anko Apr 20 '16 at 15:50
  • Well, would you like an illustration of a technique, or general remarks? Your choice. – Faheem Mitha Apr 20 '16 at 15:51
  • @FaheemMitha Whatever answers the question. Examples are trivial to construct: just pick any library function that requires a linker flag, like XListInputDevices used in the example compiler error here. – Anko Apr 20 '16 at 16:04
  • 1
    @FaheemMitha Unless I misunderstand what you mean, the header a function was defined in can easily be found by checking man, as mentioned. For XListInputDevices, the appropriate include-line is #include <X11/extensions/XInput.h>. I'm wondering how I can tell based on that what -lSomething flag I need. – Anko Apr 20 '16 at 16:12
5

The question is how to determine what linker flag to use from inspection of the source file. The example below will work for Debian. The header files are the relevant items to note here.

So, suppose one has a C source file containing the header

#include <X11/extensions/XInput.h>.

We can do a search for XInput.h using, say apt-file. If you know this header file is contained in an installed package, dpkg -S or dlocate will also work. E.g.

apt-file search XInput.h
libxi-dev: /usr/include/X11/extensions/XInput.h

That tells you that this header file belongs to the development package for libxi (for C libraries, the development packages (normally of the form libname-dev or libname-devel) contain the header files), and therefore you should use the -lxi linker flag.

Similar methods should work for any distribution with a package management system.

| improve this answer | |
  • Marking accepted: this is a workable method! I'm not on Debian, but here's a CW to complement this method, with some other distros' specific tools. – Anko Apr 20 '16 at 23:43
  • So basically, find the package name , drop the lib and drop the dev and voila ?! Or there's more details to it ? What baffles me is how -lxi translates to /usr/include/X11/extensions/XInput.h , because the method with apt-file seems to imply gcc looks for a library that somehow corresponds to apt package name,and I can't see that in the pathname – Sergiy Kolodyazhnyy Jun 10 at 8:19
  • Alright, I found the answers I needed: it has nothing to do with apt directly, but with the linker. I've put all that into an answer. – Sergiy Kolodyazhnyy Jun 10 at 8:51
2

Here's a community wiki to collect other distros' equivalent tools for Faheem's method. Feel free to edit, but keep the sort alphabetical for searching.

Arch

Use pkgfile from the extra repository, passing the header file name as a parameter.

Example:

$ pkgfile XInput.h
extra/libxi
extra/nx-headers

Debian

(and anything Debian-based using dpkg)

apt-file search for the header file name, as covered.

Gentoo

As covered in another question by warl0ck, use the pfl package's e-file program, or alternatively, the web-based Portage File List search.

1

Core of the Question: Header File vs Library Name

The core of the question was posted in the comments below the question itself:

For XListInputDevices, the appropriate include-line is #include <X11/extensions/XInput.h>. I'm wondering how I can tell based on that what -lSomething flag I need

To answer the question appropriately we need to understand the distinction between -lXi ( or rather libXi.so ) and XInput.h. The XInput.h is a header file and -l looks for object files. According to Red Hat documentation on gcc usage, section 16.1,

A special file name convention is used for libraries: A library known as foo is expected to exist as file libfoo.so or libfoo.a. This convention is automatically understood by the linking input options of GCC, but not by the output options

Both headers and libraries are needed and both are related, but not the same(ref):

... if you do #include the header the compiler does not insert the code for the function you call into program.o. It merely inserts a reference to them.

As mentioned, the headers and linkers are somewhat different, since headers store declarations (or in layman terms - descriptions) of functions and other stuff, and libraries - store actual objects (reference) or compiled version of those functions. As this answer puts it:

  • The header is a phone number you can call, while...
  • ...the library is the actual person you can reach there!

How do the headers and libraries play together ? They are both necessary steps in building an application. First, the description of functions and interfaces is gathered ( aka compiled ) and then the code you write is linked to a library where the actual interfaces and functions are implemented.

The library itself should be compiled with the header included (ref) into the library. In fact you can take a look at an example of how simple static library is created.

So the answer to your question, is as follows: there is no guaranteed relationship between the header and the library name. ( or at least I haven't found any so far ; see also this ref) . And, finding out how to match them is a matter of knowing what's in the package or in the source code.

In fact you can do this to prove the point:

$ echo '#include<errno.h> int main(){return 0;}' | gcc -S -lc

which shows that we're compiling against libc, but the header that is part of libc is different in name.

Another example to prove the point is #include <math.h> , which is part of libc but actual implementation is in libm.so.6 , so you will see code compiled with gcc -lm.

$ apt-file find  /lib/x86_64-linux-gnu/libm.so.6 
libc6: /lib/x86_64-linux-gnu/libm.so.6

Finding the related .so or .a library file

A lot of mysteries can be solved with documentation. Let's refer to gcc documentation for link options (note , emphasis in bold is mine):

-llibrary

-l library

Search the library named library when linking. (The second alternative with the library as a separate argument is only for POSIX compliance and is not recommended.)

The -l option is passed directly to the linker by GCC. Refer to your linker documentation for exact details. The general description below applies to the GNU linker.

OK, so the gcc itself isn't actually looking up the library. It is the linker's job. But let's read on.

The linker searches a standard list of directories for the library. The directories searched include several standard system directories plus any that you specify with -L.

So there is a standard list of directories. Great, this narrows down. How do we get that list ? One way among many is to use ldconfig -v which was generously explained by the user telotorium

But the gcc documentation goes further to explain:

Static libraries are archives of object files, and have file names like liblibrary.a. Some targets also support shared libraries, which typically have names like liblibrary.so. If both static and shared libraries are found, the linker gives preference to linking with the shared library unless the -static option is used.

It makes a difference where in the command you write this option; the linker searches and processes libraries and object files in the order they are specified. Thus, ‘foo.o -lz bar.o’ searches library ‘z’ after file foo.o but before bar.o. If bar.o refers to functions in ‘z’, those functions may not be loaded.

In other words, what you're really instructing gcc and subsequently ld to do, is to look for a file libxi.so (so being a shared object extension) or libxi.a in the list of standard paths. If say we store something in /opt and the ld linker doesn't know about it, passing anything to -l won't work.

The accepted answer seems to hint at this, by virtue of most debian package names for libraries corresponding to that libwhatever.so name. But I cannot confirm or deny that bit, as I know enough about debian packaging for my humble needs only, and not these minute details. What I do know is that apt may trigger ld cache to be rebuilt.

Now, what is also important to realize is that ld actually has all these directories in cache /etc/ld.so.cache. So if the directory is removed from cache, linker won't know of library's existence.


But of course if we want to test all the above theory based on the example of libxi-devel , we can do this:


    $ dpkg-query -L libxi-dev | grep -i '.so'
    /usr/share/man/man3/XIDefineCursor.3.gz
    /usr/lib/x86_64-linux-gnu/libXi.so
    /usr/share/man/man3/XIUndefineCursor.3.gz

and then check ldconfig to confirm that /usr/lib/x86_64-linux-gnu is on the list of searched directories . . . which it is:

$ ldconfig -p | grep libXi.so
    libXi.so.6 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libXi.so.6
    libXi.so.6 (libc6) => /usr/lib/i386-linux-gnu/libXi.so.6
    libXi.so (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libXi.so

Now this also raises a question, is the .h file useless ? No, apparently gcc still needs it to exist at compilation time ( because gcc does check the include-paths and because some libraries can be header-only )

$ mv /usr/include/X11/extensions/XInput.h /tmp
$ echo '#include <X11/extensions/XInput.h> void main(){return 0;}' | gcc -lXi -E -
# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "<stdin>"
<stdin>:1:36: warning: extra tokens at end of #include directive
<stdin>:1:10: fatal error: X11/extensions/XInput.h: No such file or directory
compilation terminated.
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.