elfwalk
37f62881
 #!/usr/bin/perl -w
1de5d814
 
37f62881
 # A simple toy script displaying the contents of an ELF file.
1de5d814
 
21da16a8
 # (c) 2015-2019 - Xavier Guerrin
1de5d814
 # This program is free software. It comes without any warranty, to
 # the extent permitted by applicable law. You can redistribute it
 # and/or modify it under the terms of the Do What The Fuck You Want
 # To Public License, Version 2, as published by Sam Hocevar. See
15c5097c
 # http://www.wtfpl.net/ for more details.
1de5d814
 
70b47eec
 # About formats implemented:
78092f81
 
 # System V Application Binary Interface:
 #   http://www.sco.com/developers/gabi/latest/contents.html
 # System V Application Binary Interface, Intel386 Architecture Processor Supplement:
 #   http://refspecs.linuxfoundation.org/elf/abi386-4.pdf
 # System V Application Binary Interface, AMD64 Architecture Processor Supplement:
 #    http://refspecs.linux-foundation.org/elf/x86_64-abi-0.98.pdf
 # Linux Standard Base Core Specification, Generic Part:
 #    http://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/tocobjformat.html
 # Linux Standard Base Core Specification for X86-64:
 #    http://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-AMD64/LSB-Core-AMD64/tocobjformat.html
a575282a
 # Oracle Solaris OS: Linker and Libraries Guide
 #    http://docs.oracle.com/cd/E18752_01/pdf/817-1984.pdf
0bf25930
 # GNU Hash ELF Sections by Ali Bahrami
 #    https://blogs.oracle.com/ali/entry/gnu_hash_elf_sections
70b47eec
 
37f62881
 # About the naming of functions: this script uses camel case for functions.
 # I wish to quote a friend of mine about that:
695abf35
 #   < gradator> c'est d'la merde le CamelCase
 # That's it.
 
37f62881
 use strict;
 use Fcntl;
eec3a56f
 use Term::ANSIColor qw(coloralias colored);
37f62881
 use Data::Dumper;
 
 # Constants
 use constant {
 	UNSIGNED => 0,
 	SIGNED => 1,
 	LITTLE_ENDIAN => 0,
 	BIG_ENDIAN => 1,
08c0845e
 	ELFCLASS32 => 1,
 	ELFCLASS64 => 2,
019600ce
 	R_FIELD_NONE => 0,
 	R_FIELD_WORD8 => 8,
 	R_FIELD_WORD16 => 16,
 	R_FIELD_WORD32 => 32,
 	R_FIELD_WORD64 => 64,
08c0845e
 };
 
 # ELF data types
 # Notes:
 #   - alignment was not included as it always equals size.
 #   - see http://perldoc.perl.org/constant.html#CAVEATS if you wonder about the
 #     parenthesis after ELFCLASS constants.
 our $elf_data_types = {
955a4c5f
 	# Figure 4-2: Data Types
08c0845e
 	ELFCLASS32() => {
 		'Addr'   => { 'size' => 4, 'formatter' => '0x%08X',  'signed' => UNSIGNED },
 		'Half'   => { 'size' => 2, 'formatter' => '0x%04X',  'signed' => UNSIGNED },
 		'Off'    => { 'size' => 4, 'formatter' => '0x%08X',  'signed' => UNSIGNED },
 		'Sword'  => { 'size' => 4, 'formatter' => '0x%08X',  'signed' =>   SIGNED },
 		'Word'   => { 'size' => 4, 'formatter' => '0x%08X',  'signed' => UNSIGNED },
 		'char'   => { 'size' => 1, 'formatter' => '%d',      'signed' => UNSIGNED },
 	},
 	ELFCLASS64() => {
 		'Addr'   => { 'size' => 8, 'formatter' => '0x%016X', 'signed' => UNSIGNED },
 		'Off'    => { 'size' => 8, 'formatter' => '0x%016X', 'signed' => UNSIGNED },
 		'Half'   => { 'size' => 2, 'formatter' => '0x%04X',  'signed' => UNSIGNED },
 		'Word'   => { 'size' => 4, 'formatter' => '0x%08X',  'signed' => UNSIGNED },
 		'Sword'  => { 'size' => 4, 'formatter' => '0x%08X',  'signed' =>   SIGNED },
 		'Xword'  => { 'size' => 8, 'formatter' => '0x%016X', 'signed' => UNSIGNED },
 		'Sxword' => { 'size' => 8, 'formatter' => '0x%016X', 'signed' =>   SIGNED },
 		'char'   => { 'size' => 1, 'formatter' => '%d',      'signed' => UNSIGNED },
 	},
37f62881
 };
 
9d121479
 our $elf_structures = {
af1da668
 	'e_ident' => {
 		'ordered_entries' => [
 			'EI_CLASS',
 			'EI_DATA',
 			'EI_VERSION',
 			'EI_OSABI',
 			'EI_ABIVERSION',
 		],
 		'EI_CLASS' => 'char',
 		'EI_DATA' => 'char',
 		'EI_VERSION' => 'char',
 		'EI_OSABI' => 'char',
 		'EI_ABIVERSION' => 'char',
 	},
955a4c5f
 	# Figure 4-3: ELF Header
e4ec039d
 	'file_header' => {
 		'ordered_entries' => [
 			'e_type',
 			'e_machine',
 			'e_version',
 			'e_entry',
 			'e_phoff',
 			'e_shoff',
 			'e_flags',
 			'e_ehsize',
 			'e_phentsize',
 			'e_phnum',
 			'e_shentsize',
 			'e_shnum',
 			'e_shstrndx',
 		],
 		'e_type' => 'Half',
 		'e_machine' => 'Half',
 		'e_version' => 'Word',
 		'e_entry' => 'Addr',
 		'e_phoff' => 'Off',
 		'e_shoff' => 'Off',
 		'e_flags' => 'Word',
 		'e_ehsize' => 'Half',
 		'e_phentsize' => 'Half',
 		'e_phnum' => 'Half',
 		'e_shentsize' => 'Half',
 		'e_shnum' => 'Half',
 		'e_shstrndx' => 'Half',
 	},
9d121479
 	# Figure 3. ELF-64 Section Header
 	'section_header' => {
af86a558
 		ELFCLASS32() => {
 			'ordered_entries' => [
 				'sh_name',
 				'sh_type',
 				'sh_flags',
 				'sh_addr',
 				'sh_offset',
 				'sh_size',
 				'sh_link',
 				'sh_info',
 				'sh_addralign',
 				'sh_entsize',
 			],
 			'sh_name' => 'Word',
 			'sh_type' => 'Word',
 			'sh_flags' => 'Word',
 			'sh_addr' => 'Addr',
 			'sh_offset' => 'Off',
 			'sh_link' => 'Word',
 			'sh_size' => 'Word',
 			'sh_info' => 'Word',
 			'sh_addralign' => 'Word',
 			'sh_entsize' => 'Word',
 		},
 		ELFCLASS64() => {
 			'ordered_entries' => [
 				'sh_name',
 				'sh_type',
 				'sh_flags',
 				'sh_addr',
 				'sh_offset',
 				'sh_size',
 				'sh_link',
 				'sh_info',
 				'sh_addralign',
 				'sh_entsize',
 			],
 			'sh_name' => 'Word',
 			'sh_type' => 'Word',
 			'sh_flags' => 'Xword',
 			'sh_addr' => 'Addr',
 			'sh_offset' => 'Off',
 			'sh_size' => 'Xword',
 			'sh_link' => 'Word',
 			'sh_info' => 'Word',
 			'sh_addralign' => 'Xword',
 			'sh_entsize' => 'Xword',
 		},
9d121479
 	},
aa9c0a60
 	'symbol_table' => {
 		ELFCLASS32() => {
 			'ordered_entries' => [
 				'st_name',
 				'st_value',
 				'st_size',
 				'st_info',
 				'st_other',
 				'st_shndx',
 			],
 			'st_name' => 'Word',
 			'st_value' => 'Addr',
 			'st_size' => 'Word',
 			'st_info' => 'char',
 			'st_other' => 'char',
 			'st_shndx' => 'Half',
 		},
 		ELFCLASS64() => {
 			'ordered_entries' => [
 				'st_name',
 				'st_info',
 				'st_other',
 				'st_shndx',
 				'st_value',
 				'st_size',
 			],
 			'st_name' => 'Word',
 			'st_info' => 'char',
 			'st_other' => 'char',
 			'st_shndx' => 'Half',
 			'st_value' => 'Addr',
 			'st_size' => 'Xword',
 		},
 	},
019600ce
 	'rel' => {
 		ELFCLASS32() => {
 			'ordered_entries' => [
 				'r_offset',
 				'r_info',
 			],
 			'r_offset' => 'Addr',
 			'r_info' => 'Word',
 		},
 		ELFCLASS64() => {
 			'ordered_entries' => [
 				'r_offset',
 				'r_info',
 			],
 			'r_offset' => 'Addr',
 			'r_info' => 'Xword',
 		},
 	},
 	'rela' => {
 		ELFCLASS32() => {
 			'ordered_entries' => [
 				'r_offset',
 				'r_info',
 				'r_addend',
 			],
 			'r_offset' => 'Addr',
 			'r_info' => 'Word',
 			'r_addend' => 'Sword',
 		},
 		ELFCLASS64() => {
 			'ordered_entries' => [
 				'r_offset',
 				'r_info',
 				'r_addend',
 			],
 			'r_offset' => 'Addr',
 			'r_info' => 'Xword',
 			'r_addend' => 'Sxword',
 		},
 	},
a575282a
 	'dynamic' => {
 		ELFCLASS32() => {
 			'ordered_entries' => [
 				'd_tag',
 				'd_un',
 			],
 			'd_tag' => 'Sword',
 			'd_un' => 'Word',
 		},
 		ELFCLASS64() => {
 			'ordered_entries' => [
 				'd_tag',
 				'd_un',
 			],
 			'd_tag' => 'Sxword',
 			'd_un' => 'Xword',
 		},
 	},
8531d4fa
 	'note' => {
 		ELFCLASS32() => {
 			'ordered_entries' => [
 				'namesz',
 				'descsz',
 				'type'
 			],
 			'namesz' => 'Word',
 			'descsz' => 'Word',
 			'type' => 'Word',
 		},
 		ELFCLASS64() => {
 			'ordered_entries' => [
 				'namesz',
 				'descsz',
 				'type'
 			],
 			# The SysV ABI states those should be Xword but it appears reality
 			# chose a different path.
 			'namesz' => 'Word',
 			'descsz' => 'Word',
 			'type' => 'Word',
 		},
 	},
9afc3f79
 	'program_header' => {
 		ELFCLASS32() => {
 			'ordered_entries' => [
 				'p_type',
 				'p_offset',
 				'p_vaddr',
 				'p_paddr',
 				'p_filesz',
 				'p_memsz',
 				'p_flags',
 				'p_align',
 			],
 			'p_type' => 'Word',
 			'p_offset' => 'Off',
 			'p_vaddr' => 'Addr',
 			'p_paddr' => 'Addr',
 			'p_filesz' => 'Word',
 			'p_memsz' => 'Word',
 			'p_flags' => 'Word',
 			'p_align' => 'Word',
 		},
 		ELFCLASS64() => {
 			'ordered_entries' => [
 				'p_type',
 				'p_flags',
 				'p_offset',
 				'p_vaddr',
 				'p_paddr',
 				'p_filesz',
 				'p_memsz',
 				'p_align',
 			],
 			'p_type' => 'Word',
 			'p_flags' => 'Word',
 			'p_offset' => 'Off',
 			'p_vaddr' => 'Addr',
 			'p_paddr' => 'Addr',
 			'p_filesz' => 'Xword',
 			'p_memsz' => 'Xword',
 			'p_align' => 'Xword',
 		},
 	},
ec5beb4b
 	'verdef' => {
 		'ordered_entries' => [
 			'vd_version',
 			'vd_flags',
 			'vd_ndx',
 			'vd_cnt',
 			'vd_hash',
 			'vd_aux',
 			'vd_next',
 		],
 		'vd_version' => 'Half',
 		'vd_flags' => 'Half',
 		'vd_ndx' => 'Half',
 		'vd_cnt' => 'Half',
 		'vd_hash' => 'Word',
 		'vd_aux' => 'Word',
 		'vd_next' => 'Word',
 	},
 	'verdaux' => {
 		'ordered_entries' => [
 			'vda_name',
 			'vda_next',
 		],
 		'vda_name' => 'Word',
 		'vda_next' => 'Word',
 	},
00756b8f
 	'verneed' => {
 		'ordered_entries' => [
 			'vn_version',
 			'vn_cnt',
 			'vn_file',
 			'vn_aux',
 			'vn_next',
 		],
 		'vn_version' => 'Half',
 		'vn_cnt' => 'Half',
 		'vn_file' => 'Word',
 		'vn_aux' => 'Word',
 		'vn_next' => 'Word',
 	},
 	'vernaux' => {
 		'ordered_entries' => [
 			'vna_hash',
 			'vna_flags',
 			'vna_other',
 			'vna_name',
 			'vna_next',
 		],
 		'vna_hash' => 'Word',
 		'vna_flags' => 'Half',
 		'vna_other' => 'Half',
 		'vna_name' => 'Word',
 		'vna_next' => 'Word',
 	},
0bf25930
 	'gnu_hash' => {
 		'ordered_entries' => [
 			'nbuckets',
 			'symndx',
 			'maskwords',
 			'shift2',
 		],
 		'nbuckets' => 'Word',
 		'symndx' => 'Word',
 		'maskwords' => 'Word',
 		'shift2' => 'Word',
 	},
9d121479
 };
 
