What have you found for these years?

2011-11-19

深入淺出 hello world (llvm toolchain?)

updated 2011-11-19 03:18
對了,我是看到這個才忽然想弄這個的
JVM JIT for Dummies
(What the JVM Does With Your Bytecode When You're Not Looking)


*

第一個當然是想到 jserv 的:

「深入淺出 Hello World Part I/II (台北場次)」簡報上線
演講:深入淺出Hello World (完結篇)

完全沒有感到居然已經這麼多年了啊..? 總之當初看到,大概都只粗略翻過去而已,
因為從來沒碰過這麼底層,所以很多東西看不懂,興趣也沒有說非常多。不過人
總是會想換口味,昨天很發神經地就想瞧瞧一個程式到底是怎麼運作的?

於是我寫了一個 hello world, 利用 clang compile 成 llvm bitcode:

clang -emit-llvm main.c -c -o main.bc
可以用 lli 跑跑看:
lli main.bc
接著瞧瞧相對應的 llvm assembly 長什麼樣子?
llvm-dis main.bc -o main.ll
其實我原本是直接叫 clang 輸出 llvm assembly:
clang -emit-llvm main.c -S -o main.ll
lli 也同樣可以執行 llvm assembly:
lli main.ll
可以再用 llvm-as 把 llvm assembly 編成 llvm bitcode:
llvm-as main.ll -o main.bc
接著用 llc 把 llvm bitcode 編成真正的 assembly:
llc main.bc -o main.s
再用 as 把 assembly 編成 object file:
as main.s -o main.o
最後一步則是靠 ld 把執行檔做出來... 我想知道的地方大概是從這邊開始

直接 ld main.o 很自然會找不到 printf (_printf) 的定義,畢竟這是 libc 的東西。
在 mac 上,大概就這樣 link libc:
ld main.o /usr/lib/libc.dylib
於是會吐出這個錯誤訊息:
ld: entry point (start) undefined.  Usually in crt1.o for inferred architecture x86_64
我想這大概是程式的進入點吧,於是我先試這個:
ld main.o /usr/lib/libc.dylib -e main
不過 main 沒有被定義。我不知道為什麼要叫 _main:
ld main.o /usr/lib/libc.dylib -e _main
這樣就可以了。試著執行看看:
./a.out
跑是跑得起來,有印出字,但是程式要離開時就死了:
fish: Job 1, './a.out ' terminated by signal SIGSEGV (Address boundary error)
我猜是直接去執行 main 的話,有些 OS 需要的程序就被跳過了,
因此會當掉。所以大概是要去找剛剛那個 crt1.o
ld main.o /usr/lib/libc.dylib /usr/lib/crt1.o
這樣確實就沒有任何錯誤了,運作都很正常。
crt1 可能是 c runtime 的意思吧?那個 1 就不知道了。

不過同樣的事情,在 arch linux 上就沒那麼順利了......
一樣先從最基本的開始:
ld main.o
會找不到 printf (這次沒有底線了!), 符合預期
ld main.o /usr/lib/libc.so
會吐出警告:
ld: warning: cannot find entry symbol _start; defaulting to 0000000000400270
大概是假設 _start 會在某個位置吧。這個 _start 應該跟 mac 的
entry point (start) 是差不多的意思。所以我應該去找 crt1.o
ld main.o /usr/lib/libc.so /usr/lib/crt1.o
這下可好了,還有東西沒有 link 到...
/usr/lib/crt1.o: In function `_start':
(.text+0x12): undefined reference to `__libc_csu_fini'
/usr/lib/crt1.o: In function `_start':
(.text+0x19): undefined reference to `__libc_csu_init'
我找很久,用 grep /usr/lib 後找到很多可疑的檔案,但還是不知道怎麼搞...
只好觀察:
gcc -v main.c
裡面用了 collect2 而不是 ld? 我不知道有什麼差...
總之就抄他要 link 哪些東西吧?中間過程跳過,反正我搞很久...... 最後:
ld main.o /usr/lib/libc.so /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtn.o /usr/lib/libc_nonshared.a
這也實在是太多東西,太不方便了吧 :( 總之這樣 ld 就不會抱怨了,但執行起來...
fish: Failed to execute process './a.out'. Reason:
fish: The file './a.out' or a script or ELF interpreter does not exist, or a
shared library needed for file or interpreter cannot be found.
我就卡在這不知道要怎麼繼續下去了。搞了好幾個小時(像神經病一樣),
還是看不出來怎麼做可以讓他的訊息有點變化。更可怕的是,如果用:
ldd a.out
還會吐出莫名其妙的訊息:
/usr/bin/ldd: line 118: ./a.out: No such file or directory
感覺是 bash 的問題啊!!!
bash -c ./a.out
會吐出:
bash: ./a.out: No such file or directory
但是 fish 都好好的沒問題。bash 的版本是 4.2.10

程式在這 llvm/build.sh + llvm/main.c

5 retries:

jserv said...

可以用 readelf -a 去檢驗剛編譯連結產生的 'a.out' 檔案,很可能是 ELF interpreter (如 ld-linux.so.1) 不存在

LCamel said...

$ clang -emit-llvm a.c -S -o - | llc - -o - | as - -o a.o
$ ld a.o /usr/lib/libc.so /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtn.o /usr/lib/libc_nonshared.a
$ ./a.out
bash: ./a.out: 沒有此一檔案或目錄
$ ld a.o -dynamic-linker /lib/ld-linux.so.2 /usr/lib/libc.so /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtn.o /usr/lib/libc_nonshared.a
$ ./a.out
hello
$

Lin Jen-Shin (godfat) said...

感謝兩位!!!
都忘記還有 readelf 可以用了,之前才用這個寫過 script 抓出需要的 .so 檔

用 readelf -a 去看之後,注意到裡面有這行:
[Requesting program interpreter: /lib/ld64.so.1]
這個檔案確實不存在,做個 symbolic link 把 /lib/ld-linux-x86-64.so.2
指過去之後,程式就能正常運作了!

不過其實我是先試出要加這段:
-dynamic-linker /lib/ld-linux-x86-64.so.2
程式就可以運作,再去看 gcc -v main.c 確實也是有這段,
只是我忽略掉了... 接著看了一下 -dynamic-linker 是什麼意思,
才猜出大概是什麼情況。不知道 ELF interpreter 是什麼,
正在看這頁 http://nixos.org/patchelf.html

感覺 mac (darwin) 的底層運作跟 linux 真的差很多.... @@

Andrew Liu said...

那個 bash: ./a.out: No such file or directory 是在 64bit 系統上面跑 32bit 程式缺少 32 bit library 的錯誤訊息...
之前第一次碰到也是以為是 bash 有問題

Lin Jen-Shin (godfat) said...

可是我在 64bit 系統上哪裡會 compile 出 32bit 的東西啊?

Post a Comment

All texts are licensed under CC Attribution 3.0