Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mac: Automatically build Universal x86_64 / arm64 binaries #3956

Merged
merged 8 commits into from
Aug 16, 2020
7 changes: 6 additions & 1 deletion client/app.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,12 @@ struct ACTIVE_TASK {
inline int task_state() {
return _task_state;
}

#ifdef __APPLE__
// PowerPC apps emulated on i386 Macs crash if running graphics
// TODO: We may need to adapt this for x86_64 emulated on arm64
int powerpc_emulated_on_i386;
int is_native_i386_app(char*);
#endif
int request_reread_prefs();
int request_reread_app_info();
int link_user_files();
Expand Down
90 changes: 90 additions & 0 deletions client/app_start.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@
#include <process.h>
#endif

#ifdef __APPLE__
#include <mach-o/loader.h>
#include <mach-o/fat.h>
#include <mach/machine.h>
#include <libkern/OSByteOrder.h>
#endif

#if(!defined (_WIN32) && !defined (__EMX__))
#include <fcntl.h>
#endif
Expand Down Expand Up @@ -982,6 +989,11 @@ int ACTIVE_TASK::start(bool test) {
}
app_client_shm.reset_msgs();

#ifdef __APPLE__
// PowerPC apps emulated on i386 Macs crash if running graphics
// TODO: We may need to adapt this for x86_64 emulated on arm64
// powerpc_emulated_on_i386 = ! is_native_i386_app(exec_path);
#endif
if (cc_config.run_apps_manually) {
pid = getpid(); // use the client's PID
set_task_state(PROCESS_EXECUTING, "start");
Expand Down Expand Up @@ -1237,6 +1249,84 @@ int ACTIVE_TASK::resume_or_start(bool first_time) {
return 0;
}

#ifdef __APPLE__
union headeru {
fat_header fat;
mach_header mach;
};

// TODO: We may need to adapt this for x86_64 emulated on arm64

// Read the mach-o headers to determine the architectures
// supported by executable file.
// Returns 1 if application can run natively on i386 / x86_64 Macs,
// else returns 0.
//
int ACTIVE_TASK::is_native_i386_app(char* exec_path) {
FILE *f;
int retval = 0;

headeru myHeader;
fat_arch fatHeader;

uint32_t n, i, len;
uint32_t theMagic;
integer_t theType;

f = boinc_fopen(exec_path, "rb");
if (!f) {
return retval; // Should never happen
}

myHeader.fat.magic = 0;
myHeader.fat.nfat_arch = 0;

fread(&myHeader, 1, sizeof(fat_header), f);
theMagic = myHeader.mach.magic;
switch (theMagic) {
case MH_CIGAM:
case MH_MAGIC:
case MH_MAGIC_64:
case MH_CIGAM_64:
theType = myHeader.mach.cputype;
if ((theMagic == MH_CIGAM) || (theMagic == MH_CIGAM_64)) {
theType = OSSwapInt32(theType);
}
if ((theType == CPU_TYPE_I386) || (theType == CPU_TYPE_X86_64)) {
retval = 1; // Single-architecture i386or x86_64 file
}
break;
case FAT_MAGIC:
case FAT_CIGAM:
n = myHeader.fat.nfat_arch;
if (theMagic == FAT_CIGAM) {
n = OSSwapInt32(myHeader.fat.nfat_arch);
}
// Multiple architecture (fat) file
for (i=0; i<n; i++) {
len = fread(&fatHeader, 1, sizeof(fat_arch), f);
if (len < sizeof(fat_arch)) {
break; // Should never happen
}
theType = fatHeader.cputype;
if (theMagic == FAT_CIGAM) {
theType = OSSwapInt32(theType);
}
if ((theType == CPU_TYPE_I386) || (theType == CPU_TYPE_X86_64)) {
retval = 1;
break;
}
}
break;
default:
break;
}

fclose (f);
return retval;
}
#endif

// The following runs "test_app" and sends it various messages.
// Used for testing the runtime system.
//
Expand Down
6 changes: 5 additions & 1 deletion client/cs_platforms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ LPFN_ISWOW64PROCESS fnIsWow64Process;
#endif
#endif

#if defined(__APPLE__) && (defined(__i386__) || defined(__x86_64__))
#ifdef __APPLE__
#include <sys/sysctl.h>
extern int compareOSVersionTo(int toMajor, int toMinor);
#endif
Expand Down Expand Up @@ -104,6 +104,10 @@ void CLIENT_STATE::detect_platforms() {
if (compareOSVersionTo(10, 15) < 0) {
add_platform("i686-apple-darwin");
}
#elif defined(__arm64__)
add_platform("arm64-apple-darwin");
//TODO: Add test for Mac OS Version when Apple Rosetta emulator is removed
add_platform("x86_64-apple-darwin");
#else
#error Mac client now requires a 64-bit system
#endif
Expand Down
18 changes: 2 additions & 16 deletions client/hostinfo_unix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -829,22 +829,8 @@ static void get_cpu_info_mac(HOST_INFO& host) {
"%s [x86 Family %d Model %d Stepping %d]",
brand_string, family, model, stepping
);
#else // PowerPC
char model[256];
int response = 0;
int retval;
len = sizeof(response);
retval = sysctlbyname("hw.optional.altivec", &response, &len, NULL, 0);
if (response && (!retval)) {
safe_strcpy(host.p_features, "AltiVec");
}

len = sizeof(model);
sysctlbyname("hw.model", model, &len, NULL, 0);

safe_strcpy(host.p_vendor, "Power Macintosh");
snprintf(host.p_model, p_model_size, "%s [%s Model %s] [%s]", host.p_vendor, host.p_vendor, model, host.p_features);

#else
// TODO: Add code for Apple arm64 CPU
#endif

host.p_model[p_model_size-1] = 0;
Expand Down
2 changes: 2 additions & 0 deletions clientgui/DlgAbout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ bool CDlgAbout::Create(wxWindow* parent, wxWindowID id, const wxString& caption,
m_strVersion.Printf(wxT("%s (x86)"), wxT(BOINC_VERSION_STRING));
#elif defined(__ppc__)
m_strVersion.Printf(wxT("%s (PowerPC)"), wxT(BOINC_VERSION_STRING));
#elif defined(__arm64__)
m_strVersion.Printf(wxT("%s (arm64)"), wxT(BOINC_VERSION_STRING));
#else
m_strVersion.Printf(wxT("%s (unknown)"), wxT(BOINC_VERSION_STRING));
#endif
Expand Down
45 changes: 0 additions & 45 deletions clientgui/mac/SetupSecurity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@
static OSStatus UpdateNestedDirectories(char * basepath);
static OSStatus MakeXMLFilesPrivate(char * basepath);
static OSStatus DoSudoPosixSpawn(const char *pathToTool, char *arg1, char *arg2, char *arg3, char *arg4, char *arg5, char *arg6);
#ifndef __x86_64__
static pascal Boolean ErrorDlgFilterProc(DialogPtr theDialog, EventRecord *theEvent, short *theItemHit);
#endif
#ifdef _DEBUG
static OSStatus SetFakeMasterNames(void);
#endif
Expand Down Expand Up @@ -1061,52 +1058,10 @@ static OSStatus DoSudoPosixSpawn(const char *pathToTool, char *arg1, char *arg2,
void ShowSecurityError(const char *format, ...) {
va_list args;

#ifdef __x86_64__
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
#else
char s[1024];
short itemHit;
AlertStdAlertParamRec alertParams;
ModalFilterUPP ErrorDlgFilterProcUPP;

va_start(args, format);
s[0] = vsprintf(s+1, format, args);
va_end(args);

ErrorDlgFilterProcUPP = NewModalFilterUPP(ErrorDlgFilterProc);

alertParams.movable = true;
alertParams.helpButton = false;
alertParams.filterProc = ErrorDlgFilterProcUPP;
alertParams.defaultText = "\pOK";
alertParams.cancelText = NULL;
alertParams.otherText = NULL;
alertParams.defaultButton = kAlertStdAlertOKButton;
alertParams.cancelButton = 0;
alertParams.position = kWindowDefaultPosition;

BringAppToFront();

StandardAlert (kAlertStopAlert, (StringPtr)s, NULL, &alertParams, &itemHit);

DisposeModalFilterUPP(ErrorDlgFilterProcUPP);
#endif
}


#ifndef __x86_64__
static pascal Boolean ErrorDlgFilterProc(DialogPtr theDialog, EventRecord *theEvent, short *theItemHit) {
// We need this because this is a command-line application so it does not get normal events
if (GetCurrentEventButtonState()) {
*theItemHit = kStdOkItemIndex;
return true;
}

return StdFilterProc(theDialog, theEvent, theItemHit);
}
#endif


// return time of day (seconds since 1970) as a double
Expand Down
2 changes: 2 additions & 0 deletions clientgui/mac/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,8 @@
/* Host for this compilation */
#ifdef __x86_64__
#define HOSTTYPE "x86_64-apple-darwin"
#elif defined(__arm64__)
#define HOSTTYPE "arm64-apple-darwin"
#endif

/* "Define to 1 if largefile support causes missing symbols in C++" */
Expand Down
18 changes: 16 additions & 2 deletions lib/mac/QBacktrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,11 @@ First checked in.

#if TARGET_CPU_PPC
#include <mach/ppc/thread_status.h>
#endif
#elif TARGET_CPU_X86_64
#include <mach/i386/thread_status.h>
#elif TARGET_CPU_ARM64
#include <mach/arm/thread_status.h>
#endif

#if defined(__cplusplus)
}
Expand All @@ -167,7 +170,7 @@ First checked in.

// A new architecture will require substantial changes to this file.

#if ! (TARGET_CPU_PPC || TARGET_CPU_PPC64 || TARGET_CPU_X86 || TARGET_CPU_X86_64)
#if ! (TARGET_CPU_PPC || TARGET_CPU_PPC64 || TARGET_CPU_X86 || TARGET_CPU_X86_64 || TARGET_CPU_ARM64)
#error QBacktrace: What architecture?
#endif

Expand Down Expand Up @@ -558,6 +561,7 @@ static int ReadAddr(QBTContext *context, QTMAddr addr, QTMAddr *valuePtr)
return err;
}

#if ! TARGET_CPU_ARM64
static void AddFrame(QBTContext *context, QTMAddr pc, QTMAddr fp, QBTFlags flags)
// Adds a frame to the end of the output array with the
// value specified by pc, fp, and flags.
Expand All @@ -582,6 +586,7 @@ static void AddFrame(QBTContext *context, QTMAddr pc, QTMAddr fp, QBTFlags flags

context->frameCountOut += 1;
}
#endif

static int BacktraceCore(QBTContext *context, size_t *frameCountPtr)
// The core backtrace code. This routine is called by all of the various
Expand Down Expand Up @@ -2512,6 +2517,15 @@ extern int QBTCreateThreadStateSelf(
state->rbp = (uintptr_t) fp;
#endif
}
#elif TARGET_CPU_ARM64
arm_thread_state64_t * state;

flavor = ARM_THREAD_STATE64;
state = (arm_thread_state64_t *) calloc(1, sizeof(*state));
if (state != NULL) {
state->pc = (uintptr_t) pc;
state->fp = (uintptr_t) fp;
}
#else
#error What architecture?
#endif
Expand Down
Loading