GCC - How to statically link a static mono bundle - c#

I have a .NET application that I want to port to an embedded ARM7 based Linux system. The target is a locked down system running Busybox and I have no write access to the /lib directory (which b.t.w. is empty).I do however have write access to a separate mount point with enough space for my application.
My idea is to compile a static application on a device with the same architecture and copy the binary to the target device. I am using a Beaglebone black for compilation.
How can I compile my .NET application into a 100% statically linked binary?
I am aware of the licensing restrictions of Mono (I have a commercial license).
To keep it simple, my application looks like this.
/// hello.cs
using System;
internal class Program
{
private static void Main()
{
Console.WriteLine("hello");
}
}
Step 1: Compile cs code
$ mcs hello.cs
Step 2: Bundle compiled binary with Mono and output to c code
$ mkbundle -c -o hello.c -oo bundles.o --deps hello.exe --static
OS is: Linux
Note that statically linking the LGPL Mono runtime has more licensing restrictions than
dynamically linking.
See http://www.mono-project.com/Licensing for details on licensing.
Sources: 1 Auto-dependencies: True
embedding: /root/csharp/hello.exe
embedding: /usr/local/lib/mono/4.5/mscorlib.dll
Compiling:
as -o bundles.o temp.s
Step 3: Compile resulting c code
cc -o hello -Wall `pkg-config --cflags mono-2` hello.c bundles.o `pkg-
config --libs-only-L mono-2` -Wl,-Bstatic -lmono-2.0 ./Lib2/libm.a ./Lib2/librt.a ./Lib2/libdl.a
./Lib2/libpthread.a ./Lib2/libgcc.a ./Lib2/libc.a -Wl,-Bdynamic ./Lib2/ld-linux-armhf.so.3
***** Warning message receieved (example):
/media/mono-3.10.0/mono/io-layer/sockets.c:992: warning: Using 'gethostbyname' in statically
linked applications requires at runtime the shared libraries from the glibc version used for
linking
Step 4: Check dependencies
$ ldd hello
/lib/ld-linux-armhf.so.3 (0xb6f92000)
libgcc_s.so.1 => /lib/arm-linux-gnueabihf/libgcc_s.so.1 (0xb6f5e000)
libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0xb6e7f000)
At this point, the application works on my development system but not on the target system. I am guessing due to missing libraries as the error message I get is File not found.. How can I link the remaining libraries statically?
As an experiment, I've tested a similar approach with a native C application
/// hello.c
#include <stdio.h>
int main()
{
printf("hello\n");
return 0;
}
Step 1: Compile with dynamic links
$ cc hello.c -o hello
Step 2: Check dependencies
$ ldd hello
libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0xb6e26000)
/lib/ld-linux-armhf.so.3 (0xb6f14000)
As expected, this application does not work on the target system.
Step 3: Compile with static links
$ cc hello.c -o hello -static
Step 4: Check dependencies
$ ldd hello
not a dynamic executable
This application works on my target system.
I am a total newbie on Linux systems in general and have spent many hours trying to find a solution. What worries me a bit is that I'm not even sure if it's solvable :). Any help would be highly appreciated!
Note: If it's impossible to link all libraries statically (for any reason), a solution where these are placed in the same folder as the main application would be totally acceptable. In such a scenario, how would I relink the libraries to ./?

Related

Decompilation and Protection of Mkbundled CLI Image of .NET Executable

I have complied a .net executable(GTK Based UI) using mkbundle by statically including mono runtime using
mkbundle --static hello.exe -o --deps hello
I got a CLI Image.Is this CLI Image Native Code like a Complied C Program?Can it be disassembled easily like any other normal .NET Executable? If yes is there a way to make decompilation Hard? Is Obfuscation before mkbundling an option? and Can i do AOT(Ahead of Time) Compilation? Please throw some light on this.

How to make "mkbundle --deps" option working with mono 3.2.3

