嵌入基础系统及其应用 10_第1页
嵌入基础系统及其应用 10_第2页
嵌入基础系统及其应用 10_第3页
嵌入基础系统及其应用 10_第4页
嵌入基础系统及其应用 10_第5页
已阅读5页,还剩108页未读 继续免费阅读

下载本文档

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

文档简介

第七章嵌入式Linux系统移植本章含有多个微课视频,涉及编译器实战,调试器实战,扩展等内容,请择机观看目录7.1BootLoader基本概念与典型结构7.2U-Boot7.3交叉开发环境的建立7.4交叉编译工具链7.5嵌入式Linux系统移植过程7.6Gdb调试器7.7远程调试7.8内核调试第七章嵌入式Linux系统移植(1)BootLoader基本概念与操作模式/常见的BootLoader/U-Boot/U-Boot针对RISC-V(扩展)/U-Boot针对设备树(扩展)(2)交叉开发环境交叉开发环境的建立/交叉编译工具链/clang与LLVM(扩展)(3)嵌入式Linux系统移植U-Boot的移植/内核配置、编译和移植(4)调试Gdb本地调试/远程调试/GDBSERVER实战案例BootLoader基本概念

一个嵌入式Linux系统通常可以分为以下几个部分:(1)引导加载程序及其环境参数。这里通常是指BootLoader以及相关环境参数。(2)Linux内核。基于特定嵌入式开发板的定制内核以及内核的相关启动参数。(3)文件系统。主要包括根文件系统和一般建立于Flash内存设备之上文件系统。(4)用户应用程序。基于用户的应用程序。有时在用户应用程序和内核层之间可能还会包括一个嵌入式图形用户界面程序(GUI)。常见的嵌入式GUI有QT和MiniGUI等。在嵌入式操作系统中,BootLoader是在操作系统内核运行之前运行的一小段程序,可以初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一个适合的状态,以便为最终调用操作系统内核准备好正确的环境。在嵌入式系统中,通常并没有像通用计算机中BIOS那样的固件程序,因此整个系统的加载启动任务就完全由BootLoader来完成。BootLoader是嵌入式系统在加电后执行的第一段代码,在它完成CPU和相关硬件的初始化之后,再将操作系统映像或固化的嵌入式应用程序装载到内存中然后跳转到操作系统所在的空间,启动操作系统运行。对于嵌入式系统而言,BootLoader是基于特定硬件平台来实现的。因此,几乎不可能为所有的嵌入式系统建立一个通用的BootLoader,不同的处理器架构都有不同的BootLoader。BootLoader不仅依赖于CPU的体系结构,而且依赖于嵌入式系统板级设备的相关配置。7.1.2BootLoader的操作模式大多数BootLoader都包含两种不同的操作模式:自启动模式和交互模式。这种划分仅仅对于开发人员才有意义。(1)自启动模式自启动模式也叫启动加载模式。在这种模式下,BootLoader自动从目标机上的某个固态存储设备上将操作系统加载到RAM中运行,整个过程并没有用户的介入。这种模式是BootLoader的正常工作模式(2)交互模式交互模式也叫下载模式。在这种模式下,目标机上的BootLoader将通过串口或网络等通信手段从开发主机上下载内核映像、根文件系统到RAM中。然后再被BootLoader写到目标机上的固态存储媒质(如FLASH)中,或者直接进入系统的引导。交互模式也可以通过接口(如串口)接收用户的命令。这种模式在初次固化内核、根文件系统时或者更新内核及根文件系统时都会用到。7.1.3BootLoader的典型结构BootLoader启动大多数都分为两个阶段。第一阶段主要包含依赖于CPU的体系结构硬件初始化的代码,通常都用汇编语言来实现。这个阶段的任务有:基本的硬件设备初始化(屏蔽所有的中断、关闭处理器内部指令/数据Cache等)。为第二阶段准备RAM空间。如果是从某个固态存储媒质中,则复制BootLoader的第二阶段代码到RAM。设置堆栈。跳转到第二阶段的C程序入口点。第二阶段通常用C语言完成,以便实现更复杂的功能,也使程序有更好的可读性和可移植性。这个阶段的任务有:初始化本阶段要使用到的硬件设备。检测系统内存映射。将内核映像和根文件系统映像(如果有设备树则也需要)从Flash读到RAM。为内核设置启动参数。调用内核。Exynos4412的启动流程Exynos4412微处理器的启动过程由BL0、BL1和BL2(BL为BootLoader的简称,在本书后续章节中将有详细介绍)3部分代码实现,其中BL0在出厂时已经被固化到64KB的iROM区中。(1)执行内部只读存储器iROM中的启动代码BL0(厂家固化在里面的),这段代码主要是初始化一些系统的基本配置,对应图4-2中的标志①。该段代码主要的功能有:关看门狗时钟。初始化指令cache。初始化栈、堆。初始化块设备复制函数。初始化PLL及设置系统时钟。Exynos4412的启动流程Exynos4412微处理器的启动过程由BL0、BL1和BL2(BL为BootLoader的简称,在本书后续章节中将有详细介绍)3部分代码实现,其中BL0在出厂时已经被固化到64KB的iROM区中。(2)根据OM引脚设置,从相应启动设备复制BL1到片内256KB的SRAM中(iRAM区),并检查BL1的完整性校验信息,如果检查失败,iROM将自动偿试从其它启动设备启动,如SD/MMC等。BL1的代码量一般不大,因而一般情况下BL1负责完成的工作较少,主要是进一步完善系统时钟的初始化工作、动态内存DRAM控制器的配置、通信接口的初始化等。根据安全启动秘钥值,BL1还将对操作系统镜像进行完整性校验。该过程对应图4-2中的标志②。(3)完成内动态内存DRAM控制器的初始化后,把操作系统镜像从启动设备加载到DRAM中,该过程对应图4-2中的标志③。(4)跳转到操作系统中运行。扩展:操作系统启动之前:RISC-V引导程序扩展:OpenSBI用例OpenSBI作为固件的常见用例方法:FW_PAYLOAD:带有下一个引导阶段作为有效载荷的固件。这种类型的固件直接包含了下一引导阶段的二进制代码,即payload,通常是bootloader或操作系统内核。FW_JUMP:带有静态跳转地址到下一个引导阶段的固件。FW_DYNAMIC:具有下一启动阶段动态信息的固件。常见的BootLoader

