Matt Galloway

My home on the 'net.

iPhone SDK Bug Hunting - GCC atomic builtins

For a while now I have been reporting bugs that I find in the iPhone SDK / iPhone OS to Apple because I realised that it’d be nice to help out. Some bugs have been small and some have been large, ranging from minor crashes of MobileSafari up to full blown problems in the iPhone SDK and associated frameworks.

One that I came across today had stumped me for a long time and it has to do with the GCC atomic builtins. If you’re unfamiliar with them, then a good bit of introductory reading is a great blog post on the ARM blog. Now, these atomic builtins have not been defined within the iPhone’s libc implementation, until the 3.2 SDK came along – it appears that Apple have added them. This is a good thing, because it means that we can start using them in our applications. But, we can only use them for applications running on iPhone OS >=3.2 of course. That’s where the fun begins…

I have an application which I have been developing that needs to run on both the 3G and the 3GS, i.e. both armv6 and armv7 architectures. I found that after upgrading to the 3.2 SDK I started running into a rather strange problem when running the application on an iPhone 3G (running iPhone OS 3.1.3). The error I was getting was this:

1
2
3
4
5
6
7
dyld: lazy symbol binding failed: Symbol not found: ___sync_fetch_and_add_4
  Referenced from: /var/mobile/Applications/xxx/MyApp.app/MyApp
  Expected in: /usr/lib/libSystem.B.dylib

dyld: Symbol not found: ___sync_fetch_and_add_4
  Referenced from: /var/mobile/Applications/xxx/MyApp.app/MyApp
  Expected in: /usr/lib/libSystem.B.dylib

Now that’s really odd because __sync_fetch_and_add_4 is one of those GCC atomics which shouldn’t be being linked in as I am building for an iPhone OS deployment target of 3.1. It’s worth at this stage having a quick look at – http://developer.apple.com/iphone/library/documentation/Xcode/Conceptual/iphone_development/120-Running_Applications/running_applications.html – which says:

You specify the earliest iPhone OS release on which you want your application to run with the iPhone OS Deployment Target build setting. By default, this build setting is set to the iPhone OS release that corresponds to the Base SDK build-setting value. For example, when you set Base SDK to iPhone Device 2.2.1 or iPhone Simulator 2.2.1, the value of the iPhone Deployment Target build setting is iPhone OS 2.2.1, as shown in Figure 3-3.

So that means that if I set the base SDK to 3.2 and the iPhone OS deployment target to 3.1, then I should get code that will definitely run on 3.1, right? Running my app on 3.1.3 however causes a crash simply because __sync_fetch_and_add_4 isn’t available in its libc.

After a bit of inspection I found that even this simple program caused the crash:

1
2
3
4
5
6
#include <string>

int main(int argc, char *argv[]) {
    std::string strA = "yes";
    return 0;
}

That really is a very simple program! Why would that crash! Well, with a bit of inspection using nm we can work out what’s going on. nm shows us a list of the symbols that a given object file, or binary references. Below are outputs of nm on the resulting binary from 2 different combinations of base SDK and iPhone OS deployment target.

Base SDK = 3.1.3, iPhone OS Deployment Target = 3.1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
$ nm build/Debug-iphoneos/AtomicsBug.app/AtomicsBug

build/Debug-iphoneos/AtomicsBug.app/AtomicsBug (for architecture armv6):
00002f64 s  stub helpers
00002fdc s GCC_except_table0
00003048 D _NXArgc
0000304c D _NXArgv
         U __Unwind_SjLj_Register
         U __Unwind_SjLj_Resume
         U __Unwind_SjLj_Unregister
         U __ZN9__gnu_cxx18__exchange_and_addEPVii
         U __ZNSs4_Rep10_M_destroyERKSaIcE
         U __ZNSs4_Rep20_S_empty_rep_storageE
         U __ZNSsC1EPKcRKSaIcE
         U ___gxx_personality_sj0
00003054 D ___progname
00002f58 t ___restore_vfp_d8_d15_regs
00002f50 t ___save_vfp_d8_d15_regs
00001000 A __mh_execute_header
00003050 D _environ
         U _exit
00002e9c t _main
0000301c s _pvars
         U dyld_stub_binder
00002e70 T start

build/Debug-iphoneos/AtomicsBug.app/AtomicsBug (for architecture armv7):
00002f64 s  stub helpers
00002fdc s GCC_except_table0
00003048 D _NXArgc
0000304c D _NXArgv
         U __Unwind_SjLj_Register
         U __Unwind_SjLj_Resume
         U __Unwind_SjLj_Unregister
         U __ZN9__gnu_cxx18__exchange_and_addEPVii
         U __ZNSs4_Rep10_M_destroyERKSaIcE
         U __ZNSs4_Rep20_S_empty_rep_storageE
         U __ZNSsC1EPKcRKSaIcE
         U ___gxx_personality_sj0
