template plcrash_error_t gnu_ehptr_reader::read()

in Source/PLCrashAsyncDwarfPrimitives.cpp [120:352]


template <typename machine_ptr> plcrash_error_t gnu_ehptr_reader<machine_ptr>::read (plcrash_async_mobject_t *mobj,
                                                                                     pl_vm_address_t location,
                                                                                     pl_vm_off_t offset,
                                                                                     DW_EH_PE_t encoding,
                                                                                     machine_ptr *result,
                                                                                     size_t *size)
{
    plcrash_error_t err;
    
    /* Skip DW_EH_pe_omit -- as per LSB 4.1.0, this signifies that no value is present */
    if (encoding == DW_EH_PE_omit) {
        PLCF_DEBUG("Skipping decoding of DW_EH_PE_omit pointer");
        return PLCRASH_ENOTFOUND;
    }
    
    /* Initialize the output size; we apply offsets to this size to allow for aligning the
     * address prior to reading the pointer data, etc. */
    *size = 0;
    
    /* Calculate the base address; bits 5-8 are used to specify the relative offset type */
    machine_ptr base;
    switch (encoding & 0x70) {
        case DW_EH_PE_pcrel:
            /*
             * Set the ptr PC relative base to our current read offset. The LSB specification does not define what value should
             * be used for the DW_EH_PE_pcrel base address; reviewing the available implementations demonstrates that
             * the current read buffer position should be used.
             */
            base = (machine_ptr)(location + offset);
            break;
            
        case DW_EH_PE_absptr:
            /* No flags are set */
            base = 0x0;
            break;
            
        case DW_EH_PE_textrel:
            if (!_text_base.valid) {
                PLCF_DEBUG("Cannot decode DW_EH_PE_textrel value with PLCRASH_ASYNC_DWARF_INVALID_BASE_ADDR text_addr");
                return PLCRASH_ENOTSUP;
            }
            base = _text_base.address;
            break;
            
        case DW_EH_PE_datarel:
            if (!_data_base.valid) {
                PLCF_DEBUG("Cannot decode DW_EH_PE_datarel value with PLCRASH_ASYNC_DWARF_INVALID_BASE_ADDR data_base");
                return PLCRASH_ENOTSUP;
            }
            base = _data_base.address;
            break;
            
        case DW_EH_PE_funcrel:
            if (!_func_base.valid) {
                PLCF_DEBUG("Cannot decode DW_EH_PE_funcrel value with PLCRASH_ASYNC_DWARF_INVALID_BASE_ADDR func_base");
                return PLCRASH_ENOTSUP;
            }
            
            base = _func_base.address;
            break;
            
        case DW_EH_PE_aligned: {
            /* Verify availability of required base addresses */
            if (!_has_frame_section_base) {
                PLCF_DEBUG("Cannot decode DW_EH_PE_aligned value without a valid frame section base configured");
                return PLCRASH_ENOTSUP;
            }
            
            /* Compute the offset+alignment relative to the section base */
            PLCF_ASSERT(location >= _frame_section_base);
            machine_ptr locationOffset = (machine_ptr)location - _frame_section_base;
            
            /* Apply to the VM load address for the section. */
            machine_ptr vm_addr = _frame_section_vm_addr + locationOffset;
            machine_ptr vm_aligned = (vm_addr + (sizeof(machine_ptr)-1)) & ~(sizeof(machine_ptr)-1);
            
            /* Apply the new offset to the actual load address */
            location += (vm_aligned - vm_addr);
            
            /* Set the base size to the number of bytes skipped */
            base = 0x0;
            *size = (size_t)(vm_aligned - vm_addr);
            break;
        }
            
        default:
            PLCF_DEBUG("Unsupported pointer base encoding of 0x%x", encoding);
            return PLCRASH_ENOTSUP;
    }
    
    /*
     * Decode and return the pointer value [+ offset].
     *
     * TODO: This code permits overflow to occur under the assumption that the failure will be caught
     * when safely dereferencing the resulting address. This should only occur when either bad data is presented,
     * or due to an implementation flaw in this code path -- in those cases, it would be preferable to
     * detect overflow early.
     */
    switch (encoding & 0x0F) {
        case DW_EH_PE_absptr: {
            machine_ptr value;
            
            if ((err = plcrash_async_dwarf_read_uintmax64(mobj, _byteorder, location, offset, sizeof(machine_ptr), &value)) != PLCRASH_ESUCCESS) {
                PLCF_DEBUG("Failed to read value at 0x%" PRIx64, (uint64_t) location);
                return err;
            }
            
            *result = value + base;
            *size += sizeof(machine_ptr);
            break;
        }
            
        case DW_EH_PE_uleb128: {
            uint64_t ulebv;
            pl_vm_size_t uleb_size;
            
            if ((err = plcrash_async_dwarf_read_uleb128(mobj, location, offset, &ulebv, &uleb_size)) != PLCRASH_ESUCCESS) {
                PLCF_DEBUG("Failed to read uleb128 value at 0x%" PRIx64, (uint64_t) location);
                return err;
            }
            
            *result = (machine_ptr)(ulebv + base);
            *size += uleb_size;
            break;
        }
            
        case DW_EH_PE_udata2: {
            uint16_t udata2;
            if ((err = plcrash_async_mobject_read_uint16(mobj, _byteorder, location, offset, &udata2)) != PLCRASH_ESUCCESS) {
                PLCF_DEBUG("Failed to read udata2 value at 0x%" PRIx64, (uint64_t) location);
                return err;
            }
            
            *result = udata2 + base;
            *size += 2;
            break;
        }
            
        case DW_EH_PE_udata4: {
            uint32_t udata4;
            if ((err = plcrash_async_mobject_read_uint32(mobj, _byteorder, location, offset, &udata4)) != PLCRASH_ESUCCESS) {
                PLCF_DEBUG("Failed to read udata4 value at 0x%" PRIx64, (uint64_t) location);
                return err;
            }
            
            *result = udata4 + base;
            *size += 4;
            break;
        }
            
        case DW_EH_PE_udata8: {
            uint64_t udata8;
            if ((err = plcrash_async_mobject_read_uint64(mobj, _byteorder, location, offset, &udata8)) != PLCRASH_ESUCCESS) {
                PLCF_DEBUG("Failed to read udata8 value at 0x%" PRIx64, (uint64_t) location);
                return err;
            }
            
            *result = (machine_ptr)(udata8 + base);
            *size += 8;
            break;
        }
            
        case DW_EH_PE_sleb128: {
            int64_t slebv;
            pl_vm_size_t sleb_size;
            
            if ((err = plcrash_async_dwarf_read_sleb128(mobj, location, offset, &slebv, &sleb_size)) != PLCRASH_ESUCCESS) {
                PLCF_DEBUG("Failed to read sleb128 value at 0x%" PRIx64, (uint64_t) location);
                return err;
            }
            
            *result = (machine_ptr)(slebv + base);
            *size += sleb_size;
            break;
        }
            
        case DW_EH_PE_sdata2: {
            int16_t sdata2;
            
            if ((err = plcrash_async_mobject_read_uint16(mobj, _byteorder, location, offset, (uint16_t *) &sdata2)) != PLCRASH_ESUCCESS) {
                PLCF_DEBUG("Failed to read sdata2 value at 0x%" PRIx64, (uint64_t) location);
                return err;
            }
            
            *result = sdata2 + base;
            *size += 2;
            break;
        }
            
        case DW_EH_PE_sdata4: {
            int32_t sdata4;
            if ((err = plcrash_async_mobject_read_uint32(mobj, _byteorder, location, offset, (uint32_t *) &sdata4)) != PLCRASH_ESUCCESS) {
                PLCF_DEBUG("Failed to read sdata4 value at 0x%" PRIx64, (uint64_t) location);
                return err;
            }
            
            *result = sdata4 + base;
            *size += 4;
            break;
        }
            
        case DW_EH_PE_sdata8: {
            int64_t sdata8;
            if ((err = plcrash_async_mobject_read_uint64(mobj, _byteorder, location, offset, (uint64_t *) &sdata8)) != PLCRASH_ESUCCESS) {
                PLCF_DEBUG("Failed to read sdata8 value at 0x%" PRIx64, (uint64_t) location);
                return err;
            }
            
            *result = (machine_ptr)(sdata8 + base);
            *size += 8;
            break;
        }
            
        default:
            PLCF_DEBUG("Unknown pointer encoding of type 0x%x", encoding);
            return PLCRASH_ENOTSUP;
    }
    
    /* Handle indirection; the target value may only be an absptr; there is no way to define an
     * encoding for the indirected target. */
    if (encoding & DW_EH_PE_indirect) {
        /*
         * An indirect read may refer to memory outside of the eh_frame/debug_section; as such, we use task-based reading to handle
         * indirect reads.
         *
         * TODO: This implementation should provide a resolvable GNUEHPtr value, rather than requiring resolution occur here.
         */
        return plcrash_async_dwarf_read_task_uintmax64(plcrash_async_mobject_task(mobj), _byteorder, (pl_vm_address_t) *result, 0, sizeof(machine_ptr), result);
    }
    
    return PLCRASH_ESUCCESS;

}