C66AK版动态加载技术
·1 AMP、SMP和BMP模式介绍
C66AK是TI公司开发的多核异构处理器,该处理器集成了4个ARM核和8个DSP核及其他外设。从软件的角度来看,这种多核处理器的运行模式有三种:AMP(asymmetric multi-processing,非对称多处理)、SMP(symmetric multi-processing,对称多处理)和BMP(bound multi-processing,混合多处理)。下面是三种运行模式的特点及优点,如表1所示。
图1 三种运行模式的特点及优点
在AMP运行模式下,每个CPU内核运行一个独立的操作系统或同一个操作系统的独立用户程序。
在SMP运行模式下,一个操作系统的实例可以同时管理所有的CPU内核,为各个内核分配不同的进程任务,每个CPU内核还能访问系统资源。这种模式提供运行性能。
而BMP运行模式类似SMP,一个操作系统的实例可以同时管理所有CPU内核,但每个应用被锁定在某个指定的核。与SMP相比,BMP带来了几大优势。它允许共享同一个数据集(data set)的应用独立地运行在同一个内核上,从而消除了SMP系统中会降低性能的CACHE冲突。BMP还可以让为单核环境编写的传统程序正确地运行在多核环境,即让这些应用运行在某一个内核上。在BMP系统中,锁定于一个内核的应用无法利用其他内核,即使其他内核处于空闲状态。
http://www.elecfans.com/emb/20190402899139.html
2 动态加载机制
在阐述动态加载机制前,我们首先思考一个问题:我们平时使用的一些软件或系统经常因为需求的修改或升级导致停机。当用户越来越依赖这个软件或系统时,这种停服会让人无法容忍,特别是一些关键的军事应用上的系统或软件。 因此,迫切需要一种机制能在运行状态下配置系统,换句话说,系统在运行状态下可以动态的添加模块,这就是动态加载机制。对动态加载机制的研究,对于提高SightOS系统性能,保证系统的可靠性,延长系统的生命周期,降低系统开发成本都具有十分重要的意义。
不支持动态加载机制的嵌入式操作系统,其任务代码必须与操作系统内核一起编译,链接成一个完整的可执行镜像,并下载或烧写到目标板中运行,一旦任务代码需要修改更新,则必须重新编译所有代码。这一不支持动态软件模块的缺点限制了嵌入式操作系统的灵活性和可扩展性,给应用软件的更新升级也带来了不便。
动态加载能让程序在运行期间需要调用某一模块的功能时,由加载器将该模块即时载入内存,进行相应的重定位处理后将控制权交还调用程序。动态加载机制运用动态链接的原理使得系统具有动态的加载和动态解析的能力,模块只有在被调用执行时才被链接入系统。
SightOS嵌入式实时操作系统的动态加载可分为加载、刷新和卸载三个操作。其中,加载完成读入模块(用户程序)到内存,然后对模块(用户程序)未解决的外部引用进行解析(也就是符号解析和重定位)使之可以运行的过程。刷新可以在IDE上显示模块(用户程序)的信息。当模块(用户程序)不再使用时,就从内存中卸载。
3 C66AK版动态加载实现细节
C66AK版动态加载设计参考Linux中动态文件的实现原理,采用Unix标准的ELF标准格式文件。同时,采用SMP运行模式来设计C6678版动态加载。ELF文件是地址无关的,SightOS能够在运行前,根据可执行文件的加载位置,修改各函数和变量的物理地址,使该程序能够顺利的运行。同时,该文件格式通用性广,可扩展性强,对arm体系编译器有很好的支持。
在C66AK的ARM平台上,ELF标准支持多种代码指令的重定位类型,本文通过查看相关资料,发现THUMB指令集下的2中重定位类型对解决C66AK的ARM平台的ELF文件加载有借鉴意义。这两种重定位类型分别为32位绝对重定位类型和24位相对地址跳转类型,它们在ELF标准中分别用R_ARM_ABS32宏和R_ARM_THM_JUP24宏来表示。
①R_ARM_ABS32类型
R_ARM_ABS322重定位类型大量应用于一般变量和函数的重定位,在该类型下,变量和函数地址的引用是间接进行的。例如,当代码为某个变量赋值时,编译器将生成类似下面的汇编指令:
ldr r3, [pc,#28]
str r2,[r3]
在生成的汇编指令中,第一条指令将 pc + 28地址中存储的目标变量的地址加载到 r3寄存器中,第二条指令才实现了对该变量的赋值,因此,在加载了该代码段后,只需将 pc + 28地址中存储的值修改为目标变量的绝对物理地址,即可实现变量的重定位工作。函数地址的重定位过程与变量的重定位过程完全相同。
②R_ARM_THM_JUMP24类型
R_ARM_THM_JUMP24类型主要用于函数的重定位,与R_ARM_ABS32类型不同的是,该类型通过相对地址实现对目标函数的定位。在“bl”等跳转指令中,表示相对地址的数据位宽只有24 bit,因此它只能定位到正负32MB 的区域。例如,当代码尝试调用某个函数时,编译器将生成类似下面的汇编指令:
bl 0; f7ff fffe
在该指令中,编译器将目标函数的相对地址设置为0,因此在加载了该代码段后,必须修改该指令,填入目标函数正确的相对地址,从而实现函数的重定位。
为了实现动态加载模块的加载操作,在SightOS内核中需要实现一个ELF文件加载。这个过程中,首先需要解析ELF文件头,然后将ELF文件中的代码段、只读数据段以及数据段加载到内存的适当位置中,并初始化任务的相关数据结构,其次根据重定位段和符号表段执行符号的重定位操作,重定位成功,则加载成功,否则加载失败。
3.1解析ELF文件
解析ELF文件的目的是为动态加载过程做准备,其基本步骤如下:
- 将 ELF文件头( ELF Header) 加载到内存中,通过ELF头的Magic Number、文件类型以及目标架构等字段,判断该文件是否是合法的ELF文件,若不是,则报错并终止加载过程。
- 根据 ELF 头给出的信息,找到段名字符串表 ( Section Header String Table) 和段头表( Section Header Table) 在文件中的位置,并将它们加载到内存中。
- 遍历段头表和段名字符串表,将段名字符串与表2中列出的段名进行匹配,找出各段在 ELF 文件中的位置。若该 ELF文件不包含代码段( .text) 、符号表 ( .symtab) 以及符号字符串表( .strtab) ,则认为该用户程序无法顺利加载和运行,因此报错并终止加载过程。
表2 与符号重定位相关的段
3.2符号重定位
在加载了软件模块后,代码中各函数和变量的绝 对地址也相应确定了,此时可以对这些符号进行重定位操作,其步骤如下: - 依次读取 ELF 文件的各个重定位段( 即:.rel.text,.rel.rodate以及.rel.bss) ,找到需要修改的指令或 数据在各段中的偏移量,并确定它们的重定位类型。
- 对于每个需要重定位的符号,确定它们的绝对地址:
①若该符号定义在软件模块的内部,则通过 ELF 文件的符号表段( .symtab) ,确定该符号的绝对地址,其计算过程为:
符号的绝对地址 = 符号所在段的加载地址 + 符号在段中的偏移量
②若该符号定义在内核代码中,则通过内核中的符号表中的名字查找该符号的绝对地址。
③若在镜像工程和内核中均找不到该符号的定义,则通报错误并终止符号重定位过程。 - 根据各符号的重定位类型,修改已加载到内存中的各个段的数据,使各符号的引用都指向正确的位置。
题外话
“All problems in computer science can be solved by another level of indirection(计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决)”。 --David Wheeler