0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

main函数不一定就是程序入口

学益得智能硬件 来源:学益得智能硬件 2023-06-15 17:12 次阅读
我们都知道,main函数是C程序的入口,那这个入口能不能修改?
#include 


int main()
{
    return 0;
}
答案肯定是可以的,毕竟这个入口也是人为规定的。编译分为4个步骤,预处理、编译、汇编、链接。
gcc -E test.c -o test.i
gcc-Stest.i-otest.s
gcc -c test.s -o test.o
gcctest.o-otest
最后一步链接的时候,需要用到一个叫做链接脚本的东西,链接脚本就是类似于这样的一个文件:
OUTPUT_ARCH( "riscv" )  /* 代码采用的是RISC-V架构*/
ENTRY( _start )    /*代码入口符号是_start,就是汇编启动函数的符号*/
MEMORY
{
  /* 定义了一段起始地址为0x80000000,长度为128MB的内存区域,取名叫ram*/
  ram   (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M
}
SECTIONS
{
  /* 所有输入文件中的.text段、.text.*段都合在一起,组成输出elf文件中的.text段;
  * 此外,定义了两个符号_text_start和_text_end ,注意符号'.'代表的是当前地址;
  * 生成的.text段被放在了ram这个内存区域中。
  */
  .text : {
    PROVIDE(_text_start = .);
    *(.text .text.*)
    PROVIDE(_text_end = .);
  } >ram


  .rodata : {
    PROVIDE(_rodata_start = .);
    *(.rodata .rodata.*)
    PROVIDE(_rodata_end = .);
  } >ram


  .data : {
    . = ALIGN(4096);
    PROVIDE(_data_start = .);
    *(.sdata .sdata.*)
    *(.data .data.*)
    PROVIDE(_data_end = .);
  } >ram


  .bss :{
    PROVIDE(_bss_start = .);
    *(.sbss .sbss.*)
    *(.bss .bss.*)
    *(COMMON)
    PROVIDE(_bss_end = .);
  } >ram
  PROVIDE(_memory_start = ORIGIN(ram));
  PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));
  PROVIDE(_heap_start = _bss_end);
  PROVIDE(_heap_size = _memory_end - _heap_start);
}
它规定了程序的各个部分在内存中的位置,当然里面也包含了程序的入口:
ENTRY( _start )
只要修改了入口的名字,就能实现我们想要的功能。那么问题又来了,平时在编译的时候,都是直接:
gcc hello.c
这个过程也没看到什么链接脚本。
gcc其实是一系列工具的合集,如果你想看到详细的步骤,编译的时候加上-v选项就行。
gcc test.c -o test -v
最后一步链接的时候,都会默认使用编译器自带的链接脚本。Linux下,使用:
ld --verbose
可以拿到编译器自带的链接脚本。
/* Script for -z combreloc -z separate-code */
/* Copyright (C) 2014-2020 Free Software Foundation, Inc.
   Copying and distribution of this script, with or without modification,
   are permitted in any medium without royalty provided the copyright
   notice and this notice are preserved.  */
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64",
        "elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); SEARCH_DIR("=/lib/x86_64-linux
-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu64"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib");SECTIONS
{
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = 
SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;  .interp         : { *(.interp) }
  .note.gnu.build-id  : { *(.note.gnu.build-id) }
  .hash           : { *(.hash) }
  .gnu.hash       : { *(.gnu.hash) }
  .dynsym         : { *(.dynsym) }
  .dynstr         : { *(.dynstr) }
  .gnu.version    : { *(.gnu.version) }
  .gnu.version_d  : { *(.gnu.version_d) }
  .gnu.version_r  : { *(.gnu.version_r) }
  .rela.dyn       :
    {
      *(.rela.init)
      *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
      *(.rela.fini)
      *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
      *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
      *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
      *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
      *(.rela.ctors)
      *(.rela.dtors)
      *(.rela.got)
      *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
      *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*)
      *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*)
      *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*)
      *(.rela.ifunc)
    }
  .rela.plt       :
    {
      *(.rela.plt)
      PROVIDE_HIDDEN (__rela_iplt_start = .);
      *(.rela.iplt)
      PROVIDE_HIDDEN (__rela_iplt_end = .);
    }
  . = ALIGN(CONSTANT (MAXPAGESIZE));
  .init           :
  {
    KEEP (*(SORT_NONE(.init)))
  }
  .plt            : { *(.plt) *(.iplt) }
