靈修分享:亞略巴古的演講

亞略巴古的演說是使徒行傳一篇非常著名的講道。基督徒往往用這段經文來說明上帝的屬性,同時也會參考這段經文作為和外邦人互動的一個參考方式。最近因為團契查經查到這一段,重新思想後看到了以前沒有考慮過的面向,在這邊記錄一下。

徒17:16-34保羅在雅典等候他們的時候,看見滿城都是偶像,就心裏着急;於是在會堂裏與猶太人和虔敬的人,並每日在市上所遇見的人,辯論。還有伊壁鳩魯和斯多亞兩門的學士,與他爭論。有的說:「這胡言亂語的要說甚麼?」有的說:「他似乎是傳說外邦鬼神的。」這話是因保羅傳講耶穌與復活的道。他們就把他帶到亞略‧巴古,說:「你所講的這新道,我們也可以知道嗎?因為你有些奇怪的事傳到我們耳中,我們願意知道這些事是甚麼意思。」(雅典人和住在那裏的客人都不顧別的事,只將新聞說說聽聽。)
保羅站在亞略‧巴古當中,說:「眾位雅典人哪,我看你們凡事很敬畏鬼神。我遊行的時候,觀看你們所敬拜的,遇見一座壇,上面寫着『未識之神』。你們所不認識而敬拜的,我現在告訴你們。創造宇宙和其中萬物的神,既是天地的主,就不住人手所造的殿,也不用人手服事,好像缺少甚麼;自己倒將生命、氣息、萬物,賜給萬人。他從一本本:有古卷是血脈造出萬族的人,住在全地上,並且預先定準他們的年限和所住的疆界,要叫他們尋求神,或者可以揣摩而得,其實他離我們各人不遠;我們生活、動作、存留,都在乎他。就如你們作詩的,有人說:『我們也是他所生的。』我們既是神所生的,就不當以為神的神性像人用手藝、心思所雕刻的金、銀、石。世人蒙昧無知的時候,神並不監察,如今卻吩咐各處的人都要悔改。因為他已經定了日子,要藉着他所設立的人按公義審判天下,並且叫他從死裏復活,給萬人作可信的憑據。」
眾人聽見從死裏復活的話,就有譏誚他的;又有人說:「我們再聽你講這個吧!」於是保羅從他們當中出去了。 但有幾個人貼近他,信了主,其中有亞略‧巴古的官丟尼修,並一個婦人,名叫大馬哩,還有別人一同信從。

對基督徒來說,保羅這篇講道講的真好,不但講出了上帝的超越性(像是不住人手所造的殿、也不是金銀石的彫刻),也帶出了耶穌基督復活的大能。但最近我才注意到眾人的反應:「眾人聽見從死裏復活的話,就有譏誚他的;又有人說:「我們再聽你講這個吧!」」不曉得你看到這一段有什麼感想?可能我是玻璃心吧,如果我是保羅,我大概會非常非常的難過,看起來似乎沒有人理會這個福音、這篇講道。這篇被基督…

GCC 詳細編譯步驟

使用 GCC 來編譯程式看起來相當容易,如下面的例子:

[neokent@FEDORA Projects]$ gcc hello.c -o hello
[neokent@FEDORA Projects]$ ./hello
Hello World!

但 GCC 到底做了些什麼事呢?多加一個「-v」的參數以後來試試看(下面是實際跑出來的例子,但是為了美觀起見,稍微修改了一下排版)

[neokent@FEDORA Projects]$ gcc -v hello.c -o hello
使用內建 specs。

目的:i386-redhat-linux

配置為:../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --enable-plugin --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --with-cpu=generic --host=i386-redhat-linux

執行緒模型:posix

gcc 版本 4.1.2 20070925 (Red Hat 4.1.2-33)

/usr/libexec/gcc/i386-redhat-linux/4.1.2/cc1 -quiet -v hello.c -quiet -dumpbase hello.c -mtune=generic -auxbase hello -version -o /tmp/ccdKC85q.s

忽略不存在的目錄「/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../i386-redhat-linux/include」

#include "..." 搜尋從這裡開始:
#include <...> 搜尋從這裡開始:
/usr/local/include
/usr/lib/gcc/i386-redhat-linux/4.1.2/include
/usr/include
搜尋清單結束。

GNU C 版本 4.1.2 20070925 (Red Hat 4.1.2-33) (i386-redhat-linux)
由 GNU C 版本 4.1.2 20070925 (Red Hat 4.1.2-33) 編譯。

GGC 準則:--param ggc-min-expand=64 --param ggc-min-heapsize=64394
Compiler executable checksum: ab322ce5b87a7c6c23d60970ec7b7b31
as -V -Qy -o /tmp/ccepcDPL.o /tmp/ccdKC85q.s
GNU assembler version 2.17.50.0.18 (i386-redhat-linux) using BFD version version 2.17.50.0.18-1 20070731

/usr/libexec/gcc/i386-redhat-linux/4.1.2/collect2 --eh-frame-hdr --build-id -m elf_i386 --hash-style=gnu -dynamic-linker /lib/ld-linux.so.2 -o hello /usr/lib/gcc/i386-redhat-linux/4.1.2/../../../crt1.o /usr/lib/gcc/i386-redhat-linux/4.1.2/../../../crti.o /usr/lib/gcc/i386-redhat-linux/4.1.2/crtbegin.o -L/usr/lib/gcc/i386-redhat-linux/4.1.2 -L/usr/lib/gcc/i386-redhat-linux/4.1.2 -L/usr/lib/gcc/i386-redhat-linux/4.1.2/../../.. /tmp/ccepcDPL.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i386-redhat-linux/4.1.2/crtend.o /usr/lib/gcc/i386-redhat-linux/4.1.2/../../../crtn.o

