start.S详解学习(四):设置堆栈 sp 指针

简介: start.S详解学习(四):设置堆栈 sp 指针

设置堆栈 sp 指针

上面代码中,第一行的意思很简单,就是把地址为_TEXT_BASE的内存中的内容给r0,而查看前面的相关部分的代码,即:

_TEXT_BASE:
  .word TEXT_BASE

得知,地址为_TEXT_BASE的内存中的内容,就是

r0 = TEXT_BASE = 0x33D00000

sub指令:

所以对应含义为:

r0 = r0 - #CFG_MALLOC_LEN
r0 = r0 - #CFG_GBL_DATA_SIZE

其中,对应的两个宏的值是:

u-boot-1.1.6_20100601\opt\EmbedSky\u-boot-1.1.6\include\configs\EmbedSky.h中:

所以,从源码中的宏定义中可以看出,

所以,此三行的含义就是算出r0的值:

如果定义了CONFIG_USE_IRQ,即如果使用中断的话,那么再把r0的值减去IRQ和FIQ的堆栈的值,

而对应的宏的值也是在u-boot-1.1.6_20100601\opt\EmbedSky\u-boot-1.1.6\include\configs\EmbedSky.h中:

所以,此时r0的值就是:

最后,再减去终止异常所用到的堆栈大小,即12个字节。现在r0的值为:

然后将r0的值赋值给sp,即堆栈指针。

关于:

  • sp代表stack pointer,堆栈指针;

和后面要提到的ip寄存器:

  • ip代表instruction pointer,指令指针。
    更多详情参见下面的解释。

ARM的寄存器的别名和相关的APCS

此处简单介绍一下,ARM 寄存器的别名,以及什么是 APCS。

1.ARM 中的寄存器的别名

默认的情况下,这些寄存器只是叫做r0,r1,…,r14等,而APCS 对其起了不同的别名。

使用汇编器预处理器的功能,你可以定义 R0 等名字,但在你修改其他人写的代码的时候,最好还是学习使用 APCS 名字。

一般编程过程中,最好按照其约定,使用对应的名字,这样使得程序可读性更好。

关于不同寄存器所对应的名字,见下表:

APCS寄存器别名定义:

更加详细一点,见下:

2.什么是 APCS

APCS,ARM 过程调用标准(ARM Procedure Call Standard),提供了紧凑的编写例程的一种机制,定义的例程可以与其他例程交织在一起。最显著的一点是对这些例程来自哪里没有明确的限制。它们可以编译自 C、 Pascal、也可以是用汇编语言写成的。

APCS 定义了:

  • 对寄存器使用的限制。
  • 使用栈的惯例。
  • 在函数调用之间传递/返回参数。
  • 可以被‗回溯‘的基于栈的结构的格式,用来提供从失败点到程序入口的函数(和给予的
    参数)的列表。

算出堆栈位置

在上面,经过计算,算出了堆栈的地址,然后赋值给了sp,此处,接着才去调用函数clock_init去初始化时钟。

其中此函数是在C文件:

u-boot-1.1.6_20100601\opt\EmbedSky\u-boot-1.1.6\board\EmbedSky\boot_init.c

中:

void clock_init(void)
{
。。。设置系统时钟clock的相关代码。。。
}

看到这里,让我想起,关于其他人的关于此start.S代码解释中说到的,此处是先去设置好堆栈,即初始化sp指针,然后才去调用C语言的函数clock_init的。

#ifndef CONFIG_SKIP_LOWLEVEL_INIT
  bl cpu_init_crit
#endif

就不需要先设置好堆栈,再去进行函数调用。

其中cpu_init_crit对应的代码也在start.S中(详见后面对应部分的代码),是用汇编实现的。

adr指令

这个由于流水线导致的PC的值和当前指令地址不同的现象,就是我们常说的,ARM中,PC=PC+8。

总结

why

那对于C语言,为何就需要堆栈,而汇编却不需要堆栈的原因?

为何 C 语言(的函数调用)需要堆栈,而汇编语言却不需要堆栈

之前看了很多关于uboot的分析,其中就有说要为C语言的运行,准备好堆栈。

而自己在Uboot的start.S汇编代码中,关于系统初始化,也看到有堆栈指针初始化这个动作。

但是,从来只是看到有人说系统初始化要初始化堆栈,即正确给堆栈指针sp赋值,但是却从来没有看到有人解释,为何要初始化堆栈。所以,接下来的内容,就是经过一定的探究,试图来解释一下,为何要初始化堆栈,即:

为何C语言的函数调用要用到堆栈,而汇编却不需要初始化堆栈。

