一个表情包引发的悬案!

Posted 程序员的店小二

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一个表情包引发的悬案!相关的知识,希望对你有一定的参考价值。

今天给大家看个有趣的编程知识。

看看下面几行代码:

大家觉得运行后会输出什么?

一进main函数后就进入一个while死循环,理论上来说,这程序就死在这里了。

但有人居然运行打出了“hello world”,你敢信吗?

这是著名的安全论坛——看雪论坛的一位网友(LeadroyaL)的研究过程,强烈推荐大家看看,gcc编译器到底干了什么事!

以下是该网友的原贴:


某日在群里看到这张很怪的表情包,想着这不是扯淡么,一看到运行结果顿时傻眼了。。。

尝试复现


#include <stdio.h>int main()    while(1) ;void unreachable()    printf("HelloWorld\\n");

先放一个在线编译C代码的网站:https://godbolt.org/

初步结果:只有在比较新的C++上才会触发,稳定复现,因此选定 clang++ test.cpp -O1 作为目标进行研究。

分析问题引入位置


objdump/gdb 误入歧途

通过 objdump -d a.out 可以看到,_start 函数直接调用了 unreachable 函数。(一口盐汽水就喷了在屏幕上。。。)

gdb 调试,很诡异,也直接就断在了 unreachable 里,说明二进制确实执行到了这里。

什么?还有程序没有main、从别的函数开始执行?这连接器是抽什么风?

readelf 初见端倪

说实话,我还是第一次见到 “没有main函数、但是能运行” 的程序,于是查看符号表,发现main函数还在,和unreachable指向同一个偏移,但函数大小为0。

也就是说,main函数的函数体被清空了,恰好指向了unreachable的位置,“越界”执行了下一个函数的代码。

查看汇编

clang++ -c -S test.cpp -O1 得到 test.s 汇编文件。

我去掉了汇编大部分不必要的注释,但故意保留了一部分,才能让读者知道,他读的是汇编 。可以看到,main函数里一条指令都没有,全部都是点开头的或者井号开头的注释。

去除全部注释后就是,main 和 _Z11unreachablev 指向同一段汇编代码:

查看IR

clang++ -emit-llvm -S -c test.cpp -O1 得到 test.ll IR文件。可以看出,确实在IR阶段main的内容就发生了变换,由原先的死循环被替换成了 unreachable。注意,这个unreachable是一条名叫UnreachableInstruction的指令,并非函数名。

对比 -O0 的输出结果:

最终,可以得到结论,这个反常的现象是在IR层面的编译器优化引起的。

以上是关于一个表情包引发的悬案!的主要内容,如果未能解决你的问题,请参考以下文章

X-Powered-By中的Express在哪个地方能改呢

无法禁用X-Powered-By:Express

隐藏响应的server,X-Powered-By

DouPHP去除Powered by DouPHP版权的方法

Entrust - Laravel 用户权限系统解决方案 | Laravel China 社区 - 高品质的 Laravel 和 PHP 开发者社区 - Powered by PHPHub

Sysdig and Falco now powered by eBPF