CrossCompile

Cross-compile & Linking & Make

sh1mj1 2022. 11. 11. 15:27

사전 지식

Programming Language

  • High-level language

고급 언어는 인간이 이해하기 쉽게 만들어진 언어로 컴퓨터에 대한 전문적인 지식이 없어도 저급 언어에 비해 배우기 쉽고, 기종에 관계없이 공통적으로 사용할 수 있으며, 그 의미를 쉽게 이해할 수 있는 사용자 중심의 언어다. Ex) C, C++, Java

  • Low-level language

고급 언어에 비해 이해하기 어렵고 사용하기는 불편하지만 컴퓨터가 처리하기에 용이한 컴퓨터 중심의 언어이다. 저급 언어는 크게 두가지로 나뉜다.

  • Assembly language

어셈블리어는 기계어와 고급 언어의 사이에 위치한 언어로, 인간이 이해하기 어렵다는 기계어의 단점을 보완하기 위해 나온 언어이다. 컴파일이 빠르지만 배우기 어렵고 유지보수가 힘들다.

  • machine language

컴퓨터가 직접 이해할 수 있는 언어로 2진수 형태로 표현되며 수행시간이 매우 빠르다. 기종마다 기계어가 달라 호환성이 없다는 단점이 있다.

Compile

Compile은 C, C++, Java등의 High-level language로 작성된 파일들을 machine language로 변환하는 것이다. 다시 말해서,

사람이 이해하는 언어를 컴퓨터가 이해할 수 있는 언어로 바꿔주는 과정이다.

Cross Compiling

Cross compiling은 컴파일러가 실행되는 플랫폼이 아닌 다른 플랫폼에서 실행 가능한 코드를 생성하는 작업이다.

이렇게 하는 이유는 플랫폼에서 바로 컴파일을 하는 경우 속도가 매우 느리기 때문에 이를 보완하기 위해서 컴퓨터를 이용하여 코드를 생성한다.. 예를 들어서 키오스크에서 실행될 코드를 짠다고 하면 우리는 키오스크에 키보드를 연결해서 코딩을 하지 않는다. 이 때도 성능이 좋은 컴퓨터로 코드를 생성한다!

앞으로의 수업에서는 gcc 을 이용해 컴파일을 수행할 예정이다.

gcc 에 대해 알아보기

Linking

Linking(링킹)은 여러 개의 코드와 데이터를 모아서 연결하여 메모리에 로드될 수 있고 실행될 수 있는 한 개의 파일로 만드는 작업이다. 만들어지는 파일이 메모리에 로딩되어 실행된다.

링크는

  • 컴파일시에 수행되는 경우도 있고,
  • 로딩시에 수행되는 경우도 있고,
  • 실행시에 수행되는 경우도 있다.

링커는 소프트웨어 개발에서 독립적인 컴파일을 가능하게 하는 아주 중요한 역할을 담당한다.

링커 덕분에 큰 규모의 응용프로그램을 한 개의 소스 파일로 구성하는 대신 별도로 수정할 수 있고, 컴파일할 수 있는 보다 관리할 만한 규모의 더 작은 모듈들로 나눌 수 있다. 즉, 아주 거대한 프로그램을 하나의 소스 파일이 아니라 수많은 소스 파일로 모듈화하여 개발을 진행할 수 있는 것이다. 링커 덕분에 우리는 모듈 중에 한 개를 변경할 때, 다른 파일들을 재컴파일할 필요 없이 이 파일만을 간단히 재컴파일하고  이를 다시 링크하여 변경사항을 적용할 수 있다.

출처 : https://stackoverflow.com/questions/34453675/static-vs-dynamic-linking

링킹의 종류는 크게 두가지가 있다

Static Linking (정적 링킹)

정적 링킹이란 실행 가능한 object 파일을 만들 때 프로그램에서 사용하는 모든 라이브러리 모듈을 복사하는 방식을 말한다.

