| /* |
| SPDX-License-Identifier: GPL-2.0-only |
| |
| Copyright (C) 2019 Arnaldo Carvalho de Melo <acme@redhat.com> |
| */ |
| |
| #include <fcntl.h> |
| #include <gelf.h> |
| #include <limits.h> |
| #include <malloc.h> |
| #include <errno.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <zlib.h> |
| |
| #include "libctf.h" |
| #include "ctf.h" |
| #include "dutil.h" |
| #include "gobuffer.h" |
| |
| bool ctf__ignore_symtab_function(const GElf_Sym *sym, const char *sym_name) |
| { |
| return (!elf_sym__is_local_function(sym) || |
| elf_sym__visibility(sym) != STV_DEFAULT || |
| sym->st_size == 0 || |
| memcmp(sym_name, "__libc_csu_", |
| sizeof("__libc_csu_") - 1) == 0); |
| } |
| |
| bool ctf__ignore_symtab_object(const GElf_Sym *sym, const char *sym_name) |
| { |
| return (!elf_sym__is_local_object(sym) || sym->st_size == 0 || |
| elf_sym__visibility(sym) != STV_DEFAULT || |
| strchr(sym_name, '.') != NULL); |
| } |
| |
| uint16_t ctf__get16(struct ctf *ctf, uint16_t *p) |
| { |
| uint16_t val = *p; |
| |
| if (ctf->swapped) |
| val = ((val >> 8) | (val << 8)); |
| return val; |
| } |
| |
| uint32_t ctf__get32(struct ctf *ctf, uint32_t *p) |
| { |
| uint32_t val = *p; |
| |
| if (ctf->swapped) |
| val = ((val >> 24) | |
| ((val >> 8) & 0x0000ff00) | |
| ((val << 8) & 0x00ff0000) | |
| (val << 24)); |
| return val; |
| } |
| |
| void ctf__put16(struct ctf *ctf, uint16_t *p, uint16_t val) |
| { |
| if (ctf->swapped) |
| val = ((val >> 8) | (val << 8)); |
| *p = val; |
| } |
| |
| void ctf__put32(struct ctf *ctf, uint32_t *p, uint32_t val) |
| { |
| if (ctf->swapped) |
| val = ((val >> 24) | |
| ((val >> 8) & 0x0000ff00) | |
| ((val << 8) & 0x00ff0000) | |
| (val << 24)); |
| *p = val; |
| } |
| |
| static int ctf__decompress(struct ctf *ctf, void *orig_buf, size_t orig_size) |
| { |
| struct ctf_header *hp = orig_buf; |
| const char *err_str; |
| z_stream state; |
| size_t len; |
| void *new; |
| |
| len = (ctf__get32(ctf, &hp->ctf_str_off) + |
| ctf__get32(ctf, &hp->ctf_str_len)); |
| new = malloc(len + sizeof(*hp)); |
| if (!new) { |
| fprintf(stderr, "CTF decompression allocation failure.\n"); |
| return -ENOMEM; |
| } |
| memcpy(new, hp, sizeof(*hp)); |
| |
| memset(&state, 0, sizeof(state)); |
| state.next_in = (Bytef *) (hp + 1); |
| state.avail_in = orig_size - sizeof(*hp); |
| state.next_out = new + sizeof(*hp); |
| state.avail_out = len; |
| |
| if (inflateInit(&state) != Z_OK) { |
| err_str = "struct ctf decompression inflateInit failure."; |
| goto err; |
| } |
| |
| if (inflate(&state, Z_FINISH) != Z_STREAM_END) { |
| err_str = "struct ctf decompression inflate failure."; |
| goto err; |
| } |
| |
| if (inflateEnd(&state) != Z_OK) { |
| err_str = "struct ctf decompression inflateEnd failure."; |
| goto err; |
| } |
| |
| if (state.total_out != len) { |
| err_str = "struct ctf decompression truncation error."; |
| goto err; |
| } |
| |
| ctf->buf = new; |
| ctf->size = len + sizeof(*hp); |
| |
| return 0; |
| |
| err: |
| fputs(err_str, stderr); |
| free(new); |
| return -EINVAL; |
| } |
| |
| int ctf__load(struct ctf *ctf) |
| { |
| int err = -ENOTSUP; |
| GElf_Shdr shdr; |
| Elf_Scn *sec = elf_section_by_name(ctf->elf, &ctf->ehdr, |
| &shdr, ".SUNW_ctf", NULL); |
| |
| if (sec == NULL) |
| return -ESRCH; |
| |
| Elf_Data *data = elf_getdata(sec, NULL); |
| if (data == NULL) { |
| fprintf(stderr, "%s: cannot get data of CTF section.\n", |
| __func__); |
| return -1; |
| } |
| |
| struct ctf_header *hp = data->d_buf; |
| size_t orig_size = data->d_size; |
| |
| if (hp->ctf_version != CTF_VERSION) |
| goto out; |
| |
| err = -EINVAL; |
| if (hp->ctf_magic == CTF_MAGIC) |
| ctf->swapped = 0; |
| else if (hp->ctf_magic == CTF_MAGIC_SWAP) |
| ctf->swapped = 1; |
| else |
| goto out; |
| |
| if (!(hp->ctf_flags & CTF_FLAGS_COMPR)) { |
| err = -ENOMEM; |
| ctf->buf = malloc(orig_size); |
| if (ctf->buf != NULL) { |
| memcpy(ctf->buf, hp, orig_size); |
| ctf->size = orig_size; |
| err = 0; |
| } |
| } else |
| err = ctf__decompress(ctf, hp, orig_size); |
| out: |
| return err; |
| } |
| |
| struct ctf *ctf__new(const char *filename, Elf *elf) |
| { |
| struct ctf *ctf = zalloc(sizeof(*ctf)); |
| |
| if (ctf != NULL) { |
| ctf->filename = strdup(filename); |
| if (ctf->filename == NULL) |
| goto out_delete; |
| |
| if (elf != NULL) { |
| ctf->in_fd = -1; |
| ctf->elf = elf; |
| } else { |
| |
| ctf->in_fd = open(filename, O_RDONLY); |
| if (ctf->in_fd < 0) |
| goto out_delete_filename; |
| |
| if (elf_version(EV_CURRENT) == EV_NONE) { |
| fprintf(stderr, "%s: cannot set libelf version.\n", |
| __func__); |
| goto out_close; |
| } |
| |
| ctf->elf = elf_begin(ctf->in_fd, ELF_C_READ_MMAP, NULL); |
| if (!ctf->elf) { |
| fprintf(stderr, "%s: cannot read %s ELF file.\n", |
| __func__, filename); |
| goto out_close; |
| } |
| } |
| |
| if (gelf_getehdr(ctf->elf, &ctf->ehdr) == NULL) { |
| fprintf(stderr, "%s: cannot get elf header.\n", __func__); |
| goto out_elf_end; |
| } |
| |
| switch (ctf->ehdr.e_ident[EI_CLASS]) { |
| case ELFCLASS32: ctf->wordsize = 4; break; |
| case ELFCLASS64: ctf->wordsize = 8; break; |
| default: ctf->wordsize = 0; break; |
| } |
| } |
| |
| return ctf; |
| out_elf_end: |
| if (elf == NULL) |
| elf_end(ctf->elf); |
| out_close: |
| if (elf == NULL) |
| close(ctf->in_fd); |
| out_delete_filename: |
| free(ctf->filename); |
| out_delete: |
| free(ctf); |
| return NULL; |
| } |
| |
| void ctf__delete(struct ctf *ctf) |
| { |
| if (ctf != NULL) { |
| if (ctf->in_fd != -1) { |
| elf_end(ctf->elf); |
| close(ctf->in_fd); |
| } |
| __gobuffer__delete(&ctf->objects); |
| __gobuffer__delete(&ctf->types); |
| __gobuffer__delete(&ctf->funcs); |
| elf_symtab__delete(ctf->symtab); |
| free(ctf->filename); |
| free(ctf->buf); |
| free(ctf); |
| } |
| } |
| |
| char *ctf__string(struct ctf *ctf, uint32_t ref) |
| { |
| struct ctf_header *hp = ctf->buf; |
| uint32_t off = CTF_REF_OFFSET(ref); |
| char *name; |
| |
| if (CTF_REF_TBL_ID(ref) != CTF_STR_TBL_ID_0) |
| return "(external ref)"; |
| |
| if (off >= ctf__get32(ctf, &hp->ctf_str_len)) |
| return "(ref out-of-bounds)"; |
| |
| if ((off + ctf__get32(ctf, &hp->ctf_str_off)) >= ctf->size) |
| return "(string table truncated)"; |
| |
| name = ((char *)(hp + 1) + ctf__get32(ctf, &hp->ctf_str_off) + off); |
| |
| return name[0] == '\0' ? NULL : name; |
| } |
| |
| void *ctf__get_buffer(struct ctf *ctf) |
| { |
| return ctf->buf; |
| } |
| |
| size_t ctf__get_size(struct ctf *ctf) |
| { |
| return ctf->size; |
| } |
| |
| int ctf__load_symtab(struct ctf *ctf) |
| { |
| ctf->symtab = elf_symtab__new(".symtab", ctf->elf, &ctf->ehdr); |
| return ctf->symtab == NULL ? -1 : 0; |
| } |
| |
| void ctf__set_strings(struct ctf *ctf, struct gobuffer *strings) |
| { |
| ctf->strings = strings; |
| } |
| |
| uint32_t ctf__add_base_type(struct ctf *ctf, uint32_t name, uint16_t size) |
| { |
| struct ctf_full_type t; |
| |
| t.base.ctf_name = name; |
| t.base.ctf_info = CTF_INFO_ENCODE(CTF_TYPE_KIND_INT, 0, 0); |
| t.base.ctf_size = size; |
| t.ctf_size_high = CTF_TYPE_INT_ENCODE(0, 0, size); |
| |
| gobuffer__add(&ctf->types, &t, sizeof(t) - sizeof(uint32_t)); |
| return ++ctf->type_index; |
| } |
| |
| uint32_t ctf__add_short_type(struct ctf *ctf, uint16_t kind, uint16_t type, uint32_t name) |
| { |
| struct ctf_short_type t; |
| |
| t.ctf_name = name; |
| t.ctf_info = CTF_INFO_ENCODE(kind, 0, 0); |
| t.ctf_type = type; |
| |
| gobuffer__add(&ctf->types, &t, sizeof(t)); |
| return ++ctf->type_index; |
| } |
| |
| uint32_t ctf__add_fwd_decl(struct ctf *ctf, uint32_t name) |
| { |
| return ctf__add_short_type(ctf, CTF_TYPE_KIND_FWD, 0, name); |
| } |
| |
| uint32_t ctf__add_array(struct ctf *ctf, uint16_t type, uint16_t index_type, uint32_t nelems) |
| { |
| struct { |
| struct ctf_short_type t; |
| struct ctf_array a; |
| } array; |
| |
| array.t.ctf_name = 0; |
| array.t.ctf_info = CTF_INFO_ENCODE(CTF_TYPE_KIND_ARR, 0, 0); |
| array.t.ctf_size = 0; |
| array.a.ctf_array_type = type; |
| array.a.ctf_array_index_type = index_type; |
| array.a.ctf_array_nelems = nelems; |
| |
| gobuffer__add(&ctf->types, &array, sizeof(array)); |
| return ++ctf->type_index; |
| } |
| |
| void ctf__add_short_member(struct ctf *ctf, uint32_t name, uint16_t type, |
| uint16_t offset, int64_t *position) |
| { |
| struct ctf_short_member m = { |
| .ctf_member_name = name, |
| .ctf_member_type = type, |
| .ctf_member_offset = offset, |
| }; |
| |
| memcpy(gobuffer__ptr(&ctf->types, *position), &m, sizeof(m)); |
| *position += sizeof(m); |
| } |
| |
| void ctf__add_full_member(struct ctf *ctf, uint32_t name, uint16_t type, |
| uint64_t offset, int64_t *position) |
| { |
| struct ctf_full_member m = { |
| .ctf_member_name = name, |
| .ctf_member_type = type, |
| .ctf_member_offset_high = offset >> 32, |
| .ctf_member_offset_low = offset & 0xffffffffl, |
| }; |
| |
| memcpy(gobuffer__ptr(&ctf->types, *position), &m, sizeof(m)); |
| *position += sizeof(m); |
| } |
| |
| uint32_t ctf__add_struct(struct ctf *ctf, uint16_t kind, uint32_t name, |
| uint64_t size, uint16_t nr_members, int64_t *position) |
| { |
| const bool is_short = size < CTF_SHORT_MEMBER_LIMIT; |
| uint32_t members_len = ((is_short ? sizeof(struct ctf_short_member) : |
| sizeof(struct ctf_full_member)) * |
| nr_members); |
| struct ctf_full_type t; |
| int len; |
| |
| t.base.ctf_name = name; |
| t.base.ctf_info = CTF_INFO_ENCODE(kind, nr_members, 0); |
| if (size < 0xffff) { |
| len = sizeof(t.base); |
| t.base.ctf_size = size; |
| } else { |
| len = sizeof(t); |
| t.base.ctf_size = 0xffff; |
| t.ctf_size_high = size >> 32; |
| t.ctf_size_low = size & 0xffffffff; |
| } |
| |
| gobuffer__add(&ctf->types, &t, len); |
| *position = gobuffer__allocate(&ctf->types, members_len); |
| return ++ctf->type_index; |
| } |
| |
| void ctf__add_parameter(struct ctf *ctf, uint16_t type, int64_t *position) |
| { |
| uint16_t *parm = gobuffer__ptr(&ctf->types, *position); |
| |
| *parm = type; |
| *position += sizeof(*parm); |
| } |
| |
| uint32_t ctf__add_function_type(struct ctf *ctf, uint16_t type, uint16_t nr_parms, |
| bool varargs, int64_t *position) |
| { |
| struct ctf_short_type t; |
| int len = sizeof(uint16_t) * (nr_parms + !!varargs); |
| |
| /* |
| * Round up to next multiple of 4 to maintain 32-bit alignment. |
| */ |
| if (len & 0x2) |
| len += 0x2; |
| |
| t.ctf_name = 0; |
| t.ctf_info = CTF_INFO_ENCODE(CTF_TYPE_KIND_FUNC, |
| nr_parms + !!varargs, 0); |
| t.ctf_type = type; |
| |
| gobuffer__add(&ctf->types, &t, sizeof(t)); |
| *position = gobuffer__allocate(&ctf->types, len); |
| if (varargs) { |
| unsigned int pos = *position + (nr_parms * sizeof(uint16_t)); |
| uint16_t *end_of_args = gobuffer__ptr(&ctf->types, pos); |
| *end_of_args = 0; |
| } |
| |
| return ++ctf->type_index; |
| } |
| |
| uint32_t ctf__add_enumeration_type(struct ctf *ctf, uint32_t name, uint16_t size, |
| uint16_t nr_entries, int64_t *position) |
| { |
| struct ctf_short_type e; |
| |
| e.ctf_name = name; |
| e.ctf_info = CTF_INFO_ENCODE(CTF_TYPE_KIND_ENUM, nr_entries, 0); |
| e.ctf_size = size; |
| |
| gobuffer__add(&ctf->types, &e, sizeof(e)); |
| *position = gobuffer__allocate(&ctf->types, |
| nr_entries * sizeof(struct ctf_enum)); |
| return ++ctf->type_index; |
| } |
| |
| void ctf__add_enumerator(struct ctf *ctf, uint32_t name, uint32_t value, |
| int64_t *position) |
| { |
| struct ctf_enum m = { |
| .ctf_enum_name = name, |
| .ctf_enum_val = value, |
| }; |
| |
| memcpy(gobuffer__ptr(&ctf->types, *position), &m, sizeof(m)); |
| *position += sizeof(m); |
| } |
| |
| void ctf__add_function_parameter(struct ctf *ctf, uint16_t type, |
| int64_t *position) |
| { |
| uint16_t *parm = gobuffer__ptr(&ctf->funcs, *position); |
| |
| *parm = type; |
| *position += sizeof(*parm); |
| } |
| |
| int ctf__add_function(struct ctf *ctf, uint16_t type, uint16_t nr_parms, |
| bool varargs, int64_t *position) |
| { |
| struct ctf_short_type func; |
| int len = sizeof(uint16_t) * (nr_parms + !!varargs); |
| |
| /* |
| * Round up to next multiple of 4 to maintain 32-bit alignment. |
| */ |
| if (len & 0x2) |
| len += 0x2; |
| |
| func.ctf_info = CTF_INFO_ENCODE(CTF_TYPE_KIND_FUNC, |
| nr_parms + !!varargs, 0); |
| func.ctf_type = type; |
| |
| /* |
| * We don't store the name for the function, it comes from the |
| * symtab. |
| */ |
| gobuffer__add(&ctf->funcs, &func.ctf_info, |
| sizeof(func) - sizeof(func.ctf_name)); |
| *position = gobuffer__allocate(&ctf->funcs, len); |
| if (varargs) { |
| unsigned int pos = *position + (nr_parms * sizeof(uint16_t)); |
| uint16_t *end_of_args = gobuffer__ptr(&ctf->funcs, pos); |
| *end_of_args = 0; |
| } |
| |
| return 0; |
| } |
| |
| int ctf__add_object(struct ctf *ctf, uint16_t type) |
| { |
| return gobuffer__add(&ctf->objects, &type, |
| sizeof(type)) >= 0 ? 0 : -ENOMEM; |
| } |
| |
| static const void *ctf__compress(void *orig_buf, unsigned int *size) |
| { |
| z_stream z = { |
| .zalloc = Z_NULL, |
| .zfree = Z_NULL, |
| .opaque = Z_NULL, |
| .avail_in = *size, |
| .next_in = (Bytef *)orig_buf, |
| }; |
| void *bf = NULL; |
| unsigned int bf_size = 0; |
| |
| if (deflateInit(&z, Z_BEST_COMPRESSION) != Z_OK) |
| goto out; |
| |
| #define _GOBUFFER__ZCHUNK 16384 * 1024 |
| |
| do { |
| const unsigned int new_bf_size = bf_size + _GOBUFFER__ZCHUNK; |
| void *nbf = realloc(bf, new_bf_size); |
| |
| if (nbf == NULL) |
| goto out_close_and_free; |
| |
| bf = nbf; |
| z.avail_out = _GOBUFFER__ZCHUNK; |
| z.next_out = (Bytef *)bf + bf_size; |
| bf_size = new_bf_size; |
| if (deflate(&z, Z_FULL_FLUSH) == Z_STREAM_ERROR) |
| goto out_close_and_free; |
| #if 0 |
| fprintf(stderr, |
| "%s: size=%d, bf_size=%d, total_out=%ld, total_in=%ld\n", |
| __func__, *size, bf_size, z.total_out, z.total_in); |
| #endif |
| } while (z.total_in != *size); |
| |
| if (deflate(&z, Z_FINISH) == Z_STREAM_ERROR) |
| goto out_close_and_free; |
| |
| deflateEnd(&z); |
| *size = z.total_out; |
| out: |
| return bf; |
| |
| out_close_and_free: |
| deflateEnd(&z); |
| free(bf); |
| bf = NULL; |
| goto out; |
| } |
| |
| int ctf__encode(struct ctf *ctf, uint8_t flags) |
| { |
| struct ctf_header *hdr; |
| unsigned int size; |
| void *bf = NULL; |
| int err = -1; |
| |
| /* Empty file, nothing to do, so... done! */ |
| if (gobuffer__size(&ctf->types) == 0) |
| return 0; |
| |
| size = (gobuffer__size(&ctf->types) + |
| gobuffer__size(&ctf->objects) + |
| gobuffer__size(&ctf->funcs) + |
| gobuffer__size(ctf->strings)); |
| |
| ctf->size = sizeof(*hdr) + size; |
| ctf->buf = malloc(ctf->size); |
| |
| if (ctf->buf == NULL) { |
| fprintf(stderr, "%s: malloc failed!\n", __func__); |
| return -ENOMEM; |
| } |
| |
| hdr = ctf->buf; |
| memset(hdr, 0, sizeof(*hdr)); |
| hdr->ctf_magic = CTF_MAGIC; |
| hdr->ctf_version = 2; |
| hdr->ctf_flags = flags; |
| |
| uint32_t offset = 0; |
| hdr->ctf_object_off = offset; |
| offset += gobuffer__size(&ctf->objects); |
| hdr->ctf_func_off = offset; |
| offset += gobuffer__size(&ctf->funcs); |
| hdr->ctf_type_off = offset; |
| offset += gobuffer__size(&ctf->types); |
| hdr->ctf_str_off = offset; |
| hdr->ctf_str_len = gobuffer__size(ctf->strings); |
| |
| void *payload = ctf->buf + sizeof(*hdr); |
| gobuffer__copy(&ctf->objects, payload + hdr->ctf_object_off); |
| gobuffer__copy(&ctf->funcs, payload + hdr->ctf_func_off); |
| gobuffer__copy(&ctf->types, payload + hdr->ctf_type_off); |
| gobuffer__copy(ctf->strings, payload + hdr->ctf_str_off); |
| |
| *(char *)(ctf->buf + sizeof(*hdr) + hdr->ctf_str_off) = '\0'; |
| if (flags & CTF_FLAGS_COMPR) { |
| bf = (void *)ctf__compress(ctf->buf + sizeof(*hdr), &size); |
| if (bf == NULL) { |
| printf("%s: ctf__compress failed!\n", __func__); |
| return -ENOMEM; |
| } |
| void *new_bf = malloc(sizeof(*hdr) + size); |
| if (new_bf == NULL) |
| return -ENOMEM; |
| memcpy(new_bf, hdr, sizeof(*hdr)); |
| memcpy(new_bf + sizeof(*hdr), bf, size); |
| free(bf); |
| bf = new_bf; |
| size += sizeof(*hdr); |
| } else { |
| bf = ctf->buf; |
| size = ctf->size; |
| } |
| #if 0 |
| printf("\n\ntypes:\n entries: %d\n size: %u" |
| "\nstrings:\n entries: %u\n size: %u\ncompressed size: %d\n", |
| ctf->type_index, |
| gobuffer__size(&ctf->types), |
| gobuffer__nr_entries(ctf->strings), |
| gobuffer__size(ctf->strings), size); |
| #endif |
| int fd = open(ctf->filename, O_RDWR); |
| if (fd < 0) { |
| fprintf(stderr, "Cannot open %s\n", ctf->filename); |
| return -1; |
| } |
| |
| if (elf_version(EV_CURRENT) == EV_NONE) { |
| fprintf(stderr, "Cannot set libelf version.\n"); |
| goto out_close; |
| } |
| |
| Elf *elf = elf_begin(fd, ELF_C_RDWR, NULL); |
| if (elf == NULL) { |
| fprintf(stderr, "Cannot update ELF file.\n"); |
| goto out_close; |
| } |
| |
| elf_flagelf(elf, ELF_C_SET, ELF_F_DIRTY); |
| |
| GElf_Ehdr ehdr_mem; |
| GElf_Ehdr *ehdr = gelf_getehdr(elf, &ehdr_mem); |
| if (ehdr == NULL) { |
| fprintf(stderr, "%s: elf_getehdr failed.\n", __func__); |
| goto out_close; |
| } |
| |
| /* |
| * First we look if there was already a .SUNW_ctf section to overwrite. |
| */ |
| Elf_Data *data = NULL; |
| size_t strndx; |
| GElf_Shdr shdr_mem; |
| GElf_Shdr *shdr; |
| Elf_Scn *scn = NULL; |
| |
| elf_getshdrstrndx(elf, &strndx); |
| |
| while ((scn = elf_nextscn(elf, scn)) != NULL) { |
| shdr = gelf_getshdr(scn, &shdr_mem); |
| if (shdr == NULL) |
| continue; |
| char *secname = elf_strptr(elf, strndx, shdr->sh_name); |
| if (strcmp(secname, ".SUNW_ctf") == 0) { |
| data = elf_getdata(scn, data); |
| goto out_update; |
| } |
| } |
| /* FIXME |
| * OK, if we have the section, that is ok, we can just replace the |
| * data, if not, I made a mistake on the small amount of boilerplate |
| * below, probably .relA.ted to relocations... |
| */ |
| #if 0 |
| /* Now we look if the ".SUNW_ctf" string is in the strings table */ |
| scn = elf_getscn(elf, strndx); |
| shdr = gelf_getshdr(scn, &shdr_mem); |
| |
| data = elf_getdata(scn, data); |
| |
| fprintf(stderr, "Looking for the string\n"); |
| size_t ctf_name_offset = 1; /* First byte is '\0' */ |
| while (ctf_name_offset < data->d_size) { |
| const char *cur_str = data->d_buf + ctf_name_offset; |
| |
| fprintf(stderr, "*-> %s\n", cur_str); |
| if (strcmp(cur_str, ".SUNW_ctf") == 0) |
| goto found_SUNW_ctf_str; |
| |
| ctf_name_offset += strlen(cur_str) + 1; |
| } |
| |
| /* Add the section name */ |
| const size_t ctf_name_len = strlen(".SUNW_ctf") + 1; |
| char *new_strings_table = malloc(data->d_size + ctf_name_len); |
| if (new_strings_table == NULL) |
| goto out_close; |
| |
| memcpy(new_strings_table, data->d_buf, data->d_size); |
| strcpy(new_strings_table + data->d_size, ".SUNW_ctf"); |
| ctf_name_offset = data->d_size; |
| data->d_size += ctf_name_len; |
| data->d_buf = new_strings_table; |
| elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY); |
| elf_flagshdr(scn, ELF_C_SET, ELF_F_DIRTY); |
| |
| Elf_Scn *newscn; |
| found_SUNW_ctf_str: |
| newscn = elf_newscn(elf); |
| if (newscn == NULL) |
| goto out_close; |
| |
| data = elf_newdata(newscn); |
| if (data == NULL) |
| goto out_close; |
| |
| shdr = gelf_getshdr(newscn, &shdr_mem); |
| shdr->sh_name = ctf_name_offset; |
| shdr->sh_type = SHT_PROGBITS; |
| gelf_update_shdr(newscn, &shdr_mem); |
| elf_flagshdr(newscn, ELF_C_SET, ELF_F_DIRTY); |
| #else |
| char pathname[PATH_MAX]; |
| snprintf(pathname, sizeof(pathname), "%s.SUNW_ctf", ctf->filename); |
| fd = creat(pathname, S_IRUSR | S_IWUSR); |
| if (fd == -1) { |
| fprintf(stderr, "%s: open(%s) failed!\n", __func__, pathname); |
| goto out_close; |
| } |
| if (write(fd, bf, size) != size) |
| goto out_close; |
| |
| if (close(fd) < 0) |
| goto out_unlink; |
| |
| char cmd[PATH_MAX * 2]; |
| snprintf(cmd, sizeof(cmd), "objcopy --add-section .SUNW_ctf=%s %s", |
| pathname, ctf->filename); |
| if (system(cmd) == 0) |
| err = 0; |
| out_unlink: |
| unlink(pathname); |
| return err; |
| #endif |
| out_update: |
| data->d_buf = bf; |
| data->d_size = size; |
| elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY); |
| |
| if (elf_update(elf, ELF_C_NULL) < 0) |
| goto out_close; |
| if (elf_update(elf, ELF_C_WRITE) < 0) |
| goto out_close; |
| |
| elf_end(elf); |
| err = 0; |
| out_close: |
| if (bf != ctf->buf) |
| free(bf); |
| close(fd); |
| return err; |
| } |