0

I am trying to understand how libraries and linking in gcc work. I'm trying to compile an example ncurses "Hello World" program from a file test.c:

#include <ncurses.h>

int main()
{
    initscr();                      /* Start curses mode              */
    printw("Hello World !!!");      /* Print Hello World              */
    refresh();                      /* Print it on to the real screen */
    getch();                        /* Wait for user input */
    endwin();                       /* End curses mode                */

    return 0;
}

I first compiled it (without a makefile) using this command:

gcc test.c -o test.exe -lncurses

This produced a 9kb file. After not understanding what the -lncurses switch did, I read a bit about linking, then about static and dynamic, and decided I wanted to try compiling statically à la here:

gcc -static test.c -o test.exe -lncurses

However, for some reason, that didn't work, and produced tons of ld 'undefined reference to x' errors. Example:

/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/libncurses.a(lib_echo.o): In function `echo':
(.text+0x3): undefined reference to `SP'

From somewhere else, I added in an -L/usr/lib option:

gcc -static test.c -o test.exe -L/usr/lib -lncurses

This created a larger file, 1103kb.
I can understand why that should be bigger. What I don't understand is why removing just the -static option:

gcc test.c -o test.exe -L/usr/lib -lncurses

... produces a file 184kb in size, even though it isn't statically linked to the ncurses library.

What is the -L/usr/lib option doing here? According to the online docs, it adds the directory "to the list of directories to be searched for -l". The /usr/lib directory has a libncurses.a file (amongst others) while the /usr/include contains a an ncurses.h symlinked to curses.h and an ncurses_dll.h. Is perhaps the libncurses.a file not a dynamic library? What is ncurses.h then?

Community
  • 1
  • 1
raehik
  • 124
  • 7

1 Answers1

3

This is most likely the way you want to build it:

gcc test.c -o test -lncurses

This says: "compile test.c, name the output file test and dynamically link the library (or shared object) called libncurses.so (the lib prefix is automatically added).

Note that the .exe suffix is not used on Linux, just Windows (your question is tagged with linux so I assume that is the platform you are using here).

If you statically link like this:

gcc -static test.c -o test.exe -lncurses

then the linker will look for a static version of the library (ie. libncurses.a). You get a whole bunch of linker errors, as it cannot resolve the references to the library functions, which suggests you don't have a static version of the library. Unless you have a really good reason to link statically, the norm is to use dynamic linking.

The next command just specifies some additional (but redundant) library paths:

gcc -static test.c -o test.exe -L/usr/lib -lncurses

This is simply telling the linker to search in /usr/lib when looking for libraries. However, /usr/lib is already in the default path, so this shouldn't ordinarily make a difference. If you run gcc -dumpspecs it will print all the paths and definitions that have been 'baked in' to the tool, and thus what the default search paths are for include files, libraries and so on.

The /usr/lib directory has a libncurses.a file (amongst others) while the /usr/include contains a an ncurses.h symlinked to curses.h and an ncurses_dll.h. Is perhaps the libncurses.a file not a dynamic library? What is ncurses.h then?

The symlinks are present for backwards compatibility. The ncurses library (for "new curses") supersedes the original curses, but provides curses.h so old code can still build and run. The ncurses_dll.h header provides some definitions only used for compiling on cygwin or MSVC under Windows, so you can ignore it. The ncurses.h is the real primary header for the library, and should be used for new code.

gavinb
  • 19,278
  • 3
  • 45
  • 60
  • Ah, I was getting confused between libraries and headers. However, I can't find a `libncurses.so` file on my system, and the fact remains that using `-L/usr/lib` does change the file size (implying that there is static linking going on) and I can't compile statically unless I use that option. Do you any idea why I can dynamically linking without a .so file? And though `/usr/lib` is in my gcc path, things change when I include it...? – raehik Oct 28 '13 at 23:03
  • The `-L/usr/lib` doesn't make any difference. You must be running the 64-bit flavour of Linux, so the binaries will be in `/usr/lib/x86_64-linux-gnu` instead. I tried to replicate your commands to find a file size difference, and got 8697 bytes with both commands. Can you try again to be sure? Also, you can use the `ldd` command on your binary to dump which shared libraries it is using. – gavinb Oct 28 '13 at 23:49
  • Huh - `libncurses.so` does exist, in `/usr/lib/x86_64-linux-gnu`. `ldd` tells me that the first file is linked to 64-bit libraries, as you said. `ldd` shows me less information on the `-L/usr/lib` command, none to do with `ncurses`. I can only statically link with the `-L/usr/lib` option. The `/usr/lib/libncurses*` files are all new, because I compiled and installed them yesterday (misunderstood where the libraries were actually stored). That might be part of my problem. In any case, this has solved a few of my problems, because I understand a lot better what is going on. Thank you! – raehik Oct 29 '13 at 09:40
  • You should have mentioned you compiled your own library. In that case, you shouldn't have installed it into `/usr/lib`, but rather into `/usr/local/lib` (or maybe `/opt/lib`). The system manages everything except `local` under `/usr`, and things can get confused if you mix user-built stuff with system-managed stuff. As a general rule, most distros will package the runtime libraries with a name like `libncurses5` and the developer components (headers, libraries, man pages etc, only required for compilation) as `libncurses5-dev`. Always use system packages when possible. – gavinb Oct 29 '13 at 12:14