2017-09-12 1 views
1

stm32l152C-discovery 보드에 대해 arm cortex-m3 용 C 코드를 실행 중이지만 main에서 함수 호출이 스택으로 푸시되지 않는 것을 관찰했다. 이 소스의 asm 코드를 분석했지만 괜찮습니다. 더 잘 이해하기 위해, 여기에 C 코드를 생성 한 ASM 코드를 찾아보세요 :main 함수의 C 함수가 스택에서 스택을 밀어 내지 않고있다.

main.elf:  file format elf32-littlearm 

*SYMBOL TABLE: 
00000010 l d .text 00000000 .text 
00000000 l d .debug_info 00000000 .debug_info 
00000000 l d .debug_abbrev 00000000 .debug_abbrev 
00000000 l d .debug_aranges 00000000 .debug_aranges 
00000000 l d .debug_line 00000000 .debug_line 
00000000 l d .debug_str 00000000 .debug_str 
00000000 l d .comment 00000000 .comment 
00000000 l d .ARM.attributes 00000000 .ARM.attributes 
00000000 l d .debug_frame 00000000 .debug_frame 
00000000 l df *ABS* 00000000 main.c 
00000000 l df *ABS* 00000000 clock.c 
20004ffc g  .text 00000000 _STACKTOP 
**00000028 g  F .text 000000e0 SystemClock_Config** 
20000000 g  .text 00000000 _DATA_BEGIN 
20000000 g  .text 00000000 _HEAP 
**00000010 g  F .text 00000016 main** 
20000000 g  .text 00000000 _BSS_END 
00000108 g  .text 00000000 _DATAI_BEGIN 
20000000 g  .text 00000000 _BSS_BEGIN 
00000108 g  .text 00000000 _DATAI_END 
20000000 g  .text 00000000 _DATA_END 
Disassembly of section .text: 
00000010 <main>: 

#define LL_GPIO_MODE_OUTPUT 1 

