// SPDX-FileCopyrightText: 2021 08A <dev@08a.re>
// SPDX-FileCopyrightText: 2008-2020 nibble <nibble.ds@gmail.com>
// SPDX-FileCopyrightText: 2008-2020 pancake <pancake@nopcode.org>
// SPDX-FileCopyrightText: 2008-2020 alvaro_fe <alvaro.felipe91@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only

#include "elf.h"
#include "elf/glibc_elf.h"
#include "rz_bin.h"
#include "rz_bp.h"
#include "rz_util/rz_buf.h"
#include "rz_util/rz_log.h"
#include "rz_util/rz_str.h"
#include "rz_util/rz_strbuf.h"

#define VERSYM_VERSION 0x7fff

// Modes analogous to CS_MODE_*
#define MODE_MCLASS 1
#define MODE_V8     2

struct mips_bits_translation {
	Elf_(Word) type;
	int bits;
};

struct section_note_osabi_translation {
	const char *note_name;
	const char *os_name;
};

struct machine_name_translation {
	Elf_(Half) machine;
	const char *name;
};

struct class_translation {
	unsigned char class;
	const char *name;
};

struct cpu_arch_translation {
	Elf_(Word) arch;
	const char *name;
};

struct arch_translation {
	Elf_(Half) arch;
	const char *name;
};

struct ver_flags_translation {
	ut32 flag;
	const char *name;
};

static const struct section_note_osabi_translation section_note_osabi_translation_table[] = {
	{ ".note.openbsd.ident", "openbsd" },
	{ ".note.minix.ident", "minix" },
	{ ".note.netbsd.ident", "netbsd" },
	{ ".note.android.ident", "android" }
};

static const struct machine_name_translation machine_name_translation_table[] = {
	{ EM_NONE, "No machine" },
	{ EM_M32, "AT&T WE 32100" },
	{ EM_SPARC, "SUN SPARC" },
	{ EM_386, "Intel 80386" },
	{ EM_68K, "Motorola m68k family" },
	{ EM_88K, "Motorola m88k family" },
	{ EM_860, "Intel 80860" },
	{ EM_MIPS, "MIPS R3000 big-endian" },
	{ EM_S370, "IBM System/370" },
	{ EM_MIPS_RS3_LE, "MIPS R3000 little-endian" },
	{ EM_PARISC, "HPPA" },
	{ EM_VPP500, "Fujitsu VPP500" },
	{ EM_SPARC32PLUS, "Sun's \"v8plus\"" },
	{ EM_960, "Intel 80960" },
	{ EM_PPC, "PowerPC" },
	{ EM_PPC64, "PowerPC 64-bit" },
	{ EM_S390, "IBM S390" },
	{ EM_V800, "NEC V800 series" },
	{ EM_FR20, "Fujitsu FR20" },
	{ EM_RH32, "TRW RH-32" },
	{ EM_RCE, "Motorola RCE" },
	{ EM_ARM, "ARM" },
	{ EM_BLACKFIN, "Analog Devices Blackfin" },
	{ EM_FAKE_ALPHA, "Digital Alpha" },
	{ EM_SH, "Hitachi SH" },
	{ EM_SPARCV9, "SPARC v9 64-bit" },
	{ EM_TRICORE, "Siemens Tricore" },
	{ EM_ARC, "Argonaut RISC Core" },
	{ EM_H8_300, "Hitachi H8/300" },
	{ EM_H8_300H, "Hitachi H8/300H" },
	{ EM_H8S, "Hitachi H8S" },
	{ EM_H8_500, "Hitachi H8/500" },
	{ EM_IA_64, "Intel Merced" },
	{ EM_MIPS_X, "Stanford MIPS-X" },
	{ EM_COLDFIRE, "Motorola Coldfire" },
	{ EM_68HC12, "Motorola M68HC12" },
	{ EM_MMA, "Fujitsu MMA Multimedia Accelerator" },
	{ EM_PCP, "Siemens PCP" },
	{ EM_NCPU, "Sony nCPU embeeded RISC" },
	{ EM_NDR1, "Denso NDR1 microprocessor" },
	{ EM_STARCORE, "Motorola Start*Core processor" },
	{ EM_ME16, "Toyota ME16 processor" },
	{ EM_ST100, "STMicroelectronic ST100 processor" },
	{ EM_TINYJ, "Advanced Logic Corp. Tinyj emb.fam" },
	{ EM_X86_64, "AMD x86-64 architecture" },
	{ EM_LANAI, "32bit LANAI architecture" },
	{ EM_LANAI_OLD, "32bit LANAI architecture" },
	{ EM_PDSP, "Sony DSP Processor" },
	{ EM_PDP10, "Digital Equipment Corp. PDP-10" },
	{ EM_PDP11, "Digital Equipment Corp. PDP-11" },
	{ EM_FX66, "Siemens FX66 microcontroller" },
	{ EM_ST9PLUS, "STMicroelectronics ST9+ 8/16 mc" },
	{ EM_ST7, "STmicroelectronics ST7 8 bit mc" },
	{ EM_68HC16, "Motorola MC68HC16 microcontroller" },
	{ EM_68HC11, "Motorola MC68HC11 microcontroller" },
	{ EM_68HC08, "Motorola MC68HC08 microcontroller" },
	{ EM_68HC05, "Motorola MC68HC05 microcontroller" },
	{ EM_SVX, "Silicon Graphics SVx" },
	{ EM_ST19, "STMicroelectronics ST19 8 bit mc" },
	{ EM_VAX, "Digital VAX" },
	{ EM_CRIS, "Axis Communications 32-bit embedded processor" },
	{ EM_JAVELIN, "Infineon Technologies 32-bit embedded processor" },
	{ EM_FIREPATH, "Element 14 64-bit DSP Processor" },
	{ EM_ZSP, "LSI Logic 16-bit DSP Processor" },
	{ EM_MMIX, "Donald Knuth's educational 64-bit processor" },
	{ EM_HUANY, "Harvard University machine-independent object files" },
	{ EM_PRISM, "SiTera Prism" },
	{ EM_AVR, "Atmel AVR 8-bit microcontroller" },
	{ EM_FR30, "Fujitsu FR30" },
	{ EM_D10V, "Mitsubishi D10V" },
	{ EM_D30V, "Mitsubishi D30V" },
	{ EM_V850, "NEC v850" },
	{ EM_M32R, "Mitsubishi M32R" },
	{ EM_MN10300, "Matsushita MN10300" },
	{ EM_MN10200, "Matsushita MN10200" },
	{ EM_PJ, "picoJava" },
	{ EM_OPENRISC, "OpenRISC 32-bit embedded processor" },
	{ EM_ARC_A5, "ARC Cores Tangent-A5" },
	{ EM_XTENSA, "Tensilica Xtensa Architecture" },
	{ EM_AARCH64, "ARM aarch64" },
	{ EM_PROPELLER, "Parallax Propeller" },
	{ EM_MICROBLAZE, "Xilinx MicroBlaze" },
	{ EM_RISCV, "RISC V" },
	{ EM_VIDEOCORE3, "VideoCore III" },
	{ EM_VIDEOCORE4, "VideoCore IV" },
	{ EM_LATTICEMICO32, "RISC processor for Lattice FPGA architecture" },
	{ EM_SE_C17, "Seiko Epson C17 family" },
	{ EM_TI_C6000, "The Texas Instruments TMS320C6000 DSP family" },
	{ EM_TI_C2000, "The Texas Instruments TMS320C2000 DSP family" },
	{ EM_TI_C5500, "The Texas Instruments TMS320C55x DSP family" },
	{ EM_TI_ARP32, "Texas Instruments Application Specific RISC Processor, 32bit fetch" },
	{ EM_TI_PRU, "Texas Instruments Programmable Realtime Unit" },
	{ EM_MMDSP_PLUS, "STMicroelectronics 64bit VLIW Data Signal Processor" },
	{ EM_CYPRESS_M8C, "Cypress M8C microprocessor" },
	{ EM_R32C, "Renesas R32C series microprocessors" },
	{ EM_TRIMEDIA, "NXP Semiconductors TriMedia architecture family" },
	{ EM_QDSP6, "QUALCOMM DSP6 Processor" }, // Nonstandard
	{ EM_8051, "Intel 8051 and variants" },
	{ EM_STXP7X, "STMicroelectronics STxP7x family of configurable and extensible RISC processors" },
	{ EM_NDS32, "Andes Technology compact code size embedded RISC processor family" },
	{ EM_ECOG1, "Cyan Technology eCOG1X family" },
	{ EM_MAXQ30, "Dallas Semiconductor MAXQ30 Core Micro-controllers" },
	{ EM_XIMO16, "New Japan Radio (NJR) 16-bit DSP Processor" },
	{ EM_MANIK, "M2000 Reconfigurable RISC Microprocessor" },
	{ EM_CRAYNV2, "Cray Inc. NV2 vector architecture" },
	{ EM_RX, "Renesas RX family" },
	{ EM_METAG, "Imagination Technologies META processor architecture" },
	{ EM_MCST_ELBRUS, "MCST Elbrus general purpose hardware architecture" },
	{ EM_ECOG16, "Cyan Technology eCOG16 family" },
	{ EM_CR16, "National Semiconductor CompactRISC CR16 16-bit microprocessor" },
	{ EM_ETPU, "Freescale Extended Time Processing Unit" },
	{ EM_SLE9X, "Infineon Technologies SLE9X core" },
	{ EM_L10M, "Intel L10M" },
	{ EM_K10M, "Intel K10M" },
	{ EM_AVR32, "Atmel Corporation 32-bit microprocessor family" },
	{ EM_STM8, "STMicroeletronics STM8 8-bit microcontroller" },
	{ EM_TILE64, "Tilera TILE64 multicore architecture family" },
	{ EM_TILEPRO, "Tilera TILEPro multicore architecture family" },
	{ EM_CUDA, "NVIDIA CUDA architecture" },
	{ EM_TILEGX, "Tilera TILE-Gx multicore architecture family" },
	{ EM_CLOUDSHIELD, "CloudShield architecture family" },
	{ EM_COREA_1ST, "KIPO-KAIST Core-A 1st generation processor family" },
	{ EM_COREA_2ND, "KIPO-KAIST Core-A 2nd generation processor family" },
	{ EM_ARC_COMPACT2, "Synopsys ARCompact V2" },
	{ EM_OPEN8, "Open8 8-bit RISC soft processor core" },
	{ EM_RL78, "Renesas RL78 family" },
	{ EM_VIDEOCORE5, "Broadcom VideoCore V processor" },
	{ EM_78KOR, "Renesas 78KOR family" },
	{ EM_BA1, "Beyond BA1 CPU architecture" },
	{ EM_BA2_NON_STANDARD, "Beyond BA2 CPU architecture" },
	{ EM_BA2, "Beyond BA2 CPU architecture" },
	{ EM_XCORE, "XMOS xCORE processor family" },
	{ EM_MCHP_PIC, "Microchip 8-bit PIC(r) family" },
	{ EM_INTEL205, "Reserved by Intel" },
	{ EM_INTEL206, "Reserved by Intel" },
	{ EM_INTEL207, "Reserved by Intel" },
	{ EM_INTEL208, "Reserved by Intel" },
	{ EM_INTEL209, "Reserved by Intel" },
	{ EM_KM32, "KM211 KM32 32-bit processor" },
	{ EM_KMX32, "KM211 KMX32 32-bit processor" },
	{ EM_KMX16, "KM211 KMX16 16-bit processor" },
	{ EM_KMX8, "KM211 KMX8 8-bit processor" },
	{ EM_KVARC, "KM211 KVARC processor" },
	{ EM_CDP, "Paneve CDP architecture family" },
	{ EM_COGE, "Cognitive Smart Memory Processor" },
	{ EM_COOL, "Bluechip Systems CoolEngine" },
	{ EM_NORC, "Nanoradio Optimized RISC" },
	{ EM_CSR_KALIMBA, "CSR Kalimba architecture family" },
	{ EM_Z80, "Zilog Z80" },
	{ EM_VISIUM, "Controls and Data Services VISIUMcore processor" },
	{ EM_FT32, "FTDI Chip FT32 high performance 32-bit RISC architecture" },
	{ EM_MOXIE, "Moxie processor family" },
	{ EM_AMDGPU, "AMD GPU architecture" },
	{ EM_BPF, "Linux BPF" },
	{ EM_KVX, "Kalray VLIW core of the MPPA processor family" },
	{ EM_ALPHA, "DEC Alpha" },
	{ EM_CEVA, "CEVA Processor Architecture Family" },
	{ EM_CEVA_X2, "CEVA X2 Processor Family" },
	{ EM_GRAPHCORE_IPU, "Graphcore Intelligent Processing Unit" },
	{ EM_IMG1, "Imagination Technologies (nanomips)" },
	{ EM_NFP, "Netronome Flow Processor" },
	{ EM_VE, "NEC Vector Engine" },
	{ EM_CSKY, "C-SKY processor family" },
	{ EM_ARC_COMPACT3_64, "Synopsys ARCv2.3 64-bit" },
	{ EM_MCS6502, "MOS Technology MCS 6502 processor" },
	{ EM_ARC_COMPACT3, "Synopsys ARCv2.3 32-bit" },
	{ EM_KVX, "Kalray VLIW core of the MPPA processor family" },
	{ EM_65816, "WDC 65816/65C816" },
	{ EM_LOONGARCH, "LoongArch" },
	{ EM_KF32, "ChipON KungFu32" },
	{ EM_U16_U8CORE, "LAPIS nX-U16/U8" },
	{ EM_TACHYUM, "Tachyum" },
	{ EM_56800EF, "NXP 56800EF Digital Signal Controller (DSC)" },
};

