2017-01-08 7 views
-1

私はrep文字列命令の仕組みを調べています。命令の記述に関しては、例えば、mov movは以下のニーモニックを有する。リピート文字列命令で余分なセグメント(ES)が使用されるかどうか?

rep movsl 5+4*(E)CX Move (E)CX dwords from [(E)SI] to ES:[(E)DI] 

ここで、ESは、メモリ内の余分なセグメントの先頭までのオフセットを含む余分なセグメントレジスタです。

操作の擬似コードは

while (CX != 0) { 
     *(ES*16 + DI) = *(DS*16 + SI); 
     SI++; 
     DI++; 
     CX--; 
} 

以下のように見える。しかし、担当者文字列は、余分なセグメントで動作というのは本当ではないフラットなメモリモデルに思えます。

たとえば、rep movsを使用して配列をTLS(スレッドローカルストレージ)配列にコピーする2つのスレッドを作成するテストを作成しました。 TLSデータはESではなくGSセグメントに保存されるため、論理的には機能しません。しかし、仕事。少なくとも、テストを実行している正しい結果を確認してください。 Intelコンパイラは次のコードを生成して対処します。

movl  %gs:0, %eax         #27.18 
movl  $1028, %ecx         #27.18 
movl  32(%esp), %esi        #27.18 
lea  [email protected](%eax), %edi     #27.18 
movl  %ecx, %eax         #27.18 
shrl  $2, %ecx          #27.18 
rep              #27.18 
movsl             #27.18 
movl  %eax, %ecx         #27.18 
andl  $3, %ecx          #27.18 
rep              #27.18 
movsb             #27.18 

ここで、%ediはTLS配列を指し、そこにはrep movsが格納されています。 rep movが暗黙のうちに(私が疑う)ESオフセットを使用する場合、そのようなコードは正しい結果を生成すべきではありません。

ここに何かがありますか?

私が作成したテストがあります:

#define _MULTI_THREADED 
#include <stdio.h> 
#include <stdlib.h> 
#include <pthread.h> 

#define     NUMTHREADS 2 
#define     N 257 

typedef struct { 
    int data1[N]; 
} threadparm_t; 

__thread threadparm_t TLS_data1; 

void foo(); 

void *theThread(void *parm) 
{ 
    int    rc; 
    threadparm_t  *gData; 

    pthread_t self = pthread_self(); 
    printf("Thread %u: Entered\n", self); 

    gData = (threadparm_t *)parm; 

    TLS_data1 = *gData; 

    foo(); 
    return NULL; 
} 

void foo() { 
    int i; 
    pthread_t self = pthread_self(); 
    printf("\nThread %u: foo()\n", self/1000000); 
    for (i=0; i<N; i++) { 
     printf("%d ", TLS_data1.data1[i]); 
    } 
    printf("\n\n"); 
} 


int main(int argc, char **argv) 
{ 
    pthread_t    thread[NUMTHREADS]; 
    int     rc=0; 
    int     i,j; 
    threadparm_t   gData[NUMTHREADS]; 

    printf("Enter Testcase - %s\n", argv[0]); 

    printf("Create/start threads\n"); 
    for (i=0; i < NUMTHREADS; i++) { 
    /* Create per-thread TLS data and pass it to the thread */ 
     for (j=0; j < N; j++) { 
      gData[i].data1[j] = i+1; 
     } 
     rc = pthread_create(&thread[i], NULL, theThread, &gData[i]); 
    } 
    printf("Wait for the threads to complete, and release their resources\n"); 
    for (i=0; i < NUMTHREADS; i++) { 
     rc = pthread_join(thread[i], NULL); 
    } 

    printf("Main completed\n"); 
    return 0; 
} 

答えて

0

何が不足していることであるが、これらのOPS:

movl  %gs:0, %eax 
... 
lea  [email protected](%eax), %edi 

スレッドローカルTLS_data1%ediへのゼロベースのアドレスをロードしています、ゼロベースのESセグメントで正常に動作します。

+0

OK、セグメントレジスタESとGSのデフォルト値が等しいことを意味しますか?そのような場合は別の質問がありますが、どうすればこのプログラムを実行不能にすることができますか? GSやESの価値を変更するのに実際に何をすべきですか、アセンブリを使用しないのですか? – Andrei

+0

いいえ、それはほぼ確実ではありませんが、それは問題ではありません。これら2つの命令のうちの最初の命令は、 '%gs'が指すブロックから値をロードし、その値は(0ベース、すなわち'%ds'/'%cs' /'%es'-相対的な)ポインタです。スレッドの静的TLSブロック'TLS_data1'はそのポインタへのオフセットとして見つけられ、このアドレスは'%gs'に相対的ではなく、 '%ds'または'%es'(これは同じもの)に相対的です。 – caf

+0

お返事ありがとうございます。彼らは私に考え始めました:) IA32では、gs:0はスレッド制御ブロックを指しています。しかし、そのポインタはGSではなく、仮想アドレスです。それでおしまい。 – Andrei

関連する問題