void SystemInit() ; 
int main() 
{ 
    10: b580  push {r7, lr} 
    12: b082  sub sp, #8 
    14: af00  add r7, sp, #0 
    int i = 0; 
    16: 2300  movs r3, #0 
    18: 607b  str r3, [r7, #4] 
    SystemClock_Config(); 
    **1a: f000 f805 bl 28 <SystemClock_Config> 
    for(;;) 
     i++; 
    1e: 687b  ldr r3, [r7, #4] 
    20: 3301  adds r3, #1** 
    22: 607b  str r3, [r7, #4] 
    24: e7fb  b.n 1e <main+0xe> 

} 
00000028 <SystemClock_Config>: 
    *   PLLDIV       = 3 
    *   Flash Latency(WS)    = 1 
    * @retval None 
    */ 
void SystemClock_Config(void) 
{ 
    28: b480  push {r7} 
    2a: af00  add r7, sp, #0 
    SET_BIT(FLASH->ACR, FLASH_ACR_ACC64); 
    2c: 4a33  ldr r2, [pc, #204] ; (fc <SystemClock_Config+0xd4>) 
    2e: 4b33  ldr r3, [pc, #204] ; (fc <SystemClock_Config+0xd4>) 
    30: 681b  ldr r3, [r3, #0] 
    32: f043 0304 orr.w r3, r3, #4 
    36: 6013  str r3, [r2, #0] 
    MODIFY_REG(FLASH->ACR, FLASH_ACR_LATENCY, LL_FLASH_LATENCY_1); 
    38: 4a30  ldr r2, [pc, #192] ; (fc <SystemClock_Config+0xd4>) 
    3a: 4b30  ldr r3, [pc, #192] ; (fc <SystemClock_Config+0xd4>) 
    3c: 681b  ldr r3, [r3, #0] 
    3e: f043 0301 orr.w r3, r3, #1 
    42: 6013  str r3, [r2, #0]* 
} 

실행은 PC 레지스터에 0x1a,가 0x1c,에 0x1E, 0x20에 주위를 반복합니다.

halted: PC: 0x0000001a 
halted: PC: 0x0000001c 
halted: PC: 0x0000001e 
halted: PC: 0x00000020 
halted: PC: 0x0000001a 
halted: PC: 0x0000001c 
halted: PC: 0x0000001e 
halted: PC: 0x00000020 
halted: PC: 0x0000001a 
halted: PC: 0x0000001c 
halted: PC: 0x0000001e 
halted: PC: 0x00000020 

0x1a에서 0x28 (SystemClock_Config)로 점프해야합니다.

+1

probelem는 무엇을 사용합니까? 스택을 푸시하지 않는 것은 무엇입니까? 매개 변수 - 가능한 경우 레지스터를 통해 전달됩니다. 'bl' - 함수를 호출합니다. –

+1

PS 생성 된 코드의 분석을 중지합니다. 첫째, 컴파일러가 코드 생성에 매우 뛰어 났기 때문에 이해하지 못했기 때문에 생성 된 코드가 괜찮은지 확인할 수 있습니다. 무언가가 작동하지 않으면 컴파일러가 아닌 코드가됩니다. –

+1

최적화가 비활성화되어 컴파일되므로 asm은보기 흉하고 읽기가 어렵습니다 (저장/재로드가 많음). 최소한'-Og' 또는'-O1'을 사용하십시오. –

답변

1

응용 프로그램에 인터럽트 테이블이 없습니다. 결과적으로 프로세서는 명령어를 인터럽트 벡터로 읽으며 이러한 명령어가 잘못된 주소로 해석 될 수 없기 때문에 반복적으로 폴트합니다.

STM32L1xx standard peripheral library의 지원 파일을 사용하여 적절한 링커 스크립트와 인터럽트 테이블을 생성하십시오.

+0

나는 아직까지 우리가 주소 0x00000000, 0x00000004에 대한 값을 갖고 있지 않다는 것을 알고있다. (기술적으로는 0x00000004 만 있으면됩니다.) 메인은 재미있는 0x10에서 시작하지만 부팅 성공의 핵심 단어는 표시되지 않습니다. 당신이 과거 리셋을 한 후에 부여받은 다른 문제가있을 것입니다 ... –

+0

@old_timer 리셋 벡터에 명시 적으로 배치 된 데이터가 없으므로 'main' 함수를 가리 키지 않을 가능성이 있습니다. 예를 들어 PC 추적과 관련된 '0x0000_001a'주소를 가리킬 수 있습니다. – duskwuff

+0

이 이해되었지만 실제로 어떤 일이 벌어지고 있는지 확실히 알기에는 충분한 정보가 없습니다. –

3

아주 간단한 작동을 완전히 예 :

vectors.s

.thumb 

.globl _start 
_start: 

.word 0x20001000 
.word reset 

.thumb_func 
reset: 
    bl centry 
done: b done 

so.c

unsigned int fun (unsigned int); 
unsigned int centry (void) 
{ 
    return(fun(5)+1); 
} 

fun.c

unsigned int fun (unsigned int x) 
{ 
    return(x+1); 
} 

을 flash.ld 6,
MEMORY 
{ 
    rom : ORIGIN = 0x00000000, LENGTH = 0x1000 
} 

SECTIONS 
{ 
    .text : { *(.text*) } > rom 
    .rodata : { *(.rodata*) } > rom 
} 

빌드

arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m0 vectors.s -o vectors.o 
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -mcpu=cortex-m0 -mthumb -c so.c -o so.o 
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -mcpu=cortex-m0 -mthumb -c fun.c -o fun.o 
arm-none-eabi-ld -o so.elf -T flash.ld vectors.o so.o fun.o 
arm-none-eabi-objdump -D so.elf > so.list 
arm-none-eabi-objcopy so.elf so.bin -O binary 

전체 프로그램

00000000 <_start>: 
    0: 20001000 andcs r1, r0, r0 
    4: 00000009 andeq r0, r0, r9 

00000008 <reset>: 
    8: f000 f802 bl 10 <centry> 

0000000c <done>: 
    c: e7fe  b.n c <done> 
    ... 

00000010 <centry>: 
    10: b510  push {r4, lr} 
    12: 2005  movs r0, #5 
    14: f000 f802 bl 1c <fun> 
    18: 3001  adds r0, #1 
    1a: bd10  pop {r4, pc} 

0000001c <fun>: 
    1c: 3001  adds r0, #1 
    1e: 4770  bx lr 

프로그램의 시뮬레이션 :

read32(0x00000000)=0x20001000 
read32(0x00000004)=0x00000009 
--- 0x00000008: 0xF000 
--- 0x0000000A: 0xF802 bl 0x0000000F 
--- 0x00000010: 0xB510 push {r4,lr} 
write32(0x20000FF8,0x00000000) 
write32(0x20000FFC,0x0000000D) 
--- 0x00000012: 0x2005 movs r0,#0x05 
--- 0x00000014: 0xF000 
--- 0x00000016: 0xF802 bl 0x0000001B 
--- 0x0000001C: 0x3001 adds r0,#0x01 
--- 0x0000001E: 0x4770 bx r14 
--- 0x00000018: 0x3001 adds r0,#0x01 
--- 0x0000001A: 0xBD10 pop {r4,pc} 
read32(0x20000FF8)=0x00000000 
read32(0x20000FFC)=0x0000000D 
--- 0x0000000C: 0xE7FE b 0x0000000B 
--- 0x0000000C: 0xE7FE b 0x0000000B 
--- 0x0000000C: 0xE7FE b 0x0000000B 
--- 0x0000000C: 0xE7FE b 0x0000000B 
--- 0x0000000C: 0xE7FE b 0x0000000B 
--- 0x0000000C: 0xE7FE b 0x0000000B 
--- 0x0000000C: 0xE7FE b 0x0000000B 
--- 0x0000000C: 0xE7FE b 0x0000000B 
--- 0x0000000C: 0xE7FE b 0x0000000B 
--- 0x0000000C: 0xE7FE b 0x0000000B 

확신이 다소 쓸모없는 프로그램이지만, 부팅 및 호출을 보여줍니다 함수 (함수 주소는 표시되지 않습니다. 전화를 걸면 (bl) r14가 반송 주소를 받고 r15가 분기 할 주소를 얻습니다. centry와 같은 중첩 된 함수가있는 경우 (C 진입 점 main()은 중요한 함수 이름이 아니므로 부팅 스트랩이 일치하는 한 원하는 항목을 진입 점이라고 부를 수 있음) fun를 호출하면 반송 주소를 보존해야합니다. 일반적으로 스택에 저장합니다. r4는 abi 당 64 비트 경계에 정렬되도록 스택을 푸시하기 만합니다.

시스템에서는 일반적으로 0x08000000의 링커 스크립트를 설정합니다 (stm32).

우리가 누락 된 부분은 바이너리의 시작입니다. 메모리 이미지/바이너리의 16 진수 덤프를 수행 할 수 있습니까?

베어 메탈 프로그램이 가장 간단한 부트 단계를 수행하지 못하는 경우 가장 먼저 할 일은 엔트리 포인트 또는 벡터 테이블이 아키텍처에 종속되어 있고 바이너리를 올바르게 빌드했는지 확인하는 것입니다.

이 예제에서는이 예제는 cortex-m이므로 스택 포인터 초기화 값 (사용하도록 선택한 경우)은 0x00000000에 있으므로 아무 것이나 넣을 수 있으며 원하는 경우 sp를 쓸 수 있습니다. 당신의 선택은 ... 주소 0x00000004는 엄지 모드를 표시하도록 설정된 lsbit로 리셋을 처리하는 코드의 주소 인 리셋 벡터입니다.

0x00000008 | 1 = 0x00000009입니다.

당신은 0x00000011

0x2000xxxx 다음 프로세서를 잘 부팅하지 않을 해달라고합니다. 나는 0x00000000이 stm을 위해 작동한다면 기억하지 못하는 0x08000000을 사용하는 습관이 너무 많아 이론적으로는 ... 그러나 플래시를로드하는 방법과 그 모드에서 어떤 모드/상태인지에 달려있다. 아무것도 다른 이진/메모리 이미지의 처음 두 단어로 0x08000011

0x2000xxxx

를 변경하지 않는 경우

당신은 0x08000000과 최소한 연결해야 할 수도 있습니다.

편집

노트 당신은 벡터 또는 스택 주소 자리에 (공간을 채우기에 잘 BL) 지점을 배치하는 부트 로더

.thumb 

.thumb_func 
.global _start 
_start: 
bl reset 
.word _start 
reset: 
    ldr r0,stacktop 
    mov sp,r0 
    bl notmain 
    b hang 
.thumb_func 
hang: b . 
.align 
stacktop: .word 0x20001000 

으로 모두 입력 할 수있는 하나의 바이너리를 만들 수 있습니다 스택 포인터를 나중에로드하십시오.

또는 지점

.thumb 

.thumb_func 
.global _start 
_start: 
b reset 
nop 
.word _start 
reset: 
    ldr r0,stacktop 
    mov sp,r0 
    bl notmain 
    b hang 
.thumb_func 
hang: b . 
.align 
stacktop: .word 0x20001000 
+0

stm32 로직은 0x08000000에서 0x00000000으로 맵핑하여 애플리케이션 프로그램이 0x08000011을 플래시하거나 리셋 벡터가 무엇이든간에 실제 주소로 이동한다. 마이크로 컨트롤러에서 부트/벡터 영역을 다시 매핑 할 수있는 흔한 일은 아닙니다. –

관련 문제