michael@0: // Copyright (c) 2010, Google Inc. michael@0: // All rights reserved. michael@0: // michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions are michael@0: // met: michael@0: // michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above michael@0: // copyright notice, this list of conditions and the following disclaimer michael@0: // in the documentation and/or other materials provided with the michael@0: // distribution. michael@0: // * Neither the name of Google Inc. nor the names of its michael@0: // contributors may be used to endorse or promote products derived from michael@0: // this software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: michael@0: // Original author: Jim Blandy michael@0: michael@0: // test_assembler.cc: Implementation of google_breakpad::TestAssembler. michael@0: // See test_assembler.h for details. michael@0: michael@0: #include "common/test_assembler.h" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: michael@0: namespace google_breakpad { michael@0: namespace test_assembler { michael@0: michael@0: using std::back_insert_iterator; michael@0: michael@0: Label::Label() : value_(new Binding()) { } michael@0: Label::Label(uint64_t value) : value_(new Binding(value)) { } michael@0: Label::Label(const Label &label) { michael@0: value_ = label.value_; michael@0: value_->Acquire(); michael@0: } michael@0: Label::~Label() { michael@0: if (value_->Release()) delete value_; michael@0: } michael@0: michael@0: Label &Label::operator=(uint64_t value) { michael@0: value_->Set(NULL, value); michael@0: return *this; michael@0: } michael@0: michael@0: Label &Label::operator=(const Label &label) { michael@0: value_->Set(label.value_, 0); michael@0: return *this; michael@0: } michael@0: michael@0: Label Label::operator+(uint64_t addend) const { michael@0: Label l; michael@0: l.value_->Set(this->value_, addend); michael@0: return l; michael@0: } michael@0: michael@0: Label Label::operator-(uint64_t subtrahend) const { michael@0: Label l; michael@0: l.value_->Set(this->value_, -subtrahend); michael@0: return l; michael@0: } michael@0: michael@0: // When NDEBUG is #defined, assert doesn't evaluate its argument. This michael@0: // means you can't simply use assert to check the return value of a michael@0: // function with necessary side effects. michael@0: // michael@0: // ALWAYS_EVALUATE_AND_ASSERT(x) evaluates x regardless of whether michael@0: // NDEBUG is #defined; when NDEBUG is not #defined, it further asserts michael@0: // that x is true. michael@0: #ifdef NDEBUG michael@0: #define ALWAYS_EVALUATE_AND_ASSERT(x) x michael@0: #else michael@0: #define ALWAYS_EVALUATE_AND_ASSERT(x) assert(x) michael@0: #endif michael@0: michael@0: uint64_t Label::operator-(const Label &label) const { michael@0: uint64_t offset; michael@0: ALWAYS_EVALUATE_AND_ASSERT(IsKnownOffsetFrom(label, &offset)); michael@0: return offset; michael@0: } michael@0: michael@0: uint64_t Label::Value() const { michael@0: uint64_t v = 0; michael@0: ALWAYS_EVALUATE_AND_ASSERT(IsKnownConstant(&v)); michael@0: return v; michael@0: }; michael@0: michael@0: bool Label::IsKnownConstant(uint64_t *value_p) const { michael@0: Binding *base; michael@0: uint64_t addend; michael@0: value_->Get(&base, &addend); michael@0: if (base != NULL) return false; michael@0: if (value_p) *value_p = addend; michael@0: return true; michael@0: } michael@0: michael@0: bool Label::IsKnownOffsetFrom(const Label &label, uint64_t *offset_p) const michael@0: { michael@0: Binding *label_base, *this_base; michael@0: uint64_t label_addend, this_addend; michael@0: label.value_->Get(&label_base, &label_addend); michael@0: value_->Get(&this_base, &this_addend); michael@0: // If this and label are related, Get will find their final michael@0: // common ancestor, regardless of how indirect the relation is. This michael@0: // comparison also handles the constant vs. constant case. michael@0: if (this_base != label_base) return false; michael@0: if (offset_p) *offset_p = this_addend - label_addend; michael@0: return true; michael@0: } michael@0: michael@0: Label::Binding::Binding() : base_(this), addend_(), reference_count_(1) { } michael@0: michael@0: Label::Binding::Binding(uint64_t addend) michael@0: : base_(NULL), addend_(addend), reference_count_(1) { } michael@0: michael@0: Label::Binding::~Binding() { michael@0: assert(reference_count_ == 0); michael@0: if (base_ && base_ != this && base_->Release()) michael@0: delete base_; michael@0: } michael@0: michael@0: void Label::Binding::Set(Binding *binding, uint64_t addend) { michael@0: if (!base_ && !binding) { michael@0: // We're equating two constants. This could be okay. michael@0: assert(addend_ == addend); michael@0: } else if (!base_) { michael@0: // We are a known constant, but BINDING may not be, so turn the michael@0: // tables and try to set BINDING's value instead. michael@0: binding->Set(NULL, addend_ - addend); michael@0: } else { michael@0: if (binding) { michael@0: // Find binding's final value. Since the final value is always either michael@0: // completely unconstrained or a constant, never a reference to michael@0: // another variable (otherwise, it wouldn't be final), this michael@0: // guarantees we won't create cycles here, even for code like this: michael@0: // l = m, m = n, n = l; michael@0: uint64_t binding_addend; michael@0: binding->Get(&binding, &binding_addend); michael@0: addend += binding_addend; michael@0: } michael@0: michael@0: // It seems likely that setting a binding to itself is a bug michael@0: // (although I can imagine this might turn out to be helpful to michael@0: // permit). michael@0: assert(binding != this); michael@0: michael@0: if (base_ != this) { michael@0: // Set the other bindings on our chain as well. Note that this michael@0: // is sufficient even though binding relationships form trees: michael@0: // All binding operations traverse their chains to the end, and michael@0: // all bindings related to us share some tail of our chain, so michael@0: // they will see the changes we make here. michael@0: base_->Set(binding, addend - addend_); michael@0: // We're not going to use base_ any more. michael@0: if (base_->Release()) delete base_; michael@0: } michael@0: michael@0: // Adopt BINDING as our base. Note that it should be correct to michael@0: // acquire here, after the release above, even though the usual michael@0: // reference-counting rules call for acquiring first, and then michael@0: // releasing: the self-reference assertion above should have michael@0: // complained if BINDING were 'this' or anywhere along our chain, michael@0: // so we didn't release BINDING. michael@0: if (binding) binding->Acquire(); michael@0: base_ = binding; michael@0: addend_ = addend; michael@0: } michael@0: } michael@0: michael@0: void Label::Binding::Get(Binding **base, uint64_t *addend) { michael@0: if (base_ && base_ != this) { michael@0: // Recurse to find the end of our reference chain (the root of our michael@0: // tree), and then rewrite every binding along the chain to refer michael@0: // to it directly, adjusting addends appropriately. (This is why michael@0: // this member function isn't this-const.) michael@0: Binding *final_base; michael@0: uint64_t final_addend; michael@0: base_->Get(&final_base, &final_addend); michael@0: if (final_base) final_base->Acquire(); michael@0: if (base_->Release()) delete base_; michael@0: base_ = final_base; michael@0: addend_ += final_addend; michael@0: } michael@0: *base = base_; michael@0: *addend = addend_; michael@0: } michael@0: michael@0: template michael@0: static inline void InsertEndian(test_assembler::Endianness endianness, michael@0: size_t size, uint64_t number, Inserter dest) { michael@0: assert(size > 0); michael@0: if (endianness == kLittleEndian) { michael@0: for (size_t i = 0; i < size; i++) { michael@0: *dest++ = (char) (number & 0xff); michael@0: number >>= 8; michael@0: } michael@0: } else { michael@0: assert(endianness == kBigEndian); michael@0: // The loop condition is odd, but it's correct for size_t. michael@0: for (size_t i = size - 1; i < size; i--) michael@0: *dest++ = (char) ((number >> (i * 8)) & 0xff); michael@0: } michael@0: } michael@0: michael@0: Section &Section::Append(Endianness endianness, size_t size, uint64_t number) { michael@0: InsertEndian(endianness, size, number, michael@0: back_insert_iterator(contents_)); michael@0: return *this; michael@0: } michael@0: michael@0: Section &Section::Append(Endianness endianness, size_t size, michael@0: const Label &label) { michael@0: // If this label's value is known, there's no reason to waste an michael@0: // entry in references_ on it. michael@0: uint64_t value; michael@0: if (label.IsKnownConstant(&value)) michael@0: return Append(endianness, size, value); michael@0: michael@0: // This will get caught when the references are resolved, but it's michael@0: // nicer to find out earlier. michael@0: assert(endianness != kUnsetEndian); michael@0: michael@0: references_.push_back(Reference(contents_.size(), endianness, size, label)); michael@0: contents_.append(size, 0); michael@0: return *this; michael@0: } michael@0: michael@0: #define ENDIANNESS_L kLittleEndian michael@0: #define ENDIANNESS_B kBigEndian michael@0: #define ENDIANNESS(e) ENDIANNESS_ ## e michael@0: michael@0: #define DEFINE_SHORT_APPEND_NUMBER_ENDIAN(e, bits) \ michael@0: Section &Section::e ## bits(uint ## bits ## _t v) { \ michael@0: InsertEndian(ENDIANNESS(e), bits / 8, v, \ michael@0: back_insert_iterator(contents_)); \ michael@0: return *this; \ michael@0: } michael@0: michael@0: #define DEFINE_SHORT_APPEND_LABEL_ENDIAN(e, bits) \ michael@0: Section &Section::e ## bits(const Label &v) { \ michael@0: return Append(ENDIANNESS(e), bits / 8, v); \ michael@0: } michael@0: michael@0: // Define L16, B32, and friends. michael@0: #define DEFINE_SHORT_APPEND_ENDIAN(e, bits) \ michael@0: DEFINE_SHORT_APPEND_NUMBER_ENDIAN(e, bits) \ michael@0: DEFINE_SHORT_APPEND_LABEL_ENDIAN(e, bits) michael@0: michael@0: DEFINE_SHORT_APPEND_LABEL_ENDIAN(L, 8); michael@0: DEFINE_SHORT_APPEND_LABEL_ENDIAN(B, 8); michael@0: DEFINE_SHORT_APPEND_ENDIAN(L, 16); michael@0: DEFINE_SHORT_APPEND_ENDIAN(L, 32); michael@0: DEFINE_SHORT_APPEND_ENDIAN(L, 64); michael@0: DEFINE_SHORT_APPEND_ENDIAN(B, 16); michael@0: DEFINE_SHORT_APPEND_ENDIAN(B, 32); michael@0: DEFINE_SHORT_APPEND_ENDIAN(B, 64); michael@0: michael@0: #define DEFINE_SHORT_APPEND_NUMBER_DEFAULT(bits) \ michael@0: Section &Section::D ## bits(uint ## bits ## _t v) { \ michael@0: InsertEndian(endianness_, bits / 8, v, \ michael@0: back_insert_iterator(contents_)); \ michael@0: return *this; \ michael@0: } michael@0: #define DEFINE_SHORT_APPEND_LABEL_DEFAULT(bits) \ michael@0: Section &Section::D ## bits(const Label &v) { \ michael@0: return Append(endianness_, bits / 8, v); \ michael@0: } michael@0: #define DEFINE_SHORT_APPEND_DEFAULT(bits) \ michael@0: DEFINE_SHORT_APPEND_NUMBER_DEFAULT(bits) \ michael@0: DEFINE_SHORT_APPEND_LABEL_DEFAULT(bits) michael@0: michael@0: DEFINE_SHORT_APPEND_LABEL_DEFAULT(8) michael@0: DEFINE_SHORT_APPEND_DEFAULT(16); michael@0: DEFINE_SHORT_APPEND_DEFAULT(32); michael@0: DEFINE_SHORT_APPEND_DEFAULT(64); michael@0: michael@0: Section &Section::Append(const Section §ion) { michael@0: size_t base = contents_.size(); michael@0: contents_.append(section.contents_); michael@0: for (vector::const_iterator it = section.references_.begin(); michael@0: it != section.references_.end(); it++) michael@0: references_.push_back(Reference(base + it->offset, it->endianness, michael@0: it->size, it->label)); michael@0: return *this; michael@0: } michael@0: michael@0: Section &Section::LEB128(long long value) { michael@0: while (value < -0x40 || 0x3f < value) { michael@0: contents_ += (value & 0x7f) | 0x80; michael@0: if (value < 0) michael@0: value = (value >> 7) | ~(((unsigned long long) -1) >> 7); michael@0: else michael@0: value = (value >> 7); michael@0: } michael@0: contents_ += value & 0x7f; michael@0: return *this; michael@0: } michael@0: michael@0: Section &Section::ULEB128(uint64_t value) { michael@0: while (value > 0x7f) { michael@0: contents_ += (value & 0x7f) | 0x80; michael@0: value = (value >> 7); michael@0: } michael@0: contents_ += value; michael@0: return *this; michael@0: } michael@0: michael@0: Section &Section::Align(size_t alignment, uint8_t pad_byte) { michael@0: // ALIGNMENT must be a power of two. michael@0: assert(((alignment - 1) & alignment) == 0); michael@0: size_t new_size = (contents_.size() + alignment - 1) & ~(alignment - 1); michael@0: contents_.append(new_size - contents_.size(), pad_byte); michael@0: assert((contents_.size() & (alignment - 1)) == 0); michael@0: return *this; michael@0: } michael@0: michael@0: void Section::Clear() { michael@0: contents_.clear(); michael@0: references_.clear(); michael@0: } michael@0: michael@0: bool Section::GetContents(string *contents) { michael@0: // For each label reference, find the label's value, and patch it into michael@0: // the section's contents. michael@0: for (size_t i = 0; i < references_.size(); i++) { michael@0: Reference &r = references_[i]; michael@0: uint64_t value; michael@0: if (!r.label.IsKnownConstant(&value)) { michael@0: fprintf(stderr, "Undefined label #%zu at offset 0x%zx\n", i, r.offset); michael@0: return false; michael@0: } michael@0: assert(r.offset < contents_.size()); michael@0: assert(contents_.size() - r.offset >= r.size); michael@0: InsertEndian(r.endianness, r.size, value, contents_.begin() + r.offset); michael@0: } michael@0: contents->clear(); michael@0: std::swap(contents_, *contents); michael@0: references_.clear(); michael@0: return true; michael@0: } michael@0: michael@0: } // namespace test_assembler michael@0: } // namespace google_breakpad