ARM下书写位置无关的代码

转自:http://blog.youkuaiyun.com/iamshaofa/article/details/7917301

位置无关代码,即该段代码无论放在内存的哪个地址,都能正确运行。究其原因,是因为代码里
没有使用绝对地址,都是相对地址。

位置无关的写法:

(1) B指令

B指令接受一个相对地址,因此在汇编里用B跳转到一个标号时,实际编译的结果是一个相对跳转。
相对地址有个范围限制,即目标不能太远,一般目标放在同一个文件里是肯定可以的。
_start:
    b  _reset
_reset:
      ...

(2) BL

BL用于调用函数,也是一个相对跳转

(3) ADR

获取标号的地址,在编译时会使用PC+偏移的方式得到该位置的地址。例如,当TEXT_BASE是0时
SMRDATA可能被放在0x100的位置,当TEXT_BASE为0x30000000时放在0x30000100的位置。使用ADR
总能获取正确的位置,与程序的加载地址无关。
    ADR R0, SMRDATA
SMRDATA:
    .word  0x22111120
    .word  0x00002F50
    .word  0x00000700
(相应的, LDR Rn, =LABEL是位置相关的)

(4) LDR

当加标号时,LDR可以用于伪指令,也可以真指令。
真指令: (标号前不加=号,表示取标号处的值)
    LDR R0,  SDRDATA
实际被编译为LDR R0, [PC, #NN],其中NN是目标的相对距离

伪指令: (标号前加=号,取标号的地址)
    LDR R0, = SDRDATA
实际编译的时候的时候,会在某位置存处SDRDATA的值,然后用一个LDR取出来。
显然,用LDR时,加不加=号有很大区别。
无=号:取该标号处的值,位置无关
有=号:取该标号的地址,位置相关

举例分析

例1:中断向量跳转

_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

_undefined_instruction:    .word undefined_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 fiq
其中,
ldr pc, _irq,由于没加=号,表示取值_irq处的值放在pc里 (位置无关)
_irq:  .word irq ,表示_irq存放的值是irq的绝对地址(位置有关)

例2:

bl  main ; 位置无关
ldr pc, =main; 把main的地址放在pc,位置相关

例3: 静态变量

_MAGIC_NUM:
    .word 0x12345678
取值
    LDR  R0, _MAGIC_NUM  ; 位置无关

例4: 存放标号绝对地址(绝对地址是编译的时候已经固定)

_OS_Running_p:
    .word  OS_Runing
则_OS_Running_p存放的是标号OS_Running的绝对地址

例5: 显式LDR和隐式LDR

以给某C中的变量的g_num赋值为例
(1) 使用伪指令LDR,即为隐式
    LDR  R0, =g_num    @取g_num的地址到R0
    MOV R1, #10
            STR  R1, R0
(2) 显式赋值
先定义一个变量p_g_num,用于保存g_num的地址
p_g_num:
    .word   g_num   @ g_num的绝对地址
然后赋值
    LDR R0, p_g_num
    MOV R1, #10
    STR R1,  R0
显然,两者其实一样,伪指令被展开后其实就是(2)的样子。
不同点在于:在多次引用的时候,如果使用伪指令,则会有多个临时定义。所以,
在多次引用的时候应该使用显式定义。

例6: 使用LinkScript中的变量

这种情形和例5相同
1) LinkScript中定义了两个位置
{
    __bss_start = .;
    .bss : { *(.bss) }
    _end = .;
}
2) 定义两个变量,用于存处这两个位置
.globl _bss_start
_bss_start:
    .word __bss_start

.globl _bss_end
_bss_end:
    .word _end
3) 使用这两个位置
    ldr    r0, _bss_start        /* find start of bss segment        */
    ldr    r1, _bss_end        /* stop here                        */


