Skip to content

Instantly share code, notes, and snippets.

@nico
Created July 8, 2020 02:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nico/2a2b71e143443f7bcaa2628bc70b0bc2 to your computer and use it in GitHub Desktop.
Save nico/2a2b71e143443f7bcaa2628bc70b0bc2 to your computer and use it in GitHub Desktop.
import ctypes
import os
import sys
libSystem = ctypes.CDLL('libSystem.dylib')
# `typedef void *posix_spawnattr_t;` on darwin
attrp = ctypes.c_void_p()
e = libSystem.posix_spawnattr_init(ctypes.byref(attrp))
print('spawnattr init ret:', e)
count = ctypes.c_size_t(1)
ocount = ctypes.c_size_t()
CPU_ARCH_ABI64 = 0x01000000 # 64 bit ABI
CPU_ARCH_ABI64_32 = 0x02000000 # ABI for 64-bit hardware with 32-bit types; LP32
CPU_TYPE_ARM = 12
CPU_TYPE_ARM64 = CPU_TYPE_ARM | CPU_ARCH_ABI64
pref = (ctypes.c_int * 1)(CPU_TYPE_ARM64)
e = libSystem.posix_spawnattr_setbinpref_np(ctypes.byref(attrp), count, pref, ctypes.byref(ocount))
print('binpref ret:', e, 'ocount:', ocount)
spawn = libSystem.posix_spawnp
spawn.restype = ctypes.c_int
spawn.argtypes = (
ctypes.POINTER(ctypes.c_int),
ctypes.c_char_p, ctypes.c_void_p, ctypes.c_void_p,
ctypes.POINTER(ctypes.c_char_p),
ctypes.POINTER(ctypes.c_char_p)
)
def charpp(a): return (ctypes.c_char_p * len(a))(*a)
argv = list(map(ctypes.c_char_p, b'git -c core.deltaBaseCacheLimit=2g clone --no-checkout --progress https://chromium.googlesource.com/chromium/src.git _gclient_src_mmdd8v1w'.split())) + [None]
envp = [ctypes.c_char_p(('%s=%s' % (k, os.environ[k])).encode('utf-8')) for k in os.environ] + [None]
pid = ctypes.c_int()
e = spawn(ctypes.byref(pid), ctypes.c_char_p(b"git"), None, ctypes.byref(attrp), charpp(argv), charpp(envp))
if e != 0:
print('spawn failed')
sys.exit(1)
e = libSystem.posix_spawnattr_destroy(ctypes.byref(attrp))
print('spawnattr destroy ret:', e)
os.waitpid(pid.value, 0)
@nico
Copy link
Author

nico commented Jul 8, 2020

Hm, posix_spawnattr_setbinpref_np() seems to not work when using ctypes in an intel python running under rosetta. Not that surprising, come to think of it.

Using arch(1) is way less code and works, something like this:

p = subprocess.check_call('arch -arch arm64 git -c core.deltaBaseCacheLimit=2g clone --no-checkout --progress https://chromium.googlesource.com/chromium/src.git _gclient_src_mmdd8v1w'.split())

posix_spawnattr_setbinpref_np works of course fine from C:

% clang -arch x86_64 -arch arm64 -o mysleep sleep.c
clang: error: no such file or directory: 'sleep.c'
clang: error: no input files
thakis@Nicos-Mac gittry % cat mysleep.c
#include <unistd.h>

int main() {
  sleep(30);
}
% clang -arch x86_64 -arch arm64 -o mysleep mysleep.c
% cat launch.c
#include <spawn.h>
#include <sys/wait.h>
#include <unistd.h>

#include <stdio.h>

extern char** environ;

int main() {
  printf("%x\n", CPU_TYPE_ARM64);

  posix_spawnattr_t attr;
  posix_spawnattr_init(&attr);

  cpu_type_t pref[] = { CPU_TYPE_ARM64 };
  size_t ocount;
  posix_spawnattr_setbinpref_np(&attr, 1, pref, &ocount);
  printf("count %zu\n", ocount);

  char* argv[] = { "mysleep", NULL };
  pid_t pid;
  posix_spawn(&pid, "mysleep", NULL, &attr, argv, environ);

  posix_spawnattr_destroy(&attr);

  int status;
  waitpid(pid, &status, 0);
}
% clang launch.c -o launch -arch x86_64
% ./launch

With this, mysleep shows up as "Apple" in Activity Monitor, but without the spawattr, launch launches the Intel slice instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment