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

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

    关注

    117

    文章

    3787

    浏览量

    81066
  • 函数
    +关注

    关注

    3

    文章

    4332

    浏览量

    62638
  • 编译
    +关注

    关注

    0

    文章

    658

    浏览量

    32874

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

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

收藏 人收藏

    评论

    相关推荐

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

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

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

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

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

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

    为什么高速USB并不一定表示手机拥有高速性能

    为什么高速USB并不一定表示手机拥有高速性能 数码多媒体向日用电器的发展改变了消费者接触和享受多媒体娱乐节目的方式。现在消费者可以通过扑克牌大小的设备来
    发表于 01-04 11:21 640次阅读
    为什么高速USB并<b class='flag-5'>不一定</b>表示手机拥有高速性能

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

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

    只有洁碧才是全民信赖的水牙线品牌吗?那可不一定

    身体健康就一定要保证口腔健康。使用水牙线已经是很多西方家庭会使用的清洁口腔内部的办法,对于国内的消费者来说,水牙线还算是新鲜玩意,所以面对多种多样的水牙线品牌,觉得只有洁碧,这个水牙线的创始品牌才是值得信赖的,那可不一定
    发表于 04-16 20:31 489次阅读

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

    从学习C语言开始就直写个函数,那么你知道它的标准写法什么什么样吗? main函数,又称主函数
    的头像 发表于 10-15 11:04 3317次阅读

    正确的原理图不一定能产生正确的 PCB 设计

    作者:黄刚个“xue淋淋”的案例告诉大家:正确的原理图不一定就能产生正确的PCB设计。原理图设计与PCB设计都是研发流程中的必经阶段,我们知道,原理图设计是PCB设计的前端流程,之前的案例也分析过个错误的原理图必然会导致
    的头像 发表于 12-24 13:22 2366次阅读

    STM32程序无法进入main函数的解决方法

    很多人在基于STM32单片机项目开发过程中,会遇到STM32程序无法进入main的现象,在这篇文将分享STM32程序无法进入main函数的解
    的头像 发表于 07-22 16:18 1.6w次阅读

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

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

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

    main函数是C程序入口函数,即程序的执行是从main
    发表于 08-07 17:26 1133次阅读
    探究<b class='flag-5'>一</b>下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个难对付

    为什么Python没有main函数

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

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

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

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

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