2015年12月8日火曜日

Android NDKのatof()問題

AndroidにはCランタイムライブラリのatof()関数がある機種とない機種があります。
正確にいうとatof()関数がインライン展開される機種とされない機種があります。

これらの関数を使うプログラムでは、ランタイムライブラリとNDKのバージョンが合わず動かないことがあります。

D/dalvikvm(18948): Trying to load lib /data/app-lib/com.yomei.ndkshell/libndkshell_.so 0x41ed4ba8
E/dalvikvm(18948): dlopen("/data/app-lib/com.yomei.ndkshell/libndkshell_.so") failed: Cannot load library: soinfo_relocate(linker.cpp:975): cannot locate symbol "atof" referenced by "libndkshell_.so"...
W/System.err(18948): java.lang.UnsatisfiedLinkError: Cannot load library: soinfo_relocate(linker.cpp:975): cannot locate symbol "atof" referenced by "libndkshell_.so"...
W/System.err(18948):    at java.lang.Runtime.loadLibrary(Runtime.java:371)
W/System.err(18948):    at java.lang.System.loadLibrary(System.java:535)
W/System.err(18948):    at com.yomei.SomeJni.<clinit>(SomeJni.java:8)
W/System.err(18948):    at com.yomei.SomeMain.main(SomeMain.java:283)
W/System.err(18948):    at com.yomei.SomeMain$MainActivity$1.run(SomeMain.java:357)

こんな風に怒られてしまいます。

この問題を解決するために、ランタイムライブラリ側ではatofを必ず持っていないといけなく、NDK側ではインライン展開されるヘッダーファイルでないといけないのですが、一部の古いランタイムライブラリと最近のNDKの組み合わせではそうなっていない・・・。
Google自身も64ビット化の対応で大変でもう32ビットのルールのことを忘れてるでしょ?


ネットで解決方法を調べてみたのですが、どれもまだ正しくないというかなんというか。
すべての機種で動かいないのです。

ということで、問題を分析して解決方法を考えてみました。
わかってしまえば、解決方法はすごい簡単なのですが、自分のsoファイルにatof()関数を追加すればよいだけです。こんな感じ。

atof_.c
------------------------------
#include <stdio.h>
#include <ctype.h>
#include "atof_.h"

#ifdef ANDROID
double atof(char *s)
#else
double atof__(char *s)
#endif
{
double a = 0.0;
int e = 0;
int c;
while ((c = *s++) != '\0' && isdigit(c)) {
a = a*10.0 + (c - '0');
}
if (c == '.') {
while ((c = *s++) != '\0' && isdigit(c)) {
a = a*10.0 + (c - '0');
e = e-1;
}
}
if (c == 'e' || c == 'E') {
int sign = 1;
int i = 0;
c = *s++;
if (c == '+')
c = *s++;
else if (c == '-') {
c = *s++;
sign = -1;
}
while (isdigit(c)) {
i = i*10 + (c - '0');
c = *s++;
}
e += i*sign;
}
while (e > 0) {
a *= 10.0;
e--;
}
while (e < 0) {
a *= 0.1;
e++;
}
return a;
}

------------------------------


atof_.h
------------------------------
#ifndef ATOF__H_
#define ATOF__H_


#ifdef __cplusplus
extern "C" {
#endif


double atof(char *s);
double atof__(char *s);


#ifdef __cplusplus
}
#endif


#endif
------------------------------

これで自作アプリでatof()関数を使用しても最新のNDKと古い32ビットの機種の組み合わせでも動きます。


0 件のコメント:

コメントを投稿