免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1145 | 回复: 0
打印 上一主题 下一主题

GNU compiler及binary utilities簡介 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-01-14 23:45 |只看该作者 |倒序浏览
GNU compiler及binary utilities簡介
1 各個部分的職能
通常為了把一個應用程式從原始檔案轉變為可執行的二進位碼,需要以下三個部分:
1. 編譯器
        其中編譯器部分還可細分為預處理器,C編譯器和彙編編譯器等. GCC的功能是C編譯器.
2. (目標代碼)連接器
        Binutils 最重要的成員是彙編編譯器和連接器,還包括一些二進位碼工具.
3. 程式庫
        程式庫通常是C或C++標準庫.
注意這三部分是彼此獨立的,也就是說,GCC並不是非要Binutils中的工具,
也可以使用其他彙編編譯器和連接器,也可以使用其他C程式庫.
2 使用Binutils中的工具
Binutils中的工具可以幫助我們診斷許多問題.
2.1 readelf
        readelf用來察看elf文件的內容. 用-a選項可以看見大部分內容:
        arm-linux-readelf -a rt61ap.ko
2.2 objdump
        objdump的一個重要作用是反彙編目標文件:
        arm-linux-objdump -S rtmp_main.o
        rtmp_main.o:     file format elf32-bigar
        Disassembly of section .text:
        00000000 :
        0:       e1a0c00d        mov     ip, sp
        4:       e92dddf0        stmdb   sp!, {r4, r5, r6, r7, r8, sl, fp, ip, lr, pc}
        ...
        00000218 :
        218:       e1a0c00d        mov     ip, sp
        21c:       e92dd8f0        stmdb   sp!, {r4, r5, r6, r7, fp, ip, lr, pc}
2.3 objcopy
        objcopy有一個很重要的作用是把代碼從elf文件中抽取出來,形成可執行的機器碼:
        arm-linux-objcopy -O binary -R .comment -R .note -S rt61ap.ko test.bin
        形成的結果文件test.bin可以燒到flash或下載到記憶體中去.
2.4 nm
        nm用來列出elf文件中使用到的symbol:
        arm-linux-nm rt61ap.ko
        00000854 D A_BAND_REGION_0_CHANNEL_LIST
        00035890 T aes128k128d
                 U alloc_etherdev
        T表示定義過的函數,U表示尚未定義的函數. 我們可以看出aes128k128d在rt61ap.ko裏定義了,
        其他幾個函數alloc_etherdev在rt61ap.ko中被引用,但找不到定義,它們可能在其他.o文件或程式庫中定義.
        nm對診斷連接錯誤很有幫助.
2.5 連接器ld
        連接器的主要作用是把一個或多個目標文件(程式庫)轉變為一個可執行程式.
        符號的重定位(relocation)是它最重要的工作. 傳給ld的主要參數有目標文件,-l 選項和-L選項. 例如:
        ld -o crt0.o myapp main.o subs.o -lm -lc -L/usr/lib
        注意-lc的寫法,它表示要與libc.a連接2;同樣,-lm表示要與數學庫libm.a連接. -Lpath指定庫的搜索路徑.
2.5.1 連接腳本
        ld知道如何連接各段(section)以及各段的起始位置,因為這些資訊在ld本身被編譯的時候已經內置進ld了.
        有時你可能需要自己來安排可執行文件中各段的分佈. 你可以自己寫一個連接腳本(link script),
        用-T選項傳給ld. 這是一個簡單的連接腳本:
        MEMORY
        {
        rom (rx) : ORIGIN = 0, LENGTH = 2M
        ram (!rx): ORIGIN = 0x1000000, LENGTH = 8M
        }
        SECTIONS
        {
        .text : {
        _ftext = . ;
        *( .text )
        }
        . = ALIGN( 4 );
        _etext = .;
        PROVIDE(etext = .);
        .data :
        AT (_etext)
        {
        *( .data )
        } >ram
        _edata = .;
        .bss : {
        *(.bss)
        }>ram
        . = ALIGN( 4 );
        _end = .;
        }
        它只指定了3個最重要的段:text,data和bss以及它們的位置. 定制的連接腳本在嵌入式系統中經常要用到,
        因為ld內置的腳本可能不符合需要.
