Odd behavior with odd code

M

Michael Speer

#include <stdio.h>
#include <stdlib.h>

int main( int argc , char ** argv )
{
looper:
printf( "%d\n" , argc ) ;
printf( "%x\n" , &&looper ) ;
if( argc > 0 )
((int(*)(int,char**))&&looper)( 0 , argv ) ;
return 0 ;
}

Linux version 2.6.17-10-386 (root@vernadsky) (gcc version 4.1.2
20060928 (prerelease) (Ubuntu 4.1.1-13ubuntu5)) #2 Fri Oct 13 18:41:40
UTC 2006 (Ubuntu 2.6.17-10.33-386)

gcc version 4.1.2 20060928 (prerelease) (Ubuntu 4.1.1-13ubuntu5)

On this box the code above compiles but runs in an infinite loop.
Instead of pushing the stack pointer and calling the label location as
a function with the arguments given as I expected, the compiler
instead acts as though it was a simple goto and reuses the original
arguments.

gdb backtrace sees only a single frame.
 
S

santosh

Michael said:
#include <stdio.h>
#include <stdlib.h>

int main( int argc , char ** argv )
{
looper:
printf( "%d\n" , argc ) ;
printf( "%x\n" , &&looper ) ;
if( argc > 0 )
((int(*)(int,char**))&&looper)( 0 , argv ) ;
return 0 ;
}

Linux version 2.6.17-10-386 (root@vernadsky) (gcc version 4.1.2
20060928 (prerelease) (Ubuntu 4.1.1-13ubuntu5)) #2 Fri Oct 13 18:41:40
UTC 2006 (Ubuntu 2.6.17-10.33-386)

gcc version 4.1.2 20060928 (prerelease) (Ubuntu 4.1.1-13ubuntu5)

On this box the code above compiles but runs in an infinite loop.
Instead of pushing the stack pointer and calling the label location as
a function with the arguments given as I expected, the compiler
instead acts as though it was a simple goto and reuses the original
arguments.

gdb backtrace sees only a single frame.

This group discusses ISO C. Your code uses gcc specific extensions and
thus it's behaviour is outside the scope of this group. Maybe a gcc
mailing list or group would be more appropriate.
 
W

Walter Roberson

Michael Speer said:
#include <stdio.h>
#include <stdlib.h>

You do not appear to be using anything from stdlib.h
int main( int argc , char ** argv )
{
looper:
printf( "%d\n" , argc ) ;
printf( "%x\n" , &&looper ) ;

A label is not an object, and cannot have its address taken.
A label is not even in the same namespace as objects.

If you did manage to take the address of a label, then
a %x format would not be the correct format with which to print
out the address. You need %p to print out pointers.
if( argc > 0 )
((int(*)(int,char**))&&looper)( 0 , argv ) ;
return 0 ;
}
Linux version 2.6.17-10-386 (root@vernadsky) (gcc version 4.1.2
20060928 (prerelease) (Ubuntu 4.1.1-13ubuntu5)) #2 Fri Oct 13 18:41:40
UTC 2006 (Ubuntu 2.6.17-10.33-386)

gcc version 4.1.2 20060928 (prerelease) (Ubuntu 4.1.1-13ubuntu5)

On this box the code above compiles but runs in an infinite loop.

You didn't compile with C, you compiled with gcc. gcc implements
a C-like language, but does not implement C unless you use a
number of compile options to force it to compile the way a C compiler
should. For assistance with the C-like language implemented
by gcc, you would need to ask in a gcc newsgroup. (You won't be
able to compile your program as real C.)
Instead of pushing the stack pointer and calling the label location as
a function with the arguments given as I expected, the compiler
instead acts as though it was a simple goto and reuses the original
arguments.

Anything can happen when you violate C semantics.
 
R

Roberto Waltman

Michael Speer said:
#include <stdio.h>
#include <stdlib.h>

