《嵌入式Linux开发实践教程 第2版》 课件 项目4 使用嵌入式Linux常用开发工具_第1页
《嵌入式Linux开发实践教程 第2版》 课件 项目4 使用嵌入式Linux常用开发工具_第2页
《嵌入式Linux开发实践教程 第2版》 课件 项目4 使用嵌入式Linux常用开发工具_第3页
《嵌入式Linux开发实践教程 第2版》 课件 项目4 使用嵌入式Linux常用开发工具_第4页
《嵌入式Linux开发实践教程 第2版》 课件 项目4 使用嵌入式Linux常用开发工具_第5页
已阅读5页,还剩172页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

编译程序-GCC物联网学院平震宇了解GCCGCC原名为GNUC语言编译器(GNUCCompiler),GCC(GNUCompilerCollection,GNU编译器套件)Fortran、Pascal、Objective-C、Java、Ada、Go

GCC版本1987年GCC发布首个公开发布版本。2012年为了庆祝25周年,GCC发布了GCC4.7.0版本,这是GCC一个全新的重要版本。

GCC8.2

released

[2018-07-26]GCC8.1

released

[2018-05-02]GCC7.3

released

[2018-01-25]GCC5.5

released

[2017-10-0]GCC7.2

released

[2017-08-14]GCC6.4

released

[2017-07-04]GNUBinutilsGNUBinutils,即GNUBinaryUtilities的简写,一般简称为Binutils。GNUBinutils,中文可以翻译为GNU的二进制工具集。GNUBinutils,是一组二进制工具的集合。

GNUBinutils中包含的主要工具ld链接器

将多个目标文件,链接成一个可执行文件(或目标库文件)。as汇编器

将汇编源代码,编译为(目标)机器代码

GNUBinutils中包含的其他次要工具addr2line

将地址转换为(文件名和)行号的工具,以便于调试ar

用来操作(.a)文件,比如创建,修改,提取内容等.c++filt

由于每一个重载的函数都使用与原函数相同的名称,因此,支持函数重载的语言必须拥有一种机制,以区分同一个函数的许多重载版本。c++filt将每个输入的名称看成是改编后的名称(mangledname),并设法确定用于生成该名称的编译器。dlltool

基于obj文件(基中函数标记为__declspec(dllexport))或def文件生成exp和lib文件。前者(exp文件,其中包含了.edata段)是用于创建dll,后者(lib文件,包含.idata段)用于使用dll。

GNUBinutils中包含的其他次要工具gold

一个新的,速度更快的,只针对于ELF的链接器gprof

打印出程序运行中各个函数消耗的时间,可以帮助程序员找出众多函数中耗时最多的函数。nm

列举目标文件中的符号以及与符号有关的一些信息。Objcopy

把目标文件的内容从一种文件格式复制到另一种格式的目标文件中。Objdump

显示目标文件信息,可以反编译二进制文件,也可以对对象文件进行反汇编,并查看机器代码。

GNUBinutils中包含的其他次要工具ranlib

生成索引以加快对归档文件的访问,并将结果保存到这个归档文件中readelf

显示符号、段信息、二进制文件格式的信息等,这在分析编译器如何工从源代码创建二进制文件时非常有用。size

可以列出目标文件或者一个归档文件每一段的大小以及总体的大小strings

用于提取文件中的字符串内容,使用该工具不会受到文件格式的限制。

strip

去除符号,比如用于debug的信息,在不影响程序功能的前提下,减少可执行文件的大小,减少程序的占用空间

任务要求StepOne安装GCC编译器StepTwoGCC的编译过程StepThreeGCC编译实例StepOne安装GCC通过GCC–v查看当前的GCC版本gcc-v解压gcc源码包tarxzvfgcc-4.7.4.tar.gzStepOneinstallgmp./configure--prefix=/opt/gmp-4.3.2installmpfr./configure--prefix=/opt/mpfr-2.4.2--with-gmp=/opt/gmp-4.3.2installmpcStepOne配置../gcc-4.7.4/configure--prefix=/opt/gcc-4.7--with-gmp=/opt/gmp-4.3.2--with-mpfr=/opt/mpfr-2.4.2--with-mpc=/opt/mpc-1.0.1--enable-languages=c,c++,java编译安装makemakeinstallStep2程序的编译过程

.C预处理.s.i编译.o汇编hex链接预处理阶段gcc–Ehello.c–ohello.iStep2程序的编译过程

.C预处理.s.i编译.o汇编.o链接编译阶段gcc–S

hello.i–ohello.sStep2程序的编译过程

.C预处理.s.i编译.o汇编.o链接汇编阶段gcc–chello.s–ohello.oStep2程序的编译过程

.C预处理.s.i编译.o汇编.o链接链接阶段gcchello.o–ohelloStep3

GCC编译实例编译简单的C程序gcc-g-Wallhello.c–ohello选项""-g""表示在生成的目标文件中带调试信息选项-Wall开启编译器几乎所有常用的警告warning:前面是悬崖,不要再往前走了。-w

:关闭所有警告-Wall:开启所有错误提示-Werror:把警告当作错误来处理警告选项-gformat:指定要生成的调试信息的格式以提供给其他平台的其他调试器使用-g:以操作系统的本地格式(stabs,COFF,XCOFF,或DWARF)产生调试信息-pg:产生额外代码用于输出profile信息,供分析程序gprof使用调试选项

#include<head.h>#include“myhead.h”gcc-vhello.c-I/home/test-I

dir在头文件的搜索路径列表中添加dir目录头文件的处理链接库的处理CompilerC源文件Olib目标文件Linkerhex可执行文件静态库动态库libc.so#标准C库,动态链接库libpthread.a#线程库,静态链接库一个特殊静态链接库方式,把库完整名字加入gcc-ohellohello.olibmy.a-l参数用来链接库标准表达式方式-lpthread表示链接libpthread.sogcchello.c-lpthread-ohello-L参数来指明从哪一个目录加载库在/home/test/lib/目录下有链接时所需要的库文件libmy.so

