이어서 이번에는 so파일과 안드로이드의 자바코드를 이어보겠습니다.

그러기 전에 우선 CLI 프로그램에서의 구조를 말해야 할 것 같습니다.

 

우리가 자주 쓰는 프로그램중에 make만 봐도 이런식으로 쓰는 경우가 꽤 있습니다.

 

make -j4

 

많이 쓰시는 분들은 무슨 의미인지 바로 파악이 가능하실 텐데 make를 하되 4개의 스레드를 만들어서 돌린다는 의미입니다. 여기서 -j4를 파라미터라고 합니다. 그런데 C프로그램에선 이것을 어떻게 파악을 할까요?

 

C/C++의 기본적인 main함수의 구조를 보면

int main(int argc, char[][] argv) 

이런 구조가 있습니다.

(int 대신 void를 쓰기도 하지만 어찌됐든 보통은 int가 기본입니다.)

 

여기서는 argc라는 정수형 하나와 argv라는 이중 배열 문자 혹은 문자열의 배열을 의미합니다.

그 어떤 파라미터를 받지 않는 다면 뒷부분을 모두 생략 가능합니다만 CLI특성상 뒤에 파라미터를 받는 경우가 대부분입니다.

 

즉, argc는 파라미터의 갯수이고 argv는 파라미터의 갯수입니다.

다만 argv[0]에 들어가는 것은 무조건 실행되는 프로그램의 경로를 넣습니다.

그러니까

 

make -j4

이 명령을 넣었을 때 make의 main함수에서는

argc = 2

argv[0] = /bin/make

argv[1] = -j4

 

이렇식으로 들어간다는 의미입니다. 그러면 다시한번 vlmcsd의 함수를 뜯어볼까요. (이것이 본래 main함수라는건 잊지 않으셨죠?)

int argc와 함께 CARGV argv 변수를 받고 있습니다. CARGV는 define된 무언가일겁니다. (VSCode에서 저건 보통 define 된 이름입니다.) CARGV를 한번 검색해봅시다.

 

types.h에 보면 typedef로 처리되어 있군요. 구조체가 아니었네요. const char *const * 형태를 의미합니다. 즉, 이중포인터 = 이중배열 구조를 의미합니다.

argv구조와 같다는 것입니다. 

 

이걸 말씀드리는 이유는 server_main함수를 안드로이드의 자바/코틀린에서 호출할 예정이기 때문입니다. 그러려면 함수의 구조를 그대로 만들어서 호출해야겠지요.

 

그리고 vlmcsd의 동작에 대해 말할 필요가 있습니다.

 

터미널에서 vlmcsd를 실행하면

그냥 이렇게 커서가 뚝 떨어집니다. 그래서 ps -A | grep vlmcsd를 해보면

이렇게 뒤에서 프로세스가 돌고 있는 것을 볼 수 있습니다.

이건 백그라운드에서 돌릴 수 있게끔 코드가 짜여져 있기 때문입니다. 그런데 우리는 vlmcsd를 따로 실행하는 것이 아니라 안드로이드 앱의 프로세스 내에서 이것을 돌려야 합니다. 미리 말씀드리자면 이 코드를 그대로 호출한 결과 안드로이드 프로세스가 같이 죽어버리는 결과가 나옵니다. 백그라운드 프로세스에 등록후에 자기 자신을 꺼버리게 때문입니다.

 

그래서 다른 서버프로그램 처럼 대몬으로 동작하는 것이 아니라 대기하게 하는 방식이 필요합니다. 그런데 코드를 뒤져보니 foreground에서 돌아가게 하는 코드가 존재하더군요.

사실 ./vlmcsd -h 만 해도 뜨는 파라미터입니다.

 

 

그러니까 ./vlmcsd -D 이렇게 명령을 주면 

이렇게 커서가 떨어지지 않고 대기를 하게 됩니다. 

 

이 이야기를 하는 이유는 눈치빠른 분이라면 아실거 같은데 server_main함수를 호출 할때  argv에 "-D"를 넣어야 한다는 겁니다.

그러니까 함수 호출 형태가

 

int argc = 2;

