static void read_field(Context * ctx, const Symbol * sym, ContextAddress base, ContextAddress * value) { LocationInfo * loc_info = NULL; LocationExpressionState * state = NULL; uint64_t args[1]; void * buf = NULL; size_t size = 0; size_t i; args[0] = base; if (get_location_info(sym, &loc_info) < 0) exception(errno); if (loc_info->args_cnt != 1) str_exception(ERR_OTHER, "Wrong object kind"); state = evaluate_location_expression(ctx, NULL, loc_info->value_cmds.cmds, loc_info->value_cmds.cnt, args, 1); if (state->pieces_cnt > 0) { read_location_pieces(state->ctx, state->stack_frame, state->pieces, state->pieces_cnt, loc_info->big_endian, &buf, &size); } else { ContextAddress sym_size = 0; if (state->stk_pos != 1) str_exception(ERR_OTHER, "Invalid location expression"); if (get_symbol_size(sym, &sym_size) < 0) exception(errno); size = (size_t)sym_size; buf = tmp_alloc(size); if (context_read_mem(state->ctx, (ContextAddress)state->stk[0], buf, size) < 0) exception(errno); } *value = 0; for (i = 0; i < size && i < sizeof(ContextAddress); i++) { *value = *value << 8; *value |= ((uint8_t *)buf)[loc_info->big_endian ? i : size - i - 1]; } }
int ini_server(const char * url, Protocol * p, TCFBroadcastGroup * b) { ChannelServer * serv = NULL; PeerServer * ps = NULL; Trap trap; if (!set_trap(&trap)) { bcg = NULL; proto = NULL; if (ps != NULL) peer_server_free(ps); errno = trap.error; return -1; } bcg = b; proto = p; ps = channel_peer_from_url(url); if (ps == NULL) str_exception(ERR_OTHER, "Invalid server URL"); peer_server_addprop(ps, loc_strdup("Name"), loc_strdup(PROXY_NAME)); peer_server_addprop(ps, loc_strdup("Proxy"), loc_strdup("")); SERVER_ADDPROP_HOOK; serv = channel_server(ps); if (serv == NULL) exception(errno); serv->new_conn = channel_new_connection; clear_trap(&trap); add_channel_redirection_listener(channel_redirection_listener); return 0; }
DWARFCache * get_dwarf_cache(ELF_File * File) { DWARFCache * Cache = (DWARFCache *)File->dwarf_dt_cache; if (Cache == NULL) { Trap trap; if (!sCloseListenerOK) { elf_add_close_listener(free_dwarf_cache); sCloseListenerOK = 1; } sCache = Cache = (DWARFCache *)(File->dwarf_dt_cache = loc_alloc_zero(sizeof(DWARFCache))); sCache->magic = SYM_CACHE_MAGIC; sCache->mFile = File; sCache->mObjectHash = loc_alloc_zero(sizeof(ObjectInfo *) * OBJ_HASH_SIZE); if (set_trap(&trap)) { dio_LoadAbbrevTable(File); load_symbol_tables(); load_debug_sections(); clear_trap(&trap); } else { sCache->mErrorCode = trap.error; strncpy(sCache->mErrorMsg, trap.msg, sizeof(sCache->mErrorMsg) - 1); } sCache = NULL; } if (Cache->mErrorCode) str_exception(Cache->mErrorCode, Cache->mErrorMsg); return Cache; }
static void load_debug_sections(void) { Trap trap; unsigned idx; ELF_File * File = sCache->mFile; memset(&trap, 0, sizeof(trap)); sSymbolTableLen = sCache->mSymbolTableLen; sObjectList = NULL; sObjectListTail = NULL; sCompUnitsMax = 0; for (idx = 1; idx < File->section_cnt; idx++) { ELF_Section * sec = File->sections + idx; if (sec->size == 0) continue; if (sec->name == NULL) continue; if (strcmp(sec->name, ".debug") == 0 || strcmp(sec->name, ".debug_info") == 0) { sDebugSection = sec; sParentObject = NULL; sPrevSibling = NULL; dio_EnterDebugSection(NULL, sec, 0); if (set_trap(&trap)) { while (dio_GetPos() < sec->size) { dio_ReadUnit(&sUnitDesc, entry_callback); sCompUnit->mDesc = sUnitDesc; } clear_trap(&trap); } dio_ExitSection(); sParentObject = NULL; sPrevSibling = NULL; sCompUnit = NULL; sDebugSection = NULL; if (trap.error) break; } else if (strcmp(sec->name, ".debug_ranges") == 0) { sCache->mDebugRanges = sec; } else if (strcmp(sec->name, ".debug_aranges") == 0) { sCache->mDebugARanges = sec; } else if (strcmp(sec->name, ".debug_line") == 0) { sCache->mDebugLine = sec; } else if (strcmp(sec->name, ".debug_loc") == 0) { sCache->mDebugLoc = sec; } } if (sObjectList == NULL) { loc_free(sCache->mObjectHash); sCache->mObjectHash = NULL; } sCache->mObjectList = sObjectList; sSymbolTableLen = 0; sObjectList = NULL; sObjectListTail = NULL; sCompUnitsMax = 0; if (trap.error) str_exception(trap.error, trap.msg); }
static ContextAddress find_module(Context * ctx, ELF_File * exe_file, ELF_File * module, ContextAddress r_map, ContextAddress r_brk) { #if ENABLE_Symbols Symbol * sym = NULL; int i = 0, n = 0; Symbol ** children = NULL; ContextAddress link = r_map; Symbol * sym_l_addr = NULL; Symbol * sym_l_next = NULL; Symbol * sym_l_tls_modid = NULL; if (find_symbol_by_name(ctx, STACK_NO_FRAME, r_brk, "link_map", &sym) < 0) str_exception(errno, "Cannot find loader symbol: link_map"); if (get_symbol_children(sym, &children, &n) < 0) exception(errno); for (i = 0; i < n; i++) { char * name = NULL; if (get_symbol_name(children[i], &name) < 0) exception(errno); if (name == NULL) continue; if (strcmp(name, "l_map_start") == 0) sym_l_addr = children[i]; else if (strcmp(name, "l_next") == 0) sym_l_next = children[i]; else if (strcmp(name, "l_tls_modid") == 0) sym_l_tls_modid = children[i]; } if (sym_l_addr == NULL || sym_l_next == NULL || sym_l_tls_modid == NULL) str_exception(ERR_OTHER, "Invalid 'link_map' fields"); while (link != 0) { ContextAddress l_tls_modid = 0; read_field(ctx, sym_l_tls_modid, link, &l_tls_modid); if (l_tls_modid != 0) { ContextAddress l_addr = 0; ELF_File * link_file = NULL; read_field(ctx, sym_l_addr, link, &l_addr); elf_map_to_link_time_address(ctx, l_addr, 0, &link_file, NULL); if (link_file != NULL) { if (link_file == module) return l_tls_modid; if (get_dwarf_file(link_file) == module) return l_tls_modid; } } read_field(ctx, sym_l_next, link, &link); } #endif return 0; }
ContextAddress get_tls_address(Context * ctx, ELF_File * file) { ContextAddress mod_tls_addr = 0; RegisterIdScope reg_id_scope; memset(®_id_scope, 0, sizeof(reg_id_scope)); reg_id_scope.machine = file->machine; reg_id_scope.os_abi = file->os_abi; reg_id_scope.elf64 = file->elf64; reg_id_scope.big_endian = file->big_endian; reg_id_scope.id_type = REGNUM_DWARF; switch (file->machine) { case EM_X86_64: { uint8_t buf[8]; ContextAddress tcb_addr = 0; ContextAddress vdt_addr = 0; ContextAddress mod_id = 0; RegisterDefinition * reg_def = get_reg_by_id(ctx, 58, ®_id_scope); if (reg_def == NULL) exception(errno); if (context_read_reg(ctx, reg_def, 0, reg_def->size, buf) < 0) str_exception(errno, "Cannot read TCB base register"); tcb_addr = to_address(buf, reg_def->size, reg_def->big_endian); if (elf_read_memory_word(ctx, file, tcb_addr + 8, &vdt_addr) < 0) str_exception(errno, "Cannot read TCB"); mod_id = get_module_id(ctx, file); if (elf_read_memory_word(ctx, file, vdt_addr + mod_id * 16, &mod_tls_addr) < 0) str_exception(errno, "Cannot read VDT"); if (mod_tls_addr == 0 || mod_tls_addr == ~(ContextAddress)0) str_exception(errno, "Thread local storage is not allocated yet"); } break; default: str_fmt_exception(ERR_INV_CONTEXT, "Thread local storage access is not supported yet for machine type %d", file->machine); } return mod_tls_addr; }
static ContextAddress get_module_id(Context * ctx, ELF_File * module) { ELF_File * exe_file = NULL; ContextAddress addr = elf_get_debug_structure_address(ctx, &exe_file); size_t word_size = exe_file && exe_file->elf64 ? 8 : 4; Trap trap; if (addr == 0 || exe_file == NULL) str_exception(ERR_OTHER, "Cannot find loader debug data"); if (set_trap(&trap)) { ContextAddress r_map = 0; ContextAddress r_brk = 0; ContextAddress mod_id = 0; if (elf_read_memory_word(ctx, exe_file, addr + word_size * 1, &r_map) < 0) exception(errno); if (elf_read_memory_word(ctx, exe_file, addr + word_size * 2, &r_brk) < 0) exception(errno); if (r_map != 0 && r_brk != 0) mod_id = find_module(ctx, exe_file, module, r_map, r_brk); clear_trap(&trap); if (mod_id) return mod_id; } else { str_exception(trap.error, "Cannot access target ELF loader data"); } str_exception(ERR_OTHER, "Cannot get TLS module ID"); return 0; }
static LONG NTAPI VectoredExceptionHandler(PEXCEPTION_POINTERS x) { if (is_dispatch_thread()) { DWORD exception_code = x->ExceptionRecord->ExceptionCode; if (exception_code == EXCEPTION_IN_PAGE_ERROR) { int error = ERR_OTHER; if (x->ExceptionRecord->NumberParameters >= 3) { ULONG status = (ULONG)x->ExceptionRecord->ExceptionInformation[2]; if (status != 0) error = set_nt_status_errno(status); } str_exception(error, "In page error"); } } return EXCEPTION_CONTINUE_SEARCH; }
static void command_get_cache_client(void * x) { GetArgs * args = (GetArgs *)x; Channel * c = cache_channel(); Trap trap; bbf_pos = 0; if (set_trap(&trap)) { int frame = 0; Context * ctx = NULL; RegisterDefinition * reg_def = NULL; if (id2register(args->id, &ctx, &frame, ®_def) < 0) exception(errno); if (ctx->exited) exception(ERR_ALREADY_EXITED); if ((ctx->reg_access & REG_ACCESS_RD_STOP) != 0) { check_all_stopped(ctx); } if ((ctx->reg_access & REG_ACCESS_RD_RUNNING) == 0) { if (!ctx->stopped && context_has_state(ctx)) str_exception(ERR_IS_RUNNING, "Cannot read register if not stopped"); } if (reg_def->size > bbf_len) { bbf_len += 0x100 + reg_def->size; bbf = (uint8_t *)loc_realloc(bbf, bbf_len); } bbf_pos = reg_def->size; memset(bbf, 0, reg_def->size); if (frame < 0 || is_top_frame(ctx, frame)) { if (context_read_reg(ctx, reg_def, 0, reg_def->size, bbf) < 0) exception(errno); } else { StackFrame * info = NULL; if (get_frame_info(ctx, frame, &info) < 0) exception(errno); if (read_reg_bytes(info, reg_def, 0, reg_def->size, bbf) < 0) exception(errno); } clear_trap(&trap); } cache_exit(); write_stringz(&c->out, "R"); write_stringz(&c->out, args->token); write_errno(&c->out, trap.error); json_write_binary(&c->out, bbf, bbf_pos); write_stream(&c->out, 0); write_stream(&c->out, MARKER_EOM); }
static void generate_commands(void) { int i; RegisterRules * reg; RegisterDefinition * reg_def; reg = get_reg(&frame_regs, rules.return_address_register); if (reg->rule != 0) { reg_def = get_reg_by_id(rules.ctx, rules.return_address_register, &rules.reg_id_scope); generate_register_commands(reg, get_PC_definition(rules.ctx), reg_def); } for (i = 0; i < frame_regs.regs_cnt; i++) { reg = get_reg(&frame_regs, i); if (reg->rule == 0) continue; reg_def = get_reg_by_id(rules.ctx, i, &rules.reg_id_scope); generate_register_commands(reg, reg_def, reg_def); } trace_cmds_cnt = 0; switch (frame_regs.cfa_rule) { case RULE_OFFSET: reg_def = get_reg_by_id(rules.ctx, frame_regs.cfa_register, &rules.reg_id_scope); if (reg_def != NULL) { /* TriCore : PCXI needs to be decyphered so it will point ot the CSA * which is an area of the memory where registers were saved. */ if ((rules.reg_id_scope.machine == EM_TRICORE) && (reg_def->dwarf_id == 41)) add_command(SFT_CMD_RD_REG_PCXI_TRICORE)->args.reg = reg_def; else add_command(SFT_CMD_RD_REG)->args.reg = reg_def; if (frame_regs.cfa_offset != 0) { add_command(SFT_CMD_NUMBER)->args.num = frame_regs.cfa_offset; add_command(SFT_CMD_ADD); } } break; case RULE_EXPRESSION: add_dwarf_expression_commands(frame_regs.cfa_expression, frame_regs.cfa_offset); break; default: str_exception(ERR_INV_DWARF, "Invalid .debug_frame"); break; } add_command_sequence(&dwarf_stack_trace_fp, NULL); }
static void command_set_cache_client(void * x) { SetArgs * args = (SetArgs *)x; Channel * c = cache_channel(); int notify = 0; Trap trap; if (set_trap(&trap)) { int frame = 0; Context * ctx = NULL; RegisterDefinition * reg_def = NULL; if (id2register(args->id, &ctx, &frame, ®_def) < 0) exception(errno); if (frame >= 0 && !is_top_frame(ctx, frame)) exception(ERR_INV_CONTEXT); if (ctx->exited) exception(ERR_ALREADY_EXITED); if ((ctx->reg_access & REG_ACCESS_WR_STOP) != 0) { check_all_stopped(ctx); } if ((ctx->reg_access & REG_ACCESS_WR_RUNNING) == 0) { if (!ctx->stopped && context_has_state(ctx)) str_exception(ERR_IS_RUNNING, "Cannot write register if not stopped"); } if ((size_t)args->data_len > reg_def->size) exception(ERR_INV_DATA_SIZE); if (args->data_len > 0) { if (context_write_reg(ctx, reg_def, 0, args->data_len, args->data) < 0) exception(errno); notify = 1; } clear_trap(&trap); } cache_exit(); if (notify) send_event_register_changed(args->id); write_stringz(&c->out, "R"); write_stringz(&c->out, args->token); write_errno(&c->out, trap.error); write_stream(&c->out, MARKER_EOM); loc_free(args->data); }
static U8_T read_frame_data_pointer(U1_T encoding, ELF_Section ** sec) { U8_T v = 0; if (encoding != EH_PE_omit) { U8_T pos = dio_GetPos(); switch (encoding & 0xf) { case EH_PE_absptr: v = dio_ReadAddress(sec); break; case EH_PE_uleb128: v = dio_ReadU8LEB128(); break; case EH_PE_udata2: v = dio_ReadU2(); break; case EH_PE_udata4: v = dio_ReadU4(); break; case EH_PE_udata8: v = dio_ReadU8(); break; case EH_PE_sleb128: v = dio_ReadS8LEB128(); break; case EH_PE_sdata2: v = (I2_T)dio_ReadU2(); break; case EH_PE_sdata4: v = (I4_T)dio_ReadU4(); break; case EH_PE_sdata8: v = (I8_T)dio_ReadU8(); break; default: str_exception(ERR_INV_DWARF, "Unknown encoding of .eh_frame section pointers"); break; } if (v != 0 && sec != NULL) { switch ((encoding >> 4) & 0x7) { case 0: break; case EH_PB_pcrel: *sec = rules.section; v += rules.section->addr + pos; break; case EH_PB_datarel: *sec = rules.section; v += rules.section->addr; break; case EH_PB_textrel: case EH_PB_funcrel: case EH_PB_aligned: default: str_exception(ERR_INV_DWARF, "Unknown encoding of .eh_frame section pointers"); break; } if (encoding & EH_PE_indirect) { unsigned idx; ELF_File * file = rules.section->file; size_t size = rules.address_size; U8_T res = 0; for (idx = 1; idx < file->section_cnt; idx++) { ELF_Section * sec = file->sections + idx; if ((sec->flags & SHF_ALLOC) == 0) continue; if (sec->addr <= v && sec->addr + sec->size >= v + size) { U1_T * p; size_t i; if (sec->data == NULL && elf_load(sec) < 0) exception(errno); p = (U1_T *)sec->data + (uintptr_t)(v - sec->addr); for (i = 0; i < size; i++) { res = (res << 8) | p[file->big_endian ? i : size - i - 1]; } break; } } v = res; } } }
static void generate_register_commands(RegisterRules * reg, RegisterDefinition * dst_reg_def, RegisterDefinition * src_reg_def) { if (dst_reg_def == NULL) return; trace_cmds_cnt = 0; switch (reg->rule) { case RULE_VAL_OFFSET: case RULE_OFFSET: add_command(SFT_CMD_FP); if (reg->offset != 0) { add_command(SFT_CMD_NUMBER)->args.num = reg->offset; add_command(SFT_CMD_ADD); } if (reg->rule == RULE_OFFSET) { LocationExpressionCommand * cmd = add_command(SFT_CMD_RD_MEM); cmd->args.mem.size = dst_reg_def->size; if (cmd->args.mem.size > rules.address_size) cmd->args.mem.size = rules.address_size; cmd->args.mem.big_endian = rules.reg_id_scope.big_endian; } break; case RULE_SAME_VALUE: if (src_reg_def == NULL) return; add_command(SFT_CMD_RD_REG)->args.reg = src_reg_def; break; case RULE_REGISTER: { RegisterDefinition * src_sef = get_reg_by_id(rules.ctx, reg->offset, &rules.reg_id_scope); if (src_sef != NULL) add_command(SFT_CMD_RD_REG)->args.reg = src_sef; } break; case RULE_EXPRESSION: case RULE_VAL_EXPRESSION: add_command(SFT_CMD_FP); add_dwarf_expression_commands(reg->expression, reg->offset); if (reg->rule == RULE_EXPRESSION) { LocationExpressionCommand * cmd = add_command(SFT_CMD_RD_MEM); cmd->args.mem.size = dst_reg_def->size; if (cmd->args.mem.size > rules.address_size) cmd->args.mem.size = rules.address_size; cmd->args.mem.big_endian = rules.reg_id_scope.big_endian; } break; default: str_exception(ERR_INV_DWARF, "Invalid .debug_frame"); break; } if (rules.reg_id_scope.machine == EM_MICROBLAZE && dst_reg_def != NULL && dst_reg_def->dwarf_id == 32 && rules.return_address_register == 15) { add_command(SFT_CMD_NUMBER)->args.num = 8; add_command(SFT_CMD_ADD); } if (rules.reg_id_scope.machine == EM_ARM && dst_reg_def != NULL && dst_reg_def->dwarf_id == 15) { add_command(SFT_CMD_NUMBER)->args.num = 0xfffffffe; add_command(SFT_CMD_AND); } if (dwarf_stack_trace_regs_cnt >= trace_regs_max) { int i; trace_regs_max += 16; dwarf_stack_trace_regs = (StackFrameRegisterLocation **)loc_realloc(dwarf_stack_trace_regs, trace_regs_max * sizeof(StackFrameRegisterLocation *)); for (i = dwarf_stack_trace_regs_cnt; i < trace_regs_max; i++) dwarf_stack_trace_regs[i] = NULL; } if (trace_cmds_cnt == 0) return; add_command_sequence(dwarf_stack_trace_regs + dwarf_stack_trace_regs_cnt++, dst_reg_def); }
static void add_dwarf_expression_commands(U8_T cmds_offs, U4_T cmds_size) { dio_EnterSection(NULL, rules.section, cmds_offs); while (dio_GetPos() < cmds_offs + cmds_size) { U1_T op = dio_ReadU1(); switch (op) { case OP_addr: { ELF_Section * section = NULL; U8_T lt_addr = dio_ReadAddress(§ion); ContextAddress rt_addr = elf_map_to_run_time_address( rules.ctx, rules.section->file, section, (ContextAddress)lt_addr); if (errno) str_exception(errno, "Cannot get object run-time address"); add_command(SFT_CMD_NUMBER)->args.num = rt_addr; } break; case OP_deref: { LocationExpressionCommand * cmd = add_command(SFT_CMD_RD_MEM); cmd->args.mem.size = rules.address_size; cmd->args.mem.big_endian = rules.reg_id_scope.big_endian; } break; case OP_deref_size: { LocationExpressionCommand * cmd = add_command(SFT_CMD_RD_MEM); cmd->args.mem.size = dio_ReadU1(); cmd->args.mem.big_endian = rules.reg_id_scope.big_endian; } break; case OP_const1u: add_command(SFT_CMD_NUMBER)->args.num = dio_ReadU1(); break; case OP_const1s: add_command(SFT_CMD_NUMBER)->args.num = (I1_T)dio_ReadU1(); break; case OP_const2u: add_command(SFT_CMD_NUMBER)->args.num = dio_ReadU2(); break; case OP_const2s: add_command(SFT_CMD_NUMBER)->args.num = (I2_T)dio_ReadU2(); break; case OP_const4u: add_command(SFT_CMD_NUMBER)->args.num = dio_ReadU4(); break; case OP_const4s: add_command(SFT_CMD_NUMBER)->args.num = (I4_T)dio_ReadU4(); break; case OP_const8u: add_command(SFT_CMD_NUMBER)->args.num = dio_ReadU8(); break; case OP_const8s: add_command(SFT_CMD_NUMBER)->args.num = (I8_T)dio_ReadU8(); break; case OP_constu: add_command(SFT_CMD_NUMBER)->args.num = dio_ReadU8LEB128(); break; case OP_consts: add_command(SFT_CMD_NUMBER)->args.num = dio_ReadS8LEB128(); break; case OP_and: add_command(SFT_CMD_AND); break; case OP_minus: add_command(SFT_CMD_SUB); break; case OP_or: add_command(SFT_CMD_OR); break; case OP_plus: add_command(SFT_CMD_ADD); break; case OP_plus_uconst: add_command(SFT_CMD_NUMBER)->args.num = dio_ReadU8LEB128(); add_command(SFT_CMD_ADD); break; case OP_ge: add_command(SFT_CMD_GE); break; case OP_gt: add_command(SFT_CMD_GT); break; case OP_le: add_command(SFT_CMD_LE); break; case OP_lt: add_command(SFT_CMD_LT); break; case OP_shl: add_command(SFT_CMD_SHL); break; case OP_lit0: case OP_lit1: case OP_lit2: case OP_lit3: case OP_lit4: case OP_lit5: case OP_lit6: case OP_lit7: case OP_lit8: case OP_lit9: case OP_lit10: case OP_lit11: case OP_lit12: case OP_lit13: case OP_lit14: case OP_lit15: case OP_lit16: case OP_lit17: case OP_lit18: case OP_lit19: case OP_lit20: case OP_lit21: case OP_lit22: case OP_lit23: case OP_lit24: case OP_lit25: case OP_lit26: case OP_lit27: case OP_lit28: case OP_lit29: case OP_lit30: case OP_lit31: add_command(SFT_CMD_NUMBER)->args.num = op - OP_lit0; break; case OP_reg0: case OP_reg1: case OP_reg2: case OP_reg3: case OP_reg4: case OP_reg5: case OP_reg6: case OP_reg7: case OP_reg8: case OP_reg9: case OP_reg10: case OP_reg11: case OP_reg12: case OP_reg13: case OP_reg14: case OP_reg15: case OP_reg16: case OP_reg17: case OP_reg18: case OP_reg19: case OP_reg20: case OP_reg21: case OP_reg22: case OP_reg23: case OP_reg24: case OP_reg25: case OP_reg26: case OP_reg27: case OP_reg28: case OP_reg29: case OP_reg30: case OP_reg31: { RegisterDefinition * def = get_reg_by_id(rules.ctx, op - OP_reg0, &rules.reg_id_scope); if (def == NULL) str_exception(errno, "Cannot read DWARF frame info"); add_command(SFT_CMD_RD_REG)->args.reg = def; } break; case OP_regx: { unsigned n = (unsigned)dio_ReadULEB128(); RegisterDefinition * def = get_reg_by_id(rules.ctx, n, &rules.reg_id_scope); if (def == NULL) str_exception(errno, "Cannot read DWARF frame info"); add_command(SFT_CMD_RD_REG)->args.reg = def; } break; case OP_breg0: case OP_breg1: case OP_breg2: case OP_breg3: case OP_breg4: case OP_breg5: case OP_breg6: case OP_breg7: case OP_breg8: case OP_breg9: case OP_breg10: case OP_breg11: case OP_breg12: case OP_breg13: case OP_breg14: case OP_breg15: case OP_breg16: case OP_breg17: case OP_breg18: case OP_breg19: case OP_breg20: case OP_breg21: case OP_breg22: case OP_breg23: case OP_breg24: case OP_breg25: case OP_breg26: case OP_breg27: case OP_breg28: case OP_breg29: case OP_breg30: case OP_breg31: { I8_T offs = dio_ReadS8LEB128(); RegisterDefinition * def = get_reg_by_id(rules.ctx, op - OP_breg0, &rules.reg_id_scope); if (def == NULL) str_exception(errno, "Cannot read DWARF frame info"); add_command(SFT_CMD_RD_REG)->args.reg = def; if (offs != 0) { add_command(SFT_CMD_NUMBER)->args.num = offs; add_command(SFT_CMD_ADD); } } break; case OP_bregx: { unsigned n = (unsigned)dio_ReadULEB128(); I8_T offs = dio_ReadS8LEB128(); RegisterDefinition * def = get_reg_by_id(rules.ctx, n, &rules.reg_id_scope); if (def == NULL) str_exception(errno, "Cannot read DWARF frame info"); add_command(SFT_CMD_RD_REG)->args.reg = def; if (offs != 0) { add_command(SFT_CMD_NUMBER)->args.num = offs; add_command(SFT_CMD_ADD); } } break; case OP_nop: break; default: trace(LOG_ALWAYS, "Unsupported DWARF expression op 0x%02x", op); str_exception(ERR_UNSUPPORTED, "Unsupported DWARF expression op"); } } }
static void exec_stack_frame_instruction(U8_T func_addr) { RegisterRules * reg; U1_T op = dio_ReadU1(); U4_T n; switch (op) { case CFA_nop: break; case CFA_set_loc: rules.location = read_frame_data_pointer(rules.addr_encoding, &rules.loc_section, func_addr); break; case CFA_advance_loc1: rules.location += dio_ReadU1() * rules.code_alignment; break; case CFA_advance_loc2: rules.location += dio_ReadU2() * rules.code_alignment; break; case CFA_advance_loc4: rules.location += dio_ReadU4() * rules.code_alignment; break; case CFA_offset_extended: reg = get_reg(&frame_regs, dio_ReadULEB128()); reg->rule = RULE_OFFSET; reg->offset = dio_ReadULEB128() * rules.data_alignment; break; case CFA_restore_extended: n = dio_ReadULEB128(); reg = get_reg(&frame_regs, n); *reg = *get_reg(&cie_regs, n); break; case CFA_undefined: reg = get_reg(&frame_regs, dio_ReadULEB128()); memset(reg, 0, sizeof(*reg)); break; case CFA_same_value: reg = get_reg(&frame_regs, dio_ReadULEB128()); reg->rule = RULE_SAME_VALUE; break; case CFA_register: reg = get_reg(&frame_regs, dio_ReadULEB128()); reg->rule = RULE_REGISTER; reg->offset = dio_ReadULEB128(); break; case CFA_remember_state: copy_register_rules(get_regs_stack_item(regs_stack_pos++), &frame_regs); break; case CFA_restore_state: if (regs_stack_pos <= 0) { str_exception(ERR_INV_DWARF, "Invalid DW_CFA_restore_state instruction"); } copy_register_rules(&frame_regs, get_regs_stack_item(--regs_stack_pos)); break; case CFA_def_cfa: frame_regs.cfa_rule = RULE_OFFSET; frame_regs.cfa_register = dio_ReadULEB128(); frame_regs.cfa_offset = dio_ReadULEB128(); break; case CFA_def_cfa_register: frame_regs.cfa_rule = RULE_OFFSET; frame_regs.cfa_register = dio_ReadULEB128(); break; case CFA_def_cfa_offset: frame_regs.cfa_rule = RULE_OFFSET; frame_regs.cfa_offset = dio_ReadULEB128(); break; case CFA_def_cfa_expression: frame_regs.cfa_rule = RULE_EXPRESSION; frame_regs.cfa_offset = dio_ReadULEB128(); frame_regs.cfa_expression = dio_GetPos(); dio_Skip(frame_regs.cfa_offset); break; case CFA_expression: reg = get_reg(&frame_regs, dio_ReadULEB128()); reg->rule = RULE_EXPRESSION; reg->offset = dio_ReadULEB128(); reg->expression = dio_GetPos(); dio_Skip(reg->offset); break; case CFA_offset_extended_sf: reg = get_reg(&frame_regs, dio_ReadULEB128()); reg->rule = RULE_OFFSET; reg->offset = dio_ReadSLEB128() * rules.data_alignment; break; case CFA_def_cfa_sf: frame_regs.cfa_rule = RULE_OFFSET; frame_regs.cfa_register = dio_ReadULEB128(); frame_regs.cfa_offset = dio_ReadSLEB128() * rules.data_alignment; break; case CFA_def_cfa_offset_sf: frame_regs.cfa_rule = RULE_OFFSET; frame_regs.cfa_offset = dio_ReadSLEB128() * rules.data_alignment; break; case CFA_val_offset: reg = get_reg(&frame_regs, dio_ReadULEB128()); reg->rule = RULE_VAL_OFFSET; reg->offset = dio_ReadULEB128() * rules.data_alignment; break; case CFA_val_offset_sf: reg = get_reg(&frame_regs, dio_ReadULEB128()); reg->rule = RULE_VAL_OFFSET; reg->offset = dio_ReadSLEB128() * rules.data_alignment; break; case CFA_val_expression: reg = get_reg(&frame_regs, dio_ReadULEB128()); reg->rule = RULE_VAL_EXPRESSION; reg->offset = dio_ReadULEB128(); reg->expression = dio_GetPos(); dio_Skip(reg->offset); break; case CFA_CFA_GNU_window_save: /* SPARC-specific code */ for (n = 8; n < 16; n++) { reg = get_reg(&frame_regs, n); reg->rule = RULE_REGISTER; reg->offset = n + 16; } for (n = 16; n < 32; n++) { reg = get_reg(&frame_regs, n); reg->rule = RULE_OFFSET; reg->offset = (n - 16) * (rules.reg_id_scope.machine == EM_SPARCV9 ? 8 : 4); } break; case CFA_GNU_args_size: /* This instruction specifies the total size of the arguments * which have been pushed onto the stack. Not used by the debugger. */ dio_ReadULEB128(); break; case CFA_GNU_negative_offset_ext: /* This instruction is identical to DW_CFA_offset_extended_sf * except that the operand is subtracted to produce the offset. */ reg = get_reg(&frame_regs, dio_ReadULEB128()); reg->rule = RULE_OFFSET; reg->offset = -dio_ReadSLEB128() * rules.data_alignment; break; default: switch (op >> 6) { case 0: str_exception(ERR_INV_DWARF, "Unsupported instruction in Call Frame Information"); break; case 1: /* DW_CFA_advance_loc */ rules.location += (op & 0x3f) * rules.code_alignment; break; case 2: /* DW_CFA_offset */ reg = get_reg(&frame_regs, op & 0x3f); reg->rule = RULE_OFFSET; reg->offset = dio_ReadULEB128() * rules.data_alignment; break; case 3: /* DW_CFA_restore */ n = op & 0x3f; reg = get_reg(&frame_regs, n); *reg = *get_reg(&cie_regs, n); break; } } }
static U8_T read_frame_data_pointer(U1_T encoding, ELF_Section ** sec, U8_T func_addr) { U8_T v = 0; U8_T pos; unsigned idx; ELF_File * file; if (encoding == EH_PE_omit) return 0; pos = dio_GetPos(); /* Decode the base or adjust the offset */ switch ((encoding >> 4) & 0x7) { case 0: case EH_PB_funcrel: v = func_addr; break; case EH_PB_pcrel: if (sec != NULL) { v = pos + rules.section->addr; } break; case EH_PB_datarel: if (sec != NULL) { v = rules.section->addr; } break; case EH_PB_textrel: if (sec != NULL && rules.text_section != NULL) { v = rules.text_section->addr; } break; case EH_PB_aligned: if ((pos % rules.address_size) != 0) dio_SetPos(pos + (rules.address_size - (pos % rules.address_size))); break; default: str_exception(ERR_INV_DWARF, "Unknown encoding of .eh_frame section pointers"); break; } /* Decode the value */ switch (encoding & 0xf) { case EH_PE_absptr: v += dio_ReadAddress(sec); break; case EH_PE_uleb128: v += dio_ReadU8LEB128(); break; case EH_PE_udata2: v += dio_ReadAddressX(sec, 2); break; case EH_PE_udata4: v += dio_ReadAddressX(sec, 4); break; case EH_PE_udata8: v += dio_ReadAddressX(sec, 8); break; case EH_PE_sleb128: v += dio_ReadS8LEB128(); break; case EH_PE_sdata2: v += (I2_T)dio_ReadAddressX(sec, 2); break; case EH_PE_sdata4: v += (I4_T)dio_ReadAddressX(sec, 4); break; case EH_PE_sdata8: v += (I8_T)dio_ReadAddressX(sec, 8); break; default: str_exception(ERR_INV_DWARF, "Unknown encoding of .eh_frame section pointers"); break; } if (encoding & EH_PE_indirect) { size_t size = rules.address_size; U8_T res = 0; file = rules.section->file; for (idx = 1; idx < file->section_cnt; idx++) { ELF_Section * sec = file->sections + idx; if ((sec->flags & SHF_ALLOC) == 0) continue; if (sec->addr <= v && sec->addr + sec->size >= v + size) { U1_T * p; size_t i; if (sec->data == NULL && elf_load(sec) < 0) exception(errno); p = (U1_T *)sec->data + (uintptr_t)(v - sec->addr); for (i = 0; i < size; i++) { res = (res << 8) | p[file->big_endian ? i : size - i - 1]; } break; } } v = res; } return v; }
static void relocate(void * r) { ElfRelocateFunc * func; if (!relocs->file->elf64) { if (relocs->type == SHT_REL) { Elf32_Rel bf = *(Elf32_Rel *)r; if (relocs->file->byte_swap) { SWAP(bf.r_offset); SWAP(bf.r_info); } sym_index = ELF32_R_SYM(bf.r_info); reloc_type = ELF32_R_TYPE(bf.r_info); reloc_addend = 0; } else { Elf32_Rela bf = *(Elf32_Rela *)r; if (relocs->file->byte_swap) { SWAP(bf.r_offset); SWAP(bf.r_info); SWAP(bf.r_addend); } sym_index = ELF32_R_SYM(bf.r_info); reloc_type = ELF32_R_TYPE(bf.r_info); reloc_addend = bf.r_addend; } if (sym_index != STN_UNDEF) { Elf32_Sym bf = ((Elf32_Sym *)symbols->data)[sym_index]; if (symbols->file->byte_swap) { SWAP(bf.st_name); SWAP(bf.st_value); SWAP(bf.st_size); SWAP(bf.st_info); SWAP(bf.st_other); SWAP(bf.st_shndx); } switch (bf.st_shndx) { case SHN_ABS: sym_value = bf.st_value; break; case SHN_COMMON: str_exception(ERR_INV_FORMAT, "Common relocation record unsupported"); break; case SHN_UNDEF: str_exception(ERR_INV_FORMAT, "Invalid relocation record"); break; default: if (bf.st_shndx >= symbols->file->section_cnt) str_exception(ERR_INV_FORMAT, "Invalid relocation record"); if (symbols->file->type != ET_EXEC) { sym_value = (symbols->file->sections + bf.st_shndx)->addr + bf.st_value; } else { sym_value = bf.st_value; } *destination_section = symbols->file->sections + bf.st_shndx; break; } } } else { if (relocs->type == SHT_REL) { Elf64_Rel bf = *(Elf64_Rel *)r; if (relocs->file->byte_swap) { SWAP(bf.r_offset); SWAP(bf.r_info); } sym_index = ELF64_R_SYM(bf.r_info); reloc_type = ELF64_R_TYPE(bf.r_info); reloc_addend = 0; } else { Elf64_Rela bf = *(Elf64_Rela *)r; if (relocs->file->byte_swap) { SWAP(bf.r_offset); SWAP(bf.r_info); SWAP(bf.r_addend); } sym_index = ELF64_R_SYM(bf.r_info); reloc_type = ELF64_R_TYPE(bf.r_info); reloc_addend = bf.r_addend; } if (sym_index != STN_UNDEF) { Elf64_Sym bf = ((Elf64_Sym *)symbols->data)[sym_index]; if (symbols->file->byte_swap) { SWAP(bf.st_name); SWAP(bf.st_value); SWAP(bf.st_size); SWAP(bf.st_info); SWAP(bf.st_other); SWAP(bf.st_shndx); } switch (bf.st_shndx) { case SHN_ABS: sym_value = bf.st_value; break; case SHN_COMMON: str_exception(ERR_INV_FORMAT, "Common relocation record unsupported"); break; case SHN_UNDEF: str_exception(ERR_INV_FORMAT, "Invalid relocation record"); break; default: if (bf.st_shndx >= symbols->file->section_cnt) str_exception(ERR_INV_FORMAT, "Invalid relocation record"); if (symbols->file->type != ET_EXEC) { sym_value = (symbols->file->sections + bf.st_shndx)->addr + bf.st_value; } else { sym_value = bf.st_value; } *destination_section = symbols->file->sections + bf.st_shndx; break; } } } /* For executable file we don't need to apply the relocation, * all we need is destination_section */ if (section->file->type != ET_REL) return; func = elf_relocate_funcs; while (func->machine != section->file->machine) { if (func->func == NULL) str_exception(ERR_INV_FORMAT, "Unsupported ELF machine code"); func++; } func->func(); }
int elf_enumerate_symbols (Context * ctx, const char * file_name, EnumerateSymbols ** enum_syms, EnumerateBatchSymbolsCallBack * call_back, void * args) { Trap trap; ELF_File * file; unsigned sec_idx; int has_more = 0; if (!set_trap(&trap)) { loc_free (*enum_syms); *enum_syms = NULL; return -1; } if (ctx == NULL && file_name == NULL) { assert (*enum_syms != NULL); file = elf_open ((*enum_syms)->file_name); if (file == NULL) exception (errno); /* * Check that the file is identical to the initial file and the context * still exists. */ if (file->ino != (*enum_syms)->ino || file->dev != (*enum_syms)->dev || file->mtime != (*enum_syms)->mtime) { str_exception(ERR_OTHER, "The elf symbol file has changed"); } else { ctx = id2ctx((*enum_syms)->ctxId); if (ctx == NULL) exception (ERR_INV_CONTEXT); else if (ctx->exited) exception (ERR_ALREADY_EXITED); } sec_idx = (*enum_syms)->sec_idx; } else { unsigned symtab_idx = 0; unsigned dynsym_idx = 0; unsigned ix; assert (file_name != NULL && enum_syms != NULL && *enum_syms == NULL); file = elf_open (file_name); if (file == NULL) exception (errno); if (file->sections == NULL) str_exception(ERR_OTHER, "The file does not have sections"); /* Look for the symbol table sections */ for (ix = 0; ix < file->section_cnt && (symtab_idx == 0 || dynsym_idx == 0); ix++) { ELF_Section * sec = file->sections + ix; if (sec->type == SHT_SYMTAB) symtab_idx = ix; else if (sec->type == SHT_DYNSYM) dynsym_idx = ix; } if (symtab_idx == 0 && dynsym_idx == 0) str_exception(ERR_OTHER, "The file does not have a symbol table"); /* Set priority to the symbol table */ if (symtab_idx != 0) sec_idx = symtab_idx; else sec_idx = dynsym_idx; *enum_syms = (EnumerateSymbols *)loc_alloc_zero (sizeof (EnumerateSymbols)); strlcpy ((*enum_syms)->file_name, file_name, sizeof ((*enum_syms)->file_name)); if (strlen (file_name) != strlen ((*enum_syms)->file_name)) str_exception (ERR_OTHER, "File pathname too long"); strlcpy ((*enum_syms)->ctxId, ctx->id, sizeof ((*enum_syms)->ctxId)); (*enum_syms)->dev = file->dev; (*enum_syms)->ino = file->ino; (*enum_syms)->mtime = file->mtime; (*enum_syms)->sec_idx = sec_idx; } has_more = enumerate_symbol_table(ctx, file->sections + sec_idx, *enum_syms, call_back, args); clear_trap(&trap); if (has_more == 0) { loc_free (*enum_syms); *enum_syms = NULL; } return has_more; }
void load_line_numbers(DWARFCache * Cache, CompUnit * Unit) { Trap trap; if (Unit->mFiles != NULL && Unit->mDirs != NULL) return; if (elf_load(Cache->mDebugLine)) exception(errno); dio_EnterDataSection(&Unit->mDesc, Cache->mDebugLine->data, Unit->mLineInfoOffs, Cache->mDebugLine->size); if (set_trap(&trap)) { U8_T header_pos = 0; U1_T opcode_base = 0; U1_T opcode_size[256]; U8_T header_size = 0; U1_T min_instruction_length = 0; U1_T is_stmt_default = 0; I1_T line_base = 0; U1_T line_range = 0; U8_T unit_size = 0; int dwarf64 = 0; LineNumbersState state; /* Read header */ unit_size = dio_ReadU4(); if (unit_size == 0xffffffffu) { unit_size = dio_ReadU8(); unit_size += 12; dwarf64 = 1; } else { unit_size += 4; } dio_ReadU2(); /* line info version */ header_size = dwarf64 ? dio_ReadU8() : (U8_T)dio_ReadU4(); header_pos = dio_GetPos(); min_instruction_length = dio_ReadU1(); is_stmt_default = dio_ReadU1() != 0; line_base = (I1_T)dio_ReadU1(); line_range = dio_ReadU1(); opcode_base = dio_ReadU1(); memset(opcode_size, 0, sizeof(opcode_size)); dio_Read(opcode_size + 1, opcode_base - 1); /* Read directory names */ for (;;) { char * Name = dio_ReadString(); if (Name == NULL) break; add_dir(Unit, Name); } /* Read source sFileLocks info */ for (;;) { U4_T dir = 0; FileInfo File; memset(&File, 0, sizeof(File)); File.mName = dio_ReadString(); if (File.mName == NULL) break; dir = dio_ReadULEB128(); if (dir > 0 && dir <= Unit->mDirsCnt) File.mDir = Unit->mDirs[dir - 1]; File.mModTime = dio_ReadULEB128(); File.mSize = dio_ReadULEB128(); add_file(Unit, &File); } /* Run the program */ if (header_pos + header_size != dio_GetPos()) str_exception(ERR_INV_DWARF, "Invalid line info header"); memset(&state, 0, sizeof(state)); state.mFile = 1; state.mLine = 1; if (is_stmt_default) state.mFlags |= LINE_IsStmt; while (dio_GetPos() < Unit->mLineInfoOffs + unit_size) { U1_T opcode = dio_ReadU1(); if (opcode >= opcode_base) { state.mLine += (unsigned)((int)((opcode - opcode_base) % line_range) + line_base); state.mAddress += (opcode - opcode_base) / line_range * min_instruction_length; add_state(Unit, &state); state.mFlags &= ~(LINE_BasicBlock | LINE_PrologueEnd | LINE_EpilogueBegin); } else if (opcode == 0) { U4_T op_size = dio_ReadULEB128(); U8_T op_pos = dio_GetPos(); switch (dio_ReadU1()) { case DW_LNE_define_file: { U4_T dir = 0; FileInfo File; memset(&File, 0, sizeof(File)); File.mName = dio_ReadString(); dir = dio_ReadULEB128(); if (dir > 0 && dir <= Unit->mDirsCnt) File.mDir = Unit->mDirs[dir - 1]; File.mModTime = dio_ReadULEB128(); File.mSize = dio_ReadULEB128(); add_file(Unit, &File); break; } case DW_LNE_end_sequence: state.mFlags |= LINE_EndSequence; add_state(Unit, &state); memset(&state, 0, sizeof(state)); state.mFile = 1; state.mLine = 1; if (is_stmt_default) state.mFlags |= LINE_IsStmt; else state.mFlags &= ~LINE_IsStmt; break; case DW_LNE_set_address: state.mAddress = (ContextAddress)dio_ReadAddress(); break; default: dio_Skip(op_size - 1); break; } if (dio_GetPos() != op_pos + op_size) str_exception(ERR_INV_DWARF, "Invalid line info op size"); } else { switch (opcode) { case DW_LNS_copy: add_state(Unit, &state); state.mFlags &= ~(LINE_BasicBlock | LINE_PrologueEnd | LINE_EpilogueBegin); break; case DW_LNS_advance_pc: state.mAddress += (ContextAddress)(dio_ReadU8LEB128() * min_instruction_length); break; case DW_LNS_advance_line: state.mLine += dio_ReadSLEB128(); break; case DW_LNS_set_file: state.mFile = dio_ReadULEB128(); break; case DW_LNS_set_column: state.mColumn = dio_ReadULEB128(); break; case DW_LNS_negate_stmt: state.mFlags ^= LINE_IsStmt; break; case DW_LNS_set_basic_block: state.mFlags |= LINE_BasicBlock; break; case DW_LNS_const_add_pc: state.mAddress += (255 - opcode_base) / line_range * min_instruction_length; break; case DW_LNS_fixed_advance_pc: state.mAddress += dio_ReadU2(); break; case DW_LNS_set_prologue_end: state.mFlags |= LINE_PrologueEnd; break; case DW_LNS_set_epilogue_begin: state.mFlags |= LINE_EpilogueBegin; break; case DW_LNS_set_isa: state.mISA = (U1_T)dio_ReadULEB128(); break; default: str_exception(ERR_INV_DWARF, "Invalid line info op code"); break; } } } dio_ExitSection(); clear_trap(&trap); } else { dio_ExitSection(); free_unit_cache(Unit); str_exception(trap.error, trap.msg); } }
static void read_frame_cie(U8_T fde_pos, U8_T pos) { int cie_dwarf64 = 0; U8_T saved_pos = dio_GetPos(); U8_T cie_length = 0; U8_T cie_end = 0; rules.cie_pos = pos; if (pos >= rules.section->size) { str_fmt_exception(ERR_INV_DWARF, "Invalid CIE pointer 0x%" PRIX64 " in FDE at 0x%" PRIX64, pos, fde_pos); } dio_SetPos(pos); cie_length = dio_ReadU4(); if (cie_length == ~(U4_T)0) { cie_length = dio_ReadU8(); cie_dwarf64 = 1; } cie_end = dio_GetPos() + cie_length; dio_Skip(cie_dwarf64 ? 8 : 4); rules.version = dio_ReadU1(); if (rules.version != 1 && rules.version != 3 && rules.version != 4) { str_fmt_exception(ERR_INV_DWARF, "Unsupported version of Call Frame Information: %d", rules.version); } rules.cie_aug = dio_ReadString(); if (rules.cie_aug != NULL && strcmp(rules.cie_aug, "eh") == 0) { rules.cie_eh_data = dio_ReadAddress(&rules.cie_eh_data_section); } if (rules.version >= 4) { rules.address_size = dio_ReadU1(); rules.segment_size = dio_ReadU1(); } else { rules.address_size = rules.section->file->elf64 ? 8 : 4; rules.segment_size = 0; } if (rules.segment_size != 0) { str_exception(ERR_INV_DWARF, "Unsupported Call Frame Information: segment size != 0"); } rules.code_alignment = dio_ReadULEB128(); rules.data_alignment = dio_ReadSLEB128(); rules.return_address_register = dio_ReadULEB128(); rules.lsda_encoding = 0; rules.prh_encoding = 0; rules.addr_encoding = 0; if (rules.cie_aug != NULL && rules.cie_aug[0] == 'z') { U4_T aug_length = dio_ReadULEB128(); U8_T aug_pos = dio_GetPos(); char * p = rules.cie_aug + 1; while (*p) { switch (*p++) { case 'L': rules.lsda_encoding = dio_ReadU1(); break; case 'P': { Trap trap; U8_T addr_pos; rules.prh_encoding = dio_ReadU1(); addr_pos = dio_GetPos(); if (set_trap(&trap)) { read_frame_data_pointer(rules.prh_encoding, NULL, 0); clear_trap(&trap); } else { dio_SetPos(addr_pos + rules.address_size); } } break; case 'R': rules.addr_encoding = dio_ReadU1(); break; } } dio_SetPos(aug_pos + aug_length); } clear_frame_registers(&cie_regs); clear_frame_registers(&frame_regs); regs_stack_pos = 0; while (dio_GetPos() < cie_end) { exec_stack_frame_instruction(0); } copy_register_rules(&cie_regs, &frame_regs); dio_SetPos(saved_pos); }
static void stack_trace_error(void) { str_exception(ERR_OTHER, "Invalid stack trace program"); }
void drl_relocate(ELF_Section * s, U8_T offset, void * buf, size_t size, ELF_Section ** dst) { unsigned i; ELF_Section * d = NULL; if (dst == NULL) dst = &d; else *dst = NULL; if (!s->relocate) return; section = s; destination_section = dst; reloc_offset = offset; data_buf = buf; data_size = size; for (i = 1; i < s->file->section_cnt; i++) { ELF_Section * r = s->file->sections + i; if (r->size == 0) continue; if (r->type != SHT_REL && r->type != SHT_RELA) continue; if (r->info == s->index) { uint8_t * p; uint8_t * q; unsigned ix; relocs = r; symbols = s->file->sections + r->link; if (elf_load(relocs) < 0) exception(errno); if (elf_load(symbols) < 0) exception(errno); if (r->entsize == 0 || r->size % r->entsize != 0) str_exception(ERR_INV_FORMAT, "Invalid sh_entsize"); if (r->reloc_num_zones == 0) { U8_T prev_offs = 0; unsigned max_bondaries = 2; /* default is two bondaries ... */ r->reloc_num_zones = 1; /* ... for one zone */ r->reloc_zones_bondaries = (unsigned *)loc_alloc_zero(sizeof (unsigned) * max_bondaries); r->reloc_zones_bondaries[0] = 0; /* first zone starting index */ for (ix = 0; ix < r->size / r->entsize; ix++) { U8_T offs; uint8_t * x = (uint8_t *)r->data + ix * r->entsize; if (r->file->elf64) { offs = *(U8_T *)x; if (r->file->byte_swap) SWAP(offs); } else { U4_T offs4 = *(U4_T *)x; if (r->file->byte_swap) SWAP(offs4); offs = offs4; } if (offs < prev_offs) { /* * Relocation offsets are not ordered. Store the start * index of the new zone. */ if ((r->reloc_num_zones + 1) == max_bondaries) { max_bondaries += 5; r->reloc_zones_bondaries = (unsigned *)loc_realloc(r->reloc_zones_bondaries, sizeof (unsigned) * max_bondaries); } r->reloc_zones_bondaries[r->reloc_num_zones++] = ix; } prev_offs = offs; } /* Store the last zone boundary index */ r->reloc_zones_bondaries[r->reloc_num_zones] = ix; if (r->reloc_num_zones > 1) { trace(LOG_ELF, "ELF relocations are not ordered; the performances "\ "may be degraded."); } /* * As we parsed the relocation section, it would be possible to * localize the searched offset at the same time, to optimize the * first lookup. But I don't know if it worth it, compare to some * code duplication with the rest of the routine below. */ } /* Perform a dichotomic look up for each ordered area */ for (ix = 0; ix < r->reloc_num_zones; ix++) { p = (uint8_t *)r->data + r->reloc_zones_bondaries[ix] * r->entsize; q = (uint8_t *)r->data + r->reloc_zones_bondaries[ix + 1] * r->entsize; while (p < q) { unsigned n = (q - p) / r->entsize / 2; uint8_t * x = p + n * r->entsize; assert(x < q); if (r->file->elf64) { U8_T offs = *(U8_T *)x; if (r->file->byte_swap) SWAP(offs); if (s->file->type != ET_REL) offs -= s->addr; if (offset > offs) { p = x + r->entsize; continue; } if (offset < offs) { q = x; continue; } } else { U4_T offs = *(U4_T *)x; if (r->file->byte_swap) SWAP(offs); if (s->file->type != ET_REL) offs -= (U4_T)s->addr; if (offset > offs) { p = x + r->entsize; continue; } if (offset < offs) { q = x; continue; } } relocate(x); return; } } } } }