将setJMP和longJMP与本地JMP_BUF一起使用

发布于 2025-01-27 13:20:33 字数 1908 浏览 4 评论 0原文

如果本地jmp_buf实际上由寄存器而不是堆栈内存表示,那么setJMPlongjmp有可能导致内容的内容setJmplongjmp返回时,本地jmp_buf是不确定的吗?


建议的重复是一个setJmp()调用多次允许多次执行longjmp()?在全局变量的上下文中问。有人建议,因为答案解释了该变量不会以阻止其随后被称为的方式进行修改,这也足够回答了局部变量的问题。
,但是 ,局部变量的处理与全局变量不同。特别是,如果本地jmp_buf实际上保存在寄存器中,而不是内存中,则在longjmp之后的恢复可能不会呈现可重复使用的jmp_buf varible。


作为一项学术练习,我试图使用setJMP作为goto的替代品。为了使循环替换本地为函数,jmp_buf使用的也是局部变量。

void foo (int n) {
    jmp_buf jb;
    volatile int i = 0;
    setjmp(jb);
    if (i < n) {
        do_stuff(i);
        longjmp(jb, ++i);
    }
}

感谢 vinipsmaker 指出使用setjmp具有某些限制,这使得有一定的限制将其返回值分配给变量不确定。

我知道,在setJmp调用和longjmp调用之间已修改的非挥发性本地变量在longjmp。但是,我对本地jmp_buf变量本身感到好奇,尤其是在jmp_buf变量的情况下,寄存器代表寄存器而不是堆栈上的内存。

目前尚不清楚longjmp本身是否可以被视为可以修改本地jmp_buf变量的东西,以及当setjmp返回时,这是否意味着其内容是未指定的。致电longjmp之后。

我以为我可以通过声明jbvolatile来轻松派遣问题,但是这引起了警告(我将其视为错误):

... error: passing argument 1 of ‘_setjmp’ discards ‘volatile’ qualifier from pointer target type [-Werror=discarded-qualifiers]
     setjmp(jb);
            ^~

另外,setJMP 不说它是否在设置jmp_buf之后还是在设置jmp_buf之前保存寄存器值。

如果我需要关注它,我可以创建jmp_buf的挥发性副本,然后复制其内容。但是,如果不需要,我想避免这种情况。

In the case that a local jmp_buf is actually represented by registers rather than stack memory, is it possible for setjmp or longjmp to cause the contents of the local jmp_buf to be indeterminate when setjmp returns from a longjmp?


The suggested duplicate Is it allowed to do longjmp() multiple times for one setjmp() call? asks in the context of a global variable. It was suggested since the answer explains that the variable is not modified in a way that would prevent it from being subsequently called, that sufficiently answers the question for a local variable too.
However, treatment of a local variable differs from a global variable. In particular, if the local jmp_buf variable is actually held in registers and not memory, restoration after longjmp may not render a reusable jmp_buf variable.


As an academic exercise, I was attempting to use setjmp as a substitute for goto. To keep the loop replacement local to the function, the jmp_buf used is also a local variable.

void foo (int n) {
    jmp_buf jb;
    volatile int i = 0;
    setjmp(jb);
    if (i < n) {
        do_stuff(i);
        longjmp(jb, ++i);
    }
}

Thanks to vinipsmaker for pointing out that use of setjmp has certain restrictions, which make assigning its return value to a variable undefined.

I understand that non-volatile local variables that have been modified between the setjmp call and the longjmp call are unspecified after longjmp. However, I was curious about the local jmp_buf variable itself, particularly in the case where the jmp_buf variable is represented by registers rather than memory on the stack.

It is unclear if longjmp itself can be considered something that may modify the local jmp_buf variable, and whether this means its contents are unspecified when setjmp returns after the call to longjmp.

I thought I could easily dispatch the issue by declaring jb to be volatile, but this triggered a warning (which I treat as an error):

... error: passing argument 1 of ‘_setjmp’ discards ‘volatile’ qualifier from pointer target type [-Werror=discarded-qualifiers]
     setjmp(jb);
            ^~

Also, the specification of setjmp does not speak to whether it is saving the register values as they would be after setting the jmp_buf or before setting the jmp_buf.

If I need to be concerned about it, I can create a volatile copy of the jmp_buf and copy its contents around. But, I'd like to avoid that if it isn't required.

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

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

发布评论

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

