Ada绑定OpenGL函数glTexImage2D时出现栈损坏问题排查
我通过SDL加载OpenGL函数地址的逻辑是正常的,其他OpenGL函数调用都用同一个加载子程序没问题,但唯独调用glTexImage2D时出现栈损坏。
规范中的相关代码片段
type GLenum is new Uint32; subtype glTexImage2D_Target is GLenum with Static_Predicate => glTexImage2D_Target in GL_TEXTURE_2D | GL_TEXTURE_RECTANGLE | GL_PROXY_TEXTURE_RECTANGLE; subtype glTexImage2D_Dtype is GLenum with Static_Predicate => glTexImage2D_Dtype in GL_BYTE | GL_UNSIGNED_BYTE; subtype glTexImage2D_InternalFormat is GLenum with Static_Predicate => glTexImage2D_InternalFormat in GL_DEPTH_COMPONENT | GL_DEPTH_STENCIL | GL_RED | GL_RG | GL_RGB | GL_RGBA; subtype glTexImage2D_Format is GLenum with Static_Predicate => glTexImage2D_Format in GL_RED | GL_RG | GL_RGB | GL_BGR | GL_RGBA | GL_BGRA | GL_RED_INTEGER | GL_RG_INTEGER | GL_RGB_INTEGER | GL_BGR_INTEGER | GL_RGBA_INTEGER | GL_BGRA_INTEGER | GL_STENCIL_INDEX | GL_DEPTH_COMPONENT | GL_DEPTH_STENCIL; procedure glTexImage2D(target : glTexImage2D_Target; level : Integer; internalFormat : glTexImage2D_InternalFormat; width : Integer; height : Integer; format : glTexImage2D_Format; dtype : glTexImage2D_Dtype; pixels : System.Address) with Pre => (target /= GL_TEXTURE_RECTANGLE and target /= GL_PROXY_TEXTURE_RECTANGLE) or Level = 0; procedure Set_OpenGL_Subprogram_Address(Addr : in out System.Address; Name : String); glTexImage2D_C_Address : System.Address := System.Null_Address; Could_Not_Load_OpenGL_Subprogram : exception;
体中的相关代码片段
procedure glTexImage2D(target : glTexImage2D_Target; level : Integer; internalFormat : glTexImage2D_InternalFormat; width : Integer; height : Integer; format : glTexImage2D_Format; dtype : glTexImage2D_Dtype; pixels : System.Address) is begin Set_OpenGL_Subprogram_Address(glTexImage2D_C_Address, "glTexImage2D"); declare procedure glTexImage2D_C(target : GLenum; level : GLint; internalFormat : GLint; width : GLsizei; height : GLsizei; border : GLint; format : GLenum; dtype : GLenum; data : System.Address) with Import, Convention => Stdcall, Address => glTexImage2D_C_Address; begin glTexImage2D_C(GLenum(target), GLint(level), GLint(internalFormat), GLsizei(width), GLsizei(height), GLint'(0), GLenum(format), GLenum(dtype), pixels); end; end glTexImage2D; -- 这段加载逻辑没问题,贴出仅供参考 procedure Set_OpenGL_Subprogram_Address(Addr : in out System.Address; Name : String) is begin if Addr = System.Null_Address then Addr := SDL_GL_GetProcAddress(Value(New_String(Name))); if Addr = System.Null_Address then raise Could_Not_Load_OpenGL_Subprogram with Name; end if; end if; end Set_OpenGL_Subprogram_Address;
传入的像素类型
type Uc_Array is array(Integer range <>) of aliased Interfaces.C.unsigned_char;
问题现象
通过GDB调试发现,调用glTexImage2D_C后出现栈损坏:调用前传递的参数为target=3553、level=0、internalformat=6408、width=63、height=63、format=6408、dtype=5121,pixels为有效地址,函数地址也已正确加载非空,但调用后栈指针$sp的值增加了16#24#。调整width和height的值(如设为10或1000),栈指针的变化量始终异常。
OpenGL官方原型
void glTexImage2D( GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * data);
补充说明:目前采用的延迟加载方式较为繁琐,仅为避免调用未加载函数方便调试,其他OpenGL函数用同样的嵌套子程序声明方式均能正常工作,怀疑是数据类型不匹配或调用约定问题导致。
问题原因及解决方法
1. 数据类型不匹配(最可能原因)
Ada的Integer类型大小依赖平台(64位系统上通常为64位),而C中GLint、GLsizei均为32位的int类型。如果你的GLint、GLsizei是基于AdaInteger定义的,64位平台下每个这类参数会占用8字节而非4字节,导致栈参数总大小计算错误,调用后栈指针无法正确恢复。
解决方法:改用Interfaces.C.int作为基础类型定义GL相关整数类型:
with Interfaces.C; use Interfaces.C; type GLint is new Interfaces.C.int; type GLsizei is new Interfaces.C.int;
同时调整Ada层的参数类型,比如将glTexImage2D的level、width、height改为GLint/GLsizei,避免不必要的类型转换,确保参数大小与C端完全一致。
2. 调用约定不匹配
OpenGL函数的调用约定分平台:
- Windows平台:使用
__stdcall(对应Ada的Convention => Stdcall) - Linux/macOS平台:使用
__cdecl(对应Ada的Convention => C)
如果当前运行平台不是Windows,但你在glTexImage2D_C的声明中用了Convention => Stdcall,会导致栈清理逻辑错误,进而引发栈损坏。
解决方法:根据目标平台调整调用约定,可通过条件编译适配多平台:
procedure glTexImage2D_C(...) with Import, Convention => (if System.OS_Family = Windows then Stdcall else C), Address => glTexImage2D_C_Address;
内容的提问来源于stack exchange,提问作者Devsman

