Microsoft MVP성태의 닷넷 이야기
VC++: 46. 윈도우에서 Apache Module 컴파일 (VC++) [링크 복사], [링크+제목 복사],
조회: 42781
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 5개 있습니다.)
윈도우에서 Apache Module 컴파일 (VC++)

IIS의 상황과 비교하자면, ISAPI Extension을 만드는 경우일 텐데요. 아파치에서는 이런 것을 "Module"이라고 부르는 것 같습니다.

아래는 모듈 빌드를 위해 제가 참조한 문서입니다.

아파치 모듈 mod_example
; http://httpd.apache.org/docs/trunk/ko/mod/mod_example.html

빌드 환경은 지난번 아파치 소스 코드를 컴파일할 때의 설치 환경에서 시작합니다.

Apache 소스를 윈도우 환경에서 빌드하기
; https://www.sysnet.pe.kr/2/0/1048

소스 코드는 바닥부터 만들지는 않고, 아파치 소스 코드에 내장된 예제를 기반으로 합니다. 따라서 아파치 소스 코드를 다운로드해야 합니다.

Win32 Source - httpd-2.2.19-win32-src.zip
; http://mirror.apache-kr.org//httpd/httpd-2.2.19-win32-src.zip

여기서는 지난번 환경 구성을 그대로 이어갈 것이기 때문에 다음과 같이 소스 코드 폴더가 구성되어 있다고 가정합니다.

module_handler_build_1.png

위에서 보여지고 있는 "D:\httpd_build\httpd\modules\experimental" 폴더가 바로 아파치에서 제공되는 기본 Module 소스 코드입니다. 이 소스 코드는 보관해 두는 것이 좋을 것 같아서, experimental 폴더를 그대로 같은 레벨에 "D:\httpd_build\httpd\modules\mymodule"로 복사합니다.

module_handler_build_2.png




Visual Studio 2008 IDE 안에서 컴파일 - 리소스 전처리 상수 변경


"D:\httpd_build\httpd\modules\mymodule\mod_example.dsp" 파일을 더블 클릭해서 Visual Studio 2008 IDE로 로드해서 마이그레이션해 줍니다.

곧바로 로드된 상태에서 빌드를 시작하면 다음과 같은 오류가 발생합니다.

Error result 1 returned from 'D:\Program Files\Microsoft SDKs\Windows\v6.1\bin\rc.exe'.

./Debug/BuildLog.htm 파일을 보면 rc.exe가 사용한 명령어 옵션을 확인할 수 있습니다.

rc.exe /d "_DEBUG" /d "BIN_NAME="mod_example.so"" /d "LONG_NAME="example_module for Apache"" /d "_VC80_UPGRADE=0x0600" /l 0x409 /I "\httpd_build\httpd\build\win32" /I "../../include" /I "../../srclib/apr/include" /fo"Debug/mod_example.res" ..\..\build\win32\httpd.rc"


오류 원인을 확인하기 위해 Visual Studio 명령어 창을 띄우고 이대로 한번 실행해 볼까요?

C:\Windows\System32>cd D:\httpd_build\httpd\modules\mymodule

D:\httpd_build\httpd\modules\mymodule>rc.exe /d "_DEBUG" /d "BIN_NAME="mod_example.so"" /d "LONG_NAME="example_module for Apache"" /d "_VC80_UPGRADE=0x0600" /l 0x409 /I "\httpd_build\httpd\build\win32" /I "../../include" /I "../../srclib/apr/include" /fo"Debug/mod_example.res" ..\..\build\win32\httpd.rc
Microsoft (R) Windows (R) Resource Compiler Version 6.1.7600.16385
Copyright (C) Microsoft Corporation.  All rights reserved.

fatal error RC1107: invalid usage; use RC /? for Help

