2

アセンブラ/cの質問があります。セグメントプレフィックスについて読んだばかりです。たとえば、ds:varXなどです。プレフィックスは、論理アドレスの計算に重要です。私も読みましたが、そのデフォルトは「ds」であり、ebpレジスタを使用してアドレスを計算するとすぐに「ss」が使用されます。コードの場合、「cs」がデフォルトです。それはすべて理にかなっています。今、私はcに次のものを持っています:

int x; // some static var in ds

void test(int *p){
...
*p =5;

}

... main(){

test(&x);
//now x is 5
}

ここでtest-functionの実装について考えると、スタック上のxへのポインターを取得します。ポインターを逆参照する場合は、最初にスタックからポインター値(xのアドレス)を取得し、たとえばeaxに保存します。次に、eaxを逆参照して、xの値を変更できます。しかし、c-compilerは、指定されたポインター(アドレス)がスタック上のメモリを参照しているかどうか(たとえば、別の関数からtestを呼び出し、テストのパラメーターとしてローカル変数のアドレスをプッシュした場合)またはデータセグメントをどのように認識しますか?完全な論理アドレスはどのように計算されますか?関数は、指定されたアドレスオフセットがどのセグメントに関連しているかを知ることができません。

4

6 に答える 6

3

eax一般に、セグメント化されたプラットフォームでは、あなたが提案するように、ポインター値を「に」読み取ることはできません。セグメント化されたプラットフォームでは、ポインターは通常、セグメント値とオフセット値の両方を保持します。つまり、そのようなポインターを読み取ると、セグメントとオフセットの 1 つだけでなく、少なくとも2 つのレジスターを初期化することになりますeax

ただし、特定のケースでは、いわゆるメモリモデルに依存します。セグメント化されたプラットフォームのコンパイラは、いくつかのメモリ モデルをサポートしていました。

まず、明らかな理由から、セグメント レジスタが正しい値を保持している限り、どのセグメント レジスタを使用しても問題ありません。たとえば、DSESレジスタが内部で同じ値を保持している場合、DS:<offset>は とメモリ内の同じ場所を指しますES:<offset>

たとえば、いわゆる「タイニー」メモリ モデルでは、すべてのセグメント レジスタが同じ値を保持していました。つまり、コード、データ、スタックなどすべてが 1 つのセグメントに収まっていました (これが「タイニー」と呼ばれる理由です)。このメモリ モデルでは、各ポインタはこのセグメントの単なるオフセットであり、もちろん、そのオフセットでどのセグメント レジスタを使用するかは問題ではありませんでした。

「より大きな」メモリ モデルでは、コード (CS)、スタック (SS)、およびデータ (DS) 用に個別のセグメントを持つことができます。しかし、そのようなメモリ モデルでは、ポインター オブジェクトは通常、アドレスのオフセット部分とセグメント部分の両方を内部に保持します。あなたの例では、ポインターpは実際には2つの部分からなるオブジェクトであり、セグメント値とオフセット値の両方を同時に保持します。このようなポインターを逆参照するために、コンパイラーは、セグメント値とオフセット値の両方を読み取り、それらの両方を使用するコードを生成しpます。たとえば、セグメント値はESレジスタに読み込まれ、オフセット値はsiレジスタに読み込まれます。次に、コードは値ES:[di]を読み取るためにアクセスし*pます。

コードが 1 つのセグメント (CS) に格納され、データとスタックの両方が別のセグメントに格納される「中間」メモリ モデルもあったためDS、 とSSは同じ値を保持します。DSそのプラットフォームでは、明らかに、とを区別する必要はありませんでしたSS

最大のメモリ モデルでは、複数のデータ セグメントを持つことができます。この場合、セグメント化モードでの適切なデータ アドレス指定は、実際には適切なセグメント レジスタを選択することではなく (あなたが信じているように)、ほとんどすべてのセグメント レジスタを取得して正しい値で初期化することの問題であることは明らかです。アクセスを実行する前の値。

于 2011-02-01T18:56:18.430 に答える
1

セグメント化されたメモリモデルを備えたマシンでは、C実装は、準拠するために次のいずれかを実行する必要があります。

  • 完全なアドレス(セグメントを含む)を各ポインタに格納する、または
  • アドレスが取得される変数に使用されるすべてのスタックアドレスが、同じ相対アドレスで、またはローカル変数のアドレスを取得するときにコンパイラが適用できるマジックオフセットを介して、データセグメントを介してアクセスできることを確認します。または
  • アドレスが取得されるローカル変数にはスタックを使用せず、すべての関数のエントリ/リターンで非表示のmalloc / freeを実行します(longjmp!の特別な処理を使用)。

おそらく他の方法もありますが、私が考えることができるのはこれらだけです。セグメント化されたメモリモデルは、Cにはかなり不愉快であり、正当な理由で放棄されました。

于 2011-02-01T20:09:35.777 に答える
1

AndreyT が説明したのは、DOS の日に起こったことです。最近のオペレーティング システムは、いわゆるフラット メモリ モデル(または非常に類似したモデル) を使用しています。このモデルでは、すべての (保護モードの) セグメントがすべてアドレス空間全体にアクセスできるように設定されています (つまり、基数が 0 です)。および制限 = アドレス空間全体)。

于 2011-02-01T19:28:42.003 に答える
0

x86 では、スタックを直接使用するとスタック セグメントが使用されますが、間接的に使用するとスタック セグメントがデータ セグメントとして扱われます。これは、ポインター逆参照を逆アセンブルし、スタック セクション ポインターに書き込むとわかります。x86 cs では、ss と ds は、リニア アドレッシングのため、(少なくとも非カーネル モードでは) ほとんど同じように扱われます。インテルのリファレンス マニュアルには、セグメント アドレッシングに関するセクションもあるはずです。

于 2011-02-01T18:58:13.200 に答える
0

セグメンテーションは、Intel 16 ビット 8086 プロセッサのレガシー アーティファクトです。実際には、すべてが単なる線形アドレスである仮想メモリで操作する可能性があります。フラグを付けてコンパイルし-S、結果のアセンブリを確認します。

于 2011-02-01T18:46:38.233 に答える
0

アドレスを逆参照する前に eax に移動するため、デフォルトで ds セグメントになります。ただし、Nikolai が述べたように、ユーザー レベルのコードでは、セグメントはおそらくすべて同じアドレスを指しています。

于 2011-02-01T18:53:34.720 に答える