gcchello.c-L/home/test/lib-lmy–ohello新安装一个库之后如何让系统能够找到呢?1.编辑/etc/ld.so.conf文件,加入库文件所在目录的路径2.运行ldconfig,该命令会重建/etc/ld.so.cache文件代码优化优化级别1(-O1)优化级别2(-O2)优化级别3(-O3)StepThree编译多个源文件gcc-Wallcalc.ccalc_fn.c-onewcalc

GCC编译实例调试程序-GDB物联网学院平震宇了解GDBGDB是一个强大的命令行调试工具。UNⅨ下的软件全是命令行的,这给程序开发提代供了极大的便利,命令行软件的优势在于,它们可以非常容易的集成在一起,使用几个简单的已有工具的命令,就可以做出一个非常强大的功能。

UNⅨ下的软件比Windows下的软件更能有机地结合,各自发挥各自的长处,组合成更为强劲的功能。Windows下的图形软件基本上是各自为营,互相不能调用,很不利于各种软件的相互集成。“寸有所长,尺有所短”,图形化工具还有时不如命令行的地方。

GDB功能启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)当程序被停住时,可以检查此时你的程序中所发生的事。你可以改变你的程序,将一个BUG产生的影响修正从而测试其他BUG。

GNUDebuggerGNUDebugger最初是在1988年由理查德·马修·斯托曼(RichardStallman,自由软件的精神领袖)所撰写,之后以GNU通用公共许可证的授权方式将软件发布。

任务要求Step1熟悉使用GDB调试程序的流程Step2熟悉GDB常用命令Step3GDBServer远程调试GDB常用命令任务要求Step1查看源程序Step2暂停/恢复程序运行Step3查看运行时数据StepOne安装GDB通过GDB–v查看当前的GDB版本gdb-v解压gcc源码包tarxzvfgdb-7.4.tar.gzStepTwo如何单步执行?如何打印变量的值?(printvar)

如何打印变量的地址?(print&var)

如何打印地址的数据值?(print*address)

如何查看当前运行的文件和行?(backtrace)

如何查看指定文件的代码?(listfile:N)

如何立即执行完当前的函数,但是并不是执行完整个应用程序?(finish)

如果程序是多文件的,怎样定位到指定文件的指定行或者函数?(listfile:N)

如果循环次数很多,如何执行完当前的循环?(until)

GNUGCC开发教程gnugccturiolgnugcc简介目前Linux下最常用的C语言编译器是GCC(GNUCompilerCollection),它是GNU项目中符合ANSIC标准的编译系统.是Linux的基石,操作系统内核和大部分程序都是gcc编译的,是Linux下最重要开发工具之一gcc早期是c的编译器,后来发展能支持c,c++和objectC,它可以通过不同的前端模块来支持各种语言,如Java、Fortran、Pascal、Modula-3和Ada等。

gcc是一个交叉平台的编译器,目前支持几乎所有主流CPU的处理器平台.gcc支持的文件格式gcc支持源码格式.c C源程序;.C,.cc,.cxx,.cpp C++源程序;.m Objective-C源程序;.i 预处理后的C文件;.ii 预处理后的C++文件;.s 汇编语言源程序;.S 汇编语言源程序;.h 预处理器文件;其它格式.o目标文件(Objectfile).a归档库文件(Archivefile)GCC组成gcc一般安装在/usr/bingcc是一组编译工具的总称,包含如下工作C编译器cc,cc1,cc1plus,gccC++编译器c++,cc1plus,g++源码预处理程序cpp,cpp0库文件libgcc.a,libgcc_eh.a,libgcc_s.so,ibiberty.a,libstdc++,libsupc++.agcc的起步生成一个hello,world程序gcchello.c-ohello#把hello.c编译成一个可执行程序hellogcchello.c#不指定输出名,生成一个a.outHello程序#include<stdio.h>#defineMY_NUMBER5intmain(void){printf("Hello%d,theWorld!\n",MY_NUMBER);return0;}编译hello用GCC编译程序可执行程序的构造任何一个可执行程序从源代码到可执行的二进制程序之中都要经过固定的几步预编译(Pre-Processing)这一步完成对预编译代码的处理编译(Compiling)将源代码编译成汇编代码汇编(Assembling)将汇编代码汇编成目标文件链接(Linking)将目标代码和所需要库的链成一个完整的应用程序集成开发环境(IDE)自动协助开发完成这几步,如VC++在Linux下,如果使用命令行开发工具(gcc,ld,ar)等,需要用户手工调用这一些命令来完成这几步骤.gcc在构建程序的作用gcc在构建应用程序里,会调用不同的应用程序完成每一步.因此在开发中,gcc处于一个核心地位.大部分开发只需要调用gcc即可gcc所做操作Gcc调用cpp进行预处理Gcc调用cc1进行编译,会生成汇编代码Gcc调用as对汇编代码,生成扩展名为.o的目标文件Gcc调用ld来完成对所有目标文件的链接.为什么要用gcc随着Linux的GUI改进,也出现了越来越多的IDE开发环境.象VC++,自动完成各个开发流程但这一些IDE基本上是基于gcc编译而且大部分项目,包括嵌入式开发,都是提供gcc命令行开发模式.因此用gcc开发是Linux和嵌入式开发的必须使用的工具.也是基本功之一hello编译过程分析以下将上述gcc编译过程,分成几个步骤单独进行,并生成每步运行结果供观察第一步是进行预编译,使用-E参数可以让GCC在预处理结束后停止编译过程:#

gcc-Ehello.c-ohello.i下一步是将hello.i编译为目标代码,这可以通过使用-c参数来完成,。#

gcc-chello.i-ohello.o最后一步是将生成的目标文件链接成可执行文件#

