嵌入式Linux裸机开发(四)——重定位relocate

嵌入式Linux裸机开发(四)——重定位relocate

一、位置有关编码

汇编源文件被编译成二进制可执行程序时编码方式可能与内存地址有关,也可能与内存地址无关。与内存地址有关的为位置有关编码,与内存地址无关的为位置无关编码。

程序在设计时需要规划一个程序运行时的地址(链接地址),编译连接器在链接时必须指定这个链接地址,得到的二进制程序的程序理论规划的运行时地址和编译连接器指定的链接地址才相同,程序才能正常运行。位置无关编码程序则无需设计程序时规划运行时地址,编译链接器链接时同样无需指定链接地址。

二、S5PV210的iROM启动方式与uboot启动方式分析

三星官方推荐的S5PV210的iROM启动方式规定,BL1最大为16KB,BL2最大为80KB,bootloader最大为96KB。如果bootloader的大小为86KB,根据iROM启动过程分析,开发板上电后运行在iROM阶段,即BL0阶段,BL0会初始化系统时钟、特殊设备控制寄存器和启动设备,加载外部启动设备中bootloader的BL1(大小16KB)到iRAM中运行,BL1运行时会加载bootloader中的BL2(70KB)到iRAM中运行,BL2运行时会初始化SDRAM,将OS kernel拷贝到SDRAM,最后跳转到SDRAM执行kernel,进入系统。

Uboot的启动方式规定,uboot的大小任意,开机上电后BL0开始运行,BL0会初始化系统时钟、特殊设备控制寄存器和启动设备,加载外部启动设备中uboot的BL1(大小16KB)到iRAM中运行,BL1运行时会初始化SDRAM,然后将整个uboot拷贝到SDRAM中,采用长跳转指令从iRAM中直接跳转到SDRAM中继续执行uboot(16KB开始),uboot启动后再命令行中加载、启动OS kernel。

结合三星的iROM启动方式和uboot启动方式分析,uboot的启动方式必须需要重定位。

三、重定位

1、链接地址的确定

运行时地址由运行时决定,编译链接阶段无法指定运行时地址,但程序员可以在程序设计时规划指定。链接地址由程序员在编译链接阶段通过Makefile中的-Ttext参数指定,如果没有明确指定,则默认编译链接阶段指定的链接地址为0。因此,程序员在程序设计规划时指定的地址与编译链接阶段指定的链接地址相同时,程序才能正常运行。

S5PV210的运行地址是SoC设计时规划确定,所以编译链接阶段时链接地址指定为0xD0020010。

2、程序的编译过程

源代码程序编译成可执行程序的过程包括:预编译、编译、汇编、链接四个阶段。