.plt.got        : { *(.plt.got) }
.plt.sec        : { *(.plt.sec) }
  .text           :
  {
    *(.text.unlikely .text.*_unlikely .text.unlikely.*)
    *(.text.exit .text.exit.*)
    *(.text.startup .text.startup.*)
    *(.text.hot .text.hot.*)
    *(SORT(.text.sorted.*))
    *(.text .stub .text.* .gnu.linkonce.t.*)
    /* .gnu.warning sections are handled specially by elf.em.  */
    *(.gnu.warning)
  }
  .fini           :
  {
    KEEP (*(SORT_NONE(.fini)))
  }
  PROVIDE (__etext = .);
  PROVIDE (_etext = .);
  PROVIDE (etext = .);
  . = ALIGN(CONSTANT (MAXPAGESIZE));
  /* Adjust the address for the rodata segment.  We want to adjust up to
     the same address within the page on the next page up.  */
  . = SEGMENT_START("rodata-segment", ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CO
NSTANT (MAXPAGESIZE) - 1)));  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
  .rodata1        : { *(.rodata1) }
  .eh_frame_hdr   : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
  .gcc_except_table   : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) 
}  .gnu_extab   : ONLY_IF_RO { *(.gnu_extab*) }
  /* These sections are generated by the Sun/Oracle C++ compiler.  */
  .exception_ranges   : ONLY_IF_RO { *(.exception_ranges*) }
  /* Adjust the address for the data segment.  We want to adjust up to
     the same address within the page on the next page up.  */
  . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
  /* Exception handling  */
  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) }
  .gnu_extab      : ONLY_IF_RW { *(.gnu_extab) }
  .gcc_except_table   : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) 
}  .exception_ranges   : ONLY_IF_RW { *(.exception_ranges*) }
  /* Thread Local Storage sections  */
  .tdata    :
   {
     PROVIDE_HIDDEN (__tdata_start = .);
     *(.tdata .tdata.* .gnu.linkonce.td.*)
   }
  .tbss      : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
  .preinit_array    :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  }
  .init_array    :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.
*)))    KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crte
nd?.o ) .ctors))    PROVIDE_HIDDEN (__init_array_end = .);
  }
  .fini_array    :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.
*)))    KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crte
nd?.o ) .dtors))    PROVIDE_HIDDEN (__fini_array_end = .);
  }
  .ctors          :
  {
    /* gcc uses crtbegin.o to find the start of
       the constructors, so we make sure it is
       first.  Because this is a wildcard, it
       doesn't matter if the user does not
       actually link against crtbegin.o; the
       linker won't look for a file to match a
       wildcard.  The wildcard also means that it
       doesn't matter which directory crtbegin.o
       is in.  */
    KEEP (*crtbegin.o(.ctors))
    KEEP (*crtbegin?.o(.ctors))
    /* We don't want to include the .ctor section from
       the crtend.o file until after the sorted ctors.
       The .ctor section from the crtend file contains the
       end of ctors marker and it must be last */
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
  }
  .dtors          :
  {
    KEEP (*crtbegin.o(.dtors))
    KEEP (*crtbegin?.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*(.dtors))
  }
  .jcr            : { KEEP (*(.jcr)) }
  .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.da
ta.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }  .dynamic        : { *(.dynamic) }
  .got            : { *(.got) *(.igot) }
  . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .);
  .got.plt        : { *(.got.plt) *(.igot.plt) }
  .data           :
  {
    *(.data .data.* .gnu.linkonce.d.*)
    SORT(CONSTRUCTORS)
  }
  .data1          : { *(.data1) }
  _edata = .; PROVIDE (edata = .);
  . = .;
  __bss_start = .;
  .bss            :
  {
   *(.dynbss)
   *(.bss .bss.* .gnu.linkonce.b.*)
   *(COMMON)
   /* Align here to ensure that the .bss section occupies space up to
      _end.  Align after .bss to ensure correct alignment even if the
      .bss section disappears because there are no input sections.
      FIXME: Why do we need it? When there is no .bss section, we do not
      pad the .data section.  */
   . = ALIGN(. != 0 ? 64 / 8 : 1);
  }
  .lbss   :
  {
    *(.dynlbss)
    *(.lbss .lbss.* .gnu.linkonce.lb.*)
    *(LARGE_COMMON)
  }
  . = ALIGN(64 / 8);
  . = SEGMENT_START("ldata-segment", .);
  .lrodata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)
) :  {
    *(.lrodata .lrodata.* .gnu.linkonce.lr.*)
  }
  .ldata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) 