(1)RedbootRedboot(RedHatEmbeddedDebugandBootstrap)是RedHat公司开发的一个独立运行在嵌入式系统上的BootLoader程序,是目前比较流行的一个功能、可移植性好的BootLoader。Redboot是一个采用eCos开发环境开发的应用程序,并采用了eCos的硬件抽象层作为基础,但它完全可以摆脱eCos环境运行,可以用来引导任何其他的嵌入式操作系统,如Linux、WindowsCE等。Redboot支持的处理器构架有ARM,MIPS,MN10300,PowerPC,RenesasSHx,v850,x86等,是一个完善的嵌入式系统BootLoader。(2)U-BootU-Boot(UniversalBootLoader)于2002年12月17日发布第一个版本U-Boot-0.2.0。U-Boot自发布以后已更新多次,其支持具有持续性。U-Boot是在GPL下资源代码最完整的一个通用BootLoader。(3)BlobBlob(BootLoaderObject)是由Jan-DerkBakker和ErikMouw发布的,是专门为StrongARM构架下的LART设计的BootLoader。Blob的最后版本是blob-2.0.5。Blob功能比较齐全,代码较少,比较适合做修改移植,用来引导Liunx,目前大部分S3C44B0板都用Blob修改移植后加载uClinux。(4)vivivivi是韩国mizi公司开发的BootLoader,适用于ARM9处理器,现在已经停止开发了。它是三星官方板SMDK2410采用的BootLoader。Vivi最主要的特点就是代码小巧,有利于移植新的处理器。同时vivi的软件架构和配置方法类似Linux风格,对于有过编译Linux内核经验的用户,vivi更容易上手。

U-BootPartTwo7.27.2.1U-Boot概述U-Boot,全称UniversalBootLoader,是遵循GPL条款的开放源码项目。从FADSROM、8xxROM、PPCBOOT逐步发展演化而来。其源码目录、编译形式与Linux内核很相似,事实上,不少U-Boot源码就是根据相应的Linux内核源程序进行简化而形成的,尤其是一些设备的驱动程序。

U-Boot支持多种嵌入式操作系统,主要有OpenBSD,NetBSD,FreeBSD,4.4BSD,Linux,SVR4,Esix,Solaris,Irix,SCO,Dell,NCR,VxWorks,LynxOS,pSOS,QNX,RTEMS,ARTOS,android等。同时,U-Boot除了支持PowerPC系列的处理器外,还能支持MIPS、x86、ARM、NIOS、XScale等诸多常用系列的处理器。U-Boot的主要特点有:源码开放,目前有些版本的未开源。支持多种嵌入式操作系统内核和处理器架构。可靠性和稳定性均较好。功能设置高度灵活,适合调试、产品发布等;设备驱动源码十分丰富,支持绝大多数常见硬件外设;并将对于与硬件平台相关的代码定义成宏并保留在配置文件中,开发者往往只需要修改这些宏的值就能成功使用这些硬件资源,简化了移植工作。U-Boot源码开放,目前有些版本的未开源。支持多种嵌入式操作系统内核和处理器架构。可靠性和稳定性均较好。功能设置高度灵活,适合调试、产品发布等;设备驱动源码十分丰富,支持绝大多数常见硬件外设;并将对于与硬件平台相关的代码定义成宏并保留在配置文件中,开发者往往只需要修改这些宏的值就能成功使用这些硬件资源,简化了移植工作。U-Boot的主要特点U-BOOT源码分析及编译

参考板的编译makeARCH=armCROSS_COMPILE=arm-linux-distcleanmakeARCH=armCORSS_COMPILE=arm-linux-s5pc210_universal_defconfigmakeV=1ARCH=armCROSS_COMPILE=arm-linux--j1请参考本案例微课U-Boot启动的一般流程

U-Boot启动第一阶段的主要工作U-BootSPL(

SecondaryProgramLoader,第二阶段程序加载器)对于一些SOC来说,它的内部SRAM可能会比较小,小到无法装载下一个完整的U-Boot镜像,因此就需要U-BootSPL,它主要负责初始化外部RAM和环境,并加载固件程序和真正的U-Boot镜像到RAM中执行。为了统一U-BootSPL的所有实现方式并允许简单地添加新的实现方法,U-Boot创建了通用的SPL框架。有了这个框架,几乎所有的电路板源文件都可以重复使用,不再需要代码重复或符号链接。ifeq($(CONFIG_SPL_BUILD),y)obj-y+=board_spl.oelseobj-y+=board.oendifobj-$(CONFIG_SPL_BUILD)+=foo.o#ifdefCONFIG_SPL_BUILDfoo();#endifSPL的目标文件是单独构建的,并放置在“spl”目录中。最终生成的二进制文件是u-boot-spl、u-boot-spl.bin和u-boot-spl.map。Kconfig为SPL启用了名为CONFIG_SPL_BUILD的配置选项。因此可以使用不同的设置为SPL编译源文件。可以通过Kconfig中的CONFIG_SPL选项启用SPL映像的构建。U-BootSPL(

