本人看代码,无论多长,都习惯从最开始看起。一般的裸奔的程序,从main函数看起也就够了。可惜MQX是一个复杂的系统,源码都有几十兆,如果从main开始看的话就会忽略一些关键的步骤,因此我从代码最初运行的部分开始说起。
代码之始(请原谅我糟糕的命名品味)在哪呢,这个要问链接器了。本人的编译IDE用的是Keil,在工程选项linker里我们可以找到这个东西。
Scatter
File是链接器用来链接的编译好的文件的,简单的来说假设我们一个工程包含了一大堆*.c文件,每个*.c文件都会被编译器编译为*.o文件,之后链接器将这些文件链接组成一个可执行文件.elf。而scatter文件就是用来告诉链接器是如何把那些*.o文件按照一定的顺序组装起来的。当然我这也是简单说说,详细内容可参见
ARM? Compiler toolchain Version 4.1 Using the Linker 。
打开intflash.scf,就看见如下内容
-
#! armcc -E
-
-
; The balance of unused flash is used as user flash. Make sure this value
-
; matches the one in bsp/twrk60n512/twrk60n512.h. Change them as
-
; needed to match the flash image size. MAKE SURE THIS VALUE IS ALIGNED TO
-
; (is a multiple of) THE SECTOR SIZE - 0x800
-
#define USERFLASH_BASE_ADDR 0x00060000
-
#define INTFLASH_BASE_ADDR 0x00000000
-
#define INTFLASH_SIZE (USERFLASH_BASE_ADDR - INTFLASH_BASE_ADDR)
-
-
#define MY_ALIGN(address, alignment) ((address + (alignment-1)) AND ~(alignment-1))
-
-
LOAD_REGION_INTFLASH INTFLASH_BASE_ADDR INTFLASH_SIZE
-
{
-
VECTORS INTFLASH_BASE_ADDR
-
{
-
vectors.o (.vectors_rom,+FIRST)
-
vectors.o (.cfmconfig)
-
}
-
-
CODE +0
-
{
-
* (InRoot$$Sections) ; All library sections for example, __main.o,
-
; __scatter*.o, __dc*.o, and * Region$$Table
-
* (KERNEL)
-
* (TEXT)
-
* (+RO)
-
}
-
-
RAM_VECTORS 0x1FFF0000 ; For ram vector table. Used when MQX_ROM_VECTORS is set to zero.
-
{
-
vectors.o (.vectors_ram)
-
}
-
-
NOUSER +0
-
{
-
* (.nouser)
-
}
-
-
ROUSER MY_ALIGN(ImageLimit(NOUSER), 32)
-
{
-
* (.rouser)
-
}
-
-
RWUSER MY_ALIGN(ImageLimit(ROUSER), 32)
-
{
-
* (.rwuser)
-
}
-
-
DATA MY_ALIGN(ImageLimit(RWUSER), 32)
-
{
-
* (+RW)
-
* (+ZI)
-
}
-
-
USB_BDT MY_ALIGN(ImageLimit(DATA), 512)
-
{
-
* (.usb_bdt)
-
}
-
-
KERNEL_DATA_START MY_ALIGN(ImageLimit(USB_BDT), 0x10)
-
{
-
* (KERNEL_DATA_START) ; start of kernel data
-
}
-
-
KERNEL_DATA_END 0x2000FFF0 ; RAM_END
-
{
-
* (KERNEL_DATA_END) ; end of kernel data
-
}
-
-
; mem_init writes a storeblock_struct at the end of kernel data,
-
; max size 32 bytes, so use 0x100 offset
-
BOOT_STACK_ADDR 0x2000FEF0
-
{
-
* (BOOT_STACK)
-
}
-
}
对于这个脚本,研究过linux编译的同学应该很熟悉了。最开始的0地址就是INTFLASH_BASE_ADDR,对应着VECTORS段。这个段由两个部分组成,这两个东西都是来自于vectors.c,一个叫vectors_rom,另一个叫cfmconfig。再来看vectors.c,不管cfmconfig,我们来研究最开始的vectors_rom。
-
#ifdef __ICCARM__
-
#pragma language = extended
-
#pragma segment = "CSTACK"
-
-
#pragma location = ".intvec"
-
#pragma segment = ".intvec"
-
const intvec_elem __vector_table[] =
-
#elif defined(__CC_ARM) || defined(__GNUC__)
-
__attribute__((section(".vectors_rom"))) const vector_entry __vector_table[256] __attribute__((used)) =
-
#else /* CodeWarrior compiler assumed */
-
#pragma define_section vectors_rom ".vectors_rom" ".vectors_rom" ".vectors_rom" far_abs R
-
__declspec(vectors_rom) vector_entry rom_vector[] =
-
#endif /* CodeWarrior compiler assumed */
-
{
-
(vector_entry)__BOOT_STACK_ADDRESS,
-
BOOT_START, /* 0x01 0x00000004 - ivINT_Initial_Program_Counter */
-
DEFAULT_VECTOR, /* 0x02 0x00000008 - ivINT_NMI */
-
DEFAULT_VECTOR, /* 0x03 0x0000000C - ivINT_Hard_Fault */
-
DEFAULT_VECTOR, /* 0x04 0x00000010 - ivINT_Mem_Manage_Fault */
-
DEFAULT_VECTOR, /* 0x05 0x00000014 - ivINT_Bus_Fault */
-
DEFAULT_VECTOR, /* 0x06 0x00000018 - ivINT_Usage_Fault */
-
0, /* 0x07 0x0000001C - ivINT_Reserved7 */
-
0, /* 0x08 0x00000020 - ivINT_Reserved8 */
-
0, /* 0x09 0x00000024 - ivINT_Reserved9 */
-
0, /* 0x0A 0x00000028 - ivINT_Reserved10 */
-
_svc_handler, /* 0x0B 0x0000002C - ivINT_SVCall */
-
DEFAULT_VECTOR, /* 0x0C 0x00000030 - ivINT_DebugMonitor */
-
0, /* 0x0D 0x00000034 - ivINT_Reserved13 */
-
_pend_svc, /* 0x0E 0x00000038 - ivINT_PendableSrvReq */
-
DEFAULT_VECTOR, /* 0x0F 0x0000003C - ivINT_SysTick */
-
/* Cortex external interrupt vectors */
-
DEFAULT_VECTOR, /* 0x10 0x00000040 - ivINT_DMA0 */
注意 __attribute__((section(".vectors_rom"))) const vector_entry
__vector_table[256] __attribute__((used))
,其实vectors_rom就是一个包含256个中断向量的中断向量表__vector_table[256],这个表是被链接器放在CPU总线地址为0-1024的内部flash上面。根据cortex
m4的特性,cpu复位时,会从0x0处取出堆栈指针SP,从0x4处取出复位跳转指令的位置。我们可以发现__vector_table[0]是__BOOT_STACK_ADDRESS而__vector_table[1]是BOOT_START。如果有人记忆力好的话会发现__BOOT_STACK_ADDRESS在之前的scatter文件出现过,如果没注意的话可以回过头来看(之后你会对这个动作习以为常的),BOOT_STACK_ADDR就是最后一个段,其值为0x2000FEF0,这个就是cpu复位运行时堆栈的地址。而BOOT_START在vectors.c有一个宏定义
#define BOOT_START __boot
而这个__boot就是boot.S最开始的部分。
-
ASM_COMP_SPECIFIC_DIRECTIVES
-
-
ASM_CODE_SECTION(.text)
-
SET_FUNCTION_ALIGNMENT
-
-
ASM_PUBLIC(__boot)
-
ASM_PUBLIC(__set_MSP)
-
-
ASM_PUBLIC_BEGIN(__boot)
-
ASM_PUBLIC_FUNC(__boot)
-
ASM_LABEL(__boot)
-
-
-
#if MQX_AUX_CORE
-
msr MSP, r0
-
isb #15
-
#endif
-
-
/* disable interrupts and clear pending flags */
-
ldr r0, =0xe000e180 /* NVIC_ICER0 - Interrupt Clear-Enable Registers */
-
ldr r1, =0xe000e280 /* NVIC_ICPR0 - Interrupt Clear-Pending Registers */
-
ldr r2, =0xffffffff
-
mov r3, #8
就这样我们说了半天才找到程序开始运行的地方,目前真正的主角MQX还没有运行,可以说MQX是一个浩大的工程,内容之多会让人生畏,不过它的确值得我们花时间去研究。在此我想说一下,要想研究一个拥有如此庞大代码量的系统,一个好的编辑工具是必须的。本人用的是vim,具体好处就不说了,本文说提到的各种变量的关系都是我用其中的vimgrep命令搜出来的,幸好有这个工具,不然我根本不可能在在这几十兆的代码中找出互相对应的文件。此外我也推荐使用source
insight,也是文本编辑的一大神器。
最后看一下keil的运行界面,来证实我们上述的分析。
阅读(1410) | 评论(0) | 转发(0) |