• 全局变量反汇编与重定位



    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原理详细分析

  • 相关阅读:
    c/c++ 网络编程 getaddrinfo 函数
    c/c++ 网络编程 bind函数
    c/c++ socket API 调用后的错误判断 perror errno
    python基础-面向对象编程之反射
    彻底理解Future模式
    Java并发编程:Callable、Future和FutureTask
    java异步调用方法
    理解ThreadLocal
    ReentrantReadWriteLock读写锁
    java锁优化
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/10424426.html
Copyright © 2020-2023  润新知