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

비밀번호

댓글 작성자
 




1  2  3  4  5  6  7  8  9  10  11  12  13  14  [15]  ...
NoWriterDateCnt.TitleFile(s)
13566정성태2/27/20249556오류 유형: 897. Windows 7 SDK 설치 시 ".NET Development" 옵션이 비활성으로 선택이 안 되는 경우
13565정성태2/23/20248651닷넷: 2219. .NET CLR2 보안 모델에서의 개별 System.Security.Permissions 제어
13564정성태2/22/202410163Windows: 259. Hyper-V Generation 1 유형의 VM을 Generation 2 유형으로 바꾸는 방법
13563정성태2/21/20249647디버깅 기술: 196. windbg - async/await 비동기인 경우 메모리 덤프 분석의 어려움
13562정성태2/21/20249360오류 유형: 896. ASP.NET - .NET Framework 기본 예제에서 System.Web에 대한 System.IO.FileNotFoundException 예외 발생
13561정성태2/20/202410163닷넷: 2218. C# - (예를 들어, Socket) 비동기 I/O에 대한 await 호출 시 CancellationToken을 이용한 취소파일 다운로드1
13560정성태2/19/202410215디버깅 기술: 195. windbg 분석 사례 - Semaphore 잠금으로 인한 Hang 현상 (닷넷)
13559정성태2/19/202411040오류 유형: 895. ASP.NET - System.Security.SecurityException: 'Requested registry access is not allowed.'
13558정성태2/18/202410196닷넷: 2217. C# - 최댓값이 1인 SemaphoreSlim 보다 Mutex 또는 lock(obj)를 선택하는 것이 나은 이유
13557정성태2/18/20249077Windows: 258. Task Scheduler의 Author 속성 값을 변경하는 방법
13556정성태2/17/20249456Windows: 257. Windows - Symbolic (hard/soft) Link 및 Junction 차이점
13555정성태2/15/20249667닷넷: 2216. C# - SemaphoreSlim 사용 시 주의점
13554정성태2/15/20249089VS.NET IDE: 189. Visual Studio - 닷넷 소스코드 디컴파일 찾기가 안 될 때
13553정성태2/14/20248618닷넷: 2215. windbg - thin/fat lock 없이 동작하는 Monitor.Wait + Pulse
13552정성태2/13/20248743닷넷: 2214. windbg - Monitor.Enter의 thin lock과 fat lock
13551정성태2/12/20249841닷넷: 2213. ASP.NET/Core 웹 응용 프로그램 - 2차 스레드의 예외로 인한 비정상 종료
13550정성태2/11/202410293Windows: 256. C# - Server socket이 닫히면 Accept 시켰던 자식 소켓이 닫힐까요?
13549정성태2/3/202411255개발 환경 구성: 706. C# - 컨테이너에서 실행하기 위한 (소켓) 콘솔 프로젝트 구성
13548정성태2/1/202410819개발 환경 구성: 705. "Docker Desktop for Windows" - ASP.NET Core 응용 프로그램의 소켓 주소 바인딩(IPv4/IPv6 loopback, Any)
13547정성태1/31/202410490개발 환경 구성: 704. Visual Studio - .NET 8 프로젝트부터 dockerfile에 추가된 "USER app" 설정
13546정성태1/30/202410165Windows: 255. (디버거의 영향 등으로) 대상 프로세스가 멈추면 Socket KeepAlive로 연결이 끊길까요?
13545정성태1/30/20249713닷넷: 2212. ASP.NET Core - 우선순위에 따른 HTTP/HTTPS 호스트:포트 바인딩 방법
13544정성태1/30/20249354오류 유형: 894. Microsoft.Data.SqlClient - Could not load file or assembly 'System.Security.Permissions, ...'
13543정성태1/30/20249772Windows: 254. Windows - 기본 사용 중인 5357 포트 비활성화는 방법
13542정성태1/30/20248858오류 유형: 893. Visual Studio - Web Application을 실행하지 못하는 IISExpress - 두 번째 이야기
13541정성태1/29/20249624VS.NET IDE: 188. launchSettings.json의 useSSL 옵션
1  2  3  4  5  6  7  8  9  10  11  12  13  14  [15]  ...