gcchello.o-ohello注意:gcc编译时是对输入文件扩展名是敏感的,即.c一定会当做C代码编译,.cpp,.C…一定会当成C++代码编译,这一点跟大部分Linux程序不一样gcc的结果输出是后缀名不相关的.只与输出参数相关.这跟一般Linux程序是一样gcchello.c-ohello.o#虽然后缀名是.o,但实际是一个应用程序gcc各个编译步骤多文件gcc的处理在采用模块化的设计思想进行软件开发时,通常整个程序是由多个源文件组成的,相应地也就形成了多个编译单元,使用GCC能够很好地管理这些编译单元。假设有一个由foo1.c和foo2.c两个源文件组成的程序,为了对它们进行编译,并最终生成可执行程序foo,可以使用下面这条命令:gccfoo1.cfoo2.c-ofoo在编译一个包含许多源文件的工程时,若只用一条GCC命令来完成编译是非常浪费时间的。假设项目中有100个源文件需要编译,并且每个源文件中都包含10000行代码,如果像上面那样仅用一条GCC命令来完成编译工作,那么GCC需要将每个源文件都重新编译一遍,然后再全部连接起来。很显然,这样浪费的时间相当多,尤其是当用户只是修改了其中某一个文件的时候,完全没有必要将每个文件都重新编译一遍,因为很多已经生成的目标文件是不会改变的。要解决这个问题,关键是要灵活运用GCC,同时还要借助像Make这样的工具。用gcc构造程序(1)在只有一个源代码文件构造出来的可执行程序.只需要用到如下形式gcchello.c-ohello表示将hello.c一次做完四步,构造出可执行程序hello,gcchello.c将hello.c构造一个可执行程序,有缺省名a.out,但不建议这样做.gcc-chello.c-ohello这一步骤是初学者常犯错误.以为等于在一次性构造应用程序hello但实际上这只是在编译c并生成一个目标文件hello,即便是没有.o的后缀.这个用file命令可以很容易查看,这个hello是无法执行.用gcc构造程序(2)使用多个源码的项目,如项目中包含2个以上的源代码,一般要先将源代码编译成目标代码.最后一次链接成可执行程序以链表测试程序为例.整个项目由两个c代码(test_link.c和link_list.c)和一个头文件(link_list.h)组成.头文件是包含在源代码里,由预处理程序处理,不需要编译首先各自己编译成目标文件gcc-clink_list.c#将link_list.c编译成link_list.ogcc-ctest_link.c#将test_link.c编译成test_link.o然后将各个目标文件链接成一个文件gcclink_list.otest_link.o-otest_link#生成可执行文件test_link也可以直接把两个文件在一句里编译,但强烈建议不要这样做gcclink_list.ctest_link.c-otest_link编译test_link实例用gcc构造程序(3)gcc的对绝大部分参数顺序一般没有要求,但为可读性强,最好要按一定顺序执行gcclink_list.o-otest_link.otest_link等效于gcclink_list.otest_link.o-otest_link,但前者可读性差很多.但多个-l(链接)参数的顺序是有要求的对于有头文件在多个目录时,需要在编译时多次使用用-I参数加入头文件所在目录.例如test_link.c需要用到/usr/include,当前目录下,/home/hxy目录下的头文件.则如下编译gcc-I.-I/usr/include-I/home/hxy-ctest_link.cgcc构造复杂程序一个大型项目,一个可执行程序可能拥有多个位于不同目录的头文件,多个源码文件,还可能链接一些静态库或动态库,这一些都需要用到gcc的一些扩展选项.gcc的参数参见下一节可能调用gcc很多次,如果完全手工编写,将是一个浩大的工程需要写一个类似Shell脚本的Makefile来调用gcc构造GCC选项gcc完整使用格式gcc使用格式

gcc[option|filename]...g++[option|filename]...

其中option为gcc使用时的选项(后面会再详),

而filename为欲以gcc处理的文件

