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); }
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"); }
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"); }
// 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); }
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); }