장점: 동적 링킹보다 빠르고 불일치를 고려할 필요가 없다

단점: 모든 라이브러리 모듈을 복사하기 때문에 프로그램 크기가 커지고 메모리 효율이 떨어진다. 또한 복사한 모듈에 변화가 생길 경우 변화를 적용하기 위해 다시 컴파일을 해야 하는 번거로움이 있다.

Dynamic Linking (동적 링킹)

동적 링킹이란 실행 가능한 object 파일을 만들 때 프로그램에서 사용하는 모든 라이브러리 모듈을 복사하지 않고 해당 모듈의 주소만을 가지고 있다가, 런타임에 실행 파일과 라이브러리가 메모리에 위치될 때 해당 모듈의 주소로 가서 필요한 것을 들고 오는 방식이다.

장점: 해당 모듈만 가져오기 때문에 파일의 크기가 작아 메모리와 디스크 공간을 아낄 수 있다. 또한, 모듈에 변화가 생겨도 변화를 적용하기 위해 컴파일하여 링킹할 필요가 없다.

단점: 정적 링킹 방식보다 속도가 느리고 불일치에 대한 문제를 고려해야 한다.

GCC and Make

Make

 

Make란

파일 관리 유틸리티.

파일간의 종속관계를 파악하여 Makefile에 적힌 대로 컴파일러에 명령하여 Shell 명령이 순차적으로 실행될 수 있게 한다.

Make의 장점

  • 각 파일에 대한 반복적 명령의 자동화로 인한 시간 절약
  • 프로그램의 종속 구조를 빠르게 파악할 수 있으며 관리가 용이
  • 단순 반복 작업 및 재작성을 최소화

Make 옵션

make에서 거의 모든 것은 Makefile 내부에서 지정 가능하다. 그 중 일부는 make 실행시 사용 가능하다.

옵션 설명

-C dir Makefile을 계속 읽지 말고 우선은 dir로 이동하라는 것. 순환 make에 사용됨
-d, -debug Makefile을 수행하면서 각종 정보를 모두 출력 (출력량 多)
-h, -help 옵션에 관한 도움말을 출력
-f <filename>, -file <filename> <filename>에 해당하는 파일을 Makefile로 지정
-r, -no-builtin-rules> 내장된 각종 규칙 (Suffix Rule 등)을 없는것으로 간주한다.따라서 사용자가 규칙을 새롭게 정의해 주어야 함
-t, -touch 파일의 생성 날짜를 현재 시간으로 갱신
-v, -version make의 버전을 출력
-p, -print-data-base make에서 내부적으로 세팅되어있는 값들을 출력
-k, -keep-going 에러가 나더라도 멈추지 말고 계속 진행하게 하는 명령어(make는 에러 발생시 도중에 실행을 중단한다.)

Makefile의 구성

  • Target (목표 파일) : 명령어 수행 후 나온 결과를 저장할 파일
  • Dependency (의존성) : 목표 파일을 만들기 위해 필요한 구성요소
  • Command (명령어) : 실행 되어야 할 명령어들
  • Macro (매크로) : 코드를 단순화 시킴
CC = gcc	# 매크로 정의

target1 : dependency1 dependency2	
	command1
	command2

target2 : dependency3 dependency4
	command1
	command2

변수 설명

$@ 현재 Target 이름
$* 확장자가 없는 현재의 Target
$% 대상의 이름 (해당 규칙 대상이 archive인 경우)
$< 현재 Target이 의존하는 대상들 중 변경된 것들의 목록
$? 현재 Target이 의존하는 대상들 중 변경된 것들의 목록
$^ 현재 Target이 의존하는 대상들의 전체 목록
  • 참고 : 책에서는 $<와 $?를 약간 구분하고 있지만 거의 같다고 봐도 무방하다.

Makefile 기본 패턴

