//---------------------------------------------------------------------------// // 1100101 |_ mov #0, r4 __ // // 11 |_ <0xb380 %5c4> / _|_ _____ ___ // // 0110 |_ 3.50 -> 3.60 | _\ \ / _ (_-< // // |_ base# + offset |_| /_\_\___/__/ // //---------------------------------------------------------------------------// #include #include #include #include #include #include #include #include namespace FxOS { /* Output for a single argument, which consists of one or more text segments each with their own text style. */ using ArgumentOutput = std::vector>; static inline bool output(ArgumentOutput &out, ViewAssemblyOptions::Promotion p, fmt::text_style style, std::string str) { if(p == ViewAssemblyOptions::Never) return true; if(p == ViewAssemblyOptions::Promote) out.pop_back(); out.push_back({style, std::move(str)}); return false; } static void renderArgument(AsmArgument const &arg, Argument const &a, ArgumentOutput &out, ViewAssemblyOptions const &opts) { out.push_back({{}, arg.str()}); // clang-format off enum { None, PCJump, PCRelative, PCAddr, Location, Constant, SyscallNumber, ObjectName } type = None; // clang-format on if(arg.kind == AsmArgument::PcJump) type = PCJump; else if(arg.kind == AsmArgument::PcRel) type = PCRelative; else if(arg.kind == AsmArgument::PcAddr) type = PCAddr; if(type == PCJump || type == PCRelative || type == PCAddr) { auto p = (type == PCJump) ? opts.promotions.PCJump_to_Location : (type == PCAddr) ? opts.promotions.PCAddr_to_Location : opts.promotions.PCRelative_to_Location; if(!RelConstDomain().is_constant(a.location)) return; if(output(out, p, {}, format("<%s>", a.location.str()))) return; type = (type == PCRelative) ? Location : Constant; } if(type == Location) { // TODO: Check that this is a read operation! auto p = opts.promotions.ReadLocation_to_Constant; if(!a.value || output(out, p, {}, a.value.str())) return; type = Constant; } /* Promote to object name first if available... */ if(type == Constant && a.value && opts.binary) { auto p = opts.promotions.Constant_to_ObjectName; u32 address = RelConstDomain().constant_value(a.value); BinaryObject *obj = opts.binary->objectAt(address); if(obj) { if(output(out, p, {}, obj->name())) return; type = ObjectName; } } /* ... or, as a default, a syscall number */ if(type == Constant && a.value && a.syscall_id >= 0) { auto p = opts.promotions.Constant_to_SyscallNumber; if(output(out, p, {}, format("%%%04x", a.syscall_id))) return; type = SyscallNumber; } } //=== Legacy-style instruction printer ===// static void doOldInst(u32 pc, OldInstruction &i, ViewAssemblyOptions const &opts, u32 &m_lastAddress) { OS *os = opts.binary ? opts.binary->OSAnalysis() : nullptr; ArgumentOutput argout; /* Ellipsis if there is a gap since last instruction */ if(m_lastAddress + 1 != 0 && pc != m_lastAddress + 2) printf(" ...\n"); /* Preliminary syscall number */ int syscall_id; if(os && (syscall_id = os->find_syscall(pc)) >= 0) { printf("\n<%%%04x", syscall_id); BinaryObject *obj = opts.binary ? opts.binary->objectAt(pc) : nullptr; if(obj) printf(" %s", obj->name().c_str()); printf(">\n"); } /* Raw data if instruction cannot be decoded */ printf(" %08x: %04x", pc, (i.inst ? i.inst->opcode : i.opcode)); if(!i.inst) { printf("\n"); m_lastAddress = pc; return; } /* Mnemonic */ static char const *suffixes[5] = {"", ".b", ".w", "", ".l"}; char const *suffix = suffixes[(i.inst->opsize <= 4) ? i.inst->opsize : 0]; int spacing = i.inst->arg_count ? 8 - strlen(i.inst->mnemonic) - strlen(suffix) : 0; printf(" %s%s%*s", i.inst->mnemonic, suffix, spacing, ""); /* Arguments */ for(size_t n = 0; n < i.inst->arg_count; n++) { if(n) printf(", "); renderArgument(i.inst->args[n], i.args[n], argout, opts); for(size_t i = 0; i < argout.size(); i++) { if(i != 0) printf(" "); printf("%s", argout[i].second.c_str()); } argout.clear(); } printf("\n"); m_lastAddress = pc; } void viewAssemblyLegacyRegion( Binary &binary, MemoryRegion r, ViewAssemblyOptions *opts_ptr) { ViewAssemblyOptions opts; if(opts_ptr) opts = *opts_ptr; opts.binary = &binary; u32 lastAddress = 0xffffffff; for(u32 pc = r.start & -2; pc <= r.end; pc += 2) { OldInstruction *i = binary.vspace().disasm.getInstructionAt(pc, true); if(i != nullptr) doOldInst(pc, *i, opts, lastAddress); } } void viewAssemblyLegacyAddress( Binary &binary, u32 pc, ViewAssemblyOptions *opts_ptr) { ViewAssemblyOptions opts; if(opts_ptr) opts = *opts_ptr; opts.binary = &binary; u32 lastAddress = 0xffffffff; Queue queue; queue.enqueue(pc); while(!queue.empty()) { u32 pc = queue.pop(); OldInstruction *i = binary.vspace().disasm.getInstructionAt(pc, true); if(i == nullptr) continue; /* Enqueue successors */ if(!i->terminal && !i->jump) queue.enqueue(pc + 2); if(i->jump || i->condjump) queue.enqueue(i->jmptarget); } /* Print explored instructions in increasing order of addresses */ for(u32 pc: queue.seen) { OldInstruction *i = binary.vspace().disasm.getInstructionAt(pc, false); if(i) doOldInst(pc, *i, opts, lastAddress); } } //=== Binary-API assembly printer ===// void viewAssemblyInstruction( Instruction const &inst, ViewAssemblyOptions *opts); void viewAssemblyBasicBlock(BasicBlock const &bb, ViewAssemblyOptions *opts); void viewAssemblyFunction(Function const &fun, ViewAssemblyOptions *opts); } /* namespace FxOS */