评论(4

-柠檬树下少年和吉他 2025-02-03 13:20:33

c11标准节第3点状态:

所有可访问的对象都有值,而抽象机的所有其他组件都具有状态,截至longjmp函数。
自动存储持续时间的对象是包含该功能的函数
相应的setJmp没有volatile qualified类型的宏的调用
并已在setJMP Invocation和longjmp调用之间进行了更改
不确定。

您的JMP_BUF对象未更改setJMP(JB)longjmp(jb,++ i)。呼叫之间更改的唯一变量是i,如标准所建议的那样,该变量被声明为volatile

因此,要回答您的问题,longjmp本身不能“修改本地jmp_buf [以这样的方式],当setJmp 返回”,但通过其他方式在两个调用之间修改JMP_BUF肯定会导致麻烦。

The C11 standard section §7.13.2.1 point 3 states:

All accessible objects have values, and all other components of the abstract machine have state, as of the time the longjmp function was called, except that the values of
objects of automatic storage duration that are local to the function containing the
invocation of the corresponding setjmp macro that do not have volatile-qualified type
and have been changed between the setjmp invocation and longjmp call are
indeterminate
.

Your jmp_buf object is not changed between setjmp(jb) and longjmp(jb, ++i). The only variable which is changed between the calls is i, which is declared volatile, as the standard suggests.

So, to answer your question, longjmp cannot by itself "modify the contents of the local jmp_buf [in such a way] that would cause its contents to be undefined when setjmp returns", but modifying the jmp_buf between the two calls through other means could definitely cause trouble.

败给现实 2025-02-03 13:20:33

经过几天的研究,我同意@jxh的答案(标准还不够清楚)。但是,我应该在这里添加几件事。首先,您的程序不能保证出于另一个原因而工作。根据ISO/IEC 9899:2011§7.13.1.1.4¶4:

SETJMP宏的调用仅在以下上下文之一中出现:

  • 选择或迭代语句的整个控制表达;
  • 一个关系或平等操作员的一个操作数,另一个操作数是整数常数表达式,结果表达式是选择或迭代语句的整个控制表达;
  • 一元的操作数!算子产生的表达是选择或迭代语句的整个控制表达;或
  • 表达式语句的整个表达式(可能铸造为void)。

IOW,您可能不会将setJmp()的结果分配给变量。

将此事暂时搁置一会儿,我开始怀疑我们 是否可以进行更改,以便您的代码变得合法(假设提出的最弱的保证是jmp_buf首先<之后变得无效代码> longjmp())。我想到了以下内容:

#include <setjmp.h>

void foo(int n)
{
  jmp_buf env;
  volatile int i = 0;
  while (setjmp(env) != 0);
  if (i < n) {
    do_stuff(i++);
    longjmp(env, 1);
  }
}

After several days researching the topic, I agree with @jxh's answer (standard isn't clear enough). However, there are a couple of things I should add here. First, your program is not guaranteed to work for another reason already. According to ISO/IEC 9899:2011 §7.13.1.1 ¶4:

An invocation of the setjmp macro shall appear only in one of the following contexts:

  • the entire controlling expression of a selection or iteration statement;
  • one operand of a relational or equality operator with the other operand an integer constant expression, with the resulting expression being the entire controlling expression of a selection or iteration statement;
  • the operand of a unary ! operator with the resulting expression being the entire controlling expression of a selection or iteration statement; or
  • the entire expression of an expression statement (possibly cast to void).

IOW, you may not assign the result of setjmp() to a variable.

Leaving this matter aside for a moment, I started to wonder whether we could make changes so your code becomes legal (assuming the weakest guarantee posed which is jmp_buf becoming invalid after first longjmp() on it). I came up with the following:

#include <setjmp.h>

void foo(int n)
{
  jmp_buf env;
  volatile int i = 0;
  while (setjmp(env) != 0);
  if (i < n) {
    do_stuff(i++);
    longjmp(env, 1);
  }
}
美煞众生 2025-02-03 13:20:33

没关系。

相关说明,您不需要volatilei上,因为它是由setjmp()分配给的。

在非常仔细阅读longjmp()和我的k&amp; rc副本的人页面上,jb的内容仅在您的功能正文中无效,意思是如果有第二个调用longjmp(),它将看到jb的有效视图。在可共同的假设中,有效代码不会在较新的标准版本中变得无效,因此今天仍然适用。

tl; dr您无需标记类型JMP_BUF volatile的变量。

That's fine.

On a related note, you don't need volatile on i because it's assigned to by setjmp().

On a very careful reading of the man page for longjmp() and my copy of K&R C, the contents of jb are only invalid within the body of your function, meaning if there were a second call to longjmp(), it would see a valid view of jb. Under the resaonable assumption that valid code does not become invalid in newer standard versions, this will still apply today.

