컴파일이야 고급 언어를 (특정 하드웨어 플랫폼에 맞는) 기계어로 번역하는 과정으로

이 기계어를 인스트럭션이라고 하며, 이 인스트럭션을 어셈블리어로 나타낼수있음.

 

01010101 의 경우 i386 CPU에서 ebp 레지스터의 값을 스택에 push하는 명령

 

 

1. objdump, xxd로 한번 보기

예제로 이 코드를 작성해서 어셈블리어로 보면

objdump : 바이너리 파일을 보는 명령

-S 옵션 : 디버깅 심벌과 인스트럭션, 어셈블리 코드 보여줌

위 덤프 내용은

C 소스코드 부분인 디버깅 심볼과

주소, 명령, 어셈블리 로 구성되있고,

초기화 종료 코드가 들어가 main 이외에도 아주 길다.

 

 

아쉽게도 이 플랫폼은 x86-64라 i386계열과 다른 명령어 셋인지 인스트럭션이 좀 다르게 나온다 ;

 

파일을 16진수로 보여주는 xxd로 열면 이런식으로 나온다.

 

 

 

2. 컴파일 과정

2.1 gcc x86_64-linux

gcc : ARM, DEC< AVR, i386등 아주 많은 아키텍처 지원하며, C뿐만 아닌 C++, fortran, objective-c도 컴파일 가능

이 경로의 실행파일로 cc1, cc1plus, collect2, f951이 있는데

cc1 : c컴파일러

cc1plus : c++ 컴파일러

f951 : 포트란 컴파일러

collect2 : 링커

* lto 는 찾아보니 링크 속도 최적화기? 라고 나온다.

* 원랜 전처리기 cpp0도 있다고 하는데 왜 여기는 없는진 모르겠다.

 

한번 컴파일해서 내용좀 보면

gcc -v --save-temps -o like like.c에서

-v는 컴파일 과정을 화면 출력 verbose?

--save-temps는 중간 파일 남겨두는 명령(전처리 파일 .i와 어셈블리파일 .s를 지우지 않고 나둔다)

 

위 내용을 보면 패키지 버전이 우분투 9.4.0-우분투1 20.0 4 1이고, 프로그램 프리픽스는 x86-64-linux-gnu

맨 마지막 줄쯤 보면 build, 호스트, 타겟이 x86_64-linux-gnu로 다 같다.

대강 우분투 20.04에서 돌아가는 gcc 9.4.0가지고 저런 설정으로 빌드한다는 소린거같다.

 

찾아보면 cc1과 collect2는 보이는데 전처리기와 어셈블러는 어디서 썻는질 모르겠다.

 

나온걸 보면 전처리 파일 like.i와 어셈블리 파일 like.s, 목적 파일 like.o가 보인다.

 

 

좌측은 전처리 결과, 우측은 어셈블리 코드

 

 

2.2 중간 파일의 유용함

이렇게 코드를 작성한 상태에서

 

gcc를 돌리면 like.c에 MY가 없다고 에러가 뜬다.

my_struct를 my란 이름으로 줬지만 like.c 에선 MY로 되어버려 선언되지 않은거라 에러가 뜬다.

 

하지만 보통 여러 헤드파일을 include 하고, 그 인클루드한 헤더가 다른걸 인클루드한 상태에서

심볼 정의 안했다는 에러가 뜨는 경우 찾기 힘들지만 전처리 파일인 like.i를 보면 그나마 쉽게 찾을수 있다?고 한다.

이게 뭔소린진 제대로 만나보질 않아서 아직은 잘 모르겠다.

 

 

보다가 왜 전처리 cpp0가 없나 했는데

cc1 옵션으로 -fpreprocessed like.i가 있는걸 봐선

cc1가 전처리하는 내용도 포함하고 있나보다.

 

전처리 과정을 제대로 보고싶긴한데 워낙 오래된 자료라 이 환경에서 맞출수 없어 pass

컴파일해서 어셈블리 까지 가는건 그냥 넘어가고

 

3. 바이너리

 

like.c 코드를 이걸로 돌아와 다시 진행하면

 

as 명령어로 like.s에서 ELF 바이너리 like.o를 만들었다.

