探究不同架构下向 C 函数传递过少寄存器参数的后果,安腾架构更严格!
微软开发博客导航
微软开发博客提供了丰富的内容导航,涵盖开发者相关、技术相关、编程语言相关、.NET 相关、平台开发相关、数据开发相关等多个方面。开发者相关包括面向开发者的微软、Visual Studio、Visual Studio Code 等;技术相关有 DirectX、微软代理框架;编程语言相关涉及 C++、C#、F# 等;.NET 相关包含所有 .NET 文章、.NET MAUI、AI 等;平台开发相关有 #ifdef Windows、微软铸造厂等;数据开发相关包括 Azure Cosmos DB、Azure Data Studio 等。
探究传递过少寄存器参数的后果
在探索 Windows 上各种处理器的调用约定时,我们发现很多情况下部分参数是通过寄存器传递的。假设存在一个接受两个参数的函数,若第一个参数为正数,该函数会忽略第二个参数。那么只传递一个参数(如传递零)来调用这个函数,会怎样呢?从形式上来说,C 和 C++ 语言规定,以错误的参数数量调用函数,其行为是未定义的。
栈传递参数过少的问题
如果在栈上传递的参数过少,且采用被调用者清理栈的调用约定,被调用者会从栈上清理过多的字节,导致栈不平衡,并可能导致内存损坏。即使不是被调用者清理栈的调用约定,被调用函数也会认为参数的内存存在,并可能将其用作临时存储空间,从而导致调用函数的栈帧出现内存损坏。
寄存器传递参数不足的情况
若参数通过寄存器传递,而传递的参数不足,在大多数处理器上,被调用函数会尝试使用该寄存器,并读取其中未初始化的值。但安腾(Itanium)处理器是个例外,它有“非有效事物”(Not a Thing,NaT)位,附加到每个通用寄存器,用于指示该寄存器是否包含有效值。如果未初始化的输出寄存器恰好是早期失败推测遗留下来的 NaT,被调用函数可能会在使用该寄存器做其他事情之前,将该值保存到栈上。
安腾架构的特殊情况
在安腾架构中,函数调用机制是架构层面的。调用函数会声明输出寄存器的数量,且这些寄存器在进入被调用函数时会重新编号。如果向函数传递的参数过少,默认栈帧包含的寄存器数量比函数预期的要少。从架构层面来说,读取当前帧之外的栈寄存器结果是“未定义的”,可能会引发处理器异常;写入当前帧之外的栈寄存器必须引发非法操作错误。所以,安腾架构更严格执行编程规则,要确保向函数传递正确数量的参数。
作者介绍
雷蒙德参与 Windows 的发展已有 30 多年。2003 年,他创建了“旧闻新谈”网站,其受欢迎程度超出想象。该网站催生了一本书,书名也叫《旧闻新谈》(Addison Wesley,2007 年)。他偶尔会在 Windows 开发文档的 Twitter 账号上分享一些没有实际用处的故事。
评论情况
有两条评论,肯·塞特尔指出修改后的第二个示例在第一个 `if` 语句块中应该返回 `b` 而不是 `c`;约书亚·哈德森的评论未完整显示。
