[C++] Visual Studio 2008로 nss 빌드 하기

오픈 소스가 없었다면 우리같이 작은 소프트웨어 기업은 시작도 못했을 것이다. 모든 것을 다 만드려면 사람이 많아야 하고, 필요한 것들을 다 사려면 돈이 많아야 하기 때문이다. 어쨌든 우리같이 작은 기업에게 오픈 소스는 축복이다. 하지만 늘 그렇듯이 버튼 하나 누르면 마법처럼 동작하는 상용 소프트웨어와는 다르게 오픈 소소는 시키는대로 해도 뭔가 안 되는 경우가 많은 것 같다. 물론 요즘은 정확하게 시키는 대로 하면 대체로 비슷한 결과를 얻을 수 있지만, 항상 사람 일이란게 시키는 대로만 하고 살수는 없는 노릇이다. 최근에 mozilla nss를 윈도우에서 빌드하면서도 비슷한 경험이 있었다. Visual Studio 2008을 이용해서 정적 CRT로 빌드를 하려고 했으나 몇 가지 이유로 쉽지 않았다. 여기에 그 기록을 남겨 본다.

#0

여기서 MozillaBuild를 받아서 설치한다. 그러면 기본 설치 경로인 mozilla-build에 가보면 start-shell-msvc2013.bat 파일이 존재한다. 해당 파일을 복사해서 2008으로 변경해준다. 해당 파일을 내용을 아래와 같이 수정하면 된다. 64비트를 빌드하고 싶으면 MOZ_MSVCBITS를 64로 변경해주면 된다.

@ECHO OFF

SET MOZ_MSVCBITS=32
SET MOZ_MSVCVERSION=9
SET MOZ_MSVCYEAR=2008

REM Switch CWD to the current location so that the call to start.shell-bat
REM doesn't fail if invoked from a different location.
pushd "%~dp0"

CALL start-shell.bat

수정했으면 배치파일을 실행한다. 명령 프롬프트가 뜬다. 소스를 다운로드 받을 폴더로 이동한다.

#1

해당 소스 파일 폴더에서 아래 명령어를 입력하면 최신 nspr과 nss 소스 코드를 클론할 수 있다.

$ hg clone https://hg.mozilla.org/projects/nspr
$ hg clone https://hg.mozilla.org/projects/nss

디버그, 정적 CRT를 사용하기 위해서는 아래와 같이 환경 변수를 선언해 준다.

$ export USE_DEBUG_RTL=1
$ export USE_STATIC_RTL=1

그 다음 nss 폴더로 이동해서 아래와 같이 입력하면 nspr과 동시에 빌드를 진행해 준다.

$ cd nss
$ make nss_build_all

#2

우리가 정적 라이브러리를 사용해서 빌드를 하면서 한 가지 문제가 있었다. 아직 수정되지 않은 문제로 보여진다. 이 함수가 문제였다. 전체적으로 아래와 같은 코드 흐름이다. 문제는 output_file이라는 변수가 PL_strdup와 PR_Malloc에 의해서 할당되는데 해제는 항상 PL_strfree로 한다는 점에 있다. 물론 PL_strdup와 PR_Malloc이 동일한 DLL에 있거나 정적으로 이 프로그램과 결합한다면 문제가 되지 않는다. 심지어 DLL 형태의 CRT를 사용해도 문제가 되지 않는다. 우리 케이스에서는 이게 문제였다. PL_strdup와 PR_Malloc은 서로 다른 DLL에 있는 함수였고, 이는 서로 다른 힙에 할당된다는 것을 의미했다. PL_strfree에서 크래시가 발생했다.

static char *
mkoutput(const char *input)
{
	char *output = PR_Malloc(...);

	return output;
}

int main()
{
	char *output_file = NULL;

	if(some_cond)
		output_file = PL_strdup(optstate->value);

	if(output_file == NULL)
		output_file = mkoutput(input_file);

	if(output_file) /* allocated by mkoutput function */
		PL_strfree(output_file);
}

코드를 아래와 같이 수정해주면 된다. 결국 PL_strdup만 할당한 변수를 사용하도록 통일해 주는 것이다.

static char *
mkoutput(const char *input)
{
    char *result = NULL;
    int in_len = strlen(input);
    char *output = PR_Malloc(in_len + sizeof(SGN_SUFFIX));
    int index = in_len + 1 - sizeof("." SHLIB_SUFFIX);

    if ((index > 0) &&
        (PL_strncmp(&input[index],
                    "." SHLIB_SUFFIX, sizeof("." SHLIB_SUFFIX)) == 0)) {
        in_len = index;
    }
    memcpy(output, input, in_len);
    memcpy(&output[in_len], SGN_SUFFIX, sizeof(SGN_SUFFIX));

	result = PL_strdup(output);
	PR_Free(output);

    return result;
}

#3

하지만 여기까지 수정을 하면 대체로 빌드가 상당히 진행이 되는데 테스트 빌드를 하는 도중에 오류가 발생한다. 대체로 C11, C14 기능들을 활용한 부분인데 Visual Studio 2008에서 그런걸 기대하기는 힘드니 아래와 같이 환경 변수를 선언해서 해당 부분은 빌드를 하지 않도록 한 다음 빌드를 하면 모두 성공시킬 수 있다.

$ export NSS_DISABLE_GTESTS=1
$ build nss_build_all