I am trying to bundle the application with mono 3.2.3 to a stand-alone executable. To do so, I am following this guideline. After declarating variables:
mono_version="3.2.3"
export MONO=/cygdrive/c/progra~2/Mono-$mono_version
machineconfig=$PROGRAMFILES\\Mono-$mono_version\\etc\\mono\\4.5\\machine.config
export PATH=$PATH:$MONO/bin
export PKG_CONFIG_PATH=$MONO/lib/pkgconfig
export CC="i686-pc-mingw32-gcc -U _WIN32"
mkbundle --deps command cannot localize referenced assemblies:
Unhandled Exception:
System.IO.FileNotFoundException: Could not load file or assembly 'gtk-sharp' or
one of its dependencies. The system cannot find the file specified.
File name: 'gtk-sharp'
performing exactly the same operation with mono 2.10.9:
mono_version="2.10.9"
export MONO=/cygdrive/c/progra~2/Mono-$mono_version
machineconfig=$PROGRAMFILES\\Mono-$mono_version\\etc\\mono\\4.0\\machine.config
export PATH=$PATH:$MONO/bin
export PKG_CONFIG_PATH=$MONO/lib/pkgconfig
export CC="i686-pc-mingw32-gcc -U _WIN32"
mkbundle --deps --machine-config "$machineconfig" -c UI.exe
gives positive result:
OS is: Windows
WARNING:
Check that the machine.config file you are bundling
doesn't contain sensitive information specific to this machine.
Sources: 3 Auto-dependencies: True
embedding: C:\users\piotr\desktop\authoringtool\UI\bin\debug\UI.exe
config from: C:\users\piotr\desktop\authoringtool\UI\bin\debug\UI.exe.config
embedding: C:\PROGRA~2\MONO-2~1.9\lib\mono\gac\gtk-sharp\2.12.0.0__35e10195dab3c99f\gtk-sharp.dll
embedding: C:\PROGRA~2\MONO-2~1.9\lib\mono\gac\glib-sharp\2.12.0.0__35e10195dab3c99f\glib-sharp.dll
.
.
.
embedding: C:\PROGRA~2\MONO-2~1.9\lib\mono\4.0\Mono.Posix.dll
Machine config from: C:\Program Files (x86)\Mono-2.10.9\etc\mono\4.0\machine.config
Compiling:
as -o temp.o temp.s
Does anyone know the reason of such behavior? I'm using 64-bit version of windows 7 and the Cygwin I found on the official website. The code was compiled and tested on Xamarin Studio 4.2 and Visual Studio 2010.
Howto for mkbundle on cygwin + mingw
Tested with mono 4.0.3
In mono 4.0.3, mkbundle works but it can be tricky to make it work.
First, check your setup:
Install Mono/GTK# in a path that doesn't contains spaces (ie not Program Files then)
Setup a MinGw/Cygwin working compile chain (as the one for
compiling mono on windows).
Define the mandatory environment variables for mkbundle:
mingw compiler location should be in the Windows PATH (used by cmd)
pkg-config should also be in the Windows PATH
Use the following cygwin script (it can be adapted to run on cmd)
# M_PREFIX refers to Mono installation
# For more information, search for prefix installation in Mono documentation
M_PREFIX='/cygdrive/c/Mono'
export DYLD_FALLBACK_LIBRARY_PATH=${M_PREFIX}/lib:${DYLD_FALLBACK_LIBRARY_PATH}
export LD_LIBRARY_PATH=${M_PREFIX}/lib:${M_PREFIX}/lib/mono/4.5:${LD_LIBRARY_PATH}
export C_INCLUDE_PATH=${M_PREFIX}/include:${C_INCLUDE_PATH}
export ACLOCAL_PATH=${M_PREFIX}/share/aclocal:${ACLOCAL_PATH}
export PKG_CONFIG_PATH=${M_PREFIX}/lib/pkgconfig:${PKG_CONFIG_PATH}
# Here we added the system32 to make cmd available to mkbundle
# /usr/bin is the default location for mingw
export PATH=${M_PREFIX}/bin:/cygdrive/c/Windows/system32:/usr/bin:${PATH}
export CC="i686-pc-mingw32-gcc -U _WIN32"
Then you can run:
mkbundle --deps --keeptemp my.exe my.dll -o bundled.exe
Notes:
- Copy mono-2.0.dll in the application directory as it should be distributed along the bundled exe
cp ${M_PREFIX}/bin/mono-2.0.dll .
if -z is used, zlib1.dll should be copied as well. (note that gcc invocation change also). You may need more dll depending on your usage of framework features (not exhaustive list : libglib*.dll, libgmodule*.dll, libgthread*.dll, iconv.dll, intl.dll)
-c is used to generate only stub.
You must specify all exe and dll that are needed for the bundle.
--keeptemp will keep temp.c and temp.s which could come in handy if mkbundle fail on gcc invocation.
If you want to invoke gcc by hand (and it may be needed):
i686-pc-mingw32-gcc -U _WIN32 -g -o output.exe -Wall temp.c $(pkg-config --cflags --libs mono-2) temp.o
For Console Applications
To make console application work you must remove -mwindows from the gcc command. To do that, you must invoke pkg-config --cflags --libs mono-2 and remove the -mwindows.
You should obtain something like that afterwards:
i686-pc-mingw32-gcc -g -o output.exe -Wall temp.c -mms-bitfields -IC:/Mono/include/mono-2.0 -mms-bitfields -LC:/Mono/lib -lmono-2.0 -lws2_32 -lpsapi -lole32 -lwinmm -loleaut32 -l advapi32 -lversion temp.s
Anyone can improve mkbundle
mkbundle is an open sourced C# console application (on mono github)
so it can be easily modified and recompiled depending on your needs.
Reading the code could also be helpful to understand how it works underneath.
cmd usage as the different commands used by mkbundle are hard coded so it would benefit from some parametrization enhancement.

