darwin/arm64 binary says it is running under Rosetta 2 on macOS M1 machine
Closed this issue · 2 comments
What version of Go are you using (go version)?
$ go version go version go1.16 darwin/amd64
Does this issue reproduce with the latest release?
Yes, Go 1.16 is the latest release.
What operating system and processor architecture are you using (go env)?
go env Output
$ go env GO111MODULE="" GOARCH="amd64" GOBIN="" GOCACHE="/Users/[name]/Library/Caches/go-build" GOENV="/Users/[name]/Library/Application Support/go/env" GOEXE="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="darwin" GOINSECURE="" GOMODCACHE="/Users/[name]/go/pkg/mod" GONOPROXY="" GONOSUMDB="" GOOS="darwin" GOPATH="/Users/[name]/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/usr/local/Cellar/go/1.16/libexec" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/usr/local/Cellar/go/1.16/libexec/pkg/tool/darwin_amd64" GOVCS="" GOVERSION="go1.16" GCCGO="gccgo" AR="ar" CC="clang" CXX="clang++" CGO_ENABLED="1" GOMOD="/Users/[name]/dd/gotest/darwin/go.mod" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/6d/hrpbskwn3nv8qnm51fdpmh5c0000gn/T/go-build1276543076=/tmp/go-build -gno-record-gcc-switches -fno-common"
What did you do?
Consider the following program, saved on main.go:
package main
import (
"fmt"
"golang.org/x/sys/unix"
)
func processIsTranslated() (bool, error) {
// https://developer.apple.com/documentation/apple_silicon/about_the_rosetta_translation_environment#3616845
_, err := unix.Sysctl("sysctl.proc_translated")
if err == nil {
return true, nil
} else if err.(unix.Errno) == unix.ENOENT {
return false, nil
}
return false, err
}
func main() {
if isTranslated, err := processIsTranslated(); err != nil {
panic(err)
} else if isTranslated {
fmt.Println("Running on Rosetta 2")
} else {
fmt.Println("Running natively")
}
}As per Apple's documentation, if I am translating the code correctly to Go, this should detect whether a binary is running natively or it is being translated under Rosetta.
Build the program both for amd64 and arm64 on macOS:
GOOS=darwin GOARCH=amd64 go build -o main_darwin_amd64 main.go
GOOS=darwin GOARCH=arm64 go build -o main_darwin_arm64 main.goWhat did you expect to see?
The amd64 binary should report it is running natively on a macOS with amd64 architecture and it should report it is running under Rosetta 2 on a macOS with arm64 architecture.
The arm64 binary should not be runnable on a macOS with amd64 architecture and it should report it is running natively on a macOS with arm64 architecture.
What did you see instead?
When running on a macOS with an amd64 architecture I see the following
$ uname -a Darwin [name].local 19.6.0 Darwin Kernel Version 19.6.0: Tue Jan 12 22:13:05 PST 2021; root:xnu-6153.141.16~1/RELEASE_X86_64 x86_64 i386 MacBookPro15,2 Darwin $ file ./main_darwin_amd64 ./main_darwin_amd64: Mach-O 64-bit executable x86_64 $ ./main_darwin_amd64 Running natively $ file ./main_darwin_arm64 ./main_darwin_arm64: Mach-O 64-bit executable arm64 $ ./main_darwin_arm64 -bash: ./main_darwin_arm64: Bad CPU type in executable
When running on a macOS with an arm64 architecture (with a macOS M1 chip) I see the following
$ uname -a Darwin [name].local 20.1.0 Darwin Kernel Version 20.1.0: Sat Oct 31 00:07:18 PDT 2020; root:xnu-7195.50.7~2/RELEASE_ARM64_T8020 arm64 $ file ./main_darwin_amd64 ./main_darwin_amd64: Mach-O 64-bit executable x86_64 $ ./main_darwin_amd64 Running on Rosetta 2 $ file ./main_darwin_arm64 ./main_darwin_arm64: Mach-O 64-bit executable arm64 $ ./main_darwin_arm64 Running on Rosetta 2 # ?!?
I would expect this last line to say Running natively since it is an arm64 executable on an arm64 macOS.
Compare with the following C++ program which does output the results I would expect: the arm64 version reports "Running natively" on the Mac with the M1 chip.
#include <iostream>
#include <sys/sysctl.h>
int processIsTranslated() {
int ret = 0;
size_t size = sizeof(ret);
if (sysctlbyname("sysctl.proc_translated", &ret, &size, nullptr, 0) == -1) {
if (errno == ENOENT)
return 0;
return -1;
}
return ret;
}
int main() {
int res = processIsTranslated();
if (res == 0) {
std::cout << "Process is running natively" << std::endl;
} else if (res == 1) {
std::cout << "Process is running under Rosetta" << std::endl;
} else {
std::cout << "Unexpected error code: " << res << std::endl;
}
}Notice the return ret, however this doesn't work with unix.Sysctl since it always returns a string that does not correspond to the numbers here
Closing as it was an issue in my code. The correct implementation of the function in Go is:
func processIsTranslated() (bool, error) {
// https://developer.apple.com/documentation/apple_silicon/about_the_rosetta_translation_environment#3616845
ret, err := unix.SysctlUint32("sysctl.proc_translated")
if err == nil {
return ret == 1, nil
} else if err.(unix.Errno) == unix.ENOENT {
return false, nil
}
return false, err
}