Bug 51735 - C/C++ static library binding error: Undefined symbols for architecture
Summary: C/C++ static library binding error: Undefined symbols for architecture
Alias: None
Product: iOS
Classification: Xamarin
Component: General ()
Version: XI 10.3 (iOS 10.2)
Hardware: PC Windows
: --- normal
Target Milestone: Untriaged
Assignee: Brendan Zagaeski (Xamarin Team, assistant)
Depends on:
Reported: 2017-01-24 16:45 UTC by David Greene
Modified: 2017-01-24 22:33 UTC (History)
5 users (show)

Is this bug a regression?: ---
Last known good build:

Static Library Files (12.76 KB, application/zip)
2017-01-24 16:45 UTC, David Greene
Completed working example, including Xcode and Xamarin projects (60.00 KB, application/zip)
2017-01-24 21:32 UTC, Brendan Zagaeski (Xamarin Team, assistant)

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

Description David Greene 2017-01-24 16:45:09 UTC
Created attachment 19461 [details]
Static Library Files

Customer is attempting to implement a C/C++ static library into a Xamarin.iOS project, as demonstrated in the following lightning lecture. 

*Issue: Created static library (attached) throws errors when attempting to compile the project.

*Steps taken:
1.) Created C file with three functions. 
addOne(int n), subtractOne(int n), get_time()
2.) Used SWIG to create C & C# wrapper class
3.) Created static library in Xcode and built a FAT binary using lipo with all architectures 
4.) Verified symbols in the library (“nm -arch x86_64 libexample.a")
5.) Added following mtouch arguments:
-cxx -gcc_flags "-L${ProjectDir} -lexample -force_load ${ProjectDir}/libexample.a"

*Expected Result:
Project will compile and run successfully

*Actual Results:
Error messages shown below are displayed when building the project.

*Error(s)- Full build log attached:
Undefined symbols for architecture x86_64:
"subtractOne(int)", referenced from:
_CSharp_subtractOne in libexample.a(example_wrap.o)
"addOne(int)", referenced from:
_CSharp_addOne in libexample.a(example_wrap.o)
"get_time()", referenced from:
_CSharp_get_time in libexample.a(example_wrap.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Comment 1 Brendan Zagaeski (Xamarin Team, assistant) 2017-01-24 19:24:39 UTC
## Assigning to myself for an initial quick non-engineering pass

At first glance this sounds like it shouldn't be anything too complicated, but I'll assign back to the engineering team after the quick review in case it looks like something new to me that doesn't follow the normal P/Invoke conventions.
Comment 3 Brendan Zagaeski (Xamarin Team, assistant) 2017-01-24 21:32:30 UTC
Created attachment 19466 [details]
Completed working example, including Xcode and Xamarin projects

The attached files in Comment 0 were missing Xcode and Xamarin project files as well as the SWIG module file.  I followed the instructions on [1 - 3] to fill in the missing pieces.  The resulting example (attached) builds and runs successfully.

[1] http://www.swig.org/tutorial.html
[2] http://blog.reblochon.org/2013/01/c-bindings-for-monotouch-using-swig.html
[3] https://developer.xamarin.com/guides/ios/advanced_topics/native_interop/

1. Write the example.i module file as per the SWIG tutorial [1]

%module example
extern int addOne(int n);
extern int subtractOne(int n);
extern char *get_time();

extern int addOne(int n);
extern int subtractOne(int n);
extern char *get_time();

2. Run the SWIG command, being sure to specify "__Internal" for dllimport (see for example [2]):

mkdir generated
swig -csharp -namespace Example -dllimport __Internal -outdir generated example.i

3. Create a new "iOS > Cocoa Touch Static Library" project in Xcode.

4. Replace the files in the library with the original example.c and the generated example_wrapper.c (from step 2).

5. Build the Cocoa Touch Static Library with the desired architectures for simulator and device and combined library (see also [3]):

xcodebuild -project example/example.xcodeproj -arch armv7 -arch arm64 -sdk iphoneos -configuration Release build
xcodebuild -project example/example.xcodeproj -arch i386 -arch x86_64 -sdk iphonesimulator -configuration Release build
lipo -create -output libexample.a example/build/Release-iphoneos/libexample.a example/build/Release-iphonesimulator/libexample.a

6. Patch generated/examplePINVOKE.cs to add the attributes to help AOT issues as discussed on [2].  The namespace "MonoTouch" should be replaced with "ObjCRuntime" in all cases because the blog post from [2] was based on the old Classic API namespaces.

7. Build the C# library:

mcs /noconfig /o+ /out:LibExample.dll /target:library /unsafe /r:/Library/Frameworks/Xamarin.iOS.framework/Versions/Current/lib/mono/2.1/System.dll /r:/Library/Frameworks/Xamarin.iOS.framework/Versions/Current/lib/mono/2.1/System.Core.dll /r:/Library/Frameworks/Xamarin.iOS.framework/Versions/Current/lib/mono/Xamarin.iOS/Xamarin.iOS.dll generated/*.cs 

8. Copy both the C# library and the C library into the Xamarin.iOS app project:

cp -i libexample.a UnifiedSingleViewIphone1/UnifiedSingleViewIphone1/
cp -i LibExample.dll UnifiedSingleViewIphone1/UnifiedSingleViewIphone1/

9. Add a reference to the C# library in the iOS app project, and add the additional mtouch arguments:

-gcc_flags "-L${ProjectDir} -lexample -force_load ${ProjectDir}/libexample.a"

10. Add some code to test out the library.  For example, I added the following to the `ViewDidLoad()` override:

 UILabel label = new UILabel(new CoreGraphics.CGRect(40, 40, 200, 200));
 label.Text = String.Format("addOne(5) = {0}", Example.example.addOne(5).ToString());

11. Build and run the app.  The `addOne()` method successfully produces the expected output.
Comment 5 Brendan Zagaeski (Xamarin Team, assistant) 2017-01-24 22:33:00 UTC
In case it might be informative, based on the symbol names in the `nm` output it looks like the issue with the original "libexample.a" from Comment 0 might have been that the distinction between C and C++ was not maintained consistently when building the library.  "example_wrap.c" in the attachment from Comment 0 appears to have been produced by SWIG invoked without the `-c++` command line option.  That makes sense since the base library ("example.c" in this case) is also plain C, but it means that the "example_wrap.c" file should be compiled as a plain C file in the Xcode project (not as a C++ file).