总体选项(OverallOption)-c-S-E-ofile-pipe-v-xlanguagegcc选项(1)-xlanguage明确指出后面输入文件的语言为language(而不是从文件名后缀得到的默认选择).这个选项应用于后面所有的输入文件,直到遇着下一个`-x'选项.language的可选值有`c',`objective-c',`c-header',`c++',`cpp-output',`assembler',和`assembler-with-cpp'.gcc-xc++hello.c强制用c++来编译-xnone关闭任何对语种的明确说明,因此依据文件名后缀处理后面的文件(就象是从未使用过`-x'选项).gcc选项(2)-c编译或汇编源文件,但是不作连接.编译器输出对应于源文件的目标文件.缺省情况下,GCC通过用`.o'替换源文件名后缀`.c',`.i',`.s',等等,产生目标文件名.可以使用-o选项选择其他名字.GCC忽略-c选项后面任何无法识别的输入文件(他们不需要编译或汇编).gcc–chello.cgcc选项(3)-S编译后即停止,不进行汇编.对于每个输入的非汇编语言文件,输出文件是汇编语言文件.缺省情况下,GCC通过用`.o'替换源文件名后缀`.c',`.i',等等,产生目标文件名.可以使用-o选项选择其他名字.GCC忽略任何不需要编译的输入文件.相当于编译源码,只生汇编代码

gcc–Shello.c–ohello.sgcc选项(4)-E预处理后即停止,不进行编译.预处理后的代码送往标准输出.GCC忽略任何不需要预处理的输入文件gcc–Ehello.c–ohello.i-v(在标准错误)显示执行编译阶段的命令.同时显示编译器驱动程序,预处理器,编译器的版本号.gcc-vgcc选项(5)-ofile指定输出文件为file.该选项不在乎GCC产生什么输出,无论是可执行文件,目标文件,汇编文件还是预处理后的C代码.由编译阶段决定,输入的格式gcc-Ehello.c-ohello.igcc-chello.i-ohello.ogcchello.c–ohello只能输出一个文件gcc选项(6)-pipe在编译过程的不同阶段间使用管道而非临时文件进行通信.在将源代码变成可执行文件的过程中,需要经过许多中间步骤,包含预处理、编译、汇编和连接。这些过程实际上是由不同的程序负责完成的。大多数情况下GCC可以为Linux程序员完成所有的后台工作,自动调用相应程序进行处理。是GCC在处理每一个源文件时,最终都需要生成好几个临时文件才能完成相应的工作,从而无形中导致处理速度变慢。例如,GCC在处理一个源文件时,可能需要一个临时文件来保存预处理的输出、一个临时文件来保存编译器的输出、一个临时文件来保存汇编器的输出,而读写这些临时文件显然需要耗费一定的时间。当软件项目变得非常庞大的时候,花费在这上面的代价可能会变得很沉重。解决的办法是,使用Linux提供的一种更加高效的通信方式—管道。它可以用来同时连接两个程序,其中一个程序的输出将被直接作为另一个程序的输入,这样就可以避免使用临时文件,但编译时却需要消耗更多的内存。gcc-pipefoo.c-ofoo

关于宏(macro)的选项-Dmacro定义宏macro,宏的内容定义为字符串`1'.gcctest_m.c–D__DEBUG–otest_m-Dmacro=defn定义宏macro的内容为defn.命令行上所有的`-D'选项在`-U'选项之前处理.gcctest_m.c–D__DBG_NAME=hello–otest_m-Umacro取消宏macro.`-U'选项在所有的`-D'选项之后处理,但是优先于任何`-include'gcc警告提示功能

GCC包含完整的出错检查和警告提示功能,它们可以帮助Linux程序员写出更加专业和优美的代码。编译警告代码-pedantic打开完全服从ANSIC标准所需的全部警告诊断,如里出现非标准扩展,则拒绝编译,所以叫书呆子pedant.-ansi支持符合ANSI标准的C程序.这样就会关闭GNUC中某些不兼容ANSIC的特性,例如asm,inline和typeof关键字,以及诸如unix和vax这些表明当前系统类型的预定义宏.同时开启不受欢迎和极少使用的ANSItrigraph特性,以及禁止`$'成为标识符的一部分.与pedantic区别在于,只是警告,如果需要停止编译,仍然需要打开-pedanticgcc警告提示功能(2)-Wall打开所有编译警告gcc-Wallillcode.c-oillcode-Werror视警告为错误;出现任何警告即放弃编译.gcc-Wall-Werrorillcode.c-oillcode-w禁止输出警告信息调试分析选项-g以操作系统的本地格式(stabs,COFF,XCOFF,或DWARF).产生调试信息.GDB能够使用这些调试信息,是进行gdb调试必备条件和大多数C编译器不同,GNUCC允许结合使用`-g‘和`-O’选项,但一般不建议一起使用gcchello.c-g-ohello-pg产生额外代码,用于输出profile信息,供分析程序gprof使用.所有调试选项会使用最终输出文件尺寸急剧增加,在最后发布,需要使用strip命令把调试信息去掉,striphellogcc使用库使用第三方库在Linux下开发软件时,完全不使用第三方函数库的情况是比较少见的,通常来讲都需要借助一个或多个函数库的支持才能够完成相应的功能。从程序员的角度看,函数库实际上就是一些头文件(.h)和库文件(.so或者.a)的集合。虽然Linux下的大多数函数都默认将头文件放到/usr/include/目录下,而库文件则放到/usr/lib/目录下,但并不是所有的情况都是这样。正因如此,GCC在编译时必须有自己的办法来查找所需要的头文件和库文件。GCC采用搜索目录的办法来查找所需要的文件,-I选项可以向GCC的头文件搜索路径中添加新的目录。例如,如果在/home/hxy/upgrade/include/目录下有编译时所需要的头文件,为了让GCC能够顺利地找到它们,就可以使用-I选项:gccfoo.c-I/home/xiaowp/include-ofoo在一个gcc命令中可以用多个-I两大类库形式C/C++可以使用两种库.一种是静态库,另外一种是动态库.静态库在链接时会把库目标代码与最终的可执行程序一起链接到一个文件,这样相对尺寸较大.但处理简单.而动态库是可执行程序在运行,动态加载到进程内存中去.动态库与可执行程序是分离的两部分文件.两者在作用是完全等效,主要是使用方法不同.由开发者根据项目情况自行评估使用哪种形式.Windows下的静态库是以lib为后缀名的文件,而动态库是以DLL为后缀名的文件.Linux下的动态链接库是so为后缀,和静态链接库以.a为后缀名Linux的库的命名Linux库的命名有一个特殊的要求,即要是lib打头,以.so或a结尾libc.so#标准C库,动态链接库libpthread.a#线程库,的静态链接库版本.在一般使用时,为防止不同版本库互相覆盖,一般还在系统库名后加入版本号.libm.so.6#math库ver6.0版本Libc-2.3.2.so#标准Cver2.3.2动态库但为方便gcc使用,通常都对这一些带版本名的库作一个符号链接,链接名则是标准形式,如libc.so.Linux一般把系统库放在/lib下.这是大部分库命名的习惯,也可以不遵守,如果强行做一个叫mystd.a的库,但使用起来很不方便,如不能使用-l参数等,所以建议不要这样做.gcc链接库gcc是在链接时,把库加入可以程序中.一个特殊静态链接库方式.把库完整名字加入gcc-ohellohello.olibmy.a链接hello.o,和库libmy.a到某一个程序hello中gcc-ohellohello.olibmy.so链接hello.o,和库libmy.so到某一个程序hello中,注意这里没有直接把libmy.so代码加入hello中这一方法主要用于链接不标准库名称,或混和链接(即一部分库用于静态版本,一部分库用动态版本).但不是正规用法,强烈建议不要使用这一方法gcc-l参数的使用gcc-l参数用来链接库标准表达式方式.-l接的库名,是去掉lib和后缀名(.so,.a)剩下的部分,gccfoo.c-lpthread-ofoo构造foo,链接库pthread.-lpthread表示链接libpthread.so由于去掉后缀名,gcc-l如何知道链接是动态库还是静态库?,gcc有如下规则:如果gcc所能找到库目录同时有两种版本,优先链接动态链接库版本如果gcc所能找到库目录只有静态版本,则采用静态版本如果加上-static参数,gcc则强制链接静态版本gccfoo.c-static-lpthread-ofoo-lpthread表示链接libpthread.a一个使用线程库的例子gcc-L参数的使用gcc所编译的目标文件和库通常不是在同一个目录下.因此必须强制指明gcc要从哪一个目录加载库gcc在链接时采用-L参数来指明从哪一个目录加载库例如,如果在/home/hxy/lib/目录下有链接时所需要的库文件libfoo.sogccfoo.c-L/home/hxy/lib-lfoo-ofoo一个gcc语句可以包含多个-L参数在编译时目标文件时使用-L无效标准库,gcc能自行找到,无需使用-L参数在一些应用中,链接多个库是有顺序的,大部分无所谓如在系统中liba.a使用libpthread.a中的函数,而可执行程序同时使用两个库,则使用者liba.a的链接语句放在被使用者libpthread.a的前面,gccfoo.c-L/home/hxy/lib–la-lpthread-ofoo只是为了保险.在BerkeleyDB中就出现这种情况,pthread在前,则所有线程库的函数找不到gcc-I参数的使用库的头文件通常也跟源文件不在同一个目录之下,为了让gcc找到头文件,可以-I<头文件目录>来加入头目录.gcc是在编译时使用-I,在链接时无效.这跟-L参数刚好相反一个gcc语句中可以使用多个-Igcc创建库关于库的演示代码在随后的演示中,将采用如下演示代码.Strlen.c,Strnlen.c分别实现了两个自定义示字符串长度函数这两个函数的声明在String.h中main.c分别用静态链接,隐式动态链接来测试使用了Strlen.c,Strnlen.c的函数,并使用.main_dl.c用显示动态链接来调用库代码在随后的例子里把Strlen.c,Strnlen.c分别编译成静态库.libstr.a,隐式动态库libstr.so,显示动态库libstrdl.so,库名中最好不用出现大写字母,gcc是按小写字母来查找的关于静态链接库在LINUX下,静态函数库是以.a作后缀的,类似于Windows的lib在链接后,静态库的函数都会链接到最终的可执行程序里.这样可执行程序的尺寸比动态链接要大.静态链接的好处是不需要外部文件的支持,独立运行.在嵌入式环境下,如果尺寸影响不大,最好用静态编译.创建静态链接库gcc不能直接创建静态库.必须要用归档命令ar来创建ar用于建立、修改、提取档案文件(archive)。archive是一个包含多个被包含文件的单一文件(也称之为库文件),其结构保证了可以从中检索并得到原始的被包含文件(称之为archive中的member)。ar可以把任何文件归在一起,但通常是用来把gcc编译的目标文件(.o),合在一个静态库中静态库创建$gcc-Wall-cfile1.cfile2.cfile3.c#一次性编译三个.o$arrvlibNAME.afile1.ofile2.ofile3.o#把三个o合在一起ar参数{dmpqrtx}中的操作选项在命令中只能并且必须使用其中一个,它们的含义如下:d:从库中删除模块。按模块原来的文件名指定要删除的模块。如果使用了任选项v则列出被删除的每个模块。m:该操作是在一个库中移动成员。当库中如果有若干模块有相同的符号定义(如函数定义),则成员的位置顺序很重要。如果没有指定任选项,任何指定的成员将移到库的最后。也可以使用'a','b',或'I'任选项移动到指定的位置。p:显示库中指定的成员到标准输出。如果指定任选项v,则在输出成员的内容前,将显示成员的名字。如果没有指定成员的名字,所有库中的文件将显示出来。q:快速追加。增加新模块到库的结尾处。并不检查是否需要替换。'a','b',或'I'任选项对此操作没有影响,模块总是追加的库的结尾处。如果使用了任选项v则列出每个模块。这时,库的符号表没有更新,可以用'ars'或ranlib来更新库的符号表索引。ar参数(2)r:在库中插入模块(替换)。当插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar显示一个错误消息,并不替换其他同名模块。默认的情况下,新的成员增加在库的结尾处,可以使用其他任选项来改变增加的位置。