int main( int argc , char ** argv )
{
looper:
printf( "%d\n" , argc ) ;
printf( "%x\n" , &&looper ) ;
if( argc > 0 )
((int(*)(int,char**))&&looper)( 0 , argv ) ;
return 0 ;
}
...

On this box the code above compiles but runs in an infinite loop.
Instead of pushing the stack pointer and calling the label location as
a function with the arguments given as I expected, the compiler
instead acts as though it was a simple goto and reuses the original
arguments.

I don't understand what is the meaning of &&looper, "the address of
the address of a label?". A label is neither a function nor an object,
so the unary & can not be applied to it. It this program produced
"expected results" in some other system that is sheer luck. (Or lack
thereof)


From WG14/N1124 Committee Draft — May 6, 2005 ISO/IEC 9899:TC2

6.5.3.2 Address and indirection operators

Constraints

1 The operand of the unary & operator shall be either a function
designator, the result of a [] or unary * operator, or an lvalue that
designates an object that is not a bit-field and is not declared with
the register storage-class specifier.

2 The operand of the unary * operator shall have pointer type.

Semantics

3 The unary & operator yields the address of its operand. If the
operand has type ‘‘type’’, the result has type ‘‘pointer to type’’. If
the operand is the result of a unary * operator, neither that operator
nor the & operator is evaluated and the result is as if both were
omitted, except that the constraints on the operators still apply and
the result is not an lvalue. Similarly, if the operand is the result
of a [] operator, neither the & operator nor the unary * that is
implied by the [] is evaluated and the result is as if the & operator
were removed and the [] operator were changed to a + operator.
Otherwise, the result is a pointer to the object or function
designated by its operand.

Roberto Waltman

[ Please reply to the group,
return address is invalid ]
 
G

Guest

Roberto said:
I don't understand what is the meaning of &&looper, "the address of
the address of a label?". A label is neither a function nor an object,
so the unary & can not be applied to it. It this program produced
"expected results" in some other system that is sheer luck. (Or lack
thereof)

It's not two instances of &, but it's one instance of &&, just like
how ++1 is invalid even though it would make sense if read as + + 1.
And the use of && as a unary operator applied to a label is a compiler
extension for which a diagnostic is correctly issued in conforming
mode.
 
M

Michael Speer

Santosh : I will not bother this group again with problems that
apparently do not extend to C but in.

Walter : on my system the sizeof( int ) == sizeof( pointer ) and
pointers are printed in hex so it printed the same, though I know it
is not guaranteed. Thank you for pointing out the appropriate %p
format symbol. Your insight of gcc implementing a C-like language
that deviates from the standard is also welcomed.

Roberto : I do not know why &&label takes the address of the label,
but it seems to do so readily. It is likely, judging from Santosh, a
gcc extension or a fluke. I got this informations from another thread
from which the explanation was absent.

Thank you for your comments.
 
C

CBFalconer

Michael said:
#include <stdio.h>
#include <stdlib.h>

int main( int argc , char ** argv )
{
looper:
printf( "%d\n" , argc ) ;
printf( "%x\n" , &&looper ) ;
if( argc > 0 )
((int(*)(int,char**))&&looper)( 0 , argv ) ;
return 0 ;
}

Why this contortion? Why not simply:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char ** argv) {
printf( "%d\n" , argc ) ;
if(argc > 0) return main(0, argv)
return 0 ;
}

--
<http://www.cs.auckland.ac.nz/~pgut001/pubs/vista_cost.txt>
<http://www.securityfocus.com/columnists/423>

"A man who is right every time is not likely to do very much."
-- Francis Crick, co-discover of DNA
"There is nothing more amazing than stupidity in action."
-- Thomas Matthews
 
R

Roberto Waltman

Michael Speer said:
...
Roberto : I do not know why &&label takes the address of the label,
but it seems to do so readily. It is likely, judging from Santosh, a
gcc extension or a fluke...

I learned something new: This is GCC extension.

From

http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Labels-as-Values.html#Labels-as-Values

5.3 Labels as Values

