| 1 | |
| 2 | |
| 3 | * genelf.c
|
| 4 | * Copyright (C) 2014, Google, Inc
|
| 5 | *
|
| 6 | * Contributed by:
|
| 7 | * Stephane Eranian <[email protected]>
|
| 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 "blake2s.h"
|
| 22 | #include "genelf.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 |
|
| 30 | typedef struct {
|
| 31 | unsigned int namesz;
|
| 32 | unsigned int descsz;
|
| 33 | unsigned int type;
|
| 34 | char name[0];
|
| 35 | } Elf_Note;
|
| 36 |
|
| 37 | static char shd_string_table[] = {
|
| 38 | 0,
|
| 39 | '.', 't', 'e', 'x', 't', 0,
|
| 40 | '.', 's', 'h', 's', 't', 'r', 't', 'a', 'b', 0,
|
| 41 | '.', 's', 'y', 'm', 't', 'a', 'b', 0,
|
| 42 | '.', 's', 't', 'r', 't', 'a', 'b', 0,
|
| 43 | '.', 'n', 'o', 't', 'e', '.', 'g', 'n', 'u', '.', 'b', 'u', 'i', 'l', 'd', '-', 'i', 'd', 0,
|
| 44 | '.', 'd', 'e', 'b', 'u', 'g', '_', 'l', 'i', 'n', 'e', 0,
|
| 45 | '.', 'd', 'e', 'b', 'u', 'g', '_', 'i', 'n', 'f', 'o', 0,
|
| 46 | '.', 'd', 'e', 'b', 'u', 'g', '_', 'a', 'b', 'b', 'r', 'e', 'v', 0,
|
| 47 | '.', 'e', 'h', '_', 'f', 'r', 'a', 'm', 'e', '_', 'h', 'd', 'r', 0,
|
| 48 | '.', 'e', 'h', '_', 'f', 'r', 'a', 'm', 'e', 0,
|
| 49 | };
|
| 50 |
|
| 51 | static struct buildid_note {
|
| 52 | Elf_Note desc;
|
| 53 | char name[4];
|
| 54 | u8 build_id[20];
|
| 55 | } bnote;
|
| 56 |
|
| 57 | static Elf_Sym symtab[]={
|
| 58 |
|
| 59 | { .st_name = 0,
|
| 60 | .st_info = ELF_ST_TYPE(STT_NOTYPE),
|
| 61 | .st_shndx = 0,
|
| 62 | .st_value = 0x0,
|
| 63 | .st_other = ELF_ST_VIS(STV_DEFAULT),
|
| 64 | .st_size = 0,
|
| 65 | },
|
| 66 | { .st_name = 1,
|
| 67 | .st_info = ELF_ST_BIND(STB_LOCAL) | ELF_ST_TYPE(STT_FUNC),
|
| 68 | .st_shndx = 1,
|
| 69 | .st_value = 0,
|
| 70 | .st_other = ELF_ST_VIS(STV_DEFAULT),
|
| 71 | .st_size = 0,
|
| 72 | }
|
| 73 | };
|
| 74 |
|
| 75 | static int
|
| 76 | jit_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 | enum {
|
| 156 | TAG_CODE = 0,
|
| 157 | TAG_SYMTAB = 1,
|
| 158 | TAG_STRSYM = 2,
|
| 159 | };
|
| 160 |
|
| 161 | |
| 162 | * Update the hash using the given data, also prepending a (tag, len) prefix to
|
| 163 | * ensure that distinct input tuples reliably result in distinct hashes.
|
| 164 |
|
| 165 | static void blake2s_update_tagged(struct blake2s_ctx *ctx, int tag,
|
| 166 | const void *data, size_t len)
|
| 167 | {
|
| 168 | u64 prefix = ((u64)tag << 56) | len;
|
| 169 |
|
| 170 | blake2s_update(ctx, (const u8 *)&prefix, sizeof(prefix));
|
| 171 | blake2s_update(ctx, data, len);
|
| 172 | }
|
| 173 |
|
| 174 | |
| 175 | * fd: file descriptor open for writing for the output file
|
| 176 | * load_addr: code load address (could be zero)
|
| 177 | * sym: function name (for native code - used as the symbol)
|
| 178 | * code: the native code
|
| 179 | * csize: the code size in bytes
|
| 180 |
|
| 181 | int
|
| 182 | jit_write_elf(int fd, uint64_t load_addr __maybe_unused, const char *sym,
|
| 183 | const void *code, int csize,
|
| 184 | void *debug __maybe_unused, int nr_debug_entries __maybe_unused,
|
| 185 | void *unwinding, uint64_t unwinding_header_size, uint64_t unwinding_size)
|
| 186 | {
|
| 187 | Elf *e;
|
| 188 | Elf_Data *d;
|
| 189 | Elf_Scn *scn;
|
| 190 | Elf_Ehdr *ehdr;
|
| 191 | Elf_Phdr *phdr;
|
| 192 | Elf_Shdr *shdr;
|
| 193 | uint64_t eh_frame_base_offset;
|
| 194 | char *strsym = NULL;
|
| 195 | struct blake2s_ctx ctx;
|
| 196 | int symlen;
|
| 197 | int retval = -1;
|
| 198 |
|
| 199 | if (elf_version(EV_CURRENT) == EV_NONE) {
|
| 200 | warnx("ELF initialization failed");
|
| 201 | return -1;
|
| 202 | }
|
| 203 |
|
| 204 | e = elf_begin(fd, ELF_C_WRITE, NULL);
|
| 205 | if (!e) {
|
| 206 | warnx("elf_begin failed");
|
| 207 | goto error;
|
| 208 | }
|
| 209 |
|
| 210 | |
| 211 | * setup ELF header
|
| 212 |
|
| 213 | ehdr = elf_newehdr(e);
|
| 214 | if (!ehdr) {
|
| 215 | warnx("cannot get ehdr");
|
| 216 | goto error;
|
| 217 | }
|
| 218 |
|
| 219 | ehdr->e_ident[EI_DATA] = GEN_ELF_ENDIAN;
|
| 220 | ehdr->e_ident[EI_CLASS] = GEN_ELF_CLASS;
|
| 221 | ehdr->e_machine = GEN_ELF_ARCH;
|
| 222 | ehdr->e_type = ET_DYN;
|
| 223 | ehdr->e_entry = GEN_ELF_TEXT_OFFSET;
|
| 224 | ehdr->e_version = EV_CURRENT;
|
| 225 | ehdr->e_shstrndx= unwinding ? 4 : 2;
|
| 226 |
|
| 227 | |
| 228 | * setup program header
|
| 229 |
|
| 230 | phdr = elf_newphdr(e, 1);
|
| 231 | phdr[0].p_type = PT_LOAD;
|
| 232 | phdr[0].p_offset = GEN_ELF_TEXT_OFFSET;
|
| 233 | phdr[0].p_vaddr = GEN_ELF_TEXT_OFFSET;
|
| 234 | phdr[0].p_paddr = GEN_ELF_TEXT_OFFSET;
|
| 235 | phdr[0].p_filesz = csize;
|
| 236 | phdr[0].p_memsz = csize;
|
| 237 | phdr[0].p_flags = PF_X | PF_R;
|
| 238 | phdr[0].p_align = 8;
|
| 239 |
|
| 240 | |
| 241 | * setup text section
|
| 242 |
|
| 243 | scn = elf_newscn(e);
|
| 244 | if (!scn) {
|
| 245 | warnx("cannot create section");
|
| 246 | goto error;
|
| 247 | }
|
| 248 |
|
| 249 | d = elf_newdata(scn);
|
| 250 | if (!d) {
|
| 251 | warnx("cannot get new data");
|
| 252 | goto error;
|
| 253 | }
|
| 254 |
|
| 255 | d->d_align = 16;
|
| 256 | d->d_off = 0LL;
|
| 257 | d->d_buf = (void *)code;
|
| 258 | d->d_type = ELF_T_BYTE;
|
| 259 | d->d_size = csize;
|
| 260 | d->d_version = EV_CURRENT;
|
| 261 |
|
| 262 | shdr = elf_getshdr(scn);
|
| 263 | if (!shdr) {
|
| 264 | warnx("cannot get section header");
|
| 265 | goto error;
|
| 266 | }
|
| 267 |
|
| 268 | shdr->sh_name = 1;
|
| 269 | shdr->sh_type = SHT_PROGBITS;
|
| 270 | shdr->sh_addr = GEN_ELF_TEXT_OFFSET;
|
| 271 | shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
|
| 272 | shdr->sh_entsize = 0;
|
| 273 |
|
| 274 | blake2s_init(&ctx, sizeof(bnote.build_id));
|
| 275 | blake2s_update_tagged(&ctx, TAG_CODE, code, csize);
|
| 276 |
|
| 277 | |
| 278 | * Setup .eh_frame_hdr and .eh_frame
|
| 279 |
|
| 280 | if (unwinding) {
|
| 281 | eh_frame_base_offset = ALIGN_8(GEN_ELF_TEXT_OFFSET + csize);
|
| 282 | retval = jit_add_eh_frame_info(e, unwinding,
|
| 283 | unwinding_header_size, unwinding_size,
|
| 284 | eh_frame_base_offset);
|
| 285 | if (retval)
|
| 286 | goto error;
|
| 287 | retval = -1;
|
| 288 | }
|
| 289 |
|
| 290 | |
| 291 | * setup section headers string table
|
| 292 |
|
| 293 | scn = elf_newscn(e);
|
| 294 | if (!scn) {
|
| 295 | warnx("cannot create section");
|
| 296 | goto error;
|
| 297 | }
|
| 298 |
|
| 299 | d = elf_newdata(scn);
|
| 300 | if (!d) {
|
| 301 | warnx("cannot get new data");
|
| 302 | goto error;
|
| 303 | }
|
| 304 |
|
| 305 | d->d_align = 1;
|
| 306 | d->d_off = 0LL;
|
| 307 | d->d_buf = shd_string_table;
|
| 308 | d->d_type = ELF_T_BYTE;
|
| 309 | d->d_size = sizeof(shd_string_table);
|
| 310 | d->d_version = EV_CURRENT;
|
| 311 |
|
| 312 | shdr = elf_getshdr(scn);
|
| 313 | if (!shdr) {
|
| 314 | warnx("cannot get section header");
|
| 315 | goto error;
|
| 316 | }
|
| 317 |
|
| 318 | shdr->sh_name = 7;
|
| 319 | shdr->sh_type = SHT_STRTAB;
|
| 320 | shdr->sh_flags = 0;
|
| 321 | shdr->sh_entsize = 0;
|
| 322 |
|
| 323 | |
| 324 | * setup symtab section
|
| 325 |
|
| 326 | symtab[1].st_size = csize;
|
| 327 | symtab[1].st_value = GEN_ELF_TEXT_OFFSET;
|
| 328 |
|
| 329 | scn = elf_newscn(e);
|
| 330 | if (!scn) {
|
| 331 | warnx("cannot create section");
|
| 332 | goto error;
|
| 333 | }
|
| 334 |
|
| 335 | d = elf_newdata(scn);
|
| 336 | if (!d) {
|
| 337 | warnx("cannot get new data");
|
| 338 | goto error;
|
| 339 | }
|
| 340 |
|
| 341 | d->d_align = 8;
|
| 342 | d->d_off = 0LL;
|
| 343 | d->d_buf = symtab;
|
| 344 | d->d_type = ELF_T_SYM;
|
| 345 | d->d_size = sizeof(symtab);
|
| 346 | d->d_version = EV_CURRENT;
|
| 347 |
|
| 348 | shdr = elf_getshdr(scn);
|
| 349 | if (!shdr) {
|
| 350 | warnx("cannot get section header");
|
| 351 | goto error;
|
| 352 | }
|
| 353 |
|
| 354 | shdr->sh_name = 17;
|
| 355 | shdr->sh_type = SHT_SYMTAB;
|
| 356 | shdr->sh_flags = 0;
|
| 357 | shdr->sh_entsize = sizeof(Elf_Sym);
|
| 358 | shdr->sh_link = unwinding ? 6 : 4;
|
| 359 |
|
| 360 | blake2s_update_tagged(&ctx, TAG_SYMTAB, symtab, sizeof(symtab));
|
| 361 |
|
| 362 | |
| 363 | * setup symbols string table
|
| 364 | * 2 = 1 for 0 in 1st entry, 1 for the 0 at end of symbol for 2nd entry
|
| 365 |
|
| 366 | symlen = 2 + strlen(sym);
|
| 367 | strsym = calloc(1, symlen);
|
| 368 | if (!strsym) {
|
| 369 | warnx("cannot allocate strsym");
|
| 370 | goto error;
|
| 371 | }
|
| 372 | strcpy(strsym + 1, sym);
|
| 373 |
|
| 374 | scn = elf_newscn(e);
|
| 375 | if (!scn) {
|
| 376 | warnx("cannot create section");
|
| 377 | goto error;
|
| 378 | }
|
| 379 |
|
| 380 | d = elf_newdata(scn);
|
| 381 | if (!d) {
|
| 382 | warnx("cannot get new data");
|
| 383 | goto error;
|
| 384 | }
|
| 385 |
|
| 386 | d->d_align = 1;
|
| 387 | d->d_off = 0LL;
|
| 388 | d->d_buf = strsym;
|
| 389 | d->d_type = ELF_T_BYTE;
|
| 390 | d->d_size = symlen;
|
| 391 | d->d_version = EV_CURRENT;
|
| 392 |
|
| 393 | shdr = elf_getshdr(scn);
|
| 394 | if (!shdr) {
|
| 395 | warnx("cannot get section header");
|
| 396 | goto error;
|
| 397 | }
|
| 398 |
|
| 399 | shdr->sh_name = 25;
|
| 400 | shdr->sh_type = SHT_STRTAB;
|
| 401 | shdr->sh_flags = 0;
|
| 402 | shdr->sh_entsize = 0;
|
| 403 |
|
| 404 | blake2s_update_tagged(&ctx, TAG_STRSYM, strsym, symlen);
|
| 405 |
|
| 406 | |
| 407 | * setup build-id section
|
| 408 |
|
| 409 | scn = elf_newscn(e);
|
| 410 | if (!scn) {
|
| 411 | warnx("cannot create section");
|
| 412 | goto error;
|
| 413 | }
|
| 414 |
|
| 415 | d = elf_newdata(scn);
|
| 416 | if (!d) {
|
| 417 | warnx("cannot get new data");
|
| 418 | goto error;
|
| 419 | }
|
| 420 |
|
| 421 | |
| 422 | * build-id generation
|
| 423 |
|
| 424 | blake2s_final(&ctx, bnote.build_id);
|
| 425 | bnote.desc.namesz = sizeof(bnote.name);
|
| 426 | bnote.desc.descsz = sizeof(bnote.build_id);
|
| 427 | bnote.desc.type = NT_GNU_BUILD_ID;
|
| 428 | strcpy(bnote.name, "GNU");
|
| 429 |
|
| 430 | d->d_align = 4;
|
| 431 | d->d_off = 0LL;
|
| 432 | d->d_buf = &bnote;
|
| 433 | d->d_type = ELF_T_BYTE;
|
| 434 | d->d_size = sizeof(bnote);
|
| 435 | d->d_version = EV_CURRENT;
|
| 436 |
|
| 437 | shdr = elf_getshdr(scn);
|
| 438 | if (!shdr) {
|
| 439 | warnx("cannot get section header");
|
| 440 | goto error;
|
| 441 | }
|
| 442 |
|
| 443 | shdr->sh_name = 33;
|
| 444 | shdr->sh_type = SHT_NOTE;
|
| 445 | shdr->sh_addr = 0x0;
|
| 446 | shdr->sh_flags = SHF_ALLOC;
|
| 447 | shdr->sh_size = sizeof(bnote);
|
| 448 | shdr->sh_entsize = 0;
|
| 449 |
|
| 450 | #ifdef HAVE_LIBDW_SUPPORT
|
| 451 | if (debug && nr_debug_entries) {
|
| 452 | retval = jit_add_debug_info(e, load_addr, debug, nr_debug_entries);
|
| 453 | if (retval)
|
| 454 | goto error;
|
| 455 | } else
|
| 456 | #endif
|
| 457 | {
|
| 458 | if (elf_update(e, ELF_C_WRITE) < 0) {
|
| 459 | warnx("elf_update 4 failed");
|
| 460 | goto error;
|
| 461 | }
|
| 462 | }
|
| 463 |
|
| 464 | retval = 0;
|
| 465 | error:
|
| 466 | (void)elf_end(e);
|
| 467 |
|
| 468 | free(strsym);
|
| 469 |
|
| 470 | return retval;
|
| 471 | }
|