// -*- mode: c++ -*- // Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // postfix_evaluator-inl.h: Postfix (reverse Polish) notation expression // evaluator. // // Documentation in postfix_evaluator.h. // // Author: Mark Mentovai #ifndef PROCESSOR_POSTFIX_EVALUATOR_INL_H__ #define PROCESSOR_POSTFIX_EVALUATOR_INL_H__ #include "processor/postfix_evaluator.h" #include #include #include "google_breakpad/processor/memory_region.h" #include "processor/logging.h" namespace google_breakpad { using std::istringstream; using std::ostringstream; // A small class used in Evaluate to make sure to clean up the stack // before returning failure. class AutoStackClearer { public: explicit AutoStackClearer(vector* stack) : stack_(stack) {} ~AutoStackClearer() { stack_->clear(); } private: vector* stack_; }; template bool PostfixEvaluator::EvaluateToken( const string& token, const string& expression, DictionaryValidityType* assigned) { // There are enough binary operations that do exactly the same thing // (other than the specific operation, of course) that it makes sense // to share as much code as possible. enum BinaryOperation { BINARY_OP_NONE = 0, BINARY_OP_ADD, BINARY_OP_SUBTRACT, BINARY_OP_MULTIPLY, BINARY_OP_DIVIDE_QUOTIENT, BINARY_OP_DIVIDE_MODULUS, BINARY_OP_ALIGN }; BinaryOperation operation = BINARY_OP_NONE; if (token == "+") operation = BINARY_OP_ADD; else if (token == "-") operation = BINARY_OP_SUBTRACT; else if (token == "*") operation = BINARY_OP_MULTIPLY; else if (token == "/") operation = BINARY_OP_DIVIDE_QUOTIENT; else if (token == "%") operation = BINARY_OP_DIVIDE_MODULUS; else if (token == "@") operation = BINARY_OP_ALIGN; if (operation != BINARY_OP_NONE) { // Get the operands. ValueType operand1 = ValueType(); ValueType operand2 = ValueType(); if (!PopValues(&operand1, &operand2)) { BPLOG(ERROR) << "Could not PopValues to get two values for binary " "operation " << token << ": " << expression; return false; } // Perform the operation. ValueType result; switch (operation) { case BINARY_OP_ADD: result = operand1 + operand2; break; case BINARY_OP_SUBTRACT: result = operand1 - operand2; break; case BINARY_OP_MULTIPLY: result = operand1 * operand2; break; case BINARY_OP_DIVIDE_QUOTIENT: result = operand1 / operand2; break; case BINARY_OP_DIVIDE_MODULUS: result = operand1 % operand2; break; case BINARY_OP_ALIGN: result = operand1 & (static_cast(-1) ^ (operand2 - 1)); break; case BINARY_OP_NONE: // This will not happen, but compilers will want a default or // BINARY_OP_NONE case. BPLOG(ERROR) << "Not reached!"; return false; break; } // Save the result. PushValue(result); } else if (token == "^") { // ^ for unary dereference. Can't dereference without memory. if (!memory_) { BPLOG(ERROR) << "Attempt to dereference without memory: " << expression; return false; } ValueType address; if (!PopValue(&address)) { BPLOG(ERROR) << "Could not PopValue to get value to derefence: " << expression; return false; } ValueType value; if (!memory_->GetMemoryAtAddress(address, &value)) { BPLOG(ERROR) << "Could not dereference memory at address " << HexString(address) << ": " << expression; return false; } PushValue(value); } else if (token == "=") { // = for assignment. ValueType value; if (!PopValue(&value)) { BPLOG(INFO) << "Could not PopValue to get value to assign: " << expression; return false; } // Assignment is only meaningful when assigning into an identifier. // The identifier must name a variable, not a constant. Variables // begin with '$'. string identifier; if (PopValueOrIdentifier(NULL, &identifier) != POP_RESULT_IDENTIFIER) { BPLOG(ERROR) << "PopValueOrIdentifier returned a value, but an " "identifier is needed to assign " << HexString(value) << ": " << expression; return false; } if (identifier.empty() || identifier[0] != '$') { BPLOG(ERROR) << "Can't assign " << HexString(value) << " to " << identifier << ": " << expression; return false; } (*dictionary_)[identifier] = value; if (assigned) (*assigned)[identifier] = true; } else { // The token is not an operator, it's a literal value or an identifier. // Push it onto the stack as-is. Use push_back instead of PushValue // because PushValue pushes ValueType as a string, but token is already // a string. stack_.push_back(token); } return true; } template bool PostfixEvaluator::EvaluateInternal( const string& expression, DictionaryValidityType* assigned) { // Tokenize, splitting on whitespace. istringstream stream(expression); string token; while (stream >> token) { // Normally, tokens are whitespace-separated, but occasionally, the // assignment operator is smashed up against the next token, i.e. // $T0 $ebp 128 + =$eip $T0 4 + ^ =$ebp $T0 ^ = // This has been observed in program strings produced by MSVS 2010 in LTO // mode. if (token.size() > 1 && token[0] == '=') { if (!EvaluateToken("=", expression, assigned)) { return false; } if (!EvaluateToken(token.substr(1), expression, assigned)) { return false; } } else if (!EvaluateToken(token, expression, assigned)) { return false; } } return true; } template bool PostfixEvaluator::Evaluate(const string& expression, DictionaryValidityType* assigned) { // Ensure that the stack is cleared before returning. AutoStackClearer clearer(&stack_); if (!EvaluateInternal(expression, assigned)) return false; // If there's anything left on the stack, it indicates incomplete execution. // This is a failure case. If the stack is empty, evalution was complete // and successful. if (stack_.empty()) return true; BPLOG(ERROR) << "Incomplete execution: " << expression; return false; } template bool PostfixEvaluator::EvaluateForValue(const string& expression, ValueType* result) { // Ensure that the stack is cleared before returning. AutoStackClearer clearer(&stack_); if (!EvaluateInternal(expression, NULL)) return false; // A successful execution should leave exactly one value on the stack. if (stack_.size() != 1) { BPLOG(ERROR) << "Expression yielded bad number of results: " << "'" << expression << "'"; return false; } return PopValue(result); } template typename PostfixEvaluator::PopResult PostfixEvaluator::PopValueOrIdentifier( ValueType* value, string* identifier) { // There needs to be at least one element on the stack to pop. if (!stack_.size()) return POP_RESULT_FAIL; string token = stack_.back(); stack_.pop_back(); // First, try to treat the value as a literal. Literals may have leading // '-' sign, and the entire remaining string must be parseable as // ValueType. If this isn't possible, it can't be a literal, so treat it // as an identifier instead. // // Some versions of the libstdc++, the GNU standard C++ library, have // stream extractors for unsigned integer values that permit a leading // '-' sign (6.0.13); others do not (6.0.9). Since we require it, we // handle it explicitly here. istringstream token_stream(token); ValueType literal = ValueType(); bool negative; if (token_stream.peek() == '-') { negative = true; token_stream.get(); } else { negative = false; } if (token_stream >> literal && token_stream.peek() == EOF) { if (value) { *value = literal; } if (negative) *value = -*value; return POP_RESULT_VALUE; } else { if (identifier) { *identifier = token; } return POP_RESULT_IDENTIFIER; } } template bool PostfixEvaluator::PopValue(ValueType* value) { ValueType literal = ValueType(); string token; PopResult result; if ((result = PopValueOrIdentifier(&literal, &token)) == POP_RESULT_FAIL) { return false; } else if (result == POP_RESULT_VALUE) { // This is the easy case. *value = literal; } else { // result == POP_RESULT_IDENTIFIER // There was an identifier at the top of the stack. Resolve it to a // value by looking it up in the dictionary. typename DictionaryType::const_iterator iterator = dictionary_->find(token); if (iterator == dictionary_->end()) { // The identifier wasn't found in the dictionary. Don't imply any // default value, just fail. BPLOG(INFO) << "Identifier " << token << " not in dictionary"; return false; } *value = iterator->second; } return true; } template bool PostfixEvaluator::PopValues(ValueType* value1, ValueType* value2) { return PopValue(value2) && PopValue(value1); } template void PostfixEvaluator::PushValue(const ValueType& value) { ostringstream token_stream; token_stream << value; stack_.push_back(token_stream.str()); } } // namespace google_breakpad #endif // PROCESSOR_POSTFIX_EVALUATOR_INL_H__