Create C# executable with mkbundle on windows

Im trying to create an executable from a console application.
I have installed mono,cygwin (mingw-gcc, mingw-zlib1, mingw-zlib-devel, pkg-config) and I have added the following lines to my .bashrc file
export PKG_CONFIG_PATH=/cygdrive/c/progra~1/Mono-3.2.3/lib/pkgconfig
export PATH=$PATH:/cygdrive/c/progra~1/Mono-3.2.3/bin
export CC="i686-pc-mingw32-gcc -U _WIN32"
But everytime I try to use mkbundle I receive the following message
Is there a way to make mkbundle work properly on windows.?
(Im using windows 7 x86, mono 3.2.3, the cygwin I found on the official website, xamarin studio 4.2 and net framwork 4)
This problem is still presented in the current mono version under the Windows. This happened because of mono team switched default GC to SGEN. So when you try to use mkbundle as you can see in your error mkbundle utility try to find mono-2 library but this lib didn't included in setup and you have a fail. To solve this you should configure mkbundle to use libmonosgen-2.0 instead of mono-2. Let's try to do this.
The key moment is setting this variable:
export PKG_CONFIG_PATH=/cygdrive/c/progra~1/Mono-3.2.3/lib/pkgconfig
If you go to this directory you will see a lot of *.pc files (package config). This files are responsible for configuration of linking libraries during bundling process. For some reasons mono team hard coded package config file and library to mono-2 (see this line 492). How could we fix it without rebuilding mkbundle? The solution is to use the next bundle script:
# Mono paths
mono_version="3.2.3"
export MONO=/cygdrive/c/progra~2/Mono-$mono_version
machineconfig=$PROGRAMFILES\\Mono-$mono_version\\etc\\mono\\4.5\\machine.config
export PATH=$PATH:$MONO/bin
export PKG_CONFIG_PATH=$MONO/lib/pkgconfig
# Compiller
export CC="i686-pc-mingw32-gcc -U _WIN32"
# Output file name
output_name=Prog.exe
# Produce stub only, do not compile
mkbundle --deps --machine-config "$machineconfig" -c Program.exe
# Produce helper object file. You may see errors at this step but it's a side effect of this method.
mkbundle --deps --machine-config "$machineconfig" -oo temp.o Program.exe
# Compile. Pay attention where I use monosgen-2
i686-pc-mingw32-gcc -U _WIN32 -g -o "$output_name" -Wall temp.c `pkg-config --cflags --libs monosgen-2` temp.o
# Copy libmonosgen-2.dll
cp $MONO/bin/libmonosgen-2.0.dll .
# Run
./$output_name
I had the same problem some time ago and made a script for cygwin. You can try it, would be interesting whether it still works:
mkbunde cygwin script
There are explanations in the script how to setup the environment.
Howto for mkbundle on cygwin + mingw
Here you can find an updated howto make mkbundle work on Windows
First, check your setup:
Install Mono/GTK# in a path that doesn't contains spaces (ie not Program Files then)
Setup a MinGw/Cygwin working compile chain (as the one for
compiling mono on windows).
Define the mandatory environment variables for mkbundle:
mingw compiler location should be in the Windows PATH (used by cmd)
pkg-config should also be in the Windows PATH
Use a cygwin script to defined mono and mingw required variables.
Then you can run:
mkbundle --deps --keeptemp my.exe my.dll -o bundled.exe
Notes:
Copy mono-2.0.dll in the application directory as it should be distributed along the bundled exe
You must specify all exe and dll that are needed for the bundle.
--keeptemp will keep temp.c and temp.s which could come in handy if mkbundle fail on gcc invocation.
If you want to invoke gcc by hand (and it may be needed):
i686-pc-mingw32-gcc -U _WIN32 -g -o output.exe -Wall temp.c $(pkg-config --cflags --libs mono-2) temp.o
Anyone can improve mkbundle
mkbundle is an open sourced C# console application (on mono github)
so it can be easily modified and recompiled depending on your needs.
Reading the code could also be helpful to understand how it works underneath.

