Microsoft MVP성태의 닷넷 이야기
VC++: 46. 윈도우에서 Apache Module 컴파일 (VC++) [링크 복사], [링크+제목 복사],
조회: 38406
글쓴 사람
정성태 (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)
12211정성태4/27/202019264개발 환경 구성: 486. WSL에서 Makefile로 공개된 리눅스 환경의 C/C++ 소스 코드 빌드
12210정성태4/20/202020708.NET Framework: 903. .NET Framework의 Strong-named 어셈블리 바인딩 (1) - app.config을 이용한 바인딩 리디렉션 [1]파일 다운로드1
12209정성태4/13/202017414오류 유형: 614. 리눅스 환경에서 C/C++ 프로그램이 Segmentation fault 에러가 발생한 경우 (2)
12208정성태4/12/202015977Linux: 29. 리눅스 환경에서 C/C++ 프로그램이 Segmentation fault 에러가 발생한 경우
12207정성태4/2/202015828스크립트: 19. Windows PowerShell의 NonInteractive 모드
12206정성태4/2/202018445오류 유형: 613. 파일 잠금이 바로 안 풀린다면? - The process cannot access the file '...' because it is being used by another process.
12205정성태4/2/202015105스크립트: 18. Powershell에서는 cmd.exe의 명령어를 지원하진 않습니다.
12204정성태4/1/202015105스크립트: 17. Powershell 명령어에 ';' (semi-colon) 문자가 포함된 경우
12203정성태3/18/202017952오류 유형: 612. warning: 'C:\ProgramData/Git/config' has a dubious owner: '...'.
12202정성태3/18/202021208개발 환경 구성: 486. .NET Framework 프로젝트를 위한 GitLab CI/CD Runner 구성
12201정성태3/18/202018438오류 유형: 611. git-credential-manager.exe: Using credentials for username "Personal Access Token". [1]
12200정성태3/18/202018530VS.NET IDE: 145. NuGet + Github 라이브러리 디버깅 관련 옵션 3가지 - "Enable Just My Code" / "Enable Source Link support" / "Suppress JIT optimization on module load (Managed only)"
12199정성태3/17/202016172오류 유형: 610. C# - CodeDomProvider 사용 시 Unhandled Exception: System.IO.DirectoryNotFoundException: Could not find a part of the path '...\f2_6uod0.tmp'.
12198정성태3/17/202019532오류 유형: 609. SQL 서버 접속 시 "Cannot open user default database. Login failed."
12197정성태3/17/202018829VS.NET IDE: 144. .NET Core 콘솔 응용 프로그램을 배포(publish) 시 docker image 자동 생성 - 두 번째 이야기 [1]
12196정성태3/17/202015949오류 유형: 608. The ServicedComponent being invoked is not correctly configured (Use regsvcs to re-register).
12195정성태3/16/202018268.NET Framework: 902. C# - 프로세스의 모든 핸들을 열람 - 세 번째 이야기
12194정성태3/16/202020999오류 유형: 607. PostgreSQL - Npgsql.NpgsqlException: sorry, too many clients already
12193정성태3/16/202017916개발 환경 구성: 485. docker - SAP Adaptive Server Enterprise 컨테이너 실행 [1]
12192정성태3/14/202019939개발 환경 구성: 484. docker - Sybase Anywhere 16 컨테이너 실행
12191정성태3/14/202021048개발 환경 구성: 483. docker - OracleXE 컨테이너 실행 [1]
12190정성태3/14/202015628오류 유형: 606. Docker Desktop 업그레이드 시 "The process cannot access the file 'C:\Program Files\Docker\Docker\resources\dockerd.exe' because it is being used by another process."
12189정성태3/13/202021231개발 환경 구성: 482. Facebook OAuth 처리 시 상태 정보 전달 방법과 "유효한 OAuth 리디렉션 URI" 설정 규칙
12188정성태3/13/202026023Windows: 169. 부팅 시점에 실행되는 chkdsk 결과를 확인하는 방법
12187정성태3/12/202015598오류 유형: 605. NtpClient was unable to set a manual peer to use as a time source because of duplicate error on '...'.
12186정성태3/12/202017402오류 유형: 604. The SysVol Permissions for one or more GPOs on this domain controller and not in sync with the permissions for the GPOs on the Baseline domain controller.
... 61  62  63  64  65  66  67  68  [69]  70  71  72  73  74  75  ...