稍微整理一下,去掉一些參數設定的部分,可以將上述的步驟簡化成下面幾步:
  1. /usr/libexec/gcc/i386-redhat-linux/4.1.2/cc1 hello.c -o /tmp/ccdKC85q.s
  2. as -o /tmp/ccepcDPL.o /tmp/ccdKC85q.s
  3. ld -o hello -o /tmp/ccepcDPL.o
以上三個命令分別對應於編譯步驟中的預處理+編譯組譯連結。預處理和編譯還是放在了一個命令(cc1)中進行的,可以把它再次拆分為以下兩步:
  1. cpp -o hello.i hello.c
  2. cc1 hello.i -o /tmp/ccdKC85q.s
為了詳細分析每一個編譯過程,我們寫一個 Makefile 來進行細部分解:

all:hello

hello:hello.o
ld -dynamic-linker \
/lib/ld-linux.so.2 -o hello /usr/lib/crt1.o \
/usr/lib/crti.o /usr/lib/gcc/i386-redhat-linux/4.1.2/crtbegin.o \
hello.o \
-lc /usr/lib/gcc/i386-redhat-linux/4.1.2/crtend.o /usr/lib/crtn.o

hello.o:hello.s
as -o hello.o hello.s

hello.s:hello.i
/usr/libexec/gcc/i386-redhat-linux/4.1.2/cc1 -o hello.s hello.c

hello.i:hello.c
cpp -o hello.i hello.c

clean:
rm -rf hello.i hello.s hello.o hello

可以很容易的看見,在 make 完之後,產生了 hello.i hello.s hello.o hello,以下就一個一個來看。

1. 預處理

hello.i

# 1 "hello.c"
# 1 ""
# 1 ""
...
__extension__ typedef signed long long int __int64_t;
__extension__ typedef unsigned long long int __uint64_t;
...
extern int remove (__const char *__filename) __attribute__ ((__nothrow__));
extern int rename (__const char *__old, __const char *__new) __attribute__ ((__nothrow__));
...
int main()
{
printf( "Hello World!\n" );
return;
}

從 hello.i 裡面可以看出,預處理器把所有要包含(include)的文件(包括遞歸包含的文件)的內容都添加到了C的原始碼中,然後把其輸出到輸出文件。要注意的是,就算是巨集(MACRO)也會完全被展開。
PS: 由於 hello.i 的內容實在太多了,反正所有的工具都已經列上去了,要看完整內容的話,就再跑一次吧。

2. 編譯

hello.s

.file "hello.c"
.section .rodata
.LC0:
.string "Hello World!"
.text
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $4, %esp
movl $.LC0, (%esp)
call puts
addl $4, %esp
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (GNU) 4.1.2 20070925 (Red Hat 4.1.2-33)"
.section .note.GNU-stack,"",@progbits

非常明顯,這是組合語言。這個檔案比預處理後的C檔案小了很多,去除了很多不必要的東西,比如說沒用到的類型宣告和函數宣告等。

3. 組譯

將第二步輸出的組語碼翻譯成符合一定格式的機器代碼,在Linux上一般表現為ELF目標文件。

[neokent@FEDORA 20080109_CompileProcedure]$ file hello.o
hello.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped

4. 連結

將上步生成的物件和系統函式庫的物件連結起來,最終生成了可以在特定平台運行的可執行文件。為什麼還要連接系統函式庫中的某些物件(crt1.o, crti.o等)呢?這些物件都是用來初始化或者回收 C 運行時環境的,比如說堆疊記憶體分配上下文環境的初始化等,實際上 crt 也正是C RunTime的縮寫。這也暗示了另外一點:程式並不是從main函數開始執行的,而是從 crt 中的某個入口開始的,在Linux上此入口是 _start。
PS: crt1.o and crti.o provide initialization code and crtn.o does cleanup.

如果要改成使用靜態連結的話,可以將 Makefile 改成下面這個樣子:

hello_s:hello.o
ld -m elf_i386 -static \
-o hello_s /usr/lib/crt1.o \
/usr/lib/crti.o /usr/lib/gcc/i386-redhat-linux/4.1.2/crtbeginT.o \
-L/usr/lib/gcc/i386-redhat-linux/4.1.2/ -L/usr/lib \
hello.o --start-group -lgcc -lgcc_eh \
-lc --end-group /usr/lib/gcc/i386-redhat-linux/4.1.2/crtend.o /usr/lib/crtn.o

我們來比較動態連結執行檔和靜態連結執行檔:

-rwxrwxr-x 1 neokent neokent 4667 2008-01-10 00:04 hello
-rwxrwxr-x 1 neokent neokent 545648 2008-01-10 00:04 hello_s

strip 過後

-rwxrwxr-x 1 neokent neokent 3024 2008-01-10 00:09 hello
-rwxrwxr-x 1 neokent neokent 490276 2008-01-10 00:08 hello_s

很明顯,靜態連結大的多了~

留言

這個網誌中的熱門文章

如何將Linux打造成OpenFlow Switch:Openvswitch

我弟家的新居感恩禮拜分享:善頌善禱

如何利用 Wireshark 來監聽 IEEE 802.11 的管理封包