• C语言中没有main函数生成可执行程序的几种方法


    1、define预处理指令
    这种方式很简单,只是简单地将main字符串用宏来代替,或者使用##拼接字符串。示例程序如下:
    #include <stdio.h>

    #define begin main

    int begin(void)
    {
    printf( "Hello, World! ");
    return 0;
    }


    #include <stdio.h>

    #define begin m##a##i##n

    int begin(void)
    {
    printf( "Hello, World! ");
    return 0;
    }
    严格来说,这种方式只算是一种技巧......
    2、_start函数
    _start函数是C程序的入口函数,会调用main函数。在调用main函数之前,会先执行_start函数分配必要的资源,然后再调用main函数。但是在用gcc编译程序时可以使用-nostartfiles选项来重写_start函数。示例程序如下:
    #include <stdio.h>
    #include <stdlib.h>

    _start(void) {
    printf( "Hello, World! ");
    exit( 0);
    }
    编译上面的程序的命令为:
    gcc -nostartfiles _start.c -o a.out
    反汇编生成的可执行程序,如下所示:
    a.out : file format elf64 -x86 - 64


    Disassembly of section .plt :

    0000000000400320 <puts@plt -0x10 > :
    400320 : ff 35 ea 01 20 00 pushq 0x2001ea( %rip) # 600510 <_GLOBAL_OFFSET_TABLE_+0x8>
    400326 : ff 25 ec 01 20 00 jmpq *0x2001ec( %rip) # 600518 <_GLOBAL_OFFSET_TABLE_+0x10>
    40032c : 0f 1f 40 00 nopl 0x0( %rax)

    0000000000400330 <puts@plt > :
    400330 : ff 25 ea 01 20 00 jmpq *0x2001ea( %rip) # 600520 <_GLOBAL_OFFSET_TABLE_+0x18>
    400336 : 68 00 00 00 00 pushq $0x0
    40033b : e9 e0 ff ff ff jmpq 400320 <puts@plt -0x10 >

    0000000000400340 < exit@plt > :
    400340 : ff 25 e2 01 20 00 jmpq *0x2001e2( %rip) # 600528 <_GLOBAL_OFFSET_TABLE_+0x20>
    400346 : 68 01 00 00 00 pushq $0x1
    40034b : e9 d0 ff ff ff jmpq 400320 <puts@plt -0x10 >

    Disassembly of section .text :

    0000000000400350 <_start > :
    400350 : 55 push %rbp
    400351 : 48 89 e5 mov %rsp, %rbp
    400354 : bf 68 03 40 00 mov $0x400368, %edi
    400359 : e8 d2 ff ff ff callq 400330 <puts@plt >
    40035e : bf 00 00 00 00 mov $0x0, %edi
    400363 : e8 d8 ff ff ff callq 400340 exit@plt
    上面的结果是完整的反汇编结果,我们可以看到_start函数中只有我们调用printf和exit函数相关的一些指令,并且.txt段中只有_start函数,没有看到main函数。如果将源代码中的_start替换为main,重新编译程序,反汇编的结果中会看到_start函数会调用到main。
    另外还有一点需要注意,因为这里重写了_start函数,所以gcc为默认的main函数准备的清理动作就没用上,所以如果退出的时候直接使用return,会导致程序崩溃。所以这里要使用exit()来退出程序。具体的原因可以参见这篇文章
    3、gcc的-e选项
    示例程序如下:
    #include <stdio.h>
    #include <stdlib.h>

    int nomain(int i, int j, int k) {
    printf( "Hello, World! ");
    exit( 0);
    }
    将上面的程序保存为m.c,编译命令如下所示:
    gcc -nostartfiles -e nomain m.c -o a.out
    继续使用objdump反汇编生成的可执行程序,结果如下:
    a.out : file format elf64 -x86 - 64


    Disassembly of section .plt :

    0000000000400320 <puts@plt -0x10 > :
    400320 : ff 35 f2 01 20 00 pushq 0x2001f2( %rip) # 600518 <_GLOBAL_OFFSET_TABLE_+0x8>
    400326 : ff 25 f4 01 20 00 jmpq *0x2001f4( %rip) # 600520 <_GLOBAL_OFFSET_TABLE_+0x10>
    40032c : 0f 1f 40 00 nopl 0x0( %rax)

    0000000000400330 <puts@plt > :
    400330 : ff 25 f2 01 20 00 jmpq *0x2001f2( %rip) # 600528 <_GLOBAL_OFFSET_TABLE_+0x18>
    400336 : 68 00 00 00 00 pushq $0x0
    40033b : e9 e0 ff ff ff jmpq 400320 <puts@plt -0x10 >

    0000000000400340 < exit@plt > :
    400340 : ff 25 ea 01 20 00 jmpq *0x2001ea( %rip) # 600530 <_GLOBAL_OFFSET_TABLE_+0x20>
    400346 : 68 01 00 00 00 pushq $0x1
    40034b : e9 d0 ff ff ff jmpq 400320 <puts@plt -0x10 >

    Disassembly of section .text :

    0000000000400350 <nomain > :
    400350 : 55 push %rbp
    400351 : 48 89 e5 mov %rsp, %rbp
    400354 : 48 83 ec 10 sub $0x10, %rsp
    400358 : 89 7d fc mov %edi, -0x4( %rbp)
    40035b : 89 75 f8 mov %esi, -0x8( %rbp)
    40035e : 89 55 f4 mov %edx, -0xc( %rbp)
    400361 : bf 75 03 40 00 mov $0x400375, %edi
    400366 : e8 c5 ff ff ff callq 400330 <puts@plt >
    40036b : bf 00 00 00 00 mov $0x0, %edi
    400370 : e8 cb ff ff ff callq 400340 < exit@plt >
    从上面我们可以看到指定的nomain函数位于.text段的开始位置,同样在函数结束的时候没有gcc为main函数准备的清理动作,所以在这里也只能使用exit()来退出程序,而不能使用return。
    4、nostartfiles选项
    前面已经多次使用了该选项,不过都是配合其他选项使用的,这个选项也可以单独使用,其含义为"Do not use the standard system startup files when linking"。
    示例程序如下:
    #include <stdio.h>
    #include <stdlib.h>

    void func() {
    printf( "I am func.... ");
    }

    int nomain1(int i, int j, int k) {
    func();
    printf( "%s: Hello, World! ", __func__);
    exit( 0);
    }
    上面的程序保存为k.c,然后使用下面的命令编译:
    [root@CentOS_190 ~] # gcc -nostartfiles p.c
    /usr /bin /ld : warning : cannot find entry symbol _start; defaulting to 0000000000400398
    在单独使用nostartfiles选项时会报警告,生成的可执行程序可以执行,但是会产生段错误,去掉对func()函数的调用就不会产生段错误了。将生成的可执行程序反汇编,和使用前面的方法生成可执行程序的反汇编结果比较,发现除了函数名不一样外,没有其他区别,不知道为什么会产生段错误。知道的麻烦告知一声,拜谢!
  • 相关阅读:
    Java并发(八):AbstractQueuedSynchronizer
    Java类实例化原理
    Flask【第五章】:做一个用户登录之后查看用户信息的小例子
    Flask【第四章】:Flask中的模板语言jinja2以及render_template的深度用法
    Flask【第三章】:Flask中的reques
    Flask【第二章】:Flask三剑客(HTTPResponse、render、redirect)和辅招(jsonify、send_file)
    Flask【第一章】:Flask介绍与安装
    vue之路由结合请求数据
    vue之动态路由和get传值
    vue之路由以及默认路由跳转
  • 原文地址:https://www.cnblogs.com/suncoolcat/p/3320380.html
Copyright © 2020-2023  润新知