Continued from https://github.com/nodejs/node-v0.x-archive/issues/2862. // @Fishrock123 @isaacs @jasnell @feross
os.arch() returns the architecture of the Node.js process, not the operating system. This was surprising to me and many other Node.js users. I don't really see the usefulness of that, although there might be one. My main need is to know the architecture of the operating system so I can spawn the correct binary.
There is a clear need for it:
Raising the same point I raised in the archived issue: there is no right way to implement it on multi-arch systems. That's probably the majority of server and desktop Linux systems these days.
Three out of the four linked issues seem to be about 32 vs. 64 bits Windows. They're best off checking process.env.PROCESSOR_ARCHITECTURE and process.env.PROCESSOR_ARCHITEW6432, that's probably what a built-in function would do if we added one.
@sindresorhus Does @bnoordhuis's comment address your need? If not, do you have a proposal for what the correct behavior would be on a system with (quoting @bnoordhuis's example here) "a 32 bit kernel and a mostly 64 bit userland"?
Trying to nudge this in a direction where it's either actionable or else close-able....
Thanks!
This seems like still a reasonable feature request to me.
Raising the same point I raised in the archived issue: there is no right way to implement it on multi-arch systems.
Huh? Are you sure can't make the API overcome that?
I don't think it needs to have the same API as os.arch()...
We could have it such like either of these:
This, where it is a checking function:
os.sysarch('x64') // true on 32&64bit compatible system
os.sysarch('x86') // also true on 32&64bit compatible system
Or this, where the first array value is the preferred one:
os.sysarch() === ['armv7'] // on an armv7 system
os.sysarch() === ['x64', 'x86'] // on say, a modern windows system
That seems like it would work to me, unless there for some reason isn't a way to get the architecture(s) of some systems correctly?
Try it and see how far you get. I suspect this is one of those things that seem simple at the outset but aren't.
Just one example: what should it print for a 64 bits kernel with a 32 bits emulation layer but without a 32 bits userland? How would you even go about figuring out if either condition is true?
@Fishrock123 I gave this a try a couple of weeks ago and it is easy to implement on certain platforms (e.g. Windows), but I failed to find a simple way on Linux / Unix-like platforms. I didn't expect it to be difficult either, e.g. Android has a very nice API for this (SUPPORTED_ABIS). You can start by looking at what uname and dpkg do to determine the correct platforms / architectures.
Just one example: what should it print for a 64 bits kernel with a 32 bits emulation layer but without a 32 bits userland?
Heh. My knowledge doesn't extend that far but is it actually impossible to tell from that if it can/can't run a 64 or 32 bit binary? If we can't tell which order it should be in for preference we could just hardcode it to return in some order, so long as that ordering would normally work.
Well, that's what I mean with it gets complicated fast. The absence of a 32 bits userland doesn't mean 32 bits binaries won't work. Anything that is fully statically linked will run just fine; everything else won't.
@tniessen Here is how to do it on Linux:
#include <stdio.h>
#include <sys/personality.h>
#include <sys/syscall.h>
#include <unistd.h>
int main(void) {
int ok = 0;
if (PER_LINUX == syscall(__NR_personality, PER_LINUX32))
if (PER_LINUX32 == syscall(__NR_personality, PER_LINUX))
ok = 1;
if (ok)
puts("32 bits syscalls supported");
return !ok;
}
Unfortunately, there is no way to change the personality in a thread-safe manner - it's a per-thread property, not per-process.
@bnoordhuis As far as I can tell, that should be sufficient to check whether a Linux system can execute 32 bit code. I don't think we can derive any other information about the "OS architecture" / supported ABIs from this.
from the requirement:
My main need is to know the architecture of the operating system so I can spawn the correct binary.
It appears to me that knowing the prominent or original capability of the host OS is sufficient to select a binary, not necessarily all the supported / emulated combinations / capabilities of the host.
@sindresorhus - can you clarify this? Is the feature supposed to:
native architecture of the os?If second is the case, the below snippet looks to be enough on UNIX platforms:
#include <sys/utsname.h>
#include <stdio.h>
int main() {
struct utsname u;
uname(&u);
fprintf(stderr, "%s\n", u.machine);
}
@gireeshpunathil, isn't utsname.machine a hardware property? I always assumed it does not necessarly represent the kernel architecture, e.g. when installing a "32 bit os" on a 64 bit machine.
@tniessen - yes, it is the h/w property. But when we are talking about selecting the right binary / image, aren't we aiming to match the h/w architecture, as opposed to that of the driving kernel?
For example, gcc's codegen flag spec says:
-m32
-m64
Generate code for a 32-bit or 64-bit environment. The 32-bit environment sets int,
long and pointer to 32 bits and generates code that runs on any i386 system. The
64-bit environment sets int to 32 bits and long and pointer to 64 bits and
generates code for AMD鈥檚 x86-64 architecture. For darwin only the -m64 option turns
off the -fno-pic and -mdynamic-no-pic options.
So here the environment actually means the h/w, not the OS right? Or Am I missing something?
We are using the same technique to build and run node on a given UNIX host:
UNAME_M=$(shell uname -m)
ifeq ($(findstring x86_64,$(UNAME_M)),x86_64)
DESTCPU ?= x64
else
ifeq ($(findstring ppc64,$(UNAME_M)),ppc64)
DESTCPU ?= ppc64
else
That's just an educated guess. On Linux, uname -m reflects the active personality. With setarch i686 make, it detects x86.
If the platform ABI is important too, then I don't know of a reliable way to detect that.
Looking at the linked issues, it seems that the most common use-case is to detect people running 32-bit node on a 64-bit machine, e.g. to suggest that they download the 64-bit node and use that instead.
From one of the linked issues I came across @feross's arch module, which gives you a best guess at whether the machine supports 64-bit or not.
Is there a use-case for which that module is insufficient?
My main need is to know the architecture of the operating system so I can spawn the correct binary.
What is the issue with using os.arch() here? If os.arch() is x64, then you're good to use 64-bit binaries. If it's ia32, then you should be find with 32-bit binaries. I understand wanting to use 64-bit where possible, but using 32-bit binaries if os.arch() returns ia32 should be fine (just not ideal).
@gibfahn I can think of two hypothetical use cases:
I can think of two hypothetical use cases:
Fair enough, but then we're back to https://github.com/nodejs/node/issues/17036#issuecomment-354273139, and feross/arch seems sufficient (especially as those seem like niche use cases).
@gibfahn Sure, those were just the two for which os.arch() itself might not be sufficient. You are right about using arch!
Spawning a process which might use a lot of memory, using a 32 bit binary on a 64 bit machine would unnecessarily limit it to 4GB.
This limit applies almost exclusively to consumer 32-bit Windows and 32-bit ARMs before V7A. Most of Linux distributions and 32-bit Windows Server support bigger memory (but it's still up to 4GB per process). Physical Address Extension.
where does this stand now? do we have a consensus on what are valid use cases and what would work in which cases?
I think there are too many caveats to get this right in general, and for the simple case, https://github.com/feross/arch works fine and doesn't need to be in core. I'll close this out.