I feel like I have to re-learn dynamic linking in C about once a year, so I am documenting it here. The comments in the Makefile describe the process.

Makefile:

# these are variables
CC=gcc
CFLAGS=-Wall

# label: dependency1 dependency2 ... dependencyN
all: libhello main

libhello:
# -c means compile, but don't link. -fPIC means output position independent
# code, which is required for shared libraries.
	$(CC) $(CFLAGS) -c -fPIC hello.c
# -shared means create a shared object. -Wl,means pass options to the linker.
# In this case, "-soname libhello.so.1", which sets the internal name of the
# shared object.
	$(CC) $(CFLAGS) -shared -Wl,-soname,libhello.so.1 -o libhello.so.1.0 hello.o
	cp libhello.so.1.0 libhello.so.1
	cp libhello.so.1.0 libhello.so

main:
# -L. means look in the current directory for library files. -lhello means link
# with libhello.so.  -o means output the executable to main.
	$(CC) $(CFLAGS) -L. main.c -lhello -o main

install:
# copy the shared object to /opt/lib.  /opt/lib must be included in
# LD_LIBRARY_PATH for the executable to find the shared library at runtime.
	cp libhello.so.1.0 /opt/lib/
# The symbolic links are used for versioning purposes.
	ln -sf /opt/lib/libhello.so.1.0 /opt/lib/libhello.so.1
	ln -sf /opt/lib/libhello.so.1.0 /opt/lib/libhello.so

uninstall:
	rm /opt/lib/libhello*

clean:
	rm -rf main *.o *.so*

hello.h:

void hello(char* name);

hello.c:

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

void hello(char* name)
{
   printf("hello %s\n", name);
}

main.c:

#include "hello.h"

int main(int argc, char** argv)
{
   hello("kris");
   return 0;
}