본문 바로가기

카테고리 없음

리눅스에서 작성된 c코드를 안드로이드에서 컴파일

makecsiparams를 넥서스5에서 사용할 일이 생겼다. 
make 파일에 libs/armeabi 폴더에서 연결된 스마트폰으로 실행 파일 옮기는 부분이 있는 걸 보면 분명 안드로이드랑 같이 사용하는게 맞는데 참 불친절하게도  안드로이드에서 빌드하는 코드를 써놓지 않았다. 시행 착오 거치면서 어찌저찌 성공했다. 
 
1. 운영체제 맞추기
XUbuntu 16.04 LTS desktop amd64.iso 파일을 다운로드 받는다. 
https://cdimage.ubuntu.com/ubuntu-mate/releases/16.04/release/

Ubuntu MATE 16.04.6 LTS (Xenial Xerus)

Select an image Ubuntu-MATE is distributed on two types of images described below. Desktop image The desktop image allows you to try Ubuntu-MATE without changing your computer at all, and at your option to install it permanently later. This type of image i

cdimage.ubuntu.com

 
2. ndk 다운 및 환경 변수 설정
android ndk r11c 버전을 다운로드 받는다. 
https://github.com/android/ndk/wiki/Unsupported-Downloads

Unsupported Downloads

The Android Native Development Kit. Contribute to android/ndk development by creating an account on GitHub.

github.com

 
루트 계정인 상태에서 환경변수를 설정한다. 

export PATH=$PATH:/home/pi/android-ndk-r11c

 
터미널 껐다 켜거나 컴퓨터 껐다가 켜면 다시 설정해줘야한다. 
 
 
3. mk 파일 만들기
 
빌드를 위한 mk파일을 makecsiparams 폴더 경로에 만든다. 
그전에 헤더 파일을 includes 라는 폴더에 옮겨놓는다. 

