zoukankan      html  css  js  c++  java
  • 有关信息ACM/ICPC竞争环境GCC/G++叠插件研究记录的扩展

    0、起因

    有时。DFS总是比BFS受人喜爱——毕竟DFS简单粗暴,更,而有些东西BFS不要启动,DFS它似乎是一个可行的选择……

    但是有一个问题,DFS默认直接写入到系统堆栈。系统堆栈和足够浅,此时OI传统的方法是通过手工叠写,acm究一下旁门左道——开栈外挂。

    Uva上的神贴(Set heap size and stack size in C++,http://acm.uva.es/board/viewtopic.php?f=14&t=16685)里面提到了一个拓栈外挂:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    int main2() {
        char test[255 << 20];
        memset(test, 42, sizeof(test));
        printf(":)
    ");
        return 0;
    }
    
    int main() {
        int size = 256 << 20;  // 256Mb
        char *p = malloc(size) + size;
        asm("movl  %0, %%esp
    "
            "pushl $exit
    "    // if you get a compile error here under mingw/cygwin, 
            "jmp   main2
    "    // replace exit with _exit, and main2 with _main2.
            :: "r"(p));
    }
    

    可惜这个外挂有点老了(kuangbin神牡丹江被虐哭)。我们须要依据如今的实际环境与时俱进,学习新姿势。学会灵活运用拓栈挂了


    事先声明:我汇编是为了研究这个问题现学现卖的,有什么地方讲的不正确还望各位指正。谢谢!


    1、理解原版的含义。以及原版如今碰到的问题

    原版关键的思路是,从堆上申请一大块内存,然后把esp(一个指向系统栈的寄存器)所指向的空间改为刚刚手工申请的地盘。之后把退出函数先推入,再直接跳转到main2的函数调用里去。

    这个整体思路是没错,但是在语法上,在如今的编译器下,那是要磕磕碰碰老半天了——毕竟当年是2007年4月。如今是2014年10月了,gcc3.*都变成gcc4.7.2了,当年32位还非常流行,如今比赛环境、评測环境都是烂大街的64位。不少细节须要改动了。

    为了具体说明情况,接下去将採用3个不同的測试环境

    1、Win下的MinGW4.7.2

    2、Linux(CentOS 6.5)下的gcc4.8.1

    3、ZOJ评測


    2、从攻坚Win 32位出发!

    把上述代码按凝视中的提示改动。放到MinGW去编译,结果得到编译错误:

    Undefined reference to 'main2';

    找不到main2?这也是醉了?。

    于是轮到利用网络资源的时刻了

    我也不知道为什么,我在一个讲ARM-GCC内联汇编的文章(http://www.ethernut.de/en/documents/arm-inline-asm.html)里偶然看到了一句话:

    extern long Calc(void) asm ("CALCULATE");
    好吧,我们现学现卖,我们加上一句:

    extern int main2(void) asm ("_main2");
    然后编译,没问题了!

    等一会就有个笑脸:)了!

    好了,Win 32位环境下没事了。


    3、转移到Linux 64位下的漫漫长征

    接下去,开了虚拟机,扔到Linux的GCC手上编译一下,挂了……

    提示是:

    invalid instruction suffix for `mov'
    invalid instruction suffix for `push'

    继续研究……

    中间折腾来折腾去的过程不用多说,

    可是发现pushl改成push就少了个错误,那是个好消息

    后来查资料。知道push和mov最后的字母表示操作的单位是b(1字节)。w(2字节一个word),l(4字节一个longword)

    呃,等等,64位环境。8字节呢?

    Pascal选手都知道qword吧……

    那我们猜猜看最后一个字母可不能够是q(quadword)

    于是pushl改成pushq,这里还是没问题!

    好的,那以下对mov动刀

    首先想到栈地址应该是个qword,于是应该试试看movq,但是编译失败……

    然后。64位汇编应该和32位的不一样吧……继续搜64位汇编资料。发现esp在64位下被rsp替代了

    于是语句改动成:

    "movq  %0, %%rsp
    "
    "pushq $exit
    "
    "jmp main2
    "

    编译。pass了。执行起来得到了笑脸。yes!


    4、实战环境检測——zoj为例

    于是。直接把之前的2014牡丹江H题代码套上拓栈语句试试看(注意拓栈的大小。毕竟有内存限制的)

    结果无情的返回CE……并且CE的错误提示挺无厘头的:

    (第二节新加的那一句)error: expected initializer before 'asm'

    左搞搞右搞搞,怎么都搞不正确。

    这个时候,想起来,应该去看看编译命令的

    C++: g++ foo.c -o foo -ansi -fno-asm -O2 -Wall -lm --static -DONLINE_JUDGE

    -fno-asm?!

    我也是醉了……

    于是,把asm换成__asm__继续

    然后接下来迎来了Non-zero Exit Code

    这个太无解了(毕竟汇编全然不熟),然后灵机一动想到一个好办法:

    在实际做事情的main2()函数里面。把之前我们经常使用的return 0;所有换成exit(0);

    这下oj评測环境你总没办法了吧?最终迎来了AC……


    (与这个的奋斗未完待续)

    未完毕測试的项目:

    1、之前用了VC++拓栈外挂过的可不能够用这个拓栈挂过了呢?

    2、这样的拓栈挂安全性和普适性怎样?


    5、总结

    不得不承认,作为一个课外自发研究项目,这个折腾来折腾去挺好玩的

    神犇们肯定不屑一顾

    或许不少小菜急急忙忙就收走了。然后“着火”时刻立即拉出来用了

    说老实话啊,这样的对水平提升帮助太小。这里贴出来仅仅是作为个人总结,也供大家娱乐。

    祝各位接下来水平能有实质性的突破!

    最后感谢一下队友。感谢ACDream群里的各位神犇,特别感谢kuangbin、[CUGB]fz、[bupt]leo、[NENU]Lee_vincent等人的关心和和支持和指导。


    附赠:完整改动版的G++可用拓栈外挂模板


    1、Win 32位MinGW 4.7.2环境

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    extern int main2(void) __asm__ ("_main2");
    
    int main2() {
        char test[255 << 20];
        memset(test, 42, sizeof(test));
        printf(":)
    ");
        exit(0);
    }
    
    int main() {
        int size = 256 << 20;  // 256Mb
        char *p = (char *)malloc(size) + size;
        __asm__ __volatile__(
            "movl  %0, %%esp
    "
            "pushl $_exit
    " 
            "jmp _main2
    "
            :: "r"(p));
    }

    2、Linux 64位gcc 4.8.1环境

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    extern int main2(void) __asm__ ("main2");
    
    int main2() {
        char test[255 << 20];
        memset(test, 42, sizeof(test));
        printf(":)
    ");
        exit(0);
    }
    
    int main() {
        int size = 256 << 20;  // 256Mb
        char *p = (char *)malloc(size) + size;
        __asm__ __volatile__(
            "movq  %0, %%rsp
    "
            "pushq $exit
    " 
            "jmp main2
    "
            :: "r"(p));
    }
    3、整体改动规律:

    1)extern那个伪main函数这一步不可缺少!

    不然编译器找不到的!

    2)32位下请用longword和32位寄存器,64位下请用quadword和对应的64位寄存器

    3)习惯性地return 0;或更换exit(0);大多数被保险人

    版权声明:本文博主原创文章,博客,未经同意不得转载。

  • 相关阅读:
    Spring----->projects----->Spring IO Platform(也即spring的BOM:Bill Of Materials)
    可添加功能-----机器学习管理平台
    Maven--->学习心得--->maven的Dependency Mechanism(依赖关系机制)
    Maven--->使用实例
    Maven--->学习心得--->maven的相关配置---->概述 以及 相关配置实例
    Maven--->学习心得--->maven的配置文件settings.xml
    netty学习记录(实例)
    netty学习记录
    打开CKeditor默认本地上传
    Spring Security 报There is no PasswordEncoder mapped for the id "null"
  • 原文地址:https://www.cnblogs.com/blfshiye/p/4906977.html
Copyright © 2011-2022 走看看