SecondaryProgramLoader,第二阶段程序加载器)在Exynos4412芯片上电启动时首先启动iROM里面的程序,iROM里面的程序会复制BL1到iRAM里面去执行,接着BL1会继续复制BL2到iRAM中去运行。Samsung公司已经提供了BL1二进制文件,大小为8K。BL2就是引导加载程序U-boot的二进制文件,其大小被限制在了16K,但是正常编译出来的u-boot.bin已经远远大于16K了。因此,我们要使用U-boot提供的SPL(SecondaryProgramLoader)功能来解决这个问题。SPL是U-boot中独立的一个代码分支,复用了U-boot的代码,由CONFIG_SPL_BUILD配置项控制,它把U-boot的执行分为了2个阶段。SPL是U-boot第一阶段执行的代码,完成芯片级、板级的一些初始化操作,最主要的是初始化时钟和片外内存、加载U-boot第二阶段的代码并跳转到内存中去执行这部分代码。因此,在移植引导加载程序U-boot到Exynos4412目标板时,要使能U-boot的SPL功能。U-Boot启动的一般流程:第二阶段第二阶段初始化(也叫板级初始化)代码的入口就是_main函数,该函数定义在U-boot源代码u-boot/arch/arm/lib/crt0.S文件中。_main函数完成的主要工作包括:(1)设置C代码的运行环境,为接下来调用board_init_f函数做准备。设置堆栈,为执行C语言代码做好准备。如果使用了U-boot的SPL(SecondaryProgramLoader)功能(由CONFIG_SPL_BUILD宏定义),则可以由CONFIG_SPL_STACK宏定义堆栈基址;否则使用CONFIG_SYS_INIT_SP_ADDR宏定义堆栈基址。接着调用board_init_f_alloc_reserve函数,为U-boot的全局变量GD(globaldata)数据结构分配内存空间;再调用board_init_f_init_reserve函数,对全局变量GD进行初始化。U-Boot启动的一般流程:第二阶段(1)board_init_f()(2)board_init_r()U-Boot启动的一般流程:第二阶段(2)如果当前运行的是SPL模式,则进入_main函数就直接返回没有执行初始化的函数。而正常的U-boot模式会继续执行board_init_f函数,在board_init_f函数中调用initcall_run_list函数依次执行init_sequence_f函数数组里面的函数,例如U-boot驱动模型初始化、环境变量的初始化、串口的初始化、内存的初始化、U-bootrelocation操作前的环境初始化等。(3)执行U-bootrelocation操作,把内存中的u-boot进行重定位;清除BBS段。(4)调用board_init_r函数,在board_init_r函数中调用initcall_run_list函数,执行init_sequence_r函数数组里面的函数,完成后续的板级初始化操作。最后U-boot运行到了run_main_loop函数,并且在run_main_loop函数进入命令行状态,等待终端输入命令以及对命令进行处理。

7.2.3U-Boot环境变量U-Boot的环境变量是使用U-Boot的关键,它可以由用户定义并遵守约定俗成的一些用法,也有部分是U-Boot定义并不得更改。值得注意的是在未初始化的开发板中并不存在环境变量。U-Boot在缺省的情况下会存在一些基本的环境变量,当用户执行了saveenv命令之后,环境变量会第一次保存到flash中,之后用户对环境变量的修改和保存都是基于保存在flash中的环境变量的操作。环境变量名称相关描述bootdelay

执行自动启动的等候秒数baudrate

串口控制台的波特率netmask

以太网接口的掩码ethaddr

以太网卡的网卡物理地址bootfile

缺省的下载文件bootargs

传递给内核的启动参数bootcmd

自动启动时执行的命令serverip

服务器端的ip地址ipaddr

本地ip地址stdin

标准输入设备stdout

标准输出设备stderr

标准出错设备表7-3U-Boot常用环境变量U-Boot的环境变量中最重要的两个变量是:

bootcmd和bootargs。Bootcmd是自动启动时默认执行的一些命令,因此用户可以在当前环境中定义各种不同配置,不同环境的参数设置,然后通过bootcmd配置好参数。

Bootargs是环境变量中的重中之重.7.2.4

U-Boot命令U-Boot上电启动后,按任意键退出自启动状态,进入命令行状态。工作在启动加载模式时,BootLoader会自动执行bootcmd命令,比如这行代码。bootcmd=“nandread0x1000000x800000000x300000;bootm0x80000000”U-Boot首先把内核镜像拷贝到内存地址为0x80000000的位置,然后执行bootm0x80000000命令。bootm命令实际上调用的是do_bootm_linux函数。intdo_bootm_linux(intflag,intargc,char*constargv[], bootm_headers_t*images){ /*NoneedforthoseonRISC-V*/ if(flag&BOOTM_STATE_OS_BD_T||flag&BOOTM_STATE_OS_CMDLINE) return-1; if(flag&BOOTM_STATE_OS_PREP){ boot_prep_linux(images); return0; } if(flag&(BOOTM_STATE_OS_GO|BOOTM_STATE_OS_FAKE_GO)){ boot_jump_linux(images,flag); return0; } boot_prep_linux(images); boot_jump_linux(images,flag); return0;}在do_bootm_linux函数中,会定义一个函数指针Kernel,它指向内核的入口地址。在启动阶段之间,函数指针Kernel中hartid通过a0寄存器传递,设备树的起始地址通过a1寄存器传递。config_distro_bootcmd.h文件定义了bootcmd和许多辅助命令变量,这些变量自动搜索连接的磁盘以查找引导配置文件并执行它们。开发板必须提供配置<config_distro_bootcmd.h>以便它支持正确的引导设备类型集。要提供此配置,只需在包含<config_distro_bootcmd.h>之前定义宏BOOT_TARGET_DEVICES。例如:#ifndefCONFIG_SPL_BUILD#defineBOOT_TARGET_DEVICES(func)\func(MMC,mmc,1)\func(MMC,mmc,0)\func(USB,usb,0)\func(PXE,pxe,na)\func(DHCP,dhcp,na)#include<config_distro_bootcmd.h>#endif该宏中的每个条目都定义了一个单一的启动设备(例如特定的eMMC设备或SD卡)或启动设备的类型(例如USB、磁盘)

