Tue, 06 Jan 2015 21:39:09 +0100
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 |