Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/jdk.hotspot.agent/linux/native/libsaproc/dwarf.cpp
41149 views
1
/*
2
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3
* Copyright (c) 2020, NTT DATA.
4
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5
*
6
* This code is free software; you can redistribute it and/or modify it
7
* under the terms of the GNU General Public License version 2 only, as
8
* published by the Free Software Foundation.
9
*
10
* This code is distributed in the hope that it will be useful, but WITHOUT
11
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13
* version 2 for more details (a copy is included in the LICENSE file that
14
* accompanied this code).
15
*
16
* You should have received a copy of the GNU General Public License version
17
* 2 along with this work; if not, write to the Free Software Foundation,
18
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19
*
20
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21
* or visit www.oracle.com if you need additional information or have any
22
* questions.
23
*
24
*/
25
26
#include <cstring>
27
28
#include "dwarf.hpp"
29
#include "libproc_impl.h"
30
31
/* from read_leb128() in dwarf.c in binutils */
32
uintptr_t DwarfParser::read_leb(bool sign) {
33
uintptr_t result = 0L;
34
unsigned char b;
35
unsigned int shift = 0;
36
37
while (true) {
38
b = *_buf++;
39
result |= static_cast<uintptr_t>(b & 0x7f) << shift;
40
shift += 7;
41
if ((b & 0x80) == 0) {
42
break;
43
}
44
}
45
46
if (sign && (shift < (8 * sizeof(result))) && (b & 0x40)) {
47
result |= static_cast<uintptr_t>(-1L) << shift;
48
}
49
50
return result;
51
}
52
53
uint64_t DwarfParser::get_entry_length() {
54
uint64_t length = *(reinterpret_cast<uint32_t *>(_buf));
55
_buf += 4;
56
if (length == 0xffffffff) {
57
length = *(reinterpret_cast<uint64_t *>(_buf));
58
_buf += 8;
59
}
60
return length;
61
}
62
63
bool DwarfParser::process_cie(unsigned char *start_of_entry, uint32_t id) {
64
unsigned char *orig_pos = _buf;
65
_buf = start_of_entry - id;
66
67
uint64_t length = get_entry_length();
68
if (length == 0L) {
69
return false;
70
}
71
unsigned char *end = _buf + length;
72
73
_buf += 4; // Skip ID (This value of CIE would be always 0)
74
_buf++; // Skip version (assume to be "1")
75
76
char *augmentation_string = reinterpret_cast<char *>(_buf);
77
bool has_ehdata = (strcmp("eh", augmentation_string) == 0);
78
_buf += strlen(augmentation_string) + 1; // includes '\0'
79
if (has_ehdata) {
80
_buf += sizeof(void *); // Skip EH data
81
}
82
83
_code_factor = read_leb(false);
84
_data_factor = static_cast<int>(read_leb(true));
85
_return_address_reg = static_cast<enum DWARF_Register>(*_buf++);
86
87
if (strpbrk(augmentation_string, "LP") != NULL) {
88
// Language personality routine (P) and Language Specific Data Area (LSDA:L)
89
// are not supported because we need compliant Unwind Library Interface,
90
// but we want to unwind without it.
91
//
92
// Unwind Library Interface (SysV ABI AMD64 6.2)
93
// https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf
94
return false;
95
} else if (strchr(augmentation_string, 'R') != NULL) {
96
read_leb(false); // augmentation length
97
_encoding = *_buf++;
98
}
99
100
// Clear state
101
_current_pc = 0L;
102
_cfa_reg = RSP;
103
_return_address_reg = RA;
104
_cfa_offset = 0;
105
_ra_cfa_offset = 0;
106
_bp_cfa_offset = 0;
107
_bp_offset_available = false;
108
109
parse_dwarf_instructions(0L, static_cast<uintptr_t>(-1L), end);
110
111
_buf = orig_pos;
112
return true;
113
}
114
115
void DwarfParser::parse_dwarf_instructions(uintptr_t begin, uintptr_t pc, const unsigned char *end) {
116
uintptr_t operand1;
117
_current_pc = begin;
118
119
/* for remember state */
120
enum DWARF_Register rem_cfa_reg = MAX_VALUE;
121
int rem_cfa_offset = 0;
122
int rem_ra_cfa_offset = 0;
123
int rem_bp_cfa_offset = 0;
124
125
while ((_buf < end) && (_current_pc < pc)) {
126
unsigned char op = *_buf++;
127
unsigned char opa = op & 0x3f;
128
if (op & 0xc0) {
129
op &= 0xc0;
130
}
131
132
switch (op) {
133
case 0x0: // DW_CFA_nop
134
return;
135
case 0x01: // DW_CFA_set_loc
136
operand1 = get_decoded_value();
137
if (_current_pc != 0L) {
138
_current_pc = operand1;
139
}
140
break;
141
case 0x0c: // DW_CFA_def_cfa
142
_cfa_reg = static_cast<enum DWARF_Register>(read_leb(false));
143
_cfa_offset = read_leb(false);
144
break;
145
case 0x80: {// DW_CFA_offset
146
operand1 = read_leb(false);
147
enum DWARF_Register reg = static_cast<enum DWARF_Register>(opa);
148
if (reg == RBP) {
149
_bp_cfa_offset = operand1 * _data_factor;
150
_bp_offset_available = true;
151
} else if (reg == RA) {
152
_ra_cfa_offset = operand1 * _data_factor;
153
}
154
break;
155
}
156
case 0xe: // DW_CFA_def_cfa_offset
157
_cfa_offset = read_leb(false);
158
break;
159
case 0x40: // DW_CFA_advance_loc
160
if (_current_pc != 0L) {
161
_current_pc += opa * _code_factor;
162
}
163
break;
164
case 0x02: { // DW_CFA_advance_loc1
165
unsigned char ofs = *_buf++;
166
if (_current_pc != 0L) {
167
_current_pc += ofs * _code_factor;
168
}
169
break;
170
}
171
case 0x03: { // DW_CFA_advance_loc2
172
unsigned short ofs = *(reinterpret_cast<unsigned short *>(_buf));
173
_buf += 2;
174
if (_current_pc != 0L) {
175
_current_pc += ofs * _code_factor;
176
}
177
break;
178
}
179
case 0x04: { // DW_CFA_advance_loc4
180
unsigned int ofs = *(reinterpret_cast<unsigned int *>(_buf));
181
_buf += 4;
182
if (_current_pc != 0L) {
183
_current_pc += ofs * _code_factor;
184
}
185
break;
186
}
187
case 0x0d: {// DW_CFA_def_cfa_register
188
_cfa_reg = static_cast<enum DWARF_Register>(read_leb(false));
189
break;
190
}
191
case 0x0a: // DW_CFA_remember_state
192
rem_cfa_reg = _cfa_reg;
193
rem_cfa_offset = _cfa_offset;
194
rem_ra_cfa_offset = _ra_cfa_offset;
195
rem_bp_cfa_offset = _bp_cfa_offset;
196
break;
197
case 0x0b: // DW_CFA_restore_state
198
_cfa_reg = rem_cfa_reg;
199
_cfa_offset = rem_cfa_offset;
200
_ra_cfa_offset = rem_ra_cfa_offset;
201
_bp_cfa_offset = rem_bp_cfa_offset;
202
break;
203
default:
204
print_debug("DWARF: Unknown opcode: 0x%x\n", op);
205
return;
206
}
207
}
208
}
209
210
/* from dwarf.c in binutils */
211
uint32_t DwarfParser::get_decoded_value() {
212
int size;
213
uintptr_t result;
214
215
switch (_encoding & 0x7) {
216
case 0: // DW_EH_PE_absptr
217
size = sizeof(void *);
218
result = *(reinterpret_cast<uintptr_t *>(_buf));
219
break;
220
case 2: // DW_EH_PE_udata2
221
size = 2;
222
result = *(reinterpret_cast<unsigned int *>(_buf));
223
break;
224
case 3: // DW_EH_PE_udata4
225
size = 4;
226
result = *(reinterpret_cast<uint32_t *>(_buf));
227
break;
228
case 4: // DW_EH_PE_udata8
229
size = 8;
230
result = *(reinterpret_cast<uint64_t *>(_buf));
231
break;
232
default:
233
return 0;
234
}
235
236
// On x86-64, we have to handle it as 32 bit value, and it is PC relative.
237
// https://gcc.gnu.org/ml/gcc-help/2010-09/msg00166.html
238
#if defined(_LP64)
239
if (size == 8) {
240
result += _lib->eh_frame.v_addr + static_cast<uintptr_t>(_buf - _lib->eh_frame.data);
241
size = 4;
242
} else
243
#endif
244
if ((_encoding & 0x70) == 0x10) { // 0x10 = DW_EH_PE_pcrel
245
result += _lib->eh_frame.v_addr + static_cast<uintptr_t>(_buf - _lib->eh_frame.data);
246
} else if (size == 2) {
247
result = static_cast<int>(result) + _lib->eh_frame.v_addr + static_cast<uintptr_t>(_buf - _lib->eh_frame.data);
248
size = 4;
249
}
250
251
_buf += size;
252
return static_cast<uint32_t>(result);
253
}
254
255
unsigned int DwarfParser::get_pc_range() {
256
int size;
257
uintptr_t result;
258
259
switch (_encoding & 0x7) {
260
case 0: // DW_EH_PE_absptr
261
size = sizeof(void *);
262
result = *(reinterpret_cast<uintptr_t *>(_buf));
263
break;
264
case 2: // DW_EH_PE_udata2
265
size = 2;
266
result = *(reinterpret_cast<unsigned int *>(_buf));
267
break;
268
case 3: // DW_EH_PE_udata4
269
size = 4;
270
result = *(reinterpret_cast<uint32_t *>(_buf));
271
break;
272
case 4: // DW_EH_PE_udata8
273
size = 8;
274
result = *(reinterpret_cast<uint64_t *>(_buf));
275
break;
276
default:
277
return 0;
278
}
279
280
// On x86-64, we have to handle it as 32 bit value, and it is PC relative.
281
// https://gcc.gnu.org/ml/gcc-help/2010-09/msg00166.html
282
#if defined(_LP64)
283
if ((size == 8) || (size == 2)) {
284
size = 4;
285
}
286
#endif
287
288
_buf += size;
289
return static_cast<unsigned int>(result);
290
}
291
292
bool DwarfParser::process_dwarf(const uintptr_t pc) {
293
// https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html
294
_buf = _lib->eh_frame.data;
295
unsigned char *end = _lib->eh_frame.data + _lib->eh_frame.size;
296
while (_buf <= end) {
297
uint64_t length = get_entry_length();
298
if (length == 0L) {
299
return false;
300
}
301
unsigned char *next_entry = _buf + length;
302
unsigned char *start_of_entry = _buf;
303
uint32_t id = *(reinterpret_cast<uint32_t *>(_buf));
304
_buf += 4;
305
if (id != 0) { // FDE
306
uintptr_t pc_begin = get_decoded_value() + _lib->eh_frame.library_base_addr;
307
uintptr_t pc_end = pc_begin + get_pc_range();
308
309
if ((pc >= pc_begin) && (pc < pc_end)) {
310
// Process CIE
311
if (!process_cie(start_of_entry, id)) {
312
return false;
313
}
314
315
// Skip Augumenation
316
uintptr_t augmentation_length = read_leb(false);
317
_buf += augmentation_length; // skip
318
319
// Process FDE
320
parse_dwarf_instructions(pc_begin, pc, next_entry);
321
return true;
322
}
323
}
324
325
_buf = next_entry;
326
}
327
328
return false;
329
}
330
331