TL;DR you don't need to mark variables of type jmp_buf volatile.

征棹 2025-02-03 13:20:33

tl; dr 由于标准尚不清楚,最好将本地jmp_buf视为不确定的值之后本地longjmp

ISO/IEC 9899:2018§17.13.1.1.1¶2描述了setJMP 的行为,并且描述了返回时发生的情况。

setJmp宏将其调用环境保存在其jmp_buf参数中,以稍后使用longjmp function。

...

如果返回来自直接调用,则setJMP宏将返回值零。如果返回是从调用到longjmp函数的,则setJmp宏返回非零值。

我们推断出从setJMP成功返回的导致初始化jmp_buf参数。但是,没有提及初始化是否考虑了具有自动存储持续时间的jmp_buf本身(因此,本身可以用寄存器而不是内存表示)。

ISO/IEC 9899:2018§7.13.2.1¶3描述了longjmp的行为,措辞与 marko

所有可访问的对象都有值,而抽象机的所有其他组件 254)具有状态,
截至longjmp函数,除了自动存储的对象值之外
持续时间是包含调用相应setJmp宏的函数的本地持续时间
没有挥发性合格类型,并且已在setJMP调用和
longjmp调用不确定。


254)这包括但不限于浮点状态标志和打开文件的状态。

但是,之间单词的含义在某种程度上难以捉摸。该标准可以明确指定 之间的上下文在setJmp完成之后。例如,措辞可能已经说过:

...在setJmp 返回 longjmp调用不确定的之间。

当前的措辞表明,应该将调用setJmp本身作为可能触发不确定条件的东西。

但是,longjmp返回的语义可能涵盖了此问题。 ISO/IEC 9899:2018§17.13.2.1¶4州:

longjmp完成,线程执行继续,好像是相应的调用
setJMP宏刚刚返回了val指定的值。 ...

可以将此句子解释为意味着setJMP的调用语义是从直接调用返回还是从longjmp函数返回的情况。也就是说,setJMP的返回表示JMP_BUF参数是初始化的,可以由另一个longjmp使用。但是,这尚不清楚。在最有限的解释中,就像子句仅说明setJMP返回的值,而不是调用本身。

由于语义是模棱两可的,因此可以将JMP_BUF对象值视为不确定时从longjmp返回时。

TL;DR Since the standard isn't clear, it is better to treat the value of a local jmp_buf as indeterminate after a local longjmp.

ISO/IEC 9899:2018 §17.13.1.1 ¶2 describes the behavior of setjmp, and ¶3 describes what happens on return.

The setjmp macro saves its calling environment in its jmp_buf argument for later use by the longjmp function.

...

If the return is from a direct invocation, the setjmp macro returns the value zero. If the return is from a call to the longjmp function, the setjmp macro returns a nonzero value.

We infer that a successful return from setjmp results in an initialized jmp_buf argument. However, there is no mention if the initialization takes into account of the jmp_buf itself having automatic storage duration (and so, itself could be represented by registers rather than by memory).

ISO/IEC 9899:2018 §7.13.2.1 ¶3 describes the behavior of longjmp, and is worded the same as the 2011 text cited by Marko:

All accessible objects have values, and all other components of the abstract machine254) have state,
as of the time the longjmp function was called, except that the values of objects of automatic storage
duration that are local to the function containing the invocation of the corresponding setjmp macro
that do not have volatile-qualified type and have been changed between the setjmp invocation and
longjmp call are indeterminate.


254)This includes, but is not limited to, the floating-point status flags and the state of open files.

However, the meaning of the word between is somewhat elusive. The standard could have explicitly specified the context of between to mean after setjmp completed. For example, the wording could have stated:

... changed between the setjmp return and longjmp call are indeterminate.

The current wording suggests that one should include the invocation of setjmp itself as something that may trigger the indeterminate condition.

There is a possibility that the semantics of the return of longjmp covers for this problem, however. ISO/IEC 9899:2018 §17.13.2.1 ¶4 states:

After longjmp is completed, thread execution continues as if the corresponding invocation of the
setjmp macro had just returned the value specified by val. ...

This sentence could be interpreted to mean that the invocation semantics of setjmp is the same whether it returns from direct invocation or returns from a longjmp function. That is, the return of setjmp means the jmp_buf argument is initialized and can be used by another longjmp. But again, this is not clear. In the most limiting interpretation, the as if clause only speaks to the value returned by setjmp, and not the invocation itself.

Since the semantics are ambiguous, it is proper to treat the jmp_buf object value as indeterminate upon return from longjmp.

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