2017-12-19 43 views
-1

私はAtmel AVR Microcontrollerに独自のブートローダを書き込もうとしていました。私はgithubからコードベースの1つを参照しました。コードベースについてZEVERO((void(*)(void))0)()はExit関数ですか?

私はコードベースを理解しています。しかし、ライン224で私は、コードライタがに説明を与えている

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

すなわち、私はもし条件部を理解するが、私は真の文の一部を理解しようとしていたときに、ライン Reference to the code

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

を見つけましたこれは// EXIT BOOTLOADER

私の最初の質問は、この複合宣言の意味は何ですか。 **((void(*)(void))0)();**

2番目の質問は、マイクロコントローラでコードの実行を終了しますか?

+6

いいえ(直接終了コードではありません)。クラッシュするか、そうでなければアドレス0(コードを再起動するかもしれない)でコードを実行するでしょう。定義されていない動作なので、定義された動作を与えるコンテキストがない限り悪いです。 –

+4

プラットフォームの場合、このコードは書かれているかもしれませんが、コードをリセットするかもしれません。しかし、これは決してポータブルではありません。常に標準の['exit'](https://www.tutorialspoint.com/c_standard_library/c_function_exit.htm)機能を使用してください。 –

答えて

10

@iBugが指摘したように、((void(*)(void))0)();はNULL関数ポインタで関数呼び出しを呼び出します。

実際には、プログラム制御をメモリアドレス0に転送します。今、ワークステーション上では、これは巨大なUBであり、おそらくsegfaultになります。

しかし、問題のコードはハードウェアブートローダ用であるため、UBではないため、ブートローダを終了するだけです(明らかに)。

ハードウェアレベルでは、ほとんどすべては実装に依存し、ほとんどはありませんは移植性があります。特定のハードウェアプラットフォームを対象とするCコードは、一般的に受け入れられているCのパターンや実践を代表するものではありません。

+0

うーん...いいところ。 – iBug

+0

私はUBを理解していませんか? –

+0

UB - 未定義の動作これは、標準でカバーされていないC言語の側面を意味するため、コンパイラの実装では自由に処理できます。 –

-1

アドレス0を、あたかもvoidを返して引数をとらない関数であるかのように呼び出します。または...単純にヌルポインタのビットパターンであるアドレス。それとも、それほど簡単ではありませんが、動作は定義されていません。

3

それは(ヌルアドレスのメモリにアクセスしようとしている)、アクセス違反ですので、これは未定義の動作です

てみましょう最初の分解式を取る:

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

内部void(*)(void)は、関数の型では、引数をとりません。何も返しません。 (type)0はC言語で型キャストされているので、下線部は "ヌルポインタから関数ポインタへの変換"です。空のかっこの最後のペアは、関数呼び出しであり、アドレス0で関数を呼び出します。これは未定義の動作であるため、プログラムがクラッシュします。それはUBなので、クラッシュは悪化した可能性があるため、非常に良い結果です。

+1

Umm、ヌルアドレスにアクセスする「アクセス違反」はありません。これは必須の動作ではありません。 –

+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()など)のためにウォッチドッグタイマーを使用したいかもしれません。