static const struct class_translation class_translation_table[] = {
	{ ELFCLASSNONE, "none" },
	{ ELFCLASS32, "ELF32" },
	{ ELFCLASS64, "ELF64" }
};

static const struct cpu_arch_translation gnu_mips_mach_translation_table[] = {
	{ EF_MIPS_MACH_3900, "3900 " },
	{ EF_MIPS_MACH_4010, "4010 " },
	{ EF_MIPS_MACH_4100, "4100 " },
	{ EF_MIPS_MACH_ALLEGREX, "allegrex " },
	{ EF_MIPS_MACH_4650, "4650 " },
	{ EF_MIPS_MACH_4120, "4120 " },
	{ EF_MIPS_MACH_4111, "4111 " },
	{ EF_MIPS_MACH_SB1, "sb1 " },
	{ EF_MIPS_MACH_OCTEON, "octeon " },
	{ EF_MIPS_MACH_XLR, "xlr " },
	{ EF_MIPS_MACH_OCTEON2, "octeon2 " },
	{ EF_MIPS_MACH_OCTEON3, "octeon3 " },
	{ EF_MIPS_MACH_5400, "5400 " },
	{ EF_MIPS_MACH_5900, "5900 " },
	{ EF_MIPS_MACH_IAMR2, "iamr2 " },
	{ EF_MIPS_MACH_5500, "5500 " },
	{ EF_MIPS_MACH_9000, "9000 " },
	{ EF_MIPS_MACH_LS2E, "ls2e " },
	{ EF_MIPS_MACH_LS2F, "ls2f " },
	{ EF_MIPS_MACH_GS464, "gs464 " },
	{ EF_MIPS_MACH_GS464E, "gs464e " },
	{ EF_MIPS_MACH_GS264E, "gs264e " },
};

static const struct mips_bits_translation mips_bits_translation_table[] = {
	{ EF_MIPS_ARCH_1, 32 },
	{ EF_MIPS_ARCH_2, 32 },
	{ EF_MIPS_ARCH_3, 64 },
	{ EF_MIPS_ARCH_4, 64 },
	{ EF_MIPS_ARCH_5, 64 },
	{ EF_MIPS_ARCH_32, 32 },
	{ EF_MIPS_ARCH_64, 64 },
	{ EF_MIPS_ARCH_32R2, 32 },
	{ EF_MIPS_ARCH_64R2, 64 },
	{ EF_MIPS_ARCH_32R6, 32 },
	{ EF_MIPS_ARCH_64R6, 64 },
};

static const struct cpu_arch_translation gnu_mips_arch_translation_table32[] = {
	{ EF_MIPS_ARCH_1, "mips32" },
	{ EF_MIPS_ARCH_2, "mips2" },
	{ EF_MIPS_ARCH_3, "mips3" },
	{ EF_MIPS_ARCH_4, "mips4" },
	{ EF_MIPS_ARCH_5, "mips5" },
	{ EF_MIPS_ARCH_32, "mips32" },
	{ EF_MIPS_ARCH_64, "mips64" },
	{ EF_MIPS_ARCH_32R2, "mips32r2" },
	{ EF_MIPS_ARCH_64R2, "mips64r2" },
	{ EF_MIPS_ARCH_32R6, "mips32r6" },
	{ EF_MIPS_ARCH_64R6, "mips64r6" },
};

static const struct cpu_arch_translation gnu_mips_arch_translation_table64[] = {
	{ EF_MIPS_ARCH_1, "mips64" }, // also used for generic mips, so we default to mips64
	{ EF_MIPS_ARCH_2, "mips64r2" },
	{ EF_MIPS_ARCH_3, "mips64r3" },
	{ EF_MIPS_ARCH_4, "mips64r5" },
	{ EF_MIPS_ARCH_5, "mips64r6" },
	{ EF_MIPS_ARCH_32, "mips64" },
	{ EF_MIPS_ARCH_64, "mips64" },
	{ EF_MIPS_ARCH_32R2, "mips64r2" }, // should never happen but default to 64bit
	{ EF_MIPS_ARCH_64R2, "mips64r2" },
	{ EF_MIPS_ARCH_32R6, "mips64r6" }, // should never happen but default to 64bit
	{ EF_MIPS_ARCH_64R6, "mips64r6" },
};

static const struct cpu_arch_translation cpu_hppa_translation_table[] = {
	{ EFA_PARISC_1_0, "hppa1.0" },
	{ EFA_PARISC_1_1, "hppa2.0" }, // In practice many ELF file set as 1.1 version
				       // contain 2.0 opcodes
	{ EFA_PARISC_2_0, "hppa2.0" },
};

static const struct arch_translation arch_translation_table[] = {
	{ EM_ALPHA, "alpha" },
	{ EM_ARC, "arc" },
	{ EM_ARC_A5, "arc" },
	{ EM_ARC_COMPACT3_64, "arc" },
	{ EM_ARC_COMPACT3, "arc" },
	{ EM_H8_300, "h8300" },
	{ EM_H8_300H, "h8300h" },
	{ EM_H8S, "h8s" },
	{ EM_H8_500, "h8500" },
	{ EM_AVR, "avr" },
	{ EM_BA2_NON_STANDARD, "ba2" },
	{ EM_BA2, "ba2" },
	{ EM_CRIS, "cris" },
	{ EM_68K, "m68k" },
	{ EM_MIPS, "mips" },
	{ EM_MIPS_RS3_LE, "mips" },
	{ EM_MIPS_X, "mips" },
	{ EM_IMG1, "mips" },
	{ EM_MCST_ELBRUS, "elbrus" },
	{ EM_TRICORE, "tricore" },
	{ EM_RCE, "mcore" },
	{ EM_ARM, "arm" },
	{ EM_AARCH64, "arm" },
	{ EM_QDSP6, "hexagon" },
	{ EM_BLACKFIN, "blackfin" },
	{ EM_SPARC, "sparc" },
	{ EM_SPARC32PLUS, "sparc" },
	{ EM_SPARCV9, "sparc" },
	{ EM_PPC, "ppc" },
	{ EM_PPC64, "ppc" },
	{ EM_MCHP_PIC, "pic" },
	{ EM_PARISC, "hppa" },
	{ EM_PROPELLER, "propeller" },
	{ EM_MICROBLAZE, "microblaze.gnu" },
	{ EM_RISCV, "riscv" },
	{ EM_RL78, "rl78" },
	{ EM_RX, "rx" },
	{ EM_VAX, "vax" },
	{ EM_XTENSA, "xtensa" },
	{ EM_LANAI, "lanai" },
	{ EM_VIDEOCORE3, "vc4" },
	{ EM_VIDEOCORE4, "vc4" },
	{ EM_MSP430, "msp430" },
	{ EM_SH, "sh" },
	{ EM_V810, "v810" },
	{ EM_V800, "v850" },
	{ EM_V850, "v850" },
	{ EM_IA_64, "ia64" },
	{ EM_S390, "sysz" },
	{ EM_386, "x86" },
	{ EM_X86_64, "x86" },
	{ EM_NONE, "null" },
	{ EM_BPF, "bpf" },
	{ EM_KVX, "kvx" },
	{ EM_CEVA, "ceva" },
	{ EM_CEVA_X2, "ceva" },
	{ EM_GRAPHCORE_IPU, "graphcore_ipu" },
	{ EM_NFP, "nfp" },
	{ EM_VE, "ve" },
	{ EM_CSKY, "csky" },
	{ EM_MCS6502, "mcs6502" },
	{ EM_KVX, "kvx" },
	{ EM_65816, "65816" },
	{ EM_LOONGARCH, "loongarch" },
	{ EM_KF32, "kf32" },
	{ EM_U16_U8CORE, "u16_u8core" },
	{ EM_TACHYUM, "tachyum" },
	{ EM_56800EF, "56800ef" },
	{ EM_XCORE, "xcore" },
};

static const struct ver_flags_translation ver_flags_translation_table[] = {
	{ VER_FLG_BASE, "BASE " },
	{ VER_FLG_BASE | VER_FLG_WEAK, "| " },
	{ VER_FLG_WEAK, "WEAK " },
	{ ~(VER_FLG_BASE | VER_FLG_WEAK), "| <unknown>" }
};

static ut64 get_main_offset_from_symbol(ELFOBJ *bin) {
	RzBinElfSymbol *symbol;
	rz_bin_elf_foreach_symbols(bin, symbol) {
		if (symbol->name && !strcmp(symbol->name, "main")) {
			if (symbol->paddr != UT64_MAX) {
				return symbol->paddr;
			}

			return symbol->vaddr;
		}
	}

	return UT64_MAX;
}

static ut64 get_main_offset_linux_64_pie(ELFOBJ *bin, ut64 entry, ut8 *buf) {
	/* linux64 pie main -- probably buggy in some cases */
	int bo = 29; // Begin offset may vary depending on the entry prelude
	// endbr64 - fedora bins have this
	if (buf[0] == 0xf3 && buf[1] == 0x0f && buf[2] == 0x1e && buf[3] == 0xfa) {
		// Change begin offset if binary starts with 'endbr64'
		bo = 33;
		// double xor for init and fini
		if (!memcmp(buf + 19, "\x45\x31\xc0\x31\xc9", 5)) {
			bo = 24;
		}
	}
	if (buf[bo] == 0x48) {
		ut8 ch = buf[bo + 1];
		if (ch == 0x8d) { // lea rdi, qword [rip + MAINDELTA]
			ut8 *p = buf + bo + 3;
			st32 maindelta = (st32)rz_read_le32(p);
			ut64 ventry = Elf_(rz_bin_elf_p2v)(bin, entry);
			if (ventry == UT64_MAX) {
				return UT64_MAX;
			}
			ut64 vmain = (ut64)(ventry + bo + maindelta) + 7;
			return Elf_(rz_bin_elf_v2p)(bin, vmain);
		} else if (ch == 0xc7) { // mov rdi, 0xADDR
			ut8 *p = buf + bo + 3;
			ut64 addr = (ut64)rz_read_le32(p);
			return Elf_(rz_bin_elf_v2p)(bin, addr);
		}
	}

	return UT64_MAX;
}

static ut64 get_main_offset_x86_non_pie(ELFOBJ *bin, ut64 entry, ut8 *buf) {
	// X86-NONPIE
#if RZ_BIN_ELF64
	if (!memcmp(buf, "\x49\x89\xd9", 3) && buf[156] == 0xe8) { // openbsd
		return rz_read_le32(buf + 157) + entry + 156 + 5;
	}
	if (!memcmp(buf + 29, "\x48\xc7\xc7", 3)) { // linux
		ut64 addr = (ut64)rz_read_le32(buf + 29 + 3);
		return Elf_(rz_bin_elf_v2p)(bin, addr);
	}
#else
	if (buf[23] == '\x68') {
		ut64 addr = (ut64)rz_read_le32(buf + 23 + 1);
		return Elf_(rz_bin_elf_v2p)(bin, addr);
	}
#endif

	return UT64_MAX;
}