링커는 여러 오브젝트 파일을 링크할때 인스트럭션과 데이터의 범위 등 바이너리의 구조를 알아야 잘 링크시킬수 있고

유닉스 바이너리 포멧으로 a.out, ELF 등이 있음.

 

3.1 바이너리 포멧

표준 ELF 포멧에 대해선 그림이 조금씩 다른게 여러개가 있는데

위키에서는 ELF

1. 실행, 목적, 공유라이브러리, 코어 덤프를 위한 표준 형식

2. 0개 이상의 세그먼트를 정의하는 프로그램 해더 테이블, 0개 이상 섹션을 정의한 섹션해더 테이블, 참조 데이터

3. 섹션은 링킹과 재배치에 필요한 정보 포함, 세그먼트는 런타임에 필요한 정보 가짐

4. 두 관점보면 ELF 파일의 프로그램 해더는 런타임시 세그먼트를 나타내고, 섹션해더는 바이너리 섹션의 집합

 

이게 아직 뭔소린가 싶다.

 

 

on1ystar 님의 블로그를 보면

ELF 헤더 : 파일 구성 

섹션 : 링킹에 필요한 obj 파일 정보(인스트럭션, 데이터, 심볼테이블, 재배치 등)

프로그램 헤더 테이블 : 프로세스 이미지 만드는 방식?

섹션 헤더 테이블 : 파일 섹션에 대한 정보

https://bnzn2426.tistory.com/82

 

ELF 파일 구조

ELF (Executable and Linkable Format) 유닉스 계열 시스템들의 표준 바이너리 파일로 실행 파일, 목적 파일. 공유 라이브러리 그리고 코어 덤프를 위한 표준 파일 형식 보통 ELF 파일은 ELF Header + program hea..

bnzn2426.tistory.com

 

일단 ELF 표준 포멧은 대충 넘어가고

read -a 바이너리파일 로  ELF 포멧 정보를 볼 수 있다.

 

 

지금 like.o 파일의 구조는

이런식이라고 한다.

 

 

ELF 헤더

인스트럭션 : like.s를 어셈블하여 만듬

데이터 : 전역변수가 없어서 쓰진 않으나, 있으면 전역변수 공간을 할당받음

GCC 컴파일러 버전정보

기타 섹션 : 심볼 테이블 + 재배치 섹션

* 심볼 테이블 : like.c의 심볼 정보들을 가짐 (심볼 : 메모리 주소를 가진 모든것 ex.전역변수, 구조체, 함수, 크기, 종류 등)

 

지금 작성한 코드에는 main 함수랑 printf 를쓰긴 했는데 printf가 결국 puts나 똑같아서 그런가? printf는 보이진 않는다.

 

옛날이랑 좀 달라졋는지 재배치 섹션은 심볼테이블보다 먼저 나오는데,

시간이 없어 일단 여기서 끝

리눅스를 쓸때마다 결국에는

vim이나 makefile 같은건 마주칠수밖에없는데

여전히 이런 유틸리티를 제대로 다루는 국내 서적이 거의없는 편이라

해외 서적이라도 보면서 해야할까 싶었지만 한국어만큼의 속도나 이해가 되지를 않아서 할수가 없었다.

 

그래서 나온지 한참지난 '유닉스 리눅스 프로그래밍 필수 유틸리티' 책을 사고싶었지만

진작에 품절이 되었고, 포기했는데 우연히 집근처 도서관에서 찾았고 진행하게되었다.

꼭 재발매나 ebook으로 나오면 좋겠는데 ㅠㅜ

 

1. gcc 쓰기

1.1. VIM으로 코드 작성

 

 

1.2 gcc 컴파일

 

 

-Wall : 모든 경고 메시지 출력

-O2 : 컴파일 최적화

-o : 출력 파일명

 

 

1.3 소스 파일 추가

like.c 를 위와같이 변경하고, love.c 추가 후 빌드

두 소스 코드를 컴파일 한 결과 하나의 실행파일 like가 나왔다.

 

 

1.4 gcc서 libm.a 링크

 love.c 에서 sin 함수를 쓰도록 쓰고 math.h 임포트

 

 

sin()은 기본으로 링크하는 libc.a가 아닌 libm.a에 있어 링크 옵션 명시 필요

 

 

