Elf64Parser::parseSymbolTable()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 25
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 17
c 1
b 0
f 0
nc 2
nop 4
dl 0
loc 25
rs 9.7
1
<?php
2
3
/**
4
 * This file is part of the sj-i/php-profiler package.
5
 *
6
 * (c) sji <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace PhpProfiler\Lib\Elf\Parser;
15
16
use PhpProfiler\Lib\ByteStream\IntegerByteSequence\IntegerByteSequenceReader;
17
use PhpProfiler\Lib\ByteStream\ByteReaderInterface;
18
use PhpProfiler\Lib\Elf\Structure\Elf64\Elf64DynamicStructure;
19
use PhpProfiler\Lib\Elf\Structure\Elf64\Elf64DynamicStructureArray;
20
use PhpProfiler\Lib\Elf\Structure\Elf64\Elf64GnuHashTable;
21
use PhpProfiler\Lib\Elf\Structure\Elf64\Elf64Header;
22
use PhpProfiler\Lib\Elf\Structure\Elf64\Elf64ProgramHeaderEntry;
23
use PhpProfiler\Lib\Elf\Structure\Elf64\Elf64ProgramHeaderTable;
24
use PhpProfiler\Lib\Elf\Structure\Elf64\Elf64SectionHeaderEntry;
25
use PhpProfiler\Lib\Elf\Structure\Elf64\Elf64SectionHeaderTable;
26
use PhpProfiler\Lib\Elf\Structure\Elf64\Elf64StringTable;
27
use PhpProfiler\Lib\Elf\Structure\Elf64\Elf64SymbolTable;
28
use PhpProfiler\Lib\Elf\Structure\Elf64\Elf64SymbolTableEntry;
29
30
final class Elf64Parser
31
{
32
    public function __construct(
33
        private IntegerByteSequenceReader $integer_reader
34
    ) {
35
    }
36
37
    public function parseElfHeader(ByteReaderInterface $data): Elf64Header
38
    {
39
        $e_ident = [
40
            $this->integer_reader->read8($data, 0),
41
            $this->integer_reader->read8($data, 1),
42
            $this->integer_reader->read8($data, 2),
43
            $this->integer_reader->read8($data, 3),
44
            $this->integer_reader->read8($data, 4),
45
            $this->integer_reader->read8($data, 5),
46
            $this->integer_reader->read8($data, 6),
47
            $this->integer_reader->read8($data, 7),
48
            $this->integer_reader->read8($data, 8),
49
            $this->integer_reader->read8($data, 9),
50
        ];
51
        $e_type = $this->integer_reader->read16($data, 16);
52
        $e_machine = $this->integer_reader->read16($data, 18);
53
        $e_version = $this->integer_reader->read32($data, 20);
54
        $e_entry = $this->integer_reader->read64($data, 24);
55
        $e_phoff = $this->integer_reader->read64($data, 32);
56
        $e_shoff = $this->integer_reader->read64($data, 40);
57
        $e_flags = $this->integer_reader->read32($data, 48);
58
        $e_ehsize = $this->integer_reader->read16($data, 52);
59
        $e_phentsize = $this->integer_reader->read16($data, 54);
60
        $e_phnum = $this->integer_reader->read16($data, 56);
61
        $e_shentsize = $this->integer_reader->read16($data, 58);
62
        $e_shnum = $this->integer_reader->read16($data, 60);
63
        $e_shstrndx = $this->integer_reader->read16($data, 62);
64
65
        return new Elf64Header(
66
            $e_ident,
67
            $e_type,
68
            $e_machine,
69
            $e_version,
70
            $e_entry,
71
            $e_phoff,
72
            $e_shoff,
73
            $e_flags,
74
            $e_ehsize,
75
            $e_phentsize,
76
            $e_phnum,
77
            $e_shentsize,
78
            $e_shnum,
79
            $e_shstrndx
80
        );
81
    }
82
83
    public function parseProgramHeader(ByteReaderInterface $data, Elf64Header $elf_header): Elf64ProgramHeaderTable
84
    {
85
        $program_header_table = [];
86
87
        for ($i = 0; $i < $elf_header->e_phnum; $i++) {
88
            $offset = $elf_header->e_phoff->toInt() + $elf_header->e_phentsize * $i;
89
            $p_type = $this->integer_reader->read32($data, $offset);
90
            $p_flags = $this->integer_reader->read32($data, $offset + 4);
91
            $p_offset = $this->integer_reader->read64($data, $offset + 8);
92
            $p_vaddr = $this->integer_reader->read64($data, $offset + 16);
93
            $p_paddr = $this->integer_reader->read64($data, $offset + 24);
94
            $p_filesz = $this->integer_reader->read64($data, $offset + 32);
95
            $p_memsz = $this->integer_reader->read64($data, $offset + 40);
96
            $p_align = $this->integer_reader->read64($data, $offset + 48);
97
            $program_header_table[] = new Elf64ProgramHeaderEntry(
98
                $p_type,
99
                $p_flags,
100
                $p_offset,
101
                $p_vaddr,
102
                $p_paddr,
103
                $p_filesz,
104
                $p_memsz,
105
                $p_align
106
            );
107
        }
108
109
        return new Elf64ProgramHeaderTable(...$program_header_table);
110
    }
111
112
    public function parseDynamicStructureArray(
113
        ByteReaderInterface $data,
114
        Elf64ProgramHeaderEntry $pt_dynamic
115
    ): Elf64DynamicStructureArray {
116
        $dynamic_array = [];
117
        $offset = $pt_dynamic->p_offset->lo;
118
        do {
119
            $d_tag = $this->integer_reader->read64($data, $offset);
120
            $d_un = $this->integer_reader->read64($data, $offset + 8);
121
            $dynamic_structure = new Elf64DynamicStructure($d_tag, $d_un);
122
            $dynamic_array[] = $dynamic_structure;
123
            $offset += 16;
124
        } while (!$dynamic_structure->isEnd());
125
126
        return new Elf64DynamicStructureArray(...$dynamic_array);
127
    }
128
129
    public function parseStringTable(
130
        ByteReaderInterface $data,
131
        Elf64DynamicStructureArray $dynamic_structure_array
132
    ): Elf64StringTable {
133
        /**
134
         * @var Elf64DynamicStructure $dt_strtab
135
         * @var Elf64DynamicStructure $dt_strsz
136
         */
