/**
  * Sanity check list validity. Intended to be used from the unit tests; will fire
  * an assertion if the list structure is invalid.
  *
  * This method acquires no locks and is not thread-safe. It should not be used
  * when making concurrent changes to the list, or otherwise outside of a test environment.
  */
 inline void assert_list_valid (void) {
     /* Verify list linkage in both directions. */
     node *prev = NULL;
     for (node *cur = _head; cur != NULL; cur = cur->_next) {
         PLCF_ASSERT(cur->_prev == prev);
         prev = cur;
     }
     
     PLCF_ASSERT(prev == _tail);
 }
/**
 * Iterate over list nodes. This method is async-safe. If no additional nodes are available, will return NULL.
 *
 * The list must be marked for reading before iteration is performed.
 *
 * @param current The current list node, or NULL to start iteration.
 */
template <typename V> typename async_list<V>::node *async_list<V>::next (node *current) {
    PLCF_ASSERT(_refcount > 0);
    
    if (current != NULL)
        return current->_next;
    
    return _head;
}
/**
 * Initialize the @a thread_state using the provided context.
 *
 * @param thread_state The thread state to be initialized.
 * @param uap The context to use for cursor initialization.
 *
 * All registers will be marked as available.
 */
void plcrash_async_thread_state_mcontext_init (plcrash_async_thread_state_t *thread_state, pl_mcontext_t *mctx) {
    /*
     * Copy in the thread state. Unlike the mach thread variants, mcontext_t may only represent
     * the thread state of the host process, and we may assume that the compilation target matches the mcontext_t
     * thread type.
     */
    
#if defined(PLCRASH_ASYNC_THREAD_ARM_SUPPORT) && defined(__LP64__)
    plcrash_async_thread_state_init(thread_state, CPU_TYPE_ARM64);
    
    /* Sanity check. */
    PLCF_ASSERT(sizeof(mctx->__ss) == sizeof(thread_state->arm_state.thread.ts_64));
    
    plcrash_async_memcpy(&thread_state->arm_state.thread.ts_64, &mctx->__ss, sizeof(thread_state->arm_state.thread.ts_64));

#elif defined(PLCRASH_ASYNC_THREAD_ARM_SUPPORT)
    plcrash_async_thread_state_init(thread_state, CPU_TYPE_ARM);

    /* Sanity check. */
    PLCF_ASSERT(sizeof(mctx->__ss) == sizeof(thread_state->arm_state.thread.ts_32));

    plcrash_async_memcpy(&thread_state->arm_state.thread.ts_32, &mctx->__ss, sizeof(thread_state->arm_state.thread.ts_32));
    
#elif defined(PLCRASH_ASYNC_THREAD_X86_SUPPORT) && defined(__LP64__)
    plcrash_async_thread_state_init(thread_state, CPU_TYPE_X86_64);

    /* Sanity check. */
    PLCF_ASSERT(sizeof(mctx->__ss) == sizeof(thread_state->x86_state.thread.uts.ts64));
    PLCF_ASSERT(sizeof(mctx->__es) == sizeof(thread_state->x86_state.exception.ues.es64));
    
    plcrash_async_memcpy(&thread_state->x86_state.thread.uts.ts64, &mctx->__ss, sizeof(thread_state->x86_state.thread.uts.ts64));
    plcrash_async_memcpy(&thread_state->x86_state.exception.ues.es64, &mctx->__es, sizeof(thread_state->x86_state.exception.ues.es64));

#elif defined(PLCRASH_ASYNC_THREAD_X86_SUPPORT)
    plcrash_async_thread_state_init(thread_state, CPU_TYPE_X86);

    /* Sanity check. */
    PLCF_ASSERT(sizeof(mctx->__ss) == sizeof(thread_state->x86_state.thread.uts.ts32));
    PLCF_ASSERT(sizeof(mctx->__es) == sizeof(thread_state->x86_state.exception.ues.es32));

    plcrash_async_memcpy(&thread_state->x86_state.thread.uts.ts32, &mctx->__ss, sizeof(thread_state->x86_state.thread.uts.ts32));
    plcrash_async_memcpy(&thread_state->x86_state.exception.ues.es32, &mctx->__es, sizeof(thread_state->x86_state.exception.ues.es32));
#else
#error Add platform support
#endif

    /* Mark all registers as available */
    memset(&thread_state->valid_regs, 0xFF, sizeof(thread_state->valid_regs));
}
 // Custom new/delete that do not rely on the stdlib
 void *operator new (size_t size) {
     void *ptr = malloc(size);
     PLCF_ASSERT(ptr != NULL);
     return ptr;
 };