:  {
    *(.ldata .ldata.* .gnu.linkonce.l.*)
    . = ALIGN(. != 0 ? 64 / 8 : 1);
  }
  . = ALIGN(64 / 8);
  _end = .; PROVIDE (end = .);
  . = DATA_SEGMENT_END (.);
  /* Stabs debugging sections.  */
  .stab          0 : { *(.stab) }
  .stabstr       0 : { *(.stabstr) }
  .stab.excl     0 : { *(.stab.excl) }
  .stab.exclstr  0 : { *(.stab.exclstr) }
  .stab.index    0 : { *(.stab.index) }
  .stab.indexstr 0 : { *(.stab.indexstr) }
  .comment       0 : { *(.comment) }
  .gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) }
  /* DWARF debug sections.
     Symbols in the DWARF debugging sections are relative to the beginning
     of the section so we begin them at 0.  */
  /* DWARF 1 */
  .debug          0 : { *(.debug) }
  .line           0 : { *(.line) }
  /* GNU DWARF 1 extensions */
  .debug_srcinfo  0 : { *(.debug_srcinfo) }
  .debug_sfnames  0 : { *(.debug_sfnames) }
  /* DWARF 1.1 and DWARF 2 */
  .debug_aranges  0 : { *(.debug_aranges) }
  .debug_pubnames 0 : { *(.debug_pubnames) }
  /* DWARF 2 */
  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
  .debug_abbrev   0 : { *(.debug_abbrev) }
  .debug_line     0 : { *(.debug_line .debug_line.* .debug_line_end) }
  .debug_frame    0 : { *(.debug_frame) }
  .debug_str      0 : { *(.debug_str) }
  .debug_loc      0 : { *(.debug_loc) }
  .debug_macinfo  0 : { *(.debug_macinfo) }
  /* SGI/MIPS DWARF 2 extensions */
  .debug_weaknames 0 : { *(.debug_weaknames) }
  .debug_funcnames 0 : { *(.debug_funcnames) }
  .debug_typenames 0 : { *(.debug_typenames) }
  .debug_varnames  0 : { *(.debug_varnames) }
  /* DWARF 3 */
  .debug_pubtypes 0 : { *(.debug_pubtypes) }
  .debug_ranges   0 : { *(.debug_ranges) }
  /* DWARF Extension.  */
  .debug_macro    0 : { *(.debug_macro) }
  .debug_addr     0 : { *(.debug_addr) }
  .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
  /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
}
我们把它导入一个文件中,后缀就叫lds吧。
ld--verbose>xx.lds
为了满足它的语法规则,还得删除一些东西,保留这两条杠之间的内容即可。看下链接脚本,找到ENTRY,就是程序的入口。
ENTRY(_start)
不过它并不是main函数,而是_start函数。因为在执行用户的代码之前,还有很多事情要做,这个后面在讲。如果要修改程序的入口,只要把_start改掉就行,比如改成test,然后保存文件。
ENTRY(test)
写个测试代码,代码中有main函数,也有test函数,test就是刚才我们说的入口,不过得指定退出方式,要不然程序运行的时候会出问题。
#include 
#include 


