我们都知道,main函数是C程序的入口,那这个入口能不能修改?
gcc其实是一系列工具的合集,如果你想看到详细的步骤,编译的时候加上-v选项就行。
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就是刚才我们说的入口,不过得指定退出方式,要不然程序运行的时候会出问题。
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函数不一定就是程序入口
文章出处:【微信号:学益得智能硬件,微信公众号:学益得智能硬件】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
网友可能不一定很了解的时间单位
时间单位有的网友可能不一定很了解,特别是搞FPGA的网友,提供。 无限可分,没有最小。 s(秒)以下的时间单位(千进制): 1s (秒) =1000 ms (毫秒) 1ms (毫秒) =1000
发表于 01-11 11:59
资深电子工程师都不一定真正懂得如何使用电阻
电阻作为一种最基本电子元器件,广泛运用在各种电路中,通常我们也认为电阻是用法最简单的一种电子元器件,除了功率外,没有过多的讲究。如果今天我说就这个小小的电阻,许多资深电子工程师都不一定真正懂得如何用
发表于 06-24 16:41
gpio和中断断开发现唤醒后中断不一定及时响应是为什么?
断开,发现唤醒后中断不一定及时响应,即使响应了系统也死掉了,请问这种情况怎么解决呢?我在idle_profile的基础上做的实验。deepsleep模式的功耗有点儿高了。
发表于 06-12 16:42
C语言的main函数有几种写法?
从学习C语言开始就一直写个一个函数,那么你知道它的标准写法什么什么样吗? main函数,又称主函数,是程序执行的起点,我们平时写的
C语言编程中main()函数退出之后程序去哪儿了
对于嵌入式系统,如果没有运行RTOS,那么程序开发中的主函数main()需要通过某种机制使其永远愉快的运行下去,它没有终点。如果想从main函数
C语言编程中main函数退出后程序去哪儿了?
PIC 单片机语言程序进行跟踪,发现main() 函数最后一条语句为 reset,也就是单片机直接复位,这是 MAPLAB编译器根据 PIC 单片机特点增加的复位语句。
发表于 05-31 15:28
•226次阅读
评论