gcc的幾個參數筆記

整理一下,反正一定會忘記,就放在這邊,也許以後會再新增

  • gcc --save-temp hello.c
    • gcc中間產生的檔案通通存起來
  • gcc -dumpspecs
    • 印spec,不過我完全看不懂
  • gcc -dumpmachine
    • 印出平台

其他關於巨集部份請參考這邊這邊,很臭很長,抱歉。

ssh登入遠端主機自動設定X Display 變數

前言


因為用X用習慣了,常常會連線到另外遠端電腦,然後透過X把該台電腦軟體畫面輸出到本機。這時候的標準流程會是

  • ssh登入到遠端
  • export DISPLAY=你的本機IP:0.0

每次都要打這些,還要查自己本機IP很煩。所以整理了兩光的script提供以後剪下貼上。

另外有人會問為什麼不直接用ssh -X連過去?原因是因為我習慣設完display後,開一個遠端終端機丟在背景然後登出目前的ssh session,這樣的操作在ssh -X下從來沒有成功過。我了遠端終端機後就算放在背景,一樣目前終端機會卡住。如果暴力把目前終端機關掉,那麼所有X的session都GG。實在懶得找原因,反正這是個workaround的世界(煙)。

背景說明


上面操作中,本機需要設定

  • X allow TCP
  • xhost 加入允許連進來的IP

這兩個部份請自行估狗。

另外本機和遠端主機用相同的Distribution資訊如下,測試端的遠端主機是一台沒有GUI的Ubuntu server。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 14.04.2 LTS
Release:    14.04
Codename:   trusty

使用方式


把下面的描述放在~/.profile最後面,下次ssh登入後就可以直接開啟X應用程式把畫面丟回本機了。

# Let's export DISPLAY if we are from ssh remote

if [ "$TERM" = "xterm" -a -n "$SSH_CLIENT" ] ; then
    REMOTE_IP=$(echo $SSH_CLIENT | cut -f 1 -d " ")
    if [ -z "$REMOTE_IP" ] ; then
        exit 0
    fi

    export DISPLAY=$REMOTE_IP:0.0
fi

Linux中誰來呼叫C語言中的main?

記得很久以前聽說在Linux執行檔案時,真正的起始點並不是main,加上之前有看到單純ld會幫你偷偷link一些沒看過的object檔案,所以這次就來看到底真相為何?

測試環境

因為很假掰想要順便接觸一下ARM的組語,所以這次測試就使用Qemu跑ARM的Debian。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 8.0 (jessie)
Release:    8.0
Codename:   jessie

$ file /bin/ls
/bin/ls: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=571db48d9c9e4625b7da206e748e41c237f2b202, stripped

測試原始碼,一樣是大家熟悉的Hellow world

hello1.c
#include <stdio.h>

int main()
{
    printf("Hello World\n");

    return 0;
}

不知道各位還記得前面有提過,執行檔中有.text的section。要執行的機械碼會放在這邊。我們先來看看hello1執行檔會從那邊開始?

$ readelf -h hello1
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0x102f0
  Start of program headers:          52 (bytes into file)
...
Section header string table index: 33

從readelf可以看到起始點為0x102f0,那麼0x102f0是在那邊呢?我們再去看symbol table可以看到很巧的就是.text的起始點。

$ objdump -t hello1

hello1:     file format elf32-littlearm

SYMBOL TABLE:
00010134 l    d  .interp    00000000              .interp
...
000102f0 l    d  .text  00000000              .text

好了,那麼.text這邊起始的程式是什麼?

Disassembly of section .text:

