U-Boot会对自己重定向,对于ARM架构,它只支持R_ARM_RELATIVE重定向类型。编译可重定向目标文件时,使用-fno-pic和-mword-relocations选项。前者关闭pic,后者只产生32位绝对重定向。链接可执行文件时,使用-pie选项,产生位置无关可执行文件。
uboot-arm-reloc.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | int c = 1; int d() { return 1; } static int e = 1; static int f() { return 1; } /* global var and function */ int access_global_var() { return c; } int access_global_var_addr() { return ( int )&c; } int access_global_fun() { return d(); } int access_global_fun_ptr() { return ( int )d; } /* static var and function in this file */ int access_static_var() { return e; } int access_static_var_addr() { return ( int )&e; } int access_static_fun() { return f(); } int access_static_fun_ptr() { return ( int )f; } int _start() { return 0; } |
Makefile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | TOOLCHAIN_DIR := /work/toolchain/arm-unknown-linux-gnueabi TOOLCHAIN_PREFIX := arm-unknown-linux-gnueabi TOOLCHAIN_BIN_PREFIX := $(TOOLCHAIN_DIR)/bin/$(TOOLCHAIN_PREFIX) CC := $(TOOLCHAIN_BIN_PREFIX)-gcc READELF := $(TOOLCHAIN_BIN_PREFIX)-readelf OBJDUMP := $(TOOLCHAIN_BIN_PREFIX)-objdump CFLAGS := all: $(CC) -fno-pic -mword-relocations -c -o uboot-arm-reloc.o uboot-arm-reloc.c $(CC) -pie -nostdlib -o uboot-arm-reloc uboot-arm-reloc.o $(OBJDUMP) -d uboot-arm-reloc.o > obj.objdump $(OBJDUMP) -d uboot-arm-reloc > exe.objdump $(READELF) -a uboot-arm-reloc.o > obj.readelf $(READELF) -a uboot-arm-reloc > exe.readelf |
uboot-arm-reloc.o的elf信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .text PROGBITS 00000000 000034 00015c 00 AX 0 0 4 [ 2] .rel.text REL 00000000 0004f0 000090 08 I 9 1 4 [ 3] .data PROGBITS 00000000 000190 000008 00 WA 0 0 4 [ 4] .bss NOBITS 00000000 000198 000000 00 WA 0 0 1 [ 5] .comment PROGBITS 00000000 000198 00002f 01 MS 0 0 1 [ 6] .note.GNU-stack PROGBITS 00000000 0001c7 000000 00 0 0 1 [ 7] .ARM.attributes ARM_ATTRIBUTES 00000000 0001c7 00002f 00 0 0 1 [ 8] .shstrtab STRTAB 00000000 000580 000059 00 0 0 1 [ 9] .symtab SYMTAB 00000000 0001f8 000230 10 10 24 4 [10] .strtab STRTAB 00000000 000428 0000c6 00 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), L (link order), O (extra OS processing required), G (group), T (TLS), C (compressed), x (unknown), o (OS specific), E (exclude), y (noread), p (processor specific) There are no section groups in this file. There are no program headers in this file. Relocation section '.rel.text' at offset 0x4f0 contains 18 entries: Offset Info Type Sym.Value Sym. Name 00000018 00000028 R_ARM_V4BX 00000034 00000028 R_ARM_V4BX 00000054 00000028 R_ARM_V4BX 00000058 00001802 R_ARM_ABS32 00000000 c 00000074 00000028 R_ARM_V4BX 00000078 00001802 R_ARM_ABS32 00000000 c 00000084 0000191c R_ARM_CALL 00000000 d 00000098 00000028 R_ARM_V4BX 000000b4 00000028 R_ARM_V4BX 000000b8 00001902 R_ARM_ABS32 00000000 d 000000d8 00000028 R_ARM_V4BX 000000dc 00000302 R_ARM_ABS32 00000000 .data 000000f8 00000028 R_ARM_V4BX 000000fc 00000302 R_ARM_ABS32 00000000 .data 0000011c 00000028 R_ARM_V4BX 00000138 00000028 R_ARM_V4BX 0000013c 00000802 R_ARM_ABS32 0000001c f 00000158 00000028 R_ARM_V4BX |
R_ARM_V4BX的目的是标记BX指令。armv4不支持BX指令,这样在armv4上,使用其它指令替换。除R_ARM_V4BX之外,上述存在两种重定向,R_ARM_ABS32和R_ARM_CALL。下面根据反汇编来分析。
汇编代码
1 2 3 4 5 6 7 8 9 10 | 00000038 <access_global_var>: 38: e52db004 push {fp} ; (str fp, [sp, #-4]!) 3c: e28db000 add fp, sp, #0 40: e59f3010 ldr r3, [pc, #16] ; 58 <access_global_var+0x20> 44: e5933000 ldr r3, [r3] 48: e1a00003 mov r0, r3 4c: e28bd000 add sp, fp, #0 50: e49db004 pop {fp} ; (ldr fp, [sp], #4) 54: e12fff1e bx lr 58: 00000000 .word 0x00000000 |
重定向条目:
1 | 00000058 00001802 R_ARM_ABS32 00000000 c |
可以看到,将c的地址存储到函数之后,使用pc相对位置,将其地址载入到r3,然后将内容载入到r3,再将r3复制到r0。
和访问全局变量的值类似,只是少了取内容的指令。
汇编代码
1 2 3 4 5 6 7 8 9 | 0000007c <access_global_fun>: 7c: e92d4800 push {fp, lr} 80: e28db004 add fp, sp, #4 84: ebfffffe bl 0 <d> 88: e1a03000 mov r3, r0 8c: e1a00003 mov r0, r3 90: e24bd004 sub sp, fp, #4 94: e8bd4800 pop {fp, lr} 98: e12fff1e bx lr |
重定向条目
1 | 00000084 0000191c R_ARM_CALL 00000000 d |
根据ARM ELF,R_ARM_CALL的定义如下:
28 R_ARM_CALL Static ARM ((S + A) | T) – P
可见这个一个相对PC的重定向。
和访问全局变量的地址一样。
汇编代码
1 2 3 4 5 6 7 8 9 10 | 000000bc <access_static_var>: bc: e52db004 push {fp} ; (str fp, [sp, #-4]!) c0: e28db000 add fp, sp, #0 c4: e59f3010 ldr r3, [pc, #16] ; dc <access_static_var+0x20> c8: e5933000 ldr r3, [r3] cc: e1a00003 mov r0, r3 d0: e28bd000 add sp, fp, #0 d4: e49db004 pop {fp} ; (ldr fp, [sp], #4) d8: e12fff1e bx lr dc: 00000004 .word 0x00000004 |
重定向条目
1 | 000000dc 00000302 R_ARM_ABS32 00000000 .data |
套路和x86是一样的,访问静态变量,重定向的符号是.data section,静态变量在.data中的偏移作为加数存储到要重定向的内容中。通过S+A就得到了静态变量的地址。
重定向方式和访问静态变量类似,只不过少了取值指令。
无需重定向,在.text内部跳转。
汇编代码
1 2 3 4 5 6 7 8 9 | 00000120 <access_static_fun_ptr>: 120: e52db004 push {fp} ; (str fp, [sp, #-4]!) 124: e28db000 add fp, sp, #0 128: e59f300c ldr r3, [pc, #12] ; 13c <access_static_fun_ptr+0x1c> 12c: e1a00003 mov r0, r3 130: e28bd000 add sp, fp, #0 134: e49db004 pop {fp} ; (ldr fp, [sp], #4) 138: e12fff1e bx lr 13c: 00000000 .word 0x00000000 |
重定向条目
1 | 0000013c 00000802 R_ARM_ABS32 0000001c f |
可见,直接重定向为符号f的绝对地址,没有通过.text加偏移的方式。
就是按照目标文件中的重定向内容进行重定向,如访问全局变量:
1 2 3 4 5 6 7 8 9 10 | 000001bc <access_global_var>: 1bc: e52db004 push {fp} ; (str fp, [sp, #-4]!) 1c0: e28db000 add fp, sp, #0 1c4: e59f3010 ldr r3, [pc, #16] ; 1dc <access_global_var+0x20> 1c8: e5933000 ldr r3, [r3] 1cc: e1a00003 mov r0, r3 1d0: e28bd000 add sp, fp, #0 1d4: e49db004 pop {fp} ; (ldr fp, [sp], #4) 1d8: e12fff1e bx lr 1dc: 00010374 .word 0x00010374 |
重定向为:0x10374,再看c的符号表条目:
1 | 41: 00010374 4 OBJECT GLOBAL DEFAULT 9 c |
但是添加了.rel.dyn section,这个section包含了所有的目标文件中的绝对重定向:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .interp PROGBITS 000000f4 0000f4 000013 00 A 0 0 1 [ 2] .hash HASH 00000108 000108 000018 04 A 3 0 4 [ 3] .dynsym DYNSYM 00000120 000120 000030 10 A 4 3 4 [ 4] .dynstr STRTAB 00000150 000150 000001 00 A 0 0 1 [ 5] .rel.dyn REL 00000154 000154 000030 08 A 3 0 4 [ 6] .text PROGBITS 00000184 000184 00015c 00 AX 0 0 4 [ 7] .dynamic DYNAMIC 000102e0 0002e0 000088 08 WA 4 0 4 [ 8] .got PROGBITS 00010368 000368 00000c 04 WA 0 0 4 [ 9] .data PROGBITS 00010374 000374 000008 00 WA 0 0 4 [10] .comment PROGBITS 00000000 00037c 00002e 01 MS 0 0 1 [11] .ARM.attributes ARM_ATTRIBUTES 00000000 0003aa 00002f 00 0 0 1 [12] .shstrtab STRTAB 00000000 000814 000075 00 0 0 1 [13] .symtab SYMTAB 00000000 0003dc 000320 10 14 32 4 [14] .strtab STRTAB 00000000 0006fc 000118 00 0 0 1 Relocation section '.rel.dyn' at offset 0x154 contains 6 entries: Offset Info Type Sym.Value Sym. Name 000001dc 00000017 R_ARM_RELATIVE 000001fc 00000017 R_ARM_RELATIVE 0000023c 00000017 R_ARM_RELATIVE 00000260 00000017 R_ARM_RELATIVE 00000280 00000017 R_ARM_RELATIVE 000002c0 00000017 R_ARM_RELATIVE |
根据ARM ELF手册:
23 R_ARM_RELATIVE Dynamic Data B(S) + A
重定向为新的基准地址加加数,因为原来的基准地址为0。
注意:在可执行文件中,R_ARM_CALL不需要重定向,因为它使用PC相对位置访问,该相对位置在链接阶段即可确定。
参考:
ARM IHI 0044F, current through ABI release 2.10. ELF for the ARM ® Architecture. 24 November 2015.