Mono AOT compilation - how to get the .so file?

I am using Mono on Mac, installed with Unity.
The http://www.mono-project.com/Mono:Runtime:Documentation:AOT page states that mono compiler with aot option should generate .so file.
What I get is a dylib file.
My goal is to generate so file from the managed c# dll file.
Here is the command and output (in terminal):
Gerleis-Mac:CrazInvaders gerleim$ /Applications/Unity/MonoDevelop.app/Contents/Frameworks/Mono.framework/Versions/2.10.2/bin/mono --aot -O=all iOSBuild/Data/Managed/Assembly-CSharp.dll
Mono Ahead of Time compiler - compiling assembly /Users/gerleim/Desktop/CrazInvaders/iOSBuild/Data/Managed/Assembly-CSharp.dll
Code: 2217337 Info: 87704 Ex Info: 56841 Unwind Info: 80 Class Info: 4663 PLT: 5477 GOT Info: 364454 GOT: 35852 Offsets: 109275
Executing the native assembler: as -arch i386 -W -o /var/folders/b4/4tgynrr570zd5qdng_4ljs9m0000gn/T/mono_aot_uGBs4E.o /var/folders/b4/4tgynrr570zd5qdng_4ljs9m0000gn/T/mono_aot_uGBs4E
Executing the native linker: gcc -m32 -dynamiclib -o /Users/gerleim/Desktop/CrazInvaders/iOSBuild/Data/Managed/Assembly-CSharp.dll.dylib.tmp /var/folders/b4/4tgynrr570zd5qdng_4ljs9m0000gn/T/mono_aot_uGBs4E.o
Compiled 12759 out of 12761 methods (99%)
2 methods have other problems (0%)
Methods without GOT slots: 8190 (64%)
Direct calls: 716 (20%)
JIT time: 1427 ms, Generation time: 1045 ms, Assembly+Link time: 1712 ms.
I guess there are problems with the parameters of the assembler and linker, but I don't have options to change those (see http://mono.wikia.com/wiki/Man_mono)
(When built from the Unity IDE, Unity uses mono and aot and generates .s files for XCode/iOS.)
.sos are Linux binaries, so you'd have to compile ahead-of-time on a Linux. When you do this on OS X instead, you get a .dylib because that's an OS X library binary. Even if you had a Linux binary on your Mac, it'd be useless to you.

