在嵌入式C语言中,涉及到底层的代码经常需要使用嵌入汇编。
嵌入汇编的基本格式:
asm(
"汇编语句"
::输出寄存器
:输入寄存器
:会被改变的寄存器
)
可是实际上你会发现,一般在被改变的寄存器中没有加入eax !这是为什么?
有如下代码为例:
实际上第五行不应该改成"%ecx","%eax","%edi"?asm("cld\n\t"
"rep\n\t"
:/*没有输出寄存器*/
:"c"(count-1),"a"(fill_value),"D"(dest)
:"%ecx","%edi");
EAX寄存器以称为累加器,AX寄存器是算术运算的主要寄存器, 所有的输入、输出只使用AL或AX人作为数据寄存器。so,eax寄存器是肯定会被改的,可以不输入,但是我个人觉得加上也无妨,没有做过这方面的的实验,有兴趣的话可以自己尝试。
再来看看下面一段代码,是一个strncmp的实现:
static inline int strncmp(const char * cs,const char * ct,int count)
{
register int __res ;
__asm__("cld\n"
"1:\tdecl %3\n\t"
"js 2f\n\t"
"lodsb\n\t"
"scasb\n\t"
"jne 3f\n\t"
"testb %%al,%%al\n\t"
"jne 1b\n"
"2:\txorl %%eax,%%eax\n\t"
"jmp 4f\n"
"3:\tmovl $1,%%eax\n\t"
"jl 4f\n\t"
"negl %%eax\n"
"4:"
:"=a" (__res):"D" (cs),"S" (ct),"c" (count));
return __res;
}
int main(int argc,char*argv[])
{
strncmp("hello","world",5);
}
在这段代码中,es:edi指向cs字符串,ds:esi指向ct字符串,对两者进行比较。但是我们并没有发现对es&ds进行了赋值!这究竟是怎么回事呢?
使用gcc -S -o strncmp.S strncmp.c 看一下汇编的结果:
.file "strncmp.c"
.text
.type strncmp, @function
strncmp:
pushl %ebp
movl %esp, %ebp
pushl %edi
pushl %esi
pushl %ebx
subl $4, %esp
movl 8(%ebp), %eax
movl %eax, -16(%ebp)
movl 12(%ebp), %edx
movl 16(%ebp), %ecx
movl -16(%ebp), %edi
movl %edx, %esi
#APP
# 5 "strncmp.c" 1
cld
1: decl %ecx
js 2f
lodsb
scasb
jne 3f
testb %al,%al
jne 1b
2: xorl %eax,%eax
jmp 4f
3: movl $1,%eax
jl 4f
negl %eax
4:
# 0 "" 2
#NO_APP
movl %eax, %ebx
movl %ebx, %eax
addl $4, %esp
popl %ebx
popl %esi
popl %edi
popl %ebp
ret
.size strncmp, .-strncmp
.section .rodata
.LC0:
.string "world"
.LC1:
.string "hello"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
subl $12, %esp
movl $5, 8(%esp)
movl $.LC0, 4(%esp)
movl $.LC1, (%esp)
call strncmp
leave
ret
.size main, .-main
.ident "GCC: (Ubuntu 4.4.1-4ubuntu8) 4.4.1"
.section .note.GNU-stack,"",@progbits
至此,还没看到有对es和ds进行复制的操作。因此,我有理由相信这是有编译器来决定的。而在汇编代码中,你看到这两个字符串其实是分配在相近的地址中的,因此cs和ds寄存器应该是一样的。不过,这里也纠正一个自己以前犯的错,以前还以为直接在函数参数中写入常量字符串的话,时会把字符串直接写到堆栈中,现在从汇编代码来看,确实直接写到.rodata 段中去的。