代码之家  ›  专栏  ›  技术社区  ›  Stephen Kellett

CreateThread()在64位Windows上失败,在32位Windows上工作。为什么?

  •  5
  • Stephen Kellett  · 技术社区  · 14 年前

    操作系统:Windows XP 64位,SP2。

    我有个不寻常的问题。我正在将一些代码从32位移植到64位。32位代码工作正常。但是当我为64位版本调用CreateThread()时,调用失败。我有三个地方失败了。2调用CreateThread()。1调用调用createThreadEx()的beginthreadex()。

    所有三个调用都失败,错误代码为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);
    

    如果我传递NULL而不是&threadID,它仍然失败。

    如果我将NULL作为myParam传递,它仍然失败。

    如果有人以前看过或有任何想法,请告诉我。

    谢谢你的阅读。

    简单回答:x64上的堆栈帧需要16字节对齐。

    更长的答案:

    向堆栈中添加额外数据会更改堆栈帧对齐方式。堆栈16的一个字节或更快或更晚的帧对齐。在这一点上,代码起作用了。所以我回顾了我的步骤,开始将空数据放入堆栈,而不是我认为正确的值(我一直在推送返回地址来伪造调用帧)。它仍然有效-所以数据并不重要,它必须是实际的堆栈地址。

    我很快意识到它是16字节的堆栈对齐。以前我只知道8字节的数据对齐。这个 microsoft document explains all the alignment requirements .

    对齐要求的快速摘要,用大小表示:对齐

    • 1比1
    • 4分4秒
    • 8点8分
    • 10:16
    • 16:16

    任何大于8字节的内容都将在2的下一次幂边界上对齐。

    我认为微软的错误代码有点误导人。初始STATUS_DATATYPE_unalignment可以表示为STATUS_STACK_unalignment,这将更有帮助。但是,将状态数据类型不一致变成错误错误错误,这实际上掩盖和误导了问题所在。很没用。

    在这里对数据类型不对中问题进行了更详细的描述: 64 bit porting gotcha #1! x64 Datatype misalignment.

    3 回复  |  直到 14 年前
        1
  •  1
  •   Puppy    14 年前

    64位之所以有区别,唯一的原因是64位上的线程需要64位对齐的值。如果threadID没有64位对齐,则可能导致此问题。


    另外,我还要三次检查myParam的生命周期。CreateThread在调用传入的函数之前很久就返回(我从经验中知道)。



    我突然想到:你确定你正在将64位代码注入64位进程吗?因为如果你有一个64位的CreateThread调用,并试图将它注入到一个运行在WOW64下的32位进程中,可能会发生糟糕的事情。


    开始真的没有主意了。编译器是否报告任何警告?


    错误是否是由于宿主程序中的错误而不是DLL引起的?还有一些其他代码,例如,如果您使用了\uu declspec(import/export),则加载一个DLL,它发生在main/WinMain之前。如果

        2
  •  0
  •   youfu    4 年前

    我今天碰到了这个问题。我检查了每一个论点 _beginthread / CreateThread / NtCreateThread

    API Monitor Screenshot


    那么,在哪里呢 STATUS_DATATYPE_MISALIGNMENT

    前几行 NtCreateThread公司 验证从用户模式传递的参数。

    ProbeForReadSmallStructure (ThreadContext, sizeof (CONTEXT), CONTEXT_ALIGN);
    

    #define CONTEXT_ALIGN   (sizeof(ULONG))
    

    对于amd64

    #define STACK_ALIGN (16UI64)
    ...
    #define CONTEXT_ALIGN STACK_ALIGN
    

    ThreadContext 指针未与16字节对齐, NtCreateThread公司 会回来的 .

    创建新线程 CreateRemoteThread )分配 线程上下文 从堆栈,并没有采取任何特殊措施来保证对齐要求得到满足。如果你的每一段代码都遵循microsoftx64调用约定,事情就会顺利进行,不幸的是,这对我来说并不正确。

    PS:同样的代码可以在更新的Windows上运行(比如Vista和更新版本)。但我没有查。我在Windows Server 2003 R2 x64上遇到此问题。

        3
  •  -1
  •   Albert van der Horst    5 年前

    我从事的是在windows下使用并行线程 用于计算。没有有趣的事情,没有dll调用,当然 有关区域和起始地址的所有相关数据都包含在 使用这种数据结构。 然后是数据结构的地址。 这是没有道理的。它只是工作和计算 两个螺纹或20个螺纹。

    现在64位的CreateThread不推送数据的地址 结构。这似乎难以置信,所以我给你看了那把冒烟的枪, 调试会话的转储。 enter image description here

    在右下角的子窗口中,您可以看到堆栈,并且 我用来填充参数的机制在32到64位之间是可移植的。

    底线是:CreateThread在64位和32位中以相同的方式传递堆栈上的数据参数,然后执行子例程调用。在汇编程序级别,它不是那样工作的。如果对C++中自动填充的RSP有任何隐藏的要求,那将是非常讨厌的。

    P、 不,不存在16字节对齐问题。那在我身后很久了。

        4
  •  -2
  •   Community CDub    7 年前

    尝试使用_beginthread()或_beginthreadex(),而不应直接使用CreateThread。

    See this previous question.