You can get the address of a label defined in the
current function (or a containing function) with
the unary operator `&&' ...

Sorry, can not help with your original question...


Roberto Waltman

[ Please reply to the group,
return address is invalid ]
 
K

Keith Thompson

Roberto Waltman said:
I don't understand what is the meaning of &&looper, "the address of
the address of a label?". A label is neither a function nor an object,
so the unary & can not be applied to it. It this program produced
"expected results" in some other system that is sheer luck. (Or lack
thereof)
[...]

That's actually the "&&" operator, a gcc extension that takes the
address of a label and yields a result of type void*.

Allowing conversion of a value of type void* to a pointer-to-function
type is also a gcc extension.

See the gcc documentation or gnu.gcc.help for more information.
 
R

Roberto Waltman

Keith Thompson said:
That's actually the "&&" operator, a gcc extension that takes the
address of a label and yields a result of type void*.
...
See the gcc documentation or gnu.gcc.help for more information.

Thanks, I already did. Harald van D?k and Michael Speer pointed in
that direction.
I have used gcc in several projects, but always as gcc -ansi -Wall
-pedantic -std= ...


Roberto Waltman

[ Please reply to the group,
return address is invalid ]
 
R

Richard Tobin

int main( int argc , char ** argv )
{
looper:
printf( "%d\n" , argc ) ;
printf( "%x\n" , &&looper ) ;
if( argc > 0 )
((int(*)(int,char**))&&looper)( 0 , argv ) ;
return 0 ;
}
[/QUOTE]
Why this contortion? Why not simply:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char ** argv) {
printf( "%d\n" , argc ) ;
if(argc > 0) return main(0, argv)
return 0 ;
}

Presumably that doesn't exhibit the "bug" of looping indefinitely.

-- Richard
 
M

Michael Speer

CBFalconer : I was thinking of writing a hack that would allow me to
store the label addresses in order to call a function starting at a
midway point. If I then stored static variables in a structure
outside of the list than I could create a very simple generator-like
function that starts in a different spot each time it is called
without using a switch or even recurses back to different points
within itself. Basically I was playing at what I could make the code
do. Not production.

Keith Thompson : Thank you for the information. I did not come across
it until after posting.

Just for the information of anyone here that wants it :

While compiling this code with no options or -O0 ( gcc optimization
zero ) caused it to fall into a recursive loop ( seemingly from
treating the function call as a goto ) , compiling it as -O6 ( gcc
maximum optimization ) caused it to work, redoing the stack to hold
the parameters properly.

Thank you for your comments.
 
I

Ian Collins

Michael said:
CBFalconer : I was thinking of writing a hack that would allow me to
store the label addresses in order to call a function starting at a
midway point. If I then stored static variables in a structure
outside of the list than I could create a very simple generator-like
function that starts in a different spot each time it is called
without using a switch or even recurses back to different points
within itself. Basically I was playing at what I could make the code
do. Not production.
Are you trying to implement coroutines?
 
M

Michael Speer

Are you trying to implement coroutines?

The whole of my immediate intent was to discern if it was even
possible to take the address of a label and use casting to access the
code in a function at an intermediate point. I had ideas ( the
generator or partial recursion ) of things it might be used for. From
what I know of them coroutines would be something that could be
implemented using this type of behavior. That is, if one wished to
implement them by way of the misuse of a gcc extension that when
misused thusly only compiles with the `-O6' switch being passed to the
compiler. :)
 
C

CBFalconer

Michael said:
CBFalconer : I was thinking of writing a hack that would allow me to
store the label addresses in order to call a function starting at a
midway point. If I then stored static variables in a structure
outside of the list than I could create a very simple generator-like
function that starts in a different spot each time it is called
without using a switch or even recurses back to different points
within itself. Basically I was playing at what I could make the code
do. Not production.

Keith Thompson : Thank you for the information. I did not come across
it until after posting.

I never wrote anything as foolish as that. I have similar doubts
about Keiths authorship, but we'll let him speak for himself. If
you just use proper Usenet quoting this sort of foul-up will have a
hard time occurring.

