就把这当作ARM学习的开篇,虽然最近RiscV被各大媒体推崇,我也持续看好。
但是我还是觉得短期内很难在高利润的行当对ARM和x86造成威胁,因为指令集的特点,以及各个厂家的各自为战,期待早点出现RiscV的天降猛男。
有个朋友,说他们公司因为制裁,只有V8的授权。所以开源或者自研架构又是一条不可避免的道路,不过无论如何,对于我们来说,技术是具有迁移性和相关性的,所以某种程度上,不用过于的纠结,开始行动比较重要,当然选择很重要。
突然发现我也是废话大师!
PART ONE-实现
1.ARM处理器家族
(1)什么是多内核
Candidates should be familiar with the available processors from ARM and know which of these may be used in multiprocessor configurations.
需要熟悉ARM的处理器,并且了解其中哪些是用于多处理器配置的。
ARM处理器根据其设计和应用场景的不同,有多种不同的系列。其中,Cortex-A系列是用于高计算要求的领域,如智能手机、平板电脑、汽车娱乐系统、数字电视等,它可以运行丰富的操作系统和提供交互媒体和图形体验。
在Cortex-A系列中,Cortex-A9微体系结构既可用于可伸缩的多核处理器(Cortex-A9 MPCore多核处理器),也可用于更传统的处理器(Cortex-A9单核处理器)。这两种类型的处理器都支持16、32或64KB 4路关联的L1高速缓存配置,对于可选的L2高速缓存控制器,最多支持8MB的L2高速缓存配置。这种高度的灵活性使它们能够适用于特定的应用领域和市场。
Cortex-A系列中的Cortex-A9内核可以用于多处理器配置。它既可用于可伸缩的多核处理器(Cortex-A9 MPCore多核处理器),也可用于更传统的处理器(Cortex-A9单核处理器)。
Cortex-A9 MPCore多核处理器包含两个或更多同构或异构的内核,并支持运行一个轻量级的操作系统以进行系统管理。它适合用于高性能、高吞吐量的计算应用,如服务器、高端智能手机和平板电脑等。
另外,Cortex-A15和Cortex-A17内核也是高性能处理器,可以用于多种配置中。它们具有更强的性能和功效,并支持运行复杂的操作系统和交互媒体和图形体验。
总之,ARM的Cortex-A系列处理器中的Cortex-A9、Cortex-A15和Cortex-A17内核都可以用于多处理器配置,以满足高性能和高计算要求的应用需求。
另外,Cortex-M系列是通用微处理器,一般不运行操作系统或只运行实时操作系统(RTOS)。
而Cortex-R系列是实时微处理器,它们在Cortex-M的基础上增强了实时性能,主要用于高安全性和高实时性的场合。
(2)连接硬件
Candidates should be aware of the concept of coherency hardware, what it does, why it is required and what are the benefits of using it.
需要了解连接硬件的含义,它是做什么的,为什么需要它以及它的优势是什么。
连接硬件是在多核系统中必须使用的。要理解它是如何工作的以及它是如何影响软件开发的,需要具备对多核系统以及缓存知识的了解。
在多核系统中,硬件连接和通信是一个关键的考虑因素。为了实现多个处理器核心之间的有效通信和数据共享,通常需要使用高速缓存一致性协议,如MESI协议。这些协议确保当一个核心修改了共享数据时,其他核心能够立即或最终获得更新的数据。
在多核系统中,每个核心都有自己的高速缓存(L1或L2),但它们之间需要通过共享的内存区域进行通信。当一个核心访问共享内存区域时,其他核心也需要更新它们的高速缓存以确保一致性。
为了实现这一目标,硬件需要维护一种状态机制,如MESI协议中的状态,来跟踪每个高速缓存行的状态(例如,修改、独占、共享等)。当一个核心修改了共享数据时,它会发送一个消息给其他核心,通知它们更新它们的高速缓存行状态。
这种通信机制对于确保多核系统中的数据一致性和正确性至关重要。它也影响软件开发的方面,因为开发者需要考虑如何有效地利用多核处理器资源,并确保数据的一致性和共享。
为了更有效地开发多核系统软件,开发者需要了解硬件的连接方式和通信机制,以便编写高效、可靠的并行代码。他们还需要了解多核处理器架构的特性和限制,以及如何利用硬件特性来优化软件性能。
多核心系统中的硬件连接和通信对软件开发的影响主要体现在以下几个方面:
内存一致性:在多核心系统中,每个核心都有自己的高速缓存(L1或L2),但它们之间需要通过共享的内存区域进行通信。为了确保数据的一致性,需要使用高速缓存一致性协议,如MESI协议。这些协议确保当一个核心修改了共享数据时,其他核心能够立即或最终获得更新的数据。软件开发需要考虑如何处理这种内存一致性的问题,以避免数据不一致或竞态条件。
通信开销:在多核心系统中,核心之间的通信存在一定的开销。如果大量核心频繁地进行通信,会影响系统的性能。因此,在软件开发中,需要尽量避免不必要的核心间通信,或者采用高效的通信机制来降低通信开销。
负载均衡:在多核心系统中,如果负载分布不均衡,会导致某些核心空闲而其他核心还在忙碌,从而影响系统性能。软件开发中需要考虑负载均衡的问题,通过合理地分配任务和数据,使得每个核心都能得到充分利用。
并行算法和编程模型:为了充分利用多核心系统的并行处理能力,需要采用适合并行处理的算法和编程模型。这包括使用线程、进程、数据并行等编程模式,以及选择合适的同步原语和通信机制。
系统集成和测试:在多核心系统中,由于硬件连接和通信的复杂性,系统的集成和测试变得更加困难。软件开发中需要考虑如何进行有效的集成和测试,以确保系统的正确性和可靠性。
综上所述,多核心系统中的硬件连接和通信对软件开发的影响是多方面的。
为了充分利用多核心系统的优势,开发者需要深入了解硬件的特性和限制,并采用合适的并行处理技术和编程模型来优化软件性能。
(3)通用中断控制器GIC
GIC常用于Cortex-A5多内核、Cortex-A9多内核以及Cortex-R7多内核处理器中,它也可作为可选部件存在于Cortex-A7和Cortex-A15内核中。
它提供了一种可扩展的中断处理机制,使开发者可以根据需要进行配置和使用。
GIC(Generic Interrupt Controller)是ARM Cortex-A系列处理器中使用的通用中断控制器。它为多核处理器和单核处理器提供中断管理功能,并支持可扩展的中断处理机制。
在多核处理器中,GIC允许多个核心共享中断信号,并协调各个核心对共享中断的处理。通过GIC,每个核心可以独立地响应和分发中断,确保中断处理的并行性和高效性。
此外,GIC还提供了一些可编程的中断控制功能,如设置中断优先级、子优先级、使能/禁用中断等。这些功能使得开发者能够灵活地配置和控制中断处理的行为。
总之,GIC是Cortex-A系列处理器中用于中断管理的关键组件,它为多核和单核处理器提供了通用和高效的中断处理机制。
(4)架构版本归属
Candidates must be able to identify which ARM processors conform to which Architecture versions. Architectures from ARMv4T onwards are included, with the exception of ARMv7-M and ARMv6-M architectures.
需要能够识别具体的ARM的处理器是属于哪个处理器架构版本的。ARMv4T架构之后的都需要了解。
ARMv4T架构:
ARM7TDMI系列处理器(如ARM710T、ARM720T、ARM740T等)
ARM9TDMI系列处理器(如ARM920T、ARM940T等)
ARMv5TEJ架构:
ARM7EJ-S系列处理器(基于ARMv5TEJ架构)
ARMv6架构:
ARM11系列处理器(如ARM11MPCore、ARM1176、ARM1156、ARM1136等)
XScale系列处理器(Intel公司的产品)
ARMv7架构:
Cortex-A系列处理器(如Cortex-A5、Cortex-A7、Cortex-A9、Cortex-A15等)
Cortex-R系列处理器(如Cortex-R4、Cortex-R7等)
Cortex-M系列处理器(如Cortex-M3、Cortex-M4等)
ARMv8架构:
AArch64架构(64位指令集)
AArch32架构(32位指令集)
Cortex-A50系列处理器(基于ARMv8架构,支持64位指令集)
Cortex-A53系列处理器(基于ARMv8架构,支持64位指令集)
Cortex-A57系列处理器(基于ARMv8架构,支持64位指令集)
Cortex-A72系列处理器(基于ARMv8架构,支持64位指令集)
ARMv9架构:
Cortex-X2
Cortex-A710
Cortex-A510
这些是ARM的处理器架构版本中比较常见的系列。每个系列中的处理器型号可能会有所不同,但它们都遵循相同的架构规范。希望这些信息对您有所帮助!
(5)性能
Candidates must be able to distinguish between applications, real-time and microcontroller profiles, be aware of the distinguishing features between them and understand the typical target market/applications of each.
需要能够区别ARM Cortex的应用、实时和微控制器三大系列的区别,理解它们之间的不同特性以及所针对的典型目标应用和市场。
《Cortex-A系列程序员指南》,顾名思义,它只介绍了Cortex-A系列,并没有提供很多关于Cortex-R和Cortex-M系列的知识。Cortex-A系列处理器主要应用于需要MMU内存管理单元和缓存工作的操作系统的应用场合,要求拥有高性能的处理能力。
Cortex-R系列处理器主要面向实时应用,它面向需要硬实时反应能力的嵌入式系统。这说明它的特性要求有快速、确定的中断响应,有紧密耦合的存储器(TCM)位于处理器的局部快速总线上以提供快速响应的代码和数据,有奇偶校验或者ECC校验机制来保证错误的检测和修正。
Cortex-R系列处理器使用内存保护单元MPU代替Cortex-A系列中的内存管理单元MMU来进行内存的管理和保护。
Cortex-M系列处理器面向低成本和功耗敏感的微控制器和混合信号系统。例如一些终端设备,包括智能电表、人机交互设备、自动和工业控制系统、大型家用电器、消费类产品和医疗仪器等。这类领域应用的关键是要求微量代码(更好的代码密度),易于使用以及节能。例如,Cortex-M0+内核是ARM处理器中能耗最低的内核,可达到mW/MHz。Cortex-M系列处理器仅支持16位和32位的Thumb指令集,通常不含缓存。
之前做过一段时间Zigbee的智慧农业项目,买的开发板就是M内核。
不过已经三年前了,时间好快啊!
2.指令周期时序
Candidates should be aware of the effects of pipeline and memory system on instruction execution timing.
需要了解流水线和内存系统对指令执行时间的影响。
了解一些流水线架构相关的基本知识。
了解ARM7TDMI的三级流水线(取指、译码、执行)的含义可以帮助理解流水线对执行周期时序的影响。
ARM7TDMI技术参考手册给出了这个流水线,这让理解流水线的概念变得更容易。
例如,在ARM7TDMI上分支跳转会导致2个周期的损失,因为分支跳转发生在流水线的执行阶段,此时位于取指和译码阶段的指令将被冲刷掉,流水线被重新按新的指令流填满,因此在跳转后的第一条指令执行前将产生3个周期的延迟。
同理,一旦理解了简单的流水线,“加载-使用损耗”的概念就很明了了,它用于描述当寄存器加载一个存储空间的值时的状况。
如果接下来的指令需要使用这个寄存器的值,那么它需要等待该寄存器完成数据的加载(并到达处理器流水线的相关级,可能通过专用通道到达)。
编译器(或者汇编器)会避免这种情况发生,它一般会试图把使用加载数据的指令从加载指令处分离开。
同理,一旦理解了简单的流水线,“加载-使用损耗”的概念就很明了了,它用于描述当寄存器加载一个存储空间的值时的状况。
如果接下来的指令需要使用这个寄存器的值,那么它需要等待该寄存器完成数据的加载(并到达处理器流水线的相关级,可能通过专用通道到达)。
编译器(或者汇编器)会避免这种情况发生,它一般会试图把使用加载数据的指令从加载指令处分离开。
PART TWO-软件调试
1.标准调试手段
(1)软件断点和硬件断点的区别
Candidates should be aware of the differences and limitations between Hardware and Software breakpoints. They should understand the usage of watchpoint units in configuring these two types of breakpoint.
需要清楚硬件和软件断点的区别以及各自的限制,需要了解在配置这两种类型的断点时观察点单元的使用。
在较早的处理器(如ARM7TDMI)中,并没有配备专用的断点指令BKPT,而是使用设置观察点单元来查看特定的位模式的,调试器可以在RAM的代码空间中设置无限量的软件断点。
调试器会读取需要放置断点指令的操作码,并保存到主机上。这条指令然后被一个特定的数据位替代(通常对应的是一个无效指令)。
通常,这需要处理器的数据缓存被清空并且使指令缓存无效,以确保从处理器数据端写入的特定数据可以被取指逻辑正确访问。
在较新的处理器上,包括所有的Cortex系列处理器,则采用了BKPT指令来代替原来的需要放置断点的操作码。
当断点被移除时,原本存储在主机上的原操作码会被写回。与此对应的是,硬件断点可以被设置在任何类型的存储器上(包括RAM和ROM),但它的数量则由硬件限制。
在学习这部分内容时,建议使用真实的调试器来理解它的使用和限制,获得一些实际的应用经验,这可以比书本上的解释提供更多的有益背景知识。
软件断点和硬件断点是两种不同的断点类型,它们在实现方式和适用场景上存在显著的差异。
硬件断点:
需要目标CPU的硬件支持。
断点的数目受特定硬件设计的限制。例如,ARM7/9内核最多支持两个硬件断点,而ARM11可以支持到8个硬件断点。
硬件断点可以设置在任何位置的代码上,包括ROM和RAM。
由于硬件断点设置的灵活性,它是最优先选用的断点资源。
软件断点:
通过在代码中设置特征值的方式实现。
当需要在某地址代码处设置软件断点时,仿真器会先将此处代码进行备份保护,然后将预先设定好的断点特征值(一般为0x0000等不易与代码混淆的值)写入此地址,覆盖原来的代码数据。
当程序运行到此特征值所在的地址时,仿真器识别出此处是一个软断点,便会产生中断。当取消断点时,之前受保护的代码信息会被自动恢复。
由于软件断点需要修改相应地址的值,所以一般只能设在RAM上,但是数量可以不受限制。总的来说,软件断点和硬件断点各有其特点和使用场景。硬件断点由于其设置的灵活性,通常是最优先选用的断点资源,但当需要大量设置断点时,软件断点可以作为有效的补充资源。
硬件断点在实际应用中更为常用。原因主要有以下几点:
硬件断点通常更快,因为它们直接由硬件控制,不需要软件进行干预。
硬件断点通常更可靠,因为它们不受软件运行的影响,比如内存管理、进程切换等。
硬件断点通常可以设置更多的断点,因为它们直接由硬件支持,不像软件断点可能会受到软件资源的限制。
当然,在某些情况下,软件断点也可能是更好的选择。例如,对于一些没有硬件调试支持的低端设备,或者在需要精细控制断点的位置和数量时,可能需要使用软件断点。
硬件断点在实际应用中更为常用,但具体选择哪种断点类型取决于具体的应用场景和需求。
(2)监控模式与停止模式调试
Candidates should be aware of the differences between debugging in each of the two modes and when one would be chosen over the other e.g. choosing monitor mode if interrupts must continue to be serviced while debugging.
需要了解调试的两种模式之间的区别以及何时选用哪种模式。
例如,需要在调试过程中仍旧响应中断时,则应选择监测模式来调试。
监控模式和停止模式调试之间的主要区别在于它们的目的和行为。
监控模式调试主要用于实时监测程序的运行状态和行为。在这种模式下,程序继续正常运行,同时允许开发者实时观察变量值、执行流程和函数调用等信息。监控模式调试适用于需要了解程序实时行为的情况,以便及时发现潜在问题或异常。它也适用于需要实时响应用户输入或外部事件的情况。
停止模式调试主要用于暂停程序的执行,以便进行深入检查和诊断问题。在这种模式下,程序停止执行,开发者可以对程序进行单步执行、检查变量值、查看堆栈跟踪等操作,以便更好地理解程序的状态和行为。停止模式调试适用于需要深入了解程序内部状态和行为的情况,例如处理复杂错误或调试性能问题。
在需要调试过程中仍旧响应用户中断时,监控模式调试可能更为合适。在这种模式下,程序继续正常运行,同时允许开发者实时观察和监控程序的运行状态。这样可以及时发现和修复与中断相关的问题,并确保程序的正常运行。
然而,在某些情况下,可能需要使用停止模式调试来更好地诊断问题。例如,当遇到复杂的错误或性能问题时,可能需要暂停程序的执行,以便进行更深入的检查和分析。在这种情况下,停止模式调试可以帮助开发者更好地理解程序的状态和行为,并找到问题的根本原因。
综上所述,选择监控模式还是停止模式调试取决于具体的需求和场景。监控模式更适合实时监测和响应用户输入或外部事件的情况,而停止模式更适合深入检查和诊断复杂问题的情况。根据实际情况选择合适的调试模式将有助于提高开发效率和程序的稳定性。
(3)矢量捕捉
Candidates should be aware of the function of this feature, why and when it is typically used.
需要了解矢量捕捉的功能,为什么以及何时需要使用它。
矢量捕捉是调试器捕获处理器异常中断的方法,早期开发时在异常中断服务句柄被取指之前捕获到处理器的异常中断。
许多ARM的处理器都由矢量捕获硬件来完成这一任务。而在另外一些处理器,如ARM7TDMI,调试器可以采用在异常矢量表的适当位置设置断点的方法来进行中断矢量捕获。
矢量捕捉是一种调试技术,用于在处理器异常中断发生时捕获异常信息。它通常用于嵌入式系统开发中,尤其是在调试和排除异常问题时。
矢量捕捉的主要目的是获取异常发生时的处理器状态信息,包括程序计数器(PC)、堆栈指针(SP)、寄存器内容等。这些信息可以帮助开发人员快速定位异常发生的具体位置,并理解异常发生时处理器的行为。
为什么需要使用矢量捕捉?主要有以下几个原因:
快速定位异常:通过矢量捕捉,开发人员可以获得异常发生时的详细信息,从而快速定位异常发生的具体位置。这有助于缩短调试时间,提高开发效率。
深入理解异常行为:矢量捕捉不仅提供了异常发生时的处理器状态,还可以帮助开发人员理解异常的具体行为。这有助于确定异常的根本原因,并为修复异常提供重要线索。
优化异常处理:通过矢量捕捉,开发人员可以评估异常处理程序的性能,并对其进行优化。例如,通过分析捕捉到的异常信息,开发人员可以检查异常处理程序是否正确处理了异常,并对其进行必要的调整。
何时需要使用矢量捕捉?以下是一些可能需要使用矢量捕捉的情况:
当无法正常启动程序时:如果程序无法正常启动,可能存在硬件或软件故障。在这种情况下,使用矢量捕捉可以帮助开发人员快速定位问题所在。
当程序出现未处理的异常时:在程序运行过程中,如果出现未处理的异常,可能会导致程序崩溃或数据丢失。通过矢量捕捉,可以捕获这些异常并进行调试,以修复问题。
当需要深入了解程序行为时:有时,开发人员可能需要对程序的运行行为进行深入了解。例如,在性能优化或调试复杂问题时,矢量捕捉可以帮助开发人员更好地理解程序的行为。
总之,矢量捕捉是一种非常有用的调试技术,可以帮助开发人员快速定位异常问题、深入理解异常行为并进行优化处理。在嵌入式系统开发中,特别是在调试复杂异常问题时,使用矢量捕捉可以提高开发效率和程序的稳定性。
(4)判别异常触发原因(如DFSR、SPSR、Link链接寄存器)
Candidates should understand how to determine the actual cause of an exception by using additional resources, which might include special registers that contain fault status. For example, using the DFSR, DFAR to determine cause of a Data Abort or using the SPSR and LR to locate and retrieve SVC comment field.
DFSR (Data Fault Status Register):当数据访问发生异常时,相关的异常类型和向量会写入到DFSR寄存器中。例如,当发生数据访问权限违规、地址无效或数据读取错误等情况时,会触发数据异常,相关的信息会被写入到DFSR中。
SPSR (Saved Program Status Register):在进入异常处理时,当前程序状态寄存器(PSR)会被保存到SPSR中,以便在异常处理完成后恢复执行。通过检查SPSR的状态,可以了解异常发生时的程序状态。
Link链接寄存器:在某些架构中,如ARM,链接寄存器用于保存子程序返回地址。当一个子程序开始执行时,返回地址会被推入堆栈并存放在链接寄存器中。如果发生异常,这个地址可以用于从堆栈中恢复并跳转到正确的返回地址。
为了判断具体的异常触发原因,需要根据实际场景和异常的具体表现来分析。这可能包括检查程序状态寄存器(PSR)、程序计数器(PC)、堆栈指针(SP)和其他相关的系统寄存器的状态,以及检查相关的内存和I/O操作等。此外,异常处理程序中的上下文信息和调用堆栈信息也是重要的线索。
应当理解如何使用额外的资源来判别触发一个异常中断的原因,这些资源通常包括含有错误状态的特殊寄存器,如使用DFSR、DFAR来判别数据中止的原因,采用SPSR以及Link寄存器来定位并获取SVC系统调用的参数位。
当调试一段软件时,通常有必要了解为何特定的处理器异常会发生。为此ARM处理器提供了一系列的寄存器来提供有用的信息。
在异常中断中,当前异常模式的链接寄存器(LR)会给出主程序中最靠近触发异常的指令的位置。通常触发异常的指令的位置是LR寄存器的值减去4或者8。类似地,SPSR寄存器的mode位可以给出在进入异常模式之前处理器所处的模式。
对于特定的模式,还有一些额外的信息。在取指异常或数据中止异常后,CP15寄存器的错误状态寄存器(FSR)和故障地址寄存器(FAR)是值得注意的,它们可以给出异常中止发生的原因(如一个外部存储器错误或者该地址的转换表项无效)以及产生异常中止的内存访问操作的地址。
注意:ARMv6-M和ARMv7-M架构在异常处理模型上与其他的ARM内核有所不同。
(5)跟踪
Candidates should be aware of what Trace is,what on-chip and off-chip components are required to make it work, what it is used for.Also that it is non-intrusive.
需要了解什么是跟踪,它正常工作需要哪些片上和片外模块,它是用来做什么的,以及它是属于非侵入式的。
通过跟踪,开发人员可以了解处理器在运行时执行的操作和执行的指令。为了实现跟踪功能,需要一些片上和片外模块的支持。
片上模块包括跟踪硬件单元和相关控制逻辑。这些模块负责生成跟踪数据,并将数据存储在内部缓冲区或外部存储器中,以便后续分析。片上模块是非侵入式的,即它们不会影响处理器的正常功能。
片外模块包括跟踪数据捕获单元和外部存储器。跟踪数据捕获单元负责从片上跟踪硬件单元获取跟踪数据,并将其转换为可分析的格式。外部存储器用于存储跟踪数据,以便后续分析和调试。
(6)交叉触发
ARM交叉触发技术是一种调试技术,用于在特定事件发生时触发并记录处理器的行为。通过交叉触发,开发人员可以在调试过程中观察处理器在特定事件发生时的行为,例如异常、中断或特定指令执行等。
交叉触发机制通常由硬件和软件协同实现。硬件部分负责监测事件的发生,并在事件发生时触发跟踪机制。跟踪机制记录处理器执行的相关指令和操作,并将数据存储在内部缓冲区或外部存储器中。软件部分负责读取跟踪数据,并将其转换为可分析的格式,以便开发人员能够深入了解处理器的行为。
在ARM调试中,交叉触发技术对于诊断和解决问题非常重要。开发人员可以通过交叉触发机制观察处理器在特定事件发生时的行为,从而定位问题或错误的根源。这种技术还可以用于性能分析、代码覆盖率分析等场景,帮助开发人员优化代码和改进系统性能。
ARM交叉触发技术可以应用于多种应用场景,以下是其中一些常见的应用场景:
异常和中断调试:通过交叉触发技术,开发人员可以在异常或中断发生时观察处理器的行为。这有助于确定异常或中断发生的原因,以及处理器在异常或中断处理过程中的表现。性能优化:交叉触发技术可以帮助开发人员分析处理器在不同工作负载下的性能表现。通过观察处理器在执行关键任务或高负载情况下的行为,开发人员可以发现性能瓶颈并优化代码。
代码覆盖率分析:交叉触发技术可以用于分析处理器在执行不同代码路径时的行为。通过记录处理器在特定事件发生时的行为,开发人员可以确定代码覆盖率,并确定哪些代码路径被执行和哪些未被执行。
系统稳定性分析:交叉触发技术可以帮助开发人员分析系统在不同条件下的稳定性表现。例如,在系统崩溃或挂起的情况下,开发人员可以通过交叉触发技术观察处理器在崩溃或挂起前的行为,以确定问题的根源。
安全漏洞分析:交叉触发技术可以用于安全漏洞分析中,帮助开发人员定位和处理安全问题。例如,开发人员可以通过交叉触发技术观察处理器在执行恶意代码时的行为,以便发现和处理安全漏洞。
总的来说,ARM交叉触发技术是一种强大的调试工具,可以帮助开发人员深入了解处理器的行为和性能,并解决潜在的问题或错误。它可以应用于多种应用场景,包括异常和中断调试、性能优化、代码覆盖率分析、系统稳定性分析和安全漏洞分析等。
(7)物理调试接口
Candidates should be aware of available options for making physical connections to the target for the purpose of run-control debug (e.g. JTAG, SWD)
需要理解在线调试所需要的连接到物理目标系统的物理连接选项(如JTAG、SWD)。
JTAG是一个广泛使用的工业标准,它在物理上通常需要至少5个信号引脚来连接主机计算机和ARM目标系统板。串行调试口SWD是一个拥有类似调试功能的2脚调试口,并且它通常只在较新的处理器上才有。
而从处理器抓取跟踪信息,则需要比控制代码执行大得多的带宽,因为每个周期都会有很多的32位地址和数据信息产生,因此专用的跟踪端口需要更多的引脚。
此外,跟踪信息也可以存储在片上的缓冲区中,采用较少物理引脚的慢速调试口来读取这些跟踪信息。
(8)调试访问内存
Candidates should be aware of how a debugger uses the processor to access memory and be aware of how memory translation affects this. They should also understand the potential impact of cache maintenance caused by debug operations on the state of the memory system.
需要了解调试器是如何使用处理器来访问内存的,并且理解内存转换如何影响这种访问,还需要理解对内存系统状态的调试操作可能带来对缓存内容维系的潜在影响。
调试器通常需要显示调试目标内存(或者外设)的内容并进行修改,它通常是通过处理器执行加载或者存储指令来实现的,然而有些系统则可以通过内建的调试系统单元来直接对内存进行写(或读)操作,不需要处理器本身参与内存操作。
一个良好的调试器会减少调试动作对系统的影响。例如,它通常会尽量减小调试时对缓存的修改。
如果调试器在调试窗口上显示一块内存区域的内容但调试器必须使用处理器来读取内存时,它会尽量采用非缓存方式来操作,从而确保之前缓存中的内容不会被冲刷掉。
程序员必须了解(当内存管理单元MMU被使能时)调试显示使用的是虚拟地址而非物理地址,而且缓存中的内容可能与外部存储器的内容不一致。
如果内存访问是直接通过片上系统的调试单元(而非处理器)来进行的,那么访问使用的就是物理地址而非处理器的虚拟地址转换,而且它是直接访问内存的(绕过处理器的缓存)。
(9)半主机
Candidates should be aware of semi-hosting,understand what its function is and how it can be used during development. They should also be aware of the invasive nature of using this feature and how it may affect timing etc. They should also be aware of the danger of using semi-hosting when no debugger is connected and how to retarget the standard C library.
需要理解半主机的功能是什么,在开发过程中应如何使用;
需要了解这种机制的侵入式特性以及它对时序等方面的影响;
还需要了解当目标板没有连接调试器时使用这一机制的风险,以及它如何重定向到标准C库。
半主机是一种让ARM目标板上的代码可以使用主机计算机上调试器提供的部分资源的机制。
这些资源可以包含键盘输入、显示输出、硬盘I/O。例如,程序员可以使用这种机制提供的标准C库函数,如printf()和Scanf()来使用主机上的终端屏幕和键盘。
开发的硬件通常不包含全功能的输入/输出设备,而半主机则可以让主机提供这些资源。
半主机是通过一系列专用的软件指令产生异常来实现的。
应用程序使用适当的半主机异常调用——调试中介来响应异常处理。调试中介会提供到主机所需的通信。
半主机的接口一般是通过ARM提供的调试单元作为中介来实现的。ARM的工具使用SVC 0x123456(ARM状态)或者SVC 0xAB(Thumb状态)来表示半主机调试函数。
当然,如果脱离了开发环境,运行着调试器的主机并不会连接到目标系统上。此时开发人员有必要将任何使用到半主机的库函数重定向,如fputc()函数。
之前调试一个小的IoT芯片就是,轻量级的设备没有系统,而是跑的一个协议栈。里面的打印都是fput。
这意味着需要使用能够输出字符到指定设备的代码来替代使用SVC调用半主机的库函数代码。
2.标准调试技术
(1)调用栈
Candidates must understand what a call stack is and how it may be used in debugging.
需要理解什么是调用栈以及它在调试时应如何使用。
应用程序代码主要使用堆栈来进行参数传递、保存局部变量和保存返回地址。
每个函数压入堆栈的数据都被组织为一个“堆栈帧”,当调试器停止处理器时,它可以分析堆栈中的数据,为程序员提供“调用栈”,
这是从最顶层函数调用到当前子函数之间每一层级的调用关系,它可以在调试时让用户非常方便地了解整个调用路径,了解为何程序会运行到当前的位置。
为了能够重建调用栈,调试器必须能够确定堆栈的哪些项包含了返回地址的信息。如果编译的时候包含的话,这些信息一般会存在于“调试器信息”(DWARF调试表)中,或者从一个由程序压入堆栈的“帧指针”链表获得。
当然,代码必须使用帧指针。如果这两种类型的信息都没有,那么这个调用栈无法被重建。
在多线程任务应用中,每个线程都有自己的堆栈,因此调用栈的信息也只和它对应的线程相关。
堆栈帧信息是指在函数调用过程中,由编译器自动在堆栈上分配的数据结构,用于保存函数调用的状态和局部变量等信息。在调用栈中,每个函数调用对应一个堆栈帧,包含了该函数执行所需的所有信息。
堆栈帧的主要作用是保存局部变量和函数参数。当函数被调用时,其局部变量和参数被压入堆栈中,以便在函数执行期间访问。此外,堆栈帧还包含了函数调用的返回地址、环境信息(如寄存器值)等。
在调试过程中,通过分析堆栈帧信息,可以了解函数调用的层次结构、当前执行位置、变量的值等。这有助于理解程序执行流程和定位问题。
为了能够重建调用栈并获取堆栈帧信息,编译器需要在编译时生成调试信息(如DWARF调试表)。这些调试信息包含了程序结构和变量的元数据,以及堆栈帧的布局信息。调试器可以利用这些信息来解析堆栈帧并重建调用栈,从而提供给开发人员一个完整的程序执行视图。
总之,堆栈帧信息是函数调用过程中的重要组成部分,通过它我们可以深入了解程序执行的状态和历史,并利用这些信息进行调试和优化。
(2)单步执行
Candidates must understand what single stepping is and how it may be used in debugging. They should understand the difference between Step-In and Step-Over when single-stepping.
需要理解什么是单步执行以及如何在调试中应用,它需要理解在单步执行时Step-In和Step-Over的区别。
单步执行指的是调试时调试器可以控制执行部分代码,每一次执行一条指令。Step-In和Step-Over的区别可以从函数调用中理解。
当采用Step-Over来调试函数时,整个函数会作为一步来执行,让程序员可以直接执行整个函数而不需要进入函数里面去单步执行。Step-In表示进入函数里面去单步执行函数体。
(3)开始/停止
Candidates must understand what start/stop debugging is and how it may be used.
需要理解什么是开始/停止调试以及如何使用它。
开始的含义就是按下调试器的“开始”按钮,处理器退出调试状态并重新进入正常执行状态(从当前的程序指针处开始执行),直到程序遇到某个让它停止的因素为止,这些因素通常是一个断点、观察点或者捕捉向量事件或者是从外部调试器及其他系统阻塞所产生的调试请求。
开始/停止的调试模式与不能停止代码执行的调试的系统形成鲜明的对照。在一些嵌入式系统中(如汽车引擎控制系统),在进行系统调试时是不能简单地让处理器停止执行的。
(4)打印printf
Candidates must understand how printf may be used in code instrumentation (they may also be expected to understand what other kinds of instrumentation might typically be used).
需要理解如何在代码仪器化中使用printf(还需要理解其他可用的仪器化函数)。
这是一个最基本的调试技术,通常在所有的处理器架构中使用,用于在代码中插入指令来输出一些数据,例如来显示程序的指令流或者某一时刻一些关键变量的值。
在代码仪器化中,printf函数是一种常用的调试工具,用于输出数据和信息,帮助开发人员了解程序的执行状态和变量的值。printf函数类似于其他编程语言中的输出函数,如C语言中的printf()函数或Python中的print()函数。
在代码中使用printf函数,需要在程序中插入printf语句,并在其中指定要输出的内容。这些内容包括各种类型的变量、宏、表达式等。通过输出这些内容,可以在调试过程中了解程序的执行状态、变量的值以及其他关键信息。
(5)裸机代码和应用代码
Candidates should be aware of the difference in debug options between applications running on OS-based or bare metal system (e.g. Ethernet, GDB server, ICE).
Example: Candidates should know that it is necessary to run a server program on the target system in order to debug an OS-based application.
需要了解基于操作系统的应用层软件调试和基于裸机代码的系统调试方法的差别(如以太网、GDBServer、ICE)。
例如,需要知道在目标板上调试一个基于操作系统的应用程序必须运行一个调试服务程序。
(6)RAM/ROM调试
Candidates should be aware of the limitations of debugging code running in different types of memory (e.g. breakpoints and image load).
Example: Candidates should know that it is not possible to set an unlimited number of breakpoints when executing code in ROM.More advanced candidates would understand why this is the case.
需要了解在不同类型的存储空间中进行代码调试的限制。
例如,应该知道调试在ROM中执行的代码时是不能设置无限量的断点的,更深入的了解应该知道为什么如此。
软件断点只能设置在RAM中,因为RAM是可写的空间。调试ROM中的代码的另一个通常的限制是很难修改代码来修改错误的部分。
在存储空间中进行代码调试时,确实存在一些限制,主要与存储空间的类型和特性有关。以下是对您提到的几种情况的详细解释:
ROM(只读存储器)中的代码调试:
限制:由于ROM是只读的,因此在ROM中执行的代码无法直接修改。这限制了调试过程中对代码的修改能力,例如设置断点。
为什么如此:由于ROM的设计目的就是为了保存固定数据,例如程序代码,而不是为了在运行时修改。因此,它不具备RAM的写能力,这使得在ROM中执行的代码无法直接设置断点。
软件断点与RAM:
限制:软件断点只能设置在RAM中。这是因为在RAM中,数据是可以写入的,而ROM或其他类型的存储器通常是只读的。
原因:由于RAM是可写的,所以可以在其上设置断点。当程序执行到特定的地址时,断点会被触发,然后程序执行会被暂停,允许开发人员进行调试。如果尝试在ROM或其他只读存储器上设置断点,硬件或操作系统通常会拒绝这种操作,因为这违反了只读存储器的设计目的。
修改ROM中的代码:
限制:由于ROM的只读特性,很难直接修改其中的代码来修复错误。通常需要重新编译和烧写新的ROM映像。
原因:ROM的设计初衷是为了存储固定、不可更改的数据。这意味着一旦ROM被编程(即写入数据),其内容就不能轻易更改。如果需要更改存储在ROM中的代码,通常需要重新编译应用程序,生成新的ROM映像,然后将其烧写到ROM中。这比直接在RAM中调试和修改代码要复杂得多。
总之,了解不同存储空间中代码调试的限制非常重要,因为它们影响了调试过程的效率和灵活性。在ROM中调试代码通常更加困难,因为它们的只读特性限制了对代码的修改能力。而在RAM中调试则更加灵活,因为它是可写的,允许开发人员设置断点、修改变量和执行其他调试任务。
(7)调试时序相关问题
Candidates should be aware that certain debug approaches may affect the timing of the code (e.g. halt mode) and be aware of non-invasive alternatives (e.g. trace).
需要了解某些调试方法可能会影响代码的时序(如暂停模式)并了解其他非侵入式的调试方法(如跟踪)。
在调试过程中,一些传统的调试方法可能会对代码的时序产生影响,例如在暂停模式下进行调试。这些方法可能会改变程序的执行流程和时间,从而影响程序的性能和结果。
为了解决这个问题,一些非侵入式的调试方法被引入。其中一种常用的方法是跟踪(Tracing)。
跟踪是一种监视程序执行过程的技术,通过记录程序执行过程中的关键事件和数据,帮助开发人员了解程序的执行状态和行为。与传统的调试方法不同,跟踪不会干扰程序的执行流程,因此对代码的时序影响较小。
跟踪可以通过在程序中插入跟踪语句或使用专门的跟踪工具来实现。
跟踪语句可以在程序的特定位置输出一些关键信息,例如变量的值、函数调用堆栈等。这些信息被记录下来,并在程序执行结束后进行分析。通过分析这些信息,开发人员可以了解程序的执行路径、变量的值以及其他关键信息。
除了跟踪之外,还有一些其他的非侵入式调试方法,例如使用监视点(Watchpoints)和断点(Breakpoints)。
监视点可以在程序的特定地址处设置一个触发条件,当变量的值发生变化时触发。通过使用监视点,开发人员可以观察变量的值变化,并了解程序的行为。断点可以在程序的特定位置设置一个暂停点,当程序执行到该位置时暂停。通过使用断点,开发人员可以逐步执行程序并观察变量的值和执行流程。
总之,了解某些调试方法可能会影响代码的时序是重要的。
为了减小影响,开发人员可以选择使用非侵入式的调试方法,如跟踪、监视点和断点等。这些方法可以帮助开发人员了解程序的执行状态和行为,并发现潜在的问题和优化方向。
(8)使用调试器调试软件的影响(系统可能会被更改状态)
Candidates should be aware of the implications/impact of debugging (e.g.reading memory locations).
需要了解调试的含义和影响(如读取内存位置)。
调试是软件开发过程中必不可少的一部分,它有助于发现和修复代码中的错误和问题。然而,使用调试器进行调试时,确实可能会对软件的系统状态产生影响。以下是一些可能的影响和需要注意的事项:
1、系统状态更改:在调试过程中,开发人员可能会暂停程序的执行、单步执行代码、修改变量的值等。这些操作都可能直接或间接地更改系统的状态。例如,修改变量的值可能会导致程序的行为与正常情况下不同。
2、内存读取和写入:使用调试器时,开发人员经常需要读取和修改变量的值。这些操作可能涉及到内存的读写。如果操作不当,可能会导致内存损坏、数据丢失或其他未定义的行为。
3、性能影响:在调试过程中,程序的执行可能会被暂停、中断或减速,这可能会影响程序的性能。如果频繁地进行调试,可能会导致程序的性能下降。
4、依赖性问题:在调试过程中,开发人员可能需要模拟某些外部事件或条件。这些模拟可能会导致程序的行为与实际运行时不同,从而引入依赖性问题。
5、安全性考虑:在某些情况下,调试可能会暴露敏感信息或允许恶意用户利用调试器来操纵程序的行为。因此,在调试时需要注意安全性问题,并采取适当的措施来保护系统的安全。
为了减小调试对软件系统状态的影响,开发人员应该采取一些最佳实践:
1、备份重要数据:在开始调试之前,建议备份重要的系统数据和配置,以防止意外更改导致数据丢失或损坏。
2、限制调试器的权限:确保调试器没有不必要的权限,以减少潜在的安全风险。
3、使用断言和日志记录:在代码中添加断言和日志记录可以帮助开发人员在调试过程中更好地理解程序的状态和行为。
4、谨慎使用内存操作:尽量避免直接读写内存,特别是当程序处于不稳定状态时。
5、限制调试的范围:尽量只在需要调试的部分代码上使用调试器,而不是在整个程序上运行调试器。
6、定期测试和验证:在调试过程中,定期测试和验证程序的正确性和性能可以帮助开发人员及时发现潜在的问题。
7、遵循良好的编码实践:编写高质量的代码可以减少潜在的错误和问题,从而减少调试的需要。
总之,虽然调试是软件开发中必不可少的部分,但开发人员应该意识到使用调试器可能对系统状态产生的影响,并采取适当的措施来减小这些影响。
PART THREE-架构
1.指令集
(1)LDREX/STREXCLEX
Candidates should be aware of how these instructions work and how they might be used to implement software synchronization (e.g. mutex). At this level candidates should be able to recognize a simple mutex implementation (in assembler) and be able to explain its operation. They will not be expected to write one.
Example: Candidates should be aware of what a mutex is. More advanced candidates will be aware of the exclusive access instructions which are used to implement mutexes and similar constructs.
需要了解这些指令如何工作,如何用于软件的同步(如互斥);
应该能够识别一个互斥的应用(在汇编代码中)并能解释它们的作用,但不需要自己编写一个此类代码应用。
LDREX和STREX是ARM架构中的两条指令,它们被用来实现原子操作。原子操作是一种在执行过程中不会被其他线程或进程中断的操作。
LDREX指令用于读取内存中的值,并标记对该段内存的独占访问。它会在读取寄存器Ry指向的4字节内存值后,将其保存到Rx寄存器中,同时标记对Ry指向内存区域的独占访问。如果执行LDREX指令的时候发现已经被标记为独占访问了,并不会对指令的执行产生影响。
STREX指令在更新内存数值时,会检查该段内存是否已经被标记为独占访问,并以此来决定是否更新内存中的值。如果执行这条指令的时候发现已经被标记为独占访问了,则将寄存器Ry中的值更新到寄存器Rz指向的内存,并将寄存器Rx设置成0。指令执行成功后,会将独占访问标记位清除。一旦某条STREX指令执行成功后,以后再对同一段内存尝试使用STREX指令更新的时候,会发现独占标记已经被清空了,就不能再更新了,从而实现独占访问的机制。
这两条指令常被用来在并发编程中实现线程间的同步,以避免数据竞争和其他并发问题。
在软件开发中,同步是一种重要的技术,用于确保多个线程或进程在共享资源时的正确行为。互斥(mutex)是实现软件同步的一种常见方法,它允许多个线程安全地访问共享资源。为了理解互斥的实现,开发人员需要了解底层指令的工作原理。
在汇编语言中,有一些特定的指令可以用来实现互斥和软件同步。这些指令通常涉及对内存的读写操作,以及对进程或线程状态的更改。
例如,一个简单的互斥实现可能使用以下类型的指令:
原子操作指令:这些指令在执行过程中不会被其他线程或进程中断,因此可以在没有锁的情况下安全地修改共享变量的值。例如,一些CPU提供了CMPXCHG(比较并交换)指令,它可以原子地比较和交换内存中的值。
自旋锁指令:这些指令允许一个线程持续检查一个条件,直到该条件变为真。这通常用于实现循环锁,其中线程在等待获取锁时会“自旋”。例如,CMP(比较)和JXX(条件跳转)指令可以组合使用来实现自旋锁。
阻塞和唤醒指令:这些指令允许线程阻塞自己(例如,通过WAIT或PAUSE指令),直到另一个线程唤醒它(例如,通过SIGNAL或WAKE指令)。
了解这些底层指令如何工作对于理解互斥等同步机制的实现至关重要。开发人员需要能够识别和理解这些指令在汇编代码中的应用,以便更好地理解软件的同步行为。
值得注意的是,虽然了解这些底层细节对于深入理解同步机制是有帮助的,但在实际开发中通常会使用更高级的抽象和工具(如操作系统提供的线程同步原语或高级语言中的并发库),而不是直接编写汇编代码来实现这些机制。
审核编辑:汤梓红
-
处理器
+关注
关注
68文章
19077浏览量
228721 -
ARM
+关注
关注
134文章
9021浏览量
366368 -
控制器
+关注
关注
112文章
16070浏览量
176956 -
指令集
+关注
关注
0文章
221浏览量
23337
原文标题:1.指令集
文章出处:【微信号:IC学习,微信公众号:IC学习】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论