본문 바로가기

프로그래밍 언어/Assembly

어셈블리어 | VSCode에서 C to 어셈블리 변환

CSAPP 3장을 공부중인데, 어셈블리어에 대해서 더 많은 예제가 필요함을 느꼈다. 

직접 코드를 쳐보면서 어셈블리어로 변환해보고, 확인해보고 하면 좋지 않을까?

이게 공부효율을 높여줄지는 미지수지만 그냥 재미있기도 해서 해봤다. 어차피 책에서도 직접 어셈블리 코드 뽑아내는 법을 소개하고 있다. 

 

C 파일로부터 어셈블리어 파일 생성

C파일로부터 어셈블리어 파일을 생성하는 방법은 다음과 같다. 

 

1. 테스트해보고 싶은 C 소스파일을 아무거나 만든다. (대충 hello.c)

2. 해당 디렉토리로 가서, 터미널에 다음과 같이 입력한다. 

 

gcc -S -o hello.s hello.c

 

쉽쥬? gcc -S 명령어는 컴파일 중간에 어셈블리어 코드를 뽑아내서 따로 파일로 생성해주는 코드이다. 

이렇게 하면 hello.s 가 생성된다.

 

문제1: 자신의 CPU 아키텍처와 원하는 어셈블리어 아키텍처가 다를 경우 

근데 문제가 있다. 어셈블리어는 하드웨어 의존적이기 때문에 내가 어떤 아키텍처 기반의 CPU를 쓰냐에 따라 다른 코드가 반환된다는 점이다. 내 노트북은 M1 맥북인데, M1은 ARM 아키텍처 기반으로 설계된 칩이기 때문에 위 명령어를 그대로 내 노트북에서 치면 ARM 어셈블리 코드가 나온다.

 

그러나 나는 x86-64, 그 중에서도 intel이 아닌 ATT 형식의 어셈블리 코드를 생성하기를 원한다. 이유는 CSAPP 책이 x86-64 기준으로 저술되어 있기 때문이고, 나는 해당 책을 학습하기 위한 목적으로 어셈블리 코드를 생성하는 것이기 때문이다.

 

-> 내 CPU와 다른 어셈블리 코드를 생성할 방법은??

 

-> 찾아본 결과 다음과 같이 명령어를 입력하면 된다. (GCC가 아니라 Clang 명령어를 사용한다.) (clang과 gcc는 vscode에 내부적으로 설치되어 있는 것 같으니 굳이 따로 설치할 필요 없다.)

 

clang -S -o hello.s hello.c --target=x86_64

--target 옵션을 통해 원하는 형식을 지정해주는 것이다. 

 

이렇게 하면 똑같이 .s 파일이 생성되는데, 그 안에 ATT 형식의 어셈블리 코드가 들어가 있을 것이다. 

 

main:                                   # @main
	.cfi_startproc
# %bb.0:
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset %rbp, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register %rbp
	movl	$1, -4(%rbp)
	movl	$2, -8(%rbp)

 

 

문제 2: 헤더 파일을 읽어오지 못할 경우

또 하나의 문제가 있는데, 형식은 원하는 걸로 가져오지만, printf를 할 때 쓰는 stdio.h 등 헤더파일을 읽어오지 못하는 경우가 있다. 

 

VScode에서 C 환경세팅을 할 때는 아마 마이크로소프트의 C/C++ 익스텐션을 설치했을텐데, 뭔가 Clang 명령어와 경로가 합이 맞지 않는?? 뭐 그런 문제가 있는 것 같다. 

 

이 경우 clang -S -o hello.s hello.c --target=x86_64 뒤에 -I 옵션으로 직접 헤더 파일의 경로를 넣어줘야 한다.

근데 문제는 헤더 파일의 경로를 찾는건데, 일단 VScode 공식 문서의 Clang 사용 파트를 보면(https://code.visualstudio.com/docs/cpp/config-clang-mac)

얼추 설명이 되어있긴 하다. 그러나 저건 예시일뿐, 실제 경로는 각자 PC 환경에 따라 다를 수 있다.

그래서 직접 찾아줘야 하는데, 내가 해결한 방법은 다음과 같다. 

 

1. 터미널에서 find 명령어를 통해 stdio.h 라는 이름이 포함된 경로들을 추출한다. 

find /usr /usr/local /Library -name stdio.h

대부분의 경우, 저 중에 /Library에서만 탐색을 해도 찾을 수 있다.

(에러가 너무 많이 발생한다면 에러 메시지는 숨기는 2>/dev/null 옵션을 뒤에 붙인다.)

 

그럼 아래와 같이 여러 경로들이 뜬다. 

이중에서 참조해야 하는 헤더파일이 포함된 경로를 찾아 Clang -I 옵션과 함께 넣어줘야 한다. 

 

보통은 

  • /Library/Developer/CommandLineTools/usr/include/
  • /Library/Developer/CommandLineTools/SDKs/MacOSX13.1.sdk/usr/include/ 

와 같이 생긴 경로들에 포함되어 있다. 나같은 경우에는 둘 중 아래 경로가 맞았었다. 

 

2. 그 다음은 찾은 경로를 포함해서 .s 파일을 생성하는 명령어를 넣어주면 된다. 

 

clang -S -o if_3.s if_3.c --target=x86_64 -I /Library/Developer/CommandLineTools/SDKs/MacOSX13.1.sdk/usr/include/

예를 들어 위와 같은 식이다. 

 

이렇게 하면, <stdio.h> 등과 같은 헤더 파일을 포함할 수 있어서, 더 다양한 코드를 실험해볼 수 있다. 

 


생성된 어셈블리 코드 더 편하게 보기

+) 추가적으로, VScode에서 

이 extension을 설치하면 어셈블리 코드를 조금이나마 더 가독성 좋게 볼 수 있다.

C 원본 파일
어셈블리 코드

 

공부 방법

1. GPT한테 원본 C 코드와 어셈블리 코드를 함께 넘겨주기

2. 어셈블리 동작 과정을 해설하고, 그 내용을 코드에 주석으로 달아달라고 부탁하기 

  • 프롬프트 예시: c 코드를 참고해서 어셈블리 코드를 자세하게 해석해줘. 그리고 어셈블리 코드에 해당 내용을 주석으로 충실히 담아서 반환해줘. 주석은 한국어로 작성해줘.

3. 다시 복붙해와서, 창 두개 띄워놓고 이해(해보려고 노력)하기