static ut64 get_main_offset_x86_pie(ELFOBJ *bin, ut64 entry, ut8 *buf) {
	// X86-PIE
	if (buf[0x00] == 0x48 && buf[0x1e] == 0x8d && buf[0x11] == 0xe8) {
		ut32 *pmain = (ut32 *)(buf + 0x30);
		ut64 vmain = Elf_(rz_bin_elf_p2v)(bin, (ut64)*pmain);
		ut64 ventry = Elf_(rz_bin_elf_p2v)(bin, entry);
		if (vmain >> 16 == ventry >> 16) {
			return vmain;
		}
	}
	// X86-PIE
	if (buf[0x1d] == 0x48 && buf[0x1e] == 0x8b) {
		if (!memcmp(buf, "\x31\xed\x49\x89", 4)) { // linux
			ut64 maddr, baddr;
			ut8 n32s[sizeof(ut32)] = { 0 };
			maddr = entry + 0x24 + rz_read_le32(buf + 0x20);
			if (rz_buf_read_at(bin->b, maddr, n32s, sizeof(ut32)) == -1) {
				return 0;
			}
			maddr = (ut64)rz_read_le32(&n32s[0]);
			baddr = (bin->ehdr.e_entry >> 16) << 16;
			if (Elf_(rz_bin_elf_has_segments)(bin)) {
				baddr = Elf_(rz_bin_elf_get_baddr)(bin, NULL);
			}
			maddr += baddr;
			return maddr;
		}
	}

	return UT64_MAX;
}

static ut64 get_main_offset_x86_gcc(ELFOBJ *bin, ut64 entry, ut8 *buf) {
	if (buf[0] != 0xe8 && memcmp(buf + 5, "\x50\xe8\x00\x00\x00\x00\xb8\x01\x00\x00\x00\x53", 12)) {
		return UT64_MAX;
	}

	size_t SIZEOF_CALL = 5;
	ut64 rel_addr = (ut64)(buf[1] + (buf[2] << 8) + (buf[3] << 16) + (buf[4] << 24));
	ut64 addr = Elf_(rz_bin_elf_p2v)(bin, entry + SIZEOF_CALL);
	addr += rel_addr;
	return Elf_(rz_bin_elf_v2p)(bin, addr);
}

static ut64 get_main_offset_mips(ELFOBJ *bin, ut64 entry, ut8 *buf, size_t size) {
	/* get .got, calculate offset of main symbol */
	if (memcmp(buf, "\x21\x00\xe0\x03\x01\x00\x11\x04", 8)) {
		return UT64_MAX;
	}

	/*
	   assuming the startup code looks like
	   got = gp-0x7ff0
	   got[index__libc_start_main] ( got[index_main] );

	   looking for the instruction generating the first argument to find main
	   lw a0, offset(gp)
	   */
	ut64 got_addr;

	if (!Elf_(rz_bin_elf_get_dt_info)(bin, DT_PLTGOT, &got_addr)) {
		return 0;
	}

	ut64 got_offset = Elf_(rz_bin_elf_v2p)(bin, got_addr);
	ut64 gp = got_offset + 0x7ff0;

	for (size_t i = 0; i < size; i += 4) {
		const ut32 instr = rz_read_le32(buf + i);
		if ((instr & 0xffff0000) == 0x8f840000) { // lw a0, offset(gp)
			const short delta = instr & 0x0000ffff;
			rz_buf_read_at(bin->b, /* got_entry_offset = */ gp + delta, buf, 4);
			return Elf_(rz_bin_elf_v2p)(bin, rz_read_le32(buf));
		}
	}

	return 0;
}

static ut64 get_main_offset_v850(ELFOBJ *bin, ut64 entry, ut8 *buf) {
	size_t delta = 0;

	/* distinguish by the first two instructions */

	if (!memcmp(buf, "\x20\xa6\xff\x00\x35\x06\xff\xff\x00\x00", 10)) {
		/* movea 0xFF, r0, r20; mov 0xFFFF, r21 */
		delta = 0x64;
	} else if (!memcmp(buf, "\x20\xa6\xff\x00\x00\xa8", 6)) {
		/* movea 0xFF, r0, r20 ; mov r0, r21 */
		delta = 0x4e;
	}

	if (!delta) {
		return UT64_MAX;
	}

	ut16 jmp_offset = rz_read_le16(buf + delta);
	ut64 entry_vaddr = Elf_(rz_bin_elf_p2v)(bin, entry);
	ut64 vaddr = (entry_vaddr + (delta - 2) + jmp_offset) & ~1;
	return Elf_(rz_bin_elf_v2p)(bin, vaddr);
}

static ut64 get_main_offset_arm_glibc_thumb(ELFOBJ *bin, ut64 entry, ut8 *buf) {
	size_t delta = 0;

	if (!memcmp(buf, "\xf0\x00\x0b\x4f\xf0\x00\x0e\x02\xbc\x6a\x46", 11)) {
		/* newer versions of gcc use push/pop */
		delta = 0x28;
	} else if (!memcmp(buf, "\xf0\x00\x0b\x4f\xf0\x00\x0e\x5d\xf8\x04\x1b", 11)) {
		/* older versions of gcc (4.5.x) use ldr/str */
		delta = 0x30;
	}

	if (delta) {
		ut64 tmp = rz_read_le32(buf + delta - 1) & ~1;
		ut64 pa = Elf_(rz_bin_elf_v2p)(bin, tmp);
		if (pa < rz_buf_size(bin->b)) {
			return pa;
		}
	}

	return UT64_MAX;
}

static ut64 get_main_offset_arm_glibc_non_thumb(ELFOBJ *bin, ut64 entry, ut8 *buf) {
	if (!memcmp(buf, "\x00\xb0\xa0\xe3\x00\xe0\xa0\xe3", 8)) {
		return Elf_(rz_bin_elf_v2p)(bin, rz_read_le32(buf + 0x34) & ~1);
	}

	if (!memcmp(buf, "\x24\xc0\x9f\xe5\x00\xb0\xa0\xe3", 8)) {
		return Elf_(rz_bin_elf_v2p)(bin, rz_read_le32(buf + 0x30) & ~1);
	}

	return UT64_MAX;
}

static ut64 get_main_offset_arm_glibc(ELFOBJ *bin, ut64 entry, ut8 *buf) {
	// ARM Glibc
	if (Elf_(rz_bin_elf_is_thumb_addr)(entry)) {
		return get_main_offset_arm_glibc_thumb(bin, entry, buf);
	} else {
		return get_main_offset_arm_glibc_non_thumb(bin, entry, buf);
	}

	return UT64_MAX;
}

static ut64 get_main_offset_arm64(ELFOBJ *bin, ut64 entry, ut8 *buf) {
	if (buf[0x18 + 3] != 0x58 || buf[0x2f] != 0x00) {
		return UT64_MAX;
	}

	ut64 entry_vaddr = Elf_(rz_bin_elf_p2v)(bin, entry);
	if (entry_vaddr == UT64_MAX) {
		return UT64_MAX;
	}

	ut64 main_addr = rz_read_le32(buf + 0x30);

	if (main_addr >> 16 == entry_vaddr >> 16) {
		return Elf_(rz_bin_elf_v2p)(bin, main_addr);
	}

	return UT64_MAX;
}

static ut64 get_entry_offset_from_shdr(ELFOBJ *bin) {
	RzBinElfSection *section = Elf_(rz_bin_elf_get_section_with_name)(bin, ".init.text");
	if (section) {
		return section->offset;
	}

	section = Elf_(rz_bin_elf_get_section_with_name)(bin, ".text");
	if (section) {
		return section->offset;
	}

	section = Elf_(rz_bin_elf_get_section_with_name)(bin, ".init");
	if (section) {
		return section->offset;
	}

	return UT64_MAX;
}

static ut64 compute_boffset_from_phdr(ELFOBJ *bin) {
	ut64 base = UT64_MAX;

	RzBinElfSegment *iter;
	rz_bin_elf_foreach_segments(bin, iter) {
		if (iter->data.p_type == PT_LOAD) {
			base = RZ_MIN(base, iter->data.p_offset);
		}
	}

	return base == UT64_MAX ? 0 : base;
}

static ut64 compute_baddr_from_phdr(ELFOBJ *bin) {
	ut64 base = UT64_MAX;

	RzBinElfSegment *iter;
	rz_bin_elf_foreach_segments(bin, iter) {
		if (iter->data.p_type == PT_LOAD) {
			base = RZ_MIN(base, iter->data.p_vaddr);
		}
	}

	return base == UT64_MAX ? 0 : base;
}

static bool elf_is_bind_now(ELFOBJ *bin) {
	ut64 flags_1;

	if (Elf_(rz_bin_elf_get_dt_info)(bin, DT_BIND_NOW, NULL)) {
		return true;
	}

	if (Elf_(rz_bin_elf_get_dt_info)(bin, DT_FLAGS_1, &flags_1)) {
		return flags_1 & DF_1_NOW;
	}

	return false;
}

static bool elf_has_gnu_relro(ELFOBJ *bin) {
	if (!Elf_(rz_bin_elf_has_segments)(bin)) {
		return false;
	}

	RzBinElfSegment *segment = Elf_(rz_bin_elf_get_segment_with_type)(bin, PT_GNU_RELRO);
	return segment && segment->is_valid;
}

static int get_bits_common(ELFOBJ *bin) {
	switch (bin->ehdr.e_ident[EI_CLASS]) {
	case ELFCLASS32:
		return 32;
	case ELFCLASS64:
		return 64;
	case ELFCLASSNONE:
	default:
		return 32;
	}
}

static bool has_thumb_symbol(ELFOBJ *bin) {
	RzBinElfSymbol *symbol;
	rz_bin_elf_foreach_symbols(bin, symbol) {
		if (Elf_(rz_bin_elf_is_thumb_addr)(symbol->paddr) || Elf_(rz_bin_elf_is_thumb_addr)(symbol->vaddr)) {
			return true;
		}
	}

	return false;
}

static int get_bits_mips_common(Elf_(Word) mips_type) {
	for (size_t i = 0; i < RZ_ARRAY_SIZE(mips_bits_translation_table); i++) {
		if (mips_type == mips_bits_translation_table[i].type) {
			return mips_bits_translation_table[i].bits;
		}
	}

	return 32;
}

static inline bool is_elf_class64(ELFOBJ *bin) {
	return bin->ehdr.e_ident[EI_CLASS] == ELFCLASS64;
}

static inline bool is_elf_class32(ELFOBJ *bin) {
	return bin->ehdr.e_ident[EI_CLASS] == ELFCLASS32;
}

static RzBuffer *get_riscv_attributes_section(ELFOBJ *bin) {
	RzBinElfSection *section = Elf_(rz_bin_elf_get_section_with_name)(bin, ".riscv.attributes");
	if (!section || section->type != SHT_RISCV_ATTRIBUTES /* shouldn't really be possible */) {
		return NULL;
	}
	return rz_buf_new_slice(bin->b, section->offset, section->size);
}

/// All possible results of getting a riscv attribute of a given tag
typedef enum {
	RISCV_ATTR_NONE = 0, //< no attribute was found with the given tag or parsing error of the section format
	RISCV_ATTR_TRUNCATED_NT_STRING = 1, //< attribute of type null-terminated string, too long to fit into result buffer
	RISCV_ATTR_NT_STRING = 2, //< attribute of type null-terminated string
	RISCV_ATTR_ULEB128 = 3, //< attribute of type uleb128 integer
	RISCV_ATTR_TRUNCATED_ULEB128 = 4, //< attribute of type uleb128 integer, too big to fit into result buffer
} riscv_attr_type;

/**
 * \brief Get a riscv attribute of a given tag from the .riscv.attributes section
 * 		  This section is defined in the RISC-V ELF psABI as a sequence of uleb128 encoded tags
 *        paired with either null-terminated string or uleb128 values.
 *
 * \param sec The .riscv.attributes section
 * \param attr_tag The tag of the attribute to get
 * \param result_maxlen The maximum capacity of the result buffer
 * \param [out] result The result buffer
 * \param [out] result_len The length of usable data in the result buffer, always <= result_maxlen
 * \return riscv_attr_type The type of the found attribute, indicating whether it's a string or uleb128 integer and whether it was truncated
 */
