Illegal instruction in ASM: blocking cmpxchg dest, src

I was messing around with some kind of x86 build since it appeared in many of my classes. In particular, I would like to provide a comparison and replacement function (CAS) as a user function. This is because I can implement my own locks.

I am using Linux 2.6.31 with GCC 4.1.1 on an Intel processor.

I have the following:

// int cmpxchg(int *dest, int expected, int update) .globl cmpxchg cmpxchg: pushl %ebp movl %esp, %ebp // edx holds dest movl 8(%ebp), %edx // eax holds expected value movl 12(%ebp), %eax // ecx holds the new value movl 16(%ebp), %ecx // cmpxchg dest_addr, exp_value // compare to %eax is implicit lock cmpxchgl %edx, %ecx leave ret 

This is the * .s file that I am compiling with my driver program. When I turn on the line

  lock cmpxchgl %edx, %ecx 

and execute, I get the error "Invalid instruction." When I replace the string

  cmpxchgl %edx, %ecx 

my code seems to be working fine.

First of all, do you need a lock ? I'm not sure cmpxchgl naturally atomic, so I used lock to be sure. As a user program, am I even allowed to use lock ?

thanks

==================================================== ===============

My last code (for those who may roam here in the future):

 // int cmpxchg(int *dest, int expected, int update) .globl cmpxchg cmpxchg: pushl %ebp movl %esp, %ebp // edx holds dest, use eDx for Destination ;-) movl 8(%ebp), %edx // eax holds expected value implicitly movl 12(%ebp), %eax // cmpxchg dest_add, src_value lock cmpxchgl %edx, 16(%ebp) leave ret 
+4
source share
5 answers

You need cmpxchgl %edx, (%ecx)

This operation does not make sense if the destination is not a memory operand, but the instruction allows you to assign a register. The CPU will fail if the instruction uses registration mode.

I tried, your code works with a memory operand. I do not know if you understand this, but this sequence (with the place of registration) has a popular name: "f00fc7c8 bug" or " error F00F ". In the days of Pentium, it was “HCF” (stop and catch) or the “killer poke” command, because it would throw an exception that it could not then serve because the bus was blocked and it was called by the user Mode. I think that there may be a problem with the software at the OS level.

+7
source

Ross's answer already talks about this, but I will try to clarify a couple of things.

  • Yes, the LOCK prefix is ​​necessary if you want atomicity. The only exception to this is the XCHG (not CMPXCHG ) CMPXCHG , which is blocked by default, as pointed out by aveikau.
  • Yes, it is completely legal to use LOCK from user mode code.
  • Yes, it is legal to use CMPXCHG with a register assignment operand.

However, it is not practical to use LOCK CMPXCHG with the destination operand. Quoting volume 2A of the IA-32 manual (pp. 3-538 in my copy):

The LOCK prefix can be added only to the following instructions and only to forms of instructions in which the destination operand is a memory operand: ADD, ADC, AND, BTC, BTR, BTS, CMPXCHG, CMPXCHG8B, DEC, INC, NEG, NOT, OR, SBB , SUB, XOR, XADD and XCHG.

+3
source

Your program compiles fine here (GNU as 2.20) (I inserted it into test.s and ran it like -o test.o test.s)

As for locking, Intel documentation says:

This instruction can be used with the LOCK Prefix to give a command to execute atomically. To simplify the interface to the processor bus, the destination operand receives a write cycle without taking into account the comparison result. the destination operand is written back if the comparison fails; otherwise, the source operand is written to the destination. (A processor never locks a read without creating a locked write.)

+1
source

Curious, is this final code still correct? From what I see, you are doing the comparison in the reverse order, that is, you are comparing the value of the pointer (that is, the actual address that the pointer refers to) with the integer used as an update ... then the destination is assigned as a temporary value int used as the update value. In other words, not:

 lock cmpxchgl %edx, 16(%ebp) 

I would think you need something like:

 //move the update value into ecx register movl 0x16(%ebp), %ecx //do the comparison between the value at the address pointed to by edx and eax, //and if they are the same, copy ecx into the address being pointed to by edx lock cmpxchgl %ecx, (%edx) 

Did the source code really work as planned (and not just compile), and if not, did you end up rebuilding the code to look more like the one above?

+1
source

All Articles