27

リンカー スクリプト ファイルで、2 つのシンボルを定義しました。

define symbol _region_RAM_start__     = 0xC0000000;
define symbol _region_RAM_end__       = 0xC00fffff; 

そして、以下に示すようにそれらをエクスポートしました

export symbol _region_RAM_start__;
export symbol _region_RAM_end__;

アプリケーションコードから、これらのシンボルにアクセスしようとします

extern const unsigned int _region_RAM_start__;
extern const unsigned int _region_RAM_end__;
....
int GetRAMSize()
{
    int size = 0;
    unsigned int address_1 = _region_RAM_start__;
    unsigned int address_2 = _region_RAM_end__;
    size = address_2 - address_1 + 1U;
    return size;
}

ここで、戻り値が 0x00100000 になると予想していましたが、返されるのは 0 だけです。そのため、デバッガーに目を向けると、値がそれぞれ 0xC0000000 と 0xC00fffff であることに気付き_region_RAM_start__まし_region_RAM_end__たがaddress_1address_2値は 0 です。

コンパイラの最適化は「なし」に設定されています。これはしばらくの間私を悩ませてきました。ここで私が見逃している非常に明白なものはありますか(「そもそもこれを行うべきではない」以外)

解決策 答えてくれた nm に感謝

  unsigned int address_1 = (unsigned int) (&_region_RAM_start__);

それ以外の場合address_1address_2両方ともガベージ値を含みます (つまり、アドレス 0xC0000000 と 0xC00fffff でそれぞれ利用可能な値ですが、このコードの観点からはガベージです)。

4

3 に答える 3

30

少し古いですが、とにかくお答えします…</p>

ldマニュアルから:

ソース コードからリンカー スクリプトで定義された変数にアクセスするのは直感的ではありません。特に、リンカ スクリプト シンボルは、高級言語の変数宣言と同等ではなく、代わりに値を持たないシンボルです。

先に進む前に、コンパイラは、ソース コード内の名前をシンボル テーブルに格納するときに、別の名前に変換することがよくあることに注意することが重要です。たとえば、Fortran コンパイラは通常アンダースコアを前または後に追加し、C++ は広範な名前マングリングを実行します。したがって、ソース コードで使用されている変数の名前と、リンカー スクリプトで定義されている同じ変数の名前との間に不一致が生じる可能性があります。たとえば、C では、リンカー スクリプト変数は次のように参照される場合があります。

extern int foo;

ただし、リンカー スクリプトでは、次のように定義される場合があります。

_foo = 1000;

ただし、残りの例では、名前の変換は行われていないものと想定しています。

C などの高級言語でシンボルを宣言すると、2 つのことが起こります。1 つ目は、コンパイラがシンボルのを保持するのに十分なスペースをプログラムのメモリに確保することです。2 つ目は、コンパイラがシンボルのアドレスを保持するプログラムのシンボル テーブルにエントリを作成することです。つまり、シンボル テーブルには、シンボルの値を保持するメモリ ブロックのアドレスが含まれます。たとえば、ファイル スコープでの次の C 宣言は次のようになります。

int foo = 1000;

シンボルテーブルに「foo」というエントリを作成します。このエントリは、数値 1000 が最初に格納される int サイズのメモリ ブロックのアドレスを保持します。

プログラムがシンボルを参照すると、コンパイラは、最初にシンボル テーブルにアクセスしてシンボルのメモリ ブロックのアドレスを検索し、次にそのメモリ ブロックから値を読み取るコードを生成します。そう:

foo = 1;

シンボル テーブルでシンボル foo を検索し、このシンボルに関連付けられたアドレスを取得してから、値 1 をそのアドレスに書き込みます。一方:

int * a = & foo;

シンボル テーブルでシンボル foo を検索し、そのアドレスを取得してから、このアドレスを変数 "a" に関連付けられたメモリ ブロックにコピーします。

対照的に、リンカ スクリプトのシンボル宣言は、シンボル テーブルにエントリを作成しますが、それらにメモリを割り当てません。したがって、それらは値のないアドレスです。たとえば、リンカー スクリプトの定義は次のとおりです。

foo = 1000;

@samp{foo} という名前のシンボル テーブルに、メモリ位置 1000 のアドレスを保持するエントリを作成しますが、アドレス 1000 には特別なものは格納されていません。これは、リンカ スクリプトで定義されたシンボルのにアクセスできないことを意味します。つまり、値がありません。 - できることは、リンカー スクリプトで定義されたシンボルのアドレスを使用することだけです。

したがって、ソース コードでリンカー スクリプトで定義されたシンボルを使用している場合は、常にシンボルのアドレスを取得し、その値を使用しようとしないでください。たとえば、.ROM というメモリ セクションの内容を .FLASH というセクションにコピーし、リンカー スクリプトに次の宣言が含まれているとします。

start_of_ROM   = .ROM;
end_of_ROM     = .ROM + sizeof (.ROM);
start_of_FLASH = .FLASH;

次に、コピーを実行する C ソース コードは次のようになります。

extern char start_of_ROM, end_of_ROM, start_of_FLASH;

memcpy (& start_of_FLASH, & start_of_ROM, & end_of_ROM - & start_of_ROM);

「&」演算子の使用に注意してください。それらは正しいです。

于 2016-11-03T00:50:47.783 に答える