1 /*
2 * Copyright 2021 Advanced Micro Devices, Inc.
3 *
4 * SPDX-License-Identifier: MIT
5 */
6
7 /**
8 * \file ac_rgp_elf_object_pack.c
9 *
10 * This file provides functions to create elf object for rgp profiling.
11 * The functions in this file create 64bit elf code object irrespective
12 * of if the driver is compiled as 32 or 64 bit.
13 */
14
15 #include <stdint.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <libelf.h>
19 #include "ac_msgpack.h"
20 #include "ac_rgp.h"
21
22 #include "util/bitscan.h"
23 #include "util/u_math.h"
24
25 #ifndef EM_AMDGPU
26 // Old distributions may not have this enum constant
27 #define EM_AMDGPU 224
28 #endif
29
30 char hw_stage_string[RGP_HW_STAGE_MAX][4] = {
31 ".vs",
32 ".ls",
33 ".hs",
34 ".es",
35 ".gs",
36 ".ps",
37 ".cs"
38 };
39
40 char hw_stage_symbol_string[RGP_HW_STAGE_MAX][16] = {
41 "_amdgpu_vs_main",
42 "_amdgpu_ls_main",
43 "_amdgpu_hs_main",
44 "_amdgpu_es_main",
45 "_amdgpu_gs_main",
46 "_amdgpu_ps_main",
47 "_amdgpu_cs_main"
48 };
49
50 static const char *
get_api_stage_string(gl_shader_stage stage)51 get_api_stage_string(gl_shader_stage stage)
52 {
53 switch (stage) {
54 case MESA_SHADER_VERTEX:
55 return".vertex";
56 case MESA_SHADER_TESS_CTRL:
57 return".hull";
58 case MESA_SHADER_TESS_EVAL:
59 return".domain";
60 case MESA_SHADER_GEOMETRY:
61 return".geometry";
62 case MESA_SHADER_FRAGMENT:
63 return".pixel";
64 case MESA_SHADER_MESH:
65 return ".mesh";
66 case MESA_SHADER_TASK:
67 return ".task";
68 default:
69 /* RT shaders are implemented using compute HW stages, so use ".compute"
70 for any stage other than graphics stages */
71 return".compute";
72 }
73 }
74
75 static const char *
get_hw_stage_symbol(struct rgp_code_object_record * record,unsigned index)76 get_hw_stage_symbol(struct rgp_code_object_record *record, unsigned index)
77 {
78 if (record->is_rt)
79 return record->shader_data[index].rt_shader_name;
80 else
81 return hw_stage_symbol_string[record->shader_data[index].hw_stage];
82 }
83
84 static const char *
rt_subtype_from_stage(gl_shader_stage stage)85 rt_subtype_from_stage(gl_shader_stage stage)
86 {
87 switch (stage) {
88 case MESA_SHADER_RAYGEN:
89 return "RayGeneration";
90 case MESA_SHADER_MISS:
91 return "Miss";
92 case MESA_SHADER_CLOSEST_HIT:
93 return "ClosestHit";
94 case MESA_SHADER_CALLABLE:
95 return "Callable";
96 case MESA_SHADER_INTERSECTION:
97 return "Traversal";
98 /* There are also AnyHit and Intersection subtypes, but on RADV
99 * these are inlined into the traversal shader */
100 default:
101 return "Unknown";
102 }
103 }
104
105 /**
106 * rgp profiler requires data for few variables stored in msgpack format
107 * in notes section. This function writes the data from
108 * struct rgp_code_object_record to elf object in msgpack format.
109 * for msgpack specification refer to
110 * github.com/msgpack/msgpack/blob/master/spec.md
111 */
112 static void
ac_rgp_write_msgpack(FILE * output,struct rgp_code_object_record * record,uint32_t * written_size)113 ac_rgp_write_msgpack(FILE *output,
114 struct rgp_code_object_record *record,
115 uint32_t *written_size)
116 {
117 struct ac_msgpack msgpack;
118 uint32_t num_shaders;
119 uint32_t i;
120 uint32_t mask;
121
122 num_shaders = util_bitcount(record->shader_stages_mask);
123
124 ac_msgpack_init(&msgpack);
125
126 ac_msgpack_add_fixmap_op(&msgpack, 2);
127 ac_msgpack_add_fixstr(&msgpack, "amdpal.version");
128 ac_msgpack_add_fixarray_op(&msgpack, 2);
129 ac_msgpack_add_uint(&msgpack, 2);
130 ac_msgpack_add_uint(&msgpack, 1);
131
132 ac_msgpack_add_fixstr(&msgpack, "amdpal.pipelines");
133 ac_msgpack_add_fixarray_op(&msgpack, 1);
134 ac_msgpack_add_fixmap_op(&msgpack, 6 + record->is_rt);
135
136 /* 1
137 * This not used in RGP but data needs to be present
138 */
139 ac_msgpack_add_fixstr(&msgpack, ".spill_threshold");
140 ac_msgpack_add_uint(&msgpack, 0xffff);
141
142 /* 2
143 * This not used in RGP but data needs to be present
144 */
145 ac_msgpack_add_fixstr(&msgpack, ".user_data_limit");
146 ac_msgpack_add_uint(&msgpack, 32);
147
148 /* 3 */
149 ac_msgpack_add_fixstr(&msgpack, ".shaders");
150 ac_msgpack_add_fixmap_op(&msgpack, num_shaders);
151 mask = record->shader_stages_mask;
152 while(mask) {
153 i = u_bit_scan(&mask);
154 ac_msgpack_add_fixstr(&msgpack, get_api_stage_string(i));
155 ac_msgpack_add_fixmap_op(&msgpack, 2);
156 ac_msgpack_add_fixstr(&msgpack, ".api_shader_hash");
157 ac_msgpack_add_fixarray_op(&msgpack, 2);
158 ac_msgpack_add_uint(&msgpack,
159 record->shader_data[i].hash[0]);
160 ac_msgpack_add_uint(&msgpack, 0);
161 ac_msgpack_add_fixstr(&msgpack, ".hardware_mapping");
162 ac_msgpack_add_fixarray_op(&msgpack, 1);
163 ac_msgpack_add_fixstr(&msgpack, hw_stage_string[
164 record->shader_data[i].hw_stage]);
165 }
166
167 /* 4 */
168 ac_msgpack_add_fixstr(&msgpack, ".hardware_stages");
169 ac_msgpack_add_fixmap_op(&msgpack,
170 record->num_shaders_combined);
171 mask = record->shader_stages_mask;
172 while(mask) {
173 i = u_bit_scan(&mask);
174
175 if (record->shader_data[i].is_combined)
176 continue;
177
178 ac_msgpack_add_fixstr(&msgpack, hw_stage_string[
179 record->shader_data[i].hw_stage]);
180 ac_msgpack_add_fixmap_op(&msgpack, 6);
181 ac_msgpack_add_fixstr(&msgpack, ".entry_point");
182 ac_msgpack_add_fixstr(&msgpack, get_hw_stage_symbol(record, i));
183
184 ac_msgpack_add_fixstr(&msgpack, ".sgpr_count");
185 ac_msgpack_add_uint(&msgpack,
186 record->shader_data[i].sgpr_count);
187
188 ac_msgpack_add_fixstr(&msgpack, ".vgpr_count");
189 ac_msgpack_add_uint(&msgpack,
190 record->shader_data[i].vgpr_count);
191
192 ac_msgpack_add_fixstr(&msgpack, ".scratch_memory_size");
193 ac_msgpack_add_uint(&msgpack,
194 record->shader_data[i].scratch_memory_size);
195
196 ac_msgpack_add_fixstr(&msgpack, ".wavefront_size");
197 ac_msgpack_add_uint(&msgpack,
198 record->shader_data[i].wavefront_size);
199
200 ac_msgpack_add_fixstr(&msgpack, ".lds_size");
201 ac_msgpack_add_uint(&msgpack, record->shader_data[i].lds_size);
202 }
203
204 /* 5 */
205 ac_msgpack_add_fixstr(&msgpack, ".internal_pipeline_hash");
206 ac_msgpack_add_fixarray_op(&msgpack, 2);
207 ac_msgpack_add_uint(&msgpack, record->pipeline_hash[0]);
208 ac_msgpack_add_uint(&msgpack, record->pipeline_hash[1]);
209
210 /* 6 */
211 ac_msgpack_add_fixstr(&msgpack, ".api");
212 ac_msgpack_add_fixstr(&msgpack, "Vulkan");
213
214 if (record->is_rt) {
215 /* 7 */
216 ac_msgpack_add_fixstr(&msgpack, ".shader_functions");
217 ac_msgpack_add_fixmap_op(&msgpack, num_shaders);
218 mask = record->shader_stages_mask;
219 while (mask) {
220 i = u_bit_scan(&mask);
221 ac_msgpack_add_fixstr(&msgpack, record->shader_data[i].rt_shader_name);
222 ac_msgpack_add_fixmap_op(&msgpack, 7);
223 ac_msgpack_add_fixstr(&msgpack, ".stack_frame_size_in_bytes");
224 ac_msgpack_add_uint(&msgpack, record->shader_data[i].rt_stack_size);
225
226 ac_msgpack_add_fixstr(&msgpack, ".shader_subtype");
227 ac_msgpack_add_fixstr(&msgpack, rt_subtype_from_stage(i));
228 ac_msgpack_add_fixstr(&msgpack, ".api_shader_hash");
229 ac_msgpack_add_fixarray_op(&msgpack, 2);
230 ac_msgpack_add_uint(&msgpack, record->pipeline_hash[0]);
231 ac_msgpack_add_uint(&msgpack, record->pipeline_hash[1]);
232
233 ac_msgpack_add_fixstr(&msgpack, ".sgpr_count");
234 ac_msgpack_add_uint(&msgpack, record->shader_data[i].sgpr_count);
235
236 ac_msgpack_add_fixstr(&msgpack, ".vgpr_count");
237 ac_msgpack_add_uint(&msgpack, record->shader_data[i].vgpr_count);
238
239 ac_msgpack_add_fixstr(&msgpack, ".lds_size");
240 ac_msgpack_add_uint(&msgpack, record->shader_data[i].lds_size);
241
242 ac_msgpack_add_fixstr(&msgpack, ".scratch_memory_size");
243 ac_msgpack_add_uint(&msgpack,
244 record->shader_data[i].scratch_memory_size);
245 }
246 }
247 ac_msgpack_resize_if_required(&msgpack, 4 - (msgpack.offset % 4));
248 msgpack.offset = ALIGN(msgpack.offset, 4);
249 fwrite(msgpack.mem, 1, msgpack.offset, output);
250 *written_size = msgpack.offset;
251 ac_msgpack_destroy(&msgpack);
252 }
253
254
255 static uint32_t
get_lowest_shader(uint32_t * shader_stages_mask,struct rgp_code_object_record * record,struct rgp_shader_data ** rgp_shader_data)256 get_lowest_shader(uint32_t *shader_stages_mask,
257 struct rgp_code_object_record *record,
258 struct rgp_shader_data **rgp_shader_data)
259 {
260 uint32_t i, lowest = 0;
261 uint32_t mask;
262 uint64_t base_address = -1;
263
264 if (*shader_stages_mask == 0)
265 return false;
266
267 mask = *shader_stages_mask;
268 while(mask) {
269 i = u_bit_scan(&mask);
270 if (record->shader_data[i].is_combined) {
271 *shader_stages_mask = *shader_stages_mask & ~((uint32_t)1 << i);
272 continue;
273 }
274 if (base_address > record->shader_data[i].base_address) {
275 lowest = i;
276 base_address = record->shader_data[i].base_address;
277 }
278 }
279
280 *shader_stages_mask = *shader_stages_mask & ~((uint32_t)1 << lowest);
281 *rgp_shader_data = &record->shader_data[lowest];
282 return true;
283 }
284
285 /**
286 * write the shader code into elf object in text section
287 */
288 static void
ac_rgp_file_write_elf_text(FILE * output,uint32_t * elf_size_calc,struct rgp_code_object_record * record,uint32_t * text_size)289 ac_rgp_file_write_elf_text(FILE *output, uint32_t *elf_size_calc,
290 struct rgp_code_object_record *record,
291 uint32_t *text_size)
292 {
293 struct rgp_shader_data *rgp_shader_data = NULL;
294 struct rgp_shader_data *prev_rgp_shader_data = NULL;
295 uint32_t symbol_offset = 0;
296 uint32_t mask = record->shader_stages_mask;
297 static bool warn_once = true;
298
299 while(get_lowest_shader(&mask, record, &rgp_shader_data)) {
300 if (prev_rgp_shader_data) {
301 uint32_t code_offset = rgp_shader_data->base_address -
302 prev_rgp_shader_data->base_address;
303 uint32_t gap_between_code = code_offset -
304 prev_rgp_shader_data->code_size;
305 symbol_offset += code_offset;
306 if (gap_between_code > 0x10000 && warn_once) {
307 fprintf(stderr, "Warning: shader code far from previous "
308 "(%d bytes apart). The rgp capture file "
309 "might be very large.\n", gap_between_code);
310 warn_once = false;
311 }
312
313 fseek(output, gap_between_code, SEEK_CUR);
314 *elf_size_calc += gap_between_code;
315 }
316
317 rgp_shader_data->elf_symbol_offset = symbol_offset;
318 fwrite(rgp_shader_data->code, 1, rgp_shader_data->code_size, output);
319 *elf_size_calc += rgp_shader_data->code_size;
320 prev_rgp_shader_data = rgp_shader_data;
321 }
322
323 symbol_offset += rgp_shader_data->code_size;
324 uint32_t aligned = ALIGN(symbol_offset, 256) - symbol_offset;
325 fseek(output, aligned, SEEK_CUR);
326 *elf_size_calc += aligned;
327 *text_size = symbol_offset + aligned;
328 }
329
330 /*
331 * hardcoded index for string table and text section in elf object.
332 * While populating section header table, the index order should
333 * be strictly followed.
334 */
335 #define RGP_ELF_STRING_TBL_SEC_HEADER_INDEX 1
336 #define RGP_ELF_TEXT_SEC_HEADER_INDEX 2
337
338 /*
339 * hardcode the string table so that is a single write to output.
340 * the strings are in a structure so that it is easy to get the offset
341 * of given string in string table.
342 */
343 struct ac_rgp_elf_string_table {
344 char null[sizeof("")];
345 char strtab[sizeof(".strtab")];
346 char text[sizeof(".text")];
347 char symtab[sizeof(".symtab")];
348 char note[sizeof(".note")];
349 char vs_main[sizeof("_amdgpu_vs_main")];
350 char ls_main[sizeof("_amdgpu_ls_main")];
351 char hs_main[sizeof("_amdgpu_hs_main")];
352 char es_main[sizeof("_amdgpu_es_main")];
353 char gs_main[sizeof("_amdgpu_gs_main")];
354 char ps_main[sizeof("_amdgpu_ps_main")];
355 char cs_main[sizeof("_amdgpu_cs_main")];
356 };
357
358 struct ac_rgp_elf_string_table rgp_elf_strtab = {
359 .null = "",
360 .strtab = ".strtab",
361 .text = ".text",
362 .symtab = ".symtab",
363 .note = ".note",
364 .vs_main = "_amdgpu_vs_main",
365 .ls_main = "_amdgpu_ls_main",
366 .hs_main = "_amdgpu_hs_main",
367 .es_main = "_amdgpu_es_main",
368 .gs_main = "_amdgpu_gs_main",
369 .ps_main = "_amdgpu_ps_main",
370 .cs_main = "_amdgpu_cs_main",
371 };
372
373 uint32_t rgp_elf_hw_stage_string_offset[RGP_HW_STAGE_MAX] = {
374 (uintptr_t)((struct ac_rgp_elf_string_table*)0)->vs_main,
375 (uintptr_t)((struct ac_rgp_elf_string_table*)0)->ls_main,
376 (uintptr_t)((struct ac_rgp_elf_string_table*)0)->hs_main,
377 (uintptr_t)((struct ac_rgp_elf_string_table*)0)->es_main,
378 (uintptr_t)((struct ac_rgp_elf_string_table*)0)->gs_main,
379 (uintptr_t)((struct ac_rgp_elf_string_table*)0)->ps_main,
380 (uintptr_t)((struct ac_rgp_elf_string_table*)0)->cs_main,
381 };
382
383
384 static void
ac_rgp_file_write_elf_symbol_table(FILE * output,uint32_t * elf_size_calc,struct rgp_code_object_record * record,uint32_t * symbol_table_size)385 ac_rgp_file_write_elf_symbol_table(FILE *output, uint32_t *elf_size_calc,
386 struct rgp_code_object_record *record,
387 uint32_t *symbol_table_size)
388 {
389 Elf64_Sym elf_sym;
390 uint32_t i;
391 uint32_t mask = record->shader_stages_mask;
392
393 memset(&elf_sym, 0x00, sizeof(elf_sym));
394 fwrite(&elf_sym, 1, sizeof(elf_sym), output);
395
396 uint32_t rt_name_offset = 0;
397
398 while(mask) {
399 i = u_bit_scan(&mask);
400 if (record->shader_data[i].is_combined)
401 continue;
402
403 if (record->is_rt) {
404 elf_sym.st_name = sizeof(rgp_elf_strtab) + rt_name_offset;
405 rt_name_offset += strlen(record->shader_data[i].rt_shader_name) + 1;
406 } else
407 elf_sym.st_name = rgp_elf_hw_stage_string_offset[record->shader_data[i].hw_stage];
408 elf_sym.st_info = STT_FUNC;
409 elf_sym.st_other = 0x0;
410 elf_sym.st_shndx = RGP_ELF_TEXT_SEC_HEADER_INDEX;
411 elf_sym.st_value = record->shader_data[i].elf_symbol_offset;
412 elf_sym.st_size = record->shader_data[i].code_size;
413 fwrite(&elf_sym, 1, sizeof(elf_sym), output);
414 }
415
416 *symbol_table_size = (record->num_shaders_combined + 1)
417 * sizeof(elf_sym);
418 *elf_size_calc += *symbol_table_size;
419 }
420
421
422 /* Below defines from from llvm project
423 * llvm/includel/llvm/BinaryFormat/ELF.h
424 */
425 #define ELFOSABI_AMDGPU_PAL 65
426 #define NT_AMDGPU_METADATA 32
427
428 uint8_t elf_ident[EI_NIDENT] = { ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3,
429 ELFCLASS64, ELFDATA2LSB, EV_CURRENT,
430 ELFOSABI_AMDGPU_PAL,
431 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
432 0x00, 0x00 };
433
434 #define NOTE_MSGPACK_NAME "AMDGPU"
435 struct ac_rgp_elf_note_msgpack_hdr {
436 Elf64_Nhdr hdr;
437 char name[sizeof(NOTE_MSGPACK_NAME)];
438 };
439
440 void
ac_rgp_file_write_elf_object(FILE * output,size_t file_elf_start,struct rgp_code_object_record * record,uint32_t * written_size,uint32_t flags)441 ac_rgp_file_write_elf_object(FILE *output, size_t file_elf_start,
442 struct rgp_code_object_record *record,
443 uint32_t *written_size, uint32_t flags)
444 {
445 Elf64_Ehdr elf_hdr;
446 Elf64_Shdr sec_hdr[5];
447 uint32_t elf_size_calc;
448 struct ac_rgp_elf_note_msgpack_hdr note_hdr;
449 uint32_t text_size = 0;
450 uint32_t symbol_table_size = 0;
451 uint32_t msgpack_size = 0;
452 size_t note_sec_start;
453 uint32_t sh_offset;
454 uint32_t strtab_size = sizeof(rgp_elf_strtab);
455
456 /* Give space for header in file. It will be written to file at the end */
457 fseek(output, sizeof(Elf64_Ehdr), SEEK_CUR);
458
459 elf_size_calc = sizeof(Elf64_Ehdr);
460
461 /* Initialize elf header */
462 memcpy(&elf_hdr.e_ident, &elf_ident, EI_NIDENT);
463 elf_hdr.e_type = ET_REL;
464 elf_hdr.e_machine = EM_AMDGPU;
465 elf_hdr.e_version = EV_CURRENT;
466 elf_hdr.e_entry = 0;
467 elf_hdr.e_flags = flags;
468 elf_hdr.e_shstrndx = 1; /* string table entry is hardcoded to 1*/
469 elf_hdr.e_phoff = 0;
470 elf_hdr.e_shentsize = sizeof(Elf64_Shdr);
471 elf_hdr.e_ehsize = sizeof(Elf64_Ehdr);
472 elf_hdr.e_phentsize = 0;
473 elf_hdr.e_phnum = 0;
474
475 /* write hardcoded string table */
476 fwrite(&rgp_elf_strtab, 1, sizeof(rgp_elf_strtab), output);
477 if (record->is_rt) {
478 uint32_t mask = record->shader_stages_mask;
479 while (mask) {
480 int i = u_bit_scan(&mask);
481
482 char *name = record->shader_data[i].rt_shader_name;
483 uint32_t name_len = strlen(name);
484
485 fwrite(name, 1, name_len + 1, output);
486 strtab_size += name_len + 1;
487 }
488 }
489 elf_size_calc += strtab_size;
490
491 /* write shader code as .text code */
492 ac_rgp_file_write_elf_text(output, &elf_size_calc, record, &text_size);
493
494 /* write symbol table */
495 ac_rgp_file_write_elf_symbol_table(output, &elf_size_calc, record,
496 &symbol_table_size);
497
498 /* write .note */
499 /* the .note section contains msgpack which stores variables */
500 note_sec_start = file_elf_start + elf_size_calc;
501 fseek(output, sizeof(struct ac_rgp_elf_note_msgpack_hdr), SEEK_CUR);
502 ac_rgp_write_msgpack(output, record, &msgpack_size);
503 note_hdr.hdr.n_namesz = sizeof(NOTE_MSGPACK_NAME);
504 note_hdr.hdr.n_descsz = msgpack_size;
505 note_hdr.hdr.n_type = NT_AMDGPU_METADATA;
506 memcpy(note_hdr.name, NOTE_MSGPACK_NAME "\0",
507 sizeof(NOTE_MSGPACK_NAME) + 1);
508 fseek(output, note_sec_start, SEEK_SET);
509 fwrite(¬e_hdr, 1, sizeof(struct ac_rgp_elf_note_msgpack_hdr), output);
510 fseek(output, 0, SEEK_END);
511 elf_size_calc += (msgpack_size +
512 sizeof(struct ac_rgp_elf_note_msgpack_hdr));
513
514 /* write section headers */
515 sh_offset = elf_size_calc;
516 memset(&sec_hdr[0], 0x00, sizeof(Elf64_Shdr) * 5);
517
518 /* string table must be at index 1 as used in other places*/
519 sec_hdr[1].sh_name = (uintptr_t)((struct ac_rgp_elf_string_table*)0)->strtab;
520 sec_hdr[1].sh_type = SHT_STRTAB;
521 sec_hdr[1].sh_offset = sizeof(Elf64_Ehdr);
522 sec_hdr[1].sh_size = strtab_size;
523
524 /* text must be at index 2 as used in other places*/
525 sec_hdr[2].sh_name = (uintptr_t)((struct ac_rgp_elf_string_table*)0)->text;
526 sec_hdr[2].sh_type = SHT_PROGBITS;
527 sec_hdr[2].sh_flags = SHF_ALLOC | SHF_EXECINSTR;
528 sec_hdr[2].sh_offset = sec_hdr[1].sh_offset + sec_hdr[1].sh_size;
529 sec_hdr[2].sh_size = text_size;
530 sec_hdr[2].sh_addralign = 256;
531
532 sec_hdr[3].sh_name = (uintptr_t)((struct ac_rgp_elf_string_table*)0)->symtab;
533 sec_hdr[3].sh_type = SHT_SYMTAB;
534 sec_hdr[3].sh_offset = sec_hdr[2].sh_offset +
535 align64(sec_hdr[2].sh_size, 256);
536 sec_hdr[3].sh_size = symbol_table_size;
537 sec_hdr[3].sh_link = RGP_ELF_STRING_TBL_SEC_HEADER_INDEX;
538 sec_hdr[3].sh_addralign = 8;
539 sec_hdr[3].sh_entsize = sizeof(Elf64_Sym);
540
541 sec_hdr[4].sh_name = (uintptr_t)((struct ac_rgp_elf_string_table*)0)->note;
542 sec_hdr[4].sh_type = SHT_NOTE;
543 sec_hdr[4].sh_offset = sec_hdr[3].sh_offset + sec_hdr[3].sh_size;
544 sec_hdr[4].sh_size = msgpack_size +
545 sizeof(struct ac_rgp_elf_note_msgpack_hdr);
546 sec_hdr[4].sh_addralign = 4;
547 fwrite(&sec_hdr, 1, sizeof(Elf64_Shdr) * 5, output);
548 elf_size_calc += (sizeof(Elf64_Shdr) * 5);
549
550 /* update and write elf header */
551 elf_hdr.e_shnum = 5;
552 elf_hdr.e_shoff = sh_offset;
553
554 fseek(output, file_elf_start, SEEK_SET);
555 fwrite(&elf_hdr, 1, sizeof(Elf64_Ehdr), output);
556 fseek(output, 0, SEEK_END);
557
558 *written_size = elf_size_calc;
559 }
560