대표 문자열 명령의 작동 방식을 조사하고 있습니다. 명령에 대한 설명과 관련하여 예를 들어 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--;
}
는하지만 담당자 문자열 추가 세그먼트와 작동하는지 사실이 아니다 플랫 메모리 모델에서 보이는 아래 같은 동작 모양의 의사 코드.
예를 들어, rep movs를 사용하여 TLS (스레드 로컬 저장소) 배열에 배열을 복사하는 2 개의 스레드를 만드는 테스트를 만들었습니다. TLS 데이터가 ES가 아닌 GS 세그먼트에 보관되므로 논리적으로는 작동하지 않아야합니다. 그러나 일합니다. 테스트를 실행하는 정확한 결과를 적어도보십시오. 인텔 컴파일러는 다음 코드를 작성합니다.
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;
}
OK, 세그먼트 레지스터 ES와 GS의 기본값이 같은 것을 의미합니까? 그런 경우에는 또 다른 질문이 있습니다. 어떻게하면이 프로그램을 실행 불가능하게 만들 수 있습니까? 어셈블리를 사용하지 않고 GS 나 ES의 가치를 변경하려면 실제로 무엇을해야합니까? – Andrei
아니요, 거의 확실하지 않습니다 * 같지 않지만 중요하지 않습니다. 이 두 명령어 중 첫 번째 명령어는 '% gs'가 가리키는 블록에서 값을로드하고 그 값은 (0베이스, 즉 '% ds' /'% cs' /'% es'- 관련) 포인터입니다 스레드에 대한 정적 TLS 블록 'TLS_data1'은 그 포인터에 대한 오프셋으로 발견되며,이 주소는'% gs'에 상대적인 것이 아니며 '% ds'또는 '% es'와 관련됩니다 (동일). – caf
답변 해 주셔서 감사합니다. 그들은 나를 생각하게 만들었습니다 :) IA32 %에서 gs : 0은 스레드 제어 블록을 가리 킵니다. 그러나 해당 포인터는 GS가 아닌 DS의 가상 주소입니다. 그게 전부 야. – Andrei