7

I have copied cs50.c and cs50.h from the appliance. Now, what's the command that should be run to compile this library using gcc or clang on any other Linux distro (e.g., Ubuntu 14.04 in my case)?

1

2 Answers 2

10

Compiling a library is not as easy as compiling an executable. Let's analyze the steps together.

We need to compile our library source code into position-independent code (PIC):

gcc -c -Wall -Werror -fpic cs50.c

Now we need to actually turn this object file into a shared library. We’ll call it cs50.so:

gcc -shared -o libcs50.so cs50.o

As you can see, that was actually pretty easy. We have a shared library. Let’s compile our main.c and link it with libcs50. We’ll call our final program “test.” Note that the -lcs50 option is not looking for cs50.o, but libcs50.so. GCC assumes that all libraries start with ‘lib’ and end with .so or .a (.so is for shared object or shared libraries, and .a is for archive, or statically linked libraries).

 gcc -Wall -o test main.c -lcs50
/usr/bin/ld: cannot find -lcs50
collect2: ld returned 1 exit status

The linker doesn’t know where to find libcs50. GCC has a list of places it looks by default, but our directory is not in that list. We need to tell GCC where to find libcs50.so. We will do that with the -L option. In this example, we will use the current directory, /home/username/cs50:

gcc -L/home/username/cs50 -Wall -o test main.c -lcs50

Good, no errors. Now let’s run our program:

./test
./test: error while loading shared libraries: libcs50.so: cannot open shared object file: No such file or directory

The loader can’t find the shared library. We didn’t install it in a standard location, so we need to give the loader a little help. We have a couple of options: we can use the environment variable LD_LIBRARY_PATH for this, or rpath. Let’s take a look first at LD_LIBRARY_PATH:

echo $LD_LIBRARY_PATH

There’s nothing in there. Let’s fix that by prepending our working directory to the existing LD_LIBRARY_PATH:

$ LD_LIBRARY_PATH=/home/username/cs50:$LD_LIBRARY_PATH
$ ./test
./test: error while loading shared libraries: libcs50.so: cannot open shared object file: No such file or directory

What happened? Our directory is in LD_LIBRARY_PATH, but we didn’t export it. In Linux, if you don’t export the changes to an environment variable, they won’t be inherited by the child processes. The loader and our test program didn’t inherit the changes we made. Thankfully, the fix is easy:

$ export LD_LIBRARY_PATH=/home/username/cs50:$LD_LIBRARY_PATH
$ ./test
This is a shared library test...
Hello, I'm a shared library

LD_LIBRARY_PATH is great for quick tests and for systems on which you don’t have admin privileges. As a downside, however, exporting the LD_LIBRARY_PATH variable means it may cause problems with other programs you run that also rely on LD_LIBRARY_PATH if you don’t reset it to its previous state when you’re done.

Now let’s try rpath (first we’ll clear LD_LIBRARY_PATH to ensure it’s rpath that’s finding our library). Rpath, or the run path, is a way of embedding the location of shared libraries in the executable itself, instead of relying on default locations or environment variables. We do this during the linking stage. Notice the lengthy “-Wl,-rpath=/home/username/cs50” option. The -Wl portion sends comma-separated options to the linker, so we tell it to send the -rpath option to the linker with our working directory.

$ unset LD_LIBRARY_PATH
$ gcc -L/home/username/cs50 -Wl,-rpath=/home/username/cs50 -Wall -o test main.c -lfoo
$ ./test
This is a shared library test...
Hello, I'm a shared library

Excellent, it worked. The rpath method is great because each program gets to list its shared library locations independently, so there are no issues with different programs looking in the wrong paths like there were for LD_LIBRARY_PATH.

What if we want to install our library so everybody on the system can use it? For that, you will need admin privileges. You will need this for two reasons: first, to put the library in a standard location, probably /usr/lib or /usr/local/lib, which normal users don’t have write access to. Second, you will need to modify the ld.so config file and cache. As root, do the following:

$ cp /home/username/foo/libfoo.so /usr/lib
$ chmod 0755 /usr/lib/libfoo.so

Now the file is in a standard location, with correct permissions, readable by everybody. We need to tell the loader it’s available for use, so let’s update the cache:

$ ldconfig

That should create a link to our shared library and update the cache so it’s available for immediate use. Let’s double check:

$ ldconfig -p | grep cs50
libcs50.so (libc6) => /usr/lib/libcs50.so

Now our library is installed. Before we test it, we have to clean up a few things:

Clear our LD_LIBRARY_PATH once more, just in case:

$ unset LD_LIBRARY_PATH

Re-link our executable. Notice we don’t need the -L option since our library is stored in a default location and we aren’t using the rpath option:

$ gcc -Wall -o test main.c -lcs50

Let’s make sure we’re using the /usr/lib instance of our library using ldd:

$ ldd test | grep foo
libfoo.so => /usr/lib/libfoo.so (0x00a42000)

Good, now let’s run it:

$ ./test
This is a shared library test...
Hello, I'm a shared library

Perfect!

Luigi

2
  • Great for shared, but if not using the CS50 appliance, then hand crafting makefiles or clang/gcc command line arguments are being done. Should we also show how to just use the .h/.c files directly from the compiler command? Then we have covered all scenarios (omiting a .a archive ofcourse)
    – Wayne
    Commented Jun 12, 2014 at 3:26
  • Well, the question waas about compiling the CS50 library on a different distro. A Makefile should be standard in its syntax, and the commands of gcc and clang are similar: you can use .h/.c files following the specifications once you installed the library.
    – Cygni_61
    Commented Jun 13, 2014 at 9:53
8

Update: please follow the instructions per https://github.com/cs50/libcs50#installation instead!


A shorter answer could be this.

Debian, Ubuntu

First become root, as with:

sudo su -

Then install the CS50 Library as follows:

apt-get install gcc
wget http://mirror.cs50.net/library50/c/library50-c-5.zip
unzip library50-c-5.zip
rm -f library50-c-5.zip
cd library50-c-5
gcc -c -ggdb -std=c99 cs50.c -o cs50.o
ar rcs libcs50.a cs50.o
chmod 0644 cs50.h libcs50.a
mkdir -p /usr/local/include
chmod 0755 /usr/local/include
mv -f cs50.h /usr/local/include
mkdir -p /usr/local/lib
chmod 0755 /usr/local/lib
mv -f libcs50.a /usr/local/lib
cd ..
rm -rf library50-c-5

2

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .