in Source/PLCrashAsyncCompactUnwindEncoding.c [163:367]
plcrash_error_t plcrash_async_cfe_reader_find_pc (plcrash_async_cfe_reader_t *reader, pl_vm_address_t pc, pl_vm_address_t *function_base, uint32_t *encoding) {
const plcrash_async_byteorder_t *byteorder = reader->byteorder;
const pl_vm_address_t base_addr = plcrash_async_mobject_base_address(reader->mobj);
/* Find and map the common encodings table */
uint32_t common_enc_count = byteorder->swap32(reader->header.commonEncodingsArrayCount);
uint32_t *common_enc;
{
if (VERIFY_SIZE_T(uint32_t, common_enc_count)) {
PLCF_DEBUG("CFE common encoding count extends beyond the range of size_t");
return PLCRASH_EINVAL;
}
size_t common_enc_len = common_enc_count * sizeof(uint32_t);
uint32_t common_enc_off = byteorder->swap32(reader->header.commonEncodingsArraySectionOffset);
common_enc = plcrash_async_mobject_remap_address(reader->mobj, base_addr, common_enc_off, common_enc_len);
if (common_enc == NULL) {
PLCF_DEBUG("The declared common table lies outside the mapped CFE range");
return PLCRASH_EINVAL;
}
}
/* Find and load the first level entry */
struct unwind_info_section_header_index_entry *first_level_entry = NULL;
{
/* Find and map the index */
uint32_t index_off = byteorder->swap32(reader->header.indexSectionOffset);
uint32_t index_count = byteorder->swap32(reader->header.indexCount);
if (VERIFY_SIZE_T(sizeof(struct unwind_info_section_header_index_entry), index_count)) {
PLCF_DEBUG("CFE index count extends beyond the range of size_t");
return PLCRASH_EINVAL;
}
if (index_count == 0) {
PLCF_DEBUG("CFE index contains no entries");
return PLCRASH_ENOTFOUND;
}
/*
* NOTE: CFE includes an extra entry in the total count of second-level pages, ie, from ld64:
* const uint32_t indexCount = secondLevelPageCount+1;
*
* There's no explanation as to why, and tools appear to explicitly ignore the entry entirely. We do the same
* here.
*/
PLCF_ASSERT(index_count != 0);
index_count--;
/* Load the index entries */
size_t index_len = index_count * sizeof(struct unwind_info_section_header_index_entry);
struct unwind_info_section_header_index_entry *index_entries = plcrash_async_mobject_remap_address(reader->mobj, base_addr, index_off, index_len);
if (index_entries == NULL) {
PLCF_DEBUG("The declared entries table lies outside the mapped CFE range");
return PLCRASH_EINVAL;
}
/* Binary search for the first-level entry */
#define CFE_FUN_BINARY_SEARCH_ENTVAL(_tval) (byteorder->swap32(_tval.functionOffset))
CFE_FUN_BINARY_SEARCH(pc, index_entries, index_count, first_level_entry);
#undef CFE_FUN_BINARY_SEARCH_ENTVAL
if (first_level_entry == NULL) {
PLCF_DEBUG("Could not find a first level CFE entry for pc=%" PRIx64, (uint64_t) pc);
return PLCRASH_ENOTFOUND;
}
}
/* Locate and decode the second-level entry */
uint32_t second_level_offset = byteorder->swap32(first_level_entry->secondLevelPagesSectionOffset);
uint32_t *second_level_kind = plcrash_async_mobject_remap_address(reader->mobj, base_addr, second_level_offset, sizeof(uint32_t));
switch (byteorder->swap32(*second_level_kind)) {
case UNWIND_SECOND_LEVEL_REGULAR: {
struct unwind_info_regular_second_level_page_header *header;
header = plcrash_async_mobject_remap_address(reader->mobj, base_addr, second_level_offset, sizeof(*header));
if (header == NULL) {
PLCF_DEBUG("The second-level page header lies outside the mapped CFE range");
return PLCRASH_EINVAL;
}
/* Find the entries array */
uint32_t entries_offset = byteorder->swap16(header->entryPageOffset);
uint32_t entries_count = byteorder->swap16(header->entryCount);
if (VERIFY_SIZE_T(sizeof(struct unwind_info_regular_second_level_entry), entries_count)) {
PLCF_DEBUG("CFE second level entry count extends beyond the range of size_t");
return PLCRASH_EINVAL;
}
if (!plcrash_async_mobject_verify_local_pointer(reader->mobj, (uintptr_t)header, entries_offset, entries_count * sizeof(struct unwind_info_regular_second_level_entry))) {
PLCF_DEBUG("CFE entries table lies outside the mapped CFE range");
return PLCRASH_EINVAL;
}
/* Binary search for the target entry */
struct unwind_info_regular_second_level_entry *entries = (struct unwind_info_regular_second_level_entry *) (((uintptr_t)header) + entries_offset);
struct unwind_info_regular_second_level_entry *entry = NULL;
#define CFE_FUN_BINARY_SEARCH_ENTVAL(_tval) (byteorder->swap32(_tval.functionOffset))
CFE_FUN_BINARY_SEARCH(pc, entries, entries_count, entry);
#undef CFE_FUN_BINARY_SEARCH_ENTVAL
if (entry == NULL) {
PLCF_DEBUG("Could not find a second level regular CFE entry for pc=%" PRIx64, (uint64_t) pc);
return PLCRASH_ENOTFOUND;
}
*encoding = byteorder->swap32(entry->encoding);
*function_base = byteorder->swap32(entry->functionOffset);
return PLCRASH_ESUCCESS;
}
case UNWIND_SECOND_LEVEL_COMPRESSED: {
struct unwind_info_compressed_second_level_page_header *header;
header = plcrash_async_mobject_remap_address(reader->mobj, base_addr, second_level_offset, sizeof(*header));
if (header == NULL) {
PLCF_DEBUG("The second-level page header lies outside the mapped CFE range");
return PLCRASH_EINVAL;
}
/* Record the base offset */
uint32_t base_foffset = byteorder->swap32(first_level_entry->functionOffset);
/* Find the entries array */
uint32_t entries_offset = byteorder->swap16(header->entryPageOffset);
uint32_t entries_count = byteorder->swap16(header->entryCount);
if (VERIFY_SIZE_T(sizeof(uint32_t), entries_count)) {
PLCF_DEBUG("CFE second level entry count extends beyond the range of size_t");
return PLCRASH_EINVAL;
}
if (!plcrash_async_mobject_verify_local_pointer(reader->mobj, (uintptr_t)header, entries_offset, entries_count * sizeof(uint32_t))) {
PLCF_DEBUG("CFE entries table lies outside the mapped CFE range");
return PLCRASH_EINVAL;
}
/* Binary search for the target entry */
uint32_t *compressed_entries = (uint32_t *) (((uintptr_t)header) + entries_offset);
uint32_t *c_entry_ptr = NULL;
#define CFE_FUN_BINARY_SEARCH_ENTVAL(_tval) (base_foffset + UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(byteorder->swap32(_tval)))
CFE_FUN_BINARY_SEARCH(pc, compressed_entries, entries_count, c_entry_ptr);
#undef CFE_FUN_BINARY_SEARCH_ENTVAL
if (c_entry_ptr == NULL) {
PLCF_DEBUG("Could not find a second level compressed CFE entry for pc=%" PRIx64, (uint64_t) pc);
return PLCRASH_ENOTFOUND;
}
/* Find the actual encoding */
uint32_t c_entry = byteorder->swap32(*c_entry_ptr);
uint8_t c_encoding_idx = UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(c_entry);
/* Save the function base */
*function_base = base_foffset + UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(byteorder->swap32(c_entry));
/* Handle common table entries */
if (c_encoding_idx < common_enc_count) {
/* Found in the common table. The offset is verified as being within the mapped memory range by
* the < common_enc_count check above. */
*encoding = byteorder->swap32(common_enc[c_encoding_idx]);
return PLCRASH_ESUCCESS;
}
/* Map in the encodings table */
uint32_t encodings_offset = byteorder->swap16(header->encodingsPageOffset);
uint32_t encodings_count = byteorder->swap16(header->encodingsCount);
if (VERIFY_SIZE_T(sizeof(uint32_t), encodings_count)) {
PLCF_DEBUG("CFE second level entry count extends beyond the range of size_t");
return PLCRASH_EINVAL;
}
if (!plcrash_async_mobject_verify_local_pointer(reader->mobj, (uintptr_t)header, encodings_offset, encodings_count * sizeof(uint32_t))) {
PLCF_DEBUG("CFE compressed encodings table lies outside the mapped CFE range");
return PLCRASH_EINVAL;
}
uint32_t *encodings = (uint32_t *) (((uintptr_t)header) + encodings_offset);
/* Verify that the entry is within range */
c_encoding_idx -= common_enc_count;
if (c_encoding_idx >= encodings_count) {
PLCF_DEBUG("Encoding index lies outside the second level encoding table");
return PLCRASH_EINVAL;
}
/* Save the results */
*encoding = byteorder->swap32(encodings[c_encoding_idx]);
return PLCRASH_ESUCCESS;
}
default:
PLCF_DEBUG("Unsupported second-level CFE table kind: 0x%" PRIx32 " at 0x%" PRIx32, byteorder->swap32(*second_level_kind), second_level_offset);
return PLCRASH_EINVAL;
}
// Unreachable
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunreachable-code"
__builtin_trap();
return PLCRASH_ENOTFOUND;
#pragma clang diagnostic pop
}