static riscv_attr_type get_riscv_attribute_from_section(RzBuffer *sec, ut64 attr_tag, size_t result_maxlen, ut8 *result, size_t *result_len) {
	// format:
	/*
	 * <format-byte> <subsection 4-byte length> <null-terminated vendor name> <one or more tag-value pairs> */
	/* <tag-value-pair> = <even uleb128 tag> <uleb128 value>
	 *				    | <odd  uleb128 tag> <null-terminated string value>
	 */
	ut64 curr = 0;

	// format byte
	ut8 format = 0;
	if (!sec || !rz_buf_read8_offset(sec, &curr, &format) || format != 'A') {
		RZ_LOG_ERROR("Can't read the format byte of the RISCV attrbiute section or found a different format (expected 'A' at section start)\n");
		return RISCV_ATTR_NONE;
	}

	// subsection length
	ut32 subsec_len = 0;
	if (!rz_buf_read_ble32_offset(sec, &curr, &subsec_len, /* big endian? */ false)) {
		RZ_LOG_ERROR("Can't read the subsection length of the RISCV attribute section\n");
		return RISCV_ATTR_NONE;
	}

	// skip vendor name
	ut8 byte = 1;
	while (byte != '\0') {
		if (!rz_buf_read8_offset(sec, &curr, &byte)) {
			RZ_LOG_ERROR("Can't read vendor name in RISCV attribute section\n");
			return RISCV_ATTR_NONE;
		}
	}

	// now parse the tag-value array
	while (curr < rz_buf_size(sec)) {
		ut64 tag = 0;
		ut32 num_bytes_read = rz_buf_uleb128_at(sec, curr, &tag);
		curr += num_bytes_read;

		if (num_bytes_read > 8) {
			RZ_LOG_WARN("Can't interpret tag value between offsets %" PFMT64u "-%" PFMT64u ": "
				    "tag is too large at %d bytes, parser can only represent 64-bit tags (skipped)\n",
				curr - num_bytes_read, curr, num_bytes_read);
			continue;
		}
		if (tag != attr_tag) {
			continue;
		}

		// RISCV ABI spec says that there are only 2 types of values associated with tags
		// 		1- Even tags: ULEB128 values (self-delimiting, no header length necessary)
		// 		2- Odd tags: Null-terminated strings (also self-delimiting)
		// Like ARM but even simpler, no special cases for tags below 32
		bool tag_is_odd = tag & 0x1;

		size_t result_curr = 0;
		// value is a null-terminated string
		if (tag_is_odd) {
			int string_starts_at = curr;
			while (curr < rz_buf_size(sec) && result_curr < result_maxlen) {
				bool succeeded = rz_buf_read8_offset(sec, &curr, &result[result_curr++]);
				if (!succeeded) {
					RZ_LOG_ERROR("Can't read the null-terminated string for tag %" PFMT64d ", starting at offset %d\n", tag, string_starts_at);
					break;
				}
				// done
				if (result[result_curr - 1] == '\0') {
					break;
				}
			}

			bool result_overflow = false;
			// have we filled the buffer ?
			if (result_curr >= result_maxlen) {
				result_overflow = result[result_maxlen - 1] != '\0';
				// must force the last byte to null, in case the original string was too long
				result[result_maxlen - 1] = '\0';
			}
			*result_len = result_curr;
			return result_overflow ? RISCV_ATTR_TRUNCATED_NT_STRING : RISCV_ATTR_NT_STRING;
		} else { // value is a self-delimiting ULEB128 integer
			int uleb_starts_at = curr;
			while (curr < rz_buf_size(sec) && result_curr < result_maxlen) {
				bool succeeded = rz_buf_read8_offset(sec, &curr, &result[result_curr++]);
				if (!succeeded) {
					RZ_LOG_ERROR("Can't read the ULEB128 value for tag %" PFMT64d ", starting at offset %d\n", tag, uleb_starts_at);
					break;
				}
				// last byte in the variable encoding, most-significant bit not set
				if (!(result[result_curr - 1] & 0x80)) {
					break;
				}
			}

			bool result_overflow = false;
			// have we filled the buffer ?
			if (result_curr >= result_maxlen) {
				result_overflow = result[result_maxlen - 1] & 0x80;
				// must force the last byte to a terminal byte, in case the original number was too long
				result[result_maxlen - 1] = result[result_maxlen - 1] & 0x7F;
			}
			*result_len = result_curr;
			return result_overflow ? RISCV_ATTR_TRUNCATED_ULEB128 : RISCV_ATTR_ULEB128;
		}
	}
	return RISCV_ATTR_NONE;
}

static int get_bits_mips(ELFOBJ *bin) {
	if (is_elf_class64(bin)) {
		return 64;
	}

	const Elf_(Word) mips_type = bin->ehdr.e_flags & EF_MIPS_ARCH;
	return get_bits_mips_common(mips_type);
}

static inline bool arch_is(ELFOBJ *bin, ut64 machine_id) {
	return bin->ehdr.e_machine == machine_id;
}

static inline bool arch_is_nanomips(ELFOBJ *bin) {
	return arch_is(bin, EM_IMG1);
}

static inline bool arch_is_mips(ELFOBJ *bin) {
	return bin->ehdr.e_machine == EM_MIPS ||
		bin->ehdr.e_machine == EM_MIPS_RS3_LE ||
		bin->ehdr.e_machine == EM_MIPS_X ||
		arch_is_nanomips(bin);
}

static inline bool arch_is_h8xx(ELFOBJ *bin) {
	return bin->ehdr.e_machine == EM_H8_300 ||
		bin->ehdr.e_machine == EM_H8_300H ||
		bin->ehdr.e_machine == EM_H8S ||
		bin->ehdr.e_machine == EM_H8_500;
}

static inline bool arch_is_sparc(ELFOBJ *bin) {
	return bin->ehdr.e_machine == EM_SPARC ||
		bin->ehdr.e_machine == EM_SPARC32PLUS ||
		bin->ehdr.e_machine == EM_SPARCV9;
}

static inline bool arch_is_arm(ELFOBJ *bin) {
	return bin->ehdr.e_machine == EM_ARM || bin->ehdr.e_machine == EM_AARCH64;
}

static inline bool arch_is_arcompact(ELFOBJ *bin) {
	return bin->ehdr.e_machine == EM_ARC_A5 ||
		bin->ehdr.e_machine == EM_ARC_COMPACT3_64 ||
		bin->ehdr.e_machine == EM_ARC_COMPACT3;
}

static inline bool arch_is_parisc(ELFOBJ *bin) {
	return arch_is(bin, EM_PARISC);
}

static inline bool arch_is_riscv(ELFOBJ *bin) {
	return arch_is(bin, EM_RISCV);
}

static char *read_elf_intrp(ELFOBJ *bin, ut64 addr, size_t size) {
	char *str = malloc(size + 1);
	if (!str) {
		return NULL;
	}

	if (rz_buf_read_at(bin->b, addr, (ut8 *)str, size) < 0) {
		free(str);
		return NULL;
	}

	str[size] = 0;

	return str;
}

static char *get_elf_intrp(ELFOBJ *bin, RzBinElfSegment *segment) {
	ut64 addr = segment->data.p_offset;
	size_t size = segment->data.p_filesz;

	if (!sdb_num_set(bin->kv, "elf_header.intrp_addr", addr) ||
		!sdb_num_set(bin->kv, "elf_header.intrp_size", size)) {
		return NULL;
	}

	if (size < 1 || size > rz_buf_size(bin->b)) {
		return NULL;
	}

	char *str = read_elf_intrp(bin, addr, size);
	if (!str) {
		return NULL;
	}

	if (!sdb_set(bin->kv, "elf_header.intrp", str)) {
		free(str);
		return NULL;
	}

	return str;
}

static Elf_(Xword) get_dt_rpath(ELFOBJ *bin) {
	ut64 path;

	if (Elf_(rz_bin_elf_get_dt_info)(bin, DT_RPATH, &path)) {
		return path;
	}

	if (Elf_(rz_bin_elf_get_dt_info)(bin, DT_RUNPATH, &path)) {
		return path;
	}

	return 0;
}

static char *get_ver_flags(ut32 flags) {
	char *result = NULL;

	if (!flags) {
		return rz_str_dup("none");
	}

	for (size_t i = 0; i < RZ_ARRAY_SIZE(ver_flags_translation_table); i++) {
		if (flags & ver_flags_translation_table[i].flag) {
			result = rz_str_append(result, ver_flags_translation_table[i].name);
		}
	}

	return result;
}

static bool has_dt_rpath_entry(ELFOBJ *bin) {
	return Elf_(rz_bin_elf_get_dt_info)(bin, DT_RPATH, NULL) || Elf_(rz_bin_elf_get_dt_info)(bin, DT_RUNPATH, NULL);
}

static char *get_osabi_name_from_section_note(ELFOBJ *bin, RzBinElfSection *section) {
	if (section->type != SHT_NOTE) {
		return NULL;
	}

	for (size_t i = 0; i < RZ_ARRAY_SIZE(section_note_osabi_translation_table); i++) {
		if (!strcmp(section->name, section_note_osabi_translation_table[i].note_name)) {
			return rz_str_dup(section_note_osabi_translation_table[i].os_name);
		}
	}

	return NULL;
}

static char *get_osabi_name_from_shdr(ELFOBJ *bin) {
	if (!bin->shstrtab) {
		return NULL;
	}

	RzBinElfSection *section;
	rz_bin_elf_foreach_sections(bin, section) {
		if (!section->is_valid) {
			continue;
		}

		char *tmp = get_osabi_name_from_section_note(bin, section);
		if (tmp) {
			return tmp;
		}
	}

	return NULL;
}

static char *get_osabi_name_from_ehdr(ELFOBJ *bin) {
	switch (bin->ehdr.e_ident[EI_OSABI]) {
	case ELFOSABI_LINUX:
		return rz_str_dup("linux");
	case ELFOSABI_SOLARIS:
		return rz_str_dup("solaris");
	case ELFOSABI_FREEBSD:
		return rz_str_dup("freebsd");
	case ELFOSABI_HPUX:
		return rz_str_dup("hpux");
	}

	return NULL;
}

static char *add_abi_info(ELFOBJ *bin, char *head_flag) {
	char *str = Elf_(rz_bin_elf_get_abi)(bin);

	if (str) {
		head_flag = rz_str_appendf(head_flag, " %s", str);
		free(str);
	}

	return head_flag;
}

static char *add_cpu_info(ELFOBJ *bin, char *head_flag) {
	char *str = Elf_(rz_bin_elf_get_cpu)(bin);

	if (str) {
		head_flag = rz_str_append_owned(head_flag, str);
	}

	return head_flag;
}

static char *get_head_flag(ELFOBJ *bin) {
	char *head_flag = NULL;

	head_flag = add_cpu_info(bin, head_flag);
	head_flag = add_abi_info(bin, head_flag);

	return head_flag;
}

static bool file_type_is_processor_specific(ELFOBJ *bin) {
	return bin->ehdr.e_type >= ET_LOPROC && bin->ehdr.e_type <= ET_HIPROC;
}

static bool file_type_is_os_specific(ELFOBJ *bin) {
	return bin->ehdr.e_type >= ET_LOOS && bin->ehdr.e_type <= ET_HIOS;
}

static char *get_file_type_basic(RZ_NONNULL ELFOBJ *bin) {
	switch (bin->ehdr.e_type) {
	case ET_NONE:
		return rz_str_dup("NONE (None)");
	case ET_REL:
		return rz_str_dup("REL (Relocatable file)");
	case ET_EXEC:
		return rz_str_dup("EXEC (Executable file)");
	case ET_DYN:
		return rz_str_dup("DYN (Shared object file)");
	case ET_CORE:
		return rz_str_dup("CORE (Core file)");
	}

	return NULL;
}

