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

비밀번호

댓글 작성자
 




... 76  77  78  79  [80]  81  82  83  84  85  86  87  88  89  90  ...
NoWriterDateCnt.TitleFile(s)
11936정성태6/10/201918378Math: 58. C# - 최소 자승법의 1차, 2차 수렴 그래프 변화 확인 [2]파일 다운로드1
11935정성태6/9/201919944.NET Framework: 843. C# - PLplot 출력을 파일이 아닌 Window 화면으로 변경
11934정성태6/7/201921284VC++: 133. typedef struct와 타입 전방 선언으로 인한 C2371 오류파일 다운로드1
11933정성태6/7/201919611VC++: 132. enum 정의를 C++11의 enum class로 바꿀 때 유의할 사항파일 다운로드1
11932정성태6/7/201918789오류 유형: 544. C++ - fatal error C1017: invalid integer constant expression파일 다운로드1
11931정성태6/6/201919306개발 환경 구성: 441. C# - CairoSharp/GtkSharp 사용을 위한 프로젝트 구성 방법
11930정성태6/5/201919840.NET Framework: 842. .NET Reflection을 대체할 System.Reflection.Metadata 소개 [1]
11929정성태6/5/201919407.NET Framework: 841. Windows Forms/C# - 클립보드에 RTF 텍스트를 복사 및 확인하는 방법 [1]
11928정성태6/5/201918173오류 유형: 543. PowerShell 확장 설치 시 "Catalog file '[...].cat' is not found in the contents of the module" 오류 발생
11927정성태6/5/201919408스크립트: 15. PowerShell ISE의 스크립트를 복사 후 PPT/Word에 붙여 넣으면 한글이 깨지는 문제 [1]
11926정성태6/4/201919936오류 유형: 542. Visual Studio - pointer to incomplete class type is not allowed
11925정성태6/4/201919782VC++: 131. Visual C++ - uuid 확장 속성과 __uuidof 확장 연산자파일 다운로드1
11924정성태5/30/201921420Math: 57. C# - 해석학적 방법을 이용한 최소 자승법 [1]파일 다운로드1
11923정성태5/30/201921048Math: 56. C# - 그래프 그리기로 알아보는 경사 하강법의 최소/최댓값 구하기파일 다운로드1
11922정성태5/29/201918538.NET Framework: 840. ML.NET 데이터 정규화파일 다운로드1
11921정성태5/28/201924397Math: 55. C# - 다항식을 위한 최소 자승법(Least Squares Method)파일 다운로드1
11920정성태5/28/201916053.NET Framework: 839. C# - PLplot 색상 제어
11919정성태5/27/201920312Math: 54. C# - 최소 자승법의 1차 함수에 대한 매개변수를 단순 for 문으로 구하는 방법 [1]파일 다운로드1
11918정성태5/25/201921151Math: 53. C# - 행렬식을 이용한 최소 자승법(LSM: Least Square Method)파일 다운로드1
11917정성태5/24/201922138Math: 52. MathNet을 이용한 간단한 통계 정보 처리 - 분산/표준편차파일 다운로드1
11916정성태5/24/201919952Math: 51. MathNET + OxyPlot을 이용한 간단한 통계 정보 처리 - Histogram파일 다운로드1
11915정성태5/24/201923067Linux: 11. 리눅스의 환경 변수 관련 함수 정리 - putenv, setenv, unsetenv
11914정성태5/24/201922060Linux: 10. 윈도우의 GetTickCount와 리눅스의 clock_gettime파일 다운로드1
11913정성태5/23/201918764.NET Framework: 838. C# - 숫자형 타입의 bit(2진) 문자열, 16진수 문자열 구하는 방법파일 다운로드1
11912정성태5/23/201918726VS.NET IDE: 137. Visual Studio 2019 버전 16.1부터 리눅스 C/C++ 프로젝트에 추가된 WSL 지원
11911정성태5/23/201917494VS.NET IDE: 136. Visual Studio 2019 - 리눅스 C/C++ 프로젝트에 인텔리센스가 동작하지 않는 경우
... 76  77  78  79  [80]  81  82  83  84  85  86  87  88  89  90  ...