Browse code

Bump copyright year.

Xavier G authored on 01/01/2019 17:43:24
Showing 1 changed files
... ...
@@ -2,7 +2,7 @@
2 2
 
3 3
 # A simple toy script displaying the contents of an ELF file.
4 4
 
5
-# (c) 2015-2016 - Xavier Guerrin
5
+# (c) 2015-2019 - Xavier Guerrin
6 6
 # This program is free software. It comes without any warranty, to
7 7
 # the extent permitted by applicable law. You can redistribute it
8 8
 # and/or modify it under the terms of the Do What The Fuck You Want
Browse code

Update copyright, adjust WTFPL URL.

Xavier G authored on 26/02/2016 18:18:27
Showing 1 changed files
... ...
@@ -2,12 +2,12 @@
2 2
 
3 3
 # A simple toy script displaying the contents of an ELF file.
4 4
 
5
-# (c) 2015 - Xavier Guerrin
5
+# (c) 2015-2016 - Xavier Guerrin
6 6
 # This program is free software. It comes without any warranty, to
7 7
 # the extent permitted by applicable law. You can redistribute it
8 8
 # and/or modify it under the terms of the Do What The Fuck You Want
9 9
 # To Public License, Version 2, as published by Sam Hocevar. See
10
-# http://sam.zoy.org/wtfpl/COPYING for more details.
10
+# http://www.wtfpl.net/ for more details.
11 11
 
12 12
 # About formats implemented:
13 13
 
Browse code

walkDynamic(): display the value of DT_RPATH and DT_RUNPATH.

Xavier G authored on 10/09/2015 01:53:00
Showing 1 changed files
... ...
@@ -3110,7 +3110,8 @@ sub walkDynamic {
3110 3110
 					# An entry with a DT_NULL tag marks the end of the _DYNAMIC array.
3111 3111
 					last dynamic_array;
3112 3112
 				}
