Microsoft MVP성태의 닷넷 이야기
VC++: 46. 윈도우에서 Apache Module 컴파일 (VC++) [링크 복사], [링크+제목 복사],
조회: 38361
글쓴 사람
정성태 (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

비밀번호

댓글 작성자
 




... 151  152  153  154  [155]  156  157  158  159  160  161  162  163  164  165  ...
NoWriterDateCnt.TitleFile(s)
1174정성태11/15/201126521.NET Framework: 269. 일반 참조형의 기본 메모리 소비는 얼마나 될까요? [4]
1173정성태11/14/201122742.NET Framework: 268. .NET Array는 왜 12bytes의 기본 메모리를 점유할까? [1]
1172정성태11/13/201119688.NET Framework: 267. windbg - GC Heap에서 .NET 타입에 대한 배열을 찾는 방법
1171정성태11/12/201136371.NET Framework: 266. StringBuilder에서의 OutOfMemoryException 오류 원인 분석 [4]파일 다운로드1
1170정성태11/10/201125590.NET Framework: 265. Named 동기화 개체 생성 시 System.UnauthorizedAccessException 예외 발생하는 경우
1169정성태11/10/201129392.NET Framework: 264. 다중 LAN 카드 환경에서 Dns.GetHostAddresses(local)가 반환해 주는 IP의 우선순위는 어떻게 될까요? [4]
1168정성태11/6/201125236오류 유형: 139. TlbImp : error TI0000 : A single valid machine type compatible with the input type library must be specified
1167정성태11/5/201137090개발 환경 구성: 133. Registry 등록 과정 없이 COM 개체 사용 - 두 번째 이야기 [5]파일 다운로드4
1166정성태11/5/201123107.NET Framework: 263. byte[] pData = new byte[100000]로 인한 성능 차이? [1]파일 다운로드1
1165정성태11/3/201128006개발 환경 구성: 132. "Visual Studio Command Prompt (2010)" 명령행에서 2.0 버전의 MSBuild를 구동하는 방법 [2]파일 다운로드1
1164정성태11/1/201126185.NET Framework: 262. .NET 스레드 콜 스택 덤프 (4) - .NET 4.0을 지원하지 않는 MSE 응용 프로그램 원인 분석
1163정성태10/31/201125699.NET Framework: 261. .NET 스레드 콜 스택 덤프 (3) - MSE 소스 코드 개선파일 다운로드1
1162정성태10/30/201125832.NET Framework: 260. .NET 스레드 콜 스택 덤프 (2) - Managed Stack Explorer 소스 코드를 이용한 스택 덤프 구하는 방법파일 다운로드1
1161정성태10/29/201122611.NET Framework: 259. Type.GetMethod - System.Reflection.AmbiguousMatchException파일 다운로드1
1159정성태10/28/201126055.NET Framework: 258. Roslyn 맛보기 - SyntaxTree 조작 [2]
1158정성태10/24/201125371.NET Framework: 257. Roslyn 맛보기 - Roslyn Symbol / Binding API파일 다운로드1
1157정성태10/23/201129845.NET Framework: 256. Roslyn 맛보기 - Syntax Analysis (Roslyn Syntax API) [2]
1156정성태10/23/201128290.NET Framework: 255. Roslyn 맛보기 - Roslyn Services APIs를 이용한 Code Issue 및 Code Action 기능 소개 [1]
1155정성태10/22/201126352.NET Framework: 254. Roslyn 맛보기 - C# Interactive (2)
1154정성태10/22/201133078.NET Framework: 253. Roslyn 맛보기 - C# Interactive (1)
1153정성태10/21/201141945.NET Framework: 252. Roslyn 맛보기 - C# 소스 코드를 스크립트처럼 다루는 방법 [7]파일 다운로드1
1152정성태10/20/201123638.NET Framework: 251. string.GetHashCode는 hash 값을 cache 할까?
1151정성태10/18/201122571Java: 13. 자바도 64비트에서 (2GB) OutOfMemoryException 예외가 발생할까?
1150정성태10/18/201129829.NET Framework: 250. WPF - ComboBox의 SelectionChagned 이벤트파일 다운로드1
1149정성태10/16/201125463.NET Framework: 249. WPF - d:DesignHeight 값을 구할 수 있을까?
1148정성태10/14/201131445Java: 12. 자바에서 LINQ 사용? [7]
... 151  152  153  154  [155]  156  157  158  159  160  161  162  163  164  165  ...