public void ExplictStripOption_ThirdPartyLibrary_AndWarnsIfSo(bool?strip, bool shouldStrip) { MMPTests.RunMMPTest(tmpDir => { string originalLocation = Path.Combine(Configuration.SourceRoot, "tests", "test-libraries", "libtest-fat.macos.dylib"); string newLibraryLocation = Path.Combine(tmpDir, "libTest.dylib"); File.Copy(originalLocation, newLibraryLocation); TI.UnifiedTestConfig test = CreateStripTestConfig(strip, tmpDir, $" --native-reference=\"{newLibraryLocation}\""); test.Release = true; var testResult = TI.TestUnifiedExecutable(test); var bundleDylib = Path.Combine(test.BundlePath, "Contents", "MonoBundle", "libTest.dylib"); Assert.That(bundleDylib, Does.Exist, "libTest.dylib presence in app bundle"); var architectures = MachO.GetArchitectures(bundleDylib); if (shouldStrip) { Assert.AreEqual(1, architectures.Count, "libTest.dylib should only contain 1 architecture"); Assert.AreEqual(Abi.x86_64, architectures [0], "libTest.dylib should be x86_64"); testResult.Messages.AssertWarning(2108, "libTest.dylib was stripped of architectures except x86_64 to comply with App Store restrictions. This could break existing codesigning signatures. Consider stripping the library with lipo or disabling with --optimize=-trim-architectures"); } else { Assert.AreEqual(2, architectures.Count, "libTest.dylib should contain 2+ architectures"); Assert.That(architectures, Is.EquivalentTo(new Abi [] { Abi.i386, Abi.x86_64 }), "libTest.dylib should be x86_64 + i386"); testResult.Messages.AssertWarningCount(1); // dylib ([...]/xamarin-macios/tests/mmptest/bin/Debug/tmp-test-dir/Xamarin.MMP.Tests.MMPTests.RunMMPTest47/bin/Release/UnifiedExample.app/Contents/MonoBundle/libTest.dylib) was built for newer macOS version (10.11) than being linked (10.9) } }); }
public void FromSwiftLibraryMacOS() { Stream lib = HelloSwiftAsLibrary(null); List <MachOFile> macho = MachO.Read(lib, null).ToList(); Assert.IsNotNull(macho); Assert.AreEqual(1, macho.Count); }
public static void MakeInfoPList(string pathToLibrary, string pathToPlistFile) { using (FileStream stm = new FileStream(pathToLibrary, FileMode.Open, FileAccess.Read, FileShare.Read)) { // oh dog, this code is so fragile. PLDict dict = MakeDefaultDict(pathToLibrary); string os = null; MachOFile.MinOSVersion lc = null; using (var files = MachO.Read(pathToLibrary, ReadingMode.Deferred)) { foreach (var file in files) { var osmin = file.MinOS; if (osmin == null) { throw new NotSupportedException("dylib files without a minimum supported operating system load command are not supported."); } switch (osmin.Platform) { case MachO.Platform.IOS: case MachO.Platform.IOSSimulator: os = "iphoneos"; break; case MachO.Platform.TvOS: case MachO.Platform.TvOSSimulator: os = "appletvos"; break; case MachO.Platform.WatchOS: case MachO.Platform.WatchOSSimulator: os = "watchos"; break; default: break; } if (os != null) { lc = osmin; break; } } } if (os != null && lc != null) { AddInOSSpecifics(os, lc, dict); } if (File.Exists(pathToPlistFile)) { File.Delete(pathToPlistFile); } using (FileStream pliststm = new FileStream(pathToPlistFile, FileMode.Create)) { dict.ToXml(pliststm); } } }
public static IEnumerable <FileSymbolPair> Symbols(Stream stm, Func <NListEntry, bool> entryFilter = null, Func <MachOFile, bool> fileFilter = null) { fileFilter = fileFilter ?? AllFiles; entryFilter = entryFilter ?? AllPublic; return(from file in MachO.Read(stm) where fileFilter(file) from symTab in file.load_commands.OfType <SymTabLoadCommand> () from entry in symTab.nlist where entryFilter(entry) select new FileSymbolPair(file, entry.str)); }
public void ContainsASymbolTable() { Stream lib = HelloSwiftAsLibrary(null); List <MachOFile> macho = MachO.Read(lib, null).ToList(); Assert.IsNotNull(macho); Assert.AreEqual(1, macho.Count); MachOFile file = macho [0]; bool hasSymbolTable = file.load_commands.Exists(lc => lc.cmd == (uint)MachO.LoadCommands.SymTab || lc.cmd == (uint)MachO.LoadCommands.DySymTab); Assert.IsTrue(hasSymbolTable); }
public static List <string> TargetsFromDylib(Stream stm) { List <string> targets = new List <string> (); foreach (MachOFile file in MachO.Read(stm)) { string arch = ToArchString(file.Architecture); var osmin = file.MinOS; if (osmin == null) { throw new NotSupportedException("dylib files without a minimum supported operating system load command are not supported."); } targets.Add($"{arch}-apple-{osmin.OSName}{osmin.Version.ToString ()}"); } return(targets); }
void AssertStrip(string libPath, bool shouldStrip) { var archsFound = MachO.GetArchitectures(libPath); if (shouldStrip) { Assert.AreEqual(1, archsFound.Count, "Did not contain one archs"); Assert.True(archsFound.Contains(Abi.x86_64), "Did not contain x86_64"); } else { Assert.AreEqual(2, archsFound.Count, "Did not contain two archs"); Assert.True(archsFound.Contains(Abi.i386), "Did not contain i386"); Assert.True(archsFound.Contains(Abi.x86_64), "Did not contain x86_64"); } }
void AssertStrip(string libPath, bool shouldStrip) { var archsFound = MachO.GetArchitectures(libPath); if (shouldStrip) { Assert.AreEqual(1, archsFound.Count, "Did not contain one archs"); Assert.True(archsFound.Contains(Abi.x86_64), "Did not contain x86_64"); } else { Assert.That(archsFound.Count, Is.GreaterThanOrEqualTo(2), "Did not contain two or more archs"); Assert.True(archsFound.Contains(Abi.i386) || archsFound.Contains(Abi.ARM64), "Did not contain i386 nor arm64"); Assert.True(archsFound.Contains(Abi.x86_64), "Did not contain x86_64"); } }
public void HasOneRealPublicSymbol() { Stream lib = HelloSwiftAsLibrary(null); List <MachOFile> macho = MachO.Read(lib, null).ToList(); Assert.IsNotNull(macho); Assert.AreEqual(1, macho.Count); MachOFile file = macho [0]; List <SymTabLoadCommand> symbols = file.load_commands.OfType <SymTabLoadCommand> ().ToList(); Assert.AreEqual(1, symbols.Count); NListEntryType nlet = symbols [0].nlist [0].EntryType; List <NListEntry> entries = symbols [0].nlist. Where((nle, i) => nle.IsPublic && nle.EntryType == NListEntryType.InSection).ToList(); Assert.AreEqual(1, entries.Count); }
static bool IsMacOSLib(string pathToLibrary) { using (FileStream stm = new FileStream(pathToLibrary, FileMode.Open, FileAccess.Read, FileShare.Read)) { foreach (MachOFile file in MachO.Read(stm)) { var osmin = file.MinOS; if (osmin == null) { throw new NotSupportedException("dylib files without a minimum supported operating system load command are not supported."); } if (osmin.Platform != MachO.Platform.MacOS) { return(false); } } return(true); } }
static void TLFunctionsForStream(Stream stm, PlatformName platform, Dictionary <string, string> functions) { List <NListEntry> entries = null; List <MachOFile> macho = MachO.Read(stm, null).ToList(); MachOFile file = macho [0]; List <SymTabLoadCommand> symbols = file.load_commands.OfType <SymTabLoadCommand> ().ToList(); NListEntryType nlet = symbols [0].nlist [0].EntryType; entries = symbols [0].nlist. Where((nle, i) => nle.IsPublic && nle.EntryType == NListEntryType.InSection).ToList(); bool isOldVersion = IsOldVersion(entries); foreach (var entry in entries) { if (!entry.IsSwiftEntryPoint()) { continue; } TLDefinition def = null; try { def = Decomposer.Decompose(entry.str, isOldVersion, Offset(entry)); } catch { } if (def != null) { // this skips over privatized names var tlf = def as TLFunction; if (tlf != null && tlf.Name != null && tlf.Name.Name.Contains("...")) { continue; } if (tlf?.Name != null) { if (!functions.ContainsKey(tlf.Name.Name)) { functions.Add(tlf.Name.Name, tlf.MangledName); } } } } }
public void TestLipoExecutable() { var fileA = Path.Combine(Configuration.RootPath, "tests", "test-libraries", ".libs", "macos", "libtest.arm64.dylib"); var fileB = Path.Combine(Configuration.RootPath, "tests", "test-libraries", ".libs", "macos", "libtest.x86_64.dylib"); var bundles = CreateAppBundles(fileA, fileB, "libtest.dylib"); var outputBundle = Path.Combine(Cache.CreateTemporaryDirectory(), "Merged.app"); var task = CreateTask(outputBundle, bundles); Assert.IsTrue(task.Execute(), "Task execution"); // The bundle should only contain a single file. Assert.AreEqual(1, Directory.GetFileSystemEntries(outputBundle).Length, "Files in bundle"); // The resulting dylib should contain 2 architectures. var fatLibrary = Path.Combine(outputBundle, "libtest.dylib"); Assert.That(fatLibrary, Does.Exist, "Existence"); var machO = MachO.Read(fatLibrary).ToArray(); Assert.AreEqual(2, machO.Length, "Architecture Count"); }
FileType GetFileType(string path) { if (PathUtils.IsSymlink(path)) { return(FileType.Symlink); } if (Directory.Exists(path)) { return(FileType.Directory); } if (path.EndsWith(".exe", StringComparison.Ordinal) || path.EndsWith(".dll", StringComparison.Ordinal)) { return(FileType.PEAssembly); } if (MachO.IsMachOFile(path)) { return(FileType.MachO); } if (StaticLibrary.IsStaticLibrary(path)) { return(FileType.MachO); } if (ArchitectureSpecificFiles != null) { var filename = Path.GetFileName(path); if (ArchitectureSpecificFiles.Any(v => v.ItemSpec == filename)) { return(FileType.ArchitectureSpecific); } } return(FileType.Other); }
public void TestSingleInput() { var fileA = Path.Combine(Configuration.RootPath, "tests", "test-libraries", ".libs", "macos", "libtest.arm64.dylib"); var bundle = CreateAppBundle(Path.GetDirectoryName(fileA), Path.GetFileName(fileA)); var outputBundle = Path.Combine(Cache.CreateTemporaryDirectory(), "Merged.app"); var task = CreateTask(outputBundle, bundle); Assert.IsTrue(task.Execute(), "Task execution"); // The bundle should only contain a single file. Assert.AreEqual(1, Directory.GetFileSystemEntries(outputBundle).Length, "Files in bundle"); // The resulting dylib should contain 1 architecture. var nonFatBinary = Path.Combine(outputBundle, "libtest.arm64.dylib"); Assert.That(nonFatBinary, Does.Exist, "Existence"); var machO = MachO.Read(nonFatBinary).ToArray(); Assert.AreEqual(1, machO.Length, "Architecture Count"); // and the file size should be the same as the input Assert.That(new FileInfo(fileA).Length, Is.EqualTo(new FileInfo(nonFatBinary).Length), "File length"); }
public override bool Execute() { if (FrameworkToPublish is not null && FrameworkToPublish.Length > 0) { var list = FrameworkToPublish.ToList(); for (var i = list.Count - 1; i >= 0; i--) { var item = list [i]; var frameworkExecutablePath = item.ItemSpec; try { if (frameworkExecutablePath.EndsWith(".framework", StringComparison.OrdinalIgnoreCase) && Directory.Exists(frameworkExecutablePath)) { frameworkExecutablePath = Path.Combine(frameworkExecutablePath, Path.GetFileNameWithoutExtension(frameworkExecutablePath)); } if (MachO.IsDynamicFramework(frameworkExecutablePath)) { continue; } } catch (Exception e) { Log.LogError(7091, frameworkExecutablePath, MSBStrings.E7092 /* File '{0}' is not a valid framework: {1} */, frameworkExecutablePath, e.Message); continue; } Log.LogWarning(7091, frameworkExecutablePath, MSBStrings.W7091 /* "The framework {0} is a framework of static libraries, and will not be copied to the app." */, Path.GetDirectoryName(frameworkExecutablePath)); list.RemoveAt(i); } // Copy back the list if anything was removed from it if (FrameworkToPublish.Length != list.Count) { FrameworkToPublish = list.ToArray(); } } return(!Log.HasLoggedErrors); }
static ModuleInventory FromStreamInto(Stream stm, ModuleInventory inventory, ErrorHandling errors, string fileName = null) { Ex.ThrowOnNull(errors, "errors"); Ex.ThrowOnNull(stm, "stm"); OffsetStream osstm = null; List <NListEntry> entries = null; try { List <MachOFile> macho = MachO.Read(stm, null).ToList(); MachOFile file = macho [0]; List <SymTabLoadCommand> symbols = file.load_commands.OfType <SymTabLoadCommand> ().ToList(); NListEntryType nlet = symbols [0].nlist [0].EntryType; entries = symbols [0].nlist. Where((nle, i) => nle.IsPublic && nle.EntryType == NListEntryType.InSection).ToList(); inventory.Architecture = file.Architecture; osstm = new OffsetStream(stm, file.StartOffset); } catch (Exception e) { errors.Add(ErrorHelper.CreateError(ReflectorError.kCantHappenBase + 58, e, "Unable to retrieve functions from {0}: {1}", fileName ?? "stream", e.Message)); return(inventory); } bool isOldVersion = IsOldVersion(entries); foreach (var entry in entries) { if (!entry.IsSwiftEntryPoint()) { continue; } TLDefinition def = null; try { def = Decomposer.Decompose(entry.str, isOldVersion, Offset(entry)); } catch (RuntimeException e) { var except = new RuntimeException(e.Code, false, e, $"error decomposing {entry.str}: {e.Message}, skipping"); errors.Add(except); } catch (Exception e) { var except = new RuntimeException(ReflectorError.kDecomposeBase + 0, false, e, $"unexpected error handling {entry.str}: {e.Message}, skipping."); errors.Add(except); } if (def != null) { // this skips over privatized names var tlf = def as TLFunction; if (tlf != null && tlf.Name != null && tlf.Name.Name.Contains("...")) { continue; } try { inventory.Add(def, osstm); } catch (RuntimeException e) { e = new RuntimeException(e.Code, e.Error, $"error dispensing top level definition of type {def.GetType ().Name} decomposed from {entry.str}: {e.Message}"); errors.Add(e); } } else { var ex = ErrorHelper.CreateWarning(ReflectorError.kInventoryBase + 18, $"entry {entry.str} uses an unsupported swift feature, skipping."); errors.Add(ex); } } return(inventory); }
// This is tool very similar to Apple's swift-stdlib-tool, which scans an .app and copies the required swift libraries to the app. // Unfortunately Apple's tool has a shortcoming: it scans only executables, not shared libraries / frameworks. // So this tool fixes that shortcoming: it scans all both executables and shared libraries / frameworks. // As opposed to swift-stdlib-tool, code signing is not supported, since we don't need it (for now at least). // swift-stdlib-tool also doesn't update the timestamp of the files it copies, which makes dependency tracking annoying in the MSBuild target files. public static int Main(string [] args) { const string TOOL = "swift-copy-libs"; OptionSet os = null; int? exit_code = null; var action = Action.None; var scan_executables = new List <string> (); var scan_folders = new List <string> (); var platform = string.Empty; var source_libraries = string.Empty; var destination = string.Empty; var resource_libraries = new List <string> (); var verbosity = 0; var failed = false; os = new OptionSet { { "h|help|?", "Show help", (v) => { Console.WriteLine(TOOL + " [OPTIONS]"); os.WriteOptionDescriptions(Console.Out); exit_code = 0; } }, { "copy", "Copy required swift libraries to the target location", (v) => action = Action.Copy }, { "print", "Print required swift libraries", (v) => action = Action.Print }, { "scan-executable=", "Scan the specified executable", (v) => scan_executables.Add(v) }, { "scan-folder=", "Scan the specified folder", (v) => scan_folders.Add(v) }, { "platform=", "The platform to use", (v) => platform = v }, { "source-libraries=", "The path where to find the swift libraries", (v) => source_libraries = v }, { "destination=", "The destination path for the swift libraries", (v) => destination = v }, { "resource-library=", "Additional swift libraries.", (v) => resource_libraries.Add(v) }, { "v|verbose", "Show verbose output", (v) => { verbosity++; } }, { "q|quiet", "Show less verbose output", (v) => { verbosity--; } }, { "strip-bitcode", "Strip bitcode", (v) => {} }, }; var left = os.Parse(args); if (exit_code.HasValue) { return(exit_code.Value); } if (left.Count > 0) { Console.Error.WriteLine($"{TOOL}: unexpected arguments: {left.First ()}"); return(1); } switch (action) { case Action.Copy: case Action.Print: break; default: Console.Error.WriteLine($"{TOOL}: no action specified: pass either --copy or --print."); return(1); } var files = new HashSet <string> (); files.UnionWith(scan_executables); foreach (var folder in scan_folders) { if (!Directory.Exists(folder)) { Console.WriteLine($"Could not find the folder {folder}."); continue; } files.UnionWith(Directory.EnumerateFileSystemEntries(folder, "*", SearchOption.AllDirectories)); } // Remove directories from the list. files.RemoveWhere(Directory.Exists); var process_queue = new Queue <string> (files); var processed = new HashSet <string> (); var dependencies = new HashSet <string> (); while (process_queue.Count > 0) { var file = process_queue.Dequeue(); if (processed.Contains(file)) { continue; } processed.Add(file); if (!File.Exists(file)) { Console.WriteLine($"Could not find the file {file}."); failed = true; continue; } try { var macho_file = MachO.Read(file, ReadingMode.Deferred); foreach (var slice in macho_file) { using (slice) { Console.WriteLine($"{file} [{slice.Architecture}]:"); foreach (var lc in slice.load_commands) { var cmd = (MachO.LoadCommands)lc.cmd; if (cmd != MachO.LoadCommands.LoadDylib) { continue; } var ldcmd = (DylibLoadCommand)lc; if (!ldcmd.name.StartsWith("@rpath/libswift", StringComparison.Ordinal)) { continue; } var dependency = ldcmd.name.Substring("@rpath/".Length); dependency = Path.Combine(source_libraries, platform, dependency); if (verbosity > 0) { Console.WriteLine($" {Path.GetFileName (dependency)}"); } if (!File.Exists(dependency)) { if (verbosity > 0) { Console.WriteLine($" Could not find the file {dependency}"); } } else { dependencies.Add(dependency); process_queue.Enqueue(dependency); } } } } } catch (Exception e) { if (verbosity > 0) { Console.WriteLine($"{file}: not a Mach-O file ({e.Message})"); } } } var allDependencies = dependencies.Union(resource_libraries.Select((v) => Path.Combine(source_libraries, platform, v))).ToList(); if (verbosity > 0) { Console.WriteLine($"Found {allDependencies.Count} dependencies:"); foreach (var dependency in allDependencies) { Console.WriteLine($" {dependency}"); } } if (action == Action.Copy) { foreach (var dependency in allDependencies) { var src = dependency; var tgt = Path.Combine(destination, Path.GetFileName(dependency)); var srcInfo = new FileInfo(src); var tgtInfo = new FileInfo(tgt); if (!tgtInfo.Exists || srcInfo.Length != tgtInfo.Length || srcInfo.LastWriteTimeUtc > tgtInfo.LastWriteTimeUtc) { File.Copy(src, tgt, true); } else { if (verbosity > 0) { Console.WriteLine($"Did not copy {Path.GetFileName (src)} because it's up-to-date."); } } new FileInfo(tgt).LastWriteTimeUtc = DateTime.Now; } if (verbosity >= 0) { Console.WriteLine($"Copied all dependencies ({string.Join (", ", allDependencies.Select (Path.GetFileName))}) to {destination}."); } } return(failed ? 1 : 0); }
protected override bool Init() { // Detect endianness - default is little-endianness MachO magic = (MachO)ReadUInt32(); if (magic == MachO.MH_CIGAM || magic == MachO.MH_CIGAM_64) { Endianness = Endianness.Big; } else if (magic != MachO.MH_MAGIC && magic != MachO.MH_MAGIC_64) { return(false); } Console.WriteLine("Endianness: {0}", Endianness); Position -= sizeof(uint); header = ReadObject <MachOHeader>(); // 64-bit files have an extra 4 bytes after the header is64 = false; if (magic == MachO.MH_MAGIC_64) { is64 = true; ReadUInt32(); } Console.WriteLine("Architecture: {0}-bit", is64 ? 64 : 32); // Must be executable file if ((MachO)header.FileType != MachO.MH_EXECUTE) { return(false); } Console.WriteLine("CPU Type: " + (MachO)header.CPUType); MachOLinkEditDataCommand functionStarts = null; for (var c = 0; c < header.NumCommands; c++) { var startPos = Position; var loadCommand = ReadObject <MachOLoadCommand>(); if ((MachO)loadCommand.Command == MachO.LC_SEGMENT) { var segment = ReadObject <MachOSegmentCommand>(); if (segment.Name == "__TEXT" || segment.Name == "__DATA") { for (int s = 0; s < segment.NumSections; s++) { var section = ReadObject <MachOSection>(); sections.Add(section); if (section.Name == "__text") { GlobalOffset = section.Address - section.ImageOffset; } } } } else if ((MachO)loadCommand.Command == MachO.LC_SEGMENT_64) { var segment = ReadObject <MachOSegmentCommand64>(); if (segment.Name == "__TEXT" || segment.Name == "__DATA") { for (int s = 0; s < segment.NumSections; s++) { var section64 = ReadObject <MachOSection64>(); sections64.Add(section64); if (section64.Name == "__text") { GlobalOffset = (uint)section64.Address - section64.ImageOffset; } } } } if ((MachO)loadCommand.Command == MachO.LC_FUNCTION_STARTS) { functionStarts = ReadObject <MachOLinkEditDataCommand>(); } // There might be other data after the load command so always use the specified total size to step forwards Position = startPos + loadCommand.Size; } // Must find LC_FUNCTION_STARTS load command if (functionStarts == null) { return(false); } pFuncTable = functionStarts.Offset; sFuncTable = functionStarts.Size; return(true); }
protected override bool checkMagicBE(MachO magic) => magic == MachO.MH_CIGAM_64;
protected override bool checkMagicLE(MachO magic) => magic == MachO.MH_MAGIC_64;