Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
// Copyright 2019 Google
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//      http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
 
15
#include "Crashlytics/Crashlytics/Unwind/Compact/FIRCLSCompactUnwind_Private.h"
16
#include "Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDataParsing.h"
17
#include "Crashlytics/Crashlytics/Helpers/FIRCLSDefines.h"
18
#include "Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDwarfUnwind.h"
19
#include "Crashlytics/Crashlytics/Helpers/FIRCLSFeatures.h"
20
#include "Crashlytics/Crashlytics/Unwind/FIRCLSUnwind.h"
21
#include "Crashlytics/Crashlytics/Helpers/FIRCLSUtility.h"
22
 
23
#include <string.h>
24
 
25
#if CLS_COMPACT_UNWINDING_SUPPORTED
26
 
27
#pragma mark Parsing
28
bool FIRCLSCompactUnwindInit(FIRCLSCompactUnwindContext* context,
29
                             const void* unwindInfo,
30
                             const void* ehFrame,
31
                             uintptr_t loadAddress) {
32
  if (!FIRCLSIsValidPointer(context)) {
33
    FIRCLSSDKLog("Error: invalid context passed to compact unwind init");
34
    return false;
35
  }
36
  if (!FIRCLSIsValidPointer(unwindInfo)) {
37
    FIRCLSSDKLog("Error: invalid unwind info passed to compact unwind init");
38
    return false;
39
  }
40
  if (!FIRCLSIsValidPointer(loadAddress)) {
41
    FIRCLSSDKLog("Error: invalid load address passed to compact unwind init");
42
    return false;
43
  }
44
 
45
  memset(context, 0, sizeof(FIRCLSCompactUnwindContext));
46
 
47
  if (!FIRCLSReadMemory((vm_address_t)unwindInfo, &context->unwindHeader,
48
                        sizeof(struct unwind_info_section_header))) {
49
    FIRCLSSDKLog("Error: could not read memory contents of unwindInfo\n");
50
    return false;
51
  }
52
 
53
  if (context->unwindHeader.version != UNWIND_SECTION_VERSION) {
54
    FIRCLSSDKLog("Error: bad unwind_info structure version (%d != %d)\n",
55
                 context->unwindHeader.version, UNWIND_SECTION_VERSION);
56
    return false;
57
  }
58
 
59
  // copy in the values
60
  context->unwindInfo = unwindInfo;
61
  context->ehFrame = ehFrame;
62
  context->loadAddress = loadAddress;
63
 
64
  return true;
65
}
66
 
67
void* FIRCLSCompactUnwindGetIndexData(FIRCLSCompactUnwindContext* context) {
68
  return (void*)((uintptr_t)context->unwindInfo +
69
                 (uintptr_t)context->unwindHeader.indexSectionOffset);
70
}
71
 
72
compact_unwind_encoding_t* FIRCLSCompactUnwindGetCommonEncodings(
73
    FIRCLSCompactUnwindContext* context) {
74
  return (compact_unwind_encoding_t*)((uintptr_t)context->unwindInfo +
75
                                      (uintptr_t)
76
                                          context->unwindHeader.commonEncodingsArraySectionOffset);
77
}
78
 
79
void* FIRCLSCompactUnwindGetSecondLevelData(FIRCLSCompactUnwindContext* context) {
80
  return (void*)((uintptr_t)context->unwindInfo +
81
                 context->indexHeader.secondLevelPagesSectionOffset);
82
}
83
 
84
uintptr_t FIRCLSCompactUnwindGetIndexFunctionOffset(FIRCLSCompactUnwindContext* context) {
85
  return context->loadAddress + context->indexHeader.functionOffset;
86
}
87
uintptr_t FIRCLSCompactUnwindGetTargetAddress(FIRCLSCompactUnwindContext* context, uintptr_t pc) {
88
  uintptr_t offset = FIRCLSCompactUnwindGetIndexFunctionOffset(context);
89
 
90
  if (pc <= offset) {
91
    FIRCLSSDKLog("Error: PC is invalid\n");
92
    return 0;
93
  }
94
 
95
  return pc - offset;
96
}
97
 