U-Boot对设备树的支持(扩展)在U-Boot源码的arch/<arch>/dts目录中包含一个Makefile文件,用于构建设备树blob(devicetreeblob,dtb)文件并将其嵌入到U-Boot映像中。这是一个十分有用的规则文件。如果开发者需要针对许多具有不同外设的类似开发板展开工作,开发者可以在设备树文件中描述每个板的特性,并且具有一个通用的源代码库。此功能的开启,只需要将CONFIG_OF_CONTROL添加到的开发板配置文件中。除了描述板上的硬件之外,U-Boot还使用设备树用于各种配置目的。例如,用于验证启动的公钥以特定格式嵌入到/signature节点中。实际上U-Boot使用设备树进行配置的内容众多,这包括开发板使用的设备、使用binman创建的图像格式、用于控制台的UART、用于安全启动的公钥以及许多其他内容。RISC-V架构的U-Boot引导过程(扩展)M模式U-Boot引导过程

S模式U-Boot引导过程当在M模式U-Boot中运行时,它会加载包含固件和S模式操作系统的payload镜像(例如fw_payload),在这种情况下,开发者可以使用mkimage将payload镜像打包成uImage格式,并使用bootm命令启动它。RISC-V生成启动映像可能包含用于特定平台初始化的U-BootSPL(SecondaryProgramLoader,第二阶段程序加载器,也叫辅助程序加载器)。U-BootSPL然后加载一个FIT映像(u-boot.itb),该镜像包含提供SBI的固件(例如fw_dynamic),以及在S模式下运行的常规U-Boot(或U-Boot本身).最后,加载S模式操作系统。7.3交叉开发环境的建立PartThree7.3交叉开发环境的建立主机-目标机交叉开发环境模式是由开发主机和目标机两套计算机系统内组成的。开发主机一般指通用计算机,如PC等,目标机指嵌入式开发板(系统)。通过交叉开发环境,在主机上使用开发工具(如各种SDK),针对目标机设计应用系统进行设计工作,然后下载到目标机上运行。交叉开发模式一般采用以下3个步骤:(1)在主机上编译BootLoader(引导加载程序),然后通过JTAG接口烧写到目标板。(2)在主机上编译Linux内核,然后通过BootLoader下载到目标板以启动或烧写到Flash。(3)在主机上编译各类应用程序,通过NFS运行、调试这些程序,验证无误后再将制作好的文件系统映像烧写到目标板7.3.1.主机与目标机的连接方式主机与目标机的连接方式主要有串口、以太网接口、USB接口、JTAG接口等方式连接。主机可以使用minicom、kermit或者Windows超级终端等工具,通过串口发送文件。目标机亦可以把程序运行结果通过串口返回并显示。以太网接口方式使用简单,配置灵活,支持广泛,传输速率快,缺点是网络驱动的实现比较复杂。JTAG(JointTestActionGroup,联合测试行动小组)是一种国际标准测试协议(IEEE1149.1标准),主要是用于对目标机系统中的各芯片的简单调试,和对BootLoader的下载两个功能。JTAG连接器中,其芯片内部封装了专门的测试电路TAP(TestAccessPort,测试访问口),通过专用的JTAG测试工具对内部节点进行测试。因而该方式是开发调试嵌入式系统的一种简洁高效的手段。JTAG有两种标准,14针接口和20针接口。虽然JTAG调试不占用系统资源,能够调试没有外部总线的芯片,代价也非常小,但是JTAG只能提供一种静态的调试方式,不能提供处理器实时运行时的信息。它是通过串行方式依次传递数据的,所以传送信息速度比较慢。7.3.2.主机-目标机的文件传输方式主机-目标机的文件传输方式主要有串口传输方式、网络传输方式、USB接口传输方式、JTAG接口传输方式、移动存储设备方式。串口传输协议常见的有kermit、Xmodem、Ymoderm、Zmoderm等。串口驱动程序的实现相对简单,但是速度慢,不适合较大文件的传输。USB接口方式通常将主机设为主设备端,目标机设为从设备端。与其他通信接口相比,USB接口方式速度快,配置灵活,易于使用。如果目标机上有移动存储介质如U盘等,可以制作启动盘或者复制到目标机上,从而引导启动。网络传输方式一般采用TFTP(trivialfiletransportprotocol)协议。TFTP是一个传输文件的简单协议,是TCP/IP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议,提供不复杂、开销不大的文件传输服务。端口号为69。此协议只能从文件服务器上获得或写入文件,不能列出目录,不进行认证,它传输8位数据。传输中有三种模式:netascii,这是8位的ASCII码形式;另一种是octet,这是8位源数据类型;最后一种mail已经不再支持,它将返回的数据直接返回给用户而不是保存为文件。7.3.3.文件系统的挂接-配置网络文件系统NFSNFS(NetworkFileSystem)即网络文件系统,是FreeBSD支持的文件系统中的一种,它允许网络中的计算机之间通过TCP/IP网络共享资源。在NFS的应用中,本地NFS的客户端应用可以透明地读写位于远端NFS服务器上的文件,就像访问本地文件一样。NFS的优点主要有:(1)节省本地存储空间,将常用的数据存放在一台NFS服务器上且可以通过网络访问,那么本地终端将可以减少自身存储空间的使用。(2)用户不需要在网络中的每个机器上都建有Home目录,Home目录可以放在NFS服务器上且可以在网络上被访问使用。(3)一些存储设备如软驱、CDROM和Zip等都可以在网络上被别的机器使用。这可以减少整个网络上可移动介质设备的数量。交叉编译工具链

