what does macro "USE_VAR" used for?

G

Gestorm

Hi all, I found a macro "USE_VAR" in the code of bash-3.2 as
follows:
/*file: bash-3.2/shell.c*/
344 USE_VAR(argc);
345 USE_VAR(argv);
346 USE_VAR(env);
347 USE_VAR(code);
348 USE_VAR(old_errexit_flag);
349 #if defined (RESTRICTED_SHELL)
350 USE_VAR(saverst);

I found the definition of this macro at file shell.h as follows:

116 /* Force gcc to not clobber X on a longjmp(). Old versions of gcc
mangle
117 this badly. */
118 #if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ > 8)
119 # define USE_VAR(x) ((void) &(x))
120 #else
121 # define USE_VAR(x)
122 #endif


I think these code doesn't do anything really , it just control the
process of compilation. But how does it works? If the "else"
condition in file shell.h is true, the lines 344~350 in shell.c would
turn into "; ; ; ; ;".But what would happen in another condition?
Thanx!
 
S

Stephen Sprunk

Gestorm said:
Hi all, I found a macro "USE_VAR" in the code of bash-3.2 as
follows:
/*file: bash-3.2/shell.c*/
344 USE_VAR(argc);
345 USE_VAR(argv);
346 USE_VAR(env);
347 USE_VAR(code);
348 USE_VAR(old_errexit_flag);
349 #if defined (RESTRICTED_SHELL)
350 USE_VAR(saverst);

I found the definition of this macro at file shell.h as follows:

116 /* Force gcc to not clobber X on a longjmp(). Old versions of gcc
mangle
117 this badly. */
118 #if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ > 8)
119 # define USE_VAR(x) ((void) &(x))
120 #else
121 # define USE_VAR(x)
122 #endif


I think these code doesn't do anything really , it just control the
process of compilation. But how does it works? If the "else"
condition in file shell.h is true, the lines 344~350 in shell.c would
turn into "; ; ; ; ;".But what would happen in another condition?
Thanx!

It's a trick that makes sure the compiler sees that a variable is "used"
even if it's not and therefore doesn't optimize it out or warn about a
possible coding error.

S
 
G

Gestorm

It's a trick that makes sure the compiler sees that a variable is "used"
even if it's not and therefore doesn't optimize it out or warn about a
possible coding error.
Thank you. But why does it use such a strange form? If I do this
#define USE_VAR(X) &(X)
would there be anything wrong?
 
K

Kenneth Brody

Gestorm said:
Thank you. But why does it use such a strange form? If I do this
#define USE_VAR(X) &(X)
would there be anything wrong?

[...Original code had: # define USE_VAR(x) ((void) &(x)) ...]

That might give you a different warning about taking the address of
the variable and not doing anything about it. By casting the address
to void, you explicitly tell the compiler "yes, I know I'm throwing
out the result".

--
+-------------------------+--------------------+-----------------------+
| Kenneth J. Brody | www.hvcomputer.com | #include |
| kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------+
Don't e-mail me at: <mailto:[email protected]>
 
C

Chris Torek

Hi all, I found a macro "USE_VAR" in the code of bash-3.2 as
follows:
/*file: bash-3.2/shell.c*/
344 USE_VAR(argc);
345 USE_VAR(argv);
346 USE_VAR(env);
347 USE_VAR(code);
348 USE_VAR(old_errexit_flag);
349 #if defined (RESTRICTED_SHELL)
350 USE_VAR(saverst);

I found the definition of this macro at file shell.h as follows:

116 /* Force gcc to not clobber X on a longjmp(). Old versions of gcc
mangle
117 this badly. */
118 #if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ > 8)
119 # define USE_VAR(x) ((void) &(x))
120 #else
121 # define USE_VAR(x)
122 #endif

I think these code doesn't do anything really , it just control the
process of compilation. But how does it works?

It does not (work, that is).

Read the comment:

Force gcc to not clobber X on a longjmp().

Look up setjmp() and longjmp() in your C reference. These are
macros that "act like" a kind of goto, in which a setjmp() is a
goto label target, and a longjmp() will go to that label. If your
C reference is a good one, it will mention certain restrictions on
using local variables in functions that are the targets of these
longjmp "go to"s.

In normal ordinary C, you might write something like:

void somefunc(T1 parm1, T2 parm2) {
T3 localvar = initial_value;

top:
... do various things ...
use(parm1);
if (some condition) {
... maybe change localvar and/or parm1 and/or parm2 ...
use(parm2);
use(localvar);
if (some other condition)
goto top;
... maybe change localvar and/or parm1 and/or parm2 ...
if (some third condition)
goto top;
...
}
...
}

This kind of "goto", built into the language, is only mildly bad,
as it can only "go" within one particular function, in this case
the function named somefunc(). If you do go back to the label
"top", the local variable localvar and the parameters parm1 and
parm2 hold the new values set by any assignments or adjustments,
and uses of them will see the new values.

But suppose, for some reason, you want to be able to "go to" the
label "top" from a function that somefunc() calls, even indirectly,
without having to return through all the call layers. That is,
somefunc() calls a(), and a() calls b(), and b() calls c() which
calls d() which calls e() and so on up to z(), and then deep inside
of z() you decide you should "goto top" without ever returning from
z() or y() or x() or w() or ... all the way back to a(). You *can*
do this in C, using setjmp() and longjmp(). This is the kind of
goto you should strive mightily to avoid. The "mild" goto using
the "goto" keyword is bad enough, but this kind of goto is *terrible*.
(It is a bit like an exception in C++ except that it cannot even
be caught. This means that none of a() through y() ever have a
chance to do any clean up work -- close files, free memory, etc.
-- that they might need to do. It is possible, though difficult,
to build a "better" exception-like system that *does* have an
unwind-protect, using setjmp and longjmp in a stackable fashion,
but there are pitfalls.)

Now, the biggest problem with setjmp() and longjmp() is simply their
existence at all -- but if you *do* use them, they have another
problem. You, the C programmer, must add a "volatile" qualifier
to your parameter and local variables (not in absolutely all
cases, but in enough that you might as well just add it everywhere).
That is, if you want to rewrite somefunc() to allow a longjmp to
start over at "top", you must do this:

#include <setjmp.h>

jmp_buf label_top;

void somefunc(volatile T1 parm1, volatile T2 parm2) {
volatile T3 localvar = initial_value;

setjmp(label_top);
/*
* At any point, including in functions somefunc() calls,
* you can:
* longjmp(label_top);
* to "go to" "just after" the setjmp() macro, i.e., right
* here. This is like using a label like "top:" and doing
* a "goto top", except that because label_top is a "global
* variable" of type jmp_buf, it can be goto-ed from anywhere
* in the program, not just here in somefunc().
*/
... do your same various things, tests, etc ...
}

But note these "volatile" keywords I added. They are NOT OPTIONAL
(except in certain obscure circumstances).

Whoever wrote bash-3.2/shell.c did not realize this and did not
put in the correct "volatile"s. They then thought that gcc had a
bug, and put in even more code to attempt to "trick" the compiler
into behavior as if they had written correct code in the first
place. (As it happens, some versions of gcc *do* have a bug, even
if you use the "volatile" keyword as required. But that is not
what is going on here.)

The best fix for this problem is to avoid setjmp() and longjmp()
entirely. The second-best fix is to put in the required "volatile"s.
The USE_VAR macro is a hack, and is wrong and should be removed.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,161
Messages
2,570,892
Members
47,430
Latest member
7dog123

Latest Threads

Top