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