KernelNewbies:

At the heart of the code for copying data between kernel and user-space on x86, there is the macro __copy_user, which expands to some assembly as follows :

 #define __copy_user(to,from,size)     
do {         
 int __d0, __d1;       
 __asm__ __volatile__(      
  "0: rep; movsl\n"     
  " movl %3,%0\n"     
  "1: rep; movsb\n"     
  "2:\n"       
  ".section .fixup,\"ax\"\n"    
  "3: lea 0(%3,%0,4),%0\n"    
  " jmp 2b\n"     
  ".previous\n"      
  ".section __ex_table,\"a\"\n"    
  " .align 4\n"     
  " .long 0b,3b\n"     
  " .long 1b,2b\n"     
  ".previous"      
  : "=&c"(size), "=&D" (__d0), "=&S" (__d1)  
  : "r"(size & 3), "0"(size / 4), "1"(to), "2"(from) 
  : "memory");      
} while (0)

This is perhaps an intimidating few lines, so we here we go into more detail into the code. Don't forget to read the inline assembly links on this site.

Naive copy

  "0: rep; movsl\n"
  " movl %3,%0\n"
  "1: rep; movsb\n"
  "2:\n"

The first three lines naively copy 'size' bytes from 'from' to 'to'. I write "naively" because it might happen that the copy fails; then a memory exception would occur. A memory fault could occur for several reasons, each having their own way to handle the fault. The extra code used in '__copy_user' is one way to handle simple errors.

Fixup

  ".section .fixup,\"ax\"\n"
  "3: lea 0(%3,%0,4),%0\n"    
  " jmp 2b\n"

The kernel contains a table (in section '__ex_table') containing entries (X, Y) saying that if an error occurs at address X, then jump to address Y. The entries declared in the '__copy_user' code thus say that if a memory error occurs when executing the code at the label '0:', the handler should return to (and then execute) the code at the label '3:', calculating the actual number of non-copied bytes. Likewise, an error at '1:' makes the handler return to '2:', just skipping any more copying.

Note that lea 0(%3,%0,4),%0 is equivalent to the calculation %ecx = (size % 4) + %ecx * 4.

Labels like '0:', '1:', ... are "local labels", which can be used several times for different locations in the same code. They are then referenced by '0b', '1f', ... meaning "label 0 searching backwards" and "label 1 searching forwards" respectively.

Sections

  ".previous\n"

The assembler directive .previous just tells the assembler to put the following code/data in the section used before the current section, probably the .text section.

The exception table data is put into the __ex_table section used by all exception table code in the kernel.

Exception table

  ".section __ex_table,\"a\"\n"    
  " .align 4\n"     
  " .long 0b,3b\n"     
  " .long 1b,2b\n"     
  ".previous"      

The actual exception table itself in X,Y form, as described above.

Originally by Per Persson, modified by John Levon.


CategoryDocs

KernelNewbies: Documents/CopyUser (last edited 2017-12-30 01:29:54 by localhost)