http://blog.youkuaiyun.com/iamshaofa/article/details/7917301
<think>首先,用户的问题是:“我想了解ARM汇编指示符的相关信息 请问ARM汇编指示符介绍”。 系统级指令要求: - 所有行内数学表达式必须使用 $...$ 格式。 - 独立公式使用 $$...$$ 格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,例如 [^1]。 用户提供了引用: - 引用[1]: 关于ARM汇编程序示例和一些指令。 - 引用[2]: 关于ARM汇编基础,如标号、指令、注释等。 用户提到这些引用仅供参考,我应该更多参考用户上一次的问题和我的回答。但在这个对话中,这是第一条消息,所以没有上一次的回答。我必须基于当前查询。 用户的问题是询问“ARM汇编指示符”。从引用中: - 引用[1] 提到了指示符如 `area`、`code32`、`entry`、`start`、`stop` 等。 - 引用[2] 提到了 `label:`、`instruction`、`@` 符号用于注释。 在ARM汇编中,“指示符”通常指的是汇编指令之外的伪指令或汇编器指示符(assembler directives),它们指导汇编器如何处理代码,而不是CPU执行的指令。例如: - `AREA`:定义代码或数据区域。 - `ENTRY`:指定程序入口点。 - `CODE32`:指定使用32位ARM指令集。 - `END`:表示程序结束。 用户查询是“ARM汇编指示符介绍”,所以我需要介绍这些指示符。 我的回答结构: - 使用中文。 - 结构清晰,帮助用户逐步解决问题。 - 引用结束时添加引用标识。 - 最后生成相关问题。 步骤: 1. **介绍ARM汇编指示符**:解释什么是汇编指示符,它们在汇编程序中的作用。 2. **列出常见指示符**:从引用中提取示例,如 `area`, `entry`, `code32`, `end` 等。 3. **解释每个指示符**:简短说明每个指示符的含义和用法。 4. **添加示例代码**:如果需要可以参考引用中的代码,但用户代码中已有示例。 5. **生成相关问题**:在结尾添加相关问题。 系统指令中提到“回答中引用的段落末尾自然地添加引用标识”,所以在解释时需要引用引用[1]或[2]的内容。 确保没有数学表达式,因为ARM汇编不涉及数学公式。但系统指令说“包括变量、不等式、方程等必须使用$...$”,但这里可能无关。如果有地址或数字,可能需要用$...$,如地址值,但通常是数字,直接用数字即可。 例如,在解释指令时,如果有偏移量,如 `b start`,偏移量可以用 `$offset$`,但汇编中通常是符号标签。保持简单,避免不必要的LaTeX。 最后,生成相关问题。 大纲: - 开头:介绍ARM汇编指示符。 - 主体:列出并解释常见指示符,基于引用。 - 结尾:总结。 - 相关问题:生成3-4个相关问题。 相关问题示例: 1. ARM汇编中的伪指令有哪些? 2. 如何定义堆栈区域在ARM汇编中? 3. ARM汇编程序的结构是怎样的? 4. 调试ARM汇编程序的常见方法? 引用标识: - 当提及引用内容时,自然地添加 [^1] 或 [^2]。 例如:"例如,在汇编程序开头使用 `AREA` 定义代码段 [^1]。" 现在,构建回答。 </think>ARM汇编指示符(也称为汇编器指示符或伪指令)是汇编语言中的特殊命令,用于指导汇编器如何处理代码和数据,而不是直接由CPU执行的指令。它们主要定义程序结构、内存布局、符号引用等,通常以特定关键字形式出现,例如定义代码段、指定指令集或标记程序入口。下面我将基于ARM汇编基础知识逐步介绍常见指示符及其用法,参考用户提供的引用内容进行说明。 ### 1. **ARM汇编指示符的基本作用** - 指示符不是CPU指令,而是汇编器的控制命令,用于: - 定义代码和数据的存储区域(如代码段、数据段)。 - 指定指令集版本(如32位或Thumb指令)。 - 标记程序入口和结束点。 - 管理符号和标签引用。 - 在ARM汇编中,指示符通常出现在程序开头或关键位置,以引导汇编过程。例如,在引用[1]的程序中,`AREA`、`ENTRY` 和 `END` 都是基础指示符 [^1]。 ### 2. **常见ARM汇编指示符介绍** 以下是ARM汇编中最常用的指示符,结合引用[1]和[2]的示例代码进行解释。每个指示符都以大写形式书写,后接参数(如段名或标签)。 - **`AREA`**: 定义程序的存储区域(如代码段或数据段)。参数包括区域名、类型(如`CODE`或`DATA`)和属性(如`READONLY`)。 示例: `AREA FF,CODE,READONLY` 定义了一个名为“FF”的只读代码段 [^1]。 - 作用: 帮助汇编器分配内存空间,确保代码和数据正确加载。 - **`CODE32` 或 `THUMB`**: 指定后续指令的指令集。`CODE32` 表示使用32位ARM指令集,`THUMB` 表示使用16位Thumb指令集。 示例: `CODE32` 在程序开头声明所有指令为32位ARM格式 [^1]。 - 作用: 优化代码大小和性能,兼容不同ARM架构。 - **`ENTRY`**: 标记程序的入口点(即第一条执行的指令位置)。 示例: `ENTRY` 后紧跟 `start:` 标签,表示程序从 `start` 处开始执行 [^1]。 - 作用: 确保链接器和加载器正确初始化程序。 - **`END`**: 表示程序结束,汇编器忽略此后的内容。 示例: `END` 出现在程序末尾,标记汇编过程终止 [^1]。 - 作用: 防止额外代码被错误处理。 - **标签(Label)**: 在引用[2]中,标签被定义为地址位置的标识符,以冒号(`:)结尾。它用作指示符来标记指令或数据位置 [^2]。 示例: `start:` 或 `stop:` 表示跳转目标地址。 - 作用: 支持分支指令(如 `B start`),便于代码组织和跳转。 - **注释指示符**: 使用 `@` 符号或 `/* */` 添加注释,不属于可执行代码。 示例: `MOVS R0,#0X12 @设置R0=0X12` 中的 `@` 后内容为注释 [^2]。 - 作用: 提高代码可读性,不影响汇编结果。 ### 3. **ARM汇编指示符的实际应用示例** 参考引用[1]的代码: ```assembly AREA FF,CODE,READONLY ; 定义只读代码段FF CODE32 ; 指定32位指令集 ENTRY ; 程序入口点 START: ; 标签定义的入口位置 MOV R0,#9 ; 指令示例 BL FUNC ; 调用子函数 STOP B STOP ; 无限循环 FUNC: ; 子函数标签 BX LR ; 返回指令 END ; 程序结束 ``` - 解析: - `AREA`、`CODE32`、`ENTRY` 和 `END` 构建了程序框架。 - 标签如 `START:` 和 `FUNC:` 定义分支点,配合指令如 `B`(跳转)和 `BL`(调用函数)。 - 这种结构确保汇编器正确处理代码逻辑和内存映射 [^1]。 ### 4. **总结** ARM汇编指示符是程序结构的关键元素,它们管理汇编过程,但不生成机器码。常见指示符包括 `AREA`、`CODE32`、`ENTRY`、`END` 和标签定义,这些在嵌入式开发和底层优化中广泛应用。掌握它们能提升代码效率,例如通过 `AREA` 优化内存布局或 `ENTRY` 确保启动正确性 [^1][^2]。如果您有特定场景(如ARM Cortex-M开发),建议查阅官方文档加深理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值