我们遇到了一款名为 Motorla RAZR i 的奇怪设备,它具有 x86 CPU 和 Android 版本 4.1.2(大约 2013 年)。
此设备有时会在本机代码中的 GL 操作中崩溃。我们最终将这个错误减少到只有几行无辜的函数调用:
void stackCorrupt() {
const EGLint configAttrs[] = {EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE};
const EGLint contextAttrs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
const EGLint const surfaceAttrs[] = {EGL_WIDTH, 16, EGL_HEIGHT, 16, EGL_NONE};
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, 0, 0);
EGLint numConfigs;
EGLConfig config;
eglChooseConfig(display, configAttrs, &config, 1, &numConfigs);
EGLContext context = eglCreateContext(display, config, NULL, contextAttrs);
EGLSurface surface = eglCreatePbufferSurface(display, config, surfaceAttrs);
// commenting out this call makes the stack clean again
eglMakeCurrent(display, surface, surface, context);
// only to know that the context is good
__android_log_print(ANDROID_LOG_DEBUG, "native-lib", "GL_RENDERER %s", glGetString(GL_RENDERER));
// Cleanup
eglDestroySurface(display, surface);
eglDestroyContext(display, context);
eglTerminate(display);
}
编译时-fstack-protector-strong
(如果我理解正确,这是默认值),在特定设备上调用此函数总是会导致
F /system/bin/app_process: stack corruption detected: aborted
但是,如果我使用-fno-stack-protector
,执行将继续,就好像没有发生任何不好的事情一样。这可能(如果我正确理解情况)成为任何进一步执行的陷阱。更危险(因为不明确),将-flto添加到 CFLAGS 会隐藏崩溃。
在上面的代码中,为了简洁起见,我删除了错误检查。实际上,所有调用都会成功。此外,清理部分不会更改eglMakeCurrent()的堆栈抖动行为。
我真的很高兴创建一些沙箱来安全地执行此类代码,而不会危及我的应用程序的其余部分。
附言
我在 Android 上遇到了另一个奇怪的堆栈损坏案例,使用 glGetProgramiv 时“检测到堆栈损坏”,但似乎没有提出解决方案。
在测试用例中,但不是在实际应用程序中,egl 上下文的释放会有所帮助:我在Cleanup
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)
之前调用。反汇编显示-flto关闭堆栈保护器。这不应该是一个很大的失望:如果我们的目标是优化调用,浪费时间检查是愚蠢的吗?