cpp(1) takes a .c and some .h files, preprocess the source and generates an .i file.
as(1) then takes the .s file and generates a .o file (object file).
ld(1) then takes the .o file(s) and links it/them with any libraries, resolves the symbols, and generates an executable or .a library.
a.out is the default name given to a program if none was specified with the -o switch. The reason for this is that it used to be the assembler output (before seperate linking was used), and the assembler was called a, hence a's .out file.
Alternatively, gcc foo.c baz.c -o baz will sort the entire thing out for you. :)
Instead of making a BinaryExecutable, you can make a SharedLibrary by compiling with the flags -shared and -fPIC. The latter is optional; it means to create PositionIndependentCode. If you don't use it then when the library is loaded into memory ld.so(8) will relocate the symbols for you which will write to the memory used by the library, and thusly will cause that library not to be shared between processes due to CopyOnWrite. I don't know why you wouldn't want to use PositionIndependentCode if your platform supports it, so use it. :)
gcc foo.c -shared -fPIC -c -o foo.o
You can open dynamically loaded modules using dlopen(3). You can probably link against these, although I've never bothered figuring out how.
gcc foo.c -c -o foo.o ar rcs libs/libfoo.a foo.o ranlib libs/libfoo.a gcc baz.c -Llibs/ -lfoo -o baz
lib/main.php:944: Notice: PageInfo: Cannot find action page