137
        [
138
            Elf64DynamicStructure::DT_STRTAB => $dt_strtab,
139
            Elf64DynamicStructure::DT_STRSZ => $dt_strsz
140
        ] = $dynamic_structure_array->findStringTableEntries();
141
        $offset = $dt_strtab->d_un->toInt();
142
        $size = $dt_strsz->d_un->toInt();
143
        $string_table_region = $data->createSliceAsString($offset, $size);
144
145
        return new Elf64StringTable($string_table_region);
146
    }
147
148
    public function parseStringTableFromSectionHeader(
149
        ByteReaderInterface $data,
150
        Elf64SectionHeaderEntry $section_header_entry
151
    ): Elf64StringTable {
152
        $string_table_region = $data->createSliceAsString(
153
            $section_header_entry->sh_offset->toInt(),
154
            $section_header_entry->sh_size->toInt()
155
        );
156
157
        return new Elf64StringTable($string_table_region);
158
    }
159
160
    public function parseSymbolTableFromDynamic(
161
        ByteReaderInterface $data,
162
        Elf64DynamicStructureArray $dynamic_structure_array,
163
        int $number_of_symbols
164
    ): Elf64SymbolTable {
165
        /**
166
         * @var Elf64DynamicStructure $dt_symtab
167
         * @var Elf64DynamicStructure $dt_syment
168
         */
169
        [
170
            Elf64DynamicStructure::DT_SYMTAB => $dt_symtab,
171
            Elf64DynamicStructure::DT_SYMENT => $dt_syment
172
        ] = $dynamic_structure_array->findSymbolTablEntries();
173
174
        $start_offset = $dt_symtab->d_un->toInt();
175
        $entry_size = $dt_syment->d_un->toInt();
176
177
        return $this->parseSymbolTable($data, $start_offset, $number_of_symbols, $entry_size);
178
    }
179
180
    public function parseSymbolTableFromSectionHeader(
181
        ByteReaderInterface $data,
182
        Elf64SectionHeaderEntry $section
183
    ): Elf64SymbolTable {
184
        return $this->parseSymbolTable(
185
            $data,
186
            $section->sh_offset->toInt(),
187
            (int)($section->sh_size->toInt() / $section->sh_entsize->toInt()),
188
            $section->sh_entsize->toInt()
189
        );
190
    }
191
192
    public function parseSymbolTable(
193
        ByteReaderInterface $data,
194
        int $start_offset,
195
        int $number_of_symbols,
196
        int $entry_size
197
    ): Elf64SymbolTable {
198
        $symbol_table_array = [];
199
        for ($i = 0; $i < $number_of_symbols; $i++) {
200
            $offset = $start_offset + $i * $entry_size;
201
            $st_name = $this->integer_reader->read32($data, $offset);
202
            $st_info = $this->integer_reader->read8($data, $offset + 4);
203
            $st_other = $this->integer_reader->read8($data, $offset + 5);
204
            $st_shndx = $this->integer_reader->read16($data, $offset + 6);
205
            $st_value = $this->integer_reader->read64($data, $offset + 8);
206
            $st_size = $this->integer_reader->read64($data, $offset + 16);
207
            $symbol_table_array[] = new Elf64SymbolTableEntry(
208
                $st_name,
209
                $st_info,
210
                $st_other,
211
                $st_shndx,
212
                $st_value,
213
                $st_size
214
            );
215
        }
216
        return new Elf64SymbolTable(...$symbol_table_array);
217
    }