cp *.h includes/*

 
Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VAR)

LOCAL_SRC_FILES := \
        makecsiparams.c \
        bcmwifi_channels.c
LOCAL_MODULE := makecsiparams
LOCAL_MODULE_FILENAME := makecsiparams

LOCAL_CFLAGS += -std=c99
LOCAL_CFLAGS += -fPIE
LOCAL_LDFLAGS += -fPIE -pie
LOCAL_C_INCLUDES += [현재 경로]/includes

AL_MODEL_PATH := $(TARGET_OUT_OPTIONAL_EXCUABLES)

include $(BUILD_EXECUTABLE)

 
구글 문서를 참고해도 뭐가 뭔지 모르겠다. 에러 출력이랑 nexutil 빌드설정 파일 보고 대충 때려맞췄다. 
https://developer.android.com/ndk/guides/android_mk?hl=ko 

Android.mk  |  Android NDK  |  Android Developers

Android.mk 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 이 페이지는 ndk-build에서 사용하는 Android.mk 빌드 파일의 구문에 관해 설명합니다. 개요 Android.mk 파일

developer.android.com

하다보디 대충 알겠는 것 
 
LOCAL_PATH = $(call my-dir)
include $(CLEAR_VAR) 
무조건 써야하는 것 
 
LOCAL_MODULE := 파일 이름
LOCAL_MODULE_FILENAME := 파일이름
빌드될 파일의 이름을 지정한다. 무조건 써야한다. 왜 두개나 써야하는지 모르겠다. 
 
LOCAL_CFLAGS += -std=c99 
C 표준 라이브러리(stdio.h 같은 아주 기본적인 헤더 파일)이 필요할 때 
무조건 써야한다고 보면 된다. -std=뒤에오는 값은 gnu11 같은걸로 바뀔 수도 있는데 정확하게 모르겠다. 
 
LOCAL_CFLAGS += -fPIE
LOCAL_LDFLAGS += -fPIE -pie
독립실행형 파일로 만들어주는 옵션인 것 같다. 
안쓰면 only position independent executables (pie) are supported 에러가 발생한다. 
그런데 nexutil에는 왜 이 옵션이 없지? 
 
LOCAL_C_INCLUDES = [include 파일 경로]
컴파일할 때 쓸 직접 만든 헤더들의 경로를 지정한다
 
include $(BUILD_EXECUTABLE) 
.so 파일이 아니라 바이너리 파일로 만들고 싶을 때 
 
:= 와 += 의 차이
:= 는 이전값이 지워지고 지금 값이 들어가고 +=는 이전값도 유지되면서 지금 값을 추가할 때 쓰는 것 같다. 
 
Application.mk

APP_FLOATFORM := anroid-20
APP_BUILD_SCRIPT := Android.mk
APP_ABI := armeabi armeabi-v7a x86 //nexus5에서만 사용할거면 aremebi-v7a, x86는 없어도 된다

 
빌드 명령어 

ndk-build NDK_APPLICATION_MK=`pwd`/Application.mk NDK_PROJECT_PATH=`pwd`

 
안타깝게도 빌드가 되지 않고 에러가 뜬다. 
undefind reference to ether_aton
 
 
4. 에러 해결 
 
구글링을 해도 별로 나오는 정보가 없다. 스스로 해결해야한다. 
 
에러 메시지를 해석해보면  ether_aton이 정의되어 있지 않아서 발생했다. 
ether_aton은 표준 C라이브러리 libc의 netient/ether.h에 정의되어있는 함수이다. 문자열을 받아서 48bit 주소로 변환해준다. 
표준 c 라이브러리는 LOCAL_CFLAGS  -std=c99를 하는 걸로 이미 포함시켰는데 왜 에러가 나지? 
 
테스트를 해보니 netinet/ether.h를 포함시키는 것 까지는 에러가나지 않는다. ether_aton 함수를 사용하는 순간 에러가 난다. 
 
구글링을 하다가 ndk 표준 라이브러리에 빠져있는 라이브러리도 있다는사실을 알게되었다. std의 to_string이 지원 안 된다.
https://wingsnote.com/28

Android NDK r9c 에서, std::to_string () 지원 안돼요~

Cocos2d-x 작업과 뗄레야 뗄 수 없는 NDK 빌드... 안드로이드는 기본 어플리케이션 프레임웍이 자바로 되어 있으니, C++을 사용하려면 NDK를 사용할 수 밖에 없죠. 당연히 Cocos2d-x도 안드로이드용으로

wingsnote.com

 
설치되어있는 ndk 폴더를 뒤져보니 정말로 ether_aton 함수가 없다. 그럼 직접 정의해주면 된다. 
구글링해서 ether_aton.c 소스를 찾아서  makecsiparams.c 파일에 붙여넣었다. 
https://android.googlesource.com/platform/bionic/+/master/libc/bionic/ether_aton.c 

libc/bionic/ether_aton.c - platform/bionic - Git at Google

* Copyright (C) 2010 The Android Open Source Project * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * * Redistributions of source code must retain the above copyrigh

android.googlesource.com

 
makecsiparams.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
//#include <netinet/ether.h> 주석처리
#include <net/ethernet.h>
/*
#include <sys/types.h>
#include <sys/socket.h>
#include <net/ethernet.h>
*/
#include "bcmwifi_channels.h"

//ether_aton.c 코드 시작 
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
static inline int
xdigit (char c) {
    unsigned d;
    d = (unsigned)(c-'0');
    if (d < 10) return (int)d;
    d = (unsigned)(c-'a');
    if (d < 6) return (int)(10+d);
    d = (unsigned)(c-'A');
    if (d < 6) return (int)(10+d);
    return -1;
}
/*
 * Convert Ethernet address in the standard hex-digits-and-colons to binary
 * representation.
 * Re-entrant version (GNU extensions)
 */
struct ether_addr *
ether_aton_r (const char *asc, struct ether_addr * addr)
{
    int i, val0, val1;
    for (i = 0; i < ETHER_ADDR_LEN; ++i) {
        val0 = xdigit(*asc);
        asc++;
        if (val0 < 0)
            return NULL;
        val1 = xdigit(*asc);
        asc++;
        if (val1 < 0)
            return NULL;
        addr->ether_addr_octet[i] = (u_int8_t)((val0 << 4) + val1);
        if (i < ETHER_ADDR_LEN - 1) {
            if (*asc != ':')
                return NULL;
            asc++;
        }
    }
    if (*asc != '\0')
        return NULL;
    return addr;
}
/*
 * Convert Ethernet address in the standard hex-digits-and-colons to binary
 * representation.
 */
struct ether_addr *
ether_aton (const char *asc)
{
    static struct ether_addr addr;
    return ether_aton_r(asc, &addr);
}

//ether_aton.c 코드 끝

//원본코드 시작

//...

 
다시 빌드 명령어를 써주면 제대로 컴파일이 된다.

nexus5로 makecsiparams 파일을 옮겨서 실행한 모습