交叉开发工具链就是为了编译、链接、处理和调试跨平台体系结构的程序代码。从工具链的组成上来说,它一般由多个程序构成,glibc、gcc、binutils和gdb四个软件提供分别对应着各个功能。常见的交叉编译例子如下:(1)在WindowsPC上,利用诸如类似ADS、RVDS等软件,使用armcc编译器,则可编译出针对ARMCPU的可执行代码。(2)在LinuxPC上,利用arm-linux-gcc编译器,可编译出针对LinuxARM平台的可执行代码。(3)在WindowsPC上,利用cygwin环境,运行arm-elf-gcc编译器,可编译出针对ARMCPU的可执行代码。7.4.2工具链的构建方法通常构建交叉工具链有如下三种方法。方法一:分步编译和安装交叉编译工具链所需要的库和源代码,最终生成交叉编译工具链。该方法相对比较困难,适合想深入学习构建交叉工具链的读者及用户。如果只是想使用交叉工具链,建议使用下列的方法二构建交叉工具链。方法二:通过Crosstool脚本工具来实现一次编译,生成交叉编译工具链,该方法相对于方法一要简单许多,并且出错的机会也非常少,建议大多数情况下使用该方法构建交叉编译工具链。方法三:直接通过网上下载已经制作好的交叉编译工具链。该方法的优点是简单可靠,缺点也比较明显,扩展性不足,对特定目标没有针对性,而且也存在许多未知错误的可能,建议读者慎用此方法。

GCC对于GUN编译器来说,GCC的编译要经历四个相互关联的步骤∶预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编(Assembly)和链接(Linking)。file.cc程序源文件file.ic程序预处理后文件file.cxxc++程序源文件,也可以是file.cc/file.cpp/file.c++file.iic++程序预处理后文件file.hc/c++头文件file.s汇编程序文件file.o目标代码文件Binutils与glibcBinutilsBinutils提供了一系列用来创建、管理和维护二进制目标文件的工具程序,如汇编(as)、连接(ld)、静态库归档(ar)、反汇编(objdump)、elf结构分析工具(readelf)、无效调试信息和符号的工具(strip)等。通常binutils与gcc是紧密相集成的,没有binutils的话,gcc是不能正常工作的。

glibcGlibc是gnu发布的libc库,也即c运行库。Glibc是linux系统中最底层的应用程序开发接口,几乎其它所有的运行库都倚赖于glibc。Glibc除了封装linux操作系统所提供的系统服务外,它本身也提供了许多其它一些必要功能服务的实现,比如open,malloc,printf等等。Glibc是GNU工具链的关键组件,用于和二进制工具和编译器一起使用,为目标架构生成用户空间应用程序。ELF(可执行和可链接格式,ExecutableLinkableFormat)ELF(ExecutableLinkableFormat)是一种Unix-like系统上的二进制文件格式标准。ELF文件格式对于底层的操作系统开发非常重要,当程序需要在底层进行优化,调试,排错等操作时,ELF文件可以更好地帮助程序员完成任务。ELF标准中定义的采用ELF格式的文件分为以下4类:ELF(可执行和可链接格式,ExecutableLinkableFormat)ELF格式由几个节组成,包括头、程序头表、节头表和节本身。以下是每个节的简要概述:1.头(ELFHeader):ELF头包含文件的基本信息,包括文件类型、目标体系结构、入口点和其他元数据。2.程序头表(ProgramHeaderTable):这一节描述了程序执行时将加载到内存中的文件段。程序头表中的每个条目都描述了应该加载到内存中的文件的一个连续区域,以及有关该段的内存保护、对齐和其他属性信息。3.节头表(SectionHeaderTable):此部分包含有关文件中其他节的元数据,例如它们的大小、地址和类型。节头表中的每个条目描述文件中的一个节。4.节:可执行文件的大部分由包含程序代码、数据和其他资源的各种节组成。ELF文件中的一些常见节包括文本节(包含程序的可执行代码)、数据节(包含全局和静态数据)和符号表(将程序中使用的符号映射到它们的内存中地址)。ELF(可执行和可链接格式,ExecutableLinkableFormat)ELF文件处理的相关工具GNU为程序员提供了相关的处理工具软件,存放在Binutils工具包中。该工具包中的小程序如下:ar:归档文件,将多个文件打包成一个大文件。as:被gcc调用,输入汇编文件,输出目标文件供链接器ld连接。ld:GNU链接器。被gcc调用,它把目标文件和各种库文件结合在一起,重定位数据,并链接符号引用。objcopy:执行文件格式转换。objdump:显示ELF文件的信息。readelf:显示更多ELF格式文件的信息(包括DWARF调试信息)。

make与MakefileGNUmake是一个命令工具,是一个用来控制软件构建过程的自动化管理工具。Make工具通过称为Makefile的文件来完成并自动维护编译工作,由Richard

Stallman与Roland

McGrath设计开发。Makefile是用于自动编译和链接的,一个工程有很多文件组成,每一个文件的改变都会导致工程的重新链接,但是不是所有的文件都需要重新编译,Makefile中记录有文件的信

息,在make时会决定在链接的时候需要重新编译哪些文件。make命令格式:make[-fMakefile][option][target]#maketarget#make#makeclean(伪目标)GNUmake是一种常用的编译工具,通过它,开发人员可以很方便地管理软件编译的内容、方式和时机,从而能够把主要精力集中在代码的编写上。Gnumake的主要工作是读取一个文本文件makefile。这个文件里主要是有关目的文件是从哪些依赖文件中产生的,以及用什么命令来进行这个产生过程。Makefile一个makefile主要包含有一系列的规则,如下:

target

...

:

dependencies

...

command

...

target也就是一个目标文件,可以是Object