동일한 오류가 발생하는군요. 자... 그런데 자세히 살펴보니, 옵션을 주는 곳에 필요 이상으로 인용 부호(", double quotation mark)가 들어있는 것을 확인할 수 있습니다. 그 부분을 정리해서,

rc.exe /d "_DEBUG" /d "BIN_NAME="mod_example.so"" /d "LONG_NAME="example_module for Apache"" /d "_VC80_UPGRADE=0x0600" /l 0x409 /I "\httpd_build\httpd\build\win32" /I "../../include" /I "../../srclib/apr/include" /fo"Debug/mod_example.res" ..\..\build\win32\httpd.rc"

==>
rc.exe /d "_DEBUG" /d "BIN_NAME=mod_example.so" /d "LONG_NAME=example_module for Apache" /d "_VC80_UPGRADE=0x0600" /l 0x409 /I "\httpd_build\httpd\build\win32" /I "../../include" /I "../../srclib/apr/include" /fo"Debug/mod_example.res" ..\..\build\win32\httpd.rc


다시 한번 실행하면 정상적으로 rc.exe가 리소스 파일을 컴파일합니다. 그럼, 이 수정 사항을 vcproj 프로젝트 파일에도 반영해 주어야 하는데요. 아래와 같이 프로젝트 속성 창의 "Configuration Properties" / "Resources" 범주에서 "Preprocessor Definitions" 값을 바꿔줍니다.

[기존 값]
_DEBUG,BIN_NAME="mod_example.so",LONG_NAME="example_module for Apache"

[새로운 값]
_DEBUG,BIN_NAME=mod_example.so,LONG_NAME=example_module for Apache

module_handler_build_3.png




Visual Studio 2008 IDE 안에서 컴파일 - LIB 파일 추가


계속해서 IDE상에서 빌드를 시작하면 아래와 같은 "unresolved external symbol" 오류들이 대량으로 발생합니다.

error LNK2019: unresolved external symbol __imp__ap_log_error referenced in function _trace_add 
error LNK2019: unresolved external symbol __imp__apr_table_set@12 referenced in function _trace_add 
error LNK2019: unresolved external symbol __imp__apr_pstrcat referenced in function _trace_add 
error LNK2019: unresolved external symbol __imp__apr_pool_destroy@4 referenced in function _trace_add 
...[생략]...

다시 ./Debug/BuildLog.htm 파일을 보면 link.exe가 수행된 rsp 파일에 지정된 옵션을 확인할 수 있습니다.

Creating temporary file "d:\httpd_build\httpd\modules\mymodule\Debug\RSP00000529641108.rsp" with contents
[
/OUT:".\Debug\mod_example.so" /INCREMENTAL:NO /DLL /MANIFEST /MANIFESTFILE:".\Debug\mod_example.so.intermediate.manifest" /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /DEBUG /PDB:".\Debug/mod_example.pdb" /SUBSYSTEM:WINDOWS /BASE:"@..\..\os\win32\BaseAddr.ref,mod_example.so" /DYNAMICBASE:NO /IMPLIB:".\Debug/mod_example.lib" kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib

".\Debug\mod_example.res"

".\Debug\mod_example.obj"
]


보시는 것처럼, 아파치 관련 lib 파일이 하나도 없습니다.

오류와 관련해서 검색해 보니, 다음의 글에서 어떤 lib 파일이 필요한지 알려주고 있습니다.

Post subject: unable to complie a sample code for a module
; http://www.apachelounge.com/viewtopic.php?p=16879

찾아보니까, 이 파일들은 Apache 소스 코드에는 없고, 아파치가 설치된 폴더나 Apache 소스 코드를 빌드 완료한 폴더의 하위 /lib 폴더에 있었습니다. 따라서 /lib 폴더의 내용을 "D:\httpd_build\httpd\modules\lib" 폴더로 모두 복사시키고,

module_handler_build_4.png

mod_example.c 파일에 다음과 같이 pragma 구문을 추가해 줍니다.

#pragma comment(lib, "../lib/libhttpd.lib")
#pragma comment(lib, "../lib/libapr-1.lib")
#pragma comment(lib, "../lib/libaprutil-1.lib")

module_handler_build_5.png

휴~~~, 이제 다 되었습니다. 이제 빌드를 하면 정상적으로 실행되고, 출력 폴더에는 "mod_example.so"라는 파일이 생성됩니다.




빌드 결과물 테스트


이 파일을 "Dependency Walker"로 확인해 보면 다음과 같습니다.

module_handler_build_6.png

아하... 유일하게 export 된 example_module 전역변수가 있군요. mod_exmple.c 파일에서 해당 심볼을 찾으면 다음과 같이 나옵니다.

module AP_MODULE_DECLARE_DATA example_module =
{
    STANDARD20_MODULE_STUFF,
    x_create_dir_config,    /* per-directory config creator */
    x_merge_dir_config,     /* dir config merger */
    x_create_server_config, /* server config creator */
    x_merge_server_config,  /* server config merger */
    x_cmds,                 /* command table */
    x_register_hooks,       /* set up other request processing hooks */
};

잘은 모르겠지만 초기화 시에, 아파치 측에서 example_module 변수에 포함된 각종 함수 포인터로 콜백 호출을 할 텐데, 그중에서 x_register_hooks 함수의 코드를 보면, 다시 여러 가지 메서드들의 주소를 초기화 시켜 주고 있습니다.

static void x_register_hooks(apr_pool_t *p)
{
    ap_hook_pre_config(x_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_post_config(x_post_config, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_open_logs(x_open_logs, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_child_init(x_child_init, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_handler(x_handler, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_quick_handler(x_quick_handler, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_pre_connection(x_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_process_connection(x_process_connection, NULL, NULL, APR_HOOK_MIDDLE);
    /* [1] post read_request handling */
    ap_hook_post_read_request(x_post_read_request, NULL, NULL,
                              APR_HOOK_MIDDLE);
    ap_hook_log_transaction(x_logger, NULL, NULL, APR_HOOK_MIDDLE);
#if 0
    ap_hook_http_scheme(x_http_scheme, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_default_port(x_default_port, NULL, NULL, APR_HOOK_MIDDLE);
#endif
    ap_hook_translate_name(x_translate_handler, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_map_to_storage(x_map_to_storage_handler, NULL,NULL, APR_HOOK_MIDDLE);
    ap_hook_header_parser(x_header_parser_handler, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_check_user_id(x_check_user_id, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_fixups(x_fixer_upper, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_type_checker(x_type_checker, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_access_checker(x_access_checker, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_auth_checker(x_auth_checker, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_insert_filter(x_insert_filter, NULL, NULL, APR_HOOK_MIDDLE);
}

그중에서 ap_hook_handler를 통해 x_handler를 호출해 주고 있고, 바로 이 함수가 웹 브라우저에서 주소를 치고 들어올 때 실행되는 함수가 되겠습니다.

static int x_handler(request_rec *r)
{
    x_cfg *dcfg;

    if (strcmp(r->handler, "example-handler")) {
        return DECLINED;
    }
    ... [생략]...
}

일단, 가벼운 테스트를 위해 x_handler 함수에 다음의 코드를 추가하고 빌드한 다음,

OutputDebugStringA("x_handler: start");

mod_example.so 파일을 아파치가 설치된 폴더(예제에서는 "D:\httpd_build\Apache22")의 하위 "modules" 폴더에 복사합니다. 그런 다음, "D:\httpd_build\Apache22\conf\httpd.conf" 파일을 열어서 다음의 설정을 추가합니다.

LoadModule example_module modules/mod_example.so

<Location /example-info>
 SetHandler example-handler
 </Location> 

아하,,, 로드 모듈에 있는 "example_module"이라는 단어가 낯설지 않습니다. 바로 "Dependency Walker"로 확인할 때 보았던 export 된 함수의 이름입니다.

설정을 마치고 아파치를 재시작한 다음, 웹 브라우저를 통해서 "http://localhost:6500/example-info/example-handler" 주소를 방문하면 다음과 같이 실행 결과가 표시됩니다.

module_handler_build_7.png

물론, 동시에 dbgview 윈도우 창에는 OutputDebugStringA 메서드로 전달된 "x_handler: start" 출력이 표시됩니다.

(첨부된 파일은 위에서 테스트한 mymodule 프로젝트입니다.)



[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 8/18/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 61  62  63  64  65  66  67  68  69  70  71  72  73  74  [75]  ...
NoWriterDateCnt.TitleFile(s)
12153정성태2/23/202024290.NET Framework: 898. Trampoline을 이용한 후킹의 한계파일 다운로드1
12152정성태2/23/202021320.NET Framework: 897. 실행 시에 메서드 가로채기 - CLR Injection: Runtime Method Replacer 개선 - 세 번째 이야기(Trampoline 후킹)파일 다운로드1
12151정성태2/22/202023975.NET Framework: 896. C# - Win32 API를 Trampoline 기법을 이용해 C# 메서드로 가로채는 방법 - 두 번째 이야기 (원본 함수 호출)파일 다운로드1
12150정성태2/21/202024064.NET Framework: 895. C# - Win32 API를 Trampoline 기법을 이용해 C# 메서드로 가로채는 방법 [1]파일 다운로드1
12149정성태2/20/202020968.NET Framework: 894. eBEST C# XingAPI 래퍼 - 연속 조회 처리 방법 [1]
12148정성태2/19/202025605디버깅 기술: 163. x64 환경에서 구현하는 다양한 Trampoline 기법 [1]
12147정성태2/19/202020962디버깅 기술: 162. x86/x64의 기계어 코드 최대 길이
12146정성태2/18/202022164.NET Framework: 893. eBEST C# XingAPI 래퍼 - 로그인 처리파일 다운로드1
12145정성태2/18/202023784.NET Framework: 892. eBEST C# XingAPI 래퍼 - Sqlite 지원 추가파일 다운로드1
12144정성태2/13/202023970.NET Framework: 891. 실행 시에 메서드 가로채기 - CLR Injection: Runtime Method Replacer 개선 - 두 번째 이야기파일 다운로드1
12143정성태2/13/202018377.NET Framework: 890. 상황별 GetFunctionPointer 반환값 정리 - x64파일 다운로드1
12142정성태2/12/202022290.NET Framework: 889. C# 코드로 접근하는 MethodDesc, MethodTable파일 다운로드1
12141정성태2/10/202021272.NET Framework: 888. C# - ASP.NET Core 웹 응용 프로그램의 출력 가로채기 [2]파일 다운로드1
12140정성태2/10/202022640.NET Framework: 887. C# - ASP.NET 웹 응용 프로그램의 출력 가로채기파일 다운로드1
12139정성태2/9/202022302.NET Framework: 886. C# - Console 응용 프로그램에서 UI 스레드 구현 방법
12138정성태2/9/202028549.NET Framework: 885. C# - 닷넷 응용 프로그램에서 SQLite 사용 [6]파일 다운로드1
12137정성태2/9/202020193오류 유형: 592. [AhnLab] 경고 - 디버거 실행을 탐지했습니다.
12136정성태2/6/202021858Windows: 168. Windows + S(또는 Q)로 뜨는 작업 표시줄의 검색 바가 동작하지 않는 경우
12135정성태2/6/202027646개발 환경 구성: 468. Nuget 패키지의 로컬 보관 폴더를 옮기는 방법 [2]
12134정성태2/5/202024916.NET Framework: 884. eBEST XingAPI의 C# 래퍼 버전 - XingAPINet Nuget 패키지 [5]파일 다운로드1
12133정성태2/5/202022665디버깅 기술: 161. Windbg 환경에서 확인해 본 .NET 메서드 JIT 컴파일 전과 후 - 두 번째 이야기
12132정성태1/28/202025694.NET Framework: 883. C#으로 구현하는 Win32 API 후킹(예: Sleep 호출 가로채기) [1]파일 다운로드1
12131정성태1/27/202024402개발 환경 구성: 467. LocaleEmulator를 이용해 유니코드를 지원하지 않는(한글이 깨지는) 프로그램을 실행하는 방법 [1]
12130정성태1/26/202022015VS.NET IDE: 142. Visual Studio에서 windbg의 "Open Executable..."처럼 EXE를 직접 열어 디버깅을 시작하는 방법
12129정성태1/26/202028981.NET Framework: 882. C# - 키움 Open API+ 사용 시 Registry 등록 없이 KHOpenAPI.ocx 사용하는 방법 [3]
12128정성태1/26/202023134오류 유형: 591. The code execution cannot proceed because mfc100.dll was not found. Reinstalling the program may fix this problem.
... 61  62  63  64  65  66  67  68  69  70  71  72  73  74  [75]  ...