CC=<컴파일러>
CFLAGS=<컴파일 옵션>
LDFLAGS=<링크 옵션>
LDLIBS=<링크 라이브러리 목록>
OBJS=<Object 파일 목록>
TARGET=<빌드 대상 이름>

all: $(TARGET)

clean:
    rm -f *.o
    rm -f $(TARGET)

$(TARGET): $(OBJS)
$(CC) -o $@ $(OBJS)

Makefile 예제

#---------------------------------------------------------------------#                  M   A   K   E   F   I   L   E#---------------------------------------------------------------------#  CREATION DATE : YYYY.MM.DD#---------------------------------------------------------------------# Compile/load flags:#---------------------------------------------------------------------
LIB_NAME = testLib.a

LOCAL_INC = -I./src -I./include
LOCAL_OPTIONS = -D_TEST

CPPFLAGS = $(LOCAL_INC) $(PRJ_INC) $(SOOPTIONS) $(LOCAL_OPTIONS)
LDFLAGS = $(LOCAL_LIB) $(PRJ_LIB) $(STD_LIB)

#---------------------------------------------------------------------# Object File#---------------------------------------------------------------------
LIB_OBJFILE = $(FLATFORM)/testLib.o

#---------------------------------------------------------------------# Targets#---------------------------------------------------------------------
init:
	$(MKDIR) -p $(PLATFORM)

all: $(LIB_NAME)

$(LIB_NAME): $(LIB_OBJFILE)
	$(AR) ruv $(LIBTARGET)/$@ $(LIB_OBJFILE)

clean:
	$(RM) $(LIB_OBJFILE) *.exe core
    $(RM) $(LIB_TARGET)/$(LIB_NAME)

#---------------------------------------------------------------------# Pattern rule#---------------------------------------------------------------------
$(LIB_OBJFILE): ./$(PLATFORM)/%.o: ./src/%.cpp
	$(CCC) $(CPPFLAGS) -c $< -o $@

#---------------------------------------------------------------------# End of Makefile#---------------------------------------------------------------------

사전 지식

Programming Language

  • High-level language

고급 언어는 인간이 이해하기 쉽게 만들어진 언어로 컴퓨터에 대한 전문적인 지식이 없어도 저급 언어에 비해 배우기 쉽고, 기종에 관계없이 공통적으로 사용할 수 있으며, 그 의미를 쉽게 이해할 수 있는 사용자 중심의 언어다. Ex) C, C++, Java

  • Low-level language

고급 언어에 비해 이해하기 어렵고 사용하기는 불편하지만 컴퓨터가 처리하기에 용이한 컴퓨터 중심의 언어이다. 저급 언어는 크게 두가지로 나뉜다.

  • Assembly language

어셈블리어는 기계어와 고급 언어의 사이에 위치한 언어로, 인간이 이해하기 어렵다는 기계어의 단점을 보완하기 위해 나온 언어이다. 컴파일이 빠르지만 배우기 어렵고 유지보수가 힘들다.

  • machine language

컴퓨터가 직접 이해할 수 있는 언어로 2진수 형태로 표현되며 수행시간이 매우 빠르다. 기종마다 기계어가 달라 호환성이 없다는 단점이 있다.

Compile

Compile은 C, C++, Java등의 High-level language로 작성된 파일들을 machine language로 변환하는 것이다. 다시 말해서,

사람이 이해하는 언어를 컴퓨터가 이해할 수 있는 언어로 바꿔주는 과정이다.

Cross Compiling

Cross compiling은 컴파일러가 실행되는 플랫폼이 아닌 다른 플랫폼에서 실행 가능한 코드를 생성하는 작업이다.

이렇게 하는 이유는 플랫폼에서 바로 컴파일을 하는 경우 속도가 매우 느리기 때문에 이를 보완하기 위해서 컴퓨터를 이용하여 코드를 생성한다.. 예를 들어서 키오스크에서 실행될 코드를 짠다고 하면 우리는 키오스크에 키보드를 연결해서 코딩을 하지 않는다. 이 때도 성능이 좋은 컴퓨터로 코드를 생성한다!

