/* vim: set expandtab ts=4 sw=4: */
/*
* You may redistribute this program and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "util/Bits.h"
#include "util/ArchInfo.h"
#include "util/CString.h"
#include
#if !defined(linux) || defined(android)
uint32_t ArchInfo_toAuditArch(enum ArchInfo ai)
{
return 0xffffffff;
}
#else
#include
// TODO(cjd): understand why some machines are not defining this.
#ifndef EM_ARM
#define EM_ARM 40
#endif
uint32_t ArchInfo_toAuditArch(enum ArchInfo ai)
{
switch (ArchInfo_getArch(ai)) {
case ArchInfo_Arch_AMD64: return AUDIT_ARCH_X86_64;
case ArchInfo_Arch_I386: return AUDIT_ARCH_I386;
case ArchInfo_Arch_ARM: {
if (ArchInfo_isBigEndian(ai)) {
return AUDIT_ARCH_ARMEB;
}
return AUDIT_ARCH_ARM;
}
case ArchInfo_Arch_PPC: {
switch (ArchInfo_getBits(ai)) {
case 32: return AUDIT_ARCH_PPC;
case 64: return AUDIT_ARCH_PPC64;
default:;
}
return 0xffffffff;
}
case ArchInfo_Arch_SPARC: {
switch (ArchInfo_getBits(ai)) {
case 32: return AUDIT_ARCH_SPARC;
case 64: return AUDIT_ARCH_SPARC64;
default:;
}
return 0xffffffff;
}
case ArchInfo_Arch_MIPS: {
if (ArchInfo_isBigEndian(ai)) {
switch (ArchInfo_getBits(ai)) {
case 32: return AUDIT_ARCH_MIPS;
case 64: return AUDIT_ARCH_MIPS64;
default:;
}
return 0xffffffff;
} else {
switch (ArchInfo_getBits(ai)) {
case ArchInfo_Bits_32: return AUDIT_ARCH_MIPSEL;
case ArchInfo_Bits_64: return AUDIT_ARCH_MIPSEL64;
default:;
}
return 0xffffffff;
}
}
default: return 0xffffffff;
}
}
#endif
static char* archStr(enum ArchInfo ai)
{
switch (ArchInfo_getArch(ai)) {
case ArchInfo_Arch_AMD64: return "AMD64";
case ArchInfo_Arch_ARM: return "ARM";
case ArchInfo_Arch_PPC: return "PPC";
case ArchInfo_Arch_SPARC: return "SPARC";
case ArchInfo_Arch_MIPS: return "MIPS";
case ArchInfo_Arch_I386: return "i386";
default: return "UNKNOWN";
};
}
#define archFlags_BUFF_SZ 512
static void archFlags(enum ArchInfo arch, uint8_t buff[archFlags_BUFF_SZ])
{
switch (ArchInfo_getArch(arch)) {
case ArchInfo_Arch_ARM: {
snprintf(buff, archFlags_BUFF_SZ, "%s%s",
((arch & ArchInfo_Arm_SUBTYPE_BITS) == ArchInfo_Arm_EABI) ? " EABI": "",
(arch & ArchInfo_Arm_NEON) ? " NEON": ""
);
break;
}
case ArchInfo_Arch_SPARC: {
switch (arch & ArchInfo_Sparc_SUBTYPE_BITS) {
case ArchInfo_Sparc_v8: snprintf(buff, archFlags_BUFF_SZ, " sparcv8"); break;
case ArchInfo_Sparc_v9: snprintf(buff, archFlags_BUFF_SZ, " sparcv9"); break;
}
break;
}
case ArchInfo_Arch_MIPS: {
switch (arch & ArchInfo_Mips_SUBTYPE_BITS) {
case ArchInfo_Mips_OABI: snprintf(buff, archFlags_BUFF_SZ, " OABI"); break;
case ArchInfo_Mips_NABI: snprintf(buff, archFlags_BUFF_SZ, " NABI"); break;
}
break;
}
case ArchInfo_Arch_AMD64:
case ArchInfo_Arch_PPC:
default: {
buff[0] = '\0';
break;
}
};
}
char* ArchInfo_describe(enum ArchInfo ai, struct Allocator* alloc)
{
uint8_t flagBuff[archFlags_BUFF_SZ];
archFlags(ai, flagBuff);
uint8_t buff[1024];
snprintf(buff, 1024, "%s %d-bit %sEndian %s",
archStr(ai),
ArchInfo_getBits(ai),
ArchInfo_isBigEndian(ai) ? "Big" : "Little",
flagBuff);
int len = CString_strlen(buff);
Assert_true(len < 1024);
if (buff[len-1] == ' ') { buff[--len] = '\0'; }
char* out = Allocator_malloc(alloc, len+1);
Bits_memcpy(out, buff, len+1);
return out;
}
static enum ArchInfo_Bits detectBits()
{
switch (sizeof(long)) {
case 4: return ArchInfo_Bits_32;
case 8: return ArchInfo_Bits_64;
default: return ArchInfo_Bits_UNKNOWN;
}
}
static enum ArchInfo_Bits assertBits(enum ArchInfo_Bits bits)
{
enum ArchInfo_Bits detected = detectBits();
if (bits != detected) { return ArchInfo_Bits_UNKNOWN; }
return bits;
}
enum ArchInfo_Endian
{
ArchInfo_Endian_LITTLE = (0 << ArchInfo_Endian_SHIFT),
ArchInfo_Endian_BIG = (1 << ArchInfo_Endian_SHIFT),
ArchInfo_Endian_UNKNOWN = -1,
};
static enum ArchInfo_Endian detectEndian()
{
union {
uint32_t i;
uint8_t c[4];
} bint = { .i = 0xff0000ee };
if (bint.c[0] == 0xff) {
return ArchInfo_Endian_BIG;
} else if (bint.c[0] == 0xee) {
return ArchInfo_Endian_LITTLE;
}
// 2+2=5
Assert_true(0);
return -1;
}
static enum ArchInfo_Endian assertEndian(enum ArchInfo_Endian end)
{
enum ArchInfo_Endian detected = detectEndian();
if (end != detected) { return ArchInfo_Endian_UNKNOWN; }
return end;
}
enum ArchInfo ArchInfo_detect()
{
enum ArchInfo arch = 0;
#if defined(__amd64__) || defined(__x86_64__) || defined(__AMD64__) || \
defined(_M_X64) || defined(__amd64)
arch |= ArchInfo_Arch_AMD64;
#elif defined(__i386__) || defined(__x86__) || defined(__X86__) || \
defined(_M_IX86) || defined(__i386)
arch |= ArchInfo_Arch_I386;
#elif defined(__ARM_EABI__) || defined(__arm__)
arch |= ArchInfo_Arch_ARM;
#elif defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) || defined(_ARCH_PPC64)
arch |= ArchInfo_Arch_PPC | ArchInfo_Bits_64;
#elif defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) || defined(_ARCH_PPC)
arch |= ArchInfo_Arch_PPC | ArchInfo_Bits_32;
#elif defined(__sparcv9__) || defined(__sparcv9)
arch |= ArchInfo_Arch_SPARC | ArchInfo_Sparc_v9;
#elif defined(__sparc_v8__)
arch |= ArchInfo_Arch_SPARC | ArchInfo_Sparc_v8;
#elif defined(__sparc__) || defined(__sparc)
arch |= ArchInfo_Arch_SPARC;
#elif defined(__mips__) || defined(__mips) || defined(__MIPS__)
arch |= ArchInfo_Arch_MIPS;
#else
arch |= ArchInfo_UNKNOWN;
#endif
switch ((int)arch) {
case ArchInfo_Arch_AMD64:
return arch | assertBits(ArchInfo_Bits_64) | assertEndian(ArchInfo_Endian_LITTLE);
case ArchInfo_Arch_I386: {
#define HAS_SSE2 0
#ifdef __SSE2__
#undef HAS_SSE2
#define HAS_SSE2 1
#endif
if (HAS_SSE2) {
arch |= ArchInfo_I386_SSE2;
}
return arch | assertBits(ArchInfo_Bits_32) | assertEndian(ArchInfo_Endian_LITTLE);
}
case ArchInfo_Arch_ARM: {
#define HAS_EABI 0
#ifdef __ARM_EABI__
#undef HAS_EABI
#define HAS_EABI 1
#endif
if (HAS_EABI) {
arch |= ArchInfo_Arm_EABI;
}
#define HAS_NEON 0
#ifdef __ARM_NEON__
#undef HAS_NEON
#define HAS_NEON 1
#endif
if (HAS_NEON) {
arch |= ArchInfo_Arm_NEON;
}
return arch | detectBits() | detectEndian();
}
case (ArchInfo_Arch_PPC | ArchInfo_Bits_64): {
return arch | assertBits(ArchInfo_Bits_64) | assertEndian(ArchInfo_Endian_BIG);
}
case (ArchInfo_Arch_PPC | ArchInfo_Bits_32): {
return arch | assertBits(ArchInfo_Bits_32) | assertEndian(ArchInfo_Endian_BIG);
}
case (ArchInfo_Arch_SPARC | ArchInfo_Sparc_v9): {
return arch | assertBits(ArchInfo_Bits_64) | assertEndian(ArchInfo_Endian_BIG);
}
case (ArchInfo_Arch_SPARC | ArchInfo_Sparc_v8): {
return arch | assertBits(ArchInfo_Bits_32) | assertEndian(ArchInfo_Endian_BIG);
}
case ArchInfo_Arch_SPARC: {
arch |= detectBits();
switch (ArchInfo_getBits(arch)) {
case ArchInfo_Bits_32: arch |= ArchInfo_Sparc_v8; break;
case ArchInfo_Bits_64: arch |= ArchInfo_Sparc_v9; break;
default: return ArchInfo_UNKNOWN;
}
return arch | assertEndian(ArchInfo_Endian_BIG);
}
case ArchInfo_Arch_MIPS: {
#define IS_OABI 0
#if defined(_ABIO32)
#undef IS_OABI
#define IS_OABI 1
#endif
if (IS_OABI) {
return arch | ArchInfo_Mips_OABI | assertBits(ArchInfo_Bits_32);
}
#define IS_NABI 0
#if defined(_ABIN32)
#undef IS_NABI
#define IS_NABI 1
#endif
if (IS_NABI) {
if (IS_OABI) { return ArchInfo_UNKNOWN; }
return arch | ArchInfo_Mips_NABI | assertBits(ArchInfo_Bits_32);
}
// If it's not oabi or nabi and it's not mips64 then it's UNKNOWN.
return arch | assertBits(ArchInfo_Bits_64);
}
default: return ArchInfo_UNKNOWN;
}
}