개발하는 두더지

Android NDK 32bit 64bit abi 와 2GB 이상 파일 지원 본문

Java,Android

Android NDK 32bit 64bit abi 와 2GB 이상 파일 지원

덜지 2018. 8. 13. 11:29

NDK의 경우 LP64 를 사용해서 타입의 크기가 아래와 같다


리눅스 계열에서 x64 컴파일러로 LP64를 사용하고, Visual Studio에서 x64 컴파일러로 LLP64 를 사용한다.

32bit                 64bit (LP64  vs LLP64)

int            4byte                int         4byte
long         4byte                long       8byte      vs     4byte
size_t        4byte                size_t      8byte
pointer     4byte                pointer     8byte
off_t         4byte                 off_t        8byte
long long  8byte              long long     8byte
off64_t      8byte               off64_t        8yte

Target API Set Higher Than Device API

The target API level in the NDK has a very different meaning than targetSdkVersion does in Java. The NDK target API level is your app's minimum supported API level. In ndk-build, this is your APP_PLATFORM setting.


Android에서의 TargetSdkVersion과 NDK에서의 TargetSdkVersion은 의미하는게 서로 다르다.

NDK에서는 최소 API 지원 레벨을 의미한다.



Since references to functions are (typically) resolved when a library is loaded rather than when they are first called, you cannot reference APIs that are not always present and guard their use with API level checks. If they are referred to at all, they must be present.

Problem: Your target API level is higher than the API supported by your device.

Solution: Set your target API level (APP_PLATFORM) to the minimum version of Android your app supports.

Build SystemSetting
ndk-buildAPP_PLATFORM
CMakeANDROID_PLATFORM
Standalone Toolchain--api
GradleTODO: No idea



자바는 long type이 signed 8byte라 범위가 -9223372036854775808 ~ 9223372036854775807이다.

2GB 이상 파일을 지원한다. 하지만 NDK r14 이전에는 2GB이상 파일을 지원하지 않았다.

파일을 생성하고 데이터를 쓰고 핸들을 닫는 함수들은 사용가능하지만,

fseek, ftell 과 같이 파일 포인터를 이동하고, 파일의 크기를 구해오는 함수는 사용할 수 없다.

이유는 c의 long type을 반환하는 함수들인데 long은 32bit 아키텍처에서 4byte 크기이기 때문에

즉, signed int와 크기가 똑같다. 


Linux에서는 2GB 이상 지원하기 위해 컴파일 옵션에 아래 옵션을 주거나 #define FILE_OFFSET_BITS 64 를 정의하여 2GB 파일을 지원할 수 있다.

LOCAL_CFLAGS += -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64


물론 NDK에서도 지원 가능해졌다. 정확히 몇 버전부터 몇 버전까지 위 옵션을 주는 것이 가능한지는 모르겠지만

포스팅 기준 r17 버전에서는 이미 Unifited Header라고 통합헤더가 나왔다. 

통합 헤더가 나온 이유는 버전 별 헤더의 불 일치로 인한 불편함으로 완성되었다고 한다.


어쨌든, Unified Header가 있는 환경에서

#define FILE_OFFSET_BITS 64 을 적용하면 컴파일 에러가 발생한다.

친절하게도 문서에서 아래의 옵션이 정의되어있으면 다 삭제하라고 가이드를 한다.

#if _FILE_OFFSET_BITS == 64
#error "oops, file _FILE_OFFSET_BITS == 64"
#elif defined(__USE_FILE_OFFSET64)
#error "oops, __USE_FILE_OFFSET64 is defined"
#endif



어찌됐건, 32bit, 64bit cpu 아키텍쳐 환경에서 2GB 파일을 지원하려면 off64_t 를 이용해야 될 것 같다.

/* This historical accident means that we had a 32-bit off_t on 32-bit architectures. */
/* See https://android.googlesource.com/platform/bionic/+/master/docs/32-bit-abi.md */
#if defined(__USE_FILE_OFFSET64) || defined(__LP64__)
typedef int64_t off_t;
typedef off_t loff_t;
typedef loff_t off64_t;


보기엔 off_t 와 off64_t가 typedef로 이름만 재정의 한 것인데 size를 찍어보면 32bit 환경에서 4byte, 8byte로 크기가 다르다.

또한 fseeko 는 off_t 타입을 반환 fseek64는 off64_t 를 반환한다. ftello는 off_t 반환하고 ftello64는 off64_t를 반환한다.


문제는 32bit에서 ftello64를 사용하려면 minSdkVersion을 24로 지정해야 된다. 마시멜로우도 아니고 누가부터..

off_t가 4byte가 아닌 8byte가 된다면 낮은 API 레벨로도 사용이 가능한데 아직까지는 어떻게 옵션을 주는지 모르겠다.

직접 정의하는건 컴파일 오류, 컴파일 옵션에 Flag값 설정하는건 옵션 무시를 당한다..



_FILE_OFFSET_BITS=64


Don't set _FILE_OFFSET_BITS=64 if you want to keep the behavior present in old NDKs.
Historically, setting _FILE_OFFSET_BITS=64 in the NDK did nothing. This feature was not present in the deprecated headers at all. With unified headers, the NDK now has up to date headers with support for this feature.
_FILE_OFFSET_BITS=64 is a macro you can define in your application to get support for a 64-bit off_t in 32-bit code. This works by both making off_t 64-bit (by default it is 32-bit in 32-bit code) and by implicitly replacing calls to APIs like lseek with calls to lseek64.
Support for _FILE_OFFSET_BITS=64 was not added to Android in a single release. One API, lseek64, has always been in bionic. Most APIs were added in Lollipop, and a few more were not added until later releases.
If you're targeting a release that does not support the 64-bit off_t variant of a function you are using and have set _FILE_OFFSET_BITS=64, the function will not be available. This is in contrast to the behavior for r15 and r15b (but matches r15c) where the functions were wrongly exposed with a 32-bit off_t that would be silently truncated.
Note that the 64-bit off_t APIs are still available without _FILE_OFFSET_BITS=64 under different names. For example, instead of lseek, call lseek64. Instead of off_t, use off64_t.
Finally, since this feature is new to the NDK with unified headers, if you just want to return to the pre-unified headers behavior, all you need to do is stop setting _FILE_OFFSET_BITS=64







출처

NDK 32 bit abi bugs

Using _FILE_OFFSET_BITS=64 with early api level

Introducing Android native development kit r16

C언어 데이터 모델 간의 정리(LP32 ILP32 LP64 ILP64)

Comments