2010年5月26日 星期三

[轉貼]用gcc 自製 Library

By kaineshu

轉自 PTT LinuxDev

作者: cole945 (躂躂..) 看板: LinuxDev

標題: [心得] 用gcc 自製Library

時間: Sun Nov 5 04:15:45 2006

Library可分成三種,static、shared與dynamically loaded。

1. Static libraries

Static 程式庫用於靜態連結,簡單講是把一堆object檔用ar(archiver)包裝集合起來,檔名以`.a’ 結尾。優點是執行效能通常會比後兩者快,而且因為是靜態連結,所以不易發生執行時找不到library或版本錯置而無法執行的問題。缺點則是檔案較大,維護度較低;例如library如果發現bug需要更新,那麼就必須重新連結執行檔。

1.1 編譯

編譯方式很簡單,先例用`-c’ 編出object 檔,再用ar 包起來即可。

____ hello.c ____

#include

void hello(){ printf(“Hello “); }

____ world.c ____

#include

void world(){ printf(“world.”); }

____ mylib.h ____

void hello();

void world();

$ gcc -c hello.c world.c /* 編出hello.o 與world.o */

$ ar rcs libmylib.a hello.o world.o /* 包成limylib.a */

這樣就可以建出一個檔名為libmylib.a 的檔。輸出的檔名其實沒有硬性規定,但如果想要配合gcc 的’-l’ 參數來連結,一定要以`lib’ 開頭,中間是你要的library名稱,然後緊接著`.a’ 結尾。

1.2 使用

____ main.c ____

#include “mylib.h”

int main() {

hello();

world();

}

使用上就像與一般的object 檔連結沒有差別。

$ gcc main.c libmylib.a

也可以配合gcc 的`-l’ 參數使用

$ gcc main.c -L. -lmylib

-L 參數用來指定要搜尋程式庫的目錄,`.' 表示搜尋現在所在的目錄。通常預設會搜/usr/lib 或/lib 等目錄。

-l  參數用來指定要連結的程式庫,'mylib' 表示要與mylib進行連結

,他會搜尋library名稱前加`lib'後接`.a'的檔案來連結。

$ ./a.out

Hello world.

2. Shared libraries

Shared library 會在程式執行起始時才被自動載入。因為程式庫與執行檔是分離的,所以維護彈性較好。有兩點要注意,shared library是在程式起始時就要被載入,而不是執行中用到才載入,而且在連結階段需要有該程式庫才能進行連結。

首先有一些名詞要弄懂,soname、real name與linker name。

soname 用來表示是一個特定library 的名稱,像是libmylib.so.1 。前面以`lib' 開頭,接著是該library 的名稱,然後是`.so' ,接著是版號,用來表名他的介面;如果介面改變時,就會增加版號來維護相容度。

real name 是實際放有library程式的檔案名稱,後面會再加上minor 版號與release 版號,像是libmylib.so.1.0.0 。

一般來說,版號的改變規則是(印象中在APress-Difinitive Guide to GCC中有提到,但目前手邊沒這本書),最尾碼的release版號用於程式內容的修正,介面完全沒有改變。中間的minor用於有新增加介面,但相舊介面沒改變,所以與舊版本相容。最前面的version版號用於原介面有移除或改變,與舊版不相容時。

linker name是用於連結時的名稱,是不含版號的soname ,如: libmylib.so。

通常linker name與real name是用ln 指到對應的real name ,用來提供彈性與維護性。

2.1 編譯

shared library的製作過程較複雜。

$ gcc -c -fPIC hello.c world.c

編譯時要加上-fPIC 用來產生position-independent code。也可以用-fpic參數。(不太清楚差異,只知道-fPIC 較通用於不同平台,但產生的code較大,而且編譯速度較慢)。

$ gcc -shared -Wl,-soname,libmylib.so.1 -o libmylib.so.1.0.0 \

hello.o world.o

-shared 表示要編譯成shared library

-Wl 用於參遞參數給linker,因此-soname與libmylib.so.1會被傳給linker處理。

-soname用來指名soname 為limylib.so.1

library會被輸出成libmylib.so.1.0.0 (也就是real name)

若不指定soname 的話,在編譯結連後的執行檔會以連時的library檔名為soname,並載入他。否則是載入soname指定的library檔案。

可以利用objdump 來看library 的soname。

$ objdump -p libmylib.so | grep SONAME

SONAME libmylib.so.1

若不指名-soname參數的話,則library不會有這個欄位資料。

在編譯後再用ln 來建立soname 與linker name 兩個檔案。

$ ln -s libmylib.so.1.0.0 libmylib.so

$ ln -s libmylib.so.1.0.0 libmylib.so.1

2.2 使用

與使用static library 同。

$ gcc main.c libmylib.so

以上直接指定與libmylib.so 連結。

或用

$ gcc main.c -L. -lmylib

linker會搜尋libmylib.so 來進行連結。

如果目錄下同時有static與shared library的話,會以shared為主。使用-static 參數可以避免使用shared連結。

$ gcc main.c -static -L. -lmylib

此時可以用ldd 看編譯出的執行檔與shared程式庫的相依性

