makefile示例
1. 生成.so动态库
示例一:
SoVer = 10010110
CfgVer = 10010110BinName = fnights.so
GameName = "\"fnights\""
GameID = 100BIN = $(BinName).$(SoVer)
all: clean compilerelease
clean:
rm -f $(BIN) *.ocompilerelease:
g++ -fPIC -O -shared -DNDEBUG -Wall -g -D_GAME_ID=$(GameID) -D_GAME_NAME=$(GameName) -D_CFG_VERSION=$(CfgVer) -o $(BIN) ./*.cpp
注:-fPIC:Position Independent Code,编译为地址无关的代码,so带上该选项以达到代码段共享的目的;-shared:编译为动态连接库
-D:定义预编译值;-O:表示启用优化;-Wall:打印出gcc提供的警告信息;-o:指定输出的目标文件名
关于PIC的说明:
可执行文件在链接时就知道每一行代码、每一个变量会被放到线性地址空间的什么位置,因此这些地址可以都作为常数写到代码里面。对动态库,这就不行了,这要等到加载时才知道。无非下面两种方法:
(1) 可重定位代码(relocatablecode):Windows DLL 以及不使用 -fPIC 的 Linux SO。
生成动态库时假定它被加载在地址 0 处。加载时它会被加载到一个地址(base),这时要进行一次重定位(relocation),把代码、数据段中所有的地址加上这个 base 的值。这样代码运行时就能使用正确的地址了。
(2) 位置无关代码(positionindependent code):使用 -fPIC 的 Linux SO。
这样的代码本身就能被放到线性地址空间的任意位置,无需修改就能正确执行。通常的方法是获取指令指针(如 IA32 的 EIP 寄存器)的值,加上一个偏移得到全局变量/函数的地址。
PIC vs. relocatable:
(1) PIC 的缺点主要就是代码有可能长一些。例如 IA32,由于不能直接使用[EIP+constant] 这样的寻址方式,甚至不能直接将 EIP 的值交给其他寄存器,要用到GOT(global offsettable)来定位全局变量和函数。这样导致代码的效率略低。
(2) PIC 的加载速度稍快,因为不需要做重定位。
(3) 多个进程引用同一个 PIC 动态库时,可以共用内存。这一个库在不同进程中的虚拟地址不同,但操作系统显然会把它们映射到同一块物理内存上。对于可重定位代码,则必须为每个库都在物理内存中复制一份副本,因为需要修改其中的地址。当然,主流现代操作系统都启用了分页内存机制,这使得重定位时可以使用 COW(copy on write)来节省内存(32 位 Windows 就是这样做的);然而,页面的粒度还是比较大的(例如 IA32 上是 4KiB),至少对于代码段来说能节省的相当有限。
注1:通常的建议是始终加上-fPIC 生成位置无关代码;
注2:AMD64 下,必须使用位置无关代码,否则连接失败:relocation R_X86_64_32S against `a local symbol' can not be used when making ashared object; recompile with -fPIC
注3:IA32下,能链接成功,但有警告:warning: creating a DT_TEXTREL in object. 但.so 文件可以完全正常工作。
更多请参考:Linux下生成动态链接库是否必须使用 -fPIC 的问题 Linux Debugging(七): 使用反汇编理解动态库函数调用方式GOT/PLT
示例二:
CPP = g++
SRCPATH = $(CURDIR)SRCINCLUDE = $(SRCPATH)/Include/ServerFrame/
LIBSRCINCLUDE = $(SRCPATH)/Include/BaseLib/
COMMONDEF = $(SRCPATH)/Include/CommonDef/
PUBLIC_INC = ${HOME}/project/public/include/INCLUDE = -I $(PUBLIC_INC) -I $(SRCINCLUDE) -I $(LIBSRCINCLUDE) -I $(COMMONDEF)
ALLLIBSPATH = -L$(SRCPATH)/Include/LibStatic/ -L/usr/local/lib -L${HOME}/project/public/lib -L$(SRCPATH)/Include/Lib/
SLIBS= -lbase
DLIBS= -lGameServerC_ARGS = -g -Wall -rdynamic -D_DEBUG_ -D_POSIX_MT_ -D_FILE_OFFSET_BITS=64 -fstack-protector-all $(INCLUDE) -fPIC -O2
VERSION_STR = +++ This is a DEBUG Lib!! +++
BINARY = libGame.so
SUBDIR = Msg
SUBSRC=$(shell find $(SUBDIR) -name '*.cpp')
SUBOBJ=$(SUBSRC:%.cpp=%.o)
SO_OBJS = $(patsubst %.cpp,%.o,$(wildcard *.cpp)) $(patsubst %.c,%.o,$(wildcard *.c))
SOOBJS = $(SO_OBJS) $(SUBOBJ)%.o : %.cpp
$(CPP) $(C_ARGS) -c $< -o $@
%.o : %.c
$(CPP) $(C_ARGS) -c $< -o $@
all : clean $(BINARY)$(BINARY) : $(SOOBJS)
rm -f $@
$(CPP) $(C_ARGS) -o $@ $(SOOBJS) $(ALLLIBSPATH) -Wl,-Bstatic $(SLIBS) -Wl,-Bdynamic $(DLIBS) -fPIC -shared
@echo
@echo $(VERSION_STR)
@echoclean:
rm -f $(SOOBJS)
注:-I:指定文件查找路径;-L:链接外部静态库与动态库的查找路径;
-l:指定动态库与静态库的名称(最后库的文件名为:libbase.so、libGameServer.so或libbase.a、libGameServer.a),同时存在同名的静态库与动态库时,会优先匹配动态库;
-rdynamic:指示连接器把所有符号(而不仅仅只是程序已使用到的外部符号)都添加到动态符号表(即.dynsym表)里,以便dlopen或backtrace等函数使用;
-fstack-protector:启用堆栈保护,不过只为局部变量中含有char数组的函数插入保护代码;
-Wl:告诉编译器将后面的参数传递给链接器;
-Wl,-Bstatic:告诉链接器对接下来的-l选项使用静态链接;
-Wl,-Bdynamic:告诉链接器对接下来的-l选项使用动态链接;
+++++++++++++++++++++++++++++++++++++++
动态库链接时搜索路径顺序如下:
(1)-L参数指定的路径
(2)环境变量LD_LIBRARY_PATH指定的路径
(3)配置文件/etc/ld.so.conf中指定的路径
(4)/lib; /usr/lib;
+++++++++++++++++++++++++++++++++++++++
2. 生成.a静态库
LibVer = 10010100
CFLAGS = $(CFLAG)
CC = gcc
CPP = g++
CFLAGS += -g -D_SOLIB_VERSION=$(LibVer) -DGAME_SO -I./ -I../Common -I../Externals/SDK/INCLUDE/COMMON -I../Externals/SDK/INCLUDE/SERVER -I./include -I./SoLogicCtrl
ADDLIB = ../Externals/SDK/solib/libSDK.a
LIB = libGame.a
all:$(LIB)%.o : %.cpp
$(CPP) $(CFLAGS) -c $< -o $@%.o : %.c
$(CC) $(CFLAGS) -c $< -o $@ADDOBJS = BillReporter.o \
CreateLogic.o \
SoGameLogic.o \
./include/SvrCommonDef.o \
./SoLogicCtrl/SoBaseLogicCtrl.o \
../common/GameCommonDef.o$(LIB): $(ADDOBJS)
ar -rf $@ $(ADDOBJS)install:
cp $(BIN) /home/game/dll/$(BIN)
chmod g+w /home/game/dll/$(BIN)clean:
rm -f ./*.o
rm -f ./SoLogicCtrl/*.o
rm -f ../Common/*.o
rm -f ./include/*.o
rm -f $(LIB)
+++++++++++++++++++++++++++++++++++++++
静态库链接时搜索路径顺序:
(1)-L参数指定的路径
(2)环境变量LIBRARY_PATH指定的路径
(3)/lib; /usr/lib; /usr/loacl/lib;
+++++++++++++++++++++++++++++++++++++++
3. 生成可执行程序
CC=gcc
CXX=g++MOD = -g -D_DEBUG
INC = -Iinclude -IutilLIB_SRC = util/md5.cpp \
queue.cppLIB_OBJ = md5.o \
queue.oLIB = libmd5.a
EXE_SRC = impl.cpp
EXE_OBJ = impl.o
EXE = cltall: $(LIB) $(EXE)
$(LIB): $(LIB_OBJ)
@rm -f $(LIB)
ar q $(LIB) $(LIB_OBJ)$(LIB_OBJ): $(LIB_SRC)
$(CC) -c $(LIB_SRC) $(MOD) $(INC)$(EXE): $(EXE_OBJ)
$(CXX) -o $(EXE) $(EXE_OBJ) $(LIB)$(EXE_OBJ): $(EXE_SRC)
$(CC) -c $(EXE_SRC) $(MOD) $(INC).PHONY: clean
clean:
-rm -f $(LIB) $(LIB_OBJ) $(EXE) $(EXE_OBJ)
最后会输出名为clt的可执行程序;clt依赖libmd5.a的静态库,libmd5.a有修改时,会先编译libmd5.a