앞으로의 수업에서는 gcc 을 이용해 컴파일을 수행할 예정이다.

gcc 에 대해 알아보기

Linking

Linking(링킹)은 여러 개의 코드와 데이터를 모아서 연결하여 메모리에 로드될 수 있고 실행될 수 있는 한 개의 파일로 만드는 작업이다. 만들어지는 파일이 메모리에 로딩되어 실행된다.

링크는

  • 컴파일시에 수행되는 경우도 있고,
  • 로딩시에 수행되는 경우도 있고,
  • 실행시에 수행되는 경우도 있다.

링커는 소프트웨어 개발에서 독립적인 컴파일을 가능하게 하는 아주 중요한 역할을 담당한다.

링커 덕분에 큰 규모의 응용프로그램을 한 개의 소스 파일로 구성하는 대신 별도로 수정할 수 있고, 컴파일할 수 있는 보다 관리할 만한 규모의 더 작은 모듈들로 나눌 수 있다. 즉, 아주 거대한 프로그램을 하나의 소스 파일이 아니라 수많은 소스 파일로 모듈화하여 개발을 진행할 수 있는 것이다. 링커 덕분에 우리는 모듈 중에 한 개를 변경할 때, 다른 파일들을 재컴파일할 필요 없이 이 파일만을 간단히 재컴파일하고  이를 다시 링크하여 변경사항을 적용할 수 있다.

출처 : https://stackoverflow.com/questions/34453675/static-vs-dynamic-linking

링킹의 종류는 크게 두가지가 있다

Static Linking (정적 링킹)

정적 링킹이란 실행 가능한 object 파일을 만들 때 프로그램에서 사용하는 모든 라이브러리 모듈을 복사하는 방식을 말한다.

장점: 동적 링킹보다 빠르고 불일치를 고려할 필요가 없다

단점: 모든 라이브러리 모듈을 복사하기 때문에 프로그램 크기가 커지고 메모리 효율이 떨어진다. 또한 복사한 모듈에 변화가 생길 경우 변화를 적용하기 위해 다시 컴파일을 해야 하는 번거로움이 있다.

Dynamic Linking (동적 링킹)

동적 링킹이란 실행 가능한 object 파일을 만들 때 프로그램에서 사용하는 모든 라이브러리 모듈을 복사하지 않고 해당 모듈의 주소만을 가지고 있다가, 런타임에 실행 파일과 라이브러리가 메모리에 위치될 때 해당 모듈의 주소로 가서 필요한 것을 들고 오는 방식이다.

장점: 해당 모듈만 가져오기 때문에 파일의 크기가 작아 메모리와 디스크 공간을 아낄 수 있다. 또한, 모듈에 변화가 생겨도 변화를 적용하기 위해 컴파일하여 링킹할 필요가 없다.

단점: 정적 링킹 방식보다 속도가 느리고 불일치에 대한 문제를 고려해야 한다.

GCC and Make

Make

 

Make란

파일 관리 유틸리티.

파일간의 종속관계를 파악하여 Makefile에 적힌 대로 컴파일러에 명령하여 Shell 명령이 순차적으로 실행될 수 있게 한다.

Make의 장점

  • 각 파일에 대한 반복적 명령의 자동화로 인한 시간 절약
  • 프로그램의 종속 구조를 빠르게 파악할 수 있으며 관리가 용이
  • 단순 반복 작업 및 재작성을 최소화

Make 옵션

make에서 거의 모든 것은 Makefile 내부에서 지정 가능하다. 그 중 일부는 make 실행시 사용 가능하다.

옵션 설명

