Author: jonpryor
Date: 2005-05-31 11:37:58 -0400 (Tue, 31 May 2005)
New Revision: 45263

Added:
   trunk/mcs/tools/mono-shlib-cop/
   trunk/mcs/tools/mono-shlib-cop/ChangeLog
   trunk/mcs/tools/mono-shlib-cop/Makefile
   trunk/mcs/tools/mono-shlib-cop/README
   trunk/mcs/tools/mono-shlib-cop/mono-shlib-cop.cs
   trunk/mcs/tools/mono-shlib-cop/mono-shlib-cop.exe.sources
Modified:
   trunk/mcs/tools/ChangeLog
   trunk/mcs/tools/Makefile
Log:
  * tools/Makefile (SUBDIRS): Add mono-shlib-cop to the build.
  * tools/mono-shlib-cop: Added.
  * tools/mono-shlib-cop/ChangeLog: Added.
  * tools/mono-shlib-cop/mono-shlib-cop.exe.sources: Added.
  * tools/mono-shlib-cop/mono-shlib-cop.cs: Added.
  * tools/mono-shlib-cop/Makefile: Added.
  * tools/mono-shlib-cop/README: Added.



Modified: trunk/mcs/tools/ChangeLog
===================================================================
--- trunk/mcs/tools/ChangeLog   2005-05-31 13:37:32 UTC (rev 45262)
+++ trunk/mcs/tools/ChangeLog   2005-05-31 15:37:58 UTC (rev 45263)
@@ -1,3 +1,7 @@
+2005-05-31  Joanthan Pryor  <[EMAIL PROTECTED]>
+
+       * Makefile (SUBDIRS): Add mono-shlib-cop to the build.
+
 2005-05-06  Raja R Harinath  <[EMAIL PROTECTED]>
 
        * Makefile (net_2_0_SUBDIRS): Add 'corcompare'.

Modified: trunk/mcs/tools/Makefile
===================================================================
--- trunk/mcs/tools/Makefile    2005-05-31 13:37:32 UTC (rev 45262)
+++ trunk/mcs/tools/Makefile    2005-05-31 15:37:58 UTC (rev 45263)
@@ -2,7 +2,8 @@
 SUBDIRS = \
        al cilc corcompare ictool mono-xsd security wsdl genxs sqlsharp         
\
        disco soapsuds browsercaps-updater monop gacutil mono-rpm-helpers       
\
-       resgen macpack mkbundle dtd2xsd mjs prj2make mono-service
+       resgen macpack mkbundle dtd2xsd mjs prj2make mono-service \
+       mono-shlib-cop
 
 net_1_1_bootstrap_SUBDIRS = gacutil security
 

Added: trunk/mcs/tools/mono-shlib-cop/ChangeLog
===================================================================
--- trunk/mcs/tools/mono-shlib-cop/ChangeLog    2005-05-31 13:37:32 UTC (rev 
45262)
+++ trunk/mcs/tools/mono-shlib-cop/ChangeLog    2005-05-31 15:37:58 UTC (rev 
45263)
@@ -0,0 +1,9 @@
+2005-05-31  Jonathan Pryor  <[EMAIL PROTECTED]>
+
+       * Makefile: Added
+       * README: Added
+       * ChangeLog: Added
+       * mono-shlib-cop.exe.sources: Added
+       * mono-shlib-cop.exe: Added
+
+# vim: noexpandtab

Added: trunk/mcs/tools/mono-shlib-cop/Makefile
===================================================================
--- trunk/mcs/tools/mono-shlib-cop/Makefile     2005-05-31 13:37:32 UTC (rev 
45262)
+++ trunk/mcs/tools/mono-shlib-cop/Makefile     2005-05-31 15:37:58 UTC (rev 
45263)
@@ -0,0 +1,9 @@
+thisdir = tools/mono-shlib-cop
+SUBDIRS = 
+include ../../build/rules.make
+
+LOCAL_MCS_FLAGS = -r:Mono.Posix.dll -r:Mono.GetOptions.dll
+
+PROGRAM = mono-shlib-cop.exe
+
+include ../../build/executable.make

Added: trunk/mcs/tools/mono-shlib-cop/README
===================================================================
--- trunk/mcs/tools/mono-shlib-cop/README       2005-05-31 13:37:32 UTC (rev 
45262)
+++ trunk/mcs/tools/mono-shlib-cop/README       2005-05-31 15:37:58 UTC (rev 
45263)
@@ -0,0 +1,4 @@
+mono-shlib-cop is a program to check unmanaged assembly dependencies.
+  - Does the unmanaged dependency exist?
+       - Is it a non-devel dependency (e.g. is foo.so.1 used instead of 
foo.so?)
+