t:显示库的模块表清单。一般只显示模块名。x:从库中提取一个成员。如果不指定要提取的模块,则提取库中所有的模块。但实际最多参数只有arrv....Replace+verbosearrcv....Replace+Create+verbose创建一个静态库的脚本gcc-cStrlen.cgcc-cStrnlen.c#把两个目标文件装入静态库libstr.a中arrv

libstr.aStrlen.oStrnlen.ogcc-cmain.c-I.#链接main_a时,一并链接libstr.a#gccmain.olibmy.a-omain_a#非标准用法,用全名gccmain.o-L.-lstr-omain_a#标准用法动态链接库的创建动态链接库的创建分为两步:1.编译目标文件,必须带上-fpic标志,使输出的对象模块是按照可重定位地址方式生成的gcc-cStrlen.c-fpic

gcc-cStrnlen.c-fpic

2.将加入动态库的目标文件合并在一起,必须带上-shared,明确表示是动态链接库gcc-sharedStrlen.oStrnlen.o-olibstr.so两步可以合并成一步,但一般不建议这样做gcc-fpic

-sharedStrlen.cStrnlen.c-olibstr.soso是SharedObject的缩写动态链接库的创建#gcc-fpic-sharedStrlen.cStrnlen.c-olibstr.sogcc-c-fpicStrlen.cgcc-c-fpicStrnlen.c#生成动态链接库gcc-sharedStrlen.oStrnlen.o-olibstr.so#编译测试程序gcc-cmain.c-I.#链接主程序和动态库#gccmain.olibstr.so-omain_so#非标准链接方式gccmain.o-L.-lstr-omain_so运行中使用动态链接库一个使用动态链接库的程序运行时,要做一下设置.否则应用程序会报找不到动态库的错误隐式调用和显式调用两种调用方法.隐式调用是不采用特殊系统调用,只是在gcc链接采用-l,-L链接.这样代码影响不大显式调用是在运行时,使用动态库的文件名来加载,具有灵活的特点,缺点就是必须使用特定的,不可移植的系统调用来编写.过程比较复杂一个程序运行后,可以用命令ldd来检查它使用了哪一些动态库ldd./hello隐式调用动态方法必须要能让程序运行能找动态链接库,Linux有如下几种方法.把库所在路径加入/etc/ld.so.conf,程序加载是会首先到这里路径查找设置环境变量LD_LIBRARY_PATH,把库所在路径加入这个变量中,这是最常用的方法演示代码将采用这一方法运行,写一个脚本run_so.sh将exportLD_LIBRARY_PATH和执行语句写在一行命令中,并用;隔开exportLD_LIBRARY_PATH=$PWD;./mani_so#!/bin/shexportLD_LIBRARY_PATH=$PWD:$LD_LIBRARY_PATH./main_soldd./main_so#用ldd查看用了哪些动态库隐式动态库的执行结果动态库的显示调用显示调用,Linux提供4个库函数(dlopen,dlerror,dlsym和dlclose),一个include文件(dlfcn.h)以支持动态链接装入器dlopen