0bb3b9f9
 our $elf = {
af1da668
 	'e_ident' => {
 		'_constants' => {
955a4c5f
 			# Figure 4-4: e_ident[] Identification Indexes
af1da668
 			'EI_MAG0' => 0,
 			'EI_MAG1' => 1,
 			'EI_MAG2' => 2,
 			'EI_MAG3' => 3,
 			'EI_CLASS' => 4,
 			'EI_DATA' => 5,
 			'EI_VERSION' => 6,
 			'EI_OSABI' => 7,
 			'EI_ABIVERSION' => 8,
 			'EI_PAD' => 9,
 			'EI_NIDENT' => 16,
 		},
 		'_magic' => {
 			'EI_MAG0' => 0x7f,
 			'EI_MAG1' => 0x45, # 'E'
 			'EI_MAG2' => 0x4C, # 'L'
 			'EI_MAG3' => 0x46, # 'F'
 		},
 	},
 	'EI_CLASS' => {
 		'unexplained_value' => 'fatal',
 		'name' => {
955a4c5f
 			0 => 'ELFCLASSNONE',
af1da668
 			1 => 'ELFCLASS32',
 			2 => 'ELFCLASS64',
 		},
 		'meaning' => {
955a4c5f
 			0 => 'invalid class',
af1da668
 			1 => '32-bit objects',
 			2 => '64-bit objects',
 		},
 	},
 	'EI_DATA' => {
 		'unexplained_value' => 'fatal',
 		'name' => {
955a4c5f
 			0 => 'ELFDATANONE',
af1da668
 			1 => 'ELFDATA2LSB',
 			2 => 'ELFDATA2MSB',
 		},
 		'meaning' => {
955a4c5f
 			0 => 'invalid data encoding',
af1da668
 			1 => 'object file data structure are little-endian',
 			2 => 'object file data structure are big-endian',
 		},
 	},
 	'EI_VERSION' => {
 		'unexplained_value' => 'i.e. not EV_CURRENT, ignoring',
 		'name' => { 1 => 'EV_CURRENT' },
 		'meaning' => { 1 => 'this is the expected version of the object file format' },
 	},
 	'EI_OSABI' => {
 		'unexplained_value' => 'fatal',
 		'name' => {
955a4c5f
 			0   => 'ELFOSABI_NONE',
af1da668
 			1   => 'ELFOSABI_HPUX',
955a4c5f
 			2   => 'ELFOSABI_NETBSD',
 			3   => 'ELFOSABI_GNU',
 			6   => 'ELFOSABI_SOLARIS',
 			7   => 'ELFOSABI_AIX',
 			8   => 'ELFOSABI_IRIX',
 			9   => 'ELFOSABI_FREEBSD',
 			10  => 'ELFOSABI_TRU64',
 			11  => 'ELFOSABI_MODESTO',
 			12  => 'ELFOSABI_OPENBSD',
 			13  => 'ELFOSABI_OPENVMS',
 			14  => 'ELFOSABI_NSK',
 			15  => 'ELFOSABI_AROS',
 			16  => 'ELFOSABI_FENIXOS',
 			17  => 'ELFOSABI_CLOUDABI',
 			18  => 'ELFOSABI_OPENVOS',
af1da668
 		},
 		'meaning' => {
 			0   => 'System V ABI',
955a4c5f
 			1   => 'Hewlett-Packard HP-UX',
 			2   => 'NetBSD',
 			3   => 'GNU',
 			6   => 'Sun Solaris',
 			7   => 'AIX',
 			8   => 'IRIX',
 			9   => 'FreeBSD',
 			10  => 'Compaq TRU64 UNIX',
 			11  => 'Novell Modesto',
 			12  => 'Open BSD',
 			13  => 'Open VMS',
 			14  => 'Hewlett-Packard Non-Stop Kernel',
 			15  => 'Amiga Research OS',
 			16  => 'The FenixOS highly scalable multi-core OS',
 			17  => 'Nuxi CloudABI',
 			18  => 'Stratus Technologies OpenVOS',
 		},
 		'ranges' => {
 			'ARCH' => {
 				'meaning' => 'architecture-specific',
 				'low_value' => 64,
 				'low_name' => '64',
 				'high_value' => 255,
 				'high_name' => '255',
 			},
af1da668
 		},
 	},
 	'EI_ABIVERSION' => {
 		'single_meaning' => 'version of the ABI for which the object is prepared'
 	},
e4ec039d
 	'e_type' => {
 		'unexplained_value' => 'fatal',
 		'name' => {
 			0 => 'ET_NONE',
 			1 => 'ET_REL',
 			2 => 'ET_EXEC',
 			3 => 'ET_DYN',
 			4 => 'ET_CORE',
 		},
 		'meaning' => {
 			 0 => 'no file type',
 			 1 => 'relocatable object file',
 			 2 => 'executable file',
 			 3 => 'shared object file',
 			 4 => 'core file',
 		},
 		'ranges' => {
 			'OS' => {
 				'meaning' => 'environment-specific use',
 				'low_value' => 0xFE00,
 				'low_name' => 'ET_LOOS',
 				'high_value' => 0xFEFF,
 				'high_name' => 'ET_HIOS',
 			},
 			'PROC' => {
 				'meaning' => 'processor-specific use',
 				'low_value' => 0xFF00,
 				'low_name' => 'ET_LOPROC',
 				'high_value' => 0xFFFF,
 				'high_name' => 'ET_HIPROC',
 			},
 		},
 	},
 	'e_machine' => {
955a4c5f
 		'name' => {
 			0 => 'EM_NONE',
 			1 => 'EM_M32',
 			2 => 'EM_SPARC',
 			3 => 'EM_386',
 			4 => 'EM_68K',
 			5 => 'EM_88K',
 			6 => 'EM_IAMCU',
 			7 => 'EM_860',
 			8 => 'EM_MIPS',
 			9 => 'EM_S370',
 			10 => 'EM_MIPS_RS3_LE',
 			11 => 'reserved',
 			12 => 'reserved',
 			13 => 'reserved',
 			14 => 'reserved',
 			15 => 'EM_PARISC',
 			16 => 'reserved',
 			17 => 'EM_VPP500',
 			18 => 'EM_SPARC32PLUS',
 			19 => 'EM_960',
 			20 => 'EM_PPC',
 			21 => 'EM_PPC64',
 			22 => 'EM_S390',
 			23 => 'EM_SPU',
 			24 => 'reserved',
 			25 => 'reserved',
 			26 => 'reserved',
 			27 => 'reserved',
 			28 => 'reserved',
 			29 => 'reserved',
 			30 => 'reserved',
 			31 => 'reserved',
 			32 => 'reserved',
 			33 => 'reserved',
 			34 => 'reserved',
 			35 => 'reserved',
 			36 => 'EM_V800',
 			37 => 'EM_FR20',
 			38 => 'EM_RH32',
 			39 => 'EM_RCE',
 			40 => 'EM_ARM',
 			41 => 'EM_ALPHA',
 			42 => 'EM_SH',
 			43 => 'EM_SPARCV9',
 			44 => 'EM_TRICORE',
 			45 => 'EM_ARC',
 			46 => 'EM_H8_300',
 			47 => 'EM_H8_300H',
 			48 => 'EM_H8S',
 			49 => 'EM_H8_500',
 			50 => 'EM_IA_64',
 			51 => 'EM_MIPS_X',
 			52 => 'EM_COLDFIRE',
 			53 => 'EM_68HC12',
 			54 => 'EM_MMA',
 			55 => 'EM_PCP',
 			56 => 'EM_NCPU',
 			57 => 'EM_NDR1',
 			58 => 'EM_STARCORE',
 			59 => 'EM_ME16',
 			60 => 'EM_ST100',
 			61 => 'EM_TINYJ',
 			62 => 'EM_X86_64',
 			63 => 'EM_PDSP',
 			64 => 'EM_PDP10',
 			65 => 'EM_PDP11',
 			66 => 'EM_FX66',
 			67 => 'EM_ST9PLUS',
 			68 => 'EM_ST7',
 			69 => 'EM_68HC16',
 			70 => 'EM_68HC11',
 			71 => 'EM_68HC08',
 			72 => 'EM_68HC05',
 			73 => 'EM_SVX',
 			74 => 'EM_ST19',
 			75 => 'EM_VAX',
 			76 => 'EM_CRIS',
 			77 => 'EM_JAVELIN',
 			78 => 'EM_FIREPATH',
 			79 => 'EM_ZSP',
 			80 => 'EM_MMIX',
 			81 => 'EM_HUANY',
 			82 => 'EM_PRISM',
 			83 => 'EM_AVR',
 			84 => 'EM_FR30',
 			85 => 'EM_D10V',
 			86 => 'EM_D30V',
 			87 => 'EM_V850',
 			88 => 'EM_M32R',
 			89 => 'EM_MN10300',
 			90 => 'EM_MN10200',
 			91 => 'EM_PJ',
 			92 => 'EM_OPENRISC',
 			93 => 'EM_ARC_COMPACT',
 			94 => 'EM_XTENSA',
 			95 => 'EM_VIDEOCORE',
 			96 => 'EM_TMM_GPP',
 			97 => 'EM_NS32K',
 			98 => 'EM_TPC',
 			99 => 'EM_SNP1K',
 			100 => 'EM_ST200',
 			101 => 'EM_IP2K',
 			102 => 'EM_MAX',
 			103 => 'EM_CR',
 			104 => 'EM_F2MC16',
 			105 => 'EM_MSP430',
 			106 => 'EM_BLACKFIN',
 			107 => 'EM_SE_C33',
 			108 => 'EM_SEP',
 			109 => 'EM_ARCA',
 			110 => 'EM_UNICORE',
 			111 => 'EM_EXCESS',
 			112 => 'EM_DXP',
 			113 => 'EM_ALTERA_NIOS2',
 			114 => 'EM_CRX',
 			115 => 'EM_XGATE',
 			116 => 'EM_C166',
 			117 => 'EM_M16C',
 			118 => 'EM_DSPIC30F',
 			119 => 'EM_CE',
 			120 => 'EM_M32C',
 			121 => 'reserved',
 			122 => 'reserved',
 			123 => 'reserved',
 			124 => 'reserved',
 			125 => 'reserved',
 			126 => 'reserved',
 			127 => 'reserved',
 			128 => 'reserved',
 			129 => 'reserved',
 			130 => 'reserved',
 			131 => 'EM_TSK3000',
 			132 => 'EM_RS08',
 			133 => 'EM_SHARC',
 			134 => 'EM_ECOG2',
 			135 => 'EM_SCORE7',
 			136 => 'EM_DSP24',
 			137 => 'EM_VIDEOCORE3',
 			138 => 'EM_LATTICEMICO32',
 			139 => 'EM_SE_C17',
 			140 => 'EM_TI_C6000',
 			141 => 'EM_TI_C2000',
 			142 => 'EM_TI_C5500',
 			143 => 'EM_TI_ARP32',
 			144 => 'EM_TI_PRU',
 			145 => 'reserved',
 			146 => 'reserved',
 			147 => 'reserved',
 			148 => 'reserved',
 			149 => 'reserved',
 			150 => 'reserved',
 			151 => 'reserved',
 			152 => 'reserved',
 			153 => 'reserved',
 			154 => 'reserved',
 			155 => 'reserved',
 			156 => 'reserved',
 			157 => 'reserved',
 			158 => 'reserved',
 			159 => 'reserved',
 			160 => 'EM_MMDSP_PLUS',
 			161 => 'EM_CYPRESS_M8C',
 			162 => 'EM_R32C',
 			163 => 'EM_TRIMEDIA',
 			164 => 'EM_QDSP6',
 			165 => 'EM_8051',
 			166 => 'EM_STXP7X',
 			167 => 'EM_NDS32',
 			168 => 'EM_ECOG1',
 			168 => 'EM_ECOG1X',
 			169 => 'EM_MAXQ30',
 			170 => 'EM_XIMO16',
 			171 => 'EM_MANIK',
 			172 => 'EM_CRAYNV2',
 			173 => 'EM_RX',
 			174 => 'EM_METAG',
 			175 => 'EM_MCST_ELBRUS',
 			176 => 'EM_ECOG16',
 			177 => 'EM_CR16',
 			178 => 'EM_ETPU',
 			179 => 'EM_SLE9X',
 			180 => 'EM_L10M',
 			181 => 'EM_K10M',
 			182 => 'reserved',
 			183 => 'EM_AARCH64',
 			184 => 'reserved',
 			185 => 'EM_AVR32',
 			186 => 'EM_STM8',
 			187 => 'EM_TILE64',
 			188 => 'EM_TILEPRO',
 			189 => 'EM_MICROBLAZE',
 			190 => 'EM_CUDA',
 			191 => 'EM_TILEGX',
 			192 => 'EM_CLOUDSHIELD',
 			193 => 'EM_COREA_1ST',
 			194 => 'EM_COREA_2ND',
 			195 => 'EM_ARC_COMPACT2',
 			196 => 'EM_OPEN8',
 			197 => 'EM_RL78',
 			198 => 'EM_VIDEOCORE5',
 			199 => 'EM_78KOR',
 			200 => 'EM_56800EX',
 			201 => 'EM_BA1',
 			202 => 'EM_BA2',
 			203 => 'EM_XCORE',
 			204 => 'EM_MCHP_PIC',
 			205 => 'EM_INTEL205',
 			206 => 'EM_INTEL206',
 			207 => 'EM_INTEL207',
 			208 => 'EM_INTEL208',
 			209 => 'EM_INTEL209',
 			210 => 'EM_KM32',
 			211 => 'EM_KMX32',
 			212 => 'EM_KMX16',
 			213 => 'EM_KMX8',
 			214 => 'EM_KVARC',
 			215 => 'EM_CDP',
 			216 => 'EM_COGE',
 			217 => 'EM_COOL',
 			218 => 'EM_NORC',
 			219 => 'EM_CSR_KALIMBA',
 			220 => 'EM_Z80',
 			221 => 'EM_VISIUM',
 			222 => 'EM_FT32',
 			223 => 'EM_MOXIE',
 			224 => 'EM_AMDGPU',
 		},
 		'meaning' => {
 			0 => 'No machine',
 			1 => 'AT&T WE 32100',
 			2 => 'SPARC',
 			3 => 'Intel 80386',
 			4 => 'Motorola 68000',
 			5 => 'Motorola 88000',
 			6 => 'Intel MCU',
 			7 => 'Intel 80860',
 			8 => 'MIPS I Architecture',
 			9 => 'IBM System/370 Processor',
 			10 => 'MIPS RS3000 Little-endian',
 			11 => 'Reserved for future use',
 			12 => 'Reserved for future use',
 			13 => 'Reserved for future use',
 			14 => 'Reserved for future use',
 			15 => 'Hewlett-Packard PA-RISC',
 			16 => 'Reserved for future use',
 			17 => 'Fujitsu VPP500',
 			18 => 'Enhanced instruction set SPARC',
 			19 => 'Intel 80960',
 			20 => 'PowerPC',
 			21 => '64-bit PowerPC',
 			22 => 'IBM System/390 Processor',
 			23 => 'IBM SPU/SPC',
 			24 => 'Reserved for future use',
 			25 => 'Reserved for future use',
 			26 => 'Reserved for future use',
 			27 => 'Reserved for future use',
 			28 => 'Reserved for future use',
 			29 => 'Reserved for future use',
 			30 => 'Reserved for future use',
 			31 => 'Reserved for future use',
 			32 => 'Reserved for future use',
 			33 => 'Reserved for future use',
 			34 => 'Reserved for future use',
 			35 => 'Reserved for future use',
 			36 => 'NEC V800',
 			37 => 'Fujitsu FR20',
 			38 => 'TRW RH-32',
 			39 => 'Motorola RCE',
 			40 => 'ARM 32-bit architecture (AARCH32)',
 			41 => 'Digital Alpha',
 			42 => 'Hitachi SH',
 			43 => 'SPARC Version 9',
 			44 => 'Siemens TriCore embedded processor',
 			45 => 'Argonaut RISC Core, Argonaut Technologies Inc.',
 			46 => 'Hitachi H8/300',
 			47 => 'Hitachi H8/300H',
 			48 => 'Hitachi H8S',
 			49 => 'Hitachi H8/500',
 			50 => 'Intel IA-64 processor architecture',
 			51 => 'Stanford MIPS-X',
 			52 => 'Motorola ColdFire',
 			53 => 'Motorola M68HC12',
 			54 => 'Fujitsu MMA Multimedia Accelerator',
 			55 => 'Siemens PCP',
 			56 => 'Sony nCPU embedded RISC processor',
 			57 => 'Denso NDR1 microprocessor',
 			58 => 'Motorola Star*Core processor',
 			59 => 'Toyota ME16 processor',
 			60 => 'STMicroelectronics ST100 processor',
 			61 => 'Advanced Logic Corp. TinyJ embedded processor family',
 			62 => 'AMD x86-64 architecture',
 			63 => 'Sony DSP Processor',
 			64 => 'Digital Equipment Corp. PDP-10',
 			65 => 'Digital Equipment Corp. PDP-11',
 			66 => 'Siemens FX66 microcontroller',
 			67 => 'STMicroelectronics ST9+ 8/16 bit microcontroller',
 			68 => 'STMicroelectronics ST7 8-bit microcontroller',
 			69 => 'Motorola MC68HC16 Microcontroller',
 			70 => 'Motorola MC68HC11 Microcontroller',
 			71 => 'Motorola MC68HC08 Microcontroller',
 			72 => 'Motorola MC68HC05 Microcontroller',
 			73 => 'Silicon Graphics SVx',
 			74 => 'STMicroelectronics ST19 8-bit microcontroller',
 			75 => 'Digital VAX',
 			76 => 'Axis Communications 32-bit embedded processor',
 			77 => 'Infineon Technologies 32-bit embedded processor',
 			78 => 'Element 14 64-bit DSP Processor',
 			79 => 'LSI Logic 16-bit DSP Processor',
 			80 => 'Donald Knuth\'s educational 64-bit processor',
 			81 => 'Harvard University machine-independent object files',
 			82 => 'SiTera Prism',
 			83 => 'Atmel AVR 8-bit microcontroller',
 			84 => 'Fujitsu FR30',
 			85 => 'Mitsubishi D10V',
 			86 => 'Mitsubishi D30V',
 			87 => 'NEC v850',
 			88 => 'Mitsubishi M32R',
 			89 => 'Matsushita MN10300',
 			90 => 'Matsushita MN10200',
 			91 => 'picoJava',
 			92 => 'OpenRISC 32-bit embedded processor',
 			93 => 'ARC International ARCompact processor (old spelling/synonym: EM_ARC_A5)',
 			94 => 'Tensilica Xtensa Architecture',
 			95 => 'Alphamosaic VideoCore processor',
 			96 => 'Thompson Multimedia General Purpose Processor',
 			97 => 'National Semiconductor 32000 series',
 			98 => 'Tenor Network TPC processor',
 			99 => 'Trebia SNP 1000 processor',
 			100 => 'STMicroelectronics (www.st.com) ST200 microcontroller',
 			101 => 'Ubicom IP2xxx microcontroller family',
 			102 => 'MAX Processor',
 			103 => 'National Semiconductor CompactRISC microprocessor',
 			104 => 'Fujitsu F2MC16',
 			105 => 'Texas Instruments embedded microcontroller msp430',
 			106 => 'Analog Devices Blackfin (DSP) processor',
 			107 => 'S1C33 Family of Seiko Epson processors',
 			108 => 'Sharp embedded microprocessor',
 			109 => 'Arca RISC Microprocessor',
 			110 => 'Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University',
 			111 => 'eXcess: 16/32/64-bit configurable embedded CPU',
 			112 => 'Icera Semiconductor Inc. Deep Execution Processor',
 			113 => 'Altera Nios II soft-core processor',
 			114 => 'National Semiconductor CompactRISC CRX microprocessor',
 			115 => 'Motorola XGATE embedded processor',
 			116 => 'Infineon C16x/XC16x processor',
 			117 => 'Renesas M16C series microprocessors',
 			118 => 'Microchip Technology dsPIC30F Digital Signal Controller',
 			119 => 'Freescale Communication Engine RISC core',
 			120 => 'Renesas M32C series microprocessors',
 			121 => 'Reserved for future use',
 			122 => 'Reserved for future use',
 			123 => 'Reserved for future use',
 			124 => 'Reserved for future use',
 			125 => 'Reserved for future use',
 			126 => 'Reserved for future use',
 			127 => 'Reserved for future use',
 			128 => 'Reserved for future use',
 			129 => 'Reserved for future use',
 			130 => 'Reserved for future use',
 			131 => 'Altium TSK3000 core',
 			132 => 'Freescale RS08 embedded processor',
 			133 => 'Analog Devices SHARC family of 32-bit DSP processors',
 			134 => 'Cyan Technology eCOG2 microprocessor',
 			135 => 'Sunplus S+core7 RISC processor',
 			136 => 'New Japan Radio (NJR) 24-bit DSP Processor',
 			137 => 'Broadcom VideoCore III processor',
 			138 => 'RISC processor for Lattice FPGA architecture',
 			139 => 'Seiko Epson C17 family',
 			140 => 'The Texas Instruments TMS320C6000 DSP family',
 			141 => 'The Texas Instruments TMS320C2000 DSP family',
 			142 => 'The Texas Instruments TMS320C55x DSP family',
 			143 => 'Texas Instruments Application Specific RISC Processor, 32bit fetch',
 			144 => 'Texas Instruments Programmable Realtime Unit',
 			145 => 'Reserved for future use',
 			146 => 'Reserved for future use',
 			147 => 'Reserved for future use',
 			148 => 'Reserved for future use',
 			149 => 'Reserved for future use',
 			150 => 'Reserved for future use',
 			151 => 'Reserved for future use',
 			152 => 'Reserved for future use',
 			153 => 'Reserved for future use',
 			154 => 'Reserved for future use',
 			155 => 'Reserved for future use',
 			156 => 'Reserved for future use',
 			157 => 'Reserved for future use',
 			158 => 'Reserved for future use',
 			159 => 'Reserved for future use',
 			160 => 'STMicroelectronics 64bit VLIW Data Signal Processor',
 			161 => 'Cypress M8C microprocessor',
 			162 => 'Renesas R32C series microprocessors',
 			163 => 'NXP Semiconductors TriMedia architecture family',
 			164 => 'QUALCOMM DSP6 Processor',
 			165 => 'Intel 8051 and variants',
 			166 => 'STMicroelectronics STxP7x family of configurable and extensible RISC processors',
 			167 => 'Andes Technology compact code size embedded RISC processor family',
 			168 => 'Cyan Technology eCOG1X family',
 			168 => 'Cyan Technology eCOG1X family',
 			169 => 'Dallas Semiconductor MAXQ30 Core Micro-controllers',
 			170 => 'New Japan Radio (NJR) 16-bit DSP Processor',
 			171 => 'M2000 Reconfigurable RISC Microprocessor',
 			172 => 'Cray Inc. NV2 vector architecture',
 			173 => 'Renesas RX family',
 			174 => 'Imagination Technologies META processor architecture',
 			175 => 'MCST Elbrus general purpose hardware architecture',
 			176 => 'Cyan Technology eCOG16 family',
 			177 => 'National Semiconductor CompactRISC CR16 16-bit microprocessor',
 			178 => 'Freescale Extended Time Processing Unit',
 			179 => 'Infineon Technologies SLE9X core',
 			180 => 'Intel L10M',
 			181 => 'Intel K10M',
 			182 => 'Reserved for future Intel use',
 			183 => 'ARM 64-bit architecture (AARCH64)',
 			184 => 'Reserved for future ARM use',
 			185 => 'Atmel Corporation 32-bit microprocessor family',
 			186 => 'STMicroeletronics STM8 8-bit microcontroller',
 			187 => 'Tilera TILE64 multicore architecture family',
 			188 => 'Tilera TILEPro multicore architecture family',
 			189 => 'Xilinx MicroBlaze 32-bit RISC soft processor core',
 			190 => 'NVIDIA CUDA architecture',
 			191 => 'Tilera TILE-Gx multicore architecture family',
 			192 => 'CloudShield architecture family',
 			193 => 'KIPO-KAIST Core-A 1st generation processor family',
 			194 => 'KIPO-KAIST Core-A 2nd generation processor family',
 			195 => 'Synopsys ARCompact V2',
 			196 => 'Open8 8-bit RISC soft processor core',
 			197 => 'Renesas RL78 family',
 			198 => 'Broadcom VideoCore V processor',
 			199 => 'Renesas 78KOR family',
 			200 => 'Freescale 56800EX Digital Signal Controller (DSC)',
 			201 => 'Beyond BA1 CPU architecture',
 			202 => 'Beyond BA2 CPU architecture',
 			203 => 'XMOS xCORE processor family',
 			204 => 'Microchip 8-bit PIC(r) family',
 			205 => 'Reserved by Intel',
 			206 => 'Reserved by Intel',
 			207 => 'Reserved by Intel',
 			208 => 'Reserved by Intel',
 			209 => 'Reserved by Intel',
 			210 => 'KM211 KM32 32-bit processor',
 			211 => 'KM211 KMX32 32-bit processor',
 			212 => 'KM211 KMX16 16-bit processor',
 			213 => 'KM211 KMX8 8-bit processor',
 			214 => 'KM211 KVARC processor',
 			215 => 'Paneve CDP architecture family',
 			216 => 'Cognitive Smart Memory Processor',
 			217 => 'iCelero CoolEngine',
 			218 => 'Nanoradio Optimized RISC',
 			219 => 'CSR Kalimba architecture family',
 			220 => 'Zilog Z80',
 			221 => 'Controls and Data Services VISIUMcore processor',
 			222 => 'FTDI Chip FT32 high performance 32-bit RISC architecture',
 			223 => 'Moxie processor family',
 			224 => 'AMD GPU architecture',
 		},
e4ec039d
 	},
 	'e_version' => {
955a4c5f
 		'name' => {
 			0 => 'EV_NONE',
 			1 => 'EV_CURRENT',
 		},
 		'meaning' => {
 			0 => 'invalid version',
 			1 => 'original version of the object file format',
 		},
e4ec039d
 		'unexplained_value' => 'i.e. not EV_CURRENT, ignoring',
 	},
 	'e_entry' => {
 		'single_meaning' => 'contains the virtual address of the program entry point',
 	},
 	'e_phoff' => {
 		'single_meaning' => 'contains the file offset, in bytes, of the program header table',
 	},
 	'e_shoff' => {
 		'single_meaning' => 'contains the file offset, in bytes, of the section header table',
 	},
 	'e_flags' => {
 		'single_meaning' => 'contains processor-specific flags',
 	},
 	'e_ehsize' => {
 		'single_meaning' => 'contains the size, in bytes, of the ELF header',
 	},
 	'e_phentsize' => {
 		'single_meaning' => 'contains the size, in bytes, of a program header table entry',
 	},
 	'e_phnum' => {
 		'single_meaning' => 'contains the number of entries in the program header table',
 	},
 	'e_shentsize' => {
 		'single_meaning' => 'contains the size, in bytes, of a section header table entry',
 	},
 	'e_shnum' => {
 		'single_meaning' => 'contains the number of entries in the section header table',
 	},
 	'e_shstrndx' => {
 		'single_meaning' => 'this is, within the section header table, the index of the section containing the section names',
 	},
0bb3b9f9
 	'sh_name' => {
 		'single_meaning' => 'this offset is used to locate the section name',
 	},
955a4c5f
 	# Figure 4-7: Special section Indices
 	'section_indices' => {
 		'name' => {
 			0 => 'SHN_UNDEF',
 			0xFFF1 => 'SHN_ABS',
 			0xFFF2 => 'SHN_COMMON',
 			0xFFFF => 'SHN_XINDEX',
 		},
 		'meaning' => {
 			0 => 'undefined or meaningless section reference',
 			0xFFF1 => 'the corresponding reference is an absolute value',
 			0xFFF2 => 'a symbol that has been declared as a common block',
 			0xFFFF => 'an escape value meaning that the actual section header index is too large to fit in the containing field (specific to the structure where it appears)',
 		},
 		'ranges' => {
 			'PROC' => {
 				'meaning' => 'processor-specific semantics',
 				'low_value' => 0xFF00,
 				'low_name' => 'SHN_LOPROC',
 				'high_value' => 0xFF1F,
 				'high_name' => 'SHN_HIPROC',
 			},
 			'OS' => {
 				'meaning' => 'operating system-specific semantics',
 				'low_value' => 0xFF20,
 				'low_name' => 'SHN_LOOS',
 				'high_value' => 0xFF3F,
 				'high_name' => 'SHN_HIOS',
 			},
 		},
 	},
 	# Figure 4-9: Section Types, sh_type
0bb3b9f9
 	'sh_type' => {
 		'name' => {
 			0 => 'SHT_NULL',
 			1 => 'SHT_PROGBITS',
 			2 => 'SHT_SYMTAB',
 			3 => 'SHT_STRTAB',
 			4 => 'SHT_RELA',
 			5 => 'SHT_HASH',
 			6 => 'SHT_DYNAMIC',
 			7 => 'SHT_NOTE',
 			8 => 'SHT_NOBITS',
 			9 => 'SHT_REL',
 			10 => 'SHT_SHLIB',
 			11 => 'SHT_DYNSYM',
955a4c5f
 			14 => 'SHT_INIT_ARRAY',
 			15 => 'SHT_FINI_ARRAY',
 			16 => 'SHT_PREINIT_ARRAY',
 			17 => 'SHT_GROUP',
 			18 => 'SHT_SYMTAB_SHNDX',
9fd83599
 			# 0X6FFFFFF5 to 0X6FFFFFFC come from the GNU C Library's elf.h.
 			0X6FFFFFF5 => 'SHT_GNU_ATTRIBUTES',
 			0x6FFFFFF6 => 'SHT_GNU_HASH',
 			0X6FFFFFF7 => 'SHT_GNU_LIBLIST',
 			0X6FFFFFF8 => 'SHT_CHECKSUM',
 			0X6FFFFFFA => 'SHT_SUNW_move',
 			0X6FFFFFFB => 'SHT_SUNW_COMDAT',
 			0X6FFFFFFC => 'SHT_SUNW_syminfo',
 			# These are the additional section types defined by the LSB.
 			0X6FFFFFFD => 'SHT_GNU_verdef',
 			0X6FFFFFFE => 'SHT_GNU_verneed',
 			0X6FFFFFFF => 'SHT_GNU_versym',
0bb3b9f9
 		},
 		'meaning' => {
 			0 => 'marks an unused section header',
 			1 => 'contains information defined by the program',
 			2 => 'contains a linker symbol table',
 			3 => 'contains a string table',
 			4 => 'contains "Rela" type relocation entries',
 			5 => 'contains a symbol hash table',
 			6 => 'contains dynamic linking tables',
 			7 => 'contains note information',
 			8 => 'contains uninitialized space; does not occupy any space in the file',
 			9 => 'contains "Rel" type relocation entries',
 			10 => 'reserved',
 			11 => 'contains a dynamic loader symbol table',
955a4c5f
 			14 => 'contains an array of pointers to initialization functions',
 			15 => 'contains an array of pointers to termination functions',
 			16 => 'contains an array of pointers to functions that are invoked before all other initialization functions',
 			17 => 'defines a section group, i.e. a set of sections that are related and that must be treated specially by the linker',
 			18 => 'used with SHN_XINDEX',
9fd83599
 			0X6FFFFFF5 => 'Object attributes',
 			0x6FFFFFF6 => 'GNU-style hash table',
 			0X6FFFFFF7 => 'Prelink library list',
 			0X6FFFFFF8 => 'Checksum for DSO content',
 			0X6FFFFFFA => 'Sun-specific',
 			0X6FFFFFFB => 'Sun-specific',
 			0X6FFFFFFC => 'Sun-specific',
 			0X6FFFFFFD => 'contains the symbol versions that are provided',
 			0X6FFFFFFE => 'contains the symbol versions that are required',
 			0X6FFFFFFF => 'contains the Symbol Version Table',
0bb3b9f9
 		},
 		'ranges' => {
 			'OS' => {
 				'meaning' => 'environment-specific use',
 				'low_value' => 0x60000000,
 				'low_name' => 'SHT_LOOS',
 				'high_value' => 0x6FFFFFFF,
 				'high_name' => 'SHT_HIOS',
 			},
 			'PROC' => {
 				'meaning' => 'processor-specific use',
 				'low_value' => 0x70000000,
 				'low_name' => 'SHT_LOPROC',
 				'high_value' => 0x7FFFFFFF,
 				'high_name' => 'SHT_HIPROC',
 			},
79b9f547
 		},
0bb3b9f9
 	},
955a4c5f
 	# Figure 4-11. Section Attribute Flags
0bb3b9f9
 	'sh_flags' => {
 		'is_flag' => 1,
 		'name' => {
955a4c5f
 			0x001 => 'SHF_WRITE',
 			0x002 => 'SHF_ALLOC',
 			0x004 => 'SHF_EXECINSTR',
 			0x010 => 'SHF_MERGE',
 			0x020 => 'SHF_STRINGS',
 			0x040 => 'SHF_INFO_LINK',
 			0x080 => 'SHF_LINK_ORDER',
 			0x100 => 'SHF_OS_NONCONFORMING',
 			0x200 => 'SHF_GROUP',
 			0x400 => 'SHF_TLS',
 			0x800 => 'SHF_COMPRESSED',
 			0x0FF00000 => 'SHT_MASKOS',
0bb3b9f9
 			0xF0000000 => 'SHT_MASKPROC',
 		},
 		'meaning' => {
955a4c5f
 			0x001 => 'section contains writable data',
 			0x002 => 'section is allocated in memory image of program',
 			0x004 => 'section contains executable instructions',
 			0x010 => 'data in the section may be merged to eliminate duplication',
 			0x020 => 'data elements in the section consist of null-terminated character strings',
 			0x040 => 'the sh_info field of this section header holds a section header table index',
 			0x080 => 'add special ordering requirements for link editors',
 			0x100 => 'this section requires special OS-specific processing (beyond the standard linking rules) to avoid incorrect behavior',
 			0x200 => 'this section is a member (perhaps the only one) of a section group',
 			0x400 => 'this section holds Thread-Local Storage, meaning that each separate execution flow has its own distinct instance of this data',
 			0x800 => 'section contains compressed data',
 			0x0FF00000 => 'operating system-specific semantics',
 			0xF0000000 => 'processor-specific semantics',
0bb3b9f9
 		},
 	},
 	'sh_addr' => {
 		'single_meaning' => 'virtual address of the section in memory',
 	},
 	'sh_offset' => {
 		'single_meaning' => 'offset, in bytes, of the section contents in the file',
 	},
 	'sh_size' => {
 		'single_meaning' => 'size, in bytes, of the section',
 	},
 	'sh_link' => {
 		'single_meaning' => 'index of an associated section',
 		'_use' => {
955a4c5f
 			# Figure 4-14: sh_link and sh_info Interpretation
0bb3b9f9
 			6  => 'a string table used by entries in this section',
 			5  => 'a symbol table to which the hash table applies',
955a4c5f
 			9  => 'the associated symbol table',
 			4  => 'the associated symbol table',
 			2  => 'the associated string table',
 			11 => 'the associated string table',
 			17 => 'the associated symbol table',
 			18 => 'the associated symbol table section',
0bb3b9f9
 		}
 	},
 	'sh_info' => {
 		'single_meaning' => 'extra information about the section',
 		'_use' => {
955a4c5f
 			# Figure 4-14: sh_link and sh_info Interpretation
 			9  => 'the index of the section to which relocation applies',
 			4  => 'the index of the section to which relocation applies',
 			2  => '1 greater than the symbol table index of the last local symbol (binding STB_LOCAL)',
 			11 => '1 greater than the symbol table index of the last local symbol (binding STB_LOCAL)',
 			17 => 'the symbol table index of an entry in the associated symbol table',
0bb3b9f9
 		}
 	},
 	'sh_addralign' => {
745e7cc2
 		'single_meaning' => 'contains the required alignment of the section',
0bb3b9f9
 	},
 	'sh_entsize' => {
745e7cc2
 		'single_meaning' => 'size, in bytes, of each entry; only makes sense for sections that contain fixed-size entries',
0bb3b9f9
 	},
e15c0368
 	# Figure 4-16: Special Sections
 	'elf_special_sections' => {
 		'short' => {
 			'.bss' => 'uninitialized data',
 			'.comment' => 'version control information',
 			'.data'  => 'initialized data',
 			'.data1' => 'initialized data',
 			'.debug' => 'information for symbolic debugging',
 			'.dynamic' => 'dynamic linking information',
 			'.dynstr' => 'symbol names for dynamic linking',
 			'.dynsym' => 'symbol table for dynamic linking',
 			'.fini' => 'executable instructions for process termination',
 			'.fini_array' => 'array of functions for process termination',
 			'.got' => 'global offset table',
 			'.hash' => 'symbol hash table',
 			'.init' => 'executable instructions for process initialization',
 			'.init_array' => 'array of functions for process initialization',
 			'.interp' => 'path to program interpreter',
 			'.line' => 'line numbers for debugging',
 			'.note' => 'special information',
 			'.plt' => 'procedure linkage table',
 			'.preinit_array' => 'array of functions for process pre-initialization',
 			'.rel' => 'relocation information for section %s',
 			'.rodata'  => 'read-only data',
 			'.rodata1' => 'read-only data',
 			'.shstrtab' => 'section names',
 			'.strtab' => 'symbol names',
 			'.symtab' => 'symbol table',
 			'.symtab_shndx' => 'special symbol table section index array',
 			'.tbss' => 'uninitialized thread-local data',
 			'.tdata'  => 'initialized thread-local data',
 			'.text' => 'executable instructions',
 		},
 		'long' => {
 			'.bss' => 'Historically stands for "Block Started by Symbol". This section holds uninitialized data that contribute to the program\'s memory image. By definition, the system initializes the data with zeros when the pgrogram begins to run. The section occupies no file space, as indicated by the section type, SHT_NOBITS.',
 			'.comment' => 'This section holds version control information.',
 			'.data'  => 'This section holds initialized data that contribute to the program\'s memory image.',
 			'.data1' => 'This section holds initialized data that contribute to the program\'s memory image.',
 			'.debug' => 'This section holds information for symbolic debugging. The contents are unspecified. All section names with the prefix .debug are reserved for future use in the ABI.',
 			'.dynamic' => 'This section holds dynamic linking information. This section\'s attributes will include the SHF_ALLOC bit. Whether the SHF_WRITE bit is set is processor specific.',
 			'.dynstr' => 'This section holds strings needed for dynamic linking, most commonly the strings that represent the names associated with symbol table entries.',
 			'.dynsym' => 'This section holds the dynamic linking symbol table.',
 			'.fini' => 'This section holds executable instructions that contribute to the process termination code. That is, when a program exits normally, the system arranges to execute the code in this section.',
 			'.fini_array' => 'This section holds an array of function pointers that contributes to a single termination array for the executable or shared object containing the section.',
 			'.got' => 'This section holds the global offset table.',
 			'.hash' => 'This section holds a symbol hash table (following classic ELF).',
 			'.init' => 'This section holds executable instructions that contribute to the process initialization code. When a program starts to run, the system arranges to execute the code in this section before calling the main program entry point (called "main" for C programs).',
 			'.init_array' => 'This section an array of function pointers that contributes to a single initialization array for the executable or shared object containing the section.',
 			'.interp' => 'This section holds the path name of a program interpreter. If the file has a loadable segment that includes relocation, the sections\' attributes will include the SHF_ALLOC bit; otherwise, that bit will be off.',
 			'.line' => 'This section holds line number information for symbolic debugging, which describes the correspondence between the source program and the machine code. The contents are unspecified.',
 			'.note' => 'Tis section holds special information that other programs will check for conformance, compatibility, etc.',
 			'.plt' => 'This section holds the procedure linkage table.',
 			'.preinit_array' => 'This section holds an array of function pointers that contributes to a single pre-initialization array for the executable or shared object containing the section.',
 			'.rel' => 'This section holds relocation information that apply to section %s. If the file has a loadable segment that includes relocation, the sections\' attributes will include the SHF_ALLOC bit; otherwise, that bit will be off.',
 			'.rodata'  => 'This section holds read-only data that typically contribute to a non-writable segment in the process image.',
 			'.rodata1' => 'This section holds read-only data that typically contribute to a non-writable segment in the process image.',
 			'.shstrtab' => 'This section holds section names. Its index is referenced in the file header by e_shstrndx.',
 			'.strtab' => 'This section holds strings, most commonly the strings that represent the names associated with symbol table entries.',
 			'.symtab' => 'This section holds a symbol table. If the file has a loadable segment that includes the symbol table, the section\'s attributes will include the SHF_ALLOC bit; otherwise, that bit will be off.',
 			'.symtab_shndx' => 'This section holds a special symbol table section index array.',
 			'.tbss' => 'This section holds uninitialized thread-local data that contribute to the program\'s memory image. By definition, the system initializes the data with zeros when the data is instantiated for each new execution flow. The section occupies no file space, as indicated by the section type, SHT_NOBITS. Implementations need not support thread-local storage.',
 			'.tdata' => 'This section holds initialized thread-local data that contributes to the program\'s memory image. A copy of its contents is instantiated by the system for each new execution flow. Implementations need not support thread-local storage.',
 			'.text' => 'This section holds the "text", or executable instructions, of a program.',
 		}
 	},
 	# Additional Special Sections from LSB_5.0.0
 	'lsb5-core-generic_additional_special_sections' => {
 		'short' => {
 			'.ctors' => 'constructor function pointers',
 			'.data.rel.ro' => 'read-only relocatable initialized data',
 			'.dtors' => 'destructor function pointers',
 			'.eh_frame' => 'frame unwinding information',
 			'.eh_frame_hdr' => 'frame unwinding additional information',
30cd2f27
 			'.gcc_except_table' => 'language specific data',
e15c0368
 			'.gnu.version' => 'symbol version table',
 			'.gnu.version_d' => 'version definitions',
 			'.gnu.version_r' => 'version requirements',
 			'.got.plt' => 'read-only relocatable global offset table',
 			'.jcr' => 'Java class registration information',
 			'.note.ABI-tag' => 'ABI details',
30cd2f27
 			'.stab' => 'debugging information',
e15c0368
 			'.stabstr' => 'strings for .stab section',
 		},
 		'long' => {
 			'.ctors' => 'This section contains a list of global constructor function pointers.',
 			'.data.rel.ro' => 'This section holds initialized data that contribute to the program\'s memory image. This section may be made read-only after relocations have been applied.',
 			'.dtors' => 'This section contains a list of global destructor function pointers.',
 			'.eh_frame' => 'This section contains information necessary for frame unwinding during exception handling.',
 			'.eh_frame_hdr' => 'This section contains a pointer to the .eh_frame section which is accessible to the runtime support code of a C++ application. This section may also contain a binary search table which may be used by the runtime support code to more efficiently access records in the .eh_frame section.',
 			'.gcc_except_table' => 'This section holds Language Specific Data.',
 			'.gnu.version' => 'This section contains the Symbol Version Table.',
 			'.gnu.version_d' => 'This section contains the Version Definitions.',
 			'.gnu.version_r' => 'This section contains the Version Requirements.',
 			'.got.plt' => 'This section holds the read-only portion of the GLobal Offset Table. This section may be made read-only after relocations have been applied.',
 			'.jcr' => 'This section contains information necessary for registering compiled Java classes. The contents are compiler-specific and used by compiler initialization functions.',
 			'.note.ABI-tag' => 'Specify ABI details, e.g. the earliest compatible kernel version.',
 			'.stab' => 'This section contains debugging information.',
 			'.stabstr' => 'This section contains strings associated with the debugging infomation contained in the .stab section.',
 		}
 	},
 	'lsb5-core-amd64_additional_special_sections' => {
 		'short' => {
 			'.rela.dyn' => 'relocation information for all but .plt',
 			'.rela.plt' => 'relocation information for .plt',
 		},
 		'long' => {
 			'.rela.dyn' => 'This section holds RELA type relocation information for all sections of a shared library except the PLT.',
 			'.rela.plt' => 'This section holds RELA type relocation information for the PLT section of a shared library or dynamically linked application.',
 		}
 	},
 	'gnu_special_sections' => {
 		'short' => {
 			'.gnu.hash' => 'symbol hash table, GNU-style',
 			'.note.gnu.build-id' => 'unique bits identifying the file',
 		},
 		'long' => {
 			'.gnu.hash' => 'This section holds a symbol hash table (following GNU ELF).',
 			'.note.gnu.build-id' => 'According to the GNU linker (ld) man page, the contents of the note are unique bits identifying this linked file.',
 		}
 	},
aa9c0a60
 	'st_name' => {
 		'single_meaning' => 'this offset is used to locate the symbol name',
 	},
 	'st_value' => {
 		'single_meaning' => 'associated value (absolute value or address)',
 	},
 	'st_size' => {
 		'single_meaning' => 'associated size',
 	},
 	'st_info' => {
 		'single_meaning' => 'type and binding attributes (st_type and st_bind)',
 	},
 	'st_bind' => {
 		'name' => {
 			0 => 'STB_LOCAL',
 			1 => 'STB_GLOBAL',
 			2 => 'STB_WEAK',
 		},
 		'meaning' => {
 			0 => 'the symbol is not visible outside the object file',
 			1 => 'the symbol is visible to all object files being combined',
 			2 => 'the symbol is visible to all object files with lower precedence',
 		},
 		'ranges' => {
 			'OS' => {
 				'meaning' => 'operating system-specific semantics',
 				'low_value' => 10,
 				'low_name' => 'STB_LOOS',
 				'high_value' => 12,
 				'high_name' => 'STB_HIOS',
 			},
 			'PROC' => {
 				'meaning' => 'processor-specific semantics',
 				'low_value' => 13,
 				'low_name' => 'STB_LOPROC',
 				'high_value' => 15,
 				'high_name' => 'STB_HIPROC',
 			},
 		},
 	},
 	'st_type' => {
 		'name' => {
 			0 => 'STT_NOTYPE',
 			1 => 'STT_OBJECT',
 			2 => 'STT_FUNC',
 			3 => 'STT_SECTION',
 			4 => 'STT_FILE',
 			5 => 'STT_COMMON',
 			6 => 'STT_TLS',
 		},
 		'meaning' => {
 			0 => 'the symbol type is not specified',
 			1 => 'the symbol is associated with a data object, such as a variable, an array, etc.',
 			2 => 'the symbol is associated with a function or other executable code',
 			3 => 'the symbol is associated with a section',
 			4 => 'the symbol\'s name gives the name of the source file associated with the object file',
 			5 => 'the symbol labels an uninitialized common block',
 			6 => 'the symbol specifies a Thread-Local Storage entity',
 		},
 		'ranges' => {
 			'OS' => {
 				'meaning' => 'operating system-specific semantics',
 				'low_value' => 10,
 				'low_name' => 'STT_LOOS',
 				'high_value' => 12,
 				'high_name' => 'STT_HIOS',
 			},
 			'PROC' => {
 				'meaning' => 'processor-specific semantics',
 				'low_value' => 13,
 				'low_name' => 'STT_LOPROC',
 				'high_value' => 15,
 				'high_name' => 'STT_HIPROC',
 			},
 		},
 	},
 	'st_other' => {
 		'name' => {
 			0 => 'STV_DEFAULT',
 			1 => 'STV_INTERNAL',
 			2 => 'STV_HIDDEN',
 			3 => 'STV_PROTECTED',
 		},
 		'meaning' => {
 			0 => 'respect the symbol\'s binding type',
 			1 => 'visible in other components but not preemptable',
 			2 => 'not visible to other components',
 			3 => 'defined by processor supplements',
 		},
 	},
 	'st_shndx' => {
e772dbe0
 		'name' => {
 			0 => 'SHN_UNDEF',
 			0xFFF1 => 'SHN_ABS',
 			0xFFF2 => 'SHN_COMMON',
 			0xFFFF => 'SHN_XINDEX',
 		},
 		'meaning' => {
 			0 => colored('this symbol is to be linked from another file', 'bright_yellow'),
 			0xFFF1 => 'the symbol has an absolute value that will not change because of relocation',
 			0xFFF2 => 'the symbol labels a common block that has not yet been allocated',
30cd2f27
 			0xFFFF => 'an escape value which indicates that the symbol refers to a specific location within a section, but that the section header index for that section is too large to be represented directly in the symbol table entry',
e772dbe0
 		},
 		'unexplained_value' => 'this is the index of the related section',
aa9c0a60
 	},
a575282a
 	'd_tag' => {
 		'name' => {
 			0 => 'DT_NULL',
 			1 => 'DT_NEEDED',
 			2 => 'DT_PLTRELSZ',
 			3 => 'DT_PLTGOT',
 			4 => 'DT_HASH',
 			5 => 'DT_STRTAB',
 			6 => 'DT_SYMTAB',
 			7 => 'DT_RELA',
 			8 => 'DT_RELASZ',
 			9 => 'DT_RELAENT',
 			10 => 'DT_STRSZ',
 			11 => 'DT_SYMENT',
 			12 => 'DT_INIT',
 			13 => 'DT_FINI',
 			14 => 'DT_SONAME',
 			15 => 'DT_RPATH',
 			16 => 'DT_SYMBOLIC',
 			17 => 'DT_REL',
 			18 => 'DT_RELSZ',
 			19 => 'DT_RELENT',
 			20 => 'DT_PLTREL',
 			21 => 'DT_DEBUG',
 			22 => 'DT_TEXTREL',
 			23 => 'DT_JMPREL',
 			24 => 'DT_BIND_NOW',
 			25 => 'DT_INIT_ARRAY',
 			26 => 'DT_FINI_ARRAY',
 			27 => 'DT_INIT_ARRAYSZ',
 			28 => 'DT_FINI_ARRAYSZ',
 			29 => 'DT_RUNPATH',
 			30 => 'DT_FLAGS',
 			32 => 'DT_PREINIT_ARRAY',
 			33 => 'DT_PREINIT_ARRAYSZ',
 			34 => 'DT_SYMTAB_SHNDX',
 			0x6ffffef5 => 'DT_GNU_HASH',
 			0x6ffffef6 => 'DT_TLSDESC_PLT',
 			0x6ffffef7 => 'DT_TLSDESC_GOT',
 			0x6ffffef8 => 'DT_GNU_CONFLICT',
 			0x6ffffef9 => 'DT_GNU_LIBLIST',
 			0x6ffffefa => 'DT_CONFIG',
 			0x6ffffefb => 'DT_DEPAUDIT',
 			0x6ffffefc => 'DT_AUDIT',
 			0x6ffffefd => 'DT_PLTPAD',
 			0x6ffffefe => 'DT_MOVETAB',
 			0x6ffffeff => 'DT_SYMINFO',
 			0x6ffffff0 => 'DT_VERSYM',
 			0x6ffffff9 => 'DT_RELACOUNT',
 			0x6ffffffa => 'DT_RELCOUNT',
 			0x6ffffffb => 'DT_FLAGS_1',
 			0x6ffffffc => 'DT_VERDEF',
 			0x6ffffffd => 'DT_VERDEFNUM',
 			0x6ffffffe => 'DT_VERNEED',
 			0x6fffffff => 'DT_VERNEEDNUM',
 		},
 		'meaning' => {
 			0 => 'marks the end of the _DYNAMIC array',
 			1 => 'holds the string table offset of a null-terminated string, giving the name of a needed library',
 			2 => 'holds the total size, in bytes, of the relocation entries associated with the procedure linkage table. If an entry of type DT_JMPREL is present, a DT_PLTRELSZ must accompany it. ',
 			3 => 'holds an address associated with the procedure linkage table and/or the global offset table',
 			4 => 'holds the address of the symbol hash table; this hash table refers to the symbol table referenced by the DT_SYMTAB element',
 			5 => 'holds the address of the string table; symbol names, library names, and other strings reside in this table',
 			6 => 'holds the address of the symbol table',
 			7 => 'holds the address of a relocation table (with explicit addends)',
 			8 => ' holds the total size, in bytes, of the DT_RELA relocation table',
 			9 => 'holds the size, in bytes, of a DT_RELA relocation entry',
 			10 => 'holds the size, in bytes, of the string table',
 			11 => 'holds the size, in bytes, of a symbol table entry',
 			12 => 'holds the address of the initialization function',
 			13 => 'holds the address of the termination function',
 			14 => 'holds the string table offset of a null-terminated string, giving the name of the shared object; the offset is an index into the table recorded in the DT_STRTAB entry',
 			15 => 'holds the string table offset of a null-terminated string, which is actually a library search path; the offset is an index into the table recorded in the DT_STRTAB entry',
 			16 => 'alters the dynamic linker\'s symbol resolution algorithm for references within the library',
 			17 => 'holds the address of a relocation table (with implicit addends)',
 			18 => 'holds the total size, in bytes, of the DT_REL relocation table',
 			19 => 'holds the size, in bytes, of a DT_REL relocation entry',
 			20 => 'specifies the type of relocation entry to which the procedure linkage table refers',
 			21 => 'used for debugging',
 			22 => 'one or more relocation entries might request modifications to a non-writable segment',
 			23 => ' holds the address of relocation entries associated solely with the procedure linkage table',
 			24 => 'instructs the dynamic linker to process all relocations for the object containing this entry before transferring control to the program',
 			25 => 'holds the address of the array of pointers to initialization functions',
 			26 => ' holds the address of the array of pointers to termination functions',
 			27 => 'holds the size in bytes of the array of initialization functions pointed to by the DT_INIT_ARRAY entry',
 			28 => 'holds the size in bytes of the array of termination functions pointed to by the DT_FINI_ARRAY entry',
 			29 => 'holds the string table offset of a null-terminated string, which is actually a library search path; the offset is an index into the table recorded in the DT_STRTAB entry',
 			30 => 'holds flag values specific to the object being loaded',
 			32 => 'holds the address of the array of pointers to pre-initialization functions',
 			33 => 'holds the size in bytes of the array of pre-initialization functions pointed to by the DT_PREINIT_ARRAY entry',
30cd2f27
 			34 => 'holds the address of the SHT_SYMTAB_SHNDX section associated with the dynamic symbol table referenced by the DT_SYMTAB element',
a575282a
 			0x6ffffe00 => '',
 			0x6ffffef5 => 'holds the address of the GNU-style symbol hash table; this hash table refers to the symbol table referenced by the DT_SYMTAB element',
 			0x6ffffef6 => 'location of the GOT entry used by the TLS descriptor resolver PLT entry',
 			0x6ffffef7 => 'location of the PLT entry for TLS descriptor resolved calls',
 			0x6ffffef8 => 'start of conflict section',
 			0x6ffffef9 => 'library list',
 			0x6ffffefa => 'configuration information',
 			0x6ffffefb => 'dependency auditing',
 			0x6ffffefc => 'object auditing',
 			0x6ffffefd => 'PLT padding',
 			0x6ffffefe => 'move table',
 			0x6ffffeff => 'syminfo table',
30cd2f27
 			0x6ffffeff => 'holds the address of the Syminfo table',
 			0x6ffffff0 => 'holds the address of the table provided by the .gnu.version section',
a575282a
 			0x6ffffff9 => 'holds the number of relative relocations in .rela.dyn',
 			0x6ffffffa => 'all Elf32_Rel R_*_RELATIVE relocations have been placed into a single block and this entry specifies the number of entries in that block. This permits ld.so.1 to streamline the processing of RELATIVE relocations',
 			0x6ffffffb => 'DF_1_* state flags',
 			0x6ffffffc => 'holds the address of version definition table',
 			0x6ffffffd => 'holds the number of version definitions',
 			0x6ffffffe => 'holds the address of the table with needed versions',
 			0x6fffffff => 'holds the number of needed versions',
 		},
 		'ranges' => {
 			'OS' => {
 				'meaning' => 'operating system-specific semantics',
 				'low_value' => 0x6000000D,
 				'low_name' => 'DT_LOOS',
 				'high_value' => 0X6FFFF000,
 				'high_name' => 'DT_HIOS',
 			},
 			'PROC' => {
 				'meaning' => 'processor-specific semantics',
 				'low_value' => 0x70000000,
 				'low_name' => 'DT_LOPROC',
 				'high_value' => 0X7FFFFFFF,
 				'high_name' => 'DT_HIPROC',
 			},
 			'ADDRRNG' => {
 				'meaning' => 'reserved for definition by an architecture specific part',
 				'low_value' => 0X6FFFFE00,
 				'low_name' => 'DT_ADDRRNGLO',
 				'high_value' => 0X6FFFFEFF,
 				'high_name' => 'DT_ADDRRNGHI',
 			},
 		},
 	},
 	'd_un' => {
 		'single_meaning' => 'see d_tag',
 	},
 	'dt_flags' => {
 		'is_flag' => 1,
 		'name' => {
 			1 => 'DF_ORIGIN',
 			2 => 'DF_SYMBOLIC',
 			4 => 'DF_TEXTREL',
 			8 => 'DF_BIND_NOW',
 			16 => 'DF_STATIC_TLS',
 		},
 		'meaning' => {
 			1 => 'inform the dynamic linker that the object being loaded may make reference to the $ORIGIN substitution string',
 			2 => 'instruct the dynamic linker to start its symbol search with the shared object instead of the executable file',
 			4 =>  'inform the dynamic linker that one or more relocation entries might request modifications to a non-writable segment',
 			8 => 'instruct the dynamic linker to process all relocations for the object containing this entry before transferring control to the program',
 			16 => 'instruct the dynamic linker to reject attempts to load this file dynamically',
 		},
 	},
 	'dt_flags_1' => {
 		'is_flag' => 1,
 		'name' => {
 			0x00000001 => 'DF_1_NOW',
 			0x00000002 => 'DF_1_GLOBAL',
 			0x00000004 => 'DF_1_GROUP',
 			0x00000008 => 'DF_1_NODELETE',
 			0x00000010 => 'DF_1_LOADFLTR',
 			0x00000020 => 'DF_1_INITFIRST',
 			0x00000040 => 'DF_1_NOOPEN',
 			0x00000080 => 'DF_1_ORIGIN',
 			0x00000100 => 'DF_1_DIRECT',
 			0x00000200 => 'DF_1_TRANS',
 			0x00000400 => 'DF_1_INTERPOSE',
 			0x00000800 => 'DF_1_NODEFLIB',
 			0x00001000 => 'DF_1_NODUMP',
 			0x00002000 => 'DF_1_CONFALT',
 			0x00004000 => 'DF_1_ENDFILTEE',
 			0x00008000 => 'DF_1_DISPRELDNE',
 			0x00010000 => 'DF_1_DISPRELPND',
 			0x00020000 => 'DF_1_NODIRECT',
 			0x00040000 => 'DF_1_IGNMULDEF',
 			0x00080000 => 'DF_1_NOKSYMS',
 			0x00100000 => 'DF_1_NOHDR',
 			0x00200000 => 'DF_1_EDITED',
 			0x00400000 => 'DF_1_NORELOC',
 			0x00800000 => 'DF_1_SYMINTPOSE',
 			0x01000000 => 'DF_1_GLOBAUDIT',
 			0x02000000 => 'DF_1_SINGLETON',
 		},
 		'meaning' => {
 			0x00000001 => 'perform complete relocation processing',
 			0x00000002 => 'set RTLD_GLOBAL for this object',
 			0x00000004 => 'indicate object is a member of a group',
 			0x00000008 => 'object cannot be deleted from a process',
 			0x00000010 => 'ensure immediate loading of filtees',
 			0x00000020 => 'object\'s initialization occurs first',
 			0x00000040 => 'object cannot be used with dlopen()',
 			0x00000080 => '$ORIGIN processing required',
 			0x00000100 => 'direct bindings enabled',
 			0x00000200 => 'meaning unknown / undefined',
 			0x00000400 => 'object is an interposer',
 			0x00000800 => 'ignore the default library search path',
 			0x00001000 => 'object cannot be dumped with dldump()',
 			0x00002000 => 'object is a configuration alternative',
 			0x00004000 => 'filtee terminates filter\'s search',
 			0x00008000 => 'displacement relocation has been carried out at build time',
 			0x00010000 => 'displacement relocation pending (to be applied at run-time)',
 			0x00020000 => 'object contains non-direct bindings',
 			0x00040000 => 'internal use',
 			0x00080000 => 'internal use',
 			0x00100000 => 'internal use',
 			0x00200000 => 'object has been modified since originally built',
 			0x00400000 => 'internal use',
 			0x00800000 => 'individual symbol interposers exist for this object',
 			0x01000000 => 'establish global auditing',
 			0x02000000 => 'singleton symbols are used',
 		},
 	},
ec5beb4b
 	'vd_version' => {
 		'name' => {
 			0 => 'VER_DEF_NONE',
 			1 => 'VER_DEF_CURRENT',
 		},
 		'meaning' => {
 			0 => 'invalid version',
 			1 => 'current version',
 		},
 	},
 	'vd_flags' => {
 		'is_flag' => 1,
 		'name' => {
 			1 => 'VER_FLG_BASE',
 			2 => 'VER_FLG_WEAK',
 		},
 		'meaning' => {
 			1 => 'version definition of the file',
 			2 => 'weak version identifier',
 		},
 	},
 	'vd_ndx' => {
 		'single_meaning' => 'version index numeric value referenced in the SHT_GNU_versym section',
 	},
 	'vd_cnt' => {
 		'single_meaning' => 'number of associated verdaux array entries',
 	},
 	'vd_hash' => {
 		'single_meaning' => 'version name hash value (ELF hash function)',
 	},
 	'vd_aux' => {
 		'single_meaning' => 'offset in bytes to a corresponding entry in an array of Verdaux structures',
 	},
 	'vd_next' => {
 		'single_meaning' => 'offset to the next verdef entry, in bytes',
 	},
 	'vda_name' => {
 		'single_meaning' => 'offset to the version or dependency name string in the section header, in bytes',
 	},
 	'vda_next' => {
 		'single_meaning' => 'offset to the next verdaux entry, in bytes',
 	},
00756b8f
 	'vn_version' => {
 		'name' => {
 			0 => 'VER_NEED_NONE',
 			1 => 'VER_NEED_CURRENT',
 		},
 		'meaning' => {
 			0 => 'invalid version',
 			1 => 'current version',
 		},
 	},
 	'vn_cnt' => {
 		'single_meaning' => 'number of associated vernaux array entries',
 	},
 	'vn_file' => {
 		'single_meaning' => 'offset to the file name string in the section header, in bytes',
 	},
 	'vn_aux' => {
 		'single_meaning' => 'offset in bytes to a corresponding entry in an array of Vernaux structures',
 	},
 	'vn_next' => {
 		'single_meaning' => 'offset to the next verneed entry, in bytes',
 	},
 	'vna_hash' => {
 		'single_meaning' => 'dependency name hash value (ELF hash function)',
 	},
 	'vna_flags' => {
 		'is_flag' => 1,
 		'name' => {
 			2 => 'VER_FLG_WEAK',
 			4 => 'VER_FLG_INFO',
 		},
 		'meaning' => {
 			2 => 'weak version identifier',
 			4 => 'this reference exists for informational purposes, and need not be validated at runtime',
 		},
 	},
 	'vna_other' => {
 		'single_meaning' => 'object file version identifier used in the .gnu.version symbol version array',
 	},
 	'vna_name' => {
 		'single_meaning' => 'offset to the dependency name string in the section header, in bytes',
 	},
 	'vna_next' => {
 		'single_meaning' => 'offset to the next vernaux entry, in bytes',
 	},
8531d4fa
 	'namesz' => {
 		'single_meaning' => 'size, in bytes, of the note name, i.e. a null-terminated character representation of the entry\'s owner or originator',
 	},
 	'descsz' => {
 		'single_meaning' => 'size, in bytes, of the note descriptor (excluding the padding required to align the next note, if any)',
 	},
 	'type' => {
 		'single_meaning' => 'interpretation of the descriptor -- not specified by the SysV ABI',
 	},
019600ce
 	'r_offset' => {
 		'single_meaning' => 'location at which to apply the relocation action',
 	},
 	'r_info' => {
 		'single_meaning' => 'symbol index (r_sym) and relocation type (r_type)',
 	},
 	'r_sym' => {
 		'single_meaning' => 'symbol table index with respect to which the relocation must be made',
 	},
 	'r_type' => {
 		'single_meaning' => 'the meaning is processor-specific',
 	},
 	'r_type_EM_386' => {
 		'name' => {
 			0  => 'R_386_NONE',
 			1  => 'R_386_32',
 			2  => 'R_386_PC32',
 			3  => 'R_386_GOT32',
 			4  => 'R_386_PLT32',
 			5  => 'R_386_COPY',
 			6  => 'R_386_GLOB_DAT',
 			7  => 'R_386_JMP_SLOT',
 			8  => 'R_386_RELATIVE',
 			9  => 'R_386_GOTOFF',
 			10 => 'R_386_GOTPC',
 		},
 		'meaning' => {
 			0  => relocationMeaning(R_FIELD_NONE,   'none'),
 			1  => relocationMeaning(R_FIELD_WORD32, 'S + A'),
 			2  => relocationMeaning(R_FIELD_WORD32, 'S + A - P'),
 			3  => relocationMeaning(R_FIELD_WORD32, 'G + A - P',   'computes the distance from the base of the GOT to the symbol\'s GOT entry'),
 			4  => relocationMeaning(R_FIELD_WORD32, 'L + A - P',   'computes the address of the symbol\'s PLT entry'),
 			5  => relocationMeaning(R_FIELD_NONE,   'none',        'relocation type for dynamic linking: its offset refers to a location in a writable segment'),
 			6  => relocationMeaning(R_FIELD_WORD32, 'S',           'used to set a GOT entry to the address of the specified symbol'),
 			7  => relocationMeaning(R_FIELD_WORD32, 'S',           'relocation type for dynamic linking: its offset gives the location of a PLT entry'),
 			8  => relocationMeaning(R_FIELD_WORD32, 'B + A',       'relocation type for dynamic linking: its offset gives a location within a shared object that contains a value representing a relative address'),
 			9  => relocationMeaning(R_FIELD_WORD32, 'S + A - GOT', 'computes the difference between a symbol\'s value and the address of the GOT'),
 			10 => relocationMeaning(R_FIELD_WORD32, 'GOT + A - P', 'resembles R_386_PC32 except it uses the address of teh global offset table in its calculation'),
 		},
 	},
 	'r_type_EM_X86_64' => {
 		'name' => {
 			0  => 'R_X86_64_NONE',
 			1  => 'R_X86_64_64',
 			2  => 'R_X86_64_PC32',
 			3  => 'R_X86_64_GOT32',
 			4  => 'R_X86_64_PLT32',
 			5  => 'R_X86_64_COPY',
 			6  => 'R_X86_64_GLOB_DAT',
 			7  => 'R_X86_64_JUMP_SLOT',
 			8  => 'R_X86_64_RELATIVE',
 			9  => 'R_X86_64_GOTPCREL',
 			10 => 'R_X86_64_32',
 			11 => 'R_X86_64_32S',
 			12 => 'R_X86_64_16',
 			13 => 'R_X86_64_PC16',
 			14 => 'R_X86_64_8',
 			15 => 'R_X86_64_PC8',
 			16 => 'R_X86_64_DPTMOD64',
 			17 => 'R_X86_64_DTPOFF64',
 			18 => 'R_X86_64_TPOFF64',
 			19 => 'R_X86_64_TLSGD',
 			20 => 'R_X86_64_TLSLD',
 			21 => 'R_X86_64_DTPOFF32',
 			22 => 'R_X86_64_GOTTPOFF',
 			23 => 'R_X86_64_TPOFF32',
 			24 => 'R_X86_64_PC64',
 			25 => 'R_X86_64_GOTOFF64',
 			26 => 'R_X86_64_GOTPC32',
 			27 => 'R_X86_64_GOT64',
 			28 => 'R_X86_64_GOTPCREL64',
 			29 => 'R_X86_64_GOTPC64',
 			30 => 'R_X86_64_GOTPLT64',
 			31 => 'R_X86_64_PLTOFF64',
 			32 => 'R_X86_64_SIZE32',
 			33 => 'R_X86_64_SIZE64',
 		},
 		'meaning' => {
 			0  => relocationMeaning(R_FIELD_NONE,   'none'),
 			1  => relocationMeaning(R_FIELD_WORD64, 'S + A'),
 			2  => relocationMeaning(R_FIELD_WORD32, 'S + A - P'),
 			3  => relocationMeaning(R_FIELD_WORD32, 'G + A',           'computes the distance from the base of the GOT to the symbol\'s GOT entry'),
 			4  => relocationMeaning(R_FIELD_WORD32, 'L + A - P',       'computes the address of the symbol\'s PLT entry'),
 			5  => relocationMeaning(R_FIELD_NONE,   'none',            'relocation type for dynamic linking: its offset refers to a location in a writable segment'),
 			6  => relocationMeaning(R_FIELD_WORD64, 'S',               'used to set a GOT entry to the address of the specified symbol'),
 			7  => relocationMeaning(R_FIELD_WORD64, 'S',               'relocation type for dynamic linking: its offset gives the location of a PLT entry'),
 			8  => relocationMeaning(R_FIELD_WORD64, 'B + A',           'relocation type for dynamic linking: its offset gives a location within a shared object that contains a value representing a relative address'),
 			9  => relocationMeaning(R_FIELD_WORD32, 'G + GOT + A - P', 'gives the difference between the location in the GOT where the symbol\'s address is given and the location where the relocation is applied'),
 			10 => relocationMeaning(R_FIELD_WORD32, 'S + A',           'truncates the computed value to 32-bits'),
 			11 => relocationMeaning(R_FIELD_WORD32, 'S + A',           'truncates the computed value to 32-bits'),
 			12 => relocationMeaning(R_FIELD_WORD16, 'S + A',           'this relocation is not conformant to the AMD64 ABI'),
 			13 => relocationMeaning(R_FIELD_WORD16, 'S + A - P',       'this relocation is not conformant to the AMD64 ABI'),
 			14 => relocationMeaning(R_FIELD_WORD8,  'S + A - P',       'this relocation is not conformant to the AMD64 ABI'),
 			15 => relocationMeaning(R_FIELD_WORD8,  'S + A - P',       'this relocation is not conformant to the AMD64 ABI'),
 			16 => relocationMeaning(R_FIELD_WORD64, '',                'part of the Thread-Local Storage ABI extensions'),
 			17 => relocationMeaning(R_FIELD_WORD64, '',                'part of the Thread-Local Storage ABI extensions'),
 			18 => relocationMeaning(R_FIELD_WORD64, '',                'part of the Thread-Local Storage ABI extensions'),
 			19 => relocationMeaning(R_FIELD_WORD32, '',                'part of the Thread-Local Storage ABI extensions'),
 			20 => relocationMeaning(R_FIELD_WORD32, '',                'part of the Thread-Local Storage ABI extensions'),
 			21 => relocationMeaning(R_FIELD_WORD32, '',                'part of the Thread-Local Storage ABI extensions'),
 			22 => relocationMeaning(R_FIELD_WORD32, '',                'part of the Thread-Local Storage ABI extensions'),
 			23 => relocationMeaning(R_FIELD_WORD32, '',                'part of the Thread-Local Storage ABI extensions'),
 			24 => relocationMeaning(R_FIELD_WORD64, 'S + A - P'),
 			25 => relocationMeaning(R_FIELD_WORD64, 'S + A - GOT'),
 			26 => relocationMeaning(R_FIELD_WORD32, 'GOT + A - P'),
 			27 => relocationMeaning(R_FIELD_WORD64, 'G + A',           'large model relocation type'),
 			28 => relocationMeaning(R_FIELD_WORD64, 'G + GOT - P + A', 'large model relocation type'),
 			29 => relocationMeaning(R_FIELD_WORD64, 'GOT - P + A',     'large model relocation type'),
 			30 => relocationMeaning(R_FIELD_WORD64, 'G + A',           'large model relocation type'),
 			31 => relocationMeaning(R_FIELD_WORD64, 'L - GOT + A',     'large model relocation type'),
 			32 => relocationMeaning(R_FIELD_WORD32, 'Z + A'),
 			33 => relocationMeaning(R_FIELD_WORD64, 'Z + A'),
 		},
 	},
 	'r_addend' => {
 		'single_meaning' => 'constant addend used to compute the value to be stored into the relocatable field',
 	},
0bf25930
 	'nbuckets' => {
 		'single_meaning' => 'number of hash buckets',
 	},
 	'symndx' => {
 		'single_meaning' => 'index of the first symbol in the dynamic symbol table that is to be accessible via the hash table',
 	},
 	'maskwords' => {
 		'single_meaning' => 'number of ELFCLASS sized words in the Bloom filter portion of the hash table section',
 	},
 	'shift2' => {
 		'single_meaning' => 'a shift count used by the Bloom filter',
 	},
9afc3f79
 	'p_type' => {
 		'name' => {
 			0 => 'PT_NULL',
 			1 => 'PT_LOAD',
 			2 => 'PT_DYNAMIC',
 			3 => 'PT_INTERP',
 			4 => 'PT_NOTE',
 			5 => 'PT_SHLIB',
 			6 => 'PT_PHDR',
 			7 => 'PT_TLS',
 			# Additional segment types defined by the LSB.
 			0x6474e550 => 'PT_GNU_EH_FRAME',
 			0x6474e551 => 'PT_GNU_STACK',
 			0x6474e552 => 'PT_GNU_RELRO',
 		},
 		'meaning' => {
 			0 => 'unused / ignored entry',
 			1 => 'loadable segment, described by p_filesz and p_memsz',
 			2 => 'dynamic linking information',
 			3 => 'location and size of a null-terminated path name to invoke as an interpreter',
 			4 => 'location an size of auxiliary information',
 			5 => 'reserved segment type / unspecified semantics',
 			6 => 'location and size of the program header table itself',
 			7 => 'Thread-Local Storage template',
 			0x6474e550 => 'location and size of the exception handling information as defined by the .eh_frame_hdr section',
 			0x6474e551 => 'specifies, through p_flags, whether the stack should be executable (default is yes)',
 			0x6474e552 => 'location and size of a segment which may be made read-only after relocations have been applied',
 		},
 		'ranges' => {
 			'OS' => {
 				'meaning' => 'operating system-specific semantics',
 				'low_value' => 0x60000000,
 				'low_name' => 'PT_LOOS',
 				'high_value' => 0X6FFFFFFF,
 				'high_name' => 'PT_HIOS',
 			},
 			'PROC' => {
 				'meaning' => 'processor-specific semantics',
 				'low_value' => 0x70000000,
 				'low_name' => 'PT_LOPROC',
 				'high_value' => 0X7FFFFFFF,
 				'high_name' => 'PT_HIPROC',
 			},
 		},
 	},
 	'p_flags' => {
 		'is_flag' => 1,
 		'name' => {
 			1 => 'PF_X',
 			2 => 'PF_W',
 			4 => 'PF_R',
 			0X0FF00000 => 'PF_MASKOS',
 			0xf0000000 => 'PF_MASKPROC',
 		},
 		'meaning' => {
 			1 => 'execute',
 			2 => 'write',
 			4 => 'read',
 			0X0FF00000 => 'system-specific semantics',
 			0xf0000000 => 'processor-specific semantics',
 		},
08375a4a
 		'_allowable' => {
 			1 => 'read permission',
 			2 => 'read and execute permissions',
 			3 => 'read permission',
 			4 => 'execute permission',
 			6 => 'execute permission',
 		},
9afc3f79
 	},
 	'p_offset' => {
 		'single_meaning' => 'offset from the beginning of the file at which the first byte of the segment resides',
 	},
 	'p_vaddr' => {
 		'single_meaning' => 'virtual address at which the first byte of the segment resides in memory',
 	},
 	'p_paddr' => {
 		'single_meaning' => 'segment\'s physical address, if relevant',
 	},
 	'p_filesz' => {
 		'single_meaning' => 'number of bytes in the file image of the segment',
 	},
 	'p_memsz' => {
 		'single_meaning' => 'number of bytes in the memory image of the segment',
 	},
 	'p_align' => {
 		'single_meaning' => 'value to which the segments are aligned in memory and in the file',
 	},
0bb3b9f9
 };
 