/**
 * @internal
 * Encode a ordered register list using the 10 bit register encoding as defined by the CFE format.
 *
 * @param registers The ordered list of registers to encode. These values must correspond to the CFE register values,
 * <em>not</em> the register values as defined in the PLCrashReporter thread state APIs.
 
 * @warning This API is unlikely to be useful outside the CFE encoder implementation, and should not generally be used.
 * Callers must be careful to pass only literal register values defined in the CFE format (eg, values 1-6).
 */
uint32_t apigee_plcrash_async_cfe_register_encode (const uint32_t registers[], uint32_t count) {
    /*
     * Use a positional encoding to encode each integer in the list as an integer value
     * that is less than the previous greatest integer in the list. We know that each
     * integer (numbered 1-6) may appear only once in the list.
     *
     * For example:
     *   6 5 4 3 2 1 ->
     *   5 4 3 2 1 0
     *
     *   6 3 5 2 1 ->
     *   5 2 3 1 0
     *
     *   1 2 3 4 5 6 ->
     *   0 0 0 0 0 0
     */
    uint32_t renumbered[PLCRASH_ASYNC_CFE_SAVED_REGISTER_MAX];
    for (int i = 0; i < count; ++i) {
        unsigned countless = 0;
        for (int j = 0; j < i; ++j)
            if (registers[j] < registers[i])
                countless++;
        
        renumbered[i] = registers[i] - countless - 1;
    }
    
    uint32_t permutation = 0;
    
    /*
     * Using the renumbered list, we map each element of the list (positionally) into a range large enough to represent
     * the range of any valid element, as well as be subdivided to represent the range of later elements.
     *
     * For example, if we use a factor of 120 for the first position (encoding multiples, decoding divides), that
     * provides us with a range of 0-719. There are 6 possible values that may be encoded in 0-719 (assuming later
     * division by 120), the range is broken down as:
     *
     *   0   - 119: 0
     *   120 - 239: 1
     *   240 - 359: 2
     *   360 - 479: 3
     *   480 - 599: 4
     *   600 - 719: 5
     *
     * Within that range, further positions may be encoded. Assuming a value of 1 in position 0, and a factor of
     * 24 for position 1, the range breakdown would be as follows:
     *   120 - 143: 0
     *   144 - 167: 1
     *   168 - 191: 2
     *   192 - 215: 3
     *   216 - 239: 4
     *
     * Note that due to the positional renumbering performed prior to this step, we know that each subsequent position
     * in the list requires fewer elements; eg, position 0 may include 0-5, position 1 0-4, and position 2 0-3. This
     * allows us to allocate smaller overall ranges to represent all possible elements.
     */
    PLCF_ASSERT(PLCRASH_ASYNC_CFE_SAVED_REGISTER_MAX == 6);
    switch (count) {
        case 1:
            permutation |= renumbered[0];
            break;
            
        case 2:
            permutation |= (5*renumbered[0] + renumbered[1]);
            break;
            
        case 3:
            permutation |= (20*renumbered[0] + 4*renumbered[1] + renumbered[2]);
            break;
            
        case 4:
            permutation |= (60*renumbered[0] + 12*renumbered[1] + 3*renumbered[2] + renumbered[3]);
            break;
            
        case 5:
            permutation |= (120*renumbered[0] + 24*renumbered[1] + 6*renumbered[2] + 2*renumbered[3] + renumbered[4]);
            break;
            
        case 6:
            /*
             * There are 6 elements in the list, 6 possible values for each element, and values may not repeat. The
             * value of the last element can be derived from the values previously seen (and due to the positional
             * renumbering performed above, the value of the last element will *always* be 0.
             */
            permutation |= (120*renumbered[0] + 24*renumbered[1] + 6*renumbered[2] + 2*renumbered[3] + renumbered[4]);
            break;
    }
    
    PLCF_ASSERT((permutation & 0x3FF) == permutation);
    return permutation;
}
/**
 * @internal
 * Decode a ordered register list from the 10 bit register encoding as defined by the CFE format.
 *
 * @param permutation The 10-bit encoded register list.
 * @param count The number of registers to decode from @a permutation.
 * @param registers On return, the ordered list of decoded register values. These values must correspond to the CFE
 * register values, <em>not</em> the register values as defined in the PLCrashReporter thread state APIs.
 *
 * @warning This API is unlikely to be useful outside the CFE encoder implementation, and should not generally be used.
 * Callers must be careful to pass only literal register values defined in the CFE format (eg, values 1-6).
 */