static char *get_cpu_mips(ELFOBJ *bin) {
	Elf_(Word) mips_arch = bin->ehdr.e_flags & EF_MIPS_ARCH;
	Elf_(Word) mips_ase = bin->ehdr.e_flags & EF_MIPS_ARCH_ASE;
	Elf_(Word) mips_mach = bin->ehdr.e_flags & EF_MIPS_MACH;
	bool has_mdmx = false;

	switch (mips_ase) {
	case EF_MIPS_ARCH_ASE_MDMX:
		has_mdmx = true;
		break;
	case EF_MIPS_ARCH_ASE_M16:
		return strdup("mips16");
	case EF_MIPS_ARCH_ASE_MICROMIPS:
		switch (mips_arch) {
		case EF_MIPS_ARCH_32R2:
			// r2 is included in r3
			return strdup("micro32r3");
		case EF_MIPS_ARCH_32R6:
			return strdup("micro32r6");
		default:
			break;
		}
		return strdup("micromips");
	}

	RzStrBuf sb;
	rz_strbuf_init(&sb);

	if (has_mdmx) {
		rz_strbuf_set(&sb, "mdmx ");
	}

	for (size_t i = 0; i < RZ_ARRAY_SIZE(gnu_mips_mach_translation_table); i++) {
		if (mips_mach == gnu_mips_mach_translation_table[i].arch) {
			rz_strbuf_append(&sb, gnu_mips_mach_translation_table[i].name);
			break;
		}
	}

	if (is_elf_class64(bin)) {
		for (size_t i = 0; i < RZ_ARRAY_SIZE(gnu_mips_arch_translation_table64); i++) {
			if (mips_arch == gnu_mips_arch_translation_table64[i].arch) {
				rz_strbuf_append(&sb, gnu_mips_arch_translation_table64[i].name);
				break;
			}
		}
	} else {
		for (size_t i = 0; i < RZ_ARRAY_SIZE(gnu_mips_arch_translation_table32); i++) {
			if (mips_arch == gnu_mips_arch_translation_table32[i].arch) {
				rz_strbuf_append(&sb, gnu_mips_arch_translation_table32[i].name);
				break;
			}
		}
	}

	return rz_strbuf_drain_nofree(&sb);
}

typedef struct {
	ut8 tag;
	ut64 value_int;
	char *value_str;
} arm_attribute_t;

static const ut8 *parse_arm_attribute(const ut8 *data, ut32 size, arm_attribute_t *attribute) {
	const ut8 *next_data = NULL;
	const char *error = NULL;
	ut32 n = 0;

	attribute->tag = 0;
	attribute->value_int = 0;
	attribute->value_str = NULL;

	ut64 tmp_attribute_tag = 0;
	const ut8 *attribute_value_data = rz_uleb128(data, size, &tmp_attribute_tag, &error);
	if (error) {
		RZ_LOG_WARN("ARM aeabi: cannot parse attribute tag: %s\n", error);
		RZ_FREE(error);
		return NULL;
	}
	ut32 attribute_tag_len = attribute_value_data - data;
	ut32 attribute_value_size = size - attribute_tag_len;

	if (!attribute_value_size) {
		RZ_LOG_WARN("ARM aeabi: missing data for attribute value\n");
		return NULL;
	}

	// For N >= 128, tag N has the same properties as tag N modulo 128.
	attribute->tag = tmp_attribute_tag % 128;

	switch (attribute->tag) {
	// target-related attributes
	case TAG_CPU_ARCH:
	case TAG_CPU_ARCH_PROFILE:
	case TAG_ARM_ISA_USE:
	case TAG_THUMB_ISA_USE:
	case TAG_FP_ARCH:
	case TAG_WMMX_ARCH:
	case TAG_ADVANCED_SIMD_ARCH:
	// procedure call-related attributes
	case TAG_PCS_CONFIG:
	case TAG_ABI_PCS_R9_USE:
	case TAG_ABI_PCS_RW_DATA:
	case TAG_ABI_PCS_RO_DATA:
	case TAG_ABI_PCS_GOT_USE:
	case TAG_ABI_PCS_WCHAR_T:
	case TAG_ABI_ENUM_SIZE:
	case TAG_ABI_ALIGN_NEEDED:
	case TAG_ABI_ALIGN_PRESERVED:
	case TAG_ABI_FP_ROUNDING:
	case TAG_ABI_FP_DENORMAL:
	case TAG_ABI_FP_EXCEPTIONS:
	case TAG_ABI_FP_USER_EXCEPTIONS:
	case TAG_ABI_FP_NUMBER_MODEL:
	case TAG_ABI_HARDFP_USE:
	case TAG_ABI_VFP_ARGS:
	case TAG_ABI_WMMX_ARGS:
	// optimization attributes
	case TAG_ABI_OPTIMIZATION_GOALS:
	case TAG_ABI_FP_OPTIMIZATION_GOALS:
		next_data = rz_uleb128(attribute_value_data, attribute_value_size, &attribute->value_int, &error);
		if (error) {
			RZ_LOG_WARN("ARM aeabi: cannot parse attribute value: %s\n", error);
			RZ_FREE(error);
			return NULL;
		}
		return next_data;
	// target-related attributes
	case TAG_CPU_NAME:
	case TAG_CPU_RAW_NAME:
	// generic compatibility tag
	case TAG_COMPATIBILITY:
		n = rz_str_nlen((const char *)attribute_value_data, attribute_value_size);
		if (n < attribute_value_size) {
			attribute->value_str = (char *)attribute_value_data;
			return attribute_value_data + n + 1; // including terminating null byte
		}
		RZ_LOG_WARN("ARM aeabi: cannot parse attribute value: missing terminating null byte\n");
		return NULL;
	}

	if (attribute->tag <= 32) {
		RZ_LOG_WARN("ARM aeabi: unknown private attribute tag: %d\n", attribute->tag);
		return NULL;
	}

	if (attribute->tag & 1) {
		n = rz_str_nlen((const char *)attribute_value_data, attribute_value_size);
		if (n < attribute_value_size) {
			attribute->value_str = (char *)attribute_value_data;
			return attribute_value_data + n + 1; // including terminating null byte
		}
		RZ_LOG_WARN("ARM aeabi: cannot parse attribute value: missing terminating null byte\n");
		return NULL;
	}
	next_data = rz_uleb128(attribute_value_data, attribute_value_size, &attribute->value_int, &error);
	if (error) {
		RZ_LOG_WARN("ARM aeabi: cannot parse attribute value: %s\n", error);
		RZ_FREE(error);
		return NULL;
	}
	return next_data;
}

static int get_modes_from_arm_aeabi_attributes(const ut8 *data, ut32 size) {
	int modes = 0;
	while (size) {
		arm_attribute_t attribute;
		const ut8 *next = parse_arm_attribute(data, size, &attribute);
		if (!next) {
			break;
		}

		if (attribute.tag == TAG_CPU_ARCH_PROFILE) {
			ut64 cpu_arch_profile = attribute.value_int;
			if (cpu_arch_profile == ARM_PROFILE_M) {
				modes |= MODE_MCLASS;
			}
		}

		if (attribute.tag == TAG_CPU_ARCH) {
			ut64 cpu_arch = attribute.value_int;
			if (cpu_arch >= ARM_VER_V8_A && cpu_arch <= ARM_VER_V8_1_M_MAINLINE) {
				modes |= MODE_V8;
			}
			if (cpu_arch >= ARM_VER_V9_A) {
				// This is technically not V8 anymore, but probably a better approximation than "not v8".
				modes |= MODE_V8;
			}
		}

		size -= next - data;
		data = next;
	}

	if (size > 0) {
		RZ_LOG_WARN("ARM aeabi: attributes not fully parsed (%d bytes left)\n", size);
	}

	return modes;
}

static int get_modes_from_arm_aeabi_vendor_data(const char *data, ut32 size, bool big_endian) {
	int modes = 0;

	while (size >= 5) {
		ut8 tag = rz_read_ble8(data);
		if (tag != ARM_TAG_FILE && tag != ARM_TAG_SECTION && tag != ARM_TAG_SYMBOL) {
			RZ_LOG_WARN("ARM aeabi: unknown sub-subsection tag=0x%02x\n", tag);
			break;
		}
		ut32 subsubsection_size = rz_read_ble32(data + 1, big_endian);
		if (subsubsection_size > size) {
			RZ_LOG_WARN("ARM aeabi: sub-subsection size does not fit in subsection\n");
			break;
		}

		if (tag == ARM_TAG_FILE && subsubsection_size > 5) {
			modes |= get_modes_from_arm_aeabi_attributes((ut8 *)data + 5, subsubsection_size - 5);
		}

		// Advance to the next entry.
		size -= subsubsection_size;
		data += subsubsection_size;
	}

	if (size > 0) {
		RZ_LOG_WARN("ARM aeabi: subsection not fully parsed (%d bytes left)\n", size);
	}

	return modes;
}

/**
 * \brief Get more precise CPU details from .ARM.attributes section.
 *
 * - https://github.com/ARM-software/abi-aa/blob/main/aaelf32/aaelf32.rst#536build-attributes
 * - https://github.com/ARM-software/abi-aa/blob/main/addenda32/addenda32.rst#32representing-build-attributes-in-elf-files
 */
static int get_modes_from_arm_attributes_section(ELFOBJ *bin) {
	int modes = 0;
	RzBinElfSection *section = Elf_(rz_bin_elf_get_section_with_name)(bin, ".ARM.attributes");
	if (!section || !section->is_valid || section->type != SHT_ARM_ATTRIBUTES || section->size < 1) {
		return modes;
	}

	char *buf = RZ_NEWS0(char, section->size + 1);
	if (!buf) {
		return modes;
	}
	if (rz_buf_read_at(bin->b, section->offset, (ut8 *)buf, section->size) != section->size) {
		free(buf);
		return modes;
	}
	const char *buf_end = buf + section->size + 1;

	// The current format-version is just 'A'.
	if (buf[0] != 'A') {
		free(buf);
		return modes;
	}

	bool big_endian = Elf_(rz_bin_elf_is_big_endian)(bin);

	char *subsection_ptr = buf + 1;
	while (buf_end > subsection_ptr + 4) {
		const ut32 subsection_size = rz_read_ble32(subsection_ptr, big_endian);
		if (subsection_size <= 4 || buf_end <= subsection_ptr + subsection_size) {
			RZ_LOG_WARN("ARM aeabi: subsection size\n");
			break;
		}
		ut32 i = 4;

		// We are only interested in public ("aeabi") attributes subsection
		const char aeabi[] = "aeabi";
		if (!rz_str_cmp(&subsection_ptr[i], aeabi, sizeof(aeabi))) {
			i += sizeof(aeabi);
			modes |= get_modes_from_arm_aeabi_vendor_data(&subsection_ptr[i], subsection_size - i, big_endian);
		}

		subsection_ptr += subsection_size;
	}

	free(buf);
	return modes;
}

/**
 * Because only CS_MODE_MCLASS and CS_MODE_V8 are currently used for disassembly, we only need to detect those.
 */
static char *get_cpu_arm(ELFOBJ *bin) {
	int modes = get_modes_from_arm_attributes_section(bin);

	// Render modes to string buffer
	RzStrBuf sb;
	rz_strbuf_init(&sb);
	if (modes & MODE_MCLASS) {
		rz_strbuf_append(&sb, "cortexm");
	}
	if (modes & MODE_V8) {
		if (!rz_strbuf_is_empty(&sb)) {
			rz_strbuf_append(&sb, " ");
		}
		rz_strbuf_append(&sb, "v8");
	}

	return rz_strbuf_drain_nofree(&sb);
}

static char *get_abi_mips(ELFOBJ *bin) {
	Elf_(Word) mips_eflags = bin->ehdr.e_flags;
	Elf_(Word) mips_abi = mips_eflags & EF_MIPS_ABI;

	RzStrBuf sb;
	rz_strbuf_init(&sb);

	if (mips_eflags & EF_MIPS_NOREORDER) {
		rz_strbuf_append(&sb, "noreorder ");
	}

	if (mips_eflags & EF_MIPS_PIC) {
		rz_strbuf_append(&sb, "pic ");
	}

	if (mips_eflags & EF_MIPS_CPIC) {
		rz_strbuf_append(&sb, "cpic ");
	}

	if (mips_eflags & EF_MIPS_XGOT) {
		rz_strbuf_append(&sb, "xgot ");
	}

	if (mips_eflags & EF_MIPS_UCODE) {
		rz_strbuf_append(&sb, "ucode ");
	}

	if (mips_eflags & EF_MIPS_ABI2) {
		rz_strbuf_append(&sb, "abi2 ");
	}

	if (mips_eflags & EF_MIPS_FP64) {
		rz_strbuf_append(&sb, "fp64 ");
	}

	if (mips_eflags & EF_MIPS_NAN2008) {
		rz_strbuf_append(&sb, "nan2008 ");
	}

	if (mips_abi == EF_MIPS_ABI_O32) {
		rz_strbuf_append(&sb, "o32 ");
	}

	if (mips_abi == EF_MIPS_ABI_O64) {
		rz_strbuf_append(&sb, "o64 ");
	}

	if (mips_abi == EF_MIPS_ABI_EABI32) {
		rz_strbuf_append(&sb, "eabi32 ");
	}

	if (mips_abi == EF_MIPS_ABI_EABI64) {
		rz_strbuf_append(&sb, "eabi64 ");
	}

	if (is_elf_class64(bin)) {
		rz_strbuf_append(&sb, "n64");
	} else if (is_elf_class32(bin)) {
		rz_strbuf_append(&sb, "n32");
	}

	if (rz_strbuf_is_empty(&sb)) {
		return rz_str_dup("Unknown ABI");
	}

	return rz_strbuf_drain_nofree(&sb);
}