预编译:预编译处理器对宏定义指令、条件编译指、头文件包含指令等伪指令(以# 开头的指令)和特殊符号进行处理

编译:编译阶段要作得工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。

汇编:汇编阶段把汇编语言代码翻译成目标机器指令的过程。

链接:链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体

3、链接脚本

系统预定义的程序段有:

.text:代码段

.data:初始化非0的全局变量数据段

.bss:未初始化全局变量和初始化为0的数据段

每一个链接过程都由链接脚本(linked script,一般以lds做文件后缀名)控制,链接脚本主要用于如何把输入文件内的section放入输出文件,并控制输出文件内各部分在程序地址空间的布局。

ld -verbose可以查看默认的链接脚本的内容。简单的链接脚本如下:

SECTIONS

{

.=0xD0020010;//指定当前地址为0xD0020010

.text:{

*(.text)

}

.=0x8000000;

.data:{

*(.data)

}

.bss:{

*(.bss)

}

}

四、重定位代码实践

目标:在iram中将代码从0xD0020010重定位到0xD0024000

通过链接脚本将代码链接到0xD0024000

将代码下载运行在0xD0020010

在位置无关编码代码段执行完之前将代码拷贝到0xD0024000去执行。使用长跳转指令跳转到0xD0024000地址执行,重定位完成。重定位后iram中有两份相同的代码镜像,一份在0xD0020010处,一份在0xD0024000地址处,重定位后使用长跳转指令ldr pc,=led_blink直接跳转到0xD0024000地址执行。当链接地址和运行地址相同时,长跳转和短跳转(BL)实际效果一样;当链接地址和运行地址不同时,长跳转跳转到链接地址,短跳转跳转到运行地址。

adr指令加载符号地址加载的是运行地址,ldr加载符号地址加载的链接地址。adr是小范围的地址读取伪指令,ldr是大范围的读取地址伪指令。adr是将基于PC相对偏移的地址值或基于寄存器相对地址值读取的为指令,而ldr用于加载32为立即数或一个地址到指定的寄存器中。

strat.S源码:

#define SVC_SP0xD0037D80

.global _start

_start:

//C语言运行时栈设置SVC模式下的栈 CPU复位后为SVC模式,DRAM尚未初始化,

//只有SRAM可用,0xD0037780,0xD0037D80,大小1.5K

ldr sp,=SVC_SP

//重定位

adr r0,_start//加载_start当前运行地址

ldr r1,=_start//加载_start的链接地址

ldr r2,=bss_start//加载bss段的起始地址

cmp r0,r1//比较运行地址和链接地址是否相等

beq clean_bss//相等则表明不需要重定位

//拷贝text段和data段

copy_loop:

ldr r3,[r0],#4

str r3,[r1],#4

cmp r1,r2

bne copy_loop

//清除bss段满足C语言运行时要求,编译器会负责清除bss段,

//编译器清除了运行时地址的bss段,链接地址的bss段未清除                              clean_cclean_bss:

ldr r0,=bss_start

ldr r1,=bss_end

cmp r0,r1

beq run_on_dram//相等则bss为空

mov r2,#0

//清除bss段

clear_loop:

str r2,[r0],#4//现将r2放入r0的值对应的内存地址中,然后r0=r0+4

cmp r0,r1

bne clear_loop

run_on_dram:

ldr pc,=led_blink

.end

led_blink.c源码:

#define rGPJ2CON (*((volatile unsigned int *)0xE0200280))

#define rGPJ2DAT (*((volatile unsigned int *)0xE0200284))

void led_blink(void);

void delay(void);

void led_blink(void)

{

rGPJ2CON = 0x00001111;//设置led1--led4为output

rGPJ2DAT = (0<<0 | 0<<1 | 0<<2 | 0<<3);//点亮led1--led4

delay();//延时

rGPJ2DAT = (1<<0 | 1<<1 | 1<<2 | 1<<3);//熄灭led1--led4

delay();

rGPJ2DAT = (0<<0 | 1<<1 | 1<<2 | 1<<3);//点亮led1

delay();

rGPJ2DAT = (1<<0 | 0<<1 | 1<<2 | 1<<3);//点亮led2

delay();

rGPJ2DAT = (1<<0 | 1<<1 | 0<<2 | 1<<3);//点亮led3

delay();

rGPJ2DAT = (1<<0 | 1<<1 | 1<<2 | 0<<3);//点亮led4

delay();

}

void delay(void)

{

volatile unsigned int i = 0x1FFFFF;

while(i--);

}

link.lds:

SECTIONS

{

. = 0xD0024000;

.text :{

start.o

* (.text)}

.data :{

* (.data)

}

bss_start = .;

.bss :{

* (.bss)

}

bss_end = .;

}

Makefile:

OBJS += start.o led_blink.o

CFLAGS += -Wall -O2

LDFLAGS += -Tlink.lds

CROSS_COMPILER := arm-linux-

CC := $(CROSS_COMPILER)gcc

led.bin: $(OBJS)

$(CROSS_COMPILER)ld -Tlink.lds -o led.elf $^

$(CROSS_COMPILER)objcopy -O binary led.elf led.bin

$(CROSS_COMPILER)objdump -D led.elf > led_elf.dis

gcc mkv210_image.c -o mk210

./mk210 led.bin smart210.bin

%.o:%.c

$(CC) $(CFLAGS) -c $^ -o [email protected]

%.o:%.S

$(CC) $(CFLAGS) -c $^ -o [email protected]

.PHONY = clean

clean:

rm -rf *.o *.elf *.bin *.dis mk210

时间: 2024-08-06 22:29:40

嵌入式Linux裸机开发(四)——重定位relocate的相关文章

嵌入式Linux裸机开发(十四)——ADC

嵌入式Linux裸机开发(十四)--ADC 一.AD转换简介 AD转换就是模数转换,就是把模拟信号转换成数字信号.A/D转换器是用来通过一定的电路将模拟量转变为数字量.模拟量可以是电压.电流等电信号,也可以是压力.温度.湿度.位移.声音等非电信号.在A/D转换前,输入到A/D转换器的输入信号必须经各种传感器把各种物理量转换成电压信号.A/D转换后,输出的数字信号可以有8位.10位.12位.14位和16位等. 二.ADC控制器 ADC控制器时钟: 时钟源为PCLK_PSYS,经过一次分频后得到AD

嵌入式Linux裸机开发(十)——SD卡启动

嵌入式Linux裸机开发(十)--SD卡启动 存储设备分类: 磁存储设备:软盘.硬盘.光盘.CD.磁带 Flash:NandFlash.NorFlash 缺点:时序复杂,无坏块处理机制,接口不统一 NandFlash:MLC(可靠性差,容量大).SLC(可靠性高.容量小) 扩展卡式Flash:SD卡.MMC卡.MicroSD(TF卡) 内部为NnadFlash存储颗粒,外部封装了接口,接口标准统一.通用. 缺点:频繁使用导致卡槽接触不可靠 iNand.MoviNand.eSSD: 内部为Nand

嵌入式Linux裸机开发(五)——SDRAM初始化

嵌入式Linux裸机开发(五)--SDRAM初始化 一.SDRAM初始化流程 S5PV210有两个独立的DRAM控制器,一个最大支持512MB,一个最大支持1024MB,但两个控制器必须支持相同类型的内存. 根据三星S5PV210文档可知,DDR2类型内存的初始化流程如下: 1.提供稳压电源给内存控制器和内存芯片,内存控制器必须保持CLE在低电平,此时就会提供稳压电源.注:当CKE引脚为低电平时,XDDR2SEL应该处于高电平 2.根据时钟频率正确配置PhyControl0.ctrl_start

嵌入式Linux裸机开发(七)——UART串口通信

嵌入式Linux裸机开发(七)--UART串口通信 一.UART串口通信简介 通用异步收发器简称UART,即UNIVERSAL ASYNCHRONOUS RECEIVER AND TRANSMITTER, 它用来传输串行数据.发送数据时, CPU 将并行数据写入UART,UAR按照一定的格式在一根电线上串 行发出:接收数据时, UART检测另一根电线的信号,将串行收集在缓冲区中, CPU 即可读取 UART 获得这些数据. 在 S5PV210中, UART提供了 4 对独立的异步串口I/O端口,

嵌入式Linux裸机开发(十五)——LCD

嵌入式Linux裸机开发(十五)--LCD 一.LCD简介 LCD(Liquid Crystal Display)是液晶显示器简称.LCD的构造是在两片平行的玻璃基板当中放置液晶盒,下基板玻璃上设置TFT(薄膜晶体管),上基板玻璃上设置彩色滤光片,通过TFT上的信号与电压改变来控制液晶分子的转动方向,从而达到控制每个像素点偏振光出射与否而达到显示目的. 1.LCD类型 按照背光源的不同,LCD可以分为CCFL和LED两种. A.CCFL 指用CCFL(冷阴极荧光灯管)作为背光光源的液晶显示器(L

嵌入式Linux裸机开发(一)——点亮Led

嵌入式Linux裸机开发(一)--点亮Led 开发板:友善之臂smart210 一.电路图查阅 1.底板电路图 查阅开发板底板电路图,查阅LED相关部分 LED电路工作原理: LED的正极接3.3V,负极接地时导通,LED发光. 开发板共有四颗LED,正极接3.3V,负极接开发板的LED1_LED4引脚,如果LED要点亮则需要输入低电平. 2.核心板电路图 查阅核心板电路图可知,LED1-LED4接在SoC的GPJ2_0-GPJ2_3,通过控制GPJ2_0-GPJ2_3的寄存器使对应GPIO引脚

嵌入式Linux裸机开发(九)——S5PV210定时器

嵌入式Linux裸机开发(九)--S5PV210定时器 S5PV210内部一共有四类定时器. 一.PWM定时器 1.PWM定时简介 S5PV210内部共有5个32bit的PWM定时器.PWM定时器可以生成内部中断.PWM定时器0.1.2.3具有PWM功能,可以驱动外部I/O信号.PWM定时器4是一个无外部引脚的内部定时器.PWM 定时器使用 PCLK_PSYS 作为时钟源. 每个定时器有一个由定时器时钟驱动的32位递减计数器.递减计数器的初始值是由TCNTBn自动装载而获得的.如果递减计数器减到

嵌入式Linux裸机开发(八)——S5PV210中断处理流程

嵌入式Linux裸机开发(八)--S5PV210中断处理流程 中断是一个过程,是CPU在执行当前程序的过程中因硬件或软件的原因插入了另一段程序运行的过程.因硬件原因引起的中断过程的出现是不可预测的,即随机的,而软中断是事先安排的.引起中断的信号源称之为中断源.根据中断源的来源将中断分为外部中断和内部中断.中断源来自SoC内部(内部外设)的中断称为内部中断.中断源来自SoC外部,通过中断对应的GPIO引脚产生的中断称为外部中断.中断有四种状态: Inactive(不激活):没有被激活或挂起的中断 

嵌入式Linux裸机开发(六)——S5PV210时钟系统

嵌入式Linux裸机开发(六)--S5PV210时钟系统 一.时钟系统简介 外设工作需要一定频率的时钟,这些时钟都由系统时钟提供.系统时钟一般由外部低频24MHZ晶体振荡器通过锁相环电路PLL倍频产生.通过外部的低频晶体振荡器产生系统时钟不仅可以减少干扰还可以降低成本.外设的工作频率越高,功耗越高,越不稳定.通过关闭外设的时钟可以关闭外设. 二.时钟域 S5PV210 中包含 3 大类时钟 domain, 分别是主系统时钟 domain (简称 MSYS).显示相关的时钟 domain (DSY