指令长度可变时的指令解码 [英] Instruction decoding when instructions are length-variable

查看:206
本文介绍了指令长度可变时的指令解码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下是一些说明及其相应的编码:

Here are some instructions and their corresponding encodings:

55                      push   %ebp
89 e5                   mov    %esp,%ebp
83 ec 18                sub    $0x18,%esp
a1 0c 9f 04 08          mov    0x8049f0c,%eax
85 c0                   test   %eax,%eax
74 12                   je     80484b1 <frame_dummy+0x21>
b8 00 00 00 00          mov    $0x0,%eax
85 c0                   test   %eax,%eax
74 09                   je     80484b1 <frame_dummy+0x21>
c7 04 24 0c 9f 04 08    movl   $0x8049f0c,(%esp)

当今的微处理器通常是32位或64位,我想它们通常以4字节或8字节块的形式从内存中读取数据.但是,指令的长度可以变化.微处理器如何解码这些指令?为什么它们的长度不固定以简化实现?

Today's microprocessors are often 32- or 64-bit and I guess that they usually read data from memory in 4 byte or 8 byte chunks. However, instructions can have variable length. How does a microprocessor decode these instructions and why are they not of constant length to ease implementation?

推荐答案

希望使其更具可读性.

硬件不会将内存视为一长串未组织的字节.所有处理器(固定或可变字长)均具有特定的引导方法.通常,处理器内存/地址空间中的一个已知地址带有启动代码的第一条指令的地址或第一条指令本身的地址.从那里开始,对于每条指令,当前指令的地址就是开始解码的地方.

The hardware does not look at the memory as a long list of unorganized bytes. All processors, fixed or variable word length, have a specific boot method. Usually a known address in the processors memory/address space with either an address to the first instruction of the boot code or the first instruction itself. From there and for every instruction the address of the current instruction is where to start decoding.

例如对于x86,它必须查看第一个字节.根据该字节的解码,可能需要读取更多的操作码字节.如果指令需要地址,偏移量或其他形式的立即数,则这些字节也存在.处理器很快就会确切知道该指令中有多少个字节.如果解码显示该指令包含5个字节,并且从地址0x10开始,则下一条指令位于0x10 + 5或0x15.这将永远持续下去.无条件分支(取决于处理器的不同形式)可能不同,您不假定该指令后的字节是另一条指令.有条件的或无条件的分支可以为您提供一条提示,以指示另一条指令或一系列指令从内存中开始.

For an x86 for example it has to look at the first byte. Depending on the decoding of that byte it may need to read more opcode bytes. If the instruction required an address, offset or otherwise some form of immediate value those bytes are there as well. Very quickly the processor knows exactly how many bytes are in this instruction. If the decoding shows the instruction contains 5 bytes and it started at address 0x10 then the next instruction is at 0x10+5 or 0x15. This continues forever. Unconditional branches, which depending on the processor can come in various flavors, you dont assume the bytes that follow the instruction are another instruction. Branches, conditional or unconditional give you a clue where another instruction or series of instructions start in memory.

请注意,今天的X86在解码一条指令时绝对不会一次获取一个字节,发生了合理大小的读取,一次可能是64位,并且处理器将根据需要从中取出字节.当从现代处理器读取单个字节时,内存总线仍会进行完整大小的读取,或者将总线上的所有这些位都显示在其中,内存控制器仅拉出它要保留的位,否则可能会保存该数据.您会看到一些处理器,在它们的背对背地址中可能有两个32位读取指令,但是在存储器接口上只有一个64位读取.

Note the X86 today definitely does not fetch one byte at a time when it decodes an instruction, sensible sized reads occur, probably 64 bits at a time, and the processor will pull the bytes out of that as needed. When reading a single byte from a modern processor the memory bus still does a full sized read and either presents all of those bits on the bus where the memory controller only pulls the bits it was after or it may go so far as to keep that data. You will see some processors where you may have two 32 bit read instructions at back to back addresses but only one 64 bit read happens on the memory interface.

我强烈建议您编写反汇编程序和/或仿真器.对于固定长度的指令,这很容易,您只需从头开始,然后在遍历内存时进行解码.固定的字长反汇编程序可以帮助您了解有关解码指令的信息,这是此过程的一部分,但它不会帮助您理解以下可变字长的指令以及如何在不脱离对齐的情况下将它们分开.

I highly recommend you write a disassembler and/or an emulator. For fixed length instructions it is pretty easy, you just start at the beginning and decode as you go through memory. A fixed word length disassembler may help learning about decoding instructions, which is part of this process, but it wont help your understanding of following variable word length instructions and how to separate them without getting out of alignment.

MSP430作为第一个反汇编程序是一个不错的选择.有gnu工具asm和C,等等(还有llvm).先从汇编器开始,然后是C或采用一些预制的二进制文件.他们的关键是您必须像处理程序一样遍历代码,从复位向量开始并逐步完成.解码一条指令时,您会知道它的长度并知道下一条指令在哪里,直到您打到无条件分支为止.除非程序员有意地留下陷阱来愚弄反汇编程序,否则请假定所有分支均以有效指令为条件或无条件.只需要一个下午或傍晚就可以使整个过程变得震撼,或者至少要弄懂这个概念.您不必完全解码指令,也不必使它成为一个完整的反汇编程序,只需要解码足以确定指令的长度并确定它是否是分支以及在何处分支就可以了.如果您选择16位指令,则可以一次建立一张表,列出所有可能的指令位组合及其长度,这可以节省一些时间.您仍然必须通过分支解码方式.