static char *get_cpu_hppa(ELFOBJ *bin) {
	Elf_(Word) hppa_arch = bin->ehdr.e_flags & EF_PARISC_ARCH;

	for (size_t i = 0; i < RZ_ARRAY_SIZE(cpu_hppa_translation_table); i++) {
		if (hppa_arch == cpu_hppa_translation_table[i].arch) {
			return strdup(cpu_hppa_translation_table[i].name);
		}
	}

	return strdup("Unknown HP PARISC ISA");
}

static char *get_cpu_h8xx(ELFOBJ *bin) {
	if (bin->ehdr.e_machine == EM_H8_300H) {
		return rz_str_dup("h8300h");
	} else if (bin->ehdr.e_machine == EM_H8S) {
		return rz_str_dup("h8300s");
	} else if (bin->ehdr.e_machine == EM_H8_500) {
		return rz_str_dup("h8500");
	}

	/// Reference:
	/// https://gem5.googlesource.com/arm/linux/+/b24413180f5600bcb3bb70fbed5cf186b60864bd/arch/h8300/include/asm/elf.h#29
	if (bin->ehdr.e_flags == 0x810000) {
		return rz_str_dup("h8300h");
	} else if (bin->ehdr.e_flags == 0x820000) {
		// H8S
		return rz_str_dup("h8300s");
	} else if (bin->ehdr.e_flags == 0x830000) {
		// cannot find this anywhere but is not H8300
		return rz_str_dup("h8300h");
	}
	return rz_str_dup("h8300");
}

static char *get_cpu_riscv(ELFOBJ *bin) {
	char bin_arch[256] = { 0 };
	size_t len = 0;
	riscv_attr_type typ = get_riscv_attribute_from_section(get_riscv_attributes_section(bin), T_RISCV_arch, sizeof(bin_arch), (ut8 *)bin_arch, &len);
	len = RZ_MIN(len, sizeof(bin_arch));
	if (typ == RISCV_ATTR_NT_STRING) {
		return rz_str_ndup((const char *)bin_arch, len);
	}

	// some hardcoded fallback, the mafd + vector
	return strdup("rv64i2p0_c2p0_m2p0_a2p0_f2p0_d2p0_v2p0");
}

/**
 * \brief List all imported lib
 * \param elf binary
 * \return an allocated pvector of char*
 *
 * Use dynamic information (dt_needed) to generate a list of imported lib
 */
RZ_OWN RzPVector /*<char *>*/ *Elf_(rz_bin_elf_get_libs)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, NULL);

	if (!Elf_(rz_bin_elf_has_dt_dynamic)(bin) || !bin->dynstr) {
		return NULL;
	}

	RzVector *dt_needed = Elf_(rz_bin_elf_get_dt_needed)(bin);
	if (!dt_needed) {
		return NULL;
	}

	RzPVector *result = rz_pvector_new(free);
	if (!result) {
		return NULL;
	}

	ut64 *iter = NULL;
	rz_vector_foreach (dt_needed, iter) {
		char *tmp = Elf_(rz_bin_elf_strtab_get_dup)(bin->dynstr, *iter);
		if (!tmp) {
			rz_pvector_free(result);
			return NULL;
		}

		if (!rz_pvector_push(result, tmp)) {
			rz_pvector_free(result);
			return NULL;
		}
	}

	return result;
}

static bool get_verdaux_entry_aux(ELFOBJ *bin, ut64 offset, Elf_(Verdaux) * entry) {
	return Elf_(rz_bin_elf_read_word)(bin, &offset, &entry->vda_name) &&
		Elf_(rz_bin_elf_read_word)(bin, &offset, &entry->vda_next);
}

static bool get_verdaux_entry(ELFOBJ *bin, ut64 offset, Elf_(Verdaux) * entry) {
	if (!get_verdaux_entry_aux(bin, offset, entry)) {
		RZ_LOG_WARN("Failed to read verdaux entry at 0x%" PFMT64x ".\n", offset);
		return false;
	}

	return true;
}

static bool get_verdef_entry_aux(ELFOBJ *bin, ut64 offset, Elf_(Verdef) * entry) {
	return Elf_(rz_bin_elf_read_half)(bin, &offset, &entry->vd_version) &&
		Elf_(rz_bin_elf_read_half)(bin, &offset, &entry->vd_flags) &&
		Elf_(rz_bin_elf_read_half)(bin, &offset, &entry->vd_ndx) &&
		Elf_(rz_bin_elf_read_half)(bin, &offset, &entry->vd_cnt) &&
		Elf_(rz_bin_elf_read_word)(bin, &offset, &entry->vd_hash) &&
		Elf_(rz_bin_elf_read_word)(bin, &offset, &entry->vd_aux) &&
		Elf_(rz_bin_elf_read_word)(bin, &offset, &entry->vd_next);
}

static bool get_verdef_entry(ELFOBJ *bin, ut64 offset, Elf_(Verdef) * entry) {
	if (!get_verdef_entry_aux(bin, offset, entry)) {
		RZ_LOG_WARN("Failed to read verdef entry at 0x%" PFMT64x ".\n", offset);
		return false;
	}

	return true;
}

static bool get_vernaux_entry_aux(ELFOBJ *bin, ut64 offset, Elf_(Vernaux) * entry) {
	return Elf_(rz_bin_elf_read_word)(bin, &offset, &entry->vna_hash) &&
		Elf_(rz_bin_elf_read_half)(bin, &offset, &entry->vna_flags) &&
		Elf_(rz_bin_elf_read_half)(bin, &offset, &entry->vna_other) &&
		Elf_(rz_bin_elf_read_word)(bin, &offset, &entry->vna_name) &&
		Elf_(rz_bin_elf_read_word)(bin, &offset, &entry->vna_next);
}

static bool get_vernaux_entry(ELFOBJ *bin, ut64 offset, Elf_(Vernaux) * entry) {
	if (!get_vernaux_entry_aux(bin, offset, entry)) {
		RZ_LOG_WARN("Failed to read vernaux entry at 0x%" PFMT64x ".\n", offset);
		return false;
	}

	return true;
}
static bool get_verneed_entry_aux(ELFOBJ *bin, ut64 offset, Elf_(Verneed) * entry) {
	return Elf_(rz_bin_elf_read_half)(bin, &offset, &entry->vn_version) &&
		Elf_(rz_bin_elf_read_half)(bin, &offset, &entry->vn_cnt) &&
		Elf_(rz_bin_elf_read_word)(bin, &offset, &entry->vn_file) &&
		Elf_(rz_bin_elf_read_word)(bin, &offset, &entry->vn_aux) &&
		Elf_(rz_bin_elf_read_word)(bin, &offset, &entry->vn_next);
}

static bool get_verneed_entry(ELFOBJ *bin, ut64 offset, Elf_(Verneed) * entry) {
	if (!get_verneed_entry_aux(bin, offset, entry)) {
		RZ_LOG_WARN("Failed to read verneed entry at 0x%" PFMT64x ".\n", offset);
		return false;
	}

	return true;
}

static bool get_versym_entry_sdb_from_verneed(ELFOBJ *bin, Sdb *sdb, const char *key, Elf_(Versym) versym) {
	ut64 verneed_addr;
	ut64 verneed_num;

	if (!Elf_(rz_bin_elf_get_dt_info)(bin, DT_VERNEED, &verneed_addr) || !Elf_(rz_bin_elf_get_dt_info)(bin, DT_VERNEEDNUM, &verneed_num)) {
		return false;
	}

	ut64 verneed_offset = Elf_(rz_bin_elf_v2p(bin, verneed_addr));

	if (verneed_offset == UT64_MAX) {
		return false;
	}

	ut64 verneed_entry_offset = verneed_offset;

	for (size_t i = 0; i < verneed_num; i++) {
		Elf_(Verneed) verneed_entry;
		if (!get_verneed_entry(bin, verneed_entry_offset, &verneed_entry)) {
			return false;
		}

		ut64 vernaux_entry_offset = verneed_entry_offset + verneed_entry.vn_aux;

		for (size_t j = 0; j < verneed_entry.vn_cnt; j++) {
			Elf_(Vernaux) vernaux_entry;
			if (!get_vernaux_entry(bin, vernaux_entry_offset, &vernaux_entry)) {
				return false;
			}

			if (vernaux_entry.vna_other != versym) {

				if (!vernaux_entry.vna_next) {
					break;
				}

				vernaux_entry_offset += vernaux_entry.vna_next;
				continue;
			}

			const char *tmp = Elf_(rz_bin_elf_strtab_get)(bin->dynstr, vernaux_entry.vna_name);
			if (!tmp) {
				return false;
			}

			char *value = rz_str_newf("%u (%s)", versym & VERSYM_VERSION, tmp);
			if (!value) {
				return false;
			}

			if (!sdb_set_owned(sdb, key, value)) {
				return false;
			}

			return true;
		}

		if (!verneed_entry.vn_next) {
			break;
		}

		verneed_entry_offset += verneed_entry.vn_next;
	}

	return false;
}

static bool get_versym_entry_sdb_from_verdef(ELFOBJ *bin, Sdb *sdb, const char *key, Elf_(Versym) versym) {
	ut64 verdef_addr;
	ut64 verdef_num;

	if (!Elf_(rz_bin_elf_get_dt_info)(bin, DT_VERDEF, &verdef_addr) || !Elf_(rz_bin_elf_get_dt_info)(bin, DT_VERDEFNUM, &verdef_num)) {
		return false;
	}

	ut64 verdef_offset = Elf_(rz_bin_elf_v2p(bin, verdef_addr));

	if (verdef_offset == UT64_MAX) {
		return false;
	}

	ut64 verdef_entry_offset = verdef_offset;

	for (size_t i = 0; i < verdef_num; i++) {
		Elf_(Verdef) verdef_entry;
		if (!get_verdef_entry(bin, verdef_entry_offset, &verdef_entry)) {
			return false;
		}

		if (!verdef_entry.vd_cnt || verdef_entry.vd_ndx != (versym & VERSYM_VERSION)) {
			if (!verdef_entry.vd_next) {
				break;
			}
			verdef_entry_offset += verdef_entry.vd_next;
			continue;
		}

		ut64 verdaux_entry_offset = verdef_entry_offset + verdef_entry.vd_aux;

		Elf_(Verdaux) verdaux_entry;
		if (!get_verdaux_entry(bin, verdaux_entry_offset, &verdaux_entry)) {
			return false;
		}

		const char *tmp = Elf_(rz_bin_elf_strtab_get)(bin->dynstr, verdaux_entry.vda_name);
		if (!tmp) {
			return false;
		}

		char *value = rz_str_newf("%u (%s)", versym & VERSYM_VERSION, tmp);
		if (!value) {
			return false;
		}

		if (!sdb_set_owned(sdb, key, value)) {
			return false;
		}

		return true;
	}

	return false;
}

