ELF文件分析之2 - 静态链接

0 概述

有了上面两篇文章的铺垫,再看一遍ELF1.1规范,就已经简单的入门了
这篇文章,主要讲静态链接中的符号重定向过程和ELF处理机制

2 变量内存分布

我们知道,C语言的变量按作用域来分,包括:

  1. 全局作用域变量
    未加static修饰的变量,这种变量会标记为PUBLIC,并对外可见
  2. 文件作用域变量
    static修饰的变量,这种变量会标记为LOCAK,对外不可见
  3. 局部作用域变量
    block里面的变量
    什么是block呢,就是包含在{}里面的,未被static修饰的变量,这种变量会放在栈中,没有任何标记,对外不可见

也许你会问,C语言中有很多类型的变量,比如int,char,enum等,你为什么只提变量,而不提具体类型?
这是因为,变量类型仅仅对高级语言有效,机器码里面并没有数据类型,CPU只认识特定宽度的数据,然后做基本运算。
真的,在ELF里面,只有OBJECT,FUNC两种类型符号,我们来尝试一下

首先,查看一下a.out文件的符号表

1
root@raspberrypi:/home/pi/tmp# readelf -s a.out

Symbol table '.dynsym' contains 5 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     2: 00000000     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.4 (2)
     3: 00000000     0 FUNC    GLOBAL DEFAULT  UND abort@GLIBC_2.4 (2)
     4: 00000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.4 (2)

Symbol table '.symtab' contains 122 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00010134     0 SECTION LOCAL  DEFAULT    1 
     2: 00010150     0 SECTION LOCAL  DEFAULT    2 
     3: 00010170     0 SECTION LOCAL  DEFAULT    3 
     4: 00010194     0 SECTION LOCAL  DEFAULT    4 
     5: 000101c0     0 SECTION LOCAL  DEFAULT    5 
     6: 00010210     0 SECTION LOCAL  DEFAULT    6 
     7: 00010254     0 SECTION LOCAL  DEFAULT    7 
     8: 00010260     0 SECTION LOCAL  DEFAULT    8 
     9: 00010280     0 SECTION LOCAL  DEFAULT    9 
    10: 00010288     0 SECTION LOCAL  DEFAULT   10 
    11: 000102a8     0 SECTION LOCAL  DEFAULT   11 
    12: 000102b4     0 SECTION LOCAL  DEFAULT   12 
    13: 000102f8     0 SECTION LOCAL  DEFAULT   13 
    14: 00010500     0 SECTION LOCAL  DEFAULT   14 
    15: 00010508     0 SECTION LOCAL  DEFAULT   15 
    16: 00010518     0 SECTION LOCAL  DEFAULT   16 
    17: 00010520     0 SECTION LOCAL  DEFAULT   17 
    18: 00020524     0 SECTION LOCAL  DEFAULT   18 
    19: 00020528     0 SECTION LOCAL  DEFAULT   19 
    20: 0002052c     0 SECTION LOCAL  DEFAULT   20 
    21: 00020530     0 SECTION LOCAL  DEFAULT   21 
    22: 00020618     0 SECTION LOCAL  DEFAULT   22 
    23: 00020638     0 SECTION LOCAL  DEFAULT   23 
    24: 00020640     0 SECTION LOCAL  DEFAULT   24 
    25: 00000000     0 SECTION LOCAL  DEFAULT   25 
    26: 00000000     0 SECTION LOCAL  DEFAULT   26 
    27: 00000000     0 SECTION LOCAL  DEFAULT   27 
    28: 00000000     0 SECTION LOCAL  DEFAULT   28 
    29: 00000000     0 SECTION LOCAL  DEFAULT   29 
    30: 00000000     0 SECTION LOCAL  DEFAULT   30 
    31: 00000000     0 SECTION LOCAL  DEFAULT   31 
    32: 00000000     0 SECTION LOCAL  DEFAULT   32 
    33: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/lib/gcc/arm-linux-gn
    34: 00010150     0 NOTYPE  LOCAL  DEFAULT    2 $d
    35: 000102f8     0 NOTYPE  LOCAL  DEFAULT   13 $a
    36: 00010518     0 NOTYPE  LOCAL  DEFAULT   16 $d
    37: 00010328     0 NOTYPE  LOCAL  DEFAULT   13 $d
    38: 00010508     0 NOTYPE  LOCAL  DEFAULT   15 $d
    39: 00020638     0 NOTYPE  LOCAL  DEFAULT   23 $d
    40: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/lib/gcc/arm-linux-gn
    41: 00010334     0 NOTYPE  LOCAL  DEFAULT   13 $a
    42: 00010334     0 FUNC    LOCAL  DEFAULT   13 call_weak_fn
    43: 00010350     0 NOTYPE  LOCAL  DEFAULT   13 $d
    44: 000102a8     0 NOTYPE  LOCAL  DEFAULT   11 $a
    45: 00010500     0 NOTYPE  LOCAL  DEFAULT   14 $a
    46: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/lib/gcc/arm-linux-gn
    47: 000102b0     0 NOTYPE  LOCAL  DEFAULT   11 $a
    48: 00010504     0 NOTYPE  LOCAL  DEFAULT   14 $a
    49: 00000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    50: 0002052c     0 OBJECT  LOCAL  DEFAULT   20 __JCR_LIST__
    51: 00010358     0 NOTYPE  LOCAL  DEFAULT   13 $a
    52: 00010358     0 FUNC    LOCAL  DEFAULT   13 deregister_tm_clones
    53: 0001037c     0 NOTYPE  LOCAL  DEFAULT   13 $d
    54: 00010388     0 NOTYPE  LOCAL  DEFAULT   13 $a
    55: 00010388     0 FUNC    LOCAL  DEFAULT   13 register_tm_clones
    56: 000103b4     0 NOTYPE  LOCAL  DEFAULT   13 $d
    57: 0002063c     0 NOTYPE  LOCAL  DEFAULT   23 $d
    58: 000103c0     0 NOTYPE  LOCAL  DEFAULT   13 $a
    59: 000103c0     0 FUNC    LOCAL  DEFAULT   13 __do_global_dtors_aux
    60: 000103e4     0 NOTYPE  LOCAL  DEFAULT   13 $d
    61: 00020640     1 OBJECT  LOCAL  DEFAULT   24 completed.9004
    62: 00020528     0 NOTYPE  LOCAL  DEFAULT   19 $d
    63: 00020528     0 OBJECT  LOCAL  DEFAULT   19 __do_global_dtors_aux_fin
    64: 000103e8     0 NOTYPE  LOCAL  DEFAULT   13 $a
    65: 000103e8     0 FUNC    LOCAL  DEFAULT   13 frame_dummy
    66: 00010418     0 NOTYPE  LOCAL  DEFAULT   13 $d
    67: 00020524     0 NOTYPE  LOCAL  DEFAULT   18 $d
    68: 00020524     0 OBJECT  LOCAL  DEFAULT   18 __frame_dummy_init_array_
    69: 00020640     0 NOTYPE  LOCAL  DEFAULT   24 $d
    70: 00000000     0 FILE    LOCAL  DEFAULT  ABS main.c
    71: 0001050c     0 NOTYPE  LOCAL  DEFAULT   15 $d
    72: 00010420     0 NOTYPE  LOCAL  DEFAULT   13 $a
    73: 00010460     0 NOTYPE  LOCAL  DEFAULT   13 $d
    74: 00000010     0 NOTYPE  LOCAL  DEFAULT   31 $d
    75: 00000000     0 FILE    LOCAL  DEFAULT  ABS fun.c
    76: 00010468     0 NOTYPE  LOCAL  DEFAULT   13 $a
    77: 0000003c     0 NOTYPE  LOCAL  DEFAULT   31 $d
    78: 00000000     0 FILE    LOCAL  DEFAULT  ABS elf-init.oS
    79: 00010498     0 NOTYPE  LOCAL  DEFAULT   13 $a
    80: 000104f4     0 NOTYPE  LOCAL  DEFAULT   13 $d
    81: 000104fc     0 NOTYPE  LOCAL  DEFAULT   13 $a
    82: 00000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    83: 00010520     0 NOTYPE  LOCAL  DEFAULT   17 $d
    84: 00010520     0 OBJECT  LOCAL  DEFAULT   17 __FRAME_END__
    85: 0002052c     0 NOTYPE  LOCAL  DEFAULT   20 $d
    86: 0002052c     0 OBJECT  LOCAL  DEFAULT   20 __JCR_END__
    87: 00000000     0 FILE    LOCAL  DEFAULT  ABS 
    88: 00020528     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_end
    89: 00020530     0 OBJECT  LOCAL  DEFAULT   21 _DYNAMIC
    90: 00020524     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_start
    91: 00020618     0 OBJECT  LOCAL  DEFAULT   22 _GLOBAL_OFFSET_TABLE_
    92: 000102b4     0 NOTYPE  LOCAL  DEFAULT   12 $a
    93: 000102c4     0 NOTYPE  LOCAL  DEFAULT   12 $d
    94: 000102c8     0 NOTYPE  LOCAL  DEFAULT   12 $a
    95: 000104fc     4 FUNC    GLOBAL DEFAULT   13 __libc_csu_fini
    96: 00000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
    97: 00020638     0 NOTYPE  WEAK   DEFAULT   23 data_start
    98: 00000000     0 FUNC    GLOBAL DEFAULT  UND printf@@GLIBC_2.4
    99: 00020640     0 NOTYPE  GLOBAL DEFAULT   24 __bss_start__
   100: 00020648     0 NOTYPE  GLOBAL DEFAULT   24 _bss_end__
   101: 00020640     0 NOTYPE  GLOBAL DEFAULT   23 _edata
   102: 00010500     0 FUNC    GLOBAL DEFAULT   14 _fini
   103: 00020648     0 NOTYPE  GLOBAL DEFAULT   24 __bss_end__
   104: 00020638     0 NOTYPE  GLOBAL DEFAULT   23 __data_start
   105: 00010468    48 FUNC    GLOBAL DEFAULT   13 f_add
   106: 00000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_
   107: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
   108: 0002063c     0 OBJECT  GLOBAL HIDDEN    23 __dso_handle
   109: 00010508     4 OBJECT  GLOBAL DEFAULT   15 _IO_stdin_used
   110: 00010498   100 FUNC    GLOBAL DEFAULT   13 __libc_csu_init
   111: 00020648     0 NOTYPE  GLOBAL DEFAULT   24 _end
   112: 000102f8     0 FUNC    GLOBAL DEFAULT   13 _start
   113: 00020648     0 NOTYPE  GLOBAL DEFAULT   24 __end__
   114: 00020644     4 OBJECT  GLOBAL DEFAULT   24 m                      # 0 变量符号
   115: 00020640     0 NOTYPE  GLOBAL DEFAULT   24 __bss_start
   116: 00010420    72 FUNC    GLOBAL DEFAULT   13 main                   # 1 函数符号
   117: 00000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
   118: 00020640     0 OBJECT  GLOBAL HIDDEN    23 __TMC_END__
   119: 00000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
   120: 00000000     0 FUNC    GLOBAL DEFAULT  UND abort@@GLIBC_2.4
   121: 000102a8     0 FUNC    GLOBAL DEFAULT   11 _init

我们看到symtab里面的type一栏,有FUNCOBJECTFILE等类型,同时,可以看到我们源代码里面的m被标记为OBJECT,main函数被标记为FUNC类型,GLOBAL属性。

为了验证上面说的理论,现在尝试在main.c中,添加char, unsigned char, struct 类型的变量,修改后的main.c如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>

extern int m;
extern int f_add(int a, int b);

char m_char = 1;
unsigned char m_uchar = 1;

struct
{
int a;
int b;
} m_struct =
{
1,
2,
};

int main(void)
{

m = f_add(1, 2);
printf("sum = %d\r\n", m);
return 0;
}

重新编译,读取符号,如下所示

1
root@raspberrypi:/home/pi/tmp# gcc -c -g -O0 main.c
root@raspberrypi:/home/pi/tmp# gcc main.o func.o
gcc: error: func.o: No such file or directory
root@raspberrypi:/home/pi/tmp# gcc main.o fun.o
root@raspberrypi:/home/pi/tmp# readelf -s a.out 

Symbol table '.dynsym' contains 5 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     2: 00000000     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.4 (2)
     3: 00000000     0 FUNC    GLOBAL DEFAULT  UND abort@GLIBC_2.4 (2)
     4: 00000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.4 (2)

Symbol table '.symtab' contains 126 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00010134     0 SECTION LOCAL  DEFAULT    1 
     2: 00010150     0 SECTION LOCAL  DEFAULT    2 
     3: 00010170     0 SECTION LOCAL  DEFAULT    3 
     4: 00010194     0 SECTION LOCAL  DEFAULT    4 
     5: 000101c0     0 SECTION LOCAL  DEFAULT    5 
     6: 00010210     0 SECTION LOCAL  DEFAULT    6 
     7: 00010254     0 SECTION LOCAL  DEFAULT    7 
     8: 00010260     0 SECTION LOCAL  DEFAULT    8 
     9: 00010280     0 SECTION LOCAL  DEFAULT    9 
    10: 00010288     0 SECTION LOCAL  DEFAULT   10 
    11: 000102a8     0 SECTION LOCAL  DEFAULT   11 
    12: 000102b4     0 SECTION LOCAL  DEFAULT   12 
    13: 000102f8     0 SECTION LOCAL  DEFAULT   13 
    14: 00010500     0 SECTION LOCAL  DEFAULT   14 
    15: 00010508     0 SECTION LOCAL  DEFAULT   15 
    16: 00010518     0 SECTION LOCAL  DEFAULT   16 
    17: 00010520     0 SECTION LOCAL  DEFAULT   17 
    18: 00020524     0 SECTION LOCAL  DEFAULT   18 
    19: 00020528     0 SECTION LOCAL  DEFAULT   19 
    20: 0002052c     0 SECTION LOCAL  DEFAULT   20 
    21: 00020530     0 SECTION LOCAL  DEFAULT   21 
    22: 00020618     0 SECTION LOCAL  DEFAULT   22 
    23: 00020638     0 SECTION LOCAL  DEFAULT   23 
    24: 0002064c     0 SECTION LOCAL  DEFAULT   24 
    25: 00000000     0 SECTION LOCAL  DEFAULT   25 
    26: 00000000     0 SECTION LOCAL  DEFAULT   26 
    27: 00000000     0 SECTION LOCAL  DEFAULT   27 
    28: 00000000     0 SECTION LOCAL  DEFAULT   28 
    29: 00000000     0 SECTION LOCAL  DEFAULT   29 
    30: 00000000     0 SECTION LOCAL  DEFAULT   30 
    31: 00000000     0 SECTION LOCAL  DEFAULT   31 
    32: 00000000     0 SECTION LOCAL  DEFAULT   32 
    33: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/lib/gcc/arm-linux-gn
    34: 00010150     0 NOTYPE  LOCAL  DEFAULT    2 $d
    35: 000102f8     0 NOTYPE  LOCAL  DEFAULT   13 $a
    36: 00010518     0 NOTYPE  LOCAL  DEFAULT   16 $d
    37: 00010328     0 NOTYPE  LOCAL  DEFAULT   13 $d
    38: 00010508     0 NOTYPE  LOCAL  DEFAULT   15 $d
    39: 00020638     0 NOTYPE  LOCAL  DEFAULT   23 $d
    40: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/lib/gcc/arm-linux-gn
    41: 00010334     0 NOTYPE  LOCAL  DEFAULT   13 $a
    42: 00010334     0 FUNC    LOCAL  DEFAULT   13 call_weak_fn
    43: 00010350     0 NOTYPE  LOCAL  DEFAULT   13 $d
    44: 000102a8     0 NOTYPE  LOCAL  DEFAULT   11 $a
    45: 00010500     0 NOTYPE  LOCAL  DEFAULT   14 $a
    46: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/lib/gcc/arm-linux-gn
    47: 000102b0     0 NOTYPE  LOCAL  DEFAULT   11 $a
    48: 00010504     0 NOTYPE  LOCAL  DEFAULT   14 $a
    49: 00000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    50: 0002052c     0 OBJECT  LOCAL  DEFAULT   20 __JCR_LIST__
    51: 00010358     0 NOTYPE  LOCAL  DEFAULT   13 $a
    52: 00010358     0 FUNC    LOCAL  DEFAULT   13 deregister_tm_clones
    53: 0001037c     0 NOTYPE  LOCAL  DEFAULT   13 $d
    54: 00010388     0 NOTYPE  LOCAL  DEFAULT   13 $a
    55: 00010388     0 FUNC    LOCAL  DEFAULT   13 register_tm_clones
    56: 000103b4     0 NOTYPE  LOCAL  DEFAULT   13 $d
    57: 0002063c     0 NOTYPE  LOCAL  DEFAULT   23 $d
    58: 000103c0     0 NOTYPE  LOCAL  DEFAULT   13 $a
    59: 000103c0     0 FUNC    LOCAL  DEFAULT   13 __do_global_dtors_aux
    60: 000103e4     0 NOTYPE  LOCAL  DEFAULT   13 $d
    61: 0002064c     1 OBJECT  LOCAL  DEFAULT   24 completed.9004
    62: 00020528     0 NOTYPE  LOCAL  DEFAULT   19 $d
    63: 00020528     0 OBJECT  LOCAL  DEFAULT   19 __do_global_dtors_aux_fin
    64: 000103e8     0 NOTYPE  LOCAL  DEFAULT   13 $a
    65: 000103e8     0 FUNC    LOCAL  DEFAULT   13 frame_dummy
    66: 00010418     0 NOTYPE  LOCAL  DEFAULT   13 $d
    67: 00020524     0 NOTYPE  LOCAL  DEFAULT   18 $d
    68: 00020524     0 OBJECT  LOCAL  DEFAULT   18 __frame_dummy_init_array_
    69: 0002064c     0 NOTYPE  LOCAL  DEFAULT   24 $d
    70: 00000000     0 FILE    LOCAL  DEFAULT  ABS main.c
    71: 00020642     0 NOTYPE  LOCAL  DEFAULT   23 $d
    72: 0001050c     0 NOTYPE  LOCAL  DEFAULT   15 $d
    73: 00010420     0 NOTYPE  LOCAL  DEFAULT   13 $a
    74: 00010460     0 NOTYPE  LOCAL  DEFAULT   13 $d
    75: 00000010     0 NOTYPE  LOCAL  DEFAULT   31 $d
    76: 00000000     0 FILE    LOCAL  DEFAULT  ABS fun.c
    77: 00010468     0 NOTYPE  LOCAL  DEFAULT   13 $a
    78: 0000003c     0 NOTYPE  LOCAL  DEFAULT   31 $d
    79: 00000000     0 FILE    LOCAL  DEFAULT  ABS elf-init.oS
    80: 00010498     0 NOTYPE  LOCAL  DEFAULT   13 $a
    81: 000104f4     0 NOTYPE  LOCAL  DEFAULT   13 $d
    82: 000104fc     0 NOTYPE  LOCAL  DEFAULT   13 $a
    83: 00000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    84: 00010520     0 NOTYPE  LOCAL  DEFAULT   17 $d
    85: 00010520     0 OBJECT  LOCAL  DEFAULT   17 __FRAME_END__
    86: 0002052c     0 NOTYPE  LOCAL  DEFAULT   20 $d
    87: 0002052c     0 OBJECT  LOCAL  DEFAULT   20 __JCR_END__
    88: 00000000     0 FILE    LOCAL  DEFAULT  ABS 
    89: 00020528     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_end
    90: 00020530     0 OBJECT  LOCAL  DEFAULT   21 _DYNAMIC
    91: 00020524     0 NOTYPE  LOCAL  DEFAULT   18 __init_array_start
    92: 00020618     0 OBJECT  LOCAL  DEFAULT   22 _GLOBAL_OFFSET_TABLE_
    93: 000102b4     0 NOTYPE  LOCAL  DEFAULT   12 $a
    94: 000102c4     0 NOTYPE  LOCAL  DEFAULT   12 $d
    95: 000102c8     0 NOTYPE  LOCAL  DEFAULT   12 $a
    96: 000104fc     4 FUNC    GLOBAL DEFAULT   13 __libc_csu_fini
    97: 00000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
    98: 00020638     0 NOTYPE  WEAK   DEFAULT   23 data_start
    99: 00000000     0 FUNC    GLOBAL DEFAULT  UND printf@@GLIBC_2.4
   100: 0002064c     0 NOTYPE  GLOBAL DEFAULT   24 __bss_start__
   101: 00020654     0 NOTYPE  GLOBAL DEFAULT   24 _bss_end__
   102: 0002064c     0 NOTYPE  GLOBAL DEFAULT   23 _edata
   103: 00010500     0 FUNC    GLOBAL DEFAULT   14 _fini
   104: 00020654     0 NOTYPE  GLOBAL DEFAULT   24 __bss_end__
   105: 00020638     0 NOTYPE  GLOBAL DEFAULT   23 __data_start
   106: 00010468    48 FUNC    GLOBAL DEFAULT   13 f_add
   107: 00000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_
   108: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
   109: 0002063c     0 OBJECT  GLOBAL HIDDEN    23 __dso_handle
   110: 00020640     1 OBJECT  GLOBAL DEFAULT   23 m_char                    # 0 变量符号
   111: 00010508     4 OBJECT  GLOBAL DEFAULT   15 _IO_stdin_used
   112: 00010498   100 FUNC    GLOBAL DEFAULT   13 __libc_csu_init
   113: 00020654     0 NOTYPE  GLOBAL DEFAULT   24 _end
   114: 00020641     1 OBJECT  GLOBAL DEFAULT   23 m_uchar                   # 1 变量符号
   115: 000102f8     0 FUNC    GLOBAL DEFAULT   13 _start
   116: 00020654     0 NOTYPE  GLOBAL DEFAULT   24 __end__
   117: 00020650     4 OBJECT  GLOBAL DEFAULT   24 m                         # 2 变量符号
   118: 0002064c     0 NOTYPE  GLOBAL DEFAULT   24 __bss_start
   119: 00010420    72 FUNC    GLOBAL DEFAULT   13 main
   120: 00020644     8 OBJECT  GLOBAL DEFAULT   23 m_struct                  # 3 变量符号
   121: 00000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
   122: 0002064c     0 OBJECT  GLOBAL HIDDEN    23 __TMC_END__
   123: 00000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
   124: 00000000     0 FUNC    GLOBAL DEFAULT  UND abort@@GLIBC_2.4
   125: 000102a8     0 FUNC    GLOBAL DEFAULT   11 _init

我们可以看到,几种类型的变量,对于ELF来说,除了size不同外,没什么差别
如果我们将几个变量前添加static修饰,则可以看到变量范围作用变成了LOCAL

1
71: 00020651     1 OBJECT  LOCAL  DEFAULT   23 m_uchar

而局部变量,则分配在栈里面,完全不会出现在符号表中

看完了变量类型和符号表,我们再看变量分配到了哪里

先反汇编main.o文件,如下所示

1
root@raspberrypi:/home/pi/tmp# objdump -D main.o

...
Disassembly of section .data:

00000000 <m_char>:
   0:	00000101 	andeq	r0, r0, r1, lsl #2

00000001 <m_uchar>:
   1:	01000001 	tsteq	r0, r1

00000004 <m_struct>:
   4:	00000001 	andeq	r0, r0, r1
   8:	00000002 	andeq	r0, r0, r2
...

然后看main.o符号表

1
root@raspberrypi:/home/pi/tmp# readelf -s main.o

Symbol table '.symtab' contains 27 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 FILE    LOCAL  DEFAULT  ABS main.c
     2: 00000000     0 SECTION LOCAL  DEFAULT    1 
     3: 00000000     0 SECTION LOCAL  DEFAULT    3 
     4: 00000000     0 SECTION LOCAL  DEFAULT    4 
     5: 00000001     1 OBJECT  LOCAL  DEFAULT    3 m_uchar
     6: 00000002     0 NOTYPE  LOCAL  DEFAULT    3 $d
     7: 00000000     0 SECTION LOCAL  DEFAULT    5 
     8: 00000000     0 NOTYPE  LOCAL  DEFAULT    5 $d
     9: 00000000     0 NOTYPE  LOCAL  DEFAULT    1 $a
    10: 00000050     0 NOTYPE  LOCAL  DEFAULT    1 $d
    11: 00000000     0 SECTION LOCAL  DEFAULT    6 
    12: 00000000     0 SECTION LOCAL  DEFAULT    8 
    13: 00000000     0 SECTION LOCAL  DEFAULT    9 
    14: 00000000     0 SECTION LOCAL  DEFAULT   11 
    15: 00000000     0 SECTION LOCAL  DEFAULT   13 
    16: 00000000     0 SECTION LOCAL  DEFAULT   15 
    17: 00000010     0 NOTYPE  LOCAL  DEFAULT   16 $d
    18: 00000000     0 SECTION LOCAL  DEFAULT   16 
    19: 00000000     0 SECTION LOCAL  DEFAULT   14 
    20: 00000000     0 SECTION LOCAL  DEFAULT   18 
    21: 00000000     1 OBJECT  GLOBAL DEFAULT    3 m_char
    22: 00000004     8 OBJECT  GLOBAL DEFAULT    3 m_struct
    23: 00000000    88 FUNC    GLOBAL DEFAULT    1 main
    24: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND f_add
    25: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND printf
    26: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND m

可以看到,这几个符号都没有确定地址,下面,我们看a.out里面符号信息

1
root@raspberrypi:/home/pi/tmp# objdump -D a.out
...
Disassembly of section .data:

00020648 <__data_start>:
   20648:	00000000 	andeq	r0, r0, r0

0002064c <__dso_handle>:
   2064c:	00000000 	andeq	r0, r0, r0

00020650 <m_char>:
   20650:	00000101 	andeq	r0, r0, r1, lsl #2

00020651 <m_uchar>:
   20651:	01000001 	tsteq	r0, r1

00020654 <m_struct>:
   20654:	00000001 	andeq	r0, r0, r1
   20658:	00000002 	andeq	r0, r0, r2

Disassembly of section .bss:

0002065c <__bss_start>:
   2065c:	00000000 	andeq	r0, r0, r0

00020660 <m>:
   20660:	00000000 	andeq	r0, r0, r0
...

可以看到这几个变量的内存地址已经确定下来了,同时,我们注意到上面反汇编时,会将数据部分进行反编译,这是不对的。

了解了变量的分布和符号表信息,我们看看函数的信息

3 函数内存分布

函数相关的知识,主要考虑

  1. 接口调用标准
    主要考虑参数分配,入栈,出栈的责任
  2. 函数地址
    主要是考虑函数对外的地址
  3. 内部数据调用
    主要考虑函数内部怎么调用外部变量

在分析前,我们将main.c代码恢复成最初的那样,如下所示

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>

extern int m;
extern int f_add(int a, int b);

int main(void)
{

m = f_add(1, 2);
printf("sum = %d\r\n", m);
return 0;
}

反汇编main.o,main部分的代码如下

1
root@raspberrypi:/home/pi/tmp# objdump -d main.o

main.o:     file format elf32-littlearm

Disassembly of section .text:

00000000 <main>:
   0:	e92d4800 	push	{fp, lr}
   4:	e28db004 	add	fp, sp, #4
   8:	e3a00001 	mov	r0, #1
   c:	e3a01002 	mov	r1, #2
  10:	ebfffffe 	bl	0 <f_add>                          # 0 函数调用
  14:	e1a02000 	mov	r2, r0
  18:	e59f3020 	ldr	r3, [pc, #32]	; 40 <main+0x40>
  1c:	e5832000 	str	r2, [r3]
  20:	e59f3018 	ldr	r3, [pc, #24]	; 40 <main+0x40>
  24:	e5933000 	ldr	r3, [r3]
  28:	e59f0014 	ldr	r0, [pc, #20]	; 44 <main+0x44>
  2c:	e1a01003 	mov	r1, r3
  30:	ebfffffe 	bl	0 <printf>                         # 1 函数调用
  34:	e3a03000 	mov	r3, #0
  38:	e1a00003 	mov	r0, r3
  3c:	e8bd8800 	pop	{fp, pc}

看到0/1处的函数调用,函数地址均为0

再来看看a.out里面的汇编

1
00010420 <main>:
   10420:	e92d4800 	push	{fp, lr}
   10424:	e28db004 	add	fp, sp, #4
   10428:	e3a00001 	mov	r0, #1
   1042c:	e3a01002 	mov	r1, #2
   10430:	eb00000c 	bl	10468 <f_add>
   10434:	e1a02000 	mov	r2, r0
   10438:	e59f3020 	ldr	r3, [pc, #32]	; 10460 <main+0x40>
   1043c:	e5832000 	str	r2, [r3]
   10440:	e59f3018 	ldr	r3, [pc, #24]	; 10460 <main+0x40>
   10444:	e5933000 	ldr	r3, [r3]
   10448:	e59f0014 	ldr	r0, [pc, #20]	; 10464 <main+0x44>
   1044c:	e1a01003 	mov	r1, r3
   10450:	ebffff9c 	bl	102c8 <printf@plt>
   10454:	e3a03000 	mov	r3, #0
   10458:	e1a00003 	mov	r0, r3
   1045c:	e8bd8800 	pop	{fp, pc}
   10460:	00020644 	.word	0x00020644
   10464:	0001050c 	.word	0x0001050c

函数地址已经分配

OK,铺垫已经全部介绍完,下面重点介绍ELF怎么解决变量地址绑定和函数地址绑定的问题

2 重定向需要处理的问题

链接器读取ELF文件和链接脚本,并尝试解决下面的几个问题

  1. 解决未定义符号地址
  2. 分配已有符号的地址
  3. 分配函数地址

3 变量符号重定向

ELF通过符号表来表示文件中所有的符号(函数和变量),并将外部符号定义未UND
同时,将引用到的,需要重定向的符号放置到相对符号表中,从而允许外部工具进行修正
下面实例来分析,在main.c函数中,我们引用了一个外部变量m,但在编译成.o文件时,我们并不知道m的地址,反汇编main函数,得到

1
root@raspberrypi:/home/pi/tmp# objdump -d main.o

main.o:     file format elf32-littlearm
Disassembly of section .text:

00000000 <main>:
   0:	e92d4800 	push	{fp, lr}
   4:	e28db004 	add	fp, sp, #4
   8:	e3a00001 	mov	r0, #1
   c:	e3a01002 	mov	r1, #2
  10:	ebfffffe 	bl	0 <f_add>
  14:	e1a02000 	mov	r2, r0                             #得到返回值
  18:	e59f3020 	ldr	r3, [pc, #32]	; 40 <main+0x40>   #加载m的间接地址信息
  1c:	e5832000 	str	r2, [r3]                           #通过间接取地址,来获取m的地址信息,并将返回值存在到对应ddd地址中
  20:	e59f3018 	ldr	r3, [pc, #24]	; 40 <main+0x40>
  24:	e5933000 	ldr	r3, [r3]
  28:	e59f0014 	ldr	r0, [pc, #20]	; 44 <main+0x44>
  2c:	e1a01003 	mov	r1, r3
  30:	ebfffffe 	bl	0 <printf>
  34:	e3a03000 	mov	r3, #0
  38:	e1a00003 	mov	r0, r3
  3c:	e8bd8800 	pop	{fp, pc}

重点关注第14,18,20行,我已经做了注释,可以知道PC+32处存放着m的地址信息,也就是说,程序代码里面间接的表格方式来获取对应变量地址,从而便于统一管理,
上面的反汇编注释,已经说明了 PC+32 =
main+0x40,所以,我们直接找到main函数所在的.text段,然后加上0x40偏移,就找到了m所在的地址,可以看到为00000000
这里,我们将main.o用hex形式表示,如下所示

1
root@raspberrypi:/home/pi/tmp# objdump -s main.o

main.o:     file format elf32-littlearm

Contents of section .text:
 0000 00482de9 04b08de2 0100a0e3 0210a0e3  .H-.............
 0010 feffffeb 0020a0e1 20309fe5 002083e5  ..... .. 0... ..
 0020 18309fe5 003093e5 14009fe5 0310a0e1  .0...0..........
 0030 feffffeb 0030a0e3 0300a0e1 0088bde8  .....0..........
 0040 00000000 00000000                    ........

同时,ELF为了便于链接器修改符号偏移地址,专门设置了一个相对偏移表段.rel.text,也就是说明符号相对于text段所在的偏移地址

1
Relocation section '.rel.text' at offset 0x598 contains 4 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00000010  0000141c R_ARM_CALL        00000000   f_add
00000030  0000151c R_ARM_CALL        00000000   printf
00000040  00001602 R_ARM_ABS32       00000000   m
00000044  00000502 R_ARM_ABS32       00000000   .rodata

我们可以看到m符号相对于text的偏移刚好是0x40,和我们的计算结果一样

4 函数符号重定向

查看相对符号表中的函数偏移地址,然后修改对应的跳转指令地址

5 注意事项

  1. 重定向机制为编译器提供了极大的方便,但我们也看到,在汇编级别,其实效率并不高,有很大的优化空间,比如:可以将相对取地址改为绝对取地址,从而省去了一条指令
  2. 如果一个段中出现多次函数调用,则必须多次修改函数调用处的地址
  3. 如果一个段中出现多次变量调用,只需要修正一次变量地址表即可

示例:我们修改main.c代码,如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

extern int m;
extern int f_add(int a, int b);

int main(void)
{

m = f_add(1, 2);
printf("sum = %d\r\n", m);
printf("sum = %d\r\n", m);
printf("sum = %d\r\n", m);
printf("sum = %d\r\n", m);
return 0;
}

再次查看相对符号表,如下所示

1
Relocation section '.rel.text' at offset 0x5d4 contains 7 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00000010  0000141c R_ARM_CALL        00000000   f_add
00000030  0000151c R_ARM_CALL        00000000   printf
00000044  0000151c R_ARM_CALL        00000000   printf
00000058  0000151c R_ARM_CALL        00000000   printf
0000006c  0000151c R_ARM_CALL        00000000   printf
0000007c  00001602 R_ARM_ABS32       00000000   m
00000080  00000502 R_ARM_ABS32       00000000   .rodata

6 总结

链接器先找到.rel.text段,得到obj文件中所有修改修正的符号偏移地址,然后根据链接脚本和各个段的综合,计算出符号的正确分配地址,然后再修正对应节中相应的符号偏移地址
这样,就完成了静态链接过程中的地址修正过程

5 参考资料

  1. ELF规范1.1
  2. ELF规范1.2
  3. ARM ELF 规范
  4. MDK 编译器手册
  5. IAR 编译器手册
  6. GCC 编译器手册