-C dir Makefile을 계속 읽지 말고 우선은 dir로 이동하라는 것. 순환 make에 사용됨
-d, -debug Makefile을 수행하면서 각종 정보를 모두 출력 (출력량 多)
-h, -help 옵션에 관한 도움말을 출력
-f <filename>, -file <filename> <filename>에 해당하는 파일을 Makefile로 지정
-r, -no-builtin-rules> 내장된 각종 규칙 (Suffix Rule 등)을 없는것으로 간주한다.따라서 사용자가 규칙을 새롭게 정의해 주어야 함
-t, -touch 파일의 생성 날짜를 현재 시간으로 갱신
-v, -version make의 버전을 출력
-p, -print-data-base make에서 내부적으로 세팅되어있는 값들을 출력
-k, -keep-going 에러가 나더라도 멈추지 말고 계속 진행하게 하는 명령어(make는 에러 발생시 도중에 실행을 중단한다.)

Makefile의 구성

  • Target (목표 파일) : 명령어 수행 후 나온 결과를 저장할 파일
  • Dependency (의존성) : 목표 파일을 만들기 위해 필요한 구성요소
  • Command (명령어) : 실행 되어야 할 명령어들
  • Macro (매크로) : 코드를 단순화 시킴
CC = gcc	# 매크로 정의

target1 : dependency1 dependency2	
	command1
	command2

target2 : dependency3 dependency4
	command1
	command2

변수 설명

$@ 현재 Target 이름
$* 확장자가 없는 현재의 Target
$% 대상의 이름 (해당 규칙 대상이 archive인 경우)
$< 현재 Target이 의존하는 대상들 중 변경된 것들의 목록
$? 현재 Target이 의존하는 대상들 중 변경된 것들의 목록
$^ 현재 Target이 의존하는 대상들의 전체 목록
  • 참고 : 책에서는 $<와 $?를 약간 구분하고 있지만 거의 같다고 봐도 무방하다.

Makefile 기본 패턴

CC=<컴파일러>
CFLAGS=<컴파일 옵션>
LDFLAGS=<링크 옵션>
LDLIBS=<링크 라이브러리 목록>
OBJS=<Object 파일 목록>
TARGET=<빌드 대상 이름>

all: $(TARGET)

clean:
    rm -f *.o
    rm -f $(TARGET)

$(TARGET): $(OBJS)
$(CC) -o $@ $(OBJS)

Makefile 예제

#---------------------------------------------------------------------#                  M   A   K   E   F   I   L   E#---------------------------------------------------------------------#  CREATION DATE : YYYY.MM.DD#---------------------------------------------------------------------# Compile/load flags:#---------------------------------------------------------------------
LIB_NAME = testLib.a

LOCAL_INC = -I./src -I./include
LOCAL_OPTIONS = -D_TEST

CPPFLAGS = $(LOCAL_INC) $(PRJ_INC) $(SOOPTIONS) $(LOCAL_OPTIONS)
LDFLAGS = $(LOCAL_LIB) $(PRJ_LIB) $(STD_LIB)

#---------------------------------------------------------------------# Object File#---------------------------------------------------------------------
LIB_OBJFILE = $(FLATFORM)/testLib.o

#---------------------------------------------------------------------# Targets#---------------------------------------------------------------------
init:
	$(MKDIR) -p $(PLATFORM)

all: $(LIB_NAME)

$(LIB_NAME): $(LIB_OBJFILE)
	$(AR) ruv $(LIBTARGET)/$@ $(LIB_OBJFILE)

clean:
	$(RM) $(LIB_OBJFILE) *.exe core
    $(RM) $(LIB_TARGET)/$(LIB_NAME)

#---------------------------------------------------------------------# Pattern rule#---------------------------------------------------------------------
$(LIB_OBJFILE): ./$(PLATFORM)/%.o: ./src/%.cpp
	$(CCC) $(CPPFLAGS) -c $< -o $@

#---------------------------------------------------------------------# End of Makefile#---------------------------------------------------------------------