Path: blob/master/tools/perf/arch/x86/annotate/instructions.c
50674 views
// SPDX-License-Identifier: GPL-2.01/*2* x86 instruction nmemonic table to parse disasm lines for annotate.3* This table is searched twice - one for exact match and another for4* match without a size suffix (b, w, l, q) in case of AT&T syntax.5*6* So this table should not have entries with the suffix unless it's7* a complete different instruction than ones without the suffix.8*/9static struct ins x86__instructions[] = {10{ .name = "adc", .ops = &mov_ops, },11{ .name = "add", .ops = &mov_ops, },12{ .name = "addsd", .ops = &mov_ops, },13{ .name = "and", .ops = &mov_ops, },14{ .name = "andpd", .ops = &mov_ops, },15{ .name = "andps", .ops = &mov_ops, },16{ .name = "bsr", .ops = &mov_ops, },17{ .name = "bt", .ops = &mov_ops, },18{ .name = "btr", .ops = &mov_ops, },19{ .name = "bts", .ops = &mov_ops, },20{ .name = "call", .ops = &call_ops, },21{ .name = "cmovbe", .ops = &mov_ops, },22{ .name = "cmove", .ops = &mov_ops, },23{ .name = "cmovae", .ops = &mov_ops, },24{ .name = "cmp", .ops = &mov_ops, },25{ .name = "cmpxch", .ops = &mov_ops, },26{ .name = "cmpxchg", .ops = &mov_ops, },27{ .name = "cs", .ops = &mov_ops, },28{ .name = "dec", .ops = &dec_ops, },29{ .name = "divsd", .ops = &mov_ops, },30{ .name = "divss", .ops = &mov_ops, },31{ .name = "gs", .ops = &mov_ops, },32{ .name = "imul", .ops = &mov_ops, },33{ .name = "inc", .ops = &dec_ops, },34{ .name = "ja", .ops = &jump_ops, },35{ .name = "jae", .ops = &jump_ops, },36{ .name = "jb", .ops = &jump_ops, },37{ .name = "jbe", .ops = &jump_ops, },38{ .name = "jc", .ops = &jump_ops, },39{ .name = "jcxz", .ops = &jump_ops, },40{ .name = "je", .ops = &jump_ops, },41{ .name = "jecxz", .ops = &jump_ops, },42{ .name = "jg", .ops = &jump_ops, },43{ .name = "jge", .ops = &jump_ops, },44{ .name = "jl", .ops = &jump_ops, },45{ .name = "jle", .ops = &jump_ops, },46{ .name = "jmp", .ops = &jump_ops, },47{ .name = "jna", .ops = &jump_ops, },48{ .name = "jnae", .ops = &jump_ops, },49{ .name = "jnb", .ops = &jump_ops, },50{ .name = "jnbe", .ops = &jump_ops, },51{ .name = "jnc", .ops = &jump_ops, },52{ .name = "jne", .ops = &jump_ops, },53{ .name = "jng", .ops = &jump_ops, },54{ .name = "jnge", .ops = &jump_ops, },55{ .name = "jnl", .ops = &jump_ops, },56{ .name = "jnle", .ops = &jump_ops, },57{ .name = "jno", .ops = &jump_ops, },58{ .name = "jnp", .ops = &jump_ops, },59{ .name = "jns", .ops = &jump_ops, },60{ .name = "jnz", .ops = &jump_ops, },61{ .name = "jo", .ops = &jump_ops, },62{ .name = "jp", .ops = &jump_ops, },63{ .name = "jpe", .ops = &jump_ops, },64{ .name = "jpo", .ops = &jump_ops, },65{ .name = "jrcxz", .ops = &jump_ops, },66{ .name = "js", .ops = &jump_ops, },67{ .name = "jz", .ops = &jump_ops, },68{ .name = "lea", .ops = &mov_ops, },69{ .name = "lock", .ops = &lock_ops, },70{ .name = "mov", .ops = &mov_ops, },71{ .name = "movapd", .ops = &mov_ops, },72{ .name = "movaps", .ops = &mov_ops, },73{ .name = "movdqa", .ops = &mov_ops, },74{ .name = "movdqu", .ops = &mov_ops, },75{ .name = "movsd", .ops = &mov_ops, },76{ .name = "movss", .ops = &mov_ops, },77{ .name = "movsb", .ops = &mov_ops, },78{ .name = "movsw", .ops = &mov_ops, },79{ .name = "movsl", .ops = &mov_ops, },80{ .name = "movupd", .ops = &mov_ops, },81{ .name = "movups", .ops = &mov_ops, },82{ .name = "movzb", .ops = &mov_ops, },83{ .name = "movzw", .ops = &mov_ops, },84{ .name = "movzl", .ops = &mov_ops, },85{ .name = "mulsd", .ops = &mov_ops, },86{ .name = "mulss", .ops = &mov_ops, },87{ .name = "nop", .ops = &nop_ops, },88{ .name = "or", .ops = &mov_ops, },89{ .name = "orps", .ops = &mov_ops, },90{ .name = "pand", .ops = &mov_ops, },91{ .name = "paddq", .ops = &mov_ops, },92{ .name = "pcmpeqb", .ops = &mov_ops, },93{ .name = "por", .ops = &mov_ops, },94{ .name = "rcl", .ops = &mov_ops, },95{ .name = "ret", .ops = &ret_ops, },96{ .name = "sbb", .ops = &mov_ops, },97{ .name = "sete", .ops = &mov_ops, },98{ .name = "sub", .ops = &mov_ops, },99{ .name = "subsd", .ops = &mov_ops, },100{ .name = "test", .ops = &mov_ops, },101{ .name = "tzcnt", .ops = &mov_ops, },102{ .name = "ucomisd", .ops = &mov_ops, },103{ .name = "ucomiss", .ops = &mov_ops, },104{ .name = "vaddsd", .ops = &mov_ops, },105{ .name = "vandpd", .ops = &mov_ops, },106{ .name = "vmovdqa", .ops = &mov_ops, },107{ .name = "vmovq", .ops = &mov_ops, },108{ .name = "vmovsd", .ops = &mov_ops, },109{ .name = "vmulsd", .ops = &mov_ops, },110{ .name = "vorpd", .ops = &mov_ops, },111{ .name = "vsubsd", .ops = &mov_ops, },112{ .name = "vucomisd", .ops = &mov_ops, },113{ .name = "xadd", .ops = &mov_ops, },114{ .name = "xbegin", .ops = &jump_ops, },115{ .name = "xchg", .ops = &mov_ops, },116{ .name = "xor", .ops = &mov_ops, },117{ .name = "xorpd", .ops = &mov_ops, },118{ .name = "xorps", .ops = &mov_ops, },119};120121static bool amd__ins_is_fused(struct arch *arch, const char *ins1,122const char *ins2)123{124if (strstr(ins2, "jmp"))125return false;126127/* Family >= 15h supports cmp/test + branch fusion */128if (arch->family >= 0x15 && (strstarts(ins1, "test") ||129(strstarts(ins1, "cmp") && !strstr(ins1, "xchg")))) {130return true;131}132133/* Family >= 19h supports some ALU + branch fusion */134if (arch->family >= 0x19 && (strstarts(ins1, "add") ||135strstarts(ins1, "sub") || strstarts(ins1, "and") ||136strstarts(ins1, "inc") || strstarts(ins1, "dec") ||137strstarts(ins1, "or") || strstarts(ins1, "xor"))) {138return true;139}140141return false;142}143144static bool intel__ins_is_fused(struct arch *arch, const char *ins1,145const char *ins2)146{147if (arch->family != 6 || arch->model < 0x1e || strstr(ins2, "jmp"))148return false;149150if (arch->model == 0x1e) {151/* Nehalem */152if ((strstr(ins1, "cmp") && !strstr(ins1, "xchg")) ||153strstr(ins1, "test")) {154return true;155}156} else {157/* Newer platform */158if ((strstr(ins1, "cmp") && !strstr(ins1, "xchg")) ||159strstr(ins1, "test") ||160strstr(ins1, "add") ||161strstr(ins1, "sub") ||162strstr(ins1, "and") ||163strstr(ins1, "inc") ||164strstr(ins1, "dec")) {165return true;166}167}168169return false;170}171172static int x86__cpuid_parse(struct arch *arch, char *cpuid)173{174unsigned int family, model, stepping;175int ret;176177/*178* cpuid = "GenuineIntel,family,model,stepping"179*/180ret = sscanf(cpuid, "%*[^,],%u,%u,%u", &family, &model, &stepping);181if (ret == 3) {182arch->family = family;183arch->model = model;184arch->ins_is_fused = strstarts(cpuid, "AuthenticAMD") ?185amd__ins_is_fused :186intel__ins_is_fused;187return 0;188}189190return -1;191}192193static int x86__annotate_init(struct arch *arch, char *cpuid)194{195int err = 0;196197if (arch->initialized)198return 0;199200if (cpuid) {201if (x86__cpuid_parse(arch, cpuid))202err = SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_CPUID_PARSING;203}204arch->e_machine = EM_X86_64;205arch->e_flags = 0;206arch->initialized = true;207return err;208}209210#ifdef HAVE_LIBDW_SUPPORT211static void update_insn_state_x86(struct type_state *state,212struct data_loc_info *dloc, Dwarf_Die *cu_die,213struct disasm_line *dl)214{215struct annotated_insn_loc loc;216struct annotated_op_loc *src = &loc.ops[INSN_OP_SOURCE];217struct annotated_op_loc *dst = &loc.ops[INSN_OP_TARGET];218struct type_state_reg *tsr;219Dwarf_Die type_die;220u32 insn_offset = dl->al.offset;221int fbreg = dloc->fbreg;222int fboff = 0;223224if (annotate_get_insn_location(dloc->arch, dl, &loc) < 0)225return;226227if (ins__is_call(&dl->ins)) {228struct symbol *func = dl->ops.target.sym;229230if (func == NULL)231return;232233/* __fentry__ will preserve all registers */234if (!strcmp(func->name, "__fentry__"))235return;236237pr_debug_dtp("call [%x] %s\n", insn_offset, func->name);238239/* Otherwise invalidate caller-saved registers after call */240for (unsigned i = 0; i < ARRAY_SIZE(state->regs); i++) {241if (state->regs[i].caller_saved)242state->regs[i].ok = false;243}244245/* Update register with the return type (if any) */246if (die_find_func_rettype(cu_die, func->name, &type_die)) {247tsr = &state->regs[state->ret_reg];248tsr->type = type_die;249tsr->kind = TSR_KIND_TYPE;250tsr->offset = 0;251tsr->ok = true;252253pr_debug_dtp("call [%x] return -> reg%d",254insn_offset, state->ret_reg);255pr_debug_type_name(&type_die, tsr->kind);256}257return;258}259260if (!strncmp(dl->ins.name, "add", 3)) {261u64 imm_value = -1ULL;262int offset;263const char *var_name = NULL;264struct map_symbol *ms = dloc->ms;265u64 ip = ms->sym->start + dl->al.offset;266267if (!has_reg_type(state, dst->reg1))268return;269270tsr = &state->regs[dst->reg1];271tsr->copied_from = -1;272273if (src->imm)274imm_value = src->offset;275else if (has_reg_type(state, src->reg1) &&276state->regs[src->reg1].kind == TSR_KIND_CONST)277imm_value = state->regs[src->reg1].imm_value;278else if (src->reg1 == DWARF_REG_PC) {279u64 var_addr = annotate_calc_pcrel(dloc->ms, ip,280src->offset, dl);281282if (get_global_var_info(dloc, var_addr,283&var_name, &offset) &&284!strcmp(var_name, "this_cpu_off") &&285tsr->kind == TSR_KIND_CONST) {286tsr->kind = TSR_KIND_PERCPU_BASE;287tsr->offset = 0;288tsr->ok = true;289imm_value = tsr->imm_value;290}291}292else293return;294295/* Ignore add to non-pointer or non-const types */296if (tsr->kind == TSR_KIND_POINTER ||297(dwarf_tag(&tsr->type) == DW_TAG_pointer_type &&298src->reg1 != DWARF_REG_PC && tsr->kind == TSR_KIND_TYPE && !dst->mem_ref)) {299tsr->offset += imm_value;300pr_debug_dtp("add [%x] offset %#"PRIx64" to reg%d",301insn_offset, imm_value, dst->reg1);302pr_debug_type_name(&tsr->type, tsr->kind);303}304305if (tsr->kind == TSR_KIND_CONST)306tsr->imm_value += imm_value;307308if (tsr->kind != TSR_KIND_PERCPU_BASE)309return;310311if (get_global_var_type(cu_die, dloc, ip, imm_value, &offset,312&type_die) && offset == 0) {313/*314* This is not a pointer type, but it should be treated315* as a pointer.316*/317tsr->type = type_die;318tsr->kind = TSR_KIND_PERCPU_POINTER;319tsr->offset = 0;320tsr->ok = true;321322pr_debug_dtp("add [%x] percpu %#"PRIx64" -> reg%d",323insn_offset, imm_value, dst->reg1);324pr_debug_type_name(&tsr->type, tsr->kind);325}326return;327}328329if (!strncmp(dl->ins.name, "sub", 3)) {330u64 imm_value = -1ULL;331332if (!has_reg_type(state, dst->reg1))333return;334335tsr = &state->regs[dst->reg1];336tsr->copied_from = -1;337338if (src->imm)339imm_value = src->offset;340else if (has_reg_type(state, src->reg1) &&341state->regs[src->reg1].kind == TSR_KIND_CONST)342imm_value = state->regs[src->reg1].imm_value;343344if (tsr->kind == TSR_KIND_POINTER ||345(dwarf_tag(&tsr->type) == DW_TAG_pointer_type &&346src->reg1 != DWARF_REG_PC && tsr->kind == TSR_KIND_TYPE && !dst->mem_ref)) {347tsr->offset -= imm_value;348pr_debug_dtp("sub [%x] offset %#"PRIx64" to reg%d",349insn_offset, imm_value, dst->reg1);350pr_debug_type_name(&tsr->type, tsr->kind);351}352353if (tsr->kind == TSR_KIND_CONST)354tsr->imm_value -= imm_value;355356return;357}358359if (!strncmp(dl->ins.name, "lea", 3)) {360int sreg = src->reg1;361struct type_state_reg src_tsr;362363if (!has_reg_type(state, sreg) ||364!has_reg_type(state, dst->reg1) ||365!src->mem_ref)366return;367368src_tsr = state->regs[sreg];369tsr = &state->regs[dst->reg1];370371tsr->copied_from = -1;372tsr->ok = false;373374/* Case 1: Based on stack pointer or frame pointer */375if (sreg == fbreg || sreg == state->stack_reg) {376struct type_state_stack *stack;377int offset = src->offset - fboff;378379stack = find_stack_state(state, offset);380if (!stack)381return;382383tsr->type = stack->type;384tsr->kind = TSR_KIND_POINTER;385tsr->offset = offset - stack->offset;386tsr->ok = true;387388if (sreg == fbreg) {389pr_debug_dtp("lea [%x] address of -%#x(stack) -> reg%d",390insn_offset, -src->offset, dst->reg1);391} else {392pr_debug_dtp("lea [%x] address of %#x(reg%d) -> reg%d",393insn_offset, src->offset, sreg, dst->reg1);394}395396pr_debug_type_name(&tsr->type, tsr->kind);397}398/* Case 2: Based on a register holding a typed pointer */399else if (src_tsr.ok && (src_tsr.kind == TSR_KIND_POINTER ||400(dwarf_tag(&src_tsr.type) == DW_TAG_pointer_type &&401src_tsr.kind == TSR_KIND_TYPE))) {402403if (src_tsr.kind == TSR_KIND_TYPE &&404__die_get_real_type(&state->regs[sreg].type, &type_die) == NULL)405return;406407if (src_tsr.kind == TSR_KIND_POINTER)408type_die = state->regs[sreg].type;409410/* Check if the target type has a member at the new offset */411if (die_get_member_type(&type_die,412src->offset + src_tsr.offset, &type_die) == NULL)413return;414415tsr->type = src_tsr.type;416tsr->kind = src_tsr.kind;417tsr->offset = src->offset + src_tsr.offset;418tsr->ok = true;419420pr_debug_dtp("lea [%x] address of %s%#x(reg%d) -> reg%d",421insn_offset, src->offset < 0 ? "-" : "",422abs(src->offset), sreg, dst->reg1);423424pr_debug_type_name(&tsr->type, tsr->kind);425}426return;427}428429/* Invalidate register states for other ops which may change pointers */430if (has_reg_type(state, dst->reg1) && !dst->mem_ref &&431dwarf_tag(&state->regs[dst->reg1].type) == DW_TAG_pointer_type) {432if (!strncmp(dl->ins.name, "imul", 4) || !strncmp(dl->ins.name, "mul", 3) ||433!strncmp(dl->ins.name, "idiv", 4) || !strncmp(dl->ins.name, "div", 3) ||434!strncmp(dl->ins.name, "shl", 3) || !strncmp(dl->ins.name, "shr", 3) ||435!strncmp(dl->ins.name, "sar", 3) || !strncmp(dl->ins.name, "and", 3) ||436!strncmp(dl->ins.name, "or", 2) || !strncmp(dl->ins.name, "neg", 3) ||437!strncmp(dl->ins.name, "inc", 3) || !strncmp(dl->ins.name, "dec", 3)) {438pr_debug_dtp("%s [%x] invalidate reg%d\n",439dl->ins.name, insn_offset, dst->reg1);440state->regs[dst->reg1].ok = false;441state->regs[dst->reg1].copied_from = -1;442return;443}444445if (!strncmp(dl->ins.name, "xor", 3) && dst->reg1 == src->reg1) {446/* xor reg, reg clears the register */447pr_debug_dtp("xor [%x] clear reg%d\n",448insn_offset, dst->reg1);449450state->regs[dst->reg1].kind = TSR_KIND_CONST;451state->regs[dst->reg1].imm_value = 0;452state->regs[dst->reg1].ok = true;453state->regs[dst->reg1].copied_from = -1;454return;455}456}457458if (strncmp(dl->ins.name, "mov", 3))459return;460461if (dloc->fb_cfa) {462u64 ip = dloc->ms->sym->start + dl->al.offset;463u64 pc = map__rip_2objdump(dloc->ms->map, ip);464465if (die_get_cfa(dloc->di->dbg, pc, &fbreg, &fboff) < 0)466fbreg = -1;467}468469/* Case 1. register to register or segment:offset to register transfers */470if (!src->mem_ref && !dst->mem_ref) {471if (!has_reg_type(state, dst->reg1))472return;473474tsr = &state->regs[dst->reg1];475tsr->copied_from = -1;476477if (dso__kernel(map__dso(dloc->ms->map)) &&478src->segment == INSN_SEG_X86_GS && src->imm) {479u64 ip = dloc->ms->sym->start + dl->al.offset;480u64 var_addr;481int offset;482483/*484* In kernel, %gs points to a per-cpu region for the485* current CPU. Access with a constant offset should486* be treated as a global variable access.487*/488var_addr = src->offset;489490if (var_addr == 40) {491tsr->kind = TSR_KIND_CANARY;492tsr->offset = 0;493tsr->ok = true;494495pr_debug_dtp("mov [%x] stack canary -> reg%d\n",496insn_offset, dst->reg1);497return;498}499500if (!get_global_var_type(cu_die, dloc, ip, var_addr,501&offset, &type_die) ||502!die_get_member_type(&type_die, offset, &type_die)) {503tsr->ok = false;504return;505}506507tsr->type = type_die;508tsr->kind = TSR_KIND_TYPE;509tsr->offset = 0;510tsr->ok = true;511512pr_debug_dtp("mov [%x] this-cpu addr=%#"PRIx64" -> reg%d",513insn_offset, var_addr, dst->reg1);514pr_debug_type_name(&tsr->type, tsr->kind);515return;516}517518if (src->imm) {519tsr->kind = TSR_KIND_CONST;520tsr->imm_value = src->offset;521tsr->offset = 0;522tsr->ok = true;523524pr_debug_dtp("mov [%x] imm=%#x -> reg%d\n",525insn_offset, tsr->imm_value, dst->reg1);526return;527}528529if (!has_reg_type(state, src->reg1) ||530!state->regs[src->reg1].ok) {531tsr->ok = false;532return;533}534535tsr->type = state->regs[src->reg1].type;536tsr->kind = state->regs[src->reg1].kind;537tsr->imm_value = state->regs[src->reg1].imm_value;538tsr->offset = state->regs[src->reg1].offset;539tsr->ok = true;540541/* To copy back the variable type later (hopefully) */542if (tsr->kind == TSR_KIND_TYPE || tsr->kind == TSR_KIND_POINTER)543tsr->copied_from = src->reg1;544545pr_debug_dtp("mov [%x] reg%d -> reg%d",546insn_offset, src->reg1, dst->reg1);547pr_debug_type_name(&tsr->type, tsr->kind);548}549/* Case 2. memory to register transers */550if (src->mem_ref && !dst->mem_ref) {551int sreg = src->reg1;552553if (!has_reg_type(state, dst->reg1))554return;555556tsr = &state->regs[dst->reg1];557tsr->copied_from = -1;558559retry:560/* Check stack variables with offset */561if (sreg == fbreg || sreg == state->stack_reg) {562struct type_state_stack *stack;563int offset = src->offset - fboff;564565stack = find_stack_state(state, offset);566if (stack == NULL) {567tsr->ok = false;568return;569} else if (!stack->compound) {570tsr->type = stack->type;571tsr->kind = stack->kind;572tsr->offset = stack->ptr_offset;573tsr->ok = true;574} else if (die_get_member_type(&stack->type,575offset - stack->offset,576&type_die)) {577tsr->type = type_die;578tsr->kind = TSR_KIND_TYPE;579tsr->offset = 0;580tsr->ok = true;581} else {582tsr->ok = false;583return;584}585586if (sreg == fbreg) {587pr_debug_dtp("mov [%x] -%#x(stack) -> reg%d",588insn_offset, -offset, dst->reg1);589} else {590pr_debug_dtp("mov [%x] %#x(reg%d) -> reg%d",591insn_offset, offset, sreg, dst->reg1);592}593pr_debug_type_name(&tsr->type, tsr->kind);594}595/* And then dereference the pointer if it has one */596else if (has_reg_type(state, sreg) && state->regs[sreg].ok &&597state->regs[sreg].kind == TSR_KIND_TYPE &&598die_deref_ptr_type(&state->regs[sreg].type,599src->offset + state->regs[sreg].offset, &type_die)) {600tsr->type = type_die;601tsr->kind = TSR_KIND_TYPE;602tsr->offset = 0;603tsr->ok = true;604605pr_debug_dtp("mov [%x] %#x(reg%d) -> reg%d",606insn_offset, src->offset, sreg, dst->reg1);607pr_debug_type_name(&tsr->type, tsr->kind);608}609/* Handle dereference of TSR_KIND_POINTER registers */610else if (has_reg_type(state, sreg) && state->regs[sreg].ok &&611state->regs[sreg].kind == TSR_KIND_POINTER &&612die_get_member_type(&state->regs[sreg].type,613src->offset + state->regs[sreg].offset, &type_die)) {614tsr->type = state->regs[sreg].type;615tsr->kind = TSR_KIND_TYPE;616tsr->offset = src->offset + state->regs[sreg].offset;617tsr->ok = true;618619pr_debug_dtp("mov [%x] addr %#x(reg%d) -> reg%d",620insn_offset, src->offset, sreg, dst->reg1);621pr_debug_type_name(&tsr->type, tsr->kind);622}623/* Or check if it's a global variable */624else if (sreg == DWARF_REG_PC) {625struct map_symbol *ms = dloc->ms;626u64 ip = ms->sym->start + dl->al.offset;627u64 addr;628int offset;629630addr = annotate_calc_pcrel(ms, ip, src->offset, dl);631632if (!get_global_var_type(cu_die, dloc, ip, addr, &offset,633&type_die) ||634!die_get_member_type(&type_die, offset, &type_die)) {635tsr->ok = false;636return;637}638639tsr->type = type_die;640tsr->kind = TSR_KIND_TYPE;641tsr->offset = 0;642tsr->ok = true;643644pr_debug_dtp("mov [%x] global addr=%"PRIx64" -> reg%d",645insn_offset, addr, dst->reg1);646pr_debug_type_name(&type_die, tsr->kind);647}648/* And check percpu access with base register */649else if (has_reg_type(state, sreg) &&650state->regs[sreg].kind == TSR_KIND_PERCPU_BASE) {651u64 ip = dloc->ms->sym->start + dl->al.offset;652u64 var_addr = src->offset;653int offset;654655if (src->multi_regs) {656int reg2 = (sreg == src->reg1) ? src->reg2 : src->reg1;657658if (has_reg_type(state, reg2) && state->regs[reg2].ok &&659state->regs[reg2].kind == TSR_KIND_CONST)660var_addr += state->regs[reg2].imm_value;661}662663/*664* In kernel, %gs points to a per-cpu region for the665* current CPU. Access with a constant offset should666* be treated as a global variable access.667*/668if (get_global_var_type(cu_die, dloc, ip, var_addr,669&offset, &type_die) &&670die_get_member_type(&type_die, offset, &type_die)) {671tsr->type = type_die;672tsr->kind = TSR_KIND_TYPE;673tsr->offset = 0;674tsr->ok = true;675676if (src->multi_regs) {677pr_debug_dtp("mov [%x] percpu %#x(reg%d,reg%d) -> reg%d",678insn_offset, src->offset, src->reg1,679src->reg2, dst->reg1);680} else {681pr_debug_dtp("mov [%x] percpu %#x(reg%d) -> reg%d",682insn_offset, src->offset, sreg, dst->reg1);683}684pr_debug_type_name(&tsr->type, tsr->kind);685} else {686tsr->ok = false;687}688}689/* And then dereference the calculated pointer if it has one */690else if (has_reg_type(state, sreg) && state->regs[sreg].ok &&691state->regs[sreg].kind == TSR_KIND_PERCPU_POINTER &&692die_get_member_type(&state->regs[sreg].type,693src->offset, &type_die)) {694tsr->type = type_die;695tsr->kind = TSR_KIND_TYPE;696tsr->offset = 0;697tsr->ok = true;698699pr_debug_dtp("mov [%x] pointer %#x(reg%d) -> reg%d",700insn_offset, src->offset, sreg, dst->reg1);701pr_debug_type_name(&tsr->type, tsr->kind);702}703/* Or try another register if any */704else if (src->multi_regs && sreg == src->reg1 &&705src->reg1 != src->reg2) {706sreg = src->reg2;707goto retry;708}709else {710int offset;711const char *var_name = NULL;712713/* it might be per-cpu variable (in kernel) access */714if (src->offset < 0) {715if (get_global_var_info(dloc, (s64)src->offset,716&var_name, &offset) &&717!strcmp(var_name, "__per_cpu_offset")) {718tsr->kind = TSR_KIND_PERCPU_BASE;719tsr->offset = 0;720tsr->ok = true;721722pr_debug_dtp("mov [%x] percpu base reg%d\n",723insn_offset, dst->reg1);724return;725}726}727728tsr->ok = false;729}730}731/* Case 3. register to memory transfers */732if (!src->mem_ref && dst->mem_ref) {733if (!has_reg_type(state, src->reg1) ||734!state->regs[src->reg1].ok)735return;736737/* Check stack variables with offset */738if (dst->reg1 == fbreg || dst->reg1 == state->stack_reg) {739struct type_state_stack *stack;740int offset = dst->offset - fboff;741742tsr = &state->regs[src->reg1];743744stack = find_stack_state(state, offset);745if (stack) {746/*747* The source register is likely to hold a type748* of member if it's a compound type. Do not749* update the stack variable type since we can750* get the member type later by using the751* die_get_member_type().752*/753if (!stack->compound)754set_stack_state(stack, offset, tsr->kind,755&tsr->type, tsr->offset);756} else {757findnew_stack_state(state, offset, tsr->kind,758&tsr->type, tsr->offset);759}760761if (dst->reg1 == fbreg) {762pr_debug_dtp("mov [%x] reg%d -> -%#x(stack)",763insn_offset, src->reg1, -offset);764} else {765pr_debug_dtp("mov [%x] reg%d -> %#x(reg%d)",766insn_offset, src->reg1, offset, dst->reg1);767}768if (tsr->offset != 0) {769pr_debug_dtp(" reg%d offset %#x ->",770src->reg1, tsr->offset);771}772773pr_debug_type_name(&tsr->type, tsr->kind);774}775/*776* Ignore other transfers since it'd set a value in a struct777* and won't change the type.778*/779}780/* Case 4. memory to memory transfers (not handled for now) */781}782#endif783784785