98
#pragma mark - Parsing and Lookup
99
bool FIRCLSCompactUnwindLookupFirstLevel(FIRCLSCompactUnwindContext* context, uintptr_t address) {
100
  if (!context) {
101
    return false;
102
  }
103
 
104
  // In practice, it appears that there always one more first level entry
105
  // than required. This actually makes sense, since we have to use this
106
  // info to check if we are in range. This implies there must be
107
  // at least 2 indices at a minimum.
108
 
109
  uint32_t indexCount = context->unwindHeader.indexCount;
110
  if (indexCount < 2) {
111
    return false;
112
  }
113
 
114
  // make sure our address is valid
115
  if (address < context->loadAddress) {
116
    return false;
117
  }
118
 
119
  struct unwind_info_section_header_index_entry* indexEntries =
120
      FIRCLSCompactUnwindGetIndexData(context);
121
  if (!indexEntries) {
122
    return false;
123
  }
124
 
125
  address -= context->loadAddress;  // search relative to zero
126
 
127
  // minus one because of the extra entry - see comment above
128
  for (uint32_t index = 0; index < indexCount - 1; ++index) {
129
    uint32_t value = indexEntries[index].functionOffset;
130
    uint32_t nextValue = indexEntries[index + 1].functionOffset;
131
 
132
    if (address >= value && address < nextValue) {
133
      context->firstLevelNextFunctionOffset = nextValue;
134
      context->indexHeader = indexEntries[index];
135
      return true;
136
    }
137
  }
138
 
139
  return false;
140
}
141
 
142
uint32_t FIRCLSCompactUnwindGetSecondLevelPageKind(FIRCLSCompactUnwindContext* context) {
143
  if (!context) {
144
    return 0;
145
  }
146
 
147
  return *(uint32_t*)FIRCLSCompactUnwindGetSecondLevelData(context);
148
}
149
 
150
bool FIRCLSCompactUnwindLookupSecondLevelRegular(FIRCLSCompactUnwindContext* context,
151
                                                 uintptr_t pc,
152
                                                 FIRCLSCompactUnwindResult* result) {
153
  FIRCLSSDKLog("Encountered a regular second-level page\n");
154
  return false;
155
}
156
 
157
// this only works for compressed entries right now
158
bool FIRCLSCompactUnwindBinarySearchSecondLevel(uintptr_t address,
159
                                                uint32_t* index,
160
                                                uint16_t entryCount,
161
                                                uint32_t* entryArray) {
162
  if (!index || !entryArray) {
163
    return false;
164
  }
165
 
166
  if (entryCount == 0) {
167
    return false;
168
  }
169
 
170
  if (address == 0) {
171
    return false;
172
  }
173
 
174
  uint32_t highIndex = entryCount;
175
  *index = 0;
176
 
177
  while (*index < highIndex) {
178
    uint32_t midIndex = (*index + highIndex) / 2;
179
 
180
    //        FIRCLSSDKLog("%u %u %u\n", *index, midIndex, highIndex);
181
 
182
    uintptr_t value = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entryArray[midIndex]);
183
 
184
    if (value > address) {
185
      if (highIndex == midIndex) {
186
        return false;
187
      }
188
 
189
      highIndex = midIndex;
190
      continue;
191
    }
192
 
193
    *index = midIndex;
194
 
195
    // are we at the end of the array?
196
    if (midIndex == entryCount - 1) {
197
      return false;
198
    }
199
 
200
    uintptr_t nextValue = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entryArray[midIndex + 1]);
201
    if (nextValue > address) {
202
      // we've found it
203
      break;
204
    }
205
 
206
    *index += 1;
207
  }
208
 
209
  // check to make sure we're still within bounds
210
  return *index < entryCount;
211
}
212
 
