Как использовать адресные константы во встроенной сборке GCC x86 - PullRequest
9 голосов
/ 14 февраля 2009

В цепочке инструментов GCC по умолчанию используется синтаксис ассемблера AT & T, но поддержка синтаксиса Intel доступна через директиву .intel_syntax.

Кроме того, синтаксис AT & T и Intel доступен в версиях prefix и noprefix, которые различаются в зависимости от того, требуется ли им префикс имени регистра с символом %.

В зависимости от того, какие директивы присутствуют, формат адресных констант изменяется.

Давайте рассмотрим следующий код C

*(int *)0xdeadbeef = 0x1234;

Используя objdump -d, мы находим, что он скомпилирован в следующую инструкцию ассемблера

movl $0x1234,0xdeadbeef

Поскольку здесь нет регистров, это правильный синтаксис для .att_syntax prefix и .att_syntax noprefix, т.е. встроенный в код C, они выглядят так:

__asm__(".att_syntax prefix");
__asm__("movl $0x1234,0xdeadbeef");

__asm__(".att_syntax noprefix");
__asm__("movl $0x1234,0xdeadbeef");

При желании вы можете заключить адресную константу в круглые скобки, например.

__asm__("movl $0x1234,(0xdeadbeef)");

тоже будет работать.

При добавлении сигиллы в константу простого адреса код не будет скопирован

__asm__("movl $0x1234,$0xdeadbeef"); // won't compile

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

__asm__("movl $0x1234,($0xdeadbeef)"); // doesn't warn, but doesn't work!

Это неправильно выдает инструкцию

movl $0x1234,0x0

В режиме Intel перед константой адреса должен стоять регистр сегмента, а также размер операнда и флаг PTR, если возможна неоднозначность. На моей машине (двухъядерный ноутбук Intel с Windows XP и текущими версиями MinGW и Cygwin GCC) регистр ds используется по умолчанию.

Квадратные скобки вокруг константы являются необязательными. Адресная константа также правильно распознается, если регистр сегмента опущен, но скобки присутствуют. Если пропустить регистр, в моей системе выдается предупреждение.

В режиме prefix регистр сегмента должен начинаться с префикса %, но работать будут только скобки. Это разные способы создания правильной инструкции:

__asm__(".intel_syntax noprefix");
__asm__("mov DWORD PTR ds:0xdeadbeef,0x1234");
__asm__("mov DWORD PTR ds:[0xdeadbeef],0x1234");
__asm__("mov DWORD PTR [0xdeadbeef],0x1234"); // works, but warns!

__asm__(".intel_syntax prefix");
__asm__("mov DWORD PTR %ds:0xdeadbeef,0x1234");
__asm__("mov DWORD PTR %ds:[0xdeadbeef],0x1234");
__asm__("mov DWORD PTR [0xdeadbeef],0x1234"); // works, but warns!

Если регистр сегмента и скобки пропущены, компиляция не будет

__asm__("mov DWORD PTR 0xdeadbeef,0x1234"); // won't compile

Я отмечу этот вопрос как вики сообщества , поэтому, если у вас есть что добавить, не стесняйтесь.

Ответы [ 2 ]

4 голосов
/ 14 февраля 2009

Директивы noprefix / prefix только контролируют, требуется ли для регистров префикс % (*) (по крайней мере, так кажется, и это единственное отличие, о котором упоминается в документации). Для литералов-значений всегда требуется префикс $ в синтаксисе AT & T и никогда в синтаксисе Intel. Итак, следующие работы:

__asm__(".intel_syntax prefix");
__asm__("MOV [DWORD PTR 0xDEADBEEF], 0x1234");

Если вы действительно склонны использовать встроенную синтаксическую сборку Intel в коде C, скомпилированном с GCC и собранном с помощью GAS, не забудьте также добавить следующее после него, чтобы ассемблер мог использовать оставшуюся часть (синтаксис AT & T) сборка, сгенерированная GCC:

__asm__(".att_syntax prefix");

Я вижу причину различия префикса / noprefix в том, что для синтаксиса AT & T префикс % на самом деле не нужен для регистров в архитектуре Intel, поскольку регистры имеют имена. Но для единообразия это может быть, потому что некоторые другие архитектуры (например, SPARC) имеют нумерацию зарегистрированных, и в этом случае указание только небольшого числа будет неоднозначным в отношении того, подразумевается ли адрес памяти или регистр.

1 голос
/ 14 февраля 2009

Вот мои собственные результаты:

*(int *)0xdeadbeaf = 0x1234; // reference implementation

// AT&T: addresses without sigil; parentheses are optional

__asm__(".att_syntax prefix");
__asm__("movl $0x1234,0xdeadbeaf");     // works
__asm__("movl $0x1234,(0xdeadbeaf)");   // works
__asm__("movl $0x1234,($0xdeadbeaf)");  // doesn't work, doesn't warn!
//__asm__("movl $0x1234,$0xdeadbeaf");  // doesn't compile
//__asm__("movl 0x1234,0xdeadbeaf");    // doesn't compile
//__asm__("movl 0x1234,(0xdeadbeaf)");  // doesn't compile

__asm__(".att_syntax noprefix");
// same as above: no registers used!

// Intel: addresses with square brackets or segment register prefix
// brackets without prefix will warn

__asm__(".intel_syntax noprefix");
__asm__("mov DWORD PTR ds:0xdeadbeaf,0x1234");      // works
__asm__("mov DWORD PTR ds:[0xdeadbeaf],0x1234");    // works
__asm__("mov DWORD PTR [0xdeadbeaf],0x1234");       // works, but warns!
//__asm__("mov DWORD PTR 0xdeadbeaf,0x1234");       // doesn't compile

// `prefix` will add % to register names

__asm__(".intel_syntax prefix");
__asm__("mov DWORD PTR %ds:0xdeadbeaf,0x1234");     // works
__asm__("mov DWORD PTR %ds:[0xdeadbeaf],0x1234");   // works
__asm__("mov DWORD PTR [0xdeadbeaf],0x1234");       // works, but warns!
//__asm__("mov DWORD PTR 0xdeadbeaf,0x1234");       // doesn't compile

__asm__(".att_syntax prefix");
...