将共享目标文件打开并且映射到内存中,并且返回句柄dlsym返回一个指向被请求入口点的指针dlerror

返回NULL或者一个指向描述最近错误的ASCII字符串的指针dlclose关闭句柄并且取消共享目标文件的映射这四个函数被包含在libdl.a/libdl.so之中,所以使用动态显示调用,必须要链接-ldl这一机制是后来加入,模仿Windows下的LoadLibrary/FreeLibrary机制不同调用方式对动态库本身没有任何影响,需要调整是调用库函数的应用程序,要做较大调整.dlopen()的说明void*dlopen(constchar*filename,intflag);将共享目标文件打开并且映射到内存中,并且返回句柄声明在头文件dlfcn.h之中第一个参数:指定共享库的名称,将会在下面位置查找指定的共享库。dlopencall中的绝对文件路径在LD_LIBRARY_PATH环境变量中指定的目录中在/etc/ld.so.cache中指定的库列表之中先在/usr/lib之中,然后在/lib之中第二个参数:指定如何打开共享库。RTLD_NOW:将共享库中的所有函数加载到内存RTLD_LAZY:会推后共享库中的函数的加载操作,直到调用dlsym()时方加载某函数

其它dl函数声明constchar*dlerror(void);如果上一步的dl函数出错,则返回非空字符串,指明错误原因

void*dlsym(void*handle,char*symbol);用库函数名字symbol在dlopen打开的handle查找一个函数指针,如果找到,则放入返回值中,应用程序可以直接用函数指针来操作.intdlclose(void*handle);关闭dlopen打开的句柄关于使用显示动态库的步骤必须包含头文件#include<dlfcn.h>声明使用的到库函数的函数指针用dlopen打开共享库用dlsym取得对应库函数的函数指针如果成功,使用直接调用这个指针.相当于使用库函数用完共享库后,用dlclose释放共享库使用显示调用实例(1)#include<stdio.h>#include<dlfcn.h>/*

加入头文件支持*/intmain(intargc,char*argv[]){//definefunctionpointorint(*pStrlenFun)(char*pStr);//声明对应的函数的函数指针

int(*pStrnlenFun)(char*pStr,intulMaxLen);charstr[]={"helloworld"};unsignedlongulLength=0;void*pdlHandle;char*pszErr;

pdlHandle=dlopen(“./libstrdl.so”,RTLD_LAZY);//加载动态链接库/libstrdl.soif(!pdlHandle){printf("Failedloadlibrary\n");}使用显示调用实例(2)/*接上一段代码*//*判断dlopen是否打开出错,如是,打印错误后退出*/pszErr=dlerror();

if(pszErr!=NULL){printf("%s\n",pszErr);return0;}//从库中查找库函数Strlen的函数指针

pStrlenFun=dlsym(pdlHandle,"Strlen");//获取函数的地址

pszErr=dlerror();if(pszErr!=NULL){printf("%s\n",pszErr);return0;}使用显示调用实例(3)/*接上一段代码*//*从库中查找库函数Strnlen的函数指针*/pStrnlenFun=dlsym(pdlHandle,"StrNlen");pszErr=dlerror();if(pszErr!=NULL){printf("%s\n",pszErr);return0;}printf("Thestringis:%s\n",str);ulLength=pStrlenFun(str);//直接使用函数指针,相当于调用库函数

printf("Thestringlengthis:%d(useStrlen)\n",ulLength);ulLength=pStrnlenFun(str,10);printf("Thestringlengthis:%d(useStrNlen)\n",ulLength);

dlclose(pdlHandle);//关闭共享库

return0;}创建显示调用动态库rm-flibstrdl.sorm-f*.omain#gcc-fpic-sharedStrlen.cStrnlen.c-olibstr.sogcc-c-fpicStrlen.cgcc-c-fpicStrnlen.c#创建动态链接库gcc-sharedStrlen.oStrnlen.o-olibstrdl.sogcc-cmain_dl.c-I.#必须同时加入strdl和dl库支持gccmain_dl.o-L.-lstrdl

