这段代码是否包含隐藏的错误?

发布于 2024-12-12 21:45:13 字数 1440 浏览 2 评论 0原文

以下代码:

  • 使用 gcc 版本 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5/32bits)
  • 编译时运行良好 使用 MSVC10 (Win7/32bits) 编译时运行良好
  • 使用 gcc 版本 4.5.2 运行时崩溃 (Win7 上的 MinGW) /32bits)

ma​​in.cpp

# include <iostream>
# include <csetjmp>
# include <stdexcept>

using namespace std ;

void do_work(jmp_buf context)
{
    try
    {
        throw runtime_error("Ouch !") ;
    }
    catch(exception & e)
    {
    }

    longjmp(context, -1) ;                        //BP1
}

int main(int, char *[])
{
    jmp_buf context ;

    try
    {
        if( setjmp(context) != 0 )
        {
            throw runtime_error("Oops !") ;       //BP2
        }

        do_work(context) ;
    }
    catch(exception & e)
    {
        cout << "Caught an exception saying : " << e.what() << endl ;
    }
}

我尝试调试它,但程序的行为很奇怪。有时我可能会越过第一个断点 (BP1),然后在 BP2 处崩溃,有时控制永远不会到达 BP1,就像程序陷入无限循环一样。以我的调试能力,我无法说更多。

这段代码是我能得到的最小代码,它表现出了 MinGW 4.5 的奇怪行为。我还注意到:

  • 如果我用其内容替换 do_work 函数调用,程序运行正常。
  • 如果我删除 do_work 内的 try{ ... } catch(...){ } 块,程序运行正常。
  • 优化标志无效(总是崩溃)。

我知道 C++ 代码中的 setjmp/longjmp 问题,但我被迫使用它来与一些旧版 C 代码交互。

我的问题:

  • 这是一个有缺陷/有缺陷/错误的代码吗?或者 MinGW 4.5 对代码处理不当? (指责该工具是严厉和自以为是的,但我怀疑其中的某些设置)。

感谢您的任何建议。

如有必要,请重新标记。

The following code :

  • Runs fine when compiled with gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5/32bits)
  • Runs fine when compiled with MSVC10 (Win7/32bits)
  • Crashes when running with gcc version 4.5.2 (MinGW on Win7/32bits)

main.cpp :

# include <iostream>
# include <csetjmp>
# include <stdexcept>

using namespace std ;

void do_work(jmp_buf context)
{
    try
    {
        throw runtime_error("Ouch !") ;
    }
    catch(exception & e)
    {
    }

    longjmp(context, -1) ;                        //BP1
}

int main(int, char *[])
{
    jmp_buf context ;

    try
    {
        if( setjmp(context) != 0 )
        {
            throw runtime_error("Oops !") ;       //BP2
        }

        do_work(context) ;
    }
    catch(exception & e)
    {
        cout << "Caught an exception saying : " << e.what() << endl ;
    }
}

I tried debugging it but the program behaves strangely. Sometimes I could get past the first breakpoint (BP1), then crash at BP2, and sometimes control never reachs BP1, like if the program is stuck in an infinite loop. I cannot say more with my debugging skills.

This code is the minimal I could get that exhibits the strange behavior with MinGW 4.5. I also noticed that :

  • If I replace the do_work function call by its content, the program runs fine.
  • If I remove the try{ ... } catch(...){ } block inside do_work, the program runs fine.
  • Optimization flags have no effect (always crashing).

I'm aware of the setjmp/longjmp issues in C++ code, but I'm forced to use it to interface with some legacy C code.

My question :

  • Is this a faulty/buggy/erroneous code ? Or is MinGW 4.5 mishandling the code ? (It is harsh and presumptuous to blame the tool, but I suspect some settings in it).

Thanks for any advice.

Please retag if necessary.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

你的背包 2024-12-19 21:45:15

longjmpsetjmp 是 C 函数,使用 C++ 异常处理语义和对象销毁语义调用它们是未定义的行为,这意味着它是否有效取决于实现(您的测试表明,SEH 堆栈展开语义会破坏事物,具体取决于所使用的类型,iirc 您的工作正在使用 dwarf2)