7512d10f
 my $columns = 80;
 $SIG{'WINCH'} = \&updateNumberOfColumns;
 updateNumberOfColumns();
 
695abf35
 # Simple arguments parsing: require one and exactly one argument.
 usage() if @ARGV != 1;
 
 # That arguments is expected to be an existing, readable file.
 our $elf_filepath = $ARGV[0];
 unless (-f $elf_filepath && -r $elf_filepath) {
 	exitWithMessage(120, sprintf('%s either does not exist or is not readable.', $elf_filepath));
 }
 
eec3a56f
 coloralias('main_action', 'bright_yellow');
 coloralias('error', 'red');
 coloralias('index', 'bright_white');
 coloralias('offset', 'yellow');
 coloralias('value_name', 'bright_white');
 coloralias('value', 'cyan');
 coloralias('name', 'green');
 coloralias('flag_name', 'green');
 coloralias('flag_value', 'cyan');
 
b30f27dc
 my $default_class     = ELFCLASS64;
 my $default_endianess = LITTLE_ENDIAN;
 
695abf35
 # Open the file
 our $elf_fh;
 our $opening = open($elf_fh, '<' . $elf_filepath);
 if (!$opening) {
 	exitWithMessage(115, sprintf('Unable to open %s: %s', $elf_filepath, $!));
 }
 binmode($elf_fh);
 walkELF($elf_fh);
 close($elf_fh);