after using mkbundle2, destination machine can't find libmono.0.dylib

I have a very simple .NET commandline application that I want to port to OS X.
I can run it with "mono app.exe"
However, the destination machines won't have mono installed.
So, I wanted to bundle mono inside the app.
In order to do this, I used mkbundle2:
mkbundle2 -o bundledapp.exe app.exe --deps
This works without errors, output:
OS is: Darwin
Sources: 1 Auto-dependencies: True
embedding: /Users/kclement/Projects/app/build/app.exe
embedding: /Library/Frameworks/Mono.framework/Versions/2.6.1/lib/mono/2.0/mscorlib.dll
embedding: /Library/Frameworks/Mono.framework/Versions/2.6.1/lib/mono/gac/System/2.0.0.0__b77a5c561934e089/System.dll
embedding: /Library/Frameworks/Mono.framework/Versions/2.6.1/lib/mono/gac/System.Configuration/2.0.0.0__b03f5f7f11d50a3a/System.Configuration.dll
embedding: /Library/Frameworks/Mono.framework/Versions/2.6.1/lib/mono/gac/System.Xml/2.0.0.0__b77a5c561934e089/System.Xml.dll
embedding: /Library/Frameworks/Mono.framework/Versions/2.6.1/lib/mono/gac/System.Security/2.0.0.0__b03f5f7f11d50a3a/System.Security.dll
embedding: /Library/Frameworks/Mono.framework/Versions/2.6.1/lib/mono/gac/Mono.Security/2.0.0.0__0738eb9f132ed756/Mono.Security.dll
Compiling:
as -arch i386 -o temp.o temp.s
cc -g -o bundledapp.exe -Wall temp.c `pkg-config --cflags --libs mono` temp.o
Done
I can execute this on the build machine. When I execute on a machine without mono however, it won't run.
Output:
dyld: Library not loaded: /Library/Frameworks/Mono.framework/Versions/2.6.1/lib/libmono.0.dylib
Referenced from: /Users/kristof/./bundledapp.exe
Reason: image not found
Trace/BPT trap
What am I missing? How do I include the actual mono runtime?
EDIT:
I also tried adding the --static flag.
That gives my app another license however, which I'm not sure I want.
I then no longer complains about libmono, but about libgthread-2.0.0.dylib
So, the problem is that mkbundle links to some file that reside on my mac, where I expected it to bundle them. You can clearly see that by looking up the linked resources with the command:
otool -L ./BundledApp
(where bundledApp is the output of mkbundle2)
In order to fix it, I ended up using the mkbundle nant-tasks from the monobjc project:
http://www.monobjc.net/index.php?page=mkbundle-task
I think they are pretty much an automated version of what I found here:
http://code.google.com/p/cocoa-sharp-dev/wiki/RedistributableAppWithoutInstallingMono
But that gave me exceptions.
The Monobjc nant task works without any issues, and is by far the easiest solution. I still have multiple files but that's ok, at least it works now.
Try running:
mkbundle -o bundledappname program.exe
--deps
(use a different name for the bundle rather than the same as your program.exe and do not put .exe extension to the -o flag)
Also, did you tried macpack ?
For more information on how to create bundles read here and the fine manual of mkbundle

Categories