x86_64机器码,4个字节
BSF(正向位扫描)指令正是这样做的!
0x0f 0xbc 0xc7 0xc3
在gcc样式的汇编中,这是:
.globl f
f:
bsfl %edi, %eax
ret
根据标准的64位c调用约定,输入在EDI寄存器中提供,并在EAX寄存器中返回。
由于二进制补码二进制编码,因此它适用于-ve和+ ve数字。
此外,尽管文档中说“如果源操作数的内容为0,则目标操作数的内容未定义”。,我在Ubuntu VM上发现输出f(0)
为0。
说明:
- 将上面的另存为
evenness.s
并组装gcc -c evenness.s -o evenness.o
- 将以下测试驱动程序另存为
evenness-main.c
并使用进行编译gcc -c evenness-main.c -o evenness-main.o
:
#include <stdio.h>
extern int f(int n);
int main (int argc, char **argv) {
int i;
int testcases[] = { 14, 20, 94208, 7, 0, -4 };
for (i = 0; i < sizeof(testcases) / sizeof(testcases[0]); i++) {
printf("%d, %d\n", testcases[i], f(testcases[i]));
}
return 0;
}
然后:
- 链接:
gcc evenness-main.o evenness.o -o evenness
- 跑:
./evenness
@FarazMasroor要求提供有关如何得出此答案的更多详细信息。
我对c的理解比对x86汇编的复杂性更熟悉,因此通常我使用编译器为我生成汇编代码。我知道从经验中的gcc扩展如__builtin_ffs()
,__builtin_ctz()
和__builtin_popcount()
典型地编译和组装在x86 1点或2的指令。所以我从一个c函数开始:
int f(int n) {
return __builtin_ctz(n);
}
您可以使用该-S
选项仅将其编译为Assembly- ,而不必始终使用常规的gcc编译来生成目标代码gcc -S -c evenness.c
。这给出了如下的汇编文件evenness.s
:
.file "evenness.c"
.text
.globl f
.type f, @function
f:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
rep bsfl %eax, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size f, .-f
.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4"
.section .note.GNU-stack,"",@progbits
其中很多可以打出去。特别是,我们知道具有签名的函数的c 调用约定int f(int n);
非常好而且很简单-输入参数在EDI
寄存器中传递,返回值在EAX
寄存器中返回。因此,我们可以删除大部分指令-其中许多指令与保存寄存器和设置新的堆栈框架有关。我们在这里不使用堆栈,而仅使用EAX
寄存器,因此无需担心其他寄存器。这留下了“ golfed”汇编代码:
.globl f
f:
bsfl %edi, %eax
ret
请注意,正如@zwol指出的那样,您也可以使用优化的编译来获得类似的结果。特别是-Os
完全生成上述指令(带有一些不会产生任何额外目标代码的附加汇编程序指令。)
现在gcc -c evenness.s -o evenness.o
,将其与组装在一起,然后可以将其链接到如上所述的测试驱动程序中。
有几种方法可以确定与此程序集相对应的机器代码。我最喜欢的是使用gdb disass
反汇编命令:
$ gdb ./evenness
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
...
Reading symbols from ./evenness...(no debugging symbols found)...done.
(gdb) disass /r f
Dump of assembler code for function f:
0x00000000004005ae <+0>: 0f bc c7 bsf %edi,%eax
0x00000000004005b1 <+3>: c3 retq
0x00000000004005b2 <+4>: 66 2e 0f 1f 84 00 00 00 00 00 nopw %cs:0x0(%rax,%rax,1)
0x00000000004005bc <+14>: 0f 1f 40 00 nopl 0x0(%rax)
End of assembler dump.
(gdb)
因此,我们可以看到,对于机器代码bsf
指令是0f bc c7
和ret
是c3
。