• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            tqsheng

            go.....
            隨筆 - 366, 文章 - 18, 評(píng)論 - 101, 引用 - 0
            數(shù)據(jù)加載中……

            dll和so文件區(qū)別與構(gòu)成

            動(dòng)態(tài)鏈接,在可執(zhí)行文件裝載時(shí)或運(yùn)行時(shí),由操作系統(tǒng)的裝載程序加載庫(kù)。大多數(shù)操作系統(tǒng)將解析外部引用(比如庫(kù))作為加載過(guò)程的一部分。在這些系統(tǒng)上,可執(zhí)行文件包含一個(gè)叫做import   directory的表,該表的每一項(xiàng)包含一個(gè)庫(kù)的名字。根據(jù)表中記錄的名字,裝載程序在硬盤(pán)上搜索需要的庫(kù),然后將其加載到內(nèi)存中預(yù)先不確定的位置,之后根據(jù)加載庫(kù)后確定的庫(kù)的地址更新可執(zhí)行程序。可執(zhí)行程序根據(jù)更新后的庫(kù)信息調(diào)用庫(kù)中的函數(shù)或引用庫(kù)中的數(shù)據(jù)。這種類(lèi)型的動(dòng)態(tài)加載成為裝載時(shí)加載   ,被包括Windows和Linux的大多數(shù)系統(tǒng)采用。裝載程序在加載應(yīng)用軟件時(shí)要完成的最復(fù)雜的工作之一就是加載時(shí)鏈接。  
               
              其他操作系統(tǒng)可能在運(yùn)行時(shí)解析引用。在這些系統(tǒng)上,可執(zhí)行程序調(diào)用操作系統(tǒng)API,將庫(kù)的名字,函數(shù)在庫(kù)中的編號(hào)和函數(shù)參數(shù)一同傳遞。操作系統(tǒng)負(fù)責(zé)立即解析然后代表應(yīng)用調(diào)用合適的函數(shù)。這種動(dòng)態(tài)鏈接叫做運(yùn)行時(shí)鏈接   。因?yàn)槊總€(gè)調(diào)用都會(huì)有系統(tǒng)開(kāi)銷(xiāo),運(yùn)行時(shí)鏈接要慢得多,對(duì)應(yīng)用的性能有負(fù)面影響。現(xiàn)代操作系統(tǒng)已經(jīng)很少使用運(yùn)行時(shí)鏈接。  
               
              可以動(dòng)態(tài)鏈接的庫(kù),在Windows上是dynamic   link   library   (DLL),在UNIX或Linux上是Shared   Library。庫(kù)文件是預(yù)先編譯鏈接好的可執(zhí)行文件,存儲(chǔ)在計(jì)算機(jī)的硬盤(pán)上。大多數(shù)情況下,同一時(shí)間多個(gè)應(yīng)用可以使用一個(gè)庫(kù)的同一份拷貝,操作系統(tǒng)不需要加載這個(gè)庫(kù)的多個(gè)實(shí)例。  
               
              Windows   和   Linux   的加載時(shí)鏈接是由操作系統(tǒng)來(lái)完成的,格式在不同的系統(tǒng)下有不同的區(qū)別,但是原理還是一樣的。
            linux下文件的類(lèi)型是不依賴(lài)于其后綴名的,但一般來(lái)講:
            .o,是目標(biāo)文件,相當(dāng)于windows中的.obj文件
            .so 為共享庫(kù),是shared object,用于動(dòng)態(tài)連接的,和dll差不多
            .a為靜態(tài)庫(kù),是好多個(gè).o合在一起,用于靜態(tài)連接
            .la為libtool自動(dòng)生成的一些共享庫(kù),vi編輯查看,主要記錄了一些配置信息。可以用如下命令查看*.la文件的格式   $file *.la
                  *.la: ASCII English text
            所以可以用vi來(lái)查看其內(nèi)容。
            @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
            創(chuàng)建.a庫(kù)文件和.o庫(kù)文件:
            [yufei@localhost perl_c2]$ pwd
            /home/yufei/perl_c2
            [yufei@localhost perl_c2]$ cat mylib.c
            #include <stdio.h>
            #include <string.h>
            void hello(){
                    printf("success call from perl to c library\n");
            }
            [yufei@localhost perl_c2]$ cat mylib.h
            extern void hello();
            [yufei@localhost perl_c2]$ gcc -c mylib.c
            [yufei@localhost perl_c2]$ dir
            mylib.c  mylib.h  mylib.o
            [yufei@localhost perl_c2]$ ar -r mylib.a mylib.o
            ar: 正在創(chuàng)建 mylib.a
            [yufei@localhost perl_c2]$ dir
            mylib.a  mylib.c  mylib.h  mylib.o
            @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
            111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
            動(dòng)態(tài)鏈接庫(kù)*.so的編譯與使用- -
                                                  
            動(dòng)態(tài)庫(kù)*.so在linux下用c和c++編程時(shí)經(jīng)常會(huì)碰到,最近在網(wǎng)站找了幾篇文章介紹動(dòng)態(tài)庫(kù)的編譯和鏈接,總算搞懂了這個(gè)之前一直不太了解得東東,這里做個(gè)筆記,也為其它正為動(dòng)態(tài)庫(kù)鏈接庫(kù)而苦惱的兄弟們提供一點(diǎn)幫助。
            1、動(dòng)態(tài)庫(kù)的編譯
            下面通過(guò)一個(gè)例子來(lái)介紹如何生成一個(gè)動(dòng)態(tài)庫(kù)。這里有一個(gè)頭文件:so_test.h,三個(gè).c文件:test_a.c、test_b.c、test_c.c,我們將這幾個(gè)文件編譯成一個(gè)動(dòng)態(tài)庫(kù):libtest.so。
            so_test.h:
            #include <stdio.h>
            #include <stdlib.h>
            void test_a();
            void test_b();
            void test_c();
            test_a.c:
            #include "so_test.h"
            void test_a()
            {
                printf("this is in test_a...\n");
            }
            test_b.c:
            #include "so_test.h"
            void test_b()
            {
                printf("this is in test_b...\n");
            }
            test_c.c:
            #include "so_test.h"
            void test_c()
            {
                printf("this is in test_c...\n");
            }
            將這幾個(gè)文件編譯成一個(gè)動(dòng)態(tài)庫(kù):libtest.so
            $ gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so
            2、動(dòng)態(tài)庫(kù)的鏈接
            在1、中,我們已經(jīng)成功生成了一個(gè)自己的動(dòng)態(tài)鏈接庫(kù)libtest.so,下面我們通過(guò)一個(gè)程序來(lái)調(diào)用這個(gè)庫(kù)里的函數(shù)。程序的源文件為:test.c。
            test.c:
            #include "so_test.h"
            int main()
            {
                test_a();
                test_b();
                test_c();
                return 0;
            }
            l         將test.c與動(dòng)態(tài)庫(kù)libtest.so鏈接生成執(zhí)行文件test:
            $ gcc test.c -L. -ltest -o test
            l         測(cè)試是否動(dòng)態(tài)連接,如果列出libtest.so,那么應(yīng)該是連接正常了
            $ ldd test
            l         執(zhí)行test,可以看到它是如何調(diào)用動(dòng)態(tài)庫(kù)中的函數(shù)的。
            3、編譯參數(shù)解析
            最主要的是GCC命令行的一個(gè)選項(xiàng):
                      -shared 該選項(xiàng)指定生成動(dòng)態(tài)連接庫(kù)(讓連接器生成T類(lèi)型的導(dǎo)出符號(hào)表,有時(shí)候也生成弱連接W類(lèi)型的導(dǎo)出符號(hào)),不用該標(biāo)志外部程序無(wú)法連接。相當(dāng)于一個(gè)可執(zhí)行文件
            l         -fPIC:表示編譯為位置獨(dú)立的代碼,不用此選項(xiàng)的話編譯后的代碼是位置相關(guān)的所以動(dòng)態(tài)載入時(shí)是通過(guò)代碼拷貝的方式來(lái)滿(mǎn)足不同進(jìn)程的需要,而不能達(dá)到真正代碼段共享的目的。
            l         -L.:表示要連接的庫(kù)在當(dāng)前目錄中
            l         -ltest:編譯器查找動(dòng)態(tài)連接庫(kù)時(shí)有隱含的命名規(guī)則,即在給出的名字前面加上lib,后面加上.so來(lái)確定庫(kù)的名稱(chēng)
            l         LD_LIBRARY_PATH:這個(gè)環(huán)境變量指示動(dòng)態(tài)連接器可以裝載動(dòng)態(tài)庫(kù)的路徑。
            l         當(dāng)然如果有root權(quán)限的話,可以修改/etc/ld.so.conf文件,然后調(diào)用 /sbin/ldconfig來(lái)達(dá)到同樣的目的,不過(guò)如果沒(méi)有root權(quán)限,那么只能采用輸出LD_LIBRARY_PATH的方法了。
            4、注意
                   調(diào)用動(dòng)態(tài)庫(kù)的時(shí)候有幾個(gè)問(wèn)題會(huì)經(jīng)常碰到,有時(shí),明明已經(jīng)將庫(kù)的頭文件所在目錄 通過(guò) “-I” include進(jìn)來(lái)了,庫(kù)所在文件通過(guò) “-L”參數(shù)引導(dǎo),并指定了“-l”的庫(kù)名,但通過(guò)ldd命令察看時(shí),就是死活找不到你指定鏈接的so文件,這時(shí)你要作的就是通過(guò)修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件來(lái)指定動(dòng)態(tài)庫(kù)的目錄。通常這樣做就可以解決庫(kù)無(wú)法鏈接的問(wèn)題了。
            makefile里面怎么正確的編譯和連接生成.so庫(kù)文件,然后又是在其他程序的makefile里面如何編譯和連接才能調(diào)用這個(gè)庫(kù)文件的函數(shù)????
            答:
                   你需要告訴動(dòng)態(tài)鏈接器、加載器ld.so在哪里才能找到這個(gè)共享庫(kù),可以設(shè)置環(huán)境變量把庫(kù)的路徑添加到庫(kù)目錄/lib和/usr/lib,LD_LIBRARY_PATH=$(pwd),這種方法采用命令行方法不太方便,一種替代方法
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^注釋^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            LD_LIBRARY_PATH可以在/etc/profile還是 ~/.profile還是 ./bash_profile里設(shè)置,或者.bashrc里,
            改完后運(yùn)行source /etc/profile或 . /etc/profile
            更好的辦法是添入/etc/ld.so.conf, 然后執(zhí)行 /sbin/ldconfig
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^注釋^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            是把庫(kù)路徑添加到/etc/ld.so.conf,然后以root身份運(yùn)行l(wèi)dconfig
                  也可以在連接的時(shí)候指定文件路徑和名稱(chēng) -I  -L.
                  GCC=gcc
            CFLAGS=-Wall -ggdb -fPIC
            #CFLAGS=
            all: libfunc test
            libfunc:func.o func1.o
                    $(GCC) -shared -Wl,-soname,libfunc.so.1 -o libfunc.so.1.1 $<
                    ln -sf libfunc.so.1.1 libfunc.so.1
                    ln -sf libfunc.so.1 libfunc.so
            ***********************************************注釋************************************************
            ln -s是用來(lái)創(chuàng)建軟鏈接,也就相當(dāng)于windows中的快捷方式,在當(dāng)前目錄中創(chuàng)建上一級(jí)目錄中的文件ttt的命名為ttt2軟鏈接的命令是ln -s ../ttt ttt2,如果原文件也就是ttt文件刪除的話,ttt2也變成了空文件。
            ln -d是用來(lái)創(chuàng)建硬鏈接,也就相當(dāng)于windows中文件的副本,當(dāng)原文件刪除的時(shí)候,并不影響“副本”的內(nèi)容。
            編譯目標(biāo)文件時(shí)使用gcc的-fPIC選項(xiàng),產(chǎn)生與位置無(wú)關(guān)的代碼并能被加載到任何地址:
            gcc –fPIC –g –c liberr.c –o liberr.o
            使用gcc的-shared和-soname選項(xiàng);
            使用gcc的-Wl選項(xiàng)把參數(shù)傳遞給連接器ld;
            使用gcc的-l選項(xiàng)顯示的連接C庫(kù),以保證可以得到所需的啟動(dòng)(startup)代碼,從而避免程序在使用不同的,可能不兼容版本的C庫(kù)的系統(tǒng)上不能啟動(dòng)執(zhí)行。
            gcc –g –shared –Wl,-soname,liberr.so –o liberr.so.1.0.0 liberr.o –lc
            建立相應(yīng)的符號(hào)連接:
            ln –s liberr.so.1.0.0 liberr.so.1;
            ln –s liberr.so.1.0.0 liberr.so;
            在MAKEFILE中:
            $@
                表示規(guī)則中的目標(biāo)文件集。在模式規(guī)則中,如果有多個(gè)目標(biāo),那么,"$@"就是匹配于目標(biāo)中模式定義的集合。
            $%
                僅當(dāng)目標(biāo)是函數(shù)庫(kù)文件中,表示規(guī)則中的目標(biāo)成員名。例如,如果一個(gè)目標(biāo)是"foo.a(bar.o)",那么,"$%"就是"bar.o","$@"就是 "foo.a"。如果目標(biāo)不是函數(shù)庫(kù)文件(Unix下是[.a],Windows下是[.lib]),那么,其值為空。
            $<
                依賴(lài)目標(biāo)中的第一個(gè)目標(biāo)名字。如果依賴(lài)目標(biāo)是以模式(即"%")定義的,那么"$<"將是符合模式的一系列的文件集。注意,其是一個(gè)一個(gè)取出來(lái)的。
            $?
                所有比目標(biāo)新的依賴(lài)目標(biāo)的集合。以空格分隔。
            $^
                所有的依賴(lài)目標(biāo)的集合。以空格分隔。如果在依賴(lài)目標(biāo)中有多個(gè)重復(fù)的,那個(gè)這個(gè)變量會(huì)去除重復(fù)的依賴(lài)目標(biāo),只保留一份。
            *********************************************注釋***********************************************************************
            test: test.o libfunc
                    $(GCC) -o test test.o -L. -lfunc
            %.o:%.c
                    $(GCC) -c $(CFLAGS) -o $@ $<
            clean:
                    rm -fr *.o
                    rm -fr *.so*
                    rm -fr test
            要生成.so文件,cc要帶-shared 參數(shù);要調(diào)用.so的文件,比如libfunc.so,可以在cc命令最后加上-lfunc,還要視情況加上 -L/usr/xxx 指出libfunc.so的路徑;這樣,在你要編譯的源文件中就可以調(diào)用libfunc.so這個(gè)庫(kù)文件的函數(shù).
                   前面的都說(shuō)的差不多了,最后提醒一下最好提供一個(gè)接口頭文件
                   動(dòng)態(tài)加載,用dlopen,dlclose,dlsym
             
            ref:http://niefei.blog.ccidnet.com/blog/ccid/do_showone/tid_42855.html
            1. 介紹
              使用GNU的工具我們?nèi)绾卧贚inux下創(chuàng)建自己的程序函數(shù)庫(kù)?一個(gè)“程序 函數(shù)庫(kù)”簡(jiǎn)單的說(shuō)就是一個(gè)文件包含了一些編譯好的代碼和數(shù)據(jù),這些編 譯好的代碼和數(shù)據(jù)可以在事后供其他的程序使用。程序函數(shù)庫(kù)可以使整個(gè)程序更加模塊化,更容易重新編譯,而且更方便升級(jí)。程序函數(shù)庫(kù)可分為3種類(lèi)型:靜態(tài)函 數(shù)庫(kù)(static libraries)、共享函數(shù)庫(kù)(shared libraries)和動(dòng)態(tài)加載函數(shù)庫(kù)(dynamically loaded libraries)。
              靜態(tài)函數(shù)庫(kù)是在程序執(zhí)行前就加入到目標(biāo)程序中去了;而共享函數(shù)庫(kù)則是在程序啟動(dòng)的時(shí)候加載到程序中,它可以被 不同的程序共享;動(dòng)態(tài)加載函數(shù)庫(kù)則可以在程序運(yùn)行的任何時(shí)候動(dòng)態(tài)的加載。實(shí)際上,動(dòng)態(tài)函數(shù)庫(kù)并非另外一種庫(kù)函數(shù)格式,區(qū)別是動(dòng)態(tài)加載函數(shù)庫(kù)是如何被程序員 使用的。后面我們將舉例說(shuō)明。
              本文檔主要參考Program Library HOWTO,作者是luster(hwang@ustc.edu),任何非商業(yè)目的的再次發(fā)行本文檔都是允許的,但是請(qǐng)保留作者信息和本版權(quán)聲明。本文檔首先在www.linuxaid.com.cn發(fā)布。
              2. 靜態(tài)函數(shù)庫(kù)
               靜態(tài)函數(shù)庫(kù)實(shí)際上就是簡(jiǎn)單的一個(gè)普通的目標(biāo)文件的集合,一般來(lái)說(shuō)習(xí)慣用“.a”作為文件的后綴。可以用ar這個(gè)程序來(lái)產(chǎn)生靜態(tài)函數(shù)庫(kù)文件。Ar 是archiver的縮寫(xiě)。靜態(tài)函數(shù)庫(kù)現(xiàn)在已經(jīng)不在像以前用得那么多了,主要是共享函數(shù)庫(kù)與之相比較有很多的優(yōu)勢(shì)的原因。慢慢地,大家都喜歡使用共享函數(shù) 庫(kù)了。不過(guò),在一些場(chǎng)所靜態(tài)函數(shù)庫(kù)仍然在使用,一來(lái)是保持一些與以前某些程序的兼容,二來(lái)它描述起來(lái)也比較簡(jiǎn)單。
              靜態(tài)庫(kù)函數(shù)允許程序 員把程序link起來(lái)而不用重新編譯代碼,節(jié)省了重新編譯代碼的時(shí)間。不過(guò),在今天這么快速的計(jì)算機(jī)面前,一般的程序的重新編譯也花費(fèi)不了多少時(shí)間,所以 這個(gè)優(yōu)勢(shì)已經(jīng)不是像它以前那么明顯了。靜態(tài)函數(shù)庫(kù)對(duì)開(kāi)發(fā)者來(lái)說(shuō)還是很有用的,例如你想把自己提供的函數(shù)給別人使用,但是又想對(duì)函數(shù)的源代碼進(jìn)行保密,你就 可以給別人提供一個(gè)靜態(tài)函數(shù)庫(kù)文件。理論上說(shuō),使用ELF格式的靜態(tài)庫(kù)函數(shù)生成的代碼可以比使用共享函數(shù)庫(kù)(或者動(dòng)態(tài)函數(shù) 庫(kù))的程序運(yùn)行速度上快一些,大概1-5%。
              創(chuàng)建一個(gè)靜態(tài)函數(shù)庫(kù)文件,或者往一個(gè)已經(jīng)存在地靜態(tài)函數(shù)庫(kù)文件添加新的目標(biāo)代碼,可以用下面的命令:
            ar rcs my_library.a file1.o file2.o
               這個(gè)例子中是把目標(biāo)代碼file1.o和file2.o加入到my_library.a這個(gè)函數(shù)庫(kù)文件中,如果my_library.a不存在 則創(chuàng)建一個(gè)新的文件。在用ar命令創(chuàng)建靜態(tài)庫(kù)函數(shù)的時(shí)候,還有其他一些可以選擇的參數(shù),可以參加ar的使用幫助。這里不再贅述。
              一旦 你創(chuàng)建了一個(gè)靜態(tài)函數(shù)庫(kù),你可以使用它了。你可以把它作為你編譯和連接過(guò)程中的一部分用來(lái)生成你的可執(zhí)行代碼。如果你用gcc來(lái)編譯產(chǎn)生可 執(zhí)行代碼的話,你可以用“-l”參數(shù)來(lái)指定這個(gè)庫(kù)函數(shù)。你也可以用ld來(lái)做,使用它的“-l”和“-L”參數(shù)選項(xiàng)。具體用法,可以參考info:gcc。
             3. 共享函數(shù)庫(kù)
              共享函數(shù)庫(kù)中的函數(shù)是在當(dāng)一個(gè)可執(zhí)行程序在啟動(dòng)的時(shí)候被加載。如果一個(gè)共享函數(shù)庫(kù)正常安裝,所有的程序在重新運(yùn)行的時(shí)候都可以自動(dòng)加載最新的函數(shù)庫(kù)中的函數(shù)。對(duì)于Linux系統(tǒng)還有更多的可以實(shí)現(xiàn)的功能:
            o 升級(jí)了函數(shù)庫(kù)但是仍然允許程序使用老版本的函數(shù)庫(kù)。 o 當(dāng)執(zhí)行某個(gè)特定程序的時(shí)候可以覆蓋某個(gè)特定的庫(kù)或者庫(kù)中指定的函數(shù)。 o 可以在庫(kù)函數(shù)被使用的過(guò)程中修改這些函數(shù)庫(kù)。
              3.1. 一些約定
               如果你要編寫(xiě)的共享函數(shù)庫(kù)支持所有有用的特性,你在編寫(xiě)的過(guò)程中必須遵循一系列約定。你必須理解庫(kù)的不同的名字間的區(qū)別,例如它的 “soname”和“real name”之間的區(qū)別和它們是如何相互作用的。你同樣還要知道你應(yīng)該把這些庫(kù)函數(shù)放在你文件系統(tǒng)的什么位置等等。下面我們具體看看這些問(wèn)題。
              3.1.1. 共享庫(kù)的命名
              每個(gè)共享函數(shù)庫(kù)都有個(gè)特殊的名字,稱(chēng)作“soname”。Soname名字命名必須以“lib”作為前綴,然后是函數(shù)庫(kù)的名字,然后是“.so”,最后是版本號(hào)信息。不過(guò)有個(gè)特例,就是非常底層的C庫(kù)函數(shù)都不是以lib開(kāi)頭這樣命名的。
              每個(gè)共享函數(shù)庫(kù)都有一個(gè)真正的名字(“real name”),它是包含真正庫(kù)函數(shù)代碼的文件。真名有一個(gè)主版本號(hào),和一個(gè)發(fā)行版本號(hào)。最后一個(gè)發(fā)行版本號(hào)是可選的,可以沒(méi)有。主版本號(hào)和發(fā)行版本號(hào)使你可以知道你到底是安裝了什么版本的庫(kù)函數(shù)。
            另外,還有一個(gè)名字是編譯器編譯的時(shí)候需要的函數(shù)庫(kù)的名字,這個(gè)名字就是簡(jiǎn)單的soname名字,而不包含任何版本號(hào)信息。
               管理共享函數(shù)庫(kù)的關(guān)鍵是區(qū)分好這些名字。當(dāng)可執(zhí)行程序需要在自己的程序中列出這些他們需要的共享庫(kù)函數(shù)的時(shí)候,它只要用soname就可以了; 反過(guò)來(lái),當(dāng)你要?jiǎng)?chuàng)建一個(gè)新的共享函數(shù)庫(kù)的時(shí)候,你要指定一個(gè)特定的文件名,其中包含很細(xì)節(jié)的版本信息。當(dāng)你安裝一個(gè)新版本的函數(shù)庫(kù)的時(shí)候,你只要先將這些 函數(shù)庫(kù)文件拷貝到一些特定的目錄中,運(yùn)行l(wèi)dconfig這個(gè)實(shí)用就可以。Ldconfig檢查已經(jīng)存在的庫(kù)文件,然后創(chuàng)建soname的符號(hào)鏈接到真正 的函數(shù)庫(kù),同時(shí)設(shè)置/etc/ld.so.cache這個(gè)緩沖文件。這個(gè)我們稍后再討論。
              Ldconfig并不設(shè)置鏈接的名字,通常 的做法是在安裝過(guò)程中完成這個(gè)鏈接名字的建立,一般來(lái)說(shuō)這個(gè)符號(hào)鏈接就簡(jiǎn)單的指向最新的soname 或者最新版本的函數(shù)庫(kù)文件。最好把這個(gè)符號(hào)鏈接指向soname,因?yàn)橥ǔ.?dāng)你升級(jí)你的庫(kù)函數(shù)的后,你就可以自動(dòng)使用新版本的函數(shù)庫(kù)勒。
              我們來(lái)舉例看看:
               /usr/lib/libreadline.so.3 是一個(gè)完全的完整的soname,ldconfig可以設(shè)置一個(gè)符號(hào)鏈接到其他某個(gè)真正的函數(shù)庫(kù)文件,例如是 /usr/lib/libreadline.so.3.0。同時(shí)還必須有一個(gè)鏈接名字,例如/usr/lib/libreadline.so 就是一個(gè)符號(hào)鏈接指向/usr/lib/libreadline.so.3。
            3.1.2. 文件系統(tǒng)中函數(shù)庫(kù)文件的位置
               共享函數(shù)庫(kù)文件必須放在一些特定的目錄里,這樣通過(guò)系統(tǒng)的環(huán)境變量設(shè)置,應(yīng)用程序才能正確的使用這些函數(shù)庫(kù)。大部分的源碼開(kāi)發(fā)的程序都遵循 GNU的一些標(biāo)準(zhǔn),我們可以看info幫助文件獲得相信的說(shuō)明,info信息的位置是:info: standards#Directory_Variables。GNU標(biāo)準(zhǔn)建議所有的函數(shù)庫(kù)文件都放在/usr/local/lib目錄下,而且建議命令 可執(zhí)行程序都放在/usr/local/bin目錄下。這都是一些習(xí)慣問(wèn)題,可以改變的。
              文件系統(tǒng)層次化標(biāo)準(zhǔn)FHS(Filesystem Hierarchy Standard)(http://www.pathname.com/fhs)規(guī)定了在一個(gè)發(fā)行包中大部分的函數(shù)庫(kù)文件應(yīng)該安裝到/usr/lib目錄 下,但是如果某些庫(kù)是在系統(tǒng)啟動(dòng)的時(shí)候要加載的,則放到/lib目錄下,而那些不是系統(tǒng)本身一部分的庫(kù)則放到/usr/local/lib下面。
              上面兩個(gè)路徑的不同并沒(méi)有本質(zhì)的沖突。GNU提出的標(biāo)準(zhǔn)主要對(duì)于開(kāi)發(fā)者開(kāi)發(fā)源碼的,而FHS的建議則是針對(duì)發(fā)行版本的路徑的。具體的位置信息可以看/etc/ld.so.conf里面的配置信息。
              3.2. 這些函數(shù)庫(kù)如何使用
               在基于GNU glibc的系統(tǒng)里,包括所有的linux系統(tǒng),啟動(dòng)一個(gè)ELF格式的二進(jìn)制可執(zhí)行文件會(huì)自動(dòng)啟動(dòng)和運(yùn)行一個(gè)program loader。對(duì)于Linux系統(tǒng),這個(gè)loader的名字是/lib/ld-linux.so.X(X是版本號(hào))。這個(gè)loader啟動(dòng)后,反過(guò)來(lái)就會(huì) load所有的其他本程序要使用的共享函數(shù)庫(kù)。
              到底在哪些目錄里查找共享函數(shù)庫(kù)呢?這些定義缺省的是放在 /etc/ld.so.conf文件里面,我們可以修改這個(gè)文件,加入我們自己的一些 特殊的路徑要求。大多數(shù)RedHat系列的發(fā)行包的/etc/ld.so.conf文件里面不包括/usr/local/lib這個(gè)目錄,如果沒(méi)有這個(gè)目 錄的話,我們可以修改/etc/ld.so.conf,自己手動(dòng)加上這個(gè)條目。
              如果你想覆蓋某個(gè)庫(kù)中的一些函數(shù),用自己的函數(shù)替換它們,同時(shí)保留該庫(kù)中其他的函數(shù)的話,你可以在/etc/ld.so.preload中加入你想要替換的庫(kù)(.o結(jié)尾的文件),這些preloading的庫(kù)函數(shù)將有優(yōu)先加載的權(quán)利。
               當(dāng)程序啟動(dòng)的時(shí)候搜索所有的目錄顯然會(huì)效率很低,于是Linux系統(tǒng)實(shí)際上用的是一個(gè)高速緩沖的做法。Ldconfig缺省情況下讀出 /etc/ld.so.conf相關(guān)信息,然后設(shè)置適當(dāng)?shù)胤?hào)鏈接,然后寫(xiě)一個(gè)cache到/etc/ld.so.cache這個(gè)文件中,而這個(gè) /etc/ld.so.cache則可以被其他程序有效的使用了。這樣的做法可以大大提高訪問(wèn)函數(shù)庫(kù)的速度。這就要求每次新增加一個(gè)動(dòng)態(tài)加載的函數(shù)庫(kù)的時(shí) 候,就要運(yùn)行l(wèi)dconfig來(lái)更新這個(gè)cache,如果要?jiǎng)h除某個(gè)函數(shù)庫(kù),或者某個(gè)函數(shù)庫(kù)的路徑修改了,都要重新運(yùn)行l(wèi)dconfig來(lái)更新這個(gè) cache。通常的一些包管理器在安裝一個(gè)新的函數(shù)庫(kù)的時(shí)候就要運(yùn)行l(wèi)dconfig。
              另外,F(xiàn)reeBSD使用cache的文件不一樣。FreeBSD的ELF cache是/var/run/ld-elf.so.hints,而a.out的cache責(zé)是/var/run/ld.so.hints。它們同樣是通過(guò)ldconfig來(lái)更新。
              3.3. 環(huán)境變量
               各種各樣的環(huán)境變量控制著一些關(guān)鍵的過(guò)程。例如你可以臨時(shí)為你特定的程序的一次執(zhí)行指定一個(gè)不同的函數(shù)庫(kù)。Linux系統(tǒng)中,通常變量 LD_LIBRARY_PATH就是可以用來(lái)指定函數(shù)庫(kù)查找路徑的,而且這個(gè)路徑通常是在查找標(biāo)準(zhǔn)的路徑之前查找。這個(gè)是很有用的,特別是在調(diào)試一個(gè)新的 函數(shù)庫(kù)的時(shí)候,或者在特殊的場(chǎng)合使用一個(gè)肥標(biāo)準(zhǔn)的函數(shù)庫(kù)的時(shí)候。環(huán)境變量LD_PRELOAD列出了所有共享函數(shù)庫(kù)中需要優(yōu)先加載的庫(kù)文件,功能和 /etc/ld.so.preload類(lèi)似。這些都是有/lib/ld-linux.so這個(gè)loader來(lái)實(shí)現(xiàn)的。值得一提的是, LD_LIBRARY_PATH可以在大部分的UNIX-linke系統(tǒng)下正常起作用,但是并非所有的系統(tǒng)下都可以使用,例如HP-UX系統(tǒng)下,就是用 SHLIB_PATH這個(gè)變量,而在AIX下則使用LIBPATH這個(gè)變量。
              LD_LIBRARY_PATH在開(kāi)發(fā)和調(diào)試過(guò)程中經(jīng)常大量使用,但是不應(yīng)該被一個(gè)普通用戶(hù)在安裝過(guò)程中被安裝程序修改,大家可以去參考 http://www.visi.com/~barr/ldpath.html,這里有一個(gè)文檔專(zhuān)門(mén)介紹為什么不使用LD_LIBRARY_PATH這個(gè) 變量。
              事實(shí)上還有更多的環(huán)境變量影響著程序的調(diào)入過(guò)程,它們的名字通常就是以LD_或者RTLD_打頭。大部分這些環(huán)境變量的使用的文檔都是不全,通常搞得人頭昏眼花的,如果要真正弄清楚它們的用法,最好去讀loader的源碼(也就是gcc的一部分)。
               允許用戶(hù)控制動(dòng)態(tài)鏈接函數(shù)庫(kù)將涉及到setuid/setgid這個(gè)函數(shù)如果特殊的功能需要的話。因此,GNU loader通常限制或者忽略用戶(hù)對(duì)這些變量使用setuid和setgid。如果loader通過(guò)判斷程序的相關(guān)環(huán)境變量判斷程序的是否使用了 setuid或者setgid,如果uid和euid不同,或者gid和egid部一樣,那么loader就假定程序已經(jīng)使用了setuid或者 setgid,然后就大大的限制器控制這個(gè)老鏈接的權(quán)限。如果閱讀GNU glibc的庫(kù)函數(shù)源碼,就可以清楚地看到這一點(diǎn),特別的我們可以看elf/rtld.c和sysdeps/generic/dl-sysdep.c這兩 個(gè)文件。這就意味著如果你使得uid和gid與euid和egid分別相等,然后調(diào)用一個(gè)程序,那么這些變量就可以完全起效。
            3.4. 創(chuàng)建一個(gè)共享函數(shù)庫(kù)
               現(xiàn)在我們開(kāi)始學(xué)習(xí)如何創(chuàng)建一個(gè)共享函數(shù)庫(kù)。其實(shí)創(chuàng)建一個(gè)共享函數(shù)庫(kù)非常容易。首先創(chuàng)建object文件,這個(gè)文件將加入通過(guò)gcc –fPIC 參數(shù)命令加入到共享函數(shù)庫(kù)里面。PIC的意思是“位置無(wú)關(guān)代碼”(Position Independent Code)。下面是一個(gè)標(biāo)準(zhǔn)的格式:
            gcc -shared -Wl,-soname,your_soname -o library_name file_list library_list
              下面再給一個(gè)例子,它創(chuàng)建兩個(gè)object文件(a.o和b.o),然后創(chuàng)建一個(gè)包含a.o和b.o的共享函數(shù)庫(kù)。例子中”-g”和“-Wall”參數(shù)不是必須的。
            gcc -fPIC -g -c -Wall a.cgcc -fPIC -g -c -Wall b.cgcc -shared -Wl,
            -soname,liblusterstuff.so.1 -o liblusterstuff.so.1.0.1 a.o b.o -lc
              下面是一些需要注意的地方:
            · 不用使用-fomit-frame-pointer這個(gè)編譯參數(shù)除非你不得不這樣。雖然使用了這個(gè)參數(shù)獲得的函數(shù)庫(kù)仍然可以使用,但是這使得調(diào)試程序幾乎 沒(méi)有用,無(wú)法跟蹤調(diào)試。 · 使用-fPIC來(lái)產(chǎn)生代碼,而不是-fpic。 · 某些情況下,使用gcc 來(lái)生成object文件,需要使用“-Wl,-export-dynamic”這個(gè)選項(xiàng)參數(shù)。通常,動(dòng)態(tài)函數(shù)庫(kù)的符號(hào)表里面包含了這些動(dòng)態(tài)的對(duì)象的符號(hào)。 這個(gè)選項(xiàng)在創(chuàng)建ELF格式的文件時(shí)候,會(huì)將所有的符號(hào)加入到動(dòng)態(tài)符號(hào)表中。可以參考ld的幫助獲得更詳細(xì)的說(shuō)明。
              3.5. 安裝和使用共享函數(shù)庫(kù)
              一旦你了一個(gè)共享函數(shù)庫(kù),你還需要安裝它。其實(shí)簡(jiǎn)單的方法就是拷貝你的庫(kù)文件到指定的標(biāo)準(zhǔn)的目錄(例如/usr/lib),然后運(yùn)行l(wèi)dconfig。
               如果你沒(méi)有權(quán)限去做這件事情,例如你不能修改/usr/lib目錄,那么你就只好通過(guò)修改你的環(huán)境變量來(lái)實(shí)現(xiàn)這些函數(shù)庫(kù)的使用了。首先,你需要 創(chuàng)建這些共享函數(shù)庫(kù);然后,設(shè)置一些必須得符號(hào)鏈接,特別是從soname到真正的函數(shù)庫(kù)文件的符號(hào)鏈接,簡(jiǎn)單的方法就是運(yùn)行l(wèi)dconfig:
            ldconfig -n directory_with_shared_libraries
              然后你就可以設(shè)置你的LD_LIBRARY_PATH這個(gè)環(huán)境變量,它是一個(gè)以逗號(hào)分隔的路徑的集合,這個(gè)可以用來(lái)指明共享函數(shù)庫(kù)的搜索路徑。例如,使用bash,就可以這樣來(lái)啟動(dòng)一個(gè)程序my_program:
            LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH my_program
               如果你需要的是重載部分函數(shù),則你就需要?jiǎng)?chuàng)建一個(gè)包含需要重載的函數(shù)的object文件,然后設(shè)置LD_PRELOAD環(huán)境變量。通常你可以很 方便的升級(jí)你的函數(shù)庫(kù),如果某個(gè)API改變了,創(chuàng)建庫(kù)的程序會(huì)改變soname。然而,如果一個(gè)函數(shù)升級(jí)了某個(gè)函數(shù)庫(kù)而保持了原來(lái)的soname,你可以 強(qiáng)行將老版本的函數(shù)庫(kù)拷貝到某個(gè)位置,然后重新命名這個(gè)文件(例如使用原來(lái)的名字,然后后面加.orig后綴),然后創(chuàng)建一個(gè)小的“wrapper”腳本 來(lái)設(shè)置這個(gè)庫(kù)函數(shù)和相關(guān)的東西。例如下面的例子:
            #!/bin/sh export LD_LIBRARY_PATH=/usr/local/my_lib:$LD_LIBRARY_PATH exec
            /usr/bin/my_program.orig $*
              我們可以通過(guò)運(yùn)行l(wèi)dd來(lái)看某個(gè)程序使用的共享函數(shù)庫(kù)。例如你可以看ls這個(gè)實(shí)用工具使用的函數(shù)庫(kù):
            ldd /bin/ls
                libtermcap.so.2 => /lib/libtermcap.so.2 (0x4001c000)
                libc.so.6 => /lib/libc.so.6 (0x40020000)
                /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
              通常我么可以看到一個(gè)soname的列表,包括路徑。在所有的情況下,你都至少可以看到兩個(gè)庫(kù):
            · /lib/ld-linux.so.N(N是1或者更大,一般至少2)。
            這是這個(gè)用力加載其他所有的共享庫(kù)的庫(kù)。
            · libc.so.N(N應(yīng)該大于或者等于6)。這是C語(yǔ)言函數(shù)庫(kù)。
               值得一提的是,不要在對(duì)你不信任的程序運(yùn)行l(wèi)dd命令。在ldd的manual里面寫(xiě)得很清楚,ldd是通過(guò)設(shè)置某些特殊的環(huán)境變量(例如,對(duì) 于ELF對(duì)象,設(shè)置LD_TRACE_LOADED_OBJECTS),然后運(yùn)行這個(gè)程序。這樣就有可能使得某地程序可能使得ldd來(lái)執(zhí)行某些意想不到的 代碼,而產(chǎn)生不安全的隱患。
            3.6. 不兼容的函數(shù)庫(kù)
              如果一個(gè)新版的函數(shù)庫(kù)要和老版本的二進(jìn)制的庫(kù)不兼容,則soname需要改變。對(duì)于C語(yǔ)言,一共有4個(gè)基本的理由使得它們?cè)诙M(jìn)制代碼上很難兼容:
              o. 一個(gè)函數(shù)的行文改變了,這樣它就可能與最開(kāi)始的定義不相符合。
              o. 輸出的數(shù)據(jù)項(xiàng)改變了。
              o. 某些輸出的函數(shù)刪除了。
              o. 某些輸出函數(shù)的接口改變了。
              如果你能避免這些地方,你就可以保持你的函數(shù)庫(kù)在二進(jìn)制代碼上的兼容,或者說(shuō),你可以使得你的程序的應(yīng)用二進(jìn)制接口(ABI:Application Binary Interface)上兼容。
              4. 動(dòng)態(tài)加載的函數(shù)庫(kù)Dynamically Loaded (DL) Libraries
               動(dòng)態(tài)加載的函數(shù)庫(kù)Dynamically loaded (DL) libraries是一類(lèi)函數(shù)庫(kù),它可以在程序運(yùn)行過(guò)程中的任何時(shí)間加載。它們特別適合在函數(shù)中加載一些模塊和plugin擴(kuò)展模塊的場(chǎng)合,因?yàn)樗梢栽?當(dāng)程序需要某個(gè)plugin模塊時(shí)才動(dòng)態(tài)的加載。例如,Pluggable Authentication Modules(PAM)系統(tǒng)就是用動(dòng)態(tài)加載函數(shù)庫(kù)來(lái)使得管理員可以配置和重新配置身份驗(yàn)證信息。
              Linux系統(tǒng)下,DL函數(shù)庫(kù)與其 他函數(shù)庫(kù)在格式上沒(méi)有特殊的區(qū)別,我們前面提到過(guò),它們創(chuàng)建的時(shí)候是標(biāo)準(zhǔn)的object格式。主要的區(qū)別就是 這些函數(shù)庫(kù)不是在程序鏈接的時(shí)候或者啟動(dòng)的時(shí)候加載,而是通過(guò)一個(gè)API來(lái)打開(kāi)一個(gè)函數(shù)庫(kù),尋找符號(hào)表,處理錯(cuò)誤和關(guān)閉函數(shù)庫(kù)。通常C語(yǔ)言環(huán)境下,需要包 含這個(gè)頭文件。
              Linux中使用的函數(shù)和Solaris中一樣,都是dlpoen() API。當(dāng)時(shí)不是所有的平臺(tái)都使用同樣的接口,例如HP-UX使用shl_load()機(jī)制,而Windows平臺(tái)用另外的其他的調(diào)用接口。如果你的目的 是使得你的代碼有很強(qiáng)的移植性,你應(yīng)該使用一些wrapping函數(shù)庫(kù),這樣的wrapping函數(shù)庫(kù)隱藏不同的平臺(tái)的接口區(qū)別。一種方法是使用 glibc函數(shù)庫(kù)中的對(duì)動(dòng)態(tài)加載模塊的支持,它使用一些潛在的動(dòng)態(tài)加載函數(shù)庫(kù)界面使得它們可以夸平臺(tái)使用。具體可以參考http: //developer.gnome.org/doc/API/glib/glib-dynamic-loading-of-modules.html. 另外一個(gè)方法是使用libltdl,是GNU libtool的一部分,可以進(jìn)一步參考CORBA相關(guān)資料。
              4.1. dlopen()
              dlopen函數(shù)打開(kāi)一個(gè)函數(shù)庫(kù)然后為后面的使用做準(zhǔn)備。C語(yǔ)言原形是:
            void * dlopen(const char *filename, int flag);
              如果文件名filename是以“/”開(kāi)頭,也就是使用絕對(duì)路徑,那么dlopne就直接使用它,而不去查找某些環(huán)境變量或者系統(tǒng)設(shè)置的函數(shù)庫(kù)所在的目錄了。否則dlopen()
              就會(huì)按照下面的次序查找函數(shù)庫(kù)文件:
            1. 環(huán)境變量LD_LIBRARY指明的路徑。 2. /etc/ld.so.cache中的函數(shù)庫(kù)列表。 3. /lib目錄,然后/usr/lib。不過(guò)一些很老的a.out的loader則是采用相反的次序,也就是先查/usr/lib,然后是/lib。
               Dlopen()函數(shù)中,參數(shù)flag的值必須是RTLD_LAZY或者RTLD_NOW,RTLD_LAZY的意思是resolve undefined symbols as code from the dynamic library is executed,而RTLD_NOW的含義是resolve all undefined symbols before dlopen() returns and fail if this cannot be done'。
              如果有好幾個(gè)函數(shù)庫(kù),它們之間有一些依賴(lài)關(guān)系的話,例如X依賴(lài)Y,那么你就要先加載那些被依賴(lài)的函數(shù)。例如先加載Y,然后加載X。
              dlopen()函數(shù)的返回值是一個(gè)句柄,然后后面的函數(shù)就通過(guò)使用這個(gè)句柄來(lái)做進(jìn)一步的操作。如果打開(kāi)失敗dlopen()就返回一個(gè)NULL。如果一個(gè)函數(shù)庫(kù)被多次打開(kāi),它會(huì)返回同樣的句柄。
              如果一個(gè)函數(shù)庫(kù)里面有一個(gè)輸出的函數(shù)名字為_(kāi)init,那么_init就會(huì)在dlopen()這個(gè)函數(shù)返回前被執(zhí)行。我們可以利用這個(gè)函數(shù)在我的函數(shù)庫(kù)里面做一些初始化的工作。我們后面會(huì)繼續(xù)討論這個(gè)問(wèn)題的。
              4.2. dlerror()
              通過(guò)調(diào)用dlerror()函數(shù),我們可以獲得最后一次調(diào)用dlopen(),dlsym(),或者dlclose()的錯(cuò)誤信息。
            4.3. dlsym()
              如果你加載了一個(gè)DL函數(shù)庫(kù)而不去使用當(dāng)然是不可能的了,使用一個(gè)DL函數(shù)庫(kù)的最主要的一個(gè)函數(shù)就是dlsym(),這個(gè)函數(shù)在一個(gè)已經(jīng)打開(kāi)的函數(shù)庫(kù)里面查找給定的符號(hào)。這個(gè)函數(shù)如下定義:
            void * dlsym(void *handle, char *symbol);
              函數(shù)中的參數(shù)handle就是由dlopen打開(kāi)后返回的句柄,symbol是一個(gè)以NIL結(jié)尾的字符串。
               如果dlsym()函數(shù)沒(méi)有找到需要查找的symbol,則返回NULL。如果你知道某個(gè)symbol的值不可能是NULL或者0,那么就很 好,你就可以根據(jù)這個(gè)返回結(jié)果判斷查找的symbol是否存在了;不過(guò),如果某個(gè)symbol的值就是NULL,那么這個(gè)判斷就有問(wèn)題了。標(biāo)準(zhǔn)的判斷方法 是先調(diào)用dlerror(),清除以前可能存在的錯(cuò)誤,然后調(diào)用dlsym()來(lái)訪問(wèn)一個(gè)symbol,然后再調(diào)用dlerror()來(lái)判斷是否出現(xiàn)了錯(cuò) 誤。一個(gè)典型的過(guò)程如下:
            dlerror();
            s = (actual_type) dlsym(handle, symbol_being_searched_for);
            if ((err = dlerror()) != NULL)
            {
            }
            else
            {
            }
              4.4. dlclose()
               dlopen()函數(shù)的反過(guò)程就是dlclose()函數(shù),dlclose()函數(shù)用力關(guān)閉一個(gè)DL函數(shù)庫(kù)。Dl函數(shù)庫(kù)維持一個(gè)資源利用的計(jì)數(shù) 器,當(dāng)調(diào)用dlclose的時(shí)候,就把這個(gè)計(jì)數(shù)器的計(jì)數(shù)減一,如果計(jì)數(shù)器為0,則真正的釋放掉。真正釋放的時(shí)候,如果函數(shù)庫(kù)里面有_fini()這個(gè)函 數(shù),則自動(dòng)調(diào)用_fini()這個(gè)函數(shù),做一些必要的處理。Dlclose()返回0表示成功,其他非0值表示錯(cuò)誤。
              4.5. DL Library Example
              下面是一個(gè)例子。例子中調(diào)入math函數(shù)庫(kù),然后打印2.0的余弦函數(shù)值。例子中每次都檢查是否出錯(cuò)。應(yīng)該是個(gè)不錯(cuò)的范例:
              #include
              #include
              #include
              int main(int argc, char **argv)
              {
                void *handle;
                double (*cosine)(double);
                char *error;
                handle = dlopen ("/lib/libm.so.6", RTLD_LAZY);
                if (!handle) {
                    fputs (dlerror(), stderr);
                    exit(1);
                }
                cosine = dlsym(handle, "cos");
                if ((error = dlerror()) != NULL)
              {
                    fputs(error, stderr);
                    exit(1);
                }
                printf ("%f ", (*cosine)(2.0));
                dlclose(handle);
            }
              如果這個(gè)程序名字叫foo.c,那么用下面的命令來(lái)編譯:
              gcc -o foo foo.c -ldl
            5. 其他
              5.1. nm命令
               nm命令可以列出一個(gè)函數(shù)庫(kù)文件中的符號(hào)表。它對(duì)于靜態(tài)的函數(shù)庫(kù)和共享的函數(shù)庫(kù)都起作用。對(duì)于一個(gè)給定的函數(shù)庫(kù),nm命令可以列出函數(shù)庫(kù)中定義 的所有符號(hào),包括每個(gè)符號(hào)的值和類(lèi)型。還可以給出在原程序中這個(gè)函數(shù)(符號(hào))是在多少行定義的,不過(guò)這必須要求編譯該函數(shù)庫(kù)的時(shí)候加“-l”選項(xiàng)。
               關(guān)于符號(hào)的類(lèi)型,這里我們?cè)俣嘤懻撘幌隆7?hào)的類(lèi)型是以一個(gè)字母的形式顯示的,小寫(xiě)字母表示這個(gè)符號(hào)是本地(local)的,而大寫(xiě)字母則表示 這個(gè)符號(hào)是全局的(global,externel)。一般來(lái)說(shuō),類(lèi)型有一下幾種:T、D、B、U、W。各自的含義如下:T表示在代碼段中定義的一般變量 符號(hào);D表示時(shí)初始化過(guò)的數(shù)據(jù)段;B表示初始化的數(shù)據(jù)段;U表示沒(méi)有定義的,在這個(gè)庫(kù)里面使用了,但是在其他庫(kù)中定義的符號(hào);W,weak的縮寫(xiě),表示如 果其他函數(shù)庫(kù)中也有對(duì)這個(gè)符號(hào)的定義,則其他符號(hào)的定義可以覆蓋這個(gè)定義。
              如果你知道一個(gè)函數(shù)的名字,但是你不知道這個(gè)函數(shù)在什么庫(kù)中定義的,那么可以用mn的“-o”選項(xiàng)和grep命令來(lái)查找?guī)斓拿帧?o選項(xiàng)使得顯示的每一行都有這個(gè)函數(shù)庫(kù)文件名。例如,你要查找“cos”這個(gè)是在什么地方定義的,大致可以用下面的命令:
            nm -o /lib* /usr/local/libGROUP ( /lib/libc.so.6
            /usr/lib/libc_nonshared.a )
              更多的信息可以參考texinfo文檔中關(guān)于ld鏈接的腳本部分。一般的信息還可以參考: info:ld#Options 和info:ld#Commands,也可以參考info:ld#Option Commands。
              5.4. GNU libtool
               如果你正在編譯的系統(tǒng)相很方便的移植到其他操作系統(tǒng)下,你可以使用GNU libtool來(lái)創(chuàng)建和安裝這個(gè)函數(shù)庫(kù)。GNU libtool是一個(gè)函數(shù)庫(kù)支持的典型的腳本。Libtool隱藏了使用一個(gè)可移植的函數(shù)庫(kù)的負(fù)責(zé)性。Libtool提供了一個(gè)可以移植的界面來(lái)創(chuàng)建 object文件,鏈接函數(shù)庫(kù)(靜態(tài)或者共享的),并且安裝這些庫(kù)。它還包含了libltdl,一個(gè)可移植的動(dòng)態(tài)函數(shù)庫(kù)調(diào)入程序的wrapper。更多的 詳細(xì)討論,可以在http://www.gnu.org/software/libtool/manual.html看到。
              5.5. 刪除一些符號(hào)
              在一個(gè)生產(chǎn)的文件中很多符號(hào)都是為了debug而包含的,占用了不少空間。如果空間不夠,而且這些符號(hào)也許不再需要,就可以將其中一些刪除。
               最好的方法就是先正常的生成你需要的object文件,然后debug和測(cè)試你需要的一些東西。一旦你完全測(cè)試完畢了,就可以用strip去刪 除一些不需要的符號(hào)了。Strip命令可以使你很方便的控制刪除什么符號(hào),而保留什么符號(hào)。Strip的具體用法可以參考其幫助文件。
              另外的方法就是使用GNU ld的選項(xiàng)“-S”和“-s”;“-S”會(huì)刪除一些debugger的符號(hào),而“-s”則是將所有的符號(hào)信息都刪除。通常我們可以在gcc中加這樣的參數(shù)“-Wl,-S”和“-Wl,-s”來(lái)達(dá)到這個(gè)目的。
            摘要
            下 面是一些例子,例子中我們會(huì)使用三種函數(shù)庫(kù)(靜態(tài)的、共享的和動(dòng)態(tài)加載的函數(shù)庫(kù))。文件libhello.c是一個(gè)函數(shù)庫(kù),libhello.h 是它的頭文件;demo_use.c則是一個(gè)使用了libhello函數(shù)庫(kù)的。Script_static和script_dynamic分別演示如何以 靜態(tài)和共享方式使用函數(shù)庫(kù),而后面的demo_dynamic.c和script_dynamic則表示演示如何以動(dòng)態(tài)加載函數(shù)庫(kù)的方式來(lái)使用它。
            (2002-08-25 17:38:37)
            By Wing
              6. 更多的例子
               下面是一些例子,例子中我們會(huì)使用三種函數(shù)庫(kù)(靜態(tài)的、共享的和動(dòng)態(tài)加載的函數(shù)庫(kù))。文件libhello.c是一個(gè)函數(shù)庫(kù), libhello.h是它的頭文件;demo_use.c則是一個(gè)使用了libhello函數(shù)庫(kù)的。Script_static和 script_dynamic分別演示如何以靜態(tài)和共享方式使用函數(shù)庫(kù),而后面的demo_dynamic.c和script_dynamic則表示演示 如何以動(dòng)態(tài)加載函數(shù)庫(kù)的方式來(lái)使用它。
              6.1. File libhello.c
            #include
            void hello(void)
            {
            printf("Hello, library world.
            ");
            }
              6.2. File libhello.h
            void hello(void);
              6.3. File demo_use.c
            #include "libhello.h"
            int main(void)
            {
            hello();
            return 0;
            }
              6.4. File script_static
            #!/bin/sh
            # Static library demo
            # Create static library's object file, libhello-static.o.
            # I'm using the name libhello-static to clearly
            # differentiate the static library from the
            # dynamic library examples, but you don't need to use
            # "-static" in the names of your
            # object files or static libraries.gcc -Wall -g -c -o libhello-static.o
            libhello.c
            # Create static library.ar rcs libhello-static.a libhello-static.o
            # At this point we could just copy libhello-static.a
            # somewhere else to use it.
            # For demo purposes, we'll just keep the library
            # in the current directory.
            # Compile demo_use program file.gcc -Wall -g -c demo_use.c -o demo_use.o
            # Create demo_use program; -L. causes "." to be searched during
            # creation of the program. Note that this command causes
            # the relevant object file in libhello-static.a to be
            # incorporated into file demo_use_static.gcc -g -o demo_use_static
            demo_use.o -L. -lhello-static
            # Execute the program../demo_use_static
              6.5. File script_shared
            #!/bin/sh
            # Shared library demo
            # Create shared library's object file, libhello.o.gcc -fPIC -Wall
            -g -c libhello.c
            # Create shared library.
            # Use -lc to link it against C library, since libhello
            # depends on the C library.gcc -g -shared -Wl,-soname,libhello.so.0 -o
            libhello.so.0.0 libhello.o -lc# At this point we could just copy
            libhello.so.0.0 into
            # some directory, say /usr/local/lib.
            # Now we need to call ldconfig to fix up the symbolic links.
            # Set up the soname. We could just execute:
            # ln -sf libhello.so.0.0 libhello.so.0
            # but let's let ldconfig figure it out./sbin/ldconfig -n .
            # Set up the linker name.
            # In a more sophisticated setting, we'd need to make
            # sure that if there was an existing linker name,
            # and if so, check if it should stay or not.ln -sf libhello.so.0
            libhello.so
            # Compile demo_use program file.gcc -Wall -g -c demo_use.c -o
            demo_use.o
            # Create program demo_use.
            # The -L. causes "." to be searched during creation
            # of the program; note that this does NOT mean that "."
            # will be searched when the program is executed.gcc -g -o demo_use
            demo_use.o -L. -lhello
            # Execute the program. Note that we need to tell the program
            # where the shared library is,
            using LD_LIBRARY_PATH.LD_LIBRARY_PATH="." ./demo_use
              6.6. File demo_dynamic.c
            #include
            #include
            #include
            typedef void (*simple_demo_function)(void);
            int main(void)
            {
            const char *error;
            void *module;
            simple_demo_function demo_function;
            module = dlopen("libhello.so", RTLD_LAZY);
            if (!module)
            {
              fprintf(stderr, "Couldn't open libhello.so: %s
            ",dlerror());
              exit(1);
            }
            dlerror();
            demo_function = dlsym(module, "hello");
            if ((error = dlerror()))
            {
              fprintf(stderr, "Couldn't find hello: %s
            ", error);
              exit(1);
            }
            (*demo_function)();
            dlclose(module);
            return 0;
            }
              6.7. File script_dynamic
            #!/bin/sh
            # Dynamically loaded library demo
            # Presume that libhello.so and friends have
            # been created (see dynamic example).
            # Compile demo_dynamic program file into an object file.gcc
            -Wall -g -c demo_dynamic.c
            # Create program demo_use.
            # Note that we don't have to tell it where to search
            for DL libraries,
            # since the only special library this program uses won't be
            # loaded until after the program starts up.
            # However, we DO need the option -ldl to include the library
            # that loads the DL libraries.gcc -g -o demo_dynamic
            demo_dynamic.o -ldl
            # Execute the program. Note that we need to tell the
            # program where get the dynamically loaded library,
            # using LD_LIBRARY_PATH.LD_LIBRARY_PATH="." ./demo_dynamic

            posted on 2012-12-07 22:34 tqsheng 閱讀(243) 評(píng)論(0)  編輯 收藏 引用


            只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            成人资源影音先锋久久资源网| 免费一级欧美大片久久网| 中文字幕无码av激情不卡久久| 免费国产99久久久香蕉| 久久人与动人物a级毛片| 狠狠色婷婷久久一区二区| 一本色道久久综合狠狠躁| 国内精品九九久久久精品| 久久精品国内一区二区三区| 久久精品中文字幕有码| 久久精品国产亚洲AV香蕉| 久久A级毛片免费观看| 99久久精品国产毛片| 久久笫一福利免费导航 | 久久精品国产91久久综合麻豆自制| 国产成人AV综合久久| 久久亚洲AV无码精品色午夜| 国产美女久久久| 亚洲综合久久久| 岛国搬运www久久| 亚洲午夜久久久久妓女影院 | 国产69精品久久久久9999APGF| 精品久久8x国产免费观看| 久久精品二区| 蜜臀av性久久久久蜜臀aⅴ | 无码国内精品久久人妻蜜桃| 香蕉久久一区二区不卡无毒影院| 久久人人超碰精品CAOPOREN| 亚洲AV日韩AV永久无码久久 | 久久夜色撩人精品国产| 中文字幕乱码久久午夜| 精品久久久久久无码中文野结衣| 久久国产欧美日韩精品| 久久精品国产99国产精品 | 久久久久无码中| 国产成人久久精品激情| 久久人人爽人人澡人人高潮AV| 日韩精品久久无码人妻中文字幕| 伊人久久大香线蕉综合热线| 久久成人18免费网站| 亚洲狠狠综合久久|