// compiled with : gcc -o memcpy memcpy.c -m32 -lm (32bit, math 관련 라이브러리)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#include <math.h>
unsigned long long rdtsc(){
// time stamp counter를 가져오는 명령어
asm("rdtsc");
}
char* slow_memcpy(char* dest, const char* src, size_t len){
int i;
for (i=0; i<len; i++) {
dest[i] = src[i];
}
return dest;
}
char* fast_memcpy(char* dest, const char* src, size_t len){
size_t i;
// 64-byte block fast copy
if(len >= 64){
i = len / 64;
len &= (64-1);
while(i-- > 0){
__asm__ __volatile__ (
// movdqa 메모리 xxm : 메모리에 있는 16B 데이터를 한번에 xmm 레지스터로 전송
// movntps xmm 메모리 : xmm에 있는 16B 데이터를 메모리로 복사한다
// 더블QWORD(=16B)를 XMM 레지스터와 128bit 메모리 간, XMM레지스터간 이동하는데 사용
// 피연산자가 메모리인경우 피연산자는 16Byte 경계에 정렬되어야 하며 아닐경우 예외 발생
// 16B씩 나눠서 src의 데이터를 xmm0~xmm3에 전송 후
"movdqa (%0), %%xmm0\n"
"movdqa 16(%0), %%xmm1\n"
"movdqa 32(%0), %%xmm2\n"
"movdqa 48(%0), %%xmm3\n"
// xmm0~xmm3의 데이터를 dst 메모리로 복사
"movntps %%xmm0, (%1)\n"
"movntps %%xmm1, 16(%1)\n"
"movntps %%xmm2, 32(%1)\n"
"movntps %%xmm3, 48(%1)\n"
::"r"(src),"r"(dest):"memory");
// 64B 증가시켜 복사 재개
dest += 64;
src += 64;
}
}
// byte-to-byte slow copy
if(len) slow_memcpy(dest, src, len);
return dest;
}
int main(void){
setvbuf(stdout, 0, _IONBF, 0);
setvbuf(stdin, 0, _IOLBF, 0);
printf("Hey, I have a boring assignment for CS class.. :(\n");
printf("The assignment is simple.\n");
printf("-----------------------------------------------------\n");
printf("- What is the best implementation of memcpy? -\n");
printf("- 1. implement your own slow/fast version of memcpy -\n");
printf("- 2. compare them with various size of data -\n");
printf("- 3. conclude your experiment and submit report -\n");
printf("-----------------------------------------------------\n");
printf("This time, just help me out with my experiment and get flag\n");
printf("No fancy hacking, I promise :D\n");
unsigned long long t1, t2;
int e;
char* src;
char* dest;
unsigned int low, high;
unsigned int size;
// allocate memory
char* cache1 = mmap(0, 0x4000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
char* cache2 = mmap(0, 0x4000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
src = mmap(0, 0x2000, 7, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
size_t sizes[10];
int i=0;
// setup experiment parameters
for(e=4; e<14; e++){ // 2^13 = 8K
low = pow(2,e-1);
high = pow(2,e);
printf("specify the memcpy amount between %d ~ %d : ", low, high);
scanf("%d", &size);
if( size < low || size > high ){
printf("don't mess with the experiment.\n");
exit(0);
}
sizes[i++] = size;
}
sleep(1);
printf("ok, lets run the experiment with your configuration\n");
sleep(1);
// run experiment
for(i=0; i<10; i++){
size = sizes[i];
printf("experiment %d : memcpy with buffer size %d\n", i+1, size);
dest = malloc( size );
memcpy(cache1, cache2, 0x4000); // to eliminate cache effect
t1 = rdtsc();
slow_memcpy(dest, src, size); // byte-to-byte memcpy
t2 = rdtsc();
printf("ellapsed CPU cycles for slow_memcpy : %llu\n", t2-t1);
memcpy(cache1, cache2, 0x4000); // to eliminate cache effect
t1 = rdtsc();
fast_memcpy(dest, src, size); // block-to-block memcpy
t2 = rdtsc();
printf("ellapsed CPU cycles for fast_memcpy : %llu\n", t2-t1);
printf("\n");
}
printf("thanks for helping my experiment!\n");
printf("flag : ----- erased in this source code -----\n");
return 0;
}
// 로컬에서 컴파일해서 진행하면 잘 동작. 원격에선?
// 범위 내 최솟값만 넣었는데 step5 fast_memcpy에서 Segmentation fault 발생
// 문제서버의 tmp 아래 폴더 만들고 touch memcpy.c로 파일 만들고 vi memcpy 한 후 코드 붙여넣고 :Wq해서 저장 후 gcc 컴파일해서 gdb로 분석해보자
// fast_memcpy에서 movdqa 시작하는 부분(0x080487b9)에 bp 걸고 확인해보자
// stage5 fast_memcpy에서 0x80487cc에서 ni하니깐 SEG Fault 떴다
// 0x080487cc <+52>: movntps %xmm0,(%edx) , p/x $edx 값은 0x804c4a8
// edx 값이 0x10 배수가 아니어서 그런가보다?
// edx에는 ebp+0x8=첫번째 인자가 들어가고, eax에는 ebp+0xc=두번째 인자가 들어간다
// stage5부터 문제가 생기면 edx 값 확인 후 +8 해주는 작업만 해주면 되네
// 64보다 작을 땐 slow_memcpy 루틴 따라서 상관 없지만
// 64 이상일 땐 fast_memcpy 루틴 따르므로 8+16*n 형태로 건네주어야 함
// movdqa, movntps 이용해 fastcpy 할 땐 16byte 메모리 alignment 맞춰주어야 한다~~
// cf) 서버에서 바이너리 만들 때 printf("dest: %p\n", dest); 추가해서 컴파일하면 분석 편할듯~
// 항상 src: 0xf7759000이므로 문제 x, dest만 조심하자