File Index
Files and directories
Sign in
FILE

genelf.c

File preview with syntax highlighting.

linux-6.19.14/tools/perf/util/genelf.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * genelf.c
  4 * Copyright (C) 2014, Google, Inc
  5 *
  6 * Contributed by:
  7 * 	Stephane Eranian <eranian@gmail.com>
  8 */
  9
 10#include <sys/types.h>
 11#include <stddef.h>
 12#include <libelf.h>
 13#include <string.h>
 14#include <stdlib.h>
 15#include <inttypes.h>
 16#include <err.h>
 17#ifdef HAVE_LIBDW_SUPPORT
 18#include <dwarf.h>
 19#endif
 20
 21#include "genelf.h"
 22#include "sha1.h"
 23#include "../util/jitdump.h"
 24#include <linux/compiler.h>
 25
 26#ifndef NT_GNU_BUILD_ID
 27#define NT_GNU_BUILD_ID 3
 28#endif
 29
 30typedef struct {
 31  unsigned int namesz;  /* Size of entry's owner string */
 32  unsigned int descsz;  /* Size of the note descriptor */
 33  unsigned int type;    /* Interpretation of the descriptor */
 34  char         name[0]; /* Start of the name+desc data */
 35} Elf_Note;
 36
 37static char shd_string_table[] = {
 38	0,
 39	'.', 't', 'e', 'x', 't', 0,			/*  1 */
 40	'.', 's', 'h', 's', 't', 'r', 't', 'a', 'b', 0, /*  7 */
 41	'.', 's', 'y', 'm', 't', 'a', 'b', 0,		/* 17 */
 42	'.', 's', 't', 'r', 't', 'a', 'b', 0,		/* 25 */
 43	'.', 'n', 'o', 't', 'e', '.', 'g', 'n', 'u', '.', 'b', 'u', 'i', 'l', 'd', '-', 'i', 'd', 0, /* 33 */
 44	'.', 'd', 'e', 'b', 'u', 'g', '_', 'l', 'i', 'n', 'e', 0, /* 52 */
 45	'.', 'd', 'e', 'b', 'u', 'g', '_', 'i', 'n', 'f', 'o', 0, /* 64 */
 46	'.', 'd', 'e', 'b', 'u', 'g', '_', 'a', 'b', 'b', 'r', 'e', 'v', 0, /* 76 */
 47	'.', 'e', 'h', '_', 'f', 'r', 'a', 'm', 'e', '_', 'h', 'd', 'r', 0, /* 90 */
 48	'.', 'e', 'h', '_', 'f', 'r', 'a', 'm', 'e', 0, /* 104 */
 49};
 50
 51static struct buildid_note {
 52	Elf_Note desc;		/* descsz: size of build-id, must be multiple of 4 */
 53	char	 name[4];	/* GNU\0 */
 54	u8	 build_id[SHA1_DIGEST_SIZE];
 55} bnote;
 56
 57static Elf_Sym symtab[]={
 58	/* symbol 0 MUST be the undefined symbol */
 59	{ .st_name  = 0, /* index in sym_string table */
 60	  .st_info  = ELF_ST_TYPE(STT_NOTYPE),
 61	  .st_shndx = 0, /* for now */
 62	  .st_value = 0x0,
 63	  .st_other = ELF_ST_VIS(STV_DEFAULT),
 64	  .st_size  = 0,
 65	},
 66	{ .st_name  = 1, /* index in sym_string table */
 67	  .st_info  = ELF_ST_BIND(STB_LOCAL) | ELF_ST_TYPE(STT_FUNC),
 68	  .st_shndx = 1,
 69	  .st_value = 0, /* for now */
 70	  .st_other = ELF_ST_VIS(STV_DEFAULT),
 71	  .st_size  = 0, /* for now */
 72	}
 73};
 74
 75static int
 76jit_add_eh_frame_info(Elf *e, void* unwinding, uint64_t unwinding_header_size,
 77		      uint64_t unwinding_size, uint64_t base_offset)
 78{
 79	Elf_Data *d;
 80	Elf_Scn *scn;
 81	Elf_Shdr *shdr;
 82	uint64_t unwinding_table_size = unwinding_size - unwinding_header_size;
 83
 84	/*
 85	 * setup eh_frame section
 86	 */
 87	scn = elf_newscn(e);
 88	if (!scn) {
 89		warnx("cannot create section");
 90		return -1;
 91	}
 92
 93	d = elf_newdata(scn);
 94	if (!d) {
 95		warnx("cannot get new data");
 96		return -1;
 97	}
 98
 99	d->d_align = 8;
100	d->d_off = 0LL;
101	d->d_buf = unwinding;
102	d->d_type = ELF_T_BYTE;
103	d->d_size = unwinding_table_size;
104	d->d_version = EV_CURRENT;
105
106	shdr = elf_getshdr(scn);
107	if (!shdr) {
108		warnx("cannot get section header");
109		return -1;
110	}
111
112	shdr->sh_name = 104;
113	shdr->sh_type = SHT_PROGBITS;
114	shdr->sh_addr = base_offset;
115	shdr->sh_flags = SHF_ALLOC;
116	shdr->sh_entsize = 0;
117
118	/*
119	 * setup eh_frame_hdr section
120	 */
121	scn = elf_newscn(e);
122	if (!scn) {
123		warnx("cannot create section");
124		return -1;
125	}
126
127	d = elf_newdata(scn);
128	if (!d) {
129		warnx("cannot get new data");
130		return -1;
131	}
132
133	d->d_align = 4;
134	d->d_off = 0LL;
135	d->d_buf = unwinding + unwinding_table_size;
136	d->d_type = ELF_T_BYTE;
137	d->d_size = unwinding_header_size;
138	d->d_version = EV_CURRENT;
139
140	shdr = elf_getshdr(scn);
141	if (!shdr) {
142		warnx("cannot get section header");
143		return -1;
144	}
145
146	shdr->sh_name = 90;
147	shdr->sh_type = SHT_PROGBITS;
148	shdr->sh_addr = base_offset + unwinding_table_size;
149	shdr->sh_flags = SHF_ALLOC;
150	shdr->sh_entsize = 0;
151
152	return 0;
153}
154
155/*
156 * fd: file descriptor open for writing for the output file
157 * load_addr: code load address (could be zero, just used for buildid)
158 * sym: function name (for native code - used as the symbol)
159 * code: the native code
160 * csize: the code size in bytes
161 */
162int
163jit_write_elf(int fd, uint64_t load_addr __maybe_unused, const char *sym,
164	      const void *code, int csize,
165	      void *debug __maybe_unused, int nr_debug_entries __maybe_unused,
166	      void *unwinding, uint64_t unwinding_header_size, uint64_t unwinding_size)
167{
168	Elf *e;
169	Elf_Data *d;
170	Elf_Scn *scn;
171	Elf_Ehdr *ehdr;
172	Elf_Phdr *phdr;
173	Elf_Shdr *shdr;
174	uint64_t eh_frame_base_offset;
175	char *strsym = NULL;
176	void *build_id_data = NULL, *tmp;
177	int build_id_data_len;
178	int symlen;
179	int retval = -1;
180
181	if (elf_version(EV_CURRENT) == EV_NONE) {
182		warnx("ELF initialization failed");
183		return -1;
184	}
185
186	e = elf_begin(fd, ELF_C_WRITE, NULL);
187	if (!e) {
188		warnx("elf_begin failed");
189		goto error;
190	}
191
192	/*
193	 * setup ELF header
194	 */
195	ehdr = elf_newehdr(e);
196	if (!ehdr) {
197		warnx("cannot get ehdr");
198		goto error;
199	}
200
201	ehdr->e_ident[EI_DATA] = GEN_ELF_ENDIAN;
202	ehdr->e_ident[EI_CLASS] = GEN_ELF_CLASS;
203	ehdr->e_machine = GEN_ELF_ARCH;
204	ehdr->e_type = ET_DYN;
205	ehdr->e_entry = GEN_ELF_TEXT_OFFSET;
206	ehdr->e_version = EV_CURRENT;
207	ehdr->e_shstrndx= unwinding ? 4 : 2; /* shdr index for section name */
208
209	/*
210	 * setup program header
211	 */
212	phdr = elf_newphdr(e, 1);
213	phdr[0].p_type = PT_LOAD;
214	phdr[0].p_offset = GEN_ELF_TEXT_OFFSET;
215	phdr[0].p_vaddr = GEN_ELF_TEXT_OFFSET;
216	phdr[0].p_paddr = GEN_ELF_TEXT_OFFSET;
217	phdr[0].p_filesz = csize;
218	phdr[0].p_memsz = csize;
219	phdr[0].p_flags = PF_X | PF_R;
220	phdr[0].p_align = 8;
221
222	/*
223	 * setup text section
224	 */
225	scn = elf_newscn(e);
226	if (!scn) {
227		warnx("cannot create section");
228		goto error;
229	}
230
231	d = elf_newdata(scn);
232	if (!d) {
233		warnx("cannot get new data");
234		goto error;
235	}
236
237	d->d_align = 16;
238	d->d_off = 0LL;
239	d->d_buf = (void *)code;
240	d->d_type = ELF_T_BYTE;
241	d->d_size = csize;
242	d->d_version = EV_CURRENT;
243
244	shdr = elf_getshdr(scn);
245	if (!shdr) {
246		warnx("cannot get section header");
247		goto error;
248	}
249
250	shdr->sh_name = 1;
251	shdr->sh_type = SHT_PROGBITS;
252	shdr->sh_addr = GEN_ELF_TEXT_OFFSET;
253	shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
254	shdr->sh_entsize = 0;
255
256	build_id_data = malloc(csize);
257	if (build_id_data == NULL) {
258		warnx("cannot allocate build-id data");
259		goto error;
260	}
261	memcpy(build_id_data, code, csize);
262	build_id_data_len = csize;
263
264	/*
265	 * Setup .eh_frame_hdr and .eh_frame
266	 */
267	if (unwinding) {
268		eh_frame_base_offset = ALIGN_8(GEN_ELF_TEXT_OFFSET + csize);
269		retval = jit_add_eh_frame_info(e, unwinding,
270					       unwinding_header_size, unwinding_size,
271					       eh_frame_base_offset);
272		if (retval)
273			goto error;
274		retval = -1;
275	}
276
277	/*
278	 * setup section headers string table
279	 */
280	scn = elf_newscn(e);
281	if (!scn) {
282		warnx("cannot create section");
283		goto error;
284	}
285
286	d = elf_newdata(scn);
287	if (!d) {
288		warnx("cannot get new data");
289		goto error;
290	}
291
292	d->d_align = 1;
293	d->d_off = 0LL;
294	d->d_buf = shd_string_table;
295	d->d_type = ELF_T_BYTE;
296	d->d_size = sizeof(shd_string_table);
297	d->d_version = EV_CURRENT;
298
299	shdr = elf_getshdr(scn);
300	if (!shdr) {
301		warnx("cannot get section header");
302		goto error;
303	}
304
305	shdr->sh_name = 7; /* offset of '.shstrtab' in shd_string_table */
306	shdr->sh_type = SHT_STRTAB;
307	shdr->sh_flags = 0;
308	shdr->sh_entsize = 0;
309
310	/*
311	 * setup symtab section
312	 */
313	symtab[1].st_size  = csize;
314	symtab[1].st_value = GEN_ELF_TEXT_OFFSET;
315
316	scn = elf_newscn(e);
317	if (!scn) {
318		warnx("cannot create section");
319		goto error;
320	}
321
322	d = elf_newdata(scn);
323	if (!d) {
324		warnx("cannot get new data");
325		goto error;
326	}
327
328	d->d_align = 8;
329	d->d_off = 0LL;
330	d->d_buf = symtab;
331	d->d_type = ELF_T_SYM;
332	d->d_size = sizeof(symtab);
333	d->d_version = EV_CURRENT;
334
335	shdr = elf_getshdr(scn);
336	if (!shdr) {
337		warnx("cannot get section header");
338		goto error;
339	}
340
341	shdr->sh_name = 17; /* offset of '.symtab' in shd_string_table */
342	shdr->sh_type = SHT_SYMTAB;
343	shdr->sh_flags = 0;
344	shdr->sh_entsize = sizeof(Elf_Sym);
345	shdr->sh_link = unwinding ? 6 : 4; /* index of .strtab section */
346
347	tmp = realloc(build_id_data, build_id_data_len + sizeof(symtab));
348	if (tmp == NULL) {
349		warnx("cannot allocate build-id data");
350		goto error;
351	}
352	memcpy(tmp + build_id_data_len, symtab, sizeof(symtab));
353	build_id_data = tmp;
354	build_id_data_len += sizeof(symtab);
355
356	/*
357	 * setup symbols string table
358	 * 2 = 1 for 0 in 1st entry, 1 for the 0 at end of symbol for 2nd entry
359	 */
360	symlen = 2 + strlen(sym);
361	strsym = calloc(1, symlen);
362	if (!strsym) {
363		warnx("cannot allocate strsym");
364		goto error;
365	}
366	strcpy(strsym + 1, sym);
367
368	scn = elf_newscn(e);
369	if (!scn) {
370		warnx("cannot create section");
371		goto error;
372	}
373
374	d = elf_newdata(scn);
375	if (!d) {
376		warnx("cannot get new data");
377		goto error;
378	}
379
380	d->d_align = 1;
381	d->d_off = 0LL;
382	d->d_buf = strsym;
383	d->d_type = ELF_T_BYTE;
384	d->d_size = symlen;
385	d->d_version = EV_CURRENT;
386
387	shdr = elf_getshdr(scn);
388	if (!shdr) {
389		warnx("cannot get section header");
390		goto error;
391	}
392
393	shdr->sh_name = 25; /* offset in shd_string_table */
394	shdr->sh_type = SHT_STRTAB;
395	shdr->sh_flags = 0;
396	shdr->sh_entsize = 0;
397
398	tmp = realloc(build_id_data, build_id_data_len + symlen);
399	if (tmp == NULL) {
400		warnx("cannot allocate build-id data");
401		goto error;
402	}
403	memcpy(tmp + build_id_data_len, strsym, symlen);
404	build_id_data = tmp;
405	build_id_data_len += symlen;
406
407	/*
408	 * setup build-id section
409	 */
410	scn = elf_newscn(e);
411	if (!scn) {
412		warnx("cannot create section");
413		goto error;
414	}
415
416	d = elf_newdata(scn);
417	if (!d) {
418		warnx("cannot get new data");
419		goto error;
420	}
421
422	/*
423	 * build-id generation
424	 */
425	sha1(build_id_data, build_id_data_len, bnote.build_id);
426	bnote.desc.namesz = sizeof(bnote.name); /* must include 0 termination */
427	bnote.desc.descsz = sizeof(bnote.build_id);
428	bnote.desc.type   = NT_GNU_BUILD_ID;
429	strcpy(bnote.name, "GNU");
430
431	d->d_align = 4;
432	d->d_off = 0LL;
433	d->d_buf = &bnote;
434	d->d_type = ELF_T_BYTE;
435	d->d_size = sizeof(bnote);
436	d->d_version = EV_CURRENT;
437
438	shdr = elf_getshdr(scn);
439	if (!shdr) {
440		warnx("cannot get section header");
441		goto error;
442	}
443
444	shdr->sh_name = 33; /* offset in shd_string_table */
445	shdr->sh_type = SHT_NOTE;
446	shdr->sh_addr = 0x0;
447	shdr->sh_flags = SHF_ALLOC;
448	shdr->sh_size = sizeof(bnote);
449	shdr->sh_entsize = 0;
450
451#ifdef HAVE_LIBDW_SUPPORT
452	if (debug && nr_debug_entries) {
453		retval = jit_add_debug_info(e, load_addr, debug, nr_debug_entries);
454		if (retval)
455			goto error;
456	} else
457#endif
458	{
459		if (elf_update(e, ELF_C_WRITE) < 0) {
460			warnx("elf_update 4 failed");
461			goto error;
462		}
463	}
464
465	retval = 0;
466error:
467	(void)elf_end(e);
468
469	free(strsym);
470	free(build_id_data);
471
472	return retval;
473}
~/ Read-only file preview.