3725644f
 exit(0);
695abf35
 
7512d10f
 sub updateNumberOfColumns {
 	$columns = numberOfColumns();
 }
 
 sub numberOfColumns {
 	{
 		$SIG{__WARN__} = sub { };
 		require "sys/ioctl.ph";
 	}
 	# Taken from perldoc -q "get the screen size".
 	my $tty_fh;
 	my $winsize;
 	if (!open($tty_fh, '+</dev/tty')) {
 		return 'notty';
 	}
 	if (!ioctl($tty_fh, &TIOCGWINSZ, $winsize)) {
 		return 'notty';
 	}
 	my ($row, $col, $xpixel, $ypixel) = unpack('S4', $winsize);
 	return $col;
 }
 
bbeac99e
 sub say {
 	my $indent = shift;
 	$indent = '  'x$indent;
 	my $string;
 	for my $str (@_) {
 		next if (!defined($str));
 		$string = $str;
 		$string =~ s/^/${indent}/mgs;
 		print $string . "\n";
 	}
 }
 
e354e70f
 sub sayf {
 	my $indent = shift;
 	my $string = shift;
 	my @args = @_;
 	say($indent, sprintf($string, @args));
 }
 
18f4b918
 sub announceOperation {
 	my ($ind, $prefix, $index, $suffix) = @_;
 	say(
 		$ind,
 		colored($prefix . ' #', 'underline') .
 		colored($index, 'index underline') .
 		colored($suffix, 'underline')
 	);
 }
 