3113
-				elsif ($tag_value == 1 || $tag_value == 14) { # DT_NEEDED or DT_SONAME
3113
+				elsif ($tag_value == 1 || $tag_value == 14 || $tag_value == 15 || $tag_value == 29) {
3114
+					# DT_NEEDED, DT_SONAME, DT_RPATH or DT_RUNPATH
3114 3115
 					# "The offset is an index into the table recorded in the DT_STRTAB code."
3115 3116
 					# ... except we are liable to read a DT_NEEDED entry before
3116 3117
 					# stumbling upon the DT_STRTAB one.
Browse code

walkDynamic(): display the value of DT_SONAME.

Xavier G authored on 10/09/2015 01:25:08
Showing 1 changed files
... ...
@@ -3110,7 +3110,7 @@ sub walkDynamic {
3110 3110
 					# An entry with a DT_NULL tag marks the end of the _DYNAMIC array.
3111 3111
 					last dynamic_array;
3112 3112
 				}
3113
-				elsif ($tag_value == 1) { # DT_NEEDED
3113
+				elsif ($tag_value == 1 || $tag_value == 14) { # DT_NEEDED or DT_SONAME
3114 3114
 					# "The offset is an index into the table recorded in the DT_STRTAB code."
3115 3115
 					# ... except we are liable to read a DT_NEEDED entry before
3116 3116
 					# stumbling upon the DT_STRTAB one.
Browse code

Remove extra periods in short explanation strings.

Xavier G authored on 10/09/2015 01:18:10
Showing 1 changed files
... ...
@@ -1300,14 +1300,14 @@ our $elf = {
1300 1300
 			'.dtors' => 'destructor function pointers',
1301 1301
 			'.eh_frame' => 'frame unwinding information',
1302 1302
 			'.eh_frame_hdr' => 'frame unwinding additional information',
1303
-			'.gcc_except_table' => 'language specific data.',
1303
+			'.gcc_except_table' => 'language specific data',
1304 1304
 			'.gnu.version' => 'symbol version table',
1305 1305
 			'.gnu.version_d' => 'version definitions',
1306 1306
 			'.gnu.version_r' => 'version requirements',
1307 1307
 			'.got.plt' => 'read-only relocatable global offset table',
1308 1308
 			'.jcr' => 'Java class registration information',
1309 1309
 			'.note.ABI-tag' => 'ABI details',
1310
-			'.stab' => 'debugging information.',
1310
+			'.stab' => 'debugging information',
1311 1311
 			'.stabstr' => 'strings for .stab section',
1312 1312
 		},
1313 1313
 		'long' => {
... ...
@@ -1448,7 +1448,7 @@ our $elf = {
1448 1448
 			0 => colored('this symbol is to be linked from another file', 'bright_yellow'),
1449 1449
 			0xFFF1 => 'the symbol has an absolute value that will not change because of relocation',
1450 1450
 			0xFFF2 => 'the symbol labels a common block that has not yet been allocated',
1451
-			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.',
1451
+			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',
1452 1452
 		},
1453 1453
 		'unexplained_value' => 'this is the index of the related section',
1454 1454
 	},
... ...
@@ -1542,7 +1542,7 @@ our $elf = {
1542 1542
 			30 => 'holds flag values specific to the object being loaded',
1543 1543
 			32 => 'holds the address of the array of pointers to pre-initialization functions',
1544 1544
 			33 => 'holds the size in bytes of the array of pre-initialization functions pointed to by the DT_PREINIT_ARRAY entry',
1545
-			34 => 'holds the address of the SHT_SYMTAB_SHNDX section associated with the dynamic symbol table referenced by the DT_SYMTAB element.',
1545
+			34 => 'holds the address of the SHT_SYMTAB_SHNDX section associated with the dynamic symbol table referenced by the DT_SYMTAB element',
1546 1546
 			0x6ffffe00 => '',
1547 1547
 			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',
1548 1548
 			0x6ffffef6 => 'location of the GOT entry used by the TLS descriptor resolver PLT entry',
... ...
@@ -1555,8 +1555,8 @@ our $elf = {
1555 1555
 			0x6ffffefd => 'PLT padding',
1556 1556
 			0x6ffffefe => 'move table',
1557 1557
 			0x6ffffeff => 'syminfo table',
1558
-			0x6ffffeff => 'holds the address of the Syminfo table.',
1559
-			0x6ffffff0 => 'holds the address of the table provided by the .gnu.version section.',
1558
+			0x6ffffeff => 'holds the address of the Syminfo table',
1559
+			0x6ffffff0 => 'holds the address of the table provided by the .gnu.version section',
1560 1560
 			0x6ffffff9 => 'holds the number of relative relocations in .rela.dyn',
1561 1561
 			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',
1562 1562
 			0x6ffffffb => 'DF_1_* state flags',
Browse code

Adjust calls to displayStringTableForSection().

Xavier G authored on 10/09/2015 01:15:41
Showing 1 changed files
... ...
@@ -2757,7 +2757,7 @@ sub walkSymbolTable {
2757 2757
 	my $count = $section->{'sh_size'} / $section->{'sh_entsize'};
2758 2758
 
2759 2759
 	# Where should we read names from?
2760
-	my $strtab = displayStringTableForSection($ind, $elf_data, $section_index);
2760
+	my $strtab = displayStringTableForSection($ind, $elf_data, $section_index, 'Symbol names');
2761 2761
 
2762 2762
 	my $arch = $elf_data->{'EI_CLASS'};
2763 2763
 	my $st_info = $elf_structures->{'symbol_table'}->{$arch};
... ...
@@ -3085,7 +3085,7 @@ sub walkDynamic {
3085 3085
 	return if (!defined($section));
3086 3086
 
3087 3087
 	# Where should we read names from?
3088
-	my $strtab = displayStringTableForSection($ind, $elf_data, $section_index);
3088
+	my $strtab = displayStringTableForSection($ind, $elf_data, $section_index, 'Library names');
3089 3089
 
3090 3090
 	# What kind of entries do we intend to read?
3091 3091
 	my $arch = $elf_data->{'EI_CLASS'};
... ...
@@ -3169,7 +3169,7 @@ sub walkVerdef {
3169 3169
 	sayf($ind, 'This section contains DT_VERDEFNUM = %d entries.', $dt_verdefnum);
3170 3170
 
3171 3171
 	# Where should we read names from?
3172
-	my $strtab = displayStringTableForSection($ind, $elf_data, $section_index);
3172
+	my $strtab = displayStringTableForSection($ind, $elf_data, $section_index, 'Version strings and library names');
3173 3173
 
3174 3174
 	my $offset = $section->{'sh_offset'};
3175 3175
 	sayf($ind, 'Seeking to offset sh_offset = %s', formatOffset($offset));
... ...
@@ -3230,7 +3230,7 @@ sub walkVerneed {
3230 3230
 	sayf($ind, 'This section contains DT_VERNEEDNUM = %d entries.', $dt_verneednum);
3231 3231
 
3232 3232
 	# Where should we read names from?
3233
-	my $strtab = displayStringTableForSection($ind, $elf_data, $section_index);
3233
+	my $strtab = displayStringTableForSection($ind, $elf_data, $section_index, 'Version strings and library names');
3234 3234
 
3235 3235
 	my $offset = $section->{'sh_offset'};
3236 3236
 	sayf($ind, 'Seeking to offset sh_offset = %s', formatOffset($offset));
Browse code

displayStringTableForSection(): add $content parameter.

This parameter allows the caller to specify the first words of the sentences
displayed by this function. If this extra parameter is omitted, the function
will default to "Strings". The point of this parameter is that ELF string
tables can hold various things that are not symbol names, e.g. file names or
versions.

Xavier G authored on 10/09/2015 00:52:32
Showing 1 changed files
... ...
@@ -3812,19 +3812,25 @@ sub displayStringTableForSection {
3812 3812
 	my $ind = shift;
3813 3813
 	my $elf_data = shift;
3814 3814
 	my $section_index = shift;
3815
+	my $content = shift || 'Strings';
3815 3816
 
3816 3817
 	my $strtab = displayLinkedSection($ind, $elf_data, $section_index);
3817 3818
 	if (defined($strtab)) {
3818 3819
 		if ($strtab->{'_type'} eq 'SHT_STRTAB') {
3819 3820
 			sayf(
3820 3821
 				$ind + 1,
3821
-				'Symbol names will be read from it, i.e. from offset %s.',
3822
+				'%s will be read from it, i.e. from offset %s.',
3823
+				$content,
3822 3824
 				formatOffset($strtab->{'sh_offset'})
3823 3825
 			);
3824 3826
 			return $strtab->{'_index'};
3825 3827
 		}
3826 3828
 		else {
3827
-			say($ind + 1, 'Symbol names will not be read from there though, as its type is not SHT_STRTAB.');
3829
+			sayf(
3830
+				$ind + 1,
3831
+				'%s will not be read from there though, as its type is not SHT_STRTAB.',
3832
+				$content
3833
+			);
3828 3834
 		}
3829 3835
 	}
3830 3836
 	return undef;
Browse code

Remove a pedantic parenthesis which does not make that much sense.

Xavier G authored on 10/09/2015 00:37:50
Showing 1 changed files
... ...
@@ -2561,7 +2561,7 @@ sub walkELFSections {
2561 2561
 		}
2562 2562
 	}
2563 2563
 
2564
-	say(0, colored('Parsing SHT_DYNSYM sections (and associated SHT_STRTAB sections)', 'main_action'));
2564
+	say(0, colored('Parsing SHT_DYNSYM sections', 'main_action'));
2565 2565
 	for (my $i = 1; $i < $elf_data->{'e_shnum'}; ++ $i) {
2566 2566
 		my $section = $elf_data->{'sections'}->{$i};
2567 2567
 		if ($section->{'_type'} eq 'SHT_DYNSYM') {
Browse code

walkDynamic(): display the linked section.

... and since it is supposed to be a strtab, use it to explain DT_NEEDED
entries.

Xavier G authored on 10/09/2015 00:30:08
Showing 1 changed files
... ...
@@ -3084,6 +3084,9 @@ sub walkDynamic {
3084 3084
 	my $section = $elf_data->{'sections'}->{$section_index};
3085 3085
 	return if (!defined($section));
3086 3086
 
3087
+	# Where should we read names from?
3088
+	my $strtab = displayStringTableForSection($ind, $elf_data, $section_index);
3089
+
3087 3090
 	# What kind of entries do we intend to read?
3088 3091
 	my $arch = $elf_data->{'EI_CLASS'};
3089 3092
 	my $dyn_info = $elf_structures->{'dynamic'}->{$arch};
... ...
@@ -3111,14 +3114,11 @@ sub walkDynamic {
3111 3114
 					# "The offset is an index into the table recorded in the DT_STRTAB code."
3112 3115
 					# ... except we are liable to read a DT_NEEDED entry before
3113 3116
 					# stumbling upon the DT_STRTAB one.
3114
-					# We can assume DT_STRTAB actually points to a SHT_STRTAB
3115
-					# section, typically .dynstr
3116
-					for my $section_index (keys(%{$elf_data->{'sections'}})) {
3117
-						my $section = $elf_data->{'sections'}->{$section_index};
3118
-						if ($section->{'_name'} eq '.dynstr') {
3119
-							displayStringFromSection($ind + 1, $elf_fh, $elf_data, $section_index, $value);
3120
-							last;
3121
-						}
3117
+					# As DT_STRTAB is supposed to point to a SHT_STRTAB section,
3118
+					# typically .dynstr, we can assume everything will be fine
3119
+					# if we rely on the linked strtab.
3120
+					if ($strtab) {
3121
+						displayStringFromSection($ind + 1, $elf_fh, $elf_data, $strtab, $value);
3122 3122
 					}
3123 3123
 				}
3124 3124
 				elsif ($tag_value == 20) { # DT_PLTREL
Browse code

Improve the way we format version definitions.

Indeed, it appears that, for a given version definition having multiple
auxiliary entries (verdaux), the first entry is the version specification
itself and the other ones are its dependencies.
Dependencies between version nodes are checked at compile-time only.

Xavier G authored on 23/08/2015 23:57:13
Showing 1 changed files
... ...
@@ -2223,7 +2223,11 @@ sub formatVersionDefinition {
2223 2223
 	for (my $i = 0; $i < $verdef->{'vd_cnt'}; ++ $i) {
2224 2224
 		push(@verdef_strings, $verdef->{'verdaux'}->{$i}->{'name'});
2225 2225
 	}
2226
-	return join(' / ', @verdef_strings);
2226
+	my $str = shift(@verdef_strings);
2227
+	if (scalar(@verdef_strings)) {
2228
+		$str .= sprintf(' (deps: %s)', join(', ', @verdef_strings));
2229
+	}
2230
+	return $str;
2227 2231
 }
2228 2232
 
2229 2233
 sub formatVersionRequirement {
Browse code

walkVersym(): display the linked section.

Xavier G authored on 23/08/2015 23:29:47
Showing 1 changed files
... ...
@@ -3289,6 +3289,9 @@ sub walkVersym {
3289 3289
 	my $section = $elf_data->{'sections'}->{$section_index};
3290 3290
 	return if (!defined($section));
3291 3291
 
3292
+	my $dynsym = displayLinkedSection($ind, $elf_data, $section_index);
3293
+	my $dynsym_index = defined($dynsym) ? $dynsym->{'_index'} : undef;
3294
+
3292 3295
 	# How many entries do we intend to read?
3293 3296
 	my $count = $section->{'sh_size'} / $section->{'sh_entsize'};
3294 3297
 	# Note: sh_entsize is expected to be 2 bytes (16 bits) as the LSB states
... ...
@@ -3308,7 +3311,7 @@ sub walkVersym {
3308 3311
 			next;
3309 3312
 		}
3310 3313
 
3311
-		my $symbol = symbolFromIndex($elf_data, $i);
3314
+		my $symbol = symbolFromIndex($elf_data, $i, $dynsym_index);
3312 3315
 
3313 3316
 		my $value = readELFValue($elf_fh, 'Half');
3314 3317
 		my $printable_value = colored(formatValue($value, 'Half'), 'value');
Browse code

Fix an off-by-one error when parsing SHT_GNU_versym sections.

Xavier G authored on 23/08/2015 23:03:22
Showing 1 changed files
... ...
@@ -3303,6 +3303,7 @@ sub walkVersym {
3303 3303
 	for (my $i = 0; $i < $count; ++ $i) {
3304 3304
 		sayf($ind, '[%d/%d] symbol version entry at index #%s:', $i + 1, $count, $i);
3305 3305
 		if (!$i) {
3306
+			readELFValue($elf_fh, 'Half');
3306 3307
 			say($ind + 1, 'Skipping as the symbol table entry for index 0 (STN_UNDEF) is reserved');
3307 3308
 			next;
3308 3309
 		}
Browse code

walkRela(): fetch symbols from the associated dynsym only.

Xavier G authored on 17/08/2015 21:55:27
Showing 1 changed files
... ...
@@ -3011,9 +3011,11 @@ sub walkRela {
3011 3011
 	my $rela_info = $elf_structures->{$struct}->{$arch};
3012 3012
 
3013 3013
 	# Explain sh_link.
3014
+	my $dynsym_index = undef;
3014 3015
 	my $linked_section = displayLinkedSection($ind, $elf_data, $section_index);
3015 3016
 	if (defined($linked_section) && $linked_section->{'_type'} eq 'SHT_DYNSYM') {
3016 3017
 		sayf($ind + 1, 'This is the associated symbol table.');
3018
+		$dynsym_index = $linked_section->{'_index'};
3017 3019
 	}
3018 3020
 
3019 3021
 	# Explain sh_info.
... ...
@@ -3052,7 +3054,7 @@ sub walkRela {
3052 3054
 
3053 3055
 				if ($r_sym) {
3054 3056
 					# Get the name of the associated symbol.
3055
-					my $symbol_name = symbolNameFromIndex($elf_data, $r_sym);
3057
+					my $symbol_name = symbolNameFromIndex($elf_data, $r_sym, $dynsym_index);
3056 3058
 					$section->{'_relocations'}->{$i}->{'_symbol'} = $symbol_name;
3057 3059
 					sayf($ind + 3, 'Here, %s is the index of symbol %s.', colored($r_sym, 'value'), colored($symbol_name, 'name'));
3058 3060
 				}
Browse code

symbolFromIndex() can now be instructed to fetch symbols from a specific dynsym section.

Xavier G authored on 17/08/2015 21:54:34
Showing 1 changed files
... ...
@@ -3598,8 +3598,12 @@ sub segmentIncludesSection {
3598 3598
 sub symbolFromIndex {
3599 3599
 	my $elf_data = shift;
3600 3600
 	my $index = shift;
3601
+	my $dynsym_index = shift;
3601 3602
 
3602 3603
 	for my $section_index (@{$elf_data->{'SHT_DYNSYM'}}) {
3604
+		if (defined($dynsym_index)) {
3605
+			next if ($section_index != $dynsym_index);
3606
+		}
3603 3607
 		if (exists($elf_data->{'sections'}->{$section_index}->{'_symbols'}->{$index})) {
3604 3608
 			return $elf_data->{'sections'}->{$section_index}->{'_symbols'}->{$index};
3605 3609
 		}
... ...
@@ -3610,8 +3614,9 @@ sub symbolFromIndex {
3610 3614
 sub symbolNameFromIndex {
3611 3615
 	my $elf_data = shift;
3612 3616
 	my $index = shift;
3617
+	my $dynsym_index = shift;
3613 3618
 
3614
-	my $symbol = symbolFromIndex($elf_data, $index);
3619
+	my $symbol = symbolFromIndex($elf_data, $index, $dynsym_index);
3615 3620
 	return defined($symbol) ? $symbol->{'_name'} : undef;
3616 3621
 }
3617 3622
 
Browse code

walkRela(): explain sh_link and sh_info.

Xavier G authored on 17/08/2015 21:41:10
Showing 1 changed files
... ...
@@ -3010,6 +3010,24 @@ sub walkRela {
3010 3010
 	my $struct = ($section->{'_type'} eq 'SHT_RELA') ? 'rela' : 'rel';
3011 3011
 	my $rela_info = $elf_structures->{$struct}->{$arch};
3012 3012
 
3013
+	# Explain sh_link.
3014
+	my $linked_section = displayLinkedSection($ind, $elf_data, $section_index);
3015
+	if (defined($linked_section) && $linked_section->{'_type'} eq 'SHT_DYNSYM') {
3016
+		sayf($ind + 1, 'This is the associated symbol table.');
3017
+	}
3018
+
3019
+	# Explain sh_info.
3020
+	my $target_section = getLinkedSection($elf_data, $section_index, 'sh_info');
3021
+	if (defined($target_section)) {
3022
+		sayf(
3023
+			$ind,
3024
+			'Relocations described in this section apply to section #%s: %s, of type %s.',
3025
+			colored($target_section->{'_index'}, 'index'),
3026
+			colored($target_section->{'_name'}, 'name'),
3027
+			colored($target_section->{'_type'}, 'name')
3028
+		);
3029
+	}
3030
+
3013 3031
 	# Iterate over relocation entries.
3014 3032
 	for (my $i = 0; $i < $count; ++ $i) {
3015 3033
 		sayf($ind, '[%d/%d] Relocation at index #%s:', $i + 1, $count, $i);
Browse code

getLinkedSection() now follows an arbitrary attribute.

getLinkedSection() was initially hardcoded to fetch the section linked via
sh_link. Now, thanks to one extra, optional parameter, she can follow any
attribute, e.g. sh_info.

Xavier G authored on 17/08/2015 21:38:55
Showing 1 changed files
... ...
@@ -3741,11 +3741,12 @@ sub displayHashDetailsForSymbol {
3741 3741
 sub getLinkedSection {
3742 3742
 	my $elf_data = shift;
3743 3743
 	my $section_index = shift;
3744
+	my $sh_attribute = shift || 'sh_link';
3744 3745
 
3745 3746
 	my $section = $elf_data->{'sections'}->{$section_index};
3746 3747
 	return undef if (!defined($section));
3747 3748
 
3748
-	my $linked_index = $section->{'sh_link'};
3749
+	my $linked_index = $section->{$sh_attribute};
3749 3750
 	if ($linked_index && exists($elf_data->{'sections'}->{$linked_index})) {
3750 3751
 		return $elf_data->{'sections'}->{$linked_index};
3751 3752
 	}
Browse code

SHT_RELA sections: improve description of r_sym.

Xavier G authored on 17/08/2015 21:16:09
Showing 1 changed files
... ...
@@ -3032,10 +3032,15 @@ sub walkRela {
3032 3032
 				$section->{'_relocations'}->{$i}->{'r_sym'} = $r_sym;
3033 3033
 				say($ind + 2, analyseAndExplainValue($r_sym, 'r_sym', $rela_info->{$entry}));
3034 3034
 
3035
-				# Get the name of the associated symbol.
3036
-				my $symbol_name = symbolNameFromIndex($elf_data, $r_sym);
3037
-				$section->{'_relocations'}->{$i}->{'_symbol'} = $symbol_name;
3038
-				sayf($ind + 3, 'Here, %s is the index of symbol %s.', colored($r_sym, 'value'), colored($symbol_name, 'name'));
3035
+				if ($r_sym) {
3036
+					# Get the name of the associated symbol.
3037
+					my $symbol_name = symbolNameFromIndex($elf_data, $r_sym);
3038
+					$section->{'_relocations'}->{$i}->{'_symbol'} = $symbol_name;
3039
+					sayf($ind + 3, 'Here, %s is the index of symbol %s.', colored($r_sym, 'value'), colored($symbol_name, 'name'));
3040
+				}
3041
+				else {
3042
+					sayf($ind + 3, 'Here, the index is STN_UNDEF, so the relocation uses zero as the symbol value.');
3043
+				}
3039 3044
 
3040 3045
 				# Extract r_type from r_info.
3041 3046
 				my $r_type = ($arch == ELFCLASS32 ? ($value & 0xFF) : ($value & 0xFFFFFFFF));
Browse code

Adjust indentation directives.

Xavier G authored on 10/08/2015 02:55:21
Showing 1 changed files
... ...
@@ -2777,29 +2777,28 @@ sub walkSymbolTable {
2777 2777
 			my $value = readELFValue($elf_fh, $st_info->{$entry});
2778 2778
 			$section->{'_symbols'}->{$i}->{$entry} = $value;
2779 2779
 			# and provide a first basic explanation.
2780
-			say($ind + 2, analyseAndExplainValue($value, $entry, $st_info->{$entry}));
2780
+			say($ind + 1, analyseAndExplainValue($value, $entry, $st_info->{$entry}));
2781 2781
 
2782 2782
 			if ($entry eq 'st_name') {
2783 2783
 				if (!$value) {
2784
-					sayf($ind + 3, 'This symbol table entry has no name.');
2784
+					sayf($ind + 2, 'This symbol table entry has no name.');
2785 2785
 				}
2786 2786
 				elsif ($strtab) {
2787
-					my $name = displayStringFromSection($ind + 3, $elf_fh, $elf_data, $strtab, $value);
2787
+					my $name = displayStringFromSection($ind + 2, $elf_fh, $elf_data, $strtab, $value);
2788 2788
 					if (defined($name) && length($name->{'string'})) {
2789 2789
 						$section->{'_symbols'}->{$i}->{'_name'} = $name->{'string'};
2790
-						displayHashDetailsForSymbol($ind + 4, $elf_fh, $elf_data, $section->{'_symbols'}->{$i}, $i);
2791 2790
 					}
2792 2791
 				}
2793 2792
 			}
2794 2793
 			elsif ($entry eq 'st_info') {
2795 2794
 				my $st_bind = ($value >> 4);
2796 2795
 				my $st_type = ($value & 0xf);
2797
-				say($ind + 3, analyseAndExplainValue($st_bind, 'st_bind', $st_info->{$entry}));
2798
-				say($ind + 3, analyseAndExplainValue($st_type, 'st_type', $st_info->{$entry}));
2796
+				say($ind + 2, analyseAndExplainValue($st_bind, 'st_bind', $st_info->{$entry}));
2797
+				say($ind + 2, analyseAndExplainValue($st_type, 'st_type', $st_info->{$entry}));
2799 2798
 			}
2800 2799
 			elsif ($entry eq 'st_shndx') {
2801 2800
 				sayf(
2802
-					$ind + 3,
2801
+					$ind + 2,
2803 2802
 					'Here, %s is the index of section %s.',
2804 2803
 					colored($value, 'value'),
2805 2804
 					colored($elf_data->{'sections'}->{$value}->{'_name'}, 'name')
... ...
@@ -2826,7 +2825,7 @@ sub walkSymbolTable {
2826 2825
 					}
2827 2826
 
2828 2827
 					sayf(
2829
-						$ind + 3,
2828
+						$ind + 2,
2830 2829
 						'Here, since %s, %s holds %s.',
2831 2830
 						$extra_explanation1,
2832 2831
 						colored('st_value', 'value_name'),
... ...
@@ -2835,6 +2834,10 @@ sub walkSymbolTable {
2835 2834
 				}
2836 2835
 			}
2837 2836
 		}
2837
+		# If the symbol has a name, display how to look it up through hash sections.
2838
+		if (exists($section->{'_symbols'}->{$i}->{'_name'})) {
2839
+			displayHashDetailsForSymbol($ind + 1, $elf_fh, $elf_data, $section->{'_symbols'}->{$i}, $i);
2840
+		}
2838 2841
 	}
2839 2842
 }
2840 2843
 
... ...
@@ -3021,23 +3024,23 @@ sub walkRela {
3021 3024
 			my $value = readELFValue($elf_fh, $rela_info->{$entry});
3022 3025
 			$section->{'_relocations'}->{$i}->{$entry} = $value;
3023 3026
 			# and provide a first basic explanation.
3024
-			say($ind + 2, analyseAndExplainValue($value, $entry, $rela_info->{$entry}));
3027
+			say($ind + 1, analyseAndExplainValue($value, $entry, $rela_info->{$entry}));
3025 3028
 
3026 3029
 			if ($entry eq 'r_info') {
3027 3030
 				# Extract r_sym from r_info.
3028 3031
 				my $r_sym = ($value >> ($arch == ELFCLASS32 ? 8 : 32));
3029 3032
 				$section->{'_relocations'}->{$i}->{'r_sym'} = $r_sym;
3030
-				say($ind + 3, analyseAndExplainValue($r_sym, 'r_sym', $rela_info->{$entry}));
3033
+				say($ind + 2, analyseAndExplainValue($r_sym, 'r_sym', $rela_info->{$entry}));
3031 3034
 
3032 3035
 				# Get the name of the associated symbol.
3033 3036
 				my $symbol_name = symbolNameFromIndex($elf_data, $r_sym);
3034 3037
 				$section->{'_relocations'}->{$i}->{'_symbol'} = $symbol_name;
3035
-				sayf($ind + 4, 'Here, %s is the index of symbol %s.', colored($r_sym, 'value'), colored($symbol_name, 'name'));
3038
+				sayf($ind + 3, 'Here, %s is the index of symbol %s.', colored($r_sym, 'value'), colored($symbol_name, 'name'));
3036 3039
 
3037 3040
 				# Extract r_type from r_info.
3038 3041
 				my $r_type = ($arch == ELFCLASS32 ? ($value & 0xFF) : ($value & 0xFFFFFFFF));
3039 3042
 				$section->{'_relocations'}->{$i}->{'r_type'} = $r_sym;
3040
-				say($ind + 3, analyseAndExplainValue($r_type, 'r_type', $rela_info->{$entry}, undef, $elf_data->{'e_machine'}));
3043
+				say($ind + 2, analyseAndExplainValue($r_type, 'r_type', $rela_info->{$entry}, undef, $elf_data->{'e_machine'}));
3041 3044
 			}
3042 3045
 		}
3043 3046
 	}
... ...
@@ -3047,7 +3050,7 @@ sub walkDynamic {
3047 3050
 	my $elf_fh = shift;
3048 3051
 	my $elf_data = shift;
3049 3052
 	my $section_index = shift;
3050
-	my $ind = 1;
3053
+	my $ind = 2;
3051 3054
 
3052 3055
 	my $section = $elf_data->{'sections'}->{$section_index};
3053 3056
 	return if (!defined($section));
... ...
@@ -3057,7 +3060,7 @@ sub walkDynamic {
3057 3060
 	my $dyn_info = $elf_structures->{'dynamic'}->{$arch};
3058 3061
 
3059 3062
 	my $offset = $section->{'sh_offset'};
3060
-	sayf($ind + 1, 'Seeking to offset sh_offset = %s', formatOffset($offset));
3063
+	sayf($ind, 'Seeking to offset sh_offset = %s', formatOffset($offset));
3061 3064
 	seek($elf_fh, $offset, Fcntl::SEEK_SET);
3062 3065
 
3063 3066
 	my $dyn_section = $elf_data->{'dynamic'}->{$section_index} = {};
... ...
@@ -3067,7 +3070,7 @@ sub walkDynamic {
3067 3070
 		dynamic_entry: for my $entry (@{$dyn_info->{'ordered_entries'}}) {
3068 3071
 			my $value = readELFValue($elf_fh, $dyn_info->{$entry});
3069 3072
 			$dyn_section->{$i}->{$entry} = $value;
3070
-			say($ind + 2, analyseAndExplainValue($value, $entry, $dyn_info->{$entry}));
3073
+			say($ind, analyseAndExplainValue($value, $entry, $dyn_info->{$entry}));
3071 3074
 
3072 3075
 			if ($entry eq 'd_un') {
3073 3076
 				my $tag_value = $dyn_section->{$i}->{'d_tag'};
... ...
@@ -3084,7 +3087,7 @@ sub walkDynamic {
3084 3087
 					for my $section_index (keys(%{$elf_data->{'sections'}})) {
3085 3088
 						my $section = $elf_data->{'sections'}->{$section_index};
3086 3089
 						if ($section->{'_name'} eq '.dynstr') {
3087
-							displayStringFromSection($ind + 3, $elf_fh, $elf_data, $section_index, $value);
3090
+							displayStringFromSection($ind + 1, $elf_fh, $elf_data, $section_index, $value);
3088 3091
 							last;
3089 3092
 						}
3090 3093
 					}
... ...
@@ -3102,13 +3105,13 @@ sub walkDynamic {
3102 3105
 					}
3103 3106
 					$value = sprintf('%d', $value);
3104 3107
 					$value = colored($value, 'value');
3105
-					sayf($ind + 3, 'Here, %s %s.', $value, $meaning);
3108
+					sayf($ind + 1, 'Here, %s %s.', $value, $meaning);
3106 3109
 				}
3107 3110
 				elsif ($tag_value == 30) { # DT_FLAGS
3108
-					say($ind + 3, analyseAndExplainValue($value, 'dt_flags', $dyn_info->{'d_un'}));
3111
+					say($ind + 1, analyseAndExplainValue($value, 'dt_flags', $dyn_info->{'d_un'}));
3109 3112
 				}
3110 3113
 				elsif ($tag_value == 0x6FFFFFFB) { # DT_FLAGS_1
3111
-					say($ind + 3, analyseAndExplainValue($value, 'dt_flags_1', $dyn_info->{'d_un'}));
3114
+					say($ind + 1, analyseAndExplainValue($value, 'dt_flags_1', $dyn_info->{'d_un'}));
3112 3115
 				}
3113 3116
 			}
3114 3117
 		}
... ...
@@ -3147,24 +3150,24 @@ sub walkVerdef {
3147 3150
 		my $verdef = {};
3148 3151
 
3149 3152
 		# Parse Elfxx_Verdef
3150
-		sayf($ind + 1, '[%d/%d] Version definition entry #%d:', $i + 1, $dt_verdefnum, $i);
3153
+		sayf($ind, '[%d/%d] Version definition entry #%d:', $i + 1, $dt_verdefnum, $i);
3151 3154
 		for my $entry (@{$elf_structures->{'verdef'}->{'ordered_entries'}}) {
3152 3155
 			my $type = $elf_structures->{'verdef'}->{$entry};
3153 3156
 			my $value = readELFValue($elf_fh, $type);
3154
-			say($ind + 2, analyseAndExplainValue($value, $entry, $type));
3157
+			say($ind + 1, analyseAndExplainValue($value, $entry, $type));
3155 3158
 			$verdef->{$entry} = $value;
3156 3159
 		}
3157 3160
 
3158 3161
 		if ($verdef->{'vd_cnt'} > 0) {
3159 3162
 			for (my $j = 0; $j < $verdef->{'vd_cnt'}; ++ $j) {
3160
-				sayf($ind + 2, '[%d/%d] Version definition auxiliary entry #%d:', $j + 1, $verdef->{'vd_cnt'}, $j);
3163
+				sayf($ind + 1, '[%d/%d] Version definition auxiliary entry #%d:', $j + 1, $verdef->{'vd_cnt'}, $j);
3161 3164
 				for my $entry (@{$elf_structures->{'verdaux'}->{'ordered_entries'}}) {
3162 3165
 					my $type = $elf_structures->{'verdaux'}->{$entry};
3163 3166
 					my $value = readELFValue($elf_fh, $type);
3164
-					say($ind + 3, analyseAndExplainValue($value, $entry, $type));
3167
+					say($ind + 2, analyseAndExplainValue($value, $entry, $type));
3165 3168
 					$verdef->{'verdaux'}->{$j}->{$entry} = $value;
3166 3169
 					if ($entry eq 'vda_name' && $strtab) {
3167
-						my $name = displayStringFromSection($ind + 4, $elf_fh, $elf_data, $strtab, $value);
3170
+						my $name = displayStringFromSection($ind + 3, $elf_fh, $elf_data, $strtab, $value);
3168 3171
 						if (defined($name) && length($name->{'string'})) {
3169 3172
 							$verdef->{'verdaux'}->{$j}->{'name'} = $name->{'string'};
3170 3173
 						}
... ...
@@ -3191,7 +3194,7 @@ sub walkVerneed {
3191 3194
 	# contained in the DT_VERNEEDNUM entry of the Dynamic Section .dynamic."
3192 3195
 	my @dt_verneednum_entries = dynamicEntries($elf_data, 0x6fffffff); # DT_VERNEEDNUM
3193 3196
 	if (!scalar(@dt_verneednum_entries)) {
3194
-		say($ind + 1, colored('Unable to find the DT_VERNEEDNUM entry in .dynamic, aborting the parsing of this section.', 'error'));
3197
+		say($ind, colored('Unable to find the DT_VERNEEDNUM entry in .dynamic, aborting the parsing of this section.', 'error'));
3195 3198
 		return;
3196 3199
 	}
3197 3200
 	my $dt_verneednum = shift(@dt_verneednum_entries);
... ...
@@ -3208,14 +3211,14 @@ sub walkVerneed {
3208 3211
 		my $verneed = {};
3209 3212
 
3210 3213
 		# Parse Elfxx_Verneed
3211
-		sayf($ind + 1, '[%d/%d] Version requirements entry #%d:', $i + 1, $dt_verneednum, $i);
3214
+		sayf($ind, '[%d/%d] Version requirements entry #%d:', $i + 1, $dt_verneednum, $i);
3212 3215
 		for my $entry (@{$elf_structures->{'verneed'}->{'ordered_entries'}}) {
3213 3216
 			my $type = $elf_structures->{'verneed'}->{$entry};
3214 3217
 			my $value = readELFValue($elf_fh, $type);
3215
-			say($ind + 2, analyseAndExplainValue($value, $entry, $type));
3218
+			say($ind + 1, analyseAndExplainValue($value, $entry, $type));
3216 3219
 			$verneed->{$entry} = $value;
3217 3220
 			if ($entry eq 'vn_file') {
3218
-				my $name = displayStringFromSection($ind + 3, $elf_fh, $elf_data, $strtab, $value);
3221
+				my $name = displayStringFromSection($ind + 2, $elf_fh, $elf_data, $strtab, $value);
3219 3222
 				if (defined($name) && length($name->{'string'})) {
3220 3223
 					$verneed->{'file'} = $name->{'string'};
3221 3224
 				}
... ...
@@ -3226,14 +3229,14 @@ sub walkVerneed {
3226 3229
 		if ($verneed->{'vn_cnt'} > 0) {
3227 3230
 			for (my $j = 0; $j < $verneed->{'vn_cnt'}; ++ $j) {
3228 3231
 				my $vernaux = {};
3229
-				sayf($ind + 2, '[%d/%d] Version requirements auxiliary entry #%d:', $j + 1, $verneed->{'vn_cnt'}, $j);
3232
+				sayf($ind + 1, '[%d/%d] Version requirements auxiliary entry #%d:', $j + 1, $verneed->{'vn_cnt'}, $j);
3230 3233
 				for my $entry (@{$elf_structures->{'vernaux'}->{'ordered_entries'}}) {
3231 3234
 					my $type = $elf_structures->{'vernaux'}->{$entry};
3232 3235
 					my $value = readELFValue($elf_fh, $type);
3233
-					say($ind + 3, analyseAndExplainValue($value, $entry, $type));
3236
+					say($ind + 2, analyseAndExplainValue($value, $entry, $type));
3234 3237
 					$vernaux->{$entry} = $value;
3235 3238
 					if ($entry eq 'vna_name' && $strtab) {
3236
-						my $name = displayStringFromSection($ind + 4, $elf_fh, $elf_data, $strtab, $value);
3239
+						my $name = displayStringFromSection($ind + 3, $elf_fh, $elf_data, $strtab, $value);
3237 3240
 						if (defined($name) && length($name->{'string'})) {
3238 3241
 							$vernaux->{'name'} = $name->{'string'};
3239 3242
 						}
... ...
@@ -3256,7 +3259,7 @@ sub walkVersym {
3256 3259
 	my $elf_fh = shift;
3257 3260
 	my $elf_data = shift;
3258 3261
 	my $section_index = shift;
3259
-	my $ind = 1;
3262
+	my $ind = 2;
3260 3263
 
3261 3264
 	my $section = $elf_data->{'sections'}->{$section_index};
3262 3265
 	return if (!defined($section));
... ...
@@ -3268,14 +3271,14 @@ sub walkVersym {
3268 3271
 	# type Elfxx_Half."
3269 3272
 
3270 3273
 	my $offset = $section->{'sh_offset'};
3271
-	sayf($ind + 1, 'Seeking to offset sh_offset = %s', formatOffset($offset));
3274
+	sayf($ind, 'Seeking to offset sh_offset = %s', formatOffset($offset));
3272 3275
 	seek($elf_fh, $offset, Fcntl::SEEK_SET);
3273 3276
 
3274 3277
 	# Iterate over entries of the Symbol Version Table.
3275 3278
 	for (my $i = 0; $i < $count; ++ $i) {
3276
-		sayf($ind + 1, '[%d/%d] symbol version entry at index #%s:', $i + 1, $count, $i);
3279
+		sayf($ind, '[%d/%d] symbol version entry at index #%s:', $i + 1, $count, $i);
3277 3280
 		if (!$i) {
3278
-			say($ind + 2, 'Skipping as the symbol table entry for index 0 (STN_UNDEF) is reserved');
3281
+			say($ind + 1, 'Skipping as the symbol table entry for index 0 (STN_UNDEF) is reserved');
3279 3282
 			next;
3280 3283
 		}
3281 3284
 
... ...
@@ -3326,7 +3329,7 @@ sub walkVersym {
3326 3329
 			}
3327 3330
 		}
3328 3331
 		explained:
3329
-		sayf($ind + 2, '%s: %s.', $printable_value, $explanation);
3332
+		sayf($ind + 1, '%s: %s.', $printable_value, $explanation);
3330 3333
 	}
3331 3334
 }
3332 3335
 
... ...
@@ -3334,7 +3337,7 @@ sub walkNote {
3334 3337
 	my $elf_fh = shift;
3335 3338
 	my $elf_data = shift;
3336 3339
 	my $section_index = shift;
3337
-	my $ind = 1;
3340
+	my $ind = 2;
3338 3341
 
3339 3342
 	my $section = $elf_data->{'sections'}->{$section_index};
3340 3343
 	return if (!defined($section));
... ...
@@ -3344,24 +3347,24 @@ sub walkNote {
3344 3347
 	my $note_info = $elf_structures->{'note'}->{$arch};
3345 3348
 
3346 3349
 	my $offset = $section->{'sh_offset'};
3347
-	sayf($ind + 1, 'Seeking to offset sh_offset = %s', formatOffset($offset));
3350
+	sayf($ind, 'Seeking to offset sh_offset = %s', formatOffset($offset));
3348 3351
 	seek($elf_fh, $offset, Fcntl::SEEK_SET);
3349 3352
 
3350 3353
 	# Read expected fields: namesz, descsz, type.
3351 3354
 	for my $entry (@{$note_info->{'ordered_entries'}}) {
3352 3355
 		my $value = readELFValue($elf_fh, $note_info->{$entry});
3353 3356
 		$elf_data->{'_notes'}->{$section_index}->{$entry} = $value;
3354
-		say($ind + 2, analyseAndExplainValue($value, $entry, $note_info->{$entry}));
3357
+		say($ind, analyseAndExplainValue($value, $entry, $note_info->{$entry}));
3355 3358
 	}
3356 3359
 
3357 3360
 	# Read the name.
3358 3361
 	my $name_size = $elf_data->{'_notes'}->{$section_index}->{'namesz'};
3359 3362
 	if ($name_size) {
3360 3363
 		my $name = readNullTerminatedString($elf_fh, $name_size);
3361
-		sayf($ind + 2, '%s is "%s".', colored('name', 'value_name'), colored($name, 'value'));
3364
+		sayf($ind, '%s is "%s".', colored('name', 'value_name'), colored($name, 'value'));
3362 3365
 	}
3363 3366
 	else {
3364
-		sayf($ind + 2, '%s will not be read as %s is 0.', colored('name', 'value_name'), colored('namesz', 'value_name'));
3367
+		sayf($ind, '%s will not be read as %s is 0.', colored('name', 'value_name'), colored('namesz', 'value_name'));
3365 3368
 	}
3366 3369
 
3367 3370
 	# Cross potential padding: "Padding is present, if necessary, to ensure
... ...
@@ -3370,37 +3373,37 @@ sub walkNote {
3370 3373
 	my $cur_pos = my $new_pos = tell($elf_fh);
3371 3374
 	++ $new_pos while ($new_pos % 4);
3372 3375
 	if ($new_pos != $cur_pos) {
3373
-		sayf($ind + 2, 'Seeking to offset %s to cross the padding', formatOffset($new_pos));
3376
+		sayf($ind, 'Seeking to offset %s to cross the padding', formatOffset($new_pos));
3374 3377
 		seek($elf_fh, $new_pos, Fcntl::SEEK_SET);
3375 3378
 	}
3376 3379
 
3377 3380
 	# Read the descriptor.
3378 3381
 	if ($section->{'_name'} eq '.note.ABI-tag') {
3379
-		sayf($ind + 2, 'The note section being named %s, we know how to interpret the descriptor:', colored('.note.ABI-tag', 'name'));
3382
+		sayf($ind, 'The note section being named %s, we know how to interpret the descriptor:', colored('.note.ABI-tag', 'name'));
3380 3383
 		my $value = readELFValue($elf_fh, 'Word');
3381
-		sayf($ind + 3, '%s: this is expected to be 0, signifying a Linux executable.', colored(formatValue($value, 'Word'), 'value'));
3384
+		sayf($ind + 1, '%s: this is expected to be 0, signifying a Linux executable.', colored(formatValue($value, 'Word'), 'value'));
3382 3385
 		my $kv1 = readELFValue($elf_fh, 'Word');
3383
-		sayf($ind + 3, '%s: this is the earliest kernel version compatible with this file', colored(formatValue($kv1, 'Word'), 'value'));
3386
+		sayf($ind + 1, '%s: this is the earliest kernel version compatible with this file', colored(formatValue($kv1, 'Word'), 'value'));
3384 3387
 		my $kv2 = readELFValue($elf_fh, 'Word');
3385
-		sayf($ind + 3, '%s: this is the earliest kernel major revision compatible with this file', colored(formatValue($kv2, 'Word'), 'value'));
3388
+		sayf($ind + 1, '%s: this is the earliest kernel major revision compatible with this file', colored(formatValue($kv2, 'Word'), 'value'));
3386 3389
 		my $kv3 = readELFValue($elf_fh, 'Word');
3387
-		sayf($ind + 3, '%s: this is the earliest kernel minor revision compatible with this file', colored(formatValue($kv3, 'Word'), 'value'));
3390
+		sayf($ind + 1, '%s: this is the earliest kernel minor revision compatible with this file', colored(formatValue($kv3, 'Word'), 'value'));
3388 3391
 		$elf_data->{'_kernel'} = sprintf('%d.%d.%d', $kv1, $kv2, $kv3);
3389
-		sayf($ind + 2, 'Basically, this note states this file requires at least Linux kernel %s.', colored($elf_data->{'_kernel'}, 'value'));
3392
+		sayf($ind, 'Basically, this note states this file requires at least Linux kernel %s.', colored($elf_data->{'_kernel'}, 'value'));
3390 3393
 	}
3391 3394
 	elsif ($section->{'_name'} eq '.note.gnu.build-id') {
3392 3395
 		my $desc_size = $elf_data->{'_notes'}->{$section_index}->{'descsz'};
3393
-		sayf($ind + 2, 'The note section being named %s, we know how to interpret the descriptor:', colored('.note.gnu.build-id', 'name'));
3394
-		sayf($ind + 3, 'This descriptor is expected to host a unique build ID, for debugging purposes.');
3396
+		sayf($ind, 'The note section being named %s, we know how to interpret the descriptor:', colored('.note.gnu.build-id', 'name'));
3397
+		sayf($ind + 1, 'This descriptor is expected to host a unique build ID, for debugging purposes.');
3395 3398
 		# I took the same heuristic as "file", with a minimal amount of shame.
3396 3399
 		my $id_type = ($desc_size == 16) ? 'a MD5 hash or a UUID' : 'a SHA1 hash';
3397
-		sayf($ind + 3, 'This descriptor being %d bytes, it is very likely to host %s.', $desc_size, $id_type);
3400
+		sayf($ind + 1, 'This descriptor being %d bytes, it is very likely to host %s.', $desc_size, $id_type);
3398 3401
 		my $build_id = '';
3399 3402
 		for (my $i = 0; $i < $desc_size; ++ $i) {
3400 3403
 			my $byte = readELFValue($elf_fh, 'char');
3401 3404
 			$build_id .=  sprintf('%02x', $byte);
3402 3405
 		}
3403
-		sayf($ind + 3, 'The build ID of this file is: %s.', colored($build_id, 'value'));
3406
+		sayf($ind + 1, 'The build ID of this file is: %s.', colored($build_id, 'value'));
3404 3407
 	}
3405 3408
 }
3406 3409
 
... ...
@@ -3408,23 +3411,23 @@ sub walkProgBits {
3408 3411
 	my $elf_fh = shift;
3409 3412
 	my $elf_data = shift;
3410 3413
 	my $section_index = shift;
3411
-	my $ind = 1;
3414
+	my $ind = 2;
3412 3415
 
3413 3416
 	my $section = $elf_data->{'sections'}->{$section_index};
3414 3417
 	return if (!defined($section));
3415 3418
 
3416 3419
 	my $offset = $section->{'sh_offset'};
3417
-	sayf($ind + 1, 'Seeking to offset sh_offset = %s', formatOffset($offset));
3420
+	sayf($ind, 'Seeking to offset sh_offset = %s', formatOffset($offset));
3418 3421
 	seek($elf_fh, $offset, Fcntl::SEEK_SET);
3419 3422
 
3420 3423
 	if ($section->{'_name'} eq '.interp') {
3421 3424
 		my $interp = readNullTerminatedString($elf_fh, $section->{'sh_size'});
3422 3425
 		if (length($interp)) {
3423 3426
 			$elf_data->{'.interp'} = $interp;
3424
-			sayf($ind + 1, 'This ELF file shall be interpreted using %s.', colored($interp, 'value'));
3427
+			sayf($ind, 'This ELF file shall be interpreted using %s.', colored($interp, 'value'));
3425 3428
 		}
3426 3429
 		else {
3427
-			sayf($ind + 1, 'Error: unable to retrieve the path of the interpreter.');
3430
+			sayf($ind, 'Error: unable to retrieve the path of the interpreter.');
3428 3431
 		}
3429 3432
 	}
3430 3433
 }
Browse code

Display the symbol lookup path, via .gnu.hash, for each symbol in .dynsym.

Xavier G authored on 10/08/2015 02:10:22
Showing 1 changed files
... ...
@@ -3659,7 +3659,72 @@ sub displayHashDetailsForSymbol {
3659 3659
 			}
3660 3660
 		}
3661 3661
 	}
3662
-	# TODO do the same for GNU hash sections
3662
+
3663
+	if (exists($elf_data->{'gnu_hash'})) {
3664
+		for my $gnu_section_index (keys(%{$elf_data->{'gnu_hash'}})) {
3665
+			my $gnu_section = $elf_data->{'gnu_hash'}->{$gnu_section_index};
3666
+			sayf(
3667
+				$ind,
3668
+				'Path to reach that symbol index (i.e. #%d) from its name through GNU hash section %s:',
3669
+				$symbol_index,
3670
+				colored($gnu_section->{'name'}, 'name')
3671
+			);
3672
+
3673
+			if ($symbol_index < $gnu_section->{'symndx'}) {
3674
+				sayf($ind + 1, 'This symbol is not meant to be accessible via the GNU hash section.');
3675
+				return;
3676
+			}
3677
+
3678
+			my $gnu_hash = hashSymbolNameGNUStyle($symbol->{'_name'});
3679
+			sayf($ind + 1, 'GNU hash: %s', colored(sprintf('0x%08x', $gnu_hash), 'value'));
3680
+			my $bucket_entry_index = $gnu_hash % $gnu_section->{'nbuckets'};
3681
+			my $bucket_entry_offset = $gnu_section->{'bucket_offset'} + ($bucket_entry_index * $sizeof_word);
3682
+			seek($elf_fh, $bucket_entry_offset, Fcntl::SEEK_SET);
3683
+			my $bucket_entry_value = readELFValue($elf_fh, 'Word');
3684
+			sayf(
3685
+				$ind + 1,
3686
+				'index y = bucket[GNU hash %% nbucket] = bucket[%s %% %s] = bucket[%s] = %s',
3687
+				colored(sprintf('0x%08x', $gnu_hash), 'value'),
3688
+				colored($gnu_section->{'nbuckets'}, 'value'),
3689
+				colored($bucket_entry_index, 'value'),
3690
+				colored($bucket_entry_value, 'value')
3691
+			);
3692
+			if (!$bucket_entry_value) {
3693
+				sayf($ind + 1, 'Got empty hash chain, cannot reach symbol.');
3694
+			}
3695
+
3696
+			for (my $i = 0, my $index = $bucket_entry_value; $i < 100; ++ $i, ++ $index) {
3697
+				my $chain_entry_offset = $gnu_section->{'chain_offset'} + (($index - $gnu_section->{'symndx'}) * $sizeof_word);
3698
+				seek($elf_fh, $chain_entry_offset, Fcntl::SEEK_SET);
3699
+				my $chain_entry_value = readELFValue($elf_fh, 'Word');
3700
+				sayf(
3701
+					$ind + 1,
3702
+					'chain[y - %s] = chain[%s - %s] = chain[%s] = %s',
3703
+					colored('symndx', 'value_name'),
3704
+					colored($index, 'value'),
3705
+					colored($gnu_section->{'symndx'}, 'value'),
3706
+					colored($index - $gnu_section->{'symndx'}, 'value'),
3707
+					colored(sprintf('0x%08x', $chain_entry_value), 'value'),
3708
+				);
3709
+				if (($gnu_hash & ~1) == ($chain_entry_value & ~1)) {
3710
+					my $conclusion = 'The first 31 bits match the GNU hash of the symbol we are looking for';
3711
+					# We do not need to compare names as we already know what
3712
+					# index we are looking for.
3713
+					if ($index != $symbol_index) {
3714
+						sayf($ind + 1, $conclusion . ', but this is not the expected symbol index: we must continue.');
3715
+					}
3716
+					else {
3717
+						sayf($ind + 1, $conclusion . ' -- symbol index reached');
3718
+						last;
3719
+					}
3720
+				}
3721
+				if ($chain_entry_value & 1) {
3722
+					sayf($ind + 1, 'Reached end of chain, unable to reach symbol index.');
3723
+					last;
3724
+				}
3725
+			}
3726
+		}
3727
+	}
3663 3728
 
3664 3729
 	# Restore the position of the file handler as it was before we mess with it.
3665 3730
 	seek($elf_fh, $pos, Fcntl::SEEK_SET);
Browse code

Display the contents of SHT_GNU_HASH sections.

Xavier G authored on 08/08/2015 16:26:20
Showing 1 changed files
... ...
@@ -2548,6 +2548,15 @@ sub walkELFSections {
2548 2548
 		}
2549 2549
 	}
2550 2550
 
2551
+	say(0, colored('Parsing SHT_GNU_HASH sections', 'main_action'));
2552
+	for (my $i = 1; $i < $elf_data->{'e_shnum'}; ++ $i) {
2553
+		my $section = $elf_data->{'sections'}->{$i};
2554
+		if ($section->{'_type'} eq 'SHT_GNU_HASH') {
2555
+			announceOperation($ind, 'Parsing section', $i, ' (' . colored($section->{'_name'}, 'name') . '):');
2556
+			walkGnuHashSection($elf_fh, $elf_data, $i);
2557
+		}
2558
+	}
2559
+
2551 2560
 	say(0, colored('Parsing SHT_DYNSYM sections (and associated SHT_STRTAB sections)', 'main_action'));
2552 2561
 	for (my $i = 1; $i < $elf_data->{'e_shnum'}; ++ $i) {
2553 2562
 		my $section = $elf_data->{'sections'}->{$i};
Browse code

Introduce walkGnuHashSection().

Xavier G authored on 08/08/2015 16:25:33
Showing 1 changed files
... ...
@@ -2897,6 +2897,90 @@ sub walkHashSection {
2897 2897
 	}
2898 2898
 }
2899 2899
 
2900
+sub walkGnuHashSection {
2901
+	my $elf_fh = shift;
2902
+	my $elf_data = shift;
2903
+	my $section_index = shift;
2904
+	my $ind = 2;
2905
+
2906
+	my $section = $elf_data->{'sections'}->{$section_index};
2907
+	return if (!defined($section));
2908
+
2909
+	my $linked_section = displayLinkedSection($ind, $elf_data, $section_index);
2910
+	my $symbols_count = 0;
2911
+	if (defined($linked_section)) {
2912
+		$symbols_count = $linked_section->{'sh_size'} / $linked_section->{'sh_entsize'};
2913
+	}
2914
+
2915
+	my $offset = $section->{'sh_offset'};
2916
+	sayf($ind, 'Seeking to offset sh_offset = %s', formatOffset($offset));
2917
+	seek($elf_fh, $offset, Fcntl::SEEK_SET);
2918
+
2919
+	# Analyse the header of the hash section.
2920
+	sayf($ind, 'This GNU hash section holds:');
2921
+	sayf($ind, '  - a header, made of 4 32-bit words:');
2922
+	my $gnu_hash = {
2923
+		'_index' => $section_index,
2924
+		'name' => $section->{'_name'},
2925
+	};
2926
+	for my $entry (@{$elf_structures->{'gnu_hash'}->{'ordered_entries'}}) {
2927
+		my $type = $elf_structures->{'gnu_hash'}->{$entry};
2928
+		my $value = $gnu_hash->{$entry} = readELFValue($elf_fh, $type);
2929
+		say($ind + 2, analyseAndExplainValue($value, $entry, $type));
2930
+
2931
+		if ($entry eq 'maskwords') {
2932
+			my $details = 'This value must be a power of two.';
2933
+			if (!isPowerOfTwo($value)) {
2934
+				sayf($ind + 1, colored($details, 'error'));
2935
+			}
2936
+		}
2937
+	}
2938
+
2939
+	# Compute extra values related to the Bloom filter/mask.
2940
+	$gnu_hash->{'sizeof_maskword'} = $default_class * 4;
2941
+	$gnu_hash->{'mask_offset'} = $offset + (4 * typeInfo('Word')->{'size'});
2942
+	$gnu_hash->{'mask_size'} = $gnu_hash->{'maskwords'} * $gnu_hash->{'sizeof_maskword'};
2943
+
2944
+	# Compute extra values related to the buckets.
2945
+	$gnu_hash->{'bucket_offset'} = $gnu_hash->{'mask_offset'} + $gnu_hash->{'mask_size'};
2946
+	$gnu_hash->{'bucket_size'} = $gnu_hash->{'nbuckets'} * 4;
2947
+
2948
+	# Compute extra values related to the chains.
2949
+	$gnu_hash->{'chain_offset'} = $gnu_hash->{'bucket_offset'} + $gnu_hash->{'bucket_size'};
2950
+	$gnu_hash->{'chain_count'} = $symbols_count - $gnu_hash->{'symndx'};
2951
+	$gnu_hash->{'chain_size'} = $gnu_hash->{'nbuckets'} * 4;
2952
+
2953
+	# Display these pieces of information.
2954
+	sayf(
2955
+		$ind,
2956
+		'  - 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',
2957
+		colored($gnu_hash->{'maskwords'}, 'value'),
2958
+		$default_class * 32,
2959
+		$gnu_hash->{'mask_size'},
2960
+		formatOffset($gnu_hash->{'mask_offset'})
2961
+	);
2962
+	sayf(
2963
+		$ind,
2964
+		'  - an array of %s 32-bit hash buckets, i.e. %d bytes, from %s.',
2965
+		colored($gnu_hash->{'nbuckets'}, 'value'),
2966
+		$gnu_hash->{'bucket_size'},
2967
+		formatOffset($gnu_hash->{'bucket_offset'})
2968
+	);
2969
+	sayf(
2970
+		$ind,
2971
+		'  - 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',
2972
+		colored($gnu_hash->{'chain_count'}, 'value'),
2973
+		$symbols_count,
2974
+		colored($linked_section->{'_name'}, 'name'),
2975
+		colored('symndx', 'value_name'),
2976
+		$gnu_hash->{'chain_size'},
2977
+		formatOffset($gnu_hash->{'chain_offset'})
2978
+	);
2979
+
2980
+	# Store these pieces of information for later use.
2981
+	$elf_data->{'gnu_hash'}->{$section_index} = $gnu_hash;
2982
+}
2983
+
2900 2984
 sub walkRela {
2901