File,也可以是执行文件。还可以是一个标签(Label)。targetdependencies就是,要生成那个target所需要的文件或是目标。dependenciescommand也就是make需要执行的命令。(任意的Shell命令,可以有若干行)command必须以Tab键开头。否则,make就会显示出错信息。如果某一命令行太长可以分作两行,用反斜杠(\)连接。commandMakefile的宗旨Makefile的宗旨就是:让编译器知道要编译一个文件需要依赖其他的哪些文件。当那些依赖文件有了改变,编译器会自动的发现最终的生成文件已经过时,而重新编译相应的模块。makefile带来的好处就是—“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。Makefile包含内容Makefile里主要包含了:显式规则、隐含规则、变量定义、文件指示和注释。在Makefile中的命令,必须要以[Tab]键开始。

显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。显式规则由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的。隐含规则在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点像C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。变量的定义其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。文件指示Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++,Java中的“//”一样。注释Makefile案例:简单示例#makefileCC=riscv64-linux-gnu-gcc#指定交叉编译器INSTALL =installTARGET =led8all:$(TARGET)$(TARGET):led8.cled8.h $(CC)-static$<-o$@clean: rm-rf*.o$(TARGET)CC 指明编译器的宏EXEC 表示生成的执行文件名称的宏OBJS 目标文件列表宏LDFLAGS 连接参数宏all: 编译主入口clean: 清除编译结果INSTALL:将编译成功的可执行文件安装到系统目录中,一般为/usr/local/bin目录Makefile的文件名默认的情况下,make命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“Makefile”的文件,找到了解释这个文件。在这三个文件名中,最好使用“Makefile”这个文件名,因为,这个文件名第一个字符为大写,这样有一种显目的感觉。最好不要用“GNUmakefile”,这个文件是GNU的make识别的。有另外一些make只对全小写的“makefile”文件名敏感,但是基本上来说,大多数的make都支持“makefile”和“Makefile”这两种默认文件名。当然,可以使用别的文件名来书写Makefile,比如:“Make.Linux”,“Make.Solaris”,“Make.AIX”等,如果要指定特定的Makefile,可以使用make的“-f”和“--file”参数,如:make-fMake.Linux或make--fileMake.AIX。设置makefile中文件的搜索路径在makefile中,可以通过给VPATH赋值来设置规则中目标文件和依赖文件的搜索目录。Make首先搜索当前目录,如果未找到依赖的文件,make将按照VPATH中给的目录依次搜索VPATH对makefile中所有文件都有效。如:VPATH:=//c/ming//c/ming/head也可以使用指令vpath,与VPATH在使用上的区别是:vpath可以给不同类文件指定不同的搜索目录。如:

vpath%.c//c/mingvpath%.h//c/ming/headvpath%.c表示清除所有vpath对%.c设置的搜索目录定义变量和引用变量变量的定义和应用与Linux环境变量一样,变量名要大写,变量一旦定义后,就可以通过将变量名用圆括号括起来,并在前面加上“$”符号来进行引用。变量的主要作用:保存文件名列表保存可执行命令名,如编译器保存编译器的参数变量一般都在makefile的头部定义。按照惯例,所有的makefile变量都应该是大写。GNUmake的主要预定义变量 $*

不包括扩展名的目标文件名称$+

所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件$<

第一个依赖文件的名称$?

所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚$@

目标的完整名称$^

所有的依赖文件,以空格分开,不包含重复的依赖文件$%

如果目标是归档成员,则该变量表示目标的归档成员名称引用其它的Makefile在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。include的语法是:

include<filename>

filename可以是当前操作系统Shell的文件模式(可以保含路径和通配符)在include前面可以有一些空字符,但是绝不能是[Tab]键开始。include和<filename>可以用一个或多个空格隔开。引用其它的Makefile-实例有这样几个Makefile:a.mk、b.mk、c.mk,还有一个文件叫foo.make,以及一个变量$(bar),其包含了e.mk和f.mk,那么,下面的语句:

includefoo.make*.mk$(bar)includefoo.makea.mkb.mkc.mke.mkf.mk等价于Makefile案例:LKM编译内核模块编译时需要提供一个makefile来隐藏底层大量的复杂操作,使用户通过make命令就可以完成编译的任务。下面列举一个简单的编译hello.c的makefile文件:obj-m+=hello.koKDIR:=/lib/modules/$(shelluname-r)/buildPWD:=$(shellpwd)default:$(MAKE)-C$(KDIR)SUBDIRS=$(PWD)modules编译后获得可加载的模块文件hello.koMakefile案例:驱动程序相关Makefile应用程序调用open函数打开hello_drv这个设备,打开以后可以使用write函数向hello_drv的写缓冲区writebuf中写入数据(不超过100个字节),也可以使用read函数读取读缓冲区readbuf中的数据操作,操作完成以后应用程序使用close函数关闭chrdevbase设备。驱动程序编写驱动编写好以后是需要测试的,一般编写一个简单的测试程序,测试程序运行在用户空间。测试程序很简单通过输入相应的指令来对hello_drv设备执行读或者写操作。测试程序编写Makefile案例:Makefile体系扩展:clang与LLVMLLVM(LowLevelVirtualMachine,底层虚拟机)主要涉及到编译器的中、后端,

其代码以模块的形式进行划分和实现,

包括中间表示、代码分析、优化和代码生成等。

clang主要涉及到编译器的前端,

也是采用类似的形式进行模块化实现,

包括抽象语法树、词法分析、语法分析、语义分析和LLVM中间代码生成等.

LLVMLLVM编译流程结构图

高级语言前端结构图

中间代码优化器结构图

LLVM后端代码生成器的结构图GCC和Clang对比

嵌入式Linux系统移植过程PartFive7.5嵌入式Linux系统移植过程嵌入式Linux系统的移植主要有BootLoader(如U-Boot)、Linux内核、文件系统。嵌入式Linux系统移植的一般流程是:首先构建嵌入式Linux开发环境,包括硬件环境和软件环境;其次,移植BootLoader;然后,移植Linux内核和构建根文件系统;最后,一般还要移植或开发设备驱动程序。U-Boot移植1.下载U-Boot源码2.修改相应的文件代码参考origen开发板,在U-boot源码中添加Tiny4412目标板(Tiny4412是Exynos4412的垂直细分市场产物,通过硬件裁剪和软件优化满足特定场景需求)的目录和配置文件,并修改相应的配置文件。在U-boot源代码u-boot/目录下,执行下列命令添加Tiny4412板级目录和配置文件:

