x86 asm casetable 实现 [英] x86 asm casetable implementation
问题描述
我在理解 asm x86 中的 casetable 时遇到问题.我的教授已经使用这个例子在幻灯片中解释了它:
i'm having issues in understanding casetable in asm x86. My professor has already explained it in the slides using this example:
.data
CaseTable BYTE 'A' ; lookup value
DWORD Process_A ; address of procedure
EntrySize = ($ - CaseTable)
BYTE 'B'
DWORD Process_B
BYTE 'C'
DWORD Process_C
BYTE 'D'
DWORD Process_D
NumberOfEntries = ($ - CaseTable) / EntrySize
mov ebx,OFFSET CaseTable ; point EBX to the table
mov ecx,NumberOfEntries ; loop counter
L1: cmp al,[ebx] ; match found?
jne L2 ; no: continue
call NEAR PTR [ebx + 1] ; yes: call the procedure
; +1: address after the byte
jmp Default ; and exit the loop
L2: add ebx,EntrySize ; point to next entry
loop L1 ; repeat until ECX = 0
Default:
但是,此代码不完整,我不知道如何使用它来构建我自己的 caseetable.如果有人能给我一个使用上述代码实现 casetable 的工作案例示例,并具体告诉我他何时在代码中准确调用过程,我将不胜感激?我会很感激的.我将使用示例来学习如何实现更多和其他案例.谢谢.
however, this code is incomplete and i don't know how to use it to build my own caseetable. I would appreciate it if someone can give me one working case example implementing casetable using the above code, and show me specifically when does he call the procedures exactly in the code? I would appreciate it a lot. I will use the example to learn how to implement more and other cases. Thank you.
推荐答案
你让这件事变得过于复杂.您不需要线性搜索或存储在表中的键;只需对您的值进行范围检查,然后将其用作表索引.
You are making this over-complicated. You don't need a linear search or keys stored in your table; just range-check your value and then use it as a table index.
我认为您使用的是 MASM 语法,因此我将尝试用 MASM 语法编写此代码,但我的语法可能有误.不过,实际的指令和逻辑应该是正确的.
I think you're using MASM syntax, so I'll try to write this in MASM syntax, but I may have the syntax wrong. The actual instructions and logic should be correct, though.
section .rdata ; read-only data on Windows
CaseTable:
DWORD Process_A, Process_B ; function pointers
DWORD Process_C, Process_D
NumberOfEntries = ($ - CaseTable) / 4
; optional: define constants for 'A' and 'D' and use those in the code below
; so the keys / values are still all in one place in the source.
.text ; or .code or something.
; You were missing a section directive between your data and code.
; input: selector in EAX
dispatcher: ; you were also missing a label for your function
; movzx eax, al ; if your selector really was just a byte
sub eax, 'A' ; convert to idx. values below 'A' wrap to high unsigned
cmp eax, 'D' - 'A' ; NumberOfEntries
ja @Default ; unsigned compare rejects out-of-range high or low
call [CaseTable + eax*4]
; then fall through. Use jmp as a tail-call if you don't want that.
@Default:
ret
编写漂亮(且高效)的 asm 的诀窍是看透问题,了解问题到底有多简单.您必须手动利用任何特殊情况,例如您的键都是连续的值.你是编译器.:)
The trick to writing nice (and efficient) asm is to see through the problem to how simple it really is. You have to manually take advantage of any special-case situation, like your keys all being contiguous values. You are the compiler. :)
函数指针应该指向其他函数,比如
The function pointers should point to other functions, like
Process_A:
mov eax, [esp+4] ; return first arg unchanged
ret
Process_B:
mov eax, [esp+4]
add eax, eax ; return n * 2
ret
Process_C:
mov eax, [esp+4]
lea eax, [eax + eax*2] ; return n * 3
ret
Process_D:
mov eax, [esp+4]
shl eax, 2 ; return n * 4
ret
显然,您不会为此使用调度表,您只需使用 imul
乘以从 1 到 4 的未知数.但这只是一个示例.
Obviously you wouldn't use a dispatch table for this, you'd just use imul
to multiply by an unknown number from 1 to 4. But this is just an example.
编译器知道很多用于优化 switch
/case
语句的很酷的技巧.我最喜欢的一个是当很多 case 标签都做同样的事情,clang 和 gcc 将使用即时位图并行测试任何这些情况:
Compilers know a lot of cool tricks for optimizing switch
/case
statements. One of my favourites is when a lot of case labels do the same thing, clang and gcc will use an immediate bitmap to test for any of those cases in parallel:
void errhandler(enum errtype numError) {
switch (numError) {
case ERROR_01 : // intentional fall-through
case ERROR_07 : // intentional fall-through
case ERROR_0A : // intentional fall-through
case ERROR_10 : // intentional fall-through
case ERROR_15 : // intentional fall-through
case ERROR_16 : // intentional fall-through
//case ERROR_20 : // keep the range of cases smaller for simpler 32-bit code
fire_special_event();
break;
default:
// error codes that require no additional action
break;
}
}
编译为这样的代码( <代码>clang4.0.1 -O3 -m32 在 Godbolt 编译器浏览器上)
Compiles to code like this (clang4.0.1 -O3 -m32
on the Godbolt compiler explorer)
errhandler(errtype): # @errhandler(errtype)
mov eax, dword ptr [esp + 4] # load first function arg
cmp eax, 22
ja .LBB0_2
mov ecx, 6358146 # 0x610482 is a bitmap of those error codes
bt ecx, eax
jae .LBB0_2 # aka JNC: jump if CF=0, i.e. the bit wasn't set, i.e. ((1<<eax) & ecx) was false
jmp fire_special_event() # TAILCALL
.LBB0_2:
ret
不幸的是,编译器不够聪明,无法使用 jcc 作为条件尾调用,因此它们有条件地跳过 jmp
:/
Unfortunately compilers aren't smart enough to use jcc as a conditional tail-call, so instead they conditionally jump over the jmp
:/
gcc 选择使用 mov eax, 1
/shl
而不是 bt
,即使在调整 CPU 时 bt
会更快:/
gcc chooses to use a mov eax, 1
/ shl
instead of using bt
, even when tuning for CPUs where bt
would be faster :/
这篇关于x86 asm casetable 实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!