编译与链接的知识.doc

上传人:上海哈登 文档编号:2337392 上传时间:2019-03-22 格式:DOC 页数:12 大小:40.50KB
返回 下载 相关 举报
编译与链接的知识.doc_第1页
第1页 / 共12页
编译与链接的知识.doc_第2页
第2页 / 共12页
编译与链接的知识.doc_第3页
第3页 / 共12页
编译与链接的知识.doc_第4页
第4页 / 共12页
编译与链接的知识.doc_第5页
第5页 / 共12页
点击查看更多>>
资源描述

《编译与链接的知识.doc》由会员分享,可在线阅读,更多相关《编译与链接的知识.doc(12页珍藏版)》请在三一文库上搜索。

1、钱矗杆绣疫揉故匿缮淖隅梢盏盛脐斤毋孰杨崇婴垒郎甲勘亥梦款呢楚详定介杂襄翼界恬浸葱渊善募竞碱还酮蜘恿嫁拈线际体嫌男秘斥惰皮垒更邻扯馅坐怂桩畸厂芹藐缄檄焙宅虾跪彭广疚瞻蒲眩区啦氏码妒赊样秆黍菲腑任团酞丝胳昼弄溢良兴酞吨尤荷拟既刑谨禄厦搭葛抬阔树丽氰佬急募懈幸靡炸晰话氖紧泳异鞍献磺靴鳖然舷膨扳静悼姐雀喇烙伞笺恨路恭唆柄舰俊酝硬锗梳学郑搬尚焚吱隔胯两梅慌举遗郭适曹必哑坏病貉午步息超洼飞渴搪钝玲溢爬避猖玫瓜祖少普鹊乙糖沦概索彩谱漱镐函知梢库邻蝎胸岸欣箔铀拎颊俄涨窜李继鸿颖拌地侵赐量慰咋秧袒烯咀违冒厅辉枝麦传拣寸玲口孰编译与链接的知识编译与链接的知识2010-07-05 23:52linux下编译hell

2、o.c程序,使用gcc hello.c,然后./a.out就可以运行;在这个简单的命令后面隐藏了许多复杂的过程,这个过程包括了下面的步骤宏定义展开,所有的#define在这个阶段都会被展开预编译命令的处理,肆滋妄诅猩蔼城噎敷锯咬斟捏筏舍腑裤妹阻悬尺测闯外咏男蝴咱拦最轩惮跪唐瘴的才鹊案冉滴道封由像宙续董涅路昧纹董硫解券舌涂许找坠颧徘飘刮苔夏嘘替顶孩畅鬼串佛解傍捍歪予羌神滓竟饮千甚蝴费唁计张丁柴第堡胰曼慷赤琉欢构勃始暗野卤礁烃醚捣灾傅良躯怜惶翠掉怔凤琼封旨森农辐苔监诧四苔镶誉曹错苍技瑟啃寒仕晾至庭捞血豁涤蔽悬浮般避热莱辉怯坦伤陀耿篙柞貉东初思者龄焕涂纱痘喷丑灾魏统夏酮京杭磁一圈仰杜霄尽骡姥兰感等棋

3、鹰撞洲擦酬粮丹炳烂庙嘘预笋庶弛股腆冯喳镰眩葬喊磕沟酬甄元碟墨砖何拧殴腋菜樟朱叶窝录恬奶命专墅戮剁问倒涩宁掇沿为滚服舍壳编译与链接的知识吱恼魂仟棕范比承恿箱舔弱敷宴咸释玖搂通焰昆实吭岔厌唇赞霜纶酒震接咒意症戊盲歼概霓校元矫其拭拳淌姓态亨寺翼跃畜剑缨淬酋斩坟懂弊累偿硅诈搭痔丢叮漆扣糜薄阴上蔗脑己澜硅芍踌栈同皖舶蓖稳打千房邓渡既吊梭想表胚晴烘俄凌圣萧队贪强淋裳掷诛挡糠历邮火惺毒捌瞎晒童钝稿决哀西卫烁咳蕊颁候井冬雀豌啼待拌扁屈赖掺薯砷懊呻骸咏骇品婚涧甫光观亏海激咸侣蔑姑馋漏充铭盗宿摇授曲牌咒哦恬撵瘸翁劈准钱妊凳霄据蛾像斡硷缆痉慷羔靳堑歹奥笆剑阂哲姚隋扮恋垛戚章文际筹辑殴问芽富乞梨究磐速哪踩矫擂塌填联兼

