diff options
author | John Hodge <tpg@ucc.asn.au> | 2019-11-02 11:07:23 +0800 |
---|---|---|
committer | John Hodge <tpg@ucc.asn.au> | 2019-11-02 11:07:23 +0800 |
commit | 1d02810c3cf908bfba7c15ae50eb5314603b9d85 (patch) | |
tree | 79dd5e4ef4c3ff79db0912ba546f08e61a7a8c10 /tools/standalone_miri/value.cpp | |
parent | 7111acba04d72fe4084b1a1f3209ff83efe8614d (diff) | |
parent | 8b53b38f40625ab0510f541d69db3f83332a830a (diff) | |
download | mrust-1d02810c3cf908bfba7c15ae50eb5314603b9d85.tar.gz |
Merge branch 'nightly-1.29' - #95 Working support for rustc 1.29
Diffstat (limited to 'tools/standalone_miri/value.cpp')
-rw-r--r-- | tools/standalone_miri/value.cpp | 594 |
1 files changed, 319 insertions, 275 deletions
diff --git a/tools/standalone_miri/value.cpp b/tools/standalone_miri/value.cpp index 5974a172..a497f0bd 100644 --- a/tools/standalone_miri/value.cpp +++ b/tools/standalone_miri/value.cpp @@ -13,13 +13,98 @@ #include <algorithm> #include "debug.hpp" -AllocationHandle Allocation::new_alloc(size_t size) +namespace { + static bool in_bounds(size_t ofs, size_t size, size_t max_size) { + if( !(ofs < max_size) ) + return false; + if( !(size <= max_size) ) + return false; + return ofs + size <= max_size; + } + + void set_bit(uint8_t* p, size_t i, bool v) + { + if(v) { + p[i/8] |= 1 << (i%8); + } + else { + p[i/8] &= ~(1 << (i%8)); + } + } + bool get_bit(const uint8_t* p, size_t i) { + return (p[i/8] & (1 << (i%8))) != 0; + } + void copy_bits(uint8_t* dst, size_t dst_ofs, const uint8_t* src, size_t src_ofs, size_t len) + { + // Even number of bytes, fast copy + if( dst_ofs % 8 == 0 && src_ofs % 8 == 0 && len % 8 == 0 ) + { + for(size_t i = 0; i < len/8; i ++) + { + dst[dst_ofs/8 + i] = src[src_ofs/8 + i]; + } + } + else + { + for(size_t i = 0; i < len; i ++) + { + set_bit( dst, dst_ofs+i, get_bit(src, src_ofs+i) ); + } + } + } +}; + +::std::ostream& operator<<(::std::ostream& os, const Allocation* x) +{ + os << "A(#" << x->m_index << " " << x->tag() /*<< " +" << x->size()*/ << ")"; + return os; +} + +FfiLayout FfiLayout::new_const_bytes(size_t s) +{ + return FfiLayout { + { Range {s, true, false} } + }; +} +bool FfiLayout::is_valid_read(size_t o, size_t s) const +{ + for(const auto& r : ranges) + { + if( o < r.len ) { + if( !r.is_valid ) + return false; + if( o + s <= r.len ) + { + s = 0; + break; + } + s -= (r.len - o); + o = 0; + } + else { + o -= r.len; + } + } + if( s > 0 ) + { + return false; + } + return true; +} + +uint64_t Allocation::s_next_index = 0; + +AllocationHandle Allocation::new_alloc(size_t size, ::std::string tag) { Allocation* rv = new Allocation(); + rv->m_index = s_next_index++; + rv->m_tag = ::std::move(tag); rv->refcount = 1; - rv->data.resize( (size + 8-1) / 8 ); // QWORDS - rv->mask.resize( (size + 8-1) / 8 ); // bitmap bytes + rv->m_size = size; + rv->m_data.resize( (size + 8-1) / 8 ); // QWORDS + rv->m_mask.resize( (size + 8-1) / 8 ); // bitmap bytes //LOG_DEBUG(rv << " ALLOC"); + LOG_DEBUG(rv); return AllocationHandle(rv); } AllocationHandle::AllocationHandle(const AllocationHandle& x): @@ -167,7 +252,7 @@ size_t RelocationPtr::get_size() const os << "\"" << x.str() << "\""; break; case RelocationPtr::Ty::FfiPointer: - os << "FFI " << x.ffi().source_function << " " << x.ffi().ptr_value; + os << "FFI '" << x.ffi().tag_name << "' " << x.ffi().ptr_value; break; } } @@ -191,6 +276,8 @@ void ValueCommonWrite::write_usize(size_t ofs, uint64_t v) void* ValueCommonRead::read_pointer_unsafe(size_t rd_ofs, size_t req_valid, size_t& out_size, bool& out_is_mut) const { auto ofs = read_usize(rd_ofs); + LOG_ASSERT(ofs >= Allocation::PTR_BASE, "Deref of invalid pointer"); + ofs -= Allocation::PTR_BASE; auto reloc = get_relocation(rd_ofs); if( !reloc ) { @@ -210,22 +297,16 @@ void* ValueCommonRead::read_pointer_unsafe(size_t rd_ofs, size_t req_valid, size { case RelocationPtr::Ty::Allocation: { auto& a = reloc.alloc(); - if( ofs > a.size() ) - LOG_FATAL("Out-of-bounds pointer"); - if( ofs + req_valid > a.size() ) - LOG_FATAL("Out-of-bounds pointer (" << ofs << " + " << req_valid << " > " << a.size()); - a.check_bytes_valid( static_cast<size_t>(ofs), req_valid ); - out_size = a.size() - static_cast<size_t>(ofs); + LOG_ASSERT(in_bounds(ofs, req_valid, a.size()), "Out-of-bounds pointer (" << ofs << " + " << req_valid << " > " << a.size() << ")"); + a.check_bytes_valid( ofs, req_valid ); + out_size = a.size() - ofs; out_is_mut = true; return a.data_ptr() + ofs; } case RelocationPtr::Ty::StdString: { const auto& s = reloc.str(); - if( ofs > s.size() ) - LOG_FATAL("Out-of-bounds pointer"); - if( ofs + req_valid > s.size() ) - LOG_FATAL("Out-of-bounds pointer (" << ofs << " + " << req_valid << " > " << s.size()); - out_size = s.size() - static_cast<size_t>(ofs); + LOG_ASSERT(in_bounds(ofs, req_valid, s.size()), "Out-of-bounds pointer (" << ofs << " + " << req_valid << " > " << s.size() << ")"); + out_size = s.size() - ofs; out_is_mut = false; return const_cast<void*>( static_cast<const void*>(s.data() + ofs) ); } @@ -233,11 +314,13 @@ void* ValueCommonRead::read_pointer_unsafe(size_t rd_ofs, size_t req_valid, size LOG_FATAL("read_pointer w/ function"); case RelocationPtr::Ty::FfiPointer: { const auto& f = reloc.ffi(); + size_t size = f.get_size(); + LOG_ASSERT(in_bounds(ofs, req_valid, size), "Out-of-bounds pointer (" << ofs << " + " << req_valid << " > " << size << ")"); // TODO: Validity? //if( req_valid ) // LOG_FATAL("Can't request valid data from a FFI pointer"); // TODO: Have an idea of mutability and available size from FFI - out_size = f.size - static_cast<size_t>(ofs); + out_size = size - ofs; out_is_mut = false; return reinterpret_cast<char*>(reloc.ffi().ptr_value) + ofs; } @@ -248,17 +331,38 @@ void* ValueCommonRead::read_pointer_unsafe(size_t rd_ofs, size_t req_valid, size ValueRef ValueCommonRead::read_pointer_valref_mut(size_t rd_ofs, size_t size) { auto ofs = read_usize(rd_ofs); + LOG_ASSERT(ofs >= Allocation::PTR_BASE, "Invalid pointer read"); + ofs -= Allocation::PTR_BASE; auto reloc = get_relocation(rd_ofs); + LOG_DEBUG("ValueCommonRead::read_pointer_valref_mut(" << ofs << "+" << size << ", reloc=" << reloc << ")"); if( !reloc ) { LOG_ERROR("Getting ValRef to null pointer (no relocation)"); } else { - // TODO: Validate size - return ValueRef(reloc, static_cast<size_t>(ofs), size); + // Validate size and offset are in bounds + switch(reloc.get_ty()) + { + case RelocationPtr::Ty::Allocation: + LOG_ASSERT( in_bounds(ofs, size, reloc.alloc().size()), "Deref with OOB size - " << ofs << "+" << size << " > " << reloc.alloc().size() ); + break; + case RelocationPtr::Ty::StdString: + LOG_ASSERT( in_bounds(ofs, size, reloc.str().size()), "Deref with OOB size - " << ofs << "+" << size << " > " << reloc.str().size() ); + break; + case RelocationPtr::Ty::Function: + LOG_FATAL("read_pointer_valref_mut w/ function"); + case RelocationPtr::Ty::FfiPointer: + LOG_ASSERT( in_bounds(ofs, size, reloc.ffi().get_size()), "Deref with OOB size - " << ofs << "+" << size << " > " << reloc.ffi().get_size() ); + break; + } + return ValueRef(reloc, ofs, size); } } +ValueRef ValueCommonRead::deref(size_t ofs, const ::HIR::TypeRef& ty) +{ + return read_pointer_valref_mut(ofs, ty.get_size()); +} void Allocation::resize(size_t new_size) @@ -268,18 +372,19 @@ void Allocation::resize(size_t new_size) //size_t old_size = this->size(); //size_t extra_bytes = (new_size > old_size ? new_size - old_size : 0); - this->data.resize( (new_size + 8-1) / 8 ); - this->mask.resize( (new_size + 8-1) / 8 ); + this->m_size = new_size; + this->m_data.resize( (new_size + 8-1) / 8 ); + this->m_mask.resize( (new_size + 8-1) / 8 ); } void Allocation::check_bytes_valid(size_t ofs, size_t size) const { - if( !(ofs + size <= this->size()) ) { + if( !in_bounds(ofs, size, this->size()) ) { LOG_FATAL("Out of range - " << ofs << "+" << size << " > " << this->size()); } for(size_t i = ofs; i < ofs + size; i++) { - if( !(this->mask[i/8] & (1 << (i%8))) ) + if( !(this->m_mask[i/8] & (1 << (i%8))) ) { LOG_ERROR("Invalid bytes in value - " << ofs << "+" << size << " - " << *this); throw "ERROR"; @@ -288,19 +393,20 @@ void Allocation::check_bytes_valid(size_t ofs, size_t size) const } void Allocation::mark_bytes_valid(size_t ofs, size_t size) { - assert( ofs+size <= this->mask.size() * 8 ); + assert( ofs+size <= this->m_mask.size() * 8 ); for(size_t i = ofs; i < ofs + size; i++) { - this->mask[i/8] |= (1 << (i%8)); + this->m_mask[i/8] |= (1 << (i%8)); } } Value Allocation::read_value(size_t ofs, size_t size) const { Value rv; - TRACE_FUNCTION_R("Allocation::read_value " << this << " " << ofs << "+" << size, *this << " | " << rv); + //TRACE_FUNCTION_R("Allocation::read_value " << this << " " << ofs << "+" << size, *this << " | " << size << "=" << rv); if( this->is_freed ) LOG_ERROR("Use of freed memory " << this); LOG_DEBUG(*this); + LOG_ASSERT( in_bounds(ofs, size, this->size()), "Read out of bounds (" << ofs << "+" << size << " > " << this->size() << ")" ); // Determine if this can become an inline allocation. bool has_reloc = false; @@ -308,57 +414,25 @@ Value Allocation::read_value(size_t ofs, size_t size) const { if( ofs <= r.slot_ofs && r.slot_ofs < ofs + size ) { + // NOTE: A relocation at offset zero is allowed + if( r.slot_ofs == ofs ) + continue ; has_reloc = true; } } - if( has_reloc || size > sizeof(rv.direct_data.data) ) - { - rv.allocation = Allocation::new_alloc(size); - - rv.write_bytes(0, this->data_ptr() + ofs, size); - - for(const auto& r : this->relocations) - { - if( ofs <= r.slot_ofs && r.slot_ofs < ofs + size ) - { - rv.allocation->relocations.push_back({ r.slot_ofs - ofs, r.backing_alloc }); - } - } + rv = Value::with_size(size, has_reloc); + rv.write_bytes(0, this->data_ptr() + ofs, size); - // Copy the mask bits - for(size_t i = 0; i < size; i ++) - { - size_t j = ofs + i; - const uint8_t test_mask = (1 << (j%8)); - const uint8_t set_mask = (1 << (i%8)); - bool v = (this->mask[j/8] & test_mask) != 0; - if( v ) - { - rv.allocation->mask[i/8] |= set_mask; - } - } - } - else + for(const auto& r : this->relocations) { - rv.direct_data.size = static_cast<uint8_t>(size); - - rv.write_bytes(0, this->data_ptr() + ofs, size); - rv.direct_data.mask[0] = 0; - rv.direct_data.mask[1] = 0; - - // Copy the mask bits - for(size_t i = 0; i < size; i ++) + if( ofs <= r.slot_ofs && r.slot_ofs < ofs + size ) { - size_t j = ofs + i; - const uint8_t tst_mask = 1 << (j%8); - const uint8_t set_mask = 1 << (i%8); - bool v = (this->mask[j/8] & tst_mask) != 0; - if( v ) - { - rv.direct_data.mask[i/8] |= set_mask; - } + rv.set_reloc(r.slot_ofs - ofs, /*r.size*/POINTER_SIZE, r.backing_alloc); } } + // Copy the mask bits + copy_bits(rv.get_mask_mut(), 0, m_mask.data(), ofs, size); + return rv; } void Allocation::read_bytes(size_t ofs, void* dst, size_t count) const @@ -366,19 +440,11 @@ void Allocation::read_bytes(size_t ofs, void* dst, size_t count) const if( this->is_freed ) LOG_ERROR("Use of freed memory " << this); - LOG_DEBUG("Allocation::read_bytes " << this << " " << ofs << "+" << count); + //LOG_DEBUG("Allocation::read_bytes " << this << " " << ofs << "+" << count); if(count == 0) return ; - if(ofs >= this->size() ) { - LOG_ERROR("Out of bounds read, " << ofs << "+" << count << " > " << this->size()); - throw "ERROR"; - } - if(count > this->size() ) { - LOG_ERROR("Out of bounds read, " << ofs << "+" << count << " > " << this->size()); - throw "ERROR"; - } - if(ofs+count > this->size() ) { + if( !in_bounds(ofs, count, this->size()) ) { LOG_ERROR("Out of bounds read, " << ofs << "+" << count << " > " << this->size()); throw "ERROR"; } @@ -394,14 +460,15 @@ void Allocation::write_value(size_t ofs, Value v) LOG_ERROR("Use of freed memory " << this); //if( this->is_read_only ) // LOG_ERROR("Writing to read-only allocation " << this); - if( v.allocation ) + if( v.m_inner.is_alloc ) { - size_t v_size = v.allocation->size(); - const auto& src_alloc = *v.allocation; - // Take a copy of the source mask - auto s_mask = src_alloc.mask; + const auto& src_alloc = *v.m_inner.alloc.alloc; + size_t v_size = src_alloc.size(); + assert(&src_alloc != this); // Shouldn't happen? - // Save relocations first, because `Foo = Foo` is valid. + // Take a copy of the source mask + auto s_mask = src_alloc.m_mask; + // Save relocations first, because `Foo = Foo` is valid? ::std::vector<Relocation> new_relocs = src_alloc.relocations; // - write_bytes removes any relocations in this region. write_bytes(ofs, src_alloc.data_ptr(), v_size); @@ -420,39 +487,16 @@ void Allocation::write_value(size_t ofs, Value v) } // Set mask in destination - if( ofs % 8 != 0 || v_size % 8 != 0 ) - { - // Lazy way, sets/clears individual bits - for(size_t i = 0; i < v_size; i ++) - { - uint8_t dbit = 1 << ((ofs+i) % 8); - if( s_mask[i/8] & (1 << (i %8)) ) - this->mask[ (ofs+i) / 8 ] |= dbit; - else - this->mask[ (ofs+i) / 8 ] &= ~dbit; - } - } - else - { - // Copy the mask bytes directly - for(size_t i = 0; i < v_size / 8; i ++) - { - this->mask[ofs/8+i] = s_mask[i]; - } - } + copy_bits(m_mask.data(), ofs, s_mask.data(), 0, v_size); } else { - this->write_bytes(ofs, v.direct_data.data, v.direct_data.size); - - // Lazy way, sets/clears individual bits - for(size_t i = 0; i < v.direct_data.size; i ++) + this->write_bytes(ofs, v.data_ptr(), v.size()); + copy_bits(m_mask.data(), ofs, v.get_mask(), 0, v.size()); + // TODO: Copy relocation + if( v.m_inner.direct.reloc_0 ) { - uint8_t dbit = 1 << ((ofs+i) % 8); - if( v.direct_data.mask[i/8] & (1 << (i %8)) ) - this->mask[ (ofs+i) / 8 ] |= dbit; - else - this->mask[ (ofs+i) / 8 ] &= ~dbit; + this->set_reloc(ofs, POINTER_SIZE, ::std::move(v.m_inner.direct.reloc_0)); } } } @@ -466,16 +510,8 @@ void Allocation::write_bytes(size_t ofs, const void* src, size_t count) if(count == 0) return ; - TRACE_FUNCTION_R("Allocation::write_bytes " << this << " " << ofs << "+" << count, *this); - if(ofs >= this->size() ) { - LOG_ERROR("Out of bounds write, " << ofs << "+" << count << " > " << this->size()); - throw "ERROR"; - } - if(count > this->size() ) { - LOG_ERROR("Out of bounds write, " << ofs << "+" << count << " > " << this->size()); - throw "ERROR"; - } - if(ofs+count > this->size() ) { + //TRACE_FUNCTION_R("Allocation::write_bytes " << this << " " << ofs << "+" << count, *this); + if( !in_bounds(ofs, count, this->size()) ) { LOG_ERROR("Out of bounds write, " << ofs << "+" << count << " > " << this->size()); throw "ERROR"; } @@ -501,8 +537,29 @@ void Allocation::write_bytes(size_t ofs, const void* src, size_t count) } void Allocation::write_ptr(size_t ofs, size_t ptr_ofs, RelocationPtr reloc) { + LOG_ASSERT(ptr_ofs >= Allocation::PTR_BASE, "Invalid pointer being written"); this->write_usize(ofs, ptr_ofs); - this->relocations.push_back(Relocation { ofs, /*POINTER_SIZE,*/ ::std::move(reloc) }); + this->set_reloc(ofs, POINTER_SIZE, ::std::move(reloc)); +} +void Allocation::set_reloc(size_t ofs, size_t len, RelocationPtr reloc) +{ + LOG_ASSERT(ofs % POINTER_SIZE == 0, ""); + LOG_ASSERT(len == POINTER_SIZE, ""); + // Delete any existing relocation at this position + for(auto it = this->relocations.begin(); it != this->relocations.end();) + { + if( ofs <= it->slot_ofs && it->slot_ofs < ofs + len ) + { + // Slot starts in this updated region + // - TODO: Split in half? + it = this->relocations.erase(it); + continue ; + } + // TODO: What if the slot ends in the new region? + // What if the new region is in the middle of the slot + ++ it; + } + this->relocations.push_back(Relocation { ofs, /*len,*/ ::std::move(reloc) }); } ::std::ostream& operator<<(::std::ostream& os, const Allocation& x) { @@ -513,7 +570,7 @@ void Allocation::write_ptr(size_t ofs, size_t ptr_ofs, RelocationPtr reloc) if( i != 0 ) os << " "; - if( x.mask[i/8] & (1 << (i%8)) ) + if( x.m_mask[i/8] & (1 << (i%8)) ) { os << ::std::setw(2) << ::std::setfill('0') << (int)x.data_ptr()[i]; } @@ -538,72 +595,62 @@ void Allocation::write_ptr(size_t ofs, size_t ptr_ofs, RelocationPtr reloc) Value::Value() { - this->direct_data.size = 0; - this->direct_data.mask[0] = 0; - this->direct_data.mask[1] = 0; + memset(&m_inner, 0, sizeof(m_inner)); } Value::Value(::HIR::TypeRef ty) { size_t size = ty.get_size(); // Support inline data if the data will fit within the inline region (which is the size of the metadata) - if( ty.get_size() <= sizeof(this->direct_data.data) ) + if( size <= sizeof(m_inner.direct.data) ) { // AND the type doesn't contain a pointer (of any kind) - if( ! ty.has_pointer() ) + // TODO: Pointers _are_ allowed now (but only one) + if( true || ! ty.has_pointer() ) { // Will fit in a inline allocation, nice. //LOG_TRACE("No pointers in " << ty << ", storing inline"); - this->direct_data.size = static_cast<uint8_t>(size); - this->direct_data.mask[0] = 0; - this->direct_data.mask[1] = 0; + new(&m_inner.direct) Inner::Direct(size); return ; } } // Fallback: Make a new allocation //LOG_TRACE(" Creating allocation for " << ty); - this->allocation = Allocation::new_alloc(size); + new(&m_inner.alloc) Inner::Alloc( Allocation::new_alloc(size, FMT_STRING(ty)) ); + assert(m_inner.is_alloc); } Value Value::with_size(size_t size, bool have_allocation) { Value rv; - if(have_allocation) + if(have_allocation || size > sizeof(m_inner.direct.data)) { - rv.allocation = Allocation::new_alloc(size); + new(&rv.m_inner.alloc) Inner::Alloc( Allocation::new_alloc(size, FMT_STRING("with_size(" << size << ")")) ); } else { - rv.direct_data.size = static_cast<uint8_t>(size); - rv.direct_data.mask[0] = 0; - rv.direct_data.mask[1] = 0; + new(&rv.m_inner.direct) Inner::Direct(size); } return rv; } Value Value::new_fnptr(const ::HIR::Path& fn_path) { Value rv( ::HIR::TypeRef(::HIR::CoreType { RawType::Function }) ); - assert(rv.allocation); - rv.allocation->relocations.push_back(Relocation { 0, RelocationPtr::new_fcn(fn_path) }); - rv.allocation->data.at(0) = 0; - rv.allocation->mask.at(0) = (1 << POINTER_SIZE)-1; + rv.write_ptr(0, Allocation::PTR_BASE, RelocationPtr::new_fcn(fn_path)); return rv; } Value Value::new_ffiptr(FFIPointer ffi) { Value rv( ::HIR::TypeRef(::HIR::CoreType { RawType::USize }) ); - rv.create_allocation(); - rv.allocation->relocations.push_back(Relocation { 0, RelocationPtr::new_ffi(ffi) }); - rv.allocation->data.at(0) = 0; - rv.allocation->mask.at(0) = (1 << POINTER_SIZE)-1; + assert( !rv.m_inner.is_alloc ); + rv.write_ptr(0, Allocation::PTR_BASE, RelocationPtr::new_ffi(ffi)); return rv; } Value Value::new_pointer(::HIR::TypeRef ty, uint64_t v, RelocationPtr r) { assert(ty.get_wrapper()); assert(ty.get_wrapper()->type == TypeWrapper::Ty::Borrow || ty.get_wrapper()->type == TypeWrapper::Ty::Pointer); Value rv(ty); - rv.write_usize(0, v); - rv.allocation->relocations.push_back(Relocation { 0, /*POINTER_SIZE,*/ ::std::move(r) }); + rv.write_ptr(0, v, ::std::move(r)); return rv; } Value Value::new_usize(uint64_t v) { @@ -626,59 +673,57 @@ Value Value::new_i32(int32_t v) { rv.write_i32(0, v); return rv; } +Value Value::new_i64(int64_t v) { + auto rv = Value( ::HIR::TypeRef(RawType::I64) ); + rv.write_i64(0, v); + return rv; +} void Value::create_allocation() { - assert(!this->allocation); - this->allocation = Allocation::new_alloc(this->direct_data.size); - if( this->direct_data.size > 0 ) - this->allocation->mask[0] = this->direct_data.mask[0]; - if( this->direct_data.size > 8 ) - this->allocation->mask[1] = this->direct_data.mask[1]; - ::std::memcpy(this->allocation->data.data(), this->direct_data.data, this->direct_data.size); + assert(!m_inner.is_alloc); + auto new_alloc = Allocation::new_alloc(m_inner.direct.size, "create_allocation"); // TODO: Provide a better name? + auto& direct = m_inner.direct; + if( direct.size > 0 ) + new_alloc->m_mask[0] = direct.mask[0]; + if( direct.size > 8 ) + new_alloc->m_mask[1] = direct.mask[1]; + ::std::memcpy(new_alloc->data_ptr(), direct.data, direct.size); + if( direct.reloc_0 ) + { + new_alloc->set_reloc(0, POINTER_SIZE, ::std::move(direct.reloc_0)); + } + + new(&m_inner.alloc) Inner::Alloc(::std::move(new_alloc)); } void Value::check_bytes_valid(size_t ofs, size_t size) const { if( size == 0 ) return ; - if( this->allocation ) - { - this->allocation->check_bytes_valid(ofs, size); + if( !in_bounds(ofs, size, this->size()) ) { + LOG_ERROR("Read out of bounds " << ofs+size << " >= " << this->size()); + throw "ERROR"; } - else + const auto* mask = this->get_mask(); + for(size_t i = ofs; i < ofs + size; i++) { - if( size == 0 && this->direct_data.size > 0 ) { - return ; - } - if( ofs >= this->direct_data.size ) { - LOG_ERROR("Read out of bounds " << ofs << "+" << size << " > " << int(this->direct_data.size)); - throw "ERROR"; - } - if( ofs+size > this->direct_data.size ) { - LOG_ERROR("Read out of bounds " << ofs+size << " >= " << int(this->direct_data.size)); - throw "ERROR"; - } - for(size_t i = ofs; i < ofs + size; i++) + if( !get_bit(mask, i) ) { - if( !(this->direct_data.mask[i/8] & (1 << i%8)) ) - { - LOG_ERROR("Accessing invalid bytes in value"); - throw "ERROR"; - } + LOG_ERROR("Accessing invalid bytes in value, offset " << i << " of " << *this); } } } void Value::mark_bytes_valid(size_t ofs, size_t size) { - if( this->allocation ) + if( m_inner.is_alloc ) { - this->allocation->mark_bytes_valid(ofs, size); + m_inner.alloc.alloc->mark_bytes_valid(ofs, size); } else { for(size_t i = ofs; i < ofs+size; i++) { - this->direct_data.mask[i/8] |= (1 << i%8); + m_inner.direct.mask[i/8] |= (1 << i%8); } } } @@ -686,18 +731,28 @@ void Value::mark_bytes_valid(size_t ofs, size_t size) Value Value::read_value(size_t ofs, size_t size) const { Value rv; - //TRACE_FUNCTION_R(ofs << ", " << size << ") - " << *this, rv); - if( this->allocation ) + TRACE_FUNCTION_R(ofs << ", " << size << " - " << *this, rv); + if( m_inner.is_alloc ) { - rv = this->allocation->read_value(ofs, size); + rv = m_inner.alloc.alloc->read_value(ofs, size); } else { - // Inline can become inline. - rv.direct_data.size = static_cast<uint8_t>(size); - rv.write_bytes(0, this->direct_data.data+ofs, size); - rv.direct_data.mask[0] = this->direct_data.mask[0]; - rv.direct_data.mask[1] = this->direct_data.mask[1]; + // Inline always fits in inline. + if( ofs == 0 && size == this->size() ) + { + rv = Value(*this); + } + else + { + rv.m_inner.direct.size = static_cast<uint8_t>(size); + memcpy(rv.m_inner.direct.data, this->data_ptr() + ofs, size); + copy_bits(rv.m_inner.direct.mask, 0, this->get_mask(), ofs, size); + if( ofs == 0 ) + { + rv.m_inner.direct.reloc_0 = RelocationPtr(m_inner.direct.reloc_0); + } + } } return rv; } @@ -705,27 +760,14 @@ void Value::read_bytes(size_t ofs, void* dst, size_t count) const { if(count == 0) return ; - if( this->allocation ) + if( m_inner.is_alloc ) { - this->allocation->read_bytes(ofs, dst, count); + m_inner.alloc.alloc->read_bytes(ofs, dst, count); } else { check_bytes_valid(ofs, count); - - if(ofs >= this->direct_data.size ) { - LOG_ERROR("Out of bounds read, " << ofs << "+" << count << " > " << this->size()); - throw "ERROR"; - } - if(count > this->direct_data.size ) { - LOG_ERROR("Out of bounds read, " << ofs << "+" << count << " > " << this->size()); - throw "ERROR"; - } - if(ofs+count > this->direct_data.size ) { - LOG_ERROR("Out of bounds read, " << ofs << "+" << count << " > " << this->size()); - throw "ERROR"; - } - ::std::memcpy(dst, this->direct_data.data + ofs, count); + ::std::memcpy(dst, m_inner.direct.data + ofs, count); } } @@ -733,88 +775,65 @@ void Value::write_bytes(size_t ofs, const void* src, size_t count) { if( count == 0 ) return ; - if( this->allocation ) + if( m_inner.is_alloc ) { - this->allocation->write_bytes(ofs, src, count); + m_inner.alloc.alloc->write_bytes(ofs, src, count); } else { - if(ofs >= this->direct_data.size ) { - LOG_BUG("Write to offset outside value size (" << ofs << "+" << count << " >= " << (int)this->direct_data.size << ")"); + auto& direct = m_inner.direct; + if( !in_bounds(ofs, count, direct.size) ) { + LOG_ERROR("Write extends outside value size (" << ofs << "+" << count << " >= " << (int)direct.size << ")"); } - if(count > this->direct_data.size ){ - LOG_BUG("Write larger than value size (" << ofs << "+" << count << " >= " << (int)this->direct_data.size << ")"); - } - if(ofs+count > this->direct_data.size ) { - LOG_BUG("Write extends outside value size (" << ofs << "+" << count << " >= " << (int)this->direct_data.size << ")"); - } - ::std::memcpy(this->direct_data.data + ofs, src, count); + ::std::memcpy(direct.data + ofs, src, count); mark_bytes_valid(ofs, count); + if( 0 <= ofs && ofs < POINTER_SIZE ) { + direct.reloc_0 = RelocationPtr(); + } } } void Value::write_value(size_t ofs, Value v) { - if( this->allocation ) + if( m_inner.is_alloc ) { - this->allocation->write_value(ofs, ::std::move(v)); + m_inner.alloc.alloc->write_value(ofs, ::std::move(v)); } else { - if( v.allocation && !v.allocation->relocations.empty() ) - { - this->create_allocation(); - this->allocation->write_value(ofs, ::std::move(v)); - } - else - { - write_bytes(ofs, v.direct_data.data, v.direct_data.size); + write_bytes(ofs, v.data_ptr(), v.size()); + // - Copy mask + copy_bits(this->get_mask_mut(), ofs, v.get_mask(), 0, v.size()); - // Lazy way, sets/clears individual bits - for(size_t i = 0; i < v.direct_data.size; i ++) + // TODO: Faster way of knowing where there are relocations + for(size_t i = 0; i < v.size(); i ++) + { + if( auto r = v.get_relocation(i) ) { - uint8_t dbit = 1 << ((ofs+i) % 8); - if( v.direct_data.mask[i/8] & (1 << (i %8)) ) - this->direct_data.mask[ (ofs+i) / 8 ] |= dbit; - else - this->direct_data.mask[ (ofs+i) / 8 ] &= ~dbit; + this->set_reloc(ofs + i, POINTER_SIZE, r); } } } } void Value::write_ptr(size_t ofs, size_t ptr_ofs, RelocationPtr reloc) { - if( !this->allocation ) - { - LOG_ERROR("Writing a pointer with no allocation"); - } - this->allocation->write_ptr(ofs, ptr_ofs, ::std::move(reloc)); -} - -::std::ostream& operator<<(::std::ostream& os, const Value& v) -{ - if( v.allocation ) + if( m_inner.is_alloc ) { - os << *v.allocation; + m_inner.alloc.alloc->write_ptr(ofs, ptr_ofs, ::std::move(reloc)); } else { - auto flags = os.flags(); - os << ::std::hex; - for(size_t i = 0; i < v.direct_data.size; i++) + write_usize(ofs, ptr_ofs); + if( ofs != 0 ) { - if( i != 0 ) - os << " "; - if( v.direct_data.mask[i/8] & (1 << i%8) ) - { - os << ::std::setw(2) << ::std::setfill('0') << (int)v.direct_data.data[i]; - } - else - { - os << "--"; - } + LOG_ERROR("Writing a pointer with no allocation"); } - os.setf(flags); + m_inner.direct.reloc_0 = ::std::move(reloc); } +} + +::std::ostream& operator<<(::std::ostream& os, const Value& v) +{ + os << ValueRef(const_cast<Value&>(v), 0, v.size()); return os; } extern ::std::ostream& operator<<(::std::ostream& os, const ValueRef& v) @@ -830,6 +849,8 @@ extern ::std::ostream& operator<<(::std::ostream& os, const ValueRef& v) case RelocationPtr::Ty::Allocation: { const auto& alloc = alloc_ptr.alloc(); + os << &alloc << "@" << v.m_offset << "+" << v.m_size << " "; + auto flags = os.flags(); os << ::std::hex; for(size_t i = v.m_offset; i < ::std::min(alloc.size(), v.m_offset + v.m_size); i++) @@ -837,7 +858,7 @@ extern ::std::ostream& operator<<(::std::ostream& os, const ValueRef& v) if( i != 0 ) os << " "; - if( alloc.mask[i/8] & (1 << i%8) ) + if( alloc.m_mask[i/8] & (1 << i%8) ) { os << ::std::setw(2) << ::std::setfill('0') << (int)alloc.data_ptr()[i]; } @@ -863,9 +884,7 @@ extern ::std::ostream& operator<<(::std::ostream& os, const ValueRef& v) break; case RelocationPtr::Ty::StdString: { const auto& s = alloc_ptr.str(); - assert(v.m_offset < s.size()); - assert(v.m_size < s.size()); - assert(v.m_offset + v.m_size <= s.size()); + assert( in_bounds(v.m_offset, v.m_size, s.size()) ); auto flags = os.flags(); os << ::std::hex; for(size_t i = v.m_offset; i < v.m_offset + v.m_size; i++) @@ -879,9 +898,11 @@ extern ::std::ostream& operator<<(::std::ostream& os, const ValueRef& v) break; } } - else if( v.m_value && v.m_value->allocation ) + else if( v.m_value && v.m_value->m_inner.is_alloc ) { - const auto& alloc = *v.m_value->allocation; + const auto& alloc = *v.m_value->m_inner.alloc.alloc; + + os << &alloc << "@" << v.m_offset << "+" << v.m_size << " "; auto flags = os.flags(); os << ::std::hex; @@ -890,7 +911,7 @@ extern ::std::ostream& operator<<(::std::ostream& os, const ValueRef& v) if( i != 0 ) os << " "; - if( alloc.mask[i/8] & (1 << i%8) ) + if( alloc.m_mask[i/8] & (1 << i%8) ) { os << ::std::setw(2) << ::std::setfill('0') << (int)alloc.data_ptr()[i]; } @@ -913,7 +934,7 @@ extern ::std::ostream& operator<<(::std::ostream& os, const ValueRef& v) } else if( v.m_value ) { - const auto& direct = v.m_value->direct_data; + const auto& direct = v.m_value->m_inner.direct; auto flags = os.flags(); os << ::std::hex; @@ -931,6 +952,10 @@ extern ::std::ostream& operator<<(::std::ostream& os, const ValueRef& v) } } os.setf(flags); + if(direct.reloc_0) + { + os << " { " << direct.reloc_0 << " }"; + } } else { @@ -939,11 +964,28 @@ extern ::std::ostream& operator<<(::std::ostream& os, const ValueRef& v) return os; } +void ValueRef::mark_bytes_valid(size_t ofs, size_t size) +{ + if( m_alloc ) { + switch(m_alloc.get_ty()) + { + case RelocationPtr::Ty::Allocation: + m_alloc.alloc().mark_bytes_valid(m_offset + ofs, size); + break; + default: + LOG_TODO("mark_valid in " << m_alloc); + } + } + else { + m_value->mark_bytes_valid(m_offset + ofs, size); + } +} + Value ValueRef::read_value(size_t ofs, size_t size) const { if( size == 0 ) return Value(); - if( !(ofs < m_size && size <= m_size && ofs + size <= m_size) ) { + if( !in_bounds(ofs, size, m_size) ) { LOG_ERROR("Read exceeds bounds, " << ofs << " + " << size << " > " << m_size << " - from " << *this); } if( m_alloc ) { @@ -952,25 +994,27 @@ Value ValueRef::read_value(size_t ofs, size_t size) const case RelocationPtr::Ty::Allocation: return m_alloc.alloc().read_value(m_offset + ofs, size); case RelocationPtr::Ty::StdString: { + LOG_ASSERT(in_bounds(m_offset + ofs, size, m_alloc.str().size()), ""); auto rv = Value::with_size(size, false); - //ASSERT_BUG(ofs <= m_alloc.str().size(), ""); - //ASSERT_BUG(size <= m_alloc.str().size(), ""); - //ASSERT_BUG(ofs+size <= m_alloc.str().size(), ""); - assert(m_offset+ofs <= m_alloc.str().size() && size <= m_alloc.str().size() && m_offset+ofs+size <= m_alloc.str().size()); rv.write_bytes(0, m_alloc.str().data() + m_offset + ofs, size); return rv; } + case RelocationPtr::Ty::FfiPointer: { + LOG_ASSERT(in_bounds(m_offset + ofs, size, m_alloc.ffi().get_size()), ""); + auto rv = Value::with_size(size, false); + rv.write_bytes(0, reinterpret_cast<const char*>(m_alloc.ffi().ptr_value) + m_offset + ofs, size); + return rv; + } default: - //ASSERT_BUG(m_alloc.is_alloc(), "read_value on non-data backed Value - " << ); - throw "TODO"; + LOG_TODO("read_value from " << m_alloc); } } else { return m_value->read_value(m_offset + ofs, size); } } -bool ValueRef::compare(const void* other, size_t other_len) const +bool ValueRef::compare(size_t offset, const void* other, size_t other_len) const { - check_bytes_valid(0, other_len); - return ::std::memcmp(data_ptr(), other, other_len) == 0; + check_bytes_valid(offset, other_len); + return ::std::memcmp(data_ptr() + offset, other, other_len) == 0; } |