213
bool FIRCLSCompactUnwindLookupSecondLevelCompressed(FIRCLSCompactUnwindContext* context,
214
                                                    uintptr_t pc,
215
                                                    FIRCLSCompactUnwindResult* result) {
216
  if (!context || !result) {
217
    return false;
218
  }
219
 
220
  void* ptr = FIRCLSCompactUnwindGetSecondLevelData(context);
221
 
222
  if (!ptr) {
223
    return false;
224
  }
225
 
226
  memset(result, 0, sizeof(FIRCLSCompactUnwindResult));
227
 
228
  struct unwind_info_compressed_second_level_page_header* header =
229
      (struct unwind_info_compressed_second_level_page_header*)ptr;
230
 
231
  // adjust address
232
  uintptr_t targetAddress = FIRCLSCompactUnwindGetTargetAddress(context, pc);
233
 
234
  uint32_t* entryArray = ptr + header->entryPageOffset;
235
 
236
  uint32_t index = 0;
237
 
238
  if (!FIRCLSCompactUnwindBinarySearchSecondLevel(targetAddress, &index, header->entryCount,
239
                                                  entryArray)) {
240
    FIRCLSSDKLogInfo("Unable to find PC in second level\n");
241
    return false;
242
  }
243
 
244
  uint32_t entry = entryArray[index];
245
 
246
  // Computing the fuction start address is easy
247
  result->functionStart = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry) +
248
                          FIRCLSCompactUnwindGetIndexFunctionOffset(context);
249
 
250
  // Computing the end is more complex, because we could be on the last entry. In that case, we
251
  // cannot use the next value as the end.
252
  result->functionEnd = context->loadAddress;
253
  if (index < header->entryCount - 1) {
254
    result->functionEnd += UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entryArray[index + 1]) +
255
                           context->indexHeader.functionOffset;
256
  } else {
257
    result->functionEnd += context->firstLevelNextFunctionOffset;
258
  }
259
 
260
  //    FIRCLSSDKLog("Located %lx => %lx %lx\n", pc, result->functionStart, result->functionEnd);
261
 
262
  if ((pc < result->functionStart) || (pc >= result->functionEnd)) {
263
    FIRCLSSDKLog("PC does not match computed function range\n");
264
    return false;
265
  }
266
 
267
  uint32_t encodingIndex = UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry);
268
 
269
  // encoding could be in the common array
270
  if (encodingIndex < context->unwindHeader.commonEncodingsArrayCount) {
271
    result->encoding = FIRCLSCompactUnwindGetCommonEncodings(context)[encodingIndex];
272
 
273
    //        FIRCLSSDKLog("Entry has common encoding: 0x%x\n", result->encoding);
274
  } else {
275
    encodingIndex = encodingIndex - context->unwindHeader.commonEncodingsArrayCount;
276
 
277
    compact_unwind_encoding_t* encodings = ptr + header->encodingsPageOffset;
278
 
279
    result->encoding = encodings[encodingIndex];
280
 
281
    //        FIRCLSSDKLog("Entry has compressed encoding: 0x%x\n", result->encoding);
282
  }
283
 
284
  if (result->encoding == 0) {
285
    FIRCLSSDKLogInfo("Entry has has no unwind info\n");
286
    return false;
287
  }
288
 
289
  return true;
290
}
291
 
292
bool FIRCLSCompactUnwindLookupSecondLevel(FIRCLSCompactUnwindContext* context,
293
                                          uintptr_t pc,
294
                                          FIRCLSCompactUnwindResult* result) {
295
  switch (FIRCLSCompactUnwindGetSecondLevelPageKind(context)) {
296
    case UNWIND_SECOND_LEVEL_REGULAR:
297
      FIRCLSSDKLogInfo("Found a second level regular header\n");
298
      if (FIRCLSCompactUnwindLookupSecondLevelRegular(context, pc, result)) {
299
        return true;
300
      }
301
      break;
302
    case UNWIND_SECOND_LEVEL_COMPRESSED:
303
      FIRCLSSDKLogInfo("Found a second level compressed header\n");
304
      if (FIRCLSCompactUnwindLookupSecondLevelCompressed(context, pc, result)) {
305
        return true;
306
      }
307
      break;
308
    default:
309
      FIRCLSSDKLogError("Unrecognized header kind - unable to continue\n");
310
      break;
311
  }
312
 
313
  return false;
314
}
315
 