-ldl-omain_dl运行显示调用实例代码优化Gcc代码优化(1)代码优化指的是编译器通过分析源代码,找出其中尚未达到最优的部分,然后对其重新进行组合,目的是改善程序的执行性能。GCC提供的代码优化功能非常强大,它通过编译选项-On来控制优化代码的生成,其中n是一个代表优化级别的整数。对于不同版本的GCC来讲,n的取值范围及其对应的优化效果可能并不完全相同,比较典型的范围是从0变化到2或3。编译时使用选项-O可以告诉GCC同时减小代码的长度和执行时间,其效果等价于-O1。在这一级别上能够进行的优化类型虽然取决于目标处理器,但一般都会包括线程跳转(ThreadJump)和延迟退栈(DeferredStackPops)两种优化。选项-O2告诉GCC除了完成所有-O1级别的优化之外,同时还要进行一些额外的调整工作,如处理器指令调度等。选项-O3则除了完成所有-O2级别的优化之外,还包括循环展开和其它一些与处理器特性相关的优化工作。通常来说,数字越大优化的等级越高,同时也就意味着程序的运行速度越快。许多Linux程序员都喜欢使用-O2选项,因为它在优化长度、编译时间和代码大小之间,取得了一个比较理想的平衡点。

Gcc代码优化(2)不带优化

gcc-Walloptimize.c-ooptimize做了优化gcc-Wall-Ooptimize.c-ooptimizetime借助Linux提供的time命令,可以大致统计出该程序在运行时所需要的时间,比较两次时间#time./optimize代码优化实例一个做了大量浮点数除法的程序.#include<stdio.h>intmain(void){doublecounter;doubleresult;doubletemp;for(counter=0;counter<2000.0*2000.0*2000.0/20.0+2020;counter+=(5-1)/4){temp=counter/1979;result=counter;}

printf("Resultis%lf\n",result);

return0;}代码优化的结果比较Gcc代码优化(3)-O-O1-O2多优化一些.除了涉及空间和速度交换的优化选项,执行几乎所有的优化工作.例如不进行循环展开(loopunrolling)和函数内嵌(inlining).和-O选项比较,这个选项既增加了编译时间,也提高了生成代码的运行效果.-O3优化的更多.除了打开-O2所做的一切,它还打开了-finline-functions选项.-O0不优化.如果指定了多个-O选项,不管带不带数字,最后一个选项才是生效的选项.Gcc代码优化(4)避免优化代码场合:程序开发的时候优化等级越高,消耗在编译上的时间就越长,因此在开发的时候最好不要使用优化选项,只有到软件发行或开发结束的时候,才考虑对最终生成的代码进行优化。资源受限的时候一些优化选项会增加可执行代码的体积,如果程序在运行时能够申请到的内存资源非常紧张(如一些实时嵌入式设备),那就不要对代码进行优化,因为由这带来的负面影响可能会产生非常严重的后果。跟踪调试的时候在对代码进行优化的时候,某些代码可能会被删除或改写,或者为了取得更佳的性能而进行重组,从而使跟踪和调试变得异常困难。

交叉开发GCC在其它平台的gccgcc是一个强大,开源的编译器,因此被人移植多种CPU,多个操作系统下.如移植Windows下的mingW,Cygwingcc.exe几乎在每种CPU,如ARM,MIPS,PowerPC等都有相应版本,而且在很多时候是唯一的编译器,如龙芯.gcc还有一个强大的功能,即交叉编译.即在A的操作系统下,编译B操作系统下的程序.如嵌入式操作下x86的redhat9.0下去编译ARM下的Arm-Linux的程序用于交叉开发的gcc在arm下开发的gcc通常叫arm-linux-gcc将arm-linux-gcc解压后安装将arm-linux-gcc其路径加入系统PATH中将所有需要gcc的地方改成arm-linux-gcc重新编译,即可完成简单的移植在嵌入式中,通常需要-static静态编译.编译后的可执行程序可用U盘或FTP上传到ARM开发板上课内练习,将上一次课堂练习结果移植到开发板上注意,首先修改开发HOST的对应用户的.bash_profile,假设是用户hxy,则在/home/hxy/.bash_profile的PATH改成如下定义PATH=/usr/local/arm/2.95.3/bin:$PATH这样才可以直接用arm-linux-gcc常见错误或警告dl_list.h:42:7:warning:nonewlineatendoffile这个通常是由于源码里最后一行不是UNIX文本字符要求的空行,造成,这不影响最终编译结果,如果想去掉,直接在源码中加入一个空行即可test_link.o(.text+0x308):Infunction`test2'::undefinedreferenceto`dl_save_to_file‘表示在最终链接时,没有找到dl_save_to_file这个函数的目标代码,原因有多种,如没有把这个源码包含进来,系统函数的笔误.找不到对应链接库main.c:8:47:String.h:Nosuchfileordirectory某个头文件找不到,很大可能性是没有用-I参数把String.h所有在目录加入进来../main:errorwhileloadingsharedlibraries:libstr.so:cannotopensharedobjectfile:Nosuchfileordirectory应用程序找不到自己链接的动态库libstr.so,需要把libstr.so加入到环境变量LD_LIBRARY_PATH中程序运行常见错误1在busybox下运行程序,/usr/bin/boa:/usr/bin/boa:1:Syntaxerror:"("unexpected最大可以是这个程序是用x86gcc编译的程序,然后在Arm-Linux运行时,被不匹配的strip课堂作业请开发一个程序,使用结构定义structstudent{intno;/*学号*/charname[64];/*名称*/floatheight;/*身高*/};要求创建3个学生定义,并给三个学生的三个成员都赋上值,用一个printf把一个学生的三个值打印出来,依次把所有学生信息显示要求用vi编辑,并用gcc在Linux下编译,测试通过课堂练习开发一个库函数,要求能对字符串的进行转大写转换.要求把库函数,编译成静态和动态链接库各一个,取名libtu.a,libtu.so编写一个测试程序,要求能测试libtu测试库函数把上述过程写成一个Shell脚本,一次性执行谢谢,请提问在疯狂的时代把握未来课程目标学习使用字符界面gdb来调试Linux程序gdb高级应用图形gdb界面-dddGdb简介GDB是一个强大的命令行调试工具。大家知道命令行的强大就是在于,其可以形成执行序列,形成脚本。UNIX下的软件全是命令行的,这给程序开发提代供了极大的便利,命令行软件的优势在于,它们可以非常容易的集成在一起,使用几个简单的已有工具的命令,就可以做出一个非常强大的功能。GDB相对图形界面的VC++等,命令比较难记,这是命令行界面一大缺点Linux也有基于GDB图形界面的调试器,如gdbinsight,DDD.GDB功能启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)当程序被停住时,可以检查此时你的程序中所发生的事。动态的改变你程序的执行环境GDB与VC++调试命令的对比GDB帮助象大多部复杂Linux程序一样,GDB是通过内部命令来完成调试工作gdb的命令很多,gdb把之分成许多个种类.help命令只是例出gdb的命令种类,如果要看种类中的命令,可以使用help<class>命令,如:helpbreakpoints,查看设置断点的所有命令。也可以直接help<command>来查看命令的帮助。gdb中,输入命令时,可以不用打全命令,只用打命令的前几个字符就可以了,当然,命令的前几个字符应该要标志着一个唯一的命令在gdb下,你可以敲击两次TAB键来补齐命令的全称,如果有重复的,那么gdb会把其例出来。GDB测试样例编译测试程序,一定要加上-g参数,为可执行文件加上调试信息gcc-gtst.c-otst启动GDB的方式gdb<program>

