Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | // -*- mode: c++ -*- |
michael@0 | 2 | |
michael@0 | 3 | // Copyright (c) 2010 Google Inc. |
michael@0 | 4 | // All rights reserved. |
michael@0 | 5 | // |
michael@0 | 6 | // Redistribution and use in source and binary forms, with or without |
michael@0 | 7 | // modification, are permitted provided that the following conditions are |
michael@0 | 8 | // met: |
michael@0 | 9 | // |
michael@0 | 10 | // * Redistributions of source code must retain the above copyright |
michael@0 | 11 | // notice, this list of conditions and the following disclaimer. |
michael@0 | 12 | // * Redistributions in binary form must reproduce the above |
michael@0 | 13 | // copyright notice, this list of conditions and the following disclaimer |
michael@0 | 14 | // in the documentation and/or other materials provided with the |
michael@0 | 15 | // distribution. |
michael@0 | 16 | // * Neither the name of Google Inc. nor the names of its |
michael@0 | 17 | // contributors may be used to endorse or promote products derived from |
michael@0 | 18 | // this software without specific prior written permission. |
michael@0 | 19 | // |
michael@0 | 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
michael@0 | 21 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
michael@0 | 22 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
michael@0 | 23 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
michael@0 | 24 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
michael@0 | 25 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
michael@0 | 26 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
michael@0 | 27 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
michael@0 | 28 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
michael@0 | 29 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
michael@0 | 30 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
michael@0 | 31 | |
michael@0 | 32 | // postfix_evaluator-inl.h: Postfix (reverse Polish) notation expression |
michael@0 | 33 | // evaluator. |
michael@0 | 34 | // |
michael@0 | 35 | // Documentation in postfix_evaluator.h. |
michael@0 | 36 | // |
michael@0 | 37 | // Author: Mark Mentovai |
michael@0 | 38 | |
michael@0 | 39 | #ifndef PROCESSOR_POSTFIX_EVALUATOR_INL_H__ |
michael@0 | 40 | #define PROCESSOR_POSTFIX_EVALUATOR_INL_H__ |
michael@0 | 41 | |
michael@0 | 42 | #include "processor/postfix_evaluator.h" |
michael@0 | 43 | |
michael@0 | 44 | #include <stdio.h> |
michael@0 | 45 | |
michael@0 | 46 | #include <sstream> |
michael@0 | 47 | |
michael@0 | 48 | #include "google_breakpad/processor/memory_region.h" |
michael@0 | 49 | #include "common/logging.h" |
michael@0 | 50 | |
michael@0 | 51 | namespace google_breakpad { |
michael@0 | 52 | |
michael@0 | 53 | using std::istringstream; |
michael@0 | 54 | using std::ostringstream; |
michael@0 | 55 | |
michael@0 | 56 | |
michael@0 | 57 | // A small class used in Evaluate to make sure to clean up the stack |
michael@0 | 58 | // before returning failure. |
michael@0 | 59 | template<typename ValueType> |
michael@0 | 60 | class AutoStackClearer { |
michael@0 | 61 | public: |
michael@0 | 62 | explicit AutoStackClearer(vector<StackElem<ValueType> > *stack) |
michael@0 | 63 | : stack_(stack) {} |
michael@0 | 64 | ~AutoStackClearer() { stack_->clear(); } |
michael@0 | 65 | |
michael@0 | 66 | private: |
michael@0 | 67 | vector<StackElem<ValueType> > *stack_; |
michael@0 | 68 | }; |
michael@0 | 69 | |
michael@0 | 70 | |
michael@0 | 71 | template<typename ValueType> |
michael@0 | 72 | bool PostfixEvaluator<ValueType>::EvaluateToken( |
michael@0 | 73 | const string &token, |
michael@0 | 74 | const string &expression, |
michael@0 | 75 | DictionaryValidityType *assigned) { |
michael@0 | 76 | // There are enough binary operations that do exactly the same thing |
michael@0 | 77 | // (other than the specific operation, of course) that it makes sense |
michael@0 | 78 | // to share as much code as possible. |
michael@0 | 79 | enum BinaryOperation { |
michael@0 | 80 | BINARY_OP_NONE = 0, |
michael@0 | 81 | BINARY_OP_ADD, |
michael@0 | 82 | BINARY_OP_SUBTRACT, |
michael@0 | 83 | BINARY_OP_MULTIPLY, |
michael@0 | 84 | BINARY_OP_DIVIDE_QUOTIENT, |
michael@0 | 85 | BINARY_OP_DIVIDE_MODULUS, |
michael@0 | 86 | BINARY_OP_ALIGN |
michael@0 | 87 | }; |
michael@0 | 88 | |
michael@0 | 89 | BinaryOperation operation = BINARY_OP_NONE; |
michael@0 | 90 | if (token == "+") |
michael@0 | 91 | operation = BINARY_OP_ADD; |
michael@0 | 92 | else if (token == "-") |
michael@0 | 93 | operation = BINARY_OP_SUBTRACT; |
michael@0 | 94 | else if (token == "*") |
michael@0 | 95 | operation = BINARY_OP_MULTIPLY; |
michael@0 | 96 | else if (token == "/") |
michael@0 | 97 | operation = BINARY_OP_DIVIDE_QUOTIENT; |
michael@0 | 98 | else if (token == "%") |
michael@0 | 99 | operation = BINARY_OP_DIVIDE_MODULUS; |
michael@0 | 100 | else if (token == "@") |
michael@0 | 101 | operation = BINARY_OP_ALIGN; |
michael@0 | 102 | |
michael@0 | 103 | if (operation != BINARY_OP_NONE) { |
michael@0 | 104 | // Get the operands. |
michael@0 | 105 | ValueType operand1 = ValueType(); |
michael@0 | 106 | ValueType operand2 = ValueType(); |
michael@0 | 107 | if (!PopValues(&operand1, &operand2)) { |
michael@0 | 108 | BPLOG(ERROR) << "Could not PopValues to get two values for binary " |
michael@0 | 109 | "operation " << token << ": " << expression; |
michael@0 | 110 | return false; |
michael@0 | 111 | } |
michael@0 | 112 | |
michael@0 | 113 | // Perform the operation. |
michael@0 | 114 | ValueType result; |
michael@0 | 115 | switch (operation) { |
michael@0 | 116 | case BINARY_OP_ADD: |
michael@0 | 117 | result = operand1 + operand2; |
michael@0 | 118 | break; |
michael@0 | 119 | case BINARY_OP_SUBTRACT: |
michael@0 | 120 | result = operand1 - operand2; |
michael@0 | 121 | break; |
michael@0 | 122 | case BINARY_OP_MULTIPLY: |
michael@0 | 123 | result = operand1 * operand2; |
michael@0 | 124 | break; |
michael@0 | 125 | case BINARY_OP_DIVIDE_QUOTIENT: |
michael@0 | 126 | result = operand1 / operand2; |
michael@0 | 127 | break; |
michael@0 | 128 | case BINARY_OP_DIVIDE_MODULUS: |
michael@0 | 129 | result = operand1 % operand2; |
michael@0 | 130 | break; |
michael@0 | 131 | case BINARY_OP_ALIGN: |
michael@0 | 132 | result = |
michael@0 | 133 | operand1 & (static_cast<ValueType>(-1) ^ (operand2 - 1)); |
michael@0 | 134 | break; |
michael@0 | 135 | case BINARY_OP_NONE: |
michael@0 | 136 | // This will not happen, but compilers will want a default or |
michael@0 | 137 | // BINARY_OP_NONE case. |
michael@0 | 138 | BPLOG(ERROR) << "Not reached!"; |
michael@0 | 139 | return false; |
michael@0 | 140 | break; |
michael@0 | 141 | } |
michael@0 | 142 | |
michael@0 | 143 | // Save the result. |
michael@0 | 144 | PushValue(result); |
michael@0 | 145 | } else if (token == "^") { |
michael@0 | 146 | // ^ for unary dereference. Can't dereference without memory. |
michael@0 | 147 | if (!memory_) { |
michael@0 | 148 | BPLOG(ERROR) << "Attempt to dereference without memory: " << |
michael@0 | 149 | expression; |
michael@0 | 150 | return false; |
michael@0 | 151 | } |
michael@0 | 152 | |
michael@0 | 153 | ValueType address; |
michael@0 | 154 | if (!PopValue(&address)) { |
michael@0 | 155 | BPLOG(ERROR) << "Could not PopValue to get value to dereference: " << |
michael@0 | 156 | expression; |
michael@0 | 157 | return false; |
michael@0 | 158 | } |
michael@0 | 159 | |
michael@0 | 160 | ValueType value; |
michael@0 | 161 | if (!memory_->GetMemoryAtAddress(address, &value)) { |
michael@0 | 162 | BPLOG(ERROR) << "Could not dereference memory at address " << |
michael@0 | 163 | HexString(address) << ": " << expression; |
michael@0 | 164 | return false; |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | PushValue(value); |
michael@0 | 168 | } else if (token == "=") { |
michael@0 | 169 | // = for assignment. |
michael@0 | 170 | ValueType value; |
michael@0 | 171 | if (!PopValue(&value)) { |
michael@0 | 172 | BPLOG(INFO) << "Could not PopValue to get value to assign: " << |
michael@0 | 173 | expression; |
michael@0 | 174 | return false; |
michael@0 | 175 | } |
michael@0 | 176 | |
michael@0 | 177 | // Assignment is only meaningful when assigning into an identifier. |
michael@0 | 178 | // The identifier must name a variable, not a constant. Variables |
michael@0 | 179 | // begin with '$'. |
michael@0 | 180 | const UniqueString* identifier; |
michael@0 | 181 | if (PopValueOrIdentifier(NULL, &identifier) != POP_RESULT_IDENTIFIER) { |
michael@0 | 182 | BPLOG(ERROR) << "PopValueOrIdentifier returned a value, but an " |
michael@0 | 183 | "identifier is needed to assign " << |
michael@0 | 184 | HexString(value) << ": " << expression; |
michael@0 | 185 | return false; |
michael@0 | 186 | } |
michael@0 | 187 | if (identifier == ustr__empty() || Index(identifier,0) != '$') { |
michael@0 | 188 | BPLOG(ERROR) << "Can't assign " << HexString(value) << " to " << |
michael@0 | 189 | identifier << ": " << expression; |
michael@0 | 190 | return false; |
michael@0 | 191 | } |
michael@0 | 192 | |
michael@0 | 193 | dictionary_->set(identifier, value); |
michael@0 | 194 | if (assigned) |
michael@0 | 195 | assigned->set(identifier, true); |
michael@0 | 196 | } else { |
michael@0 | 197 | // Push it onto the stack as-is, but first convert it either to a |
michael@0 | 198 | // ValueType (if a literal) or to a UniqueString* (if an identifier). |
michael@0 | 199 | // |
michael@0 | 200 | // First, try to treat the value as a literal. Literals may have leading |
michael@0 | 201 | // '-' sign, and the entire remaining string must be parseable as |
michael@0 | 202 | // ValueType. If this isn't possible, it can't be a literal, so treat it |
michael@0 | 203 | // as an identifier instead. |
michael@0 | 204 | // |
michael@0 | 205 | // Some versions of the libstdc++, the GNU standard C++ library, have |
michael@0 | 206 | // stream extractors for unsigned integer values that permit a leading |
michael@0 | 207 | // '-' sign (6.0.13); others do not (6.0.9). Since we require it, we |
michael@0 | 208 | // handle it explicitly here. |
michael@0 | 209 | istringstream token_stream(token); |
michael@0 | 210 | ValueType literal = ValueType(); |
michael@0 | 211 | bool negative = false; |
michael@0 | 212 | if (token_stream.peek() == '-') { |
michael@0 | 213 | negative = true; |
michael@0 | 214 | token_stream.get(); |
michael@0 | 215 | } |
michael@0 | 216 | if (token_stream >> literal && token_stream.peek() == EOF) { |
michael@0 | 217 | PushValue(negative ? (-literal) : literal); |
michael@0 | 218 | } else { |
michael@0 | 219 | PushIdentifier(ToUniqueString(token)); |
michael@0 | 220 | } |
michael@0 | 221 | } |
michael@0 | 222 | return true; |
michael@0 | 223 | } |
michael@0 | 224 | |
michael@0 | 225 | template<typename ValueType> |
michael@0 | 226 | bool PostfixEvaluator<ValueType>::EvaluateInternal( |
michael@0 | 227 | const string &expression, |
michael@0 | 228 | DictionaryValidityType *assigned) { |
michael@0 | 229 | // Tokenize, splitting on whitespace. |
michael@0 | 230 | istringstream stream(expression); |
michael@0 | 231 | string token; |
michael@0 | 232 | while (stream >> token) { |
michael@0 | 233 | // Normally, tokens are whitespace-separated, but occasionally, the |
michael@0 | 234 | // assignment operator is smashed up against the next token, i.e. |
michael@0 | 235 | // $T0 $ebp 128 + =$eip $T0 4 + ^ =$ebp $T0 ^ = |
michael@0 | 236 | // This has been observed in program strings produced by MSVS 2010 in LTO |
michael@0 | 237 | // mode. |
michael@0 | 238 | if (token.size() > 1 && token[0] == '=') { |
michael@0 | 239 | if (!EvaluateToken("=", expression, assigned)) { |
michael@0 | 240 | return false; |
michael@0 | 241 | } |
michael@0 | 242 | |
michael@0 | 243 | if (!EvaluateToken(token.substr(1), expression, assigned)) { |
michael@0 | 244 | return false; |
michael@0 | 245 | } |
michael@0 | 246 | } else if (!EvaluateToken(token, expression, assigned)) { |
michael@0 | 247 | return false; |
michael@0 | 248 | } |
michael@0 | 249 | } |
michael@0 | 250 | |
michael@0 | 251 | return true; |
michael@0 | 252 | } |
michael@0 | 253 | |
michael@0 | 254 | template<typename ValueType> |
michael@0 | 255 | bool PostfixEvaluator<ValueType>::Evaluate(const Module::Expr& expr, |
michael@0 | 256 | DictionaryValidityType* assigned) { |
michael@0 | 257 | // The expression is being exevaluated only for its side effects. Skip |
michael@0 | 258 | // expressions that denote values only. |
michael@0 | 259 | if (expr.how_ != Module::kExprPostfix) { |
michael@0 | 260 | BPLOG(ERROR) << "Can't evaluate for side-effects: " << expr; |
michael@0 | 261 | return false; |
michael@0 | 262 | } |
michael@0 | 263 | |
michael@0 | 264 | // Ensure that the stack is cleared before returning. |
michael@0 | 265 | AutoStackClearer<ValueType> clearer(&stack_); |
michael@0 | 266 | |
michael@0 | 267 | if (!EvaluateInternal(expr.postfix_, assigned)) |
michael@0 | 268 | return false; |
michael@0 | 269 | |
michael@0 | 270 | // If there's anything left on the stack, it indicates incomplete execution. |
michael@0 | 271 | // This is a failure case. If the stack is empty, evalution was complete |
michael@0 | 272 | // and successful. |
michael@0 | 273 | if (stack_.empty()) |
michael@0 | 274 | return true; |
michael@0 | 275 | |
michael@0 | 276 | BPLOG(ERROR) << "Incomplete execution: " << expr; |
michael@0 | 277 | return false; |
michael@0 | 278 | } |
michael@0 | 279 | |
michael@0 | 280 | template<typename ValueType> |
michael@0 | 281 | bool PostfixEvaluator<ValueType>::EvaluateForValue(const Module::Expr& expr, |
michael@0 | 282 | ValueType* result) { |
michael@0 | 283 | switch (expr.how_) { |
michael@0 | 284 | |
michael@0 | 285 | // Postfix expression. Give to the evaluator and return the |
michael@0 | 286 | // one-and-only stack element that should be left over. |
michael@0 | 287 | case Module::kExprPostfix: { |
michael@0 | 288 | // Ensure that the stack is cleared before returning. |
michael@0 | 289 | AutoStackClearer<ValueType> clearer(&stack_); |
michael@0 | 290 | |
michael@0 | 291 | if (!EvaluateInternal(expr.postfix_, NULL)) |
michael@0 | 292 | return false; |
michael@0 | 293 | |
michael@0 | 294 | // A successful execution should leave exactly one value on the stack. |
michael@0 | 295 | if (stack_.size() != 1) { |
michael@0 | 296 | BPLOG(ERROR) << "Expression yielded bad number of results: " |
michael@0 | 297 | << "'" << expr << "'"; |
michael@0 | 298 | return false; |
michael@0 | 299 | } |
michael@0 | 300 | |
michael@0 | 301 | return PopValue(result); |
michael@0 | 302 | } |
michael@0 | 303 | |
michael@0 | 304 | // Simple-form expressions |
michael@0 | 305 | case Module::kExprSimple: |
michael@0 | 306 | case Module::kExprSimpleMem: { |
michael@0 | 307 | // Look up the base value |
michael@0 | 308 | bool found = false; |
michael@0 | 309 | ValueType v = dictionary_->get(&found, expr.ident_); |
michael@0 | 310 | if (!found) { |
michael@0 | 311 | // The identifier wasn't found in the dictionary. Don't imply any |
michael@0 | 312 | // default value, just fail. |
michael@0 | 313 | static uint64_t n_complaints = 0; // This isn't threadsafe. |
michael@0 | 314 | n_complaints++; |
michael@0 | 315 | if (is_power_of_2(n_complaints)) { |
michael@0 | 316 | BPLOG(INFO) << "Identifier " << FromUniqueString(expr.ident_) |
michael@0 | 317 | << " not in dictionary (kExprSimple{Mem})" |
michael@0 | 318 | << " (shown " << n_complaints << " times)"; |
michael@0 | 319 | } |
michael@0 | 320 | return false; |
michael@0 | 321 | } |
michael@0 | 322 | |
michael@0 | 323 | // Form the sum |
michael@0 | 324 | ValueType sum = v + (int64_t)expr.offset_; |
michael@0 | 325 | |
michael@0 | 326 | // and dereference if necessary |
michael@0 | 327 | if (expr.how_ == Module::kExprSimpleMem) { |
michael@0 | 328 | ValueType derefd; |
michael@0 | 329 | if (!memory_ || !memory_->GetMemoryAtAddress(sum, &derefd)) { |
michael@0 | 330 | return false; |
michael@0 | 331 | } |
michael@0 | 332 | *result = derefd; |
michael@0 | 333 | } else { |
michael@0 | 334 | *result = sum; |
michael@0 | 335 | } |
michael@0 | 336 | return true; |
michael@0 | 337 | } |
michael@0 | 338 | |
michael@0 | 339 | default: |
michael@0 | 340 | return false; |
michael@0 | 341 | } |
michael@0 | 342 | } |
michael@0 | 343 | |
michael@0 | 344 | |
michael@0 | 345 | template<typename ValueType> |
michael@0 | 346 | typename PostfixEvaluator<ValueType>::PopResult |
michael@0 | 347 | PostfixEvaluator<ValueType>::PopValueOrIdentifier( |
michael@0 | 348 | ValueType *value, const UniqueString** identifier) { |
michael@0 | 349 | // There needs to be at least one element on the stack to pop. |
michael@0 | 350 | if (!stack_.size()) |
michael@0 | 351 | return POP_RESULT_FAIL; |
michael@0 | 352 | |
michael@0 | 353 | StackElem<ValueType> el = stack_.back(); |
michael@0 | 354 | stack_.pop_back(); |
michael@0 | 355 | |
michael@0 | 356 | if (el.isValue) { |
michael@0 | 357 | if (value) |
michael@0 | 358 | *value = el.u.val; |
michael@0 | 359 | return POP_RESULT_VALUE; |
michael@0 | 360 | } else { |
michael@0 | 361 | if (identifier) |
michael@0 | 362 | *identifier = el.u.ustr; |
michael@0 | 363 | return POP_RESULT_IDENTIFIER; |
michael@0 | 364 | } |
michael@0 | 365 | } |
michael@0 | 366 | |
michael@0 | 367 | |
michael@0 | 368 | template<typename ValueType> |
michael@0 | 369 | bool PostfixEvaluator<ValueType>::PopValue(ValueType *value) { |
michael@0 | 370 | ValueType literal = ValueType(); |
michael@0 | 371 | const UniqueString* token; |
michael@0 | 372 | PopResult result; |
michael@0 | 373 | if ((result = PopValueOrIdentifier(&literal, &token)) == POP_RESULT_FAIL) { |
michael@0 | 374 | return false; |
michael@0 | 375 | } else if (result == POP_RESULT_VALUE) { |
michael@0 | 376 | // This is the easy case. |
michael@0 | 377 | *value = literal; |
michael@0 | 378 | } else { // result == POP_RESULT_IDENTIFIER |
michael@0 | 379 | // There was an identifier at the top of the stack. Resolve it to a |
michael@0 | 380 | // value by looking it up in the dictionary. |
michael@0 | 381 | bool found = false; |
michael@0 | 382 | ValueType v = dictionary_->get(&found, token); |
michael@0 | 383 | if (!found) { |
michael@0 | 384 | // The identifier wasn't found in the dictionary. Don't imply any |
michael@0 | 385 | // default value, just fail. |
michael@0 | 386 | BPLOG(INFO) << "Identifier " << FromUniqueString(token) |
michael@0 | 387 | << " not in dictionary"; |
michael@0 | 388 | return false; |
michael@0 | 389 | } |
michael@0 | 390 | |
michael@0 | 391 | *value = v; |
michael@0 | 392 | } |
michael@0 | 393 | |
michael@0 | 394 | return true; |
michael@0 | 395 | } |
michael@0 | 396 | |
michael@0 | 397 | |
michael@0 | 398 | template<typename ValueType> |
michael@0 | 399 | bool PostfixEvaluator<ValueType>::PopValues(ValueType *value1, |
michael@0 | 400 | ValueType *value2) { |
michael@0 | 401 | return PopValue(value2) && PopValue(value1); |
michael@0 | 402 | } |
michael@0 | 403 | |
michael@0 | 404 | |
michael@0 | 405 | template<typename ValueType> |
michael@0 | 406 | void PostfixEvaluator<ValueType>::PushValue(const ValueType &value) { |
michael@0 | 407 | StackElem<ValueType> el(value); |
michael@0 | 408 | stack_.push_back(el); |
michael@0 | 409 | } |
michael@0 | 410 | |
michael@0 | 411 | template<typename ValueType> |
michael@0 | 412 | void PostfixEvaluator<ValueType>::PushIdentifier(const UniqueString* str) { |
michael@0 | 413 | StackElem<ValueType> el(str); |
michael@0 | 414 | stack_.push_back(el); |
michael@0 | 415 | } |
michael@0 | 416 | |
michael@0 | 417 | |
michael@0 | 418 | } // namespace google_breakpad |
michael@0 | 419 | |
michael@0 | 420 | |
michael@0 | 421 | #endif // PROCESSOR_POSTFIX_EVALUATOR_INL_H__ |