如何在 Windows 上使用 Clang/LLVM 交叉编译 STM32L4 cortex-m4 mcu
How to crosscompile for STM32L4 cortex-m4 mcu using Clang/LLVM on Windows
我一直在尝试编译一个简单的应用程序,在 Windows 上使用 Clang 为 stm32l4 micro 实现 USB CDC 设备。
该代码由 STMCubeMX 生成,并进行了一些小的更改,因此它只是回显通过虚拟 com 端口发送的任何内容。
使用来自 ubuntu 的 arm-none-eabi-gcc 工具链使用 generated make file works just fine. After flashing to the micro controller it does exactly what it's supposed to do. To compile with clang from windows, i made this fancy build script 进行编译(从 asm/c 来源生成对象,link 那些. 使用这些选项:
set TARGET_TRIPE=--target=arm-none-eabi
set ARCH=-march=armv7e-m
set CPU=-mcpu=cortex-m4
set FPU=-mfpu=fpv4-sp-d16 -mfloat-abi=hard
set MCU=%TARGET_TRIPE% %ARCH% %CPU% %FPU% -mthumb -mlittle-endian
set COMMON_FLAGS=-Wall %OPTIMIZATIONS% --sysroot=%SYSROOT% -fdata-sections -ffunction-sections -O0
set C_FLAGS=%MCU% %C_DEFINES% %COMMON_FLAGS% %C_INCLUDES% -c
set ASM_FLAGS=%MCU% %ASM_DEFINES% %COMMON_FLAGS% -x assembler-with-cpp -c
set LD_FLAGS=%MCU% %COMMON_FLAGS% -nostdlib -nostartfiles -fuse-ld=lld -T%LD_SCRIPT% -Wl,-Map=%BUILD_DIR%/%PROJECT_NAME%.map,--cref,--gc-sections %LIBDIRS% %LIBS%
)。
编译成功,固件镜像看起来没问题,但是烧写后,微控制器什么也没做。甚至外部 crystal 都无法启动,因此固件映像显然有问题。我不知道,为什么会这样。 Clang/GCC. Repo 生成的二进制文件 source.
编辑:clang 二进制图像大约有 400MB 大,这似乎也不对。
编辑 2:
这是我正在使用的链接描述文件:
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x2000A000; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
/* Specify the memory areas */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 40K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 128K
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(8);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(8);
} >FLASH
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(8);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(8);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(8);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(8);
} >FLASH
.ARM.extab :
{
. = ALIGN(8);
*(.ARM.extab* .gnu.linkonce.armextab.*)
. = ALIGN(8);
} >FLASH
.ARM : {
. = ALIGN(8);
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
. = ALIGN(8);
} >FLASH
.preinit_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(8);
} >FLASH
.init_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(8);
} >FLASH
.fini_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(8);
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(8);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(8);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}
这是我正在使用的启动代码:
.syntax unified
.cpu cortex-m4
.fpu softvfp
.thumb
.global g_pfnVectors
.global Default_Handler
/* start address for the initialization values of the .data section.
defined in linker script */
.word _sidata
/* start address for the .data section. defined in linker script */
.word _sdata
/* end address for the .data section. defined in linker script */
.word _edata
/* start address for the .bss section. defined in linker script */
.word _sbss
/* end address for the .bss section. defined in linker script */
.word _ebss
.equ BootRAM, 0xF1E0F85F
/**
* @brief This is the code that gets called when the processor first
* starts execution following a reset event. Only the absolutely
* necessary set is performed, after which the application
* supplied main() routine is called.
* @param None
* @retval : None
*/
.section .text.Reset_Handler
.weak Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
ldr sp, =_estack /* Set stack pointer */
/* Copy the data segment initializers from flash to SRAM */
movs r1, #0
b LoopCopyDataInit
CopyDataInit:
ldr r3, =_sidata
ldr r3, [r3, r1]
str r3, [r0, r1]
adds r1, r1, #4
LoopCopyDataInit:
ldr r0, =_sdata
ldr r3, =_edata
adds r2, r0, r1
cmp r2, r3
bcc CopyDataInit
ldr r2, =_sbss
b LoopFillZerobss
/* Zero fill the bss segment. */
FillZerobss:
movs r3, #0
str r3, [r2], #4
LoopFillZerobss:
ldr r3, = _ebss
cmp r2, r3
bcc FillZerobss
/* Call the clock system intitialization function.*/
bl SystemInit
/* Call CRT entry point */
//bl _mainCRTStartup
bl __libc_init_array
bl main
LoopForever:
b LoopForever
.size Reset_Handler, .-Reset_Handler
/**
* @brief This is the code that gets called when the processor receives an
* unexpected interrupt. This simply enters an infinite loop, preserving
* the system state for examination by a debugger.
*
* @param None
* @retval : None
*/
.section .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
b Infinite_Loop
.size Default_Handler, .-Default_Handler
/******************************************************************************
*
* The minimal vector table for a Cortex-M4. Note that the proper constructs
* must be placed on this to ensure that it ends up at physical address
* 0x0000.0000.
*
******************************************************************************/
.section .isr_vector,"a",%progbits
.type g_pfnVectors, %object
.size g_pfnVectors, .-g_pfnVectors
g_pfnVectors:
.word _estack
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
.word 0
.word 0
.word 0
.word 0
.word SVC_Handler
.word DebugMon_Handler
.word 0
.word PendSV_Handler
.word SysTick_Handler
.word WWDG_IRQHandler
.word PVD_PVM_IRQHandler
.word TAMP_STAMP_IRQHandler
.word RTC_WKUP_IRQHandler
.word FLASH_IRQHandler
.word RCC_IRQHandler
.word EXTI0_IRQHandler
.word EXTI1_IRQHandler
.word EXTI2_IRQHandler
.word EXTI3_IRQHandler
.word EXTI4_IRQHandler
.word DMA1_Channel1_IRQHandler
.word DMA1_Channel2_IRQHandler
.word DMA1_Channel3_IRQHandler
.word DMA1_Channel4_IRQHandler
.word DMA1_Channel5_IRQHandler
.word DMA1_Channel6_IRQHandler
.word DMA1_Channel7_IRQHandler
.word ADC1_2_IRQHandler
.word 0
.word 0
.word 0
.word 0
.word EXTI9_5_IRQHandler
.word TIM1_BRK_TIM15_IRQHandler
.word TIM1_UP_TIM16_IRQHandler
.word TIM1_TRG_COM_IRQHandler
.word TIM1_CC_IRQHandler
.word TIM2_IRQHandler
.word 0
.word 0
.word I2C1_EV_IRQHandler
.word I2C1_ER_IRQHandler
.word I2C2_EV_IRQHandler
.word I2C2_ER_IRQHandler
.word SPI1_IRQHandler
.word SPI2_IRQHandler
.word USART1_IRQHandler
.word USART2_IRQHandler
.word USART3_IRQHandler
.word EXTI15_10_IRQHandler
.word RTC_Alarm_IRQHandler
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word TIM6_IRQHandler
.word 0
.word DMA2_Channel1_IRQHandler
.word DMA2_Channel2_IRQHandler
.word DMA2_Channel3_IRQHandler
.word DMA2_Channel4_IRQHandler
.word DMA2_Channel5_IRQHandler
.word 0
.word 0
.word 0
.word COMP_IRQHandler
.word LPTIM1_IRQHandler
.word LPTIM2_IRQHandler
.word USB_IRQHandler
.word DMA2_Channel6_IRQHandler
.word DMA2_Channel7_IRQHandler
.word LPUART1_IRQHandler
.word QUADSPI_IRQHandler
.word I2C3_EV_IRQHandler
.word I2C3_ER_IRQHandler
.word 0
.word 0
.word 0
.word TSC_IRQHandler
.word 0
.word AES_IRQHandler
.word RNG_IRQHandler
.word FPU_IRQHandler
.word CRS_IRQHandler
/*******************************************************************************
*
* Provide weak aliases for each Exception handler to the Default_Handler.
* As they are weak aliases, any function with the same name will override
* this definition.
*
*******************************************************************************/
.weak NMI_Handler
.thumb_set NMI_Handler,Default_Handler
.weak HardFault_Handler
.thumb_set HardFault_Handler,Default_Handler
.weak MemManage_Handler
.thumb_set MemManage_Handler,Default_Handler
.weak BusFault_Handler
.thumb_set BusFault_Handler,Default_Handler
.weak UsageFault_Handler
.thumb_set UsageFault_Handler,Default_Handler
.weak SVC_Handler
.thumb_set SVC_Handler,Default_Handler
.weak DebugMon_Handler
.thumb_set DebugMon_Handler,Default_Handler
.weak PendSV_Handler
.thumb_set PendSV_Handler,Default_Handler
.weak SysTick_Handler
.thumb_set SysTick_Handler,Default_Handler
.weak WWDG_IRQHandler
.thumb_set WWDG_IRQHandler,Default_Handler
.weak PVD_PVM_IRQHandler
.thumb_set PVD_PVM_IRQHandler,Default_Handler
.weak TAMP_STAMP_IRQHandler
.thumb_set TAMP_STAMP_IRQHandler,Default_Handler
.weak RTC_WKUP_IRQHandler
.thumb_set RTC_WKUP_IRQHandler,Default_Handler
.weak FLASH_IRQHandler
.thumb_set FLASH_IRQHandler,Default_Handler
.weak RCC_IRQHandler
.thumb_set RCC_IRQHandler,Default_Handler
.weak EXTI0_IRQHandler
.thumb_set EXTI0_IRQHandler,Default_Handler
.weak EXTI1_IRQHandler
.thumb_set EXTI1_IRQHandler,Default_Handler
.weak EXTI2_IRQHandler
.thumb_set EXTI2_IRQHandler,Default_Handler
.weak EXTI3_IRQHandler
.thumb_set EXTI3_IRQHandler,Default_Handler
.weak EXTI4_IRQHandler
.thumb_set EXTI4_IRQHandler,Default_Handler
.weak DMA1_Channel1_IRQHandler
.thumb_set DMA1_Channel1_IRQHandler,Default_Handler
.weak DMA1_Channel2_IRQHandler
.thumb_set DMA1_Channel2_IRQHandler,Default_Handler
.weak DMA1_Channel3_IRQHandler
.thumb_set DMA1_Channel3_IRQHandler,Default_Handler
.weak DMA1_Channel4_IRQHandler
.thumb_set DMA1_Channel4_IRQHandler,Default_Handler
.weak DMA1_Channel5_IRQHandler
.thumb_set DMA1_Channel5_IRQHandler,Default_Handler
.weak DMA1_Channel6_IRQHandler
.thumb_set DMA1_Channel6_IRQHandler,Default_Handler
.weak DMA1_Channel7_IRQHandler
.thumb_set DMA1_Channel7_IRQHandler,Default_Handler
.weak ADC1_2_IRQHandler
.thumb_set ADC1_2_IRQHandler,Default_Handler
.weak EXTI9_5_IRQHandler
.thumb_set EXTI9_5_IRQHandler,Default_Handler
.weak TIM1_BRK_TIM15_IRQHandler
.thumb_set TIM1_BRK_TIM15_IRQHandler,Default_Handler
.weak TIM1_UP_TIM16_IRQHandler
.thumb_set TIM1_UP_TIM16_IRQHandler,Default_Handler
.weak TIM1_TRG_COM_IRQHandler
.thumb_set TIM1_TRG_COM_IRQHandler,Default_Handler
.weak TIM1_CC_IRQHandler
.thumb_set TIM1_CC_IRQHandler,Default_Handler
.weak TIM2_IRQHandler
.thumb_set TIM2_IRQHandler,Default_Handler
.weak I2C1_EV_IRQHandler
.thumb_set I2C1_EV_IRQHandler,Default_Handler
.weak I2C1_ER_IRQHandler
.thumb_set I2C1_ER_IRQHandler,Default_Handler
.weak I2C2_EV_IRQHandler
.thumb_set I2C2_EV_IRQHandler,Default_Handler
.weak I2C2_ER_IRQHandler
.thumb_set I2C2_ER_IRQHandler,Default_Handler
.weak SPI1_IRQHandler
.thumb_set SPI1_IRQHandler,Default_Handler
.weak SPI2_IRQHandler
.thumb_set SPI2_IRQHandler,Default_Handler
.weak USART1_IRQHandler
.thumb_set USART1_IRQHandler,Default_Handler
.weak USART2_IRQHandler
.thumb_set USART2_IRQHandler,Default_Handler
.weak USART3_IRQHandler
.thumb_set USART3_IRQHandler,Default_Handler
.weak EXTI15_10_IRQHandler
.thumb_set EXTI15_10_IRQHandler,Default_Handler
.weak RTC_Alarm_IRQHandler
.thumb_set RTC_Alarm_IRQHandler,Default_Handler
.weak TIM6_IRQHandler
.thumb_set TIM6_IRQHandler,Default_Handler
.weak DMA2_Channel1_IRQHandler
.thumb_set DMA2_Channel1_IRQHandler,Default_Handler
.weak DMA2_Channel2_IRQHandler
.thumb_set DMA2_Channel2_IRQHandler,Default_Handler
.weak DMA2_Channel3_IRQHandler
.thumb_set DMA2_Channel3_IRQHandler,Default_Handler
.weak DMA2_Channel4_IRQHandler
.thumb_set DMA2_Channel4_IRQHandler,Default_Handler
.weak DMA2_Channel5_IRQHandler
.thumb_set DMA2_Channel5_IRQHandler,Default_Handler
.weak COMP_IRQHandler
.thumb_set COMP_IRQHandler,Default_Handler
.weak LPTIM1_IRQHandler
.thumb_set LPTIM1_IRQHandler,Default_Handler
.weak LPTIM2_IRQHandler
.thumb_set LPTIM2_IRQHandler,Default_Handler
.weak USB_IRQHandler
.thumb_set USB_IRQHandler,Default_Handler
.weak DMA2_Channel6_IRQHandler
.thumb_set DMA2_Channel6_IRQHandler,Default_Handler
.weak DMA2_Channel7_IRQHandler
.thumb_set DMA2_Channel7_IRQHandler,Default_Handler
.weak LPUART1_IRQHandler
.thumb_set LPUART1_IRQHandler,Default_Handler
.weak QUADSPI_IRQHandler
.thumb_set QUADSPI_IRQHandler,Default_Handler
.weak I2C3_EV_IRQHandler
.thumb_set I2C3_EV_IRQHandler,Default_Handler
.weak I2C3_ER_IRQHandler
.thumb_set I2C3_ER_IRQHandler,Default_Handler
.weak TSC_IRQHandler
.thumb_set TSC_IRQHandler,Default_Handler
.weak AES_IRQHandler
.thumb_set AES_IRQHandler,Default_Handler
.weak RNG_IRQHandler
.thumb_set RNG_IRQHandler,Default_Handler
.weak FPU_IRQHandler
.thumb_set FPU_IRQHandler,Default_Handler
.weak CRS_IRQHandler
.thumb_set CRS_IRQHandler,Default_Handler
启动代码和 linker 脚本均由 STM32CubeMX 生成
这是从启动代码中调用的主要函数:
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USB_DEVICE_Init();
while (1)
{
}
}
初始化外围设备和 CDC usb 设备。
矢量反汇编table:
Disassembly of section .isr_vector:
08000000 <g_pfnVectors>:
8000000: 00 a0 adr r0, #0
8000002: 00 20 movs r0, #0
8000004: 81 53 strh r1, [r0, r6]
8000006: 00 08 lsrs r0, r0, #32
8000008: a5 04 lsls r5, r4, #18
800000a: 00 08 lsrs r0, r0, #32
800000c: a7 04 lsls r7, r4, #18
800000e: 00 08 lsrs r0, r0, #32
8000010: ab 04 lsls r3, r5, #18
8000012: 00 08 lsrs r0, r0, #32
8000014: af 04 lsls r7, r5, #18
8000016: 00 08 lsrs r0, r0, #32
8000018: b3 04 lsls r3, r6, #18
800001a: 00 08 lsrs r0, r0, #32
...
800002c: b7 04 lsls r7, r6, #18
800002e: 00 08 lsrs r0, r0, #32
8000030: b9 04 lsls r1, r7, #18
8000032: 00 08 lsrs r0, r0, #32
8000034: 00 00 movs r0, r0
8000036: 00 00 movs r0, r0
8000038: bb 04 lsls r3, r7, #18
800003a: 00 08 lsrs r0, r0, #32
800003c: bd 04 lsls r5, r7, #18
800003e: 00 08 lsrs r0, r0, #32
8000040: d1 53 strh r1, [r2, r7]
8000042: 00 08 lsrs r0, r0, #32
8000044: d1 53 strh r1, [r2, r7]
8000046: 00 08 lsrs r0, r0, #32
8000048: d1 53 strh r1, [r2, r7]
800004a: 00 08 lsrs r0, r0, #32
800004c: d1 53 strh r1, [r2, r7]
800004e: 00 08 lsrs r0, r0, #32
8000050: d1 53 strh r1, [r2, r7]
8000052: 00 08 lsrs r0, r0, #32
8000054: d1 53 strh r1, [r2, r7]
8000056: 00 08 lsrs r0, r0, #32
8000058: d1 53 strh r1, [r2, r7]
800005a: 00 08 lsrs r0, r0, #32
800005c: d1 53 strh r1, [r2, r7]
800005e: 00 08 lsrs r0, r0, #32
8000060: d1 53 strh r1, [r2, r7]
8000062: 00 08 lsrs r0, r0, #32
8000064: d1 53 strh r1, [r2, r7]
8000066: 00 08 lsrs r0, r0, #32
8000068: d1 53 strh r1, [r2, r7]
800006a: 00 08 lsrs r0, r0, #32
800006c: d1 53 strh r1, [r2, r7]
800006e: 00 08 lsrs r0, r0, #32
8000070: d1 53 strh r1, [r2, r7]
8000072: 00 08 lsrs r0, r0, #32
8000074: d1 53 strh r1, [r2, r7]
8000076: 00 08 lsrs r0, r0, #32
8000078: d1 53 strh r1, [r2, r7]
800007a: 00 08 lsrs r0, r0, #32
800007c: d1 53 strh r1, [r2, r7]
800007e: 00 08 lsrs r0, r0, #32
8000080: d1 53 strh r1, [r2, r7]
8000082: 00 08 lsrs r0, r0, #32
8000084: d1 53 strh r1, [r2, r7]
8000086: 00 08 lsrs r0, r0, #32
8000088: d1 53 strh r1, [r2, r7]
800008a: 00 08 lsrs r0, r0, #32
...
800009c: d1 53 strh r1, [r2, r7]
800009e: 00 08 lsrs r0, r0, #32
80000a0: d1 53 strh r1, [r2, r7]
80000a2: 00 08 lsrs r0, r0, #32
80000a4: d1 53 strh r1, [r2, r7]
80000a6: 00 08 lsrs r0, r0, #32
80000a8: d1 53 strh r1, [r2, r7]
80000aa: 00 08 lsrs r0, r0, #32
80000ac: d1 53 strh r1, [r2, r7]
80000ae: 00 08 lsrs r0, r0, #32
80000b0: d1 53 strh r1, [r2, r7]
80000b2: 00 08 lsrs r0, r0, #32
...
80000bc: d1 53 strh r1, [r2, r7]
80000be: 00 08 lsrs r0, r0, #32
80000c0: d1 53 strh r1, [r2, r7]
80000c2: 00 08 lsrs r0, r0, #32
80000c4: d1 53 strh r1, [r2, r7]
80000c6: 00 08 lsrs r0, r0, #32
80000c8: d1 53 strh r1, [r2, r7]
80000ca: 00 08 lsrs r0, r0, #32
80000cc: d1 53 strh r1, [r2, r7]
80000ce: 00 08 lsrs r0, r0, #32
80000d0: d1 53 strh r1, [r2, r7]
80000d2: 00 08 lsrs r0, r0, #32
80000d4: d1 53 strh r1, [r2, r7]
80000d6: 00 08 lsrs r0, r0, #32
80000d8: d1 53 strh r1, [r2, r7]
80000da: 00 08 lsrs r0, r0, #32
80000dc: d1 53 strh r1, [r2, r7]
80000de: 00 08 lsrs r0, r0, #32
80000e0: d1 53 strh r1, [r2, r7]
80000e2: 00 08 lsrs r0, r0, #32
80000e4: d1 53 strh r1, [r2, r7]
80000e6: 00 08 lsrs r0, r0, #32
...
8000118: d1 53 strh r1, [r2, r7]
800011a: 00 08 lsrs r0, r0, #32
800011c: 00 00 movs r0, r0
800011e: 00 00 movs r0, r0
8000120: d1 53 strh r1, [r2, r7]
8000122: 00 08 lsrs r0, r0, #32
8000124: d1 53 strh r1, [r2, r7]
8000126: 00 08 lsrs r0, r0, #32
8000128: d1 53 strh r1, [r2, r7]
800012a: 00 08 lsrs r0, r0, #32
800012c: d1 53 strh r1, [r2, r7]
800012e: 00 08 lsrs r0, r0, #32
8000130: d1 53 strh r1, [r2, r7]
8000132: 00 08 lsrs r0, r0, #32
...
8000140: d1 53 strh r1, [r2, r7]
8000142: 00 08 lsrs r0, r0, #32
8000144: d1 53 strh r1, [r2, r7]
8000146: 00 08 lsrs r0, r0, #32
8000148: d1 53 strh r1, [r2, r7]
800014a: 00 08 lsrs r0, r0, #32
800014c: c7 04 lsls r7, r0, #19
800014e: 00 08 lsrs r0, r0, #32
8000150: d1 53 strh r1, [r2, r7]
8000152: 00 08 lsrs r0, r0, #32
8000154: d1 53 strh r1, [r2, r7]
8000156: 00 08 lsrs r0, r0, #32
8000158: d1 53 strh r1, [r2, r7]
800015a: 00 08 lsrs r0, r0, #32
800015c: d1 53 strh r1, [r2, r7]
800015e: 00 08 lsrs r0, r0, #32
8000160: d1 53 strh r1, [r2, r7]
8000162: 00 08 lsrs r0, r0, #32
8000164: d1 53 strh r1, [r2, r7]
8000166: 00 08 lsrs r0, r0, #32
...
8000174: d1 53 strh r1, [r2, r7]
8000176: 00 08 lsrs r0, r0, #32
8000178: 00 00 movs r0, r0
800017a: 00 00 movs r0, r0
800017c: d1 53 strh r1, [r2, r7]
800017e: 00 08 lsrs r0, r0, #32
8000180: d1 53 strh r1, [r2, r7]
8000182: 00 08 lsrs r0, r0, #32
8000184: d1 53 strh r1, [r2, r7]
8000186: 00 08 lsrs r0, r0, #32
8000188: d1 53 strh r1, [r2, r7]
800018a: 00 08 lsrs r0, r0, #32
800018c: 00 00 movs r0, r0
800018e: 00 00 movs r0, r0
所以这激发了我尝试 clang/llvm 几年后...
现在我在Linux而不是Windows,但是你应该可以适应Windows(或者当然双启动Linux或者把Linux 在虚拟机或其他什么)。
源自 clang/llvm 站点上的构建说明
rm -rf /opt/llvmv6m
rm -rf llvm-project
git clone https://github.com/llvm/llvm-project.git
cd llvm-project
mkdir build
cd build
cmake -DLLVM_ENABLE_PROJECTS=clang -DCMAKE_CROSSCOMPILING=True -DCMAKE_INSTALL_PREFIX=/opt/llvmv6m -DLLVM_DEFAULT_TARGET_TRIPLE=armv6m-none-eabi -DLLVM_TARGET_ARCH=ARM -DLLVM_TARGETS_TO_BUILD=ARM -G "Unix Makefiles" ../llvm
make
sudo make install
我就是这样建造的。是的,我知道你的是 cortex-m4 所有的 cortex-ms(到目前为止)都支持 armv6-m,你可以很容易地制作这个 armv7m。是基于这些网页的实验,有趣的是,现在我不必在命令行上指定内容来指定体系结构或 cpu,很想知道这是否仍然是通用的 clang 交叉编译器,而 armv6m 只是默认值。无论如何...
这比简单的无限循环稍微复杂一点,但可以使用 gnu 中没有的 llvm 功能。
start.s
.thumb
.cpu cortex-m0
.globl _start
_start:
.word 0x20001000
.word reset
.word loop
.word loop
.thumb_func
reset:
bl notmain
.thumb_func
loop:
b .
notmain.c
unsigned int fun ( void );
unsigned int notmain ( void )
{
return(fun());
}
fun.c
unsigned int fun ( void )
{
return(5);
}
内存映射
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
生成文件
all :
arm-none-eabi-as start.s -o start.o
clang -O2 -fomit-frame-pointer -c notmain.c -o notmain.o
clang -O2 -fomit-frame-pointer -c fun.c -o fun.o
arm-none-eabi-ld -T memmap start.o notmain.o fun.o -o basic.elf
arm-none-eabi-objdump -D basic.elf > basic.list
clang -fomit-frame-pointer -c -emit-llvm notmain.c -o notmain.bc
clang -fomit-frame-pointer -c -emit-llvm fun.c -o fun.bc
llc $(LLCOPS) notmain.bc -filetype=obj -o notmain.not.o
llc $(LLCOPS) fun.bc -filetype=obj -o fun.not.o
arm-none-eabi-ld -T memmap start.o notmain.not.o fun.not.o -o not.elf
arm-none-eabi-objdump -D not.elf > not.list
llvm-link notmain.bc fun.bc -o notmain.not.bc
opt -O2 notmain.not.bc -o notmain.opt.bc
llc $(LLCOPS) notmain.opt.bc -filetype=obj -o notmain.opt.o
arm-none-eabi-ld -T memmap start.o notmain.opt.o -o opt.elf
arm-none-eabi-objdump -D opt.elf > opt.list
clean:
rm -f *.S
rm -f *.o
rm -f *.list
rm -f *.elf
rm -f *.bc
basic.list
Disassembly of section .text:
08000000 <_start>:
8000000: 20001000 andcs r1, r0, r0
8000004: 08000011 stmdaeq r0, {r0, r4}
8000008: 08000015 stmdaeq r0, {r0, r2, r4}
800000c: 08000015 stmdaeq r0, {r0, r2, r4}
08000010 <reset>:
8000010: f000 f802 bl 8000018 <notmain>
08000014 <loop>:
8000014: e7fe b.n 8000014 <loop>
...
08000018 <notmain>:
8000018: b580 push {r7, lr}
800001a: f000 f801 bl 8000020 <fun>
800001e: bd80 pop {r7, pc}
08000020 <fun>:
8000020: 2005 movs r0, #5
8000022: 4770 bx lr
not.list
Disassembly of section .text:
08000000 <_start>:
8000000: 20001000 andcs r1, r0, r0
8000004: 08000011 stmdaeq r0, {r0, r4}
8000008: 08000015 stmdaeq r0, {r0, r2, r4}
800000c: 08000015 stmdaeq r0, {r0, r2, r4}
08000010 <reset>:
8000010: f000 f802 bl 8000018 <notmain>
08000014 <loop>:
8000014: e7fe b.n 8000014 <loop>
...
08000018 <notmain>:
8000018: b580 push {r7, lr}
800001a: f000 f801 bl 8000020 <fun>
800001e: bd80 pop {r7, pc}
08000020 <fun>:
8000020: 2005 movs r0, #5
8000022: 4770 bx lr
opt.list
Disassembly of section .text:
08000000 <_start>:
8000000: 20001000 andcs r1, r0, r0
8000004: 08000011 stmdaeq r0, {r0, r4}
8000008: 08000015 stmdaeq r0, {r0, r2, r4}
800000c: 08000015 stmdaeq r0, {r0, r2, r4}
08000010 <reset>:
8000010: f000 f802 bl 8000018 <notmain>
08000014 <loop>:
8000014: e7fe b.n 8000014 <loop>
...
08000018 <notmain>:
8000018: b580 push {r7, lr}
800001a: f000 f802 bl 8000022 <fun>
800001e: 2005 movs r0, #5
8000020: bd80 pop {r7, pc}
08000022 <fun>:
8000022: 2005 movs r0, #5
8000024: 4770 bx lr
这里有趣的部分是您可以跨 files/objects 进行优化,这是您使用 gnu 工具 AFAIK 无法做到的。所以,实际上 LLVM 做得非常糟糕,必须对此进行调查。
现在我使用了 gnus 链接器和汇编器,但仍然不确定如何解决我在尝试仅使用 clang 构建时遇到错误的问题。
这些都足够通用,可以在您的处理器上 运行 如此处所示,使用新项目或工具寻找关键陷阱。
08000000 <_start>:
8000000: 20001000
8000004: 08000011
8000008: 08000015
800000c: 08000015
08000010 <reset>:
08000014 <loop>:
为了使 cortex-m 正确启动并且不会立即无法工作,向量 table 需要为向量设置 lsbit,从重置开始,在这种情况下重置位于 0x08000010,因此向量table 条目需要为 0x08000011 才能使代码 运行。这就是我们在这里看到的,所以我们不会因此而失败。
虽然有些 mcus 没有 0x1000 字节,但我认为你的有,所以 0x20001000 是堆栈指针的一个好的起始位置。
从那里开始,这又不仅仅是一个无限循环,它更复杂,但这应该 运行 在您的处理器上并且不会失败。如果你把它改成这样:
.thumb
.cpu cortex-m0
.globl _start
_start:
.word 0x20001000
.word reset
.word loop
.word loop
.thumb_func
reset:
mov r0,#0
b reset
.thumb_func
loop:
b .
(假设这成为一个 gnu 工具项目,而不是 llvm/clang 您使用的是什么汇编器和链接器?)
然后使用调试器(stlink 加 openocd 加 telnet)您可以停止并恢复并检查 r0 以查看它是 运行ning。
.thumb
.cpu cortex-m0
.globl _start
_start:
.word 0x20001000
.word reset
.word loop
.word loop
.thumb_func
reset:
bl notmain
.thumb_func
loop:
b .
.thumb_func
bounce:
bx lr
void bounce ( unsigned int );
unsigned int notmain ( void )
{
for(ra=0;;ra++) bounce(ra);
return(0);
}
它添加了一点 clang/llvm 并且如果你 stop/resume.
看到 r0 正在改变
有些芯片会查看向量是否存在,如果它看到 0xFFs 那么它可能会进入引导加载程序,因此使用调试器您还可以检查 0x00000000 和 0x08000000,尝试在 telnet/openocd 命令上重置暂停行,然后 mdw 0 20
查看 cpu 将在地址零处看到什么,看看它是否是您的向量 table.
如果你克服了这些非常简单但非常致命的常见问题,那么你可能正在处理其他问题,比如 clang 不喜欢 while(1) 循环,也许他们最终修复了那个错误,但是当我提交它时他们拒绝了因此,如果有代码等待使用 while(1) 更改状态位,那么这可能就是问题所在。在上面的操作之后,我会采取一些小的步骤,因为你一直在尝试时钟,所以你在时钟初始化之后调用并查看时钟初始化是否 运行 完成并返回到 main()。
您是使用 clang/llvm 来构建您正在使用的库,还是它们是预先构建的供您与 clang/llvm 一起使用?
编辑
做了更多工作,以防以上内容与任何人相关并且不会被删除。
改为
-DLLVM_ENABLE_PROJECTS='clang;lld'
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
/DISCARD/ : {
*(.ARM.exidx*)
}
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
也许还有其他一些事情。现在反汇编也与您的反汇编相似,数据以单独的小字节序排列。
Disassembly of section .text:
08000000 _start:
8000000: 00 10 asrs r0, r0, #32
8000002: 00 20 movs r0, #0
8000004: 11 00 movs r1, r2
8000006: 00 08 lsrs r0, r0, #32
8000008: 15 00 movs r5, r2
800000a: 00 08 lsrs r0, r0, #32
800000c: 15 00 movs r5, r2
800000e: 00 08 lsrs r0, r0, #32
08000010 reset:
8000010: 00 f0 02 f8 bl #4
08000014 loop:
8000014: fe e7 b #-4 <loop>
8000016: d4 d4 bmi #-88 <start.c+0x7ffffc2>
08000018 notmain:
8000018: 80 b5 push {r7, lr}
800001a: 00 f0 02 f8 bl #4
800001e: 05 20 movs r0, #5
8000020: 80 bd pop {r7, pc}
08000022 fun:
8000022: 05 20 movs r0, #5
8000024: 70 47 bx lr
现在关于 400MB 的评论。
0x20000000 - 0x08000000 = 0x18000000 = 402653184.
这可能是你的问题,所以听起来你有一些.data。
让我开始一个新的:
start.s
.text
/*.syntax unified*/
.cpu cortex-m0
.code 16
.globl _start
_start:
.word 0x20001000
.word reset
.word loop
.word loop
.thumb_func
reset:
bl notmain
.thumb_func
loop:
b .
notmain.c
unsigned int notmain ( void )
{
return(7);
}
生成文件
all :
clang -c start.s -o start.o
clang -O2 -fomit-frame-pointer -c notmain.c -o notmain.o
ld.lld -T memmap start.o notmain.o -o basic.elf
llvm-objdump -D basic.elf > basic.list
llvm-objcopy -O binary basic.elf basic.bin
clean:
rm -f *.o
rm -f *.list
rm -f *.elf
内存映射
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
/DISCARD/ : {
*(.ARM.exidx*)
}
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
.data : { *(.data*) } > ram
}
这会生成一个 28 字节的 basic.bin 文件
08000000 _start:
8000000: 00 10 asrs r0, r0, #32
8000002: 00 20 movs r0, #0
8000004: 11 00 movs r1, r2
8000006: 00 08 lsrs r0, r0, #32
8000008: 15 00 movs r5, r2
800000a: 00 08 lsrs r0, r0, #32
800000c: 15 00 movs r5, r2
800000e: 00 08 lsrs r0, r0, #32
08000010 reset:
8000010: 00 f0 02 f8 bl #4
08000014 loop:
8000014: fe e7 b #-4 <loop>
8000016: d4 d4 bmi #-88 <start.c+0x7ffffc2>
08000018 notmain:
8000018: 07 20 movs r0, #7
800001a: 70 47 bx lr
现在让我们添加.data:
unsigned int x = 5;
unsigned int notmain ( void )
{
return(7);
}
现在我的 basic.bin 是 402653188 字节。
正在发生的事情是 objcopy 正在制作一个二进制内存映像,该映像从第一个可加载或相关 space 开始并以最后一个结束
Disassembly of section .text:
08000000 _start:
8000000: 00 10 asrs r0, r0, #32
8000002: 00 20 movs r0, #0
8000004: 11 00 movs r1, r2
8000006: 00 08 lsrs r0, r0, #32
8000008: 15 00 movs r5, r2
800000a: 00 08 lsrs r0, r0, #32
800000c: 15 00 movs r5, r2
800000e: 00 08 lsrs r0, r0, #32
08000010 reset:
8000010: 00 f0 02 f8 bl #4
08000014 loop:
8000014: fe e7 b #-4 <loop>
8000016: d4 d4 bmi #-88 <start.c+0x7ffffc2>
08000018 notmain:
8000018: 07 20 movs r0, #7
800001a: 70 47 bx lr
Disassembly of section .data:
20000000 x:
20000000: 05 00 movs r5, r0
20000002: 00 00 movs r0, r0
从 0x08000000 到 0x20000002(含)
0x20000003 - 0x08000000 = 402653187 所以他们将它填充到最近的单词(或半字)。
你不能将它加载到你的微控制器中,它无论如何都不会工作,你的程序需要包含在非易失性存储器中...闪存...
第一步:
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
/DISCARD/ : {
*(.ARM.exidx*)
}
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
.data : { *(.data*) } > ram AT > rom
}
改成ram AT rom.
basic.bin 文件现在是 32 字节。
00000000 00 10 00 20 11 00 00 08 15 00 00 08 15 00 00 08 |... ............|
00000010 00 f0 02 f8 fe e7 d4 d4 07 20 70 47 05 00 00 00 |......... pG....|
00000020
Disassembly of section .text:
08000000 _start:
8000000: 00 10 asrs r0, r0, #32
8000002: 00 20 movs r0, #0
8000004: 11 00 movs r1, r2
8000006: 00 08 lsrs r0, r0, #32
8000008: 15 00 movs r5, r2
800000a: 00 08 lsrs r0, r0, #32
800000c: 15 00 movs r5, r2
800000e: 00 08 lsrs r0, r0, #32
08000010 reset:
8000010: 00 f0 02 f8 bl #4
08000014 loop:
8000014: fe e7 b #-4 <loop>
8000016: d4 d4 bmi #-88 <start.c+0x7ffffc2>
08000018 notmain:
8000018: 07 20 movs r0, #7
800001a: 70 47 bx lr
Disassembly of section .data:
20000000 x:
20000000: 05 00 movs r5, r0
20000002: 00 00 movs r0, r0
注意二进制文件的结尾:
70 47 05 00 00 00
它有最后一个 .text 项 70 47 然后是 .data 项。
让工具为您完成工作
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
/DISCARD/ : {
*(.ARM.exidx*)
}
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
__data_rom_start__ = .;
.data : {
__data_start__ = .;
*(.data*)
} > ram AT > rom
__data_end__ = .;
__data_size__ = __data_end__ - __data_start__;
}
basic.bin 当前仍然是 32 字节但是
llvm-nm basic.elf
20000004 D __data_end__
0800001c T __data_rom_start__
00000004 A __data_size__
20000000 D __data_start__
08000000 T _start
08000014 t loop
08000018 T notmain
08000010 t reset
20000000 D x
现在我们知道在地址 0x0800001c 的闪存中,嵌入的 .data 开始,它的大小为 4 个字节,其在 ram 中的目标是 0x20000000 所以现在 bootstrap 代码可以将 .data 从闪存复制到调用 C 入口点之前的 ram。
现在你已经完成了所有这些,我假设你知道关于链接器脚本和 bootstrap 的所有这些,但是你得到的是 400MB 的二进制文件,这表明有其他东西泄漏到 ram 中地址 space.
检查反汇编 (objdump -D) and.or readelf and/or nm 输出以找出其中的内容并将其与要复制的 bootstrap 代码一起添加到链接描述文件它。
添加一些.bss
unsigned int x = 5;
unsigned int y;
unsigned int notmain ( void )
{
return(7);
}
来自 objdump
Disassembly of section .bss:
20000000 y:
...
Disassembly of section .data:
20000004 x:
20000004: 05 00 movs r5, r0
20000006: 00 00 movs r0, r0
来自 readelf
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x001000 0x08000000 0x08000000 0x0001c 0x0001c R E 0x1000
LOAD 0x002000 0x20000000 0x20000000 0x00000 0x00004 RW 0x1000
LOAD 0x002004 0x20000004 0x0800001c 0x00004 0x00004 RW 0x1000
.bss 看起来有点吓人,但它并没有以二进制形式结束,它又是 32 字节。但我们在这里看到 .data 物理上位于闪存中,但希望位于 ram 中,这正是我们对此类平台的期望。也许你可以从 readelf 找到内存泄漏。
您的 objcopy -O 二进制输出应该适合闪存并包含 100% 的程序和数据,否则如果您仅从该 400MByte 文件中提取闪存部分,它将无法工作,可能会有或可能有一些数据项不会在那里运行预期的软件。或者它可能是一些愚蠢的字符串 table 东西或其他一些并非真正用于二进制文件但碰巧有一个节名称到目前为止未在链接器脚本中处理的东西。
抱歉,400MB 对我来说从一开始就应该很明显,这是准备新项目时另一个常见的链接描述文件陷阱。我个人从不使用 .data 也不依赖 .bss 所以没有这些问题,但你的体验可能不同,但我仍然非常清楚。 (当你的 .text 为 0 且 ram 为 0x80000000 或更高时,你会得到千兆字节大小的文件,这会更有趣)。
我一直在尝试编译一个简单的应用程序,在 Windows 上使用 Clang 为 stm32l4 micro 实现 USB CDC 设备。 该代码由 STMCubeMX 生成,并进行了一些小的更改,因此它只是回显通过虚拟 com 端口发送的任何内容。
使用来自 ubuntu 的 arm-none-eabi-gcc 工具链使用 generated make file works just fine. After flashing to the micro controller it does exactly what it's supposed to do. To compile with clang from windows, i made this fancy build script 进行编译(从 asm/c 来源生成对象,link 那些. 使用这些选项:
set TARGET_TRIPE=--target=arm-none-eabi
set ARCH=-march=armv7e-m
set CPU=-mcpu=cortex-m4
set FPU=-mfpu=fpv4-sp-d16 -mfloat-abi=hard
set MCU=%TARGET_TRIPE% %ARCH% %CPU% %FPU% -mthumb -mlittle-endian
set COMMON_FLAGS=-Wall %OPTIMIZATIONS% --sysroot=%SYSROOT% -fdata-sections -ffunction-sections -O0
set C_FLAGS=%MCU% %C_DEFINES% %COMMON_FLAGS% %C_INCLUDES% -c
set ASM_FLAGS=%MCU% %ASM_DEFINES% %COMMON_FLAGS% -x assembler-with-cpp -c
set LD_FLAGS=%MCU% %COMMON_FLAGS% -nostdlib -nostartfiles -fuse-ld=lld -T%LD_SCRIPT% -Wl,-Map=%BUILD_DIR%/%PROJECT_NAME%.map,--cref,--gc-sections %LIBDIRS% %LIBS%
)。
编译成功,固件镜像看起来没问题,但是烧写后,微控制器什么也没做。甚至外部 crystal 都无法启动,因此固件映像显然有问题。我不知道,为什么会这样。 Clang/GCC. Repo 生成的二进制文件 source.
编辑:clang 二进制图像大约有 400MB 大,这似乎也不对。
编辑 2:
这是我正在使用的链接描述文件:
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x2000A000; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
/* Specify the memory areas */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 40K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 128K
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(8);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(8);
} >FLASH
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(8);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(8);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(8);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(8);
} >FLASH
.ARM.extab :
{
. = ALIGN(8);
*(.ARM.extab* .gnu.linkonce.armextab.*)
. = ALIGN(8);
} >FLASH
.ARM : {
. = ALIGN(8);
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
. = ALIGN(8);
} >FLASH
.preinit_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(8);
} >FLASH
.init_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(8);
} >FLASH
.fini_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(8);
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(8);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(8);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}
这是我正在使用的启动代码:
.syntax unified
.cpu cortex-m4
.fpu softvfp
.thumb
.global g_pfnVectors
.global Default_Handler
/* start address for the initialization values of the .data section.
defined in linker script */
.word _sidata
/* start address for the .data section. defined in linker script */
.word _sdata
/* end address for the .data section. defined in linker script */
.word _edata
/* start address for the .bss section. defined in linker script */
.word _sbss
/* end address for the .bss section. defined in linker script */
.word _ebss
.equ BootRAM, 0xF1E0F85F
/**
* @brief This is the code that gets called when the processor first
* starts execution following a reset event. Only the absolutely
* necessary set is performed, after which the application
* supplied main() routine is called.
* @param None
* @retval : None
*/
.section .text.Reset_Handler
.weak Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
ldr sp, =_estack /* Set stack pointer */
/* Copy the data segment initializers from flash to SRAM */
movs r1, #0
b LoopCopyDataInit
CopyDataInit:
ldr r3, =_sidata
ldr r3, [r3, r1]
str r3, [r0, r1]
adds r1, r1, #4
LoopCopyDataInit:
ldr r0, =_sdata
ldr r3, =_edata
adds r2, r0, r1
cmp r2, r3
bcc CopyDataInit
ldr r2, =_sbss
b LoopFillZerobss
/* Zero fill the bss segment. */
FillZerobss:
movs r3, #0
str r3, [r2], #4
LoopFillZerobss:
ldr r3, = _ebss
cmp r2, r3
bcc FillZerobss
/* Call the clock system intitialization function.*/
bl SystemInit
/* Call CRT entry point */
//bl _mainCRTStartup
bl __libc_init_array
bl main
LoopForever:
b LoopForever
.size Reset_Handler, .-Reset_Handler
/**
* @brief This is the code that gets called when the processor receives an
* unexpected interrupt. This simply enters an infinite loop, preserving
* the system state for examination by a debugger.
*
* @param None
* @retval : None
*/
.section .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
b Infinite_Loop
.size Default_Handler, .-Default_Handler
/******************************************************************************
*
* The minimal vector table for a Cortex-M4. Note that the proper constructs
* must be placed on this to ensure that it ends up at physical address
* 0x0000.0000.
*
******************************************************************************/
.section .isr_vector,"a",%progbits
.type g_pfnVectors, %object
.size g_pfnVectors, .-g_pfnVectors
g_pfnVectors:
.word _estack
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
.word 0
.word 0
.word 0
.word 0
.word SVC_Handler
.word DebugMon_Handler
.word 0
.word PendSV_Handler
.word SysTick_Handler
.word WWDG_IRQHandler
.word PVD_PVM_IRQHandler
.word TAMP_STAMP_IRQHandler
.word RTC_WKUP_IRQHandler
.word FLASH_IRQHandler
.word RCC_IRQHandler
.word EXTI0_IRQHandler
.word EXTI1_IRQHandler
.word EXTI2_IRQHandler
.word EXTI3_IRQHandler
.word EXTI4_IRQHandler
.word DMA1_Channel1_IRQHandler
.word DMA1_Channel2_IRQHandler
.word DMA1_Channel3_IRQHandler
.word DMA1_Channel4_IRQHandler
.word DMA1_Channel5_IRQHandler
.word DMA1_Channel6_IRQHandler
.word DMA1_Channel7_IRQHandler
.word ADC1_2_IRQHandler
.word 0
.word 0
.word 0
.word 0
.word EXTI9_5_IRQHandler
.word TIM1_BRK_TIM15_IRQHandler
.word TIM1_UP_TIM16_IRQHandler
.word TIM1_TRG_COM_IRQHandler
.word TIM1_CC_IRQHandler
.word TIM2_IRQHandler
.word 0
.word 0
.word I2C1_EV_IRQHandler
.word I2C1_ER_IRQHandler
.word I2C2_EV_IRQHandler
.word I2C2_ER_IRQHandler
.word SPI1_IRQHandler
.word SPI2_IRQHandler
.word USART1_IRQHandler
.word USART2_IRQHandler
.word USART3_IRQHandler
.word EXTI15_10_IRQHandler
.word RTC_Alarm_IRQHandler
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word TIM6_IRQHandler
.word 0
.word DMA2_Channel1_IRQHandler
.word DMA2_Channel2_IRQHandler
.word DMA2_Channel3_IRQHandler
.word DMA2_Channel4_IRQHandler
.word DMA2_Channel5_IRQHandler
.word 0
.word 0
.word 0
.word COMP_IRQHandler
.word LPTIM1_IRQHandler
.word LPTIM2_IRQHandler
.word USB_IRQHandler
.word DMA2_Channel6_IRQHandler
.word DMA2_Channel7_IRQHandler
.word LPUART1_IRQHandler
.word QUADSPI_IRQHandler
.word I2C3_EV_IRQHandler
.word I2C3_ER_IRQHandler
.word 0
.word 0
.word 0
.word TSC_IRQHandler
.word 0
.word AES_IRQHandler
.word RNG_IRQHandler
.word FPU_IRQHandler
.word CRS_IRQHandler
/*******************************************************************************
*
* Provide weak aliases for each Exception handler to the Default_Handler.
* As they are weak aliases, any function with the same name will override
* this definition.
*
*******************************************************************************/
.weak NMI_Handler
.thumb_set NMI_Handler,Default_Handler
.weak HardFault_Handler
.thumb_set HardFault_Handler,Default_Handler
.weak MemManage_Handler
.thumb_set MemManage_Handler,Default_Handler
.weak BusFault_Handler
.thumb_set BusFault_Handler,Default_Handler
.weak UsageFault_Handler
.thumb_set UsageFault_Handler,Default_Handler
.weak SVC_Handler
.thumb_set SVC_Handler,Default_Handler
.weak DebugMon_Handler
.thumb_set DebugMon_Handler,Default_Handler
.weak PendSV_Handler
.thumb_set PendSV_Handler,Default_Handler
.weak SysTick_Handler
.thumb_set SysTick_Handler,Default_Handler
.weak WWDG_IRQHandler
.thumb_set WWDG_IRQHandler,Default_Handler
.weak PVD_PVM_IRQHandler
.thumb_set PVD_PVM_IRQHandler,Default_Handler
.weak TAMP_STAMP_IRQHandler
.thumb_set TAMP_STAMP_IRQHandler,Default_Handler
.weak RTC_WKUP_IRQHandler
.thumb_set RTC_WKUP_IRQHandler,Default_Handler
.weak FLASH_IRQHandler
.thumb_set FLASH_IRQHandler,Default_Handler
.weak RCC_IRQHandler
.thumb_set RCC_IRQHandler,Default_Handler
.weak EXTI0_IRQHandler
.thumb_set EXTI0_IRQHandler,Default_Handler
.weak EXTI1_IRQHandler
.thumb_set EXTI1_IRQHandler,Default_Handler
.weak EXTI2_IRQHandler
.thumb_set EXTI2_IRQHandler,Default_Handler
.weak EXTI3_IRQHandler
.thumb_set EXTI3_IRQHandler,Default_Handler
.weak EXTI4_IRQHandler
.thumb_set EXTI4_IRQHandler,Default_Handler
.weak DMA1_Channel1_IRQHandler
.thumb_set DMA1_Channel1_IRQHandler,Default_Handler
.weak DMA1_Channel2_IRQHandler
.thumb_set DMA1_Channel2_IRQHandler,Default_Handler
.weak DMA1_Channel3_IRQHandler
.thumb_set DMA1_Channel3_IRQHandler,Default_Handler
.weak DMA1_Channel4_IRQHandler
.thumb_set DMA1_Channel4_IRQHandler,Default_Handler
.weak DMA1_Channel5_IRQHandler
.thumb_set DMA1_Channel5_IRQHandler,Default_Handler
.weak DMA1_Channel6_IRQHandler
.thumb_set DMA1_Channel6_IRQHandler,Default_Handler
.weak DMA1_Channel7_IRQHandler
.thumb_set DMA1_Channel7_IRQHandler,Default_Handler
.weak ADC1_2_IRQHandler
.thumb_set ADC1_2_IRQHandler,Default_Handler
.weak EXTI9_5_IRQHandler
.thumb_set EXTI9_5_IRQHandler,Default_Handler
.weak TIM1_BRK_TIM15_IRQHandler
.thumb_set TIM1_BRK_TIM15_IRQHandler,Default_Handler
.weak TIM1_UP_TIM16_IRQHandler
.thumb_set TIM1_UP_TIM16_IRQHandler,Default_Handler
.weak TIM1_TRG_COM_IRQHandler
.thumb_set TIM1_TRG_COM_IRQHandler,Default_Handler
.weak TIM1_CC_IRQHandler
.thumb_set TIM1_CC_IRQHandler,Default_Handler
.weak TIM2_IRQHandler
.thumb_set TIM2_IRQHandler,Default_Handler
.weak I2C1_EV_IRQHandler
.thumb_set I2C1_EV_IRQHandler,Default_Handler
.weak I2C1_ER_IRQHandler
.thumb_set I2C1_ER_IRQHandler,Default_Handler
.weak I2C2_EV_IRQHandler
.thumb_set I2C2_EV_IRQHandler,Default_Handler
.weak I2C2_ER_IRQHandler
.thumb_set I2C2_ER_IRQHandler,Default_Handler
.weak SPI1_IRQHandler
.thumb_set SPI1_IRQHandler,Default_Handler
.weak SPI2_IRQHandler
.thumb_set SPI2_IRQHandler,Default_Handler
.weak USART1_IRQHandler
.thumb_set USART1_IRQHandler,Default_Handler
.weak USART2_IRQHandler
.thumb_set USART2_IRQHandler,Default_Handler
.weak USART3_IRQHandler
.thumb_set USART3_IRQHandler,Default_Handler
.weak EXTI15_10_IRQHandler
.thumb_set EXTI15_10_IRQHandler,Default_Handler
.weak RTC_Alarm_IRQHandler
.thumb_set RTC_Alarm_IRQHandler,Default_Handler
.weak TIM6_IRQHandler
.thumb_set TIM6_IRQHandler,Default_Handler
.weak DMA2_Channel1_IRQHandler
.thumb_set DMA2_Channel1_IRQHandler,Default_Handler
.weak DMA2_Channel2_IRQHandler
.thumb_set DMA2_Channel2_IRQHandler,Default_Handler
.weak DMA2_Channel3_IRQHandler
.thumb_set DMA2_Channel3_IRQHandler,Default_Handler
.weak DMA2_Channel4_IRQHandler
.thumb_set DMA2_Channel4_IRQHandler,Default_Handler
.weak DMA2_Channel5_IRQHandler
.thumb_set DMA2_Channel5_IRQHandler,Default_Handler
.weak COMP_IRQHandler
.thumb_set COMP_IRQHandler,Default_Handler
.weak LPTIM1_IRQHandler
.thumb_set LPTIM1_IRQHandler,Default_Handler
.weak LPTIM2_IRQHandler
.thumb_set LPTIM2_IRQHandler,Default_Handler
.weak USB_IRQHandler
.thumb_set USB_IRQHandler,Default_Handler
.weak DMA2_Channel6_IRQHandler
.thumb_set DMA2_Channel6_IRQHandler,Default_Handler
.weak DMA2_Channel7_IRQHandler
.thumb_set DMA2_Channel7_IRQHandler,Default_Handler
.weak LPUART1_IRQHandler
.thumb_set LPUART1_IRQHandler,Default_Handler
.weak QUADSPI_IRQHandler
.thumb_set QUADSPI_IRQHandler,Default_Handler
.weak I2C3_EV_IRQHandler
.thumb_set I2C3_EV_IRQHandler,Default_Handler
.weak I2C3_ER_IRQHandler
.thumb_set I2C3_ER_IRQHandler,Default_Handler
.weak TSC_IRQHandler
.thumb_set TSC_IRQHandler,Default_Handler
.weak AES_IRQHandler
.thumb_set AES_IRQHandler,Default_Handler
.weak RNG_IRQHandler
.thumb_set RNG_IRQHandler,Default_Handler
.weak FPU_IRQHandler
.thumb_set FPU_IRQHandler,Default_Handler
.weak CRS_IRQHandler
.thumb_set CRS_IRQHandler,Default_Handler
启动代码和 linker 脚本均由 STM32CubeMX 生成
这是从启动代码中调用的主要函数:
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USB_DEVICE_Init();
while (1)
{
}
}
初始化外围设备和 CDC usb 设备。
矢量反汇编table:
Disassembly of section .isr_vector:
08000000 <g_pfnVectors>:
8000000: 00 a0 adr r0, #0
8000002: 00 20 movs r0, #0
8000004: 81 53 strh r1, [r0, r6]
8000006: 00 08 lsrs r0, r0, #32
8000008: a5 04 lsls r5, r4, #18
800000a: 00 08 lsrs r0, r0, #32
800000c: a7 04 lsls r7, r4, #18
800000e: 00 08 lsrs r0, r0, #32
8000010: ab 04 lsls r3, r5, #18
8000012: 00 08 lsrs r0, r0, #32
8000014: af 04 lsls r7, r5, #18
8000016: 00 08 lsrs r0, r0, #32
8000018: b3 04 lsls r3, r6, #18
800001a: 00 08 lsrs r0, r0, #32
...
800002c: b7 04 lsls r7, r6, #18
800002e: 00 08 lsrs r0, r0, #32
8000030: b9 04 lsls r1, r7, #18
8000032: 00 08 lsrs r0, r0, #32
8000034: 00 00 movs r0, r0
8000036: 00 00 movs r0, r0
8000038: bb 04 lsls r3, r7, #18
800003a: 00 08 lsrs r0, r0, #32
800003c: bd 04 lsls r5, r7, #18
800003e: 00 08 lsrs r0, r0, #32
8000040: d1 53 strh r1, [r2, r7]
8000042: 00 08 lsrs r0, r0, #32
8000044: d1 53 strh r1, [r2, r7]
8000046: 00 08 lsrs r0, r0, #32
8000048: d1 53 strh r1, [r2, r7]
800004a: 00 08 lsrs r0, r0, #32
800004c: d1 53 strh r1, [r2, r7]
800004e: 00 08 lsrs r0, r0, #32
8000050: d1 53 strh r1, [r2, r7]
8000052: 00 08 lsrs r0, r0, #32
8000054: d1 53 strh r1, [r2, r7]
8000056: 00 08 lsrs r0, r0, #32
8000058: d1 53 strh r1, [r2, r7]
800005a: 00 08 lsrs r0, r0, #32
800005c: d1 53 strh r1, [r2, r7]
800005e: 00 08 lsrs r0, r0, #32
8000060: d1 53 strh r1, [r2, r7]
8000062: 00 08 lsrs r0, r0, #32
8000064: d1 53 strh r1, [r2, r7]
8000066: 00 08 lsrs r0, r0, #32
8000068: d1 53 strh r1, [r2, r7]
800006a: 00 08 lsrs r0, r0, #32
800006c: d1 53 strh r1, [r2, r7]
800006e: 00 08 lsrs r0, r0, #32
8000070: d1 53 strh r1, [r2, r7]
8000072: 00 08 lsrs r0, r0, #32
8000074: d1 53 strh r1, [r2, r7]
8000076: 00 08 lsrs r0, r0, #32
8000078: d1 53 strh r1, [r2, r7]
800007a: 00 08 lsrs r0, r0, #32
800007c: d1 53 strh r1, [r2, r7]
800007e: 00 08 lsrs r0, r0, #32
8000080: d1 53 strh r1, [r2, r7]
8000082: 00 08 lsrs r0, r0, #32
8000084: d1 53 strh r1, [r2, r7]
8000086: 00 08 lsrs r0, r0, #32
8000088: d1 53 strh r1, [r2, r7]
800008a: 00 08 lsrs r0, r0, #32
...
800009c: d1 53 strh r1, [r2, r7]
800009e: 00 08 lsrs r0, r0, #32
80000a0: d1 53 strh r1, [r2, r7]
80000a2: 00 08 lsrs r0, r0, #32
80000a4: d1 53 strh r1, [r2, r7]
80000a6: 00 08 lsrs r0, r0, #32
80000a8: d1 53 strh r1, [r2, r7]
80000aa: 00 08 lsrs r0, r0, #32
80000ac: d1 53 strh r1, [r2, r7]
80000ae: 00 08 lsrs r0, r0, #32
80000b0: d1 53 strh r1, [r2, r7]
80000b2: 00 08 lsrs r0, r0, #32
...
80000bc: d1 53 strh r1, [r2, r7]
80000be: 00 08 lsrs r0, r0, #32
80000c0: d1 53 strh r1, [r2, r7]
80000c2: 00 08 lsrs r0, r0, #32
80000c4: d1 53 strh r1, [r2, r7]
80000c6: 00 08 lsrs r0, r0, #32
80000c8: d1 53 strh r1, [r2, r7]
80000ca: 00 08 lsrs r0, r0, #32
80000cc: d1 53 strh r1, [r2, r7]
80000ce: 00 08 lsrs r0, r0, #32
80000d0: d1 53 strh r1, [r2, r7]
80000d2: 00 08 lsrs r0, r0, #32
80000d4: d1 53 strh r1, [r2, r7]
80000d6: 00 08 lsrs r0, r0, #32
80000d8: d1 53 strh r1, [r2, r7]
80000da: 00 08 lsrs r0, r0, #32
80000dc: d1 53 strh r1, [r2, r7]
80000de: 00 08 lsrs r0, r0, #32
80000e0: d1 53 strh r1, [r2, r7]
80000e2: 00 08 lsrs r0, r0, #32
80000e4: d1 53 strh r1, [r2, r7]
80000e6: 00 08 lsrs r0, r0, #32
...
8000118: d1 53 strh r1, [r2, r7]
800011a: 00 08 lsrs r0, r0, #32
800011c: 00 00 movs r0, r0
800011e: 00 00 movs r0, r0
8000120: d1 53 strh r1, [r2, r7]
8000122: 00 08 lsrs r0, r0, #32
8000124: d1 53 strh r1, [r2, r7]
8000126: 00 08 lsrs r0, r0, #32
8000128: d1 53 strh r1, [r2, r7]
800012a: 00 08 lsrs r0, r0, #32
800012c: d1 53 strh r1, [r2, r7]
800012e: 00 08 lsrs r0, r0, #32
8000130: d1 53 strh r1, [r2, r7]
8000132: 00 08 lsrs r0, r0, #32
...
8000140: d1 53 strh r1, [r2, r7]
8000142: 00 08 lsrs r0, r0, #32
8000144: d1 53 strh r1, [r2, r7]
8000146: 00 08 lsrs r0, r0, #32
8000148: d1 53 strh r1, [r2, r7]
800014a: 00 08 lsrs r0, r0, #32
800014c: c7 04 lsls r7, r0, #19
800014e: 00 08 lsrs r0, r0, #32
8000150: d1 53 strh r1, [r2, r7]
8000152: 00 08 lsrs r0, r0, #32
8000154: d1 53 strh r1, [r2, r7]
8000156: 00 08 lsrs r0, r0, #32
8000158: d1 53 strh r1, [r2, r7]
800015a: 00 08 lsrs r0, r0, #32
800015c: d1 53 strh r1, [r2, r7]
800015e: 00 08 lsrs r0, r0, #32
8000160: d1 53 strh r1, [r2, r7]
8000162: 00 08 lsrs r0, r0, #32
8000164: d1 53 strh r1, [r2, r7]
8000166: 00 08 lsrs r0, r0, #32
...
8000174: d1 53 strh r1, [r2, r7]
8000176: 00 08 lsrs r0, r0, #32
8000178: 00 00 movs r0, r0
800017a: 00 00 movs r0, r0
800017c: d1 53 strh r1, [r2, r7]
800017e: 00 08 lsrs r0, r0, #32
8000180: d1 53 strh r1, [r2, r7]
8000182: 00 08 lsrs r0, r0, #32
8000184: d1 53 strh r1, [r2, r7]
8000186: 00 08 lsrs r0, r0, #32
8000188: d1 53 strh r1, [r2, r7]
800018a: 00 08 lsrs r0, r0, #32
800018c: 00 00 movs r0, r0
800018e: 00 00 movs r0, r0
所以这激发了我尝试 clang/llvm 几年后...
现在我在Linux而不是Windows,但是你应该可以适应Windows(或者当然双启动Linux或者把Linux 在虚拟机或其他什么)。
源自 clang/llvm 站点上的构建说明
rm -rf /opt/llvmv6m
rm -rf llvm-project
git clone https://github.com/llvm/llvm-project.git
cd llvm-project
mkdir build
cd build
cmake -DLLVM_ENABLE_PROJECTS=clang -DCMAKE_CROSSCOMPILING=True -DCMAKE_INSTALL_PREFIX=/opt/llvmv6m -DLLVM_DEFAULT_TARGET_TRIPLE=armv6m-none-eabi -DLLVM_TARGET_ARCH=ARM -DLLVM_TARGETS_TO_BUILD=ARM -G "Unix Makefiles" ../llvm
make
sudo make install
我就是这样建造的。是的,我知道你的是 cortex-m4 所有的 cortex-ms(到目前为止)都支持 armv6-m,你可以很容易地制作这个 armv7m。是基于这些网页的实验,有趣的是,现在我不必在命令行上指定内容来指定体系结构或 cpu,很想知道这是否仍然是通用的 clang 交叉编译器,而 armv6m 只是默认值。无论如何...
这比简单的无限循环稍微复杂一点,但可以使用 gnu 中没有的 llvm 功能。
start.s
.thumb
.cpu cortex-m0
.globl _start
_start:
.word 0x20001000
.word reset
.word loop
.word loop
.thumb_func
reset:
bl notmain
.thumb_func
loop:
b .
notmain.c
unsigned int fun ( void );
unsigned int notmain ( void )
{
return(fun());
}
fun.c
unsigned int fun ( void )
{
return(5);
}
内存映射
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
生成文件
all :
arm-none-eabi-as start.s -o start.o
clang -O2 -fomit-frame-pointer -c notmain.c -o notmain.o
clang -O2 -fomit-frame-pointer -c fun.c -o fun.o
arm-none-eabi-ld -T memmap start.o notmain.o fun.o -o basic.elf
arm-none-eabi-objdump -D basic.elf > basic.list
clang -fomit-frame-pointer -c -emit-llvm notmain.c -o notmain.bc
clang -fomit-frame-pointer -c -emit-llvm fun.c -o fun.bc
llc $(LLCOPS) notmain.bc -filetype=obj -o notmain.not.o
llc $(LLCOPS) fun.bc -filetype=obj -o fun.not.o
arm-none-eabi-ld -T memmap start.o notmain.not.o fun.not.o -o not.elf
arm-none-eabi-objdump -D not.elf > not.list
llvm-link notmain.bc fun.bc -o notmain.not.bc
opt -O2 notmain.not.bc -o notmain.opt.bc
llc $(LLCOPS) notmain.opt.bc -filetype=obj -o notmain.opt.o
arm-none-eabi-ld -T memmap start.o notmain.opt.o -o opt.elf
arm-none-eabi-objdump -D opt.elf > opt.list
clean:
rm -f *.S
rm -f *.o
rm -f *.list
rm -f *.elf
rm -f *.bc
basic.list
Disassembly of section .text:
08000000 <_start>:
8000000: 20001000 andcs r1, r0, r0
8000004: 08000011 stmdaeq r0, {r0, r4}
8000008: 08000015 stmdaeq r0, {r0, r2, r4}
800000c: 08000015 stmdaeq r0, {r0, r2, r4}
08000010 <reset>:
8000010: f000 f802 bl 8000018 <notmain>
08000014 <loop>:
8000014: e7fe b.n 8000014 <loop>
...
08000018 <notmain>:
8000018: b580 push {r7, lr}
800001a: f000 f801 bl 8000020 <fun>
800001e: bd80 pop {r7, pc}
08000020 <fun>:
8000020: 2005 movs r0, #5
8000022: 4770 bx lr
not.list
Disassembly of section .text:
08000000 <_start>:
8000000: 20001000 andcs r1, r0, r0
8000004: 08000011 stmdaeq r0, {r0, r4}
8000008: 08000015 stmdaeq r0, {r0, r2, r4}
800000c: 08000015 stmdaeq r0, {r0, r2, r4}
08000010 <reset>:
8000010: f000 f802 bl 8000018 <notmain>
08000014 <loop>:
8000014: e7fe b.n 8000014 <loop>
...
08000018 <notmain>:
8000018: b580 push {r7, lr}
800001a: f000 f801 bl 8000020 <fun>
800001e: bd80 pop {r7, pc}
08000020 <fun>:
8000020: 2005 movs r0, #5
8000022: 4770 bx lr
opt.list
Disassembly of section .text:
08000000 <_start>:
8000000: 20001000 andcs r1, r0, r0
8000004: 08000011 stmdaeq r0, {r0, r4}
8000008: 08000015 stmdaeq r0, {r0, r2, r4}
800000c: 08000015 stmdaeq r0, {r0, r2, r4}
08000010 <reset>:
8000010: f000 f802 bl 8000018 <notmain>
08000014 <loop>:
8000014: e7fe b.n 8000014 <loop>
...
08000018 <notmain>:
8000018: b580 push {r7, lr}
800001a: f000 f802 bl 8000022 <fun>
800001e: 2005 movs r0, #5
8000020: bd80 pop {r7, pc}
08000022 <fun>:
8000022: 2005 movs r0, #5
8000024: 4770 bx lr
这里有趣的部分是您可以跨 files/objects 进行优化,这是您使用 gnu 工具 AFAIK 无法做到的。所以,实际上 LLVM 做得非常糟糕,必须对此进行调查。
现在我使用了 gnus 链接器和汇编器,但仍然不确定如何解决我在尝试仅使用 clang 构建时遇到错误的问题。
这些都足够通用,可以在您的处理器上 运行 如此处所示,使用新项目或工具寻找关键陷阱。
08000000 <_start>:
8000000: 20001000
8000004: 08000011
8000008: 08000015
800000c: 08000015
08000010 <reset>:
08000014 <loop>:
为了使 cortex-m 正确启动并且不会立即无法工作,向量 table 需要为向量设置 lsbit,从重置开始,在这种情况下重置位于 0x08000010,因此向量table 条目需要为 0x08000011 才能使代码 运行。这就是我们在这里看到的,所以我们不会因此而失败。
虽然有些 mcus 没有 0x1000 字节,但我认为你的有,所以 0x20001000 是堆栈指针的一个好的起始位置。
从那里开始,这又不仅仅是一个无限循环,它更复杂,但这应该 运行 在您的处理器上并且不会失败。如果你把它改成这样:
.thumb
.cpu cortex-m0
.globl _start
_start:
.word 0x20001000
.word reset
.word loop
.word loop
.thumb_func
reset:
mov r0,#0
b reset
.thumb_func
loop:
b .
(假设这成为一个 gnu 工具项目,而不是 llvm/clang 您使用的是什么汇编器和链接器?)
然后使用调试器(stlink 加 openocd 加 telnet)您可以停止并恢复并检查 r0 以查看它是 运行ning。
.thumb
.cpu cortex-m0
.globl _start
_start:
.word 0x20001000
.word reset
.word loop
.word loop
.thumb_func
reset:
bl notmain
.thumb_func
loop:
b .
.thumb_func
bounce:
bx lr
void bounce ( unsigned int );
unsigned int notmain ( void )
{
for(ra=0;;ra++) bounce(ra);
return(0);
}
它添加了一点 clang/llvm 并且如果你 stop/resume.
看到 r0 正在改变有些芯片会查看向量是否存在,如果它看到 0xFFs 那么它可能会进入引导加载程序,因此使用调试器您还可以检查 0x00000000 和 0x08000000,尝试在 telnet/openocd 命令上重置暂停行,然后 mdw 0 20
查看 cpu 将在地址零处看到什么,看看它是否是您的向量 table.
如果你克服了这些非常简单但非常致命的常见问题,那么你可能正在处理其他问题,比如 clang 不喜欢 while(1) 循环,也许他们最终修复了那个错误,但是当我提交它时他们拒绝了因此,如果有代码等待使用 while(1) 更改状态位,那么这可能就是问题所在。在上面的操作之后,我会采取一些小的步骤,因为你一直在尝试时钟,所以你在时钟初始化之后调用并查看时钟初始化是否 运行 完成并返回到 main()。
您是使用 clang/llvm 来构建您正在使用的库,还是它们是预先构建的供您与 clang/llvm 一起使用?
编辑
做了更多工作,以防以上内容与任何人相关并且不会被删除。
改为
-DLLVM_ENABLE_PROJECTS='clang;lld'
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
/DISCARD/ : {
*(.ARM.exidx*)
}
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
也许还有其他一些事情。现在反汇编也与您的反汇编相似,数据以单独的小字节序排列。
Disassembly of section .text:
08000000 _start:
8000000: 00 10 asrs r0, r0, #32
8000002: 00 20 movs r0, #0
8000004: 11 00 movs r1, r2
8000006: 00 08 lsrs r0, r0, #32
8000008: 15 00 movs r5, r2
800000a: 00 08 lsrs r0, r0, #32
800000c: 15 00 movs r5, r2
800000e: 00 08 lsrs r0, r0, #32
08000010 reset:
8000010: 00 f0 02 f8 bl #4
08000014 loop:
8000014: fe e7 b #-4 <loop>
8000016: d4 d4 bmi #-88 <start.c+0x7ffffc2>
08000018 notmain:
8000018: 80 b5 push {r7, lr}
800001a: 00 f0 02 f8 bl #4
800001e: 05 20 movs r0, #5
8000020: 80 bd pop {r7, pc}
08000022 fun:
8000022: 05 20 movs r0, #5
8000024: 70 47 bx lr
现在关于 400MB 的评论。
0x20000000 - 0x08000000 = 0x18000000 = 402653184.
这可能是你的问题,所以听起来你有一些.data。
让我开始一个新的:
start.s
.text
/*.syntax unified*/
.cpu cortex-m0
.code 16
.globl _start
_start:
.word 0x20001000
.word reset
.word loop
.word loop
.thumb_func
reset:
bl notmain
.thumb_func
loop:
b .
notmain.c
unsigned int notmain ( void )
{
return(7);
}
生成文件
all :
clang -c start.s -o start.o
clang -O2 -fomit-frame-pointer -c notmain.c -o notmain.o
ld.lld -T memmap start.o notmain.o -o basic.elf
llvm-objdump -D basic.elf > basic.list
llvm-objcopy -O binary basic.elf basic.bin
clean:
rm -f *.o
rm -f *.list
rm -f *.elf
内存映射
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
/DISCARD/ : {
*(.ARM.exidx*)
}
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
.data : { *(.data*) } > ram
}
这会生成一个 28 字节的 basic.bin 文件
08000000 _start:
8000000: 00 10 asrs r0, r0, #32
8000002: 00 20 movs r0, #0
8000004: 11 00 movs r1, r2
8000006: 00 08 lsrs r0, r0, #32
8000008: 15 00 movs r5, r2
800000a: 00 08 lsrs r0, r0, #32
800000c: 15 00 movs r5, r2
800000e: 00 08 lsrs r0, r0, #32
08000010 reset:
8000010: 00 f0 02 f8 bl #4
08000014 loop:
8000014: fe e7 b #-4 <loop>
8000016: d4 d4 bmi #-88 <start.c+0x7ffffc2>
08000018 notmain:
8000018: 07 20 movs r0, #7
800001a: 70 47 bx lr
现在让我们添加.data:
unsigned int x = 5;
unsigned int notmain ( void )
{
return(7);
}
现在我的 basic.bin 是 402653188 字节。
正在发生的事情是 objcopy 正在制作一个二进制内存映像,该映像从第一个可加载或相关 space 开始并以最后一个结束
Disassembly of section .text:
08000000 _start:
8000000: 00 10 asrs r0, r0, #32
8000002: 00 20 movs r0, #0
8000004: 11 00 movs r1, r2
8000006: 00 08 lsrs r0, r0, #32
8000008: 15 00 movs r5, r2
800000a: 00 08 lsrs r0, r0, #32
800000c: 15 00 movs r5, r2
800000e: 00 08 lsrs r0, r0, #32
08000010 reset:
8000010: 00 f0 02 f8 bl #4
08000014 loop:
8000014: fe e7 b #-4 <loop>
8000016: d4 d4 bmi #-88 <start.c+0x7ffffc2>
08000018 notmain:
8000018: 07 20 movs r0, #7
800001a: 70 47 bx lr
Disassembly of section .data:
20000000 x:
20000000: 05 00 movs r5, r0
20000002: 00 00 movs r0, r0
从 0x08000000 到 0x20000002(含)
0x20000003 - 0x08000000 = 402653187 所以他们将它填充到最近的单词(或半字)。
你不能将它加载到你的微控制器中,它无论如何都不会工作,你的程序需要包含在非易失性存储器中...闪存...
第一步:
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
/DISCARD/ : {
*(.ARM.exidx*)
}
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
.data : { *(.data*) } > ram AT > rom
}
改成ram AT rom.
basic.bin 文件现在是 32 字节。
00000000 00 10 00 20 11 00 00 08 15 00 00 08 15 00 00 08 |... ............|
00000010 00 f0 02 f8 fe e7 d4 d4 07 20 70 47 05 00 00 00 |......... pG....|
00000020
Disassembly of section .text:
08000000 _start:
8000000: 00 10 asrs r0, r0, #32
8000002: 00 20 movs r0, #0
8000004: 11 00 movs r1, r2
8000006: 00 08 lsrs r0, r0, #32
8000008: 15 00 movs r5, r2
800000a: 00 08 lsrs r0, r0, #32
800000c: 15 00 movs r5, r2
800000e: 00 08 lsrs r0, r0, #32
08000010 reset:
8000010: 00 f0 02 f8 bl #4
08000014 loop:
8000014: fe e7 b #-4 <loop>
8000016: d4 d4 bmi #-88 <start.c+0x7ffffc2>
08000018 notmain:
8000018: 07 20 movs r0, #7
800001a: 70 47 bx lr
Disassembly of section .data:
20000000 x:
20000000: 05 00 movs r5, r0
20000002: 00 00 movs r0, r0
注意二进制文件的结尾:
70 47 05 00 00 00
它有最后一个 .text 项 70 47 然后是 .data 项。
让工具为您完成工作
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
/DISCARD/ : {
*(.ARM.exidx*)
}
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
__data_rom_start__ = .;
.data : {
__data_start__ = .;
*(.data*)
} > ram AT > rom
__data_end__ = .;
__data_size__ = __data_end__ - __data_start__;
}
basic.bin 当前仍然是 32 字节但是
llvm-nm basic.elf
20000004 D __data_end__
0800001c T __data_rom_start__
00000004 A __data_size__
20000000 D __data_start__
08000000 T _start
08000014 t loop
08000018 T notmain
08000010 t reset
20000000 D x
现在我们知道在地址 0x0800001c 的闪存中,嵌入的 .data 开始,它的大小为 4 个字节,其在 ram 中的目标是 0x20000000 所以现在 bootstrap 代码可以将 .data 从闪存复制到调用 C 入口点之前的 ram。
现在你已经完成了所有这些,我假设你知道关于链接器脚本和 bootstrap 的所有这些,但是你得到的是 400MB 的二进制文件,这表明有其他东西泄漏到 ram 中地址 space.
检查反汇编 (objdump -D) and.or readelf and/or nm 输出以找出其中的内容并将其与要复制的 bootstrap 代码一起添加到链接描述文件它。
添加一些.bss
unsigned int x = 5;
unsigned int y;
unsigned int notmain ( void )
{
return(7);
}
来自 objdump
Disassembly of section .bss:
20000000 y:
...
Disassembly of section .data:
20000004 x:
20000004: 05 00 movs r5, r0
20000006: 00 00 movs r0, r0
来自 readelf
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x001000 0x08000000 0x08000000 0x0001c 0x0001c R E 0x1000
LOAD 0x002000 0x20000000 0x20000000 0x00000 0x00004 RW 0x1000
LOAD 0x002004 0x20000004 0x0800001c 0x00004 0x00004 RW 0x1000
.bss 看起来有点吓人,但它并没有以二进制形式结束,它又是 32 字节。但我们在这里看到 .data 物理上位于闪存中,但希望位于 ram 中,这正是我们对此类平台的期望。也许你可以从 readelf 找到内存泄漏。
您的 objcopy -O 二进制输出应该适合闪存并包含 100% 的程序和数据,否则如果您仅从该 400MByte 文件中提取闪存部分,它将无法工作,可能会有或可能有一些数据项不会在那里运行预期的软件。或者它可能是一些愚蠢的字符串 table 东西或其他一些并非真正用于二进制文件但碰巧有一个节名称到目前为止未在链接器脚本中处理的东西。
抱歉,400MB 对我来说从一开始就应该很明显,这是准备新项目时另一个常见的链接描述文件陷阱。我个人从不使用 .data 也不依赖 .bss 所以没有这些问题,但你的体验可能不同,但我仍然非常清楚。 (当你的 .text 为 0 且 ram 为 0x80000000 或更高时,你会得到千兆字节大小的文件,这会更有趣)。