这个函数在不同的架构下有着不同的实现,因为它主要是用嵌入汇编写的,我将其简化后得到下面的代码。
#define __copy_user(to,from,size)
int __d0, __d1, __d2;
// 0为size、1为to、2为from
__asm__ __volatile__(
" cmp $7,%0\n"
" jbe 1f\n" // size < 7跳转到标签1
" movl %1,%0\n"
" negl %0\n"
" andl $7,%0\n"
" subl %0,%3\n"
"4: rep; movsb\n"
" movl %3,%0\n"
" shrl $2,%0\n"
" andl $3,%3\n"
" .align 2,0x90\n"
"0: rep; movsl\n"
" movl %3,%0\n"
"1: rep; movsb\n"
"2:\n"
".section .fixup,\"ax\"\n"
"5: addl %3,%0\n"
" jmp 2b\n"
"3: lea 0(%3,%0,4),%0\n"
" jmp 2b\n"
".previous\n"
".section __ex_table,\"a\"\n"
" .align 4\n"
" .long 4b,5b\n"
" .long 0b,3b\n"
" .long 1b,2b\n"
".previous"
: "=&c"(size), "=&D" (__d0), "=&S" (__d1), "=r"(__d2)
: "3"(size), "0"(size), "1"(to), "2"(from)
: "memory");
函数的参数分别是:
to:写入地址
from:源地址
size:写入量
嵌入汇编的输出"=&c"(size), "=&D" (__d0), "=&S" (__d1), "=r"(__d2)
=&c"(size)
:size使用ecx寄存器,=表示只能用于写,&表示输出限定,表示ecx只作为输出用,不会被输入使用。
"=&D" (__d0)
:表示__do使用di寄存器,只用于写。
"=&S" (__d1)
: 表示__d1使用si寄存器,只用于写。
"=r"(__d2)
:表示__d2使用寄存器操作数(r表示general寄存器操作数)
汇编输入 "3"(size), "0"(size), "1"(to), "2"(from)
数字表示他们对应的操作数,3表示与输出的"=r"(__d2)
一样使用内存来写入,0使用的ecx、1使用di、2使用si
汇编代码首先:
cmp $7,%0\n
jbe 1f
如果size < 7跳转到标签1,将ds:si
(from)的ecx
(size)个字节复制到es:di
(to)
1: rep; movsb
如果size >= 7那么继续向下执行。
movl %1,%0 // 将to保存到ecx
negl %0 // ecx取负
andl $7,%0 // ecx与7,与上一步一起相当于取余%8
subl %0,%3 // %3是内存中的变量,保存的是(size - size%8)的值
4: rep; movsb // 传输ds:si到es:di,将余数按字节复制到目的内存
刚刚把余数复制过去了,现在需要把剩下的大块对齐的内存复制
movl %3,%0
shrl $2,%0
andl $3,%3
.align 2,0x90
0: rep; movsl // 传送双字,也就是每次传输4字节
参考
- gcc嵌入汇编 https://dirtysalt.github.io/html/gcc-asm.html