Может быть, это может работать как огромный небезопасный хак, который работает только в игрушечных экспериментах. Если вы хотите установить новый стек, сделайте это в рукописной asm перед вызовом функции C.
Этот хак может сработать для вызова на foo()
, но как насчет return 0;
? Сгенерированный компилятором код будет пытаться извлечь адрес возврата из текущего% esp.
(или, если оптимизация отключена, будет использоваться leave
, который устанавливает ESP = EBP
, прежде чем выдавать сохраненный EBP. Это переключится обратно к начальному стеку. Таким образом, поведение зависит от уровня оптимизации! Вы не хотите этого.)
Используйте GDB, чтобы пошагово выполнить свой код и фактически наблюдать изменение значений reg, например, с помощью layout reg
.
Но да, &myStack + 1
является адресом одного конца конца массива, и в результате получается movl $myStack+1024, %eax
в качестве настройки для оператора asm (где расширяется %0
на %eax
в шаблоне, потому что компилятор выбрал этот регистр для операнда "rm"
. Вы не дали ему возможность немедленной константы, или он просто сделал бы это с movl $myStack+1024, %esp
).
https://godbolt.org/z/Nz6DgA показывает, что он «работает» и затем сразу же обработает sh, когда достигнет такта с включенной оптимизацией, потому что он пытается pop
с ESP, указывающим на один за другим -конец myStack
.
I В настоящее время я работаю над реализацией потоков на уровне ядра, поэтому идея состоит в том, чтобы назначить отдельный стек для каждого потока и переключаться между ними
Особенно, если main
должен фактически return
, тогда да, вам нужно настроить стек перед , используя его для call
чего угодно. В противном случае этот конечный адрес возврата будет находиться в неправильном стеке!
Например, в Linux библиотека pthread создает новый поток с новым стеком, выделяя его с помощью mmap()
, а затем передает этот адрес стека как операнд к clone()
. Таким образом, новый поток никогда не использует стек родителя, а только свой собственный стек. Я предполагаю, что создание стека потоков на стороне ядра для новой задачи аналогично. Вы выделяете новый стек, , затем используете его для нового контекста потока.
Вы можете поместить "адрес возврата" вверху, чтобы первая функция, вызванная в новом потоке, фактически возвращала в функцию выхода / очистки потока. Возможно, с некоторой ассм за это. Или сделайте фактическую точку входа потока функцией, которая не возвращает, вместо этого очистите контекст потока и переключитесь на другой поток или вызовите свой планировщик или что-то в этом роде.
Это то, что я собираюсь сделать для того, чтобы иметь поток, использующий свой собственный стек, в отличие от основного. Тем не менее, у меня не включена подкачка страниц, и я хотел бы реализовать создание стека как можно более простым (таким образом, массив C кажется простым решением)
К сожалению, это слишком просто и не на самом деле работает.
Да, вы можете использовать массив C для стека потоков (если у вас ровно один дополнительный поток ...), проблема в том, как вы переключаетесь на него.
Вам нужно в какой-то момент написать функцию переключения контекста, которая сохраняет один контекст регистра и загружает другой. (Например, Google, вы можете найти несколько здесь, в Переполнении стека, и, вероятно, что-то в https://www.osdev.org/.)
Создать новую структуру контекста потока в памяти с указателем стека, указывающим на вершина стека потока и его EIP, указывающий на точку входа потока. Вызовите функцию переключения контекста, чтобы переключиться на этот новый контекст.
Из POV компилятора C функция переключения контекста выглядит как любой другой вызов функции. Он возвращает , в конечном итоге , и, возможно, изменил любые глобально достижимые C объекты. Неважно, что ESP временно указывал куда-то еще. «Как и любой другой вызов функции» включает в себя сжатые регистры с закрытыми вызовами, BTW, поэтому вам не нужно сохранять / восстанавливать EAX / ECX / EDX. Вызывающая функция переключения контекста уже предполагает, что они уничтожены.
Обычно вы должны писать это вручную в asm, а не в asm. Смена ESP с inline asm чревата опасностью, и официально задокументировано как не поддерживаемое G CC.
Это связано с тем, что компилятор требует, чтобы значение указателя стека было таким же после оператора asm, как и при входе в оператор
См. Также https://gcc.gnu.org/wiki/DontUseInlineAsm