1、参考链接
【精华】程序员的自我修养视频教程_哔哩哔哩_bilibili
2、栈的初始化
1、概念解析:
栈桢---sp栈指针 fp栈基址
栈:栈是一种具有后进先出的数据组织方式,也就是说后放进去的数据后面取出来(也可以是数据结构体对象)。栈底是第一个进栈的数据所在的位置,栈顶是最后一个数据所处在的位置。
数据组织有:链表、图、树等等。我们把数据的组织实现方式当成黑盒使用即可。
pop(stack,&data)
push(stack,&data)
我并不关心pop的实现细节,我只是利用的pop的特性,针对某个已经的stack,将大量的数据依次通过pop入栈,最后再通过push出栈,就能实现逆序了,同理也可以实现其他功能;
满栈/空栈解析:
可以根据sp指针指向的位置,栈可以分为满栈和空栈
满栈:当堆栈指针sp总是指向最后压入堆栈的数据。*sp也就是访问最后一个数据.(arm采用的是满栈)
空栈:当堆栈指针sp总是指向最后压入堆栈的数据的下一个位置。*(sp-1)也就是访问最后一个数据。
升栈/降栈
根据sp指针移动的方向,栈可以分为升栈和降栈,注意arm使用的是降栈
升栈:随着数据的入栈,sp从低地址向高地址移动
降栈:随着数据的入栈,sp从高地址向低地址移动
栈桢:
栈桢就是函数所使用的那部分栈,所有的函数的栈桢串起来就是整个程序的完整栈。栈的顶部和底部分别通过两个寄存器来保存,分别是sp(r13)栈顶和fp(r11)栈底。
以下是arm的栈布局方式,main stack fram为调用函数的栈桢,func1 stack frame为当前函数(被调用者)的栈桢,栈底在高地址,栈顶在地址,栈向下增长。途中FP就是func1函数的栈基地址,他指向函数的栈桢起始地址;sp则是函数的栈指针,它指向当前栈顶的位置。
ARM的压栈顺序是PC、LR、栈指针SP、栈基址FP、传入参数个数,以及指针,本地变量,和临时变量。如果函数准备调用另外一个函数,跳转之前临时变量先保存另一个函数的参数。
举证分析:
#include <stdio.h>
int func1()
{
...
}int main()
{
...
func1();
...
}
3、栈作用
(1)保存局部变量
#include <stdio.h>
int main()
{
int a;
a++;
return a;
}
/******************************************************
反汇编找到main函数
dongry@d-linux:~/test/hardwork/stack$ arm-linux-gcc -g stack1.c -o stack1
dongry@d-linux:~/test/hardwork/stack$ arm-linux-objdump -D -S stack1 >dump
dongry@d-linux:~/test/hardwork/stack$ vim dump/main
*******************************************************/
/*反汇编代码*/
000083a0 <main>:
#include <stdio.h>
int main()
{
83a0: e1a0c00d mov ip, sp
83a4: e92dd800 stmdb sp!, {fp, ip, lr, pc}
83a8: e24cb004 sub fp, ip, #4 ; 0x4
83ac: e24dd004 sub sp, sp, #4 ; 0x4
int a;
a++;
83b0: e51b3010 ldr r3, [fp, #-16]
83b4: e2833001 add r3, r3, #1 ; 0x1
83b8: e50b3010 str r3, [fp, #-16]
return a;
83bc: e51b3010 ldr r3, [fp, #-16]
}
/*分析*/
mov ip,sp //保存sp到ip,先把原来的fp sp保存起来
stmdb sp!,{fp,ip,lr,pc} /*先对sp-4,再对fp,ip,lr,pc压栈*/
//sp=sp-4;push {pc};sp=pc; /*先压pc*/
//sp=sp-4;push {lr};sp=lr; /*压lr*/
//sp=sp-4;push {ip};sp=ip; /*压ip*/
//sp=sp-4;push {fp};sp=fp; /*压fp*/
sub fp,ip,#4 //fp指向ip-4
sub sp,sp,#4 //开辟一块空间
ldr r3,[fp,#-16] //临时存放在[fp-16],将内存中的值放入到寄存器中, 为什么要用r3?
add r3,r3,#1
str r3,[fp,#-16] //寄存器存到内存中
(2)参数传递--参数大于4个或者是结构体传参的时候
#include <stdio.h>
void func(int a,int b,int c,int d,int e,int f)
{
int k;
int l;
k=e+f;
l=a+b;
}int main()
{
func(1,2,3,4,5,6);
return 0;
}
4、栈初始化
堆栈初始化代码
210 内存512MB
2440 内存64MB 内存起始地址30000000
所以可以把sp设置为30000000+64MB
bl init_stack:
ldr sp, =34000000 只有一行代码
mov pc,lr
5、裸机源码
gboot.lds
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS {
. = 0x30008000;
. = ALIGN(4);
.text :
{
start.o (.text)
*(.text)
}
. = ALIGN(4);
.data :
{
*(.data)
}
. = ALIGN(4);
bss_start = .;
.bss :
{
*(.bss)
}
bss_end = .;
}
Makefile
all: start.o
arm-linux-ld -Tgboot.lds -o gboot.elf $^
arm-linux-objcopy -O binary gboot.elf gboot.bin
%.o : %.S
arm-linux-gcc -g -c $^
%.o : %.c
arm-linux-gcc -g -c $^
.PHONY: clean
clean:
rm *.o *.elf *.bin
start.lds
.text
.global _start
_start:
b reset
ldr pc, _undifined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
_undifined_instruction: .word undifined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word reset
undifined_instruction:
nop
software_interrupt:
nop
prefetch_abort:
nop
data_abort:
nop
not_used:
nop
irq:
nop
fiq:
nop
reset:
bl set_svc
bl disable_watchdog
bl disable_interrupt
bl disable_mmu
bl init_clock
bl init_sdram
bl copy_to_ram
bl init_stack
bl light_led
set_svc:
mrs r0, cpsr
bic r0, r0,#0x1f
orr r0, r0,#0xd3
msr cpsr, r0
mov pc, lr
#define pWTCON 0x53000000
disable_watchdog:
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
mov pc, lr
disable_interrupt:
mvn r1, #0x0
ldr r0, =0x4a000008
str r1, [r0]
mov pc, lr
disable_mmu:
mcr p15,0,r0,c7,c7,0
mrc p15,0,r0,c1,c0,0
bic r0, r0, #0x00000007
mcr p15,0,r0,c1,c0,0
mov pc, lr
#define CLKDIVN 0x4c000014
#define MPLLCON 0x4c000008
#define MPLL_405MHZ ((127<<12)|(2<<4)|(1<<0))
init_clock:
ldr r0, =CLKDIVN
mov r1, #0x5
str r1, [r0]
mcr p15,0,r0,c1,c0,0
orr r0,r0,#0xc0000000
mcr p15,0,r0,c1,c0,0
ldr r0, =MPLLCON
ldr r1, =MPLL_405MHZ
str r1, [r0]
mov pc, lr
#define mem_contrl 0x48000000
init_sdram:
ldr r0, =mem_contrl
add r3, r0, #4*13
adrl r1, mem_data
0:
ldr r2, [r1], #4
str r2, [r0], #4
cmp r0, r3
bne 0b
mov pc, lr
copy_to_ram:
ldr r0, =0x0
ldr r1, =0x30008000
add r3, r0, #1024*4
copy_loop:
ldr r2, [r0], #4
str r2, [r1], #4
cmp r0, r3
bne copy_loop
mov pc, lr
init_stack:
ldr sp, =0x34000000
mov pc ,lr
mem_data:
.long 0x22000000
.long 0x00000700
.long 0x00000700
.long 0x00000700
.long 0x00000700
.long 0x00000700
.long 0x00000700
.long 0x00018001
.long 0x00018001
.long 0x008c04f5
.long 0x000000b1
.long 0x00000030
.long 0x00000030
#define GPBCON 0x56000010
#define GPBDAT 0x56000014
light_led:
ldr r0, =GPBCON
ldr r1,=0x15400
str r1, [r0]
ldr r0, =GPBDAT
ldr r1,=0x6BF
str r1, [r0]
mov pc, lr