Я изучаю, как работают инструкции 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;
}
ОК, вы имеете в виду, что значения по умолчанию для сегментных регистров ES и GS равны? В таком случае возникает другой вопрос: как я могу сделать эту программу неработоспособной? Что я должен делать, чтобы изменить значение GS или ES, но не использовать сборку? – Andrei
Нет, они почти наверняка * не * равны, но это не имеет значения. Первая из этих двух команд загружает значение из блока, на которое указывает '% gs', и это значение является (0-base, то есть'% ds'/'% cs' /'% es' -relative) указателем на статический блок TLS для потока. 'TLS_data1' затем будет найден как смещение к этому указателю, и снова этот адрес не относится к'% gs', это относительно '% ds' или'% es' (которые одинаковы). – caf
Спасибо за ответы. Они заставили меня задуматься :) На IA32% gs: 0 указывает на блок управления потоком. Но этот указатель является виртуальным адресом в DS, а не GS. Вот и все. – Andrei