为什么局部静态对象的初始化使用隐藏的保护标志? [英] Why does initialization of local static objects use hidden guard flags?

查看:81
本文介绍了为什么局部静态对象的初始化使用隐藏的保护标志?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C ++中,本地静态对象在第一次需要它们时初始化一次(如果初始化有副作用,则相关):

Local static objects in C++ are initialized once, the first time they are needed (which is relevant if the initialization has a side effect):

void once() {
    static bool b = [] {
        std::cout << "hello" << std::endl; return true;
    } ();
}

once将在第一次调用它时显示"hello",但如果再次调用它则不会显示.

once will print "hello" the first time it is called, but not if it is called again.

我将这种模式的一些变体放入了 Compiler Explorer 中,并注意到所有著名的实现(GCC,Clang,ICC,VS)本质上是做同样的事情:创建一个隐藏变量guard variable for once()::b,并检查是否需要这次"初始化主变量;如果是这样,它将被初始化,然后设置防护,并且下次它将不会跳到初始化代码.例如(通过调用extern bool init_b();代替lambda来最小化):

I've put a few variations of this pattern into Compiler Explorer and noticed that all of the big-name implementations (GCC, Clang, ICC, VS) essentially do the same thing: a hidden variable guard variable for once()::b is created, and checked to see whether the primary variable needs to be initialized "this time"; if it does, it gets initialized and then the guard is set, and next time it won't jump out to the initialization code. e.g. (minimized by replacing the lambda with a call to extern bool init_b();):

once():
        movzx   eax, BYTE PTR guard variable for once()::b[rip]
        test    al, al
        je      .L16
        ret
.L16:
        push    rbx
        mov     edi, OFFSET FLAT:guard variable for once()::b
        call    __cxa_guard_acquire
        test    eax, eax
        jne     .L17
        pop     rbx
        ret
.L17:
        call    init_b()
        pop     rbx
        mov     edi, OFFSET FLAT:guard variable for once()::b
        jmp     __cxa_guard_release
        mov     rbx, rax
        mov     edi, OFFSET FLAT:guard variable for once()::b
        call    __cxa_guard_abort
        mov     rdi, rbx
        call    _Unwind_Resume

...从带有-O3的GCC 6.3开始.

...from GCC 6.3 with -O3.

这不是没有道理的,而且我知道实际上条件一致时,条件跳转几乎是自由的.但是,我的直觉仍然是通过 un 有条件地跳转到初始化代码来实现这一点,而初始化代码的最后一个动作是使用nop指令覆盖了原始跳转.不一定在每个平台上都是一个选择,但是x86系列在您可以读取或写入的内容以及位置方面似乎相当宽松.

This isn't unreasonable, and I know that in practice conditional jumps are close to free anyway when the condition is consistent. However, my gut feeling would still have been to implement this by unconditionally jumping to the initialization code, which as its last action overwrites the originating jump with nop instructions. Not necessarily an option on every platform, but the x86 family seems quite liberal about what you can read or write, and where.

这个没有任何主流编译器使用的看似简单的想法有什么问题? (或者我只需要更努力地尝试我的示例?)

What's so wrong with this apparently-simple idea that no mainstream compiler uses it? (Or do I just need to try harder with my examples?)

推荐答案

在大多数现代操作系统上,修改程序加载的代码都会导致问题.这既可能导致性能问题(未修改的代码可以在某些系统上的dll的许多实例之间共享页面),也可能导致安全问题(防止使用可执行的空间保护技术).

On most modern operating systems modifying the code loaded with the program causes issues. This can both cause performance issues (Unmodified code can share pages between many instances of a dll on some systems), and security issues (preventing the use of executable space protection technologies).

这篇关于为什么局部静态对象的初始化使用隐藏的保护标志?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