멤버십 기술면접 기간만 되면 늘어나는 방문자 수..

이번에도 어김없이 멤버십 기술 면접 시기가 되면 제 블로그 방문자 수가 두 배 이상 늘어나는군요..
제가 처음 지원했을 때 기술 면접에 대한 경험을 써놓은 글을 찾아오시는가 봅니다.
리퍼러 기록을 보면 “멤버쉽”, “기술면접” 등등으로 구글에서 검색해서 오시는 분들이 많더라구요.
이제 1년반 동안 멤버십 생활을 하고 있는데..
가끔 기술면접에 오신 분들이나 합격해서 들어온 사람들이 제 블로그를 봤다는 이야기를 하면
부끄럽기도 하고.. 기분 좋기도 하고 그럽니다. ㅋㅋ
뭐.. 보셨으면 댓글 하나라도 남겨주시지 하는 맘도 들긴 합니다만..
제 욕심이겠지요. ㅎㅎ
수원 멤버십 기술전형은 그제로 끝이 났으니.. 이후로 오셔서 글을 보실 분들은 아마도 제가 얼굴을 못 볼 듯하네요..
제가 올해까지 멤버십 생활을 할 것 같으니 말입니다. ㅎㅎ

기술전형 보시는 분들 화이팅입니다. 🙂
붙으시면 아마도 신입회원 OT 때 뵙게 될 겁니다.
그 때 블로그 글 잘 봤노라고 인사나 한마디 해주시면 감사하겠네요.^^

Crosstool을 이용한 Softfloat EABI 툴체인 빌드 방법

 안드로이드에서 구동될 커널이나 C/C++ 애플리케이션, 라이브러리 등은 ARM용 EABI와 soft-float을 지원하는 툴체인으로 빌드되어야 합니다. 현재 많이 쓰이는 안드로이드 포팅이나 라이브러리 빌드를 위해 사용하는 툴체인을 실제 구글링해보시면 대부분 Codesourcery 사의 툴체인을 사용 중인 것 같은데요. 저 역시, 처음에는 Codesourcery 사의 2008q3 버전의 툴체인(arm-none-linux-gnueabi-*)을 사용했었습니다. 그런데, 그 툴체인으로 커널을 빌드하니까, 왠 일인지 이미지만 3GB인지, 300MB인지가 나와서 실제 부팅 중 Uncompressing Linux…………….. 메시지에서 재부팅되거나 멈춰버리는 것이었습니다.
 그래서, 자체적으로 툴체인 빌드를 하기 위한 방법을 찾아보았습니다. KLDP 위키의 Android Porting On Real Target 에서 역시 툴체인 빌드를 해서 썼다고 되어 있기 때문이기도 하구요. 일단은 KLDP 위키의 방법과 같은 방법을 사용하기로 하고(저 역시,
“To make life easier,”) 같은 방법인 crosstool과 EABI patch를 이용하기로 했습니다. 다음은 그 방법을 설명합니다.

 일단, http://www.kegel.com/crosstool/ 에서 crosstool 0.43을 다운로드 받고 압축을 해제합니다.
 crosstool의 사용법은 http://www.kegel.com/crosstool/current/doc/crosstool-howto.html 에서 설명하고 있습니다. 약간 설명을 드리자면, 빌드하고자 하는 툴체인의 .sh 파일 안의 TARBALLS_DIR, RESULT_TOP, GCC_LANGUAGES 를 수정해야 하는데, TARBALLS_DIR은 crosstool이 받아올 Source tarball을 저장할 디렉토리이고, RESULT_TOP은 빌드된 최종 툴체인이 저장될 디렉토리의 탑 디렉토리(이 디렉토리는 만들어져 있어야 하고, 빌드하는 유저에게 쓰기 권한이 있어야 합니다.), GCC_LANGUAGES 는 빌드할 툴체인에서 지원할 언어에 대한 것입니다. 파일을 열어보시면 금방 아실 수 있을 겁니다.
 자, 이제 soft-float eabi 패치를 구하여야 합니다. KLDP 위키 글을 따라가보면, Khem Raj의 a glibc 2.5+ nptl build for arm softfloat eabi patch를 적용했다고 나와 있습니다. 링크된 글의 본문에 나와있는 패치를 파일로 저장한 후 아까 받았던 crosstool에 적용시킵니다. 패치를 적용한 후 빌드를 수행하면, 잘 되지 않습니다. follow된 글을 따라가보면 스크립트 안의 "-nounpack" 옵션을 제거하고 다시 해보란 코멘트를 찾아볼 수 있습니다.
 그럼 -nounpack을 제거하고 다시 시도해 봅시다. 그래도 잘 안될텐데, 기억에는 아마도 arm-softfloat.dat 파일 안의 “KERNELCONFIG=`pwd`/arm.config” 내용을 그대로 복사해서 arm-softfloat-eabi.dat 파일 안에 넣어주었던 것 같습니다. 그리고 나서도 저는 안되었는데, 그 이유는 스크립트 내부에서 사용되는 awk를 gawk가 아닌 mawk를 사용해서 발생한 문제였습니다. 우분투 8.04에서 기본으로 깔린 awk가 mawk더군요(제가 진행한 작업은 모두 우분투 8.04 하에서 진행되었습니다.). mawk를 사용하고 싶다면, http://sourceware.org/ml/crossgcc/2007-07/msg00029.html 을 참조하여 패치한 후 진행합니다. 그냥 귀찮다면, 패치 제작자가 권장하는 gawk를 사용하시구요. 우분투에서 기본 awk를 gawk로 바꾸는 방법은 gawk를 내려받아 설치한 후(sudo apt-get install gawk), 기본 awk를 gawk로 설정하시면(sudo update-alternative –config awk:확실하지 않습니다.;;) 될 겁니다.
 아무튼, 이제 빌드하면(당연히 빌드 이전에 demo-arm-softfloat-eabi.sh 파일 안의 세 변수는 적절히 설정되어 있어야 합니다.) 설정되어 있던 디렉토리에 툴체인이 빌드되어 있는 것을 보실 수 있습니다.

 KLDP의 원문을 곱씹으며 읽어볼겸, 영어공부도 할겸, 영어에 익숙하지 않은 개발자를 위해서겸, 겸사겸사, 번역해 보았습니다. 허접한 번역이지만 보실 분은 http://wiki.dasomoli.org/wiki.php/AndroidPortingOnRealTarget/ko 를 보시면 됩니다(우리나라 사람이 쓴 원문을 번역한다는 것이 참 아이러니합니다만..).

