1 // 2 // assembly.cs: Assembly declaration and specifications 3 // 4 // Authors: 5 // Miguel de Icaza (miguel@ximian.com) 6 // Marek Safar (marek.safar@gmail.com) 7 // 8 // Copyright 2001, 2002, 2003 Ximian, Inc. 9 // Copyright 2004-2011 Novell, Inc. 10 // Copyright 2011-2013 Xamarin Inc 11 // 12 13 14 using System; 15 using System.IO; 16 using System.Collections.Generic; 17 using System.Globalization; 18 using System.Security; 19 using System.Security.Cryptography; 20 using System.Security.Permissions; 21 using Mono.Security.Cryptography; 22 using Mono.CompilerServices.SymbolWriter; 23 using System.Linq; 24 25 #if STATIC 26 using IKVM.Reflection; 27 using IKVM.Reflection.Emit; 28 using SecurityType = System.Collections.Generic.List<IKVM.Reflection.Emit.CustomAttributeBuilder>; 29 #else 30 using SecurityType = System.Collections.Generic.Dictionary<System.Security.Permissions.SecurityAction, System.Security.PermissionSet>; 31 using System.Reflection; 32 using System.Reflection.Emit; 33 #endif 34 35 namespace Mono.CSharp 36 { 37 public interface IAssemblyDefinition 38 { 39 string FullName { get; } 40 bool IsCLSCompliant { get; } 41 bool IsMissing { get; } 42 string Name { get; } 43 GetPublicKeyToken()44 byte[] GetPublicKeyToken (); IsFriendAssemblyTo(IAssemblyDefinition assembly)45 bool IsFriendAssemblyTo (IAssemblyDefinition assembly); 46 } 47 48 public class AssemblyReferenceMessageInfo 49 { AssemblyReferenceMessageInfo(AssemblyName dependencyName, Action<Report> reportMessage)50 public AssemblyReferenceMessageInfo (AssemblyName dependencyName, Action<Report> reportMessage) 51 { 52 this.DependencyName = dependencyName; 53 this.ReportMessage = reportMessage; 54 } 55 56 public AssemblyName DependencyName { get; private set; } 57 public Action<Report> ReportMessage { get; private set; } 58 } 59 60 public abstract class AssemblyDefinition : IAssemblyDefinition 61 { 62 // TODO: make it private and move all builder based methods here 63 public AssemblyBuilder Builder; 64 protected AssemblyBuilderExtension builder_extra; 65 MonoSymbolFile symbol_writer; 66 67 bool is_cls_compliant; 68 bool wrap_non_exception_throws; 69 bool wrap_non_exception_throws_custom; 70 bool has_user_debuggable; 71 72 protected ModuleContainer module; 73 readonly string name; 74 protected readonly string file_name; 75 76 byte[] public_key, public_key_token; 77 bool delay_sign; 78 79 // Holds private/public key pair when private key 80 // was available 81 StrongNameKeyPair private_key; 82 83 Attribute cls_attribute; 84 Method entry_point; 85 86 protected List<ImportedModuleDefinition> added_modules; 87 SecurityType declarative_security; 88 Dictionary<ITypeDefinition, Attribute> emitted_forwarders; 89 AssemblyAttributesPlaceholder module_target_attrs; 90 91 // Win32 version info values 92 string vi_product, vi_product_version, vi_company, vi_copyright, vi_trademark; 93 string pa_file_version, pa_assembly_version; 94 AssemblyDefinition(ModuleContainer module, string name)95 protected AssemblyDefinition (ModuleContainer module, string name) 96 { 97 this.module = module; 98 this.name = Path.GetFileNameWithoutExtension (name); 99 100 wrap_non_exception_throws = true; 101 102 delay_sign = Compiler.Settings.StrongNameDelaySign; 103 104 // 105 // Load strong name key early enough for assembly importer to be able to 106 // use the keys for InternalsVisibleTo 107 // This should go somewhere close to ReferencesLoading but don't have the place yet 108 // 109 if (Compiler.Settings.HasKeyFileOrContainer) { 110 LoadPublicKey (Compiler.Settings.StrongNameKeyFile, Compiler.Settings.StrongNameKeyContainer); 111 } 112 } 113 AssemblyDefinition(ModuleContainer module, string name, string fileName)114 protected AssemblyDefinition (ModuleContainer module, string name, string fileName) 115 : this (module, name) 116 { 117 this.file_name = fileName; 118 } 119 120 #region Properties 121 122 public Attribute CLSCompliantAttribute { 123 get { 124 return cls_attribute; 125 } 126 } 127 128 public CompilerContext Compiler { 129 get { 130 return module.Compiler; 131 } 132 } 133 134 // 135 // Assembly entry point, aka Main method 136 // 137 public Method EntryPoint { 138 get { 139 return entry_point; 140 } 141 set { 142 entry_point = value; 143 } 144 } 145 146 public string FullName { 147 get { 148 return Builder.FullName; 149 } 150 } 151 152 public bool HasCLSCompliantAttribute { 153 get { 154 return cls_attribute != null; 155 } 156 } 157 158 // TODO: This should not exist here but will require more changes 159 public MetadataImporter Importer { 160 get; set; 161 } 162 163 public bool IsCLSCompliant { 164 get { 165 return is_cls_compliant; 166 } 167 } 168 169 bool IAssemblyDefinition.IsMissing { 170 get { 171 return false; 172 } 173 } 174 175 public bool IsSatelliteAssembly { get; private set; } 176 177 public string Name { 178 get { 179 return name; 180 } 181 } 182 183 public bool WrapNonExceptionThrows { 184 get { 185 return wrap_non_exception_throws; 186 } 187 } 188 189 protected Report Report { 190 get { 191 return Compiler.Report; 192 } 193 } 194 195 public MonoSymbolFile SymbolWriter { 196 get { 197 return symbol_writer; 198 } 199 } 200 201 #endregion 202 AddModule(ImportedModuleDefinition module)203 public void AddModule (ImportedModuleDefinition module) 204 { 205 if (added_modules == null) { 206 added_modules = new List<ImportedModuleDefinition> (); 207 added_modules.Add (module); 208 } 209 } 210 ApplyAttributeBuilder(Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa)211 public void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa) 212 { 213 if (a.IsValidSecurityAttribute ()) { 214 a.ExtractSecurityPermissionSet (ctor, ref declarative_security); 215 return; 216 } 217 218 if (a.Type == pa.AssemblyCulture) { 219 string value = a.GetString (); 220 if (value == null || value.Length == 0) 221 return; 222 223 if (Compiler.Settings.Target == Target.Exe) { 224 Report.Error (7059, a.Location, "Executables cannot be satellite assemblies. Remove the attribute or keep it empty"); 225 return; 226 } 227 228 if (value == "neutral") 229 value = ""; 230 231 if (Compiler.Settings.Target == Target.Module) { 232 SetCustomAttribute (ctor, cdata); 233 } else { 234 builder_extra.SetCulture (value, a.Location); 235 } 236 237 IsSatelliteAssembly = true; 238 return; 239 } 240 241 if (a.Type == pa.AssemblyVersion) { 242 string value = a.GetString (); 243 if (value == null || value.Length == 0) 244 return; 245 246 var vinfo = IsValidAssemblyVersion (value, true); 247 if (vinfo == null) { 248 Report.Error (7034, a.Location, "The specified version string `{0}' does not conform to the required format - major[.minor[.build[.revision]]]", 249 value); 250 return; 251 } 252 253 if (Compiler.Settings.Target == Target.Module) { 254 SetCustomAttribute (ctor, cdata); 255 } else { 256 builder_extra.SetVersion (vinfo, a.Location); 257 pa_assembly_version = vinfo.ToString (); 258 } 259 260 return; 261 } 262 263 if (a.Type == pa.AssemblyAlgorithmId) { 264 const int pos = 2; // skip CA header 265 uint alg = (uint) cdata [pos]; 266 alg |= ((uint) cdata [pos + 1]) << 8; 267 alg |= ((uint) cdata [pos + 2]) << 16; 268 alg |= ((uint) cdata [pos + 3]) << 24; 269 270 if (Compiler.Settings.Target == Target.Module) { 271 SetCustomAttribute (ctor, cdata); 272 } else { 273 builder_extra.SetAlgorithmId (alg, a.Location); 274 } 275 276 return; 277 } 278 279 if (a.Type == pa.AssemblyFlags) { 280 const int pos = 2; // skip CA header 281 uint flags = (uint) cdata[pos]; 282 flags |= ((uint) cdata [pos + 1]) << 8; 283 flags |= ((uint) cdata [pos + 2]) << 16; 284 flags |= ((uint) cdata [pos + 3]) << 24; 285 286 // Ignore set PublicKey flag if assembly is not strongnamed 287 if ((flags & (uint) AssemblyNameFlags.PublicKey) != 0 && public_key == null) 288 flags &= ~(uint) AssemblyNameFlags.PublicKey; 289 290 if (Compiler.Settings.Target == Target.Module) { 291 SetCustomAttribute (ctor, cdata); 292 } else { 293 builder_extra.SetFlags (flags, a.Location); 294 } 295 296 return; 297 } 298 299 if (a.Type == pa.TypeForwarder) { 300 TypeSpec t = a.GetArgumentType (); 301 if (t == null || TypeManager.HasElementType (t)) { 302 Report.Error (735, a.Location, "Invalid type specified as an argument for TypeForwardedTo attribute"); 303 return; 304 } 305 306 if (emitted_forwarders == null) { 307 emitted_forwarders = new Dictionary<ITypeDefinition, Attribute> (); 308 } else if (emitted_forwarders.ContainsKey (t.MemberDefinition)) { 309 Report.SymbolRelatedToPreviousError (emitted_forwarders[t.MemberDefinition].Location, null); 310 Report.Error (739, a.Location, "A duplicate type forward of type `{0}'", 311 t.GetSignatureForError ()); 312 return; 313 } 314 315 emitted_forwarders.Add (t.MemberDefinition, a); 316 317 if (t.MemberDefinition.DeclaringAssembly == this) { 318 Report.SymbolRelatedToPreviousError (t); 319 Report.Error (729, a.Location, "Cannot forward type `{0}' because it is defined in this assembly", 320 t.GetSignatureForError ()); 321 return; 322 } 323 324 if (t.IsNested) { 325 Report.Error (730, a.Location, "Cannot forward type `{0}' because it is a nested type", 326 t.GetSignatureForError ()); 327 return; 328 } 329 330 AddTypeForwarders (t, a.Location); 331 return; 332 } 333 334 if (a.Type == pa.Extension) { 335 a.Error_MisusedExtensionAttribute (); 336 return; 337 } 338 339 if (a.Type == pa.InternalsVisibleTo) { 340 string assembly_name = a.GetString (); 341 if (assembly_name == null) { 342 Report.Error (7030, a.Location, "Friend assembly reference cannot have `null' value"); 343 return; 344 } 345 346 if (assembly_name.Length == 0) 347 return; 348 #if STATIC 349 ParsedAssemblyName aname; 350 ParseAssemblyResult r = Fusion.ParseAssemblyName (assembly_name, out aname); 351 if (r != ParseAssemblyResult.OK) { 352 Report.Warning (1700, 3, a.Location, "Friend assembly reference `{0}' is invalid and cannot be resolved", 353 assembly_name); 354 return; 355 } 356 357 if (aname.Version != null || aname.Culture != null || aname.ProcessorArchitecture != ProcessorArchitecture.None) { 358 Report.Error (1725, a.Location, 359 "Friend assembly reference `{0}' is invalid. InternalsVisibleTo declarations cannot have a version, culture or processor architecture specified", 360 assembly_name); 361 362 return; 363 } 364 365 if (public_key != null && !aname.HasPublicKey) { 366 Report.Error (1726, a.Location, 367 "Friend assembly reference `{0}' is invalid. Strong named assemblies must specify a public key in their InternalsVisibleTo declarations", 368 assembly_name); 369 return; 370 } 371 #endif 372 } else if (a.Type == pa.RuntimeCompatibility) { 373 wrap_non_exception_throws_custom = true; 374 } else if (a.Type == pa.AssemblyFileVersion) { 375 pa_file_version = a.GetString (); 376 if (string.IsNullOrEmpty (pa_file_version) || IsValidAssemblyVersion (pa_file_version, false) == null) { 377 Report.Warning (7035, 1, a.Location, "The specified version string `{0}' does not conform to the recommended format major.minor.build.revision", 378 pa_file_version, a.Name); 379 return; 380 } 381 382 // File version info decoding from blob is not supported 383 var cab = new CustomAttributeBuilder ((ConstructorInfo)ctor.GetMetaInfo (), new object [] { pa_file_version }); 384 Builder.SetCustomAttribute (cab); 385 return; 386 } else if (a.Type == pa.AssemblyProduct) { 387 vi_product = a.GetString (); 388 } else if (a.Type == pa.AssemblyCompany) { 389 vi_company = a.GetString (); 390 } else if (a.Type == pa.AssemblyCopyright) { 391 vi_copyright = a.GetString (); 392 } else if (a.Type == pa.AssemblyTrademark) { 393 vi_trademark = a.GetString (); 394 } else if (a.Type == pa.Debuggable) { 395 has_user_debuggable = true; 396 } else if (a.Type == pa.AssemblyInformationalVersion) { 397 vi_product_version = a.GetString (); 398 } 399 400 // 401 // Win32 version info attributes AssemblyDescription and AssemblyTitle cannot be 402 // set using public API and because we have blob like attributes we need to use 403 // special option DecodeVersionInfoAttributeBlobs to support values extraction 404 // 405 406 SetCustomAttribute (ctor, cdata); 407 } 408 AddTypeForwarders(TypeSpec type, Location loc)409 void AddTypeForwarders (TypeSpec type, Location loc) 410 { 411 builder_extra.AddTypeForwarder (type.GetDefinition (), loc); 412 413 var ntypes = MemberCache.GetDeclaredNestedTypes (type); 414 if (ntypes == null) 415 return; 416 417 foreach (var nested in ntypes) { 418 if (nested.IsPrivate) 419 continue; 420 421 AddTypeForwarders (nested, loc); 422 } 423 } 424 425 // 426 // When using assembly public key attributes InternalsVisibleTo key 427 // was not checked, we have to do it later when we actually know what 428 // our public key token is 429 // CheckReferencesPublicToken()430 void CheckReferencesPublicToken () 431 { 432 var references = builder_extra.GetReferencedAssemblies (); 433 foreach (var an in references) { 434 if (public_key != null && an.GetPublicKey ().Length == 0) { 435 Report.Error (1577, "Referenced assembly `{0}' does not have a strong name", 436 an.FullName); 437 } 438 439 var ci = an.CultureInfo; 440 if (!ci.Equals (CultureInfo.InvariantCulture)) { 441 Report.Warning (8009, 1, "Referenced assembly `{0}' has different culture setting of `{1}'", 442 an.Name, ci.Name); 443 } 444 445 var ia = Importer.GetImportedAssemblyDefinition (an); 446 if (ia == null) 447 continue; 448 449 var an_references = GetNotUnifiedReferences (an); 450 if (an_references != null) { 451 foreach (var r in an_references) { 452 // 453 // Secondary check when assembly references is resolved but not used. For example 454 // due to type-forwarding 455 // 456 if (references.Any (l => l.Name == r.DependencyName.Name)) { 457 r.ReportMessage (Report); 458 } 459 } 460 } 461 462 if (!ia.IsFriendAssemblyTo (this)) 463 continue; 464 465 var attr = ia.GetAssemblyVisibleToName (this); 466 var atoken = attr.GetPublicKeyToken (); 467 468 if (ArrayComparer.IsEqual (GetPublicKeyToken (), atoken)) 469 continue; 470 471 Report.SymbolRelatedToPreviousError (ia.Location); 472 Report.Error (281, 473 "Friend access was granted to `{0}', but the output assembly is named `{1}'. Try adding a reference to `{0}' or change the output assembly name to match it", 474 attr.FullName, FullName); 475 } 476 } 477 CreateAssemblyName()478 protected AssemblyName CreateAssemblyName () 479 { 480 var an = new AssemblyName (name); 481 482 if (public_key != null && Compiler.Settings.Target != Target.Module) { 483 if (delay_sign) { 484 an.SetPublicKey (public_key); 485 } else { 486 if (public_key.Length == 16) { 487 Report.Error (1606, "Could not sign the assembly. ECMA key can only be used to delay-sign assemblies"); 488 } else if (private_key == null) { 489 Error_AssemblySigning ("The specified key file does not have a private key"); 490 } else { 491 an.KeyPair = private_key; 492 } 493 } 494 } 495 496 return an; 497 } 498 CreateModuleBuilder()499 public virtual ModuleBuilder CreateModuleBuilder () 500 { 501 if (file_name == null) 502 throw new NotSupportedException ("transient module in static assembly"); 503 504 var module_name = Path.GetFileName (file_name); 505 506 // Always initialize module without symbolInfo. We could be framework dependent 507 // but returned ISymbolWriter does not have all what we need therefore some 508 // adaptor will be needed for now we alwayas emit MDB format when generating 509 // debug info 510 return Builder.DefineDynamicModule (module_name, module_name, false); 511 } 512 Emit()513 public virtual void Emit () 514 { 515 if (Compiler.Settings.Target == Target.Module) { 516 module_target_attrs = new AssemblyAttributesPlaceholder (module, name); 517 module_target_attrs.CreateContainer (); 518 module_target_attrs.DefineContainer (); 519 module_target_attrs.Define (); 520 module.AddCompilerGeneratedClass (module_target_attrs); 521 } else if (added_modules != null) { 522 ReadModulesAssemblyAttributes (); 523 } 524 525 if (Compiler.Settings.GenerateDebugInfo) { 526 symbol_writer = new MonoSymbolFile (); 527 } 528 529 module.EmitContainer (); 530 531 if (module.HasExtensionMethod) { 532 var pa = module.PredefinedAttributes.Extension; 533 if (pa.IsDefined) { 534 SetCustomAttribute (pa.Constructor, AttributeEncoder.Empty); 535 } 536 } 537 538 if (!IsSatelliteAssembly) { 539 if (!has_user_debuggable && Compiler.Settings.GenerateDebugInfo) { 540 var pa = module.PredefinedAttributes.Debuggable; 541 if (pa.IsDefined) { 542 var modes = System.Diagnostics.DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints; 543 if (!Compiler.Settings.Optimize) 544 modes |= System.Diagnostics.DebuggableAttribute.DebuggingModes.DisableOptimizations; 545 546 pa.EmitAttribute (Builder, modes); 547 } 548 } 549 550 if (!wrap_non_exception_throws_custom) { 551 PredefinedAttribute pa = module.PredefinedAttributes.RuntimeCompatibility; 552 if (pa.IsDefined && pa.ResolveBuilder ()) { 553 var prop = module.PredefinedMembers.RuntimeCompatibilityWrapNonExceptionThrows.Get (); 554 if (prop != null) { 555 AttributeEncoder encoder = new AttributeEncoder (); 556 encoder.EncodeNamedPropertyArgument (prop, new BoolLiteral (Compiler.BuiltinTypes, true, Location.Null)); 557 SetCustomAttribute (pa.Constructor, encoder.ToArray ()); 558 } 559 } 560 } 561 562 if (declarative_security != null) { 563 #if STATIC 564 foreach (var entry in declarative_security) { 565 Builder.__AddDeclarativeSecurity (entry); 566 } 567 #else 568 throw new NotSupportedException ("Assembly-level security"); 569 #endif 570 } 571 } 572 573 CheckReferencesPublicToken (); 574 575 SetEntryPoint (); 576 } 577 GetPublicKeyToken()578 public byte[] GetPublicKeyToken () 579 { 580 if (public_key == null || public_key_token != null) 581 return public_key_token; 582 583 HashAlgorithm ha = SHA1.Create (); 584 byte[] hash = ha.ComputeHash (public_key); 585 // we need the last 8 bytes in reverse order 586 public_key_token = new byte[8]; 587 Buffer.BlockCopy (hash, hash.Length - 8, public_key_token, 0, 8); 588 Array.Reverse (public_key_token, 0, 8); 589 return public_key_token; 590 } 591 GetNotUnifiedReferences(AssemblyName assemblyName)592 protected virtual List<AssemblyReferenceMessageInfo> GetNotUnifiedReferences (AssemblyName assemblyName) 593 { 594 return null; 595 } 596 597 // 598 // Either keyFile or keyContainer has to be non-null 599 // LoadPublicKey(string keyFile, string keyContainer)600 void LoadPublicKey (string keyFile, string keyContainer) 601 { 602 if (keyContainer != null) { 603 try { 604 private_key = new StrongNameKeyPair (keyContainer); 605 public_key = private_key.PublicKey; 606 } catch { 607 Error_AssemblySigning ("The specified key container `" + keyContainer + "' does not exist"); 608 } 609 610 return; 611 } 612 613 bool key_file_exists = File.Exists (keyFile); 614 615 // 616 // For attribute based KeyFile do additional lookup 617 // in output assembly path 618 // 619 if (!key_file_exists && Compiler.Settings.StrongNameKeyFile == null) { 620 // 621 // The key file can be relative to output assembly 622 // 623 string test_path = Path.Combine (Path.GetDirectoryName (file_name), keyFile); 624 key_file_exists = File.Exists (test_path); 625 if (key_file_exists) 626 keyFile = test_path; 627 } 628 629 if (!key_file_exists) { 630 Error_AssemblySigning ("The specified key file `" + keyFile + "' does not exist"); 631 return; 632 } 633 634 using (FileStream fs = new FileStream (keyFile, FileMode.Open, FileAccess.Read)) { 635 byte[] snkeypair = new byte[fs.Length]; 636 fs.Read (snkeypair, 0, snkeypair.Length); 637 638 // check for ECMA key 639 if (snkeypair.Length == 16) { 640 public_key = snkeypair; 641 return; 642 } 643 644 try { 645 // take it, with or without, a private key 646 RSA rsa = CryptoConvert.FromCapiKeyBlob (snkeypair); 647 // and make sure we only feed the public part to Sys.Ref 648 byte[] publickey = CryptoConvert.ToCapiPublicKeyBlob (rsa); 649 650 // AssemblyName.SetPublicKey requires an additional header 651 byte[] publicKeyHeader = new byte[8] { 0x00, 0x24, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00 }; 652 653 // Encode public key 654 public_key = new byte[12 + publickey.Length]; 655 Buffer.BlockCopy (publicKeyHeader, 0, public_key, 0, publicKeyHeader.Length); 656 657 // Length of Public Key (in bytes) 658 int lastPart = public_key.Length - 12; 659 public_key[8] = (byte) (lastPart & 0xFF); 660 public_key[9] = (byte) ((lastPart >> 8) & 0xFF); 661 public_key[10] = (byte) ((lastPart >> 16) & 0xFF); 662 public_key[11] = (byte) ((lastPart >> 24) & 0xFF); 663 664 Buffer.BlockCopy (publickey, 0, public_key, 12, publickey.Length); 665 } catch { 666 Error_AssemblySigning ("The specified key file `" + keyFile + "' has incorrect format"); 667 return; 668 } 669 670 if (delay_sign) 671 return; 672 673 try { 674 // TODO: Is there better way to test for a private key presence ? 675 CryptoConvert.FromCapiPrivateKeyBlob (snkeypair); 676 private_key = new StrongNameKeyPair (snkeypair); 677 } catch { } 678 } 679 } 680 ReadModulesAssemblyAttributes()681 void ReadModulesAssemblyAttributes () 682 { 683 foreach (var m in added_modules) { 684 var cattrs = m.ReadAssemblyAttributes (); 685 if (cattrs == null) 686 continue; 687 688 module.OptAttributes.AddAttributes (cattrs); 689 } 690 } 691 Resolve()692 public void Resolve () 693 { 694 if (Compiler.Settings.Unsafe && module.PredefinedTypes.SecurityAction.Define ()) { 695 // 696 // Emits [assembly: SecurityPermissionAttribute (SecurityAction.RequestMinimum, SkipVerification = true)] 697 // when -unsafe option was specified 698 // 699 Location loc = Location.Null; 700 701 MemberAccess system_security_permissions = new MemberAccess (new MemberAccess ( 702 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Security", loc), "Permissions", loc); 703 704 var req_min = module.PredefinedMembers.SecurityActionRequestMinimum.Resolve (loc); 705 706 Arguments pos = new Arguments (1); 707 pos.Add (new Argument (req_min.GetConstant (null))); 708 709 Arguments named = new Arguments (1); 710 named.Add (new NamedArgument ("SkipVerification", loc, new BoolLiteral (Compiler.BuiltinTypes, true, loc))); 711 712 Attribute g = new Attribute ("assembly", 713 new MemberAccess (system_security_permissions, "SecurityPermissionAttribute"), 714 new Arguments[] { pos, named }, loc, false); 715 g.AttachTo (module, module); 716 717 // Disable no-location warnings (e.g. obsolete) for compiler generated attribute 718 Compiler.Report.DisableReporting (); 719 try { 720 var ctor = g.Resolve (); 721 if (ctor != null) { 722 g.ExtractSecurityPermissionSet (ctor, ref declarative_security); 723 } 724 } finally { 725 Compiler.Report.EnableReporting (); 726 } 727 } 728 729 if (module.OptAttributes == null) 730 return; 731 732 // Ensure that we only have GlobalAttributes, since the Search isn't safe with other types. 733 if (!module.OptAttributes.CheckTargets()) 734 return; 735 736 cls_attribute = module.ResolveAssemblyAttribute (module.PredefinedAttributes.CLSCompliant); 737 738 if (cls_attribute != null) { 739 is_cls_compliant = cls_attribute.GetClsCompliantAttributeValue (); 740 } 741 742 if (added_modules != null && Compiler.Settings.VerifyClsCompliance && is_cls_compliant) { 743 foreach (var m in added_modules) { 744 if (!m.IsCLSCompliant) { 745 Report.Error (3013, 746 "Added modules must be marked with the CLSCompliant attribute to match the assembly", 747 m.Name); 748 } 749 } 750 } 751 752 Attribute a = module.ResolveAssemblyAttribute (module.PredefinedAttributes.RuntimeCompatibility); 753 if (a != null) { 754 var val = a.GetNamedValue ("WrapNonExceptionThrows") as BoolConstant; 755 if (val != null) 756 wrap_non_exception_throws = val.Value; 757 } 758 } 759 ResolveAssemblySecurityAttributes()760 protected void ResolveAssemblySecurityAttributes () 761 { 762 string key_file = null; 763 string key_container = null; 764 765 if (module.OptAttributes != null) { 766 foreach (Attribute a in module.OptAttributes.Attrs) { 767 // cannot rely on any resolve-based members before you call Resolve 768 if (a.ExplicitTarget != "assembly") 769 continue; 770 771 // TODO: This code is buggy: comparing Attribute name without resolving is wrong. 772 // However, this is invoked by CodeGen.Init, when none of the namespaces 773 // are loaded yet. 774 // TODO: Does not handle quoted attributes properly 775 switch (a.Name) { 776 case "AssemblyKeyFile": 777 case "AssemblyKeyFileAttribute": 778 case "System.Reflection.AssemblyKeyFileAttribute": 779 if (Compiler.Settings.StrongNameKeyFile != null) { 780 Report.SymbolRelatedToPreviousError (a.Location, a.GetSignatureForError ()); 781 Report.Warning (1616, 1, "Option `{0}' overrides attribute `{1}' given in a source file or added module", 782 "keyfile", "System.Reflection.AssemblyKeyFileAttribute"); 783 } else { 784 string value = a.GetString (); 785 if (!string.IsNullOrEmpty (value)) { 786 Error_ObsoleteSecurityAttribute (a, "keyfile"); 787 key_file = value; 788 } 789 } 790 break; 791 case "AssemblyKeyName": 792 case "AssemblyKeyNameAttribute": 793 case "System.Reflection.AssemblyKeyNameAttribute": 794 if (Compiler.Settings.StrongNameKeyContainer != null) { 795 Report.SymbolRelatedToPreviousError (a.Location, a.GetSignatureForError ()); 796 Report.Warning (1616, 1, "Option `{0}' overrides attribute `{1}' given in a source file or added module", 797 "keycontainer", "System.Reflection.AssemblyKeyNameAttribute"); 798 } else { 799 string value = a.GetString (); 800 if (!string.IsNullOrEmpty (value)) { 801 Error_ObsoleteSecurityAttribute (a, "keycontainer"); 802 key_container = value; 803 } 804 } 805 break; 806 case "AssemblyDelaySign": 807 case "AssemblyDelaySignAttribute": 808 case "System.Reflection.AssemblyDelaySignAttribute": 809 bool b = a.GetBoolean (); 810 if (b) { 811 Error_ObsoleteSecurityAttribute (a, "delaysign"); 812 } 813 814 delay_sign = b; 815 break; 816 } 817 } 818 } 819 820 // We came here only to report assembly attributes warnings 821 if (public_key != null) 822 return; 823 824 // 825 // Load the strong key file found in attributes when no 826 // command line key was given 827 // 828 if (key_file != null || key_container != null) { 829 LoadPublicKey (key_file, key_container); 830 } else if (delay_sign) { 831 Report.Warning (1607, 1, "Delay signing was requested but no key file was given"); 832 } 833 } 834 EmbedResources()835 public void EmbedResources () 836 { 837 // 838 // Add Win32 resources 839 // 840 if (Compiler.Settings.Win32ResourceFile != null) { 841 Builder.DefineUnmanagedResource (Compiler.Settings.Win32ResourceFile); 842 } else { 843 Builder.DefineVersionInfoResource (vi_product, 844 vi_product_version ?? pa_file_version ?? pa_assembly_version, 845 vi_company, 846 vi_copyright, 847 vi_trademark); 848 } 849 850 if (Compiler.Settings.Win32IconFile != null) { 851 builder_extra.DefineWin32IconResource (Compiler.Settings.Win32IconFile); 852 } 853 854 if (Compiler.Settings.Resources != null) { 855 if (Compiler.Settings.Target == Target.Module) { 856 Report.Error (1507, "Cannot link resource file when building a module"); 857 } else { 858 int counter = 0; 859 foreach (var res in Compiler.Settings.Resources) { 860 if (!File.Exists (res.FileName)) { 861 Report.Error (1566, "Error reading resource file `{0}'", res.FileName); 862 continue; 863 } 864 865 if (res.IsEmbeded) { 866 Stream stream; 867 if (counter++ < 10) { 868 stream = File.OpenRead (res.FileName); 869 } else { 870 // TODO: SRE API requires resource stream to be available during AssemblyBuilder::Save 871 // we workaround it by reading everything into memory to compile projects with 872 // many embedded resource (over 3500) references 873 stream = new MemoryStream (File.ReadAllBytes (res.FileName)); 874 } 875 876 module.Builder.DefineManifestResource (res.Name, stream, res.Attributes); 877 } else { 878 Builder.AddResourceFile (res.Name, Path.GetFileName (res.FileName), res.Attributes); 879 } 880 } 881 } 882 } 883 } 884 Save()885 public void Save () 886 { 887 PortableExecutableKinds pekind = PortableExecutableKinds.ILOnly; 888 ImageFileMachine machine; 889 890 switch (Compiler.Settings.Platform) { 891 case Platform.X86: 892 pekind |= PortableExecutableKinds.Required32Bit; 893 machine = ImageFileMachine.I386; 894 break; 895 case Platform.X64: 896 pekind |= PortableExecutableKinds.PE32Plus; 897 machine = ImageFileMachine.AMD64; 898 break; 899 case Platform.IA64: 900 machine = ImageFileMachine.IA64; 901 break; 902 case Platform.AnyCPU32Preferred: 903 #if STATIC 904 pekind |= PortableExecutableKinds.Preferred32Bit; 905 machine = ImageFileMachine.I386; 906 break; 907 #else 908 throw new NotSupportedException (); 909 #endif 910 case Platform.Arm: 911 #if STATIC 912 machine = ImageFileMachine.ARM; 913 break; 914 #else 915 throw new NotSupportedException (); 916 #endif 917 case Platform.AnyCPU: 918 default: 919 machine = ImageFileMachine.I386; 920 break; 921 } 922 923 Compiler.TimeReporter.Start (TimeReporter.TimerType.OutputSave); 924 try { 925 if (Compiler.Settings.Target == Target.Module) { 926 SaveModule (pekind, machine); 927 } else { 928 Builder.Save (module.Builder.ScopeName, pekind, machine); 929 } 930 } catch (ArgumentOutOfRangeException) { 931 Report.Error (16, "Output file `{0}' exceeds the 4GB limit", name); 932 } catch (Exception e) { 933 Report.Error (16, "Could not write to file `{0}'. {1}", name, e.Message); 934 } 935 Compiler.TimeReporter.Stop (TimeReporter.TimerType.OutputSave); 936 937 // Save debug symbols file 938 if (symbol_writer != null && Compiler.Report.Errors == 0) { 939 // TODO: it should run in parallel 940 Compiler.TimeReporter.Start (TimeReporter.TimerType.DebugSave); 941 942 var filename = file_name + ".mdb"; 943 try { 944 // We mmap the file, so unlink the previous version since it may be in use 945 File.Delete (filename); 946 } catch { 947 // We can safely ignore 948 } 949 950 module.WriteDebugSymbol (symbol_writer); 951 952 using (FileStream fs = new FileStream (filename, FileMode.Create, FileAccess.Write)) { 953 symbol_writer.CreateSymbolFile (module.Builder.ModuleVersionId, fs); 954 } 955 956 Compiler.TimeReporter.Stop (TimeReporter.TimerType.DebugSave); 957 } 958 } 959 SaveModule(PortableExecutableKinds pekind, ImageFileMachine machine)960 protected virtual void SaveModule (PortableExecutableKinds pekind, ImageFileMachine machine) 961 { 962 Report.RuntimeMissingSupport (Location.Null, "-target:module"); 963 } 964 SetCustomAttribute(MethodSpec ctor, byte[] data)965 void SetCustomAttribute (MethodSpec ctor, byte[] data) 966 { 967 if (module_target_attrs != null) 968 module_target_attrs.AddAssemblyAttribute (ctor, data); 969 else 970 Builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), data); 971 } 972 SetEntryPoint()973 void SetEntryPoint () 974 { 975 if (!Compiler.Settings.NeedsEntryPoint) { 976 if (Compiler.Settings.MainClass != null) 977 Report.Error (2017, "Cannot specify -main if building a module or library"); 978 979 return; 980 } 981 982 PEFileKinds file_kind; 983 984 switch (Compiler.Settings.Target) { 985 case Target.Library: 986 case Target.Module: 987 file_kind = PEFileKinds.Dll; 988 break; 989 case Target.WinExe: 990 file_kind = PEFileKinds.WindowApplication; 991 break; 992 default: 993 file_kind = PEFileKinds.ConsoleApplication; 994 break; 995 } 996 997 if (entry_point == null) { 998 string main_class = Compiler.Settings.MainClass; 999 if (main_class != null) { 1000 // TODO: Handle dotted names 1001 var texpr = module.GlobalRootNamespace.LookupType (module, main_class, 0, LookupMode.Probing, Location.Null); 1002 if (texpr == null) { 1003 Report.Error (1555, "Could not find `{0}' specified for Main method", main_class); 1004 return; 1005 } 1006 1007 var mtype = texpr.MemberDefinition as ClassOrStruct; 1008 if (mtype == null) { 1009 Report.Error (1556, "`{0}' specified for Main method must be a valid class or struct", main_class); 1010 return; 1011 } 1012 1013 Report.Error (1558, mtype.Location, "`{0}' does not have a suitable static Main method", mtype.GetSignatureForError ()); 1014 } else { 1015 string pname = file_name == null ? name : Path.GetFileName (file_name); 1016 Report.Error (5001, "Program `{0}' does not contain a static `Main' method suitable for an entry point", 1017 pname); 1018 } 1019 1020 return; 1021 } 1022 1023 Builder.SetEntryPoint (entry_point.MethodBuilder, file_kind); 1024 } 1025 Error_ObsoleteSecurityAttribute(Attribute a, string option)1026 void Error_ObsoleteSecurityAttribute (Attribute a, string option) 1027 { 1028 Report.Warning (1699, 1, a.Location, 1029 "Use compiler option `{0}' or appropriate project settings instead of `{1}' attribute", 1030 option, a.Name); 1031 } 1032 Error_AssemblySigning(string text)1033 void Error_AssemblySigning (string text) 1034 { 1035 Report.Error (1548, "Error during assembly signing. " + text); 1036 } 1037 IsFriendAssemblyTo(IAssemblyDefinition assembly)1038 public bool IsFriendAssemblyTo (IAssemblyDefinition assembly) 1039 { 1040 return false; 1041 } 1042 IsValidAssemblyVersion(string version, bool allowGenerated)1043 static Version IsValidAssemblyVersion (string version, bool allowGenerated) 1044 { 1045 string[] parts = version.Split ('.'); 1046 if (parts.Length < 1 || parts.Length > 4) 1047 return null; 1048 1049 var values = new int[4]; 1050 for (int i = 0; i < parts.Length; ++i) { 1051 if (!int.TryParse (parts[i], out values[i])) { 1052 if (parts[i].Length == 1 && parts[i][0] == '*' && allowGenerated) { 1053 if (i == 2) { 1054 // Nothing can follow * 1055 if (parts.Length > 3) 1056 return null; 1057 1058 // Generate Build value based on days since 1/1/2000 1059 TimeSpan days = DateTime.Today - new DateTime (2000, 1, 1); 1060 values[i] = System.Math.Max (days.Days, 0); 1061 i = 3; 1062 } 1063 1064 if (i == 3) { 1065 // Generate Revision value based on every other second today 1066 var seconds = DateTime.Now - DateTime.Today; 1067 values[i] = (int) seconds.TotalSeconds / 2; 1068 continue; 1069 } 1070 } 1071 1072 return null; 1073 } 1074 1075 if (values[i] > ushort.MaxValue) 1076 return null; 1077 } 1078 1079 return new Version (values[0], values[1], values[2], values[3]); 1080 } 1081 } 1082 1083 public class AssemblyResource : IEquatable<AssemblyResource> 1084 { AssemblyResource(string fileName, string name)1085 public AssemblyResource (string fileName, string name) 1086 : this (fileName, name, false) 1087 { 1088 } 1089 AssemblyResource(string fileName, string name, bool isPrivate)1090 public AssemblyResource (string fileName, string name, bool isPrivate) 1091 { 1092 FileName = fileName; 1093 Name = name; 1094 Attributes = isPrivate ? ResourceAttributes.Private : ResourceAttributes.Public; 1095 } 1096 1097 public ResourceAttributes Attributes { get; private set; } 1098 public string Name { get; private set; } 1099 public string FileName { get; private set; } 1100 public bool IsEmbeded { get; set; } 1101 1102 #region IEquatable<AssemblyResource> Members 1103 Equals(AssemblyResource other)1104 public bool Equals (AssemblyResource other) 1105 { 1106 return Name == other.Name; 1107 } 1108 1109 #endregion 1110 } 1111 1112 // 1113 // A placeholder class for assembly attributes when emitting module 1114 // 1115 class AssemblyAttributesPlaceholder : CompilerGeneratedContainer 1116 { 1117 static readonly string TypeNamePrefix = "<$AssemblyAttributes${0}>"; 1118 public static readonly string AssemblyFieldName = "attributes"; 1119 1120 Field assembly; 1121 AssemblyAttributesPlaceholder(ModuleContainer parent, string outputName)1122 public AssemblyAttributesPlaceholder (ModuleContainer parent, string outputName) 1123 : base (parent, new MemberName (GetGeneratedName (outputName)), Modifiers.STATIC | Modifiers.INTERNAL) 1124 { 1125 assembly = new Field (this, new TypeExpression (parent.Compiler.BuiltinTypes.Object, Location), Modifiers.PUBLIC | Modifiers.STATIC, 1126 new MemberName (AssemblyFieldName), null); 1127 1128 AddField (assembly); 1129 } 1130 AddAssemblyAttribute(MethodSpec ctor, byte[] data)1131 public void AddAssemblyAttribute (MethodSpec ctor, byte[] data) 1132 { 1133 assembly.SetCustomAttribute (ctor, data); 1134 } 1135 GetGeneratedName(string outputName)1136 public static string GetGeneratedName (string outputName) 1137 { 1138 return string.Format (TypeNamePrefix, outputName); 1139 } 1140 } 1141 1142 // 1143 // Extension to System.Reflection.Emit.AssemblyBuilder to have fully compatible 1144 // compiler. This is a default implementation for framework System.Reflection.Emit 1145 // which does not implement any of the methods 1146 // 1147 public class AssemblyBuilderExtension 1148 { 1149 protected readonly CompilerContext ctx; 1150 AssemblyBuilderExtension(CompilerContext ctx)1151 public AssemblyBuilderExtension (CompilerContext ctx) 1152 { 1153 this.ctx = ctx; 1154 } 1155 AddModule(string module)1156 public virtual System.Reflection.Module AddModule (string module) 1157 { 1158 ctx.Report.RuntimeMissingSupport (Location.Null, "-addmodule"); 1159 return null; 1160 } 1161 AddPermissionRequests(PermissionSet[] permissions)1162 public virtual void AddPermissionRequests (PermissionSet[] permissions) 1163 { 1164 ctx.Report.RuntimeMissingSupport (Location.Null, "assembly declarative security"); 1165 } 1166 AddTypeForwarder(TypeSpec type, Location loc)1167 public virtual void AddTypeForwarder (TypeSpec type, Location loc) 1168 { 1169 ctx.Report.RuntimeMissingSupport (loc, "TypeForwardedToAttribute"); 1170 } 1171 DefineWin32IconResource(string fileName)1172 public virtual void DefineWin32IconResource (string fileName) 1173 { 1174 ctx.Report.RuntimeMissingSupport (Location.Null, "-win32icon"); 1175 } 1176 GetReferencedAssemblies()1177 public virtual AssemblyName[] GetReferencedAssemblies () 1178 { 1179 return null; 1180 } 1181 SetAlgorithmId(uint value, Location loc)1182 public virtual void SetAlgorithmId (uint value, Location loc) 1183 { 1184 ctx.Report.RuntimeMissingSupport (loc, "AssemblyAlgorithmIdAttribute"); 1185 } 1186 SetCulture(string culture, Location loc)1187 public virtual void SetCulture (string culture, Location loc) 1188 { 1189 ctx.Report.RuntimeMissingSupport (loc, "AssemblyCultureAttribute"); 1190 } 1191 SetFlags(uint flags, Location loc)1192 public virtual void SetFlags (uint flags, Location loc) 1193 { 1194 ctx.Report.RuntimeMissingSupport (loc, "AssemblyFlagsAttribute"); 1195 } 1196 SetVersion(Version version, Location loc)1197 public virtual void SetVersion (Version version, Location loc) 1198 { 1199 ctx.Report.RuntimeMissingSupport (loc, "AssemblyVersionAttribute"); 1200 } 1201 } 1202 1203 abstract class AssemblyReferencesLoader<T> where T : class 1204 { 1205 protected readonly CompilerContext compiler; 1206 1207 protected readonly List<string> paths; 1208 AssemblyReferencesLoader(CompilerContext compiler)1209 protected AssemblyReferencesLoader (CompilerContext compiler) 1210 { 1211 this.compiler = compiler; 1212 1213 paths = new List<string> (); 1214 paths.Add (Directory.GetCurrentDirectory ()); 1215 paths.AddRange (compiler.Settings.ReferencesLookupPaths); 1216 } 1217 HasObjectType(T assembly)1218 public abstract T HasObjectType (T assembly); GetDefaultReferences()1219 protected abstract string[] GetDefaultReferences (); LoadAssemblyFile(string fileName, bool isImplicitReference)1220 public abstract T LoadAssemblyFile (string fileName, bool isImplicitReference); LoadReferences(ModuleContainer module)1221 public abstract void LoadReferences (ModuleContainer module); 1222 Error_FileNotFound(string fileName)1223 protected void Error_FileNotFound (string fileName) 1224 { 1225 compiler.Report.Error (6, "Metadata file `{0}' could not be found", fileName); 1226 } 1227 Error_FileCorrupted(string fileName)1228 protected void Error_FileCorrupted (string fileName) 1229 { 1230 compiler.Report.Error (9, "Metadata file `{0}' does not contain valid metadata", fileName); 1231 } 1232 Error_AssemblyIsModule(string fileName)1233 protected void Error_AssemblyIsModule (string fileName) 1234 { 1235 compiler.Report.Error (1509, 1236 "Referenced assembly file `{0}' is a module. Consider using `-addmodule' option to add the module", 1237 fileName); 1238 } 1239 Error_ModuleIsAssembly(string fileName)1240 protected void Error_ModuleIsAssembly (string fileName) 1241 { 1242 compiler.Report.Error (1542, 1243 "Added module file `{0}' is an assembly. Consider using `-r' option to reference the file", 1244 fileName); 1245 } 1246 LoadReferencesCore(ModuleContainer module, out T corlib_assembly, out List<Tuple<RootNamespace, T>> loaded)1247 protected void LoadReferencesCore (ModuleContainer module, out T corlib_assembly, out List<Tuple<RootNamespace, T>> loaded) 1248 { 1249 compiler.TimeReporter.Start (TimeReporter.TimerType.ReferencesLoading); 1250 1251 loaded = new List<Tuple<RootNamespace, T>> (); 1252 1253 // 1254 // Load mscorlib.dll as the first 1255 // 1256 if (module.Compiler.Settings.StdLib) { 1257 corlib_assembly = LoadAssemblyFile ("mscorlib.dll", true); 1258 } else { 1259 corlib_assembly = default (T); 1260 } 1261 1262 T a; 1263 foreach (string r in module.Compiler.Settings.AssemblyReferences) { 1264 a = LoadAssemblyFile (r, false); 1265 if (a == null || EqualityComparer<T>.Default.Equals (a, corlib_assembly)) 1266 continue; 1267 1268 var key = Tuple.Create (module.GlobalRootNamespace, a); 1269 if (loaded.Contains (key)) 1270 continue; 1271 1272 loaded.Add (key); 1273 } 1274 1275 if (corlib_assembly == null) { 1276 // 1277 // Requires second pass because HasObjectType can trigger assembly load event 1278 // 1279 for (int i = 0; i < loaded.Count; ++i) { 1280 var assembly = loaded [i]; 1281 1282 // 1283 // corlib assembly is the first referenced assembly which contains System.Object 1284 // 1285 corlib_assembly = HasObjectType (assembly.Item2); 1286 if (corlib_assembly != null) { 1287 if (corlib_assembly != assembly.Item2) { 1288 var ca = corlib_assembly; 1289 i = loaded.FindIndex (l => l.Item2 == ca); 1290 } 1291 1292 if (i >= 0) 1293 loaded.RemoveAt (i); 1294 1295 break; 1296 } 1297 } 1298 } 1299 1300 foreach (var entry in module.Compiler.Settings.AssemblyReferencesAliases) { 1301 a = LoadAssemblyFile (entry.Item2, false); 1302 if (a == null) 1303 continue; 1304 1305 var key = Tuple.Create (module.CreateRootNamespace (entry.Item1), a); 1306 if (loaded.Contains (key)) 1307 continue; 1308 1309 loaded.Add (key); 1310 } 1311 1312 if (compiler.Settings.LoadDefaultReferences) { 1313 foreach (string r in GetDefaultReferences ()) { 1314 a = LoadAssemblyFile (r, true); 1315 if (a == null) 1316 continue; 1317 1318 var key = Tuple.Create (module.GlobalRootNamespace, a); 1319 if (loaded.Contains (key)) 1320 continue; 1321 1322 loaded.Add (key); 1323 } 1324 } 1325 1326 compiler.TimeReporter.Stop (TimeReporter.TimerType.ReferencesLoading); 1327 } 1328 } 1329 } 1330