cdboard/samsung/cporigentiny4412–rafmvtiny4412/origen.ctiny4412/tiny4412.cmvtiny4412/tools/mkorigenspl.ctiny4412/tools/mktiny4412spl.ccd../../cpinclude/configs/origen.hinclude/configs/tiny4412.hcpconfigs/origen_defconfigconfigs/tiny4412_defconfigcdarch/arm/dts/cpexynos4412-origen.dtsexynos4412-tiny4412.dts修改配置文件:(1)修改board/samsung/tiny4412/Kconfig;(2)修改board/samsung/tiny4412/Makefile;(3)修改arch/arm/mach-exynos/Kconfig;(4)修改arch/arm/mach-exynos/Makefile;(5)修改arch/arm/mach-exynos/exynos4_setup.h;(6)修改arch/arm/dts/Makefile;(7)修改board/samsung/tiny4412/tools/mktiny4412spl.c。U-Boot移植3.编译U-BootU-Boot的编译命令主要分两步进行。第一步是配置;第二步是编译。以编译Exyson4210处理器的origen开发板为例,其编译方法是在Linux内核源码根目录执行下列命令:makeARCH=armorigen_defconfigmakeARCH=armuImageCROSS_COMPILE=arm-linux-编译完成后,会在Linux内核源码根目录下的arch/arm/boot目录下生成uImage二进制内核镜像。4.烧写到开发板上,运行和调试新开发的板子没有任何程序可以执行,也不能启动,需要先将U-Boot烧写到flash或者SD卡Makefile体系Makefile编译、连接的大致工作流程为:(1)内核源码根目录下的.config文件中定义了很多变量,Makefile通过这些变量的值来决定源文件编译的方式(编译进内核、编译成模块、不编译),以及涉及哪些子目录和源文件。(2)根目录下的顶层的Makefile决定根目录下有哪些子目录将被编译进内核,arch/$(ARCH)/Makefile决定arch/$(ARCH)目录下哪些文件和目录被编译进内核。(3)各级子目录下的Makefile决定所在目录下的源文件的编译方式,以及进入哪些子目录继续调用它们的Makefile。(4)在顶层Makefile和arch/$(ARCH)/Makefile中还设置了全局的编译、连接选项:CFLAGS(编译C文件的选项)、LDFLAGS(连接文件的选项)、AFLAGS(编译汇编文件的选项)、ARFLAGS(制作库文件的选项)。

(5)各级子目录下的Makefile可设置局部的编译、连接选项:EXTRA_CFLAGS、EXTRA_LDFLAGS、EXTRA_AFLAGS、EXTRA_ARFLAGS。

(6)最后,顶层Makefile按照一定的顺序组织文件,根据连接脚本生成内核映像文件。Linux内核的配置

#makemenuconfigARCH=armCROSS_COMPILE=arm-linux-这个过程就称为“配置内核”内核配置的基本原则是把不必要的功能都去掉,不仅可以减小内核大小,还可以节省编译内核和内核模块的时间。在内核源码的绝大多数子目录中,都具有一个Makefile文件和Kconfig文件。Kconfig就是内核配置界面的源文件,它的内容被内核配置程序读取用以生成配置界面,从而供开发人员配置内核,并根据具体的配置在内核源码根目录下生成相应的配置文件config。Kconfig文件的基本要素是config条目(entry),它用来配置一个选项,或者可以说,它用于生成一个变量,这个变量会连同它的值一起被写入配置文件.config中。内核移植(用设备树)

当前Linux内核对Exyson4系列芯片的支持比较完善了,Exyson4412芯片属于Exyson4系列,在移植的时候可以直接复用这些相关代码。在移植Linux内核到目标板前,要在Linux内核源码下添加目标板的设备树文件和目标板的配置文件。对于ARM架构的芯片,设备树文件存放在Linux内核源码根目录下的arch/arm/boot/dts目录下,目标板配置文件存放在Linux内核源码根目录下的arch/arm/configs目录下。设备树文件直接复用内核源码根目录下的arch/arm/boot/dts/exynos-4412-tiny4412.dts这个文件,目标板配置文件参考Linux内核源码根目录下的arch/arm/configs/exynos4_defconfig这个文件。在Linux内核源码目录执行如下命令添加相应的配置文件:

cdarch/arm/boot/dts/cpexynos4412-tiny4412.dtsexynos4412-tiny4412SDK.dtscparch/arm/configs/exynos4_defconfigarch/arm/configs/tiny4412SDK_defconfig内核配置与编译内核移植(用设备树)

通过修改Linux内核源码根目录下的arch/arm/boot/dts/Makefile文件,使内核在编译过程中编译新添加的exynos4412-tiny4412SDK.dts文件,对应的修改如下:

diff--gita/arch/arm/boot/dts/Makefileb/arch/arm/boot/dts/Makefileindex30bbc37..0410e7f---a/arch/arm/boot/dts/Makefile+++b/arch/arm/boot/dts/Makefile@@-115,6+115,7@@dtb-$(CONFIG_ARCH_EXYNOS4)+=\exynos4412-origen.dtb\exynos4412-smdk4412.dtb\exynos4412-tiny4412.dtb\+exynos4412-tiny4412SDK.dtb\exynos4412-trats2.dtb配置完成后,修改arch/arm/boot/dts/exynos4412-tiny4412SDK.dts文件,在chosen中增加了bootargs的设置:

