Making libraries isn't as simple as it seems.  Especially not when you use native C and C++ code to get started.  For what I know there are 2 types of libraries.  The static and the dynamic.  The static library or archive are the most easy to create in assembly language.  The dynamic on the other hand isn't so easy to deal with.  Things to take into account are the Global Offset Table and the technique to get your local data, global data, local subroutines and the global subroutines.  As if not enough we have to deal with subroutine calls to local routines and from other libraries.

This section is to get started with both types of libraries

Archive files - static libraries

An archive or static library is a library with a collection of routines that are collected, usually for one type of target, e.g. strings, integer math, float math, .... It's in fact one file with several object files.  The archive can grow by adding more object files, you can get the object files separately from an archive and/or you can delete existing objectfiles from it.  Data and routines that are available for users are declared global, if not they are private for the archive itself.
You don't have to link them, so the GNU Linker ld is not needed.  For more of the options of archive files just type man ar.  With objdump you can look into the file.

The code from an archive is included in your source code at link time what is visible when using objdump -S filename.  After that is performed you can delete or relocate a archive file.  The benefit is that your code runs faster because there is no need to load a shared library and let the system do some administration.  A disadvantage is that your program grows bigger depending the size and amount of routines you need from an archive.  Many programs with the same code from an archive can benefit from a shared library but in opposite to shared libraries each program has it's own copy and if an archive is updated all programs need to be reassembled.

Shared libraries

As already explained in previous paragraph, dynamic libraries contains code that is called from one or more programs.  The disadvantage is that coding is less obvious and I dare to say more difficult than archive files but not that difficult that you should use higher level languages to make one.  Code from object files, archive files can be included and calls to other libraries are possible.  I'm not trying to explain everything about shared libaries.  For that a more detailed explanation is given on the website Computer sience from bottom up - Chapter 9.

The example given here demonstrates the use of the Global Offset Table (GOT), how to access global variables and routines from other libraries.  What do take in consideration when making data global or not, and methods to access these.

One last thing worth mentioning is the -R . option in the link command.  This option arrange that the libarie (or a symlink to it) will be searched in the same directory as the main program is in.  A nice option when you want to test and debug the application.

Three make files to conclude are given here, one for archive files, one for shared libraries and one to build a program that uses both.  The only thing to do is do some modifications for the names and the paths were things are located.


Makefile for archive files

LIBNAME=staticlib
AS=nasm
LD=ld
AR          = ar
AROPT	    = rcs
O           = .o
ASM         = .asm
INC         = .inc
LST         = .lst
ARCH        = .a
ASFLAGS     = "-felf64"
LDFLAGS     = -s -melf_x86_64
#only for the library name
DEV         = -dev

OBJS = $(NAME)$(O)

$(LIBNAME)$(ARCH): $(LIBNAME)$(O)
	$(AR) $(AROPT) $(LIBNAME)$(ARCH) $(LIBNAME)$(O)

$(LIBNAME)$(O): $(LIBNAME)$(ASM)
	$(AS) $(ASFLAGS) $(LIBNAME)$(ASM) -o $(LIBNAME)$(O)

$(LIBNAME)$(DEV)$(ARCH): $(LIBNAME)$(DEV)$(O)
	$(AR) $(AROPT) $(LIBNAME)$(DEV)$(ARCH) $(LIBNAME)$(DEV)$(O)

$(LIBNAME)$(DEV)$(O): $(LIBNAME)$(ASM)
	$(AS) $(ASFLAGS) $(LIBNAME)$(ASM) -l $(LIBNAME)$(DEV)$(LST) -o $(LIBNAME)$(DEV)$(O)

all:
	$(MAKE) release
	$(MAKE) debug

release:
	$(MAKE) $(LIBNAME)$(ARCH) ASFLAGS="-felf64" LDFLAGS="-melf_x86_64" AROPT="rcs"

debug:
	$(MAKE) $(LIBNAME)$(DEV)$(ARCH) DEV="-dev" ASFLAGS="-felf64 -Fdwarf -g" LDFLAGS="-g -melf_x86_64" AROPT="rcs"

