| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
- 운영체제
- disassemble
- load effective address
- movaps
- modrm
- call instruction
- effective address
- vex prefix
- NASM
- instruction
- compare
- WPF
- OS
- csproj
- GCC
- C#
- 유효 주소
- C언어
- C
- sib
- movdqu
- struct
- return
- EVEX
- 어셈블리
- movups
- rex prefix
- assembly
- modr/m
- evex prefix
- Today
- Total
목록전체 글 (21)
프로그래밍 잡화점
어셈블리를 하다보면 여러가지 종류의 prefix가 있는 것을 볼 수 있다. 오늘은 REX에 대해서 먼저 알아보자. 사실 실제로 어셈블리를 하면서 REX prefix를 직접 만나는 일은 없었을 것이다. 왜냐하면 저 prefix는 어셈블리에 알게 모르게 어셈블러에 의해 처리되고 있기 때문이다. 다음 코드를 한번 봐보자mov rax, edx 이 코드에 무려 REX prefix가 들어있다. 이 코드를 바이트 코드 형태로 분석해보자.48 : REX.W89 : mov r/m16/32/64 r16/32/64d0 : modr/m (11 000:rax 010:rdx) REX.W라는 REX prefix가 붙은 것을 볼 수 있다. 결론부터 이야기 하자면 REX prefix는 Register EXtension prefix라는..
어셈블리 기본 Instruction중 하나인 LEA(Load Effective Address)에 대해 알아보자.Effective Address가 무엇인가?예전에 어셈블리와 기계어에 대한 포스트에서 언급한 바 있는 SIB란 녀석이 있었다. Base + Scale * Index로 계산되는데, 여기에 Displacement까지 더해서 나온 주솟값.이 주솟값을 Effective Address, 한국어론 유효 주소라고 부른다. 그렇다면 어셈블리에서 LEA는 주로 어디에 사용되는가? 가장 간단한 예시로는 특정 레지스터의 주소를 기반으로 다른 주소를 구할 때 사용할 수 있을 것이다. 예를 들어, 가장 간단한 함수에서 사용하는 변수에 접근한다고 생각해보자.그럼 다음과 같은 코드를 작성할 수 있을 것이다.... ..
최근 어셈블리를 사용하던 도중 왜 돌아가지 라는 생각이 드는 코드가 있었다. mov eax, [arr+edx*3] 엥? 뭐가 문제인데요? 이전 글을 참고해 보자. Scale의 경우에는 보이다 싶이 1, 2, 4, 8만 사용될 수 있다. 그렇다. Instruction 상에는 저런 SIB가 존재할 수 없다는 얘기다. 가장 간단한 방법을 통해 어떤일이 벌어졌는지 알아보자. gdb로 빌드된 파일을 역어셈 해보았더니 맞다. 눈치 빠른 사람은 벌써 눈치를 챘을 텐데 원래 우리가 해석할려고 했던 것에서 뭐가 문제인지 알아냇을 것이다. 저 코드를 이상하다고 보고 해석했다면, arr을 base, edx를 index, 3을 scale로 해석했을 것이다. arr을 base로 두어야 하는가? ModR/M에서 SIB 형태만 존..
최근 IR -> Binary 프로젝트를 작업하면서 Instruction이 Byte코드로 변환되는 과정을 공부했었다. 여러 블로그와 인텔 문서까지 봤는데 좀 쉽게 설명하면서 내용을 정리하고자 올린다. (Intel® 64 and IA-32 Architectures Software Developer Manuals < Intel 메뉴얼) (http://ref.x86asm.net/coder.html < opcode 목록 *AVX 미포함* ) 어셈블리와 기계어가 왜 1:1 대응인지 알아보러 가보자 Instruction Format Instruction의 구조는 간단하면서도 생각외로 복잡하다. 다음 표를 보자 Instruction 하나에 많은 것들이 들어가는데 앞에서부터 천천히 살펴보자 Instruction Pref..
오늘 할 것은 Assembly에서 "직접" SIMD를 사용해보는 것이다. (구글링과 직접 실험을 통해 얻은 지식을 바탕으로 한 것이기 때문에 더 효율적이고 보편적인 코드가 있을 수 있음) 가끔 프로그램을 분해 해보면 movups같은 이상한 instruction들이 보이곤 하는데, SIMD랑 연관이 있는 명령어 였다. SSE의 경우 xmm이란 128 레지스터를 사용하는데 값을 xmm에 로드하기 위해서 사용되는 명령어가 바로 movups, movaps, movdqa, movdqu가 있다. (이 외에도 꽤 많은 명령어가 있다) 이런 명령어를 통해서 xmm에 값을 로드할 수 있는데 SIMD를 사용할 땐 아주 중요한게 하나 있다. 바로 align을 맞춰야 한다는 것. stack align에 대해서 간단하게 설명하..
함수의 반환에 대한 고찰 오늘은 함수 반환에 대해 간단하게 알아보고자 한다. * 컴파일 옵션은 다음과 같다 gcc -o Main Main.c -O0 보통 함수에서 값을 반환하게 되면, eax(32bits), rax(64bits) 레지스터를 사용하게 된다. 그리하여 thinkcs.tistory.com 예전이 이런 포스팅을 한적이 있었다. 어셈블리를 사용하면서 얻은 지식이 있어 추가적으로 보완하고자 글을 쓰게 되었다. struct를 반환하게 되면 어셈블리에선 어떠한 일이 일어나는가? 에 대해서 알아보도록 하자 일단 먼저 struct는 레지스터에 들어갈 수 있는 크기가 아니다 보니 값 그 자체로 반환되지는 않는다. 보통 struct를 반환하는 함수 내에서 어딘가에는 struct를 선언하는 코드가 존재한다. 즉..
오늘은 함수 반환에 대해 간단하게 알아보고자 한다. * 컴파일 옵션은 다음과 같다 gcc -o Main Main.c -O0 보통 함수에서 값을 반환하게 되면, eax(32bits), rax(64bits) 레지스터를 사용하게 된다. 그리하여 main에서 int를 반환할때도 eax나 rax에 exit_code를 전달해서 main을 탈출하게 되는데. 여기서 한가지 의문이 생기게 된다. 그렇다면 구조체 같이 복잡한 구조의 타입을 반환하면 어떻게 될까? 다음과 같이 한 구조체를 만들고 컴파일을 한 뒤 어셈블리 코드로 뜯어보았다 typedef struct { int a, b, c; } A; A test() { A a = { 1, 2, 3 }; return a; } int main() { A a = test(); r..
C# 타입 키워드에는 여러개가 존재한다. 그 중, C/C++, Java에 없는 키워드가 하나 존재하는데, 바로 오늘의 주인공 string이다. C/C++에서는 사용할 수 없는 기능, switch문에 string을 쓰는 방식에 대해 알아보고자 한다. char* 또는 string을 switch에 넣을 수 없는 C/C++과 다르게, C#은 switch에서도 string이 사용가능하다. 곰곰히 생각해보면 의문점이 생긴다. C/C++에서 switch에 string을 못넣는 이유는 당연하다. 왜냐? 비교할 방법이 없기 때문이다. C/C++을 공부해본 사람이라면 알 수 있을 것이다. char* == char*은 문자열 비교가 아니라 포인터 비교란 걸. 물론, strcmp이나 string 클래스에서는 ==를 지원하지만..
들어가기 앞서 - 해당 글은 GCC와 C파일을 기준으로 작성되었습니다. 컴파일러마다 다를 수도 있습니다. 기본적으로 어셈을 배울 때 종료 시에 eax 또는 rax를 0으로 초기화한 후 ret을 해주는 과정을 볼 수 있다xor eax, eax ; = 0 mov eax, 0 ; 위와 같은 코드이다. 그러나 위를 많이 쓰는 경향이 있다. ret ; 종료 지시문 (return)C언어에서 main에서 return 0;을 사용하는 것과 같다고 보면 된다. GCC의 경우 return 0을 안넣어도 알아서 추가해주는 똑똑한 컴파일러다. 그런데 여기서 의문이 생긴다. "만약, main의 반환형이 int가 아니라 다른 타입이면 어떤 일이 발생할까?" 처음에는 int와 void의 비교를 위해서 두 코드를 역어셈 해보았다.두..
먼저 GCC의 최적화 방식을 알아보자. GCC는 상수가 들어왔을 때 해당 값에 따라서 비교하는 명령어(어셈에선 cmp)를 생성하지 않는다. 즉, 다음과 같은 식에서 GCC는 cmp와 je(jump)를 생성하지 않고 바로 내부 코드를 생성한다 if (true) n = 1; mov DWORD PTR [rbp-0x4],0x1 이 방식은 연산자가 존재할 때에도 성립한다. if (true || false); if (true && false); 이 상황에서도 위와 같은 방식이 적용된다. 따라서 if문 내부의 상수의 값이 들어가면 gcc는 이를 먼저 처리하고 그 처리에 따라 뒤에 나올 코드를 넣을지 않넣을지를 결정한다. 추가로 if문 내부 코드가 존재하지 않다면 다음 두가지 경우로 나눠진다. 1. 조건문이 상수인 경..