Beispiel #1
0
        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));
 }
Beispiel #4
0
        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);
        }
Beispiel #5
0
        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);
        }
Beispiel #6
0
        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);
        }
Beispiel #7
0
 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);
     }
 }
Beispiel #8
0
        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);
    }
Beispiel #12
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);
        }