695abf35
 sub exitWithMessage {
 	my $rc = $_[0];
 	my $message = $_[1];
 	print "Error: " unless ($rc == 0);
 	print $message . "\n";
 	exit($rc);
 }
 
 sub usage {
 	print "Usage: ${0} file\n";
 	exit(125);
 }
 
37f62881
 sub setDefaultEndianess {
 	$default_endianess = shift;
 }
 
35e67507
 sub setDefaultClass {
 	$default_class = shift;
 }
 
 sub typeInfo {
 	my $type = shift;
 	my $class = shift;
 
 	$class = $default_class if (!defined($class));
 
 	if (!exists($elf_data_types->{$class})) {
 		exitWithMessage(150, "tried to work with unknown ELF class (${class})");
 	}
 
 	if (!exists($elf_data_types->{$class}->{$type})) {
 		exitWithMessage(148, "tried to work with unknown type (${type})");
 	}
 
 	return $elf_data_types->{$class}->{$type};
 }
 
 # Expected arguments:
 #   - file descriptor
 #   - type, e.g. 'Half', 'Word', 'Addr', etc.
 #   - ELF Class, e.g. ELFCLASS32 or ELFCLASS64
 #   - endianess: either LITTLE_ENDIAN or BIG_ENDIAN
 sub readELFValue {
 	my $fd = shift;
 	my $type = shift;
 	my $class = shift;
 	my $endianess = shift;
 
 	# Default values.
 	$class = $default_class if (!defined($class));
 	$endianess = $default_endianess if (!defined($endianess));
 
 	my $type_info = typeInfo($type, $class);
 
 	return readValue(
 		$fd,
 		$type_info->{'size'},
 		$type_info->{'signed'},
 		$endianess
 	);
 }
 
37f62881
 # Expected arguments:
 #   - file descriptor
 #   - amount of bytes to read and parse
 #   - 0 for unsigned, 1 for signed
 #   - endianess: either LITTLE_ENDIAN or BIG_ENDIAN
 sub readValue {
 	my $fd = shift;
 	my $size = shift;
 	my $signed = shift;
 	my $endianess = shift;
 
cc8aa577
 	# Check arguments.
 	if (!defined($signed) || ($signed != SIGNED && $signed != UNSIGNED)) {
 		exitWithMessage(8, 'attempted to call readValue with invalid signedness');
 	}
 	if (!defined($endianess) || ($endianess != LITTLE_ENDIAN && $endianess != BIG_ENDIAN)) {
 		exitWithMessage(8, 'attempted to call readValue with invalid endianess');
37f62881
 	}
 
 	my $data;
83035aa2
 	my $reading = read($fd, $data, $size);
 	if (!defined($reading)) {
c4e72113
 		exitWithMessage(6, sprintf("error when attempting to read %d bytes: %s.", $size, $!));
83035aa2
 	}
 	elsif ($reading == 0) {
 		exitWithMessage(5, "reached end of file.");
 	}
 	elsif ($reading != $size) {
c4e72113
 		exitWithMessage(4, sprintf("was asked to read %d bytes but could only read %d.", $size, $reading));
83035aa2
 	}
 
37f62881
 	my $unpack_arg;
8e03dc1f
 	if ($size == 1) {
 		$unpack_arg = ($signed ? 'c' : 'C');
 	}
 	elsif ($size == 2 || $size == 4) {
37f62881
 		# n stands for "network" (i.e. big-endian)
 		# v stands for "VAX" (i.e. little-endian)
 		$unpack_arg = $endianess ? 'n' : 'v';
 		# lowercase is 16 bits, uppercase is 32 bits
 		$unpack_arg = uc($unpack_arg) if ($size == 4);
 		$unpack_arg .= '!' if ($signed);
 	}
 	elsif ($size == 8) {
 		$unpack_arg = ($signed ? 'q' : 'Q') . ($endianess ? '>' : '<');
 	}
 	return unpack($unpack_arg, $data);
 	# Unpack cheat sheet:
 	#   n  An unsigned short (16-bit) in "network" (big-endian) order.
 	#   N  An unsigned long (32-bit) in "network" (big-endian) order.
 	#   v  An unsigned short (16-bit) in "VAX" (little-endian) order.
 	#   V  An unsigned long (32-bit) in "VAX" (little-endian) order
 	#   n! A signed short (16-bit) in "network" (big-endian) order.
 	#   N! A signed long (32-bit) in "network" (big-endian) order.
 	#   v! A signed short (16-bit) in "VAX" (little-endian) order.
 	#   V! A signed long (32-bit) in "VAX" (little-endian) order
 	#   q> A signed quad (64-bit) value (big-endian).
 	#   Q> An unsigned quad (64-bit) value (big-endian).
 	#   q< A signed quad (64-bit) value (little-endian).
 	#   Q< An unsigned quad (64-bit) value (little-endian).
 }
 
35e67507
 sub formatValue {
 	my $value = shift;
 	my $type = shift;
 	my $class = shift;
 
 	my $type_info = typeInfo($type, $class);
 	return sprintf($type_info->{'formatter'}, $value);
 }
 
d46303eb
 sub formatOffset {
 	my $offset = shift;
 	$offset = formatValue($offset, 'Off');
 	return colored($offset, 'offset');
 }
 
1f77fdb7
 sub formatFlags {
 	my @flags = sort(@_);
 	my $flags_string = join('+', @flags) || '-';
 	return $flags_string;
 }
 
0cfc53d2
 sub formatVersionDefinition {
 	my $verdef = shift;
 
 	my @verdef_strings;
 	for (my $i = 0; $i < $verdef->{'vd_cnt'}; ++ $i) {
 		push(@verdef_strings, $verdef->{'verdaux'}->{$i}->{'name'});
 	}
0e633a4d
 	my $str = shift(@verdef_strings);
 	if (scalar(@verdef_strings)) {
 		$str .= sprintf(' (deps: %s)', join(', ', @verdef_strings));
 	}
 	return $str;
0cfc53d2
 }
 
888be9b2
 sub formatVersionRequirement {
 	my $vernaux = shift;
 
 	my $verneed_string = sprintf(
 		'%s (%s)',
 		$vernaux->{'name'},
 		$vernaux->{'verneed'}->{'file'}
 	);
 	return $verneed_string;
 }
 
695abf35
 sub walkELF {
37f62881
 	my $elf_fh = shift;
 
407b70b7
 	my $elf_data = {};
9070b8d0
 	say(0, colored("Parsing file header", 'main_action'));
407b70b7
 	walkELFHeaderMagicNumber($elf_fh, $elf_data);
 	walkELFHeaderIdent($elf_fh, $elf_data);
 	walkELFHeaderFields($elf_fh, $elf_data);
78111860
 	walkELFSections($elf_fh, $elf_data);
a09248ae
 	walkELFProgramHeader($elf_fh, $elf_data);
37f62881
 }
 
49c61b18
 sub analyseMachineSpecificValue {
 	my $table_name = shift;
 	my $value = shift;
 	my $machine = shift;
 
 	if (defined($machine)) {
 		# Does $machine look like a machine name?
 		if ($machine !~ m#^EM_#) {
 			# Seems not -- assume $machine is a e_machine value instead.
 			$machine = $elf->{'e_machine'}->{'name'}->{$machine};
 		}
 		# Ensure $machine does look like a machine name now.
 		if (defined($machine) && $machine =~ m#^EM_#) {
 			my $machine_table_name = $table_name . '_' . $machine;
 			if (exists($elf->{$machine_table_name})) {
 				return analyseValue($machine_table_name, $value);
 			}
 		}
 	}
 	return analyseValue($table_name, $value);
 }
 
056a18c9
 sub analyseValue {
 	my $table_name = shift;
 	my $value = shift;
 
 	return undef if (!exists($elf->{$table_name}));
 	my $table= $elf->{$table_name};
 
 	my $return = {};
 
 	# Can we get a single, systematic meaning?
 	if (exists($table->{'single_meaning'})) {
 		$return->{'single_meaning'} = $table->{'single_meaning'};
 	}
 
 	# Are we dealing with a set of flags, by chance?
6f2b95ca
 	if (exists($table->{'is_flag'}) && $table->{'is_flag'}) {
056a18c9
 		$return->{'is_flag'} = 1;
 		$return->{'name'} = {};
 		$return->{'meaning'} = {};
 		for my $mask (keys(%{$table->{'name'}})) {
 			if (($value & $mask) == $mask) {
 				$return->{'name'}->{$mask}    = $table->{'name'}->{$mask};
 				$return->{'meaning'}->{$mask} = $table->{'meaning'}->{$mask};
 			}
 		}
 		return $return;
 	}
 
 	# Can we get a value-specific name and/or a meaning?
 	for my $info ('name', 'meaning') {
 		if (exists($table->{$info})) {
 			if (exists($table->{$info}->{$value})) {
 				$return->{$info} = $table->{$info}->{$value};
 			}
 		}
 	}
 	return $return if (keys(%$return));
 
 	# It seems we got neither a name nor a meaning.
 	# => Try ranges now.
 	if (exists($table->{'ranges'})) {
 		for my $range (keys(%{$table->{'ranges'}})) {
 			my $lo = $table->{'ranges'}->{$range}->{'low_value'};
 			my $hi = $table->{'ranges'}->{$range}->{'high_value'};
 			if ($value >= $lo && $value <= $hi) {
 				return $table->{'ranges'}->{$range};
 			}
 		}
 	}
 
fdcbc43d
 	# Still there? Seriously? Well, is this fatal?
 	if (exists($table->{'unexplained_value'})) {
 		$return->{'unexplained_value'} = $table->{'unexplained_value'};
 		return $return;
 	}
056a18c9
 	return undef;
 }
 
74f168fd
 sub analyseAndExplainValue {
 	my $value = shift;
 	my $table_name = shift;
 	my $type = shift;
 	my $class = shift;
49c61b18
 	my $machine = shift;
74f168fd
 
49c61b18
 	my $analysis = analyseMachineSpecificValue($table_name, $value, $machine);
74f168fd
 	return undef if (!defined($analysis));
 
 	my $str = sprintf(
 		'%s is %s',
 		colored($table_name, 'value_name'),
 		colored(formatValue($value, $type, $class), 'value')
 	);
 
 	if (exists($analysis->{'single_meaning'})) {
 		$str .= sprintf(' -- %s.', $analysis->{'single_meaning'});
 	}
 	elsif (exists($analysis->{'is_flag'})) {
 		if (keys(%{$analysis->{'name'}})) {
 			$str .= ", i.e. the following flags:";
 			for my $mask (keys(%{$analysis->{'name'}})) {
 				$str .= sprintf(
 					"\n  - %s (%s): %s",
 					colored($analysis->{'name'}->{$mask}, 'flag_name'),
 					colored(formatValue($mask, $type, $class), 'flag_value'),
 					$analysis->{'meaning'}->{$mask}
 				);
 			}
 		}
 		else {
 			$str .= " (no flags)";
 		}
 	}
 	elsif (exists($analysis->{'name'}) && exists($analysis->{'meaning'})) {
 		$str .= sprintf(
 			', i.e. %s: %s.',
 			colored($analysis->{'name'}, 'name'),
 			$analysis->{'meaning'}
 		);
 	}
 	elsif (exists($analysis->{'low_value'})) {
 		$str .= sprintf(
 			', between %s (%s) and %s (%s): %s',
 			$analysis->{'low_name'},
 			formatValue($analysis->{'low_value'}, $type, $class),
 			$analysis->{'high_name'},
 			formatValue($analysis->{'high_value'}, $type, $class),
 			$analysis->{'meaning'}
 		);
 	}
fdcbc43d
 	elsif (exists($analysis->{'unexplained_value'})) {
 		if ($analysis->{'unexplained_value'} eq 'fatal') {
 			$str .= ", which is illegal, aborting.";
 			exitWithMessage(100, $str);
 		}
 		elsif ($analysis->{'unexplained_value'} eq 'warning') {
 			$str .= ", which is probably abnormal, ignoring.";
 		}
 		else {
 			$str .= ", " . $analysis->{'unexplained_value'} . ".";
 		}
 	}
74f168fd
 	return $str;
 }
 
abe92edd
 sub analyseSectionName {
 	my $name = shift;
 	my $return = {};
 	my @specs = (
 		'gnu_special_sections',
 		'lsb5-core-amd64_additional_special_sections',
 		'lsb5-core-generic_additional_special_sections',
 		'elf_special_sections',
 	);
 	for my $desc_type ('short', 'long') {
 		for my $sections_type (@specs) {
 			my $known_sections = $elf->{$sections_type}->{$desc_type};
 			if (exists($known_sections->{$name})) {
 				$return->{$desc_type} = $known_sections->{$name};
 				last;
 			}
 			elsif (exists($known_sections->{'.rel'})) {
 				if ($name =~ m/^\.rela?\.(.+)$/) {
 					$return->{$desc_type} = sprintf($known_sections->{'.rel'}, $1);
 				}
 			}
 		}
 	}
 	return $return;
 }
 
37f62881
 sub walkELFHeaderMagicNumber {
 	my $elf_fh = shift;
 
8e649ca1
 	for (my $i = 0; $i < 4; ++ $i) {
 		my $byte = sprintf('EI_MAG%d', $i);
 		my $expected = $elf->{'e_ident'}->{'_magic'}->{$byte};
 		my $value = readELFValue($elf_fh, 'char');
37f62881
 
8e649ca1
 		my $msg = sprintf(
 			'%s is %s (%s)',
 			colored($byte, 'value_name'),
 			colored(sprintf('0x%02X', $value), 'value'),
 			colored(sprintf('%c',     $value), 'value')
 		);
37f62881
 
8e649ca1
 		if ($value == $expected) {
 			say(2, $msg . ' as expected.');
 		}
 		else {
 			$msg .= sprintf(
 				' but %s (%s) was expected, aborting.',
 				colored(sprintf('0x%02X', $expected), 'value'),
 				colored(sprintf('%c',     $expected), 'value')
 			);
 			exitWithMessage(100, $msg);
 		}
37f62881
 	}
 }
 
 sub walkELFHeaderIdent {
 	my $elf_fh = shift;
 	my $elf_data = shift;
 	my $data;
 
85bbae8e
 	my $ind = 2;
 	for my $entry (@{$elf_structures->{'e_ident'}->{'ordered_entries'}}) {
 		my $type = $elf_structures->{'e_ident'}->{$entry};
 		my $value = $elf_data->{$entry} = readELFValue($elf_fh, $type);
 		say($ind, analyseAndExplainValue($elf_data->{$entry}, $entry, $type));
37f62881
 
85bbae8e
 		if ($entry eq 'EI_CLASS') {
 			setDefaultClass($value);
 		}
 		elsif ($entry eq 'EI_DATA') {
 			# Our constants are "0 and 1" vs "1 and 2" for the ELF format.
 			setDefaultEndianess($value - 1);
 		}
37f62881
 	}
 
85bbae8e
 	my $ei_nident = $elf->{'e_ident'}->{'_constants'}->{'EI_NIDENT'};
99848307
 	sayf($ind, 'Seeking to offset EI_NIDENT (%d)', $ei_nident);
85bbae8e
 	seek($elf_fh, $ei_nident, Fcntl::SEEK_SET);
37f62881
 }
 
 sub walkELFHeaderFields {
 	my $elf_fh = shift;
 	my $elf_data = shift;
 	my $data;
 
1a45d514
 	my $ind = 1;
 	for my $entry (@{$elf_structures->{'file_header'}->{'ordered_entries'}}) {
 		my $type = $elf_structures->{'file_header'}->{$entry};
 		my $value = $elf_data->{$entry} = readELFValue($elf_fh, $type);
 		say($ind, analyseAndExplainValue($elf_data->{$entry}, $entry, $type));
37f62881
 
1a45d514
 		if ($entry eq 'e_entry') {
 			say($ind + 1, 'Here, zero means there is no entry point.') if (!$value);
 		}
 		elsif ($entry eq 'e_phnum') {
 			my $e_phentsize = $elf_data->{'e_phentsize'};
99848307
 			sayf($ind + 1, 'conclusion: the program header table is %d x %d = %d bytes', $e_phentsize, $value, $e_phentsize * $value);
1a45d514
 		}
 		elsif ($entry eq 'e_shnum') {
 			my $e_shentsize = $elf_data->{'e_shentsize'};
99848307
 			sayf($ind + 1, 'conclusion: sections are indexed from 0 (this index is reserved though) to %d.', $value - 1);
 			sayf($ind + 1, 'conclusion: the section header table is %d x %d = %d bytes', $e_shentsize, $value, $e_shentsize * $value);
1a45d514
 		}
37f62881
 	}
695abf35
 }
1de5d814
 
78111860
 sub walkELFSections {
 	my $elf_fh = shift;
 	my $elf_data = shift;
 
addb6923
 	say(0, colored('Parsing section headers', 'main_action'));
78111860
 	$elf_data->{'sections'} = {};
b2b327ad
 	my $ind = 1;
78111860
 
eb719e44
 	announceOperation($ind, 'Parsing section header', $elf_data->{'e_shstrndx'}, ' first as it holds section names:');
 	walkELFSectionHeader($elf_fh, $elf_data, $elf_data->{'e_shstrndx'});
78111860
 
eb719e44
 	announceOperation($ind, 'Not parsing section header', 0, ' as it is reserved.');
b2b327ad
 	say($ind + 1, "It must contain only zeroes.");
78111860
 
 	for (my $i = 1; $i < $elf_data->{'e_shnum'}; ++ $i) {
 		next if ($i == $elf_data->{'e_shstrndx'});
eb719e44
 		announceOperation($ind, 'Parsing section header', $i, ':');
 		walkELFSectionHeader($elf_fh, $elf_data, $i);
78111860
 	}
a24db31b
 
 	# Display a summary of sections.
 	say(0, colored('Summary of sections', 'main_action'));
 	my $table_format = '%3s %-30s %-20s %-30s %-30s';
99848307
 	sayf(1, $table_format, '#', 'Name', 'Type', 'Flags', 'Use');
a24db31b
 	for (my $i = 1; $i < $elf_data->{'e_shnum'}; ++ $i) {
 		my $section = $elf_data->{'sections'}->{$i};
99848307
 		sayf(
 			1,
a24db31b
 			$table_format,
 			$i,
 			colored($section->{'_name'}, 'name'),
 			$section->{'_type'},
 			formatFlags(values(%{$section->{'_flags'}})),
 			analyseSectionName($section->{'_name'})->{'short'}
 		);
 	}
f93db39f
 
cf79a7a1
 	say(0, colored('Parsing SHT_HASH sections', 'main_action'));
 	for (my $i = 1; $i < $elf_data->{'e_shnum'}; ++ $i) {
 		my $section = $elf_data->{'sections'}->{$i};
 		if ($section->{'_type'} eq 'SHT_HASH') {
 			announceOperation($ind, 'Parsing section', $i, ' (' . colored($section->{'_name'}, 'name') . '):');
 			walkHashSection($elf_fh, $elf_data, $i);
 		}
 	}
 
8041b348
 	say(0, colored('Parsing SHT_GNU_HASH sections', 'main_action'));
 	for (my $i = 1; $i < $elf_data->{'e_shnum'}; ++ $i) {
 		my $section = $elf_data->{'sections'}->{$i};
 		if ($section->{'_type'} eq 'SHT_GNU_HASH') {
 			announceOperation($ind, 'Parsing section', $i, ' (' . colored($section->{'_name'}, 'name') . '):');
 			walkGnuHashSection($elf_fh, $elf_data, $i);
 		}
 	}
 
7ba7a961
 	say(0, colored('Parsing SHT_DYNSYM sections', 'main_action'));
f93db39f
 	for (my $i = 1; $i < $elf_data->{'e_shnum'}; ++ $i) {
 		my $section = $elf_data->{'sections'}->{$i};
 		if ($section->{'_type'} eq 'SHT_DYNSYM') {
18f4b918
 			announceOperation($ind, 'Parsing section', $i, ' (' . colored($section->{'_name'}, 'name') . '):');
f93db39f
 			walkSymbolTable($elf_fh, $elf_data, $i);
f94bfd81
 			push(@{$elf_data->{'SHT_DYNSYM'}}, $i);
f93db39f
 		}
 	}
7267f831
 
 	say(0, colored('Parsing SHT_REL/SHT_RELA sections', 'main_action'));
 	for (my $i = 1; $i < $elf_data->{'e_shnum'}; ++ $i) {
 		my $section = $elf_data->{'sections'}->{$i};
 		if ($section->{'_type'} =~ '^SHT_RELA?$') {
18f4b918
 			announceOperation($ind, 'Parsing section', $i, ' (' . colored($section->{'_name'}, 'name') . '):');
7267f831
 			walkRela($elf_fh, $elf_data, $i);
 		}
 	}
463a1c89
 
a5793d7b
 	say(0, colored('Parsing SHT_DYNAMIC sections', 'main_action'));
 	for (my $i = 1; $i < $elf_data->{'e_shnum'}; ++ $i) {
 		my $section = $elf_data->{'sections'}->{$i};
 		if ($section->{'_type'} eq 'SHT_DYNAMIC') {
 			announceOperation($ind, 'Parsing section', $i, ' (' . colored($section->{'_name'}, 'name') . '):');
 			walkDynamic($elf_fh, $elf_data, $i);
 		}
 	}
 
44aeea2b
 	say(0, colored('Parsing SHT_GNU_verdef sections', 'main_action'));
 	for (my $i = 1; $i < $elf_data->{'e_shnum'}; ++ $i) {
 		my $section = $elf_data->{'sections'}->{$i};
 		if ($section->{'_type'} eq 'SHT_GNU_verdef') {
 			announceOperation($ind, 'Parsing section', $i, ' (' . colored($section->{'_name'}, 'name') . '):');
 			walkVerdef($elf_fh, $elf_data, $i);
 		}
 	}
 
3cbe1c43
 	say(0, colored('Parsing SHT_GNU_verneed sections', 'main_action'));
 	for (my $i = 1; $i < $elf_data->{'e_shnum'}; ++ $i) {
 		my $section = $elf_data->{'sections'}->{$i};
 		if ($section->{'_type'} eq 'SHT_GNU_verneed') {
 			announceOperation($ind, 'Parsing section', $i, ' (' . colored($section->{'_name'}, 'name') . '):');
 			walkVerneed($elf_fh, $elf_data, $i);
 		}
 	}
 
2dd06420
 	say(0, colored('Parsing SHT_GNU_versym sections', 'main_action'));
 	for (my $i = 1; $i < $elf_data->{'e_shnum'}; ++ $i) {
 		my $section = $elf_data->{'sections'}->{$i};
 		if ($section->{'_type'} eq 'SHT_GNU_versym') {
 			announceOperation($ind, 'Parsing section', $i, ' (' . colored($section->{'_name'}, 'name') . '):');
 			walkVersym($elf_fh, $elf_data, $i);
 		}
 	}
 
463a1c89
 	say(0, colored('Parsing SHT_NOTE sections', 'main_action'));
 	for (my $i = 1; $i < $elf_data->{'e_shnum'}; ++ $i) {
 		my $section = $elf_data->{'sections'}->{$i};
 		if ($section->{'_type'} =~ '^SHT_NOTE$') {
 			announceOperation($ind, 'Parsing section', $i, ' (' . colored($section->{'_name'}, 'name') . '):');
 			walkNote($elf_fh, $elf_data, $i);
 		}
 	}
6940150c
 
 	say(0, colored('Parsing known SHT_PROGBITS sections', 'main_action'));
 	for (my $i = 1; $i < $elf_data->{'e_shnum'}; ++ $i) {
 		my $section = $elf_data->{'sections'}->{$i};
 		if ($section->{'_type'} eq 'SHT_PROGBITS') {
 			if ($section->{'_name'} =~ m/^\.interp/) {
 				announceOperation($ind, 'Parsing section', $i, ' (' . colored($section->{'_name'}, 'name') . '):');
 				walkProgBits($elf_fh, $elf_data, $i);
 			}
 		}
 	}
78111860
 }
 
eb719e44
 sub walkELFSectionHeader {
78111860
 	my $elf_fh = shift;
 	my $elf_data = shift;
 	my $section_index = shift;
 
b4926357
 	my $ind = 2;
78111860
 	my $offset = sectionOffset($elf_data, $section_index);
99848307
 	sayf(
b4926357
 		$ind,
99848307
 		"Seeking to offset shoff + (%s x shentsize) = %s.",
 		colored($section_index, 'index'),
 		formatOffset($offset)
b4926357
 	);
78111860
 	seek($elf_fh, $offset, Fcntl::SEEK_SET);
 
af86a558
 	my $arch = $elf_data->{'EI_CLASS'};
 	my $sh_info = $elf_structures->{'section_header'}->{$arch};
 
b4926357
 	my $type, my $value, my $details;
78111860
 	my $section = {};
b4926357
 	# For each entry of a section header...
af86a558
 	for my $entry (@{$sh_info->{'ordered_entries'}}) {
b4926357
 		# get its type, ...
af86a558
 		$type = $sh_info->{$entry};
b4926357
 		# read its value, ...
 		$value = $section->{$entry} = readELFValue($elf_fh, $type);
 		# and provide a first basic explanation.
 		say($ind, analyseAndExplainValue($section->{$entry}, $entry, $type));
 
 		# For some entries, complete with extra explanations or details.
 		my $details = '';
b57d33f8
 		if ($entry eq 'sh_type') {
 			# Store the human name of the type for later use.
 			my $analysis = analyseValue('sh_type', $value);
 			$section->{'_type'} = $analysis->{'name'} if (exists($analysis->{'name'}));
 			if (exists($analysis->{'high_name'})) {
 				$section->{'_type'} = $analysis->{'low_name'} . '-' . $analysis->{'high_name'};
 			}
 		}
 		elsif ($entry eq 'sh_flags') {
 			# Store the human names of the flags for later use.
 			my $analysis = analyseValue('sh_flags', $value);
 			$section->{'_flags'} = $analysis->{'name'} if (exists($analysis->{'name'}));
 		}
 		elsif ($entry eq 'sh_addr') {
b4926357
 			$details = "Here, zero means the section is not allocated to the memory image of the program." if (!$value);
 		}
 		elsif ($entry eq 'sh_size') {
 			if ($section->{'sh_type'} == 8) { # 8 is SHT_NOBITS
 				$details = sprintf(
 					"Here, since this is a %s section, this is the amount of space\noccupied in memory, not in the file.",
 					colored('SHT_NOBITS', 'name')
 				);
 			}
 		}
 		elsif ($entry eq 'sh_link') {
 			if ($value) {
 				# sh_link is non-zero -- what is the use of this link, considering the current section type?
 				my $sh_type = $section->{'sh_type'};
 				if (exists($elf->{'sh_link'}->{'_use'}->{$sh_type})) {
 					$details = sprintf(
 						"Here, since this is a %s section, section #%s should be \n%s",
 						colored($elf->{'sh_type'}->{'name'}->{$sh_type}, 'name'),
 						colored($section->{'sh_link'}, 'index'),
 						$elf->{'sh_link'}->{'_use'}->{$sh_type}
 					);
 				}
 			}
 		}
 		elsif ($entry eq 'sh_info') {
 			if ($value) {
 				# sh_info is non-zero -- what is the use of this info, considering the current section type?
 				my $sh_type = $section->{'sh_type'};
 				if (exists($elf->{'sh_info'}->{'_use'}->{$sh_type})) {
 					$details = sprintf(
 						"Here, since this is a %s section, this should represent \n%s.",
 						colored($elf->{'sh_type'}->{'name'}->{$sh_type}, 'name'),
 						$elf->{'sh_info'}->{'_use'}->{$sh_type}
 					);
 				}
 			}
 		}
 		elsif ($entry eq 'sh_addralign') {
 			$details = 'This value must be a power of two.';
 			if (!isPowerOfTwo($section->{'sh_addralign'})) {
 				$details = colored($details, 'error');
 			}
 		}
 		say($ind + 1, $details) if (length($details));
 	}
78111860
 
295938be
 	# Push the resulting section into $elf_data so readStringFromSection() can
 	# leverage it.
968ad784
 	$section->{'_index'} = $section_index;
78111860
 	$elf_data->{'sections'}->{$section_index} = $section;
 
295938be
 	my $name = displayStringFromSection($ind, $elf_fh, $elf_data, $elf_data->{'e_shstrndx'}, $section->{'sh_name'});
 	if (defined($name) && length($name->{'string'})) {
 		$section->{'_name'} = $name->{'string'};
 		my $analysis = analyseSectionName($section->{'_name'});
 		if (defined($analysis->{'long'})) {
 			say($ind, colored('Expected use based on the name:', 'value_name'));
 			say($ind + 1, $analysis->{'long'});
b4926357
 		}
 	}
78111860
 }
 
2d66bea6
 sub walkSymbolTable {
 	my $elf_fh = shift;
 	my $elf_data = shift;
 	my $section_index = shift;
 	my $ind = 2;
 
 	my $section = $elf_data->{'sections'}->{$section_index};
 	return if (!defined($section));
 
 	# How many entries do we intend to read?
 	my $count = $section->{'sh_size'} / $section->{'sh_entsize'};
 
 	# Where should we read names from?
9cfb0abd
 	my $strtab = displayStringTableForSection($ind, $elf_data, $section_index, 'Symbol names');
2d66bea6
 
 	my $arch = $elf_data->{'EI_CLASS'};
 	my $st_info = $elf_structures->{'symbol_table'}->{$arch};
 
 	# Iterate over symbol table entries.
 	for (my $i = 0; $i < $count; ++ $i) {
99848307
 		sayf($ind, '[%d/%d] Symbol at index #%s:', $i + 1, $count, $i);
2d66bea6
 
 		if (!$i) {
 			say($ind + 1, 'Skipping as the symbol table entry for index 0 (STN_UNDEF) is reserved');
 			next;
 		}
 
 		my $offset = $section->{'sh_offset'} + ($i * $section->{'sh_entsize'});
99848307
 		sayf($ind + 1, 'Seeking to offset sh_offset + (%s x sh_entsize) = %s', colored($i, 'index'), formatOffset($offset));
2d66bea6
 		seek($elf_fh, $offset, Fcntl::SEEK_SET);
 
 		# For each field of the current entry...
 		for my $entry (@{$st_info->{'ordered_entries'}}) {
 			# read its value, ...
 			my $value = readELFValue($elf_fh, $st_info->{$entry});
 			$section->{'_symbols'}->{$i}->{$entry} = $value;
 			# and provide a first basic explanation.
4a5e4799
 			say($ind + 1, analyseAndExplainValue($value, $entry, $st_info->{$entry}));
2d66bea6
 
6a1c09e3
 			if ($entry eq 'st_name') {
 				if (!$value) {
4a5e4799
 					sayf($ind + 2, 'This symbol table entry has no name.');
6a1c09e3
 				}
804b6d7e
 				elsif ($strtab) {
4a5e4799
 					my $name = displayStringFromSection($ind + 2, $elf_fh, $elf_data, $strtab, $value);
6a1c09e3
 					if (defined($name) && length($name->{'string'})) {
 						$section->{'_symbols'}->{$i}->{'_name'} = $name->{'string'};
 					}
 				}
 			}
 			elsif ($entry eq 'st_info') {
2d66bea6
 				my $st_bind = ($value >> 4);
 				my $st_type = ($value & 0xf);
4a5e4799
 				say($ind + 2, analyseAndExplainValue($st_bind, 'st_bind', $st_info->{$entry}));
 				say($ind + 2, analyseAndExplainValue($st_type, 'st_type', $st_info->{$entry}));
2d66bea6
 			}
c290b4aa
 			elsif ($entry eq 'st_shndx') {
99848307
 				sayf(
4a5e4799
 					$ind + 2,
99848307
 					'Here, %s is the index of section %s.',
 					colored($value, 'value'),
 					colored($elf_data->{'sections'}->{$value}->{'_name'}, 'name')
c290b4aa
 				) unless (exists($elf->{'st_shndx'}->{'name'}->{$value}));
 			}
 			elsif ($entry eq 'st_value' && $value) {
 				my $type_analysis = analyseValue('e_type', $elf_data->{'e_type'});
 				# Refine analysis based on file type.
 				if ($type_analysis->{'name'} =~ m/ET_EXEC|ET_DYN|ET_REL/) {
 					# Assume ET_EXEC or ET_DYN first.
 					my $extra_explanation1 = 'this file has type ' . colored($type_analysis->{'name'}, 'name');
 					my $extra_explanation2 = 'the virtual address of this symbol, assuming the associated section was loaded in memory at virtual address ' . colored('sh_addr', 'value_name');
 
 					# Overwrite or complete if ET_REL.
 					if ($type_analysis->{'name'} eq 'ET_REL') {
 						# Refine analysis based on st_shndx.
 						if ($section->{'_symbols'}->{$i}->{'st_shndx'} == 0xFFF2) {
 							$extra_explanation1 .= ' and since ' . colored('st_shndx', 'value_name') . ' is ' . colored('SHN_COMMON', 'name');
 							$extra_explanation2 = 'alignment constraints for this symbol'
 						}
 						else {
 							$extra_explanation2 = 'a section offset from the beginning of the section that ' . colored('st_shndx', 'value_name') . ' identifies';
 						}
 					}
 
99848307
 					sayf(
4a5e4799
 						$ind + 2,
99848307
 						'Here, since %s, %s holds %s.',
 						$extra_explanation1,
 						colored('st_value', 'value_name'),
 						$extra_explanation2
c290b4aa
 					);
 				}
2d66bea6
 			}
 		}
4a5e4799
 		# If the symbol has a name, display how to look it up through hash sections.
 		if (exists($section->{'_symbols'}->{$i}->{'_name'})) {
 			displayHashDetailsForSymbol($ind + 1, $elf_fh, $elf_data, $section->{'_symbols'}->{$i}, $i);
 		}
2d66bea6
 	}
 }
 
2b068782
 sub walkHashSection {
 	my $elf_fh = shift;
 	my $elf_data = shift;
 	my $section_index = shift;
 	my $ind = 2;
 
 	my $section = $elf_data->{'sections'}->{$section_index};
 	return if (!defined($section));
 
c0c547f4
 	displayLinkedSection($ind, $elf_data, $section_index);
 
2b068782
 	my $offset = $section->{'sh_offset'};
 	sayf($ind, 'Seeking to offset sh_offset = %s', formatOffset($offset));
 	seek($elf_fh, $offset, Fcntl::SEEK_SET);
 
 	# Analyse the structure of the hash section.
 	my $nbucket = readELFValue($elf_fh, 'Word');
 	my $nchain = readELFValue($elf_fh, 'Word');
 	my $sizeof_word = $elf_data_types->{$default_class}->{'Word'}->{'size'};
 	my $bucket_offset = $offset + (2 * $sizeof_word);
 	my $chain_offset = $bucket_offset + ($nbucket * $sizeof_word);
 
 	# Display these pieces of information.
 	sayf($ind, 'This ELF hash section holds:');
 	sayf(
 		$ind,
 		'  - %s bucket entries, i.e. %d bytes, from %s;',
 		colored($nbucket, 'value'),
 		$nbucket * $sizeof_word,
 		formatOffset($bucket_offset)
 	);
 	sayf(
 		$ind,
 		'  - %s chain entries, i.e. %d bytes, from %s.',
 		colored($nchain, 'value'),
 		$nchain * $sizeof_word,
 		formatOffset($chain_offset)
 	);
 
 	# Store these pieces of information for later use.
 	$elf_data->{'elf_hash'}->{$section_index} = {
 		'name' => $section->{'_name'},
 		'nbucket' => $nbucket,
 		'nchain' => $nchain,
 		'bucket_offset' => $bucket_offset,
 		'chain_offset' => $chain_offset,
 	};
 
 	# Optionally display non-zero bucket/chain entries.
 	# This is not done by default because it is hardly relevant for anybody.
 	if (exists($ENV{'ELFWALK_DISPLAY_ELF_HASH_BUCKET_ENTRIES'})) {
 		sayf($ind, 'Non-zero bucket entries:');
 		for (my $i = 0; $i < $nbucket; ++ $i) {
 			my $value = readELFValue($elf_fh, 'Word');
 			next unless ($value);
 			sayf($ind + 1, '#%d: %s, i.e. %s', $i, colored(sprintf('0x%08x', $value), 'value'), colored(sprintf('%d', $value), 'value'));
 		}
 	}
 	if (exists($ENV{'ELFWALK_DISPLAY_ELF_HASH_CHAIN_ENTRIES'})) {
 		sayf($ind, 'Non-zero chain entries:');
 		for (my $i = 0; $i < $nchain; ++ $i) {
 			my $value = readELFValue($elf_fh, 'Word');
 			next unless ($value);
 			sayf($ind + 1, '#%d: %s, i.e. %s', $i, colored(sprintf('0x%08x', $value), 'value'), colored(sprintf('%d', $value), 'value'));
 		}
 	}
 }
 
f5e2f5ed
 sub walkGnuHashSection {
 	my $elf_fh = shift;
 	my $elf_data = shift;
 	my $section_index = shift;
 	my $ind = 2;
 
 	my $section = $elf_data->{'sections'}->{$section_index};
 	return if (!defined($section));
 
 	my $linked_section = displayLinkedSection($ind, $elf_data, $section_index);
 	my $symbols_count = 0;
 	if (defined($linked_section)) {
 		$symbols_count = $linked_section->{'sh_size'} / $linked_section->{'sh_entsize'};
 	}
 
 	my $offset = $section->{'sh_offset'};
 	sayf($ind, 'Seeking to offset sh_offset = %s', formatOffset($offset));
 	seek($elf_fh, $offset, Fcntl::SEEK_SET);
 
 	# Analyse the header of the hash section.
 	sayf($ind, 'This GNU hash section holds:');
 	sayf($ind, '  - a header, made of 4 32-bit words:');
 	my $gnu_hash = {
 		'_index' => $section_index,
 		'name' => $section->{'_name'},
 	};
 	for my $entry (@{$elf_structures->{'gnu_hash'}->{'ordered_entries'}}) {
 		my $type = $elf_structures->{'gnu_hash'}->{$entry};
 		my $value = $gnu_hash->{$entry} = readELFValue($elf_fh, $type);
 		say($ind + 2, analyseAndExplainValue($value, $entry, $type));
 
 		if ($entry eq 'maskwords') {
 			my $details = 'This value must be a power of two.';
 			if (!isPowerOfTwo($value)) {
 				sayf($ind + 1, colored($details, 'error'));
 			}
 		}
 	}
 
 	# Compute extra values related to the Bloom filter/mask.
 	$gnu_hash->{'sizeof_maskword'} = $default_class * 4;
 	$gnu_hash->{'mask_offset'} = $offset + (4 * typeInfo('Word')->{'size'});
 	$gnu_hash->{'mask_size'} = $gnu_hash->{'maskwords'} * $gnu_hash->{'sizeof_maskword'};
 
 	# Compute extra values related to the buckets.
 	$gnu_hash->{'bucket_offset'} = $gnu_hash->{'mask_offset'} + $gnu_hash->{'mask_size'};
 	$gnu_hash->{'bucket_size'} = $gnu_hash->{'nbuckets'} * 4;
 
 	# Compute extra values related to the chains.
 	$gnu_hash->{'chain_offset'} = $gnu_hash->{'bucket_offset'} + $gnu_hash->{'bucket_size'};
 	$gnu_hash->{'chain_count'} = $symbols_count - $gnu_hash->{'symndx'};
 	$gnu_hash->{'chain_size'} = $gnu_hash->{'nbuckets'} * 4;
 
 	# Display these pieces of information.
 	sayf(
 		$ind,
 		'  - a Bloom filter, %s %d-bit words long, i.e. %s bytes long, from %s; this filter is used to rapidly reject attempts to look up symbols that do not exist in the object',
 		colored($gnu_hash->{'maskwords'}, 'value'),
 		$default_class * 32,
 		$gnu_hash->{'mask_size'},
 		formatOffset($gnu_hash->{'mask_offset'})
 	);
 	sayf(
 		$ind,
 		'  - an array of %s 32-bit hash buckets, i.e. %d bytes, from %s.',
 		colored($gnu_hash->{'nbuckets'}, 'value'),
 		$gnu_hash->{'bucket_size'},
 		formatOffset($gnu_hash->{'bucket_offset'})
 	);
 	sayf(
 		$ind,
 		'  - an array of %s (%d symbols in %s - %s) 32-bit hash chain values, one per dynamic symbol that is to be accessible from the hash table, i.e. %d bytes, from %s',
 		colored($gnu_hash->{'chain_count'}, 'value'),
 		$symbols_count,
 		colored($linked_section->{'_name'}, 'name'),
 		colored('symndx', 'value_name'),
 		$gnu_hash->{'chain_size'},
 		formatOffset($gnu_hash->{'chain_offset'})
 	);
 
 	# Store these pieces of information for later use.
 	$elf_data->{'gnu_hash'}->{$section_index} = $gnu_hash;
 }
 