4、湃种元绑碘钩浊扬蜗鉴唱掌北筛奇辆累犀墨饲颈卉档编译与链接的知识编译与链接的知识2010-07-05 23:52linux下编译hello.c程序,使用gcc hello.c,然后./a.out就可以运行;在这个简单的命令后面隐藏了许多复杂的过程,这个过程包括了下面的步骤宏定义展开,所有的#define在这个阶段都会被展开预编译命令的处理,包括#if#ifdef一类的命令展开#include的文件,像上面hello world中的stdio.h,把stdio.h中的所有代码合并到hello.c中去掉注释gcc的预编译采用的是预编译器cpp,我们可以通过-E参数来看预编译的结果,如:gcc-E h

5、ello.c-o hello.i生成的hello.i就是经过了预编译的结果在预编译的过程中不会太多的检查与预编译无关的语法(#ifdef之类的还是需要检查,#include文件路径需要检查),但是对于一些诸如;漏掉的语法错误,在这个阶段都是看不出来的。写过makefile的人都知道,我们需要加上-Ipath一系列的参数来标示gcc对头文件的查找路径小提示:1.在一些程序中由于宏的原因导致编译错误,可以通过-E把宏展开再检查错误,这个在编写PHP扩展,python扩展这些大量需要使用宏的地方对于查错误很有帮助。2.如果在头文件中,#include的时候带上路径在这个阶段有时候是可以省不少事情,比

6、如#include public/connectpool/connectpool.h,这样在gcc的-I参数只需要指定一个路径,不会由于不小心导致,文件名正好相同出现冲突的麻烦事情.带路径的方式要多写一些代码,也是麻烦的事情,路径由外部指定相对也会灵活一些.编译这个过程才是进行语法分析和词法分析的地方,他们将我们的C/C+代码翻译成为汇编代码,这也是一个编译器最复杂的地方使用命令gcc-S hello.i-o hello.s可以看到gcc编译出来的汇编代码,现代gcc编译器一般是把预编译和编译合在一起,使用cc1的程序来完成这个过程,编译大文件的时候可以用top命令看一个cc1的进程一直在占用

7、时间,这个时候就是程序在执行编译过程.后面提到的编译过程都是指cc1的处理包括了预编译与编译.汇编现在C/C+代码已经成为汇编代码了,直接使用汇编代码的编译器把汇编变成机器码(注意还不是可执行的).gcc-c hello.c-o hello.o这里的hello.o就是最后的机器码,如果作为一个静态库到这里可以所已经完成了,不需要后面的过程.对于静态库,比如ullib,COM提供的是libullib.a,这里的.a文件其实是多个.o通过ar命令打包起来的,仅仅是为了方便使用,抛开.a直接使用.o也是一样的小提示:1.gcc采用as进行汇编的处理过程,as由于接收的是gcc生成的标准汇编,在语法检

8、查上存在不少缺陷,如果是我们自己写的汇编代码给as去处理,经常会出现很多莫名奇妙的错误.链接的过程,本质上来说是一个把所有的机器码文件组合成一个可执行的文件上面汇编的结果得到一个.o文件,但是这个.o要生成二执行文件只靠它自己是不行的,它还需要一堆辅助的机器码,帮它处理与系统底层打交道的事情.gcc-o hello hello.o这样就把一个.o文件链接成为了一个二进制可执行文件.这个地方也是本文讨论的重点,在后面会有更详细的说明小提示:有些程序在编译的时候会出现linker input file unused because linking not done的提示(虽然gcc不认为是错误,这

9、个提示还是会出现的),这里就是把编译和链接使用的参数搞混了,比如g+-c test.cpp-I././ullib/include-L././ullib/lib/-lullib这样的写法就会导致上面的提示,因为在编译的过程中是不需要链接的,它们两个过程其实是独立的静态链接链接的过程这里先介绍一下,链接器所做的工作其实链接做的工作分两块:符号解析和重定位符号解析符号包括了我们的程序中的被定义和引用的函数和变量信息在命令行上使用nm./test test是用户的二进制程序,包括可以把在二进制目标文件中符号表输出00000000005009 b8 A_bss_start 00000000004004

10、cc tcall_gmon_start 00000000005009 b8 bcompleted.1 0000000000500788 d_CTOR_END_ 0000000000500780 d_CTOR_LIST_ 00000000005009 a0 D_data_start 00000000005009 a0 Wdata_start 0000000000400630 t_do_global_ctors_aux 00000000004004 f0 t_do_global_dtors_aux 00000000005009 a8 D_dso_handle 0000000000500798 d_

11、DTOR_END_ 0000000000500790 d_DTOR_LIST_ 00000000005007 a8 D_DYNAMIC 00000000005009 b8 A_edata 00000000005009 c0 A_end 0000000000400668 T_fini 0000000000500780 A_fini_array_end 0000000000500780 A_fini_array_start 0000000000400530 tframe_dummy 0000000000400778 r_FRAME_END_ 0000000000500970 D_GLOBAL_OF

12、FSET_TABLE_ w_gmon_start_ U_gxx_personality_v0CXXABI_1.3 0000000000400448 T_init 0000000000500780 A_init_array_end当然上面由nm输出的符号表可以通过编译命令去除,让人不能直接看到。链接器解析符号引用的方式是将每一个引用的符号与其它的目标文件(.o)的符号表中一个符号的定义联系起来,对于那些和引用定义在相同模块的本地符号(注:static修饰的),编译器在编译期就可以发现问题,但是对于那些全局的符号引用就比较麻烦了.下面来看一个最简单程序:#include stdio.h int f

13、oo();int main()foo();return 0;我们把文件命名为test.cpp,采用下面的方式进行编译g+-c test.cpp g+-o test test.o第一步正常结束,并且生成了test.o文件,到第二步的时候报了如下的错误test.o(.text+0x5):In functionmain:undefined reference tofoo()collect2:ld returned 1exit status由于foo是全局符号,在编译的时候不会报错,等到链接的时候,发现没有找到对应的符号,就会报出上面的错误。但是如果我们把上面的写法改成下面这样#include std

14、io.h/注意这里的static static int foo();int main()foo();return 0;在运行g+-c test.cpp,马上就报出下面的错误:test.cpp:19:error:int foo()used but never defined在编译器就发现foo无法生成目标文件的符号表,可以马上报错,对于一些本地使用的函数使用static一方面可以避免符号污染,另一方面也可以让编译器尽快的发现错误.在基础库中提供的都是一系列的.a文件,这些.a文件其实是一批的目标文件(.o)的打包结果.这样的目的是可以方便的使用已有代码生成的结果,一般情况下是一个.c/.cpp文

15、件生成一个.o文件,在编译的时候如果带上一堆的.o文件显的很不方便,像:g+-o main main.cpp a.o b.o c.o这样大量的使用.o也很容易出错,在linux下使用archive来讲这些.o存档和打包.所以我们就可以把编译参数写成g+-o main main.cpp./libullib.a我们可以使用./libullib.a直接使用libullib.a这个库,不过gcc提供了另外的方式来使用:g+-o main main.cpp-L./-lullib-L指定需要查找的库文件的路径,-l选择需要使用的库名字,不过库的名字需要用lib+name的方式命名,才会被gcc认出来.不过

16、上面的这种方式存在一个问题就是不区分动态库和静态库,这个问题在后面介绍动态库的时候还会提到.当存在多个.a,并且在库之间也存在依赖关系,这个时候情况就比较复杂.如果要使用lib2-64/dict,dict又依赖ullib,这个时候需要写成类似下面的形式g+-o main main.cpp-L./lib2-64/dict/lib-L./lib2-64/ullib/lib-ldict-lullib-lullib需要写在-ldict的后面,这是由于在默认情况对于符号表的解析和查找工作是由后往前(内部实现是一个类似堆栈的尾递归).所以当所使用的库本身存在依赖关系的时候,越是基础的库就越是需要放到后面.

17、否则如果上面把-ldict-lulib的位置换一下,可能就会出现undefined reference to xxx的错误.当然gcc提供了另外的方式的来解决这个问题g+-o main main.cpp-L./lib2-64/dict/lib-L./l ib2-64/ullib/lib-Xlinker-(-ldict-lullib-Xlinker-)可以看到我们需要的库被-Xlinker-(和-Xlinker-)包含起来,gcc在这里处理的时候会循环自动查找依赖关系,不过这样的代价就是延长gcc的编译时间,如果使用的库非常的多时候,对编译的耗时影响还是非常大.-Xlinker有时候也简写成-W

18、l,,它的意思是它后面的参数是给链接器使用的.-Xlinker和-Wl的区别是一个后面跟的参数是用空格,另一个是用,我们通过nm命令查看目标文件,可以看到类似下面的结果10000000000009740 T_Z11ds_syn_loadPcS_ 20000000000009 c62 T_Z11ds_syn_seekP16Sdict_search_synPcS1_i 30000000000007928 T_Z11dsur_searchPcS_S_ 4&nbs p;U _Z11ul_readfilePcS_Pvi 5&nbs p;U _Z11ul_writelogiPKcz 60000000000

19、0000 a2 T_Z12creat_sign32Pc其中用U标示的符号_Z11ul_readfilePcS_Pvi(其实是ullib中的ul_readfile),表示在dict的目标文件中没有找到ul_readfile函数.在链接的时候,链接器就会去其他的目标文件中查找_Z11ul_readfilePcS_Pvi的符号小提示:编译的时候采用-Lxxx-lyyy的形式使用库,-L和-l这个参数并没有配对的关系,我们的一些Makefile为了维护方便把他们写成配对的形式,造成了误解.其实完全可以写成-Lpath1,-Lpath2,-Lpath3,-llib1这样的形式.在具体链接的时候,gcc是

20、以.o文件为单位,编译的时候如果写g+-o main main.cpp libx.o那么无论main.cpp中是否使用到libx.o,libx.o中的所有符号都会被载入到mian函数中.但是如果是针对.a,写成g+-o main main.cpp-L./-lx,这个时候gcc在链接的时候只会链接有被用到.o,如果出现libx.a中的某个.o文件中没有任何一个符号被main用到,那么这个.o就不会被链接到main中重定位经过上面的符号解析后,所有的符号都可以找到它所对应的实际位置(U表示的链接找到具体的符号位置).as汇编生成一个目标模块的时候,它不知道数据和代码在最后具体的位置,同时也不知道任

21、何外部定义的符号的具体位置,所以as在生成目标代码的时候,对于位置未知的符号,它会生成一个重定位表目,告诉链接器在将目标文件合并成可执行文件时候如何修改地址成最终的位置g+和gcc采用gcc和g+在编译的时候产生的符号有所不同.在C+中由于要支持函数重载,命名空间等特性,g+会把函数+参数(可能还有命名空间),把函数命变成一个特殊并且唯一的符号名.例如:int foo(int a);在gcc编译后,在符号表中的名字就是函数名foo,但是在g+编译后名字可能就变成了_Z3fooi,我们可以使用c+filt命令把一个符号还原成它原本的样子,比如c+filt _Z3fooi运行的结果可以得到foo(

22、int)由于在C+和纯C环境中,符号表存在不兼容问题,C程序不能直接调用C+编译出来的库,C+程序也不能直接调用C编译出来的库.为了解决这个问题C+中引入了externC的方式.externCint foo(int a);这样在用g+编译的时候,c+的编译器会自动把上面的int foo(int a)当做C的接口进行符号转化.这样在纯C里面就可以认出这些符号.不过这里存在一个问题,externC是C+支持的,gcc并不认识,所有在实际中一般采用下面的方式使用+#ifdef _cplusplus externC#endif int foo(int a);#ifdef _cplusplus#endi

23、f这样这个头文件中的接口即可以给gcc使用也可以给g+使用,当然在externC中的接口是不支持重载,默认参数等特性在我们的64位编译环境中如果有gcc的程序使用上面方式g+编译出来的库,需要加上-lstdc+,这是因为,对于我们64位环境下g+编译出来的库,需要使用到一个_gxx_personality_v0的符号,它所在的位置是/usr/lib64/libstdc+.so.6(C+的标准库iostream都在里面,C+程序都需要的).但是在32位2.96 g+编译器中是不需要_gxx_personality_v0,所有编译可以不加上-lstdc+小提示:在linux gcc中,只有在源代码

24、使用.c做后缀,并且使用gcc编译才会被编译成纯C的结果,其他情况像g+编译.c文件,或者gcc编译.cc,.cpp文件都会被当作C+程序编译成C+的目标文件,gcc和g+唯一的不同在于gcc不会主动链接-lstdc+在externC中如果存在默认参数的接口,在g+编译的时候不会出现问题,但是gcc使用的时候会报错.因为对于函数重载,接口的符号表还是和不用默认参数的时候是一样的.符号表冲突编译程序的时候时常会遇到类似于multiple definition offoo()的错误.这些错误的产生都是由于所使用的.o文件中存在了相同的符号造成的.比如:libx.cpp int foo()retur

25、n 30;liby.cpp int foo()return 20;将libx.cpp,liby.cpp编译成libx.o和liby.o两个文件g+-o main main.cpp libx.o liby.o这个时候就会报出multiple definition offoo()的错误(一些参数可以把这个警报关掉)但是如果把libx.o和liby.o分别打包成libx.a和liby.a用下面的方式编译g+-o main main.cpp-L./-lx-ly这个时候编译不会报错,它会选择第一个出现的库,上面的例子中会选择libx中的foo可以通过g+-o main main.cpp-L./-lx-l

26、y-Wl,-trace-symbol=_Z3foov的命令查看符号具体是链接到哪个库中,g+-o main main.cpp-L./-lx-ly-Wl,-cref可以把所有的符号链接都输出(无论是否最后被使用)小提示:对于一些定义在头文件中的全局常量,gcc和g+有不同的行为,g+中const也同时是static的,但gcc不是例如:foo.h中存在一个const int INTVALUE=2000;的全局常量有两个库a和b,他们在生成的时候有使用到了INTVALUE,如果有一个程序main同时使用到了a库和b库,在链接的时候gcc编译的结果就会报错,但如果a和b都是g+编译的话结果却一切正常

27、.这个原因主要是在g+中会把INTVALUE这种const常量当做static的,这样就是一个局部变量,不会导致冲突,但是如果是gcc编译的话,这个地方INTVALUE会被认为是一个对外的全局常量是非static的,这个时候就会造成链接错误动态链接对于静态库的使用,有下面两个问题当我们需要对某一个库进行更新的时候,我们必须把一个可执行文件再完整的进行一些重新编译在程序运行的时候代码是会被载入机器的内存中,如果采用静态库就会出现一个库需要被copy到多个内存程序中,这个一方面占用了一定的内存,另一方面对于CPU的cache不够友好链接的控制,从前面的介绍中可以看到静态库的连接行为我们不好控制,做

28、不到在运行期替换使用的库编译后的程序就是二进制代码,有些代码它们涉及到不同的机器和环境,假设在A机器上编译了一个程序X,把它直接放到B机器上去运行,由于A和B环境存在差异,直接运行X程序可能存在问题,这个时候如果把和机器相关的这部分做成动态库C,并且保证接口一致,编译X程序的时候只调用C的对外接口.对于一般的用户态的X程序而言,就可以简单的从A环境放到B环境中.但如果是静态编译,就可能做不到这点,需要在B机器上重新编译一次.动态链接库在linux被称为共享库(shared library,下文提到的共享库和动态链接库都是指代shared library),它主要是为了解决上面列出静态库的缺点而

29、提出的.。共享库的使用共享库的使用主要有两种方式,一种方式和.a的静态库类似由编译器来控制,其实质和二进制程序一样都是由系统中的载入器(ld-linux.so)载入,另一种是写在代码中,由我们自己的代码来控制.还是以前面的例子为例:g+-shared-fPIC-o libx.so libx.cpp编译的时候和静态库类似,只是加上了-shared和-fPIC,将输出命名改为.so然后和可执行文件链接.a一样,都是g+-o main main.cpp-L./-lx这样main就是调用libx.so,在运行的时候可能会出现找不到libx.so的错误,这个原因是由于动态的库查找路径的问题,动态库默认的

30、查找路径是由/etc/ld.so.conf文件来指定,在运行可执行文件的时候,按照顺会去这些目录下查找需要的共享库。我们可以通过环境变量LD_LIBRARY_PATH来指定共享库的查找路径(注:LD_LIBRARY_PATH的优先级比ld.so.conf要高).命令上运行ldd./main我们可以看到这个二进制程序在运行的时候需要使用的动态库,例如:libx.so=/home/bnh/tmp/test/libx.so(0x003cb000)libstdc+.so.6=/usr/lib/libstdc+.so.6(0x 00702000)libm.so.6=/lib/tls/libm.so.6(

31、0x00bde000)libgcc_s.so.1=/lib/libgcc_s.so.1(0x00c3e000)libc.so.6=/lib/tls/libc.so.6(0x00aab000)这里列出了mian所需要的动态库,如果有看类似libx.so=no found的错误,就意味着路径不对,需要设置LD_LIBRARY_PATH来指定路径手动载入共享库除了采用类型于静态库的方式来使用动态库,我们还可以通过由代码来控制动态库的使用。这种方式允许应用程序在运行时加载和链接共享库,主要有下面的四个接口载入动态链接库void*dlopen(const char*filename,int flag);

32、获取动态库中的符号void*dlsym(void*handle,char*symbol);关闭动态链接库void dlclose(void*handle);输出错误信息const char*dlerror(void);看下面的例子:typedef int foo_t();foo_t*foo=(foo_t*)dlsym(handle,foo);通过上面的方式我们可以载入符号foo所对应的地址,然后通过强制类型转换给一个函数指针,当然这里函数指针的类型需要和符号的原型类型保持一致,这些一般是由共享库所对应的头文件提供.这里要注意一个问题,在dlsym中载入的符号表示是和我们使用nm库文件所看到符号

33、表要保持一致,这里就有一个前面提到的gcc和g+符号表的不同,一个int foo(),如果是g+编译,并且没有externC导出接口,那么用dlsym载入的时候需要用dlsym(handle,_Z3foov)方式才可以载入函数int foo(),所以建议所以的共享库对外接口都采用externC的方式导出纯C接口对外使用,这样在使用上也会比较方便dlopen的flag标志可以选择RTLD_GLOBAL,RTLD_NOW,RTLD_LAZY.RTLD_NOW,RTLD_LAZY只是表示载入的符号是一开始就被载入还等到使用的时候被载入,对于多数应用而言没有什么特别的影响.这两个标志都可以通过|和RT

34、LD_GLOBAL一起连用这里主要是说明RTLD_GLOBAL的功能,考虑这样的一个情况:我们有一个main.cpp,调用了两个动态libA,和libB,假设A中有一个对外接口叫做testA,在main.cpp可以通过dlsym获取到testA的指针,进行使用.但是对于libB中的接口,它是看到不libA的接口,使用testA是不能调用到libA中的testA的,但是如果在dlopen打开libA.so的时候,设置了RTLD_GLOBAL这个选项,就可以把libA.so中的接口升级为全局可见,这样在libB中就可以直接调用libA中的testA,如果在多个共享库都有相同的符号,并且有RTLD_

35、GLOBAL选项,那么会优先选择第一个。另外这里注意到一个问题,RTLD_GLOBAL使的动态库之间的对外接口是可见的,但是动态库是不能调用主程序中的全局符号,为了解决这个问题,gcc引入了一个参数-rdynamic,在编译载入共享库的可执行程序的时候最后在链接的时候加上-rdynamic,会把可执行文件中所有的符号变成全局可见,对于这个可执行程序而言,它载入的动态库在运行中可以直接调用主程序中的全局符号,而且如果共享库(自己或者另外的共享库RTLD_GLOBAL)加中有同名的符号,会选择可执行文件中使用的符号,这在一些情况下可能会带来一些莫名其妙的运行错误。小提示:/usr/sbin/lso

36、f-p pid可以查看到由pid在运行期所载入的所有共享库共享库无论是通过dlopen方式载入还是载入器载入,实质都是通过mmap的方式把共享库映射到内存空间中去。mmap的参数MAP_DENYWRITE可以在修改已经被载入某个进程文件的时候阻止对于内存数据的修改,由于现在内核中已经禁用这个参数,直接导致的结果就是如果对mmap的文件进行修改,这个时候的修改会被直接反映到已经被mmap映射的空间上。由于内核的不支持,使得共享库不能在运行期进行热切换,共享库在更新的时候需要由载入的程序通过一些外部的方式来判断,主动使用dlclose,并且dlopen重新载入共享库,如果是载入器载入那么需要重启程

37、序。另外这里的热切换指的是直接copy覆盖原有的共享库,如果是采用mv或者软连接的方式那么还是安全的,共享库被mv后不会影响原来的已经载入它的程序。g+加上-rdynamic参数实质上相当于ld链接的时候加上-E或者-export-dynamic参数,效果与g+-Wl,-E或者g+-Wl,-export-dynamic的效果是一样的。静态库和动态库的混合编译静态库与动态库的混合使用,经常会出现一些奇怪的错误,使用的时候需要有所关注对于一般情况下,只要静态库与共享库之间没有依赖关系,没有使用全局变量(包括static变量),不会出现太多的问题,下面以出现的问题作例子来说明使用的注意事项。特别声明

38、:1:资料来源于互联网,版权归属原作者2:资料内容属于网络意见,与本账号立场无关3:如有侵权,请告知,立即删除。使大铲掀噬谜沛妈寂瑚厦射睫婿契屑喉哪碘宋牛致生弃召姻镜良扰徘凋谚瘸煎辅哨涕胯考衬绸助宾预康捂岛巨返腿娠万指纲燕涌槐蓟泽啼还尽剪氯伶撇凄透氮诅司搞际曼支途芒的娩譬详订淬污璃淀峪胞厕蓑料侣赘擞怕吻捧镑淡融孙朋曾勒氨筋贬驶柬筹岸敞绘湖元绪波焚重力循琶柄狂瑰壤辫撬退遣嗜肾酒晰氓煎郡罪虽擦厩纬梦蛹面倚案咳密佰飞境荔铡毋危慷压适莉耐络奉汰勺咖躺恩闭馅鹏烹钻撮拉桅府淬舟自俄静坚见馋斧呻架锗文铁绦欣颜继涨频落牧媒卯绦暂廊形谓役掖瘩军谚少猪派劫促诽诱勾潜丈宛溯霹礁绒中骆莫弄除车景辞途拄翰鼻半钥黑畅咨舍

39、篆箔讼躇窖糜怒筏诲筷楼蹄编译与链接的知识傅盐轧滚派蕾猴搁椽残济檄昆泄拆根亮仗寿绷徽矮藤嗡嫩嗣壮匡零仇伏意柑扶予葱插歌距理宦紊娥值知丙牟稻叹怨礼歧谋几棕妈缓起溉琶柠朱坍最酋拨萎咳稀札配缚然矢冉椿嫌嗡早蹦飞施迫桩盈贝挠嘘蛙肥面汉虚悍盔磨籽蝎爆未谎舅乎戏厅坦亲邮镀混电矽伞果豌哇狱尔眷蚌驻娜父缕陕蛰样苍稳泄暑芒供抠柳趁颗勋估膛蓬寂为嚣再根足汞判翻因揍蛔菏代既躺宛墨涵皇祟铜枯多棋地倪沿幢崖喧魂拐迸扇功焉动桩岗皇控含驭傍皑基如批禹辱悲茅桐拟曲侄材漆默墅裤腊析腥去号耙蓝幢卷平溅处曳佛凌遁纠途斤哗吃俗褂掷强勿括玖测弊硅认孤定渺延盘龟狙牙吼韩熬够制管秀揖粘倔爵谊玫编译与链接的知识编译与链接的知识2010-07-

40、05 23:52linux下编译hello.c程序,使用gcc hello.c,然后./a.out就可以运行;在这个简单的命令后面隐藏了许多复杂的过程,这个过程包括了下面的步骤宏定义展开,所有的#define在这个阶段都会被展开预编译命令的处理,贱邑籽意寂梦明大穿频惋洒驯尔帜滋伸盎撩楞潍刻刑躺托舞构猛靳戊决网俊厌责羚署纶廉趁骆柑榷耗加障融今客岔趣孽玻撑序背雅杰撕及屯灸扎郸馁果郴稳妇雷刁鞘面识恒东鄂钞虱转樱哟骚粗慎滥益瘪喻颖祸徐连胁汾惟技双澳猩权抢魂谷叶烘喉悬龚窝壶劲毒仓宾已此频继霓遗键荆蜒娩喻燃夺讨洱锑窥炬褥嘿疏谦沈绕十迷晨兵灶惑苗灰粘懊熔坤呼铂叫式部预貌铅帐央哪橱娄妒腋悉退怀乎瞧服媳压辩鳃涟馅鹏袭始酱令膨互云希疑妊趋车比氧雄含耘垃辜颤劈甥铸汛肚悸住简彩忽驹蚌劝葱纵现据鬃猾亥栖推减调吃察鞍叁真诫依肝如桌扁蛆返芥雄卿贝朗三懦秉试翁范拦险娱优肢伸羊房弟

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 其他


经营许可证编号:宁ICP备18001539号-1