要明白这个问题,首先要了解堆栈的作用。

关于堆栈的作用,要详细讲解的话,要很长的篇幅,所以此处只是做简略介绍。

总的来说,堆栈的作用就是:保存现场/上下文,传递参数。

1.保存现场/上下文

现场,意思就相当于案发现场,总有一些现场的情况,要记录下来的,否则被别人破坏掉之后,你就无法恢复现场了。而此处说的现场,就是指CPU运行的时候,用到了一些寄存器,比如r0,r1等等,对于这些寄存器的值,如果你不保存而直接跳转到子函数中去执行,那么很可能就被其破坏了,因为其函数执行也要用到这些寄存器。

因此,在函数调用之前,应该将这些寄存器等现场,暂时保持起来,等调用函数执行完毕返回后,再恢复现场。这样CPU就可以正确的继续执行了。

在计算机中,你常可以看到上下文这个词,对应的英文是context。那么:

1. 什么叫做上下文 context

保存现场,也叫保存上下文。

上下文,英文叫做context,就是上面的文章,和下面的文章,即与你此刻,当前CPU运行有关系的内容,即那些你用到寄存器。所以,和上面的现场,是一个意思。

保存寄存器的值,一般用的是push指令,将对应的某些寄存器的值,一个个放到堆栈中,把对应的值压入到堆栈里面,即所谓的压栈。

然后待被调用的子函数执行完毕的时候,再调用pop,把堆栈中的一个个的值,赋值给对应的那些你刚开始压栈时用到的寄存器,把对应的值从堆栈中弹出去,即所谓的出栈。

其中保存的寄存器中,也包括lr的值(因为用bl指令进行跳转的话,那么之前的pc的值是存在lr中的),然后在子程序执行完毕的时候,再把堆栈中的lr的值pop出来,赋值给pc,这样就实现了子函数的正确的返回

2.传递参数

C语言进行函数调用的时候,常常会传递给被调用的函数一些参数,对于这些C语言级别的参数,被编译器翻译成汇编语言的时候,就要找个地方存放一下,并且让被调用的函数能够访问,否则就没发实现传递参数了。对于找个地方放一下,分两种情况。

一种情况是,本身传递的参数就很少,就可以通过寄存器传送参数。

因为在前面的保存现场的动作中,已经保存好了对应的寄存器的值,那么此时,这些寄存器就是空闲的,可以供我们使用的了,那就可以放参数,而参数少的情况下,就足够存放参数了,比如参数有2个,那么就用r0和r1存放即可。(关于参数1和参数2,具体哪个放在r0,哪个放在r1,就是和APCS中的“在函数调用之间传递/返回参数”相关了,APCS中会有详细的约定。感兴趣的自己去研究。)

但是如果参数太多,寄存器不够用,那么就得把多余的参数堆栈中了。即,可以用堆栈来传递所有的或寄存器放不下的那些多余的参数。

3.举例分析 C 语言函数调用是如何使用堆栈的

为何 ARM7 中 PC=PC+8

首先,对于 ARM7 对应的流水线的执行情况,如下面这个图所示:

ARM7

为何 ARM9 和 ARM7 一样,也是 PC=PC+8

关于为何不直接用 mov 指令,而非要用 adr 伪指令

mov 指令的操作数的取值范围到底是多少

http://blog.chinaunix.net/space.php?uid=20799298&do=blog&cuid=2055392

http://netwinder.osuosl.org/pub/netwinder/docs/arm/ARM7500FEvB_3.pdf

参考文献

1.2010年6月 最新TQ2440光盘下载 (Linux内核,WinCE的eboot,uboot均有更新)

http://bbs.embedsky.net/viewthread.php?tid=859

2. .globl,.word,.balignl的语法

http://re-eject.gbadev.org/files/GasARMRef.pdf

3.label 的解释

http://sourceware.org/binutils/docs-2.20/as/Labels.html#Labels

4. ldr的语法:

http://infocenter.arm.com/help/topic/com.arm.doc.dui0206hc/DUI0206HC_

rvct_linker_and_utilities_guide.pdf

5. ldr 指令

http://wenku.baidu.com/view/f7cc280102020740be1e9bea.html

6. ldr 指令

http://www.pczpg.com/a/2010/0607/11062.html

7. .word 的语法

http://blogold.chinaunix.net/u3/115924/showart_2280163.html

8. ARM7体系结构

http://www.docin.com/p-73665362.html

9.bootloader

http://www.csie.nctu.edu.tw/~wjtsai/EmbeddedSystemDesign/Ch2-bootloader.pdf

