對大多數童鞋來說理解編譯器將.c文件編譯為.o文件并不大困難,但是卻難以明白最后鏈接的過程是什么作用和為什么要這樣做? 還有就是我們在樣例工程中啟動的文件為什么是自己編寫的,它又怎樣做到將程序入口引導到main函數上,那么在這篇中我們就來深入的討論下這兩個話題。
鏈接器
鏈接的過程
首先,想要明白鏈接器的工作原理我們還是要來深入的看看整個編譯過程中具體的方式和原理。 我想大家都知道高級語言出現之前我們所用的匯編語言是除機器碼外最接近硬件的語言。使用匯編的代碼甚至可以很容易的手動轉換為機器代碼。那么接下來的介紹就需要童鞋們多少了解一點匯編程序了(如8051的匯編)。 在單片機執行的過程中命令被執行的順序只有兩種:順序執行和根據指令跳轉執行位置。在匯編的代碼中,良好的寫法是把各個函數分塊放在儲存的不同位置上,并在前面寫上程序的標號 (如:“START:”),最后由編譯器將START程序處的地址裝入寫有 START標號跳轉指令的地方。 由此,我們就可以理解C語言被編譯為二進制執行文件的過程了,首先每個C文件都被編譯為了.o的,帶有未解析地址的中間文件,而后工具鏈的鏈接器將所有C文件的.o文件鏈接將他們有序的排列到儲存中,并將他們個個函數處的地址解析使得其他不同地方的函數能夠跳轉到該函數的入口地址,由此一個有序排列的可被單片機執行的文件便生成了。 至于其中各個.c文件產生的功能在單片機儲存中的排列順序和地址位置,在最后我們鏈接器工作產生的.map文件中是有顯示的,如下面從樣例工程中.map文件中復制的片段:.isr_vector0x080000000x134 0x08000000.=ALIGN(0x4) *(.isr_vector) .isr_vector0x080000000x134./USER/CoIDE_startup.o 0x08000000g_pfnVectors 0x08000134.=ALIGN(0x4) .text0x080001340x1464 0x08000134.=ALIGN(0x4) *(.text) .text0x080001340x5c/home/yangliu/Library/gcc-arm-none-eabi-5_4-2016q3/bin/../lib/gcc/arm-none-eabi/5.4.1/armv7-m/crtbegin.o .text0x080001900x80./USER/main.o 0x08000190main .text0x080002100x68./USER/CoIDE_startup.o 0x08000210Reset_Handler 0x08000210Default_Reset_Handler 0x08000268EXTI2_IRQHandler 0x08000268TIM8_TRG_COM_IRQHandler 0x08000268TIM8_CC_IRQHandler 0x08000268TIM1_CC_IRQHandler 0x08000268TIM6_IRQHandler 0x08000268PVD_IRQHandler 0x08000268SDIO_IRQHandler 0x08000268EXTI3_IRQHandler 0x08000268EXTI0_IRQHandler 0x08000268I2C2_EV_IRQHandler 0x08000268ADC1_2_IRQHandler123456789101112131415161718192021222324252627 所以我們的gcc鏈接器就是用來做這個工作的,當然不只是gcc的鏈接器,世上所有c程序的編譯工具鏈應該都是以這種理念設計的。。當然不排除我見識少,沒見過特殊的。
工具鏈中鏈接器的用法
在實際中,鏈接器的執行程序實際上是arm-none-eabi-ld這個文件,但是我再實際的編寫過程中在遇到.c和.cpp文件混合的工程中,ld會在鏈接過程中報錯。而對此官方的說明是推薦使用arm-none-eabi-gcc指令來鏈接工程,它會自動的調用ld程序且不會出現上面這種情況,所以接下來我們都是以arm-none-eabi-gcc指令來介紹鏈接器工作的。$(CC)$(C_OBJ)-Tstm32_f103ze_gcc.ld-o$(TARGET).elf-mthumb-mcpu=cortex-m3-Wl,--start-group-lc-lm-Wl,--end-group-specs=nano.specs-specs=nosys.specs-static-Wl,-cref,-u,Reset_Handler-Wl,-Map=Project.map-Wl,--gc-sections-Wl,--defsym=malloc_getpagesize_P=0x80 1 在上面這段截取自樣例工程makefile的代碼片中,我們可以看到在最后生成.elf文件時的指令。變量CC為arm-none-eabi-gcc,變量OBJ為所有.o文件。**-o xx.elf**為鏈接.o文件生成.elf文件。
ld文件
在鏈接的過過程中與編譯過程相比其中顯著的與編譯指令不同的便是 -T xx.ld。 在這里 -T xx.ld實際上是調用了一個.ld的文件,那么.ld文件是做什么的呢? 這里就比較高深了,在51單片機中我們知道最后在生成代碼后51單片機內存中會有如 code、xdata、data的區段,來講代碼中執行部分、變量部分等分區塊放置,而.ld就是一種鏈接器使用的規則性文件,他告訴鏈接器單片機系統的ROM、RAM的地址和他們的大小等信息,并指示鏈接器將什么代碼保存在什么位置。 對于.ld文件它是有一套自己的語法及設置參數的規則的,大家可以不具體作了解,但求看懂其中一部分的信息。/*EntryPoint*/ ENTRY(Reset_Handler) /*Highestaddressoftheusermodestack*/ _estack=0x20010000;/*endof64KRAM*/ /*Generatealinkerrorifheapandstackdon'tfitintoRAM*/ _Min_Heap_Size=0;/*requiredamountofheap*/ _Min_Stack_Size=0x200;/*requiredamountofstack*/ /*Specifythememoryareas*/ MEMORY { FLASH(rx):ORIGIN=0x08000000,LENGTH=512K RAM(xrw):ORIGIN=0x20000000,LENGTH=64K MEMORY_B1(rx):ORIGIN=0x60000000,LENGTH=0K } SECTIONS { /*ThestartupcodegoesfirstintoFLASH*/ .isr_vector: { .=ALIGN(4); KEEP(*(.isr_vector))/*Startupcode*/ .=ALIGN(4); }>FLASH /*TheprogramcodeandotherdatagoesintoFLASH*/ .text: { .=ALIGN(4); *(.text)/*.textsections(code)*/ *(.text*)/*.text*sections(code)*/ *(.glue_7)/*gluearmtothumbcode*/ *(.glue_7t)/*gluethumbtoarmcode*/ *(.eh_frame) KEEP(*(.init)) KEEP(*(.fini)) .=ALIGN(4); _etext=.;/*defineaglobalsymbolsatendofcode*/ }>FLASH /*ConstantdatagoesintoFLASH*/ .rodata: { .=ALIGN(4); *(.rodata)/*.rodatasections(constants,strings,etc.)*/ *(.rodata*)/*.rodata*sections(constants,strings,etc.)*/ .=ALIGN(4); }>FLASH .ARM.extab:{*(.ARM.extab*.gnu.linkonce.armextab.*)}>FLASH .ARM:{ __exidx_start=.; *(.ARM.exidx*) __exidx_end=.; }>FLASH .preinit_array: { PROVIDE_HIDDEN(__preinit_array_start=.); KEEP(*(.preinit_array*)) PROVIDE_HIDDEN(__preinit_array_end=.); }>FLASH .init_array: { PROVIDE_HIDDEN(__init_array_start=.); KEEP(*(SORT(.init_array.*))) KEEP(*(.init_array*)) PROVIDE_HIDDEN(__init_array_end=.); }>FLASH .fini_array: { PROVIDE_HIDDEN(__fini_array_start=.); KEEP(*(SORT(.fini_array.*))) KEEP(*(.fini_array*)) PROVIDE_HIDDEN(__fini_array_end=.); }>FLASH /*usedbythestartuptoinitializedata*/ _sidata=LOADADDR(.data); /*InitializeddatasectionsgoesintoRAM,loadLMAcopyaftercode*/ .data: { .=ALIGN(4); _sdata=.;/*createaglobalsymbolatdatastart*/ *(.data)/*.datasections*/ *(.data*)/*.data*sections*/ .=ALIGN(4); _edata=.;/*defineaglobalsymbolatdataend*/ }>RAMAT>FLASH /*Uninitializeddatasection*/ .=ALIGN(4); .bss: { /*Thisisusedbythestartupinordertoinitializethe.bsssecion*/ _sbss=.;/*defineaglobalsymbolatbssstart*/ __bss_start__=_sbss; *(.bss) *(.bss*) *(COMMON) .=ALIGN(4); _ebss=.;/*defineaglobalsymbolatbssend*/ __bss_end__=_ebss; }>RAM /*User_heap_stacksection,usedtocheckthatthereisenoughRAMleft*/ ._user_heap_stack: { .=ALIGN(4); PROVIDE(end=.); PROVIDE(_end=.); .=.+_Min_Heap_Size; .=.+_Min_Stack_Size; .=ALIGN(4); }>RAM /*MEMORY_bank1section,codemustbelocatedhereexplicitly*/ /*Example:externintfoo(void)__attribute__((section(".mb1text")));*/ .memory_b1_text: { *(.mb1text)/*.mb1textsections(code)*/ *(.mb1text*)/*.mb1text*sections(code)*/ *(.mb1rodata)/*read-onlydata(constants)*/ *(.mb1rodata*) }>MEMORY_B1 /*Removeinformationfromthestandardlibraries*/ /DISCARD/: { libc.a(*) libm.a(*) libgcc.a(*) } .ARM.attributes0:{*(.ARM.attributes)} }123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 至于鏈接時其他的鏈接參數大部分和編譯參數相同,不同的也就是:
--start-group-lc-lm-Wl,--end-group-specs=nano.specs-specs=nosys.specs-static-Wl,-cref,-u,Reset_Handler-Wl,-Map=Project.map-Wl,--gc-sections-Wl,--defsym=malloc_getpagesize_P=0x80 1 對于這些指令我只是大致的清楚是什么,但具體的一些參數我也不大了解,如果大家有興趣可以自己檢索一下,或者最好的辦法就是到工具鏈中的說明文檔尋找說明。 在我們實際的工程建立及編寫中,我們使用的都是從別處找來的ld文件,在樣例工程中的.ld文件只要在內存大小堆棧等位置上根據stm32具體的型號稍作修改就可以使用了。 或者在之后我們介紹libopencm3的驅動庫中,其作者就有寫好的所有芯片型號的ld文件,我們也可以從那里復制并修改以用于我們自己的工程。其中ld文件中一些變量如堆棧大小等我們會在講解啟動文件的過程中來解析,因為啟動文件和ld文件中的東西息息相關。
啟動文件
很多剛接觸stm32不久的童鞋對stm32的啟動文件的印象大多就是教程里的一句話:啟動文件就是stm32在執行main函數前將系統初始化并把PC(即程序計數器,也就是當前執行代碼位置的指針)設置到main函數的文件。確實在KEIL或IAR之類的集成開發環境中我們不必關心啟動文件的存在,但是在我們的gcc的使用中,我們就需要去理解這個文件了。 在樣例工程中,我放置的是一個從CooCox開源集成開發環境中拷貝修改的啟動文件,在USER目錄下的CoIDE_startup.c,這里我就不放文件的內容了,我們只去其中一部分來講。 想要理解啟動代碼,首先我們需要看看GNU編譯器的與其他編譯器不同的新特性之一:*_attribute*((xxx)),在gcc中attribute關鍵詞用于為函數或變量等賦予特性,就像MDK中的weak 說明符類似,只不過attribute的使用更具多樣性且靈活。 其次我們要知道,在我們使用的Cortex-M3內核中,程序執行的最開始會從ROM首地址的第一位取出MSP的數值(即棧頂地址指針寄存器),然后會在第二位取出復位中斷函數的地址,并跳轉過去。且在一般來說,單片機系統的所有中斷向量表初始時會放在ROM的最前段,所以我們定義了一個函數指針數組在堆棧初始值的后方,構成了這樣一個被裝入ROM首段地址的數據:__attribute__((used,section(".isr_vector"))) void(*constg_pfnVectors[])(void)= { /*----------CoreExceptions-------------------------------------------------*/ (void*)&pulStack[STACK_SIZE],/*! Reset_Handler,/*! NMI_Handler,/*! HardFault_Handler,/*! MemManage_Handler,/*! BusFault_Handler,/*! UsageFault_Handler,/*! 0,0,0,0,/*! SVC_Handler,/*! DebugMon_Handler,/*! 0,/*! PendSV_Handler,/*! SysTick_Handler,/*! /*----------ExternalExceptions---------------------------------------------*/ WWDG_IRQHandler,/*!?0:?Window?Watchdog??????????????????????*/ PVD_IRQHandler,/*!?1:?PVD?through?EXTI?Line?detect?????????*/ TAMPER_IRQHandler,/*!?2:?Tamper???????????????????????????????*/ RTC_IRQHandler,/*!?3:?RTC??????????????????????????????????*/ FLASH_IRQHandler,/*!?4:?Flash????????????????????????????????*/ RCC_IRQHandler,/*!?5:?RCC??????????????????????????????????*/ EXTI0_IRQHandler,/*!?6:?EXTI?Line?0??????????????????????????*/ EXTI1_IRQHandler,/*!?7:?EXTI?Line?1??????????????????????????*/ EXTI2_IRQHandler,/*!?8:?EXTI?Line?2??????????????????????????*/ EXTI3_IRQHandler,/*!?9:?EXTI?Line?3??????????????????????????*/ EXTI4_IRQHandler,/*!10:?EXTI?Line?4??????????????????????????*/ DMA1_Channel1_IRQHandler,/*!11:?DMA1?Channel?1???????????????????????*/ DMA1_Channel2_IRQHandler,/*!12:?DMA1?Channel?2???????????????????????*/ DMA1_Channel3_IRQHandler,/*!13:?DMA1?Channel?3???????????????????????*/ DMA1_Channel4_IRQHandler,/*!14:?DMA1?Channel?4???????????????????????*/ DMA1_Channel5_IRQHandler,/*!15:?DMA1?Channel?5???????????????????????*/ DMA1_Channel6_IRQHandler,/*!16:?DMA1?Channel?6???????????????????????*/ DMA1_Channel7_IRQHandler,/*!17:?DMA1?Channel?7???????????????????????*/ ADC1_2_IRQHandler,/*!18:?ADC1?&?ADC2??????????????????????????*/ USB_HP_CAN1_TX_IRQHandler,/*!19:?USB?High?Priority?or?CAN1?TX?????????*/ USB_LP_CAN1_RX0_IRQHandler,/*!20:?USB?Low??Priority?or?CAN1?RX0????????*/ CAN1_RX1_IRQHandler,/*!21:?CAN1?RX1?????????????????????????????*/ CAN1_SCE_IRQHandler,/*!22:?CAN1?SCE?????????????????????????????*/ EXTI9_5_IRQHandler,/*!23:?EXTI?Line?9..5???????????????????????*/ TIM1_BRK_IRQHandler,/*!24:?TIM1?Break???????????????????????????*/ TIM1_UP_IRQHandler,/*!25:?TIM1?Update??????????????????????????*/ TIM1_TRG_COM_IRQHandler,/*!26:?TIM1?Trigger?and?Commutation?????????*/ TIM1_CC_IRQHandler,/*!27:?TIM1?Capture?Compare?????????????????*/ TIM2_IRQHandler,/*!28:?TIM2?????????????????????????????????*/ TIM3_IRQHandler,/*!29:?TIM3?????????????????????????????????*/ TIM4_IRQHandler,/*!30:?TIM4?????????????????????????????????*/ I2C1_EV_IRQHandler,/*!31:?I2C1?Event???????????????????????????*/ I2C1_ER_IRQHandler,/*!32:?I2C1?Error???????????????????????????*/ I2C2_EV_IRQHandler,/*!33:?I2C2?Event???????????????????????????*/ I2C2_ER_IRQHandler,/*!34:?I2C2?Error???????????????????????????*/ SPI1_IRQHandler,/*!35:?SPI1?????????????????????????????????*/ SPI2_IRQHandler,/*!36:?SPI2?????????????????????????????????*/ USART1_IRQHandler,/*!37:?USART1???????????????????????????????*/ USART2_IRQHandler,/*!38:?USART2???????????????????????????????*/ USART3_IRQHandler,/*!39:?USART3???????????????????????????????*/ EXTI15_10_IRQHandler,/*!40:?EXTI?Line?15..10?????????????????????*/ RTCAlarm_IRQHandler,/*!41:?RTC?Alarm?through?EXTI?Line??????????*/ USBWakeUp_IRQHandler,/*!42:?USB?Wakeup?from?suspend??????????????*/ TIM8_BRK_IRQHandler,/*!43:?TIM8?Break???????????????????????????*/ TIM8_UP_IRQHandler,/*!44:?TIM8?Update??????????????????????????*/ TIM8_TRG_COM_IRQHandler,/*!45:?TIM8?Trigger?and?Commutation?????????*/ TIM8_CC_IRQHandler,/*!46:?TIM8?Capture?Compare?????????????????*/ ADC3_IRQHandler,/*!47:?ADC3?????????????????????????????????*/ FSMC_IRQHandler,/*!48:?FSMC?????????????????????????????????*/ SDIO_IRQHandler,/*!49:?SDIO?????????????????????????????????*/ TIM5_IRQHandler,/*!50:?TIM5?????????????????????????????????*/ SPI3_IRQHandler,/*!51:?SPI3?????????????????????????????????*/ UART4_IRQHandler,/*!52:?UART4????????????????????????????????*/ UART5_IRQHandler,/*!52:?UART5????????????????????????????????*/ TIM6_IRQHandler,/*!53:?TIM6?????????????????????????????????*/ TIM7_IRQHandler,/*!54:?TIM7?????????????????????????????????*/ DMA2_Channel1_IRQHandler,/*!55:?DMA2?Channel1????????????????????????*/ DMA2_Channel2_IRQHandler,/*!56:?DMA2?Channel2????????????????????????*/ DMA2_Channel3_IRQHandler,/*!57:?DMA2?Channel3????????????????????????*/ DMA2_Channel4_5_IRQHandler,/*!58:?DMA2?Channel4?&?Channel5?????????????*/ (void*)0xF108F85F/*! }; 注意在數組的attribute的修飾中,它將函數的位置規定在了[section(“.isr_vector”)]的位置,而[.isr_vector]則在ld文件中定義在FLASH開始的地方:
/*ThestartupcodegoesfirstintoFLASH*/ .isr_vector: { .=ALIGN(4); KEEP(*(.isr_vector))/*Startupcode*/ .=ALIGN(4); }>FLASH 所以顯而易見的,在啟動后第二個周期里內核讀取了復位向量表的地址并跳轉了過去,所以單片機的啟動代碼必然存放于rest vector中,我們在啟動文件中找到復位函數:
#pragmaweakReset_Handler=Default_Reset_Handler voidDefault_Reset_Handler(void) { /*Initializedataandbss*/ unsignedlong*pulSrc,*pulDest; /*CopythedatasegmentinitializersfromflashtoSRAM*/ pulSrc=&_sidata; for(pulDest=&_sdata;pulDest&_edata;?) ??{ ????*(pulDest++)?=?*(pulSrc++); ??} ??/*Zerofillthebsssegment.Thisisdonewithinlineassemblysincethis willclearthevalueofpulDestifitisnotkeptinaregister.*/ __asm("ldrr0,=_sbss " "ldrr1,=_ebss " "movr2,#0 " ".thumb_func " "zero_loop: " "cmpr0,r1 " "itlt " "strltr2,[r0],#4 " "bltzero_loop"); /*Setupthemicrocontrollersystem.*/ SystemInit(); /*Calltheapplication'sentrypoint.*/ main(); } 在啟動函數中我們可以清晰地看到,在最后一步中,單片機的程序被轉入到了main函數的入口,那么在執行main函數之前,C語言,和內聯匯編程序干了什么呢?首先頭位置的C語言將終端向量表從ROM頭位置,復制到了RAM頭位置(即:0x20000000),這里在RAM中的終端向量表時間上沒有沒我們用到,當然這是因為在M3的內核中,它允許用戶在NIVC的寄存器中重新定義終端向量表的位置,我們可以使用
NVIC_SetVectorTable(NVIC_VectTab_FLASH,0); 這個函數來將終端向量表設置到到0x20000000位置。該功能實際上是用于方便裝有系統的環境中使用,可以加快終端響應的速度,同時可以快速的動態的更改終端處理的程序。 當然在我們的應用中并未使用到這一特性,所以此處的復制中斷向量表的操作是可以刪除的,它在此的作用只是為了防止用戶在程序中使用了重定向向量表語句而使得程序跑飛所添加的。因為終端向量是系統最基礎穩定性的保證,如果在硬件錯誤發生等中斷發生的情況下單片機無法正確的跳轉,會對代碼調試和系統穩定運行帶來嚴重的影響。 之后緊跟的這幾條匯編代碼實現的是:全局變量與靜態變量的初始化并將其從flash中調入內存,即在C語言運行全局變量與靜態變量的初始化操作。在此之后, SystemInit();函數被調用,配置好時鐘等參數。最后我們的main函數就可以執行啦~。 這便是是我們在這個例程中使用的啟動文件,而在keil工程中,這個文件是用匯編代碼寫成的,但這些文件功能都是一樣的,設置終端向量表,初始化全局與靜態變量,進入main函數,都是這樣的流程。 在gcc的環境中我們也可以是用匯編編寫這樣的文件,我們面前的選擇有很多,當然我們沒必要自己編寫這些鏈接文件和啟動代碼,在之后的實際的工程建立中我會告訴大家實際的方法。不過在此之前我們還是要先把基礎的內容學好再說。
其他的說明
在文件中我們看到了**_sidata、_sdata**等變量,這些變量在文件的前面部分被定義為外部:externunsignedlong_sidata;/*! externunsignedlong_sdata;/*! externunsignedlong_edata;/*! externunsignedlong_sbss;/*! externunsignedlong_ebss;/*! 而該文件卻并未包含任何.h文件,那么他們從哪來的呢?細心的同學可能已經注意到了,我們之前提到過,這些變量的定義實際上都來自于ld文件中,他們在ld文件中被定義,最后鏈接器會將他們轉換為實際的地址給我們的程序所使用的。 最后再說一下 attribute ((weak))屬性,該屬性表面其后的變量或是函數為弱申明,即在沒有其他申明情況下調用改函數,而如果其他地方申明了,則會頂替該函數。所以在啟動文件中,他們被用來修飾中斷處理函為中斷向量表提供一個默認的地址,而當用戶定義后,就將地址轉為用戶定義的位置。
總結
說了這么多,這也是我們在這個系列中比較難以理解的部分,因為涉及到了GNU C的特性和計算機編譯鏈接的最基礎的部分,還有Cortex-M3內核工作的方式,但是請大家仔細的去理解學習,如果看了這篇文章還不懂那就多查查相關的資料,當你理解并貫通 這些知識時,你會發現原來在單片機上c語言是這樣工作的,原來中斷系統是這么的重要,你會發現單片機在你的眼前是如此的透徹。 在最后,我們還要說說,其實很多同學目前掌握的都是一個很簡單的單片機應用方式,這都是被keil、IAR之流慣壞的,實際上在單片機背后,其實際的工作復雜而又充滿著精致的設計,這點我們會在之后的nuttx系統使用中見到。 那時你會發現原來我們使用的M3單片機還有這么多的我們之前沒用過的中斷,原來m3的內核如此強大。對此我推薦大家還是學一遍51單片機的匯編教程,當你理解和使用過匯編后,你會更容易理解未來的講解內容,同時也更容易理解此篇的內容。當然如果大家有興趣可以先自己看看由宋巖前輩翻譯的Cortex-M3 權威指南,來提前感受一下Cortex-M3內核的魅力。
審核編輯:湯梓紅
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。
舉報投訴
-
單片機
+關注
關注
6039文章
44574瀏覽量
636319 -
STM32
+關注
關注
2270文章
10906瀏覽量
356530 -
編譯器
+關注
關注
1文章
1635瀏覽量
49171
原文標題:STM32高級開發——鏈接器與啟動文件
文章出處:【微信號:技術讓夢想更偉大,微信公眾號:技術讓夢想更偉大】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
常用編輯器之GCC編譯器
,輸出結果是一樣的。elf@ubuntu:~/work/example/hello$ gcc hello.c4、GCC編譯過程GCC編譯器的編譯
發表于 08-24 11:05
matlab的m文件編譯為dll文件
');fprintf(fid,'%f',y);fclose('all');使用過mcc命令,但不能成功編譯,是不是mcc編譯器有局限性?對這樣的m文件該用什么方法進行編譯?求高手指導!
發表于 05-18 21:21
使用編譯器將預處理文件的編譯的命令是什么?
如果你使用的是集成開發環境,那么你點擊編譯按鈕就可生成可執行文件,然后點擊運行即可運行。那么,你知道從源代碼到可執行文件經歷了哪些過程嗎。僅僅是編譯
ASM源文件編譯器軟件免費下載
本文檔的主要內容詳細介紹的是ASM源文件編譯器軟件免費下載。適用于32位計算機,asm編譯器,將ASM51.exe放在同一目錄,在dos狀態編譯
發表于 08-07 08:00
?5次下載
華為方舟編譯器使用指南
當前方舟編譯器支持 Java/Kotlin 程序字節碼的前端輸入,其它編程語言的支持(如 C/C++/JS 等)還在規劃中,方舟編譯器的中間表示(IR)轉換
發表于 10-14 14:56
?1次下載
淺談hightec的編譯鏈接文件
hightec的編譯鏈接文件的后綴為ld,因此后文簡稱ld文件,ld文件主要分為三個部分:宏定義、MEMORY命令、SECTIONS命令。
評論