2.5.2 部分連接
        ld是連接器. 有時我們可以用它來``部分連接''幾個文件以生成一個目標文件,
        該目標文件以後還可以再次與其他目標文件連接. ``部分連接''在uClinux中的應用:
        genromfs -d romfs -f romfs.img
        ld -r -o romfs.o romfs.img
        ld並不關心romfs.img的文件格式是什麼. 但生成的romfs.o是ELF格式,它以後還會和其他.o文件連接最終生成linux.elf.
2.6 ar
        ar通常用來製作庫文件---即含有許多.o文件的.a文件.如果你想瞭解某個庫文件的內容,可以ar tv libabc.a
        製作一個庫文件也很簡單:
        ar rs libabc.a a.o b.o c.o
        事實上你可以把任何文件打包成一個存檔文件,ar並不關心a.o,b.o,c.o等的格式. 你還可以把存檔文件裏的文件解開:
        ar x libabc.a a.o
3 GCC作為編譯器和連接器的大門
3.1 統一的入口
        GCC本身並不是編譯器或連接器. 它可以作為預處理器,C編譯器,彙編編譯器和連接器的入口. 例如,
        你運行命令\n        gcc -o a1.o -c a1.c
        的時候,gcc自動調用了預處理器cpp,彙編編譯器as. 你也可以用gcc來編譯彙編文件:
        gcc -o a1.o -c a1.S
        在Makefile中我們通常定義變數LD為gcc,用gcc來連接目標文件:
        LD = arm-elf-gcc
        ...
        $(OUTPUT_NAME): $(OBJECTS)
        echo "Linking... "
        echo "Creating file $@..."
        $(LD) -o $@ $(STARTUP) $^ $(LDFLAGS)
        用gcc作為編譯器和連接器的入口的好處是:它會傳一些額外的參數給編譯器和連接器.
        比如,通常程式會與標準C程式庫相連接.
        如果你用ld來連接,必須傳給ld 許多參數,象crt0.o,-lc等等. 因為這些東西並不是生成可執行文件所必需的,
        因此這些資訊不會內嵌在ld中. 如果你用gcc作為連接器,這些選項就會自動被加上去.
3.2 gcc的specs文件
        specs 存放在/usr/lib/gcc-lib/$(ARCH)-$(OS)/$(VERSION)下面.
        gcc的上述行為是通過它的specs文件實現的. 我們打開specs文件,選取一些片斷看一下:
        *startfile:
        crti%O%s crtbegin%O%s crt0%O%s
                此條資訊告訴我們,startfile是crti.o,crtbegin.o和crt0.o. 在連接的時候它們會被置於你的應用程式的最前面.
        *predefines:
        -D__ELF__
                ELF 會被預定義. 因此如果你在程式中有如下語句:
                #ifdef __ELF__
                do_something();
                #endif
                do something肯定會被編譯進代碼.
        *link:
        %{mbig-endian:-EB} -X
                如果命令行中有形如-mbig-endian的參數,就傳給連接器``-EB -X''的參數
        *lib:
        %{!shared:%{g*:-lg} %{!p:%{!pg:-lc}}%{p:-lc_p}%{pg:-lc_p}}
                連接的時候,會根據不同的選項,添加庫libc或libc p等.
        *link_command:
        %{!fsyntax-only:%{!c:%{!M:%{!MM:%{!E:%{!S: %(linker) %l %X %{o*}
        %{A} %{d} %{e*} %{m} %{N} %{n} %{r} %{s} %{t} %{u*} %{x} %{z} %{Z}
        %{!A:%{!nostdlib:%{!nostartfiles:%S}}} %{static:} %{L*}
        %(link_libgcc) %o
        %{!nostdlib:%{!nodefaultlibs:%(link_gcc_c_sequence)}}
        %{!A:%{!nostdlib:%{!nostartfiles:%E}}} %{T*} }}}}}}
                這是連接的規則,比較複雜. 不過我們可以看出,gcc為我們額外做了許多事情.