2. Make로 프로젝트 관리

 

Makefile 작성 후 make 를 입력하면 /usr/bin/make가 이 파일을 읽고 동작

make 시 6번줄을

make clean 시 9번줄 실행

 

 

Makefile에서

like : like.c love.c 는 의존관계를 나타냄

like를 만드는데 like.c love.c 필요

:를 기준으로 전자 like는 $@와 대치

:를 기준으로 후자 like.c love.c는 $^와 대치되어 6번 명령을 실행

-> 계속 gcc 칠 필요없이 make 명령으로 쉽게 빌드, 정리 가능!

 

 

3. 디버깅 

3.1 준비

이렇게 작성하고 빌드하면 워닝이 줄줄이 뜨는데 디버깅할거니 넘어가고

 

실행하면 에러가 난다 C 공부한 사람은 왜 이런지 알겠지만 gdb로 보자

 

3.2 gdb 시작

 

디버깅을 하기위해 gcc에서 -g 옵션을 추가하자.

-g : 바이너리에 디버깅 심벌 추가. 소스코드 보면서 디버깅 가능!

* 디버깅 심벌없는 바이너리를 디버깅하는 경우 역어셈블 한뒤 어셈블리어로 해야함.

 

gdb like로 gdb에서 like 읽자

list 명령을 주면 10줄 정도 소스코드가 오는데

list 함수명의 경우 위 아래 5줄씩서 10줄이 나오도록 한다.

 

 

3.3 브레이크 걸고 디버깅 시작

 

b main으로 main함수 진입점 브레이크 -> 브레이크 없으면 그대로 지나가 디버깅 불가

r로 실행해서 main 진입점에서 걸린다....

 

오래 된 내용이여선지 제대로 동작하지 않아 pass

 

 

 

4. autotools

4.1 autotools 개요

- autotools : autoconf 패키지, automake 패키지에 있는 모든 유틸을 지칭

- autoconf : configure.ac 입력 -> configure 스크립트 생성

- configure 스크립트 : 시스템 정보를 모아 config.status 스크립트

- config.status 스크립트 : 모은 시스템 정보와 Makefile.in을 입력 -> Makefile 생성

- Makefile.in : Makefile의 템플릿

- automake : Makefile.in 자동 생성. 많은 시스템 정보를 써서 이식성이 높은 템플릿 만듬

autoconf를 이용한 Makefile 생성 과정
autoamke를 이용한 생성과정

 

 

 

4.2 configure.ac, Makefile.in 작성해서 Makefile 만들기

 

configurea.c 작성하고 autoconf 실행

 

생성된 configure 스크립트

 

@CC@, @CFLAGS@, @LIBS@는 configure 스크립트에 의해 수집한 시스템 정보로 바뀜

 

처음 configure 스크립트를 실행할때 2095번줄에 ';' 가 들어가 실행이 되지 않았는데, 제거 후 위와 같이 makefile 생성

 

 

4.3 automake로 makefile 만들기

autoamke를 이용한 생성과정

다시 automake를 이용한 과정을 보면 Makefile.am을 만들어야 하는데

cofngirue.ac를 조금 수정하면

 

AM_INIT_AUTOMAKE 부분을 추가하고

Makefile.am 작성

 

 

근데 autoconf를 실행하려해도 자꾸 정의되지 않은 매크로라며 에러 떠서 진행이 안됬는데

 

 

https://github.com/VowpalWabbit/vowpal_wabbit/issues/969

Ea37이란 분이 aclocal로 m4 env를 초기화부터하라고 조언하는데 해결됬다.

 

aclocal

autoconf

automake --add-missing --foreing --copy 순으로 실행해서 잘 진행됬고

아까 만든 Makefile.in보다 훨씬 이식성 좋은 Makefile.in이 만들어 졌다.

 

기존 Makefile을 지우고 다시 ./configure를 실행해서 보면

 

훨씬 복잡하고, 기능이 다양한 Makefile이 자동으로 완성

 

make 실행한 결과, 아까 디버깅 하다 말아서 오류나던 실행파일 그대로 빌드되었다.

 

 

이렇게 힘든 튜토리얼은 처음이다..

+ Recent posts