longjmp and setjmp are c functions, invoking them with C++ exception handling semantics and object destruction semantics is undefined behavior, which means its up to the implementation whether it works or not (your testing shows this, the SEH stack unwind semantics break things, depending on the type used, iirc your working ones are using dwarf2)

木緿 2024-12-19 21:45:14

Unix 上的 longjmp(3) 手册页显示:

longjmp() 例程不能在以下例程之后调用:
调用 setjmp() 例程返回

我认为它解释了您的担忧“有时控制永远不会达到 BP1”。
我不认为“运行良好”是可靠的判断。我宁愿期望它随机运行良好,并且通常会弄乱堆栈。

在将 longjmp/setjmp 与 C++ 异常混合使用时,应考虑一些明确的建议,以避免崩溃和未定义的行为:

  • 不要在 C++ 程序中使用 setjmp/longjmp。
  • 如果您在可能发生异常的程序中使用 setjmp/longjmp 函数,只要它们不交互,您就是安全的。
  • 切勿在 try 子句和 catch 子句中进行 longjmp 进出。
  • 切勿在自动对象的初始化点上进行 longjmp。
  • 切勿使用 longjmp 破坏自动对象的 pasy 点,特别是当析构函数不平凡时。
  • 切勿从信号处理程序中抛出。
  • 切勿从嵌套信号处理程序调用 longjmp。
  • 只要在 X 处抛出异常并在 X 处捕获异常会产生相同的效果,从位置 X 到位置 Y 的 longjmp 行为就保持可预测和有效。
  • 如果将 setjmp/longjmp 与异常混合使用,则不要期望可移植性。
  • 相反,请参阅您正在使用的编译器文档中的相关详细信息。例如,如果您使用 Visual C++,请阅读使用 setjmp/longjmp

问题提到处理用 C++ 编写的程序中的遗留 C 代码。在审查 Boost 库之一期间,对 jpeg 库中的 sjlj 问题进行了有趣的讨论。讨论很长,但这里是带有推荐选项的要点

The longjmp(3) man page on Unix says:

The longjmp() routines may not be called after the routine which
called the setjmp() routines returns

I think it explains explains your concern that "sometimes control never reachs BP1".
I don't think "runs fine" is reliable judgement. I'd rather expect it randomly runs fine and generally is messing up with stack.

There are a few clear recommendations that should be taken into account while working mixing longjmp/setjmp with C++ exceptions in order to avoid crashes and undefined behaviour:

  • Do not use setjmp/longjmp in C++ programs.
  • If you use setjmp/longjmp functions in program where exceptions can occur, you are safe as long as they do not interact.
  • Never longjmp in or out of try clause and catch clause.
  • Never longjmp over point of initialization of automatic objects.
  • Never longjmp pasy point of destruction automatic objects, especially if destructor is non-trivial.
  • Never throw from signal handler.
  • Never invoke longjmp from nested signal handler.
  • Behaviour of longjmp from location X to location Y stays predictable and valid as long as if exception thrown at X and caught at X would have the same effect.
  • If you mix setjmp/longjmp with exceptions, do not expect portability.
  • Instead, refer to relevant details in documentation compiler you are using. For example, if you use Visual C++, read Use setjmp/longjmp

The question mentions dealing with legacy C code in programs written in C++. There has been an interesting discussion on sjlj issues in jpeg library during review of one of Boost libraries. The discussion was long but here is essence with recommended options.

愁以何悠 2024-12-19 21:45:14

C++ 仅对 longjmp() 的使用做了一个额外的限制:

如果任何自动对象将被抛出的异常破坏,并将控制权转移到程序中的另一个(目标)点,则调用在将控制转移到同一(目标)点的抛出点处的 longjmp(jbuf, val) 具有未定义的行为。 (18.7)

您似乎意识到了这一点,但您的程序没有这样做。我投票编译器缺陷。

C++ only makes one additional restriction on the use of longjmp():

If any automatic objects would be destroyed by a thrown exception transferring control to another (destination) point in the program, then a call to longjmp(jbuf, val) at the throw point that transfers control to the same (destination) point has undefined behavior. (18.7)

You seem to be aware of this, and your program doesn't do it. I vote compiler defect.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文