우분투 8.04를 고진샤 K800x에서 사용하기

몇 달 전부터 고진샤의 K800X 모델을 사용중입니다.

감도는 떨어지지만 나름 터치스크린도 갖추고 있고, 하드도 120G나 되는 놈입니다.

IDE to USB 젠더를 이용해서 CD롬을 연결하고 거기에 우분투 8.04 CD를 이용해서 Hardy Heron을 설치했습니다.

그런데 지금까지는 터치스크린이 작동은 하는데 제대로 포인트가 잡히지 않아서 있으나 마나였습니다.

Calibration하려고 했었는데 아직 지원이 제대로 안되는 것 같길래 두고 있었습니다만,

어제 밤에 갑자기 생각이 나서 구글에 검색해보니, 런치패드에 버그로 제출된 내용이 있더군요.

어느 사용자가 결국 배포하는 드라이버를 패치해서 사용하는데 성공한 것 같습니다.

이 패치를 사용해서 penmount에서 드라이버를 작성해서 배포 중인 것 같네요.(버전은 비록 Beta2지만요)

설치해서 조금의 삽질을 하니 잘 작동합니다. ㅎㅎ

관련 xorg.conf 내용입니다.

[#M_PenMount 터치스크린 관련 xorg.conf 설정|<<접기>>| Section “InputDevice”
    Identifier    “Configured Mouse”
    Driver        “mouse”
    Option        “CorePointer”
    Option        “Device”    “/dev/input/mouse0”
EndSection

Section “InputDevice”
    Identifier    “Synaptics Touchpad”
    Driver        “synaptics”
    Option        “SendCoreEvents”    “true”
    Option        “Device”        “/dev/psaux”
    Option        “Protocol”        “auto-dev”
    Option        “HorizEdgeScroll”    “0”
EndSection

Section “InputDevice”
    Identifier    “PenMount”
    Driver        “penmount”
    Option        “Protocol”        “PM6000USB”
    Option        “Device”        “/dev/input/event2”
    Option        “PMode”            “1”
    Option        “MinX”            “10”
    Option        “MaxX”            “1000”
    Option        “MinY”            “10”
    Option        “MaxY”            “1000”
    Option        “ADBit”            “10”
    Option        “Baudrate”        “19200”
    Option        “Beep”            “0”        #  0 = no beep, 1 = beep enabled
    Option        “HoldTime”            “700000”
    Option        “SendCoreEvents”    “True”   
EndSection

….

Section “ServerLayout”
    Identifier    “Default Layout”
    Screen        “Default Screen”
    InputDevice    “Synaptics Touchpad”
    #InputDevice    “Penmount” “AlwaysCore”
    InputDevice    “Penmount” “SendCoreEvents”
EndSection
_M#]

/usr/share/penmount/gCalib 16

위와 같은 명령으로 Calibration이 가능합니다. 16 대신 0, 4, 9, 25 와 같은 값을 써서 Calibration point를 몇 개로 할 것인지 정할 수 있습니다.

터치스크린은 OneNote와 같은 프로그램에서 강점을 발휘합니다. 리눅스용 프로그램으로 뭐가 있을까 찾아보니 jarnal 이란 프로그램이 있는 것 같은데, 우분투에서는 그 비슷한 프로그램으로 xournal 이란 프로그램을 패키지로 제공하는군요. 조금 써보니, 프로그램의 문제인지, 원래 포인트가 흔들리는 건지는 알 수 없지만, 선이 좀 삐뚤군요.

한영키 설정은 ~/.Xmodmap 에

keycode 113 = Hangul

추가로 가능합니다.

이제, 여기다 졸업 논문과 진행 중인 프로젝트 관련 몇 가지 설정을 해서 사용해봐야겠습니다. 흐흐..

C++ library를 안드로이드에서 돌리기 위한 삽질

요즘 C/C++ 로 된 OpenCV 라이브러리를 안드로이드 플랫폼 안에 포팅해서 자바 라이브러리로 만드는 과정 중인데.. 순수 C App는 안드로이드 내부의 라이브러리로 빌드/실행이 가능한데 C++ 쪽은 문제를 겪고 있다.

내가 생각한 해결 방법은 그냥 GNU C++ library를 통째로 안드로이드 안으로 이식하려고 생각한 것이었다.

그러나 나같은 삽질을 한 사람이 또 있겠지 싶은 마음에..

여기저기 뒤져보던 중 구글의 Andorid Interanls 그룹에서 여러가지 포스팅 들을 찾을 수 있었는데,

글 들 중 가장 최신의 글은 삽질하지 말고 기다려라. 라는 것이다-_-

좌절이다.. OTL…

그러나 그 이전인 2월의 글 들 중 C++ Application 을 포팅하는데 성공했다는 사람(Susmith M R)이 있다.

이 포스팅(Dynamic Shared Library in Android Another Approach – Don’t Use android Linker)에서 찾을 수 있는데, 안드로이드 내부의 링커(/system/lib/linker)를 사용하지 말라는 이야기이다.

gcc의 옵션으로 -dynamic-linker=/system/lib/linker 대신,

링커 파일(ld-linux-so.6)을 넣고 –dynamic-linker=/system/lib/ld-linux-so.6 옵션으로 컴파일 한다는 건데.

링커 자체가 바뀐다면 추측상으론 DATA 섹션에서 RO 영역과 RW 영역 구분을 없앴던 부분도 수행하지 않고(바꿔 말하면 armelf_linux_eabi.xsc 를 사용하지 않고) 사용할 수 있을 듯도 하다. 음.. 잉? 아닌가? 음.. 생각해봐야겠다..

한번 해봐야겠다.

참고 :

Problem loading C++ library in android


Need little help for JNI


Dynamic Shared Library in Android Another Approach – Don’t Use android Linker

OpenCV의 안드로이드 포팅을 위한 첫단계!

OpenCV의 리눅스 소스를 안드로이드에 포팅하기 위해서 cvCreateImage와 cvReleaseImage 함수가 들어있는 cxcore 라이브러리의 빌드에 들어갔다.

소스의 루트디렉토리에서 ./configure 의 옵션을 조정하여 빌드하는 방법에 실패하고,

직접 cxcore/src 디렉토리 안에서 makefile을 작성하여 빌드하였다.

다음은 빌드할 때 사용한 makefile이다.

.SUFFIXES : .cpp .o

CC = arm-none-linux-gnueabi-gcc
LD = arm-none-linux-gnueabi-ld

INC =
LIBS =
CFLAGS = -I/usr/lib/jvm/java-6-sun/include -I/usr/lib/jvm/java-6-sun/include/linux -I../include -fpic -c
#LDFLAGS = -shared -T armelf_linux_eabi.xsc –dynamic-linker /system/bin/linker -nostdlib -rpath /system/lib -L/home/dasomoli/lib/arm-none-linux-eabi/ -lcv -lcxcore -lml
LDFLAGS = -shared -nostdlib -T /home/dasomoli/src/opencv_bak/opencv-1.0.0/armelf_linux_eabi.xsc -rpath /system/lib -rpath . -L . -L/home/dasomoli/androidinternal/system/lib -lc -lm -lstdc++

OBJS = cxalloc.o cxjacobieigens.o cxpersistence.o cxarithm.o cxlogic.o cxprecomp.o cxarray.o cxlut.o cxrand.o cxcmp.o cxmathfuncs.o cxsumpixels.o cxconvert.o cxmatmul.o cxsvd.o cxcopy.o cxmatrix.o cxswitcher.o cxdatastructs.o cxmean.o cxtables.o cxdrawing.o cxmeansdv.o cxutils.o cxdxt.o cxminmaxloc.o dummy.o cxerror.o cxnorm.o cximage.o cxouttext.o
SRCS = cxalloc.cpp cxjacobieigens.cpp cxpersistence.cpp cxarithm.cpp cxlogic.cpp cxprecomp.cpp cxarray.cpp cxlut.cpp cxrand.cpp cxcmp.cpp cxmathfuncs.cpp cxsumpixels.cpp cxconvert.cpp cxmatmul.cpp cxsvd.cpp cxcopy.cpp cxmatrix.cpp cxswitcher.cpp cxdatastructs.cpp cxmean.cpp cxtables.cpp cxdrawing.cpp cxmeansdv.cpp cxutils.cpp cxdxt.cpp cxminmaxloc.cpp dummy.cpp cxerror.cpp cxnorm.cpp cximage.cpp cxouttext.cpp

TARGET = libcxcore.so

all : $(TARGET)

$(TARGET) : $(OBJS)
    $(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)

$(OBJS) : $(SRCS)
    $(CC) $(CFLAGS) $(SRCS)

dep :
    gccmakedep $(INC) $(SRCS)

clean :
    rm -rf $(OBJS) $(TARGET) core

new :
    $(MAKE) clean
    $(MAKE)

CFLAGS의 옵션 중 -I/usr/lib/jvm/java-6-sun/include와 -I/usr/lib/jvm/java-6-sun/include/linux 옵션은 구글 안드로이드 Application 개발시 사용한 java 6 라이브러리의 include 경로이고, ../include 는 실제 $OPENCV_HOME/cxcore/include 이다. -c 옵션으로 *.o 파일을 생성한다.

물론, 이전에 javah로 구글 안드로이드 Application의 JNI를 사용하는 클래스의 C/C++용 헤더 파일(org_swssm_NativeOpenCV.h)이 생성되어져 있었고, 헤더 파일 안의 함수는 cxarray.cpp 함수 안에 구현되어져 있었다. 당연히 cxarray.cpp 에는 #include “org_swssm_NativeOpenCV.h” 가 들어가 있다. cpp 안의 함수 안에서 클래스를 env->FindClass 로 찾을 때 org.swssm.andCvSize 와 같이 클래스의 전체 경로를 적어주어야 한다는 점에 주의하자.

LFLAGS의 옵션 중 -T /home/dasomoli/src/opencv_bak/opencv-1.0.0/armelf_linux_eabi.xsc는 이전에 언급했던 툴체인 내의 ldscript의 수정본이다. 또한, -nostdlib 는 툴체인의 기본 라이브러리를 사용하지 않겠다는 옵션으로 보인다. 또, -rpath /system/lib -rpath . 는 라이브러리 실행 시 참조할 라이브러리 경로로써 안드로이드의 라이브러리 폴더인 /system/lib와 라이브러리가 들어가 있는 곳의 경로를 참조하도록 한다. -L . -L/home/dasomoli/androidinternal/system/lib 는 링킹 타임에 참조할 라이브러리가 들어가 있는 경로로 /home/androidinternal/ 아래에 Benno의 System image 가 압축 해제되어 있다. 이는 안드로이드에서 실제 사용되는 library를 링킹 시 참조하여 링킹하도록 한다. -lc -lm -lstdc++ 는 각각 안드로이드 /system/lib 의 libc.so, libm.so, libstdc++.so 를 참조하도록 한다.

make가 성공적으로 이루어지기 위해서는 OpenCV의 cxcore 라이브러리 내의 소스들에 약간의 수정이 필요한데, 이는 안드로이드 내부에서 사용되는 라이브러리들에서 지원되지 않는 함수(혹은, 내가 제대로 링크시키지 못했거나..;;)들을 제거해야 하기 때문이다. 대표적인 함수들로 assert 함수가 있으므로 include안의 #include <assert.h>를 주석처리 하고, cxtypes.h 내의 assert 문들이 사용된 부분을 삭제하도록 한다. 추가적으로 #define assert(x) (x) 하여 bool 처리만 하도록 처리하기도 하였다.

make clean; make를 마치면 libcxcore.so 파일이 생성되는데 이를 안드로이드 에뮬레이터의 /system/lib 안에 넣어주도록 한다.(adb push libcxcore.so /system/lib/)

안드로이드 플랫폼 내부에서의 libcxcore.so 파일의 동작여부를 확인하기 위해서 별도의 간단한 application을 build하여 테스트하도록 한다. 먼저 main.c 를 작성한다.

#include <stdio.h>
#include “cxcore.h”

int main(void)
{
    IplImage *pImage;
    CvSize size;

    size.width = 100;
    size.height = 100;

    pImage = cvCreateImage(size, 8, 3);

    cvReleaseImage(&pImage);

    return 0;
}

cvCreateImage와 cvReleaseImage 함수만을 사용하여 동작하는 지를 살펴본다.

main.c의 구동을 위해서 함수의 entry point가 되는 _start 함수를 구현한다. 이는 http://honeypod.blogspot.com/2007/12/dynamically-linked-hello-world-for.html 에서 사용한 start.c 파일을 그대로 사용하였다.(http://honeypod.blogspot.com/2007/12/initialize-libc-for-android.html의 _start 함수를 어셈블러로 구현하여 사용할 수도 있을 것 같다.)

#include <stdlib.h>
extern int main(int argc, char **argv);

void _start(int argc, char **argv)
{
        exit (main (argc, argv));
}

기억상으로는 start.o 는 arm-none-linux-gnueabi-gcc -c start.c 하여 그대로 사용했던 듯 하다.
main.o 는 arm-none-linux-gnueabi-gcc -c -I../include main.c 와 같이 하여 cxcore의 include 경로를 추가한 후 컴파일 하여 생성한다.
중요한 cxcoretest라는 간단한 Executable 생성은 다음의 옵션으로 한다.

arm-none-linux-gnueabi-ld –dynamic-linker /system/bin/linker -nostdlib -rpath /system/lib -rpath . -L . -L ~/androidinternal/system/lib -lc -lcxcore -lstdc++ -lm -ldl -o cxcoretest start.o main.o

shared library 생성시와는 다르게 armelf_linux_eabi.xsc 를 사용하지 않음에 주의한다. 이를 안드로이드 에뮬레이터에 넣고(adb push cxcoretest /data/) 실행해본다.

# ./cxcoretest
WARNING: `libcxcore.so` is not a prelinked library
#

실행은 잘 되는 것을 볼 수 있다. 근데, 제대로 호출된 것 맞는건가? 확인해보자. Benno의 strace를 받아 안드로이드에 넣고(adb push strace /data/), strace로 실행(./strace ./cxcoretest)해보자!

execve(“./cxcoretest”, [“./cxcoretest”], [/* 10 vars */]) = 0
getpid()                                = 1662
syscall_983045(0xb0015cb0, 0xb00128d8, 0x3d4, 0, 0xbeddddc8, 0x1, 0, 0xf0005, 0xb00128d8, 0, 0, 0xbeddddc4, 0, 0xbedddd78, 0xb0000dd9, 0xb00016fc, 0x10, 0xb0015cb0, 0, 0, 0xc764, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) = 0
gettid()                                = 1662
sigaction(SIGILL, {0xb0001471, [], SA_RESTART}, {SIG_DFL}, 0) = 0
sigaction(SIGABRT, {0xb0001471, [], SA_RESTART}, {SIG_DFL}, 0) = 0
sigaction(SIGBUS, {0xb0001471, [], SA_RESTART}, {SIG_DFL}, 0) = 0
sigaction(SIGFPE, {0xb0001471, [], SA_RESTART}, {SIG_DFL}, 0) = 0
sigaction(SIGSEGV, {0xb0001471, [], SA_RESTART}, {SIG_DFL}, 0) = 0
sigaction(SIGSTKFLT, {0xb0001471, [], SA_RESTART}, {SIG_DFL}, 0) = 0
open(“libc.so”, O_RDONLY|O_LARGEFILE)   = -1 ENOENT (No such file or directory)
open(“/system/lib/libc.so”, O_RDONLY|O_LARGEFILE) = 3
lseek(3, -8, SEEK_END)                  = 241860
read(3, “\0\0\340\257PRE “, 8)          = 8
mmap2(0xafe00000, 245760, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0xafe00000
close(3)                                = 0
mmap2(0xafe3c000, 45056, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0) = 0xafe3c000
mprotect(0xafe00000, 233472, PROT_READ|PROT_EXEC) = 0
open(“libcxcore.so”, O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open(“/system/lib/libcxcore.so”, O_RDONLY|O_LARGEFILE) = 3
lseek(3, -8, SEEK_END)                  = 2199920
read(3, “veSlice\0”, 8)                 = 8
fstat64(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 0), …}) = 0
brk(0)                                  = 0x13000
brk(0x13000)                            = 0x13000
brk(0x14000)                            = 0x14000
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo …}) = 0
write(1, “WARNING: `libcxcore.so` is not a”…, 51WARNING: `libcxcore.so` is not a prelinked library
) = 51
lseek(3, 0, SEEK_END)                   = 2199928
mmap2(0x80100000, 2203648, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x80100000
close(3)                                = 0
open(“libm.so”, O_RDONLY|O_LARGEFILE)   = -1 ENOENT (No such file or directory)
open(“/system/lib/libm.so”, O_RDONLY|O_LARGEFILE) = 3
lseek(3, -8, SEEK_END)                  = 133184
read(3, “\0\0\300\257PRE “, 8)          = 8
mmap2(0xafc00000, 135168, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0xafc00000
close(3)                                = 0
mprotect(0xafc00000, 131072, PROT_READ|PROT_EXEC) = 0
open(“libstdc++.so”, O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open(“/system/lib/libstdc++.so”, O_RDONLY|O_LARGEFILE) = 3
lseek(3, -8, SEEK_END)                  = 4144
read(3, “\0\0\320\257PRE “, 8)          = 8
mmap2(0xafd00000, 8192, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0xafd00000
close(3)                                = 0
mprotect(0x80100000, 1986560, PROT_READ|PROT_EXEC) = 0
brk(0)                                  = 0x14000
brk(0x14000)                            = 0x14000
brk(0x15000)                            = 0x15000
brk(0x16000)                            = 0x16000
mprotect(0, 0, PROT_READ|PROT_EXEC)     = 0
brk(0x1d000)                            = 0x1d000
exit_group(0)                           = ?
Process 1662 detached

음… 봐도 잘 모르겠다.. 잘 안되는 것 같기도 하고… -_-;;;

흥미로운 점은, 빨간 색으로 표시한 부분을 보면 알겠지만, 사용된 안드로이드에서 제공하는 모든 library의 마지막 4 바이트에는 “PRE “가 들어가있다.

일단 JNI를 사용하는 Application을 사용해서 죽는지부터 보자-_-;;;

Call 버튼을 눌러보자!

사용자 삽입 이미지
오! 안죽는다! 안죽는게 뭐가 대단하냐고? ..당신은 삽질을 덜 한 것이다-_-+

그럼 cvCreateImage가 리턴하는 포인터의 값을 int로 찍어보자. 일단 값을 1234로 넣어두고 리턴하는 값을 받은 후에 찍어보자. 이왕 하는 김에 Object로 리턴하는 andCvSize 클래스의 값도 찍어보자!

                    int pImage = 1234;
                    Log.i(LOG_TAG, “pImage = ” + pImage);
                    pImage = NativeOpenCV.andcvCreateImage(cvSize, 8, 3);
                   
                    Log.i(LOG_TAG, “after call, pImage = ” + pImage);

                    andCvSize cvSize2 = NativeOpenCV.andcvGetSize(pImage);
                   
                    Log.i(LOG_TAG, “cvSize2.width : ” + cvSize2.width + “, cvSize2.height : ” + cvSize2.height);
                   
                    NativeOpenCV.andcvReleaseImage(pImage);

adb logcat

I/CallOpenCV( 1830): pImage = 1234
D/dalvikvm( 1830): LOADING path /system/lib/libcxcore.so 0x4006ddb0
I/dalvikvm( 1830): Added shared lib /system/lib/libcxcore.so 0x4006ddb0
I/dalvikvm( 1830): No JNI_OnLoad found in /system/lib/libcxcore.so 0x4006ddb0
D/dalvikvm( 1830): +++ not scanning ‘/system/lib/libwebcore.so’ for ‘andcvCreateImage’ (wrong CL)
D/dalvikvm( 1830): +++ not scanning ‘/system/lib/libmedia_jni.so’ for ‘andcvCreateImage’ (wrong CL)
I/CallOpenCV( 1830): after call, pImage = 1459360
D/dalvikvm( 1830): +++ not scanning ‘/system/lib/libwebcore.so’ for ‘andcvGetSize’ (wrong CL)
D/dalvikvm( 1830): +++ not scanning ‘/system/lib/libmedia_jni.so’ for ‘andcvGetSize’ (wrong CL)
I/CallOpenCV( 1830): cvSize2.width : 100, cvSize2.height : 100
D/dalvikvm( 1830): +++ not scanning ‘/system/lib/libwebcore.so’ for ‘andcvReleaseImage’ (wrong CL)

D/dalvikvm( 1830): +++ not scanning ‘/system/lib/libmedia_jni.so’ for ‘andcvReleaseImage’ (wrong CL)

오오! 잘된다! 한번 더!

I/CallOpenCV( 1830): onClick
I/CallOpenCV( 1830): calling native method
I/CallOpenCV( 1830): pImage = 1234

I/CallOpenCV( 1830): after call, pImage = 1459360


I/CallOpenCV( 1830): cvSize2.width : 100, cvSize2.height : 100

잉? 포인터 값이 같네? 제대로 된 거 맞나? Release했던 부분을 놔뒀다가 다시 사용하나? cvReleaseImage를 주석처리 하고 계속 할당하는지 보자!

I/CallOpenCV( 1889): pImage = 1234
D/dalvikvm( 1889): LOADING path /system/lib/libcxcore.so 0x40060948
I/dalvikvm( 1889): Added shared lib /system/lib/libcxcore.so 0x40060948
I/dalvikvm( 1889): No JNI_OnLoad found in /system/lib/libcxcore.so 0x40060948
D/dalvikvm( 1889): +++ not scanning ‘/system/lib/libwebcore.so’ for ‘andcvCreateImage’ (wrong CL)
D/dalvikvm( 1889): +++ not scanning ‘/system/lib/libmedia_jni.so’ for ‘andcvCreateImage’ (wrong CL)
I/CallOpenCV( 1889): after call, pImage = 1460544
D/dalvikvm( 1889): +++ not scanning ‘/system/lib/libwebcore.so’ for ‘andcvGetSize’ (wrong CL)
D/dalvikvm( 1889): +++ not scanning ‘/system/lib/libmedia_jni.so’ for ‘andcvGetSize’ (wrong CL)
I/CallOpenCV( 1889): cvSize2.width : 100, cvSize2.height : 100
I/CallOpenCV( 1889): onClick
I/CallOpenCV( 1889): calling native method
I/CallOpenCV( 1889): pImage = 1234
I/CallOpenCV( 1889): after call, pImage = 1490752
I/CallOpenCV( 1889): cvSize2.width : 100, cvSize2.height : 100

후후후.. 포인터 값이 계속 바뀌는군.. 그럼 계속 할당되는 게 맞겠지 ㅋㅋㅋ

그럼 성공!!! 으하하하하하하!

참고 :
http://groups.google.com/group/android-developers/browse_thread/thread/b3376922f5b7a93a
http://groups.google.co.jp/group/android-developers/browse_thread/thread/ed437a61e678aab8
http://benno.id.au/blog/2007/11/13/android-native-apps
http://honeypod.blogspot.com/2007/12/shared-library-hello-world-for-android.html
http://honeypod.blogspot.com/2007/12/dynamically-linked-hello-world-for.html
http://benno.id.au/blog/
http://honeypod.blogspot.com/2007/12/initialize-libc-for-android.html
http://davanum.wordpress.com/2007/12/09/android-invoke-jni-based-methods-bridging-cc-and-java/
http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/jniTOC.html

JNI library 참조 경로를 설정하고 싶다면..

java 실행시 argument 로

-Djava.library.path=/home/dasomoli/workspace/JNIOpenCVTest/bin

와 같이 써넣어 주시면 됩니다. 그러니까 이렇게요..

java -Djava.library.path=./ JNIOpenCVTest

저 자바 프로젝트에서 사용하는 library 파일은 .class 파일과 같은 경로에 들어가 있습니다. 그래서 “./” 로 준 것이죠!

이클립스라면 Run -> Open Run Dialog… 에서 해당 프로젝트의 Argument의 VM arguments: 에 위와 같이 써주시거나

혹은

프로젝트의 속성에서 Java Build Path 탭의 Libraries 아래의 JRE System Library 아래 항목중 Native library location: 에 경로를 추가해 주시면 됩니다.

Reference : eclipse와 gcc를 이용한 native 만들기(http://eahn.tistory.com/19)

우분투 8.04(Hardy Heron)에서 Eclipse 사용시..

우분투 8.04에서 Eclipse 사용 시 다음과 같은 메시지가 뜬다면!

Could not initialize the application’s security component. The most
likely cause is problems with files in your application’s profile
directory. Please check that this directory has no read/write
restrictions and your hard disk is not full or close to full. It is
recommended that you exit the application and fix the problem. If you
continue to use this session, you might see incorrect application
behaviour when accessing security features.

$HOME/.mozilla/eclipse 폴더를 수동으로 만들어 주시면 됩니다.

출처 : https://bugs.launchpad.net/ubuntu/+source/eclipse/+bug/188380

JNI + Android

JNI 사용 구조는 다음 그림으로 요약할 수 있습니다.

Android에서 사용하는데 필요한 것은 5 단계에서 Cross Compiler로 컴파일 되어야 한다는 것입니다.

그리고 컴파일 된 결과가 “.DLL”이 아니라 “.so” 파일로 나온다는 거겠죠.

Cross Compiler는 http://www.codesourcery.com/gnu_toolchains/arm/download.html 에서 구할 수 있습니다.

그런데 gcc의 -shared 옵션으로 .so 파일을 만들고 실행해보면, 사용될 때 App가 죽어버립니다.

이는 precompiled-library에 관련된 문제로 보이는데, 이를 위해서 컴파일 단계의 ldscript를 수정해서 사용합니다.

http://honeypod.blogspot.com/2007/12/shared-library-hello-world-for-android.html 를 참고하면 다음과 같은 부분을 볼 수 있습니다.

Now, the default linker script need to be modified. The default
linker script is available at
$toolchains_home/arm-none-linux-gnueabi/lib/ldscripts/armelf_linux_eabi.xsc.
Copy it to the current directory. Comment out three lines and add one
line replacing the first commented out line.

/* . = ALIGN (CONSTANT (MAXPAGESIZE)) – ((CONSTANT (MAXPAGESIZE) – .) & (CONSTANT (MAXPAGESIZE) – 1)); . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); */
. = .; /* this line replaces the line above */

/* . = DATA_SEGMENT_RELRO_END (0, .); */

/* . = DATA_SEGMENT_END (.); */

$toolchains_home/arm-none-linux-gnueabi/lib/ldscripts/ 경로에 있는 armelf_linux_eabi.xsc 파일을 수정해야 한다는 이야기입니다.

수정한 파일을 사용해서 다음 명령으로 컴파일하면 실제 안드로이드에서 동작하는 라이브러리를 만들 수 있습니다.

arm-none-linux-gnueabi-gcc -I/usr/lib/jvm/java-6-sun/include -I/usr/lib/jvm/java-6-sun/include/linux -fpic -c <JNI 함수 구현소스>.c

arm-none-linux-gnueabi-ld -T armelf_linux_eabi.xsc -shared -o <library명>.so <Object파일>.o

그리고 emulator 실행 후 다음 명령으로 에뮬레이터에서 실행가능합니다.

adb push <라이브러리>.so /system/lib
adb install <JNI를 사용하는 Android APP>.apk

내가 생각하는 KLDP의 장점 중 한가지..

간단한 질문에도 생각해 볼 수 있는 좋은 답변들과 논쟁들이 많이 달린다는 것이다.

JNI의 C/C++에서의 포인터 관련 처리를 어떻게 하는지 찾아보려고 무작정 검색하던 중

KLDP의 글 하나가 걸려들었다.

바로 이 글!

자바에서의 포인터는 있는가? 라는 논쟁이 나오고, 포인터는 그냥 개념에 불과하다는 이야기,

반대로 자바에는 포인터가 없다는 이야기, 포인터를 레퍼런스의 일종으로 보아야 된다는 이야기까지…

이런 답글을 살펴보다 보면, 내 자신이 알고 있는 지식이 얼마나 보잘 것 없는지..

그리고, 정확한 용어의 사용과 이해가 얼마나 중요한 것인지 생각해보게 된다.

교수님들이 말씀하시던,

용어의 의미를 정확히 알고 있어야 하고, 그 의미를 정확히 이해해야 한다는 말씀에 고개가 끄덕여진다.

아무튼 실제 글보다 댓글에서 줄줄이 캐어내듯 뭔가가 나오는 것이 KLDP의 한 장점이 아닐까 한다. ㅋㅋ