What does this syntax mean?
This unreadable mess is GCC Extended ASM , which has a common format
asm [volatile] ( AssemblerTemplate : OutputOperands [ : InputOperands [ : Clobbers ] ] )
In this case, the __asm__ operator contains only AssemblerTemplate and InputOperands . Part of the input operands explains what %0 and %1 mean, and how ecx and edx get their meaning:
- The first input operand is
"m" (*&__tmp.a) , so %0 becomes m emory address __tmp.a (to be completely honest, I'm not sure why *& is needed here). - The second input operand is
"m" (*&__tmp.b) , so %1 becomes the m emory address __tmp.b . - The third input operand is
"d" (_TSS(n)) , so register D X will contain _TSS(n) when this code is run. - The fourth input operand
"c" ((long) task[n]) , so the E C X register will contain task[n] when this code is run.
When cleaning, the code can be interpreted as follows
cmpl %ecx, _current je 1f movw %dx, __tmp.b ;; the address of __tmp.b xchgl %ecx, _current ljmp __tmp.a ;; the address of __tmp.a cmpl %ecx, _last_task_used_math jne 1f clts 1:
How does ljmp %0 ?
Note that there are two forms of the ljmp instruction (also called jmpf ). The one you know ( EA operation code) takes two immediate arguments: one for the segment, one for the offset. The one used here (operation code FF /5 ) is different: the segment and address arguments are not in the code stream, but somewhere in the memory, and the instruction points to the address.
In this case, the ljmp argument points to the __tmp structure at the __tmp . The first four bytes ( __tmp.a ) contain the offset, and the next two bytes (the lower half of __tmp.b ) contain the segment.
This indirect ljmp __tmp.a will be equivalent to ljmp [__tmp.b]:[__tmp.a] , except that ljmp segment:offset can only accept immediate arguments. If you want to switch to arbitrary TSS without self-modifying code (which would be a terrible idea), an indirect instruction will be used.
Also note that __tmp.a never initialized. We can assume that _TSS(n) refers to the shutter of the task (because this is how you switch the context from TSS), and the offset for the transitions through the task gate is ignored.
Where is the old instruction pointer located?
This piece of code does not store the old EIP in TSS.
(I think after this point, but I think this assumption is reasonable.)
The old EIP is stored in the kernel space stack, which corresponds to the old task.
Linux 0.11 allocates a stack of 0 (for the stack) for each task (see the copy_process function in fork.c , which initializes TSS). When an interrupt occurs during task A, the old EIP is stored in the kernel space stack, not in the user space stack. If the kernel decides to switch to task B, the kernel stack also switches. When the kernel eventually switches to task A, this stack switches back, and through iret we can return to where we were in task A.