C언어 실습

오감자·2023년 9월 13일
0
  1. 자료형 크기 나타내기
  2. 자료형 크기를 모르면?(오버플로우)
  3. 비트 연산 프로그래밍
  4. 코드가 실행파일이 되기까지

1. 자료형 크기 나타내기

프로그래밍 언어에는 다양한 형태의 데이터를 해석하기 위해 자료형을 정의하고 있다.
정수를 나타내는 int, 문자를 타나내는 char가 그 예시이다.
그에따라 각 자료형은 다른 크기를 가지고 있다.
각 자료형의 크기를 한번 알아보자

Source

#include <stdio.h>

int main(int argc, char* argv[]) {

	printf("Size of char : %zu bytes\n", sizeof(char));
	printf("Size of short : %zu bytes\n", sizeof(short));
	printf("Size of int : %zu bytes\n", sizeof(int));
	printf("Size of long : %zu bytes\n", sizeof(long));
	printf("Size of long long : %zu bytes\n", sizeof(long long));
	printf("Size of float : %zu bytes\n", sizeof(float));
	printf("Size of double : %zu bytes\n", sizeof(double));
	printf("Size of long double : %zu bytes\n", sizeof(long double));
	printf("Size of pointer : %zu bytes\n", sizeof(void*));

	return 0;
}

Result

2. 자료형 크기를 모르면?

자료형 크기를 고려하지 않고 코드를 짜면, 의도하지 않은 버그가 발생할 수 있다.
가령, 자료형 char는 1바이트로 -128 ~ 127사이의 수를 나타낼 수 있다.
char val1 = 127val에 1을 더하면 어떤 일이 발생할까?
char val2 = -128val에 1을 빼면 어떤 일이 발생할까?
코드로 알아보도록 하자

Source

#include <stdio.h>
#include <limits.h>

int main(int argc, char* argv[]) {

	char value1 = CHAR_MAX;
	printf("Original value1: %d\n", value1);
	value1 = value1 + 1;
	printf("Value1 after add 1: %d\n", value1);
	
	char value2 = CHAR_MIN;
	printf("Original value2: %d\n", value2);
	value2 = value2 - 1;
	printf("Value2 after sub 1: %d\n", value2);

	return 0;
}

Result

why?

char는 1바이트로 8비트의 크기를 가지고 있으며, 최상단 비트는 부호비트로 작용한다.
따라서 -128 ~ 127 범위의 수를 나타낼 수 있다

1000 0000 ~ 0111 1111

따라서 value1의 경우,

0111 1111 + 1 = 1000 0000
127 + 1 = -128

value2의 경우,

1000 0000 - 1 = 0111 1111
-128 - 1 = 127

이를 오버플로(Overflow)라고 하는데, 이로 인한 취약점이 많이 발견되고 있다.
신경써서 코딩하자

3. 비트 연산 프로그램

특정 비트를 켜고 끄는 프로그램을 구현해보자

Source

#include <stdio.h>

// 특정 위치의 비트를 1로 설정하는 함수
unsigned char set_bit(unsigned char value, int position){
	return value | (1 << position);
}

// 특정 위치의 비트를 끄는 함수
unsigned char clear_bit(unsigned char value, int position){
	return value & ~(1 << position);
}

int main() {
	unsigned char value = 0b00001000;	//value = 8 (0000 1000)
	int position;
    
	printf("position : ");
	scanf("%d", &position);
	position-=1;
	
	// position번째 비트를 설정
	value = set_bit(value, position);
	printf("Value after setting %d bit: %d\n", position, value);

	// position번째 비트 off
	value = clear_bit(value, position);
	printf("Value after clearing %d bit: %d\n", position, value);
	
	return 0;
}

Result

4. 코드가 실행파일이 되기까지

소스코드는 그 자체로 실행이 불가능하다. 컴퓨터는 기계어만 알아듣는다.
소스코드가 기계어로 변하는 과정 예제 프로그램으로 알아보자

실습 코드

#include <stdio.h>

int main(){
	printf("Hello, world!\n");
    
    return 0;
}

전처리 과정 (.i 파일 확인하기)

gcc -E main.c -o main.i

컴파일 과정 (.s 파일 확인하기)

gcc -S main.i -o main.s

어셈블 과정 (.o 파일 확인하기)

gcc -c main.s -o main.o

여기서부터 파일 내용이 확인이 안된다.
👉 기계어이기 때문에

file * 명령어를 통해 현재 디렉토리에 있는 파일들의 정보를 보자

  • ELF(Excutable and Linkable Format): 윈도우에서 .EXE파일과 비슷
  • 64-bit: 64비트 아키텍쳐로 컴파일
  • LSB: Little Endian 바이트 순서 사용
  • relocatable: 재배치 가능. 즉, 링커에 의해 다른 오브젝트 파일이나 라이브러리와 연결될 수 있음
  • x86-64: x86-64(또는 AMD) 아키텍쳐용으로 컴파일 됨
  • version 1(SYSV): ELF 포맷의 버전과 변형. SYSV는 System V 유닉스를 의미함.
  • not stripped: "stripped"는 디버깅 정보나 심볼 테이블 등의 추가 정보를 제거한 상태를 의미.
    "not stripped"는 이러한 추가정보가 오브젝트 파일에 포함되어 있음

오브젝트 파일

링커에 의해 실행 가능한 바이너리나 라이브러리로 만들어 지기 전의 형태

  • 바이너리 형태: 기계어 코드를 포함하는 바이너리 파일. 그러나 오브젝트파일 자체로는 실행 불가
  • 재배치 가능: 오브젝트 파일은 다른 오브젝트 파일이나 라이브러리와 링크되어 완전한 프로그램 형성
  • 심볼 테이블: 심볼 테이블 포함
    • 파일 내의 각 함수나 변수의 이름과 위치 정보를 나타냄. 링커가 참조하는 테이블
  • 플랫폼 컴파일러 특정: 아키텍처, 운영 체제, 컴파일러에 따라 다르게 생성될 수 있음
    • ex) 리눅스는 .o, 윈도우는 .obj
readelf
  • 전용 도구: ELF 파일 형식에 특화된 도구. ELF 파일의 모든 섹션과 헤더 정보 확인 가능
  • 주요 기능: ELF 파일 헤더, 섹션 헤더, 프로그램 헤더, 심볼 테이블, 노트 섹션 등의 정보 표시

objdump
  • 다기능 도구: ELF 파일뿐만 아니라 다른 파일 형식의 바이너리에도 사용 가능
  • 디스어셈블: 바이너리 코드를 어셈블리 언어로 디스어셈블
  • 다양한 정보: 바이너리의 헤더 정보, 섹션 내용, 심볼 테이블 등 다양한 정보 표시. 또한, 바이너리의 공유 라이브러리 의존성, 재배치 정보 등도 확인

링킹 과정

gcc main.o -o main

오브젝트 파일과 실행파일 차이



부수적인 파일 정보가 붙었고, 파일의 크기도 달라졌다.



readelf와 objdump로 확인한 결과 많은 부분이 달라졌다.

  • 링킹되면서 다른 파일과 많이 합쳐지고 재배치됨
  • 실행에 필요한 함수들이 많이 생김


gdb를 통해 확인하면, 실제 메모리에 주소가 올라간 것을 확인할 수 있다

0개의 댓글

관련 채용 정보