chosen{stdout-path="/serial@13800000";bootargs="root=/dev/ram0rwrootfstype=ext4console=ttySAC0,115200init=/linuxrcearlyprintk";};内核移植(用设备树)修改完成后,执行命令:makeARCH=armuImageCROSS_COMPILE=arm-linux-编译Linux内核。编译完成后,会在Linux内核源码根目录下的arch/arm/boot目录下生成uImage二进制内核镜像,在arch/arm/boot/dts/目录下会生成目标板上用的设备树镜像文件exynos4412-tiny4412SDK.dtb。把移植好的U-boot烧进SD卡,接下来把uImage、和exynos4412-tiny4412SDK.dtb复制进SD卡,目标开发板选择从SD卡启动,使其进入U-boot命令行状态,在U-boot命令行输入下列命令启动Linux内核:

loadmmc00x40007000uImageloadmmc00x42000000exynos4412-tiny4412SDK.dtbbootm0x40007000-0x42000000U-boot读取了SD卡上的uImage和exynos4412-tiny4412SDK.dtb设备树二进制文件到内存,并从指定的内存地址开始执行内核。";};Gdb调试器PartSix7.6gdb是用于调试C、C++等语言编写的程序。它可以控制程序的执行,设置断点,查看和修改变量,查看堆栈信息,以及进行反汇编等操作。它还支持多线程和远程调试,帮助开发者快速定位和修复程序中的错误。

gdb是基于ptrace系统调用实现的调试器,通过ptrace系统调用来监控和控制目标进程的执行流程。

我们把gdb程序称为调试器,把待调试的程序称为目标程序。gdb和目标程序为父子关系,即gdb进程为目标进程的父进程。gdb工作原理GDB(GNUDeBugger)是自由软件基金会(FreeSoftwareFoundation,FSF)的软件工具之一。它的作用是协助程序员找到代码中的错误。(1)进入GDB

Gdbtesttest是要调试的程序,由gcctest.c-g-otest生成。进入后提示符变为(Gdb)。(2)查看源码(Gdb)l源码会进行行号提示。如果需要查看在其他文件中定义的函数,在l后加上函数名即可定位到这个函数的定义及查看附近的其他源码。或者:使用断点或单步运行,到某个函数处使用s进入这个函数。(3)设置断点(Gdb)b6这样会在运行到源码第6行时停止,可以查看变量的值、堆栈情况等;这个行号是Gdb的行号。(4)查看断点处情况(Gdb)

infob可以键入"infob"来查看断点处情况,可以设置多个断点;(5)运行代码(Gdb)

r(6)显示变量值(Gdb)

pn在程序暂停时,键入"p变量名"(print)即可;GDB在显示变量值时都会在对应值之前加上"$N"标记,它是当前变量值的引用标记,以后若想再次引用此变量,就可以直接写作"$N",而无需写冗长的变量名;(7)观察变量(Gdb)

watchn在某一循环处,往往希望能够观察一个变量的变化情况,这时就可以键入命令"watch"来观察变量的变化情况,GDB在"n"设置了观察点;(8)单步运行(Gdb)

n(9)程序继续运行(Gdb)

c使程序继续往下运行,直到再次遇到断点或程序结束;(10)退出GDB

(Gdb)

q远程调试PartSix7.7在嵌入式软件调试过程中,调试器通常运行于主机环境中,被调试的软件则运行于基于特定硬件平台的目标机上。主机上的调试器通过串口、并口或网卡接口等通信方式与目标机进行通信,控制目标机上程序的运行,实现对目标程序的调试,这种调试方式称为远程调试。常用的远程调试技术主要有插桩(stub)和片上调试(OnChipDebugging,OCD)两种。插桩(stub)指在目标操作系统和调试器内分别加入某些软件模块实现调试;片上调试指在微处理器芯片内嵌入额外的控制电路实现对目标程序的调试。片上调试方式不占用目标平台的通信端口,但它依赖于硬件。插桩方式仅需要一个用于通信的端口,其他全部由软件实现。7.7.1远程调试工具的构成插桩方式下的调试环境构成远程调试工具由三部分构成:主机端的调试器、远程通信协议和插桩模块。前两部分可采用GDB(GNUdebugger)调试器来解决。为了监控和调试程序,主机GDB通过串行协议使用内存读写命令,无损害地将目标程序原指令用一个trap指令代替,从而完成断点设置动作。7.7.2通信协议-RSP$<data>#[chksum]该数据包分为四部分:第一部分是包头,由字符“$”构成;第二部分是数据包内容,对应调试信息,它可以是调试器发布的命令串,也可以是目标机的应答信息,数据包中应该至少有一个字节;第三部分是字符“#”,它是调试信息的结束标志;

第四部分是由两位十六进制数的ASCII码字符构成的校验码,7.7.3远程调试的实现方法及设置相对于宿主机远程调试环境的建立过程,目标机调试stub的实现更要复杂,它要提供一系列实现与主机GDB的通信和对被调试程序的控制功能的函数。这些功能函数GDB有的已经提供,如Gdb文件包中的m68k-stub.c、i386-stub.c等文件提供了一些相应目标平台的stub子函数,有的函数需要开发者根据特定目标平台自行设计实现。stub的主要子函数:sets_debug_traps():函数指针初始化,捕捉调试中断进入handle.exception()函数。Handle_exception():该函数是stub的核心部分。程序运行被中断时,首先发送一些主机的状态信息,如寄存器的值,然后在主机Gdb的控制下执行程序,并检索和发送Gdb需要的数据信息,直到主机Gdb要求程序继续运行,handle_exception()交还控制权给程序。Breakpoint():该功能函数可以被调试程序中设置断点。除以上函数外,开发人员需要针对特定目标平台,为stub实现以下底层功能函数,才能使调试stub正常与主机GDB协同工作,比如:GetDebugChar()、putDebugChar():读写通过Gdb远程串行协议与主机交互的数据。ExceptionHandler():各目标平台对系统中断向量的组织

温馨提示

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

评论

0/150

提交评论