// 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. // Original author: Jim Blandy // test_assembler.cc: Implementation of google_breakpad::TestAssembler. // See test_assembler.h for details. #include "common/test_assembler.h" #include #include #include namespace google_breakpad { namespace test_assembler { using std::back_insert_iterator; Label::Label() : value_(new Binding()) { } Label::Label(uint64_t value) : value_(new Binding(value)) { } Label::Label(const Label& label) { value_ = label.value_; value_->Acquire(); } Label::~Label() { if (value_->Release()) delete value_; } Label& Label::operator=(uint64_t value) { value_->Set(NULL, value); return *this; } Label& Label::operator=(const Label& label) { value_->Set(label.value_, 0); return *this; } Label Label::operator+(uint64_t addend) const { Label l; l.value_->Set(this->value_, addend); return l; } Label Label::operator-(uint64_t subtrahend) const { Label l; l.value_->Set(this->value_, -subtrahend); return l; } // When NDEBUG is #defined, assert doesn't evaluate its argument. This // means you can't simply use assert to check the return value of a // function with necessary side effects. // // ALWAYS_EVALUATE_AND_ASSERT(x) evaluates x regardless of whether // NDEBUG is #defined; when NDEBUG is not #defined, it further asserts // that x is true. #ifdef NDEBUG #define ALWAYS_EVALUATE_AND_ASSERT(x) x #else #define ALWAYS_EVALUATE_AND_ASSERT(x) assert(x) #endif uint64_t Label::operator-(const Label& label) const { uint64_t offset; ALWAYS_EVALUATE_AND_ASSERT(IsKnownOffsetFrom(label, &offset)); return offset; } uint64_t Label::Value() const { uint64_t v = 0; ALWAYS_EVALUATE_AND_ASSERT(IsKnownConstant(&v)); return v; }; bool Label::IsKnownConstant(uint64_t* value_p) const { Binding* base; uint64_t addend; value_->Get(&base, &addend); if (base != NULL) return false; if (value_p) *value_p = addend; return true; } bool Label::IsKnownOffsetFrom(const Label& label, uint64_t* offset_p) const { Binding* label_base, *this_base; uint64_t label_addend, this_addend; label.value_->Get(&label_base, &label_addend); value_->Get(&this_base, &this_addend); // If this and label are related, Get will find their final // common ancestor, regardless of how indirect the relation is. This // comparison also handles the constant vs. constant case. if (this_base != label_base) return false; if (offset_p) *offset_p = this_addend - label_addend; return true; } Label::Binding::Binding() : base_(this), addend_(), reference_count_(1) { } Label::Binding::Binding(uint64_t addend) : base_(NULL), addend_(addend), reference_count_(1) { } Label::Binding::~Binding() { assert(reference_count_ == 0); if (base_ && base_ != this && base_->Release()) delete base_; } void Label::Binding::Set(Binding* binding, uint64_t addend) { if (!base_ && !binding) { // We're equating two constants. This could be okay. assert(addend_ == addend); } else if (!base_) { // We are a known constant, but BINDING may not be, so turn the // tables and try to set BINDING's value instead. binding->Set(NULL, addend_ - addend); } else { if (binding) { // Find binding's final value. Since the final value is always either // completely unconstrained or a constant, never a reference to // another variable (otherwise, it wouldn't be final), this // guarantees we won't create cycles here, even for code like this: // l = m, m = n, n = l; uint64_t binding_addend; binding->Get(&binding, &binding_addend); addend += binding_addend; } // It seems likely that setting a binding to itself is a bug // (although I can imagine this might turn out to be helpful to // permit). assert(binding != this); if (base_ != this) { // Set the other bindings on our chain as well. Note that this // is sufficient even though binding relationships form trees: // All binding operations traverse their chains to the end, and // all bindings related to us share some tail of our chain, so // they will see the changes we make here. base_->Set(binding, addend - addend_); // We're not going to use base_ any more. if (base_->Release()) delete base_; } // Adopt BINDING as our base. Note that it should be correct to // acquire here, after the release above, even though the usual // reference-counting rules call for acquiring first, and then // releasing: the self-reference assertion above should have // complained if BINDING were 'this' or anywhere along our chain, // so we didn't release BINDING. if (binding) binding->Acquire(); base_ = binding; addend_ = addend; } } void Label::Binding::Get(Binding** base, uint64_t* addend) { if (base_ && base_ != this) { // Recurse to find the end of our reference chain (the root of our // tree), and then rewrite every binding along the chain to refer // to it directly, adjusting addends appropriately. (This is why // this member function isn't this-const.) Binding* final_base; uint64_t final_addend; base_->Get(&final_base, &final_addend); if (final_base) final_base->Acquire(); if (base_->Release()) delete base_; base_ = final_base; addend_ += final_addend; } *base = base_; *addend = addend_; } template static inline void InsertEndian(test_assembler::Endianness endianness, size_t size, uint64_t number, Inserter dest) { assert(size > 0); if (endianness == kLittleEndian) { for (size_t i = 0; i < size; i++) { *dest++ = (char) (number & 0xff); number >>= 8; } } else { assert(endianness == kBigEndian); // The loop condition is odd, but it's correct for size_t. for (size_t i = size - 1; i < size; i--) *dest++ = (char) ((number >> (i * 8)) & 0xff); } } Section& Section::Append(Endianness endianness, size_t size, uint64_t number) { InsertEndian(endianness, size, number, back_insert_iterator(contents_)); return *this; } Section& Section::Append(Endianness endianness, size_t size, const Label& label) { // If this label's value is known, there's no reason to waste an // entry in references_ on it. uint64_t value; if (label.IsKnownConstant(&value)) return Append(endianness, size, value); // This will get caught when the references are resolved, but it's // nicer to find out earlier. assert(endianness != kUnsetEndian); references_.push_back(Reference(contents_.size(), endianness, size, label)); contents_.append(size, 0); return *this; } #define ENDIANNESS_L kLittleEndian #define ENDIANNESS_B kBigEndian #define ENDIANNESS(e) ENDIANNESS_ ## e #define DEFINE_SHORT_APPEND_NUMBER_ENDIAN(e, bits) \ Section& Section::e ## bits(uint ## bits ## _t v) { \ InsertEndian(ENDIANNESS(e), bits / 8, v, \ back_insert_iterator(contents_)); \ return *this; \ } #define DEFINE_SHORT_APPEND_LABEL_ENDIAN(e, bits) \ Section& Section::e ## bits(const Label& v) { \ return Append(ENDIANNESS(e), bits / 8, v); \ } // Define L16, B32, and friends. #define DEFINE_SHORT_APPEND_ENDIAN(e, bits) \ DEFINE_SHORT_APPEND_NUMBER_ENDIAN(e, bits) \ DEFINE_SHORT_APPEND_LABEL_ENDIAN(e, bits) DEFINE_SHORT_APPEND_LABEL_ENDIAN(L, 8); DEFINE_SHORT_APPEND_LABEL_ENDIAN(B, 8); DEFINE_SHORT_APPEND_ENDIAN(L, 16); DEFINE_SHORT_APPEND_ENDIAN(L, 32); DEFINE_SHORT_APPEND_ENDIAN(L, 64); DEFINE_SHORT_APPEND_ENDIAN(B, 16); DEFINE_SHORT_APPEND_ENDIAN(B, 32); DEFINE_SHORT_APPEND_ENDIAN(B, 64); #define DEFINE_SHORT_APPEND_NUMBER_DEFAULT(bits) \ Section& Section::D ## bits(uint ## bits ## _t v) { \ InsertEndian(endianness_, bits / 8, v, \ back_insert_iterator(contents_)); \ return *this; \ } #define DEFINE_SHORT_APPEND_LABEL_DEFAULT(bits) \ Section& Section::D ## bits(const Label& v) { \ return Append(endianness_, bits / 8, v); \ } #define DEFINE_SHORT_APPEND_DEFAULT(bits) \ DEFINE_SHORT_APPEND_NUMBER_DEFAULT(bits) \ DEFINE_SHORT_APPEND_LABEL_DEFAULT(bits) DEFINE_SHORT_APPEND_LABEL_DEFAULT(8) DEFINE_SHORT_APPEND_DEFAULT(16); DEFINE_SHORT_APPEND_DEFAULT(32); DEFINE_SHORT_APPEND_DEFAULT(64); Section& Section::Append(const Section& section) { size_t base = contents_.size(); contents_.append(section.contents_); for (vector::const_iterator it = section.references_.begin(); it != section.references_.end(); it++) references_.push_back(Reference(base + it->offset, it->endianness, it->size, it->label)); return *this; } Section& Section::LEB128(long long value) { while (value < -0x40 || 0x3f < value) { contents_ += (value & 0x7f) | 0x80; if (value < 0) value = (value >> 7) | ~(((unsigned long long) -1) >> 7); else value = (value >> 7); } contents_ += value & 0x7f; return *this; } Section& Section::ULEB128(uint64_t value) { while (value > 0x7f) { contents_ += (value & 0x7f) | 0x80; value = (value >> 7); } contents_ += value; return *this; } Section& Section::Align(size_t alignment, uint8_t pad_byte) { // ALIGNMENT must be a power of two. assert(((alignment - 1) & alignment) == 0); size_t new_size = (contents_.size() + alignment - 1) & ~(alignment - 1); contents_.append(new_size - contents_.size(), pad_byte); assert((contents_.size() & (alignment - 1)) == 0); return *this; } void Section::Clear() { contents_.clear(); references_.clear(); } bool Section::GetContents(string* contents) { // For each label reference, find the label's value, and patch it into // the section's contents. for (size_t i = 0; i < references_.size(); i++) { Reference& r = references_[i]; uint64_t value; if (!r.label.IsKnownConstant(&value)) { fprintf(stderr, "Undefined label #%zu at offset 0x%zx\n", i, r.offset); return false; } assert(r.offset < contents_.size()); assert(contents_.size() - r.offset >= r.size); InsertEndian(r.endianness, r.size, value, contents_.begin() + r.offset); } contents->clear(); std::swap(contents_, *contents); references_.clear(); return true; } } // namespace test_assembler } // namespace google_breakpad