我們都知道,main函數(shù)是C程序的入口,那這個(gè)入口能不能修改?
gcc其實(shí)是一系列工具的合集,如果你想看到詳細(xì)的步驟,編譯的時(shí)候加上-v選項(xiàng)就行。
int main()
{
return 0;
}
答案肯定是可以的,畢竟這個(gè)入口也是人為規(guī)定的。編譯分為4個(gè)步驟,預(yù)處理、編譯、匯編、鏈接。
gcc -E test.c -o test.i
gcc-Stest.i-otest.s
gcc -c test.s -o test.o
gcctest.o-otest
最后一步鏈接的時(shí)候,需要用到一個(gè)叫做鏈接腳本的東西,鏈接腳本就是類似于這樣的一個(gè)文件:
OUTPUT_ARCH( "riscv" ) /* 代碼采用的是RISC-V架構(gòu)*/
ENTRY( _start ) /*代碼入口符號(hào)是_start,就是匯編啟動(dòng)函數(shù)的符號(hào)*/
MEMORY
{
/* 定義了一段起始地址為0x80000000,長(zhǎng)度為128MB的內(nèi)存區(qū)域,取名叫ram*/
ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M
}
SECTIONS
{
/* 所有輸入文件中的.text段、.text.*段都合在一起,組成輸出elf文件中的.text段;
* 此外,定義了兩個(gè)符號(hào)_text_start和_text_end ,注意符號(hào)'.'代表的是當(dāng)前地址;
* 生成的.text段被放在了ram這個(gè)內(nèi)存區(qū)域中。
*/
.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);
}
它規(guī)定了程序的各個(gè)部分在內(nèi)存中的位置,當(dāng)然里面也包含了程序的入口:
ENTRY( _start )
只要修改了入口的名字,就能實(shí)現(xiàn)我們想要的功能。那么問題又來了,平時(shí)在編譯的時(shí)候,都是直接:
gcc hello.c
這個(gè)過程也沒看到什么鏈接腳本。gcc其實(shí)是一系列工具的合集,如果你想看到詳細(xì)的步驟,編譯的時(shí)候加上-v選項(xiàng)就行。
gcc test.c -o test -v
最后一步鏈接的時(shí)候,都會(huì)默認(rèn)使用編譯器自帶的鏈接腳本。在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_*) }
}
我們把它導(dǎo)入一個(gè)文件中,后綴就叫l(wèi)ds吧。
ld--verbose>xx.lds
為了滿足它的語(yǔ)法規(guī)則,還得刪除一些東西,保留這兩條杠之間的內(nèi)容即可。看下鏈接腳本,找到ENTRY,就是程序的入口。
ENTRY(_start)
不過它并不是main函數(shù),而是_start函數(shù)。因?yàn)樵趫?zhí)行用戶的代碼之前,還有很多事情要做,這個(gè)后面在講。如果要修改程序的入口,只要把_start改掉就行,比如改成test,然后保存文件。
ENTRY(test)
寫個(gè)測(cè)試代碼,代碼中有main函數(shù),也有test函數(shù),test就是剛才我們說的入口,不過得指定退出方式,要不然程序運(yùn)行的時(shí)候會(huì)出問題。
voidtest()
{
printf("this is test ...
");
exit(0);
}
int main()
{
printf("helloworld
");
return 0;
}
編譯代碼,使用-T選項(xiàng),指定鏈接腳本。
gcctest.c-otest-Txx.lds
運(yùn)行程序,代碼執(zhí)行的是test函數(shù)。
root@Turbo:test# ./test
this is test ...
root@Turbo:test#
修改程序的入口還有一個(gè)更簡(jiǎn)單的方法,gcc編譯的時(shí)候,直接使用-e選項(xiàng),也能達(dá)到一樣的效果。
gcc test.c -o test -e test
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。
舉報(bào)投訴
-
程序
+關(guān)注
關(guān)注
117文章
3797瀏覽量
81439 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4346瀏覽量
62990 -
編譯
+關(guān)注
關(guān)注
0文章
661瀏覽量
33060
原文標(biāo)題:main函數(shù)不一定就是程序入口
文章出處:【微信號(hào):學(xué)益得智能硬件,微信公眾號(hào):學(xué)益得智能硬件】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
網(wǎng)友可能不一定很了解的時(shí)間單位
時(shí)間單位有的網(wǎng)友可能不一定很了解,特別是搞FPGA的網(wǎng)友,提供。 無限可分,沒有最小。 s(秒)以下的時(shí)間單位(千進(jìn)制): 1s (秒) =1000 ms (毫秒) 1ms (毫秒) =1000
發(fā)表于 01-11 11:59
gpio和中斷斷開發(fā)現(xiàn)喚醒后中斷不一定及時(shí)響應(yīng)是為什么?
斷開,發(fā)現(xiàn)喚醒后中斷不一定及時(shí)響應(yīng),即使響應(yīng)了系統(tǒng)也死掉了,請(qǐng)問這種情況怎么解決呢?我在idle_profile的基礎(chǔ)上做的實(shí)驗(yàn)。deepsleep模式的功耗有點(diǎn)兒高了。
發(fā)表于 06-12 16:42
為什么高速USB并不一定表示手機(jī)擁有高速性能
為什么高速USB并不一定表示手機(jī)擁有高速性能
數(shù)碼多媒體向日用電器的發(fā)展改變了消費(fèi)者接觸和享受多媒體娛樂節(jié)目的方式?,F(xiàn)在消費(fèi)者可以通過撲克牌大小的設(shè)備來
發(fā)表于 01-04 11:21
?649次閱讀
![為什么高速USB并<b class='flag-5'>不一定</b>表示手機(jī)擁有高速性能](https://file1.elecfans.com//web2/M00/A5/98/wKgZomUMOSiAMSneAAFvoNDxVP8002.jpg)
C語(yǔ)言程序的main函數(shù)免費(fèi)下載
本文檔的主要內(nèi)容詳細(xì)介紹的是C語(yǔ)言程序的main函數(shù)免費(fèi)下載。
發(fā)表于 09-26 14:48
?3次下載
只有潔碧才是全民信賴的水牙線品牌嗎?那可不一定!
身體健康就一定要保證口腔健康。使用水牙線已經(jīng)是很多西方家庭會(huì)使用的清潔口腔內(nèi)部的辦法,對(duì)于國(guó)內(nèi)的消費(fèi)者來說,水牙線還算是新鮮玩意,所以面對(duì)多種多樣的水牙線品牌,覺得只有潔碧,這個(gè)水牙線的創(chuàng)始品牌才是值得信賴的,那可不一定
發(fā)表于 04-16 20:31
?502次閱讀
C語(yǔ)言的main函數(shù)有幾種寫法?
從學(xué)習(xí)C語(yǔ)言開始就一直寫個(gè)一個(gè)函數(shù),那么你知道它的標(biāo)準(zhǔn)寫法什么什么樣嗎? main函數(shù),又稱主函數(shù)
正確的原理圖不一定能產(chǎn)生正確的 PCB 設(shè)計(jì)
作者:黃剛一個(gè)“xue淋淋”的案例告訴大家:正確的原理圖不一定就能產(chǎn)生正確的PCB設(shè)計(jì)。原理圖設(shè)計(jì)與PCB設(shè)計(jì)都是研發(fā)流程中的必經(jīng)階段,我們知道,原理圖設(shè)計(jì)是PCB設(shè)計(jì)的前端流程,之前的案例也分析過一個(gè)錯(cuò)誤的原理圖必然會(huì)導(dǎo)致
STM32程序無法進(jìn)入main函數(shù)的解決方法
很多人在基于STM32單片機(jī)項(xiàng)目開發(fā)過程中,會(huì)遇到STM32程序無法進(jìn)入main的現(xiàn)象,在這篇文將分享STM32程序無法進(jìn)入main函數(shù)的解
D語(yǔ)言編寫單片(STM32F401cc)機(jī)應(yīng)用需要用到的技巧 - 主入口函數(shù)
D語(yǔ)言編寫單片機(jī)應(yīng)用需要用到的技巧 - 主入口函數(shù)入口函數(shù)入口函數(shù)單片機(jī)
發(fā)表于 11-29 21:06
?13次下載
![D語(yǔ)言編寫單片(STM32F401cc)機(jī)應(yīng)用需要用到的技巧 - 主<b class='flag-5'>入口</b><b class='flag-5'>函數(shù)</b>](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
探究一下C語(yǔ)言中main函數(shù)各種不同的寫法
main函數(shù)是C程序的入口函數(shù),即程序的執(zhí)行是從main
發(fā)表于 08-07 17:26
?1177次閱讀
![探究<b class='flag-5'>一</b>下C語(yǔ)言中<b class='flag-5'>main</b><b class='flag-5'>函數(shù)</b>各種不同的寫法](https://file.elecfans.com/web2/M00/5D/00/pYYBAGLvhKyAVgBDAAKg_fw4wfs226.png)
為什么Python沒有main函數(shù)?
今天的文章中,我們來討論一下為什么有的編程語(yǔ)言有main函數(shù),而Python為什么沒有main函數(shù)。
發(fā)表于 08-17 11:47
?349次閱讀
c語(yǔ)言源程序main函數(shù)的位置
C語(yǔ)言源程序中的main函數(shù)是程序的入口點(diǎn),它被認(rèn)為是C語(yǔ)言程序的起點(diǎn)。在執(zhí)行
GD32 MCU啟動(dòng)后如何運(yùn)行到main函數(shù)
GD32 MCU啟動(dòng)后如何運(yùn)行到main函數(shù)入口?你是否也有這樣的疑慮。在執(zhí)行到main函數(shù)之前MCU干了哪些事情呢?下面為大家解答。
![GD32 MCU啟動(dòng)后如何運(yùn)行到<b class='flag-5'>main</b><b class='flag-5'>函數(shù)</b>](https://file1.elecfans.com/web2/M00/BD/77/wKgaomWkkcSAE23_AABJpQ74rO8590.png)
評(píng)論