2017-01-08 10 views
-1

Я изучаю, как работают инструкции string string. Что касается описания инструкций, например, 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 потока, которые копируют массив в массив TLS (поток локального хранилища), используя rep movs. Логически, что не должно работать, поскольку данные TLS хранятся в сегменте GS, а не в ES. Но работает. По крайней мере, посмотрите правильный результат выполнения теста. Компилятор 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

Что вам не хватает в том, что эти опс:

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