You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ada绑定OpenGL函数glTexImage2D时出现栈损坏问题排查

调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 01:17:50