• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            天衣有縫

            冠蓋滿京華,斯人獨憔悴~
            posts - 35, comments - 115, trackbacks - 0, articles - 0
               :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

            gcc, as, ld的一些筆記(原創)

            Posted on 2007-04-19 13:34 天衣有縫 閱讀(14198) 評論(1)  編輯 收藏 引用 所屬分類: program

            1.本文不是教程,只是描述c語言(gcc環境),編譯器,連接器,加載器,at&t匯編,ia32一些相關知識和筆記,很多需要深入的地方需要大家尋找相關的資料學習。如果發現錯誤,請留言或通知我jinglexy at yahoo dot com dot cn,這個是我的msn。打字不易,請轉載時保留作者:http://www.shnenglu.com/jinglexy

            2.gcc安裝的各個部分:

             

            g++

            c++編譯器,鏈接時使用c++

             

            gcc

            c編譯器,鏈接時使用c

             

            cc1

            實際的c編譯器

             

            cc1plus

            實際的c++編譯器

             

            collect2

            使用collect2產生特定的全局初始化代碼,后臺處理是傳遞參數給ld完成實際的鏈接工作。

             

            crt0.o

            初始化和結束代碼

             

            libgcc

            平臺相關的庫

            gcc安裝需要的文件:

            gcc-core-3.4.6.tar.gz2          gcc核心編譯器,默認只包含c編譯器

            gcc-g++-3.4.6.tar.bz2           g++編譯器

            gcc-testsuite-3.4.6.tar.bz2     測試套件

            ./configure && make && make install

             

            3.binutils安裝的各個部分

             

            as

            gnu匯編工具

             

            gprof

            性能分析工具

             

            ld

            gnu鏈接器

             

            make

             

             

            objcopy

            目標文件從二進制格式翻譯或復制到另一種

             

            objdump

            顯示目標文件的各種信息

             

            strings

            顯示文件的字符串

             

            strip

            去除符合表

             

            readelf

            分析elf并顯示信息

            鏈接器可以讀寫各種目標文件中的信息,通過BFDbinary file descriptor)提供的工具實現,BFD定義了類似a.out, elf, coff等目標文件的格式。

             

            4.gcc預處理程序

            1)define指令

            #可將傳遞的宏字符串化

            ##將兩個名字連接成一個(注意不是連接成字符串)

            例:#define  TEST(ARGTERM)        \

                    printf(“the term “ #ARGTERM “is a string\n”)

            使用__VA_ARGS__定義可變參數宏

            例:#define err(...)    fprintf(stderr, __VA_ARGS)

                err (“%s %d\n”, “error code is”, 48);

            為了消除無參數時的逗號,可以用下面方法定義:

                   # define err(...)        fprintf(stderr, ##__VA_ARGS)

                   一種等同的方法是:

                   #define dprintf(fmt, arg...)    printf(fmt, ##arg)

            其他例:#define  PASTE(a, b)          a##b

            2)error warning指令

            #error “y here? bad boy!”

            3)if, elif, else, endif指令

                   支持的運算符:加減乘除,位移,&&||!

                   示例:#if defined (CONFIG_A) || defined (CONFIG_B)

                                        ……

                         #endif

            4)gcc預定義宏

             

            __BASE_FILE__

            完整的源文件名路徑

             

            __cplusplus

            測試c++程序

             

            __DATE__

             

             

            __FILE__

            源文件名

             

            __func__

            替代__FUNCTION____FUNCTION__以被GNU不推薦使用

             

            __TIME__

             

             

            __LINE__

             

             

            __VERSION__

            gcc版本

             

             

             

             

            5)幾個簡單例子:

            1

            #define   min(X,  Y)  \

                (__extension__ ({typeof (X) __x = (X), __y = (Y);  \

                (__x < __y) ? __x : __y; }))

            #define   max(X,  Y)  \

                (__extension__ ({typeof (X) __x = (X), __y = (Y);  \

                (__x > __y) ? __x : __y; }))

            這樣做的目的是消除宏對XY的改變的影響,例如:result = min(x++, --y); printf(x, y);

            補充:圓括號定義的符合語句可以生成返回值,例:

                          result = ({ int a = 5;

                                        int b;

                                        b = a + 3;

                                        });          將返回8

            2

            #define dprintfbin(buf, size)   do{  int i;            \

                    printf("%s(%d)@",                           \

                              __FUNCTION__, __LINE__);          \

                    for(i = 0; i < size - 1; i++){              \

                           if(0 == i % 16)                      \

                                 printf("\n");                  \

                           printf("0x%02x ", ((char*)buf)[i]);  \

                    }                                           \

                    printf("0x%02x\n", ((char*)buf)[i]);        \

            }while(0)

            這個比較簡單,不用解釋了

             

            3

            #ifdef __cplusplus

            extern "C"{

            #endif

            int foo1(void);

            int foo2(void);

            #ifdef __cplusplus

            }

            #endif

            作用:在c++程序中使用c函數及庫,c++編譯程序時將函數名粉碎成自己的方式,在沒有extern的情況下可能是_Z3_foo1_Z3_foo2將導致連接錯誤,這里的extern表示在連接庫時,使用foo1foo2函數名。

             

            5.gcc編譯的一些知識

            gcc  -E  hello.c  -o  hello.i             只預處理

            gcc  -S  hello.c  -o  hello.s             只編譯

            gcc  -c  -fpic  first.c  second.c

            編譯成共享庫:-fpic選項告訴連接器使用got表定位跳轉指令,使加載器可以加載該動態庫到任何地址(具體過程可在本文后面找到)

             

            6.gccc語言的擴展

            void fetal_error()  __attribute__(noreturn); 聲明函數:無返回值

            __attribute__((noinline)) int foo1(){……}定義函數:不擴展為內聯函數

            int getlim()  __attribute__((pure, noinline));聲明函數:不內聯,不修改全局變量

            void mspec(void)  __attribute__((section(“specials”)));聲明函數:連接到特定節中

            補充:除非使用-O優化級別,否則函數不會真正的內聯

            其他屬性:

            函數

            always_inline

             

            函數

            const

            pure

            函數

            constructor

            加入到crt0調用的初始化函數表

            函數

            deprecated

            無論何時調用函數,總是讓編譯器警告

            函數

            destructor

             

            函數

            section

            放到命名的section中,而不是默認的.text

            變量

            aligned

            分配該變量內存地址時對齊屬性,例:

            int value __attribute__((aligned(32)));

            變量

            deprecated

            無論何時引用變量,總是讓編譯器警告

            變量

            packed

            使數據結構使用最小的空間,例如:

            typedef  struct  zrecord{

                          char a;

                          int b __attribute((packed));

            }zrecord_t;

            變量b在內存中和a沒有空隙

            變量

            section

            同上,例:

            int trigger __attribute__((section(“domx”))) = 0;

            類型

            aligned

            同上,例:

            struc blockm{

                          char j[3];

            }__attribute__((aligned(32)));

            類型

            deprecated

            同上

            類型

            packed

            同上

             

             

             

             

            gcc內嵌函數:

            void *__builtin_return_address(unsigned int level);

            void *__builtin_frame_address(unsigned int leve);

            以上兩個函數可以用于回溯函數棧,如果編譯器優化成noframe呢,誰愿意驗證一下?

            gcc使用__asm__, __typeof__, __inline__替代asm, typeof, inline-std-ansi會使后者失去功能。

             

            標識符局部化,使用__label__標簽

            int main(……){

                   {

                                 __label__ jmp1;

                                 goto jmp1;

                   }

                   goto jmp1;                    /* 錯誤:jmp1未定義 */

            }

             

            typeof的一些技巧:

             

            char *chptr

            a char point

             

            typeof (*chptr) ch;

            a char

             

            typeof (ch) *chptr2;

            a char point

             

            typeof(chptr) chparray[10];

            ten char pointers

             

            typeof(*chptr) charray[10];

            ten char

             

            typeof (ch) charray2[10];

            ten chars

             

            7.objdump程序

             

            -a

             

            文檔頭文件信息

             

            -d

             

            可執行代碼的反匯編

             

            -D

             

            反匯編可執行代碼及數據

             

            -f

             

            完整文件頭的內容

             

            -h

             

            section

             

            -p

             

            目標格式的文件頭內容

            調試器呢?網上的gdb教程已足夠的多,不再畫蛇添足了。

             

            8.平臺IA32的一些知識

            指令碼格式:

            指令前綴(04字節)

            操作碼(13字節)

            可選修飾符(04字節)

            可選數據元素(04字節)

            指令前綴:較重要的有內存鎖定前綴(smp系統中使用)

            操作碼:ia32唯一必須的部分

            修飾符:使用哪些寄存器,尋址方式,SIB字節

            數據元素:靜態數值或內存位置

             

            ia32比較重要的技術:指令預取,解碼管線,分支預測,亂序執行引擎

            (網絡上可以找到很多相關的文章)

             

            通用寄存器(832位):eax, ebx, ecx, edx, esi, edi, esp, ebp

            端寄存器(616位):cs, ds, ss, es, fs, gs

            指令指針(132位):eip

            浮點寄存器(880位):形成一個fpu堆棧

            控制寄存器(532位):cr0, cr1, cr2, cr3, cr4

                          較重要的是cr0:控制操作模式和處理器狀態

                                           cr3:內存分頁表描述寄存器

            調試寄存器(832位):

            標識寄存器(132位):狀態,控制,系統(共使用17位):陷阱,中斷,進位,溢出等

            說明:mmx使用fpu堆棧作為寄存器,sse, sse2, sse3沒有寄存器,只提供相關的指令功能。

            9.gas匯編工具:asat&t風格)語法說明

            使用$標識立即數

            再寄存器前面加上%

            源操作數在前,目標操作數在后

            使用$獲取變量地址

            長跳轉使用:ljmp $section, $offset

            一個簡單的匯編語言程序框架:

            .section .data

                          ……

            .section .bss

                          ……

            .section .text

            .globl _start

            _start:

                   ……

             

            范例:

            #cpuid2.s View the CPUID Vendor ID string using C library calls

            .section .datatext

            output:

                .asciz "The processor Vendor ID is '%s'\n"

            .section .bss

                .lcomm buffer, 12

            .section .text

            .globl _start

            _start:

                movl $0, %eax

                cpuid

                movl $buffer, %edi

                movl %ebx, (%edi)

                movl %edx, 4(%edi)

                movl %ecx, 8(%edi)

                pushl $buffer

                pushl $output

                call printf

                addl $8, %esp

                pushl $0

            call exit

             

            偽指令說明:

            data

            .ascii

            定義字符串,沒有\0結束標記

            data

            .asciz

            \0結束標記

            data

            .byte

            字節

            data

            .int

            32

            data

            .long

            32

            data

            .shot

            16

            bss

            .lcomm

            對于上面的例子是聲明12字節的緩沖區,l標識local,僅當前匯編程序可用

            bss

            .comm

            通用內存區域

            data/text

            .equ

            .equ  LINUX_SYS_CALL, 0x80

            movl $ LINUX_SYS_CALL, %eax

            說明:equ不是宏而是常量,會占據數據/代碼段空間

             

            指令集說明:

             

            movb/movw/movl

             

             

            cmov

            根據cf, of, pf, zf等標識位判斷并mov

             

            xchg

            操作時會lock內存,非常耗費cpu時間

             

            bswap

            翻轉寄存器中字節序

             

            xadd

             

             

            pushx, popx

             

             

            pushad, popad

             

             

            jmp

             

             

            call

             

             

            cmp

             

             

            jz/jb/jne/jge

             

             

            loop

             

             

            addb/addw/addl

             

             

            subb/subw/subl

             

             

            dec/inc

             

             

            mulb/muw/mull

            無符號乘法

            源操作數長度

            目標操作數

            目標位置

            8

            al

            ax

            16

            ax

            dx:ax

            32

            eax

            edx:eax

             

            imul有符合乘法

             

             

            divb/divw/divl

            無符合除法

            (被除數在eax中,除數在指令中給出)

            被除數

            被除數長

            余數

            ax

            16

            al

            ah

            dx:ax

            32

            ax

            dx

            edx:eax

            64

            eax

            edx

             

            idiv有符合除法

             

             

            sal/shl/sar/shr

            移位

             

            rol/ror/rcl/rcr

            循環移位

             

            leal

            取地址:leal  output, %eax

            等同于:movl  $output, %eax

             

            rep

            rep movsb      執行ecx

             

            lodsb/lodsw/lodsl

            stosb/stosw/stosl

            取存內存中的數據









             

            gas程序范例(函數調用):

            文件1area.s定義函數area

            # area.s - The areacircumference function

            .section .text

            .type area, @function

            .globl area

            area:

               pushl %ebp

               movl %esp, %ebp

               subl $4, %esp

               fldpi

               filds 8(%ebp)

               fmul %st(0), %st(0)

               fmulp %st(0), %st(1)

               fstps -4(%ebp)

               movl -4(%ebp), %eax

               movl %ebp, %esp

               popl %ebp

               ret

             

            文件2functest4.s調用者

            # functest4.s - An example of using external functions

            .section .data

            precision:

               .byte 0x7f, 0x00

            .section .bss

               .lcomm result, 4

            .section .text

            .globl _start

            _start:

               nop

               finit

               fldcw precision

             

               pushl $10

               call area

               addl $4, %esp

               movl %eax, result

             

               pushl $2

               call area

               addl $4, %esp

               movl %eax, result

             

               pushl $120

               call area

               addl $4, %esp

               movl %eax, result

             

               movl $1, %eax

               movl $0, %ebx

               int $0x80

             

            10.讀連接器和加載器的一些筆記,感謝原作者colyli at gmail dot com,看了他翻譯的lnl及寫的一個os,受益匪淺。

            如果不是很深入的研究連接器和加載器的話,了解一些原理就足夠了。舉個例子說明吧:

              1 #include <unistd.h>

              2 #include <stdlib.h>

              3 #include <stdio.h>

              4 #include <string.h>

              5

              6 int a = 1;

              7 int main()

              8 {      

              9         printf("value: %d\n", a);

             10        

             11         return 0;

             12 }

            編譯指令:gcc -c hello.c -o hello.o                   匯編

            gcc -o hello hello.o                          編譯

            objdump -d hello.o                          反匯編目標文件

            objdump -d hello                             反匯編可執行文件

            比較兩端結果:

            objdump -d hello.o

            objdump -d hello

            00000000 <main>:

               0:   55         push  %ebp

               1:   89 e5       mov  %esp,%ebp

               3:   83 ec 08    sub   $0x8,%esp

               6:   83 e4 f0     and  $0xfffffff0,%esp

               9:   b8 00 00 00 00  mov    $0x0,%eax

               e:   83 c0 0f        add    $0xf,%eax

              11:   83 c0 0f        add    $0xf,%eax

              14:   c1 e8 04        shr    $0x4,%eax

              17:   c1 e0 04        shl   $0x4,%eax

              1a:   29 c4          sub   %eax,%esp

              1c:   83 ec 08        sub  $0x8,%esp

              1f:   ff 35 00 00 00 00  pushl  0x0

              25:   68 00 00 00 00   push   $0x0

              2a:   e8 fc ff ff ff call  2b <main+0x2b>

              2f:   83 c4 10        add $0x10,%esp

              32:   b8 00 00 00 00  mov  $0x0,%eax

              37:   c9            leave 

              38:   c3            ret   

            08048368 <main>:

             8048368: 55      push   %ebp

             8048369: 89 e5    mov    %esp,%ebp

             804836b: 83 ec 08  sub    $0x8,%esp

             804836e: 83 e4 f0  and    $0xfffffff0,%esp

             8048371: b8 00 00 00 00  mov  $0x0,%eax

             8048376: 83 c0 0f        add  $0xf,%eax

             8048379: 83 c0 0f       add   $0xf,%eax

             804837c: c1 e8 04       shr   $0x4,%eax

             804837f:  c1 e0 04      shl   $0x4,%eax

             8048382: 29 c4          sub  %eax,%esp

             8048384: 83 ec 08       sub   $0x8,%esp

             8048387: ff 35 94 95 04 08 pushl 0x8049594

             804838d: 68 84 84 04 08  push $0x8048484

             8048392:   e8 19 ff ff ff   call  80482b0

                                                       <printf@plt>

             8048397: 83 c4 10       add  $0x10,%esp

             804839a:  b8 00 00 00 00 mov $0x0,%eax

             804839f: c9            leave 

             80483a0: c3            ret   

             80483a1: 90           nop   

             80483a2: 90           nop   

             80483a3: 90           nop   

            簡單說明:由于程序運行時訪問內存,執行跳轉都需要確切的地址。所以匯編處理的目標文件里面沒有包含,而是把這個工作放到連接器中:即定位地址。

            當程序需要動態鏈接到某個庫上時,使用該庫的got表動態定位跳轉即可。

            具體可以看colyli大俠的《鏈接器和加載器Beta 2》,及《從程序員角度看ELF

             

            11.連接器腳本ld—script(相關內容來自《GLD中文手冊》)

            ld --verbose查看默認鏈接腳本

            ld把一定量的目標文件跟檔案文件連接起來,并重定位它們的數據,連接符號引用.一般在編譯一個程序時,最后一步就是運行ld

            實例1

            SECTIONS
            {
                  . = 0x10000;
                  .text : { *(.text) }
                  . = 0x8000000;
                  .data : { *(.data) }
                  .bss : { *(.bss) }
            }

                          注釋:“.”是定位計數器,設置當前節的地址。

             

            實例2

            floating_point = 0;
                SECTIONS
                {

            . = ALIGN(4);
                  .text :
                    {
                      *(.text)
                       _etext = .;

            PROVIDE(etext = .);
                }

             

            . = ALIGN(4);
                  _bdata = (. + 3) & ~ 3;
                  .data : { *(.data) }
                }

            注釋:定義一個符合_etext,地址為.text結束的地方,注意源程序中不能在此定義該符合,否則鏈接器會提示重定義,而是應該象下面這樣使用:

            extern char _etext;

            但是可以在源程序中使用etext符合,連接器不導出它到目標文件。

             

            實例3

              SECTIONS {
                  outputa 0x10000 :
                    {
                    all.o
                    foo.o (.input1)
                    }
                  outputb :
                    {
                    foo.o (.input2)
                    foo1.o (.input1)
                    }
                  outputc :
                    {
                    *(.input1)
                    *(.input2)
                    }
              }

            這個例子是一個完整的連接腳本。它告訴連接器去讀取文件all.o中的所有節,并把它們放到輸出節outputa的開始位置處, 該輸出節是從位置0x10000處開始的。從文件foo.o中來的所有節.input1在同一個輸出節中緊密排列。 從文件foo.o中來的所有節.input2全部放入到輸出節outputb中,后面跟上從foo1.o中來的節.input1。來自所有文件的所有余下的.input1.input2節被寫入到輸出節outputc中。

             

            示例4:連接器填充法則:

               SECTIONS { .text : { *(.text) } LONG(1) .data : { *(.data) } }                    錯誤

                SECTIONS { .text : { *(.text) ; LONG(1) } .data : { *(.data) } }           正確

             

            示例5VMALMA不同的情況

                SECTIONS
                  {
                  .text 0x1000 : { *(.text) _etext = . ; }
                  .mdata 0x2000 :
                    AT ( ADDR (.text) + SIZEOF (.text) )
                    { _data = . ; *(.data); _edata = . ;  }
                  .bss 0x3000 :
                    { _bstart = . ;  *(.bss) *(COMMON) ; _bend = . ;}
                }

            程序:

                extern char _etext, _data, _edata, _bstart, _bend;
                char *src = &_etext;
                char *dst = &_data;

             

                /* ROM has data at end of text; copy it. */
                while (dst &lt; &_edata) {
                  *dst++ = *src++;
                }

             

                /* Zero bss */
                for (dst = &_bstart; dst&lt; &_bend; dst++)
                  *dst = 0;

            示例6linux-2.6.14/arch/i386/kernel $ vi vmlinux.lds.S

            linux內核的鏈接腳本,自行分析吧,有點復雜哦。

            Feedback

            # re: gcc, as, ld的一些筆記(原創)  回復  更多評論   

            2007-06-29 14:05 by V086.cn
            9743
            久久93精品国产91久久综合| 久久国产精品免费一区二区三区| 亚洲精品国产成人99久久| 性做久久久久久久| 久久影院亚洲一区| 国产69精品久久久久99| 亚洲国产二区三区久久| 日本免费一区二区久久人人澡| 97久久超碰成人精品网站| 99久久无色码中文字幕| 日本免费久久久久久久网站| 丰满少妇人妻久久久久久4| 久久本道久久综合伊人| 久久人人超碰精品CAOPOREN | 欧美麻豆久久久久久中文| 久久亚洲色一区二区三区| 亚洲国产综合久久天堂| 亚洲综合熟女久久久30p| 久久久久久久久无码精品亚洲日韩 | 伊人色综合久久天天人守人婷| 欧美精品丝袜久久久中文字幕| 色婷婷噜噜久久国产精品12p| 久久艹国产| 久久人人爽人人爽人人片AV东京热| 久久高清一级毛片| 亚洲人成无码久久电影网站| 久久狠狠爱亚洲综合影院| 久久精品国产第一区二区三区| 久久这里只有精品久久| 色综合久久久久综合99| 久久精品人成免费| 97久久超碰国产精品旧版| 色综合久久久久综合99| 99久久精品日本一区二区免费| 久久露脸国产精品| 久久精品免费一区二区| 国产69精品久久久久9999| 中文字幕久久精品无码| 青青国产成人久久91网| 狠狠色丁香久久婷婷综合| 很黄很污的网站久久mimi色|