$ldd a.out

linux-gate.so.1 => (0xffffe000)

[1;33mlibmylib.so.1 => not found[m

libc.so.6 => /lib/libc.so.6 (0xb7dd6000)

/lib/ld-linux.so.2 (0xb7f07000)

輸出結果顯示出該執行檔需要libmylib.so.1 這個shared library。會顯示not found 因為沒指定該library所在的目錄,所找不到該library。因為編譯時有指定-soname參數為libmylib.so.1 的關係,所以該執行檔會

載入libmylib.so.1。否則以libmylib.so連結,執行檔則會變成要求載入libmylib.so

$ ./a.out

./a.out: error while loading shared libraries: [1;33mlibmylib.so.1[m:

cannot open shared object file: No such file or directory

因為找不到libmylib.so.1 所以無法執行程式。

有幾個方式可以處理。

a. 把libmylib.so.1 安裝到系統的library目錄,如/usr/lib下

b. 設定/etc/ld.so.conf ,加入一個新的library搜尋目錄,並執行ldconfig

更新快取

c. 設定LD_LIBRARY_PATH 環境變數來搜尋library

這個例子是加入目前的目錄來搜尋要載作的library

$ LD_LIBRARY_PATH=. ./a.out

Hello world.

3. Dynamically loaded libraries

Dynamicaaly loaded libraries 才是像windows 所用的DLL ,在使用到

時才載入,編譯連結時不需要相關的library。動態載入庫常被用於像plug-ins的應用。

3.1 使用方式

動態載入是透過一套dl function來處理。

#include

void *dlopen(const char *filename, int flag);

開啟載入filename 指定的library。

void *dlsym(void *handle, const char *symbol);

取得symbol 指定的symbol name在library被載入的記憶體位址。

int dlclose(void *handle);

關閉dlopen開啟的handle。

char *dlerror(void);

傳回最近所發生的錯誤訊息。

____ dltest.c ____

#include

#include

#include

int main() {

void *handle;

void (*f)();

char *error;

/* 開啟之前所撰寫的libmylib.so 程式庫*/

handle = dlopen("./libmylib.so", RTLD_LAZY);

if( !handle ) {

fputs( dlerror(), stderr);

exit(1);

}

/* 取得hello function 的address */

f = dlsym(handle, "hello");

if(( error=dlerror())!=NULL) {

fputs(error, stderr);

exit(1);

}

/* 呼叫該function */

f();

dlclose(handle);

}

編譯時要加上-ldl 參數來與dl library 連結

$ gcc dltest.c -ldl

結果會印出Hello 字串

$ ./a.out

Hello

關於dl的詳細內容請參閱man dlopen

--

參考資料:

Creating a shared and static library with the gnu compiler [gcc]

http://www.adp-gmbh.ch/cpp/gcc/create_lib.html

Program Library HOWTO

http://tldp.org/HOWTO/Program-Library-HOWTO/index.html

APress – Definitive Guide to GCC

※發信站: 批踢踢實業坊(ptt.cc)




2010年5月20日 星期四

emacs and program tracing

使用 emacs tracing pogram 之時,陸續發現幾個方便的按鍵,因為沒什麼系統性,又怕忘記,只好開個 blog 來紀錄:

C-M-p
C-M-n

   如同 Visual C++ 的括號追蹤,游標可以來回跳至成對的括號位置。
   小技巧,遇到 { 時,游標要放在括號的位置上
                 遇到 } 時,游標要放在括號的右邊一格

C-c C-p
C-c C-n
   從游標處往上找,移至最近的 #ifdef  
   從游標處往下找,移至最近的 #endif 後面一格

C-c C-u
   移至上一層的 #ifdef
[ Note ] C-c 是 mode specific command 之意,在此例就是 C mode


C-x C-b
   列出 buffers list

C-x C-→
C-x C-←

   跳至前後一個 buffer

C-x C-q
   toggle buffer read only

M-% string RET new-string RET
   Replace string with new-string

C-M-% gregexp RET new-string RET
   Replace some matches for regexp with newstring.

C-h a [RET] apropos-key
   可以用某些 key word 找出命令。例如讓 apropos-key`window` 就會找出含有 `window` 字串的命令。

C-x {
  buffer window 水平變大

C-x }
   buffer window 水平變小

C-x z
   重複上一個指令動作,緊接著直接按 z 就可再重複

C-h i
   可以在 emacs 裡面看 info pages, 進去後,按 [m]emacs [RET] 可以跳進 emacs info 閱讀。(其實就是在emacs里邊閱讀 info pages)
例如:在
(emacs)Top:: > *Note Keyboard Macros:: > Save Keyboard Macro 一節,
可以找到有關設定 macro 的方法。

M-n 餵給命令所需參數
   以 C-v (往上捲動,內容往下)這命令為例,內定的參數是捲動一整頁,若給它前面加參數1,像這樣:
     M-1 C-v
 就只會往上捲一行了。參數也可以是負數喔!例如這樣按:
    按住 "Alt" 然後按 "-" "1" 然後放開 "Alt" 就是輸入參數 -1 的意思,mini-buffer 表示為
     M- M-1
  用在剛剛的命令就成了 :M- M-1 C-v "往下捲動一行" 了。
  OK, 亂玩一下,恰好有個命令是往下捲動一頁 M-v
 所以 M- M-1 C-v 效果等於 M-1 M-v,都會往下捲動一行
 
另外 C-u 是輸入參數 4 的意思
 C-u C-v 往上捲 4 行,C-u C-u C-v 表示會捲 16 行喔

C-s C-w ... C-s
  尋找 String, 中間的 C-w 可以從游標處往後選字,直到你要尋找的字整個進來為止,接著按 C-s 就可以望下開始找了!超好用!

M-x set-buffer-file-coding-system RET unix
or
C-x  <RET>  f  unix  <RET>
    把 Dos 文字格式(\r\n)轉為 Unix文字格式(\n)

M-x delete-trailing-whitespace <RET>
    移除多餘的行尾空白,符合 Linux Coding Style

下面是我的 .emacs 內容:

; auto complete
(add-to-list 'load-path "~/.emacs.d/")
(require 'auto-complete-config)
(add-to-list 'ac-dictionary-directories "~/.emacs.d/ac-dict")
(ac-config-default)

; edit properties
(modify-frame-parameters nil '((wait-for-wm . nil)))
(setq scroll-margin 0 scroll-conservatively 10000 )
(global-font-lock-mode "t")
;(require 'hl-line)
;(global-hl-line-mode t)
(transient-mark-mode t)
(setq ring-bell-function 'ignore)
;(require 'paren)
;(show-paren-mode 1)
(setq c-default-style "linux")
;(setq c-basic-offset 4)

(which-function-mode "t")

(require 'color-theme)
(color-theme-initialize)
(color-theme-hober)

(load-file "/usr/share/emacs/site-lisp/xcscope.el")
(require 'xcscope)

(setq cscope-do-not-update-database "t")
(setq cscope-set-initial-directory "./")
(autoload 'gtags-mode "gtags" "" t)

(setq ecb-tip-of-the-day nil)

; key bindings
;bind C-> to 'M-x enlarge-window'
(global-set-key (kbd "C-.") (quote enlarge-window))
;bind C-< to 'M-x shrink-window'
(global-set-key (kbd "C-,") (quote shrink-window))
;set <M-f11> as 'scroll-other-window-up-one-line'
(fset 'scroll-other-window-up-one-line "\2551\226")
;set <f11> as 'scroll-other-window-down-one-line'
(fset 'scroll-other-window-down-one-line "\261\226")

;(global-set-key [f5] 'cscope-find-this-file)
;(global-set-key [f6] 'cscope-find-this-symbol)
;(global-set-key [f7] 'cscope-pop-mark)
;(global-set-key [f8] 'cscope-find-global-definition)
;(global-set-key [f9] 'cscope-find-global-definition-no-prompting)
;(global-set-key [M-up] 'cscope-prev-symbol)'gtags-find-rtag)
;(global-set-key [M-down] 'cscope-next-symbol)

(global-set-key "\C-cgA" 'gtags-visit-rootdir)
(global-set-key "\C-cgr" 'gtags-find-rtag)
(global-set-key "\C-cgs" 'gtags-find-symbol)

(global-set-key [f5] 'gtags-find-file)
(global-set-key [f6] 'gtags-find-with-grep)
(global-set-key [f7] 'gtags-pop-stack)
(global-set-key [f8] 'gtags-find-tag)
(global-set-key [f9] 'gtags-find-tag-from-here)


(global-set-key [M-f11] 'scroll-other-window-up-one-line)
(global-set-key [f11] 'scroll-other-window-down-one-line)
(global-set-key [C-f12] 'ecb-activate)
(global-set-key [f12] 'ecb-toggle-ecb-windows)
(global-set-key [C-f1] 'color-theme-select)

(setq c-mode-hook '(lambda () (gtags-mode 1) (auto-complete-mode 1)))

(let ((frame '((left . 0)
           (top . 0)
           (width . 110)
           (height . 28)
           (tool-bar-lines . nil)
           (vertical-scroll-bars . nil)
           (background-color . "black")
           (foreground-color . "white")         
)))
  (setq default-frame-alist frame)
  (setq initial-frame-alist frame)
)

;(defun my-font-face ()
;  (custom-set-faces
;   '(default ((t (:inherit nil :stipple nil :background "black" :foreground "white" :inverse-video nil :box nil :strike-through nil :overline nil :underline nil :slant normal :weight normal :height 138 :width normal :foundry "unknown" :family "DejaVu Sans Mono"))))
;   '(ecb-tag-header-face ((t (:background "midnight blue"))))
;   '(ecb-default-highlight-face ((((class color) (background dark)) (:background "#28FF46FF66FF"))))
;))

(custom-set-variables
  ;; custom-set-variables was added by Custom.
  ;; If you edit it by hand, you could mess it up, so be careful.
  ;; Your init file should contain only one such instance.
  ;; If there is more than one, they won't work right.
 '(display-time-mode t)
 '(ecb-options-version "2.32")
 '(show-paren-mode t))


#