218
219
    /**
220
     * @param ByteReaderInterface $data
221
     * @param Elf64DynamicStructureArray $dynamic_structure_array
222
     * @return Elf64GnuHashTable|null
223
     */
224
    public function parseGnuHashTable(
225
        ByteReaderInterface $data,
226
        Elf64DynamicStructureArray $dynamic_structure_array
227
    ): ?Elf64GnuHashTable {
228
        $dt_gnu_hash = $dynamic_structure_array->findGnuHashTableEntry();
229
        if (is_null($dt_gnu_hash)) {
230
            return null;
231
        }
232
        $offset = $dt_gnu_hash->d_un->toInt();
233
        $nbuckets = $this->integer_reader->read32($data, $offset);
234
        $symoffset = $this->integer_reader->read32($data, $offset + 4);
235
        $bloom_size = $this->integer_reader->read32($data, $offset + 8);
236
        $bloom_shift = $this->integer_reader->read32($data, $offset + 12);
237
        $bloom_offset = $offset + 16;
238
        $bloom = [];
239
        for ($i = 0; $i < $bloom_size; $i++) {
240
            $bloom[] = $this->integer_reader->read64($data, $bloom_offset + $i * 8);
241
        }
242
        $buckets_offset = $offset + 16 + $bloom_size * 8;
243
        $buckets = [];
244
        for ($i = 0; $i < $nbuckets; $i++) {
245
            $buckets[] = $this->integer_reader->read32($data, $buckets_offset + $i * 4);
246
        }
247
248
        $chain_offset = $offset + 16 + $bloom_size * 8 + $nbuckets * 4;
249
250
        if ($buckets === []) {
251
            return null;
252
        }
253
254
        $max_bucket_index = max($buckets);
255
        $last_chain_offset = $chain_offset + ($max_bucket_index - $symoffset) * 4;
256
        $last_chain_item = $this->integer_reader->read32($data, $last_chain_offset);
257
        for (; ($last_chain_item & 1) === 0; $last_chain_offset += 4) {
258
            $last_chain_item = $this->integer_reader->read32($data, $last_chain_offset);
259
        }
260
261
        $chain = [];
262
        for (; $chain_offset <= $last_chain_offset; $chain_offset += 4) {
263
            $chain[] = $this->integer_reader->read32($data, $chain_offset);
264
        }
265
266
        return new Elf64GnuHashTable(
267
            $nbuckets,
268
            $symoffset,
269
            $bloom_size,
270
            $bloom_shift,
271
            $bloom,
272
            $buckets,
273
            $chain
274
        );
275
    }
276
277
    public function parseSectionHeader(ByteReaderInterface $data, Elf64Header $elf_header): Elf64SectionHeaderTable
278
    {
279
        $section_header_array = [];
280
281
        $offset = $elf_header->e_shoff->toInt();
282
        for ($i = 0; $i < $elf_header->e_shnum; $i++) {
283
            $sh_name = $this->integer_reader->read32($data, $offset);
284
            $sh_type = $this->integer_reader->read32($data, $offset + 4);
285
            $sh_flags = $this->integer_reader->read64($data, $offset + 8);
286
            $sh_addr = $this->integer_reader->read64($data, $offset + 16);
287
            $sh_offset = $this->integer_reader->read64($data, $offset + 24);
288
            $sh_size = $this->integer_reader->read64($data, $offset + 32);
289
            $sh_link = $this->integer_reader->read32($data, $offset + 40);
290
            $sh_info = $this->integer_reader->read32($data, $offset + 44);
291
            $sh_addralign = $this->integer_reader->read64($data, $offset + 48);
292
            $sh_entsize = $this->integer_reader->read64($data, $offset + 56);
293
            $section_header_array[] = new Elf64SectionHeaderEntry(
294
                $sh_name,
295
                $sh_type,
296
                $sh_flags,
297
                $sh_addr,
298
                $sh_offset,
299
                $sh_size,
300
                $sh_link,
301
                $sh_info,
302
                $sh_addralign,
303
                $sh_entsize
304
            );
305
306
            $offset += $elf_header->e_shentsize;
307
        }
308
309
        return new Elf64SectionHeaderTable(
310
            $this->parseStringTableFromSectionHeader(
311
                $data,
312
                $section_header_array[$elf_header->e_shstrndx]
313
            ),
314
            ...$section_header_array
315
        );
316
    }
317
}
318