000102f0 <_start>:
   102f0:       e3a0b000        mov     fp, #0
   102f4:       e3a0e000        mov     lr, #0
   102f8:       e49d1004        pop     {r1}            ; (ldr r1, [sp], #4)
   102fc:       e1a0200d        mov     r2, sp
   10300:       e52d2004        push    {r2}            ; (str r2, [sp, #-4]!)
   10304:       e52d0004        push    {r0}            ; (str r0, [sp, #-4]!)
   10308:       e59fc010        ldr     ip, [pc, #16]   ; 10320 <_start+0x30>
   1030c:       e52dc004        push    {ip}            ; (str ip, [sp, #-4]!)
   10310:       e59f000c        ldr     r0, [pc, #12]   ; 10324 <_start+0x34>
   10314:       e59f300c        ldr     r3, [pc, #12]   ; 10328 <_start+0x38>
   10318:       ebffffeb        bl      102cc <__libc_start_main@plt>
   1031c:       ebfffff0        bl      102e4 <abort@plt>
   10320:       000104b4        .word   0x000104b4
   10324:       00010420        .word   0x00010420
   10328:       00010448        .word   0x00010448

很有趣,沒看到main(),反而看到_start。到底是_start是什麼呢?還記得Linker script嗎?裏面有一個ENTRY指令,可以指定程式從那邊開始跑,先來看一下預設的ENTRY是不是也是_start?

$ ld --verbose | grep ENTRY
ENTRY(_start)

目前我們只知道執行檔起始點是_start,而不是main,那顯然有人幫你把執行檔加碼,以至於你的執行檔出現了_start。最偷懶的方式就是去找binary看看是不是有這樣的symbol。

user@host:/usr/lib$ find -name "*.[ao]" -exec nm -A {} \;  2> /dev/null | grep " _start$"
./arm-linux-gnueabi/crt1.o:00000000 T _start
./arm-linux-gnueabi/gcrt1.o:00000000 T _start
./arm-linux-gnueabi/Scrt1.o:00000000 T _start
./debug/usr/lib/arm-linux-gnueabi/crt1.o:00000000 T _start
./debug/usr/lib/arm-linux-gnueabi/gcrt1.o:00000000 T _start
./debug/usr/lib/arm-linux-gnueabi/Scrt1.o:00000000 T _start

OK,的確有object檔案裡面有_start,我們再來確認編譯的時候會不會link這些檔案。

$ gcc -v hello1.c 
Using built-in specs.
COLLECT_GCC=gcc
...
COLLECT_GCC_OPTIONS='-v' '-march=armv4t' '-mfloat-abi=soft' 
...
-X --hash-style=gnu -m armelf_linux_eabi
...
/usr/lib/gcc/arm-linux-gnueabi/4.9/../../../arm-linux-gnueabi/crt1.o 
...

_start會呼叫外部函數__libc_start_main,我們透過LD_DEBUG來看一下。

$ LD_DEBUG=all ./hello1 2>&1 |grep __libc_start_main
       890: symbol=__libc_start_main;  lookup in file=./hello1 [0]
       890: symbol=__libc_start_main;  lookup in file=/lib/arm-linux-gnueabi/libc.so.6 [0]
       890: binding file ./hello1 [0] to /lib/arm-linux-gnueabi/libc.so.6 [0]: normal symbol `__libc_start_main' [GLIBC_2.4]

可以看到,在./hello1中有去找__libc_start_main,最後去libc.so.6找,並且找出libc.so.6__libc_start_main的位址(即binding)。而__libc_start_mainprototype如下

int __libc_start_main(int (*main) (int, char **, char **), int argc, char ** ubp_av, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void (*stack_end));

看到有趣的東西嘛?我有看到

  • main函數當作function pointer傳入
  • main函數的參數
  • 其他不知道三小的function pointer
    • init
    • fini
    • rtld_fini

從這邊我可以猜測這個函數就是呼叫一堆callback function,這些callback function就是上面列的死人骨頭。

手冊的說明可以看到__libc_start_main()是用來執行環境的初始化、呼叫main函數並且傳遞參數,當main函數結束後處理回傳值。手冊提到的範例詳細行為有

  • 檢查權限,確保安全性
  • thread subsystem初始化 (我可不知道什麼thread subsystem唷)
  • rtld_fini註冊release callback function,當shared object結束時使用該callback釋放資源
  • 呼叫init callback function
  • 呼叫main callback function並且帶入參數
  • 當main callback function結束後,將回傳值作為參數呼叫exit

我們再回頭看看_start的組合語言:

000102f0 <_start>:
   102f0:       e3a0b000        mov     fp, #0
   102f4:       e3a0e000        mov     lr, #0
   102f8:       e49d1004        pop     {r1}            ; (ldr r1, [sp], #4)
   102fc:       e1a0200d        mov     r2, sp
   10300:       e52d2004        push    {r2}            ; (str r2, [sp, #-4]!)
   10304:       e52d0004        push    {r0}            ; (str r0, [sp, #-4]!)
   10308:       e59fc010        ldr     ip, [pc, #16]   ; 10320 <_start+0x30>
   1030c:       e52dc004        push    {ip}            ; (str ip, [sp, #-4]!)
   10310:       e59f000c        ldr     r0, [pc, #12]   ; 10324 <_start+0x34>
   10314:       e59f300c        ldr     r3, [pc, #12]   ; 10328 <_start+0x38>
   10318:       ebffffeb        bl      102cc <__libc_start_main@plt>
   1031c:       ebfffff0        bl      102e4 <abort@plt>
   10320:       000104b4        .word   0x000104b4
   10324:       00010420        .word   0x00010420
   10328:       00010448        .word   0x00010448

有趣的地方是這3個位址

   10320:       000104b4        .word   0x000104b4
   10324:       00010420        .word   0x00010420
   10328:       00010448        .word   0x00010448

這邊可以看到這3個位址分別是

  • 10320: 000104b4 .word 0x000104b4
    • __libc_csu_fini
  • 10324: 00010420 .word 0x00010420
    • main
  • 10328: 00010448 .word 0x00010448
    • __libc_csu_init

也就是說,main__libc_csu_init分別當作第一和第四參數傳給__libc_start_main,而__libc_csu_fini則被丟到stack,一樣傳給__libc_start_main了。

結論

Linux執行程式的起始點並不是main,而是glibc binary中crt1.o準備的_start。這個start主要將你的main,還有一些hook函數丟給__libc_start_main,接下來libc的__libc_start_main樵好事情後才真正執行你的main,並且還要在main結束後清理戰場。

延伸閱讀

參考資料

完整反組譯程式碼

Hello.dis
$ cat hello1.dis 

hello1:     file format elf32-littlearm


Disassembly of section .init:

0001029c <_init>:
   1029c:   e92d4008    push    {r3, lr}
   102a0:   eb000021    bl  1032c <call_weak_fn>
   102a4:   e8bd4008    pop {r3, lr}
   102a8:   e12fff1e    bx  lr

Disassembly of section .plt:

000102ac <puts@plt-0x14>:
   102ac:   e52de004    push    {lr}        ; (str lr, [sp, #-4]!)
   102b0:   e59fe004    ldr lr, [pc, #4]    ; 102bc <_init+0x20>
   102b4:   e08fe00e    add lr, pc, lr
   102b8:   e5bef008    ldr pc, [lr, #8]!
   102bc:   00010318    .word   0x00010318

000102c0 <puts@plt>:
   102c0:   e28fc600    add ip, pc, #0, 12
   102c4:   e28cca10    add ip, ip, #16, 20 ; 0x10000
   102c8:   e5bcf318    ldr pc, [ip, #792]! ; 0x318

000102cc <__libc_start_main@plt>:
   102cc:   e28fc600    add ip, pc, #0, 12
   102d0:   e28cca10    add ip, ip, #16, 20 ; 0x10000
   102d4:   e5bcf310    ldr pc, [ip, #784]! ; 0x310

000102d8 <__gmon_start__@plt>:
   102d8:   e28fc600    add ip, pc, #0, 12
   102dc:   e28cca10    add ip, ip, #16, 20 ; 0x10000
   102e0:   e5bcf308    ldr pc, [ip, #776]! ; 0x308

000102e4 <abort@plt>:
   102e4:   e28fc600    add ip, pc, #0, 12
   102e8:   e28cca10    add ip, ip, #16, 20 ; 0x10000
   102ec:   e5bcf300    ldr pc, [ip, #768]! ; 0x300

Disassembly of section .text:

000102f0 <_start>:
   102f0:   e3a0b000    mov fp, #0
   102f4:   e3a0e000    mov lr, #0
   102f8:   e49d1004    pop {r1}        ; (ldr r1, [sp], #4)
   102fc:   e1a0200d    mov r2, sp
   10300:   e52d2004    push    {r2}        ; (str r2, [sp, #-4]!)
   10304:   e52d0004    push    {r0}        ; (str r0, [sp, #-4]!)
   10308:   e59fc010    ldr ip, [pc, #16]   ; 10320 <_start+0x30>
   1030c:   e52dc004    push    {ip}        ; (str ip, [sp, #-4]!)
   10310:   e59f000c    ldr r0, [pc, #12]   ; 10324 <_start+0x34>
   10314:   e59f300c    ldr r3, [pc, #12]   ; 10328 <_start+0x38>
   10318:   ebffffeb    bl  102cc <__libc_start_main@plt>
   1031c:   ebfffff0    bl  102e4 <abort@plt>
   10320:   000104b4    .word   0x000104b4
   10324:   00010420    .word   0x00010420
   10328:   00010448    .word   0x00010448

0001032c <call_weak_fn>:
   1032c:   e59f3014    ldr r3, [pc, #20]   ; 10348 <call_weak_fn+0x1c>
   10330:   e59f2014    ldr r2, [pc, #20]   ; 1034c <call_weak_fn+0x20>
   10334:   e08f3003    add r3, pc, r3
   10338:   e7932002    ldr r2, [r3, r2]
   1033c:   e3520000    cmp r2, #0
   10340:   012fff1e    bxeq    lr
   10344:   eaffffe3    b   102d8 <__gmon_start__@plt>
   10348:   00010298    .word   0x00010298
   1034c:   0000001c    .word   0x0000001c

00010350 <deregister_tm_clones>:
   10350:   e59f301c    ldr r3, [pc, #28]   ; 10374 <deregister_tm_clones+0x24>
   10354:   e59f001c    ldr r0, [pc, #28]   ; 10378 <deregister_tm_clones+0x28>
   10358:   e0603003    rsb r3, r0, r3
   1035c:   e3530006    cmp r3, #6
   10360:   912fff1e    bxls    lr
   10364:   e59f3010    ldr r3, [pc, #16]   ; 1037c <deregister_tm_clones+0x2c>
   10368:   e3530000    cmp r3, #0
   1036c:   012fff1e    bxeq    lr
   10370:   e12fff13    bx  r3
   10374:   000205ff    .word   0x000205ff
   10378:   000205fc    .word   0x000205fc
   1037c:   00000000    .word   0x00000000

00010380 <register_tm_clones>:
   10380:   e59f1024    ldr r1, [pc, #36]   ; 103ac <register_tm_clones+0x2c>
   10384:   e59f0024    ldr r0, [pc, #36]   ; 103b0 <register_tm_clones+0x30>
   10388:   e0601001    rsb r1, r0, r1
   1038c:   e1a01141    asr r1, r1, #2
   10390:   e0811fa1    add r1, r1, r1, lsr #31
   10394:   e1b010c1    asrs    r1, r1, #1
   10398:   012fff1e    bxeq    lr
   1039c:   e59f3010    ldr r3, [pc, #16]   ; 103b4 <register_tm_clones+0x34>
   103a0:   e3530000    cmp r3, #0
   103a4:   012fff1e    bxeq    lr
   103a8:   e12fff13    bx  r3
   103ac:   000205fc    .word   0x000205fc
   103b0:   000205fc    .word   0x000205fc
   103b4:   00000000    .word   0x00000000

000103b8 <__do_global_dtors_aux>:
   103b8:   e92d4010    push    {r4, lr}
   103bc:   e59f401c    ldr r4, [pc, #28]   ; 103e0 <__do_global_dtors_aux+0x28>
   103c0:   e5d43000    ldrb    r3, [r4]
   103c4:   e3530000    cmp r3, #0
   103c8:   1a000002    bne 103d8 <__do_global_dtors_aux+0x20>
   103cc:   ebffffdf    bl  10350 <deregister_tm_clones>
   103d0:   e3a03001    mov r3, #1
   103d4:   e5c43000    strb    r3, [r4]
   103d8:   e8bd4010    pop {r4, lr}
   103dc:   e12fff1e    bx  lr
   103e0:   000205fc    .word   0x000205fc

000103e4 <frame_dummy>:
   103e4:   e92d4008    push    {r3, lr}
   103e8:   e59f0028    ldr r0, [pc, #40]   ; 10418 <frame_dummy+0x34>
   103ec:   e5903000    ldr r3, [r0]
   103f0:   e3530000    cmp r3, #0
   103f4:   1a000001    bne 10400 <frame_dummy+0x1c>
   103f8:   e8bd4008    pop {r3, lr}
   103fc:   eaffffdf    b   10380 <register_tm_clones>
   10400:   e59f3014    ldr r3, [pc, #20]   ; 1041c <frame_dummy+0x38>
   10404:   e3530000    cmp r3, #0
   10408:   0afffffa    beq 103f8 <frame_dummy+0x14>
   1040c:   e1a0e00f    mov lr, pc
   10410:   e12fff13    bx  r3
   10414:   eafffff7    b   103f8 <frame_dummy+0x14>
   10418:   000204e8    .word   0x000204e8
   1041c:   00000000    .word   0x00000000

00010420 <main>:
   10420:   e92d4800    push    {fp, lr}
   10424:   e28db004    add fp, sp, #4
   10428:   e59f0014    ldr r0, [pc, #20]   ; 10444 <main+0x24>
   1042c:   ebffffa3    bl  102c0 <puts@plt>
   10430:   e3a03000    mov r3, #0
   10434:   e1a00003    mov r0, r3
   10438:   e24bd004    sub sp, fp, #4
   1043c:   e8bd4800    pop {fp, lr}
   10440:   e12fff1e    bx  lr
   10444:   000104c8    .word   0x000104c8

00010448 <__libc_csu_init>:
   10448:   e92d43f8    push    {r3, r4, r5, r6, r7, r8, r9, lr}
   1044c:   e59f6058    ldr r6, [pc, #88]   ; 104ac <__libc_csu_init+0x64>
   10450:   e59f5058    ldr r5, [pc, #88]   ; 104b0 <__libc_csu_init+0x68>
   10454:   e08f6006    add r6, pc, r6
   10458:   e08f5005    add r5, pc, r5
   1045c:   e0656006    rsb r6, r5, r6
   10460:   e1a07000    mov r7, r0
   10464:   e1a08001    mov r8, r1
   10468:   e1a09002    mov r9, r2
   1046c:   ebffff8a    bl  1029c <_init>
   10470:   e1b06146    asrs    r6, r6, #2
   10474:   0a00000a    beq 104a4 <__libc_csu_init+0x5c>
   10478:   e2455004    sub r5, r5, #4
   1047c:   e3a04000    mov r4, #0
   10480:   e2844001    add r4, r4, #1
   10484:   e5b53004    ldr r3, [r5, #4]!
   10488:   e1a00007    mov r0, r7
   1048c:   e1a01008    mov r1, r8
   10490:   e1a02009    mov r2, r9
   10494:   e1a0e00f    mov lr, pc
   10498:   e12fff13    bx  r3
   1049c:   e1540006    cmp r4, r6
   104a0:   1afffff6    bne 10480 <__libc_csu_init+0x38>
   104a4:   e8bd43f8    pop {r3, r4, r5, r6, r7, r8, r9, lr}
   104a8:   e12fff1e    bx  lr
   104ac:   00010088    .word   0x00010088
   104b0:   00010080    .word   0x00010080

000104b4 <__libc_csu_fini>:
   104b4:   e12fff1e    bx  lr

Disassembly of section .fini:

000104b8 <_fini>:
   104b8:   e92d4008    push    {r3, lr}
   104bc:   e8bd4008    pop {r3, lr}
   104c0:   e12fff1e    bx  lr

在X86 Linux下透過Qemu安裝ARM的Debian系統

安裝順序如下

測試環境

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 14.04.1 LTS
Release:    14.04
Codename:   trusty

安裝Qemu

$ sudo apt-get install qemu-system-arm

拉ARM安裝相關檔案

  • 首先先來看Qemu有支援那些ARM平台
    $ qemu-system-arm -machine help
    Supported machines are:
    versatileab          ARM Versatile/AB (ARM926EJ-S)
    versatilepb          ARM Versatile/PB (ARM926EJ-S)
    lm3s811evb           Stellaris LM3S811EVB
    ...
    

安裝ARM版本Debian需要

  • kernel
  • initrd
  • ISO 首先到提供Debian下載的網站中的debian/dists找一個你想安裝的版本,我選了7.8 (Wheezy)。 假設Debain下載網站叫host

先抓平台相關的kernel和initrd,路徑如下

http://host/debian/dists/Debian7.8/main/installer-armel/20130430/images/

下面有不同的ARM平台,還記得上面qemu-system-arm -machine help,請和這邊目錄下的比對,挑一個順眼的。我使用
versatile,所以就切到下面的目錄

http://host/debian/dists/Debian7.8/main/installer-armel/20130430/images/versatile/netboot/

把下面的兩個檔案拉下來

  • initrd.gz
  • vmlinuz-3.2.0-4-versatile

接下來在同樣的主機上,下載ISO檔。

http://host/debian-cd/7.8.0/armel

開始安裝

透過下面的指令安裝虛擬磁碟,請自行決定大小

$ qemu-img create debian.img 8G

然後叫qemu載入ARM kernel,initrd,以及ISO

$ qemu-system-arm -M versatileab -kernel ./vmlinuz-3.2.0-4-versatile -initrd ./initrd.gz -cdrom ./debian-7.8.0-armel-DVD-1.iso -hda debian.img -m 1024

這邊可以看到versatileab又出現了,請往上找一下這個字串吧。

抽出虛擬磁碟的kernel和initrd

最tricky的地方在這邊,理論上你要透過loopback裝置mount 虛擬磁碟,複製/boot就可以了。但是現實就是,因為磁碟機/root的partition有offset,所以直接mount程式無法辨認Filesystem所以無法mount。正確方式如下

$ sudo fdisk -l -u debian.img 

Disk debian.img: 8589 MB, 8589934592 bytes
255 heads, 63 sectors/track, 1044 cylinders, total 16777216 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000823a0

     Device Boot      Start         End      Blocks   Id  System
debian.img1            2048    15988735     7993344   83  Linux
debian.img2        15990782    16775167      392193    5  Extended
debian.img5        15990784    16775167      392192   82  Linux swap / Solaris

有兩個東西要注意

  • unit為512 bytes
  • root partition offset為2048個unit

所以正確的mount方式如下

$ sudo mount -o loop,offset=$((2048 * 512)) debian.img /mnt

接下來抽出就簡單了,請在剛才安裝的虛擬磁碟檔案同一個目錄操作。

$ mkdir boot
$ cp /mnt/boot/* boot/ -rv

載入安裝的系統

這邊就照表操課,我有指定localhost將port 2222 forward到Qemu的port 22,以便將來ssh進去

$ qemu-system-arm -M versatileab -kernel ./boot/vmlinuz-3.2.0-4-versatile -initrd ./boot/initrd.img-3.2.0-4-versatile -hda debian.img -m 1024 -append "root=/dev/sda1" -redir tcp:2222::22

驗收看看是不是真的可以連進去,並且裏面真的是ARM的binary?

$ ssh -p 2222 user@localhost
user@localhost's password: 
Linux debian 3.2.0-4-versatile #1 Debian 3.2.65-1+deb7u1 armv5tejl

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
You have mail.
Last login: Fri Feb  6 09:35:07 2015
user@debian:~$ file /bin/ls
/bin/ls: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, BuildID[sha1]=0x5bc97dbca9ac168932d898a5e2eaf68e8fde5e16, stripped

參考資料

談談autotools 的 build, target, 和host

先來個小故事,某天組裝工趁放假跑去看老房子。發現只有一間上面有動物的造型。因為年代久遠,只看得出來有四肢腳而已,而不知道是那種動物。好奇之餘問了店家,店家說他們承租,不知道那是什麼東西。後來一位長者經過,就順便請教他同一個問題,長者說他住這邊好幾年都不知道上面有這東西。最後遇到當地文史工作室才知道是當時主人的商標,一隻獅子。

我們常常用的東西,因為太習以為常,以至於發生視而不見的狀況屢見不鮮。但是惡魔就在細節裏面,而長時間地視而不見就會把自己對於這樣的事物感受性變低。

回到主體。組裝的時候,常常缺東缺西的。這時候就需要cross-compile一些套件頂一下,所以如果有人組裝過的話,應該會對下面的命令倍感親切。

$ ./configure --host=mipsel

如果組裝夠次數夠多,也會看到--target--build的參數似乎和這個有關。找時間稍微了解一下

對於--target目前手上的資料無法讓我完全理解,請注意!

根據Autoconf手冊說明,這些參數的預設值如下:

  • --build
    • 從config.guess中猜的
  • --host
    • 設成和--build相同
  • --target
    • 設成和--host相同

因為這樣的連動性,如果你要cross-compile,那麼下了--host後麻煩不要省掉--build,不然autotool會把build設成和host一樣。

Automake手冊裏面的定義

  • --build
    • 你build code,下xxx-gcc那台機器平台
  • --host
    • 產生的binary可以執行的平台
  • --target
    • 告訴gcc要產生什麼平台的機械碼
    • 一般來說會和--host一樣同樣的平台,根據上面的連動,你設了--host就可以省略這個選項
    • 特例是compile cross-compiler或是binutils,toolchain。這時候是指定build在host上,但是處理的對象是cross platform的機械碼

Autotool選擇gcc的方式如下

  • --host--build相同
    • 用你電腦上的gcc,來編譯autotool的套件。
  • --host--build不同
    • 你要自備cross-compiler
    • 如果有指定--target的為gcc的prefix
    • --host=xxx, --target=zzz,會使用zzz-gcc產生xxx平台的機械碼
    • 用該cross-compiler來編譯autotool的套件
    • 這時候要保證cross compiler可以吃的library也存在,常常會為了編一個套件cross compile一堆套件

看的有點混亂嘛?沒關係,autoconf 2.69手冊有提到,要cross-compile的話,唯一(if and only if)的條件就是指令--host指定和你現在的平台不同就好。

參考資料

如何trace bash script中的function

在組裝的過程中,常常會遇到用source去載入一個shell script,裏面通常是一組的環境變數。然而如果專案大到某個程度,這個script就會塞入很多複雜的function,例如某專案的build/envsetup.sh。這時候要去trace用肉眼去看實在是太過殘酷,最近為了這個問題想到了一招很簡單的方式,那就是:

$ bash -x

沒錯就這麼簡單,直接來看例子吧。首先我們有一個script裏面有function。

demo.sh
#!/bin/bash

function whatsoever()
{
    echo "My code works, I don't know why."
}

接下來就是剛才講的操作

$ bash -x
+ '[' -z '\s-\v\$ ' ']'
+ shopt -s checkwinsize
... # 中間發生超多事,有興趣自行研究。發現好玩的再跟我說。

$ cd /tmp
+ cd /tmp
$ . demo.sh
+ . demo.sh
$ whatsoever 
+ whatsoever
+ echo 'My code works, I don'\''t know why.'
My code works, I don't know why.

打完收工,謝謝收看。

補充

網友Scott Tasi大大說這個方式和下面的方式相同:

set -x

我就學天線寶寶再重複一下實驗

$ set -x
$ . demo.sh
+ . demo.sh
$ whatsoever 
+ whatsoever
+ echo 'My code works, I don'\''t know why.'
My code works, I don't know why.
$ set +x
+ set +x
$ whatsoever 
My code works, I don't know why.

結論就是,用set -xset +x就可以了。不過bash -x跑出來的訊息還真的嚇了我一跳,原來載入一個bash有那麼東的東西要做。這就是所謂的微言大意嗎?

使用debootstrap 在本機中操作其他Ubuntu 版本 (命令列)

好吧,這個標題我也不滿意,不過就將就一下吧。

簡單來說在Ubuntu內只要可以找得到的distribution,你就有辦法讓它在你的Ubuntu使用。使用順序如下:

下載你想要執行的套件

命令:

debootstrap --arch=你要跑的target平台 --variant=minbase 你要跑的版本代號 自己PC的版本代號 [mirror site]

要怎麼知道arch有哪些呢,你可以連到任意一個Ubuntu archive中的ubuntu/dists/[版本代號]/main中就可以看到了。可以看範例網頁,這邊可以看到有i386amd64兩種架構。

而版本代號可以在任意一個Ubuntu archive中的ubuntu/dists/看到。範例網頁可以看到10.04 (lucid), 12.04 (precise)等。

另外--variant問男人可以看到有

  • minbase
    • 最少安裝
  • buildd
    • 多安裝build code需要的套件
  • fakechroot
    • 只安裝不需要root的套件 (怪怪的??)
  • scratchbox
    • 看不懂,跳過

我使用的範例如下:我要在x86-64位元的Ubuntu 14.04 (trusty) 下面安裝32位元(i386)的10.04 (lucid)最少套件的話,就會使用下面的指令。

$ sudo debootstrap --arch=i386 --variant=minbase lucid trusty ftp://ftp.tku.edu.tw/ubuntu/
I: Retrieving Release 
I: Retrieving Release.gpg 
I: Checking Release signature
...
I: Unpacking apt...
I: Configuring the base system...
I: Configuring apt...
I: Configuring libc-bin...
I: Base system installed successfully.

debootstrap會把下載並解開的套件放在你的指定的target名稱目錄中,這次範例使用trusty,所以我們看看trusty目錄有什麼東西?

$ cd trusty
$ tree -L 1 -d
.
├── bin
├── boot
├── dev
├── etc
├── home
├── lib
├── media
├── mnt
├── opt
├── proc
├── root
├── sbin
├── selinux
├── srv
├── sys
├── tmp
├── usr
└── var

可以看到他就是一個root file system。接下來要做的就是。

使用chroot切換到用debootstrap安裝的root file system

先確認我電腦上的Ubuntu版本

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 14.04.1 LTS
Release:    14.04
Codename:   trusty

$ file /bin/ls
/bin/ls: ELF 64-bit LSB  executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=64d095bc6589dd4bfbf1c6d62ae985385965461b, stripped

切換指令如下

$ cd trusty # 假設你還沒切換過去
$ sudo mount --bind /dev ./dev
$ sudo mount --bind /dev/pts ./dev/pts
$ sudo mount --bind /proc ./proc
$ sudo mount --bind /sys ./sys
$ sudo chroot .

好啦,口說無憑,我們先來看看是不是真的換到lucid,32位元版本吧

# apt-get install lsb-release # 因為最少安裝所以沒有lsb_release
Reading package lists... Done
Building dependency tree... Done
...

# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 10.04 LTS
Release:    10.04
Codename:   lucid

# file /bin/ls
/bin/ls: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, stripped

這東西能做什麼呢?有時候開發套件需要在不同的Ubuntu版本測試,這時候後這招頂著先應該會比用VM再來得有效率一些。

另外要注意的是,我測試的時候自己的電腦應該有裝過很多東西了。這表示你要按表操課可能會因為缺乏套件所以不會成功,例如debootstrap可能就要自行安裝了我猜。

嘛,這就是人參,出現這種狀況就當作磨練吧。

參考資料

  • man debootstrap

Talking about information technology presentions in Taiwan

OK. This should be the first English article in my blog. I do hope this will also be the last one. English is not my mother tongue, so please bear with that. It should be clear to address my point of view although there should be some glitches throughout my article.

The purpose of my technical articles and slides is to provide useful information for the people who uses Chinese. Since I went to college to learn computer engineering, what I saw in the class and laboratory meetings, the slides always wrote in English. Even after I graduated, it still mostly true in the events or presentations I attended locally. However, most professors and speakers uses mandarin to describe their ideas and thoughts. It is rare to have a completed English presentation in those scenarios, never to say discuss in English. If that so, why people uses English in their slides?

Don’t get me wrong. I totally agree English is extremely important in information technology area. Most of the first hand information writes in English. If you need to cooperate with people overseas, you use English. However, the scenario I call into question is that if your audiences uses mandarin, you speak in mandarin, why bother to write slides in English? Can you make sure what you wrote are 100% grammar corrected? Will this exclude the newbie who does not familiar in English yet if they search keyword in the Internet? A ideal article in the Internet should be useful for the people who need it. If you write your article and slide in Chinese, you will benefit the people who uses Chinese. There are already plenty of useful information in English. Why don’t you just use your mother tongue to describe your thought and what you found?