2017-12-19 2 views
-1

Atmel AVR 마이크로 컨트롤러에 자체 부트 로더를 작성하려고했습니다. github에서 코드베이스 중 하나를 참조했습니다. 코드베이스에 대해 ZEVERO((void (*) (void)) 0)()은 Exit 함수입니까?

감사드립니다. 기본 코드에서 코드베이스를 이해합니다. 하지만 라인 (224)에서 나는 줄 Reference to the code

**if (pgm_read_word(0) != 0xFFFF) ((void(*)(void))0)(); //EXIT BOOTLOADER** 

내가 조건의 경우 부분을 이해를 발견하지만 난 코드 작가가에 대한 설명을 주신 즉

**((void(*)(void))0)();**

진정한 문 부분을 이해하려고 할 때 이것은 // EXIT BOOTLOADER

첫 번째 질문은이 복합 선언의 의미는 무엇입니까? **((void(*)(void))0)();**

두 번째 질문은 마이크로 컨트롤러에서 코드 실행을 종료하는 것입니다.

+6

아니요 (직접 종료 코드는 아닙니다). 아마도 충돌을 일으키거나 주소 0 (다시 시작 코드 일 수 있음)에서 코드를 실행할 것입니다. 정의되지 않은 동작이므로 정의 된 동작을 제공하는 컨텍스트가 없으면 좋지 않습니다. –

+4

플랫폼의 경우이 코드가 작성되며 재설정 코드 일 수 있습니다. 그러나 이것은 결코 이식성이 없습니다. 항상 표준 ['exit'] (https://www.tutorialspoint.com/c_standard_library/c_function_exit.htm) 함수를 사용하십시오. –

답변

10

@iBug가 지적했듯이 ((void(*)(void))0)();은 NULL 함수 포인터에서 함수 호출을 호출합니다.

사실상 프로그램 제어가 메모리 주소 0으로 전달됩니다. 이제는 워크 스테이션에서 엄청난 UB가 될 것이므로 대부분 세그 폴트가 발생합니다.

그러나 해당 코드는 하드웨어 부트 로더 용이므로 UB가 아니며 부트 로더를 종료합니다. 하드웨어 수준에서

거의 모든 구현 의존, 거의 아무것도 휴대용입니다. 특정 하드웨어 플랫폼을 대상으로하는 C 코드는 일반적으로 받아 들여지는 C 패턴 및 관행을 대표하는 방식으로 기대할 수 없습니다.

+0

음 ... 좋은 장소. – iBug

+0

UB에 대해 이해하지 못합니까? –

+0

UB - 정의되지 않은 동작 이것은 표준에서 다루지 않는 C 언어의 한 측면을 의미하므로 컴파일러 구현은 원하는대로 처리 할 수 ​​있습니다. –

-1

단순히 주소 0을 호출하는 것으로, 이는 void을 반환하고 인수를 사용하지 않는 함수입니다. 또는 간단히 말해 널 포인터의 비트 패턴 인 주소. 또는 덜 간단하게 말하면, 동작은 정의되지 않으므로 예상치 못한 일을 할 수 있습니다.

3

가 액세스 위반이 있기 때문에 이것은 정의되지 않은 동작입니다 (null의 주소에서 메모리에 액세스하려고)

하자 첫번째 떨어져 식 걸릴 :

((void(*)(void)) 0)() 
~~~~~~~~~~~~~~~~~~~~~~ 

을 내부 void(*)(void) 함수의 유형입니다 포인터, 인수를 취하지 않고 아무것도 반환하지 않습니다. (type)0은 C로 형 변환되므로 밑줄 친 부분은 "널 포인터가 함수 포인터로 변환되었습니다."입니다. 빈 괄호의 마지막 쌍은 주소 0에서 함수를 호출하는 함수 호출입니다. 이것은 정의되지 않은 동작이므로 프로그램이 충돌합니다. 그것이 UB이기 때문에, 더 나쁜 일이 일어날 수 있으므로 충돌은 매우 좋은 결과입니다.

+1

음, null 주소에 액세스하는 "액세스 위반"이 없습니다. 이는 꼭 필요한 동작이 아닙니다. –

+0

널 포인터를 역 참조하는 것은 UB 일뿐입니다. –

+0

@ Jean-BaptisteYunès 이것은 "널 포인터를 역 참조"하지 않습니다. 그것은 "값 0을 갖는 포인터의 참조 해제"입니다. 똑같이 보일지라도 다른 것. – tofro

5

((void(*)(void))0)(); NULL 함수 포인터를 호출하려고합니다. AVR 마이크로 컨트롤러에 대한 사용자 프로그램 (부트 로더 아님)은 일반적으로 주소 0에서 실행을 시작합니다. AVR-GCC의 ABI는 NULL 포인터를 모두 0 비트 표현으로 사용하므로이 호출은 (특히) 사용자 프로그램으로 실행을 전송합니다.기본적으로 __asm__ __volatile__("jmp 0");의 느린 버전으로 작동하며 사용자 프로그램의 시작 코드가 스택 포인터를 다시 초기화한다고 가정합니다.

NULL 함수 포인터를 통한 호출은 정의되지 않은 동작이므로이 트릭이 다른 컴파일러, 이후 버전의 GCC 또는 다른 최적화 설정에서도 작동한다는 보장이 없습니다.

if (pgm_read_word(0) != 0xFFFF)은 사용자 프로그램이 있는지를 확인하기 전에 검사해야합니다. 지워졌지 만 기록되지 않은 프로그램 메모리 단어는 0xFFFF로 읽히지 만 대부분의 프로그램은 나머지 부분을 건너 뛰는 JMP 명령으로 시작합니다. 인터럽트 벡터 테이블 및 JMP 명령어의 첫 번째 워드는 절대로 0xFFFF가 아닙니다.

0

이 기능은 단순히 일반적으로 자신의 프로그램에 의해 정의되지 않은이 주소의 코드로 0

, 오히려 특정 환경에 의해 행동 해결하기 위해 점프를 초래 호출 전에 지적 한 바와 같이 전적으로이 환경에 달려 있습니다.

AVR에 AVM/Atmel : 0으로 점프하면 하드웨어가 다시 시작될 때와 거의 같은 동작을하지만 결과적으로 MCU는 인터럽트를 활성화/비활성화 상태로 유지합니다. "진짜"리셋). "클리너"프로그램은 "실제"재설정 (wdt_reset() 외)을 위해 워치 독 타이머를 사용하려고 할 수 있습니다.

관련 문제