[net.lang.c] mildly obfuscating c

tom@puff.UUCP (12/07/85)

ok, guys, now i will admit that the below code is *not* kosher. 
but the question still remains, if you run this program, what will
your output be?  does the machine you compile it on make
a difference?  does defining ARG to be something, say 999,
make a difference?   what if VAR were auto or static?

-------------------------------------------------------------
# define ARG
# define VAR register
# define CALL(x) (*(int (*)()) (x))(ARG)

main() {
    VAR thing = 0;
stuff:
    printf("here it goes, thing is %d\n",thing);
    if (!thing++) 
        CALL(stuff);
    printf("one there it went, thing is %d\n",thing);
    printf("two there it went, thing is still %d\n",thing);
}


/* lint outputs
test.c:
test.c(8): warning: questionable conversion of function pointer
*/
-------------------------------------------------------------

note the lint output.  no kidding.


on a vax, i get a reserved intruction trap as soon as i call the
label.  on a gould, the program runs normally, but i get this:

-------------------------------------------------------------
here it goes, thing is 0
here it goes, thing is 1
here it goes, thing is 1
two there it went, thing is still 2
-------------------------------------------------------------

which is quite interesting.  the third line has the wrong string
on the stack at the time.  

on a pyramid, this is the bizarre result:

-------------------------------------------------------------
here it goes, thing is 0
here it goes, thing is 536151860
one there it went, thing is 536151861
two there it went, thing is still 536151861
one there it went, thing is 1
two there it went, thing is still 1
-------------------------------------------------------------


i haven't been able to figure out anyway to "goto" a label that
i don't know.  for example, i would like to do this:

-------------------------------------------------------------
main() {
	int (*jump[])() = { l1,l2,l3,l4,l5,l6,l7,l8 }

	l1: /* code */
	l2: /* code */
	l3: /* code */
	l4: /* code */
	l5: /* code */
	l6: /* code */
	l7: /* code */
	l8: /* code */

	goto *jump[whatever]
}
-------------------------------------------------------------

aside from the forward-referencing problem of the unseen labels, 
this is a still syntax error.   anyone have any way to do this?


tom

steve@hcradm.UUCP (Steve Pozgaj) (12/10/85)

In article <564@puff.UUCP> tom@puff.UUCP writes:
>i haven't been able to figure out anyway to "goto" a label that
>i don't know.  for example, i would like to do this:
>
>-------------------------------------------------------------
>main() {
>	int (*jump[])() = { l1,l2,l3,l4,l5,l6,l7,l8 }
>
>	l1: /* code */
>	l2: /* code */
>	l3: /* code */
>	l4: /* code */
>	l5: /* code */
>	l6: /* code */
>	l7: /* code */
>	l8: /* code */
>
>	goto *jump[whatever]
>}
>-------------------------------------------------------------
>
>aside from the forward-referencing problem of the unseen labels, 
>this is a still syntax error.   anyone have any way to do this?

Yes, it's called a "switch" statement, and was designed to obviate the need for
people translating Fortran to C and attempting to kludge out a computed goto.
A little careful rethought can usually recast computed goto's in a switch.

jbuck@epicen.UUCP (Joe Buck) (12/10/85)

In article <564@puff.UUCP> tom@puff.UUCP writes:
>ok, guys, now i will admit that the below code is *not* kosher. 
>but the question still remains, if you run this program, what will
>your output be?  does the machine you compile it on make
>a difference?  does defining ARG to be something, say 999,
>make a difference?   what if VAR were auto or static?
>
>-------------------------------------------------------------
># define ARG
># define VAR register
># define CALL(x) (*(int (*)()) (x))(ARG)
>
>main() {
>    VAR thing = 0;
>stuff:
>    printf("here it goes, thing is %d\n",thing);
>    if (!thing++) 
>        CALL(stuff);
>    printf("one there it went, thing is %d\n",thing);
>    printf("two there it went, thing is still %d\n",thing);
>}
>
>
>/* lint outputs
>test.c:
>test.c(8): warning: questionable conversion of function pointer
>*/
>-------------------------------------------------------------
>
>note the lint output.  no kidding.
>
>
>on a vax, i get a reserved intruction trap as soon as i call the
>label.

On a Vax, subroutines called by the CALLS instruction (which is what
the compiler generates) start with a two-byte register save mask. There
isn't one at the label, rather there's some random pattern, hence the
trap. I can't give details for other machines, but you can rest assured
that registers and stack won't be saved and restored properly in most cases.
That's the real reason this isn't valid.