c79a49b5
 sub walkRela {
 	my $elf_fh = shift;
 	my $elf_data = shift;
 	my $section_index = shift;
 	my $ind = 2;
 
 	my $section = $elf_data->{'sections'}->{$section_index};
 	return if (!defined($section));
 
 	# How many entries do we intend to read?
 	my $count = $section->{'sh_size'} / $section->{'sh_entsize'};
 
 	# What kind of entries do we intend to read?
 	my $arch = $elf_data->{'EI_CLASS'};
 	my $struct = ($section->{'_type'} eq 'SHT_RELA') ? 'rela' : 'rel';
 	my $rela_info = $elf_structures->{$struct}->{$arch};
 
6957e524
 	# Explain sh_link.
d412c185
 	my $dynsym_index = undef;
6957e524
 	my $linked_section = displayLinkedSection($ind, $elf_data, $section_index);
 	if (defined($linked_section) && $linked_section->{'_type'} eq 'SHT_DYNSYM') {
 		sayf($ind + 1, 'This is the associated symbol table.');
d412c185
 		$dynsym_index = $linked_section->{'_index'};
6957e524
 	}
 
 	# Explain sh_info.
 	my $target_section = getLinkedSection($elf_data, $section_index, 'sh_info');
 	if (defined($target_section)) {
 		sayf(
 			$ind,
 			'Relocations described in this section apply to section #%s: %s, of type %s.',
 			colored($target_section->{'_index'}, 'index'),
 			colored($target_section->{'_name'}, 'name'),
 			colored($target_section->{'_type'}, 'name')
 		);
 	}
 
c79a49b5
 	# Iterate over relocation entries.
 	for (my $i = 0; $i < $count; ++ $i) {
99848307
 		sayf($ind, '[%d/%d] Relocation at index #%s:', $i + 1, $count, $i);
c79a49b5
 
 		my $offset = $section->{'sh_offset'} + ($i * $section->{'sh_entsize'});
99848307
 		sayf($ind + 1, 'Seeking to offset sh_offset + (%s x sh_entsize) = %s', colored($i, 'index'), formatOffset($offset));
c79a49b5
 		seek($elf_fh, $offset, Fcntl::SEEK_SET);
 
 		# For each field of the current entry...
 		for my $entry (@{$rela_info->{'ordered_entries'}}) {
 			# read its value, ...
 			my $value = readELFValue($elf_fh, $rela_info->{$entry});
 			$section->{'_relocations'}->{$i}->{$entry} = $value;
 			# and provide a first basic explanation.
4a5e4799
 			say($ind + 1, analyseAndExplainValue($value, $entry, $rela_info->{$entry}));
c79a49b5
 
 			if ($entry eq 'r_info') {
 				# Extract r_sym from r_info.
 				my $r_sym = ($value >> ($arch == ELFCLASS32 ? 8 : 32));
 				$section->{'_relocations'}->{$i}->{'r_sym'} = $r_sym;
4a5e4799
 				say($ind + 2, analyseAndExplainValue($r_sym, 'r_sym', $rela_info->{$entry}));
c79a49b5
 
ec14e72e
 				if ($r_sym) {
 					# Get the name of the associated symbol.
d412c185
 					my $symbol_name = symbolNameFromIndex($elf_data, $r_sym, $dynsym_index);
ec14e72e
 					$section->{'_relocations'}->{$i}->{'_symbol'} = $symbol_name;
 					sayf($ind + 3, 'Here, %s is the index of symbol %s.', colored($r_sym, 'value'), colored($symbol_name, 'name'));
 				}
 				else {
 					sayf($ind + 3, 'Here, the index is STN_UNDEF, so the relocation uses zero as the symbol value.');
 				}
c79a49b5
 
 				# Extract r_type from r_info.
 				my $r_type = ($arch == ELFCLASS32 ? ($value & 0xFF) : ($value & 0xFFFFFFFF));
 				$section->{'_relocations'}->{$i}->{'r_type'} = $r_sym;
4a5e4799
 				say($ind + 2, analyseAndExplainValue($r_type, 'r_type', $rela_info->{$entry}, undef, $elf_data->{'e_machine'}));
c79a49b5
 			}
 		}
 	}
 }
 
acda50f6
 sub walkDynamic {
 	my $elf_fh = shift;
 	my $elf_data = shift;
 	my $section_index = shift;
4a5e4799
 	my $ind = 2;
acda50f6
 
 	my $section = $elf_data->{'sections'}->{$section_index};
 	return if (!defined($section));
 
bd29dc9d
 	# Where should we read names from?
9cfb0abd
 	my $strtab = displayStringTableForSection($ind, $elf_data, $section_index, 'Library names');
bd29dc9d
 
acda50f6
 	# What kind of entries do we intend to read?
 	my $arch = $elf_data->{'EI_CLASS'};
 	my $dyn_info = $elf_structures->{'dynamic'}->{$arch};
 
 	my $offset = $section->{'sh_offset'};
4a5e4799
 	sayf($ind, 'Seeking to offset sh_offset = %s', formatOffset($offset));
acda50f6
 	seek($elf_fh, $offset, Fcntl::SEEK_SET);
 
4229dad6
 	my $dyn_section = $elf_data->{'dynamic'}->{$section_index} = {};
acda50f6
 	# Read expected fields: namesz, descsz, type.
 	my $i = 0;
 	dynamic_array: while (1) {
 		dynamic_entry: for my $entry (@{$dyn_info->{'ordered_entries'}}) {
 			my $value = readELFValue($elf_fh, $dyn_info->{$entry});
 			$dyn_section->{$i}->{$entry} = $value;
4a5e4799
 			say($ind, analyseAndExplainValue($value, $entry, $dyn_info->{$entry}));
acda50f6
 
 			if ($entry eq 'd_un') {
 				my $tag_value = $dyn_section->{$i}->{'d_tag'};
 				if (!$tag_value) { # DT_NULL
 					# An entry with a DT_NULL tag marks the end of the _DYNAMIC array.
 					last dynamic_array;
 				}
ba847fcb
 				elsif ($tag_value == 1 || $tag_value == 14 || $tag_value == 15 || $tag_value == 29) {
 					# DT_NEEDED, DT_SONAME, DT_RPATH or DT_RUNPATH
acda50f6
 					# "The offset is an index into the table recorded in the DT_STRTAB code."
 					# ... except we are liable to read a DT_NEEDED entry before
 					# stumbling upon the DT_STRTAB one.
bd29dc9d
 					# As DT_STRTAB is supposed to point to a SHT_STRTAB section,
 					# typically .dynstr, we can assume everything will be fine
 					# if we rely on the linked strtab.
 					if ($strtab) {
 						displayStringFromSection($ind + 1, $elf_fh, $elf_data, $strtab, $value);
acda50f6
 					}
 				}
 				elsif ($tag_value == 20) { # DT_PLTREL
 					my $meaning;
 					if ($value == 17) { # DT_REL
 						$meaning = 'means DT_REL';
 					}
 					elsif ($value == 7) { # DT_RELA
 						$meaning = 'means DT_RELA';
 					}
 					else {
 						$meaning = 'should be either DT_REL or DT_RELA.'
 					}
 					$value = sprintf('%d', $value);
 					$value = colored($value, 'value');
4a5e4799
 					sayf($ind + 1, 'Here, %s %s.', $value, $meaning);
acda50f6
 				}
 				elsif ($tag_value == 30) { # DT_FLAGS
4a5e4799
 					say($ind + 1, analyseAndExplainValue($value, 'dt_flags', $dyn_info->{'d_un'}));
acda50f6
 				}
 				elsif ($tag_value == 0x6FFFFFFB) { # DT_FLAGS_1
4a5e4799
 					say($ind + 1, analyseAndExplainValue($value, 'dt_flags_1', $dyn_info->{'d_un'}));
acda50f6
 				}
 			}
 		}
 		++ $i;
 	}
 }
 
0cfc53d2
 sub walkVerdef {
 	my $elf_fh = shift;
 	my $elf_data = shift;
 	my $section_index = shift;
 	my $ind = 2;
 
 	my $section = $elf_data->{'sections'}->{$section_index};
 	return if (!defined($section));
 
 	# How many entries do we intend to read?
 	# Answer from the LSB: "The number of entries in this section shall be
 	# contained in the DT_VERDEFNUM entry of the Dynamic Section .dynamic."
 	my @dt_verdefnum_entries = dynamicEntries($elf_data, 0x6ffffffd); # DT_VERDEFNUM
 	if (!scalar(@dt_verdefnum_entries)) {
 		say($ind, colored('Unable to find the DT_VERDEFNUM entry in .dynamic, aborting the parsing of this section.', 'error'));
 		return;
 	}
 	my $dt_verdefnum = shift(@dt_verdefnum_entries);
 	sayf($ind, 'This section contains DT_VERDEFNUM = %d entries.', $dt_verdefnum);
 
 	# Where should we read names from?
9cfb0abd
 	my $strtab = displayStringTableForSection($ind, $elf_data, $section_index, 'Version strings and library names');
0cfc53d2
 
 	my $offset = $section->{'sh_offset'};
 	sayf($ind, 'Seeking to offset sh_offset = %s', formatOffset($offset));
 	seek($elf_fh, $offset, Fcntl::SEEK_SET);
 
 	for (my $i = 0; $i < $dt_verdefnum; ++ $i) {
 		my $verdef = {};
 
 		# Parse Elfxx_Verdef
4a5e4799
 		sayf($ind, '[%d/%d] Version definition entry #%d:', $i + 1, $dt_verdefnum, $i);
0cfc53d2
 		for my $entry (@{$elf_structures->{'verdef'}->{'ordered_entries'}}) {
 			my $type = $elf_structures->{'verdef'}->{$entry};
 			my $value = readELFValue($elf_fh, $type);
4a5e4799
 			say($ind + 1, analyseAndExplainValue($value, $entry, $type));
0cfc53d2
 			$verdef->{$entry} = $value;
 		}
 
 		if ($verdef->{'vd_cnt'} > 0) {
 			for (my $j = 0; $j < $verdef->{'vd_cnt'}; ++ $j) {
4a5e4799
 				sayf($ind + 1, '[%d/%d] Version definition auxiliary entry #%d:', $j + 1, $verdef->{'vd_cnt'}, $j);
0cfc53d2
 				for my $entry (@{$elf_structures->{'verdaux'}->{'ordered_entries'}}) {
 					my $type = $elf_structures->{'verdaux'}->{$entry};
 					my $value = readELFValue($elf_fh, $type);
4a5e4799
 					say($ind + 2, analyseAndExplainValue($value, $entry, $type));
0cfc53d2
 					$verdef->{'verdaux'}->{$j}->{$entry} = $value;
 					if ($entry eq 'vda_name' && $strtab) {
4a5e4799
 						my $name = displayStringFromSection($ind + 3, $elf_fh, $elf_data, $strtab, $value);
0cfc53d2
 						if (defined($name) && length($name->{'string'})) {
 							$verdef->{'verdaux'}->{$j}->{'name'} = $name->{'string'};
 						}
 					}
 				}
 			}
 		}
 		my $verdef_index = $verdef->{'vd_ndx'};
 		$elf_data->{'verdef'}->{$section_index}->{$verdef_index} = $verdef;
 	}
 }
 
888be9b2
 sub walkVerneed {
 	my $elf_fh = shift;
 	my $elf_data = shift;
 	my $section_index = shift;
 	my $ind = 2;
 
 	my $section = $elf_data->{'sections'}->{$section_index};
 	return if (!defined($section));
 
 	# How many entries do we intend to read?
 	# Answer from the LSB: "The number of entries in this section shall be
 	# contained in the DT_VERNEEDNUM entry of the Dynamic Section .dynamic."
 	my @dt_verneednum_entries = dynamicEntries($elf_data, 0x6fffffff); # DT_VERNEEDNUM
 	if (!scalar(@dt_verneednum_entries)) {
4a5e4799
 		say($ind, colored('Unable to find the DT_VERNEEDNUM entry in .dynamic, aborting the parsing of this section.', 'error'));
888be9b2
 		return;
 	}
 	my $dt_verneednum = shift(@dt_verneednum_entries);
 	sayf($ind, 'This section contains DT_VERNEEDNUM = %d entries.', $dt_verneednum);
 
 	# Where should we read names from?
9cfb0abd
 	my $strtab = displayStringTableForSection($ind, $elf_data, $section_index, 'Version strings and library names');
888be9b2
 
 	my $offset = $section->{'sh_offset'};
 	sayf($ind, 'Seeking to offset sh_offset = %s', formatOffset($offset));
 	seek($elf_fh, $offset, Fcntl::SEEK_SET);
 
 	for (my $i = 0; $i < $dt_verneednum; ++ $i) {
 		my $verneed = {};
 
 		# Parse Elfxx_Verneed
4a5e4799
 		sayf($ind, '[%d/%d] Version requirements entry #%d:', $i + 1, $dt_verneednum, $i);
888be9b2
 		for my $entry (@{$elf_structures->{'verneed'}->{'ordered_entries'}}) {
 			my $type = $elf_structures->{'verneed'}->{$entry};
 			my $value = readELFValue($elf_fh, $type);
4a5e4799
 			say($ind + 1, analyseAndExplainValue($value, $entry, $type));
888be9b2
 			$verneed->{$entry} = $value;
 			if ($entry eq 'vn_file') {
4a5e4799
 				my $name = displayStringFromSection($ind + 2, $elf_fh, $elf_data, $strtab, $value);
888be9b2
 				if (defined($name) && length($name->{'string'})) {
 					$verneed->{'file'} = $name->{'string'};
 				}
 			}
 		}
 		$elf_data->{'verneed'}->{$section_index}->{$i} = $verneed;
 
 		if ($verneed->{'vn_cnt'} > 0) {
 			for (my $j = 0; $j < $verneed->{'vn_cnt'}; ++ $j) {
 				my $vernaux = {};
4a5e4799
 				sayf($ind + 1, '[%d/%d] Version requirements auxiliary entry #%d:', $j + 1, $verneed->{'vn_cnt'}, $j);
888be9b2
 				for my $entry (@{$elf_structures->{'vernaux'}->{'ordered_entries'}}) {
 					my $type = $elf_structures->{'vernaux'}->{$entry};
 					my $value = readELFValue($elf_fh, $type);
4a5e4799
 					say($ind + 2, analyseAndExplainValue($value, $entry, $type));
888be9b2
 					$vernaux->{$entry} = $value;
 					if ($entry eq 'vna_name' && $strtab) {
4a5e4799
 						my $name = displayStringFromSection($ind + 3, $elf_fh, $elf_data, $strtab, $value);
888be9b2
 						if (defined($name) && length($name->{'string'})) {
 							$vernaux->{'name'} = $name->{'string'};
 						}
 					}
 				}
 				# Attach vernaux entries to their parent verneed entry.
 				$verneed->{'vernaux'}->{$j} = $vernaux;
 				# Keep a reference to the parent verneed entry.
 				$vernaux->{'verneed'} = $verneed;
 				# Store vernaux entries in $elf_data indexed by their vna_other
 				# value as this is the equivalent of vd_ndx.
 				my $vernaux_index = $vernaux->{'vna_other'};
 				$elf_data->{'vernaux'}->{$section_index}->{$vernaux_index} = $vernaux;
 			}
 		}
 	}
 }
 
2487ca67
 sub walkVersym {
 	my $elf_fh = shift;
 	my $elf_data = shift;
 	my $section_index = shift;
4a5e4799
 	my $ind = 2;
2487ca67
 
 	my $section = $elf_data->{'sections'}->{$section_index};
 	return if (!defined($section));
 
57db1a31
 	my $dynsym = displayLinkedSection($ind, $elf_data, $section_index);
 	my $dynsym_index = defined($dynsym) ? $dynsym->{'_index'} : undef;
 
2487ca67
 	# How many entries do we intend to read?
 	my $count = $section->{'sh_size'} / $section->{'sh_entsize'};
 	# Note: sh_entsize is expected to be 2 bytes (16 bits) as the LSB states
 	# that "The .gnu.version section shall contain an array of elements of
 	# type Elfxx_Half."
 
 	my $offset = $section->{'sh_offset'};
4a5e4799
 	sayf($ind, 'Seeking to offset sh_offset = %s', formatOffset($offset));
2487ca67
 	seek($elf_fh, $offset, Fcntl::SEEK_SET);
 
 	# Iterate over entries of the Symbol Version Table.
 	for (my $i = 0; $i < $count; ++ $i) {
4a5e4799
 		sayf($ind, '[%d/%d] symbol version entry at index #%s:', $i + 1, $count, $i);
2487ca67
 		if (!$i) {
79acf947
 			readELFValue($elf_fh, 'Half');
4a5e4799
 			say($ind + 1, 'Skipping as the symbol table entry for index 0 (STN_UNDEF) is reserved');
2487ca67
 			next;
 		}
 
57db1a31
 		my $symbol = symbolFromIndex($elf_data, $i, $dynsym_index);
2487ca67
 
 		my $value = readELFValue($elf_fh, 'Half');
 		my $printable_value = colored(formatValue($value, 'Half'), 'value');
 
 		my $explanation = 'symbol ' . colored($symbol->{'_name'}, 'name');
 		if (!$value) {
 			$explanation .= ' is local, not available outside this ELF object';
 		}
 		elsif ($value == 1) {
 			$explanation .= ' is defined in this object and is globally available';
 		}
 		else {
 			# "Bit number 15 controls whether or not the object is hidden; if
 			# this bit is set, the object cannot be used and the static linker
 			# will ignore the symbol's presence in the object."
 			if (($value & 0x8000) == 0x8000) {
 				$explanation .= ' is marked as hidden (0x8000) and';
 				# Remove bit #15 prior to the vernaux / verdef lookup.
 				$value &= 0x7FFF;
 			}
 
 			# Check requirements (vernaux entries).
 			my $vernaux;
 			for my $i (keys(%{$elf_data->{'vernaux'}})) {
 				if (exists($elf_data->{'vernaux'}->{$i}->{$value})) {
 					$vernaux = $elf_data->{'vernaux'}->{$i}->{$value};
 					$vernaux = formatVersionRequirement($vernaux);
 					$vernaux = colored($vernaux, 'name');
 					$explanation .= ' requires version ' . $vernaux;
 					goto explained;
 				}
 			}
 
 			# Otherwise, we must display its version _definition_.
 			my $verdef;
 			for my $i (keys(%{$elf_data->{'verdef'}})) {
 				if (exists($elf_data->{'verdef'}->{$i}->{$value})) {
 					$verdef = $elf_data->{'verdef'}->{$i}->{$value};
 					$verdef = formatVersionDefinition($verdef);
 					$verdef = colored($verdef, 'name');
 					$explanation .= ' is provided for version ' . $verdef;
 					goto explained;
 				}
 			}
 		}
 		explained:
4a5e4799
 		sayf($ind + 1, '%s: %s.', $printable_value, $explanation);
2487ca67
 	}
 }
 
583987eb
 sub walkNote {
 	my $elf_fh = shift;
 	my $elf_data = shift;
 	my $section_index = shift;
4a5e4799
 	my $ind = 2;
583987eb
 
 	my $section = $elf_data->{'sections'}->{$section_index};
 	return if (!defined($section));
 
 	# What kind of entries do we intend to read?
 	my $arch = $elf_data->{'EI_CLASS'};
 	my $note_info = $elf_structures->{'note'}->{$arch};
 
 	my $offset = $section->{'sh_offset'};
4a5e4799
 	sayf($ind, 'Seeking to offset sh_offset = %s', formatOffset($offset));
583987eb
 	seek($elf_fh, $offset, Fcntl::SEEK_SET);
 
 	# Read expected fields: namesz, descsz, type.
 	for my $entry (@{$note_info->{'ordered_entries'}}) {
 		my $value = readELFValue($elf_fh, $note_info->{$entry});
 		$elf_data->{'_notes'}->{$section_index}->{$entry} = $value;
4a5e4799
 		say($ind, analyseAndExplainValue($value, $entry, $note_info->{$entry}));
583987eb
 	}
 
 	# Read the name.
 	my $name_size = $elf_data->{'_notes'}->{$section_index}->{'namesz'};
 	if ($name_size) {
 		my $name = readNullTerminatedString($elf_fh, $name_size);
4a5e4799
 		sayf($ind, '%s is "%s".', colored('name', 'value_name'), colored($name, 'value'));
583987eb
 	}
 	else {
4a5e4799
 		sayf($ind, '%s will not be read as %s is 0.', colored('name', 'value_name'), colored('namesz', 'value_name'));
583987eb
 	}
 
 	# Cross potential padding: "Padding is present, if necessary, to ensure
 	# 4-byte alignment for the descriptor. Such padding is not included in
 	# namesz."
 	my $cur_pos = my $new_pos = tell($elf_fh);
 	++ $new_pos while ($new_pos % 4);
 	if ($new_pos != $cur_pos) {
4a5e4799
 		sayf($ind, 'Seeking to offset %s to cross the padding', formatOffset($new_pos));
583987eb
 		seek($elf_fh, $new_pos, Fcntl::SEEK_SET);
 	}
 
 	# Read the descriptor.
 	if ($section->{'_name'} eq '.note.ABI-tag') {
4a5e4799
 		sayf($ind, 'The note section being named %s, we know how to interpret the descriptor:', colored('.note.ABI-tag', 'name'));
583987eb
 		my $value = readELFValue($elf_fh, 'Word');
4a5e4799
 		sayf($ind + 1, '%s: this is expected to be 0, signifying a Linux executable.', colored(formatValue($value, 'Word'), 'value'));
583987eb
 		my $kv1 = readELFValue($elf_fh, 'Word');
4a5e4799
 		sayf($ind + 1, '%s: this is the earliest kernel version compatible with this file', colored(formatValue($kv1, 'Word'), 'value'));
583987eb
 		my $kv2 = readELFValue($elf_fh, 'Word');
4a5e4799
 		sayf($ind + 1, '%s: this is the earliest kernel major revision compatible with this file', colored(formatValue($kv2, 'Word'), 'value'));
583987eb
 		my $kv3 = readELFValue($elf_fh, 'Word');
4a5e4799
 		sayf($ind + 1, '%s: this is the earliest kernel minor revision compatible with this file', colored(formatValue($kv3, 'Word'), 'value'));
583987eb
 		$elf_data->{'_kernel'} = sprintf('%d.%d.%d', $kv1, $kv2, $kv3);
4a5e4799
 		sayf($ind, 'Basically, this note states this file requires at least Linux kernel %s.', colored($elf_data->{'_kernel'}, 'value'));
583987eb
 	}
 	elsif ($section->{'_name'} eq '.note.gnu.build-id') {
 		my $desc_size = $elf_data->{'_notes'}->{$section_index}->{'descsz'};
4a5e4799
 		sayf($ind, 'The note section being named %s, we know how to interpret the descriptor:', colored('.note.gnu.build-id', 'name'));
 		sayf($ind + 1, 'This descriptor is expected to host a unique build ID, for debugging purposes.');
583987eb
 		# I took the same heuristic as "file", with a minimal amount of shame.
 		my $id_type = ($desc_size == 16) ? 'a MD5 hash or a UUID' : 'a SHA1 hash';
4a5e4799
 		sayf($ind + 1, 'This descriptor being %d bytes, it is very likely to host %s.', $desc_size, $id_type);
583987eb
 		my $build_id = '';
 		for (my $i = 0; $i < $desc_size; ++ $i) {
 			my $byte = readELFValue($elf_fh, 'char');
 			$build_id .=  sprintf('%02x', $byte);
 		}
4a5e4799
 		sayf($ind + 1, 'The build ID of this file is: %s.', colored($build_id, 'value'));
583987eb
 	}
 }
 
