我在Visual Studio 2008上测试了一些代码并注意到了security_cookie.我能理解它的重点,但我不明白这条指令的目的是什么.
rep ret /* REP to avoid AMD branch prediction penalty */
Run Code Online (Sandbox Code Playgroud)
当然我可以理解评论:)但是这个前缀exaclty在上下文中做了ret什么,如果ecx是!= 0 会发生什么?显然,ecx当我调试它时,忽略循环计数,这是预期的.
我发现这里的代码在这里(由编译器注入安全性):
void __declspec(naked) __fastcall __security_check_cookie(UINT_PTR cookie)
{
/* x86 version written in asm to preserve all regs */
__asm {
cmp ecx, __security_cookie
jne failure
rep ret /* REP to avoid AMD branch prediction penalty */
failure:
jmp __report_gsfailure
}
}
Run Code Online (Sandbox Code Playgroud) 此循环在英特尔Conroe/Merom上每3个周期运行一次,imul按预期方式在吞吐量方面存在瓶颈.但是在Haswell/Skylake上,它每11个循环运行一次,显然是因为setnz al它依赖于最后一个循环imul.
; synthetic micro-benchmark to test partial-register renaming
mov ecx, 1000000000
.loop: ; do{
imul eax, eax ; a dep chain with high latency but also high throughput
imul eax, eax
imul eax, eax
dec ecx ; set ZF, independent of old ZF. (Use sub ecx,1 on Silvermont/KNL or P4)
setnz al ; ****** Does this depend on RAX as well as ZF?
movzx eax, al
jnz .loop ; }while(ecx);
Run Code Online (Sandbox Code Playgroud)
如果setnz al …
想象一下,您希望将一系列x86汇编指令与某些边界对齐.例如,您可能希望将循环对齐到16或32字节的边界,或者将指令打包以使它们有效地放置在uop缓存中或其他任何位置.
实现这一目标的最简单方法是单字节NOP指令,紧接着是多字节NOP.虽然后者通常效率更高,但这两种方法都不是免费的:NOP使用前端执行资源,并且还计入现代x86上的4宽1重命名限制.
另一个选择是以某种方式延长一些指令以获得所需的对齐.如果这样做没有引入新的停顿,它似乎比NOP方法更好.如何在最近的x86 CPU上有效地延长指令?
在理想的世界中,延长技术同时是:
有一种方法不可能同时满足所有上述要点,因此很好的答案可能会解决各种权衡问题.
1 AMD Ryzen的限制为5或6.
KbL i7-8550U
我正在研究 uops-cache 的行为并遇到了关于它的误解。
如英特尔优化手册2.5.2.2(我的)中所述:
解码的 ICache 由 32 组组成。每组包含八种方式。 每路最多可容纳六个微操作。
——
Way 中的所有微操作表示在代码中静态连续的指令,并且它们的 EIP 位于相同的对齐 32 字节区域内。
——
最多三种方式可以专用于相同的 32 字节对齐块,从而允许在原始 IA 程序的每个 32 字节区域中缓存总共 18 个微操作。
——
无条件分支是 Way 中的最后一个微操作。
情况1:
考虑以下例程:
uop.h
void inhibit_uops_cache(size_t);
Run Code Online (Sandbox Code Playgroud)
uop.S
align 32
inhibit_uops_cache:
mov edx, esi
mov edx, esi
mov edx, esi
mov edx, esi
mov edx, esi
mov edx, esi
jmp decrement_jmp_tgt
decrement_jmp_tgt:
dec rdi
ja inhibit_uops_cache ;ja is intentional to avoid Macro-fusion
ret
Run Code Online (Sandbox Code Playgroud)
为了确保例程的代码实际上是 32 字节对齐的,这里是 asm …
回想一下,x86体系结构定义0x0F 0x1F [mod R/M]为多字节NOP。
现在,我看一个8字节NOP的特殊情况:
0x0F 0x1F 0x84 0x__ 0x__ 0x__ 0x__ 0x__
Run Code Online (Sandbox Code Playgroud)
最后5个字节具有任意值。
第三个字节[mod R/M]拆分得到:

mod = 10b:参数为reg1+ DWORD大小的位移reg2 = 000b:(我们不在乎)reg1 = 100b:表示参数改为SIB字节+ DWORD大小的位移。现在,举一个具体的例子
0x0F 0x1F 0x84 0x12 0x34 0x56 0x78 0x9A
Run Code Online (Sandbox Code Playgroud)
我有
SIB = 0x12displacement = 0x9A785634:DWORD现在,我添加0x66指令前缀以指示位移应为WORD而不是DWORD:
0x66 0x0F 0x1F 0x84 0x12 0x34 0x56 0x78 0x9A
Run Code Online (Sandbox Code Playgroud)
我希望0x78 0x9A被“切断”并被视为新的指示。但是,当编译它并objdump在生成的可执行文件上运行时,它仍然使用所有4个字节(一个DWORD)作为位移。
在这种情况下,我是否误解了“位移”的含义?还是0x66前缀对多字节NOP指令没有任何影响?
让我们MOV EAX,[0xFFFFFFFF]以64位模式将指令编码为67A1FFFFFFFF(有效地址大小由67前缀从默认的64位切换为32位)。
英特尔指令参考手册(从2015年12月起,文档订购号:325383-057US)在第Vol。2A 2-11说:
2.2.1.3
64位模式下的位移寻址使用现有的32位ModR / M和SIB编码。ModR / M和SIB大小不变。它们保留为8位或32位,并被符号扩展为64位。
这表明应该对32bit位移进行符号扩展,但是我不确定这是否也涉及特殊的moffs寻址模式。英特尔在下一页上说:
2.2.1.6 RIP相对寻址
RIP相对寻址是通过64位模式而不是64位地址大小启用的。地址大小前缀的使用不会禁用RIP相对寻址。地址大小前缀的作用是将计算的有效地址截断并将其零扩展为32位。
这表明在相对寻址模式下,将disp32符号扩展到64位,再添加到RIP,然后截断并进行零扩展。但是,我不确定同一规则是否适用于绝对寻址模式,MOV moffs操作就是这种情况。
从A)FFFFFFFFFFFFFFFF或B)00000000FFFFFFFF将EAX加载到哪个地址?
我试图找出 x86 16 位寻址模式(MASM 程序集)中不存在比例因子的原因。而32位和64位寻址模式都有比例因子。这背后是否有实际原因或者不需要它?如果您能解释一下,我将不胜感激。
可以组合不同组件来创建有效地址的所有可能方式:
16 位和 32 位寻址模式之间的差异
int main()
{
00211000 push ebp
00211001 mov ebp,esp
00211003 sub esp,10h
char charVar1;
short shortVar1;
int intVar1;
long longVar1;
charVar1 = 11;
00211006 mov byte ptr [charVar1],0Bh
shortVar1 = 11;
0021100A mov eax,0Bh
0021100F mov word ptr [shortVar1],ax
intVar1 = 11;
00211013 mov dword ptr [intVar1],0Bh
longVar1 = 11;
0021101A mov dword ptr [longVar1],0Bh
}
Run Code Online (Sandbox Code Playgroud)
其他数据类型不通过寄存器,但只有短类型通过寄存器。怎么了?
我想使用 AT&T 风格的汇编将内存中某个地址的值复制到寄存器。我知道这应该不难,我认为在英特尔风格中它是这样的:
mov rdi, [0xdeadbeef]
Run Code Online (Sandbox Code Playgroud)
但我对 AT&T 风格(或一般的组装)了解不多。我搜索了它,但我得到的所有例子mov都不包括这个。
那么谁能告诉我该指令是什么样的?
另外,在哪里可以找到 AT&T 风格的 x86_64 汇编指令的完整列表?
我有%rep一个预处理器指令,它创建一个预处理器循环。我想在其中声明标签,可能带有一些串联,但我无法获得正确的语法。
%assign i 0
%rep 64
label_%i: ;this, of course, doesn't work
inc rax
%assign i i+1
%endrep
Run Code Online (Sandbox Code Playgroud)
那么如何强制 NASM 预处理器label_i为每次“迭代”生成呢?
我正在编写自己的汇编程序并尝试对 ADC 指令进行编码,我对立即值有疑问,尤其是在将 8 位值添加到 AX 寄存器时。
添加 16 位值时:adc ax, 0xff33被编码为15 33 ff正确的。但是如果adc ax, 0x33被编码为会重要15 33 00吗?
Nasm 将83 d0 33其编码为显然是正确的,但我的方法也正确吗?
本页的add说明文档说明如下:
请注意我突出显示的两条指令.
我在NASM中尝试了以下代码(符合第一个突出显示的指令):
add WORD [myvar], BYTE 0xA5
Run Code Online (Sandbox Code Playgroud)
但是我收到以下错误:
警告:有符号字节值超出边界
我究竟做错了什么?
我对x86-64二进制编码很新.我正在尝试修复一些旧的"汇编程序"代码.
无论如何,我正在尝试做这样的事情(英特尔语法):
mov [rbp+rcx], al
Run Code Online (Sandbox Code Playgroud)
汇编程序目前正在生成:
88 04 0D
Run Code Online (Sandbox Code Playgroud)
但这似乎不是一个有效的指示.如果我将SIB字节中的基数更改rbp为其他寄存器,则可以正常工作.另一种使其工作的方法是添加一个零字节的位移(88 44 0D 00).这似乎与其他类似的操作码一起发生.
为什么我不能rbp在那里使用mod=00?
assembly ×12
x86 ×11
x86-64 ×3
intel ×2
machine-code ×2
nasm ×2
performance ×2
64-bit ×1
att ×1
c ×1
nop ×1
opcode ×1
optimization ×1
prefix ×1
preprocessor ×1
x86-16 ×1