How to set 2 byte wchar_t output?

harper picture harper · Oct 21, 2013 · Viewed 12.6k times · Source

The GCC uses a 4-byte wchar_t by default. I can set the option -fshort-wchar to get 2 bytes per wchar_t in the L"string constants". But when I set the compiler option to my source file I get the famous warning message

foo.o uses 2-byte wchar_t yet the output is to use 4-byte wchar_t; use of wchar_t values across objects may fail

Since I really want 2-byte wchar_t I also want the output to use this variant. Is there any linker option to tell it what I want?

Edit

This warning doesn't inhibit the linker to produce a valid output. But the dozens of false warnings cover other messages.

Answer

Ilya picture Ilya · May 27, 2014

In binutils you can find this error message in bfd/elf32-arm.c as:

"warning: %B uses %u-byte wchar_t yet the output is to use %u-byte wchar_t; use of wchar_t values across objects may fail"

However, if you look further at binutils, you'd realize that the output's wchar_t size is not initialized to 4 anywhere. So what determines the "output wchar_t size"? Actually, the first object given to ld initializes the output attributes. The next objects merge their attributes into it. If you link with gcc/g++, it executes ld internally, so try gcc -v and see how ld is executed. This will give you insight into what internal object files (in addition to your own) it implicitly links into your executable.

For example, linking with gcc (e.g. gcc -v -shared -o libfoobar.so foo.o bar.o) results in invocation of:

ld ... crtbegin_so.o foo.o bar.o crtend_so.o ...

i.e. the following objects are actually linked (in order):

  • crtbegin_so.o (implicitly)
  • foo.o
  • bar.o
  • crtend_so.o (implicitly)

Here's what ld does:

  1. The output attributes set starts off empty.
  2. Merging crtbegin_so.o attributes. Now output attributes contain out_attr[Tag_ABI_PCS_wchar_t] == 4
  3. Merging foo.o attributes. If foo.o was built with -fshort-wchar, then in_attr[Tag_ABI_PCS_wchar_t] == 2 and this will result in a conflict and the warning you're seeing.

If you were to swap crtbegin_so.o and foo.o on the ld command line, you'd get the following warning instead:

ld: warning: android-ndk-r9d/platforms/android-16/arch-arm/usr/lib/crtbegin_so.o uses 4-byte wchar_t yet the output is to use 2-byte wchar_t; use of wchar_t values across objects may fail

As you can see, it's not a matter of incompatibility of the input with the output, but rather (perceived) incompatibility between two object files linked together.

What can we do about it?

  • As of 2008, ld supports the --no-wchar-size-warning flag to suppress this warning. But as you said, indiscriminately suppressing warnings has its drawbacks.

  • You can rebuild your toolchain with -fshort-wchar.

  • You can strip the Tag_ABI_PCS_wchar_t tags from your internal gcc object binaries if you truly believe they're sizeof(wchar_t)-agnostic. This might be easier than rebuilding your toolchain. For that, you can use this utility I once wrote. (You might need to unpack libgcc.a, change its object files and repack it.)