环球消息!main函数不一定就是程序入口

2023-06-15 17:10:14 来源:学益得智能硬件


(相关资料图)

我们都知道,main函数是C程序的入口,那这个入口能不能修改?

#include int main(){    return 0;}
答案肯定是可以的,毕竟这个入口也是人为规定的。 编译分为4个步骤,预处理、编译、汇编、链接。
gcc -E test.c -o test.igcc-Stest.i-otest.sgcc -c test.s -o test.ogcctest.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)) + (. & (CONSTANT (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 *crtend?.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 *crtend?.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.*) *(.data.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
审核编辑:汤梓红

标签:

上一篇:如何计算DC-DC的输入电容Cin与输出电容Cout
下一篇:最后一页