| // 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 <jimb@mozilla.com> <jimb@red-bean.com> |
| |
| // test_assembler.cc: Implementation of google_breakpad::TestAssembler. |
| // See test_assembler.h for details. |
| |
| #include "common/test_assembler.h" |
| |
| #include <assert.h> |
| #include <stdio.h> |
| |
| #include <iterator> |
| |
| 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<typename Inserter> |
| 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<string>(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<string>(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<string>(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 §ion) { |
| size_t base = contents_.size(); |
| contents_.append(section.contents_); |
| for (vector<Reference>::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 |