Недопустимая инструкция в ASM: блокировка cmpxchg dest, src - PullRequest
4 голосов
/ 17 ноября 2009

Я возился с какой-то сборкой x86, так как она появлялась во многих моих уроках. В частности, я хотел показать сравнение и обмен (CAS) как пользовательскую функцию. Это сделано для того, чтобы я мог реализовать свои собственные блокировки.

Я использую Linux 2.6.31 с GCC 4.1.1 на процессоре Intel.

У меня есть следующее:

// 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

Это файл * .s, который я компилирую с моей программой драйвера. Когда я включаю строку

  lock cmpxchgl %edx, %ecx

и выполнить, я получаю ошибку «Недопустимая инструкция». Когда я заменяю строку на

  cmpxchgl %edx, %ecx

мой код работает нормально.

Прежде всего, lock необходим? Я не уверен, является ли cmpxchgl естественно атомарным, поэтому я использовал lock, чтобы быть уверенным. Могу ли я использовать в качестве пользовательской программы lock?

Спасибо

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

Мой окончательный код (для тех, кто может бродить здесь в будущем):

// 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

Ответы [ 5 ]

7 голосов
/ 17 ноября 2009

Вам нужно cmpxchgl %edx, (%ecx)

Эта операция не имеет смысла, если адресат не является операндом памяти, однако инструкция допускает регистр адресата. Процессор выйдет из строя, если инструкция использует режим регистрации.

Я пробовал, ваш код работает с операндом памяти. Я не знаю, понимаете ли вы это, но эта последовательность (с адресом регистрации) имеет популярное имя: «ошибка f00fc7c8» или « ошибка F00F ». В дни Pentium это была команда "HCF" (остановка и загорание) или "убийца тыкают", поскольку она генерирует исключение, которое не сможет обслуживать, потому что шина заблокирована, и она вызывается пользователем Режим. Я думаю, что, возможно, был обходной путь программного обеспечения уровня ОС.

3 голосов
/ 17 ноября 2009

Ответ Росса уже говорит о большей части этого, но я постараюсь уточнить пару вещей.

  1. Да, префикс LOCK необходим, если вы хотите атомарность. Единственное исключение - инструкция XCHG (не CMPXCHG), которая по умолчанию заблокирована, как указывал Асвейкау.
  2. Да, вполне законно использовать LOCK из кода режима пользователя.
  3. Да, вполне допустимо использовать CMPXCHG с операндом назначения регистра.

Тем не менее, не разрешено использовать LOCK CMPXCHG вместе с операндом-адресатом регистра. Цитата 2A Руководство по IA-32 (стр. 3-538 в моем экземпляре):

Префикс LOCK может добавляться только к следующим инструкциям и только к тем формам инструкций, где операндом-адресатом является операнд памяти: ADD, ADC, AND, BTC, BTR, BTS, CMPXCHG, CMPXCHG8B, DEC, INC , NEG, НЕ, ИЛИ, SBB, SUB, XOR, XADD и XCHG.

1 голос
/ 06 сентября 2010

Любопытно, этот окончательный код все еще правильный? Из того, что я вижу, вы делаете сравнение в обратном порядке, то есть вы сравниваете значение указателя (т.е. фактический адрес, на который ссылается указатель) с целым числом, используемым в качестве обновления ... более в качестве значения обновления указывается целевое временное int. Другими словами, а не:

lock cmpxchgl %edx, 16(%ebp)

Я думаю, вы бы хотели что-то вроде:

//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)

Действительно ли исходный код работал как запланировано (не просто компилируется), и если нет, то вы в итоге реорганизовали код так, чтобы он выглядел больше как выше?

1 голос
/ 17 ноября 2009

Ваша программа прекрасно компилируется (GNU как 2.20) (я вставил ее в test.s и запустил как -o test.o test.s )

Что касается блокировки, в документации Intel написано:

Эта инструкция может использоваться с Префикс LOCK для разрешения инструкции быть выполненным атомарно. Упростить интерфейс к шине процессора, целевой операнд получает цикл записи без учета Результат сравнения. целевой операнд записывается обратно, если сравнение не удается; в противном случае операнд источника записывается в место назначения. (Процессор никогда производит заблокированное чтение без также создание заблокированной записи.)

0 голосов
/ 17 ноября 2009

Это вряд ли является источником проблемы, но в официальной документации также говорится, что инструкция не поддерживается ранее, чем архитектура 486. Это происходит в реальном режиме с переопределением защищенного режима?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...