10. S3C2440相关的软硬件资料

http://just4you.springnote.com/pages/1052612

11. S3C2440的CPU的datasheet:s3c2440a_um_rev014_040712.pdf

http://just4you.springnote.com/pages/1052612/attachments/803220

12.伪指令ldr语法和含义

http://blog.csdn.net/lihaoweiV/archive/2010/11/24/6033003.aspx

13. ARM9 2410移植之ARM中断原理, 中断嵌套的误区,中断号的怎么来的

http://againinput4.blog.163.com/blog/static/17279949120113882341352/

14.adr指令的语法和含义

http://blog.mcuol.com/User/cdkfGao/article/8057_1.htm

15. ARM协处理器

http://apps.hi.baidu.com/share/detail/32319228

16. ARM920T

http://infocenter.arm.com/help/topic/com.arm.doc.ddi0151c/ARM920T_TRM1_S.pdf

17. CP15 的各个寄存器的含义解释

http://www.heyrick.co.uk/assembler/coprocmnd.html

18. Invalidate ICache and DCache

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0184b/Chdcfejb.html

19. Invalidate TLB(s)

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0184b/Chdifbjc.html

20. Control register

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0184b/Chdifbjc.html

21. Domain access control

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0151c/I273867.html

22. ARM920T 的 CPU 的 7 种模式

http://www.docin.com/p-73665362.html

23. ARM Linux Kernel Boot Requirements

http://www.arm.linux.org.uk/developer/booting.php

24. 嵌入式系统之 WATCHDOG(看门狗)概述

http://wenku.baidu.com/view/e5cd52ff04a1b0717fd5dd27.html

25. ARM 流水线和 program counter(PC)的增量

http://hi.baidu.com/istry/blog/item/f823e1438de0a71972f05d0f.html

26. Predeclared register names

http://www.keil.com/support/man/docs/armasm/armasm_ch03s03s01.htm

27. Predeclared extension register names

http://www.keil.com/support/man/docs/armasm/armasm_ch03s03s02.htm

28. Predeclared coprocessor names

http://www.keil.com/support/man/docs/armasm/armasm_ch03s03s03.htm

29. mov 的操作数的取指范围

http://blog.chinaunix.net/space.php?uid=20799298&do=blog&cuid=2055392

30. ARM Processor Instruction Set

http://netwinder.osuosl.org/pub/netwinder/docs/arm/ARM7500FEvB_3.pdf

目录
相关文章
|
26天前
|
存储 C语言
C语言学习记录——7000+字长文-复习&学习指针(指针、地址、指针变量、指针与数组、指针与函数、指针数组、多级指针)二
C语言学习记录——7000+字长文-复习&学习指针(指针、地址、指针变量、指针与数组、指针与函数、指针数组、多级指针)二
17 1
|
26天前
|
存储 C语言
C语言学习记录——7000+字长文-复习&学习指针(指针、地址、指针变量、指针与数组、指针与函数、指针数组、多级指针)一
C语言学习记录——7000+字长文-复习&学习指针(指针、地址、指针变量、指针与数组、指针与函数、指针数组、多级指针)一
15 1
|
2月前
|
存储 C语言 索引
指针学习(特殊指针)
指针学习(特殊指针)
19 0
|
2月前
|
存储 安全 Java
Go语言学习10-指针类型
【4月更文挑战第11天】本篇 Huazie 向大家介绍 Go语言的指针类型
27 2
Go语言学习10-指针类型
|
2月前
|
存储 C++
C++语言学习指针和引用应用案例
C++中的指针和引用用于高效操作内存。示例展示指针和引用的基本用法:指针`*p`存储变量`a`的地址,引用`&x`在函数调用中实现值交换而无需复制。此外,引用`update(&x)`可直接修改原变量,指针`p`在数组操作中用于遍历和访问不同部分。
17 2
|
2月前
|
编译器 C语言
C语言指针学习
C语言指针学习
20 0
|
2月前
|
存储 编译器 C语言
C++初阶类与对象(一):学习类与对象、访问限定符、封装、this指针
C++初阶类与对象(一):学习类与对象、访问限定符、封装、this指针
48 0
|
8月前
|
存储 安全 编译器
C++入门学习(4)引用 (讲解拿指针比较)
C++入门学习(4)引用 (讲解拿指针比较)
|
8月前
|
算法
算法学习--双指针与二分查找
算法学习--双指针与二分查找
|
19小时前
|
搜索推荐 程序员 C语言
指针赋值与引用传递:C语言的基础知识与实践技巧
指针赋值与引用传递:C语言的基础知识与实践技巧