char[][] argv = {"./', "-D"};

server_main(argc, argv);

이렇게 되어야 한다는 겁니다.  argc가 2인 이유는 1번째는 무조건 경로가 들어가야 하므로 추가 파라미터가 2번째부터 들어가야 하기 때문입니다. 아래 argv 보면 첫번째 배열에는 "./"가 들어가 있지요. 그냥 무엇이 들어가야 할지 몰라서 현위치를 지정했습니다.

 

재미없는 이론 시간은 이제 끝났습니다. 이제 진짜로 Android Studio에서 이 코드를 넣어봅시다. Android Studio의 세팅은 생략하겠습니다.

 

Android Studio에서 New Project를 해봅시다.

 

여기서 Native C++을 사용하겠습니다. 

그리고 저는 낡은 인간(?)이므로 JAVA를 사용할 예정입니다. Kotlin이 익숙하신 분은 Kotlin을 쓰시면 됩니다.

 

 

 

그리고 컴파일러는 그냥 기본 툴체인을 사용합니다. 어차피 저희는 표준C를 사용하는 것이니 그냥 편한대로 하면 됩니다.

 

`

이제 기본적인 구조가 완성 되었습니다. 이 기본 구조도 분석해볼 필요는 있는데 굳이 따로 하지는 않겠습니다. 간단하게 C++에서 문자열을 리턴하는 함수를 native-lib.cpp에 만들고 MainActivity에서 native로 해당 함수(stringFromJNI() )를 선언한뒤 onCreate할때 해당 함수를 호출하는 방식입니다.

 

여기에 우리가 만들려고 했던 C소스코드를 넣어봅시다.

https://moordev.tistory.com/440

 

그냥 해보는 C/C++ 프로그램 안드로이드 포팅 -2

이번에는 안드로이드로 포팅하고자하는 C코드의 분석을 해볼 생각입니다.해당 프로그램은 make를 하면 두가지 프로그램이 나옵니다.vlmcsdvlmcs 이중 vlmcsd는 서버이고 vlmcs는 클라이언트입니다.저

moordev.tistory.com

우리는 이전 글에서 어떤 파일이 필요한지 알고 있습니다.

crypto.c
crypto_internal.c
kms.c
endian.c
output.c
shared_globals.c
rpc.c
network.c
helpers.c
vlmcsd.c
kmsdata.c
ifaddrs-android.c

프로젝트경로/app/src/main/cpp/에

위 파일들을 복사합니다.

CMakeList.txr가 있는 곳을 찾아보자

그냥 귀찮으면 

그냥 Visual Studio관련 파일 제외하고 싸그리 복사해도 됩니다. Cmake를 통해 어차피 필요한 파일만 컴파일 할거니까요.

 

 

그러면 AndroidStudio에서 이렇게 파일들이 주르륵 뜨게 됩니다. 사실 다 쓸 필요도 없고 필요한 것만 있으면 되지만 귀찮은 관계로 그냥 복사해서 넣었습니다. 

 

이제 Cmakelist.txt를 수정합시다.

 

여기서 add_library부분을 보시면 native-lib.cpp라고 되어있는것을 볼 수 있습니다.

그  위에 

 

include_directories(${CMAKE_SOURCE_DIR})

이렇게 한줄을 우선 넣어줍시다. 이건 h파일을 찾을 때 소스코드가 있는 폴더를 찾으라는 의미입니다. *.h파일을 참조를 많이 하기 때문에 같이 적어두는 겁니다. 그리고 add_library안에 있는  native-lib.cpp위에 

 

crypto.c
crypto_internal.c
kms.c
endian.c
output.c
shared_globals.c
rpc.c
network.c
helpers.c
vlmcsd.c
kmsdata.c
ifaddrs-android.c

 

이 것을 적어줍니다. 우리에게 필요한 c소스코드 리스트인것이지요.

 

그러면

 

그럼 이제 한번 그냥 컴파일...을 하면 작동 될리가 없지요.

 

다음 글에서는 JAVA코드와 CPP파일 수정으로 해당 프로그램이 동작하도록 코드를 수정해보겠습니다. 

이제 거의 다 왔습니다. 

,

이번에는 안드로이드로 포팅하고자하는 C코드의 분석을 해볼 생각입니다.

해당 프로그램은 make를 하면 두가지 프로그램이 나옵니다.
vlmcsd
vlmcs

 


이중 vlmcsd는 서버이고 vlmcs는 클라이언트입니다.
저희가 쓸건 vlmcsd이므로 이것만 분석을 해볼 생각입니다.

일단 두가지 중 하나만 쓸것이니 모든 소스파일이 필요하지는 않을겁니다. Makefile을 열어서 어떻게 설정되어있나 봅시다.


???

PLEASE USE THE GNI VERSION OF MAKE (gmake) INSTEAD OF make
라고 합니다.

즉 이게 아니고 GNUmakefile을 열어야합니다.



이쪽을 열어서 all: (아무 파라미터없이 make만 쳤을때 실행되는 위치)아래에 보니 -C src라고 되어있습니다. src로 디렉토리를 바꾸란겁니다. 즉, src폴더 안에 있는 GNUmakefile을 읽어서 확인해야합니다.

 



SRCS =로 시작되는 위치를 봅시다.
crypto.c부터 helpers.c까지 이건 기본적으로 필요한 소스파일들 일것 일겁니다.

 


그리고 그 아래에
VLMCSD_SRCS를 보면 $(SRCS)에 vlmsd.c kmsdata.c
두가지 파일이 추가로 되는걸 볼 수있습니다.

 


여기에 아래를 보면 MSRPC어쩌고 있는데 이건 Windows용이니 이걸 제외했을때 network.c rpc.c두가지를 또 추가하는 군요.

 


그리고 Android에선
ifaddr-android.c
이것도 추가해야합니다.
이제 필요한 파일이 뭔지 파악했습니다. 이것이 필요한 이유는 Android Studio에서 CMakeList.txt에 쓸 소스파일을 전부 적어줘야하기 때문입니다.

 

즉 필요한 파일은

        crypto.c
        crypto_internal.c
        kms.c 
        endian.c 
        output.c 
        shared_globals.c
        rpc.c
        network.c
        helpers.c
        vlmcsd.c 
        kmsdata.c
        ifaddrs-android.c

이렇게 되겠습니다.

그럼 이 파일들에서 main함수를 찾아야겠네요.

그런데 c파일에 main이 안 나옵니다. 이럴땐 h파일을 뜯어봐야합니다. vlmcsd.h에 이런 문구가 있군요.


#define server_main main
즉, vlmcsd.c의 server_main(int argc, CARGV argv)함수가 main함수라는 의미였습니다.

이제 엔트리포인트(프로그램 시작부분)을 찾았으니 Android에서 server_main함수를 호출 하게 만들면 되겠군요.

 

일단 이 h파일을 이용하면 server_main함수를 main함수로 치환해 버리니

#if MULTI~~~부터 #else까지 주석처리하고 #endif까지 주석처리해서 없애야 하겠습니다.

 

 

왜냐하면 안드로이드에선 so 라이브러리 형태로 만들어서 자바에서 호출을 할 예정이거든요. 근데 main함수가 따로 있으면 문제가 생기겠지요.

이제 원본파일 구성은 확인했으니 다음글에서는 Android Studio에서 NDK로 Wrapping하는것을 해보겠습니다.

,

C/C++로 짜여진걸 안드로이드로 포팅하려면 우선 기본 프로그램을 so라이브러리 형태로 바꾼뒤 안드로이드의 JNI로 해당 라이브러리를 호출하는 방식으로 만들어집니다.

대신 생각해야하는게 몇 가지 있습니다.

1. 리눅스나 유닉스의 지원이 되는가
2. Xorg등 GUI툴킷에 종속되어있는가

1의 경우는 당연하게도 Windows/Dos만 되는건 애초에 ELF(*.so)형식으로 만들수가 없기 때문입니다. 이건 리눅스나 유닉스용 포팅이 우선되어야합니다.

2는 안드로이드에서는 Xorg나 Wayland가 안 돌아가기 때문입니다. GUI툴킷도 여러종류가 있지만 GTK에 종속된경우 포팅이 까다롭습니다. 그냥 새로짜는게 더 빠른편입니다. Qt의 경우 그나마 낫지만 역시 OS에서 제공하는 기능을 쓸 경우 그것도 대체할 궁리를 해야합니다.

그래서 생각해보다가 주로 CLI와 단일파일로 구성된 서버프로그램을 한번 포팅해보려고 합니다. 서버프로그램은 대부분 백그라운드 대몬형태로 돌아가며 추가입력을 받거나 하는 경우가 거의 없습니다. 한번 실행하면 계속 돌아가기 때문에 그 부분만 신경쓰면 될겁니다.

그래서 결정한게 vlmcsd라 불리는 KMS(MS의 정품인증서버)구현체를 포팅하고자 합니다.
https://github.com/kkkgo/vlmcsd

GitHub - kkkgo/vlmcsd: 🔑Portable open-source KMS Emulator in C

🔑Portable open-source KMS Emulator in C. Contribute to kkkgo/vlmcsd development by creating an account on GitHub.

github.com

순수 C로 짜여져있고 빌드후에 단일파일로 나오게 되는 구조입니다. 그리고 vlmcs라는 클라이언트를 따로 제공하고 있어서 서버의 포팅이 정상적으로 되었는지 확인이 가능합니다.

무엇보다 안드로이드 지원이 이미 고려되어있어서 좀 더 수월 할것 같은것도 한 몫했습니다.
다만 여기서 기본적인 안드로이드 지원은 Termux같은 별도의 안드로이드 내부 터미널에서 쓰는걸 전제로 되어있어서 이래저래 삽질이 필요하긴합니다.

실제로 저장소에서 제공하는 바이너리를 adb shell로 실행하면(실행권한 문제로 /data/local/tmp로 밀어넣어야 합니다.) 실행이 되는걸 볼 수있긴한데 우린 이 방법대신 일반 안드로이드 앱형태로 포팅하는걸 목적으로 하겠습니다.

방법은 아마 이렇게 되겠지요.
1. 기본 프로그램의 main함수를 찾는다.
2. main함수의 이름을 바꾸고 JNI가 지원되도록 형태를 바꾼다.(JNIEXPORT로 시작하는 함수로)
3. 프로그램의 make를 고쳐서 so형태로 나오게 바꾼다.
4. 안드로이드 스튜디오에서 so를 NDK기반 프로젝트에 밀어넣고 위에서 바꾼 main함수를 native로 만든다.
5. 몇몇 부분은 필요에 따라 C구현을 일부고친다.

자바스레드와 이것저것 구현할게 많지만 전체적인 방식은 이와 비슷할겁니다.

,