program也就是你的执行文件,一般在当前目录下gdb<program>core用gdb同时调试一个运行程序和core文件,core是程序非法执行后coredump后产生的文件gdb<program><PID>如果你的程序是一个服务程序,那么你可以指定这个服务程序运行时的进程ID。gdb会自动attach上去,并调试他。program应该在PATH环境变量中搜索得到。操作GDB常见命令GDB的启动选项GDB启动时,可以加上一些GDB的启动选项--symbols<file>-s<file>

从指定文件中读取符号表。-sefile

从指定文件中读取符号表信息,并把他用在可执行文件中。--core<file>

-c<file>

调试时coredump的core文件。--directory<directory>

-d<directory>

加入一个源文件的搜索路径。默认搜索路径是环境变量中PATH所定义的路径。fileprogram详细的开关可以用gdb--helpgdb的基本调试调试器的基本功能任何一种调试器,都必须具备如下基本功能建立可执行程序与源码的联系(查看源码)设置断点执行行基本的调试命令程序暂停后,查看各种信息查看源程序

GDB可以打印出所调试程序的源代码,当然,在程序编译时一定要加上-g的参数,把源程序信息编译到执行文件中。当程序停下来以后,GDB会报告程序停在了那个文件的第几行上。你可以用list命令来打印程序的源代码。可以缩写为llist<linenum>

显示程序第linenum行的周围的源程序。list<function>

显示函数名为function的函数的源程序。list

显示当前行后面的源程序。list-

显示当前行前面的源程序。一般是打印当前行的上5行和下5行,如果显示函数是是上2行下8行,默认是10行,当然,你也可以定制显示的范围,使用下面命令可以设置一次显示源程序的行数。setlistsize<count>

设置一次显示源代码的行数。setlistsize20设置显示一次20行showlistsize

查看当前listsize的设置。

查看源程序(2)list命令还有下面的用法list<first>,<last>

显示从first行到last行之间的源代码。list,<last>

显示从当前行到last行之间的源代码。list+

往后显示源代码。

一般来说在list后面可以跟以下这们的参数:<linenum>行号。

<+offset>当前行号的正偏移量。

<-offset>当前行号的负偏移量。

<filename:linenum>哪个文件的哪一行。lgdb_tst.c:20<function>函数名。

<filename:function>

哪个文件中的哪个函数lgdb_tst.c:main<*address>程序运行时的语句在内存中的地址。l*0x0804835a调试程序执行gdbgdbgdb_tst设置断点通常至少要设一个断点,要不然gdb会直接运行到程序结束.bmain#在主函数入口设断点设置命令行参数如果程序需要用到命令行参数,直接在gdb命令是无法输入setargs

可指定运行时参数。如:gdb>setargs1020304050开始调试进入gdb提示符后,gdb并没有进调试状态需要用r,即run进行调试在GDB中运行程序

当以gdb<program>方式启动gdb后,gdb会在PATH路径和当前目录中搜索<program>的源文件。如要确认gdb是否读到源文件,可使用l或list命令,看看gdb是否能列出源代码。在gdb中,运行程序使用r或是run命令。程序的运行,你有可能需要设置下面四方面的事。程序运行参数。setargs可指定运行时参数。如:setargs1020304050运行环境path<dir>可设定程序的运行路径。showpaths查看程序的运行路径。setenvironmentvarname[=value]设置环境变量。如:setenvUSER=hchenshowenvironment[varname]查看环境变量。工作目录。cd<dir>

相当于shell的cd命令。pwd显示当前的所在目录。程序的输入输出infoterminal显示你程序用到的终端的模式。使用重定向控制程序输出。如:run>outfiletty命令可以指写输入输出的终端设备。如:tty/dev/ttyb常用调试命令

当程序被停住了,你可以用continue命令恢复程序的运行直到程序结束,或下一个断点到来。也可以使用step或next命令单步跟踪程序。continue[ignore-count]

c[ignore-count]

fg[ignore-count]

恢复程序运行,直到程序结束,或是下一个断点到来。ignore-count表示忽略其后的断点次数。continue,c,fg三个命令都是一样的意思。step<count>

单步跟踪,如果有函数调用,他会进入该函数。进入函数的前提是,此函数被编译有debug信息。很像VC等工具中的stepin。后面可以加count也可以不加,不加表示一条条地执行,加表示执行后面的count条指令,然后再停住。next<count>

同样单步跟踪,如果有函数调用,他不会进入该函数。很像VC等工具中的stepsetstep-mode

setstep-modeon

打开step-mode模式,于是,在进行单步跟踪时,程序不会因为没有debug信息而不停住。这个参数有很利于查看机器码。常用调试命令(2)setstep-modoff

关闭step-mode模式。finish

运行程序,直到当前函数完成返回。并打印函

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论