voidtest()
{
    printf("this is test ...
");
exit(0);
}


int main()
{
    printf("helloworld
");
    return 0;
}
编译代码,使用-T选项,指定链接脚本。
gcctest.c-otest-Txx.lds
运行程序,代码执行的是test函数。
root@Turbo:test# ./test 
this is test ...
root@Turbo:test#
修改程序的入口还有一个更简单的方法,gcc编译的时候,直接使用-e选项,也能达到一样的效果。
gcc test.c -o test -e test

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 程序
    +关注

    关注

    114

    文章

    3631

    浏览量

    79541
  • 函数
    +关注

    关注

    3

    文章

    3868

    浏览量

    61308
  • 编译
    +关注

    关注

    0

    文章

    615

    浏览量

    32392

原文标题:main函数不一定就是程序入口

文章出处:【微信号:学益得智能硬件,微信公众号:学益得智能硬件】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    main函数不一定就是程序入口

    我们都知道,main函数是C程序入口,那这个入口能不能修改?
    发表于 06-15 17:09 322次阅读

    网友可能不一定很了解的时间单位

    时间单位有的网友可能不一定很了解,特别是搞FPGA的网友,提供。 无限可分,没有最小。 s(秒)以下的时间单位(千进制): 1s (秒) =1000 ms (毫秒) 1ms (毫秒) =1000
    发表于 01-11 11:59

    资深电子工程师都不一定真正懂得如何使用电阻

    电阻作为种最基本电子元器件,广泛运用在各种电路中,通常我们也认为电阻是用法最简单的种电子元器件,除了功率外,没有过多的讲究。如果今天我说就这个小小的电阻,许多资深电子工程师都不一定真正懂得如何用
    发表于 06-24 16:41

    gpio和中断断开发现唤醒后中断不一定及时响应是为什么?

    断开,发现唤醒后中断不一定及时响应,即使响应了系统也死掉了,请问这种情况怎么解决呢?我在idle_profile的基础上做的实验。deepsleep模式的功耗有点儿高了。
    发表于 06-12 16:42

    main函数介绍

    函数介绍main 函数作为应用程序入口,在头文件“#include ”中。
    发表于 11-05 07:31

    C语言程序main函数免费下载

    本文档的主要内容详细介绍的是C语言程序main函数免费下载。
    发表于 09-26 14:48 3次下载

    C语言的main函数有几种写法?

    从学习C语言开始就一直写个一个函数,那么你知道它的标准写法什么什么样吗? main函数,又称主函数,是程序执行的起点,我们平时写的
    的头像 发表于 10-15 11:04 3067次阅读

    D语言编写单片(STM32F401cc)机应用需要用到的技巧 - 主入口函数

    D语言编写单片机应用需要用到的技巧 - 主入口函数入口函数入口函数单片机
    发表于 11-29 21:06 13次下载
    D语言编写单片(STM32F401cc)机应用需要用到的技巧 - 主<b class='flag-5'>入口</b><b class='flag-5'>函数</b>

    C语言编程中main()函数退出之后程序去哪儿了

    对于嵌入式系统,如果没有运行RTOS,那么程序开发中的主函数main()需要通过某种机制使其永远愉快的运行下去,它没有终点。如果想从main函数
    的头像 发表于 03-22 17:44 2381次阅读

    探究一下C语言中main函数各种不同的写法

    main函数是C程序入口函数,即程序的执行是从main
    发表于 08-07 17:26 446次阅读
    探究一下C语言中<b class='flag-5'>main</b><b class='flag-5'>函数</b>各种不同的写法

    4个并不一定比3个难对付

    4个并不一定比3个难对付
    发表于 11-03 08:04 0次下载
    4个并<b class='flag-5'>不一定</b>比3个难对付

    C语言编程中main函数退出后程序去哪儿了?

    PIC 单片机语言程序进行跟踪,发现main() 函数最后一条语句为 reset,也就是单片机直接复位,这是 MAPLAB编译器根据 PIC 单片机特点增加的复位语句。
    发表于 05-31 15:28 226次阅读
    C语言编程中<b class='flag-5'>main</b><b class='flag-5'>函数</b>退出后<b class='flag-5'>程序</b>去哪儿了?

    为什么Python没有main函数

    今天的文章中,我们来讨论一下为什么有的编程语言有main函数,而Python为什么没有main函数
    发表于 08-17 11:47 171次阅读

    c语言源程序main函数的位置

    C语言源程序中的main函数程序入口点,它被认为是C语言程序的起点。在执行
    的头像 发表于 11-24 10:23 912次阅读

    GD32 MCU启动后如何运行到main函数

    GD32 MCU启动后如何运行到main函数入口?你是否也有这样的疑虑。在执行到main函数之前MCU干了哪些事情呢?下面为大家解答。
    的头像 发表于 01-15 10:00 454次阅读
    GD32 MCU启动后如何运行到<b class='flag-5'>main</b><b class='flag-5'>函数</b>