Метки в GCC встраиваемой сборки - PullRequest
37 голосов
/ 10 октября 2010

В ходе моих экспериментов со встроенной сборкой GCC я столкнулся с новой проблемой, связанной с метками и встроенным кодом.

Рассмотрим следующий простой прыжок:

__asm__
(
    "jmp out;"
    "out:;"
    :
    :
);

Это ничего не делает, кроме перехода к метке out. Этот код компилируется нормально. Но если вы поместите его в функцию, а затем скомпилируете с флагами оптимизации, компилятор скажет: «Ошибка: символ« out »уже определен».

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

Итак, как мне обойти это? Неужели нельзя использовать метки во встроенной сборке? В этом руководстве по встроенной сборке GCC упоминается, что:

Таким образом, вы можете сделать ставку вашей сборки в макросы CPP и встроенный C функции, так что любой может использовать его в качестве любая функция C / макрос. Встроенные функции очень похожи на макросы, но иногда чище использовать. Остерегайтесь этого во всех этих случаях код будет дублируется, поэтому только локальные метки (из 1: стиль) должен быть определен в этом АСМ код.

Я пытался найти больше информации об этих "локальных метках", но, похоже, не могу найти ничего, касающегося встроенной сборки. Похоже, что в руководстве говорится, что локальная метка - это число, за которым следует двоеточие (например, 1:), поэтому я попытался использовать такую ​​метку. Интересно, что код скомпилирован, но во время выполнения он просто вызвал ошибку сегментации. Хм ...

Значит, есть предложения, советы, ответы ...?

Ответы [ 2 ]

48 голосов
/ 10 октября 2010

A объявление локальной метки - это действительно число, за которым следует двоеточие.Но для ссылки на локальную метку требуется суффикс f или b, в зависимости от того, хотите ли вы посмотреть вперед или назад, то есть 1f относится к следующей метке 1: внаправление вперед.

Таким образом, объявление метки как 1: является правильным;но чтобы сослаться на это, нужно сказать jmp 1f (потому что в этом случае вы прыгаете вперед).

29 голосов
/ 19 апреля 2013

Ну, этот вопрос не молодеет, но есть два других интересных решения.

1) В этом примере используется% =.% = в шаблоне ассемблера заменяется числом, которое «уникально для каждого insn во всей компиляции. Это полезно для создания локальных меток, которые упоминаются более одного раза в данном insn».Обратите внимание, что для использования% = у вас (по-видимому) должен быть хотя бы один вход (хотя вам, вероятно, не нужно его использовать).

int a = 3;
asm (
    "test %0\n\t"
    "jnz to_here%=\n\t"
    "jz to_there%=\n\t"
    "to_here%=:\n\t"
    "to_there%=:"
    ::"r" (a));

Это выводит:

test %eax
jnz to_here14
jz to_there14
to_here14:
to_there14:

В качестве альтернативы, вы можете использовать asm goto (я думаю, что добавлено в v4.5).Это фактически позволяет вам переходить к меткам c, а не просто к меткам asm:

asm goto ("jmp %l0\n"
 : /* no output */
 : /* no input */
 : /* no clobber */
 : gofurther);

printf("Didn't jump\n");

// c label:
gofurther:
printf("Jumped\n");
...