void apigee_plcrash_async_cfe_register_decode (uint32_t permutation, uint32_t count, uint32_t registers[]) {
    PLCF_ASSERT(count <= PLCRASH_ASYNC_CFE_SAVED_REGISTER_MAX);
    
    /*
     * Each register is encoded by mapping the values to a 10-bit range, and then further sub-ranges within that range,
     * with a subrange allocated to each position. See the encoding function for full documentation.
     */
	int permunreg[PLCRASH_ASYNC_CFE_SAVED_REGISTER_MAX];
#define PERMUTE(pos, factor) do { \
permunreg[pos] = permutation/factor; \
permutation -= (permunreg[pos]*factor); \
} while (0)
    
    PLCF_ASSERT(PLCRASH_ASYNC_CFE_SAVED_REGISTER_MAX == 6);
	switch (count) {
		case 6:
            PERMUTE(0, 120);
            PERMUTE(1, 24);
            PERMUTE(2, 6);
            PERMUTE(3, 2);
            PERMUTE(4, 1);
            
            /*
             * There are 6 elements in the list, 6 possible values for each element, and values may not repeat. The
             * value of the last element can be derived from the values previously seen (and due to the positional
             * renumbering performed, the value of the last element will *always* be 0).
             */
            permunreg[5] = 0;
			break;
		case 5:
            PERMUTE(0, 120);
            PERMUTE(1, 24);
            PERMUTE(2, 6);
            PERMUTE(3, 2);
            PERMUTE(4, 1);
			break;
		case 4:
            PERMUTE(0, 60);
            PERMUTE(1, 12);
            PERMUTE(2, 3);
            PERMUTE(3, 1);
			break;
		case 3:
            PERMUTE(0, 20);
            PERMUTE(1, 4);
            PERMUTE(2, 1);
			break;
		case 2:
            PERMUTE(0, 5);
            PERMUTE(1, 1);
			break;
		case 1:
            PERMUTE(0, 1);
			break;
	}
#undef PERMUTE
    
	/* Recompute the actual register values based on the position-relative values. */
	bool position_used[PLCRASH_ASYNC_CFE_SAVED_REGISTER_MAX+1] = { 0 };
    
	for (uint32_t i = 0; i < count; ++i) {
		int renumbered = 0;
		for (int u = 1; u < 7; u++) {
			if (!position_used[u]) {
				if (renumbered == permunreg[i]) {
					registers[i] = u;
					position_used[u] = true;
					break;
				}
				renumbered++;
			}
		}
	}
}
 /**
  * Rethrn the otal length of the opcode stream, in bytes, for this rule. This method is only applicable to and may only be called on
  * DWARF_CFA_STATE_CFA_TYPE_EXPRESSION rules.
  */
 pl_vm_size_t expression_length (void) {
     PLCF_ASSERT(_cfa_type == DWARF_CFA_STATE_CFA_TYPE_EXPRESSION);
     return _cfa_data.expression.length;
 }
/**
 * Return the compact frame encoding entry for @a pc via @a encoding, if available.
 *
 * @param reader The initialized CFE reader which will be searched for the entry.
 * @param pc The PC value to search for within the CFE data. Note that this value must be relative to
 * the target Mach-O image's __TEXT vmaddr.
 * @param function_base On success, will be populated with the base address of the function. This value is relative to
 * the image's load address, rather than the in-memory address of the loaded image.
 * @param encoding On success, will be populated with the compact frame encoding entry.
 *
 * @return Returns PLFRAME_ESUCCCESS on success, or one of the remaining error codes if a CFE parsing error occurs. If
 * the entry can not be found, PLFRAME_ENOTFOUND will be returned.
 */