--
<http://www.cs.auckland.ac.nz/~pgut001/pubs/vista_cost.txt>
<http://www.securityfocus.com/columnists/423>

"A man who is right every time is not likely to do very much."
-- Francis Crick, co-discover of DNA
"There is nothing more amazing than stupidity in action."
-- Thomas Matthews
 
K

Keith Thompson

CBFalconer said:
I never wrote anything as foolish as that. I have similar doubts
about Keiths authorship, but we'll let him speak for himself. If
you just use proper Usenet quoting this sort of foul-up will have a
hard time occurring.

The strings "CBFalconer :" and "Keith Thompson :" were, I'm sure,
intended to address the following comments to us, not to imply that we
had written them. (Note the lack of the word "wrote", or "writes", or
anything similar).

Posting separate followups quoting (some of) what each of us had
written would have been more in keeping with usual Usenet practice,
but combining both in a single followup is not horrid IMHO. The
prefixes don't particularly look (to me) like attribution lines.
 
C

Chris Torek

While compiling this code

[which, as a reminder that should also make this article stand-alone,
was some code using gcc-specific extensions in ways other than those
intended by the gcc-extension authors, in my opinion anyway]
with no options or -O0 ( gcc optimization zero ) caused it to fall
into a recursive loop ( seemingly from treating the function call
as a goto ) , compiling it as -O6 ( gcc maximum optimization )
caused it to work, redoing the stack to hold the parameters properly.

That implies that either or both of the following are true:

- gcc has a bug in dealing with this extension, or
- you are not using the extension in a way that even gcc defines
(i.e., you are invoking not only undefined-by-the-C-standard
behavior, but even undefined-by-gcc-extension behavior).

I believe the latter is true. The former may or may not also be
true.
 
S

Stephen Sprunk

Michael Speer said:
The whole of my immediate intent was to discern if it was even
possible to take the address of a label and use casting to access the
code in a function at an intermediate point. I had ideas ( the
generator or partial recursion ) of things it might be used for. From
what I know of them coroutines would be something that could be
implemented using this type of behavior.

The problem with casting a pointer-to-label to a pointer-to-function is that
a label is not a function. The beginning of a function contains a prologue
which builds the appropriate stack frame, retrieves arguments from wherever
they are, etc. The compiler doesn't know it needs to do that at a label
used as a function entry point -- in fact it probably can't do it even if it
does know it needs to, since you can (usually) arrive at the label when it's
_not_ being used as a function entry point.

My guess is that this extension was created so that labels could be stored
in variables and one could later do "goto variable;", Since there's no
pointer-to-label type, void* is the logical choice for this abuse -- but you
need some sort of construct to turn it back into a pointer-to-label (which
I'm assuming goto can do) to actually make use of it. Instead, you're
turning that pointer-to-object (which is really a pointer-to-label) into a
pointer-to-function and trying to call it, which is so far out there it's
not even just "wrong".

There's probably a portable way to implement whatever you're trying to do;
give us the problem first, not the solution, and we can try to help.

S
 
G

Guest

Stephen said:
The problem with casting a pointer-to-label to a pointer-to-function is that
a label is not a function. The beginning of a function contains a prologue
which builds the appropriate stack frame, retrieves arguments from wherever
they are, etc. The compiler doesn't know it needs to do that at a label
used as a function entry point -- in fact it probably can't do it even if it
does know it needs to, since you can (usually) arrive at the label when it's
_not_ being used as a function entry point.

My guess is that this extension was created so that labels could be stored
in variables and one could later do "goto variable;",

A compiler must treat

void f(void) {
void *p;
goto p;
p:
return;
}

as a jump to label p, not to the address referenced by variable p.

[OT] In "GNU C", the syntax for a jump to a stored address is goto *p;
[/OT]
 

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
473,767
Messages
2,569,571
Members
45,045
Latest member
DRCM

Latest Threads

Top