00003054 D ___progname
00001000 A __mh_execute_header
00003050 D _environ
         U _exit
00002eb0 t _main
0000301c s _pvars
         U dyld_stub_binder
00002e84 T start

Base SDK = 3.2, iPhone OS Deployment Target = 3.1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
$nm build/Debug-iphoneos/AtomicsBug.app/AtomicsBug

build/Debug-iphoneos/AtomicsBug.app/AtomicsBug (for architecture armv6):
00002f64 s  stub helpers
00002fdc s GCC_except_table0
00003048 D _NXArgc
0000304c D _NXArgv
         U __Unwind_SjLj_Register
         U __Unwind_SjLj_Resume
         U __Unwind_SjLj_Unregister
         U __ZNSs4_Rep10_M_destroyERKSaIcE
         U __ZNSs4_Rep20_S_empty_rep_storageE
         U __ZNSsC1EPKcRKSaIcE
         U ___gxx_personality_sj0
00003054 D ___progname
00002f58 t ___restore_vfp_d8_d15_regs
00002f50 t ___save_vfp_d8_d15_regs
         U ___sync_fetch_and_add_4
00001000 A __mh_execute_header
00003050 D _environ
         U _exit
00002ea4 t _main
0000301c s _pvars
         U dyld_stub_binder
00002e78 T start

build/Debug-iphoneos/AtomicsBug.app/AtomicsBug (for architecture armv7):
00002f74 s  stub helpers
00002fe0 s GCC_except_table0
00003044 D _NXArgc
00003048 D _NXArgv
         U __Unwind_SjLj_Register
         U __Unwind_SjLj_Resume
         U __Unwind_SjLj_Unregister
         U __ZNSs4_Rep10_M_destroyERKSaIcE
         U __ZNSs4_Rep20_S_empty_rep_storageE
         U __ZNSsC1EPKcRKSaIcE
         U ___gxx_personality_sj0
00003050 D ___progname
00001000 A __mh_execute_header
0000304c D _environ
         U _exit
00002ea8 t _main
00003018 s _pvars
         U dyld_stub_binder
00002e7c T start

Notice how it’s referencing __sync_fetch_and_add_4 in the armv6 version of the binary created with base SDK 3.2? That’s bad! After a bit of digging into the header files supplied with the SDKs we find where the problem stems from – it’s in the c++config.h header file. Here is the difference between the file supplied with 3.1.3 SDK to the file supplied with 3.2 SDK:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ diff -u /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.1.3.sdk/usr/include/c++/4.2.1/armv6-apple-darwin9/bits/c++config.h /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.2.sdk/usr/include/c++/4.2.1/armv6-apple-darwin10/bits/c++config.h

--- /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.1.3.sdk/usr/include/c++/4.2.1/armv6-apple-darwin9/bits/c++config.h  2009-12-18 09:19:17.000000000 +0000
+++ /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.2.sdk/usr/include/c++/4.2.1/armv6-apple-darwin10/bits/c++config.h   2010-03-16 05:39:05.000000000 +0000
@@ -879,7 +879,7 @@
 /* #undef _GLIBCXX_VERSION */

 /* Define if builtin atomic operations are supported on this host. */
-/* #undef _GLIBCXX_ATOMIC_BUILTINS */
+#define _GLIBCXX_ATOMIC_BUILTINS 1

 /* Define to use concept checking code from the boost libraries. */
 /* #undef _GLIBCXX_CONCEPT_CHECKS */

This means that for any file compiled with the 3.2 SDK, GCC is told that it has the atomic builtins and so it creates code that links against them. So, there’s the problem!

Apple are trying to get everyone to use base SDK and iPhone OS deployment target settings rather than just building for an old SDK, but they need to make sure 100% that their SDKs are sane enough to cope with the asymmetry.

I have uploaded a sample project that shows the problem: AtomicsBug Project.

Updates

  • EDIT: I’ve found that if you use gcc-4.0 rather than gcc-4.2 then the problem doesn’t appear. This is because gcc-4.0 doesn’t use __sync_fetch_and_add for its atomic functions. (Note: this isn’t a fix but is a quick workaround for anyone experiencing the problem).

  • Update [17/05/2010]: It appears that Apple are aware of the bug. Fingers crossed that it’ll be fixed in future SDKs.

Comments