apigee_plcrash_error_t apigee_plcrash_async_cfe_reader_find_pc (apigee_plcrash_async_cfe_reader_t *reader, pl_vm_address_t pc, pl_vm_address_t *function_base, uint32_t *encoding) {
    const apigee_plcrash_async_byteorder_t *byteorder = reader->byteorder;
    const pl_vm_address_t base_addr = apigee_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 APIGEE_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 = apigee_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 APIGEE_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 APIGEE_PLCRASH_EINVAL;
        }
        
        if (index_count == 0) {
            PLCF_DEBUG("CFE index contains no entries");
            return APIGEE_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 = apigee_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 APIGEE_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 APIGEE_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 = apigee_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 = apigee_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 APIGEE_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 APIGEE_PLCRASH_EINVAL;
            }
            
            if (!apigee_plcrash_async_mobject_verify_local_pointer(reader->mobj, 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 APIGEE_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 APIGEE_PLCRASH_ENOTFOUND;
            }

            *encoding = byteorder->swap32(entry->encoding);
            *function_base = byteorder->swap32(entry->functionOffset);
            return APIGEE_PLCRASH_ESUCCESS;
        }

        case UNWIND_SECOND_LEVEL_COMPRESSED: {
            struct unwind_info_compressed_second_level_page_header *header;
            header = apigee_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 APIGEE_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 APIGEE_PLCRASH_EINVAL;
            }
            
            if (!apigee_plcrash_async_mobject_verify_local_pointer(reader->mobj, header, entries_offset, entries_count * sizeof(uint32_t))) {
                PLCF_DEBUG("CFE entries table lies outside the mapped CFE range");
                return APIGEE_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 APIGEE_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 APIGEE_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 APIGEE_PLCRASH_EINVAL;
            }

            if (!apigee_plcrash_async_mobject_verify_local_pointer(reader->mobj, header, encodings_offset, encodings_count * sizeof(uint32_t))) {
                PLCF_DEBUG("CFE compressed encodings table lies outside the mapped CFE range");
                return APIGEE_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 APIGEE_PLCRASH_EINVAL;
            }

            /* Save the results */
            *encoding = byteorder->swap32(encodings[c_encoding_idx]);
            return APIGEE_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 APIGEE_PLCRASH_EINVAL;
    }

    // Unreachable
    __builtin_trap();
    return APIGEE_PLCRASH_ENOTFOUND;
}
 /**
  * Return the target-relative absolute address of the expression opcode stream for this rule. This method is only applicable to and may only be called on
  * DWARF_CFA_STATE_CFA_TYPE_EXPRESSION rules.
  */
 pl_vm_address_t expression_address (void) {
     PLCF_ASSERT(_cfa_type == DWARF_CFA_STATE_CFA_TYPE_EXPRESSION);
     return _cfa_data.expression.address;
 }
 /**
  * Return the signed register offset for this rule. This method is only applicable to and may only be called on
  * DWARF_CFA_STATE_CFA_TYPE_REGISTER_SIGNED rules.
  */
 machine_ptr_s register_offset_signed (void) {
     PLCF_ASSERT(_cfa_type == DWARF_CFA_STATE_CFA_TYPE_REGISTER_SIGNED);
     return _cfa_data.reg.offset.s_off;
 }
 /**
  * Return the unsigned register offset for this rule. This method is only applicable to and may only be called on
  * DWARF_CFA_STATE_CFA_TYPE_REGISTER rules.
  */
 machine_ptr register_offset (void) {
     PLCF_ASSERT(_cfa_type == DWARF_CFA_STATE_CFA_TYPE_REGISTER);
     return _cfa_data.reg.offset.u_off;
 }
 /**
  * Return the register number for this rule. This method is only applicable to and may only be called on
  * DWARF_CFA_STATE_CFA_TYPE_REGISTER and DWARF_CFA_STATE_CFA_TYPE_REGISTER_SIGNED rules.
  */
 dwarf_cfa_state_regnum_t register_number (void) {
     PLCF_ASSERT(_cfa_type == DWARF_CFA_STATE_CFA_TYPE_REGISTER || _cfa_type == DWARF_CFA_STATE_CFA_TYPE_REGISTER_SIGNED);
     return _cfa_data.reg.regnum;
 }
 /** Destroy the referenced value. */
 ~ReferenceValue() {
     PLCF_ASSERT(refs == 0);
 }