可可西

makefile示例

1. 生成.so动态库

示例一

SoVer = 10010110
CfgVer = 10010110

BinName = fnights.so
GameName = "\"fnights\""
GameID = 100

BIN = (BinName).(SoVer)

all: clean compilerelease

clean:
    rm -f $(BIN) *.o

compilerelease:
    g++ -fPIC -O -shared -DNDEBUG -Wall -g -D_GAME_ID=(GameID)DGAMENAME=(GameName) -D_CFG_VERSION=(CfgVer)o(BIN) ./*.cpp

 注-fPICPosition 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/PUBLICINC={HOME}/project/public/include/

INCLUDE = -I (PUBLICINC)I(SRCINCLUDE) -I (LIBSRCINCLUDE)I(COMMONDEF)

ALLLIBSPATH = -L(SRCPATH)/Include/LibStatic/L/usr/local/libL{HOME}/project/public/lib -L$(SRCPATH)/Include/Lib/
SLIBS= -lbase
DLIBS= -lGameServer

C_ARGS = -g -Wall -rdynamic -D_DEBUG_ -D_POSIX_MT_ -D_FILE_OFFSET_BITS=64 -fstack-protector-all (INCLUDE)fPICO2VERSIONSTR=+++ThisisaDEBUGLib!!+++BINARY=libGame.soSUBDIR=MsgSUBSRC=(shell find (SUBDIR)name.cpp)SUBOBJ=(SUBSRC:%.cpp=%.o)

SO_OBJS = (patsubst(wildcard *.cpp)) (patsubst(wildcard *.c))
SOOBJS = (SOOBJS)(SUBOBJ)

%.o : %.cpp
    (CPP)(C_ARGS) -c <o@
%.o : %.c
    (CPP)(C_ARGS) -c <o@

all : clean $(BINARY)

(BINARY):(SOOBJS)
    rm -f @(CPP) (CARGS)o@ (SOOBJS)(ALLLIBSPATH) -Wl,-Bstatic (SLIBS)Wl,Bdynamic(DLIBS) -fPIC -shared
    @echo
    @echo $(VERSION_STR)
    @echo

clean:
    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=gccCPP=g++CFLAGS+=gDSOLIBVERSION=(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 -Iutil

LIB_SRC = util/md5.cpp \
queue.cpp

LIB_OBJ = md5.o \
queue.o

LIB = libmd5.a
EXE_SRC = impl.cpp
EXE_OBJ = impl.o
EXE = clt

all: (LIB)(EXE)


(LIB):(LIB_OBJ)
    @rm -f (LIB)arq(LIB) $(LIB_OBJ)

(LIBOBJ):(LIB_SRC)
    (CC)c(LIB_SRC) (MOD)(INC)

(EXE):(EXE_OBJ)
    (CXX)o(EXE) (EXEOBJ)(LIB)

(EXEOBJ):(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

posted on   可可西  阅读(714)  评论(0编辑  收藏  举报

编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)

导航

统计信息

点击右上角即可分享
微信分享提示