316
bool FIRCLSCompactUnwindLookup(FIRCLSCompactUnwindContext* context,
317
                               uintptr_t pc,
318
                               FIRCLSCompactUnwindResult* result) {
319
  if (!context || !result) {
320
    return false;
321
  }
322
 
323
  // step 1 - find the pc in the first-level index
324
  if (!FIRCLSCompactUnwindLookupFirstLevel(context, pc)) {
325
    FIRCLSSDKLogWarn("Unable to find pc in first level\n");
326
    return false;
327
  }
328
 
329
  FIRCLSSDKLogDebug("Found first level (second => %u)\n",
330
                    context->indexHeader.secondLevelPagesSectionOffset);
331
 
332
  // step 2 - use that info to find the second-level information
333
  // that second actually has the encoding info we're looking for.
334
  if (!FIRCLSCompactUnwindLookupSecondLevel(context, pc, result)) {
335
    FIRCLSSDKLogInfo("Second-level PC lookup failed\n");
336
    return false;
337
  }
338
 
339
  return true;
340
}
341
 
342
#pragma mark - Unwinding
343
bool FIRCLSCompactUnwindLookupAndCompute(FIRCLSCompactUnwindContext* context,
344
                                         FIRCLSThreadContext* registers) {
345
  if (!context || !registers) {
346
    return false;
347
  }
348
 
349
  uintptr_t pc = FIRCLSThreadContextGetPC(registers);
350
 
351
  // little sanity check
352
  if (pc < context->loadAddress) {
353
    return false;
354
  }
355
 
356
  FIRCLSCompactUnwindResult result;
357
 
358
  memset(&result, 0, sizeof(result));
359
 
360
  if (!FIRCLSCompactUnwindLookup(context, pc, &result)) {
361
    FIRCLSSDKLogInfo("Unable to lookup compact unwind for pc %p\n", (void*)pc);
362
    return false;
363
  }
364
 
365
  // Ok, armed with the encoding, we can actually attempt to modify the registers. Because
366
  // the encoding is arch-specific, this function has to be defined per-arch.
367
  if (!FIRCLSCompactUnwindComputeRegisters(context, &result, registers)) {
368
    FIRCLSSDKLogError("Failed to compute registers\n");
369
    return false;
370
  }
371
 
372
  return true;
373
}
374
 
375
#if CLS_DWARF_UNWINDING_SUPPORTED
376
bool FIRCLSCompactUnwindDwarfFrame(FIRCLSCompactUnwindContext* context,
377
                                   uintptr_t dwarfOffset,
378
                                   FIRCLSThreadContext* registers) {
379
  if (!context || !registers) {
380
    return false;
381
  }
382
 
383
  // Everyone's favorite! Dwarf unwinding!
384
  FIRCLSSDKLogInfo("Trying to read dwarf data with offset %lx\n", dwarfOffset);
385
 
386
  FIRCLSDwarfCFIRecord record;
387
 
388
  if (!FIRCLSDwarfParseCFIFromFDERecordOffset(&record, context->ehFrame, dwarfOffset)) {
389
    FIRCLSSDKLogError("Unable to init FDE\n");
390
    return false;
391
  }
392
 
393
  if (!FIRCLSDwarfUnwindComputeRegisters(&record, registers)) {
394
    FIRCLSSDKLogError("Failed to compute DWARF registers\n");
395
    return false;
396
  }
397
 
398
  return true;
399
}
400
#endif
401
 
402
#else
403
INJECT_STRIP_SYMBOL(compact_unwind)
404
#endif