tools/profiler/LulExidx.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
michael@0 3
michael@0 4 /* libunwind - a platform-independent unwind library
michael@0 5 Copyright 2011 Linaro Limited
michael@0 6
michael@0 7 This file is part of libunwind.
michael@0 8
michael@0 9 Permission is hereby granted, free of charge, to any person obtaining
michael@0 10 a copy of this software and associated documentation files (the
michael@0 11 "Software"), to deal in the Software without restriction, including
michael@0 12 without limitation the rights to use, copy, modify, merge, publish,
michael@0 13 distribute, sublicense, and/or sell copies of the Software, and to
michael@0 14 permit persons to whom the Software is furnished to do so, subject to
michael@0 15 the following conditions:
michael@0 16
michael@0 17 The above copyright notice and this permission notice shall be
michael@0 18 included in all copies or substantial portions of the Software.
michael@0 19
michael@0 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
michael@0 21 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
michael@0 22 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
michael@0 23 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
michael@0 24 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
michael@0 25 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
michael@0 26 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
michael@0 27
michael@0 28
michael@0 29 // Copyright (c) 2010 Google Inc.
michael@0 30 // All rights reserved.
michael@0 31 //
michael@0 32 // Redistribution and use in source and binary forms, with or without
michael@0 33 // modification, are permitted provided that the following conditions are
michael@0 34 // met:
michael@0 35 //
michael@0 36 // * Redistributions of source code must retain the above copyright
michael@0 37 // notice, this list of conditions and the following disclaimer.
michael@0 38 // * Redistributions in binary form must reproduce the above
michael@0 39 // copyright notice, this list of conditions and the following disclaimer
michael@0 40 // in the documentation and/or other materials provided with the
michael@0 41 // distribution.
michael@0 42 // * Neither the name of Google Inc. nor the names of its
michael@0 43 // contributors may be used to endorse or promote products derived from
michael@0 44 // this software without specific prior written permission.
michael@0 45 //
michael@0 46 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
michael@0 47 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
michael@0 48 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
michael@0 49 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
michael@0 50 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
michael@0 51 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
michael@0 52 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
michael@0 53 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
michael@0 54 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 55 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
michael@0 56 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 57
michael@0 58
michael@0 59 // Derived from libunwind, with extensive modifications.
michael@0 60
michael@0 61 // This file translates EXIDX unwind information into the same format
michael@0 62 // that LUL uses for CFI information. Hence LUL's CFI unwinding
michael@0 63 // abilities also become usable for EXIDX.
michael@0 64 //
michael@0 65 // See: "Exception Handling ABI for the ARM Architecture", ARM IHI 0038A
michael@0 66 // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf
michael@0 67
michael@0 68 // EXIDX data is presented in two parts:
michael@0 69 //
michael@0 70 // * an index table. This contains two words per routine,
michael@0 71 // the first of which identifies the routine, and the second
michael@0 72 // of which is a reference to the unwind bytecode. If the
michael@0 73 // bytecode is very compact -- 3 bytes or less -- it can be
michael@0 74 // stored directly in the second word.
michael@0 75 //
michael@0 76 // * an area containing the unwind bytecodes.
michael@0 77 //
michael@0 78 // General flow is: ExceptionTableInfo::Start iterates over all
michael@0 79 // of the index table entries (pairs). For each entry, it:
michael@0 80 //
michael@0 81 // * calls ExceptionTableInfo::ExtabEntryExtract to copy the bytecode
michael@0 82 // out into an intermediate buffer.
michael@0 83
michael@0 84 // * uses ExceptionTableInfo::ExtabEntryDecode to parse the intermediate
michael@0 85 // buffer. Each bytecode instruction is bundled into a
michael@0 86 // arm_ex_to_module::extab_data structure, and handed to ..
michael@0 87 //
michael@0 88 // * .. ARMExToModule::ImproveStackFrame, which in turn hands it to
michael@0 89 // ARMExToModule::TranslateCmd, and that generates the pseudo-CFI
michael@0 90 // records that Breakpad stores.
michael@0 91
michael@0 92 // This file is derived from the following files in
michael@0 93 // toolkit/crashreporter/google-breakpad:
michael@0 94 // src/common/arm_ex_to_module.cc
michael@0 95 // src/common/arm_ex_reader.cc
michael@0 96
michael@0 97 #include "mozilla/Assertions.h"
michael@0 98 #include "mozilla/NullPtr.h"
michael@0 99
michael@0 100 #include "LulExidxExt.h"
michael@0 101
michael@0 102
michael@0 103 #define ARM_EXBUF_START(x) (((x) >> 4) & 0x0f)
michael@0 104 #define ARM_EXBUF_COUNT(x) ((x) & 0x0f)
michael@0 105 #define ARM_EXBUF_END(x) (ARM_EXBUF_START(x) + ARM_EXBUF_COUNT(x))
michael@0 106
michael@0 107 namespace lul {
michael@0 108
michael@0 109 // Translate command from extab_data to command for Module.
michael@0 110 int ARMExToModule::TranslateCmd(const struct extab_data* edata,
michael@0 111 LExpr& vsp) {
michael@0 112 int ret = 0;
michael@0 113 switch (edata->cmd) {
michael@0 114 case ARM_EXIDX_CMD_FINISH:
michael@0 115 /* Copy LR to PC if there isn't currently a rule for PC in force. */
michael@0 116 if (curr_rules_.mR15expr.mHow == LExpr::UNKNOWN) {
michael@0 117 if (curr_rules_.mR14expr.mHow == LExpr::UNKNOWN) {
michael@0 118 curr_rules_.mR15expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R14, 0);
michael@0 119 } else {
michael@0 120 curr_rules_.mR15expr = curr_rules_.mR14expr;
michael@0 121 }
michael@0 122 }
michael@0 123 break;
michael@0 124 case ARM_EXIDX_CMD_SUB_FROM_VSP:
michael@0 125 vsp = vsp.add_delta(- static_cast<long>(edata->data));
michael@0 126 break;
michael@0 127 case ARM_EXIDX_CMD_ADD_TO_VSP:
michael@0 128 vsp = vsp.add_delta(static_cast<long>(edata->data));
michael@0 129 break;
michael@0 130 case ARM_EXIDX_CMD_REG_POP:
michael@0 131 for (unsigned int i = 0; i < 16; i++) {
michael@0 132 if (edata->data & (1 << i)) {
michael@0 133 // See if we're summarising for int register |i|. If so,
michael@0 134 // describe how to pull it off the stack. The cast of |i| is
michael@0 135 // a bit of a kludge but works because DW_REG_ARM_Rn has the
michael@0 136 // value |n|, for 0 <= |n| <= 15 -- that is, for the ARM
michael@0 137 // general-purpose registers.
michael@0 138 LExpr* regI_exprP = curr_rules_.ExprForRegno((DW_REG_NUMBER)i);
michael@0 139 if (regI_exprP) {
michael@0 140 *regI_exprP = vsp.deref();
michael@0 141 }
michael@0 142 vsp = vsp.add_delta(4);
michael@0 143 }
michael@0 144 }
michael@0 145 /* Set cfa in case the SP got popped. */
michael@0 146 if (edata->data & (1 << 13)) {
michael@0 147 vsp = curr_rules_.mR13expr;
michael@0 148 }
michael@0 149 break;
michael@0 150 case ARM_EXIDX_CMD_REG_TO_SP: {
michael@0 151 MOZ_ASSERT (edata->data < 16);
michael@0 152 int reg_no = edata->data;
michael@0 153 // Same comment as above, re the casting of |reg_no|, applies.
michael@0 154 LExpr* reg_exprP = curr_rules_.ExprForRegno((DW_REG_NUMBER)reg_no);
michael@0 155 if (reg_exprP) {
michael@0 156 if (reg_exprP->mHow == LExpr::UNKNOWN) {
michael@0 157 curr_rules_.mR13expr = LExpr(LExpr::NODEREF, reg_no, 0);
michael@0 158 } else {
michael@0 159 curr_rules_.mR13expr = *reg_exprP;
michael@0 160 }
michael@0 161 vsp = curr_rules_.mR13expr;
michael@0 162 }
michael@0 163 break;
michael@0 164 }
michael@0 165 case ARM_EXIDX_CMD_VFP_POP:
michael@0 166 /* Don't recover VFP registers, but be sure to adjust the stack
michael@0 167 pointer. */
michael@0 168 for (unsigned int i = ARM_EXBUF_START(edata->data);
michael@0 169 i <= ARM_EXBUF_END(edata->data); i++) {
michael@0 170 vsp = vsp.add_delta(8);
michael@0 171 }
michael@0 172 if (!(edata->data & ARM_EXIDX_VFP_FSTMD)) {
michael@0 173 vsp = vsp.add_delta(4);
michael@0 174 }
michael@0 175 break;
michael@0 176 case ARM_EXIDX_CMD_WREG_POP:
michael@0 177 for (unsigned int i = ARM_EXBUF_START(edata->data);
michael@0 178 i <= ARM_EXBUF_END(edata->data); i++) {
michael@0 179 vsp = vsp.add_delta(8);
michael@0 180 }
michael@0 181 break;
michael@0 182 case ARM_EXIDX_CMD_WCGR_POP:
michael@0 183 // Pop wCGR registers under mask {wCGR3,2,1,0}, hence "i < 4"
michael@0 184 for (unsigned int i = 0; i < 4; i++) {
michael@0 185 if (edata->data & (1 << i)) {
michael@0 186 vsp = vsp.add_delta(4);
michael@0 187 }
michael@0 188 }
michael@0 189 break;
michael@0 190 case ARM_EXIDX_CMD_REFUSED:
michael@0 191 case ARM_EXIDX_CMD_RESERVED:
michael@0 192 ret = -1;
michael@0 193 break;
michael@0 194 }
michael@0 195 return ret;
michael@0 196 }
michael@0 197
michael@0 198 void ARMExToModule::AddStackFrame(uintptr_t addr, size_t size) {
michael@0 199 // Here we are effectively reinitialising the EXIDX summariser for a
michael@0 200 // new code address range. smap_ stays unchanged. All other fields
michael@0 201 // are reinitialised.
michael@0 202 vsp_ = LExpr(LExpr::NODEREF, DW_REG_ARM_R13, 0);
michael@0 203 (void) new (&curr_rules_) RuleSet();
michael@0 204 curr_rules_.mAddr = (uintptr_t)addr;
michael@0 205 curr_rules_.mLen = (uintptr_t)size;
michael@0 206 if (0) {
michael@0 207 char buf[100];
michael@0 208 sprintf(buf, " AddStackFrame %llx .. %llx",
michael@0 209 (uint64_t)addr, (uint64_t)(addr + size - 1));
michael@0 210 log_(buf);
michael@0 211 }
michael@0 212 }
michael@0 213
michael@0 214 int ARMExToModule::ImproveStackFrame(const struct extab_data* edata) {
michael@0 215 return TranslateCmd(edata, vsp_) ;
michael@0 216 }
michael@0 217
michael@0 218 void ARMExToModule::DeleteStackFrame() {
michael@0 219 }
michael@0 220
michael@0 221 void ARMExToModule::SubmitStackFrame() {
michael@0 222 // JRS: I'm really not sure what this means, or if it is necessary
michael@0 223 // return address always winds up in pc
michael@0 224 //stack_frame_entry_->initial_rules[ustr__ZDra()] // ".ra"
michael@0 225 // = stack_frame_entry_->initial_rules[ustr__pc()];
michael@0 226 // maybe don't need to do anything here?
michael@0 227
michael@0 228 // the final value of vsp is the new value of sp
michael@0 229 curr_rules_.mR13expr = vsp_;
michael@0 230
michael@0 231 // Finally, add the completed RuleSet to the SecMap
michael@0 232 if (curr_rules_.mLen > 0) {
michael@0 233
michael@0 234 // Futz with the rules for r4 .. r11 in the same way as happens
michael@0 235 // with the CFI summariser:
michael@0 236 /* Mark callee-saved registers (r4 .. r11) as unchanged, if there is
michael@0 237 no other information about them. FIXME: do this just once, at
michael@0 238 the point where the ruleset is committed. */
michael@0 239 if (curr_rules_.mR7expr.mHow == LExpr::UNKNOWN) {
michael@0 240 curr_rules_.mR7expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R7, 0);
michael@0 241 }
michael@0 242 if (curr_rules_.mR11expr.mHow == LExpr::UNKNOWN) {
michael@0 243 curr_rules_.mR11expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R11, 0);
michael@0 244 }
michael@0 245 if (curr_rules_.mR12expr.mHow == LExpr::UNKNOWN) {
michael@0 246 curr_rules_.mR12expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R12, 0);
michael@0 247 }
michael@0 248 if (curr_rules_.mR14expr.mHow == LExpr::UNKNOWN) {
michael@0 249 curr_rules_.mR14expr = LExpr(LExpr::NODEREF, DW_REG_ARM_R14, 0);
michael@0 250 }
michael@0 251
michael@0 252 // And add them
michael@0 253 smap_->AddRuleSet(&curr_rules_);
michael@0 254
michael@0 255 if (0) {
michael@0 256 curr_rules_.Print(log_);
michael@0 257 }
michael@0 258 if (0) {
michael@0 259 char buf[100];
michael@0 260 sprintf(buf, " SubmitStackFrame %llx .. %llx",
michael@0 261 (uint64_t)curr_rules_.mAddr,
michael@0 262 (uint64_t)(curr_rules_.mAddr + curr_rules_.mLen - 1));
michael@0 263 log_(buf);
michael@0 264 }
michael@0 265 }
michael@0 266 }
michael@0 267
michael@0 268
michael@0 269 #define ARM_EXIDX_CANT_UNWIND 0x00000001
michael@0 270 #define ARM_EXIDX_COMPACT 0x80000000
michael@0 271 #define ARM_EXTBL_OP_FINISH 0xb0
michael@0 272 #define ARM_EXIDX_TABLE_LIMIT (255*4)
michael@0 273
michael@0 274 using lul::ARM_EXIDX_CMD_FINISH;
michael@0 275 using lul::ARM_EXIDX_CMD_SUB_FROM_VSP;
michael@0 276 using lul::ARM_EXIDX_CMD_ADD_TO_VSP;
michael@0 277 using lul::ARM_EXIDX_CMD_REG_POP;
michael@0 278 using lul::ARM_EXIDX_CMD_REG_TO_SP;
michael@0 279 using lul::ARM_EXIDX_CMD_VFP_POP;
michael@0 280 using lul::ARM_EXIDX_CMD_WREG_POP;
michael@0 281 using lul::ARM_EXIDX_CMD_WCGR_POP;
michael@0 282 using lul::ARM_EXIDX_CMD_RESERVED;
michael@0 283 using lul::ARM_EXIDX_CMD_REFUSED;
michael@0 284 using lul::exidx_entry;
michael@0 285 using lul::ARM_EXIDX_VFP_SHIFT_16;
michael@0 286 using lul::ARM_EXIDX_VFP_FSTMD;
michael@0 287 using lul::MemoryRange;
michael@0 288
michael@0 289
michael@0 290 static void* Prel31ToAddr(const void* addr)
michael@0 291 {
michael@0 292 uint32_t offset32 = *reinterpret_cast<const uint32_t*>(addr);
michael@0 293 // sign extend offset32[30:0] to 64 bits -- copy bit 30 to positions
michael@0 294 // 63:31 inclusive.
michael@0 295 uint64_t offset64 = offset32;
michael@0 296 if (offset64 & (1ULL << 30))
michael@0 297 offset64 |= 0xFFFFFFFF80000000ULL;
michael@0 298 else
michael@0 299 offset64 &= 0x000000007FFFFFFFULL;
michael@0 300 return ((char*)addr) + (uintptr_t)offset64;
michael@0 301 }
michael@0 302
michael@0 303
michael@0 304 // Extract unwind bytecode for the function denoted by |entry| into |buf|,
michael@0 305 // and return the number of bytes of |buf| written, along with a code
michael@0 306 // indicating the outcome.
michael@0 307
michael@0 308 ExceptionTableInfo::ExExtractResult
michael@0 309 ExceptionTableInfo::ExtabEntryExtract(const struct exidx_entry* entry,
michael@0 310 uint8_t* buf, size_t buf_size,
michael@0 311 /*OUT*/size_t* buf_used)
michael@0 312 {
michael@0 313 MemoryRange mr_out(buf, buf_size);
michael@0 314
michael@0 315 *buf_used = 0;
michael@0 316
michael@0 317 # define PUT_BUF_U8(_byte) \
michael@0 318 do { if (!mr_out.Covers(*buf_used, 1)) return ExOutBufOverflow; \
michael@0 319 buf[(*buf_used)++] = (_byte); } while (0)
michael@0 320
michael@0 321 # define GET_EX_U32(_lval, _addr, _sec_mr) \
michael@0 322 do { if (!(_sec_mr).Covers(reinterpret_cast<const uint8_t*>(_addr) \
michael@0 323 - (_sec_mr).data(), 4)) \
michael@0 324 return ExInBufOverflow; \
michael@0 325 (_lval) = *(reinterpret_cast<const uint32_t*>(_addr)); } while (0)
michael@0 326
michael@0 327 # define GET_EXIDX_U32(_lval, _addr) \
michael@0 328 GET_EX_U32(_lval, _addr, mr_exidx_)
michael@0 329 # define GET_EXTAB_U32(_lval, _addr) \
michael@0 330 GET_EX_U32(_lval, _addr, mr_extab_)
michael@0 331
michael@0 332 uint32_t data;
michael@0 333 GET_EXIDX_U32(data, &entry->data);
michael@0 334
michael@0 335 // A function can be marked CANT_UNWIND if (eg) it is known to be
michael@0 336 // at the bottom of the stack.
michael@0 337 if (data == ARM_EXIDX_CANT_UNWIND)
michael@0 338 return ExCantUnwind;
michael@0 339
michael@0 340 uint32_t pers; // personality number
michael@0 341 uint32_t extra; // number of extra data words required
michael@0 342 uint32_t extra_allowed; // number of extra data words allowed
michael@0 343 uint32_t* extbl_data; // the handler entry, if not inlined
michael@0 344
michael@0 345 if (data & ARM_EXIDX_COMPACT) {
michael@0 346 // The handler table entry has been inlined into the index table entry.
michael@0 347 // In this case it can only be an ARM-defined compact model, since
michael@0 348 // bit 31 is 1. Only personalities 0, 1 and 2 are defined for the
michael@0 349 // ARM compact model, but 1 and 2 are "Long format" and may require
michael@0 350 // extra data words. Hence the allowable personalities here are:
michael@0 351 // personality 0, in which case 'extra' has no meaning
michael@0 352 // personality 1, with zero extra words
michael@0 353 // personality 2, with zero extra words
michael@0 354 extbl_data = nullptr;
michael@0 355 pers = (data >> 24) & 0x0F;
michael@0 356 extra = (data >> 16) & 0xFF;
michael@0 357 extra_allowed = 0;
michael@0 358 }
michael@0 359 else {
michael@0 360 // The index table entry is a pointer to the handler entry. Note
michael@0 361 // that Prel31ToAddr will read the given address, but we already
michael@0 362 // range-checked above.
michael@0 363 extbl_data = reinterpret_cast<uint32_t*>(Prel31ToAddr(&entry->data));
michael@0 364 GET_EXTAB_U32(data, extbl_data);
michael@0 365 if (!(data & ARM_EXIDX_COMPACT)) {
michael@0 366 // This denotes a "generic model" handler. That will involve
michael@0 367 // executing arbitary machine code, which is something we
michael@0 368 // can't represent here; hence reject it.
michael@0 369 return ExCantRepresent;
michael@0 370 }
michael@0 371 // So we have a compact model representation. Again, 3 possible
michael@0 372 // personalities, but this time up to 255 allowable extra words.
michael@0 373 pers = (data >> 24) & 0x0F;
michael@0 374 extra = (data >> 16) & 0xFF;
michael@0 375 extra_allowed = 255;
michael@0 376 extbl_data++;
michael@0 377 }
michael@0 378
michael@0 379 // Now look at the the handler table entry. The first word is
michael@0 380 // |data| and subsequent words start at |*extbl_data|. The number
michael@0 381 // of extra words to use is |extra|, provided that the personality
michael@0 382 // allows extra words. Even if it does, none may be available --
michael@0 383 // extra_allowed is the maximum number of extra words allowed. */
michael@0 384 if (pers == 0) {
michael@0 385 // "Su16" in the documentation -- 3 unwinding insn bytes
michael@0 386 // |extra| has no meaning here; instead that byte is an unwind-info byte
michael@0 387 PUT_BUF_U8(data >> 16);
michael@0 388 PUT_BUF_U8(data >> 8);
michael@0 389 PUT_BUF_U8(data);
michael@0 390 }
michael@0 391 else if ((pers == 1 || pers == 2) && extra <= extra_allowed) {
michael@0 392 // "Lu16" or "Lu32" respectively -- 2 unwinding insn bytes,
michael@0 393 // and up to 255 extra words.
michael@0 394 PUT_BUF_U8(data >> 8);
michael@0 395 PUT_BUF_U8(data);
michael@0 396 for (uint32_t j = 0; j < extra; j++) {
michael@0 397 GET_EXTAB_U32(data, extbl_data);
michael@0 398 extbl_data++;
michael@0 399 PUT_BUF_U8(data >> 24);
michael@0 400 PUT_BUF_U8(data >> 16);
michael@0 401 PUT_BUF_U8(data >> 8);
michael@0 402 PUT_BUF_U8(data >> 0);
michael@0 403 }
michael@0 404 }
michael@0 405 else {
michael@0 406 // The entry is invalid.
michael@0 407 return ExInvalid;
michael@0 408 }
michael@0 409
michael@0 410 // Make sure the entry is terminated with "FINISH"
michael@0 411 if (*buf_used > 0 && buf[(*buf_used) - 1] != ARM_EXTBL_OP_FINISH)
michael@0 412 PUT_BUF_U8(ARM_EXTBL_OP_FINISH);
michael@0 413
michael@0 414 return ExSuccess;
michael@0 415
michael@0 416 # undef GET_EXTAB_U32
michael@0 417 # undef GET_EXIDX_U32
michael@0 418 # undef GET_U32
michael@0 419 # undef PUT_BUF_U8
michael@0 420 }
michael@0 421
michael@0 422
michael@0 423 // Take the unwind information extracted by ExtabEntryExtract
michael@0 424 // and parse it into frame-unwind instructions. These are as
michael@0 425 // specified in "Table 4, ARM-defined frame-unwinding instructions"
michael@0 426 // in the specification document detailed in comments at the top
michael@0 427 // of this file.
michael@0 428 //
michael@0 429 // This reads from |buf[0, +data_size)|. It checks for overruns of
michael@0 430 // the input buffer and returns a negative value if that happens, or
michael@0 431 // for any other failure cases. It returns zero in case of success.
michael@0 432 int ExceptionTableInfo::ExtabEntryDecode(const uint8_t* buf, size_t buf_size)
michael@0 433 {
michael@0 434 if (buf == nullptr || buf_size == 0)
michael@0 435 return -1;
michael@0 436
michael@0 437 MemoryRange mr_in(buf, buf_size);
michael@0 438 const uint8_t* buf_initially = buf;
michael@0 439
michael@0 440 # define GET_BUF_U8(_lval) \
michael@0 441 do { if (!mr_in.Covers(buf - buf_initially, 1)) return -1; \
michael@0 442 (_lval) = *(buf++); } while (0)
michael@0 443
michael@0 444 const uint8_t* end = buf + buf_size;
michael@0 445
michael@0 446 while (buf < end) {
michael@0 447 struct lul::extab_data edata;
michael@0 448 memset(&edata, 0, sizeof(edata));
michael@0 449
michael@0 450 uint8_t op;
michael@0 451 GET_BUF_U8(op);
michael@0 452 if ((op & 0xc0) == 0x00) {
michael@0 453 // vsp = vsp + (xxxxxx << 2) + 4
michael@0 454 edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP;
michael@0 455 edata.data = (((int)op & 0x3f) << 2) + 4;
michael@0 456 }
michael@0 457 else if ((op & 0xc0) == 0x40) {
michael@0 458 // vsp = vsp - (xxxxxx << 2) - 4
michael@0 459 edata.cmd = ARM_EXIDX_CMD_SUB_FROM_VSP;
michael@0 460 edata.data = (((int)op & 0x3f) << 2) + 4;
michael@0 461 }
michael@0 462 else if ((op & 0xf0) == 0x80) {
michael@0 463 uint8_t op2;
michael@0 464 GET_BUF_U8(op2);
michael@0 465 if (op == 0x80 && op2 == 0x00) {
michael@0 466 // Refuse to unwind
michael@0 467 edata.cmd = ARM_EXIDX_CMD_REFUSED;
michael@0 468 } else {
michael@0 469 // Pop up to 12 integer registers under masks {r15-r12},{r11-r4}
michael@0 470 edata.cmd = ARM_EXIDX_CMD_REG_POP;
michael@0 471 edata.data = ((op & 0xf) << 8) | op2;
michael@0 472 edata.data = edata.data << 4;
michael@0 473 }
michael@0 474 }
michael@0 475 else if ((op & 0xf0) == 0x90) {
michael@0 476 if (op == 0x9d || op == 0x9f) {
michael@0 477 // 9d: Reserved as prefix for ARM register to register moves
michael@0 478 // 9f: Reserved as perfix for Intel Wireless MMX reg to reg moves
michael@0 479 edata.cmd = ARM_EXIDX_CMD_RESERVED;
michael@0 480 } else {
michael@0 481 // Set vsp = r[nnnn]
michael@0 482 edata.cmd = ARM_EXIDX_CMD_REG_TO_SP;
michael@0 483 edata.data = op & 0x0f;
michael@0 484 }
michael@0 485 }
michael@0 486 else if ((op & 0xf0) == 0xa0) {
michael@0 487 // Pop r4 to r[4+nnn], or
michael@0 488 // Pop r4 to r[4+nnn] and r14 or
michael@0 489 unsigned end = (op & 0x07);
michael@0 490 edata.data = (1 << (end + 1)) - 1;
michael@0 491 edata.data = edata.data << 4;
michael@0 492 if (op & 0x08) edata.data |= 1 << 14;
michael@0 493 edata.cmd = ARM_EXIDX_CMD_REG_POP;
michael@0 494 }
michael@0 495 else if (op == ARM_EXTBL_OP_FINISH) {
michael@0 496 // Finish
michael@0 497 edata.cmd = ARM_EXIDX_CMD_FINISH;
michael@0 498 buf = end;
michael@0 499 }
michael@0 500 else if (op == 0xb1) {
michael@0 501 uint8_t op2;
michael@0 502 GET_BUF_U8(op2);
michael@0 503 if (op2 == 0 || (op2 & 0xf0)) {
michael@0 504 // Spare
michael@0 505 edata.cmd = ARM_EXIDX_CMD_RESERVED;
michael@0 506 } else {
michael@0 507 // Pop integer registers under mask {r3,r2,r1,r0}
michael@0 508 edata.cmd = ARM_EXIDX_CMD_REG_POP;
michael@0 509 edata.data = op2 & 0x0f;
michael@0 510 }
michael@0 511 }
michael@0 512 else if (op == 0xb2) {
michael@0 513 // vsp = vsp + 0x204 + (uleb128 << 2)
michael@0 514 uint64_t offset = 0;
michael@0 515 uint8_t byte, shift = 0;
michael@0 516 do {
michael@0 517 GET_BUF_U8(byte);
michael@0 518 offset |= (byte & 0x7f) << shift;
michael@0 519 shift += 7;
michael@0 520 } while ((byte & 0x80) && buf < end);
michael@0 521 edata.data = offset * 4 + 0x204;
michael@0 522 edata.cmd = ARM_EXIDX_CMD_ADD_TO_VSP;
michael@0 523 }
michael@0 524 else if (op == 0xb3 || op == 0xc8 || op == 0xc9) {
michael@0 525 // b3: Pop VFP regs D[ssss] to D[ssss+cccc], FSTMFDX-ishly
michael@0 526 // c8: Pop VFP regs D[16+ssss] to D[16+ssss+cccc], FSTMFDD-ishly
michael@0 527 // c9: Pop VFP regs D[ssss] to D[ssss+cccc], FSTMFDD-ishly
michael@0 528 edata.cmd = ARM_EXIDX_CMD_VFP_POP;
michael@0 529 GET_BUF_U8(edata.data);
michael@0 530 if (op == 0xc8) edata.data |= ARM_EXIDX_VFP_SHIFT_16;
michael@0 531 if (op != 0xb3) edata.data |= ARM_EXIDX_VFP_FSTMD;
michael@0 532 }
michael@0 533 else if ((op & 0xf8) == 0xb8 || (op & 0xf8) == 0xd0) {
michael@0 534 // b8: Pop VFP regs D[8] to D[8+nnn], FSTMFDX-ishly
michael@0 535 // d0: Pop VFP regs D[8] to D[8+nnn], FSTMFDD-ishly
michael@0 536 edata.cmd = ARM_EXIDX_CMD_VFP_POP;
michael@0 537 edata.data = 0x80 | (op & 0x07);
michael@0 538 if ((op & 0xf8) == 0xd0) edata.data |= ARM_EXIDX_VFP_FSTMD;
michael@0 539 }
michael@0 540 else if (op >= 0xc0 && op <= 0xc5) {
michael@0 541 // Intel Wireless MMX pop wR[10]-wr[10+nnn], nnn != 6,7
michael@0 542 edata.cmd = ARM_EXIDX_CMD_WREG_POP;
michael@0 543 edata.data = 0xa0 | (op & 0x07);
michael@0 544 }
michael@0 545 else if (op == 0xc6) {
michael@0 546 // Intel Wireless MMX pop wR[ssss] to wR[ssss+cccc]
michael@0 547 edata.cmd = ARM_EXIDX_CMD_WREG_POP;
michael@0 548 GET_BUF_U8(edata.data);
michael@0 549 }
michael@0 550 else if (op == 0xc7) {
michael@0 551 uint8_t op2;
michael@0 552 GET_BUF_U8(op2);
michael@0 553 if (op2 == 0 || (op2 & 0xf0)) {
michael@0 554 // Spare
michael@0 555 edata.cmd = ARM_EXIDX_CMD_RESERVED;
michael@0 556 } else {
michael@0 557 // Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0}
michael@0 558 edata.cmd = ARM_EXIDX_CMD_WCGR_POP;
michael@0 559 edata.data = op2 & 0x0f;
michael@0 560 }
michael@0 561 }
michael@0 562 else {
michael@0 563 // Spare
michael@0 564 edata.cmd = ARM_EXIDX_CMD_RESERVED;
michael@0 565 }
michael@0 566
michael@0 567 int ret = handler_->ImproveStackFrame(&edata);
michael@0 568 if (ret < 0) return ret;
michael@0 569 }
michael@0 570 return 0;
michael@0 571
michael@0 572 # undef GET_BUF_U8
michael@0 573 }
michael@0 574
michael@0 575 void ExceptionTableInfo::Start()
michael@0 576 {
michael@0 577 const struct exidx_entry* start
michael@0 578 = reinterpret_cast<const struct exidx_entry*>(mr_exidx_.data());
michael@0 579 const struct exidx_entry* end
michael@0 580 = reinterpret_cast<const struct exidx_entry*>(mr_exidx_.data()
michael@0 581 + mr_exidx_.length());
michael@0 582
michael@0 583 // Iterate over each of the EXIDX entries (pairs of 32-bit words).
michael@0 584 // These occupy the entire .exidx section.
michael@0 585 for (const struct exidx_entry* entry = start; entry < end; ++entry) {
michael@0 586
michael@0 587 // Figure out the code address range that this table entry is
michael@0 588 // associated with.
michael@0 589 //
michael@0 590 // I don't claim to understand the biasing here. It appears that
michael@0 591 // (Prel31ToAddr(&entry->addr))
michael@0 592 // - mapping_addr_ + loading_addr_) & 0x7fffffff
michael@0 593 // produces a SVMA. Adding the text_bias_ gives plausible AVMAs.
michael@0 594 uint32_t svma = (reinterpret_cast<char*>(Prel31ToAddr(&entry->addr))
michael@0 595 - mapping_addr_ + loading_addr_) & 0x7fffffff;
michael@0 596 uint32_t next_svma;
michael@0 597 if (entry < end - 1) {
michael@0 598 next_svma = (reinterpret_cast<char*>(Prel31ToAddr(&((entry + 1)->addr)))
michael@0 599 - mapping_addr_ + loading_addr_) & 0x7fffffff;
michael@0 600 } else {
michael@0 601 // This is the last EXIDX entry in the sequence, so we don't
michael@0 602 // have an address for the start of the next function, to limit
michael@0 603 // this one. Instead use the address of the last byte of the
michael@0 604 // text section associated with this .exidx section, that we
michael@0 605 // have been given. So as to avoid junking up the CFI unwind
michael@0 606 // tables with absurdly large address ranges in the case where
michael@0 607 // text_last_svma_ is wrong, only use the value if it is nonzero
michael@0 608 // and within one page of |svma|. Otherwise assume a length of 1.
michael@0 609 //
michael@0 610 // In some cases, gcc has been observed to finish the exidx
michael@0 611 // section with an entry of length 1 marked CANT_UNWIND,
michael@0 612 // presumably exactly for the purpose of giving a definite
michael@0 613 // length for the last real entry, without having to look at
michael@0 614 // text segment boundaries.
michael@0 615 bool plausible = false;
michael@0 616 next_svma = svma + 1;
michael@0 617 if (text_last_svma_ != 0) {
michael@0 618 uint32_t maybe_next_svma = text_last_svma_ + 1;
michael@0 619 if (maybe_next_svma > svma && maybe_next_svma - svma <= 4096) {
michael@0 620 next_svma = maybe_next_svma;
michael@0 621 plausible = true;
michael@0 622 }
michael@0 623 }
michael@0 624 if (!plausible) {
michael@0 625 char buf[100];
michael@0 626 snprintf(buf, sizeof(buf),
michael@0 627 "ExceptionTableInfo: implausible EXIDX last entry size %d"
michael@0 628 "; using 1 instead.", (int32_t)(text_last_svma_ - svma));
michael@0 629 buf[sizeof(buf)-1] = 0;
michael@0 630 log_(buf);
michael@0 631 }
michael@0 632 }
michael@0 633
michael@0 634 // Extract the unwind info into |buf|. This might fail for
michael@0 635 // various reasons. It involves reading both the .exidx and
michael@0 636 // .extab sections. All accesses to those sections are
michael@0 637 // bounds-checked.
michael@0 638 uint8_t buf[ARM_EXIDX_TABLE_LIMIT];
michael@0 639 size_t buf_used = 0;
michael@0 640 ExExtractResult res = ExtabEntryExtract(entry, buf, sizeof(buf), &buf_used);
michael@0 641 if (res != ExSuccess) {
michael@0 642 // Couldn't extract the unwind info, for some reason. Move on.
michael@0 643 switch (res) {
michael@0 644 case ExInBufOverflow:
michael@0 645 log_("ExtabEntryExtract: .exidx/.extab section overrun");
michael@0 646 break;
michael@0 647 case ExOutBufOverflow:
michael@0 648 log_("ExtabEntryExtract: bytecode buffer overflow");
michael@0 649 break;
michael@0 650 case ExCantUnwind:
michael@0 651 log_("ExtabEntryExtract: function is marked CANT_UNWIND");
michael@0 652 break;
michael@0 653 case ExCantRepresent:
michael@0 654 log_("ExtabEntryExtract: bytecode can't be represented");
michael@0 655 break;
michael@0 656 case ExInvalid:
michael@0 657 log_("ExtabEntryExtract: index table entry is invalid");
michael@0 658 break;
michael@0 659 default: {
michael@0 660 char buf[100];
michael@0 661 snprintf(buf, sizeof(buf),
michael@0 662 "ExtabEntryExtract: unknown error: %d", (int)res);
michael@0 663 buf[sizeof(buf)-1] = 0;
michael@0 664 log_(buf);
michael@0 665 break;
michael@0 666 }
michael@0 667 }
michael@0 668 continue;
michael@0 669 }
michael@0 670
michael@0 671 // Finally, work through the unwind instructions in |buf| and
michael@0 672 // create CFI entries that Breakpad can use. This can also fail.
michael@0 673 // First, add a new stack frame entry, into which ExtabEntryDecode
michael@0 674 // will write the CFI entries.
michael@0 675 handler_->AddStackFrame(svma + text_bias_, next_svma - svma);
michael@0 676 int ret = ExtabEntryDecode(buf, buf_used);
michael@0 677 if (ret < 0) {
michael@0 678 handler_->DeleteStackFrame();
michael@0 679 char buf[100];
michael@0 680 snprintf(buf, sizeof(buf),
michael@0 681 "ExtabEntryDecode: failed with error code: %d", ret);
michael@0 682 buf[sizeof(buf)-1] = 0;
michael@0 683 log_(buf);
michael@0 684 continue;
michael@0 685 }
michael@0 686 handler_->SubmitStackFrame();
michael@0 687 } /* iterating over .exidx */
michael@0 688 }
michael@0 689
michael@0 690 } // namespace lul

mercurial