荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: Rog (leang kei ka), 信区: Linux
标  题: [CLDP]Gcc-Howto
发信站: BBS 荔园晨风站 (Sun Dec 13 20:19:21 1998), 站内信件

【 以下文字转载自 Rog 的信箱 】
【 原文由 rog.bbs@@IEESR-SUN.stu.edu.cn 所发表 】
发信人: linux (熊哥), 信区: Unix
标  题: [CLDP]Gcc-Howto
发信站: 郁金香 BBS (Mon Oct 19 21:32:05 1998), 转信


                          The Linux GCC HOWTO中译版V0.1

 作者: Daniel Barlow <dan@detached.demon.co.uk>
 译者: 陈建勋(Frank J.S. Chen)<frank63@ms5.hinet.net>

    v1.17, 28 February 1996

      _________________________________________________________________

    这篇文件的内容包括了如何在Linux下启用(set up)GNU
    C的编译器(compiler)以及怎麽建立(set
    up)那些可以拿来开发软体的程式库(libraries);同时,
    对於程式码的编译(compiling),连结(linking),执行(running)与除错(debuggin
    g)等等,也会有概观(overview)的说明.这篇文件写作的材料,泰半是来自於Mitch
    D'Souza所收集GCC-FAQ,而这篇文件取代了GCC-FAQ.另外一个来源就是ELF-HOWTO
    了;可以这麽说,Linux GCC-HOWTO快要永久的取代ELF-HOWTO,
    成为GCC最主要的说明文件了.
    这是第一份公开发行的版本(不须理会版本序号;那是RCS的杰作(artifact)).有葼
    魏沃刚虢ㄒ榈?,我都很欢?.
      _________________________________________________________________

    ~~GCC HOWTO中译版说明文件 V0.1 补充说明 Taipei, Taiwan, R.O.C.
     1. 这份译文为Linux document projects(LDP)中文芬爰苹
        系列之一.目前之网址为http://www.linux.org.tw/CLDP/.欢?
        各位网友踊跃投入此一计?.
     2. 我并没有完全按照晕闹鹱址
        译.为了力求译文通畅可读,我会稍稍的重组一部份的文字,加油添醋,或是精
        简誀
        文;这样做的话,可以弥补中英文间语法结构的差异性,且语气可以贯通无?.
     3. 一些关键字与专业词汇等,会附加上晕牡プ?.
     4. 遇有转译困难,唯晕某<淖只?,如bugs,shadow
        password,padding之类的,则保留誀
        文不变.若阁下对这些字汇有适当译辞的,请不吝指教.
     5. 内文中若遇有"[译者注:**]"之标记,则为本人额外之注解.
     6. 对这篇译文有任何建议与疑问的,请email至frank63@ms5.hinet.net.
     7. WWW Home Page: http://wwwhome.fancy.com.tw/~frank/.
     8. 此中译文件之芬肴ㄒ讶〉糜⒓作者Daniel Barlow
        先生之同意;另,陈建勋先生保有此份中译版文件所有的权利,你可以任意的繝
        奖?,以各种媒体'完整'散布这份中译文件,唯此补充说明需誀
        封不动附上,且不可任意更动译文.
     9. 芬肫鹗既掌谖?:11/7/97;截止日期为:11/19/97

   1. 生火上路(Preliminaries)!


           1.1. ELF vs. a.out
           1.2. 作者的私语(Administrata)
           1.3. 印刷与排版(typography)

   2. 上哪抓这些东东?


           2.1. 这份文件座落之处
           2.2. 其它相关的说明文件
           2.3. GCC
           2.4. C程式库与标头档
           2.5. 相关联的工具 (as, ld, ar, strings etc)

   3. GCC的安装(installation)与启用(setup)


           3.1. GCC的版本
           3.2. 东东装好後都到哪儿去了?
           3.3. 标头档ㄋㄟ?标头档ㄋㄟ?
           3.4. 建立交叉编译器(Building cross compilers)

   4. 移植(Porting)与编译(Compiling)程式


           4.1. gcc自行定义的符号
           4.2. 线上求助说明(invocation)
           4.3. 移植能力(Portability)

   5. Debugging and Profiling


           5.1. Preventative maintenance (lint)
           5.2. 除错(Debugging)
           5.3. 旁敲侧击(Profiling)

   6. 连结(Linking)


           6.1. 共享程式库 vs静态程式库
           6.2. Interrogating libraries (`which library is sin() in?')
           6.3. X档案???
           6.4. 建立你自己的程式库(Building your own libraries)

   7. 动态载入(Dynamic Loading)


           7.1. 基本概念
           7.2. 错误讯息(Error messages)
           7.3. 控制动态载入器的运作
           7.4. 以动态载入撰写程式

   8. 与发展人士联络


           8.1. Bug报表
           8.2. 兄⒄範

   9. 结语


           9.1. 名人榜
           9.2. 芬霠
           9.3. 欢尤魏蔚幕乩?(Feedback)
           9.4. 合法的行迳规定

   10. 索引

    The Linux GCC HOWTO中译版V0.1 : 生火上路(Preliminaries)!
    Previous: The Linux GCC HOWTO中译版V0.1
    Next: 上哪抓这些东东?
      _________________________________________________________________

 1. 生火上路(Preliminaries)!

   1.1. ELF vs. a.out

    目前,Linux的发展正波涛汹涌的进行著.简单一点讲,Linux有两种执行档的格式(
    formats)可用,取决於你的系统是怎麽整合起来的;你可能两种都有.读了这份文紶
    ?,你就会知道是那一种了.

    那,要怎麽区别呢?执行公用程式(utility)'file' (例如,file
    /bin/bash)就对了.就ELF格式的程式码来讲,显示出来的讯息会含有ELF的字眼;葼
    绻莂.out格式的,讯息内就会箝有 Linux/i386的字样了.

    ELF与a.out格式的差异之处,会在後续的章节中讨论(很广泛喔).ELF是比较新的笭
    袷?,一般而言,接受的程度较佳.

   1.2. 作者的私语(Administrata)

    版权说明(copyright
    information)与合法的行迳规定(legalese),就摆在这份文件的尾端.除此之外,螤
    ?......,我还有一些不得不提醒你的话要讲:就算你□著没事干,也不要在Usenet
    上丢一些呆瓜问的问题;还有啊,不要老以为自己C的功力深厚,专门发表一些不是
    bugs的bugs出来丢人现眼,
    告诉别人你不学无术.最後;嚼口香糖的时候,不妨挖挖你的鼻孔(,and picking
    your nose while chewing gum)! [译者注:不知道这是那一国的幽默? eh?
    :-)另一种可能是晕挠腥甭┳只?, 像是"and not picking your nose while
    chewing gum."]

   1.3. 印刷与排版(typography)

    如果你现在读的是Postscript,dvi或者是html格式的话,那麽你所看到的字型变粻
    突岜戎欢链课淖指袷降娜硕嘁恍?.特别的是,档案名称(filenames),命令(comm
    ands),命令的输出(command output)与摘录出来的允悸?(source
    code)等,统统都是打字机的字型样式(form).这样做的话,对於某些需要强调的变
    数(variables)以及没有特定结果的□例(random
    things)而言,就可以达到强调的效果了.

    读这份文件的同时,你也会得到一个有用的(usable)索引(index).假若是dvi,
    postscript之类的版本,索引的数字就是章节(section)的编号;如果是HTML的话,
    这些数字会按顺序排列,你可以用滑鼠左键来连结(linking)相对的索引;如果你繝
    吹氖谴?(plain)文字版本的话, 数字就只是数字,
    没别的含意;建议你赶快升级为妙哩!

    我所用的shell是Bourne shell(不是C shell),举的例子自然是Bourne
    shell的语法.如果你用的是C shell的话, 环境变数设定的语法会像下面这样:

 % setenv FOO bar

    要是用Bourne shell的话, 我会这样子写:

 $ FOO=bar; export FOO

    如果提示符号(prompt)显示的是井字符号#,而不是钱字符号
    $,那麽,很有可能是这个命令只适用root而已.当然啦!要是你试了这些□例,结果
    弄得你的系统发生灾变,我可是一点责任也不会负的喔!祝你心情好啊!:-)
    [译者注:牵拖(闽南语) _ .]

    11/8/97译.

      _________________________________________________________________

    The Linux GCC HOWTO中译版V0.1 : 生火上路(Preliminaries)!
    Previous: The Linux GCC HOWTO中译版V0.1
    Next: 上哪抓这些东东? The Linux GCC HOWTO中译版V0.1 : 上哪抓这些东东?
    Previous: 生火上路(Preliminaries)!
    Next: GCC的安装(installation)与启用(setup)
      _________________________________________________________________

 2. 上哪抓这些东东?

   2.1. 这份文件座落之处

    这份文件是Linux HOWTO系列之一.易言之,你可以在所有存放Linux
    HOWTO文件的网站上面找到它的芳踪,例
    如http://sunsite.unc.edu/pub/linux/docs/HOWTO/.HTML格式的版本(可能会是
    较新的版本)可以从http://ftp.linux.org.uk/~barlow/howto/gcc-howto.html?
    厦孀ハ吕?.

   2.2. 其它相关的说明文件

    gcc正式的说明文件是附在发行的允悸?(source
    distribution)内(往下看就有了!),里头有textinfo与.info两种档案.要是你的蜖
    妨铀俾使豢?,或者是有一片cdrom;不然的话,有高度的耐心也成,你可以自己
    把它untar,然後再把相对应的位元一一拷贝到/usr/info的目录底下.假如你的条
    件与上述的不符,不妨到
    tsx-11站上去找一找.不过,我想,没有必要老是惦记著最新的版本吧.

    libc的文件说明有两种来源.一种是GNU
    libc,以.info的格式储存,除了stdio之外,其馀Linux
    libc的说明都相当详尽精确.另一种可以在Linux的archivemanpages
    上找到系统呼叫(system
    call)(第2节)与libc函数(function)(第3节)的文件说明.

   2.3. GCC

    解答有二:

    (a)你可以在ftp://tsx-11.mit.edu:/pub/linux/packages/GCC/的网站上找到
    正式的Linux
    GCC发行系统(distribution),且已编译好的(read-compiled)可执行档(in
    binary).当我在写这份文件时,2.7.2(gcc-2.7.2.bin.tar.gz)是最新的版本.

    (b)自由软体基金会(Free Software Foundation)所发布的GCC最新誀
    始码可以从网站GNU
    archives上取得.没有必要非得与上述的版本一致才行,不过这个版本的确是目前
    最新的.Linux
    GCC的维护人士(maintainers)让你可以很轻松的自行编译这个最新的版本.confi
    gure命令稿(script)会帮你自动建好(set it all
    up)所有该做的事.建议你有空不妨到tsx-11看看,说不定会有修正的版本(patche
    s)是你会想要用的(apply).

    如果想要编译出一些有用的东东(non-trivial)(不是我罗唆,还是有不少细琐的稜
    诹?!),下面一小节所谈的也是你要具备的:

   2.4. C程式库与标头档

    在这儿你该选的是取决於(i)你的系统是ELF亦或是a.out的;(ii)你希望你的系
    统变成哪一种?如果你是从libc 4升级到libc
    5,那麽给你一个良心的建议,去看看ELF-HOWTO文件.你一定会问,在ELF文件的哪稜
    ??嘿!嘿!不偏不倚,就差不多跟这份文件一样的位置.你可以在网站tsx-11上面
    找到你想要的.

    libc-5.2.18.bin.tar.gz
           --- ELF共享程式库(ELF shared library images),静态程式库(static
           libraries)与标头档(include files)(针对C语言与数学程式库的).

    libc-5.2.18.tar.gz
           ---libc-5.2.18.bin.tar.gz的誀
           始码.这两个档案你都需要,.bin.套件(package)内含有标头档(header
           files).如果此时你正犹豫不决,不晓得是要老身亲自下海,动手编译C程薁
           娇?;还是直接用编译好的二进位档(binaries)就可以了.有这种困扰的人
           ,来,看我的嘴形:用人家编译好的二进位档不就解决了嘛.只有在你想要N
           YS或是shadow password的情况下,你才需要自己的手来推动是指出
(indicates)你目前在用的gcc是为了486的微处理器(processor)而
        写的-可能你的电脑是386或者是586.这3种微处理器的晶片(chips)所编译而
        成的程式码,彼此间是可以相容使用的.差别之处是486的程式码在某些地方訝
        屑由蟨adding的功能,所以可以在486上面跑得比较快.这对386的机器而言,誀
        谥葱谐淌降男?(performance)上并没有什麽不良的影响(detrimental
        effect),只不过真的(does)会让程式码变得稍稍的大了些.
      * box.
        这可以说一点也不重要;不过也可能另有所指(像是slackware或者是debian)
        ,或者根本什麽也不是(所以罗!完整的目录名称是i486-linux).假如你是实紶
        傻拇?,亲自动手建立属於自己的gcc,那麽你可以在建立的过程中(build
        time)设定这一项,以装点门面(cosmetic effect).就像我做的一样:-).
      * linux.
        其实这是指linuxelf,或者是linuxaout.这一点会令人引起不必要的困惑,究
        竟是指哪一种会根据你所用的版本而异.
           + linux
             意指ELF若版本序号是2.7.0或是更新的版本;否则的话,就是a.out的了
             .
           + linuxaout
             意指a.out的格式.当linux的定义(definition)从a.out更换到ELF时,l
             inuxaout就会顺水推舟,摇身一变,成了一个目标物件(target).因此,臓
             悴换峥吹饺魏伟姹拘蚂?2.7.0的gcc有linuxaout格式的.
           + linuxelf 已緺
             过时了.通常那是指2.6.3版的gcc,而这个版本也可用来产生ELF的可执
             行档(executables).要注意的是,gcc
             2.6.3版在产生ELF程式码时会有bugs-如果你目前用的是这个版本,建覡
             槟愀峡焐?.
      * 2.7.2 版本的序号.

    所以,总结起来,我有2.7.2版的gcc,可以产生ELF格式的程式码.就这麽简单,惊讶
    吧!eh?

   3.2. 东东装好後都到哪儿去了?

    如果安装gcc时没有仔细的看著萤幕,或者你是从一个完整的发行系统内把gcc单稜
    雷コ隼窗沧暗幕?,那麽也许你会想知道到底这些东东装好後是住在整个档案系统
    (file-system)的那个地方.几个重点如下:

      * /usr/lib/gcc-lib/target/version/
        (与子目录(sub-directories))大部份的编译器(compilers)就是住在这儿的
        .在这儿有可执行的程式,实际在做编译的工作;另外,还有一些特定版本的(v
        ersion-specific)程式库与标头档(include files)等.
      * /usr/bin/gcc
        指编译器的驱动程式(driver)--就是你实际在命令列(command
        line)上执行的程式.这个目录可供各种版本的gcc使用,只要你用不同的编译
        器目录(如上所述)来安装就可以了.要知道内定的版本是那一个,在shell提薁
        痉畔麓騡cc -v.要是想强迫执行某个版本,就换打gcc -V version.例如:

 # gcc -v
 Reading specs from /usr/lib/gcc-lib/i486-box-linux/2.7.2/specs
 gcc version 2.7.2
 # gcc -V 2.6.3 -v
 Reading specs from /usr/lib/gcc-lib/i486-box-linux/2.6.3/specs
 gcc driver version 2.7.2 executing gcc version 2.6.3
      * /usr/target/(bin|lib|include)/. 如果你装了数种的目标物件(multiple
        targets),例如a.out与elf,或者某一种的交叉编译器(cross-compiler)等等
        ;那些属於非主流目标物件(non-native target(s))的程式库,binutils(as,
        ld等等)工具与标头档(header
        files)等都可以在这儿找到.即使你只安装了一种gcc,还是可以在这儿找到諣
        庑┰
        本就是替它们准备的东东.如果不是在这儿,那麽就应该是在/usr/(bin|lib|
        include)了.
      * /lib/,/usr/lib
        与其它的目录等,都是主流系统(native-system)的程式库目录.许多的应用碃
        淌蕉蓟嵊玫?/lib/cpp
        ,因此你也需要它---作法上,不是从/usr/lib/gcc-lib/target/version/
        目录里拷贝,就是弄个符号连结(symlink)指向那儿.
        [译者注:所谓nattoconf.h时是'Many files depend on
<linux/autoconf.h>,which
        otherwise may not
        exist,*'.此处之otherwise之词性应为形容词(adj),指'另一情况','另一种
        ','不同的'之意,将晕男稳荽首泳洳鹂从ξ?: (i). Many files depend
        on <linux/autoconf.h>. (ii).<linux/autoconf.h> of other condition
        may not exist. 与下一句互相比对,此处应同指在不同版本之情况下.]
        所以,当你在目录/usr/src/linux底下,解开核心的程式码时,就照著下面指薁
        镜淖霭?!

 $ cd /usr/src/linux
 $ su
 # make config
 [回答接下来的问题.通常回答得正不正确并不重要,除非你打算继续□起(go on and
build
 )你的核心.]
 # cd /usr/include
 # ln -s ../src/linux/include/linux .
 # ln -s ../src/linux/include/asm .
      * 诸如<float.h>, <limits.h>,<varargs.h>, <stdarg.h>
        与<stddef.h>之类的档案,会随著不同的编译器版本而异,属於你自己'个人'
        的档案,可以在
        /usr/lib/gcc-lib/i486-box-linux/2.7.2/include/与其它有相类似(相同)
        目录名称的地方(places of that ilk)找到. 11/11/97译

   3.4. 建立交叉编译器(Building cross compilers)

     3.4.1. 将Linux当作目标作业平台(target platform)

    假设你已灸玫絞cc的允悸?,通常你只要依袸NSTALL档内的指示便可一切ok.
    make後面黏个configure --target=i486-linux --host=XXX on platform
    XXX,就能帮你变把戏了(do the
    trick).要注意的是,你会需要Linux与核心的标头档的;而且你也需要建立交叉组
    译器(cross assembler)与交叉连结器(cross
    linker),来源是ftp://tsx-11.mit.edu/pub/linux/packages/GCC/

     3.4.2. Linux当成来源作业平台(source platform),MSDOS作为目标作业平台

    Ugh.很明显的,这个大概需要用到"emx"套件(package)或者是"go"延伸套件(exte
    nder).请自行去ftp://sunsite.unc.edu/pub/Linux/devel/msdos看看.我并没有
    测试过这个,因此也无法保证(vouch)它的功能(abilities).

      _________________________________________________________________

    The Linux GCC HOWTO中译版V0.1 : GCC的安装(installation)与启用(setup)
    Previous: 上哪抓这些东东?
    Next: 移植(Porting)与编译(Compiling)程式 The Linux GCC HOWTO中译版V0.1
    : 移植(Porting)与编译(Compiling)程式
    Previous: GCC的安装(installation)与启用(setup)
    Next: Debugging and Profiling
      _________________________________________________________________

 4. 移植(Porting)与编译(Compiling)程式

   4.1. gcc自行定义的符号

    只要执行gcc时,附加
    -v这个参数(switch),就能找出你所用的这版gcc,自动帮你定义了什麽符号(symb
    ols).例如,我的机器看起来会像这样:

 $ echo 'main(){printf("hello world\n");}' | gcc -E -v -
 Reading specs from /usr/lib/gcc-lib/i486-box-linux/2.7.2/specs
 gcc version 2.7.2
  /usr/lib/gcc-lib/i486-box-linux/2.7.2/cpp -lang-c -v -undef
 -D__GNUC__=2 -D__GNUC_MINOR__=7 -D__ELF__ -Dunix -Di386 -Dlinux
 -D__ELF__ -D__unix__ -D__i386__ -D__linux__ -D__unix -D__i386
 -D__linux -Asystem(unix) -Asystem(posix) -Acpu(i386)
 -Amachine(i386) -D__i486__ -

    假若目前你正在写的程式码,会用到一些Linux独有的特性(Linux-specific
    features),那麽把那些无法移植的程式码(non-portable
    bits),以条件式编译(conditional compilation)的前置命令封括(enclose
    in)起来,可是个不错的主意呢!

 #ifdef __linux__
 /* ... funky stuff ... */
 #endif /* linux */

    用__linux__即可达成目的;看仔细一点,不是linux啊.仅管後者也有定义,毕竟,葼
    匀徊皇荘OSIX的标准(not POSIX compliant).

   4.2. 线上求助说明(invocation)

    gcc编译器参数(switches)的说明文件是gcc info page(在Emacs内,按下C-h
    i,然後选'gcc'的选项).要是弄不出来,不是卖你CD-ROM的人,没把这个东东压给臓
    ?,不然就是你现在用的是旧版的.这种情况下,最好的方法是移动尊臀到archivef
    tp://prep.ai.mit.edu/pub/gnu或是它的mirrors站台上,把gcc的誀
    始档案抓回家,重新烹饪一番.

    gcc manual page (gcc.1) 可以说是已緺
    过时了.一旦你吃饱撑著没事干要去看看它的话,它就会告诉你这件事,叫你别无翣
    牧?.

     4.2.1. 旗正飘飘~(flags)

    在命令列(command
    line)上执行gcc时,只要在它的屁股後面加上-On的选项,就能让gcc乖乖的替你生
    出最佳化後的机器码(output
    code).这里的n是一个可有可无的小整数.不同的gcc版本,n的意义与其正确的(ex
    act)功效都不一样;不过,典型的□围是从0(不要鸡婆,我不要最佳化)变化到2(最
    佳化要多一点),再到3(最佳化要再多一点,多一点).

    gcc在其内部会将这些转译成一系列的-f
    与-m选项(options).执行gcc时带上旗号(flags)-v与-Q,你就能很清楚的看出每覡
    恢值燃兜?-O是对应(maps)到那些选项(options).例如,就-O2来讲,我的gcc告诉螤
    宜?:

 enabled: -fdefer-pop -fcse-follow-jumps -fcse-skip-blocks
 -fexpensive-optimizations
          -fthread-jumps -fpeephole -fforce-mem -ffunction-cse -finline
          -fcaller-saves -fpcc-struct-return -frerun-cse-after-loop
          -fcommon -fgnu-linker -m80387 -mhard-float -mno-soft-float
          -mno-386 -m486 -mieee-fp -mfp-ret-in-387

    要是你用的最佳化等级(optimization level)高於你的编译器所能支援的(e.g.
    -O6),那麽它的效果,就跟你用你的编译器所能提供的最高等级的,是一样的结果.
    说实在的,发行出去的gcc程式码,用在编译时竟是如此处理这等问题,实非什麽好
    的构想.日後若是有更进步的最佳化方法具体整合到新的版本里,而你(或你的use
    rs)还是试著这样做的话,可能就会发现,gcc会中断你的程式(break your
    code)了.

    从gcc
    2.7.0到2.7.2的users应该注意到,使用时-O2会有一个bug存在.更糟糕的是,强度
    折减(strength reduction)居然没有用(doesn't
    work)!要是你喜欢重新编译gcc的话,是有那麽一个修正的版本(patch)可以更正諣
    庀畲砦?;不然的话,一定要确定每次编译时都会加上-fno-strength-reduce喔!

    11/12/97译

       4.2.1.1. 有个性的微处理器(Processor-specific)

    有一些-m的旗号无法藉由各种等级的-O来打开,然而却是十分有用的.这之中最主
    要的是-m386与-m486两种,用来告诉gcc该把正在编译的程式码视作专为386或是4
    86机器所写的.不论是用哪一种来编译程式码,都可以在彼此的机器上执行,-m486
    编译出来的码会比较大,可是拿来在386的机器上跑也不会比较慢就是了.

    目前尚无-mpentium或是-m586的旗号.Linus建议我们,可以用-m486
    -malign-loops=2 -malign-jumps=2
    -malign-functions=2,来得到最佳化的486程式码(486 code
    optimizations),而这样做正好就可以避免alignment(Pentium并不需要)有过大禒
    膅aps发生. Michael Meissner说:

      我的第六感(hunch)告诉我,
      -mno-strength-reduce(嘿!我可不是在谈强度折减的bug啊,那已緺
      是另外一个争论的战场了.)一样也可以在x86的机器上,产生较快的程式码,这
      是因为x86的机器对暂存器(register)有著不可磨灭的□渴在(and GCC's
      method of grouping registers into spill registers vs. other
      registers doesn't h位叭允抢醋造禠inus:

      要注意的是,如果你想要得到最佳状况的执行成果(optimal
      performance),可千万别相信我的话.无论如何,一定要进行测试.gcc编译器还
      有许多的参数(switches)可用,其中可能就有一种最特别的组合(set),可以给
      你最佳化的结果喔.

    11/14/97译

     4.2.2. Internal compiler error: cc1 got fatal signal 11

    Signal 11是指 SIGSEGV, 或者 `segmentation violation'.通常这是指
    说gcc对自己所用的指标感到困惑起来,而且还尝试著写入不属於它的记忆体.所覡
    ?,这可能是一个gcc的bug.

    然而,大部份而言,gcc是一件緺
    过严密测试且可靠度佳的软体佳作.它也用了大量复杂的资料结构与惊人的指标薁
    ?.简言之,若是要评选本世纪最挑惕与最一丝不□的RAM测试程式(RAM
    tester)的话,gcc绝对可以一摘后冠的.假如你无法重新复制这只bug---当你重新
    开始编译时,错误的讯息并没有一直出现在同一个地方---那几乎可以确定,是你禒
    挠蔡灞旧碛形侍?(CPU,记忆体,主机板或是快取记忆体).千万不要因为你的电脑繝
    梢酝ü绦虻牟馐?(power-on
    checks),或者Windows可以跑得很顺,或者其它什麽的,就回过头来大肆宣传说这薁
    莋cc的一个bug;你所做的这些测试动作,通常没有什麽实际上的价值,而且没有价
    值也是很合理的结论.另外,也不要因为编译核心时,总是停留在`make
    zImage'的阶段,就要大骂这是gcc的bug---当然它会停在那儿啊!做`make
    zImage'时,需要编译的档案可能超过200档案;我们正在研拟一个比较小的地方来
    取代.

    如果你可以重覆产生这个bug,而且(最好是这样啦)可以写一个短小的程式来展示
    这只bug的话,你就可以把它做成bug报表(bug
    report),然後email给FSF,或者是linux-gcc邮件表列(linux-gcc mailing
    list).你可以去参考gcc的说明文件,看看有什麽详细的资讯,是他们所需要的.

   4.3. 移植能力(Portability)

    据报,近日来许多正面消息指出,若有某件东东到现在都还没移植到Linux上去,那
    麽可以肯定的是,它一定一点价值也没有.:-)

    嗯!正疽坏?.一般而言,允悸胫恍枰鲆恍┚植康男薷?(minor
    changes),就可以克服(get over)Linux
    100%与POSIX相容的特质(compliance).如果你做了任何的修改,而将此部份传回(
    passing back)给誀
    作者,会是很有建设性的举动(worthwhile).这样日後就只需要用到'make',就能禒
    玫揭桓隹芍葱械牡蛋噶?.

     4.3.1. BSD教派(BSDisms) (有 bsd_ioctl, daemon 与 <sgtty.h>)

    编译程式时,可以配合-I/usr/include/bsd与连结-lbsd的程式库.(例如:在你的M
    akefile档内,把-I/usr/include/bsd加到CFLAGS那一行;把-lbsd加到LDFLAGS那覡
    恍?).如果你真的那麽想要BSD型态的信号行为(BSD type signal
    behavior),也不再需要加上-D__USE_BSD_SIGNAL了.那是因为当你用了-I/usr/in
    clude/bsd与含括了标头档<signal.h>之後,make就自动会把它加入了.

     4.3.2. 失落的封印(`Missing' signals)(SIGBUS, SIGEMT, SIGIOT, SIGTRAP,
     SIGSYS etc)

    Linux与POSIX是完全相容的.不过,有些信号并不是POSIX定义的---ISO/IEC
    9945-1:1990 (IEEE Std 1003.1-1990), paragraph B.3.3.1.1 sez:

      "在POSIX.1中省略了SIGBUS, SIGEMT, SIGIOT, SIGTRAP,
      与SIGSYS信号,那是因为它们的行为(behavior)与实作方式是息息相关的(imp
      lementations dependent),而且也无法进行适当的分门别类(adequately
      categorized).确认实作方式後(conforming
      implementations),便可以生产出(deliver)这些信号,可以必须以文件说明(d
      ocument)它们是在什麽样的环境(circumstances)下生产出来的,以及指出与藸
      堑姆⒄瓜喙氐娜魏蜗拗?(any restrictions concerning their
      delivery)".

    如欲修正此点,最简单,也是最笨的(cheesy)方法就是以SIGUNUSED重新定义这些袪
    藕?.而正确的方法应是以条件式的编译#ifdef来处理这些问题才对:

 #ifdef SIGSYS
 /* ... non-posix SIGSYS code here .... */
 #endif

    11/15/97译

     4.3.3. K & R

    gcc是个与ANSI相容的编译器;奇怪的是,目前大多数的程式码都不符合ANSI所定禒
    谋曜?.如果你热爱ANSI,喜欢用ANSI提供的标准来撰写C程式,似乎除了在编译器禒
    钠旌派霞由?-traditional之外,就没有什麽其它的可以多谈的了.There is a
    certain amount of finer-grained control over which varieties of brain
    damage to emulate;请自行查阅gcc info page.

    要注意的是,尽管你用了-traditional来改变语言,它的效果也仅局限在gcc所能範
    唤邮艿摹跷?.例如, -traditional会打开(turn
    on)-fwritable-strings,使得字串常数(string
    constants)移至资料记忆体空间(data space)内(从程式码记忆体空间(text
    space),这地方是不能任意写入的).这样做会让程式码的记忆体空间无形中增加禒
    ?.

     4.3.4. 前置处理器(Preprocessor)的符号卯上函数孕托?(prototypes)

    最常见的问题是,如众所皆知,Linux中有许多常用的函数都定义成巨集(macros)礌
    娣旁诒晖返?(header files)内,此时若有相似的函数誀
    型宣告出现在程式码内,前置处理器会拒绝进行语法分析(parse)的前置作业.常紶
    挠衋toi()与atol().

     4.3.5. sprintf()

    在大部份的Unix系统上, sprintf(string, fmt,
    ...)传回的是string的指标,然而,这方面Linux(遵褷
    ANSI)传回的却是放入string内的字元数目.进行移植时,尤其是针对SunOS,需有緺
    醯男?.

     4.3.6. fcntl 与相关的函数; FD_*家族的定义到底摆在哪里?

    就在 <sys/time.h>里头. 为了真正的誀
    型宣告,当你用了fcntl,可能你也想含括标头档<unistd.h>进来.

    一般而言,函数的manual page会在SYNOPSIS章节内列出需要的标头档.

     4.3.7. select()
     的计时(time-out)---程式执行时会处於忙碌-等待的状态(busy-waiting).

    很久很久以前, select()的计时参数(time-out
    parameter)只有读的属性(read-only)而已.即使到了最近,manual
    pages仍然有下面这段的警告:

      select()照理讲应该是藉由适当的修正时间的数值,再传回自誀
      始计时(original
      time-out)开始後所剩馀的时间.未来的版本可能会使这项功能实现.因此,就臓
      壳岸?,若假定在呼叫select()之後,计时指标(time-out
      pointer)仍然不会让人给修正过,可是一种非常不明智的想法喔!

    未来就在我们的眼前了!至少,在这儿你绝对可以看到.
    函数select()传回的,是扣除等待尚未到达的资料所耗费的时间後,其剩馀的时间
    值.如果在计时结束时,都没有资料传送进来,计时引数(time-out
    argument)便会设为0;如果接著还有任何的select(),以同样的time-out
    structure来呼叫,那麽select()便会立刻结束.

    若要修正这项问题,只要每次呼叫select()前,都把计时数值(time-out
    value)放到time-out structure内,就没有问题了.把下面的程式码,

       struct timeval timeout;
       timeout.tv_sec = 1; timeout.tv_usec = 0;
       while (some_condition)
             select(n,readfds,writefds,exceptfds,&timeout);

    改成,

       struct timeval timeout;
       while (some_condition) {
             timeout.tv_sec = 1; timeout.tv_usec = 0;
             select(n,readfds,writefds,exceptfds,&timeout);
       }

    这个问题,在有些版本的Mosaic里是相当著名的,只消一次的等待,Mosaic就挂了.
    Mosaic的萤幕右上角,是不是有个圆圆的,会旋转的地球动粻
    .那颗球转得愈快,就表示资料从网路上传送过来的速率愈慢!

     4.3.8. 产生中断的系统呼叫(Interrupted system calls)

       4.3.8.1. 徵兆(Symptom):

    当一支程式以Ctrl-Z中止(stop),然後再重新执行(restart)时--或者是其它可以
    产生Ctrl-C中断(interruption)信号的情况,如子程序(child
    process)终结(termination)等--系统就会抱怨说"interrupted system
    call"或是"write: unknown error",或者诸如此类的讯息.

       4.3.8.2. 问题点:

    POSIX的系统检查信号的次数,比起一些旧版的Unix是要多那麽一点.如果是Linux
    ,可能就会执行signal handlers了--

      * 非同步地(asynchronously)(计时器的滴答声)
      * 系统呼叫的传回值(on return from any system call)
      * 在下列系统呼叫的执行期间: select(), pause(), connect(),accept(),
        read() on terminals, sockets, pipes or files in /proc, write() on
        terminals, sockets, pipes or the line printer, open() on FIFOs,
        PTYs or serial lines,ioctl() on terminals, fcntl() with command
        F_SETLKW, wait4(), syslog(), any TCP or NFS operations.

    就其它的作业系统而言,你需要的可能就是下面这些系统呼叫(system calls)了:
    creat(), close(), getmsg(), putmsg(), msgrcv(), msgsnd(), recv(),
    send(), wait(), waitpid(), wait3(), tcdrain(), sigpause(), semop() to
    this list.

    在系统呼叫期间,若有一信号(那支程式本身应准备好handler因应了)产生,handl
    er就会被呼叫.当handler将控制权转移回系统呼叫时,它会侦测出它已緺
    产生中断,而且传回值会立刻设定成-1,errno设定成EINTR.程式并没有想到会发蔂
    庵质?,所以就会bottles out了.

    有两种修正的方法可以选择:

    (1) 对每个你自行安装(install)的signal
    handler,都须在sigaction旗号加上SA_RESTART.例如,把下列的程式,

   signal (sig_nr, my_signal_handler);

    改成,

   signal (sig_nr, my_signal_handler);
   { struct sigaction sa;
     sigaction (sig_nr, (struct sigaction *)0, &sa);
 #ifdef SA_RESTART
     sa.sa_flags |= SA_RESTART;
 #endif
 #ifdef SA_INTERRUPT
     sa.sa_flags &= ~ SA_INTERRUPT;
 #endif
     sigaction (sig_nr, &sa, (struct sigaction *)0);
   }

    要注意的是,当这部份的变更大量应用到系统呼叫之後,呼叫read(),
    write(),ioctl(), select(), pause() 与
    connect()时,你仍然得自行检查(check for)EINTR.如下所示.

    (2) 你自己得很明确地(explicitly)检查EINTR:

    这里有两个针对read()与ioctl()的例子.

    允嫉某淌狡?,使用read().

 int result;
 while (len > 0) {
   result = read(fd,buffer,len);
   if (result < 0) break;
   buffer += result; len -= result;
 }

    修改成,

 int result;
 while (len > 0) {
   result = read(fd,buffer,len);
   if (result < 0) { if (errno != EINTR) break; }
   else { buffer += result; len -= result; }
 }

    允嫉某淌狡?,使用ioctl().

 int result;
 result = ioctl(fd,cmd,addr);

    修改成,

 int result;
 do { result = ioctl(fd,cmd,addr); }
 while ((result == -1) && (errno == EINTR));

    注意一点,有些版本的BSD Unix,其内定的行为(default
    behaviour)是重新执行系统呼叫.若要让系统呼叫中断,得使用
    SV_INTERRUPT或SA_INTERRUPT旗号.

     4.3.9. 可以写入的字串(Writable strings)

    gcc对其users总怀抱著乐观的想法(optimistic
    view),相信当他们打算让某个字串当作常数来用时---那它就真的只是字串常数稜
    ?.因此,这种字串常数会储存在程式码的记忆体区段内(in the code area of
    the
    program).这块区域可以page到磁碟机的image上,避免耗掉swap的记忆体空间,而
    且任何尝试写入的举动都会造成分页的错误(segmentation
    fault).这可是一种特色呢!

    对老旧一点的程式而言,
    这可能会产生一个问题.例如,呼叫mktemp(),传递引数(arguments)是字串常数.
    mktemp()会尝试著在*适当的位置(in place)*重新写入它的引数.

    修正的方法不外乎(a)以-fwritable-strings编译,迫使gcc将此常数置放在资料紶
    且涮蹇占?(data space)内.或者(b)将侵犯地权的部份(offending
    parts)重新改写,配置一个不为常数的字串(non-constant
    string),在呼叫前,先以strcpy()将资料拷贝进去.

     4.3.10. 为什麽呼叫execl()会失败?

    那是因为你呼叫的方式不对.execl的第一个引数是你想要执行的程式名.第二个訝
    虢有囊岜涑赡闼艚械某淌降腶rgv阵列(array).记住:传统上,argv[0]是
    只有当程式没有带著引数执行时,才会有设定值.所以罗,你应该这样写:

 execl("/bin/ls","ls",NULL);

    而不是只有,

 execl("/bin/ls", NULL);

    执行程式而不带任何引数(with no
    arguments),可解释成(construe)是一种邀请函(invitation),目的是把此程式的
    动态程式库独立(dynamic library dependencies)的特性印出来(print
    out).至少,a.out是这样的.就ELF而言,事情就不是这样了.

    (如果你想得知此程式库的资讯,有一些更简单的介面可用;参考动态载入(dynami
    c loading)那一章节,或是ldd的manual page.)

    11/16/97译

      _________________________________________________________________

    The Linux GCC HOWTO中译版V0.1 : 移植(Porting)与编译(Compiling)程式
    Previous: GCC的安装(installation)与启用(setup)
    Next: Debugging and Profiling The Linux GCC HOWTO中译版V0.1 :
    Debugging and Profiling
    Previous: 移植(Porting)与编译(Compiling)程式
    Next: 连结(Linking)
      _________________________________________________________________

 5. Debugging and Profiling

   5.1. Preventative maintenance (lint)

    lint对Linux而言并没有很广泛的用途,主要是因为大部份的人都能满足於gcc所虪
    峁┑木嫜断?(warnings).可能最有用的就是-Wall参数了---这个参数的用途是
    要求gcc将所有的警告讯息显现出来.but probably has more mnemonic value
    if thought of as the thing you bang your head against.

    网路上有一个实用的public domain lint,位於
    ftp://larch.lcs.mit.edu/pub/Larch/lclint.我并不知道这个站到底有多好就?
    橇?.

   5.2. 除错(Debugging)

     5.2.1. 我要怎样做才能将除错资讯放到一支程式里头?

    你需要添加-g的参数来编译与连结程式,而且不可以用-fomit-frame-pointer参薁
    ?.事实上,你不需要重新编译所有的程式,只需重新编译目前你正在除错的部份即
    可.

    就a.out的格式(configurations)而言,共享程式库(shared
    libraries)是以-fomit-frame-pointer编译而成,这个时候,gdb就变得英雄无用螤
    渲亓?.连结时给定-g的选项,应该就隐含著静态连结(static
    linking)了;这就是为什麽要加-g的砸蛄?.

    如果连结器(linker)连结失败,告诉你找不到libg.a,那就是在/usr/lib/的目录禒
    紫?,少了libg.a.libg.a是特殊的C语言侦错程式库(special debugging-enabled
    C
    library).一般在libc的套件内就会提供libg.a;不然的话(新版是这样的),你可臓
    苄枰胠ibc的誀
    始码自己建立了.不过,实际上你应该不需要才对.不管是什麽目的,大部份的情况
    下,只需将libg.a连结到/usr/lib/libc.a,你就能得到足够的资讯了.

       5.2.1.1. 那,能不能把除错资讯给拿掉?

    很多的GNU软体在编译连结时,都会设定-g的选项,而这样做会造成执行档过大的螤
    侍?(通常是静态的).实际上,这并不是一个很热门的想法.

    如果程式本身有autoconf,产生了configure命令稿,通常你就可以用./configure
    CFLAGS=或是./configure
    CFLAGS=-O2来关掉除错资讯.不然的话,你得检查检查Makefile了.当然啦,假如你
    用的是ELF,程式便会以动态的方式连结(dynamically
    linked),不论是否有-g的设定;因此你可以平常心把-g拿掉(strip).

     5.2.2. 实用的软体(Available software)

    据了解,一般人都使用gdb.你可以从GNU archive sites拿到誀
    始程式;或者是到tsx-11拿可执行档.xxgdb是一个X介面的除错程式(debugger),譅
    不秅db(也就是说你得先安装好gdb,才能再装xxgdb).xxgdb的誀
    始码可以在ftp://ftp.x.org/contrib/xxgdb-1.08.tar.gz找到.

    另外,UPS除错程式已由Rick
    Sladkey移植成功.UPS可以在X底下活得很好,不像xxgdb那样---仅仅是gdb的X前稜
    私槊?(X front end).这支除错程式有一大堆优良的特点,and if you spend any
    time debugging stuff, you probably should check it
    out.先前编译(precompiled)好的Linux版与修正版(patches)的誀
    始码可以在ftp://sunsite.unc.edu/pub/Linux/devel/debuggers/找到.而最初?
    脑始程式则放在 ftp://ftp.x.org/contrib/ups-2.45.2.tar.Z.

    你可能会发现另一个用来除错的工具strace,也是相当的有用.它可以显示出由程
    序(process)所产生的系统呼叫,而且还拥有其它众多繁复的功能(multiplicity)
    ,像是如果你手边没有允悸氲幕?,strace可以帮你找出(figure
    out)有那些路径(path-names)已编译进执行档(binaries)内; exacerbating
    race conditions in programs that you suspect contain
    them;还有,strace可拿来学习程式是怎麽在电脑中执行的.最新的版本(目前是3.
    0.8)可在找到ftp://ftp.std.com/pub/jrs/.

     5.2.3. 背景程式(Background (daemon) programs)

    早期典型的常驻程式(daemon
    programs)是执行fork(),然後终止(terminate)父程序(parent).这样的做法使得
    除错的时间减短了.

    了解(get
    around)这点的最简单的方法就是替fork()设一个breakpoint.当程式停止时,强茽
    萬ork()传回0.

 (gdb) list
 1       #include <stdio.h>
 2
 3       main()
 4       {
 5         if(fork()==0) printf("child\n");
 6         else printf("parent\n");
 7       }
 (gdb) break fork
 Breakpoint 1 at 0x80003b8
 (gdb) run
 Starting program: /home/dan/src/hello/./fork
 Breakpoint 1 at 0x400177c4

 Breakpoint 1, 0x400177c4 in fork ()
 (gdb) return 0
 Make selected stack frame return now? (y or n) y
 #0  0x80004a8 in main ()
     at fork.c:5
 5         if(fork()==0) printf("child\n");
 (gdb) next
 Single stepping until exit from function fork,
 which has no line number information.
 child
 7       }

     5.2.4. 核心档案(Core files)

    当Linux开机时,通常组态(configuration)会设定成不要产生核心档案.要是你那
    麽喜欢它们的话,可以用shell的builtin命令使其重新生效:就C-shell相容的she
    ll(如tcsh)而言,会是下面这样:

 % limit core unlimited

    而类似Bourne shell的shell(sh,bash,zsh,pdksh)则使用下面的语法:

 $ ulimit -c unlimited

    如果你想要有个多才多艺(versatility)的核心档命名(core file naming)(for
    example, if you're trying to conduct a post-mortem using a debugger
    that's buggy itself)
    ,那麽你可以对你的核心程式(kernel)做一点小小的更动(mod).找一找fs/binfmt
    _aout.c与fs/binfmt_elf.c档内与下列相符的程式片段(in newer kernels,
    you'll have to grep around a little in older ones):

         memcpy(corefile,"core.",5);
 #if 0
         memcpy(corefile+5,current->comm,sizeof(current->comm));
 #else
         corefile[4] = '\0';
 #endif

    将0换成1.

   5.3. 旁敲侧击(Profiling)

    Profiling是用来检核一支程式中那些部份(which
    bits)是最常呼叫或是执行的时间最久的方法.这对程式的最佳化与找出何时时间
    是浪费掉的而言,是相当好的方式.你必须就你所要的时程资讯(timing
    information)的目的档案(object
    files)加上-p来编译,而且如果要让输出的档案(output files)有意义(make
    sense),你也会需要gprof(来自binutils套件的命令).参阅gprof的manual
    page,可得知其细节.

    11/18/97译

      _________________________________________________________________

    The Linux GCC HOWTO中译版V0.1 : Debugging and Profiling
    Previous: 移植(Porting)与编译(Compiling)程式
    Next: 连结(Linking) The Linux GCC HOWTO中译版V0.1 : 连结(Linking)
    Previous: Debugging and Profiling
    Next: 动态载入(Dynamic Loading)
      _________________________________________________________________

 6. 连结(Linking)

    由於静态(static)与共享(shared)程式库两者间不相容的格式(incompatible
    binary
    formats)的差异性(distinction)与动词*link*过量使用(overloading)於指称*睜
    嘁胪瓿舍岬氖虑?*与*当编译过的程式使用时(invoke)所发生的事情*这两件事上
    头,使得这一章节变得复杂了许多.( and, actually, the overloading of the
    word `load' in a comparable but opposite
    sense)不过,再复杂也就是这样了,所以阁下不必过於担心.

    为了稍微减轻读者的困惑,我们称执行期间(runtime)所发生的事为*动态载入(dy
    namic
    loading)*,这一主题会在下一章节中谈到.你也会在别的地方看到我把动态载入脿
    枋龀?*动态连结(dynamic
    linking)*,不过不会是在这一章节中.换句话说,这一章节所谈的,全部是指发生誀
    诒嘁虢崾岬牧?(linking).

   6.1. 共享程式库 vs静态程式库

    建立程式的最後一个步骤便是连结;也就是将所有分散的小程式(pieces)组合起罓
    ?,看看是否遗漏了些什麽.很明显的,有一些事情是很多程式都会想做的---例如,
    开启档案(open
    files),接著所有与开档有关的小程式(pieces)就会以程式库的档案型态提供给臓
    愕某淌?.在普通的Linux系统上,这些小程式可以在/lib与/usr/lib/目录底下找禒
    ?.

    当你用的是一静态的程式库时,连结器会找出程式所需的模组(bits),然後实际(p
    hysically)将它们拷贝到执行档内.然而,对共享程式库而言,就不是这样了.共享
    程式库会在执行档内留下一个符号(note),指明*当程式执行时,首先必须载入这笭
    龀淌娇?*.很明显的,共享程式库是试图使执行档变得更小;也等同於使用更少的紶
    且涮逵氪诺占?.Linux内定的行为是连结共享程式库,只要Linux能找到这些共蠣
    沓淌娇獾幕?,就没什麽问题;不然,Linux就会连结静态的了.如果你想要共享程式
    库的话,检查这些程式库(*.sa for a.out, *.so for
    ELF)是否住在它们该在的地方,而且是可读取的.

    在Linux上,静态程式库会有类似libname.a这样的名称;而共享程式库则称做libn
    ame.so.x.y.z,此处的x.y.z是指版本序号的样式.共享程式库通常都会有连结符籂
    胖赶蚓蔡淌娇?(很重要的)与(on a.out
    configurations)相关联的.sa档案.标准的程式库会包含共享与静态程式库两种笭
    袷?.

    你可以用ldd (List Dynamic
    Dependencies)来查出某支程式需要哪些共享程式库.

 $ ldd /usr/bin/lynx
         libncurses.so.1 => /usr/lib/libncurses.so.1.9.6
         libc.so.5 => /lib/libc.so.5.2.18

    这是说在我的系统上,WWW浏览器(browser)*lynx*会依赖libc.so.5 (the C
    library)与libncurses.so.1(终端机萤幕的控制)的存在(presence).若某支程式
    缺乏独立性(dependencies), ldd就会说`statically linked'或是`statically
    linked (ELF)'.

   6.2. Interrogating libraries (`which library is sin() in?')

    nm 程式库名称
    应该会列出此程式库名称所参考到的所有符号(symbols).这个指令可以应用在静
    态与共享程式库上.假设你想知道tcgetattr()是在哪儿定义的:你可以如此做

 $ nm libncurses.so.1 |grep tcget
          U tcgetattr

    *U*说明了*未定义(undefined)*---也就是说ncurses程式库有用到tegetattr(),
    但是并没有定义它.你也可以这样做,

 $ nm libc.so.5 | grep tcget
 00010fe8 T __tcgetattr
 00010fe8 W tcgetattr
 00068718 T tcgetpgrp

    *W*说明了*弱势(weak)*,意指符号虽已定义,但可由不同程式库中的另一定义所葼
    〈?(overridden).而最直接的(straightforward)*正常(normal)*定义(像是tcge
    tpgrp)是由*T*所标示.

    标题所谈的问题,最简明的答案便是libm.(so|a)了.所有定义在<math.h>的函数稜
    急A粼趍aths程式库内;因此,当你用到其中任何一个函数时,都需要以-lm的参数
    连结此程式库.

   6.3. X档案???

    ld: Output file requires shared library `libfoo.so.1`

    ld与其相类似的命令在搜寻档案的策略(strategy)上,会依据版本的差异而有所矤
    煌?,但是唯一一个你可以合理假设的内定目录便是/usr/lib了.如果你希望身处藸
    Φ某淌娇庖擦腥胨蜒暗男辛兄?,那麽你就必须以-L选项告知gcc或是ld.

    要是你发现一点效果也没有,就赶紧察看看那档案是不是还乖乖的躺在誀
    地.就a.out而言,以-lfoo参数来连结,会驱使ld去寻找libfoo.sa (shared
    stubs);如果没有成功,就会换成寻找libfoo.a (static).就ELF而言,
    ld会先找libfoo.so,然後是libfoo.a.libfoo.so通常是一个符号连结,连结至lib
    foo.so.x.

   6.4. 建立你自己的程式库(Building your own libraries)

     6.4.1. 版本控制(Version control)

    与其它任何的程式一样,程式库也有修正不完的bugs的问题存在.它们也可能产生
    出一些新的特点,更改目前存在的模组的功效,或是将旧的移除掉.这对正在使用藸
    堑某淌蕉?,可能会是一个大问题.如果有一支程式是根据那些旧的特点来执袪
    械幕?,那怎麽办?

    所以,我们引进(introduce)了程式库版本编号(versioning)的观念.我们将程式繝
    ?*次要(minor)*与*主要(major)*的变更分门别类(categorize),同时我们规定*礌
    我?*的变更是不允许用到这程式库的旧程式发生中断的现象(break).你可以从程
    式库的档名分辨出它的版本(实际上,严格来讲,对ELF而言仅仅是一场天大的谎言
    ;继续读将下去,便可明白为什麽了):
    libfoo.so.1.2的主要版本是1,次要版本是2.次要版本的编号可能真有其事,也可
    能什麽都没有---libc在这一点上用了*修正程度(patch-level)*的观念,而给出翣
    嗣葡駆ibc.so.5.2.18这样的程式库.次要版本的编号内若是放一些字母,底线,
    或是任何可以列印的ASCII字元,也是很合理的说.

    ELF与a.out格式最主要的差别之一就是在建立共享程式库上.我们先看ELF,因为藸
    冉霞虻ヒ恍?.

     6.4.2. ELF? 它到底是什麽东东ㄋㄟ?

    ELF (Executable and Linking Format) 最初是由USL(UNIX System
    Laboratories)所发展的二进位格式(binary
    format),而目前正应用於Solaris与System V Release
    4上面.由於ELF所增涨的弹性(flexibility)远远超过Linux过去所用的a.out格式
    ,因此GCC与C程式库的发展人士於去年(1995)决定改用ELF为Linux标准的二进位笭
    袷?.

       6.4.2.1. 怎麽又来了?

    这一节是来自於'/news-archives/comp.sys.sun.misc'的文件.

      ELF("Executable Linking
      Format")是於SVR4所引进的新式改良目的档格式.ELF比起COFF可是多出了不蔂
      俚墓δ?.以ELF而言,它*是*可由使用者自行延伸的(user-extensible).ELF视
      一目的档为节区(sections)如串列般的组合,而且此串列可为任意的(arbitra
      rily)长度(而不是一固定大小的阵列).这些节区与COFF的不一样,并不需要固
      定在某个地方,也不需要以某种顺序排列.如果users希望能补捉到新的资料,藸
      潜憧梢约尤胄碌慕谇侥康牡的?.ELF也有一个更强而有力的除错格式,称螤
      狣WARF(Debugging With Attribute Record
      Format)-目前Linux并不完全支援.DWARF DIEs(Debugging Information
      Entries)的连结串列会在ELF内形成.debug的节区.DWARF
      DIEs的每一个.debug节区并非一些少量的(small)且固定大小的(fixed-size)
      资讯记录(information
      records)的集合(collection),而是一任意长度的串列,拥有复杂的属性,而且
      程式的资料会以□围为根据的树状资料结构(scope-based
      tree)写出来.DIEs所能补捉到的大量资讯是COFF的.debug节区无法望其项背禒
      ?.(像是C++的继承图).

      ELF档案是从SVR4(Solaris 2.0 ?)ELF存取程式库(ELF access
      library)内存取的.此程式库可提供一简便快速的介面予ELF.使用ELF存取程薁
      娇庾钪饕亩骰葜槐闶?,你不再需要去察看一个ELF档的qua了.就UNIX的档
      案而言,它是以Elf*的型式来存取;呼叫elf_open()之後,从此时开始,你只需籂
      艚衑lf_foobar()来处理档案的某一部份(components)即可,并不需要把档案薁
      导试诖诺系膇mage搞得一团乱.

    ELF的优缺点与升级至ELF等级所需緺
    历的种种痛苦(contortions),已在ELF-HOWTO内论及;我并不打算在这儿涂浆糊.E
    LF HOWTO应该与这份文件相同之处有同样的主题才是.

       6.4.2.2. ELF共享程式库

    若想建立libfoo.so成为共享程式库,基本的步骤会像下面这样:

 $ gcc -fPIC -c *.c
 $ gcc -shared -Wl,-soname,libfoo.so.1 -o libfoo.so.1.0 *.o
 $ ln -s libfoo.so.1.0 libfoo.so.1
 $ ln -s libfoo.so.1 libfoo.so
 $ LD_LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATH ; export LD_LIBRARY_PATH

    这会产生一个名为libfoo.so.1.0的共享程式库,以及给予ld适当的连结(libfoo.
    so)还有使得动态载入程式(dynamic
    loader)能找到它(libfoo.so.1).为了进行测试,我们将目前的目录加到LD_LIBRA
    RY_PATH里.

    当你津津乐道於程式库制做成功之时,
    别忘了把它移到如/usr/local/lib的目录底下,并且重新建立正确的连结路径.
    libfoo.so.1与libfoo.so.1.0的连结会由ldconfig依日期不断的更新;就大部份禒
    南低忱此?,ldconfig会在开机程序中执行.
    libfoo.so的连结必须由手动方式更新.如果你对程式库所有组成份子(如标头档禒
    ?)的升级,总是抱持著一丝不□的态度(scrupulous),那麽最简单的方法就是让li
    bfoo.so ->
    libfoo.so.1;如此一来,ldconfig便会替你同时保留最新的连结.要是你没有这麽
    做,你自行设定的东东就会在数日後造成千奇百怪的花样出现.到时候,可别说我脿
    惶嵝涯惆?!

 $ su
 # cp libfoo.so.1.0 /usr/local/lib
 # /sbin/ldconfig
 # ( cd /usr/local/lib ; ln -s libfoo.so.1 libfoo.so )

       6.4.2.3. 版本编号, soname与符号连结

    每一个程式库都有一个soname.当连结器发现它正在搜寻的程式库中有这样的一笭
    雒?,连结器便会将soname箝入(embed)连结中的二进位档内,
    而不是它正在运作的实际的档名.在程式执行期间,动态载入程式会搜寻拥有sona
    me这样的档名的档案,而不是程式库的档名.因此,一个名为libfoo.so的程式库,緺
    涂梢杂幸桓鰈ibbar.so的soname了.而且所有连结到libbar.so的程式,当程式开薁
    贾葱惺?,会寻找的便是libbar.so了.

    这听起来好像一点意义也没有,但是这一点,对於了解数个不同版本的同一个程式
    库是如何在单一系统上共存(coexist)的誀
    因,却是关键之钥.Linux程式库标准的命名方式,比如说是libfoo.so.1.2,而且给
    这个程式库一个libfoo.so.1的soname.如果此程式库是加到标准程式库的目录底
    下(e.g. /usr/lib),ldconfig会建立符号连结libfoo.so.1 ->
    libfoo.so.1.2,使其正确的image能於执行期间(run-time)找到.你也需要连结li
    bfoo.so -> libfoo.so.1,使ld能於连结期间(link-time)找到正确的soname.

    所以罗,当你修正程式库内的bugs,或是添加了新的函数进去(任何不会对现存的碃
    淌皆斐刹焕?(adversely)影响的改变),你会重建此程式库,保留誀
    本已有的soname,然後更改程式库档名.而当你对程式库的变更会使得现有的程式
    (binaries)中断(break),那麽你只需增加soname中的编号---此例中,称新版本为
    libfoo.so.2.0,而soname变成libfoo.so.2.紧接著,再将libfoo.so的连结转向新
    的版本;至此,世界又再度恢复了和平!

    其实你不须要以此种方式来替程式库命名,不过这的确是个好的传统(convention
    ).ELF所赋予你在程式库命名上的弹性,会使得人气礌
    呼呼的搞不清楚状况;有这样的弹性在,也并不表示你就得去用它.

    ELF总结:假设緺
    由你睿智的观察发现有个惯例说:程式库主要的升级会破坏(break)相容性(compa
    tibility);而次要的升级则可能不会;那麽以下面的方式来连结,所有的一切就都
    会相安无事了.

 gcc -shared -Wl,-soname,libfoo.so.major -o libfoo.so.major.minor

     6.4.3. a.out---旧旧的格式~

    建立共享程式库的便利性(ease)是升级至ELF的主要誀
    因之一.那也是说,a.out可能还是有用处在的.上ftp站去抓ftp://tsx-11.mit.ed
    u/pub/linux/packages/GCC/src/tools-2.17.tar.gz;解压缩後你会发现有20页禒
    奈募梢月亩亮?.我很不喜欢自己党派的偏见(partisan)表现得那麽的淋璃
    尽致,可是从上下文间,应该也可以很清楚的嗅出我从来不拿石头砸自己的脚的脾
    气吧!:-)

       6.4.3.1. ZMAGIC vs QMAGIC

    QMAGIC是一种类似旧格式的a.out(亦称为ZMAGIC)的可执行档
    格式,这种格式会使得第一个分页无法map.当0-4096的□围内没有mapping存在时
    ,则可允许NULL dereference trapping更加的容易.所产生的边界效应(side
    effect)是你的执行档会比较小(大约是1K左右).

    只有即将作废的连结器有支援ZMAGIC,一半已埋入棺材的连结器有支援这两种格薁
    ?;而目前的版本仅支援QMAGIC而已.事实上,这并没有多大的影响,那是因为目前禒
    暮诵牧街指袷蕉寄苤葱?.

    *file*命令应该可以确认程式是不是QMAGIC的格式的.

       6.4.3.2. 档案配置(File Placement)

    一a.out(DLL)的共享程式库包含两个真实的档案与一个符号连结.就*foo*这个用
    於整份文件做为□例的程式库而言,这些档案会是libfoo.sa与libfoo.so.1.2;符
    号连结会是libfoo.so.1,而且会指向libfoo.so.1.2.这些是做什麽用的?

    在编译时,
    ld会寻找libfoo.sa.这是程式库的*stub*档案,而且含有所有执行期间连结所需禒
    膃xported的资料与指向函数的指标.

    执行期间,动态载入程式会寻找libfoo.so.1.这仅仅是一个符号连结,而不是真实
    的档案,故程式库可更新成较新的且已修正错误的版本,而不会损毁任何此时正在
    使用此程式库的应用程式.在新版---比如说libfoo.so.1.3---已完整呈现时,ldc
    onfig会以一极微小的操作,将连结指向新的版本,使得任何誀
    本使用旧版的程式不会感到丝毫的不悦.

    DLL程式库(我知道这是无谓的反覆(tautology)---所以对我提出告诉吧!)通常会
    比它们的静态副本(static
    counterparts)要来得大多了.它们是以*洞(holes)*的形式来保留空间以便日後禒
    睦┏?.这种*洞*可以不占用任何的磁碟空间.一个简单的cp呼叫,或是使用makeho
    le程式,就可以达到这样效果(achieve).因为它们的位址是固定在同一位置上,所
    以在建立程式库後,你可以把它们拿掉.千万不要试著夺走ELF的程式库.

       6.4.3.3. ``libc-lite''?

    libc-lite是轻量级(light-weight)的libc版本.可用来存放在磁碟片上,与
    替大部份低微的(menial)UNIX任务收尾(suffice).它没有包含curses, dbm,
    termcap等等的程式码.如果你的/lib/libc.so.4是连结到一个lite的libc,那麽綘
    ㄒ槟阋酝暾陌姹救〈?.

     6.4.4. 连结:常见的问题

    把你连结时所遭遇的问题寄给我!我可能什麽事也不会做,但是只要累积了足够的
    数量, 我会把它们写起来*.

    你想共享,偏偏程式却连结成静态的!
           检查你提供给ld的连结是否正确,使ld能找到每一个对应的共享程式库.緺
           虴LF而言,这是指一个符号连结libfoo.so,连结至image;就a.out而言,就
           是libfoo.sa档了.很多人将ELF binutils
           2.5升级至2.6之後,就产生了这个问题---早期的版本搜寻共享程式库时綘
           嫌兄腔?,所以并没有将所有的连结建立起来.後来,为了与其它的架构相葼
           ?,这项充满智慧的行为被人给删除掉了,另外,这样的*智慧*判断错误的粻
           氏嗟备?,所造成的麻烦比它所解决的问题还多,所以留著也是害人精,矤
           蝗绻槿ベ?!

    The DLL tool `mkimage' fails to find libgcc, or
           从libc.so.4.5.x之後,libgcc已不再是共享的格式.因此,你必须在*-lgc
           c*出现之处以`gcc
           -print-libgcc-file-name`取代(完整的倒单引号(back-quotes)).另外,
           删除所有/usr/lib/libgcc*的档案.这点很重要哩.

    __NEEDS_SHRLIB_libc_4 multiply defined messages
           是同样的问题所造成的另一种结果.

    ``Assertion failure'' message when rebuilding a DLL ?
           这一条神秘的(cryptic)讯息最有可能的砸蚴?,在誀
           始的jump.vars档案内,由於保留的空间太少, 以致於造成其中一个jump
           table
           slots溢满(overflow).你可以执行tools-2.17.tar.gz套件所提供的`get
           size'命令,定出所有嫌疑犯(culprit(s))的踪迹.可能唯一的解决方法是
           ,解除(bump)此程式库主要的版本编号,强迫它回到不相容的年代(be
           backward incompatible).

    ld: output file needs shared library libc.so.4
           通常这是发生在当你连结的程式库不是libc(如X程式库),而且在命令列訝
           昧?-g的参数,却没有一并使用-static,所发出的错误讯息.

           共享程式库的.sa
           stubs通常有一个未定义的符号_NEEDS_SHRLIB_libc_4;而这一点可藉由l
           ibc.sa
           stub来解决.然而,以-g来编译时,会使得连结以libg.a或libc.a来结束;覡
           虼苏飧龇乓恢本兔挥薪饩?,也就会导致上面的错误讯息了.

           总之,以-g的旗号编译时别忘了加上-static,不然就别用-g来连结.通常,
           以-g编译各个独立的档案时,所获得的除错资讯已緺
           足够,连结时就可以不需要它了.

      _________________________________________________________________

    The Linux GCC HOWTO中译版V0.1 : 连结(Linking)
    Previous: Debugging and Profiling
    Next: 动态载入(Dynamic Loading) The Linux GCC HOWTO中译版V0.1 :
    动态载入(Dynamic Loading)
    Previous: 连结(Linking)
    Next: 与发展人士联络
      _________________________________________________________________

 7. 动态载入(Dynamic Loading)

    这一章节目前是简短了一点;当我掠尽ELF
    HOWTO时,就是这部份再度扩展的时候了.

   7.1. 基本概念

    Linux有共享程式库,如果之前你已坐著读完上一章节,想必现在一听到像这样的藸
    荡?,便会立刻感到头昏.有一些照惯例而言是在连结时期便该完成的工作(matchi
    ng-names-to-places),必须延迟到载入时期(load-time)才能完成.

   7.2. 错误讯息(Error messages)

    把你连结的错误寄给我!我不会做任何的事,不过我可以把它们写起来**

    can't load library: /lib/libxxx.so, Incompatible version
           (a. out only)
           这是指你没有xxx程式库的正确的主要版本.可别以为随随
           便便弄个连结到你目前拥有的版本就可以了,如果幸运的话,
           就只会造成你的程式分页错误而已.去抓新的版本.ELF类似的情况会造成
           像下面这样的讯息:

 ftp: can't load library 'libreadline.so.2'

    warning using incompatible library version xxx
           (a. out
           only)你的程式库的次要版本比起这支程式用来编译的还要旧.程式依然繝
           梢灾葱?.只是可能啦!我想,升个级应该没什麽伤害吧!

   7.3. 控制动态载入器的运作

    有一组环境变数会让动态载入器有所反应.大部份的环境变数对ldd的用途要比起
    对一般users的还要来得更多.而且可以很方便的设定成由ldd配合各种参数来执袪
    ?.这些变数包括

      * LD_BIND_NOW --- 正常来讲,函数在呼叫之前是不会让程式寻找(looked
        up)的.设定这个旗号会使得程式库一载入,所有的寻找(lookups)便会发生,蜖
        币苍斐善鹗嫉氖奔?(startup
        time)较慢.当你想测试程式,确定所有的连结都没有问题时,这项旗号就变得
        很有用.
      * LD_PRELOAD
        可以设定一个档案,使其具有*覆盖*(overriding)函数定义的能力.例如,如範
        阋馐约且涮宸峙涞姆铰?(strategies),而且还想置换*malloc*,那麽你繝
        梢孕春米急柑婊坏母背淌?(routine),并把它编译成mallolc.,然後:

 $ LD_PRELOAD=malloc.o; export LD_PRELOAD
 $ some_test_program
        LD_ELF_PRELOAD 与 LD_AOUT_PRELOAD
        很类似,但是仅适用於正确的二进位型态.如果设定了
        LD_something_PRELOAD 与 LD_PRELOAD ,比较明确的那一个会被用到.
      * LD_LIBRARY_PATH
        是一连串以分号隔离的目录名称,用来搜寻共享程式库.对ld而言,并没有任籂
        蔚挠跋?;这项只有在执行期间才有影响.另外,对执行setuid与setgid的程式
        而言,这一项是无效的.而LD_ELF_LIBRARY_PATH与LD_AOUT_LIBRARY_PATH这翣
        街制旌趴筛莞鞅鸬亩恍褪椒直鸬枷虿煌乃蜒奥肪?.一般正常的运作
        下,不应该会用到LD_LIBRARY_PATH;把需要搜寻的目录加到/etc/ld.so.conf
        /里;然後重新执行ldconfig.
      * LD_NOWARN 仅适用於a.out.一旦设定了这一项(LD_NOWARN=true; export
        LD_NOWARN),它会告诉载入器必须处理fatal-warnings(像是次要版本不相容
        等)的警告讯息.
      * LD_WARN 仅适用於ELF.设定这一项时,它会将通常是致命讯息的"Can*t find
        library"转换成警告讯息.对正常的操作而言,这并没有多大的用处,可是对l
        dd就很重要了.
      * LD_TRACE_LOADED_OBJECTS
        仅适用於ELF.而且会使得程式以为它们是由ldd所执行的:

 $ LD_TRACE_LOADED_OBJECTS=true /usr/bin/lynx
         libncurses.so.1 => /usr/lib/libncurses.so.1.9.6
         libc.so.5 => /lib/libc.so.5.2.18

   7.4. 以动态载入撰写程式

    如果你很熟悉Solaris
    2.x所支援的动态载入的工作的话,你会发现Linux在这点上与其非常的相近.这一
    部份在H.J.Lu的ELF程式设计文件内与dlopen(3)的manual
    page(可以在ld.so的套件上找到)上有广泛的含盖(cover).这里有个不错的简单
    趵?:以-ldl连结.

 #include <dlfcn.h>
 #include <stdio.h>

 main()
 {
   void *libc;
   void (*printf_call)();

   if(libc=dlopen("/lib/libc.so.5",RTLD_LAZY))
   {
     printf_call=dlsym(libc,"printf");
     (*printf_call)("hello, world\n");
   }

 }

      _________________________________________________________________

    The Linux GCC HOWTO中译版V0.1 : 动态载入(Dynamic Loading)
    Previous: 连结(Linking)
    Next: 与发展人士联络 The Linux GCC HOWTO中译版V0.1 : 与发展人士联络
    Previous: 动态载入(Dynamic Loading)
    Next: 结语
      _________________________________________________________________

 8. 与发展人士联络

   8.1. Bug报表

    把问题写下来.这是针对Linux的,亦或是gcc在其它系统上所发生的问题.与kerne
    l的版本相关吗?或者是程式库的版本?如果改用静态方式连结,问题是不是就消失
    了?你可以节录一小段程式来展示这只bug吗?

    当你做了这些事情之後,你将会知道程式内的bugs是什麽.就gcc而言,bug报表程袪
    蚴且詉nfo档来说明的.如果是ld.so或是C,maths程式库,将email寄到linux-gcc@
    vger.rutgers.edu.如果可能的话,包含一支自己自足的小程式以展示这个bug,而
    且附上说明,描述你想要让这支程式做些什麽与实际上它又做了些什麽.

   8.2. 兄⒄範

    如果你想要帮忙发展gcc或是C程式库,第一件事便是加入linux-gcc@vger.rutger
    s.edu邮件表列(mailing list).如果你只是想看看mailing
    list在讨论些什麽,这里有一个表列的archives,位於http://homer.ncm.com/lin
    ux-gcc/.接下来的事,就看你想做什麽了.

      _________________________________________________________________

    The Linux GCC HOWTO中译版V0.1 : 与发展人士联络
    Previous: 动态载入(Dynamic Loading)
    Next: 结语 The Linux GCC HOWTO中译版V0.1 : 结语
    Previous: 与发展人士联络
    Next: 索引
      _________________________________________________________________

 9. 结语

   9.1. 名人榜

      Only presidents, editors, and people with tapeworms have the right
      to use the editorial ``we''.

    (Mark Twain)

    这份HOWTO文件几乎完全根植於Mitchum Dsouza的GCC-FAQ;
    文件内大部份的资讯(not to mention a reasonable amount of the
    text)是直接来自於GCC-FAQ的.
    这份HOWTO文件用到的第一人称代名词,可视为我们两人其中一个;通常,要是有人
    说"我还没有测试过这些;如果它烧了(toast)你的硬碟/系统/配偶,可别怪我!",臓
    钦庋幕笆视渺段伊┥砩?.

    对这份文件有贡献的名人雅士如下所列(以名字的ASCII码的顺序): Andrew
    Tefft, Axel Boldt, Bill Metzenthen, Bruce Evans, Bruno Haible, Daniel
    Barlow, Daniel Quinlan, David Engel, Dirk Hohndel, Eric Youngdale,
    Fergus Henderson, H.J. Lu, Jens Schweikhardt, Kai Petzke, Michael
    Meissner, Mitchum DSouza, Olaf Flebbe, Paul Gortmaker, Rik Faith,
    Steven S. Dick, Tuomas J Lukka, 当然还有Linux
    Torvalds,没有了他,这整个运动就会变得一点意义也没有了,所以不可能让他孤禒
    サ?.:-)

    请不要觉得有任何的冒犯之处,如果您的名字没有出现在这儿,而您对这份文件(H
    OWTO或是FAQ)又曾居泄毕椎幕?,请email给我,我会改正过来的.

   9.2. 芬霠

    到目前为止,这份文件还没有已知的窢
    译版出现.如果你希望生一个出来,请尽管去做,不过一定得告诉我相关的事宜.我
    讲的语言会是你想要窢
    的语言,那机率(很遗憾)只有好几百分之一,不过还是先把这摆一旁吧!不管是什鳡
    岱绞?,我都会很乐意帮忙的.

   9.3. 欢尤魏蔚幕乩?(Feedback)

    寄信给我dan@detached.demon.co.uk.我的PGP public key (ID 5F263625)
    可在我的烘培鸡web pages上使用, 如果你觉得事情有必要保密的话.

   9.4. 合法的行迳规定

    All trademarks used in this document are acknowledged as being owned
    by their respective owners.

    This document is copyright (C) 1996 Daniel Barlow
    <dan@detached.demon.co.uk> It may be reproduced and distributed in
    whole or in part, in any medium physical or electronic, as long as
    this copyright notice is retained on all copies. Commercial
    redistribution is allowed and encouraged; however, the author would
    like to be notified of any such distributions.

    All translations, derivative works, or aggregate works incorporating
    any Linux HOWTO documents must be covered under this copyright notice.
    That is, you may not produce a derivative work from a HOWTO and impose
    additional restrictions on its distribution. Exceptions to these rules
    may be granted under certain conditions; please contact the Linux
    HOWTO coordinator at the address given below.

    In short, we wish to promote dissemination of this information through
    as many channels as possible. However, we do wish to retain copyright
    on the HOWTO documents, and would like to be notified of any plans to
    redistribute the HOWTOs.

    If you have questions, please contact Greg Hankins, the Linux HOWTO
    coordinator, at gregh@sunsite.unc.edu via email.

      _________________________________________________________________

    The Linux GCC HOWTO中译版V0.1 : 结语
    Previous: 与发展人士联络
    Next: 索引 The Linux GCC HOWTO中译版V0.1 : 索引
    Previous: 结语
    Next: The Linux GCC HOWTO中译版V0.1
      _________________________________________________________________

 10. 索引

    以非文数字的(non-alphabetical)字元起始的入口处(entries),是以ASCII码的藸
    承蚺帕械?.

      * -fwritable-strings 39 56
      * /lib/cpp 16
      * a.out 1
      * ar 10
      * as 8
      * <asm/*.h> 19
      * atoi() 40
      * atol() 41
      * binaries too big 63 65 77
      * chewing gum 3
      * cos() 68
      * debugging 59
      * dlopen() 82
      * dlsym() 83
      * documentation 4
      * EINTR 52
      * elf 0 71
      * execl() 57
      * fcntl 47
      * FD_CLR 44
      * FD_ISSET 45
      * FD_SET 43
      * FD_ZERO 46
      * file 2
      * <float.h> 20
      * gcc 6
      * gcc -fomit-frame-pointer 61
      * gcc -g 60
      * gcc -v 14
      * gcc, bugs 15 28 29 84
      * gcc, flags 13 25 26
      * gdb 64
      * header files 17
      * interrupted system calls 51
      * ld 9
      * LD_* environment variables 80
      * ldd 81
      * libc 7
      * libg.a 62
      * libgcc 79
      * <limits.h> 21
      * lint 58
      * <linux/*.h> 18
      * manual pages 5
      * <math.h> 70
      * maths 69
      * mktemp() 55
      * optimisation 27
      * QMAGIC 76
      * segmentation fault 30 54
      * segmentation fault, in GCC 33
      * select() 50
      * SIGBUS 34
      * SIGEMT 35
      * SIGIOT 36
      * SIGSEGV 31 53
      * SIGSEGV, in gcc 32
      * SIGSYS 38
      * SIGTRAP 37
      * sin() 67
      * soname 73
      * sprintf() 42
      * statically linked binaries, unexpected 66 78
      * <stdarg.h> 23
      * <stddef.h> 24
      * strings 11
      * <sys/time.h> 48
      * <unistd.h> 49
      * <varargs.h> 22
      * version numbers 12 74
      * weird things 72
      * ZMAGIC 75

      _________________________________________________________________

    The Linux GCC HOWTO中译版V0.1 : 索引
    Previous: 结语
    Next: The Linux GCC HOWTO中译版V0.1

--

████████████████████
█ 曼桢有这么个脾气,一样东西一旦属于她█
█ 了, 她总是越看越好,以为它是世界上最█
█ 好的,...他知道,因为他曾经是属于她的█
████████████████████

※ 来源:.郁金香 BBS bbs.stu.edu.cn.[FROM: energy-lx.stu.ed]
--
※ 转寄:.郁金香 BBS bbs.stu.edu.cn.[FROM: 202.96.151.222]
--
※ 转载:.BBS 荔园晨风站 bbs.szu.edu.cn.[FROM: 192.168.0.141]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店