重复字符串指令是否使用额外段(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基础的零基础部分可以很好地工作。
好的,你的意思是段寄存器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