Former title was: crash on vsprintf starting from Android12 (api >= 31)
My Android app uses a native library (libexif) built with NDK. At some time in my native code (adapted from exif), I call the vsprintf function which makes the app crash.
Formerly in exif, it was a call to vfprintf (stderr, format, args); that I replaced by vsprintf so as to store the string for later use.
static void
log_func (ExifLog *log, ExifLogCode code, const char *domain,
const char *format, va_list args, void *data)
{
char dest[1024] = {0, };
vsprintf(dest, format, args);
}
The message (as per the output from an API where it works) should be:
The tag 'ComponentsConfiguration' contains an invalid number of components (3, expected 4).
The format variable contains: "The tag '%s' contains an invalid number of components (%i, expected %i)."
I couldn't check the values of the arguments (haven't found how to print them to check their values).
This works without any problems up to API30 (on the emulator).
On an image using API31 it crashes at the call of the vsprintf function.
Updating NDK to 25b doesn't fix either.
My API30 image is x86, the API31 image is x86_64.
Any idea to fix/workaround this?
Other parts of code that may be of interest:
#ifndef NO_VERBOSE_TAG_STRINGS
static void
exif_entry_log (ExifEntry *e, ExifLogCode code, const char *format, ...)
{
va_list args;
ExifLog *l = NULL;
if (e && e->parent && e->parent->parent)
l = exif_data_get_log (e->parent->parent);
va_start (args, format);
exif_logv (l, code, "ExifEntry", format, args);
va_end (args);
}
#else
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define exif_entry_log(...) do { } while (0)
#elif defined(__GNUC__)
#define exif_entry_log(x...) do { } while (0)
#else
#define exif_entry_log (void)
#endif
#endif
#define CC(entry,target,v,maxlen) \
{ \
if (entry->components != target) { \
exif_entry_log (entry, EXIF_LOG_CODE_CORRUPT_DATA, \
_("The tag '%s' contains an invalid number of " \
"components (%i, expected %i)."), \
exif_tag_get_name (entry->tag), \
(int) entry->components, (int) target); \
break; \
} \
}
// Then this call later
CC (e, 4, val, maxlen);
Update:
In the meantime since API30 images have x86 arch, and API31 images are x86_64 I just tried API30 with a x86_64. For now:
API 30 x86 → no crash in app
API 30 x86_64 → crash in app
API 31 x86_64 → crash in app
So this looks like it is x86 vs x86_64 emulator image related.
On a real arm8 device, there is no crash either.
I also found that the args passed to vsprintf don't have the expected values. And If a simulate vsprintf with the expected values, vsprintf works fine. So the problem is likely not vsprintf.
The cause of the problem was that I was using args twice: Once in vsprintf and once in vsprintf.
This is now allowed according to https://stackoverflow.com/a/10807375/15401262 and leads to undefined behavior.
It can't be used twice and one has to use va_copy in that case.
Related
I am using pjsip library through android ndk. I used Android NDK 16 to compile pjsip. One user (HTC One, Android 5.0.2) faced a lot of crashes while trying to start our project. This is the error:
Fatal Exception: java.lang.UnsatisfiedLinkError
dlopen failed: cannot locate symbol "stdin" referenced by "libpjsua2.so"...
... .PjSipApp.<clinit>
In the project we have .so-files for 4 abis, as stated in google android ndk official docs: arm64-v8a, armeabi-v7a, x86, x86_64.
Here is how I load libraries in code:
public class PjSipApp {
private static final String TAG = "Pjsua-PjSipApp";
static {
try {
System.loadLibrary("openh264");
System.loadLibrary("yuv");
} catch (UnsatisfiedLinkError ignore) {
}
System.loadLibrary("pjsua2");
Log.v(TAG, "LibraryLoaded");
}...
So System.loadLibrary("pjsua2"); throws this UnsatisfiedLinkError.
Maybe anyone knows, what is the reason for this?
You probably compiled binaries with a higher API Level.
This is how they are defined in stdio.h
#if __ANDROID_API__ >= 23
extern FILE* stdin __INTRODUCED_IN(23);
extern FILE* stdout __INTRODUCED_IN(23);
extern FILE* stderr __INTRODUCED_IN(23);
/* C99 and earlier plus current C++ standards say these must be macros. */
#define stdin stdin
#define stdout stdout
#define stderr stderr
#else
/* Before M the actual symbols for stdin and friends had different names. */
extern FILE __sF[] __REMOVED_IN(23);
#define stdin (&__sF[0])
#define stdout (&__sF[1])
#define stderr (&__sF[2])
#endif
So, if you built for API level >= 23, the symbol will resolve to File* stdin which doesn't exist in API < 23. I'd suggest to reduce the API level.
I have the following macro in a logging library of mine :
#define TRACE_E(__logCat,__format,...) \
do { \
::dbg::LogCategory * const __catPtrVal = (::dbg::LogCategory *)(__logCat); \
if( NULL != __catPtrVal && __catPtrVal->IsEnabled() ) \
{ \
__catPtrVal->Error( __format, __VA_ARGS__ ); \
} \
} while( false )
Under Visual Studio (2008) it works as intended, i.e i can do both TRACE_E( pLog, "some message without parameters" ); and TRACE_E( pLog, "some message with parameters %d %d", 4, 8 );
But when using this same library with eclipse and the Android NDK i'm getting a compilation error if i don't pass at least one parameter after the format string in my macro, i.e TRACE_E( pLog, "some message without parameters" ); is not valid, but TRACE_E( pLog, "some message without parameters", 0 ); is, which forces me to pass a dummy parameter when none is needed.
Is there any difference of behaviour with variadic macros when using g++ rather than Visual Studio's compiler ? Thank you.
Yes. What you are attempting is not possible in standard C or C++.
This is arguably a defect in the respective standards for which different compilers have different workarounds. Visual Studio tries to make it work as-is, gcc and clang require the following syntax:
__catPtrVal->Error( __format, ##__VA_ARGS__ );
This is described here for gcc; clang just adopted gcc's way of doing things. Unfortunately, MSVC does not understand this syntax. There is, to my knowledge, no portable way of solving this in the general case.
For your particular macro, though, you could simply write
#define TRACE_E(__logCat,...) \
do { \
::dbg::LogCategory * const __catPtrVal = (::dbg::LogCategory *)(__logCat); \
if( NULL != __catPtrVal && __catPtrVal->IsEnabled() ) \
{ \
__catPtrVal->Error(__VA_ARGS__ ); \
} \
} while( false )
Since the only place where you use __format is directly before __VA_ARGS__.
Side note: You're using a lot of reserved identifiers there. Unless you're writing a standard library implementation, you should go easier on the underscores.
I am building ffmpeg and stuck in an unusual spot. Inside libavutil we have float_dsp.h and float_dsp.c files. Inside these file there is a declaration of a methond which is:
void (*butterflies_float)(float *av_restrict v1, float *av_restrict v2, int len);
float (*scalarproduct_float)(const float *v1, const float *v2, int len);
when building and compiling this file i get this error and have no idea what to do. I think problem is somewhere else but again have no idea at all. Error is
jni/ffmpeg/libavcodec/../libavutil/float_dsp.h:150: error: expected ';', ',' or ')' before 'v1'
jni/ffmpeg/libavcodec/../libavutil/float_dsp.h:161: error: expected ';' before 'float'
Anybody who wants to help please take a step forward because i have not really got any support on ffmpeg during couple of weeks now.
Regards
This may be caused by:
not set GCC to C99 mode where restrict keyword is supported
restrict was redefined by some #define
restrict is not supported for particular architecture but this is unlikely
GCC is set to C++ mode where restrict keyword is not supported GCC according http://gcc.gnu.org/onlinedocs/gcc/Restricted-Pointers.html.
There are other supported forms by GCC like __restrict and __restrict__.
ffmpeg configure script sets macro av_restrict to just restrict which can be checked in produced config.h file.
Fix could be to change configure detection code and remove invalid case restrict:
--- ./configure.orig 2014-01-15 18:53:59.000000000 +0100
+++ ./configure 2014-03-13 17:50:45.754442028 +0100
## -3896,7 +3896,7 ##
EOF
_restrict=
-for restrict_keyword in restrict __restrict__ __restrict; do
+for restrict_keyword in __restrict__ __restrict; do
check_cc <<EOF && _restrict=$restrict_keyword && break
void foo(char * $restrict_keyword p);
EOF
Here is simple binarization function
void binarize(void *output, const void *input, int begin, int end, uint8_t threshold) {
#ifdef __ARM_NEON__
uint8x16_t thresholdVector = vdupq_n_u8(threshold);
uint8x16_t highValueVector = vdupq_n_u8(255);
uint8x16_t* __restrict inputVector = (uint8x16_t*)input;
uint8x16_t* __restrict outputVector = (uint8x16_t*)output;
for ( ; begin < end; begin += 16, ++inputVector, ++outputVector) {
*outputVector = (*inputVector > thresholdVector) & highValueVector;
}
#endif
}
It works fine on iOS. However when I'm compiling it for Android it gives me an error:
error: invalid operands of types 'uint8x16_t {aka __vector(16)
__builtin_neon_uqi}' and 'uint8x16_t {aka __vector(16) __builtin_neon_uqi}' to binary 'operator>'
I use this flag in Android.mk to enable NEON:
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_ARM_NEON := true
endif
The difference comes because of different compilers. For iOS you are compiling with Clang but for Android you are building the code with GCC (unless you override the defaults).
GCC is much more stupid about vector types and could not use them with C/C++ operators like > or &. So you have two basic options:
Try to compile with Clang from the latest Android NDK r8c
Put NDK_TOOLCHAIN_VERSION=clang3.1 to your Application.mk for this.
Rewrite your code explicitly using vld1q_u8 for load, vst1q_u8 for store, vcgtq_u8 for operator > and vandq_u8 for operator &
I'm compiling a nexus one android kernel from source as found on HTCs developer website. I've obtained an ARM tool chain by DLing the android NDK from the android dev site. I am able to run make clean and make defconfig without incident, but when I run make, it only gets so fare before running into compiler errors.
Currently i see the following:
$MY_DIR/nexus_one/arch/arm/include/asm/glue.h:156:29: error: '#' is not followed by a macro parameter
The offending line is:
1 /*
2 * Instruction Fault Status Register. (New register as of ARMv6)
3 * If processor has IFSR then set value, else set translation fault
4 */
5 #if defined(CONFIG_CPU_ABRT_EV7) || defined(CONFIG_CPU_ABRT_EV6)
6 # define CPU_PABORT_IFSR(reg) mrc p15, 0, reg, cr5, cr0, 1 #asm macro;
7 #else
8 # define CPU_PABORT_IFSR(reg) mov reg, #5 #asm macro;
9 #endif
Specifically, line 8 above is what hoses the compiler. Apparently you can't have that second # sign, but i'm not really sure whats going on in this code, and it looks pretty important so i don't want to touch it.
I'm guessing i'm compiling with the wrong tool chain maybe? Or perhaps i have configured things wrong somehow? Does any one have any idea what this is all about?
thanks,
brian
I strongly recommend you to use CodeSourcery toolchain for Linux for compiling Linux kernel.
CodeSourcery GNU Toolchain for ARM Processors: 2008q1-126 version
Turns out it was nothing to do with the specific toolchain. The # sign needed 'escaping' of some sort. The solution was as follows:
/* this is needed to get the mov reg, 5 macro to compile and function correctly */
#define hash_hackery #
#define f(x) x
#if defined(CONFIG_CPU_ABRT_EV7) || defined(CONFIG_CPU_ABRT_EV6)
# define CPU_PABORT_IFSR(reg) mrc p15, 0, reg, cr5, cr0, 1 #asm macro;
#else
# define CPU_PABORT_IFSR(reg) mov reg, f(hash_hackery)5 #asm macro;
#endif
This post was very informative in finding the answer.