linux arm32启动代码分析
首先将 linux kernel 代码编译好以后,在目录 arch/arm/kernel 下生成链接脚本文件 vmlinux.lds (vmlinux.lds由vmlinux.lds.S编译而来)。首先分析此脚本来熟悉 linux kernel 二进制代码分布结构。
在 vmlinux.lds.S 中
ENTRY(stext)
指明了linux内核入口,入口为stext。符号stext定义在 arch/arm/kernel/head.S 文件中:
.arm
__HEAD
ENTRY(stext)
ARM_BE8(setend be ) @ ensure we are in BE8 mode
THUMB( adr r9, BSYM(1f) ) @ Kernel is always entered in ARM.
THUMB( bx r9 ) @ If this is a Thumb-2 kernel,
THUMB( .thumb ) @ switch to Thumb now.
THUMB(1: )
ENTRY在 include/linux/linkage.h 中定义
#ifndef ENTRY
#define ENTRY(name) \
.globl name ASM_NL \
ALIGN ASM_NL \
name:
#endif
通过代码可以看到 ENTRY 宏只是对全局符号的导出起到包装作用,
下面分析ENTRY语句下的五条指令
1、ARM_BE8(setend be )
ARM_BE8在 arch/arm/include/asm/assembler.h 中定义
/* Select code for any configuration running in BE8 mode */
#ifdef CONFIG_CPU_ENDIAN_BE8
#define ARM_BE8(code...) code
#else
#define ARM_BE8(code...)
#endif
如果开启 CONFIG_CPU_ENDIAN_BE8 选项,code(setend be)则会被原封不动编译到内核中,如果没有开启此选项,code(setend be)就不会被编译到内核中。
(extension) #define后的省略号的意义如下(以PDEBUG为例):
#define PDEBUG(fmt, args...) printk( KERN_DEBUG "scull: " fmt, ## args)
// example
PDEBUG("a=%d, b=%d", a, b);
// 展开后
printk( KERN_DEBUG "scull: " "a=%d, b=%d", a, b);
(1)arm BE8 mode 是什么
根据博客 https://blog.richliu.com/2010/04/08/907/arm11-be8-and-be32 所述:
BE8 和 BE32 是字节序相关的概念,举例:
假设需要将 0x11223344 存入内存中:
little endian:
memory address | 0 | 1 | 2 | 3 |
data | 0x44 | 0x33 | 0x22 | 0x11 |
big endian:
memory address | 0 | 1 | 2 | 3 |
data | 0x11 | 0x22 | 0x33 | 0x44 |
现假设对0x11223344内存存储分布如下:
memory address | 0 | 1 | 2 | 3 |
data | 0x11 | 0x22 | 0x33 | 0x44 |
在BE32 mode下:
LDR r0, [0]
# r0 = 0x44332211
LDRB r0, [0]
# r0 = 0x00000044
LDRB r0, [3]
# r0 = 0x00000011
在BE8 mode下:
LDR r0, [0]
# r0 = 0x11223344
LDRB r0, [0]
# r0 = 0x00000011
LDRB r0, [3]
# r0 = 0x00000044
关于 byte invariant endianness 的概念可以参照博客 https://blog.csdn.net/moreaction/article/details/5280067/
(2)setend be 指令的含义
setend 指令选择数据访问的字节序,在百度文库 https://wenku.baidu.com/view/6f83c4c2951ea76e58fafab069dc5022aaea46e5.html 解释了这个指令。在linux内核代码中,如果开启了 CONFIG_CPU_ENDIAN_BE8 选项,则 setend be 指令会被编译到内核中并执行,此指令的执行代表选择数据访问的字节序为 BE8 模式,BE8 模式的数据访问效果如上文所述。
2、THUMB( adr r9, BSYM(1f) )
3、THUMB( bx r9 )
4、THUMB( .thumb )
5、THUMB(1: )
在 2\3\4\5 中,THUMB、BSYM都定义在 arch/arm/include/asm/unified.h 中:
#ifdef CONFIG_THUMB2_KERNEL
#if __GNUC__ < 4
#error Thumb-2 kernel requires gcc >= 4
#endif
/* The CPSR bit describing the instruction set (Thumb) */
#define PSR_ISETSTATE PSR_T_BIT
#define ARM(x...)
#define THUMB(x...) x
#ifdef __ASSEMBLY__
#define W(instr) instr.w
#define BSYM(sym) sym + 1
#else
#define WASM(instr) #instr ".w"
#endif
#else /* !CONFIG_THUMB2_KERNEL */
/* The CPSR bit describing the instruction set (ARM) */
#define PSR_ISETSTATE 0
#define ARM(x...) x
#define THUMB(x...)
#ifdef __ASSEMBLY__
#define W(instr) instr
#define BSYM(sym) sym
#else
#define WASM(instr) #instr
#endif
#endif /* CONFIG_THUMB2_KERNEL */