>i haven't been able to figure out anyway to "goto" a label that
>i don't know.  for example, i would like to do this:
>
>-------------------------------------------------------------
>main() {
>	int (*jump[])() = { l1,l2,l3,l4,l5,l6,l7,l8 }
>
>	l1: /* code */
>	l2: /* code */
>...
>	l8: /* code */
>
>	goto *jump[whatever]
>}
>aside from the forward-referencing problem of the unseen labels, 
>this is a still syntax error.   anyone have any way to do this?
>tom

Sure do.

	switch(whatever) {
	case 0: /* code at l1 above */
		break; /* unless your intention is to fall through
			  to the next case */
	case 1: /* code at l2 above */
	...
	case 7: /* code at l8 above */
	}

etc. This should have identical effect, and will compile into code
that looks very much like the above in many cases.

There are rare situations where goto's are reasonable (I never use
them myself), but I really think you should get out of the habit of
thinking that way.
-- 
Joe Buck				|  Entropic Processing, Inc.
UUCP: {ucbvax,ihnp4}!dual!epicen!jbuck  |  10011 N. Foothill Blvd.
ARPA: dual!epicen!jbuck@BERKELEY.ARPA   |  Cupertino, CA 95014

franka@mmintl.UUCP (Frank Adams) (12/10/85)

In article <564@puff.UUCP> tom@puff.UUCP writes:
>i haven't been able to figure out anyway to "goto" a label that
>i don't know.  for example, i would like to do this:
>
>-------------------------------------------------------------
>main() {
>	int (*jump[])() = { l1,l2,l3,l4,l5,l6,l7,l8 }
>
>	l1: /* code */
>	l2: /* code */
>	l3: /* code */
>	l4: /* code */
>	l5: /* code */
>	l6: /* code */
>	l7: /* code */
>	l8: /* code */
>
>	goto *jump[whatever]
>}
>-------------------------------------------------------------

First, why do you want to do this?  The correct way to write this code in
c is:

switch(whatever) {
   case 0: /* code */
   break;

   case 1: /* code */
   break;

   /* etc */
}

This covers 99.9% of all uses of computed GOTOs.  If you have one of the
rare applications it doesn't cover, put the GOTO's in the switch statement:

switch(whatever) {
   case 0: goto l1;

   /* etc */
}

Frank Adams                           ihpn4!philabs!pwa-b!mmintl!franka
Multimate International    52 Oakland Ave North    E. Hartford, CT 06108

oleg@birtch.UUCP (Oleg Kiselev) (12/11/85)

In article <564@puff.UUCP> tom@puff.UUCP writes:
>ok, guys, now i will admit that the below code is *not* kosher. 
>but the question still remains, if you run this program, what will
>your output be?  does the machine you compile it on make
>a difference?  does defining ARG to be something, say 999,
>make a difference?   what if VAR were auto or static?
>
>-------------------------------------------------------------
># define ARG
># define VAR register
># define CALL(x) (*(int (*)()) (x))(ARG)
>
>main() {
>    VAR thing = 0;
>stuff:
>    printf("here it goes, thing is %d\n",thing);
>    if (!thing++) 
>        CALL(stuff);
>    printf("one there it went, thing is %d\n",thing);
>    printf("two there it went, thing is still %d\n",thing);
>}
>
>
>/* lint outputs
>test.c:
>test.c(8): warning: questionable conversion of function pointer
>*/
>-------------------------------------------------------------
>
>note the lint output.  no kidding.
.......[DELETED]........
>on a pyramid, this is the bizarre result:
>
>-------------------------------------------------------------
>here it goes, thing is 0
>here it goes, thing is 536151860
>one there it went, thing is 536151861
>two there it went, thing is still 536151861
>one there it went, thing is 1
>two there it went, thing is still 1
>-------------------------------------------------------------

Actually, this is not too bizarre....

