코드를 순서대로 따라가보면 ,
memcpy 성능 실험을 위해 사용자가 복사하라 바이트 수를 단계뼐로 입력받아 배열에 저장하는 준비과정을 수행한다.
cache1과 cache2를 캐시 메모리 용도로 사용하기 위해 동적 할당하고,
src 역시 같은 방식으로 할당한다.
이후 10번의 반복을 하는데, low와 high 사이 범위의 수를 입력 받고 memcpy를 통해 시간을 계산하고 (rtdsc 이게 시간 측정해주는 asm인듯) 계속 출력한다.
근데 64이상의 숫자가 들어갔을 때의 fast_memcpy에서 계속 프로세스가 진행이 되지 않는데,
16의 배수를 넣어도 64의 배수를 넣어도 무조건 64보다 큰 경우 fast_memcpy가 왜인지는 모르겠지만 멈춘다.
__asm__ __volatile__ (
"movdqa (%0), %%xmm0\n"
"movdqa 16(%0), %%xmm1\n"
"movdqa 32(%0), %%xmm2\n"
"movdqa 48(%0), %%xmm3\n"
"movntps %%xmm0, (%1)\n"
"movntps %%xmm1, 16(%1)\n"
"movntps %%xmm2, 32(%1)\n"
"movntps %%xmm3, 48(%1)\n"
::"r"(src),"r"(dest):"memory");
문제가 되는 코드이다.
한 번에 16바이트씩, 총 64바이트를 빠르게 다른 메모리 위치로 통째로 옮기는 작업인데,
일단 movdqa %0 %%mm0 이 명령어는 16바이트를 한 번에 옮기는 명령어로, 뒤에 붙은 'a'는 aligned의 약자이며 메모리 주소가 16바이트 단위로 정렬 되어 있어야 한다고 한다.
src 포인터가 가리키는 곳에서 16바이트를 꺼내서 CPU의 xmm0에 넣어라라는 뜻이다.
%0은 gcc style의 inline asm 문법에서 사용하는 place holder로, %n일때 n번째 피연산자 레지스터의 이름으로 치환하겠다는 뜻이다. 즉 asm의 마지막 줄에 있는 src가 0번째 인자, dest가 1번째 인자로 사용된다."r" 은 src, dest 값을 임의의 범용 레지스터에 넣고 쓰겠다는 뜻이며 이때 src와 dest가 임의 레지스터에 값이 넣어지고 0번째, 1번째 레지스터로 지정이 되어 위의 인라인 어셈블리에서 %0, %1로 사용 가능해진다.
16(%0) 이거는 %0 으로부터 16바이트 만큼 떨어진 위치임을 의미하기 때문에, 결국 src에서 총 64바이트를 옮겨 xmm 레지스터들에 저장한다는 것을 의미한다.
movntps는 SSE 명령어 (streaming SIMD extension) 으로, SSE는 여러 개의 데이터를 병렬로 처리해서 부동 소수점 연산이나 간단한 정수 연산을 빠르게 해줄 수 있는 명령어 일종이다.
ntps는 Non-Temporal 저장을 의미하여 캐시에 담지 않고 바로 메모리에 쓰라는 명령어로 cache pollution 방지 및 대용량의 연속 쓰기의 이점을 제공한다. ps는 packed single-precision을 의미하여 총 16바이트를 메모리에 저장한다는 뜻으로 빠르게 xmm 레지스터들에 담긴 값들을 dest로 옮긴다는 것을 의미한다.
즉, asm 어셈에는 딱히 문제는 없다.
Dockerfile을 읽어보니, 컴파일 할 때 -m32로 컴파일하여 x86으로 컴파일이 된다.
이 환경에서는 malloc이 8바이트 배수로만 정렬하기 때문에 movdqa같은 16의 배수로 이루어진 포인터를 요구하는 명령어에서 오류를 발생시켜서 더 이상 진행이 안 되는 것으로 보인다.
구 버전에서는 malloc이 chunk 단위로 연속 할당을 하기 때문에 아무래도 처음으로 할당한 지점이 둘째 할당의 시작점을 결정하는 구조인 것 같다. 아무래도 x86 환경이다 보니 8byte header가 붙어 있으니 이걸 고려해서 16의 배수가 되도록 입력값을 넣어주면 될 것 같다.
왜냐하면 malloc은 기본적으로 인자로 들어온 size 바이트만큼 딱 할당해 주는 것이 아닌, 내부적으로 메타 데이터 (header) 8바이트와 패딩을 포함한 chunk 단위로 관리하기 때문에, size에 header 크기를 더하고 alignment를 완료한 단위로 할
당하고 사용자에게 반환하는 포인터는 이 header 이후 size 크기만큼만 주기 때문이다.

이런식으로 최대 값에서 8씩 뺀 값으로 입력 값을 넣어주었더니 flag 출력까지 정상적으로 동작했다.
flag : b0thers0m3_m3m0ry_4lignment