clean:
	rm -f $(LIBNAME)$(ARCH) $(LIBNAME)$(DEV)$(ARCH) $(LIBNAME)$(LST) $(LIBNAME)$(O) $(LIBNAME)$(DEV)$(O) *$(LST)

Makefile for shared libraries

LIBNAME=sharedlib
LIBS=-lc `pkg-config --libs gtk+-3.0`
VERSION=1.0
O = .o
SO = .so
LST = .lst
ASM = .asm
ASFLAGS= -felf64
LDFLAGS=-s -melf_x86_64
DEV=-dev
SONAME=$(LIBNAME)$(SO).1

$(LIBNAME)$(SO) : $(LIBNAME)$(O) 
	ld $(LIBS) --dynamic-linker /lib64/ld-linux-x86-64.so.2 -shared -soname $(SONAME) -o $(LIBNAME)$(SO).$(VERSION) $(LIBNAME)$(O) -R .
	ln -sf $(LIBNAME)$(SO).$(VERSION) $(LIBNAME)$(SO).1

$(LIBNAME)$(O) : $(LIBNAME)$(ASM)
	nasm $(ASFLAGS) -o $(LIBNAME)$(O) $(LIBNAME)$(ASM)
	
$(LIBNAME)$(DEV)$(SO) : $(LIBNAME)$(DEV)$(O) 
	ld $(LIBS) --dynamic-linker /lib64/ld-linux-x86-64.so.2 -shared -soname $(SONAME) -o $(LIBNAME)$(DEV)$(SO).$(VERSION) $(LIBNAME)$(DEV)$(O) -R .
	ln -sf $(LIBNAME)$(DEV)$(SO).$(VERSION) $(LIBNAME)$(SO).1

$(LIBNAME)$(DEV)$(O) : $(LIBNAME)$(ASM)
	nasm $(ASFLAGS) -o $(LIBNAME)$(DEV)$(O) -l $(LIBNAME)$(LST) $(LIBNAME)$(ASM)

all:
	$(MAKE) release
	$(MAKE) debug

release:
	$(MAKE) $(LIBNAME)$(SO) ASFLAGS="-felf64" LDFLAGS="-melf_x86_64"

debug:
	$(MAKE) $(LIBNAME)$(DEV)$(SO) ASFLAGS="-felf64 -Fdwarf -g" LDFLAGS="-g -melf_x86_64"
	
clean:
	rm -f *$(O) $(LIBNAME)$(SO) $(LIBNAME)$(DEV)$(SO) $(LIBNAME)$(SO).$(VERSION) $(LIBNAME)$(DEV)$(SO).$(VERSION) *$(LST) $(SONAME)

Makefile for the source file

NAME=libexample
AS=nasm
LD=ld

O           = .o
ASM         = .asm
INC         = .inc
LST         = .lst

ASFLAGS     = "-felf64"
LDFLAGS     = -s -melf_x86_64
LIST        =
SHAREDLIBS  = ./shared-library/sharedlib.so.1
STATICLIBS  = static-library/staticlib.a
SHAREDLIBSDEV  = ./shared-library/sharedlib.so.1
STATICLIBSDEV  = static-library/staticlib-dev.a

OBJS = $(NAME)$(O)

$(NAME): $(OBJS)
	$(LD) $(SHAREDLIBS) --dynamic-linker /lib64/ld-linux-x86-64.so.2 $(LDFLAGS) -o $(NAME) $(OBJS) $(STATICLIBS) -R .

$(NAME)$(O): $(NAME)$(ASM)
	$(AS) $(ASFLAGS) $(NAME)$(ASM) $(LIST) -o $(NAME)$(O)

all:
	$(MAKE) debug
	$(MAKE) release
	
debug:
	$(MAKE) $(NAME) ASFLAGS="-felf64 -Fdwarf" LDFLAGS="-g -melf_x86_64" LIST="-l $(NAME)$(LST)" SHAREDLIBS=$(SHAREDLIBSDEV) STATICLIBS=$(STATICLIBSDEV)

release:
	$(MAKE) $(NAME) ASFLAGS="-felf64" LDFLAGS="-melf_x86_64"

clean:
	rm -f $(NAME) $(NAME)$(LST) $(NAME)$(O)