4 製作交叉編譯器
4.1 一些基本概念
        製作交叉編譯器的時候,GCC有所謂的target,host和build的概念.
        交叉編譯器所生成的代碼運行在target上面,交叉編譯器本身運行在host上,而製作交叉編譯器的機器是build.
        因此我們可以在i386-pc-linux系統上製作一套在cygwin上運行的交叉編譯器,該編譯器生成在ARM處理器上運行的代碼.
4.2 準備工作
        我們將在cygwin環境下製作一套arm-elf的交叉編譯器. 我們需要的套裝軟體有: GCC, Binutils和libc.
        libc可以有多種選擇,我們在此選用newlib.
        首先把下載好的套裝軟體解開:
        mkdir c:/build
        cd c:/build
        tar -jxf binutils-2.13.1.tar.bz2
        tar -zxf gcc-3.2.3.tar.gz
        tar -zxf newlib-1.11.0.tar.gz
        目錄binutils-2.13.1,gcc-3.2.3和newlib-1.11.0會生成.
        這三個套裝軟體都建議原始檔案目錄和編譯目錄分開.
        因此我們要另建三個目錄:
        mkdir build-bin build-gcc build-newlib
4.3 開始編譯
        製作交叉編譯器的步驟是:
        1. 編譯binutils
        2. 編譯一個最簡單的gcc
        3. 編譯libc
        4. 再次編譯gcc,生成功能完整的gcc
4.3.1 編譯binutils
        編譯binutils很簡單:
        cd /cygdrive/c/build/build-bin
        /cygdrive/c/build/binutils-2.13.1/configure --target=arm-elf --prefix=/cygdrive/c/bar --nfp
        make all install
4.3.2 編譯最簡gcc
        然後我們編譯一個最簡的gcc,它只能編譯C程式:
        cd /cygdrive/c/build/build-gcc
        /cygdrive/c/build/gcc-3.2.3/configure --target=arm-elf
        --prefix=/cygdrive/c/bar --with-newlib --without-headers \
        --enable-languages=c --disable-threads --nfp
        make all install
4.3.3 編譯newlib
        再用剛才編譯好的gcc和binutils編譯newlib:
        cd /cygdrive/c/build/build-newlib
        CFLAGS=-O2 CXXFLAGS=-O2 /cygdrive/c/build/newlib-1.11.0/configure
        --target=arm-elf --prefix=/cygdrive/c/bar \
        --srcdir=/cygdrive/c/build/newlib-1.11.0 --nfp
        make all install \
        CC_FOR_TARGET=/cygdrive/c/bar/bin/arm-elf-gcc \
        AS_FOR_TARGET=/cygdrive/c/bar/bin/arm-elf-as \
        LD_FOR_TARGET=/cygdrive/c/bar/bin/arm-elf-ld \
        AR_FOR_TARGET=/cygdrive/c/bar/bin/arm-elf-ar \
        RANLIB_FOR_TARGET=/cygdrive/c/bar/bin/arm-elf-ranlib
4.3.4 再次編譯gcc
        最後重新編譯gcc,這次它充分利用了剛編好的newlib,可以支援C之外的其他語言了:
        cd /cygdrive/c/build/build-gcc
        /cygdrive/c/build/gcc-3.2.3/configure --target=arm-elf \
        --prefix=/cygdrive/c/bar --with-newlib \
        --with-headers=/cygdrive/c/build/newlib-1.11.0/newlib/libc/include \
        --enable-languages=c++ --disable-threads --nfp
        make all install
        這樣我們的交叉編譯器就做好了,它生成在目錄c:/bar裏.


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/29985/showart_231922.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP