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;
}