全局變量反匯編與重定位



title: 全局變量反匯編與重定位
date: 2019/02/23 21:10:08
toc: true

全局變量反匯編與重定位

引入

本文完整代碼地址 https://gitee.com/layty/Jz2440/tree/master/Assembler/02-global_test

思考這樣一個問題,全局變量是怎么存儲的,怎么使用的,反匯編后是什么樣子的?

  1. 最理想的取地址應該是這樣的,但是並沒有這樣的指令,操作數不可能是一個32位的數

    ldr r0,[a的地址]
    
  2. 所以我們還想這樣,還需要 將 r1 賦值32位的地址..所以單純的賦值沒有辦法實現這個操作

    ldr r0,[r1]   
    
  3. 也就是需要這么做

    ldr r0,[pc,offset] ;lable1
    
    lable1:  addr_val
    
    label2_addr_val:  val 
    

    mark

我們寫這樣一段代碼測試一下.

#include <stdio.h>
int a=0;
int b=1;

int test(int c)
{
    c=c+1;
    printf("hello %d",c);
    return c;
}
typedef int (*funpt)(int);
funpt pt =&test;

int main()
{
    a=3;
    b=b+1;
    test(a);
    pt(b);
}

makefiel如下,反匯編中加入c語言信息,加入-S選項,cc中加入-g選項

all:
	arm-linux-gcc    -g  -o o.out globl_var.c -Wl,-Map=gcc.map
	arm-linux-objdump -D -S  o.out>o.dis
claen:
	rm *.out

nopie分析

一般我們編譯的時候都是nopie,pie我是在編譯uboot時候了解到的,本篇文章也是關於uboot重定位分析的子章節

MAP

我們先來看下map文件的地址,有助於后續的分析

.text 		0x00008380                test
.bss        0x000105f8                a
.data      	0x000105f0                pt
            0x000105ec                b

普通變量