static Sdb *get_gnu_versym(ELFOBJ *bin) {
	ut64 versym_addr;

	if (!Elf_(rz_bin_elf_get_dt_info)(bin, DT_VERSYM, &versym_addr)) {
		return false;
	}

	ut64 versym_offset = Elf_(rz_bin_elf_v2p(bin, versym_addr));

	if (versym_offset == UT64_MAX) {
		return NULL;
	}

	ut64 number_of_symbols = Elf_(rz_bin_elf_get_number_of_dynamic_symbols)(bin);
	if (!number_of_symbols) {
		return NULL;
	}

	Sdb *sdb = sdb_new0();
	if (!sdb) {
		return NULL;
	}

	if (!sdb_num_set(sdb, "num_entries", number_of_symbols) ||
		!sdb_num_set(sdb, "addr", versym_addr) ||
		!sdb_num_set(sdb, "offset", versym_offset)) {
		sdb_free(sdb);
		return NULL;
	}

	ut64 versym_entry_offset = versym_offset;

	for (size_t i = 0; i < number_of_symbols; i++) {
		char key[32];
		if (rz_strf(key, "entry%zu", i) == NULL) {
			sdb_free(sdb);
			return NULL;
		}

		Elf_(Versym) versym_entry;
		if (!Elf_(rz_bin_elf_read_versym)(bin, &versym_entry_offset, &versym_entry)) {
			sdb_free(sdb);
			return NULL;
		}

		switch (versym_entry) {
		case VER_NDX_LOCAL:
			if (!sdb_set(sdb, key, "0 (*local*)")) {
				sdb_free(sdb);
				return NULL;
			}
			break;
		case VER_NDX_GLOBAL:
			if (!sdb_set(sdb, key, "1 (*global*)")) {
				sdb_free(sdb);
				return NULL;
			}
			break;
		default:
			if (get_versym_entry_sdb_from_verneed(bin, sdb, key, versym_entry)) {
				break;
			}

			if (!get_versym_entry_sdb_from_verdef(bin, sdb, key, versym_entry)) {
				sdb_free(sdb);
				return NULL;
			}
		}
	}

	return sdb;
}

static Sdb *get_vernaux_entry_sdb(ELFOBJ *bin, Elf_(Vernaux) vernaux_entry, size_t index) {
	const char *tmp = Elf_(rz_bin_elf_strtab_get)(bin->dynstr, vernaux_entry.vna_name);
	if (!tmp) {
		return NULL;
	}

	Sdb *sdb_vernaux = sdb_new0();
	if (!sdb_vernaux) {
		return NULL;
	}

	char *flags = get_ver_flags(vernaux_entry.vna_flags);
	if (!sdb_set_owned(sdb_vernaux, "flags", flags)) {
		sdb_free(sdb_vernaux);
		return NULL;
	}

	if (!sdb_num_set(sdb_vernaux, "idx", index) ||
		!sdb_num_set(sdb_vernaux, "version", vernaux_entry.vna_other) ||
		!sdb_set(sdb_vernaux, "name", tmp)) {
		sdb_free(sdb_vernaux);
		return NULL;
	}

	return sdb_vernaux;
}

static Sdb *get_verneed_entry_sdb_aux(ELFOBJ *bin, Elf_(Verneed) verneed_entry, size_t index) {
	const char *tmp = Elf_(rz_bin_elf_strtab_get)(bin->dynstr, verneed_entry.vn_file);
	if (!tmp) {
		return NULL;
	}

	Sdb *sdb_version = sdb_new0();
	if (!sdb_version) {
		return NULL;
	}

	if (!sdb_num_set(sdb_version, "cnt", verneed_entry.vn_cnt) ||
		!sdb_num_set(sdb_version, "idx", index) ||
		!sdb_num_set(sdb_version, "vn_version", verneed_entry.vn_version) ||
		!sdb_set(sdb_version, "file_name", tmp)) {
		sdb_free(sdb_version);
		return NULL;
	}

	return sdb_version;
}

static Sdb *get_verneed_entry_sdb(ELFOBJ *bin, Elf_(Verneed) verneed_entry, size_t offset) {
	Sdb *sdb_version = get_verneed_entry_sdb_aux(bin, verneed_entry, offset);
	if (!sdb_version) {
		return NULL;
	}

	ut64 vernaux_entry_offset = offset + verneed_entry.vn_aux;

	for (size_t i = 0; i < verneed_entry.vn_cnt; i++) {
		Elf_(Vernaux) vernaux_entry;
		if (!get_vernaux_entry(bin, vernaux_entry_offset, &vernaux_entry)) {
			sdb_free(sdb_version);
			return NULL;
		}

		Sdb *sdb_vernaux = get_vernaux_entry_sdb(bin, vernaux_entry, vernaux_entry_offset);
		if (!sdb_vernaux) {
			sdb_free(sdb_version);
			return NULL;
		}

		char key[32];
		if (!sdb_ns_set(sdb_version, rz_strf(key, "vernaux%zu", i), sdb_vernaux)) {
			sdb_free(sdb_vernaux);
			sdb_free(sdb_version);
			return NULL;
		}

		sdb_free(sdb_vernaux);

		if (!vernaux_entry.vna_next) {
			break;
		}

		vernaux_entry_offset += vernaux_entry.vna_next;
	}

	return sdb_version;
}

static Sdb *get_gnu_verneed(ELFOBJ *bin) {
	ut64 verneed_addr;
	ut64 verneed_num;

	if (!Elf_(rz_bin_elf_get_dt_info)(bin, DT_VERNEED, &verneed_addr) || !Elf_(rz_bin_elf_get_dt_info)(bin, DT_VERNEEDNUM, &verneed_num)) {
		return NULL;
	}

	ut64 verneed_offset = Elf_(rz_bin_elf_v2p(bin, verneed_addr));
	if (verneed_offset == UT64_MAX) {
		RZ_LOG_WARN("Failed to convert verneed virtual address to physical address.\n");
		return NULL;
	}

	Sdb *sdb = sdb_new0();
	if (!sdb) {
		return NULL;
	}

	if (!sdb_num_set(sdb, "num_entries", verneed_num) ||
		!sdb_num_set(sdb, "addr", verneed_addr) ||
		!sdb_num_set(sdb, "offset", verneed_offset)) {
		sdb_free(sdb);
		return NULL;
	}

	for (size_t i = 0; i < verneed_num; i++) {
		Elf_(Verneed) verneed_entry;
		if (!get_verneed_entry(bin, verneed_offset, &verneed_entry)) {
			sdb_free(sdb);
			return NULL;
		}

		Sdb *sdb_version = get_verneed_entry_sdb(bin, verneed_entry, verneed_offset);
		if (!sdb_version) {
			sdb_free(sdb);
			return NULL;
		}

		char key[32];
		if (!sdb_ns_set(sdb, rz_strf(key, "version%zu", i), sdb_version)) {
			sdb_free(sdb_version);
			sdb_free(sdb);
			return NULL;
		}

		sdb_free(sdb_version);

		if (!verneed_entry.vn_next) {
			break;
		}

		verneed_offset += verneed_entry.vn_next;
	}

	return sdb;
}

RZ_IPI RZ_OWN Sdb *Elf_(rz_bin_elf_get_symbols_info)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, false);

	if (!Elf_(rz_bin_elf_is_executable)(bin)) {
		return NULL;
	}

	Sdb *res = sdb_new0();
	if (!res) {
		return NULL;
	}

	Sdb *sdb = get_gnu_verneed(bin);
	if (sdb && !sdb_ns_set(res, "verneed", sdb)) {
		sdb_free(res);
		sdb_free(sdb);
		return NULL;
	}
	sdb_free(sdb);

	sdb = get_gnu_versym(bin);
	if (sdb && !sdb_ns_set(res, "versym", sdb)) {
		sdb_free(res);
		sdb_free(sdb);
		return NULL;
	}
	sdb_free(sdb);

	return res;
}

/**
 * \brief Get the compiler info from the .comment section
 * \param elf binary
 * \return a ptr to an allocated string
 *
 * ...
 */
RZ_OWN char *Elf_(rz_bin_elf_get_compiler)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, NULL);

	RzBinElfSection *section = Elf_(rz_bin_elf_get_section_with_name)(bin, ".comment");
	if (!section) {
		return NULL;
	}

	ut64 offset = section->offset;
	ut64 size = RZ_MIN(section->size, 128);
	if (size < 1) {
		return NULL;
	}

	char *result = malloc(size + 1);
	if (!result) {
		return NULL;
	}

	if (rz_buf_read_at(bin->b, offset, (ut8 *)result, size) < 1) {
		free(result);
		return NULL;
	}
	result[size] = 0;

	size_t result_len = strlen(result);
	char *end = result + result_len;

	if (result_len != size && end[1]) {
		end[0] = ' ';
	}

	rz_str_trim(result);
	char *res = rz_str_escape(result);

	free(result);
	return res;
}

/**
 * \brief Return a string representing the application binary interface
 * \param elf type
 * \return allocated string
 *
 * Only work on mips right now. Use the elf header to deduce the application
 * binary interface
 */
RZ_OWN char *Elf_(rz_bin_elf_get_abi)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, NULL);

	if (arch_is_mips(bin)) {
		return get_abi_mips(bin);
	}

	return NULL;
}

/**
 * \brief Get the elf binary architecture
 * \param elf binary
 * \return an allocated string
 *
 * With the elf header (e_machine) deduce the elf architecture
 */
RZ_OWN char *Elf_(rz_bin_elf_get_arch)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, NULL);

	for (size_t i = 0; i < RZ_ARRAY_SIZE(arch_translation_table); i++) {
		if (bin->ehdr.e_machine == arch_translation_table[i].arch) {
			return rz_str_dup(arch_translation_table[i].name);
		}
	}

	RZ_LOG_INFO("elf: unknown e_machine %u! defaulting to x86.\n", (ut32)bin->ehdr.e_machine);
	// fallback on x86
	return rz_str_dup("x86");
}

/**
 * \brief Return a string representing the cpu
 * \param elf type
 * \return allocated string
 *
 * Use the elf header to deduce the cpu
 */
RZ_OWN char *Elf_(rz_bin_elf_get_cpu)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, NULL);

	if (arch_is_nanomips(bin)) {
		// we return i7200 which includes nanomips (generic) & nms1.
		return rz_str_dup("i7200");
	} else if (arch_is_mips(bin)) {
		// check which is the correct known mips cpus.
		return get_cpu_mips(bin);
	} else if (arch_is_sparc(bin)) {
		// Capstone has only v8 and v9 for now.
		// So no finer distinction necessary.
		return bin->ehdr.e_machine == EM_SPARC ? strdup("v8") : strdup("v9");
	} else if (arch_is_parisc(bin)) {
		return get_cpu_hppa(bin);
	} else if (arch_is_arm(bin)) {
		return get_cpu_arm(bin);
	} else if (arch_is_h8xx(bin)) {
		return get_cpu_h8xx(bin);
	} else if (arch_is_riscv(bin)) {
		return get_cpu_riscv(bin);
	}
	return NULL;
}

/**
 * \brief Return a string representing the elf class
 * \param elf binary
 * \return allocated string
 *
 * Check the elf header (e_ident) to deduce the elf class
 */
RZ_OWN char *Elf_(rz_bin_elf_get_elf_class)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, NULL);

	for (size_t i = 0; i < RZ_ARRAY_SIZE(class_translation_table); i++) {
		if (bin->ehdr.e_ident[EI_CLASS] == class_translation_table[i].class) {
			return rz_str_dup(class_translation_table[i].name);
		}
	}

	return rz_str_newf("<unknown: %x>", bin->ehdr.e_ident[EI_CLASS]);
}

/**
 * \brief Return a string representing the file type
 * \param elf binary
 * \return allocated string
 *
 * Use the elf header (e_type) to deduce the file type
 */
RZ_OWN char *Elf_(rz_bin_elf_get_file_type)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, NULL);

	char *result = get_file_type_basic(bin);
	if (result) {
		return result;
	}

	if (file_type_is_processor_specific(bin)) {
		return rz_str_newf("Processor Specific: %x", bin->ehdr.e_type);
	}

	if (file_type_is_os_specific(bin)) {
		return rz_str_newf("OS Specific: %x", bin->ehdr.e_type);
	}

	return rz_str_newf("<unknown>: %x", bin->ehdr.e_type);
}

/**
 * \brief Return the head flag
 * \return allocated string
 *
 * ...
 */
RZ_OWN char *Elf_(rz_bin_elf_get_head_flag)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, NULL);

	char *head_flag = get_head_flag(bin);

	if (RZ_STR_ISEMPTY(head_flag)) {
		free(head_flag);
		return rz_str_dup("unknown_flag");
	}

	return head_flag;
}

/**
 * \brief Return a string representing the machine name
 * \param elf type
 * \return allocated string
 *
 * Use http://www.sco.com/developers/gabi/latest/ch4.eheader.html and the elf
 * header (e_machine)
 */