The MSP430 is a good choice as a first disassembler. There are gnu tools asm and C, etc (and llvm for that matter). Start with assembler then C or take some pre-made binaries. They key is you have to walk the code like the processor, start with the reset vector and walk your way through. When you decode one instruction you know its length and know where the next instruction is until you hit an unconditional branch. Unless the programmer has intentionally left a trap to fool the disassembler, assume that all branches conditional or unconditional point at valid instructions. An afternoon or evening is all that is required to bang out the whole thing or at least get the concept. You dont necessarily need to completely decode the instruction, dont have to make this a full blown disassembler, only need to decode enough to determine the length of the instruction and determine if it is a branch and if so where. Being a 16 bit instruction you can, if you choose, one time build a table of all possible instruction bit combinations and what their lengths are, that may save some time. You still have to decode your way through branches.

有些人可能会使用递归,而是使用内存映射向我显示什么字节是一条指令的开始,哪些字节/字是一条指令的一部分,但不是第一个字节/字,还有我尚未解码的字节.我从打断开始, 重置向量,并使用它们标记指令的起点.然后进入 一个对指令进行解码以寻找更多起点的循环.如果通过 没有其他起点,那么我已经完成了该阶段.如果在任何时候我发现一条指令的起点落在一条指令的中间,那么就会出现一个需要人工干预才能解决的问题.例如,拆解旧的视频游戏rom,您可能会看到这个手写的汇编程序.编译器生成的指令 往往非常干净可预测.如果我通过一个清晰的指令存储图和剩下的内容来完成此操作(假设数据),我可以通过一遍知道指令在哪里,然后解码并打印出来.可变字长指令集的反汇编器永远无法做到的是找到每条指令.如果指令集具有例如跳转表或其他某种要执行的运行时计算地址,则您将在没有实际执行代码的情况下找到所有这些地址.

Some folks might use recursion, instead I use a memory map showing me what bytes are the start of an instruction, what bytes/words are part of an instruction but not the first byte/word and what bytes I have not decoded yet. I start by taking the interrupt and reset vectors and using those to mark the starting point for instructions. and then go into a loop that decodes the instructions looking for more starting points. If a pass happens with no other starting points then I have finished that phase. If at any point I find an instruction starting point that lands in the middle of an instruction there is a problem that will require human intervention to solve. Disassembling old video game roms for example you are likely to see this, hand written assembler. Compiler generated instructions tend to be very clean and predicable. If I get through this with a clean memory map of instructions and what is left over, (assume data) I can make one pass knowing where instructions are and decode and print those out. What a disassembler for variable word length instructions sets can never do is find every instruction. If the instruction set has for example a jump table or otherwise some sort of runtime computed address for executing, you wont find all of those without actually executing the code.

那里有许多现有的仿真器和反汇编器,如果您想尝试遵循而不是编写自己的仿真器和反汇编器,我自己也有几个 http://github.com/dwelch67 .

There are a number of existing emulators and disassemblers out there, if you want to try to follow along instead of writing your own, I have a few myself http://github.com/dwelch67.

对于可变和固定字长,有其优缺点.固定具有优势,易于阅读,易于解码,一切都很好,但是考虑一下ram,尤其是缓存,您可以在与ARM相同的缓存中添加更多x86指令.另一方面,ARM可以更轻松地解码,更少的逻辑,功耗等,为您带来更大的收益.从历史上看,内存是昂贵的逻辑,而逻辑是昂贵的,随便走一个字节就是它的工作方式.一个单字节操作码将您限制为256条指令,因此扩展为某些需要更多字节的操作码,更不用说使它变长的立即数和地址了.保持反向兼容性数十年,您最终会回到现在.

There are pros and cons for and against variable and fixed word length. Fixed has advantages sure, easy to read easy to decode, everything is nice and proper, but think about ram, the cache in particular, you can cram a lot more x86 instructions in the same cache as an ARM. On the other hand an ARM can decode much easier, far less logic, power, etc more bang for your buck. Historically memory was expensive logic was expensive, and a byte as you go was how it worked. a single byte opcode limited you to 256 instructions, so that expanded into some opcodes needing more bytes not to mention the immediates and addresses that made it variable word length anyway. Keep reverse compatibility for decades and you end up where you are now.

例如,要添加到所有这些混乱中,ARM现在具有可变字长指令集. Thumb具有单个变量字指令,即分支,但您可以轻松地将其解码为固定长度.但是他们创建了thumb2,它确实类似于可变字长的指令集.同样,许多/大多数支持32位ARM指令的处理器也支持16位Thumb指令,因此即使使用ARM处理器,您也不能简单地按字对齐数据并随身进行解码,而必须使用可变字长.更糟糕的是,通过执行对ARM到/从拇指过渡的解码会被解码,通常您不能简单地从拇指拆卸并弄清楚手臂.混合模式编译器生成的分支通常涉及将地址加载到分支的寄存器,然后使用bx指令分支到该寄存器,因此反汇编程序将需要查看bx,在执行时向后看分支中使用的寄存器,然后希望您在那里找到负载,并希望它是从中加载的.text段.

To add to all of this confusion ARM for example now has a variable word length instruction set. Thumb had a single variable word instruction, the branch, but you can easily decode this as fixed length. But they created thumb2 which really does resemble a variable word length instruction set. Also many/most of the processors that support the 32 bit ARM istructions also support 16 bit thumb instructions, so even with an ARM processor you cannot simply align the data by words and decode as you go, you have to use variable word length. What is worse the ARM to/from thumb transitions are decoded by executing, you normally cannot simply disassemble and figure out arm from thumb. A mixed mode compiler generated branch often involves loading a register with the address to branch to then using a bx instruction to branch to it, so the disassembler would need to look at the bx, look backwards in execution for the register used in the branch and hope you find a load there and hope that is the .text segment it is loading from.

这篇关于指令长度可变时的指令解码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