47dc4fea
 sub walkProgBits {
 	my $elf_fh = shift;
 	my $elf_data = shift;
 	my $section_index = shift;
4a5e4799
 	my $ind = 2;
47dc4fea
 
 	my $section = $elf_data->{'sections'}->{$section_index};
 	return if (!defined($section));
 
 	my $offset = $section->{'sh_offset'};
4a5e4799
 	sayf($ind, 'Seeking to offset sh_offset = %s', formatOffset($offset));
47dc4fea
 	seek($elf_fh, $offset, Fcntl::SEEK_SET);
 
 	if ($section->{'_name'} eq '.interp') {
 		my $interp = readNullTerminatedString($elf_fh, $section->{'sh_size'});
 		if (length($interp)) {
 			$elf_data->{'.interp'} = $interp;
4a5e4799
 			sayf($ind, 'This ELF file shall be interpreted using %s.', colored($interp, 'value'));
47dc4fea
 		}
 		else {
4a5e4799
 			sayf($ind, 'Error: unable to retrieve the path of the interpreter.');
47dc4fea
 		}
 	}
 }
 
a09248ae
 sub walkELFProgramHeader {
 	my $elf_fh = shift;
 	my $elf_data = shift;
 
 	say(0, colored('Parsing program header', 'main_action'));
 	my $ind = 1;
 
 	my $arch = $elf_data->{'EI_CLASS'};
 	my $ph_info = $elf_structures->{'program_header'}->{$arch};
 
 	for (my $i = 0; $i < $elf_data->{'e_phnum'}; ++ $i) {
18f4b918
 		announceOperation($ind, 'Parsing segment', $i, ':');
a09248ae
 
 		my $offset = $elf_data->{'e_phoff'} + ($i * $elf_data->{'e_phentsize'});
99848307
 		sayf(
a09248ae
 			$ind + 1,
99848307
 			'Seeking to offset e_phoff + (%s x e_phentsize) = %s',
 			colored($i, 'index'),
 			formatOffset($offset)
a09248ae
 		);
 		seek($elf_fh, $offset, Fcntl::SEEK_SET);
 
 		for my $entry (@{$ph_info->{'ordered_entries'}}) {
 			my $value = readELFValue($elf_fh, $ph_info->{$entry});
 			$elf_data->{'program_header'}->{$i}->{$entry} = $value;
 			say($ind + 1, analyseAndExplainValue($value, $entry, $ph_info->{$entry}));
 
 			if ($entry eq 'p_align') {
 				my $comment;
 				if ($value == 0 || $value == 1) {
 					# Values 0 and 1 mean no alignment is required.
99848307
 					sayf($ind + 2, 'Here, %s means no alignment is required.', colored($value, 'value'));
a09248ae
 				}
 				else {
 					# Otherwise, p_align should be a positive, integral power
 					# of 2,
 					$comment = 'Here, ' . colored($value, 'value') . ' ';
 					$comment .= isPowerOfTwo($value) ? 'is' : 'should be';
 					$comment .= ' a power of two, as expected by the SysV ABI.';
 					say($ind + 2, $comment);
 
 					# ... and p_vaddr should equal p_offset, modulo p_align.
 					my $p_vaddr  = $elf_data->{'program_header'}->{$i}->{'p_vaddr'};
 					my $p_offset = $elf_data->{'program_header'}->{$i}->{'p_offset'};
 					my $is_aligned = (($p_vaddr - $p_offset) % $value);
 					$comment = $is_aligned ? 'should equal' : 'equals';
99848307
 					sayf(
 						$ind + 2,
a09248ae
 						'Also, %s %s %s, modulo %s, as expected by the SysV ABI.',
 						colored('p_vaddr', 'value_name'),
 						$comment,
 						colored('p_offset', 'value_name'),
 						colored('p_align', 'value_name')
 					);
 				}
 			}
3f3f0122
 			elsif ($entry eq 'p_flags') {
 				# Focus on the lowest 3 bits.
 				my $perm_flags = ($value & 0x7);
 				# This hash associates perm sets with extra, allowable perms.
 				my $allowable = $elf->{'p_flags'}->{'_allowable'};
 				sayf(
 					$ind + 2,
 					'ABI-conforming systems may also provide %s.',
 					$allowable->{$perm_flags}
 				) if (exists($allowable->{$perm_flags}));
 			}
a045164c
 			elsif ($entry eq 'p_filesz') {
 				my $segment = $elf_data->{'program_header'}->{$i};
 				if (getSectionsInSegment($elf_data, $i)) {
 					say($ind + 2, 'This segment covers the following sections:');
 					for my $i (@{$segment->{'_sections'}}) {
 						my $section_name = $elf_data->{'sections'}->{$i}->{'_name'};
 						sayf(
 							$ind + 3,
 							'- section #%s: %s',
 							colored($i, 'index'),
 							colored($section_name, 'name')
 						);
 					}
 				}
 			}
 		}
 	}
 }
 
 sub getSectionsInSegment {
 	my $elf_data = shift;
 	my $segment_index = shift;
 
 	my $segment = $elf_data->{'program_header'}->{$segment_index};
 	$segment->{'_sections'} = [];
 
 	# Iterate over known sections...
 	for (my $i = 1; $i < $elf_data->{'e_shnum'}; ++ $i) {
 		my $section = $elf_data->{'sections'}->{$i};
 		# to check whether they are included in the current segment.
 		if (segmentIncludesSection($segment, $section)) {
 			push(@{$segment->{'_sections'}}, $i);
a09248ae
 		}
 	}
a045164c
 	return scalar(@{$segment->{'_sections'}});
 }
 
 sub sectionEnd {
 	my $section = shift;
 
 	my $section_end = $section->{'sh_offset'};
 	if ($section->{'_type'} ne 'SHT_NOBITS') {
 		$section_end += $section->{'sh_size'};
 	}
 	return $section_end;
a09248ae
 }
 
78111860
 sub sectionOffset {
 	my $elf_data = shift;
 	my $section_index = shift;
 
 	return undef if ($section_index < 0);
 	return undef if ($section_index >= $elf_data->{'e_shnum'});
 	return $elf_data->{'e_shoff'} + ($section_index * $elf_data->{'e_shentsize'});
 }
 
a045164c
 sub segmentIncludesSection {
 	my $segment = shift;
 	my $section = shift;
 
 	my $section_start = $section->{'sh_offset'};
 	my $section_end = sectionEnd($section);
 
 	my $segment_start = $segment->{'p_offset'};
 	my $segment_end = $segment_start + $segment->{'p_filesz'};
 
 	my $segment_includes_section = (
 		$segment_start <= $section_start &&
 		$section_end <= $segment_end
 	);
 	return $segment_includes_section;
 }
 
e35676f7
 sub symbolFromIndex {
f94bfd81
 	my $elf_data = shift;
 	my $index = shift;
715e6988
 	my $dynsym_index = shift;
f94bfd81
 
 	for my $section_index (@{$elf_data->{'SHT_DYNSYM'}}) {
715e6988
 		if (defined($dynsym_index)) {
 			next if ($section_index != $dynsym_index);
 		}
e35676f7
 		if (exists($elf_data->{'sections'}->{$section_index}->{'_symbols'}->{$index})) {
 			return $elf_data->{'sections'}->{$section_index}->{'_symbols'}->{$index};
f94bfd81
 		}
 	}
 	return undef;
 }
 
e35676f7
 sub symbolNameFromIndex {
 	my $elf_data = shift;
 	my $index = shift;
715e6988
 	my $dynsym_index = shift;
e35676f7
 
715e6988
 	my $symbol = symbolFromIndex($elf_data, $index, $dynsym_index);
e35676f7
 	return defined($symbol) ? $symbol->{'_name'} : undef;
 }
 
14d87bde
 sub dynamicEntries {
 	my $elf_data = shift;
 	my $entry_tag = shift;
 
 	my @entries;
 	for my $dynsec_index (keys(%{$elf_data->{'dynamic'}})) {
 		my $dynsec = $elf_data->{'dynamic'}->{$dynsec_index};
 		for my $i (keys(%{$dynsec})) {
 			if ($dynsec->{$i}->{'d_tag'} == $entry_tag) {
 				push(@entries, $dynsec->{$i}->{'d_un'});
 			}
 		}
 	}
 	return @entries;
 }
 
be02f9bb
 sub displayHashDetailsForSymbol {
 	my $ind = shift;
 	my $elf_fh = shift;
 	my $elf_data = shift;
 	my $symbol = shift;
 	my $symbol_index = shift;
 
 	# Backup the position of the file handler as we are going to mess with it.
 	my $pos = tell($elf_fh);
 
 	my $sizeof_word = $elf_data_types->{$default_class}->{'Word'}->{'size'};
 
 	if (exists($elf_data->{'elf_hash'})) {
 		for my $hash_section_index (keys(%{$elf_data->{'elf_hash'}})) {
 			my $hash_section = $elf_data->{'elf_hash'}->{$hash_section_index};
 			sayf(
 				$ind,
 				'Path to reach that symbol index (i.e. #%d) from its name through hash section %s:',
 				$symbol_index,
 				colored($hash_section->{'name'}, 'name')
 			);
 
 			my $elf_hash = hashSymbolName($symbol->{'_name'});
 			sayf($ind + 1, 'ELF hash: %s', colored(sprintf('0x%08x', $elf_hash), 'value'));
 
 			my $bucket_entry_index = $elf_hash % $hash_section->{'nbucket'};
 			my $bucket_entry_offset = $hash_section->{'bucket_offset'} + ($bucket_entry_index * $sizeof_word);
 			seek($elf_fh, $bucket_entry_offset, Fcntl::SEEK_SET);
 			my $bucket_entry_value = readELFValue($elf_fh, 'Word');
 
 			sayf(
 				$ind + 1,
 				'index y = bucket[ELF hash %% nbucket] = bucket[%s %% %s] = bucket[%s] = %s',
 				colored(sprintf('0x%08x', $elf_hash), 'value'),
 				colored($hash_section->{'nbucket'}, 'value'),
 				colored($bucket_entry_index, 'value'),
 				colored($bucket_entry_value, 'value')
 			);
 
 			for (my $i = 0, my $index = $bucket_entry_value; $index != $symbol_index && $i < 100; ++ $i) {
 				my $chain_entry_offset = $hash_section->{'chain_offset'} + ($index * $sizeof_word);
 				seek($elf_fh, $chain_entry_offset, Fcntl::SEEK_SET);
 				my $chain_entry_value = readELFValue($elf_fh, 'Word');
 				sayf(
 					$ind + 1,
 					'chain[y] = chain[%s] = %s%s',
 					colored($index, 'value'),
 					colored($chain_entry_value, 'value'),
 					($chain_entry_value == $symbol_index) ? ' -- symbol index reached' : ''
 				);
 				$index = $chain_entry_value;
 			}
 		}
 	}
74213bd9
 
 	if (exists($elf_data->{'gnu_hash'})) {
 		for my $gnu_section_index (keys(%{$elf_data->{'gnu_hash'}})) {
 			my $gnu_section = $elf_data->{'gnu_hash'}->{$gnu_section_index};
 			sayf(
 				$ind,
 				'Path to reach that symbol index (i.e. #%d) from its name through GNU hash section %s:',
 				$symbol_index,
 				colored($gnu_section->{'name'}, 'name')
 			);
 
 			if ($symbol_index < $gnu_section->{'symndx'}) {
 				sayf($ind + 1, 'This symbol is not meant to be accessible via the GNU hash section.');
 				return;
 			}
 
 			my $gnu_hash = hashSymbolNameGNUStyle($symbol->{'_name'});
 			sayf($ind + 1, 'GNU hash: %s', colored(sprintf('0x%08x', $gnu_hash), 'value'));
 			my $bucket_entry_index = $gnu_hash % $gnu_section->{'nbuckets'};
 			my $bucket_entry_offset = $gnu_section->{'bucket_offset'} + ($bucket_entry_index * $sizeof_word);
 			seek($elf_fh, $bucket_entry_offset, Fcntl::SEEK_SET);
 			my $bucket_entry_value = readELFValue($elf_fh, 'Word');
 			sayf(
 				$ind + 1,
 				'index y = bucket[GNU hash %% nbucket] = bucket[%s %% %s] = bucket[%s] = %s',
 				colored(sprintf('0x%08x', $gnu_hash), 'value'),
 				colored($gnu_section->{'nbuckets'}, 'value'),
 				colored($bucket_entry_index, 'value'),
 				colored($bucket_entry_value, 'value')
 			);
 			if (!$bucket_entry_value) {
 				sayf($ind + 1, 'Got empty hash chain, cannot reach symbol.');
 			}
 
 			for (my $i = 0, my $index = $bucket_entry_value; $i < 100; ++ $i, ++ $index) {
 				my $chain_entry_offset = $gnu_section->{'chain_offset'} + (($index - $gnu_section->{'symndx'}) * $sizeof_word);
 				seek($elf_fh, $chain_entry_offset, Fcntl::SEEK_SET);
 				my $chain_entry_value = readELFValue($elf_fh, 'Word');
 				sayf(
 					$ind + 1,
 					'chain[y - %s] = chain[%s - %s] = chain[%s] = %s',
 					colored('symndx', 'value_name'),
 					colored($index, 'value'),
 					colored($gnu_section->{'symndx'}, 'value'),
 					colored($index - $gnu_section->{'symndx'}, 'value'),
 					colored(sprintf('0x%08x', $chain_entry_value), 'value'),
 				);
 				if (($gnu_hash & ~1) == ($chain_entry_value & ~1)) {
 					my $conclusion = 'The first 31 bits match the GNU hash of the symbol we are looking for';
 					# We do not need to compare names as we already know what
 					# index we are looking for.
 					if ($index != $symbol_index) {
 						sayf($ind + 1, $conclusion . ', but this is not the expected symbol index: we must continue.');
 					}
 					else {
 						sayf($ind + 1, $conclusion . ' -- symbol index reached');
 						last;
 					}
 				}
 				if ($chain_entry_value & 1) {
 					sayf($ind + 1, 'Reached end of chain, unable to reach symbol index.');
 					last;
 				}
 			}
 		}
 	}
be02f9bb
 
 	# Restore the position of the file handler as it was before we mess with it.
 	seek($elf_fh, $pos, Fcntl::SEEK_SET);
 }
 
624e8a0b
 sub getLinkedSection {
 	my $elf_data = shift;
 	my $section_index = shift;
33d7c924
 	my $sh_attribute = shift || 'sh_link';
624e8a0b
 
 	my $section = $elf_data->{'sections'}->{$section_index};
 	return undef if (!defined($section));
 
33d7c924
 	my $linked_index = $section->{$sh_attribute};
624e8a0b
 	if ($linked_index && exists($elf_data->{'sections'}->{$linked_index})) {
 		return $elf_data->{'sections'}->{$linked_index};
 	}
 	return undef;
 }
 
 
 sub displayLinkedSection {
 	my $ind = shift;
 	my $elf_data = shift;
 	my $section_index = shift;
 
 	my $linked_section = getLinkedSection($elf_data, $section_index);
 	if (!$linked_section) {
 		sayf($ind, 'This section is not associated to any other section.');
 	}
 	else {
 		sayf(
 			$ind,
 			'This section is associated with section #%s: %s, of type %s.',
 			colored($linked_section->{'_index'}, 'index'),
 			colored($linked_section->{'_name'}, 'name'),
 			colored($linked_section->{'_type'}, 'name')
 		);
 	}
 	return $linked_section;
 }
 
c832f527
 sub displayStringTableForSection {
 	my $ind = shift;
 	my $elf_data = shift;
 	my $section_index = shift;
b9adf243
 	my $content = shift || 'Strings';
c832f527
 
77dc1ace
 	my $strtab = displayLinkedSection($ind, $elf_data, $section_index);
 	if (defined($strtab)) {
c832f527
 		if ($strtab->{'_type'} eq 'SHT_STRTAB') {
 			sayf(
 				$ind + 1,
b9adf243
 				'%s will be read from it, i.e. from offset %s.',
 				$content,
c832f527
 				formatOffset($strtab->{'sh_offset'})
 			);
77dc1ace
 			return $strtab->{'_index'};
c832f527
 		}
 		else {
b9adf243
 			sayf(
 				$ind + 1,
 				'%s will not be read from there though, as its type is not SHT_STRTAB.',
 				$content
 			);
c832f527
 		}
 	}
 	return undef;
 }
 
cd64feaa
 sub displayStringFromSection {
 	my $ind = shift;
 	my $elf_fh = shift;
 	my $elf_data = shift;
 	my $section_index = shift;
 	my $rel_offset = shift;
 
 	my $name = readStringFromSection($elf_fh, $elf_data, $section_index, $rel_offset);
 	if (defined($name) && length($name->{'string'})) {
 		sayf(
 			$ind,
 			'Fetched name at offset %s: %s',
 			formatOffset($name->{'abs_offset'}),
 			colored($name->{'string'}, 'name')
 		);
 	}
 	else {
 		say($ind, colored('Error: unable to retrieve the name.', 'error'));
 	}
 	return $name;
 }
 
7469b231
 sub readStringFromSection {
 	my $elf_fh = shift;
 	my $elf_data = shift;
 	my $section_index = shift;
 	my $rel_offset = shift;
 
 	my $section = $elf_data->{'sections'}->{$section_index};
 	return if (!defined($section));
 
 	my $abs_offset = $section->{'sh_offset'} + $rel_offset;
 
 	# Compute a sensible maximum expected length.
 	my $max = $section->{'sh_size'} - $rel_offset;
 	# It does not make sense if we are asked to fetch data in a section at an
 	# offset beyond the size of this section.
 	return undef if ($max < 0);
 
 	my $string = readStringAtOffset($elf_fh, $abs_offset, $max, 1);
 	return {
 		'string' => $string,
 		'rel_offset' => $rel_offset,
 		'abs_offset' => $abs_offset,
 	};
 }
 
288cc585
 sub readStringAtOffset {
 	my $elf_fh = shift;
 	my $offset = shift;
dd0368a0
 	my $max = shift;
e8401490
 	my $restore_pos = shift;
 
 	my $pos;
 	if (defined($restore_pos) && $restore_pos) {
 		$pos = tell($elf_fh);
 	}
288cc585
 
78111860
 	# Seek to that offset.
288cc585
 	seek($elf_fh, $offset, Fcntl::SEEK_SET);
 
 	# Read data into a string until we stumble upon EOF or 0x00.
e8401490
 	my $string = readNullTerminatedString($elf_fh, $max);
 
 	if (defined($restore_pos) && $restore_pos) {
 		seek($elf_fh, $pos, Fcntl::SEEK_SET);
 	}
 
 	return $string;
288cc585
 }
 
 sub readNullTerminatedString {
 	my $elf_fh = shift;
33e7a342
 	my $max = shift;
78111860
 
 	# Read data into a string until we stumble upon EOF or 0x00.
 	my $name;
 	my $read_bytes;
 	while (1) {
 		$read_bytes = read($elf_fh, my $char, 1);
 		last if ($read_bytes != 1 || $char eq "\x0");
 		$name .= $char;
33e7a342
 		last if (defined($max) && length($name) >= $max);
78111860
 	}
 	return $name;
 }
 
b4926357
 sub isPowerOfTwo {
 	my $value = shift;
 	return $value && !($value & ($value - 1));
 }
019600ce
 
be02f9bb
 sub hashSymbolName {
 	my $name = shift;
 
 	my $h = 0;
 	my $g;
 	for (my $i = 0; $i < length($name); ++ $i) {
 		$h = ($h << 4) + ord(substr($name, $i, 1));
 		$h ^= $g >> 24 if ($g = $h & 0xf0000000);
 		$h &= ~$g;
 	}
 	return $h;
 }
 
f5e2f5ed
 sub hashSymbolNameGNUStyle {
 	my $name = shift;
 
 	use bigint;
 	my $h = 5381;
 	my $ch;
 	for (my $i = 0; $i < length($name); ++ $i) {
 		$h = ($h * 33) + ord(substr($name, $i, 1));
 	}
 	return $h & 0xffffffff;
 }
 
019600ce
 # Relocations meanings were written based on the "calculation" column found in
 # SysV ABI AMD64 supplement. This column uses a particular notation to describe
 # relocations. That notation was copied "as is" in $elf.
 # This procedure rewrites this notation to something expected to be more
 # understandable (or at least less cryptic).
 sub relocationMeaning {
 	my $field = shift;
 	my $calculation = shift;
 	my $description = shift;
 
 	$calculation =~ s,none,No relocation,;
 	$calculation =~ s,GOT,GOT address,;
 	$calculation =~ s,A,addend,;
 	$calculation =~ s,B,base address,;
 	$calculation =~ s,G,symbol GOT offset,;
 	$calculation =~ s,L,symbol PLT place,;
 	$calculation =~ s,P,r_offset place,;
 	$calculation =~ s,S,symbol value,;
 	$calculation =~ s,Z,symbol size,;
 
 	my $final_description = '';
 	if ($field > R_FIELD_NONE) {
 		$final_description .= sprintf('for a %d-bit field: ', $field);
 	}
 	$final_description .= $calculation;
 	if (defined($description) && length($description)) {
 		$final_description .= '; ' . $description;
 	}
 
 	return $final_description;
 }