main函數入手,先從[pc, #88]的地方取得一個值,再從這個值所表示的地址中取值

    a=3;
    83cc:	e59f2050 	ldr	r2, [pc, #80]	; 8424 <main+0x64>
    83d0:	e3a03003 	mov	r3, #3	; 0x3
    83d4:	e5823000 	str	r3, [r2]
    
    8424:	000105f8 	.word	0x000105f8
    8428:	000105ec 	.word	0x000105ec
    842c:	000105f0 	.word	0x000105f0  
    
000105ec <b>:
   105ec:	00000001 	.word	0x00000001

000105f0 <pt>:
   105f0:	00008380 	.word	0x00008380
Disassembly of section .bss:

000105f4 <completed.5877>:
   105f4:	00000000 	.word	0x00000000

000105f8 <a>:
   105f8:	00000000 	.word	0x00000000 

從代碼上可以看出來8424這個地方存儲的就是變量的地址,也就是這么一個示意圖

mark

可以看到a所表示的地址的值,確實是0,實際上這個是存在bss段的.

接着來看代碼b=b+1,多了一個取值的步驟

    b=b+1;
    83d8:	e59f3048 	ldr	r3, [pc, #72]	; 8428 <main+0x68>
    83dc:	e5933000 	ldr	r3, [r3]
    83e0:	e2832001 	add	r2, r3, #1	; 0x1
    83e4:	e59f303c 	ldr	r3, [pc, #60]	; 8428 <main+0x68>
    83e8:	e5832000 	str	r2, [r3]
    
    8424:	000105f8 	.word	0x000105f8
    8428:	000105ec 	.word	0x000105ec
    842c:	000105f0 	.word	0x000105f0 
    
    000105ec <b>:
    105ec:	00000001 	.word	0x00000001

指針變量

接着來看下函數指針pt的處理

    pt(b);
    ; 取出指針的地址,這個和我們普通變量操作中先取出變量的地址是一樣的
    83fc:	e59f3028 	ldr	r3, [pc, #40]	; 842c <main+0x6c>
    ; 獲取變量的值,也就是指針的值,這里就是test的地址值了
    ; 可以看到 [000105f0]=00008380  確實是 00008380 <test>:
    8400:	e5932000 	ldr	r2, [r3]
    ;取出變量b的地址 然后取出b的值
    8404:	e59f301c 	ldr	r3, [pc, #28]	; 8428 <main+0x68>
    8408:	e5933000 	ldr	r3, [r3]
    ; 傳遞為第一個參數
    840c:	e1a00003 	mov	r0, r3
    8410:	e1a0e00f 	mov	lr, pc
    ;r2在上面賦值就是test的值
    8414:	e12fff12 	bx	r2
    
    842c:	000105f0 	.word	0x000105f0
    000105f0 <pt>:
    105f0:	00008380 	.word	0x00008380
    00008380 <test>:

print函數

在函數中調用了庫函數,這里也是使用了相對跳轉了

printf("hello %d",c);
839c:	e59f0018 	ldr	r0, [pc, #24]	; 83bc <test+0x3c>
83a0:	e51b1008 	ldr	r1, [fp, #-8]
83a4:	ebffffc5 	bl	82c0 <_init+0x48>

pie分析

接下去的分析都是針對重定位uboot的,與一般的程序沒有關系了,不屬於全局變量反匯編的分析

MAP

.text 		0x00008380                test
.bss        0x000105f8                a
.data      	0x000105f0                pt
            0x000105ec                b
// 上面是nopie 下面是pie的
.text	0x000005f8                test
.data 	0x0000887c                pt
.data 	0x00008878                b
.bss  	0x00008884                a

匯編方式

加入pie選項和其實發現,單純的這段maintest代碼是一致的

mark

mark

地址分析

 69c:	00008884 	.word	0x00008884		;存放的a的地址的lable
 6a0:	00008878 	.word	0x00008878		;存放的b的地址的lable
 6a4:	0000887c 	.word	0x0000887c		;存放的pt的地址的lable

搜索這幾個地址值69c6a06a4

00000400 <.rel.dyn>:
 ;a
 420:	0000069c 	muleq	r0, ip, r6
 424:	00000017 	andeq	r0, r0, r7, lsl r0
 ;b
 428:	000006a0 	andeq	r0, r0, r0, lsr #13
 42c:	00000017 	andeq	r0, r0, r7, lsl r0
 ;pt
 430:	000006a4 	andeq	r0, r0, r4, lsr #13
 434:	00000017 	andeq	r0, r0, r7, lsl r0
 
 450:	0000887c 	andeq	r8, r0, ip, ror r8
 454:	00000017 	andeq	r0, r0, r7, lsl r0

同時,還有一個關鍵的東西,就是指針的值是個地址的時候,他的值也會出現在這個表里面

 450:	0000887c 	andeq	r8, r0, ip, ror r8
 454:	00000017 	andeq	r0, r0, r7, lsl r0

這是什么意思呢

當指針的值是個地址的時候,當重定位之后,這個地址應該同樣加上偏移.

uboot的pie

 .bss           0x000ae4e0        0x4 board/samsung/smdk2410/libsmdk2410.o
                0x000ae4e0                a
 .text          0x000560dc      0x240 board/samsung/smdk2410/libsmdk2410.o
                0x00056278                main2
                0x00056258                test
.data          0x0006adfc        0x8 board/samsung/smdk2410/libsmdk2410.o
                0x0006adfc                b
                0x0006ae00                pt

變量分析

00056278 <main2>:
   56278:	e92d4010 	push	{r4, lr}
   ;取出b的地址
   5627c:	e59f402c 	ldr	r4, [pc, #44]	; 562b0 <main2+0x38>
   		;r4=0006adfc是b的地址
   ;取出a的地址
   56280:	e59f302c 	ldr	r3, [pc, #44]	; 562b4 <main2+0x3c>
   56284:	e5942000 	ldr	r2, [r4]
   56288:	e3a01003 	mov	r1, #3	; 0x3
   5628c:	e2822001 	add	r2, r2, #1	; 0x1
   56290:	e1a00001 	mov	r0, r1
   56294:	e5831000 	str	r1, [r3]
   56298:	e5842000 	str	r2, [r4]
   5629c:	ebffffed 	bl	56258 <test>
   
   ; pt(b)
   562a0:	e5940000 	ldr	r0, [r4]
   562a4:	e1a0e00f 	mov	lr, pc
   ;到這里為止,r4依然是b的地址,也就是說 pt的地址就是b后面,pt的尋址都是按照b來的
   ;手動計算一下 r4+4=6ae00 這確實是pt的實際地址
   ;	0006ae00 <pt>:
   ;	6ae00:	00056258 	.word	0x00056258
   
   562a8:	e594f004 	ldr	pc, [r4, #4]
   562ac:	e8bd8010 	pop	{r4, pc}

我們來看下具體的地址信息

;----------------------------------------------------------------------   
   562b0:	0006adfc 	.word	0x0006adfc ;這個存的是b的地址
   562b4:	000ae4e0 	.word	0x000ae4e0 ;這個存的是a的地址
   ; 這里沒有lable存儲着pt的地址,程序依靠先找到b的地址,b地址下一個偏移就是pt的地址
   ; pt里面的值也是個地址
   0006ae00 <pt>:
   6ae00:	00056258 	.word	0x00056258

接下來的就是應該重定位的變量信息,也就是lable和地址,可以看到lable和指針都出現了這張表里面

	;存放b的lable的地址
   71854:	000562b0 	.word	0x000562b0
   71858:	00000017 	.word	0x00000017
   ;存放a的lable的地址
   7185c:	000562b4 	.word	0x000562b4
   71860:	00000017 	.word	0x00000017
   ;這里沒有存放pt的lable地址 ,依靠b的地址來尋找
   ;pt的值也是個地址值,那么這個地址也應該處理
   72c1c:	0006ae00 	.word	0x0006ae00
   72c20:	00000017 	.word	0x00000017

小結重定位數據段

也就是說,這個特殊的段<.rel.dyn>應該存放有所有值是地址的單元的地址.

反匯編文件可以看成數據段加指令段,這里考慮數據段

0000: ....
....
xxxx: val     當val表示的是一個地址的時候,現在有兩種情況

也就是說包含了

  • lable 的地址,存放的是全局變量的地址
  • 指針的地址,這個指針存放的是個地址

有了上面的<.rel.dyn>段重定位數據段就很方便了,將里面的地址挑出來,對其值和其存放的地址都加上偏移即可

  1. 取出a的地址,此時a的地址應該加上offset
  2. a的地址怎么取的,也是從一個lable中取的,這個lable也是經過offset的了
  3. 當a的值是個地址,比如是個函數指針,這個函數指針的值也應該加上offset,才是新定位的函數地址

mark

總結起來一句話

*(adr+offset)=*(adr+offset)+offset

參考文章

uboot的relocation原理詳細分析


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM