https://xrefandroid.com/android-10.0.0_r47/xref/bionic/linker/linker.cpp

relocate分析

只保留relocate中與arm64有關的部份,刪除了其他架構&tls相關的東西。

relocate做了以下事情:

  1. 調用soinfo_do_lookup獲取類型為ElfW(Sym)s
  2. s傳入resolve_symbol_address函數,它會返回對應符號的地址sym_addr
  3. 最後會根據不同的重定向類型來進行重定向,主要分為3類R_GENERIC_JUMP_SLOTR_GENERIC_GLOB_DATR_GENERIC_RELATIVE

reloc指向待重定向的地址,根據不同的重定向類型,修改為不同的值。

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
template<typename ElfRelIteratorT>
bool soinfo::relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& rel_iterator,
const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
const size_t tls_tp_base = __libc_shared_globals()->static_tls_layout.offset_thread_pointer();
std::vector<std::pair<TlsDescriptor*, size_t>> deferred_tlsdesc_relocs;

for (size_t idx = 0; rel_iterator.has_next(); ++idx) {
const auto rel = rel_iterator.next();
if (rel == nullptr) {
return false;
}

ElfW(Word) type = ELFW(R_TYPE)(rel->r_info);
ElfW(Word) sym = ELFW(R_SYM)(rel->r_info);

ElfW(Addr) reloc = static_cast<ElfW(Addr)>(rel->r_offset + load_bias);
ElfW(Addr) sym_addr = 0;
const char* sym_name = nullptr;
ElfW(Addr) addend = get_addend(rel, reloc);

DEBUG("Processing \"%s\" relocation at index %zd", get_realpath(), idx);
if (type == R_GENERIC_NONE) {
continue;
}

const ElfW(Sym)* s = nullptr;
soinfo* lsi = nullptr;

if (sym == 0) {
// ...
} else if (ELF_ST_BIND(symtab_[sym].st_info) == STB_LOCAL && is_tls_reloc(type)) {
// tls?
} else {
sym_name = get_string(symtab_[sym].st_name);
const version_info* vi = nullptr;

if (!lookup_version_info(version_tracker, sym, sym_name, &vi)) {
return false;
}
// 1. 調用soinfo_do_lookup獲取類型為ElfW(Sym)的s。
if (!soinfo_do_lookup(this, sym_name, vi, &lsi, global_group, local_group, &s)) {
return false;
}

if (s == nullptr) {
// We only allow an undefined symbol if this is a weak reference...
s = &symtab_[sym];
if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, get_realpath());
return false;
}

switch (type) {
case R_GENERIC_JUMP_SLOT:
case R_GENERIC_GLOB_DAT:
case R_GENERIC_RELATIVE:
case R_GENERIC_IRELATIVE:
case R_GENERIC_TLS_DTPMOD:
case R_GENERIC_TLS_DTPREL:
case R_GENERIC_TLS_TPREL:
case R_GENERIC_TLSDESC:
case R_AARCH64_ABS64:
case R_AARCH64_ABS32:
case R_AARCH64_ABS16:
break;
default:
DL_ERR("unknown weak reloc type %d @ %p (%zu)", type, rel, idx);
return false;
}
} else { // We got a definition.
if (is_tls_reloc(type)) {
// ...
} else {
// ...tls

// 2. 將s傳入resolve_symbol_address函數,它會返回對應符號的地址sym_addr
sym_addr = lsi->resolve_symbol_address(s);
}
}
count_relocation(kRelocSymbol);
}

// 3. 根據不同的重定向類型來進行重定向
switch (type) {
case R_GENERIC_JUMP_SLOT:
*reinterpret_cast<ElfW(Addr)*>(reloc) = (sym_addr + addend);
break;
case R_GENERIC_GLOB_DAT:
*reinterpret_cast<ElfW(Addr)*>(reloc) = (sym_addr + addend);
break;
case R_GENERIC_RELATIVE:
*reinterpret_cast<ElfW(Addr)*>(reloc) = (load_bias + addend);
break;
case R_GENERIC_IRELATIVE:
{

ElfW(Addr) ifunc_addr = call_ifunc_resolver(load_bias + addend);
*reinterpret_cast<ElfW(Addr)*>(reloc) = ifunc_addr;
}
break;

case R_AARCH64_ABS64:
*reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend;
break;
case R_AARCH64_ABS32:
{
const ElfW(Addr) min_value = static_cast<ElfW(Addr)>(INT32_MIN);
const ElfW(Addr) max_value = static_cast<ElfW(Addr)>(UINT32_MAX);
if ((min_value <= (sym_addr + addend)) &&
((sym_addr + addend) <= max_value)) {
*reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend;
} else {
DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx",
sym_addr + addend, min_value, max_value);
return false;
}
}
break;
case R_AARCH64_ABS16:
{
const ElfW(Addr) min_value = static_cast<ElfW(Addr)>(INT16_MIN);
const ElfW(Addr) max_value = static_cast<ElfW(Addr)>(UINT16_MAX);
if ((min_value <= (sym_addr + addend)) &&
((sym_addr + addend) <= max_value)) {
*reinterpret_cast<ElfW(Addr)*>(reloc) = (sym_addr + addend);
} else {
DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx",
sym_addr + addend, min_value, max_value);
return false;
}
}
break;
case R_AARCH64_PREL64:
*reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend - rel->r_offset;
break;
case R_AARCH64_PREL32:
{
const ElfW(Addr) min_value = static_cast<ElfW(Addr)>(INT32_MIN);
const ElfW(Addr) max_value = static_cast<ElfW(Addr)>(UINT32_MAX);
if ((min_value <= (sym_addr + addend - rel->r_offset)) &&
((sym_addr + addend - rel->r_offset) <= max_value)) {
*reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend - rel->r_offset;
} else {
DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx",
sym_addr + addend - rel->r_offset, min_value, max_value);
return false;
}
}
break;
case R_AARCH64_PREL16:
{
const ElfW(Addr) min_value = static_cast<ElfW(Addr)>(INT16_MIN);
const ElfW(Addr) max_value = static_cast<ElfW(Addr)>(UINT16_MAX);
if ((min_value <= (sym_addr + addend - rel->r_offset)) &&
((sym_addr + addend - rel->r_offset) <= max_value)) {
*reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend - rel->r_offset;
} else {
DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx",
sym_addr + addend - rel->r_offset, min_value, max_value);
return false;
}
}
break;

case R_AARCH64_COPY:
DL_ERR("%s R_AARCH64_COPY relocations are not supported", get_realpath());
return false;
default:
DL_ERR("unknown reloc type %d @ %p (%zu)", type, rel, idx);
return false;
}
}

return true;
}

實例分析

  • 源代碼如下:
    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
    44
    45
    46
    47
    48
    49
    50
    51
    52
    #include <string.h>
    #include <jni.h>
    #include <stdio.h>

    typedef void(*PPP)();
    __attribute__((visibility("default"))) void myFunc()
    {
    int i = 0;
    i = 1;
    i = 2;
    return;
    }

    PPP g1 = myFunc;
    PPP g2;

    __attribute__((constructor)) int mian_()
    {

    __asm("nop");
    __asm("nop");
    __asm("nop");
    PPP s1 = myFunc;

    __asm("nop");
    __asm("nop");
    __asm("nop");
    s1();

    __asm("nop");
    __asm("nop");
    __asm("nop");
    g1();

    __asm("nop");
    __asm("nop");
    __asm("nop");
    g2 = myFunc;
    g2();

    __asm("nop");
    __asm("nop");
    __asm("nop");
    myFunc();


    __asm("nop");
    __asm("nop");
    __asm("nop");
    return 0;

    }

從010可以看到.dynamic就在.got上面

image.png

將生成的so拉入IDA,ctrl+s定位到.got節,它上面確實就是.dynamic節。

.dynamic節中保存了很多so的信息,其中就包含所有的重定位信息,下圖紅框中的就是重定位信息對應的Elf64_Dyn元素,DT_RELA對應.rela.dyn ( FileOffset為0x4B0 ),DT_JMPREL對應.rela.plt ( FileOffset為0x570 )。

雙擊0x4B0跳到對應的重定向表。

image.png

