본문 바로가기
운영체제/[ecourse] Windows Programming

3-2 Dynamic Library

by 헛둘이 2022. 9. 11.
DLL 만들기
  • DLL : 동적 연결 라이브러리
// dllmain.cpp : DLL 애플리케이션의 진입점을 정의합니다.
#include "pch.h"

//dll을 외부에서 사용하려면 __declspec(dllexport) 지시어를 통해 export 해주어야 한다.
//이를 export 심볼이라고 한다.
__declspec(dllexport) int add(int a, int b)
{
	return a + b;
}

__declspec(dllexport) int __stdcall sub(int a, int b)
{
	return a - b;
}
  • dll 샘플 코드

 

 

DLL 빌드 방법
  1. 개발자 명령 프롬프트에서 빌드

여기서 만들어지는 lib 파일은 export되는 함수의 링크 정보만 담겨 있다.

 

 

 2. 통합개발환경에서 빌드

  • dll은 f5로 실행할 수 없고 ctrl + shift + b를 통해 빌드만 진행

 

 

 


Symbol Export
  • dll도 PE포맷으로 되어 있어서 PE View로 볼 수 있음
  • dll은 실행되는 파일에 포함되는 게 아니라 실행 파일에서 호출하는 것 
  • .edata 섹션에 보면 add, sub 함수의 export 정보를 볼 수 있다.
  • .edata 섹션은 크기가 작아서 .rdata 섹션이나 .text 섹션에 포함되는 경우가 많다.

.rdata 섹션에 속한 .edata 섹션

 

 

 

 


DLL 배포하기
  • 위의 과정을 거치면서 .lib파일과 .dll 파일이 만들어지는데,
  • 함수를 호출하려면 선언도 필요하기 때문에, 헤더 파일도 같이 배포한다.
  • 헤더 파일에도 함수 선언 앞에 __declspec(dllexport)를 붙어야 한다.
  • (생략 가능하나 붙이는 것이 효율적)
  • 따라서 dll을 컴파일할 때 필요한 것은 dll, lib, h 총 3개의 파일이다. 
    • cpp에 헤더를 포함하고 #pragma comment(lib, "")을 통해 헤더와 lib를 가져온다.
    • 이 시점에 lib를 통해 exe의 .idata영역에 어떤 dll에 어떤 함수가 사용되는지 주소와 같은 정보들이 들어간다.
    • 따라서 빌드 시점에는 dll이 필요 없음
    • idata에 name table은 어떤 함수를 쓰는지도 나오는데 이 주소를 조작할 수 있다고 함

#include <iostream>
#include "dllimport.h"

#pragma comment(lib, "dllmain.lib")

int main()
{
    std::cout << add(3, 5) << std::endl;
}

 

 

 

 

 

 

 

 


CRT 함수와 라이브러리 개념
  • 우리가 알고 있는 모든 라이브러리들은 dll로 배포가 된다.
  • 윈도우 설치될 때 많은 dll이 설치되고 user32.dll, gdi32.dll, kernel32.dll이 유명하다.

 

dll을 쓰려면 dll과 lib와 header가 필요한데 그건 어디서?
  • MessageBox의 경우를 예로 들면,
  • 구현부는 User32.dll 안에 있는데 이는 시스템 폴더에 있어서 환경변수의 영향을 받는다.
  • User32.lib는 VS의 경우 추가종속성 란에 들어가 있다.
  • 헤더의 경우 WinUser.h 안에 있는데 이 헤더는 Windows.h안에 포함되어 있다.
  • 그래서 개발자 명령 프롬프트에서 빌드하려면 cl main.c user32.lib 라고 뒤에 언급해주어야 한다.

 

 

CRT 함수란?
  • C에서 제공하는 printf 등등 기본적인 함수들
  • 이 함수들은 lib버전과 dll 버전을 모두 제공한다.
  • VS에서 빌드하면 기본적으로 dll 버전으로 빌드되며,
  • 옵션을 변경하려면 프로젝트 -> 속성메뉴 -> C/C++ -> 코드 생성 -> 런타임 라이브러리에서 변경 가능
  • 다중 스레드 DLL -> DLL버전
  • 다중 스레드 -> LIB버전

 

 

 

 

 

 

 


DLL의 명시적 연결
  • 위에서 언급한 방법들은 프로그램 시작 시 DLL을 같이 로드하는 방법으로,
  • 이 방법을 암시적 연결이라고 한다.
  • 명시적 연결은 원하는 시점에 함수를 통해 dll을 load하는 방법이다.
  • 이 방식의 경우 헤더와 lib가 필요 없어진다.
#include <iostream>
#include <Windows.h>
#include "dllimport.h"

#pragma comment(lib, "dllmain.lib")

typedef int (*dllFunc)(int, int);

int main()
{
    HMODULE hMod = LoadLibrary(L"dllmain.dll");
    dllFunc f = (dllFunc)GetProcAddress(hMod, "?add@@YAHHH@Z");

    std::cout << f(3, 5) << std::endl;
    FreeLibrary(hMod);
}
  • 라이브러리를 LoadLibrary 함수로 로드한 후,
  • GetProcAddress 함수를 이용해서 가져올 함수 주소를 가져와서 사용한다.

 

 

 

#include <iostream>
#include <Windows.h>
#include "dllimport.h"

#pragma comment(lib, "dllmain.lib")

typedef int (__stdcall *dllFunc)(int, int);

int main()
{
    HMODULE hMod = LoadLibrary(L"dllmain.dll");
    dllFunc f = (dllFunc)GetProcAddress(hMod, "?sub@@YGHHH@Z");

    std::cout << f(3, 5) << std::endl;
    FreeLibrary(hMod);
}
  • __stdcall 처럼 함수호출규약을 변경한 경우 그 정보를 함수 포인터에 기재해주어야 한다.

'운영체제 > [ecourse] Windows Programming' 카테고리의 다른 글

4-1. Kernel Object  (0) 2022.09.13
3-3. 가상 주소 공간과 DLL  (0) 2022.09.12
3-1. Static Library  (0) 2022.09.08
2-3. 라이브러리 개념  (0) 2022.09.07
2-2. 실행 파일 포맷  (0) 2022.09.07

댓글