makefile:
test.bin: test arm-2440-linux-gnueabi-objcopy -I elf32-littlearm test -O binary test.bin cp test.bin ../../ test : test.o arm-2440-linux-gnueabi-ld -Ttext=0x30000000 test.o -o test test.o : test.S arm-2440-linux-gnueabi-as test.S -o test.o clean: rm -f test.o test test.bin ../../test.bin
test.S:
mov ip, sp stmfd sp!, {fp, ip, lr, pc} sub fp, ip, #4 ldr r0, =str mov lr, pc ldr pc, show sub sp, fp, #12 ldmfd sp, {fp, sp, pc} show: .word 0x33f9303c str: .asciz "1111111"
删除线chrome下竟然不能用 只能用框框了 下一个框框是上次错误的分析:
这段程序编译链接格式化后 不管下载到sdram的哪个位置都能打印出hello 为什么? 我们明明指定了text段的地址为30000000 而我们不管下载到30000000 或者 40000000 程序都打印出了相同的结果
这段程序编译链接格式化后 下载到40000000 运行出错 下载到30000000运行正确
原因是我们指定了text段地址为30000000
我们接着看一下:
[leftover-crazy@leftover-crazy 7nd]$ arm-2440-linux-gnueabi-objdump -d test.o -h test.o: file format elf32-littlearm Sections: Idx Name Size VMA LMA File off Algn 0 .text 00000030 00000000 00000000 00000034 2**2 CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE 1 .data 00000000 00000000 00000000 00000064 2**0 CONTENTS, ALLOC, LOAD, DATA 2 .bss 00000000 00000000 00000000 00000064 2**0 ALLOC 3 .ARM.attributes 00000014 00000000 00000000 00000064 2**0 CONTENTS, READONLY Disassembly of section .text: 00000000 <show-0x20>: 0: e1a0c00d mov ip, sp 4: e92dd800 push {fp, ip, lr, pc} 8: e24cb004 sub fp, ip, #4 ; 0x4 c: e59f0018 ldr r0, [pc, #24] ; 2c <str+0x8> 10: e1a0e00f mov lr, pc 14: e59ff004 ldr pc, [pc, #4] ; 20 <show> 18: e24bd00c sub sp, fp, #12 ; 0xc 1c: e89da800 ldm sp, {fp, sp, pc} 00000020 <show>: 20: 33f9303c .word 0x33f9303c 00000024 <str>: 24: 31313131 .word 0x31313131 28: 00313131 .word 0x00313131 2c: 00000024 .word 0x00000024 [leftover-crazy@leftover-crazy 7nd]$ arm-2440-linux-gnueabi-objdump -d test -h test: file format elf32-littlearm Sections: Idx Name Size VMA LMA File off Algn 0 .text 00000030 30000000 30000000 00008000 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .ARM.attributes 00000014 00000000 00000000 00008030 2**0 CONTENTS, READONLY Disassembly of section .text: 30000000 <show-0x20>: 30000000: e1a0c00d mov ip, sp 30000004: e92dd800 push {fp, ip, lr, pc} 30000008: e24cb004 sub fp, ip, #4 ; 0x4 3000000c: e59f0018 ldr r0, [pc, #24] ; 3000002c <str+0x8> 30000010: e1a0e00f mov lr, pc 30000014: e59ff004 ldr pc, [pc, #4] ; 30000020 <show> 30000018: e24bd00c sub sp, fp, #12 ; 0xc 3000001c: e89da800 ldm sp, {fp, sp, pc} 30000020 <show>: 30000020: 33f9303c .word 0x33f9303c 30000024 <str>: 30000024: 31313131 .word 0x31313131 30000028: 00313131 .word 0x00313131 3000002c: 30000024 .word 0x30000024
看看这里:
c: e59f0018 ldr r0, [pc, #24] ; 2c <str+0x8>
14: e59ff004 ldr pc, [pc, #4] ; 20 <show>
删除线chrome下竟然不能用 只能用框框了 下一个框框是上次错误的分析:
反汇编的结果 取址取值都是根据pc寄存器的内容加上偏移值得到 而不管我们程序下载到何处运行pc加偏移总能取到相同的数据 所以不管下载到哪里都是一样的运行结果 都能得到正确的结果
这是编译器优化的结果 这种代码就是与位置无关的代码
再来说说程序下载到内存时 各个段总是text段在前 data段在后的--- 固定的方式存放的
看看test 中并无data段 所有指令数据都是存放在text段中 是readonly的
顺序存储加上readonly 采用pc+偏移的形式寻址 决定了它就是与位置无关的代码
当然 这只是对这段程序的分析 而非对与位置无关代码下定义
以上分析出错了 原因是我开发板先下载程序到30000000 然后又下载到40000000 进行测试 得到一样的正确结果
刚刚仔细看了一下代码 发现自己在忽悠自己 哈哈 睁眼说瞎话了 这里
其实是将2c: 00000024 .word 0x00000024 这个地址所存放的数据读入r0中 所以这是一段与位置有关的代码 虽然pc加偏移与位置无关 但是pc加便宜读取到的数据却是与位置有关的
而我在测试的时候也犯了低级的错误 开发板没有重新上电 导致内存数据没被改写 uboot下md查看发现两个区域的数据是一样的 这也解释了为了结果正确
而当我将开发板重新上电 然后直接下载到40000000 的时候 程序执行出错了 没得到预期的结果 哎
之前的分析就不删除了 虽然有部分错误 但是仍然是值得以后留着提醒自己的
ldr r0, [pc, #24]
而我们再看看part8中的代码相反 是一段与位置有关的代码
我们看看它的反汇编结果:
[leftover-crazy@leftover-crazy 7nd]$ arm-2440-linux-gnueabi-objdump -d ../6nd/test -h ../6nd/test: file format elf32-littlearm Sections: Idx Name Size VMA LMA File off Algn 0 .text 000000c8 30000000 30000000 00008000 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .rodata 00000038 300000c8 300000c8 000080c8 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 2 .data 00000004 30008100 30008100 00008100 2**2 CONTENTS, ALLOC, LOAD, DATA 3 .comment 00000025 00000000 00000000 00008104 2**0 CONTENTS, READONLY 4 .ARM.attributes 00000026 00000000 00000000 00008129 2**0 CONTENTS, READONLY Disassembly of section .text: 30000000 <_start>: 30000000: e1a0c00d mov ip, sp 30000004: e92dd800 push {fp, ip, lr, pc} 30000008: e24cb004 sub fp, ip, #4 ; 0x4 3000000c: eb000002 bl 3000001c <test> 30000010: eb000019 bl 3000007c <test2> 30000014: e24bd00c sub sp, fp, #12 ; 0xc 30000018: e89da800 ldm sp, {fp, sp, pc} 3000001c <test>: 3000001c: e1a0c00d mov ip, sp 30000020: e92dd800 push {fp, ip, lr, pc} 30000024: e24cb004 sub fp, ip, #4 ; 0x4 30000028: e59f0048 ldr r0, [pc, #72] ; 30000078 <hello+0x1c> 3000002c: e1a0e00f mov lr, pc 30000030: e59ff020 ldr pc, [pc, #32] ; 30000058 <show> 30000034: e24bd00c sub sp, fp, #12 ; 0xc 30000038: e89da800 ldm sp, {fp, sp, pc} 3000003c <test3>: 3000003c: e1a0c00d mov ip, sp 30000040: e92dd800 push {fp, ip, lr, pc} 30000044: e24cb004 sub fp, ip, #4 ; 0x4 30000048: e1a0e00f mov lr, pc 3000004c: e59ff004 ldr pc, [pc, #4] ; 30000058 <show> 30000050: e24bd00c sub sp, fp, #12 ; 0xc 30000054: e89da800 ldm sp, {fp, sp, pc} 30000058 <show>: 30000058: 33f9303c .word 0x33f9303c 3000005c <hello>: 3000005c: 6c6c6568 .word 0x6c6c6568 30000060: 6275206f .word 0x6275206f 30000064: 20746f6f .word 0x20746f6f 30000068: 206d7361 .word 0x206d7361 3000006c: 6c6c6163 .word 0x6c6c6163 30000070: 6d736120 .word 0x6d736120 30000074: 000a2120 .word 0x000a2120 30000078: 3000005c .word 0x3000005c 3000007c <test2>: 3000007c: e1a0c00d mov ip, sp 30000080: e92dd800 push {fp, ip, lr, pc} 30000084: e24cb004 sub fp, ip, #4 ; 0x4 30000088: e24dd008 sub sp, sp, #8 ; 0x8 3000008c: e59f3028 ldr r3, [pc, #40] ; 300000bc <test2+0x40> 30000090: e50b3010 str r3, [fp, #-16] 30000094: e59f3024 ldr r3, [pc, #36] ; 300000c0 <test2+0x44> 30000098: e5933000 ldr r3, [r3] 3000009c: e59f0020 ldr r0, [pc, #32] ; 300000c4 <test2+0x48> 300000a0: e1a0e00f mov lr, pc 300000a4: e12fff13 bx r3 300000a8: e51b0010 ldr r0, [fp, #-16] 300000ac: ebffffe2 bl 3000003c <test3> 300000b0: e24bd00c sub sp, fp, #12 ; 0xc 300000b4: e89d6800 ldm sp, {fp, sp, lr} 300000b8: e12fff1e bx lr 300000bc: 300000c8 .word 0x300000c8 300000c0: 30008100 .word 0x30008100 300000c4: 300000e4 .word 0x300000e4
这里:
3000000c: eb000002 bl 3000001c <test>
30000010: eb000019 bl 3000007c <test2>
这个寻址的方式是立即数寻址,直接跳转到3000****处执行
而30000000是我们在链接的时候指定了text段的地址 elf映像又顺序存储了各个段 当程序执行到这两句指令的时候 如果我们把程序下载到指定的入口点 30000000 则3000001c 3000007c即存储了我们预设的数据 但是如果我们将程序下载到40000000 那么3000001c 3000007c读取到的数据将是一堆垃圾数据 也是不可预期的 这类代码即是与位置有关的代码 也就是说采用绝对地址读取数据的指令就会生成与位置有关的代码
不用多做对比也明白 与位置有关的代码在实际使用中约束更大 而如果是提供给操作系统调用的话 装载到内存的地址是由操作系统决定的 不能保证 也几乎不可能装载到链接时指定的位置执行
再来谈谈编写与位置无关代码的注意事项:
1.并不是说哪个指令就是与位置有关或者与位置无关的指令,我们编写的代码经过汇编后 编译器会自动优化 也可能不优化 所以无法从指令使用方面来判定一份代码是否为与位置有关或者无关的代码,那么如果判断呢? 其实只要objdump -d反汇编看看结果 分析一下就有80%的把握了 呵呵 当然这也不是百分百的肯定 最简单最实际的办法 还是调试 实践测试
2.一般调用c函数的代码都可能是与位置有关的代码 但不是绝对
3.尽量避免源文件写得太长 如果源文件太长的话 出现与位置有关的代码的可能性也会增加 4.应该注意ldr指令的使用 绝大部分ldr伪指令 可能产生与位置有关的代码 ldr 寄存器,=标签 这种指令尽量用adr代替 因为反汇编后会发现adr伪指令会被汇编成add指令