這裡就是重定向表,每個Elf64_Rela占8*3=24個字節,<r_offset, r_info, r_addend>

  • r_offset:待重定向符號的FileOffset。
  • r_info:包括typesym,前者是重定向類型,後者是symtab_符號表的索引,通過ELF64_R_SYMELF64_R_TYPE宏來解析r_info
  • r_addend:額外的信息,下圖中只有0x403類型的重定向的這個值才不為0

image.png

解析r_info的宏定義&用法:

1
2
3
4
5
#define ELF64_R_SYM(i) ((i) >> 32)
#define ELF64_R_TYPE(i) ((i) & 0xffffffff)

ElfW(Word) type = ELFW(R_TYPE)(rel->r_info);
ElfW(Word) sym = ELFW(R_SYM)(rel->r_info);

上述的symtab_符號表可以從010裡看出,.rela.dyns_link3,代表與之鏈接的符號表是索引為3的那個section,即.dynsym

image.png

403重定向

403重定向即R_GENERIC_RELATIVE類型的重定向,以一組實際的數據<0x1898, 0x403, 0x71C>來分析其重定向流程。

首先計算typesym

1
2
ElfW(Word) type = ELFW(R_TYPE)(rel->r_info); // 0x403
ElfW(Word) sym = ELFW(R_SYM)(rel->r_info); // 0x0

403重定向的sym固定為0,而符號表中的第0項也全都是0

image.png

403重定向的目標是當前so中的一些函數,本例的0x1898指向的是.fini_array中的函數。這也是為什麼叫做R_GENERIC_RELATIVE( 相對 )。

image.png

最終會將0x1898地址指向的內容設置為0x71C

1
2
3
case R_GENERIC_RELATIVE:
*reinterpret_cast<ElfW(Addr)*>(reloc) = (load_bias + addend);
break;

402重定向

402重定向即R_GENERIC_JUMP_SLOT類型的重定向,以一組實際的數據<0x1AB0, 0x100000402, 0>來分析其重定向流程。

首先計算typesym

1
2
ElfW(Word) type = ELFW(R_TYPE)(rel->r_info); // 0x402
ElfW(Word) sym = ELFW(R_SYM)(rel->r_info); // 0x1

0x1對應的符號信息如下,符號名為__cxa_finalize,與IDA看到的一致。

image.png

402重定向的目標是一些外部的導入函數和內部函數( .init_array中用到的函數,如myFunc )。

最終會將0x1AB0地址指向的內容設置為sym_addr( 具體sym_addr是什麼沒有深究,maybe是真實的函數地址? )。

1
2
3
case R_GENERIC_JUMP_SLOT:
*reinterpret_cast<ElfW(Addr)*>(reloc) = (sym_addr + addend);
break;

401重定向

401重定向即R_GENERIC_GLOB_DAT類型的重定向,以一組實際的數據<0x1A88, 0x600000401, 0>來分析其重定向流程。

首先計算typesym

1
2
ElfW(Word) type = ELFW(R_TYPE)(rel->r_info); // 0x401
ElfW(Word) sym = ELFW(R_SYM)(rel->r_info); // 0x6

0x6對應的符號信息如下,符號名為g1,是一個全局變量。

image.png

最終同樣會將0x1A88地址指向的內容設置為sym_addr

1
2
3
case R_GENERIC_GLOB_DAT:
*reinterpret_cast<ElfW(Addr)*>(reloc) = (sym_addr + addend);
break;

101重定向

101重定向即R_AARCH64_ABS64,屬於Static Data relocations

本例中有2個這種類型的重定向塊,一個是屬於myFunc、另一個是屬於.init_array裡的函數。

image.png

最終的重定位實現與上面一樣:

1
2
3
case R_AARCH64_ABS64:
*reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend;
break;

ext:readelf工具使用

使用readelf可以很方便查看一個elf文件的各種信息,直接在cmd輸入指令即可使用:

1
2
3
4
5
6
7
8
# 1. 查看重定向信息
readelf -r <so_path>

# 2. 查看.dynsym table
readelf --dyn-syms <so_path>

# 3. 查看.dynamic
readelf - <so_path>