Added: trunk/mcs/tools/mono-shlib-cop/mono-shlib-cop.cs
===================================================================
--- trunk/mcs/tools/mono-shlib-cop/mono-shlib-cop.cs    2005-05-31 13:37:32 UTC 
(rev 45262)
+++ trunk/mcs/tools/mono-shlib-cop/mono-shlib-cop.cs    2005-05-31 15:37:58 UTC 
(rev 45263)
@@ -0,0 +1,387 @@
+//
+// mono-shlib-cop.cs: Check unmanaged dependencies
+//
+// Compile as:
+//    mcs mono-shlib-cop.cs -r:Mono.Posix -r:Mono.GetOptions
+//
+// Authors:
+//  Jonathan Pryor ([EMAIL PROTECTED])
+//
+// (C) 2005 Jonathan Pryor
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+//
+// About:
+//    mono-shlib-cop is designed to inspect an assembly and report about
+//    potentially erroneous practices.  In particular, this includes:
+//      - DllImporting a .so which is a symlink (which typically requires the
+//        -devel packages on Linux distros, thus bloating installation and
+//        angering users)
+//      - etc.
+//
+// Implementation:
+//    - Each assembly needs to be loaded into an AppDomain so that we can
+//      adjust the ApplicationBase path (which will allow us to more reliably
+//      load assemblies which depend upon assemblies in the same directory).
+//      We can share AppDomains (1/directory), but we (alas) can't use a
+//      single AppDomain for the entire app.
+//    - Thus, algorithm:
+//      - Create AppDomain with ApplicationBase path set to directory assembly
+//        resides in
+//      - Create an AssemblyChecker instance within the AppDomain
+//      - Check an assembly with AssemblyChecker; get back 
AssemblyCheckResults.
+//      - Merge results together (to eliminate duplicate messages)
+//      - Print results.
+//
+// TODO:
+//    - AppDomain use
+//    - Message merging
+//    - dllmap loading (need to read $prefix/etc/mono/config and
+//      assembly.config files to look up potential maps)
+//    - dllmap caching?  (Is it possible to avoid reading the .config file
+//      into each AppDomain at least once?  OS file caching may keep perf from
+//      dieing with all the potential I/O.)
+//    - Make -r work correctly (-r:Mono.Posix should read Mono.Posix from the
+//      GAC and inspect it.)
+//
+#define TRACE
+
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+using Mono.GetOptions;
+using Mono.Unix;
+
+[assembly: AssemblyTitle ("mono-shlib-cop")]
+[assembly: AssemblyCopyright ("(C) 2005 Jonathan Pryor")]
+[assembly: AssemblyDescription ("Looks up shared library dependencies of 
managed code")]
+[assembly: Mono.Author ("Jonathan Pryor")]
+[assembly: Mono.UsageComplement ("[ASSEMBLY]+ [-r:ASSEMBLY_REF]+")]
+[assembly: Mono.ReportBugsTo ("[EMAIL PROTECTED]")]
+
+namespace Mono.Unmanaged.Check {
+       [Serializable]
+       sealed class AssemblyCheckResults {
+               private ArrayList errors = new ArrayList ();
+               private ArrayList warnings = new ArrayList ();
+
+               public string[] Errors {
+                       get {return (string[]) errors.ToArray (typeof(string));}
+               }
+               public string[] Warnings {
+                       get {return (string[]) warnings.ToArray 
(typeof(string));}
+               }
+
+               internal AssemblyCheckResults ()
+               {
+               }
+
+               internal AssemblyCheckResults (string[] errors, string[] 
warnings)
+               {
+                       this.errors.AddRange (errors);
+                       this.warnings.AddRange (warnings);
+               }
+
+               internal void Check (Type type)
+               {
+                       BindingFlags bf = BindingFlags.Instance | 
BindingFlags.Static | 
+                               BindingFlags.Public | BindingFlags.NonPublic;
+
+                       foreach (MemberInfo mi in type.GetMembers (bf)) {
+                               CheckMember (type, mi);
+                       }
+               }
+
+               private void CheckMember (Type type, MemberInfo mi)
+               {
+                       DllImportAttribute[] dia = null;
+                       switch (mi.MemberType) {
+                               case MemberTypes.Constructor: case 
MemberTypes.Method: {
+                                       MethodBase mb = (MethodBase) mi;
+                                       dia = new 
DllImportAttribute[]{GetDllImportInfo (mb)};
+                                       break;
+                               }
+                               case MemberTypes.Event: {
+                                       EventInfo ei = (EventInfo) mi;
+                                       MethodBase add = ei.GetAddMethod (true);
+                                       MethodBase remove = ei.GetRemoveMethod 
(true);
+                                       dia = new DllImportAttribute[]{
+                                               GetDllImportInfo (add), 
GetDllImportInfo (remove)};
+                                       break;
+                               }
+                               case MemberTypes.Property: {
+                                       PropertyInfo pi = (PropertyInfo) mi;
+                                       MethodInfo[] accessors = 
pi.GetAccessors (true);
+                                       if (accessors == null)
+                                               break;
+                                       dia = new 
DllImportAttribute[accessors.Length];
+                                       for (int i = 0; i < dia.Length; ++i)
+                                               dia [i] = GetDllImportInfo 
(accessors [i]);
+                                       break;
+                               }
+                       }
+                       if (dia == null)
+                               return;
+
+                       foreach (DllImportAttribute d in dia) {
+                               if (d == null)
+                                       continue;
+                               CheckLibrary (type, d.Value);
+                       }
+               }
+
+               private static DllImportAttribute GetDllImportInfo (MethodBase 
method)
+               {
+                       if (method == null)
+                               return null;
+
+                       if ((method.Attributes & MethodAttributes.PinvokeImpl) 
== 0)
+                               return null;
+
+                       try {
+                       // .NET 2.0 synthesizes pseudo-attributes such as 
DllImport
+                       DllImportAttribute dia = (DllImportAttribute) 
Attribute.GetCustomAttribute (method, 
+                                               typeof(DllImportAttribute), 
false);
+                       if (dia != null)
+                               return dia;
+
+                       // We're not on .NET 2.0; assume we're on Mono and use 
some internal
+                       // methods...
+                       Type MonoMethod = Type.GetType 
("System.Reflection.MonoMethod", false);
+                       if (MonoMethod == null) {
+                               return null;
+                       }
+                       MethodInfo GetDllImportAttribute = 
+                               MonoMethod.GetMethod ("GetDllImportAttribute", 
+                                               BindingFlags.Static | 
BindingFlags.NonPublic);
+                       if (GetDllImportAttribute == null) {
+                               return null;
+                       }
+                       IntPtr mhandle = method.MethodHandle.Value;
+                       return (DllImportAttribute) 
GetDllImportAttribute.Invoke (null, 
+                                       new object[]{mhandle});
+                       }
+                       catch (Exception e) {
+                               Trace.WriteLine ("Exception getting 
DllImportAttribute: " + e.ToString());
+                               return null;
+                       }
+               }
+               
+               [DllImport ("libgmodule-2.0.so")]
+               private static extern IntPtr g_module_open (string filename, 
int flags);
+               private static int G_MODULE_BIND_LAZY = 1 << 0;
+               private static int G_MODULE_BIND_LOCAL = 1 << 1;
+               // private static int G_MODULE_BIND_MASK = 0x03;
+
+               [DllImport ("libgmodule-2.0.so")]
+               private static extern int g_module_close (IntPtr handle);
+
+               [DllImport ("libgmodule-2.0.so")]
+               private static extern IntPtr g_module_error ();
+
+               [DllImport ("libgmodule-2.0.so")]
+               private static extern IntPtr g_module_name (IntPtr h);
+
+               [DllImport ("libgmodule-2.0.so")]
+               private static extern IntPtr g_module_build_path (
+                       string directory, string module_name);
+
+               [DllImport ("libglib-2.0.so")]
+               private static extern void g_free (IntPtr mem);
+
+               private void CheckLibrary (Type type, string library)
+               {
+                       Trace.WriteLine ("Trying to load base library: " + 
library);
+                       string found = null;
+                       string error = null;
+                       string soname = null;
+                       foreach (string name in GetLibraryNames (type, 
library)) {
+                               error = null;
+                               IntPtr h = g_module_open (name, 
G_MODULE_BIND_LAZY |
+                                       G_MODULE_BIND_LOCAL);
+                               try {
+                                       Trace.WriteLine ("    Trying library 
name: " + name);
+                                       if (h != IntPtr.Zero) {
+                                               found = name;
+                                               soname = 
Marshal.PtrToStringAnsi (g_module_name (h));
+                                               break;
+                                       }
+                                       error = Marshal.PtrToStringAnsi 
(g_module_error ());
+                                       Trace.WriteLine ("\tError loading 
library `" + name + "': " + error);
+                               }
+                               finally {
+                                       if (h != IntPtr.Zero)
+                                               g_module_close (h);
+                               }
+                       }
+
+                       if (found == null) {
+                               errors.Add ("Unable to load library " + library 
+ ": " + error);
+                               return;
+                       }
+
+                       Trace.WriteLine ("Able to load library " + library + "; 
soname=" +
+                       soname + "; found=" + found);
+                       // UnixFileInfo f = new UnixFileInfo (soname);
+                       if (found.EndsWith (".so")) {
+                               warnings.Add (string.Format ("Type `{0}' 
depends on potential " + 
+                                       "development library `{1}'.", 
type.FullName, found));
+                       }
+               }
+
+               private static string[] GetLibraryNames (Type type, string 
library)
+               {
+                       // TODO: keep in sync with
+                       // mono/metadata/loader.c:mono_lookup_pinvoke_call
+                       ArrayList names = new ArrayList ();
+
+                       string dll_map = GetDllMapEntry (type);
+                       if (dll_map != null) 
+                               names.Add (dll_map);
+
+                       names.Add (library);
+                       int _dll_index = library.LastIndexOf (".dll");
+                       if (_dll_index >= 0)
+                               names.Add (library.Substring (0, _dll_index));
+
+                       if (!library.StartsWith ("lib"))
+                               names.Add ("lib" + library);
+
+                       IntPtr s = g_module_build_path (null, library);
+                       if (s != IntPtr.Zero) {
+                               try {
+                                       names.Add (Marshal.PtrToStringAnsi (s));
+                               }
+                               finally {
+                                       g_free (s);
+                               }
+                       }
+
+                       s = g_module_build_path (".", library);
+                       if (s != IntPtr.Zero) {
+                               try {
+                                       names.Add (Marshal.PtrToStringAnsi (s));
+                               }
+                               finally {
+                                       g_free (s);
+                               }
+                       }
+
+                       return (string[]) names.ToArray (typeof(string));
+               }
+
+               private static string GetDllMapEntry (Type type)
+               {
+                       // TODO
+                       return null;
+               }
+       }
+
+       sealed class AssemblyChecker : MarshalByRefObject {
+
+               public AssemblyCheckResults CheckFile (string file)
+               {
+                       try {
+                               return Check (Assembly.LoadFile (file));
+                       }
+                       catch (FileNotFoundException e) {
+                               return new AssemblyCheckResults (
+                                       new string[]{"Could not load `" + file 
+ "': " + e.Message},
+                                       new string[]{}
+                               );
+                       }
+               }
+
+               private AssemblyCheckResults Check (Assembly a)
+               {
+                       AssemblyCheckResults r = new AssemblyCheckResults ();
+                       foreach (Type t in a.GetTypes ()) {
+                               r.Check (t);
+                       }
+                       return r;
+               }
+
+               public AssemblyCheckResults CheckWithPartialName (string 
partial)
+               {
+                       AssemblyName an = new AssemblyName ();
+                       an.Name = partial;
+                       try {
+                               Assembly a = Assembly.Load (an);
+                               return Check (a);
+                       }
+                       catch (FileNotFoundException e) {
+                               return new AssemblyCheckResults (
+                                       new string[]{"Could not load assembly 
reference `" + partial + "': " + e.Message}, 
+                                       new string[]{}
+                               );
+                       }
+               }
+       }
+
+       class MyOptions : Options {
+               [Option (int.MaxValue, "Assemblies to load by partial names 
(e.g. from the GAC)", 'r')]
+               public string[] references = new string[]{};
+
+       }
+
+       class Runner {
+               public static void Main (string[] args)
+               {
+#if TEST
+                       AssemblyCheckResults r = new AssemblyCheckResults ();
+                       foreach (Type t in 
Assembly.GetExecutingAssembly().GetTypes()) {
+                               Trace.WriteLine ("Checking Type: " + 
t.FullName);
+                               r.Check (t);
+                       }
+
+                       PrintResults (r);
+#else
+                       MyOptions o = new MyOptions ();
+                       o.ProcessArgs (args);
+
+                       AssemblyChecker checker = new AssemblyChecker ();
+                       foreach (string assembly in o.RemainingArguments) {
+                               PrintResults (checker.CheckFile (assembly));
+                       }
+
+                       foreach (string assembly in o.references) {
+                               PrintResults (checker.CheckWithPartialName 
(assembly));
+                       }
+#endif
+               }
+
+               private static void PrintResults (AssemblyCheckResults acr)
+               {
+                       foreach (string s in acr.Errors)
+                               Console.WriteLine ("error: " + s);
+                       foreach (string s in acr.Warnings)
+                               Console.WriteLine ("warning: " + s);
+               }
+       }
+}
+

Added: trunk/mcs/tools/mono-shlib-cop/mono-shlib-cop.exe.sources
===================================================================
--- trunk/mcs/tools/mono-shlib-cop/mono-shlib-cop.exe.sources   2005-05-31 
13:37:32 UTC (rev 45262)
+++ trunk/mcs/tools/mono-shlib-cop/mono-shlib-cop.exe.sources   2005-05-31 
15:37:58 UTC (rev 45263)
@@ -0,0 +1 @@
+mono-shlib-cop.cs

_______________________________________________
Mono-patches maillist  -  [email protected]
http://lists.ximian.com/mailman/listinfo/mono-patches

Reply via email to