Ticket #2 (closed defect: fixed)
Crash on attempt to write float value to stream
| Reported by: | dmc | Owned by: | ognian |
|---|---|---|---|
| Priority: | blocker | Milestone: | r3-crystax-3 |
| Component: | ndk | Version: | r3-crystax-2 |
| Severity: | Keywords: | ||
| Cc: | ognian | Blocked By: | |
| Blocking: |
Description
Example:
#include <cstdio>
#include <sstream>
int main()
{
std::stringstream ostr;
std::printf("Before write\n");
ostr << 1.0;
std::printf("After write\n");
return 0;
}
Expected output:
Before write
After write
Actual output:
Before write
Application crashed when attempting write any float value to C++ stream (it should be any C++ stream, not only stringstream). Probably it is because Bionic does not support C locales, but this is not confirmed and need to be investigated.
Attachments
Change History
comment:2 Changed 22 months ago by ognian
Further investigation shows that the trouble is caused by lack of locale support. The implementation of setlocale() is return hardcoded 0:
http://android.git.kernel.org/?p=platform/bionic.git;a=blob_plain;f=libc/stdlib/locale.c;hb=HEAD
I can think of two workarounds:
- Rebuild bionic to return a pointer to "C" string, maybe Google should have implemented it that way. Currently this will bother users of ndk-r3-crystax because it'll require to link statically against the custom bionic.
- Modify libstdc++ so it contains
#define setlocale(category, locale) "C"
comment:3 Changed 22 months ago by dmc
I believe the best way is second. The only I want note is about macros. Let avoid them if possible. Let see to the libstdc++ sources - how setlocale used there? Is it always std::setlocale? If yes, let add new inline function in std namespace for that (somewhere in the common used libstdc++ header):
inline char* setlocale (int category, char const *locale)
{
return "C";
}
comment:4 follow-up: ↓ 5 Changed 22 months ago by dmc
- Owner changed from dmc to ognian
- Status changed from new to assigned
comment:5 in reply to: ↑ 4 Changed 22 months ago by ognian
Are you sure returning a local char[] will work well with inline? Is there a chance that the function won't be actually inlined by compiler and return the address of freed data?
I would suggest a safer realization, in case this is not standard behavior:
static const char* const _c_locale_str = "C";
inline const char* setlocale (int category, char const* locale) {
return _c_locale_str;
}
In libc documentation is stated that returned value of setlocale shouldn't be changed, so I guess we'll be safe with that implementation.
comment:6 Changed 22 months ago by dmc
Yes, I'm sure. "C" actually is not local char[] - it is pointer to the statically defined zero-terminated char array located in data segment. This is how C and C++ works.
comment:8 Changed 21 months ago by dmc
- Status changed from assigned to closed
- Resolution set to fixed
After lot of attempts I have not found good solution to redefine setlocale in gcc sources - if I declare setlocale in std namespace - it works only for C++ code, but there are also lot of C sources using setlocale. But I can not declare setlocale as extern "C" function in global namespace - it will conflict with Bionic one. Macros is not solution too - there are lot of places in gcc sources where explicit "#undef setlocale" used to prevent macros.
So I've found all places where setlocale return value used and fixed them like this:
__old = setlocale(.....); if (__old == NULL || __old[0] == '\0') __old = "C";
This do the trick. Now C++ streams works fine with float/double values.

I have located the exact place it crashes - /gcc/gcc-4.4.0/libstdc++-v3/config/locale/generic/c_locale.h:
char* __old = std::setlocale(LC_NUMERIC, NULL); char* __sav = NULL; if (__builtin_strcmp(__old, "C")) { const size_t __len = __builtin_strlen(__old) + 1; __sav = new char[__len]; __builtin_memcpy(__sav, __old, __len); std::setlocale(LC_NUMERIC, "C"); }in strcmp(), because old is NULL. I could use some advice how to fix it.