重复字符串指令是否使用额外段(ES)?

问题描述:

我在调查rep字符串指令是如何工作的。关于指令的描述,例如rep movsl具有以下助记符。重复字符串指令是否使用额外段(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--; 
} 

但似乎在平面内存模式是不正确的,代表字符串附加段操作的伪码。

例如我创建了一个测试,创建2个线程,使用rep movs将数组复制到TLS(线程本地存储)数组。逻辑上说,这不应该起作用,因为TLS数据保存在GS细分中,而不是ES中。但工作。至少看到运行测试的正确结果。 英特尔编译器生成以下应对代码片段。

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; 
} 

你所缺少的是,这些OPS:

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

加载的线程本地TLS_data1%edi从零开始的地址,这对于ES基础的零基础部分可以很好地工作。

+0

好的,你的意思是段寄存器ES和GS的默认值是相等的?在这种情况下还有另外一个问题,我该如何让这个程序不可行?我应该怎样做才能改变GS或ES的价值,但不使用组装? – Andrei

+0

不,他们几乎可以肯定*不是平等的,但没关系。这两条指令中的第一条指令从'%gs'指向的块加载一个值,并且该值是一个(0-base,即'%ds' /'%cs' /'%es' -relative)指针线程的静态TLS块。然后找到'TLS_data1'作为该指针的偏移量,并且该地址与'%gs'不同,它与'%ds'或'%es'(它们是相同的)相对。 – caf

+0

谢谢你的答案。他们让我开始思考:)在IA32%gs:0指向线程控制块。但是该指针是DS中的虚拟地址,而不是GS。而已。 – Andrei