Solved Detect x86 on amd64

Is there a sysctl that will show that an x86 process is running on amd64? hw.machine and hw.machine_arch both seem to return i386 in this case.
 
Last edited:
It should be one of the dev.cpu or dev.cpulist sysctls. E.g. my dev.cpu.0.pnpinfo shows compat=arm ;D
Or kern.supported_archs.

There is also uname -m, and uname -p.
 
A process is what runs on a processor. So a 32bit process is an x86 executable.

Specifically I need to be able to detect if a process needs to set LD_PRELOAD (32bit on 32bit or 64bit on 64bit) or LD_32_PRELOAD (32bit on 64bit). One solution would be to use a wrapper that sets an environment variable like RUNNING_ON_A_64BIT_KERNEL. It would be a bit cleaner if I could detect this programmatically. The code I'm working on is using the hw.machine sysctl, but that doesn't return the type of the kernel, so currently it is failing. As an alternative I did think of using the 'cpuid' assembler instruction, but I don't know if it can tell me about the mode that the kernel is running in.
 
I am confused how an AMD64 system could display x86 as its machine. If it's AMD64, it's a fair bet it's running a 64 bit kernel.

What does calling sysctl(3) give you? Pass it CTL_HW and HW_MACHINE. It should return AMD64 for a 64 bit hardware platform.
In fact, HW_MACHINE_ARCH would probably also return AMD64.

(I know on NetBSD, HW_MACHINE_ARCH will return x86_64 for AMD64.)

I am not sure what the best approach is to get the kernel's bit size. I suspect using confstr(3) and passing it LONG_BIT (this is defined in /usr/include/sys/limits.h). It will return 32 or 64 as appropriate. There may be a mnemonic for sysconf(3) but I couldn't find an obvious one.

Determining the program's format might just be as easy as using elf(5). It definitely knows what bit width it's compiled for.
 
I am confused how an AMD64 system could display x86 as its machine. If it's AMD64, it's a fair bet it's running a 64 bit kernel.

What does calling sysctl(3) give you? Pass it CTL_HW and HW_MACHINE. It should return AMD64 for a 64 bit hardware platform.
In fact, HW_MACHINE_ARCH would probably also return AMD64.

Currently the code does call sysctl with CTL_HW and HW_MACHINE, but it returns i386 even though the kernel is amd64. One thing that I haven't looked at is the fact that the wrapper application is amd64 and it does an exec of the x86 process. I'll try to see what happens if I launch the x86 process directly.

Determining the program's format might just be as easy as using elf(5). It definitely knows what bit width it's compiled for.

I always know the bitness of the executable. It's the bitness of the kernel that it is running on that I need to determine.
 
It should be enough to check /usr/lib32 or /libexec/ld-elf32.so.1 paths for existence.

See PR 240432; same thing happens with native FreeBSD executables. Most likely this is an oversight.

Does an x86 install of FreeBSD have just /usr/lib and /libexec/ld-elf.so.1 ?

If so then this should work - I can just to a stat() or something on these paths.
 
Currently the code does call sysctl with CTL_HW and HW_MACHINE, but it returns x86 even though the kernel is amd64. One thing that I haven't looked at is the fact that the wrapper application is amd64 and it does an exec of the x86 process. I'll try to see what happens if I launch the x86 process directly.
Seriously? I just tried this and on a 64 bit machine it's answering AMD64. That's FreeBSD 12.1R and 11.3R. What's the environment?

I always know the bitness of the executable. It's the bitness of the kernel that I need to determine.

It should have read s/program/kernel.

Either way, you can get it using elf or confstr.
 
Seriously? I just tried this and on a 64 bit machine it's answering AMD64. That's FreeBSD 12.1R and 11.3R. What's the environment?

On FreeBSD 12.1 p3. This is with Valgrind. 'valgrind' itself is an amd64 executable. Depending on the bitness of the guest executable, it will exec either tool-x86-freebsd or tool-amd64-freebsd [where tool is none, memcheck, callgrind etc.].

The tool itself is fairly special. It does not link with any libraries, dynamic or static. The code is here, function 'Bool VG_(is32on64)(void) '. When compiled for amd64, this simply returns 'False'. When compiled for x86, is uses 'VG_(sysctl)' to query 'hw.machine', where I'm getting 'x86' instead of 'amd64'. As I said, this is a totally standalone executable, so 'VG_(sysctl)()' (via a few other functions) will ultimately collect the syscall arguments and call 'int 80' in inline assembler.

There is at least one issue that I've seen with this sysctl interface (which happens with the very first sysctl when executing ld-elf32.so. I assumed that the sysctl was behaving the same in this environment as it would in a regular libc linked x86 executable. I'll write a small test exe this evening.

I made a small example, and it does exhibit the behaviour that I see with Valgrind.


C:
#include <stdio.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char** argv)
{
#if defined(__x86_64)
    printf("running on amd64\n");
#else
    printf("running on x86\n");
#endif
    char machbuf[32];
    int oid[] = { CTL_HW, HW_MACHINE};
    size_t len = sizeof(machbuf);
    int error =  sysctl(oid, 2, machbuf, &len, NULL, 0);
    if (error)
    {
        perror("sysctl failed");
        exit(1);
    }
    
    printf("sysctl returned %s\n", machbuf);
    
#if defined(__x86_64)
    error = execve("./sysctl32", argv, NULL);
    if (error)
    {
        perror("execve failed\n");
    }
#endif
}

Compile this twice
Code:
clang -o sysctl64 sysctl.c
clang -m32 -o sysctl32 sysctl.c

Then run
32bit only

./sysctl32
running on x86
sysctl returned i386

64bit which execs the 32bit
./sysctl64
running on amd64
sysctl returned amd64
running on x86
sysctl returned i386
 
The valgrind code does *somewhat* clear up what you're trying to achieve.

Your posted example is doing exactly what it should. Once you switch to 32 bit mode, the kernel is effectively 32 bit for your process.

This is called adaptive machine architecture. Take a read of:

/usr/src/sys/x86/x86/identcpu.c

Your example would report the same state in NetBSD (and OpenBSD and probably Linux), BTW.

So, back to your problem. As I understand it you need a way of identifying the running OS's word size then invoking LD_PRELOAD variants depending on the size: 32 or 64 bit. Surely your only choice is to interrogate the /boot/kernel/kernel?

Now, I understand the less elegant way is to use a pre-launch script setting environment vars, but given your constraints it might be the only way. It's that or open /boot/kernel/kernel and read the ELF header.

But, I think I'm misunderstanding something, so apologies for that. I'm rather distracted at the moment.
 
I used a check for "/libexec/ld-elf32.so.1" and it seems to work.

C:
   SysRes res;
   struct vg_stat stat_buf;
   res = VG_(stat)("/libexec/ld-elf32.so.1", &stat_buf);
   if (!sr_isError(res)) {
      // file exists, we're running on amd64
      VG_(debugLog)(1, "check-os-bitness", "i386 executable on amd64 kernel\n");
      return True;
   } else {
      VG_(debugLog)(1, "check-os-bitness", "i386 executable on i386 kernel\n");
      return False;
   }
 
Back
Top