【疑惑】链接地址,运行地址

链接地址:链接脚本中指定的,编译器使用,体现在反汇编代码中,

运行地址:PC(程序计数器)中的地址。

若程序的运行地址和链接地址不一样,需要用到位置无关码,位置相关码,在去网上学习相关概念的过程中,记录了下来。

首先需要会看反汇编的代码

从左到右面的依次是链接地址,机器码,机器码对应的汇编代码(反汇编),注释。

然后学习位置无关码和位置相关码,

(此处引用)

00000000        30000000 <_start>:
                        30000000:    e3a0da01     mov    sp, #4096    ; 0x1000
                        30000004:    eb00000a     bl    30000034 <disable_watch_dog>
                        30000008:    eb00000d     bl    30000044 <clock_init>
                        3000000c:    eb000026     bl    300000ac <memsetup>
                        30000010:    eb000040     bl    30000118 <copy_steppingstone_to_sdram>
                        30000014:    e59ff00c     ldr    pc, [pc, #12]    ; 30000028 <.text+0x28>

                        30000018 <on_sdram>:
                        30000018:    e3a0d30d     mov    sp, #872415232    ; 0x34000000
                        3000001c:    e59fe008     ldr    lr, [pc, #8]    ; 3000002c <.text+0x2c>
                        30000020:    e59ff008     ldr    pc, [pc, #8]    ; 30000030 <.text+0x30>

                        30000024 <halt_loop>:
                        30000024:    eafffffe     b    30000024 <halt_loop>
                        30000028:    30000018     andcc    r0, r0, r8, lsl r0
                        3000002c:    30000024     andcc    r0, r0, r4, lsr #32
                        30000030:    30000200     andcc    r0, r0, r0, lsl #4

  00000034      30000034 <disable_watch_dog>:   ...  ...

反汇编中可以看出当执行ldr pc, =on_sdram 时的反汇编是 ldr pc, [pc, #12] ; 相当于pc=*(pc+12)=30000018,此时的*(pc+12)是指的pc+12地址所指的地址,所以无论pc怎么变都是指的30000018这个常量来执行on_sdram,属于绝对转移

在这个例子中我认识到链接地址也是放到存储器中,但若运行地址为0,在存储器中程序也是从0000000000000034依次读取的吗,但这样存储是在不算入链接地址占用的存储空间。

就是说烧写程序中带有链接地址,但好像用PC读取指令是无视链接地址占用的存储空间,我不理解在运行程序时链接地址是如何使用的,比如指令执行的顺序:取址,译码,执行,一开始取址PC为00000000 该地址的机器码对应链接地址30000000处的机器码,很明显取址忽略了链接地址。但运行到执行ldr pc, =on_sdram 时,“*(pc+12)是指的pc+12地址所指的地址”又用到了链接地址。

那么问题来了,在运行地址和链接地址下如何解释指令的执行顺序,假设运行地址是00000000

考虑实际应用中,即链接脚本中指定的链接地址,是重定位使用,即在自己程序烧写完成后,先应该有一段引导程序,引导程序将烧写的程序重定位到链接地址处。

还是不对,通过这个作者(乱世半仙)的介绍,并没有引导程序,

PC(程序计数器)刚开始为地址00000000,该地址的机器码是不是这个呢?

第二个伪指令表示将标号_start的值给到寄存器r1,即立即数传值,此时_start标号的值为0x30000000,adr如何获得运行地址传给r0?

经过又一番挣扎,了解了literal pool (文字池),ARM汇编语言代码节中的文字池是什么-电子发烧友网

那为什么要使用文字池呢?当想要在一条指令中使用一个 4字节长度的常量数据(这个数据可能是内存地址,可能是数字常量)的时候,由于ARM指令集是定长的(ARM指令4字节或Thumb指令2字节),就无法把这个4字节的常量数据编码在一条编译后的指令中。此时,ARM编译器(编译C源程序)/汇编器(编译汇编程序) 就会在代码节中分配一块内存,并把这个4字节的数据常量保存起来,之后,再使用一条指令把这个4 字节的数字常量加载到寄存器中参与运算。 在写C程序中,文字池的分配是由编译器在编译时自行分配安排的,但是,在写汇编程序时,开发者可以自己进行文字池的分配,当然如果没有自己分配汇编器也会代劳。

比如:LDR R0, =0X12345678,会将0X12345678存入文字池

再去理解上面的加载运行地址和链接地址到寄存器,标号_start(0x30000000)代表的链接地址会存放到文字池中。

这样看来反汇编程序中显示的链接地址并不是都会烧写进去,烧写的只是标号的链接地址,且烧写的位置在一块独立内存(文字池)中,不和指令一起存放。

t通过对adr r0,_start和ldr r1,=_start两条汇编指令的理解我意识到链接地址是给汇编器看和用的,链接地址体现在标号和指令上,比如

adr  r0,_start:PC+偏移  偏移量是根据链接地址确定的即指令的链接地址和标号的链接地址,这一步应该在汇编器实现,汇编器用add/sub  PC  #off_set指令替换adr伪指令,

ldr r1,=_start :将标号_start的值即链接地址传给寄存器,标号的链接地址存放到了文字池。但我不太理解作者的这句话

“这里取得的是标号 _start 的绝对地址,这个绝对地址(链接地址)是在链接的时候确定的。它要占用 2 个 32bit的空间,一条是指令,另一条是文字池中存放_start 的绝对地址”

这里的 2 个 32bit的空间 如何理解呢

 由于ARM指令集是定长的(ARM指令4字节或Thumb指令2字节),就无法把这个4字节的常量数据编码在一条编译后的指令中。此时,ARM编译器(编译C源程序)/汇编器(编译汇编程序) 就会在代码节中分配一块内存,并把这个4字节的数据常量保存起来,之后,再使用一条指令把这个4 字节的数字常量加载到寄存器中参与运算。于是就需要2个32bit的空间了。

//2022-3-30//

经过这段时间的学习,对之前的困顿有了新的理解,对于伪指令ADR,通过汇编反汇编可以看到会用sub和add指令替换,即目标距离当前运行指令的offset,属于位置无关跳转指令;对于伪指令LDR Rn,=label;若label不能通过立即数表示,会在某一地址存放label,然后通过读内存指令读取label的值。

反汇编最左侧的链接地址并不会烧写进flash中,我觉得链接地址只是给程序提供了绝对地址的引用。

但又碰到了新的问题:

连接脚本中会有_bss_end=.这样的用法,然后C源文件通过&_bss_end实现对该符号值的引用,这里涉及了编译知识,好比定义一个全局整型变量int  a=123,假设a的地址为0x12345678;汇编代码中引用a的值应该是根据链接地址,比如main函数实现a=a-1,汇编代码应该这样实现,上电后先对data进行初始化,

LDR  r0 ,[12345678]

sub  r0,r0,#1;

STR r0,[12345678]

对符号_bss_end的引用在汇编代码中又是怎样的呢?

作者:喜欢吃猫的鱼

出处: http://www.cnblogs.com/lifexy/p/7117345.html

作者:宋桓公
出处:宋桓公 - 博客园

adr adrl ldr mov总结整理 - 宋桓公 - 博客园

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为你解答关于面向对象实现疑惑加解密的问题。 首先,我们需要确定加解密算法的类型。对于对称加密算法,常用的算法有DES、AES等。这些算法的实现都需要密钥,所以我们需要在类中定义一个私有成员变量来存储密钥。同时,我们还需要定义两个方法,一个用于加密,一个用于解密。 以AES算法为例,我们可以定义一个AesEncryptor类,其中包含一个私有成员变量key用于存储密钥,以及两个公有方法encrypt和decrypt。 ```python import hashlib from Crypto.Cipher import AES class AesEncryptor: def __init__(self, key): self.key = hashlib.sha256(key.encode()).digest() self.aes = AES.new(self.key, AES.MODE_ECB) def encrypt(self, plaintext): padding_size = 16 - len(plaintext) % 16 plaintext += bytes([padding_size] * padding_size) ciphertext = self.aes.encrypt(plaintext) return ciphertext def decrypt(self, ciphertext): plaintext = self.aes.decrypt(ciphertext) padding_size = plaintext[-1] plaintext = plaintext[:-padding_size] return plaintext ``` 在这个例子中,我们使用了hashlib模块来对密钥进行哈希,以确保密钥的安全性。同时,我们使用了pycryptodome模块中的AES类来实现加解密。encrypt方法用于加密明文,decrypt方法用于解密密文。 使用时,我们可以创建一个AesEncryptor对象,然后调用其encrypt和decrypt方法来加解密数据。 ```python encryptor = AesEncryptor('my secret key') ciphertext = encryptor.encrypt(b'hello world') print(ciphertext) plaintext = encryptor.decrypt(ciphertext) print(plaintext) ``` 希望这个例子能够帮助你更好地理解如何使用面向对象实现疑惑加解密。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值