c - IOCCC 1986/wall.c - 为什么 TCC 在处理早期 C 代码方面胜过 GCC?
问题描述
IOCCC 早期的另一颗明珠是Larry Wall 1986 年的条目:
http://www.ioccc.org/years.html#1986(墙)
我怀疑今天没有 C 编译器可以直接直接编译该源代码,因为它包含严重的预处理器滥用:
- 最新
TDM-GCC 9.2.0
设置为 ANSI 模式失败 - 最后
TCC 0.9.27
失败
然而,在从混淆的原始代码(总是使用 GCC's cpp -traditional
)中提取预处理代码之后,TCC 和 GCC 都设法编译它;然而,GCC 的努力白费了,因为程序在尝试开始解码其混淆的介绍文本时阻塞(对于那些想要深入研究的人来说,这里不会破坏它!)
另一方面,TCC 设法对 的隐式声明进行简要警告system()
,read()
并write()
快速生成一个工作程序。
我尝试使用 GDB 逐步执行 GCC 代码,这就是我发现编译的 GCC 代码在for
遍历文本字符串以对其进行解码的循环的第二遍时阻塞的方式:
[Inferior 1 (process 9460) exited with code 030000000005]
该进程 ID 无关紧要,因为它代表崩溃的调试构建可执行文件。但是,退出代码保持不变。
显然,TCC 比 GCC 更适合此类 IOCCC 条目。后者仍然能够成功编译甚至运行一些条目,但是对于像这样的棘手案例,TCC 很难被击败。它唯一的缺点是在预处理像这个例子这样极度滥用的代码时它不够用。它在某些预处理条目之间留下空格,因此无法将它们连接到作者预期的 C 关键字中,而 GCC 的cpp
作品 100%。
我的问题是,听起来很哲学,甚至是修辞:
与 TCC 不同,现代 GCC 中是什么导致它无法编译或在编译早期的 C 程序时产生不可用的代码?
提前感谢所有反馈,我很感激!
注意:我使用的是带有 WSL 2 的 Windows 10 版本 2004;GCC 在 Windows 和 WSL 2 环境中都失败了。我也计划在 WSL 2 中编译 TCC,以便在该环境中进行比较。
PS:当它最终按预期执行时,我非常喜欢这个程序。毫无疑问,当之无愧当年的“最全能大奖”!
解决方案
崩溃是由程序写入字符串文字的内容引起的。“传统” C 编译器通常会将它们放在可写内存中,但在现代系统上,它们基本上总是在只读内存中。我很惊讶它不会与 TCC 一起崩溃。
这是该程序的一个版本,它在我的计算机上编译时不会遇到 GCC(即使有非常高级别的警告),并且似乎可以正常工作。我做了尽可能少的改变。像往常一样最好的 IOCCC 条目,预处理和重新格式化几乎没有帮助,尽管它们确实为临时逆向工程师消除了一些陷阱。
该程序假定system
调用 Bourne 风格的 shell,并且该 shell 可以使用 Unix 风格的stty
命令。此外,如果执行字符集不是 ASCII,它将出现故障(可能以一种有趣的方式)。
#include <stdlib.h>
#include <unistd.h>
const char o[] = ",,B3-u;.(&*5., /(b*(1\036!a%\031m,,,,,\r\n";
static char *ccc (char *cc)
{
char *cccc = cc;
int c;
for (; (c = (*cc)); *cc++ = c)
{
switch (0xb + (c >> 5))
{
case '\v':
break;
case '\f':
switch (c)
{
case (8098) & ('|' + 3):
c = (8098) >> ('\n' - 3);
break;
case (6055) & ('|' + 3):
c = (6055) >> ('\n' - 3);
break;
case (14779) & ('|' + 3):
c = (14779) >> ('\n' - 3);
break;
case (10682) & ('|' + 3):
c = (10682) >> ('\n' - 3);
break;
case (15276) & ('|' + 3):
c = (15276) >> ('\n' - 3);
break;
case (11196) & ('|' + 3):
c = (11196) >> ('\n' - 3);
break;
case (15150) & ('|' + 3):
c = (15150) >> ('\n' - 3);
break;
case (11070) & ('|' + 3):
c = (11070) >> ('\n' - 3);
break;
case (15663) & ('|' + 3):
c = (15663) >> ('\n' - 3);
break;
case (11583) & ('|' + 3):
c = (11583) >> ('\n' - 3);
break;
}
break;
default:
c += o[c & (38 - 007)];
switch (c -= '-' - 1)
{
case 0214:
case 0216:
c += 025;
/*fallthru*/
case 0207:
c -= 4;
/*fallthru*/
case 0233:
c += ' ' - 1;
}
}
c &= 'z' + 5;
}
return cccc;
}
int
main (void)
{
char c[] = "O";
char cccc[] = "dijs QH.soav Vdtnsaoh DmfpaksoQz;kkt oa, -dijs";
char ccccc[] = ";kkt -oa, dijszdijs QQ";
system (ccc (cccc));
for (;;)
{
read (0, c, 1);
*c &= '~' + 1;
write (1, ccc (c), '\0');
switch (*c)
{
case 4:
system (ccc (ccccc));
return 0;
case 13:
write (1, o + ' ', 3);
break;
case 127:
write (1, "\b \b", 3);
break;
default:
write (1, c, 1);
break;
}
}
return 0;
}
推荐阅读
- ios - 获取所有状态的用户位置(即背景、前景或应用程序终止或终止)
- c++ - 来自函数参数的c ++静态数组声明
- javascript - 为什么 JS 源映射通常以令牌粒度?
- kubernetes - Kubernetes Ingress - 将外部路径引用到服务中的另一个路径
- java - 我想在java中以矩阵/数组形式保存数据
- python - flask.session 正在将数据从一个视图丢失到另一个视图
- html - 单击按钮时从 DIV 表中删除行
- python - 如何将 annotation.xml 文件转换为 train.txt 和 val.txt 用于对象检测?
- javascript - 对象值的总和数组
- bash - 如何说 shell 脚本在给定的时间范围内运行?