Gcc удаляет встроенный код ассемблера
Кажется, что gcc 4.6.2 удаляет код, который он считает неиспользованным из функций.
test.c
int main(void) {
goto exit;
handler:
__asm__ __volatile__("jmp 0x0");
exit:
return 0;
}
Разборка main()
0x08048404 <+0>: push ebp
0x08048405 <+1>: mov ebp,esp
0x08048407 <+3>: nop # <-- This is all whats left of my jmp.
0x08048408 <+4>: mov eax,0x0
0x0804840d <+9>: pop ebp
0x0804840e <+10>: ret
Параметры компилятора
Нет оптимизаций, просто gcc -m32 -o test test.c
(-m32
, потому что я на 64-битной машине).
Как я могу остановить это поведение?
Изменить: Предпочтительно, используя параметры компилятора, а не путем изменения кода.
Ответы
Ответ 1
Обновление 2012/6/18
Просто подумав об этом, можно положить goto exit
в блок asm, а это значит, что нужно изменить только одну строку кода:
int main(void) {
__asm__ ("jmp exit");
handler:
__asm__ __volatile__("jmp $0x0");
exit:
return 0;
}
Это значительно чище, чем мое другое решение ниже (и, возможно, более приятное, чем текущее значение @ugoren).
Это довольно хаки, но, похоже, это работает: скрыть обработчик в условном выражении, который никогда не может быть соблюден при нормальных условиях, но не останавливайте его на устранении, прекратив компилятор, чтобы он мог правильно выполнить свой анализ с помощью ряда встроенных ассемблер.
int main (void) {
int x = 0;
__asm__ __volatile__ ("" : "=r"(x));
// compiler can't tell what the value of x is now, but it always 0
if (x) {
handler:
__asm__ __volatile__ ("jmp $0x0");
}
return 0;
}
Даже с -O3
сохраняется jmp
:
testl %eax, %eax
je .L2
.L3:
jmp $0x0
.L2:
xorl %eax, %eax
ret
(Это кажется действительно изворотливым, поэтому я надеюсь, что есть лучший способ сделать это. отредактируйте просто класть volatile
перед x
работает, поэтому вам не нужно делать встроенную азбуку asm.)
Ответ 2
Похоже, это так, как есть. Когда gcc
видит, что код внутри функции недоступен, он удаляет его. Другие компиляторы могут отличаться.
В gcc
ранняя фаза компиляции - это построение "графика потока управления" - графика "базовых блоков", каждая из которых свободна от условий, связанных ветвями. При испускании фактического кода части графика, которые недоступны из корня, отбрасываются.
Это не является частью фазы оптимизации и поэтому не зависит от параметров компиляции.
Таким образом, любое решение предполагало бы, что gcc
считает, что код доступен.
Мое предложение:
Вместо того, чтобы помещать код сборки в недостижимое место (где GCC может удалить его), вы можете поместить его в доступное место и пропустить проблемную инструкцию:
int main(void) {
goto exit;
exit:
__asm__ __volatile__ (
"jmp 1f\n"
"jmp $0x0\n"
"1:\n"
);
return 0;
}
Также см. этот поток об этой проблеме.
Ответ 3
Будет ли это работать, сделайте так, чтобы gcc не мог знать его недостижимый
int main(void)
{
volatile int y = 1;
if (y) goto exit;
handler:
__asm__ __volatile__("jmp 0x0");
exit:
return 0;
}
Ответ 4
Я никогда не слышал о том, как предотвратить gcc от удаления недостижимого кода; кажется, что независимо от того, что вы делаете, когда gcc обнаруживает недостижимый код, он всегда удаляет его (используйте параметр gcc -Wunreachable-code
, чтобы увидеть, что он считает недостижимым).
Тем не менее, вы все равно можете поставить этот код в статическую функцию, и он не будет оптимизирован:
static int func()
{
__asm__ __volatile__("jmp $0x0");
}
int main(void)
{
goto exit;
handler:
func();
exit:
return 0;
}
P.S
Это решение особенно удобно, если вы хотите избежать избыточности кода при имплантации одного и того же кода кода "обработчик" в более чем одном месте в исходном коде.
Ответ 5
gcc может дублировать операторы asm внутри функций и удалять их во время оптимизации (даже при -O0), поэтому это никогда не будет работать надежно.
один из способов сделать это надежно - использовать глобальный оператор asm (т.е. оператор asm вне любой функции). gcc скопирует это прямо на выход, и вы можете без проблем использовать глобальные метки.
Ответ 6
Не могли бы вы попытаться добавить параметры -O0
в компилятор, который должен удалить любую оптимизацию, ket me know!
pedr0