This is the way it works on our Pyramid:
The way the code is written, declaration of "thing" precedes the label.
So, the routine call to stuff jumps to _stuff symbol address. _thing symbol
is initialized by code located BEFORE _stuff address, probably by 
"movw $0,<whatever>". Then there follows a printf call. If you do not take care
of placing you variables outside the routine ( making them global) or declaring
them static, Pyramid's C compiler will optimize EVERY possible variable to be
"register" (&'d things are exempt, I think). So, what happens in THIS program is
that the assignment to _thing is actually a register assignment. When you
call a subroutine, the register frame on Pyramid shifts and a new set of 
registers is presented to the routine to use as local automatic storage. And
the register where the code expects to find _thing ( some l-register?) is now
uninitialized. 

That would mean also that if more than n (8?16?) calls are made recursively
something will change ( when Pyramid's processor runs out of its 528(?) 
registers and has to push things on stack). Hey, anybody has time to check that?
-- 
Disclamer: My employers go to church every Sunday, listen to Country music,
and donate money to GOP. I am just a deviant.
+-------------------------------+ Don't bother, I'll find the door!
| "VIOLATORS WILL BE TOAD!"	|                       Oleg Kiselev. 
|		Dungeon Police	|...!{trwrb|scgvaxd}!felix!birtch!oleg
--------------------------------+...!{ihnp4|randvax}!ucla-cs!uclapic!oac6!oleg

root@rsch.wisc.edu (Thomas Scott Christiansen) (12/11/85)

Thanks to all of you who have mailed me suggestions regarding my
C question on computed gotos.  No, I have not been doing too much 
FORTRAN programming.  I was actually just curious what various
systems would do with that code.  Most of you suggested that I
use a switch statement.  My own solution (which I have actually
used) is the jump table code given below.  Any routine that really 
wants to exit will do a "longjump(env,1);" to return from the msgs()
function.  Forgive me for declaring all of these functions ints;
the compiler I was using hated voids with a passion.  

tom
===================================================================
extern int superuser;
extern jmp_buf env;
extern int show_menu();

msgs() {
    int (*jump[128])();
    static beenhere = 0;
    
    load_table(jump);
    
    if (setjmp(env) == 0)
        for (;;) {
            show_menu();    
            (*jump [get_opt()])();
        } 
}

load_table( j )
    int (*j[])();
{
    register i;

    extern
        leave_comment(),enter_message(),file_transfer(),kill_message(),
        go_back(),newdir(),whos_on(),finger(),get_help(),quick_summary(),
        reset_time(),do_shell(),delete_comment(), show_comments(),
        long_summary(),say_time_on(),message_stat(),logout(),do_nothing(),
        switch_expert(),long_menu(),super_expert(),get_mesg(),toggle_pause();

    for (i=0;i < 128; i++)
        j[i] = do_nothing; 

    j [ 'C' ] = leave_comment;   j [ 'E' ] = enter_message;
    j [ 'F' ] = file_transfer;   j [ 'K' ] = kill_message;
    j [ 'M' ] = go_back;         j [ 'G' ] = logout;
    j [ 'N' ] = newdir;          j [ 'O' ] = whos_on;
    j [ 'P' ] = toggle_pause;    j [ 'Q' ] = quick_summary;
    j [ 'S' ] = long_summary;    j [ 'R' ] = get_mesg;
    j [ 'T' ] = say_time_on;     j [ 'U' ] = do_finger;
    j [ '#' ] = message_stat;    j [ 'X' ] = switch_expert;
    j [ '?' ] = long_menu;       j [ 'H' ] = long_menu;

    if ( superuser  ) { /* sysop */
        j [ '6' ] = reset_time;     j [ '7' ] = do_shell;
        j [ '8' ] = delete_comment; j [ '9' ] = show_comments;
    }
}
===================================================================

daemon@houligan.UUCP (12/18/85)

> i haven't been able to figure out anyway to "goto" a label that
> i don't know.  for example, i would like to do this:
>
> -------------------------------------------------------------
> main() {
> 	int (*jump[])() = { l1,l2,l3,l4,l5,l6,l7,l8 }
>
> 	l1: /* code */
> 	l2: /* code */
> 	l3: /* code */
> 	l4: /* code */
> 	l5: /* code */
> 	l6: /* code */
> 	l7: /* code */
> 	l8: /* code */
>
> 	goto *jump[whatever]
> }
> -------------------------------------------------------------
>
> aside from the forward-referencing problem of the unseen labels,
> this is a still syntax error.   anyone have any way to do this?

The simplest method to accomplish this is the "switch" statement.
--tgi
	while (--tgi)	/* my mind continues to decay */
		;	/* even though I do nothing.. */    

{brl-bmd,ccvaxa,pur-ee,sun}!csd-gould!midas!tgi (Craig Strickland @ Gould)
305/587-2900 x5014  CompuServe: 76545,1007   Source: BDQ615   MCIMail: 272-3350