jun-wiki

View My GitHub Profile

Posts (Latest 10 updated) :
Read all
Contents:
  1. 3.2.2 코-드 yeah-je
  2. 3.2.3 형식에 대한 설명


씨에스에이피피가 되

csapp 3

3.2.2 코-드 yeah-je

mstore.c라는 C 코드 파일 작성했다고 가정

// mstore.c

long mult2(long, long);

void multstore(long x, long y, long *dest) {
    long t = mult2(x, y);
    *dest = t;
}

C 컴파일러가 생성한 어셈블리 코드 확인하고프다면 명령줄에 -s 옵션 사용

# bash

linux> gcc -Og -S mstore.c

이렇게 하면 gcc가 컴파일만 해서 mtore.s를 만들고 스탑한다

그렇게 나온 어셈블리 코드 파일에서 중요한 내용이다 ↓

multstore:
    pushq %rbx
    movq %rdx, %rbx
    call mult2
    movq %rax, (%rbx)
    popq %rbx
    ret

들여쓰기된 각 줄은 하나의 기계어 명령어이다

이건 읽기 가능하다

예를 들어 pushq 명령은 %rbx 레지스터의 내용을 스택에 저장하라는 의미다

__


-c 옵션을 사용하면, gcc는 컴파일과 어셈블까지 수행한다

# bash

linux> gcc -Og -c mstore.c

이렇게 하면 바이너리 형식의 객체 파일 mstore.o을 만드는데 이는 못읽는다…

53 48 89 d3 e8 00 00 00 00 48 89 03 5b c3

이따구로 나오니 말이다

읽을 수 있다면 댓글로 알려주길 바란다



이로 얻을 수 있는 교훈

기계가 실행하는 프로그램은 단순히 명령어들을 인코딩한 바이트들의 연속이지
이 바이트들이 어떤 소스 코드에서 왔는지에 대한 정보는 거의 담겨 있지 않다




기계어 파일의 내용을 확인하고 싶다면 디스어셈블러를 사용해라

기계어같은 걸 우리가 읽을 수 있게 변환해준다

linux에선 obdjump로 가능하다고 한다

# bash

linux> objdump -d mstore.o

결과 : (줄 번호와 설명은 이해를 돕기 위해 추가했다고 한다)

Disassembly of function multstore in binary file mstore.o
1 0000000000000000 <multstore>:
2  0: 53                   push %rbx
3  1: 48 89 d3             mov %rdx,%rbx
4  4: e8 00 00 00 00       callq 9 <multstore+0x9>
5  9: 48 89 03             mov %rax,(%rbx)
6  c: 5b                   pop %rbx
7  d: c3                   retq

왼쪽엔 16진수 값이 나오고
오른쪽에 해당되는 어셈블리 명령어가 나온다


주목할 특징은

  1. x86-64 명령어는 1~15바이트까지 다양한 길이
    • 자주 쓰이는 명령어일수록 짧게 인코딩됨

  2. 고유한 디코딩 가능
    • 예: 바이트 53은 무조건 pushq %rbx 명령으로 해석됨

  3. 디스어셈블러는 바이트 시퀀스만 보고 어셈블리 코드 생성
    • 소스 코드나 어셈블리 원본이 없어도 해석 가능

  4. 명령어 표기법에 약간 차이
    • gcc는 접미사 ‘q’를 붙이지만, objdump는 대부분 생략하거나 선택적으로 붙임


마지막엔 함수 추가해 실행 파일 만드는 법 나온다

대충 요약하면 작성 함수 뿐 아니라 운영체제 상호작용 및 프로그램 시작/종료 코드 때문에

파일 크기가 더 크게 나온다

실행 파일도 디스어셈블 가능하나 차이점 존재

주소값이 추가되며 명령어 호출 주소가 채워짐, 추가 명령어도 포함됨





3.2.3 형식에 대한 설명

gcc가 생성한 어셈블리 코드는 사람이 읽기 어렵다 (진짜임)

영양가 1도 없는 정보랑 뭐하는지도 안알려주는 놈이기 때문이다

그렇기에 우린 더 좋은 버전을 쓸 것이다

지시문 같은 쓸모 없는 건 갖다 버리고 각 줄에 줄 번호와 설명 주석이 추가된 버전 말이다

예시)

// c

void multstore(long x, long y, long *dest)
x in %rdi, y in %rsi, dest in %rdx
1 multstore:
2     pushq %rbx           # %rbx 저장
3     movq %rdx, %rbx      # dest %rbx 복사
4     call mult2           # mult2(x, y) 호출
5     movq %rax, (%rbx)    # 결과를 *dest 저장
6     popq %rbx            # %rbx 복원
7     ret                  # 반환
  • 보면 알다시피 왼쪽은 줄 번호고 오른쪽 주석은 설명이다


(따봉하는 개구리 짤)


이것이 일반적으로 어셈블리어 프로그래머들이 사용하는 형식이다