RZ_OWN char *Elf_(rz_bin_elf_get_machine_name)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, NULL);

	for (size_t i = 0; i < RZ_ARRAY_SIZE(machine_name_translation_table); i++) {
		if (bin->ehdr.e_machine == machine_name_translation_table[i].machine) {
			return rz_str_dup(machine_name_translation_table[i].name);
		}
	}

	return rz_str_newf("<unknown>: 0x%x", bin->ehdr.e_machine);
}

/**
 * \brief Return the os application binary interface name
 * \param elf binary
 * \return an allocated string
 *
 * Check the ehdr or the shdr to get the os name
 */
RZ_OWN char *Elf_(rz_bin_elf_get_osabi_name)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, NULL);

	char *name = get_osabi_name_from_ehdr(bin);
	if (name) {
		return name;
	}

	name = get_osabi_name_from_shdr(bin);
	if (name) {
		return name;
	}

	return rz_str_dup("linux");
}

/**
 * \brief Get the rpath
 * \param elf binary
 * \return allocated string
 *
 * Use DT_RPATH or DT_RUNPATH to return the string
 */
RZ_OWN char *Elf_(rz_bin_elf_get_rpath)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, NULL);

	if (!Elf_(rz_bin_elf_has_segments)(bin) || !bin->dynstr || !has_dt_rpath_entry(bin)) {
		return NULL;
	}

	Elf_(Xword) val = get_dt_rpath(bin);
	return Elf_(rz_bin_elf_strtab_get_dup)(bin->dynstr, val);
}

/**
 * \brief Get the program interpreter
 * \param elf binary
 * \return an allocated string
 *
 * Get the program interpreter from the phdr
 */
RZ_OWN char *Elf_(rz_bin_elf_get_intrp)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, NULL);

	if (!Elf_(rz_bin_elf_has_segments)(bin)) {
		return NULL;
	}

	RzBinElfSegment *segment = Elf_(rz_bin_elf_get_segment_with_type)(bin, PT_INTERP);
	if (!segment || !segment->is_valid) {
		return NULL;
	}

	return get_elf_intrp(bin, segment);
}

/**
 * \brief Check if the binary is stripped
 * \param elf binary
 * \param is_stripped ?
 * \return a ptr to a new allocated RzBinSymbol
 *
 * Use the shdr to check if the binary is stripped
 */
bool Elf_(rz_bin_elf_is_stripped)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, false);

	if (!Elf_(rz_bin_elf_has_sections)(bin)) {
		return true;
	}

	RzBinElfSection *section;
	rz_bin_elf_foreach_sections(bin, section) {
		if (section->type == SHT_SYMTAB) {
			return false;
		}
	}

	return true;
}

/**
 * \brief Check if the stack is not executable
 * \param elf binary
 * \return true, false
 *
 * Check p_flags from the segment PT_GNU_STACK
 */
bool Elf_(rz_bin_elf_has_nx)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, false);

	if (!Elf_(rz_bin_elf_has_segments)(bin)) {
		return false;
	}

	RzBinElfSegment *segment = Elf_(rz_bin_elf_get_segment_with_type)(bin, PT_GNU_STACK);
	if (!segment || !segment->is_valid) {
		return false;
	}

	return !(segment->data.p_flags & PF_X);
}

/**
 * \brief Check if the elf use virtual address
 * \param elf binary
 * \return always true
 *
 * Return always true
 */
bool Elf_(rz_bin_elf_has_va)(ELFOBJ *bin) {
	return true;
}

/**
 * \brief Check if the elf binary is executable
 * \param elf binary
 * \return is_executable ?
 *
 * Use the elf header entry e_type to deduct if the elf is executable.
 */
bool Elf_(rz_bin_elf_is_executable)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, false);

	const Elf_(Half) type = bin->ehdr.e_type;
	return type == ET_EXEC || type == ET_DYN;
}

/**
 * \brief Check if the elf binary is relocatable
 * \param elf binary
 * \return is_relocatable ?
 *
 * Use the elf header entry e_type to deduct if the elf is relocatable.
 */
bool Elf_(rz_bin_elf_is_relocatable)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, false);
	return bin->ehdr.e_type == ET_REL;
}

/**
 * \brief Check if the binary is statically-linked library
 * \param elf binary
 * \return is_static ?
 *
 * Check the presence of PT_INTERP or PT_DYNAMIC in the program header
 */
bool Elf_(rz_bin_elf_is_static)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, false);

	RzBinElfSegment *segment = Elf_(rz_bin_elf_get_segment_with_type)(bin, PT_INTERP);
	if (segment && segment->is_valid) {
		return false;
	}

	segment = Elf_(rz_bin_elf_get_segment_with_type)(bin, PT_DYNAMIC);
	if (segment && segment->is_valid) {
		return false;
	}

	return true;
}

/**
 * \brief Return the elf bits
 * \param elf binary
 * \return the number of bits
 *
 * ...
 */
int Elf_(rz_bin_elf_get_bits)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, 0);

	if (arch_is_arcompact(bin) ||
		arch_is_h8xx(bin) ||
		arch_is(bin, EM_MSP430)) {
		return 16;
	}

	/* Hack for Ps2 */
	if (arch_is_mips(bin)) {
		return get_bits_mips(bin);
	}

	/* Hack for Thumb */
	if (Elf_(rz_bin_elf_is_arm_binary_supporting_thumb)(bin)) {
		if (!Elf_(rz_bin_elf_is_static)(bin) && has_thumb_symbol(bin)) {
			return 16;
		}

		ut64 entry = Elf_(rz_bin_elf_get_entry_offset)(bin);
		if (Elf_(rz_bin_elf_is_thumb_addr)(entry)) {
			return 16;
		}
	}

	return get_bits_common(bin);
}

/**
 * \brief Analyse if the elf binary has relro or partial relro
 * \param elf binary
 * \return RZ_BIN_ELF_NO_RELRO, RZ_BIN_ELF_PART_RELRO or RZ_BIN_ELF_FULL_RELRO
 *
 * Check if the elf has bind now enable and with PT_GNU_RELRO can deduct the
 * relro type
 */
int Elf_(rz_bin_elf_has_relro)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, RZ_BIN_ELF_NO_RELRO);

	bool is_bind_now = elf_is_bind_now(bin);
	bool has_gnu_relro = elf_has_gnu_relro(bin);

	if (has_gnu_relro) {
		if (is_bind_now) {
			return RZ_BIN_ELF_FULL_RELRO;
		}

		return RZ_BIN_ELF_PART_RELRO;
	}

	return RZ_BIN_ELF_NO_RELRO;
}

/**
 * \brief Analyse if the elf binary was compiled with -Wl,-z,nobtcfi on OpenBSD
 */
bool Elf_(rz_bin_elf_has_nobtcfi)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, false);
	if (!Elf_(rz_bin_elf_has_segments)(bin)) {
		return false;
	}
	RzBinElfSegment *segment = Elf_(rz_bin_elf_get_segment_with_type)(bin, PT_OPENBSD_NOBTCFI);
	return segment && segment->is_valid;
}

/**
 * \brief Check the binary endianness
 * \param elf type
 * \return is_big_endian ?
 *
 * Use the elf header (e_ident[EI_DATA]) to check the binary endianness
 */
bool Elf_(rz_bin_elf_is_big_endian)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, false);

	return bin->ehdr.e_ident[EI_DATA] == ELFDATA2MSB;
}

/**
 * \brief Compute the base address of the binary
 * \param elf binary
 * \return the base address
 *
 * To compute the base address, one determines the memory
 * address associated with the lowest p_vaddr value for a
 * PT_LOAD segment.
 */
ut64 Elf_(rz_bin_elf_get_baddr)(RZ_NONNULL ELFOBJ *bin, RZ_NULLABLE RzBinObjectLoadOptions *opts) {
	rz_return_val_if_fail(bin, 0);
	if (opts && opts->force_elf_to_use_baddr) {
		return opts->baseaddr;
	}

	if (Elf_(rz_bin_elf_is_relocatable)(bin)) {
		return RZ_BIN_ELF_DEFAULT_BADDR_RELOC;
	}

	if (Elf_(rz_bin_elf_has_segments)(bin)) {
		return compute_baddr_from_phdr(bin);
	}

	return 0;
}

/**
 * \brief Compute the base offset of the binary
 * \param elf binary
 * \return the base offset
 *
 * To compute the base address, one determines the memory
 * address associated with the lowest p_offset value for a
 * PT_LOAD segment.
 */
ut64 Elf_(rz_bin_elf_get_boffset)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, 0);

	if (Elf_(rz_bin_elf_has_segments)(bin)) {
		return compute_boffset_from_phdr(bin);
	}

	return 0;
}

/**
 * \brief Get the entry offset
 * \param elf binary
 * \return the entry offset
 *
 * Get the entry offset from the elf header (e_entry), and if the information
 * isn't defined section header will be used.
 */
ut64 Elf_(rz_bin_elf_get_entry_offset)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, UT64_MAX);

	if (!Elf_(rz_bin_elf_is_executable)(bin)) {
		return UT64_MAX;
	}

	ut64 entry = bin->ehdr.e_entry;
	if (entry) {
		ut64 tmp = Elf_(rz_bin_elf_v2p)(bin, entry);
		if (tmp == UT64_MAX) {
			return entry;
		}

		return tmp;
	}

	return get_entry_offset_from_shdr(bin);
}

/**
 * \brief Compute the fini offset of the binary
 * \param elf binary
 * \return the init offset
 *
 * Get the offset from the vaddr store in the dynamic section (dt_fini)
 */
ut64 Elf_(rz_bin_elf_get_fini_offset)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, 0);

	ut64 addr;

	if (!Elf_(rz_bin_elf_has_dt_dynamic)(bin)) {
		return 0;
	}

	if (!Elf_(rz_bin_elf_get_dt_info)(bin, DT_FINI, &addr)) {
		return 0;
	}

	return Elf_(rz_bin_elf_v2p)(bin, addr);
}

/**
 * \brief Compute the init offset of the binary
 * \param elf binary
 * \return the init offset
 *
 * Get the offset from the vaddr store in the dynamic section (dt_init)
 */
ut64 Elf_(rz_bin_elf_get_init_offset)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, 0);

	ut64 addr;

	if (!Elf_(rz_bin_elf_has_dt_dynamic)(bin)) {
		return 0;
	}

	if (!Elf_(rz_bin_elf_get_dt_info)(bin, DT_INIT, &addr)) {
		return 0;
	}

	return Elf_(rz_bin_elf_v2p)(bin, addr);
}

/**
 * \brief Compute the main offset of the binary
 * \param elf binary
 * \return the main offset
 *
 * ...
 */
ut64 Elf_(rz_bin_elf_get_main_offset)(RZ_NONNULL ELFOBJ *bin) {
	rz_return_val_if_fail(bin, UT64_MAX);

	ut8 buf[256] = { 0 };
	ut64 entry = Elf_(rz_bin_elf_get_entry_offset)(bin);
	ut64 main_addr;

	if (entry == UT64_MAX || entry > bin->size || (entry + sizeof(buf)) > bin->size) {
		return UT64_MAX;
	}

	if (rz_buf_read_at(bin->b, entry, buf, sizeof(buf)) < 0) {
		return UT64_MAX;
	}

	main_addr = get_main_offset_arm64(bin, entry, buf);
	if (main_addr != UT64_MAX) {
		return main_addr;
	}

	main_addr = get_main_offset_arm_glibc(bin, entry, buf);
	if (main_addr != UT64_MAX) {
		return main_addr;
	}

	main_addr = get_main_offset_mips(bin, entry, buf, RZ_ARRAY_SIZE(buf));
	if (main_addr != UT64_MAX) {
		return main_addr;
	}

	main_addr = get_main_offset_x86_gcc(bin, entry, buf);
	if (main_addr != UT64_MAX) {
		return main_addr;
	}

	main_addr = get_main_offset_x86_pie(bin, entry, buf);
	if (main_addr != UT64_MAX) {
		return main_addr;
	}

	main_addr = get_main_offset_x86_non_pie(bin, entry, buf);
	if (main_addr != UT64_MAX) {
		return main_addr;
	}

	main_addr = get_main_offset_linux_64_pie(bin, entry, buf);
	if (main_addr != UT64_MAX) {
		return main_addr;
	}

	main_addr = get_main_offset_v850(bin, entry, buf);
	if (main_addr != UT64_MAX) {
		return main_addr;
	}

	return get_main_offset_from_symbol(bin);
}
