Skip to content

Commit

Permalink
Merge pull request #3956 from BOINC/mac_build_arm64_x86_64_Universal
Browse files Browse the repository at this point in the history
Mac: Automatically build Universal x86_64 / arm64 binaries
  • Loading branch information
davidpanderson authored Aug 16, 2020
2 parents 4510473 + d9526db commit b2ca098
Show file tree
Hide file tree
Showing 20 changed files with 868 additions and 572 deletions.
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

0 comments on commit b2ca098

Please sign in to comment.