x86 模拟器团队发现代码坏到在模拟过程中直接把它修好的那次

平台 / 驱动程序对坏代码的变通处理

  • 许多评论将模拟器“修复”坏代码与现代层如 Proton/Wine、GPU 驱动程序和浏览器相提并论,这些层都会针对每个应用程序提供大量特例和黑客式修补。
  • GPU 驱动程序通常会通过可执行文件名或显式 API 来识别游戏,然后应用游戏专用的优化或正确性变通,甚至有时会为了基准测试而降低画质。
  • 这被认为很脆弱:针对某个标题(例如“hl2.exe”)的优化可能会提升性能、破坏其他游戏,或者依赖未定义行为。
  • 浏览器引擎也有类似的按站点“特例”表;操作系统和运行时也曾提供过应用专用 shim(例如针对 SimCity)。

基准测试、作弊与竞争性优化

  • 几个例子提到驱动程序会为流行基准测试“作弊”(如 Quake 3、DirectX 演示),在检测到基准测试路径时降低画质或跳过工作。
  • 厂商通常会尽量通知游戏开发者修复正确性 bug,但往往先在驱动中发布变通方案;大型工作室会得到更多关注。
  • 仅影响性能的调整有时会留在驱动中作为竞争优势,而不是向上游提交。

I/O 与库病态行为

  • 多个故事强调了病态的 I/O 使用:极小的读取(以 1 字节或 4 字节块进行的 fread/ReadFile)、OS 组件发出大量小型系统调用,以及应用程序实际上击穿了缓冲层。
  • 一位评论者指出,某个游戏对 fread(data, 1, 65536, f) 的使用暴露了一个糟糕的 stdlib 实现或未缓冲模式,导致 65k 次字节级读取;为修复性能,hook 中需要一个缓存层。
  • 关于这种 fread 用法在语义上是否错误存在争论(大多数人认为没问题;问题在实现)。

栈探测、守卫页与未初始化内存

  • 讨论涵盖了 Windows 上的栈探测:较大的栈帧必须逐页触碰,以触发守卫页并检测溢出。
  • 有人指出,许多现实中的栈分配只是“尽人事听天命”,但 Windows 编译器会为较大的栈帧添加探测。
  • 几条评论描述了会用哨兵模式或零值预填充栈内存的自定义或系统功能(例如编译器开关、自动栈初始化),以检测未初始化变量 bug。

循环展开与代码膨胀

  • 有人为循环展开辩护,认为这是一种有效优化,但很多人指出其收益递减以及代码尺寸成本,尤其是当巨大的展开块带来缓存惩罚时。
  • “用 256 KB 的代码初始化 64 KB” 这个例子被广泛视为过度优化和糟糕编译器行为的极端案例。