Format fixes (+ add links) in 2004/vik2/README.md
This commit is contained in:
1 parent
6aa1141e29
commit
ca56f68cb4
1 file changed
+116
-97
+116
-97
@@ -1,11 +1,9 @@
|
|||||||
# Best Abuse of CPP
|
# Best Abuse of CPP
|
||||||
|
|
||||||
Daniel Vik
|
Daniel Vik
|
||||||
926 Genter Street
|
US
|
||||||
La Jolla, Ca 92037
|
<https://www.vik.cc/>
|
||||||
US
|
<http://www.danielvik.com>
|
||||||
|
|
||||||
http://www.vik.cc/
|
|
||||||
|
|
||||||
## To build:
|
## To build:
|
||||||
|
|
||||||
@@ -27,174 +25,194 @@ echo "Do or do not. There is no try."
|
|||||||
|
|
||||||
## Judges' remarks:
|
## Judges' remarks:
|
||||||
|
|
||||||
The fun thing about CPP abuses is that, in most cases, running the
|
The fun thing about [CPP](https://en.wikipedia.org/wiki/C_preprocessor) abuses
|
||||||
preprocessor on them doesn't help you decipher the obfuscated code.
|
is that, in most cases, running the
|
||||||
That's assuming your preprocessor can take it at all.
|
[preprocessor](https://en.wikipedia.org/wiki/C_preprocessor) on them doesn't
|
||||||
|
help you decipher the obfuscated code. That's assuming your preprocessor can
|
||||||
|
take it at all.
|
||||||
|
|
||||||
## Author's remarks:
|
## Author's remarks:
|
||||||
|
|
||||||
### Introduction
|
### Introduction
|
||||||
|
|
||||||
It's been a while since the last pre-processor killer won the ioccc.
|
It's been a while since the last
|
||||||
I thought it was time to relive hour long compile times so I made a
|
[pre-processor](https://en.wikipedia.org/wiki/C_preprocessor) killer won the
|
||||||
program that does (almost) all computation in the pre-processor. The
|
IOCCC. I thought it was time to relive hour long compile times so I made a
|
||||||
program even causes gcc (and other pre-processors) to crash when
|
program that does (almost) all computation in the pre-processor. The program
|
||||||
configured to do maximum number of computations.
|
even causes `gcc` (and other pre-processors) to crash when configured to do
|
||||||
The default configuration of the makefile is set to make the program
|
maximum number of computations. The default configuration of the
|
||||||
compile in a not too long time.
|
[Makefile](Makefile) is set to make the program compile in a not too long time.
|
||||||
|
|
||||||
I've got cpp to crash in a couple of diffenent ways. Most common is to
|
I've got `cpp` to crash in a couple of different ways. Most common is to
|
||||||
get a segmentation fault, but a couple of times it just got stuck in
|
get a segmentation fault, but a couple of times it just got stuck in
|
||||||
some infinite loop.
|
some infinite loop.
|
||||||
|
|
||||||
I actually had to write my own pre-processor to verify that the program
|
I actually had to write my own
|
||||||
was doing the right thing...
|
[pre-processor](https://en.wikipedia.org/wiki/C_preprocessor) to verify that the
|
||||||
|
program was doing the right thing...
|
||||||
|
|
||||||
Since it takes a very long time to compile the program, there is a
|
Since it takes a very long time to compile the program, there is a
|
||||||
symbol that can be defined to shorten the compile time and avoid
|
symbol that can be defined to shorten the compile time and avoid
|
||||||
compiler errors.
|
compiler errors.
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
The program implements an 11-bit ALU in the pre-processor. The ALU has
|
The program implements an 11-bit
|
||||||
two registers, X and Y, for calculations and one status register SR.
|
[ALU](https://en.wikipedia.org/wiki/Arithmetic_logic_unit) in the
|
||||||
Each bit in the registers are represented by one symbol that are either
|
[pre-processor](https://en.wikipedia.org/wiki/C_preprocessor). The ALU has two
|
||||||
defined or undefined.
|
[registers](https://en.wikipedia.org/wiki/Processor_register), `X` and `Y`, for
|
||||||
|
calculations and one status register `SR`. Each bit in the registers are
|
||||||
|
represented by one symbol that are either defined or undefined.
|
||||||
|
|
||||||
The ALU has six instructions:
|
The ALU has six instructions:
|
||||||
|
|
||||||
CLR_X - Clear register X
|
- `CLR_X` - Clear [register](https://en.wikipedia.org/wiki/Processor_register) `X`.
|
||||||
|
|
||||||
CLR_Y - Clear register Y
|
- `CLR_Y` - Clear [register](https://en.wikipedia.org/wiki/Processor_register) `Y`.
|
||||||
|
|
||||||
ADD - Calculates X + Y and stores the result in X. If the result
|
- `ADD` - Calculates `X` + `Y` and stores the result in `X`. If the result
|
||||||
is larger than 11 bits, the CARRY bit in SR is set otherwise
|
is larger than 11 bits, the [CARRY
|
||||||
the CARRY bit is cleared.
|
bit](https://en.wikipedia.org/wiki/Carry_flag) in `SR` is set otherwise
|
||||||
|
the `CARRY` bit is cleared.
|
||||||
|
|
||||||
SUB - Calculates X - Y and stores the result in X. If Y was larger
|
- `SUB` - Calculates `X` - `Y` and stores the result in `X`. If `Y` was larger
|
||||||
than X, the CARRY bit in SR is set otherwise the CARRY bit
|
than `X`, the [CARRY bit](https://en.wikipedia.org/wiki/Carry_flag)
|
||||||
is cleared.
|
in `SR` is set otherwise the `CARRY` bit is cleared.
|
||||||
|
|
||||||
TST - If X == Y the EQ bit in SR is set, otherwise the EQ bit is
|
- `TST` - If `X` == `Y` the `EQ` bit in `SR` is set, otherwise the `EQ` bit is
|
||||||
cleared. If X < Y the LT bit in SR is set, othervise the LT
|
cleared. If `X` < `Y` the `LT` bit in `SR` is set, otherwise the `LT`
|
||||||
bit is cleared.
|
bit is cleared.
|
||||||
|
|
||||||
INC2 - Calculates X + 2 and stores the result in X. The CARRY bit
|
- `INC2` - Calculates `X` + 2 and stores the result in `X`. The [CARRY
|
||||||
is handled as in the ADD instruction.
|
bit](https://en.wikipedia.org/wiki/Carry_flag) is handled as in the
|
||||||
|
`ADD` instruction.
|
||||||
|
|
||||||
Internally the ALU implements a one bit adder with a carry in and carry
|
Internally the [ALU](https://en.wikipedia.org/wiki/Arithmetic_logic_unit)
|
||||||
out. This adder is the core of the ALU. The ADD instruction uses the one
|
implements a one bit adder with a carry in and carry out. This adder is the core
|
||||||
bit adder to create the 11-bit adder. The SUB instruction is implemented
|
of the ALU. The `ADD` instruction uses the one bit adder to create the 11-bit
|
||||||
by adding ~Y to X and then add 1. Finally the TST instruction calculates
|
adder. The SUB instruction is implemented by adding `~Y` to `X` and then add 1.
|
||||||
X-Y and checks the result and the CARRY bit in the SR register to set
|
Finally the `TST` instruction calculates `X-Y` and checks the result and the
|
||||||
the EQ and LT bits.
|
[CARRY bit](https://en.wikipedia.org/wiki/Carry_flag) in the `SR`
|
||||||
|
[register](https://en.wikipedia.org/wiki/Processor_register) to set the `EQ` and
|
||||||
|
`LT` bits.
|
||||||
|
|
||||||
All calculations are made using only the #ifdef, #infdef, #else, #endif,
|
All calculations are made using only `#ifdef`, `#ifndef`, `#else`, `#endif`,
|
||||||
#define and #undef. I never assign a value to a defined symbol. The
|
`#define` and `#undef`. I never assign a value to a defined symbol. The
|
||||||
logic is done by assuming that a defined symbol is 1 and an undefined
|
logic is done by assuming that a defined symbol is 1 and an undefined
|
||||||
symbol is 0. (I guess this means that I have unlimited memory as long as
|
symbol is 0. (I guess this means that I have unlimited memory as long as
|
||||||
I only store zeroes (!)).
|
I only store zeroes (!)).
|
||||||
|
|
||||||
The application that uses the pre-processor ALU is a prime number
|
The application that uses the
|
||||||
generator. The application uses the same pre-processor directives.
|
[pre-processor](https://en.wikipedia.org/wiki/C_preprocessor)
|
||||||
Needed variables are stored in memory represended by one defined or
|
[ALU](https://en.wikipedia.org/wiki/Arithmetic_logic_unit) is a [prime
|
||||||
undefined symbol per bit. When doing arithmetics, the variables are
|
number](https://en.wikipedia.org/wiki/Prime_number)
|
||||||
loaded into the X and Y registers by defining / undefining the X and Y
|
generator. The application uses the same pre-processor directives.
|
||||||
register bits. Then the calcualtion is made and the result is moved back
|
Needed variables are stored in memory represented by one defined or
|
||||||
to the memory.
|
undefined symbol per bit. When doing arithmetics, the variables are
|
||||||
|
loaded into the `X` and `Y` registers by defining / undefining the `X` and `Y`
|
||||||
|
[register](https://en.wikipedia.org/wiki/Processor_register) bits. Then the
|
||||||
|
calculation is made and the result is moved back to the memory.
|
||||||
|
|
||||||
The choice of application may be a bit unfortunate since we've already
|
The choice of application may be a bit unfortunate since we've already
|
||||||
seen a winning entry calculating prime nubmers in the pre-processor.
|
seen a winning entry calculating [prime
|
||||||
There is a big difference though. The 1988 applin entry uses pre-
|
numbers](https://en.wikipedia.org/wiki/Prime_number) in the
|
||||||
processor arithmethic (in a very nice way though). This program only
|
[pre-processor](https://en.wikipedia.org/wiki/C_preprocessor).
|
||||||
defines and undefines symbols in a well defined ;) way and doesn't do
|
There is a big difference though. The 1988 applin entry uses pre-
|
||||||
|
processor arithmetic (in a very nice way though). This program only
|
||||||
|
defines and undefines symbols in a well defined ;) way and doesn't do
|
||||||
any pre-processor arithmetics.
|
any pre-processor arithmetics.
|
||||||
|
|
||||||
The reason why I choosed to do a prime number application is that the
|
The reason why I chose to do a [prime
|
||||||
algorithm can be written to do a lot of calculations without a lot of
|
number](https://en.wikipedia.org/wiki/Prime_number) application is that the
|
||||||
nesting (gnu cpp allows only 200 nested includes).
|
algorithm can be written to do a lot of calculations without a lot of nesting
|
||||||
|
(`gnu cpp` allows only 200 nested includes).
|
||||||
|
|
||||||
### Build and Run
|
### Build and Run
|
||||||
|
|
||||||
First, do a first pre processing of the initial definitions. The FNAME
|
First, do a first pre-processing of the initial definitions. The FNAME
|
||||||
symbol must be defined to the name of the output file. If you want to
|
symbol must be defined to the name of the output file. If you want to
|
||||||
speed up the compilation, define the STOP symbol to anything from _3 to
|
speed up the compilation, define the `STOP` symbol to anything from `_3` to
|
||||||
_10. (The default value is _10 which will make the compilation take some
|
`_10`. (The default value is `_10` which will make the compilation take some
|
||||||
time.) Prime numbers up to 2^STOP will be calculated.
|
time.) Prime numbers up to `2^STOP` will be calculated.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cpp '-DSTOP=_5' '-DFNAME="tmp1.c"' prog.c tmp1.c
|
cpp '-DSTOP=_5' '-DFNAME="tmp1.c"' prog.c tmp1.c
|
||||||
```
|
```
|
||||||
|
|
||||||
Then use cpp or any other pre-processor to do the actual work. This will
|
Then use `cpp` or any other
|
||||||
take some time....
|
[pre-processor](https://en.wikipedia.org/wiki/C_preprocessor) to do the actual
|
||||||
|
work. This will take some time....
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cpp tmp1.c > tmp2.c
|
cpp tmp1.c > tmp2.c
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally compile the pre processed file
|
Finally compile the pre-processed file
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cc -O tmp2.c -o prog
|
cc -O tmp2.c -o prog
|
||||||
```
|
```
|
||||||
|
|
||||||
Then run the program to reveil all prime numbers up to 32 (if you used
|
Then run the program to reveal all prime numbers up to 32 (if you used
|
||||||
STOP=_5 as above):
|
`STOP=_5` as above):
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./prog
|
./vik2
|
||||||
```
|
```
|
||||||
|
|
||||||
### Verification
|
### Verification
|
||||||
|
|
||||||
I wasn't able to compile the full program which calculates prime numbers
|
I wasn't able to compile the full program which calculates [prime
|
||||||
up to 1024 using gnu cpp (or any other pre-processor) so I had to write
|
numbers](https://en.wikipedia.org/wiki/Prime_number) up to 1024 using `gnu cpp`
|
||||||
my own. Knowing how the program works it was quite easy to reduce the
|
(or any other [pre-processor](https://en.wikipedia.org/wiki/C_preprocessor)) so
|
||||||
compilation time from several hours to about one second. I also added
|
I had to write my own. Knowing how the program works it was quite easy to reduce
|
||||||
some statistics to my home made pre-processor and I found that
|
the compilation time from several hours to about one second. I also added some
|
||||||
calculating prime numbers up to 1024 makes the program include itself
|
statistics to my home made pre-processor and I found that calculating prime
|
||||||
over 6.8 million times (see the statistics table below).
|
numbers up to 1024 makes the program include itself over 6.8 million times (see
|
||||||
|
the statistics table below).
|
||||||
|
|
||||||
### Obfuscation
|
### Obfuscation
|
||||||
|
|
||||||
Well, since 98.5% of the program is preprocessor directives, the main
|
Well, since 98.5% of the program is
|
||||||
obfuscation is the use of preprocessor directives.
|
[preprocessor](https://en.wikipedia.org/wiki/C_preprocessor) directives, the
|
||||||
|
main obfuscation is the use of preprocessor directives.
|
||||||
|
|
||||||
Doing arithmetics without acutally using any arithmetic operations makes
|
Doing arithmetics without actually using any arithmetic operations makes
|
||||||
the program quite hard to follow.
|
the program quite hard to follow.
|
||||||
|
|
||||||
In order to actually do the calculations, the program includes itself a
|
In order to actually do the calculations, the program includes itself a
|
||||||
lot.
|
lot.
|
||||||
|
|
||||||
The symbol names are made as short as possible to meet the space limits.
|
The symbol names are made as short as possible to meet the space limits.
|
||||||
This of course adds to the obfuscation (even though the program is
|
This of course adds to the obfuscation (even though the program is
|
||||||
quite obfuscated with longer, readable macro names.
|
quite obfuscated with longer, readable macro names.
|
||||||
|
|
||||||
Finally, I made the 1.5% c code a bit harder to read by breaking all
|
Finally, I made the 1.5% C code a bit harder to read by breaking all
|
||||||
lines after three characters.
|
lines after three characters.
|
||||||
|
|
||||||
The program has 1029 lines which makes it the longest entry (so far).
|
The program has 1029 lines which makes it the longest entry (so far).
|
||||||
|
|
||||||
Even though the pre-processed and indented output is easier to read, it
|
Even though the pre-processed and indented output is easier to read, it
|
||||||
is quite hard to find the code in it. The size of the pre-processed file
|
is quite hard to find the code in it. The size of the pre-processed file
|
||||||
is several Mb when using gnu cpp.
|
is several Mb when using `gnu cpp`.
|
||||||
|
|
||||||
### Limitations
|
### Limitations
|
||||||
|
|
||||||
In order to fit the program within the space limits, I had to define the
|
In order to fit the program within the space limits, I had to define the
|
||||||
#define, #endif, and some other directives as single character symbols.
|
`#define`, `#endif`, and some other directives as single character symbols.
|
||||||
This does not compile on all pre-processors but using gnu cpp as a first
|
This does not compile on all
|
||||||
compilation step solves this.
|
[pre-processors](https://en.wikipedia.org/wiki/C_preprocessor) but using `gnu
|
||||||
|
cpp` as a first compilation step solves this.
|
||||||
|
|
||||||
### Statistics
|
### Statistics
|
||||||
|
|
||||||
The table below contains some interesting statistics about the
|
The table below contains some interesting statistics about the compilation of
|
||||||
compilation of the program. The intermediate file size is the size of
|
the program. The intermediate file size is the size of the output file of the
|
||||||
the output file of the cpp pre-processor. The time taken to pre process
|
cpp [pre-processor](https://en.wikipedia.org/wiki/C_preprocessor). The time
|
||||||
the program were measured using gnu cpp on my 2.0MHz P4 with 512Mb ram
|
taken to pre-process the program were measured using gnu cpp on my 2.0MHz P4
|
||||||
and a lot of disk.
|
with 512Mb ram and a lot of disk.
|
||||||
|
|
||||||
|
```
|
||||||
Generated Recursive Max nest Intermediate
|
Generated Recursive Max nest Intermediate
|
||||||
Level prime nrs includes count file size Time
|
Level prime nrs includes count file size Time
|
||||||
|
|
||||||
@@ -208,6 +226,7 @@ and a lot of disk.
|
|||||||
_10 < 1024 6 826 389 114 63 782 912(*) 2h 47'58"(*)
|
_10 < 1024 6 826 389 114 63 782 912(*) 2h 47'58"(*)
|
||||||
|
|
||||||
(*) Before cpp crashed.
|
(*) Before cpp crashed.
|
||||||
|
```
|
||||||
|
|
||||||
## Copyright and CC BY-SA 4.0 License:
|
## Copyright and CC BY-SA 4.0 License:
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user