执行make是总是那么酣畅淋漓,有一种说不出的快感。
好,执行make ,注意在makefile中添加
ARCH=arm
CROSS_COMPILE=arm-linux-
make 会有很多的错误,一般是提醒你没有定义一些东西,那么这些东西在哪里定义,大部分在之前提到的那个carl210.h文件中定义。
其实这样直接执行make是不合适的,因为肯定会有错,就是看看是否能不能正常的编译。
分析程序我们一般都找main函数,main函数是一个入口,其实在mian函数之前有一段汇编的代码,初始化好堆栈才能使用C的,那现在就找那个函数的入口地址。
三星的210是armv7的,而我们也配置了CPU的类型等等。所以到armv7下找一个文件名是start.s的文件,好开始分析这个文件,在看这个文件之前还有提到在之前的makefile分析中提到的LDSCRIPT的这个连接脚本最后指向了LDSCRIPT := $(TOPDIR)/$(CPUDIR)/u-boot.lds
首先了解uboot的链接脚本board/my2410/u-boot.lds,它定义了目标程序各部分的链接顺序。
/*
* January 2004 - Changed to support H4 device
* Copyright (c) 2004-2008 Texas Instruments
*
* (C) Copyright 2002
* Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">/*指定输出可执行文件为ELF格式,32为,ARM小端*/</span>
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">/*指定输出可执行文件为ARM平台*/</span>
OUTPUT_ARCH(arm)
<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">/*起始代码段为 _start*/</span>
ENTRY(_start)
SECTIONS
{
<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;"> /* 指定可执行image文件的全局入口点,通常这个地址都放在ROM(flash)0x0位置*/</span>
. = 0x00000000;
. = ALIGN(4); //4字节对齐
.text : //text字段
{
arch/arm/cpu/armv7/start.o (.text) //主人公第一阶段入口文件
*(.text)
}
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
. = ALIGN(4);
.data : {
*(.data)
}
. = ALIGN(4); //当前位置4字节对齐
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4);
__image_copy_end = .;
.rel.dyn : {
__rel_dyn_start = .;
*(.rel*)
__rel_dyn_end = .;
}
.dynsym : {
__dynsym_start = .;
*(.dynsym)
}
_end = .;
.bss __rel_dyn_start (OVERLAY) : {
__bss_start = .;
*(.bss)
. = ALIGN(4);
__bss_end__ = .;
}
/DISCARD/ : { *(.dynstr*) }
/DISCARD/ : { *(.dynamic*) }
/DISCARD/ : { *(.plt*) }
/DISCARD/ : { *(.interp*) }
/DISCARD/ : { *(.gnu*) }
}
其实这个文件的作用是告诉我们入口的文件是arch/arm/cpu/armv7/start.S,还有是各个字段存放的位置。
1.text字段
2.rodata
3.data字段
4.uboot的一些命令
5..rel.dyn 字段
6..dynsym字段
7.bss字段
好开始看arch/arm/cpu/armv7/start.S文件
.globl _start
_start: b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
1.声明一个全局的标号_start
2.b reset 跳转到reset处
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr,r0
设置CPU为svc32模式
#if defined(CONFIG_OMAP34XX)
/* Copy vectors to mask ROM indirect addr */
adr r0, _start @ r0 <- current position of code
add r0, r0, #4 @ skip reset vector
mov r2, #64 @ r2 <- size to copy
add r2, r0, r2 @ r2 <- source end address
mov r1, #SRAM_OFFSET0 @ build vect addr
mov r3, #SRAM_OFFSET1
add r1, r1, r3
mov r3, #SRAM_OFFSET2
add r1, r1, r3
想想也不会定义CONFIG_OMAP34XX的,我们的是210。
3.ldr pc, _undefined_instruction 跳转到_undefined_instruction
以_undefined_instruction为例,就是,此处分配了一个word=32bit=4字节的地址空间,里面存放的值是undefined_instruction而此处_undefined_instruction也就是该地址空间的地址了。用C语言来表达就是:
_undefined_instruction = &undefined_instruction
或*_undefined_instruction = undefined_instruction
在后面的代码,我们可以看到,undefined_instruction也是一个标号,即一个地址值,对应着就是在发生“未定义指令”的时候,系统所要去执行的代码。(其他几个对应的“软件中断”,“预取指错误”,“数据错误”,“未定义”,“(普通)中断”,“快速中断”,也是同样的做法,跳转到对应的位置执行对应的代码。)
所以:
ldr pc, 标号1
......
标号1:.word 标号2
......
标号2:
......(具体要执行的代码)
的意思就是,将地址为标号1中内容载入到pc,而地址为标号1中的内容,正好装的是标号2。
用C语言表达其实很简单:
PC = *(标号1) = 标号2
对PC赋值,即是实现代码跳转,所以整个这段汇编代码的意思就是:
跳转到标号2的位置,执行对应的代码。
.global _end_vect
_end_vect:
.balignl 16,0xdeadbeef
声明一个_end_vect的全局标号,然后就是在以后的代码中都要16字节对齐,不足的地方用deadbeef补齐
.globl _TEXT_BASE
_TEXT_BASE:
.word CONFIG_SYS_TEXT_BASE
声明一个全局的标号_TEXT_BASE,而_TEXT_BASE被定义为CONFIG_SYS_TEXT_BASE,那么这个
CONFIG_SYS_TEXT_BASE一般在
carl210.h中定义的。
#ifdef CONFIG_TEGRA2
/*
* Tegra2 uses 2 separate CPUs - the AVP (ARM7TDMI) and the CPU (dual A9s).
* U-Boot runs on the AVP first, setting things up for the CPU (PLLs,
* muxes, clocks, clamps, etc.). Then the AVP halts, and expects the CPU
* to pick up its reset vector, which points here.
*/
.globl _armboot_start
_armboot_start:
.word _start
#endif
那显然我们不是Tegra2
/*
* These are defined in the board-specific linker script.
*/
.globl _bss_start_ofs
_bss_start_ofs:
.word __bss_start - _start
.globl _bss_end_ofs
_bss_end_ofs:
.word __bss_end__ - _start
.globl _end_ofs
_end_ofs:
.word _end - _start
在armv7下的u-boot.lds中有__bss_start等的定义,其实上面的这些代码得出的结果就是要算出相对于起始地址的偏移量,并声明这些为全局的标号,以便其他的文件能够使用这些个标号或者是变量。
#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
/* IRQ stack memory (calculated at run-time) + 8 bytes */
.globl IRQ_STACK_START_IN
IRQ_STACK_START_IN:
.word 0x0badc0de
这个是当定义使用IRQ时会定义IRQ_STACK_START,FIQ_STACK_START,我们不用。
以上这些都是类似于变量的定义以及初始化,其实真正的代码是在reset中,上面有所提到。
当arm进入svc32 mode,继续看
#if defined(CONFIG_OMAP34XX)
/* Copy vectors to mask ROM indirect addr */
adr r0, _start @ r0 <- current position of code
add r0, r0, #4 @ skip reset vector
mov r2, #64 @ r2 <- size to copy
add r2, r0, r2 @ r2 <- source end address
mov r1, #SRAM_OFFSET0 @ build vect addr
mov r3, #SRAM_OFFSET1
add r1, r1, r3
mov r3, #SRAM_OFFSET2
add r1, r1, r3
显然不会定义,我们的是210。
next:
ldmia r0!, {r3 - r10} @ copy from source address [r0]
stmia r1!, {r3 - r10} @ copy to target address [r1]
cmp r0, r2 @ until source end address [r2]
bne next @ loop until equal */
#if !defined(CONFIG_SYS_NAND_BOOT) && !defined(CONFIG_SYS_ONENAND_BOOT)
/* No need to copy/exec the clock code - DPLL adjust already done
* in NAND/oneNAND Boot.
*/
bl cpy_clk_code @ put dpll adjust code behind vectors
#endif /* NAND Boot */
#endif
/* the mask ROM code should have PLL and others stable */
都不会执行的。
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
CONFIG_SKIP_LOWLEVEL_INIT 是一般不会定义的,所以bl cpu_init_crit ,即跳转到cpu_init_crit
cpu_init_crit:
/*
* Invalidate L1 I/D
*/
mov r0, #0 @ set up for MCR
mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs
mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
/*
* disable MMU stuff and caches
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002000 @ clear bits 13 (--V-)
bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
orr r0, r0, #0x00000800 @ set bit 12 (Z---) BTB
mcr p15, 0, r0, c1, c0, 0
看英文注释就知道什么意思,具体代码是什么意思,看《arm体系结构与编程》
/*
* Jump to board specific initialization...
* The Mask ROM will have already initialized
* basic memory. Go here to bump up clock rate and handle
* wake up conditions.
*/
mov ip, lr @ persevere link reg across call
bl lowlevel_init @ go setup pll,mux,memory
mov lr, ip @ restore link
mov pc, lr @ back to my caller
看英文注释,是跳转的到和板级相关的带吗中,初始化pll,memory等等,bl
lowlevel_init 会跳转到这个lowlevel_init的地方。下节讲关于这个函数在哪里定义。