char * a = (void*)*(arg + 7);
- вещь, которая «случается с работой», если она вообще работает. Если вы не пишете __attribute__((naked))
функции, которые только используют встроенный asm, компилятор полностью определяет, как он распределяет стековую память. Похоже, что вы получаете rsp
, хотя это не гарантируется для этого неподдерживаемого использования local-asm local. (Использование запрошенного регистра гарантируется только при использовании в качестве операнда для встроенного оператора asm.)
Если вы компилируете с отключенной оптимизацией, gcc зарезервирует слоты стека для локальных пользователей, поэтому char * b = a;
заставит gcc корректировать RSP при вводе функции , поэтому ваш хак может изменить код gcc на gen соответствует жестко заданному смещению +7
(умноженному на 8 байт), которое вы указали в источнике.
При входе в _start
содержимое стека: argc
в (%rsp)
, argv[]
, начиная с 8(%rsp)
. Над завершающим указателем NULL для argv [] массив envp[]
также находится в памяти стека. Вот почему вы получаете CLUTTER_IM_MODULE=xim
, когда жестко закодированное смещение получает неправильный слот стека.
// in absence of main() argc seems to be placed in rsi register
Это, вероятно, осталось от динамического компоновщика (который выполняется в вашем процессе до _start
). Если вы скомпилировали с gcc -static -nostdlib -fno-pie
, ваша _start
будет реальной точкой входа в процесс, достигаемой непосредственно из ядра, со всеми регистрами = 0 (кроме RSP). Обратите внимание, что ABI говорит неопределенный; Linux решает обнулить их, чтобы избежать утечки информации.
Вы можете написать void _start(){}
в GNU C, который надежно работает с и без включенной оптимизации и работает по правильным причинам, без встроенного asm (но все еще зависит от соглашения о вызовах SysV ABI x86-64 и макета стека ввода процесса). Не требуется жесткого кодирования смещений, которые случаются в коде gen gcc. Как получить значение аргументов, используя встроенную сборку в C без Glibc? . Он использует такие вещи, как int argc = (int)__builtin_return_address(0);
, потому что _start
не является функцией: первое, что нужно в стеке, это argc, а не адрес возврата. Это не красиво и не рекомендуется, но, учитывая соглашение о вызовах, вы можете получить gcc для генерации кода, который знает, где что находится.
Ваш кодоблокатор регистрируется, не сообщая об этом компилятору. Все в этом коде неприятно, и нет никаких оснований ожидать, что какой-либо из них будет работать согласованно. И если это произойдет, это случайно и может сломаться с различными окружающими кода или параметров компилятора. Если вы хотите написать целые функции, сделайте это в автономном asm (или в встроенном asm в глобальной области видимости) и объявите прототип C, чтобы компилятор мог его вызвать.
Посмотрите на вывод gcc asm и посмотрите, что он сгенерировал вокруг вашего кода. (например, поместите ваш код в http://godbolt.org/).. Вы, вероятно, увидите его, используя регистры, которые вы засорили в вашем ассемблере. (Если вы не скомпилировали с отключенной оптимизацией, в этом случае он ничего не хранит в регистрах между операторами C, чтобы поддерживать согласованную отладку. Только сглаживание RSP или RBP может вызвать проблемы, другие встроенные ошибки asm clobber останутся незамеченными.) Но слипание красной зоны все равно будет проблемой.
См. Также https://stackoverflow.com/tags/inline-assembly/info для ссылок на руководства и учебные пособия.
Правильный способ использования встроенного asm (если есть правильный путь) - это, как правило, позволить компилятору делать как можно больше . Таким образом, чтобы сделать системный вызов write, вы должны делать все с ограничениями ввода / вывода, и единственной инструкцией внутри шаблона asm будет "syscall"
, как в этом хорошем примере my_write
function: Как вызвать систему вызов через sysenter во встроенной сборке? (Фактический ответ имеет 32-разрядную int $0x80
и x86-64 syscall
, но не встроенную версию asm с использованием 32-разрядной sysenter
, поскольку это не гарантированно стабильный ABI ).
См. Также В чем разница между «asm», «__asm» и «__asm __»? для другого примера.
https://gcc.gnu.org/wiki/DontUseInlineAsm по многим причинам, почему вы не должны его использовать (например, победить постоянное распространение и другие оптимизации).
Помните, что ограничение ввода указателя для встроенного оператора asm не подразумевает, что указанная память также является входом или выходом. Используйте "memory"
clobber или см. at & t asm inline c ++ проблема для обхода обхода фиктивного операнда.