При попытке повторного использования какого-то старого кода (https://github.com/chaos4ever/chaos/blob/master/libraries/system/system_calls.h#L387, FWIW) я обнаружил, что некоторые из семантики gcc
кажутся изменился довольно тонким, но все же опасным образом в течение последних 10-15 лет...: P
Код, который хорошо подходит для старых версий gcc
, например 2.95. Во всяком случае, вот код:
static inline return_type system_call_service_get(const char *protocol_name, service_parameter_type *service_parameter,
tag_type *identification)
{
return_type return_value;
asm volatile("pushl %2\n"
"pushl %3\n"
"pushl %4\n"
"lcall %5, $0"
: "=a" (return_value),
"=g" (*service_parameter)
: "g" (identification),
"g" (service_parameter),
"g" (protocol_name),
"n" (SYSTEM_CALL_SERVICE_GET << 3));
return return_value;
}
Проблема с вышеприведенным кодом заключается в том, что gcc
(4.7 в моем случае) скомпилирует это со следующим кодом asm (синтаксис AT & T):
# 392 "../system/system_calls.h" 1
pushl 68(%esp) # This pointer (%esp + 0x68) is valid when the inline asm is entered.
pushl %eax
pushl 48(%esp) # ...but this one is not (%esp + 0x48), since two dwords have now been pushed onto the stack, so %esp is not what the compiler expects it to be
lcall $456, $0
# Restoration of %esp at this point is done in the called method (i.e. lret $12)
Проблема: переменные (identification
и protocol_name
) находятся в стеке в вызывающем контексте. Таким образом, gcc
(с оптимизацией получилось, не зная, имеет ли значение), просто получит значения оттуда и передаст его в секцию inline asm. Но, поскольку я нажимаю вещи в стеке, смещения, которые вычисляются gcc
, будут отключены на 8 в третьем вызове (pushl 48(%esp)
).:)
Мне потребовалось много времени, чтобы понять, сначала это было не все очевидно.
Самый простой способ - это, конечно, использовать входное ограничение r
, чтобы убедиться, что это значение находится в регистре. Но есть ли другой, лучший способ? Разумеется, одним из очевидных способов было бы переписать весь интерфейс системного вызова, чтобы не вставлять вещи в стек в первую очередь (и вместо этого использовать регистры, например, Linux), но это не рефакторинг, который мне нравится делать сегодня вечером...
Есть ли способ сообщить gcc
inline asm, что "стек неустойчив"? Как вы, ребята, занимались такими вещами в прошлом?
Обновление позже в тот же вечер. Я нашел соответствующий поток gcc
ML (https://gcc.gnu.org/ml/gcc-help/2011-06/msg00206.html) но, похоже, это не помогло. Кажется, что указание %esp
в списке clobber должно заставить его делать смещения от %ebp
вместо этого, но это не работает, и я подозреваю, что эффект -O2 -fomit-frame-pointer
имеет здесь эффект. У меня есть оба этих флажка.