CreateThread () не работает на 64-битной Windows, работает на 32-битной Windows.Зачем? - PullRequest
5 голосов
/ 15 июня 2010

Операционная система: Windows XP 64 bit, SP2.

У меня необычная проблема.Я портирую некоторый код с 32 бит на 64 бит.32-битный код работает просто отлично.Но когда я вызываю CreateThread () для 64-битной версии, вызов не выполняется.У меня есть три места, где это не удается.2 вызвать CreateThread ().1 вызывает метод beginthreadex (), который вызывает метод CreateThread ().

Все три вызова завершаются ошибкой с кодом ошибки 0x3E6, «Недопустимый доступ к ячейке памяти».

Проблема в том, что все входные параметры верны.

HANDLE  h;
DWORD   threadID;

h = CreateThread(0,            // default security
                 0,            // default stack size
                 myThreadFunc, // valid function to call
                 myParam,      // my param
                 0,            // no flags, start thread immediately
                 &threadID);

Все три вызова CreateThread () выполняются из библиотеки DLL, которую я вставил в целевую программу в начале выполнения программы (это происходит до того, как программа дойдет до начала main () / WinMain ()).Если я вызываю CreateThread () из целевой программы (те же параметры), скажем через меню, это работает.Те же параметры и т.д. Причудливый.

Если я передаю NULL вместо & threadID, он все равно не работает.

Если я передаю NULL как myParam, он все равно не работает.

Яне вызывая CreateThread из DllMain (), так что это не проблема.Я запутался, и поиск в Google и т. Д. Не дал соответствующих ответов.

Если кто-то видел это раньше или у вас есть идеи, пожалуйста, дайте мне знать.

Спасибо за чтение.

ОТВЕТ

Краткий ответ: стековые рамки на x64 должны быть выровнены на 16 байт.

Более длинный ответ: после долгого удара головой об стену отладчикаи публикуя ответы на различные предложения (все из которых помогли в некотором роде, подтолкнув меня к пробованию новых указаний), я начал изучать что-если о том, что было в стеке, до вызова CreateThread ().Это оказалась красная сельдь, но это привело к решению.

Добавление дополнительных данных в стек изменяет выравнивание кадров стека.Рано или поздно один из тестов возвращает вас к 16-байтовому выравниванию стека.На тот момент код работал.Поэтому я пересмотрел свои шаги и начал помещать в стек NULL-данные, а не то, что считал правильными (я выдвигал адреса возврата, чтобы подделать кадр вызова).Это все еще работало - поэтому данные не важны, это должны быть фактические адреса стека.

Я быстро понял, что это было 16-байтовое выравнивание для стека.Ранее я знал только о 8-байтовом выравнивании данных.В этом документе Microsoft объясняются все требования к выравниванию .

Если в x64 не выровнен кадр стека на x64, компилятор может поместить большие (8 или более) данные в неправильные границы выравнивания, когда онпомещает данные в стек.

Отсюда проблема, с которой я столкнулся - код перехвата вызывался со стеком, который не был выровнен по границе 16 байт.

Краткий обзор требований к выравниванию, выраженный в виде размера: выравнивание

  • 1: 1
  • 2: 2
  • 4: 4
  • 8: 8
  • 10: 16
  • 16: 16

Все, что больше 8 байт, выровнено по следующей степени 2.

Я думаю, что код ошибки Microsoft немного вводит в заблуждение.Начальный STATUS_DATATYPE_MISALIGNMENT может быть выражен как STATUS_STACK_MISALIGNMENT, что было бы более полезным.Но затем превращение STATUS_DATATYPE_MISALIGNMENT в ERROR_NOACCESS - это фактически маскирует и вводит в заблуждение относительно того, в чем проблема.Очень бесполезно

Спасибо всем, кто разместил предложения.Даже если я не согласен с предложениями, они побудили меня протестировать в самых разных направлениях (включая те, с которыми я не согласен).

Более подробное описание проблемы несовпадения типов данных написано здесь: 64-битное портирование, погрешность №1!x64 Несовпадение типов данных.

Ответы [ 3 ]

1 голос
/ 15 июня 2010

Единственная причина, по которой 64-битная версия будет иметь значение, заключается в том, что для 64-битных потоков требуется 64-битные выровненные значения.Если threadID не выровнен на 64 бита, вы можете вызвать эту проблему.


Хорошо, идея не в этом.Вы уверены, что можно вызывать CreateThread перед main / WinMain?Это объяснило бы, почему это работает в меню - потому что это после main / WinMain.

Кроме того, я бы трижды проверил время жизни myParam.CreateThread возвращает (это я знаю из опыта) задолго до того, как вызывается функция, которую вы передаете.


Разместите код подпрограммы потока (или всего несколько строк).


У меня неожиданно возникает мысль: вы уверены, что вводите свой 64-битный код в 64-битный процесс?Потому что если у вас есть 64-битный вызов CreateThread и вы пытаетесь внедрить его в 32-битный процесс, работающий под WOW64, могут произойти плохие вещи.


Начинаете серьезно исчерпывать идеи.Сообщает ли компилятор о каких-либо предупреждениях?


Может ли ошибка быть вызвана ошибкой в ​​программе хоста, а не в DLL?Есть некоторый другой код, такой как загрузка DLL, если вы использовали __declspec (импорт / экспорт), который происходит до main / WinMain.Если , то, например, DLLMain содержит ошибку.

0 голосов
/ 03 сентября 2018

Я использую параллельные потоки под окнами для расчетов.Никаких забавных дел, никаких dll-звонков и, конечно, никаких обратных звонков.Следующее работает в 32-битных окнах.Я установил стек для своих вычислений, в пределах области, зарезервированной для моей программы.Все соответствующие данные об областях и начальных адресах содержатся в структуре данных, которая передается в CreateThread в качестве параметра 3. Вызываемый адрес содержит небольшую подпрограмму ассемблера, которая использует эту структуру данных.Действительно, эта подпрограмма находит адрес для возврата в стеке, а затем адрес структуры данных.Нет причин углубляться в это.Он просто работает, и он прекрасно рассчитывает число простых чисел ниже 2 000 000 000, в одном потоке, в двух потоках или в 20 потоках.

Теперь CreateThread в 64 битах не выдвигает адрес структуры данных.Это кажется неправдоподобным, поэтому я покажу вам курящий пистолет, дамп сеанса отладки.enter image description here

В подокне в правом нижнем углу вы видите стек, и есть только адрес возврата, среди моря нулей.Механизм, который я использую для заполнения параметров, переносим между 32 и 64 битами.Ни один другой вызов не демонстрирует разницу между размерами слов.Более того, почему адрес кода будет работать, а не адрес данных?

Суть: можно ожидать, что CreateThread передает параметр данных в стек точно так же, как в 64 битах, как в 32 битах, затемвызов подпрограммы.На уровне ассемблера это не работает таким образом.Если есть какие-то скрытые требования, например, к RSP, которые автоматически заполняются в C ++, это было бы очень неприятно.

PS Нет, проблем с выравниванием 16 байтов нет.Это лежит позади меня.

0 голосов
/ 24 июля 2010

Попробуйте вместо этого использовать _beginthread () или _beginthreadex (), вам не следует использовать CreateThread напрямую.

См. Этот предыдущий вопрос.

...