Index: head/contrib/elftoolchain/elfcopy/ascii.c =================================================================== --- head/contrib/elftoolchain/elfcopy/ascii.c (revision 275368) +++ head/contrib/elftoolchain/elfcopy/ascii.c (revision 275369) @@ -1,1078 +1,1079 @@ /*- * Copyright (c) 2010,2011 Kai Wang * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include +#include #include #include #include #include #include "elfcopy.h" ELFTC_VCSID("$Id: ascii.c 2358 2011-12-19 18:22:32Z kaiwang27 $"); static void append_data(struct section *s, const void *buf, size_t sz); static char hex_digit(uint8_t n); static int hex_value(int x); static void finalize_data_section(struct section *s); static int ishexdigit(int x); static int ihex_read(const char *line, char *type, uint64_t *addr, uint64_t *num, uint8_t *data, size_t *sz); static void ihex_write(int ofd, int type, uint64_t addr, uint64_t num, const void *buf, size_t sz); static void ihex_write_00(int ofd, uint64_t addr, const void *buf, size_t sz); static void ihex_write_01(int ofd); static void ihex_write_04(int ofd, uint16_t addr); static void ihex_write_05(int ofd, uint64_t e_entry); static struct section *new_data_section(struct elfcopy *ecp, int sec_index, uint64_t off, uint64_t addr); static int read_num(const char *line, int *len, uint64_t *num, size_t sz, int *checksum); static int srec_read(const char *line, char *type, uint64_t *addr, uint8_t *data, size_t *sz); static void srec_write(int ofd, char type, uint64_t addr, const void *buf, size_t sz); static void srec_write_symtab(int ofd, const char *ofn, Elf *e, Elf_Scn *scn, GElf_Shdr *sh); static void srec_write_S0(int ofd, const char *ofn); static void srec_write_Sd(int ofd, char dr, uint64_t addr, const void *buf, size_t sz, size_t rlen); static void srec_write_Se(int ofd, uint64_t e_entry, int forceS3); static void write_num(char *line, int *len, uint64_t num, size_t sz, int *checksum); #define _LINE_BUFSZ 1024 #define _DATA_BUFSZ 256 /* * Convert ELF object to S-Record. */ void create_srec(struct elfcopy *ecp, int ifd, int ofd, const char *ofn) { Elf *e; Elf_Scn *scn; Elf_Data *d; GElf_Ehdr eh; GElf_Shdr sh; uint64_t max_addr; size_t rlen; int elferr, addr_sz; char dr; if ((e = elf_begin(ifd, ELF_C_READ, NULL)) == NULL) errx(EXIT_FAILURE, "elf_begin() failed: %s", elf_errmsg(-1)); /* Output a symbol table for `symbolsrec' target. */ if (!strncmp(ecp->otgt, "symbolsrec", strlen("symbolsrec"))) { scn = NULL; while ((scn = elf_nextscn(e, scn)) != NULL) { if (gelf_getshdr(scn, &sh) == NULL) { warnx("gelf_getshdr failed: %s", elf_errmsg(-1)); (void) elf_errno(); continue; } if (sh.sh_type != SHT_SYMTAB) continue; srec_write_symtab(ofd, ofn, e, scn, &sh); break; } } if (ecp->flags & SREC_FORCE_S3) dr = '3'; else { /* * Find maximum address size in the first iteration. */ max_addr = 0; scn = NULL; while ((scn = elf_nextscn(e, scn)) != NULL) { if (gelf_getshdr(scn, &sh) == NULL) { warnx("gelf_getshdr failed: %s", elf_errmsg(-1)); (void) elf_errno(); continue; } if ((sh.sh_flags & SHF_ALLOC) == 0 || sh.sh_type == SHT_NOBITS || sh.sh_size == 0) continue; if ((uint64_t) sh.sh_addr > max_addr) max_addr = sh.sh_addr; } elferr = elf_errno(); if (elferr != 0) warnx("elf_nextscn failed: %s", elf_errmsg(elferr)); if (max_addr <= 0xFFFF) dr = '1'; else if (max_addr <= 0xFFFFFF) dr = '2'; else dr = '3'; } if (ecp->flags & SREC_FORCE_LEN) { addr_sz = dr - '0' + 1; if (ecp->srec_len < 1) rlen = 1; else if (ecp->srec_len + addr_sz + 1 > 255) rlen = 255 - (addr_sz + 1); else rlen = ecp->srec_len; } else rlen = 16; /* Generate S0 record which contains the output filename. */ srec_write_S0(ofd, ofn); /* Generate S{1,2,3} data records for section data. */ scn = NULL; while ((scn = elf_nextscn(e, scn)) != NULL) { if (gelf_getshdr(scn, &sh) == NULL) { warnx("gelf_getshdr failed: %s", elf_errmsg(-1)); (void) elf_errno(); continue; } if ((sh.sh_flags & SHF_ALLOC) == 0 || sh.sh_type == SHT_NOBITS || sh.sh_size == 0) continue; if (sh.sh_addr > 0xFFFFFFFF) { warnx("address space too big for S-Record file"); continue; } (void) elf_errno(); if ((d = elf_getdata(scn, NULL)) == NULL) { elferr = elf_errno(); if (elferr != 0) warnx("elf_getdata failed: %s", elf_errmsg(-1)); continue; } if (d->d_buf == NULL || d->d_size == 0) continue; srec_write_Sd(ofd, dr, sh.sh_addr, d->d_buf, d->d_size, rlen); } elferr = elf_errno(); if (elferr != 0) warnx("elf_nextscn failed: %s", elf_errmsg(elferr)); /* Generate S{7,8,9} end of block recrod. */ if (gelf_getehdr(e, &eh) == NULL) errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", elf_errmsg(-1)); srec_write_Se(ofd, eh.e_entry, ecp->flags & SREC_FORCE_S3); } void create_elf_from_srec(struct elfcopy *ecp, int ifd) { char line[_LINE_BUFSZ], name[_LINE_BUFSZ]; uint8_t data[_DATA_BUFSZ]; GElf_Ehdr oeh; struct section *s, *shtab; FILE *ifp; uint64_t addr, entry, off, sec_addr; uintmax_t st_value; size_t sz; int _ifd, first, sec_index, in_symtab, symtab_created; char *rlt; char type; if ((_ifd = dup(ifd)) < 0) err(EXIT_FAILURE, "dup failed"); if ((ifp = fdopen(_ifd, "r")) == NULL) err(EXIT_FAILURE, "fdopen failed"); /* Create EHDR for output .o file. */ if (gelf_newehdr(ecp->eout, ecp->oec) == NULL) errx(EXIT_FAILURE, "gelf_newehdr failed: %s", elf_errmsg(-1)); if (gelf_getehdr(ecp->eout, &oeh) == NULL) errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", elf_errmsg(-1)); /* Initialise e_ident fields. */ oeh.e_ident[EI_CLASS] = ecp->oec; oeh.e_ident[EI_DATA] = ecp->oed; /* * TODO: Set OSABI according to the OS platform where elfcopy(1) * was build. (probably) */ oeh.e_ident[EI_OSABI] = ELFOSABI_NONE; oeh.e_machine = ecp->oem; oeh.e_type = ET_REL; oeh.e_entry = 0; ecp->flags |= RELOCATABLE; /* Create .shstrtab section */ init_shstrtab(ecp); ecp->shstrtab->off = 0; /* Data sections are inserted after EHDR. */ off = gelf_fsize(ecp->eout, ELF_T_EHDR, 1, EV_CURRENT); if (off == 0) errx(EXIT_FAILURE, "gelf_fsize() failed: %s", elf_errmsg(-1)); /* Create data sections. */ s = NULL; first = 1; sec_index = 1; sec_addr = entry = 0; while (fgets(line, _LINE_BUFSZ, ifp) != NULL) { if (line[0] == '\r' || line[0] == '\n') continue; if (line[0] == '$' && line[1] == '$') { ecp->flags |= SYMTAB_EXIST; while ((rlt = fgets(line, _LINE_BUFSZ, ifp)) != NULL) { if (line[0] == '$' && line[1] == '$') break; } if (rlt == NULL) break; continue; } if (line[0] != 'S' || line[1] < '0' || line[1] > '9') { warnx("Invalid srec record"); continue; } if (srec_read(line, &type, &addr, data, &sz) < 0) { warnx("Invalid srec record or mismatched checksum"); continue; } switch (type) { case '1': case '2': case '3': if (sz == 0) break; if (first || sec_addr != addr) { if (s != NULL) finalize_data_section(s); s = new_data_section(ecp, sec_index, off, addr); if (s == NULL) { warnx("new_data_section failed"); break; } sec_index++; sec_addr = addr; first = 0; } append_data(s, data, sz); off += sz; sec_addr += sz; break; case '7': case '8': case '9': entry = addr; break; default: break; } } if (s != NULL) finalize_data_section(s); if (ferror(ifp)) warn("fgets failed"); /* Insert .shstrtab after data sections. */ if ((ecp->shstrtab->os = elf_newscn(ecp->eout)) == NULL) errx(EXIT_FAILURE, "elf_newscn failed: %s", elf_errmsg(-1)); insert_to_sec_list(ecp, ecp->shstrtab, 1); /* Insert section header table here. */ shtab = insert_shtab(ecp, 1); /* * Rescan and create symbol table if we found '$$' section in * the first scan. */ symtab_created = 0; in_symtab = 0; if (ecp->flags & SYMTAB_EXIST) { if (fseek(ifp, 0, SEEK_SET) < 0) { warn("fseek failed"); ecp->flags &= ~SYMTAB_EXIST; goto done; } while (fgets(line, _LINE_BUFSZ, ifp) != NULL) { if (in_symtab) { if (line[0] == '$' && line[1] == '$') { in_symtab = 0; continue; } if (sscanf(line, "%s $%jx", name, &st_value) != 2) { warnx("Invalid symbolsrec record"); continue; } if (!symtab_created) { create_external_symtab(ecp); symtab_created = 1; } add_to_symtab(ecp, name, st_value, 0, SHN_ABS, ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, 1); } if (line[0] == '$' && line[1] == '$') { in_symtab = 1; continue; } } } if (ferror(ifp)) warn("fgets failed"); if (symtab_created) { finalize_external_symtab(ecp); create_symtab_data(ecp); /* Count in .symtab and .strtab section headers. */ shtab->sz += gelf_fsize(ecp->eout, ELF_T_SHDR, 2, EV_CURRENT); } else ecp->flags &= ~SYMTAB_EXIST; done: fclose(ifp); /* Set entry point. */ oeh.e_entry = entry; /* * Write the underlying ehdr. Note that it should be called * before elf_setshstrndx() since it will overwrite e->e_shstrndx. */ if (gelf_update_ehdr(ecp->eout, &oeh) == 0) errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s", elf_errmsg(-1)); /* Generate section name string table (.shstrtab). */ set_shstrtab(ecp); /* Update sh_name pointer for each section header entry. */ update_shdr(ecp, 0); /* Renew oeh to get the updated e_shstrndx. */ if (gelf_getehdr(ecp->eout, &oeh) == NULL) errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", elf_errmsg(-1)); /* Resync section offsets. */ resync_sections(ecp); /* Store SHDR offset in EHDR. */ oeh.e_shoff = shtab->off; /* Update ehdr since we modified e_shoff. */ if (gelf_update_ehdr(ecp->eout, &oeh) == 0) errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s", elf_errmsg(-1)); /* Write out the output elf object. */ if (elf_update(ecp->eout, ELF_C_WRITE) < 0) errx(EXIT_FAILURE, "elf_update() failed: %s", elf_errmsg(-1)); /* Release allocated resource. */ free_elf(ecp); } void create_ihex(int ifd, int ofd) { Elf *e; Elf_Scn *scn; Elf_Data *d; GElf_Ehdr eh; GElf_Shdr sh; int elferr; uint16_t addr_hi, old_addr_hi; if ((e = elf_begin(ifd, ELF_C_READ, NULL)) == NULL) errx(EXIT_FAILURE, "elf_begin() failed: %s", elf_errmsg(-1)); old_addr_hi = 0; scn = NULL; while ((scn = elf_nextscn(e, scn)) != NULL) { if (gelf_getshdr(scn, &sh) == NULL) { warnx("gelf_getshdr failed: %s", elf_errmsg(-1)); (void) elf_errno(); continue; } if ((sh.sh_flags & SHF_ALLOC) == 0 || sh.sh_type == SHT_NOBITS || sh.sh_size == 0) continue; if (sh.sh_addr > 0xFFFFFFFF) { warnx("address space too big for Intel Hex file"); continue; } (void) elf_errno(); if ((d = elf_getdata(scn, NULL)) == NULL) { elferr = elf_errno(); if (elferr != 0) warnx("elf_getdata failed: %s", elf_errmsg(-1)); continue; } if (d->d_buf == NULL || d->d_size == 0) continue; addr_hi = (sh.sh_addr >> 16) & 0xFFFF; if (addr_hi > 0 && addr_hi != old_addr_hi) { /* Write 04 record if addr_hi is new. */ old_addr_hi = addr_hi; ihex_write_04(ofd, addr_hi); } ihex_write_00(ofd, sh.sh_addr, d->d_buf, d->d_size); } elferr = elf_errno(); if (elferr != 0) warnx("elf_nextscn failed: %s", elf_errmsg(elferr)); if (gelf_getehdr(e, &eh) == NULL) errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", elf_errmsg(-1)); ihex_write_05(ofd, eh.e_entry); ihex_write_01(ofd); } void create_elf_from_ihex(struct elfcopy *ecp, int ifd) { char line[_LINE_BUFSZ]; uint8_t data[_DATA_BUFSZ]; GElf_Ehdr oeh; struct section *s, *shtab; FILE *ifp; uint64_t addr, addr_base, entry, num, off, rec_addr, sec_addr; size_t sz; int _ifd, first, sec_index; char type; if ((_ifd = dup(ifd)) < 0) err(EXIT_FAILURE, "dup failed"); if ((ifp = fdopen(_ifd, "r")) == NULL) err(EXIT_FAILURE, "fdopen failed"); /* Create EHDR for output .o file. */ if (gelf_newehdr(ecp->eout, ecp->oec) == NULL) errx(EXIT_FAILURE, "gelf_newehdr failed: %s", elf_errmsg(-1)); if (gelf_getehdr(ecp->eout, &oeh) == NULL) errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", elf_errmsg(-1)); /* Initialise e_ident fields. */ oeh.e_ident[EI_CLASS] = ecp->oec; oeh.e_ident[EI_DATA] = ecp->oed; /* * TODO: Set OSABI according to the OS platform where elfcopy(1) * was build. (probably) */ oeh.e_ident[EI_OSABI] = ELFOSABI_NONE; oeh.e_machine = ecp->oem; oeh.e_type = ET_REL; oeh.e_entry = 0; ecp->flags |= RELOCATABLE; /* Create .shstrtab section */ init_shstrtab(ecp); ecp->shstrtab->off = 0; /* Data sections are inserted after EHDR. */ off = gelf_fsize(ecp->eout, ELF_T_EHDR, 1, EV_CURRENT); if (off == 0) errx(EXIT_FAILURE, "gelf_fsize() failed: %s", elf_errmsg(-1)); /* Create data sections. */ s = NULL; first = 1; sec_index = 1; addr_base = rec_addr = sec_addr = entry = 0; while (fgets(line, _LINE_BUFSZ, ifp) != NULL) { if (line[0] == '\r' || line[0] == '\n') continue; if (line[0] != ':') { warnx("Invalid ihex record"); continue; } if (ihex_read(line, &type, &addr, &num, data, &sz) < 0) { warnx("Invalid ihex record or mismatched checksum"); continue; } switch (type) { case '0': /* Data record. */ if (sz == 0) break; rec_addr = addr_base + addr; if (first || sec_addr != rec_addr) { if (s != NULL) finalize_data_section(s); s = new_data_section(ecp, sec_index, off, rec_addr); if (s == NULL) { warnx("new_data_section failed"); break; } sec_index++; sec_addr = rec_addr; first = 0; } append_data(s, data, sz); off += sz; sec_addr += sz; break; case '1': /* End of file record. */ goto done; case '2': /* Extended segment address record. */ addr_base = addr << 4; break; case '3': /* Start segment address record (CS:IP). Ignored. */ break; case '4': /* Extended linear address record. */ addr_base = num << 16; break; case '5': /* Start linear address record. */ entry = num; break; default: break; } } done: if (s != NULL) finalize_data_section(s); if (ferror(ifp)) warn("fgets failed"); fclose(ifp); /* Insert .shstrtab after data sections. */ if ((ecp->shstrtab->os = elf_newscn(ecp->eout)) == NULL) errx(EXIT_FAILURE, "elf_newscn failed: %s", elf_errmsg(-1)); insert_to_sec_list(ecp, ecp->shstrtab, 1); /* Insert section header table here. */ shtab = insert_shtab(ecp, 1); /* Set entry point. */ oeh.e_entry = entry; /* * Write the underlying ehdr. Note that it should be called * before elf_setshstrndx() since it will overwrite e->e_shstrndx. */ if (gelf_update_ehdr(ecp->eout, &oeh) == 0) errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s", elf_errmsg(-1)); /* Generate section name string table (.shstrtab). */ set_shstrtab(ecp); /* Update sh_name pointer for each section header entry. */ update_shdr(ecp, 0); /* Renew oeh to get the updated e_shstrndx. */ if (gelf_getehdr(ecp->eout, &oeh) == NULL) errx(EXIT_FAILURE, "gelf_getehdr() failed: %s", elf_errmsg(-1)); /* Resync section offsets. */ resync_sections(ecp); /* Store SHDR offset in EHDR. */ oeh.e_shoff = shtab->off; /* Update ehdr since we modified e_shoff. */ if (gelf_update_ehdr(ecp->eout, &oeh) == 0) errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s", elf_errmsg(-1)); /* Write out the output elf object. */ if (elf_update(ecp->eout, ELF_C_WRITE) < 0) errx(EXIT_FAILURE, "elf_update() failed: %s", elf_errmsg(-1)); /* Release allocated resource. */ free_elf(ecp); } #define _SEC_NAMESZ 64 #define _SEC_INIT_CAP 1024 static struct section * new_data_section(struct elfcopy *ecp, int sec_index, uint64_t off, uint64_t addr) { char *name; if ((name = malloc(_SEC_NAMESZ)) == NULL) errx(EXIT_FAILURE, "malloc failed"); snprintf(name, _SEC_NAMESZ, ".sec%d", sec_index); return (create_external_section(ecp, name, name, NULL, 0, off, SHT_PROGBITS, ELF_T_BYTE, SHF_ALLOC | SHF_WRITE, 1, addr, 0)); } static void finalize_data_section(struct section *s) { Elf_Data *od; if ((od = elf_newdata(s->os)) == NULL) errx(EXIT_FAILURE, "elf_newdata() failed: %s", elf_errmsg(-1)); od->d_align = s->align; od->d_off = 0; od->d_buf = s->buf; od->d_size = s->sz; od->d_version = EV_CURRENT; } static void append_data(struct section *s, const void *buf, size_t sz) { uint8_t *p; if (s->buf == NULL) { s->sz = 0; s->cap = _SEC_INIT_CAP; if ((s->buf = malloc(s->cap)) == NULL) err(EXIT_FAILURE, "malloc failed"); } while (sz + s->sz > s->cap) { s->cap *= 2; if ((s->buf = realloc(s->buf, s->cap)) == NULL) err(EXIT_FAILURE, "realloc failed"); } p = s->buf; memcpy(&p[s->sz], buf, sz); s->sz += sz; } static int srec_read(const char *line, char *type, uint64_t *addr, uint8_t *data, size_t *sz) { uint64_t count, _checksum, num; size_t addr_sz; int checksum, i, len; checksum = 0; len = 2; if (read_num(line, &len, &count, 1, &checksum) < 0) return (-1); *type = line[1]; switch (*type) { case '0': case '1': case '5': case '9': addr_sz = 2; break; case '2': case '8': addr_sz = 3; break; case '3': case '7': addr_sz = 4; break; default: return (-1); } if (read_num(line, &len, addr, addr_sz, &checksum) < 0) return (-1); count -= addr_sz + 1; if (*type >= '0' && *type <= '3') { for (i = 0; (uint64_t) i < count; i++) { if (read_num(line, &len, &num, 1, &checksum) < 0) return -1; data[i] = (uint8_t) num; } *sz = count; } else *sz = 0; if (read_num(line, &len, &_checksum, 1, NULL) < 0) return (-1); if ((int) _checksum != (~checksum & 0xFF)) return (-1); return (0); } static void srec_write_symtab(int ofd, const char *ofn, Elf *e, Elf_Scn *scn, GElf_Shdr *sh) { char line[_LINE_BUFSZ]; GElf_Sym sym; Elf_Data *d; const char *name; size_t sc; int elferr, i; #define _WRITE_LINE do { \ if (write(ofd, line, strlen(line)) != (ssize_t) strlen(line)) \ errx(EXIT_FAILURE, "write failed"); \ } while (0) (void) elf_errno(); if ((d = elf_getdata(scn, NULL)) == NULL) { elferr = elf_errno(); if (elferr != 0) warnx("elf_getdata failed: %s", elf_errmsg(-1)); return; } if (d->d_buf == NULL || d->d_size == 0) return; snprintf(line, sizeof(line), "$$ %s\r\n", ofn); _WRITE_LINE; sc = d->d_size / sh->sh_entsize; for (i = 1; (size_t) i < sc; i++) { if (gelf_getsym(d, i, &sym) != &sym) { warnx("gelf_getsym failed: %s", elf_errmsg(-1)); continue; } if (GELF_ST_TYPE(sym.st_info) == STT_SECTION || GELF_ST_TYPE(sym.st_info) == STT_FILE) continue; if ((name = elf_strptr(e, sh->sh_link, sym.st_name)) == NULL) { warnx("elf_strptr failed: %s", elf_errmsg(-1)); continue; } snprintf(line, sizeof(line), " %s $%jx\r\n", name, (uintmax_t) sym.st_value); _WRITE_LINE; } snprintf(line, sizeof(line), "$$ \r\n"); _WRITE_LINE; #undef _WRITE_LINE } static void srec_write_S0(int ofd, const char *ofn) { srec_write(ofd, '0', 0, ofn, strlen(ofn)); } static void srec_write_Sd(int ofd, char dr, uint64_t addr, const void *buf, size_t sz, size_t rlen) { const uint8_t *p, *pe; p = buf; pe = p + sz; while (pe - p >= (int) rlen) { srec_write(ofd, dr, addr, p, rlen); addr += rlen; p += rlen; } if (pe - p > 0) srec_write(ofd, dr, addr, p, pe - p); } static void srec_write_Se(int ofd, uint64_t e_entry, int forceS3) { char er; if (e_entry > 0xFFFFFFFF) { warnx("address space too big for S-Record file"); return; } if (forceS3) er = '7'; else { if (e_entry <= 0xFFFF) er = '9'; else if (e_entry <= 0xFFFFFF) er = '8'; else er = '7'; } srec_write(ofd, er, e_entry, NULL, 0); } static void srec_write(int ofd, char type, uint64_t addr, const void *buf, size_t sz) { char line[_LINE_BUFSZ]; const uint8_t *p, *pe; int len, addr_sz, checksum; if (type == '0' || type == '1' || type == '5' || type == '9') addr_sz = 2; else if (type == '2' || type == '8') addr_sz = 3; else addr_sz = 4; checksum = 0; line[0] = 'S'; line[1] = type; len = 2; write_num(line, &len, addr_sz + sz + 1, 1, &checksum); write_num(line, &len, addr, addr_sz, &checksum); for (p = buf, pe = p + sz; p < pe; p++) write_num(line, &len, *p, 1, &checksum); write_num(line, &len, ~checksum & 0xFF, 1, NULL); line[len++] = '\r'; line[len++] = '\n'; if (write(ofd, line, len) != (ssize_t) len) err(EXIT_FAILURE, "write failed"); } static void ihex_write_00(int ofd, uint64_t addr, const void *buf, size_t sz) { uint16_t addr_hi, old_addr_hi; const uint8_t *p, *pe; old_addr_hi = (addr >> 16) & 0xFFFF; p = buf; pe = p + sz; while (pe - p >= 16) { ihex_write(ofd, 0, addr, 0, p, 16); addr += 16; p += 16; addr_hi = (addr >> 16) & 0xFFFF; if (addr_hi != old_addr_hi) { old_addr_hi = addr_hi; ihex_write_04(ofd, addr_hi); } } if (pe - p > 0) ihex_write(ofd, 0, addr, 0, p, pe - p); } static int ihex_read(const char *line, char *type, uint64_t *addr, uint64_t *num, uint8_t *data, size_t *sz) { uint64_t count, _checksum; int checksum, i, len; *sz = 0; checksum = 0; len = 1; if (read_num(line, &len, &count, 1, &checksum) < 0) return (-1); if (read_num(line, &len, addr, 2, &checksum) < 0) return (-1); if (line[len++] != '0') return (-1); *type = line[len++]; checksum += *type - '0'; switch (*type) { case '0': for (i = 0; (uint64_t) i < count; i++) { if (read_num(line, &len, num, 1, &checksum) < 0) return (-1); data[i] = (uint8_t) *num; } *sz = count; break; case '1': if (count != 0) return (-1); break; case '2': case '4': if (count != 2) return (-1); if (read_num(line, &len, num, 2, &checksum) < 0) return (-1); break; case '3': case '5': if (count != 4) return (-1); if (read_num(line, &len, num, 4, &checksum) < 0) return (-1); break; default: return (-1); } if (read_num(line, &len, &_checksum, 1, &checksum) < 0) return (-1); if ((checksum & 0xFF) != 0) { return (-1); } return (0); } static void ihex_write_01(int ofd) { ihex_write(ofd, 1, 0, 0, NULL, 0); } static void ihex_write_04(int ofd, uint16_t addr) { ihex_write(ofd, 4, 0, addr, NULL, 2); } static void ihex_write_05(int ofd, uint64_t e_entry) { if (e_entry > 0xFFFFFFFF) { warnx("address space too big for Intel Hex file"); return; } ihex_write(ofd, 5, 0, e_entry, NULL, 4); } static void ihex_write(int ofd, int type, uint64_t addr, uint64_t num, const void *buf, size_t sz) { char line[_LINE_BUFSZ]; const uint8_t *p, *pe; int len, checksum; if (sz > 16) errx(EXIT_FAILURE, "Internal: ihex_write() sz too big"); checksum = 0; line[0] = ':'; len = 1; write_num(line, &len, sz, 1, &checksum); write_num(line, &len, addr, 2, &checksum); write_num(line, &len, type, 1, &checksum); if (sz > 0) { if (buf != NULL) { for (p = buf, pe = p + sz; p < pe; p++) write_num(line, &len, *p, 1, &checksum); } else write_num(line, &len, num, sz, &checksum); } write_num(line, &len, (~checksum + 1) & 0xFF, 1, NULL); line[len++] = '\r'; line[len++] = '\n'; if (write(ofd, line, len) != (ssize_t) len) err(EXIT_FAILURE, "write failed"); } static int read_num(const char *line, int *len, uint64_t *num, size_t sz, int *checksum) { uint8_t b; *num = 0; for (; sz > 0; sz--) { if (!ishexdigit(line[*len]) || !ishexdigit(line[*len + 1])) return (-1); b = (hex_value(line[*len]) << 4) | hex_value(line[*len + 1]); *num = (*num << 8) | b; *len += 2; if (checksum != NULL) *checksum = (*checksum + b) & 0xFF; } return (0); } static void write_num(char *line, int *len, uint64_t num, size_t sz, int *checksum) { uint8_t b; for (; sz > 0; sz--) { b = (num >> ((sz - 1) * 8)) & 0xFF; line[*len] = hex_digit((b >> 4) & 0xF); line[*len + 1] = hex_digit(b & 0xF); *len += 2; if (checksum != NULL) *checksum = (*checksum + b) & 0xFF; } } static char hex_digit(uint8_t n) { return ((n < 10) ? '0' + n : 'A' + (n - 10)); } static int hex_value(int x) { if (isdigit(x)) return (x - '0'); else if (x >= 'a' && x <= 'f') return (x - 'a' + 10); else return (x - 'A' + 10); } static int ishexdigit(int x) { if (isdigit(x)) return (1); if ((x >= 'a' && x <= 'f') || (x >= 'A' && x <= 'F')) return (1); return (0); } Index: head/contrib/elftoolchain/elfcopy/segments.c =================================================================== --- head/contrib/elftoolchain/elfcopy/segments.c (revision 275368) +++ head/contrib/elftoolchain/elfcopy/segments.c (revision 275369) @@ -1,493 +1,494 @@ /*- * Copyright (c) 2007-2010,2012 Kai Wang * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include +#include #include #include #include #include "elfcopy.h" ELFTC_VCSID("$Id: segments.c 2542 2012-08-12 16:14:15Z kaiwang27 $"); static void insert_to_inseg_list(struct segment *seg, struct section *sec); /* * elfcopy's segment handling is relatively simpler and less powerful than * libbfd. Program headers are modified or copied from input to output objects, * but never re-generated. As a result, if the input object has incorrect * program headers, the output object's program headers will remain incorrect * or become even worse. */ /* * Check whether a section is "loadable". If so, add it to the * corresponding segment list(s) and return 1. */ int add_to_inseg_list(struct elfcopy *ecp, struct section *s) { struct segment *seg; int loadable; if (ecp->ophnum == 0) return (0); /* * Segment is a different view of an ELF object. One segment can * contain one or more sections, and one section can be included * in one or more segments, or not included in any segment at all. * We call those sections which can be found in one or more segments * "loadable" sections, and call the rest "unloadable" sections. * We keep track of "loadable" sections in their containing * segment(s)' v_sec queue. These information are later used to * recalculate the extents of segments, when sections are removed, * for example. */ loadable = 0; STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) { if (s->off < seg->off) continue; if (s->off + s->sz > seg->off + seg->fsz && s->type != SHT_NOBITS) continue; if (s->off + s->sz > seg->off + seg->msz) continue; insert_to_inseg_list(seg, s); if (seg->type == PT_LOAD) s->seg = seg; s->lma = seg->addr + (s->off - seg->off); loadable = 1; } return (loadable); } void adjust_addr(struct elfcopy *ecp) { struct section *s, *s0; struct segment *seg; struct sec_action *sac; uint64_t dl, lma, old_vma, start, end; int found, i; /* * Apply VMA and global LMA changes in the first iteration. */ TAILQ_FOREACH(s, &ecp->v_sec, sec_list) { /* Only adjust loadable section's address. */ if (!s->loadable || s->seg == NULL) continue; /* Apply global LMA adjustment. */ if (ecp->change_addr != 0) s->lma += ecp->change_addr; if (!s->pseudo) { old_vma = s->vma; /* Apply global VMA adjustment. */ if (ecp->change_addr != 0) s->vma += ecp->change_addr; /* Apply section VMA adjustment. */ sac = lookup_sec_act(ecp, s->name, 0); if (sac == NULL) continue; if (sac->setvma) s->vma = sac->vma; if (sac->vma_adjust != 0) s->vma += sac->vma_adjust; } } /* * Apply sections LMA change in the second iteration. */ TAILQ_FOREACH(s, &ecp->v_sec, sec_list) { /* Only adjust loadable section's LMA. */ if (!s->loadable || s->seg == NULL) continue; /* * Check if there is a LMA change request for this * section. */ sac = lookup_sec_act(ecp, s->name, 0); if (sac == NULL) continue; if (!sac->setlma && sac->lma_adjust == 0) continue; lma = s->lma; if (sac->setlma) lma = sac->lma; if (sac->lma_adjust != 0) lma += sac->lma_adjust; if (lma == s->lma) continue; /* * Check if the LMA change is viable. * * 1. Check if the new LMA is properly aligned accroding to * section alignment. * * 2. Compute the new extent of segment that contains this * section, make sure it doesn't overlap with other * segments. */ #ifdef DEBUG printf("LMA for section %s: %#jx\n", s->name, lma); #endif if (lma % s->align != 0) errx(EXIT_FAILURE, "The load address %#jx for " "section %s is not aligned to %ju", (uintmax_t) lma, s->name, s->align); if (lma < s->lma) { /* Move section to lower address. */ if (lma < s->lma - s->seg->addr) errx(EXIT_FAILURE, "Not enough space to move " "section %s load address to %#jx", s->name, (uintmax_t) lma); start = lma - (s->lma - s->seg->addr); if (s == s->seg->v_sec[s->seg->nsec - 1]) end = start + s->seg->msz; else end = s->seg->addr + s->seg->msz; } else { /* Move section to upper address. */ if (s == s->seg->v_sec[0]) start = lma; else start = s->seg->addr; end = lma + (s->seg->addr + s->seg->msz - s->lma); if (end < start) errx(EXIT_FAILURE, "Not enough space to move " "section %s load address to %#jx", s->name, (uintmax_t) lma); } #ifdef DEBUG printf("new extent for segment containing %s: (%#jx,%#jx)\n", s->name, start, end); #endif STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) { if (seg == s->seg || seg->type != PT_LOAD) continue; if (start > seg->addr + seg->msz) continue; if (end < seg->addr) continue; errx(EXIT_FAILURE, "The extent of segment containing " "section %s overlaps with segment(%#jx,%#jx)", s->name, seg->addr, seg->addr + seg->msz); } /* * Update section LMA and file offset. */ if (lma < s->lma) { /* * To move a section to lower load address, we decrease * the load addresses of the section and all the * sections that are before it, and we increase the * file offsets of all the sections that are after it. */ dl = s->lma - lma; for (i = 0; i < s->seg->nsec; i++) { s0 = s->seg->v_sec[i]; s0->lma -= dl; #ifdef DEBUG printf("section %s LMA set to %#jx\n", s0->name, (uintmax_t) s0->lma); #endif if (s0 == s) break; } for (i = i + 1; i < s->seg->nsec; i++) { s0 = s->seg->v_sec[i]; s0->off += dl; #ifdef DEBUG printf("section %s offset set to %#jx\n", s0->name, (uintmax_t) s0->off); #endif } } else { /* * To move a section to upper load address, we increase * the load addresses of the section and all the * sections that are after it, and we increase the * their file offsets too unless the section in question * is the first in its containing segment. */ dl = lma - s->lma; for (i = 0; i < s->seg->nsec; i++) if (s->seg->v_sec[i] == s) break; if (i >= s->seg->nsec) errx(EXIT_FAILURE, "Internal: section `%s' not" " found in its containing segement", s->name); for (; i < s->seg->nsec; i++) { s0 = s->seg->v_sec[i]; s0->lma += dl; #ifdef DEBUG printf("section %s LMA set to %#jx\n", s0->name, (uintmax_t) s0->lma); #endif if (s != s->seg->v_sec[0]) { s0->off += dl; #ifdef DEBUG printf("section %s offset set to %#jx\n", s0->name, (uintmax_t) s0->off); #endif } } } } /* * Apply load address padding. */ if (ecp->pad_to != 0) { /* * Find the section with highest load address. */ s = NULL; STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) { if (seg->type != PT_LOAD) continue; for (i = seg->nsec - 1; i >= 0; i--) if (seg->v_sec[i]->type != SHT_NOBITS) break; if (i < 0) continue; if (s == NULL) s = seg->v_sec[i]; else { s0 = seg->v_sec[i]; if (s0->lma > s->lma) s = s0; } } if (s == NULL) goto issue_warn; /* No need to pad if the pad_to address is lower. */ if (ecp->pad_to <= s->lma + s->sz) goto issue_warn; s->pad_sz = ecp->pad_to - (s->lma + s->sz); #ifdef DEBUG printf("pad section %s load to address %#jx by %#jx\n", s->name, (uintmax_t) ecp->pad_to, (uintmax_t) s->pad_sz); #endif } issue_warn: /* * Issue a warning if there are VMA/LMA adjust requests for * some nonexistent sections. */ if ((ecp->flags & NO_CHANGE_WARN) == 0) { STAILQ_FOREACH(sac, &ecp->v_sac, sac_list) { if (!sac->setvma && !sac->setlma && !sac->vma_adjust && !sac->lma_adjust) continue; found = 0; TAILQ_FOREACH(s, &ecp->v_sec, sec_list) { if (s->pseudo || s->name == NULL) continue; if (!strcmp(s->name, sac->name)) { found = 1; break; } } if (!found) warnx("cannot find section `%s'", sac->name); } } } static void insert_to_inseg_list(struct segment *seg, struct section *sec) { struct section *s; int i; seg->nsec++; seg->v_sec = realloc(seg->v_sec, seg->nsec * sizeof(*seg->v_sec)); if (seg->v_sec == NULL) err(EXIT_FAILURE, "realloc failed"); /* * Sort the section in order of offset. */ for (i = seg->nsec - 1; i > 0; i--) { s = seg->v_sec[i - 1]; if (sec->off >= s->off) { seg->v_sec[i] = sec; break; } else seg->v_sec[i] = s; } if (i == 0) seg->v_sec[0] = sec; } void setup_phdr(struct elfcopy *ecp) { struct segment *seg; GElf_Phdr iphdr; size_t iphnum; int i; if (elf_getphnum(ecp->ein, &iphnum) == 0) errx(EXIT_FAILURE, "elf_getphnum failed: %s", elf_errmsg(-1)); ecp->ophnum = ecp->iphnum = iphnum; if (iphnum == 0) return; /* If --only-keep-debug is specified, discard all program headers. */ if (ecp->strip == STRIP_NONDEBUG) { ecp->ophnum = 0; return; } for (i = 0; (size_t)i < iphnum; i++) { if (gelf_getphdr(ecp->ein, i, &iphdr) != &iphdr) errx(EXIT_FAILURE, "gelf_getphdr failed: %s", elf_errmsg(-1)); if ((seg = calloc(1, sizeof(*seg))) == NULL) err(EXIT_FAILURE, "calloc failed"); seg->addr = iphdr.p_vaddr; seg->off = iphdr.p_offset; seg->fsz = iphdr.p_filesz; seg->msz = iphdr.p_memsz; seg->type = iphdr.p_type; STAILQ_INSERT_TAIL(&ecp->v_seg, seg, seg_list); } } void copy_phdr(struct elfcopy *ecp) { struct segment *seg; struct section *s; GElf_Phdr iphdr, ophdr; int i; STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) { if (seg->type == PT_PHDR) { if (!TAILQ_EMPTY(&ecp->v_sec)) { s = TAILQ_FIRST(&ecp->v_sec); if (s->pseudo) seg->addr = s->lma + gelf_fsize(ecp->eout, ELF_T_EHDR, 1, EV_CURRENT); } seg->fsz = seg->msz = gelf_fsize(ecp->eout, ELF_T_PHDR, ecp->ophnum, EV_CURRENT); continue; } seg->fsz = seg->msz = 0; for (i = 0; i < seg->nsec; i++) { s = seg->v_sec[i]; seg->msz = s->off + s->sz - seg->off; if (s->type != SHT_NOBITS) seg->fsz = seg->msz; } } /* * Allocate space for program headers, note that libelf keep * track of the number in internal variable, and a call to * elf_update is needed to update e_phnum of ehdr. */ if (gelf_newphdr(ecp->eout, ecp->ophnum) == NULL) errx(EXIT_FAILURE, "gelf_newphdr() failed: %s", elf_errmsg(-1)); /* * This elf_update() call is to update the e_phnum field in * ehdr. It's necessary because later we will call gelf_getphdr(), * which does sanity check by comparing ndx argument with e_phnum. */ if (elf_update(ecp->eout, ELF_C_NULL) < 0) errx(EXIT_FAILURE, "elf_update() failed: %s", elf_errmsg(-1)); /* * iphnum == ophnum, since we don't remove program headers even if * they no longer contain sections. */ i = 0; STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) { if (i >= ecp->iphnum) break; if (gelf_getphdr(ecp->ein, i, &iphdr) != &iphdr) errx(EXIT_FAILURE, "gelf_getphdr failed: %s", elf_errmsg(-1)); if (gelf_getphdr(ecp->eout, i, &ophdr) != &ophdr) errx(EXIT_FAILURE, "gelf_getphdr failed: %s", elf_errmsg(-1)); ophdr.p_type = iphdr.p_type; ophdr.p_vaddr = seg->addr; ophdr.p_paddr = seg->addr; ophdr.p_flags = iphdr.p_flags; ophdr.p_align = iphdr.p_align; ophdr.p_offset = seg->off; ophdr.p_filesz = seg->fsz; ophdr.p_memsz = seg->msz; if (!gelf_update_phdr(ecp->eout, i, &ophdr)) err(EXIT_FAILURE, "gelf_update_phdr failed :%s", elf_errmsg(-1)); i++; } } Index: head/contrib/elftoolchain/libelftc/libelftc_dem_gnu3.c =================================================================== --- head/contrib/elftoolchain/libelftc/libelftc_dem_gnu3.c (revision 275368) +++ head/contrib/elftoolchain/libelftc/libelftc_dem_gnu3.c (revision 275369) @@ -1,3238 +1,3238 @@ /*- * Copyright (c) 2007 Hyogeol Lee * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include "_libelftc.h" ELFTC_VCSID("$Id: libelftc_dem_gnu3.c 2179 2011-11-18 03:05:47Z jkoshy $"); /** * @file cpp_demangle.c * @brief Decode IA-64 C++ ABI style implementation. * * IA-64 standard ABI(Itanium C++ ABI) references. * * http://www.codesourcery.com/cxx-abi/abi.html#mangling \n * http://www.codesourcery.com/cxx-abi/abi-mangling.html */ enum type_qualifier { TYPE_PTR, TYPE_REF, TYPE_CMX, TYPE_IMG, TYPE_EXT, TYPE_RST, TYPE_VAT, TYPE_CST }; struct vector_type_qualifier { size_t size, capacity; enum type_qualifier *q_container; struct vector_str ext_name; }; enum read_cmd { READ_FAIL, READ_NEST, READ_TMPL, READ_EXPR, READ_EXPL, READ_LOCAL, READ_TYPE, READ_FUNC, READ_PTRMEM }; struct vector_read_cmd { size_t size, capacity; enum read_cmd *r_container; }; struct cpp_demangle_data { struct vector_str output; /* output string vector */ struct vector_str output_tmp; struct vector_str subst; /* substitution string vector */ struct vector_str tmpl; struct vector_str class_type; struct vector_read_cmd cmd; bool paren; /* parenthesis opened */ bool pfirst; /* first element of parameter */ bool mem_rst; /* restrict member function */ bool mem_vat; /* volatile member function */ bool mem_cst; /* const member function */ int func_type; const char *cur; /* current mangled name ptr */ const char *last_sname; /* last source name */ }; #define CPP_DEMANGLE_TRY_LIMIT 128 #define FLOAT_SPRINTF_TRY_LIMIT 5 #define FLOAT_QUADRUPLE_BYTES 16 #define FLOAT_EXTENED_BYTES 10 #define SIMPLE_HASH(x,y) (64 * x + y) static void cpp_demangle_data_dest(struct cpp_demangle_data *); static int cpp_demangle_data_init(struct cpp_demangle_data *, const char *); static int cpp_demangle_get_subst(struct cpp_demangle_data *, size_t); static int cpp_demangle_get_tmpl_param(struct cpp_demangle_data *, size_t); static int cpp_demangle_push_fp(struct cpp_demangle_data *, char *(*)(const char *, size_t)); static int cpp_demangle_push_str(struct cpp_demangle_data *, const char *, size_t); static int cpp_demangle_push_subst(struct cpp_demangle_data *, const char *, size_t); static int cpp_demangle_push_subst_v(struct cpp_demangle_data *, struct vector_str *); static int cpp_demangle_push_type_qualifier(struct cpp_demangle_data *, struct vector_type_qualifier *, const char *); static int cpp_demangle_read_array(struct cpp_demangle_data *); static int cpp_demangle_read_encoding(struct cpp_demangle_data *); static int cpp_demangle_read_expr_primary(struct cpp_demangle_data *); static int cpp_demangle_read_expression(struct cpp_demangle_data *); static int cpp_demangle_read_expression_binary(struct cpp_demangle_data *, const char *, size_t); static int cpp_demangle_read_expression_unary(struct cpp_demangle_data *, const char *, size_t); static int cpp_demangle_read_expression_trinary(struct cpp_demangle_data *, const char *, size_t, const char *, size_t); static int cpp_demangle_read_function(struct cpp_demangle_data *, int *, struct vector_type_qualifier *); static int cpp_demangle_local_source_name(struct cpp_demangle_data *ddata); static int cpp_demangle_read_local_name(struct cpp_demangle_data *); static int cpp_demangle_read_name(struct cpp_demangle_data *); static int cpp_demangle_read_nested_name(struct cpp_demangle_data *); static int cpp_demangle_read_number(struct cpp_demangle_data *, long *); static int cpp_demangle_read_nv_offset(struct cpp_demangle_data *); static int cpp_demangle_read_offset(struct cpp_demangle_data *); static int cpp_demangle_read_offset_number(struct cpp_demangle_data *); static int cpp_demangle_read_pointer_to_member(struct cpp_demangle_data *); static int cpp_demangle_read_sname(struct cpp_demangle_data *); static int cpp_demangle_read_subst(struct cpp_demangle_data *); static int cpp_demangle_read_subst_std(struct cpp_demangle_data *); static int cpp_demangle_read_subst_stdtmpl(struct cpp_demangle_data *, const char *, size_t); static int cpp_demangle_read_tmpl_arg(struct cpp_demangle_data *); static int cpp_demangle_read_tmpl_args(struct cpp_demangle_data *); static int cpp_demangle_read_tmpl_param(struct cpp_demangle_data *); static int cpp_demangle_read_type(struct cpp_demangle_data *, int); static int cpp_demangle_read_uqname(struct cpp_demangle_data *); static int cpp_demangle_read_v_offset(struct cpp_demangle_data *); static char *decode_fp_to_double(const char *, size_t); static char *decode_fp_to_float(const char *, size_t); static char *decode_fp_to_float128(const char *, size_t); static char *decode_fp_to_float80(const char *, size_t); static char *decode_fp_to_long_double(const char *, size_t); static int hex_to_dec(char); static void vector_read_cmd_dest(struct vector_read_cmd *); static int vector_read_cmd_find(struct vector_read_cmd *, enum read_cmd); static int vector_read_cmd_init(struct vector_read_cmd *); static int vector_read_cmd_pop(struct vector_read_cmd *); static int vector_read_cmd_push(struct vector_read_cmd *, enum read_cmd); static void vector_type_qualifier_dest(struct vector_type_qualifier *); static int vector_type_qualifier_init(struct vector_type_qualifier *); static int vector_type_qualifier_push(struct vector_type_qualifier *, enum type_qualifier); -int cpp_demangle_gnu3_push_head; +static int cpp_demangle_gnu3_push_head; /** * @brief Decode the input string by IA-64 C++ ABI style. * * GNU GCC v3 use IA-64 standard ABI. * @return New allocated demangled string or NULL if failed. * @todo 1. Testing and more test case. 2. Code cleaning. */ char * cpp_demangle_gnu3(const char *org) { struct cpp_demangle_data ddata; ssize_t org_len; unsigned int limit; char *rtn; if (org == NULL || (org_len = strlen(org)) < 2) return (NULL); if (org_len > 11 && !strncmp(org, "_GLOBAL__I_", 11)) { if ((rtn = malloc(org_len + 19)) == NULL) return (NULL); snprintf(rtn, org_len + 19, "global constructors keyed to %s", org + 11); return (rtn); } if (org[0] != '_' || org[1] != 'Z') return (NULL); if (!cpp_demangle_data_init(&ddata, org + 2)) return (NULL); cpp_demangle_gnu3_push_head = 0; rtn = NULL; if (!cpp_demangle_read_encoding(&ddata)) goto clean; limit = 0; while (*ddata.cur != '\0') { /* * Breaking at some gcc info at tail. e.g) @@GLIBCXX_3.4 */ if (*ddata.cur == '@' && *(ddata.cur + 1) == '@') break; if (!cpp_demangle_read_type(&ddata, 1)) goto clean; if (limit++ > CPP_DEMANGLE_TRY_LIMIT) goto clean; } if (ddata.output.size == 0) goto clean; if (ddata.paren && !vector_str_push(&ddata.output, ")", 1)) goto clean; if (ddata.mem_vat && !vector_str_push(&ddata.output, " volatile", 9)) goto clean; if (ddata.mem_cst && !vector_str_push(&ddata.output, " const", 6)) goto clean; if (ddata.mem_rst && !vector_str_push(&ddata.output, " restrict", 9)) goto clean; rtn = vector_str_get_flat(&ddata.output, (size_t *) NULL); clean: cpp_demangle_data_dest(&ddata); return (rtn); } static void cpp_demangle_data_dest(struct cpp_demangle_data *d) { if (d == NULL) return; vector_read_cmd_dest(&d->cmd); vector_str_dest(&d->class_type); vector_str_dest(&d->tmpl); vector_str_dest(&d->subst); vector_str_dest(&d->output_tmp); vector_str_dest(&d->output); } static int cpp_demangle_data_init(struct cpp_demangle_data *d, const char *cur) { if (d == NULL || cur == NULL) return (0); if (!vector_str_init(&d->output)) return (0); if (!vector_str_init(&d->output_tmp)) goto clean1; if (!vector_str_init(&d->subst)) goto clean2; if (!vector_str_init(&d->tmpl)) goto clean3; if (!vector_str_init(&d->class_type)) goto clean4; if (!vector_read_cmd_init(&d->cmd)) goto clean5; assert(d->output.container != NULL); assert(d->output_tmp.container != NULL); assert(d->subst.container != NULL); assert(d->tmpl.container != NULL); assert(d->class_type.container != NULL); d->paren = false; d->pfirst = false; d->mem_rst = false; d->mem_vat = false; d->mem_cst = false; d->func_type = 0; d->cur = cur; d->last_sname = NULL; return (1); clean5: vector_str_dest(&d->class_type); clean4: vector_str_dest(&d->tmpl); clean3: vector_str_dest(&d->subst); clean2: vector_str_dest(&d->output_tmp); clean1: vector_str_dest(&d->output); return (0); } static int cpp_demangle_push_fp(struct cpp_demangle_data *ddata, char *(*decoder)(const char *, size_t)) { size_t len; int rtn; const char *fp; char *f; if (ddata == NULL || decoder == NULL) return (0); fp = ddata->cur; while (*ddata->cur != 'E') ++ddata->cur; ++ddata->cur; if ((f = decoder(fp, ddata->cur - fp)) == NULL) return (0); rtn = 0; if ((len = strlen(f)) > 0) rtn = cpp_demangle_push_str(ddata, f, len); free(f); return (rtn); } static int cpp_demangle_push_str(struct cpp_demangle_data *ddata, const char *str, size_t len) { if (ddata == NULL || str == NULL || len == 0) return (0); if (cpp_demangle_gnu3_push_head > 0) return (vector_str_push(&ddata->output_tmp, str, len)); return (vector_str_push(&ddata->output, str, len)); } static int cpp_demangle_push_subst(struct cpp_demangle_data *ddata, const char *str, size_t len) { if (ddata == NULL || str == NULL || len == 0) return (0); if (!vector_str_find(&ddata->subst, str, len)) return (vector_str_push(&ddata->subst, str, len)); return (1); } static int cpp_demangle_push_subst_v(struct cpp_demangle_data *ddata, struct vector_str *v) { size_t str_len; int rtn; char *str; if (ddata == NULL || v == NULL) return (0); if ((str = vector_str_get_flat(v, &str_len)) == NULL) return (0); rtn = cpp_demangle_push_subst(ddata, str, str_len); free(str); return (rtn); } static int cpp_demangle_push_type_qualifier(struct cpp_demangle_data *ddata, struct vector_type_qualifier *v, const char *type_str) { struct vector_str subst_v; size_t idx, e_idx, e_len; int rtn; char *buf; if (ddata == NULL || v == NULL) return (0); if ((idx = v->size) == 0) return (1); rtn = 0; if (type_str != NULL) { if (!vector_str_init(&subst_v)) return (0); if (!vector_str_push(&subst_v, type_str, strlen(type_str))) goto clean; } e_idx = 0; while (idx > 0) { switch (v->q_container[idx - 1]) { case TYPE_PTR: if (!cpp_demangle_push_str(ddata, "*", 1)) goto clean; if (type_str != NULL) { if (!vector_str_push(&subst_v, "*", 1)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &subst_v)) goto clean; } break; case TYPE_REF: if (!cpp_demangle_push_str(ddata, "&", 1)) goto clean; if (type_str != NULL) { if (!vector_str_push(&subst_v, "&", 1)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &subst_v)) goto clean; } break; case TYPE_CMX: if (!cpp_demangle_push_str(ddata, " complex", 8)) goto clean; if (type_str != NULL) { if (!vector_str_push(&subst_v, " complex", 8)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &subst_v)) goto clean; } break; case TYPE_IMG: if (!cpp_demangle_push_str(ddata, " imaginary", 10)) goto clean; if (type_str != NULL) { if (!vector_str_push(&subst_v, " imaginary", 10)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &subst_v)) goto clean; } break; case TYPE_EXT: if (e_idx > v->ext_name.size - 1) goto clean; if ((e_len = strlen(v->ext_name.container[e_idx])) == 0) goto clean; if ((buf = malloc(sizeof(char) * (e_len + 1))) == NULL) goto clean; memcpy(buf, " ", 1); memcpy(buf + 1, v->ext_name.container[e_idx], e_len); if (!cpp_demangle_push_str(ddata, buf, e_len + 1)) { free(buf); goto clean; } if (type_str != NULL) { if (!vector_str_push(&subst_v, buf, e_len + 1)) { free(buf); goto clean; } if (!cpp_demangle_push_subst_v(ddata, &subst_v)) { free(buf); goto clean; } } free(buf); ++e_idx; break; case TYPE_RST: if (!cpp_demangle_push_str(ddata, " restrict", 9)) goto clean; if (type_str != NULL) { if (!vector_str_push(&subst_v, " restrict", 9)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &subst_v)) goto clean; } break; case TYPE_VAT: if (!cpp_demangle_push_str(ddata, " volatile", 9)) goto clean; if (type_str != NULL) { if (!vector_str_push(&subst_v, " volatile", 9)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &subst_v)) goto clean; } break; case TYPE_CST: if (!cpp_demangle_push_str(ddata, " const", 6)) goto clean; if (type_str != NULL) { if (!vector_str_push(&subst_v, " const", 6)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &subst_v)) goto clean; } break; }; --idx; } rtn = 1; clean: if (type_str != NULL) vector_str_dest(&subst_v); return (rtn); } static int cpp_demangle_get_subst(struct cpp_demangle_data *ddata, size_t idx) { size_t len; if (ddata == NULL || ddata->subst.size <= idx) return (0); if ((len = strlen(ddata->subst.container[idx])) == 0) return (0); if (!cpp_demangle_push_str(ddata, ddata->subst.container[idx], len)) return (0); /* skip '_' */ ++ddata->cur; return (1); } static int cpp_demangle_get_tmpl_param(struct cpp_demangle_data *ddata, size_t idx) { size_t len; if (ddata == NULL || ddata->tmpl.size <= idx) return (0); if ((len = strlen(ddata->tmpl.container[idx])) == 0) return (0); if (!cpp_demangle_push_str(ddata, ddata->tmpl.container[idx], len)) return (0); ++ddata->cur; return (1); } static int cpp_demangle_read_array(struct cpp_demangle_data *ddata) { size_t i, num_len, exp_len, p_idx, idx; const char *num; char *exp; if (ddata == NULL || *(++ddata->cur) == '\0') return (0); if (*ddata->cur == '_') { if (*(++ddata->cur) == '\0') return (0); if (!cpp_demangle_read_type(ddata, 0)) return (0); if (!cpp_demangle_push_str(ddata, "[]", 2)) return (0); } else { if (ELFTC_ISDIGIT(*ddata->cur) != 0) { num = ddata->cur; while (ELFTC_ISDIGIT(*ddata->cur) != 0) ++ddata->cur; if (*ddata->cur != '_') return (0); num_len = ddata->cur - num; assert(num_len > 0); if (*(++ddata->cur) == '\0') return (0); if (!cpp_demangle_read_type(ddata, 0)) return (0); if (!cpp_demangle_push_str(ddata, "[", 1)) return (0); if (!cpp_demangle_push_str(ddata, num, num_len)) return (0); if (!cpp_demangle_push_str(ddata, "]", 1)) return (0); } else { p_idx = ddata->output.size; if (!cpp_demangle_read_expression(ddata)) return (0); if ((exp = vector_str_substr(&ddata->output, p_idx, ddata->output.size - 1, &exp_len)) == NULL) return (0); idx = ddata->output.size; for (i = p_idx; i < idx; ++i) if (!vector_str_pop(&ddata->output)) { free(exp); return (0); } if (*ddata->cur != '_') { free(exp); return (0); } ++ddata->cur; if (*ddata->cur == '\0') { free(exp); return (0); } if (!cpp_demangle_read_type(ddata, 0)) { free(exp); return (0); } if (!cpp_demangle_push_str(ddata, "[", 1)) { free(exp); return (0); } if (!cpp_demangle_push_str(ddata, exp, exp_len)) { free(exp); return (0); } if (!cpp_demangle_push_str(ddata, "]", 1)) { free(exp); return (0); } free(exp); } } return (1); } static int cpp_demangle_read_expr_primary(struct cpp_demangle_data *ddata) { const char *num; if (ddata == NULL || *(++ddata->cur) == '\0') return (0); if (*ddata->cur == '_' && *(ddata->cur + 1) == 'Z') { ddata->cur += 2; if (*ddata->cur == '\0') return (0); if (!cpp_demangle_read_encoding(ddata)) return (0); ++ddata->cur; return (1); } switch (*ddata->cur) { case 'b': switch (*(++ddata->cur)) { case '0': return (cpp_demangle_push_str(ddata, "false", 5)); case '1': return (cpp_demangle_push_str(ddata, "true", 4)); default: return (0); }; case 'd': ++ddata->cur; return (cpp_demangle_push_fp(ddata, decode_fp_to_double)); case 'e': ++ddata->cur; if (sizeof(long double) == 10) return (cpp_demangle_push_fp(ddata, decode_fp_to_double)); return (cpp_demangle_push_fp(ddata, decode_fp_to_float80)); case 'f': ++ddata->cur; return (cpp_demangle_push_fp(ddata, decode_fp_to_float)); case 'g': ++ddata->cur; if (sizeof(long double) == 16) return (cpp_demangle_push_fp(ddata, decode_fp_to_double)); return (cpp_demangle_push_fp(ddata, decode_fp_to_float128)); case 'i': case 'j': case 'l': case 'm': case 'n': case 's': case 't': case 'x': case 'y': if (*(++ddata->cur) == 'n') { if (!cpp_demangle_push_str(ddata, "-", 1)) return (0); ++ddata->cur; } num = ddata->cur; while (*ddata->cur != 'E') { if (!ELFTC_ISDIGIT(*ddata->cur)) return (0); ++ddata->cur; } ++ddata->cur; return (cpp_demangle_push_str(ddata, num, ddata->cur - num)); default: return (0); }; } static int cpp_demangle_read_expression(struct cpp_demangle_data *ddata) { if (ddata == NULL || *ddata->cur == '\0') return (0); switch (SIMPLE_HASH(*ddata->cur, *(ddata->cur + 1))) { case SIMPLE_HASH('s', 't'): ddata->cur += 2; return (cpp_demangle_read_type(ddata, 0)); case SIMPLE_HASH('s', 'r'): ddata->cur += 2; if (!cpp_demangle_read_type(ddata, 0)) return (0); if (!cpp_demangle_read_uqname(ddata)) return (0); if (*ddata->cur == 'I') return (cpp_demangle_read_tmpl_args(ddata)); return (1); case SIMPLE_HASH('a', 'a'): /* operator && */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "&&", 2)); case SIMPLE_HASH('a', 'd'): /* operator & (unary) */ ddata->cur += 2; return (cpp_demangle_read_expression_unary(ddata, "&", 1)); case SIMPLE_HASH('a', 'n'): /* operator & */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "&", 1)); case SIMPLE_HASH('a', 'N'): /* operator &= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "&=", 2)); case SIMPLE_HASH('a', 'S'): /* operator = */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "=", 1)); case SIMPLE_HASH('c', 'l'): /* operator () */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "()", 2)); case SIMPLE_HASH('c', 'm'): /* operator , */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, ",", 1)); case SIMPLE_HASH('c', 'o'): /* operator ~ */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "~", 1)); case SIMPLE_HASH('c', 'v'): /* operator (cast) */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "(cast)", 6)); case SIMPLE_HASH('d', 'a'): /* operator delete [] */ ddata->cur += 2; return (cpp_demangle_read_expression_unary(ddata, "delete []", 9)); case SIMPLE_HASH('d', 'e'): /* operator * (unary) */ ddata->cur += 2; return (cpp_demangle_read_expression_unary(ddata, "*", 1)); case SIMPLE_HASH('d', 'l'): /* operator delete */ ddata->cur += 2; return (cpp_demangle_read_expression_unary(ddata, "delete", 6)); case SIMPLE_HASH('d', 'v'): /* operator / */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "/", 1)); case SIMPLE_HASH('d', 'V'): /* operator /= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "/=", 2)); case SIMPLE_HASH('e', 'o'): /* operator ^ */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "^", 1)); case SIMPLE_HASH('e', 'O'): /* operator ^= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "^=", 2)); case SIMPLE_HASH('e', 'q'): /* operator == */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "==", 2)); case SIMPLE_HASH('g', 'e'): /* operator >= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, ">=", 2)); case SIMPLE_HASH('g', 't'): /* operator > */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, ">", 1)); case SIMPLE_HASH('i', 'x'): /* operator [] */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "[]", 2)); case SIMPLE_HASH('l', 'e'): /* operator <= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "<=", 2)); case SIMPLE_HASH('l', 's'): /* operator << */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "<<", 2)); case SIMPLE_HASH('l', 'S'): /* operator <<= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "<<=", 3)); case SIMPLE_HASH('l', 't'): /* operator < */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "<", 1)); case SIMPLE_HASH('m', 'i'): /* operator - */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "-", 1)); case SIMPLE_HASH('m', 'I'): /* operator -= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "-=", 2)); case SIMPLE_HASH('m', 'l'): /* operator * */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "*", 1)); case SIMPLE_HASH('m', 'L'): /* operator *= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "*=", 2)); case SIMPLE_HASH('m', 'm'): /* operator -- */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "--", 2)); case SIMPLE_HASH('n', 'a'): /* operator new[] */ ddata->cur += 2; return (cpp_demangle_read_expression_unary(ddata, "new []", 6)); case SIMPLE_HASH('n', 'e'): /* operator != */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "!=", 2)); case SIMPLE_HASH('n', 'g'): /* operator - (unary) */ ddata->cur += 2; return (cpp_demangle_read_expression_unary(ddata, "-", 1)); case SIMPLE_HASH('n', 't'): /* operator ! */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "!", 1)); case SIMPLE_HASH('n', 'w'): /* operator new */ ddata->cur += 2; return (cpp_demangle_read_expression_unary(ddata, "new", 3)); case SIMPLE_HASH('o', 'o'): /* operator || */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "||", 2)); case SIMPLE_HASH('o', 'r'): /* operator | */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "|", 1)); case SIMPLE_HASH('o', 'R'): /* operator |= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "|=", 2)); case SIMPLE_HASH('p', 'l'): /* operator + */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "+", 1)); case SIMPLE_HASH('p', 'L'): /* operator += */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "+=", 2)); case SIMPLE_HASH('p', 'm'): /* operator ->* */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "->*", 3)); case SIMPLE_HASH('p', 'p'): /* operator ++ */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "++", 2)); case SIMPLE_HASH('p', 's'): /* operator + (unary) */ ddata->cur += 2; return (cpp_demangle_read_expression_unary(ddata, "+", 1)); case SIMPLE_HASH('p', 't'): /* operator -> */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "->", 2)); case SIMPLE_HASH('q', 'u'): /* operator ? */ ddata->cur += 2; return (cpp_demangle_read_expression_trinary(ddata, "?", 1, ":", 1)); case SIMPLE_HASH('r', 'm'): /* operator % */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "%", 1)); case SIMPLE_HASH('r', 'M'): /* operator %= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, "%=", 2)); case SIMPLE_HASH('r', 's'): /* operator >> */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, ">>", 2)); case SIMPLE_HASH('r', 'S'): /* operator >>= */ ddata->cur += 2; return (cpp_demangle_read_expression_binary(ddata, ">>=", 3)); case SIMPLE_HASH('r', 'z'): /* operator sizeof */ ddata->cur += 2; return (cpp_demangle_read_expression_unary(ddata, "sizeof", 6)); case SIMPLE_HASH('s', 'v'): /* operator sizeof */ ddata->cur += 2; return (cpp_demangle_read_expression_unary(ddata, "sizeof", 6)); }; switch (*ddata->cur) { case 'L': return (cpp_demangle_read_expr_primary(ddata)); case 'T': return (cpp_demangle_read_tmpl_param(ddata)); }; return (0); } static int cpp_demangle_read_expression_binary(struct cpp_demangle_data *ddata, const char *name, size_t len) { if (ddata == NULL || name == NULL || len == 0) return (0); if (!cpp_demangle_read_expression(ddata)) return (0); if (!cpp_demangle_push_str(ddata, name, len)) return (0); return (cpp_demangle_read_expression(ddata)); } static int cpp_demangle_read_expression_unary(struct cpp_demangle_data *ddata, const char *name, size_t len) { if (ddata == NULL || name == NULL || len == 0) return (0); if (!cpp_demangle_read_expression(ddata)) return (0); return (cpp_demangle_push_str(ddata, name, len)); } static int cpp_demangle_read_expression_trinary(struct cpp_demangle_data *ddata, const char *name1, size_t len1, const char *name2, size_t len2) { if (ddata == NULL || name1 == NULL || len1 == 0 || name2 == NULL || len2 == 0) return (0); if (!cpp_demangle_read_expression(ddata)) return (0); if (!cpp_demangle_push_str(ddata, name1, len1)) return (0); if (!cpp_demangle_read_expression(ddata)) return (0); if (!cpp_demangle_push_str(ddata, name2, len2)) return (0); return (cpp_demangle_read_expression(ddata)); } static int cpp_demangle_read_function(struct cpp_demangle_data *ddata, int *ext_c, struct vector_type_qualifier *v) { size_t class_type_size, class_type_len, limit; const char *class_type; if (ddata == NULL || *ddata->cur != 'F' || v == NULL) return (0); ++ddata->cur; if (*ddata->cur == 'Y') { if (ext_c != NULL) *ext_c = 1; ++ddata->cur; } if (!cpp_demangle_read_type(ddata, 0)) return (0); if (*ddata->cur != 'E') { if (!cpp_demangle_push_str(ddata, "(", 1)) return (0); if (vector_read_cmd_find(&ddata->cmd, READ_PTRMEM)) { if ((class_type_size = ddata->class_type.size) == 0) return (0); class_type = ddata->class_type.container[class_type_size - 1]; if (class_type == NULL) return (0); if ((class_type_len = strlen(class_type)) == 0) return (0); if (!cpp_demangle_push_str(ddata, class_type, class_type_len)) return (0); if (!cpp_demangle_push_str(ddata, "::*", 3)) return (0); ++ddata->func_type; } else { if (!cpp_demangle_push_type_qualifier(ddata, v, (const char *) NULL)) return (0); vector_type_qualifier_dest(v); if (!vector_type_qualifier_init(v)) return (0); } if (!cpp_demangle_push_str(ddata, ")(", 2)) return (0); limit = 0; for (;;) { if (!cpp_demangle_read_type(ddata, 0)) return (0); if (*ddata->cur == 'E') break; if (limit++ > CPP_DEMANGLE_TRY_LIMIT) return (0); } if (vector_read_cmd_find(&ddata->cmd, READ_PTRMEM) == 1) { if (!cpp_demangle_push_type_qualifier(ddata, v, (const char *) NULL)) return (0); vector_type_qualifier_dest(v); if (!vector_type_qualifier_init(v)) return (0); } if (!cpp_demangle_push_str(ddata, ")", 1)) return (0); } ++ddata->cur; return (1); } /* read encoding, encoding are function name, data name, special-name */ static int cpp_demangle_read_encoding(struct cpp_demangle_data *ddata) { if (ddata == NULL || *ddata->cur == '\0') return (0); /* special name */ switch (SIMPLE_HASH(*ddata->cur, *(ddata->cur + 1))) { case SIMPLE_HASH('G', 'V'): /* sentry object for 1 time init */ if (!cpp_demangle_push_str(ddata, "guard variable for ", 20)) return (0); ddata->cur += 2; break; case SIMPLE_HASH('T', 'c'): /* virtual function covariant override thunk */ if (!cpp_demangle_push_str(ddata, "virtual function covariant override ", 36)) return (0); ddata->cur += 2; if (*ddata->cur == '\0') return (0); if (!cpp_demangle_read_offset(ddata)) return (0); if (!cpp_demangle_read_offset(ddata)) return (0); return (cpp_demangle_read_encoding(ddata)); case SIMPLE_HASH('T', 'D'): /* typeinfo common proxy */ break; case SIMPLE_HASH('T', 'h'): /* virtual function non-virtual override thunk */ if (cpp_demangle_push_str(ddata, "virtual function non-virtual override ", 38) == 0) return (0); ddata->cur += 2; if (*ddata->cur == '\0') return (0); if (!cpp_demangle_read_nv_offset(ddata)) return (0); return (cpp_demangle_read_encoding(ddata)); case SIMPLE_HASH('T', 'I'): /* typeinfo structure */ /* FALLTHROUGH */ case SIMPLE_HASH('T', 'S'): /* RTTI name (NTBS) */ if (!cpp_demangle_push_str(ddata, "typeinfo for ", 14)) return (0); ddata->cur += 2; if (*ddata->cur == '\0') return (0); return (cpp_demangle_read_type(ddata, 1)); case SIMPLE_HASH('T', 'T'): /* VTT table */ if (!cpp_demangle_push_str(ddata, "VTT for ", 8)) return (0); ddata->cur += 2; return (cpp_demangle_read_type(ddata, 1)); case SIMPLE_HASH('T', 'v'): /* virtual function virtual override thunk */ if (!cpp_demangle_push_str(ddata, "virtual function virtual override ", 34)) return (0); ddata->cur += 2; if (*ddata->cur == '\0') return (0); if (!cpp_demangle_read_v_offset(ddata)) return (0); return (cpp_demangle_read_encoding(ddata)); case SIMPLE_HASH('T', 'V'): /* virtual table */ if (!cpp_demangle_push_str(ddata, "vtable for ", 12)) return (0); ddata->cur += 2; if (*ddata->cur == '\0') return (0); return (cpp_demangle_read_type(ddata, 1)); }; return (cpp_demangle_read_name(ddata)); } static int cpp_demangle_read_local_name(struct cpp_demangle_data *ddata) { size_t limit; if (ddata == NULL) return (0); if (*(++ddata->cur) == '\0') return (0); if (!cpp_demangle_read_encoding(ddata)) return (0); limit = 0; for (;;) { if (!cpp_demangle_read_type(ddata, 1)) return (0); if (*ddata->cur == 'E') break; if (limit++ > CPP_DEMANGLE_TRY_LIMIT) return (0); } if (*(++ddata->cur) == '\0') return (0); if (ddata->paren == true) { if (!cpp_demangle_push_str(ddata, ")", 1)) return (0); ddata->paren = false; } if (*ddata->cur == 's') ++ddata->cur; else { if (!cpp_demangle_push_str(ddata, "::", 2)) return (0); if (!cpp_demangle_read_name(ddata)) return (0); } if (*ddata->cur == '_') { ++ddata->cur; while (ELFTC_ISDIGIT(*ddata->cur) != 0) ++ddata->cur; } return (1); } static int cpp_demangle_read_name(struct cpp_demangle_data *ddata) { struct vector_str *output, v; size_t p_idx, subst_str_len; int rtn; char *subst_str; if (ddata == NULL || *ddata->cur == '\0') return (0); output = cpp_demangle_gnu3_push_head > 0 ? &ddata->output_tmp : &ddata->output; subst_str = NULL; switch (*ddata->cur) { case 'S': return (cpp_demangle_read_subst(ddata)); case 'N': return (cpp_demangle_read_nested_name(ddata)); case 'Z': return (cpp_demangle_read_local_name(ddata)); }; if (!vector_str_init(&v)) return (0); p_idx = output->size; rtn = 0; if (!cpp_demangle_read_uqname(ddata)) goto clean; if ((subst_str = vector_str_substr(output, p_idx, output->size - 1, &subst_str_len)) == NULL) goto clean; if (subst_str_len > 8 && strstr(subst_str, "operator") != NULL) { rtn = 1; goto clean; } if (!vector_str_push(&v, subst_str, subst_str_len)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &v)) goto clean; if (*ddata->cur == 'I') { p_idx = output->size; if (!cpp_demangle_read_tmpl_args(ddata)) goto clean; free(subst_str); if ((subst_str = vector_str_substr(output, p_idx, output->size - 1, &subst_str_len)) == NULL) goto clean; if (!vector_str_push(&v, subst_str, subst_str_len)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &v)) goto clean; } rtn = 1; clean: free(subst_str); vector_str_dest(&v); return (rtn); } static int cpp_demangle_read_nested_name(struct cpp_demangle_data *ddata) { struct vector_str *output, v; size_t limit, p_idx, subst_str_len; int rtn; char *subst_str; if (ddata == NULL || *ddata->cur != 'N') return (0); if (*(++ddata->cur) == '\0') return (0); while (*ddata->cur == 'r' || *ddata->cur == 'V' || *ddata->cur == 'K') { switch (*ddata->cur) { case 'r': ddata->mem_rst = true; break; case 'V': ddata->mem_vat = true; break; case 'K': ddata->mem_cst = true; break; }; ++ddata->cur; } output = cpp_demangle_gnu3_push_head > 0 ? &ddata->output_tmp : &ddata->output; if (!vector_str_init(&v)) return (0); rtn = 0; limit = 0; for (;;) { p_idx = output->size; switch (*ddata->cur) { case 'I': if (!cpp_demangle_read_tmpl_args(ddata)) goto clean; break; case 'S': if (!cpp_demangle_read_subst(ddata)) goto clean; break; case 'T': if (!cpp_demangle_read_tmpl_param(ddata)) goto clean; break; default: if (!cpp_demangle_read_uqname(ddata)) goto clean; }; if ((subst_str = vector_str_substr(output, p_idx, output->size - 1, &subst_str_len)) == NULL) goto clean; if (!vector_str_push(&v, subst_str, subst_str_len)) { free(subst_str); goto clean; } free(subst_str); if (!cpp_demangle_push_subst_v(ddata, &v)) goto clean; if (*ddata->cur == 'E') break; else if (*ddata->cur != 'I' && *ddata->cur != 'C' && *ddata->cur != 'D') { if (!cpp_demangle_push_str(ddata, "::", 2)) goto clean; if (!vector_str_push(&v, "::", 2)) goto clean; } if (limit++ > CPP_DEMANGLE_TRY_LIMIT) goto clean; } ++ddata->cur; rtn = 1; clean: vector_str_dest(&v); return (rtn); } /* * read number * number ::= [n] */ static int cpp_demangle_read_number(struct cpp_demangle_data *ddata, long *rtn) { long len, negative_factor; if (ddata == NULL || rtn == NULL) return (0); negative_factor = 1; if (*ddata->cur == 'n') { negative_factor = -1; ++ddata->cur; } if (ELFTC_ISDIGIT(*ddata->cur) == 0) return (0); errno = 0; if ((len = strtol(ddata->cur, (char **) NULL, 10)) == 0 && errno != 0) return (0); while (ELFTC_ISDIGIT(*ddata->cur) != 0) ++ddata->cur; assert(len >= 0); assert(negative_factor == 1 || negative_factor == -1); *rtn = len * negative_factor; return (1); } static int cpp_demangle_read_nv_offset(struct cpp_demangle_data *ddata) { if (ddata == NULL) return (0); if (!cpp_demangle_push_str(ddata, "offset : ", 9)) return (0); return (cpp_demangle_read_offset_number(ddata)); } /* read offset, offset are nv-offset, v-offset */ static int cpp_demangle_read_offset(struct cpp_demangle_data *ddata) { if (ddata == NULL) return (0); if (*ddata->cur == 'h') { ++ddata->cur; return (cpp_demangle_read_nv_offset(ddata)); } else if (*ddata->cur == 'v') { ++ddata->cur; return (cpp_demangle_read_v_offset(ddata)); } return (0); } static int cpp_demangle_read_offset_number(struct cpp_demangle_data *ddata) { bool negative; const char *start; if (ddata == NULL || *ddata->cur == '\0') return (0); /* offset could be negative */ if (*ddata->cur == 'n') { negative = true; start = ddata->cur + 1; } else { negative = false; start = ddata->cur; } while (*ddata->cur != '_') ++ddata->cur; if (negative && !cpp_demangle_push_str(ddata, "-", 1)) return (0); assert(start != NULL); if (!cpp_demangle_push_str(ddata, start, ddata->cur - start)) return (0); if (!cpp_demangle_push_str(ddata, " ", 1)) return (0); ++ddata->cur; return (1); } static int cpp_demangle_read_pointer_to_member(struct cpp_demangle_data *ddata) { size_t class_type_len, i, idx, p_idx; int p_func_type, rtn; char *class_type; if (ddata == NULL || *ddata->cur != 'M' || *(++ddata->cur) == '\0') return (0); p_idx = ddata->output.size; if (!cpp_demangle_read_type(ddata, 0)) return (0); if ((class_type = vector_str_substr(&ddata->output, p_idx, ddata->output.size - 1, &class_type_len)) == NULL) return (0); rtn = 0; idx = ddata->output.size; for (i = p_idx; i < idx; ++i) if (!vector_str_pop(&ddata->output)) goto clean1; if (!vector_read_cmd_push(&ddata->cmd, READ_PTRMEM)) goto clean1; if (!vector_str_push(&ddata->class_type, class_type, class_type_len)) goto clean2; p_func_type = ddata->func_type; if (!cpp_demangle_read_type(ddata, 0)) goto clean3; if (p_func_type == ddata->func_type) { if (!cpp_demangle_push_str(ddata, " ", 1)) goto clean3; if (!cpp_demangle_push_str(ddata, class_type, class_type_len)) goto clean3; if (!cpp_demangle_push_str(ddata, "::*", 3)) goto clean3; } rtn = 1; clean3: if (!vector_str_pop(&ddata->class_type)) rtn = 0; clean2: if (!vector_read_cmd_pop(&ddata->cmd)) rtn = 0; clean1: free(class_type); return (rtn); } /* read source-name, source-name is */ static int cpp_demangle_read_sname(struct cpp_demangle_data *ddata) { long len; if (ddata == NULL || cpp_demangle_read_number(ddata, &len) == 0 || len <= 0 || cpp_demangle_push_str(ddata, ddata->cur, len) == 0) return (0); assert(ddata->output.size > 0); if (vector_read_cmd_find(&ddata->cmd, READ_TMPL) == 0) ddata->last_sname = ddata->output.container[ddata->output.size - 1]; ddata->cur += len; return (1); } static int cpp_demangle_read_subst(struct cpp_demangle_data *ddata) { long nth; if (ddata == NULL || *ddata->cur == '\0') return (0); /* abbreviations of the form Sx */ switch (SIMPLE_HASH(*ddata->cur, *(ddata->cur + 1))) { case SIMPLE_HASH('S', 'a'): /* std::allocator */ if (cpp_demangle_push_str(ddata, "std::allocator", 14) == 0) return (0); ddata->cur += 2; if (*ddata->cur == 'I') return (cpp_demangle_read_subst_stdtmpl(ddata, "std::allocator", 14)); return (1); case SIMPLE_HASH('S', 'b'): /* std::basic_string */ if (!cpp_demangle_push_str(ddata, "std::basic_string", 17)) return (0); ddata->cur += 2; if (*ddata->cur == 'I') return (cpp_demangle_read_subst_stdtmpl(ddata, "std::basic_string", 17)); return (1); case SIMPLE_HASH('S', 'd'): /* std::basic_iostream > */ if (!cpp_demangle_push_str(ddata, "std::iostream", 19)) return (0); ddata->last_sname = "iostream"; ddata->cur += 2; if (*ddata->cur == 'I') return (cpp_demangle_read_subst_stdtmpl(ddata, "std::iostream", 19)); return (1); case SIMPLE_HASH('S', 'i'): /* std::basic_istream > */ if (!cpp_demangle_push_str(ddata, "std::istream", 18)) return (0); ddata->last_sname = "istream"; ddata->cur += 2; if (*ddata->cur == 'I') return (cpp_demangle_read_subst_stdtmpl(ddata, "std::istream", 18)); return (1); case SIMPLE_HASH('S', 'o'): /* std::basic_ostream > */ if (!cpp_demangle_push_str(ddata, "std::ostream", 18)) return (0); ddata->last_sname = "istream"; ddata->cur += 2; if (*ddata->cur == 'I') return (cpp_demangle_read_subst_stdtmpl(ddata, "std::ostream", 18)); return (1); case SIMPLE_HASH('S', 's'): /* * std::basic_string, * std::allocator > * * a.k.a std::string */ if (!cpp_demangle_push_str(ddata, "std::string", 11)) return (0); ddata->last_sname = "string"; ddata->cur += 2; if (*ddata->cur == 'I') return (cpp_demangle_read_subst_stdtmpl(ddata, "std::string", 11)); return (1); case SIMPLE_HASH('S', 't'): /* std:: */ return (cpp_demangle_read_subst_std(ddata)); }; if (*(++ddata->cur) == '\0') return (0); /* substitution */ if (*ddata->cur == '_') return (cpp_demangle_get_subst(ddata, 0)); else { errno = 0; /* substitution number is base 36 */ if ((nth = strtol(ddata->cur, (char **) NULL, 36)) == 0 && errno != 0) return (0); /* first was '_', so increase one */ ++nth; while (*ddata->cur != '_') ++ddata->cur; assert(nth > 0); return (cpp_demangle_get_subst(ddata, nth)); } /* NOTREACHED */ return (0); } static int cpp_demangle_read_subst_std(struct cpp_demangle_data *ddata) { struct vector_str *output, v; size_t p_idx, subst_str_len; int rtn; char *subst_str; if (ddata == NULL) return (0); if (!vector_str_init(&v)) return (0); subst_str = NULL; rtn = 0; if (!cpp_demangle_push_str(ddata, "std::", 5)) goto clean; if (!vector_str_push(&v, "std::", 5)) goto clean; ddata->cur += 2; output = cpp_demangle_gnu3_push_head > 0 ? &ddata->output_tmp : &ddata->output; p_idx = output->size; if (!cpp_demangle_read_uqname(ddata)) goto clean; if ((subst_str = vector_str_substr(output, p_idx, output->size - 1, &subst_str_len)) == NULL) goto clean; if (!vector_str_push(&v, subst_str, subst_str_len)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &v)) goto clean; if (*ddata->cur == 'I') { p_idx = output->size; if (!cpp_demangle_read_tmpl_args(ddata)) goto clean; free(subst_str); if ((subst_str = vector_str_substr(output, p_idx, output->size - 1, &subst_str_len)) == NULL) goto clean; if (!vector_str_push(&v, subst_str, subst_str_len)) goto clean; if (!cpp_demangle_push_subst_v(ddata, &v)) goto clean; } rtn = 1; clean: free(subst_str); vector_str_dest(&v); return (rtn); } static int cpp_demangle_read_subst_stdtmpl(struct cpp_demangle_data *ddata, const char *str, size_t len) { struct vector_str *output; size_t p_idx, substr_len; int rtn; char *subst_str, *substr; if (ddata == NULL || str == NULL || len == 0) return (0); output = cpp_demangle_gnu3_push_head > 0 ? &ddata->output_tmp : &ddata->output; p_idx = output->size; substr = NULL; subst_str = NULL; if (!cpp_demangle_read_tmpl_args(ddata)) return (0); if ((substr = vector_str_substr(output, p_idx, output->size - 1, &substr_len)) == NULL) return (0); rtn = 0; if ((subst_str = malloc(sizeof(char) * (substr_len + len + 1))) == NULL) goto clean; memcpy(subst_str, str, len); memcpy(subst_str + len, substr, substr_len); subst_str[substr_len + len] = '\0'; if (!cpp_demangle_push_subst(ddata, subst_str, substr_len + len)) goto clean; rtn = 1; clean: free(subst_str); free(substr); return (rtn); } static int cpp_demangle_read_tmpl_arg(struct cpp_demangle_data *ddata) { if (ddata == NULL || *ddata->cur == '\0') return (0); switch (*ddata->cur) { case 'L': return (cpp_demangle_read_expr_primary(ddata)); case 'X': return (cpp_demangle_read_expression(ddata)); }; return (cpp_demangle_read_type(ddata, 0)); } static int cpp_demangle_read_tmpl_args(struct cpp_demangle_data *ddata) { struct vector_str *v; size_t arg_len, idx, limit, size; char *arg; if (ddata == NULL || *ddata->cur == '\0') return (0); ++ddata->cur; if (!vector_read_cmd_push(&ddata->cmd, READ_TMPL)) return (0); if (!cpp_demangle_push_str(ddata, "<", 1)) return (0); limit = 0; v = cpp_demangle_gnu3_push_head > 0 ? &ddata->output_tmp : &ddata->output; for (;;) { idx = v->size; if (!cpp_demangle_read_tmpl_arg(ddata)) return (0); if ((arg = vector_str_substr(v, idx, v->size - 1, &arg_len)) == NULL) return (0); if (!vector_str_find(&ddata->tmpl, arg, arg_len) && !vector_str_push(&ddata->tmpl, arg, arg_len)) { free(arg); return (0); } free(arg); if (*ddata->cur == 'E') { ++ddata->cur; size = v->size; assert(size > 0); if (!strncmp(v->container[size - 1], ">", 1)) { if (!cpp_demangle_push_str(ddata, " >", 2)) return (0); } else if (!cpp_demangle_push_str(ddata, ">", 1)) return (0); break; } else if (*ddata->cur != 'I' && !cpp_demangle_push_str(ddata, ", ", 2)) return (0); if (limit++ > CPP_DEMANGLE_TRY_LIMIT) return (0); } return (vector_read_cmd_pop(&ddata->cmd)); } /* * Read template parameter that forms in 'T[number]_'. * This function much like to read_subst but only for types. */ static int cpp_demangle_read_tmpl_param(struct cpp_demangle_data *ddata) { long nth; if (ddata == NULL || *ddata->cur != 'T') return (0); ++ddata->cur; if (*ddata->cur == '_') return (cpp_demangle_get_tmpl_param(ddata, 0)); else { errno = 0; if ((nth = strtol(ddata->cur, (char **) NULL, 36)) == 0 && errno != 0) return (0); /* T_ is first */ ++nth; while (*ddata->cur != '_') ++ddata->cur; assert(nth > 0); return (cpp_demangle_get_tmpl_param(ddata, nth)); } /* NOTREACHED */ return (0); } static int cpp_demangle_read_type(struct cpp_demangle_data *ddata, int delimit) { struct vector_type_qualifier v; struct vector_str *output; size_t p_idx, type_str_len; int extern_c, is_builtin; long len; char *type_str; if (ddata == NULL) return (0); output = &ddata->output; if (!strncmp(ddata->output.container[ddata->output.size - 1], ">", 1)) { cpp_demangle_gnu3_push_head++; output = &ddata->output_tmp; } else if (delimit == 1) { if (ddata->paren == false) { if (!cpp_demangle_push_str(ddata, "(", 1)) return (0); if (ddata->output.size < 2) return (0); ddata->paren = true; ddata->pfirst = true; /* Need pop function name */ if (ddata->subst.size == 1 && !vector_str_pop(&ddata->subst)) return (0); } if (ddata->pfirst) ddata->pfirst = false; else if (*ddata->cur != 'I' && !cpp_demangle_push_str(ddata, ", ", 2)) return (0); } assert(output != NULL); /* * [r, V, K] [P, R, C, G, U] builtin, function, class-enum, array * pointer-to-member, template-param, template-template-param, subst */ if (!vector_type_qualifier_init(&v)) return (0); extern_c = 0; is_builtin = 1; p_idx = output->size; type_str = NULL; again: /* builtin type */ switch (*ddata->cur) { case 'a': /* signed char */ if (!cpp_demangle_push_str(ddata, "signed char", 11)) goto clean; ++ddata->cur; goto rtn; case 'A': /* array type */ if (!cpp_demangle_read_array(ddata)) goto clean; is_builtin = 0; goto rtn; case 'b': /* bool */ if (!cpp_demangle_push_str(ddata, "bool", 4)) goto clean; ++ddata->cur; goto rtn; case 'C': /* complex pair */ if (!vector_type_qualifier_push(&v, TYPE_CMX)) goto clean; ++ddata->cur; goto again; case 'c': /* char */ if (!cpp_demangle_push_str(ddata, "char", 4)) goto clean; ++ddata->cur; goto rtn; case 'd': /* double */ if (!cpp_demangle_push_str(ddata, "double", 6)) goto clean; ++ddata->cur; goto rtn; case 'e': /* long double */ if (!cpp_demangle_push_str(ddata, "long double", 11)) goto clean; ++ddata->cur; goto rtn; case 'f': /* float */ if (!cpp_demangle_push_str(ddata, "float", 5)) goto clean; ++ddata->cur; goto rtn; case 'F': /* function */ if (!cpp_demangle_read_function(ddata, &extern_c, &v)) goto clean; is_builtin = 0; goto rtn; case 'g': /* __float128 */ if (!cpp_demangle_push_str(ddata, "__float128", 10)) goto clean; ++ddata->cur; goto rtn; case 'G': /* imaginary */ if (!vector_type_qualifier_push(&v, TYPE_IMG)) goto clean; ++ddata->cur; goto again; case 'h': /* unsigned char */ if (!cpp_demangle_push_str(ddata, "unsigned char", 13)) goto clean; ++ddata->cur; goto rtn; case 'i': /* int */ if (!cpp_demangle_push_str(ddata, "int", 3)) goto clean; ++ddata->cur; goto rtn; case 'j': /* unsigned int */ if (!cpp_demangle_push_str(ddata, "unsigned int", 12)) goto clean; ++ddata->cur; goto rtn; case 'K': /* const */ if (!vector_type_qualifier_push(&v, TYPE_CST)) goto clean; ++ddata->cur; goto again; case 'l': /* long */ if (!cpp_demangle_push_str(ddata, "long", 4)) goto clean; ++ddata->cur; goto rtn; case 'm': /* unsigned long */ if (!cpp_demangle_push_str(ddata, "unsigned long", 13)) goto clean; ++ddata->cur; goto rtn; case 'M': /* pointer to member */ if (!cpp_demangle_read_pointer_to_member(ddata)) goto clean; is_builtin = 0; goto rtn; case 'n': /* __int128 */ if (!cpp_demangle_push_str(ddata, "__int128", 8)) goto clean; ++ddata->cur; goto rtn; case 'o': /* unsigned __int128 */ if (!cpp_demangle_push_str(ddata, "unsigned _;int128", 17)) goto clean; ++ddata->cur; goto rtn; case 'P': /* pointer */ if (!vector_type_qualifier_push(&v, TYPE_PTR)) goto clean; ++ddata->cur; goto again; case 'r': /* restrict */ if (!vector_type_qualifier_push(&v, TYPE_RST)) goto clean; ++ddata->cur; goto again; case 'R': /* reference */ if (!vector_type_qualifier_push(&v, TYPE_REF)) goto clean; ++ddata->cur; goto again; case 's': /* short, local string */ if (!cpp_demangle_push_str(ddata, "short", 5)) goto clean; ++ddata->cur; goto rtn; case 'S': /* substitution */ if (!cpp_demangle_read_subst(ddata)) goto clean; is_builtin = 0; goto rtn; case 't': /* unsigned short */ if (!cpp_demangle_push_str(ddata, "unsigned short", 14)) goto clean; ++ddata->cur; goto rtn; case 'T': /* template parameter */ if (!cpp_demangle_read_tmpl_param(ddata)) goto clean; is_builtin = 0; goto rtn; case 'u': /* vendor extended builtin */ ++ddata->cur; if (!cpp_demangle_read_sname(ddata)) goto clean; is_builtin = 0; goto rtn; case 'U': /* vendor extended type qualifier */ if (!cpp_demangle_read_number(ddata, &len)) goto clean; if (len <= 0) goto clean; if (!vector_str_push(&v.ext_name, ddata->cur, len)) return (0); ddata->cur += len; goto again; case 'v': /* void */ if (!cpp_demangle_push_str(ddata, "void", 4)) goto clean; ++ddata->cur; goto rtn; case 'V': /* volatile */ if (!vector_type_qualifier_push(&v, TYPE_VAT)) goto clean; ++ddata->cur; goto again; case 'w': /* wchar_t */ if (!cpp_demangle_push_str(ddata, "wchar_t", 6)) goto clean; ++ddata->cur; goto rtn; case 'x': /* long long */ if (!cpp_demangle_push_str(ddata, "long long", 9)) goto clean; ++ddata->cur; goto rtn; case 'y': /* unsigned long long */ if (!cpp_demangle_push_str(ddata, "unsigned long long", 18)) goto clean; ++ddata->cur; goto rtn; case 'z': /* ellipsis */ if (!cpp_demangle_push_str(ddata, "ellipsis", 8)) goto clean; ++ddata->cur; goto rtn; }; if (!cpp_demangle_read_name(ddata)) goto clean; is_builtin = 0; rtn: if ((type_str = vector_str_substr(output, p_idx, output->size - 1, &type_str_len)) == NULL) goto clean; if (is_builtin == 0) { if (!vector_str_find(&ddata->subst, type_str, type_str_len) && !vector_str_push(&ddata->subst, type_str, type_str_len)) goto clean; } if (!cpp_demangle_push_type_qualifier(ddata, &v, type_str)) goto clean; free(type_str); vector_type_qualifier_dest(&v); if (cpp_demangle_gnu3_push_head > 0) { if (*ddata->cur == 'I' && cpp_demangle_read_tmpl_args(ddata) == 0) return (0); if (--cpp_demangle_gnu3_push_head > 0) return (1); if (!vector_str_push(&ddata->output_tmp, " ", 1)) return (0); if (!vector_str_push_vector_head(&ddata->output, &ddata->output_tmp)) return (0); vector_str_dest(&ddata->output_tmp); if (!vector_str_init(&ddata->output_tmp)) return (0); if (!cpp_demangle_push_str(ddata, "(", 1)) return (0); ddata->paren = true; ddata->pfirst = true; } return (1); clean: free(type_str); vector_type_qualifier_dest(&v); return (0); } /* * read unqualified-name, unqualified name are operator-name, ctor-dtor-name, * source-name */ static int cpp_demangle_read_uqname(struct cpp_demangle_data *ddata) { size_t len; if (ddata == NULL || *ddata->cur == '\0') return (0); /* operator name */ switch (SIMPLE_HASH(*ddata->cur, *(ddata->cur + 1))) { case SIMPLE_HASH('a', 'a'): /* operator && */ if (!cpp_demangle_push_str(ddata, "operator&&", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('a', 'd'): /* operator & (unary) */ if (!cpp_demangle_push_str(ddata, "operator&", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('a', 'n'): /* operator & */ if (!cpp_demangle_push_str(ddata, "operator&", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('a', 'N'): /* operator &= */ if (!cpp_demangle_push_str(ddata, "operator&=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('a', 'S'): /* operator = */ if (!cpp_demangle_push_str(ddata, "operator=", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('c', 'l'): /* operator () */ if (!cpp_demangle_push_str(ddata, "operator()", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('c', 'm'): /* operator , */ if (!cpp_demangle_push_str(ddata, "operator,", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('c', 'o'): /* operator ~ */ if (!cpp_demangle_push_str(ddata, "operator~", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('c', 'v'): /* operator (cast) */ if (!cpp_demangle_push_str(ddata, "operator(cast)", 14)) return (0); ddata->cur += 2; return (cpp_demangle_read_type(ddata, 1)); case SIMPLE_HASH('d', 'a'): /* operator delete [] */ if (!cpp_demangle_push_str(ddata, "operator delete []", 18)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('d', 'e'): /* operator * (unary) */ if (!cpp_demangle_push_str(ddata, "operator*", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('d', 'l'): /* operator delete */ if (!cpp_demangle_push_str(ddata, "operator delete", 15)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('d', 'v'): /* operator / */ if (!cpp_demangle_push_str(ddata, "operator/", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('d', 'V'): /* operator /= */ if (!cpp_demangle_push_str(ddata, "operator/=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('e', 'o'): /* operator ^ */ if (!cpp_demangle_push_str(ddata, "operator^", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('e', 'O'): /* operator ^= */ if (!cpp_demangle_push_str(ddata, "operator^=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('e', 'q'): /* operator == */ if (!cpp_demangle_push_str(ddata, "operator==", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('g', 'e'): /* operator >= */ if (!cpp_demangle_push_str(ddata, "operator>=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('g', 't'): /* operator > */ if (!cpp_demangle_push_str(ddata, "operator>", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('i', 'x'): /* operator [] */ if (!cpp_demangle_push_str(ddata, "operator[]", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('l', 'e'): /* operator <= */ if (!cpp_demangle_push_str(ddata, "operator<=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('l', 's'): /* operator << */ if (!cpp_demangle_push_str(ddata, "operator<<", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('l', 'S'): /* operator <<= */ if (!cpp_demangle_push_str(ddata, "operator<<=", 11)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('l', 't'): /* operator < */ if (!cpp_demangle_push_str(ddata, "operator<", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('m', 'i'): /* operator - */ if (!cpp_demangle_push_str(ddata, "operator-", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('m', 'I'): /* operator -= */ if (!cpp_demangle_push_str(ddata, "operator-=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('m', 'l'): /* operator * */ if (!cpp_demangle_push_str(ddata, "operator*", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('m', 'L'): /* operator *= */ if (!cpp_demangle_push_str(ddata, "operator*=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('m', 'm'): /* operator -- */ if (!cpp_demangle_push_str(ddata, "operator--", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('n', 'a'): /* operator new[] */ if (!cpp_demangle_push_str(ddata, "operator new []", 15)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('n', 'e'): /* operator != */ if (!cpp_demangle_push_str(ddata, "operator!=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('n', 'g'): /* operator - (unary) */ if (!cpp_demangle_push_str(ddata, "operator-", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('n', 't'): /* operator ! */ if (!cpp_demangle_push_str(ddata, "operator!", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('n', 'w'): /* operator new */ if (!cpp_demangle_push_str(ddata, "operator new", 12)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('o', 'o'): /* operator || */ if (!cpp_demangle_push_str(ddata, "operator||", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('o', 'r'): /* operator | */ if (!cpp_demangle_push_str(ddata, "operator|", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('o', 'R'): /* operator |= */ if (!cpp_demangle_push_str(ddata, "operator|=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('p', 'l'): /* operator + */ if (!cpp_demangle_push_str(ddata, "operator+", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('p', 'L'): /* operator += */ if (!cpp_demangle_push_str(ddata, "operator+=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('p', 'm'): /* operator ->* */ if (!cpp_demangle_push_str(ddata, "operator->*", 11)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('p', 'p'): /* operator ++ */ if (!cpp_demangle_push_str(ddata, "operator++", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('p', 's'): /* operator + (unary) */ if (!cpp_demangle_push_str(ddata, "operator+", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('p', 't'): /* operator -> */ if (!cpp_demangle_push_str(ddata, "operator->", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('q', 'u'): /* operator ? */ if (!cpp_demangle_push_str(ddata, "operator?", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('r', 'm'): /* operator % */ if (!cpp_demangle_push_str(ddata, "operator%", 9)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('r', 'M'): /* operator %= */ if (!cpp_demangle_push_str(ddata, "operator%=", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('r', 's'): /* operator >> */ if (!cpp_demangle_push_str(ddata, "operator>>", 10)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('r', 'S'): /* operator >>= */ if (!cpp_demangle_push_str(ddata, "operator>>=", 11)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('r', 'z'): /* operator sizeof */ if (!cpp_demangle_push_str(ddata, "operator sizeof ", 16)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('s', 'r'): /* scope resolution operator */ if (!cpp_demangle_push_str(ddata, "scope resolution operator ", 26)) return (0); ddata->cur += 2; return (1); case SIMPLE_HASH('s', 'v'): /* operator sizeof */ if (!cpp_demangle_push_str(ddata, "operator sizeof ", 16)) return (0); ddata->cur += 2; return (1); }; /* vendor extened operator */ if (*ddata->cur == 'v' && ELFTC_ISDIGIT(*(ddata->cur + 1))) { if (!cpp_demangle_push_str(ddata, "vendor extened operator ", 24)) return (0); if (!cpp_demangle_push_str(ddata, ddata->cur + 1, 1)) return (0); ddata->cur += 2; return (cpp_demangle_read_sname(ddata)); } /* ctor-dtor-name */ switch (SIMPLE_HASH(*ddata->cur, *(ddata->cur + 1))) { case SIMPLE_HASH('C', '1'): /* FALLTHROUGH */ case SIMPLE_HASH('C', '2'): /* FALLTHROUGH */ case SIMPLE_HASH('C', '3'): if (ddata->last_sname == NULL) return (0); if ((len = strlen(ddata->last_sname)) == 0) return (0); if (!cpp_demangle_push_str(ddata, "::", 2)) return (0); if (!cpp_demangle_push_str(ddata, ddata->last_sname, len)) return (0); ddata->cur +=2; return (1); case SIMPLE_HASH('D', '0'): /* FALLTHROUGH */ case SIMPLE_HASH('D', '1'): /* FALLTHROUGH */ case SIMPLE_HASH('D', '2'): if (ddata->last_sname == NULL) return (0); if ((len = strlen(ddata->last_sname)) == 0) return (0); if (!cpp_demangle_push_str(ddata, "::~", 3)) return (0); if (!cpp_demangle_push_str(ddata, ddata->last_sname, len)) return (0); ddata->cur +=2; return (1); }; /* source name */ if (ELFTC_ISDIGIT(*ddata->cur) != 0) return (cpp_demangle_read_sname(ddata)); /* local source name */ if (*ddata->cur == 'L') return (cpp_demangle_local_source_name(ddata)); return (1); } /* * Read local source name. * * References: * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775 * http://gcc.gnu.org/viewcvs?view=rev&revision=124467 */ static int cpp_demangle_local_source_name(struct cpp_demangle_data *ddata) { /* L */ if (ddata == NULL || *ddata->cur != 'L') return (0); ++ddata->cur; /* source name */ if (!cpp_demangle_read_sname(ddata)) return (0); /* discriminator */ if (*ddata->cur == '_') { ++ddata->cur; while (ELFTC_ISDIGIT(*ddata->cur) != 0) ++ddata->cur; } return (1); } static int cpp_demangle_read_v_offset(struct cpp_demangle_data *ddata) { if (ddata == NULL) return (0); if (!cpp_demangle_push_str(ddata, "offset : ", 9)) return (0); if (!cpp_demangle_read_offset_number(ddata)) return (0); if (!cpp_demangle_push_str(ddata, "virtual offset : ", 17)) return (0); return (!cpp_demangle_read_offset_number(ddata)); } /* * Decode floating point representation to string * Return new allocated string or NULL * * Todo * Replace these functions to macro. */ static char * decode_fp_to_double(const char *p, size_t len) { double f; size_t rtn_len, limit, i; int byte; char *rtn; if (p == NULL || len == 0 || len % 2 != 0 || len / 2 > sizeof(double)) return (NULL); memset(&f, 0, sizeof(double)); for (i = 0; i < len / 2; ++i) { byte = hex_to_dec(p[len - i * 2 - 1]) + hex_to_dec(p[len - i * 2 - 2]) * 16; if (byte < 0 || byte > 255) return (NULL); #if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN ((unsigned char *)&f)[i] = (unsigned char)(byte); #else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ ((unsigned char *)&f)[sizeof(double) - i - 1] = (unsigned char)(byte); #endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ } rtn_len = 64; limit = 0; again: if ((rtn = malloc(sizeof(char) * rtn_len)) == NULL) return (NULL); if (snprintf(rtn, rtn_len, "%fld", f) >= (int)rtn_len) { free(rtn); if (limit++ > FLOAT_SPRINTF_TRY_LIMIT) return (NULL); rtn_len *= BUFFER_GROWFACTOR; goto again; } return rtn; } static char * decode_fp_to_float(const char *p, size_t len) { size_t i, rtn_len, limit; float f; int byte; char *rtn; if (p == NULL || len == 0 || len % 2 != 0 || len / 2 > sizeof(float)) return (NULL); memset(&f, 0, sizeof(float)); for (i = 0; i < len / 2; ++i) { byte = hex_to_dec(p[len - i * 2 - 1]) + hex_to_dec(p[len - i * 2 - 2]) * 16; if (byte < 0 || byte > 255) return (NULL); #if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN ((unsigned char *)&f)[i] = (unsigned char)(byte); #else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ ((unsigned char *)&f)[sizeof(float) - i - 1] = (unsigned char)(byte); #endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ } rtn_len = 64; limit = 0; again: if ((rtn = malloc(sizeof(char) * rtn_len)) == NULL) return (NULL); if (snprintf(rtn, rtn_len, "%ff", f) >= (int)rtn_len) { free(rtn); if (limit++ > FLOAT_SPRINTF_TRY_LIMIT) return (NULL); rtn_len *= BUFFER_GROWFACTOR; goto again; } return rtn; } static char * decode_fp_to_float128(const char *p, size_t len) { long double f; size_t rtn_len, limit, i; int byte; unsigned char buf[FLOAT_QUADRUPLE_BYTES]; char *rtn; switch(sizeof(long double)) { case FLOAT_QUADRUPLE_BYTES: return (decode_fp_to_long_double(p, len)); case FLOAT_EXTENED_BYTES: if (p == NULL || len == 0 || len % 2 != 0 || len / 2 > FLOAT_QUADRUPLE_BYTES) return (NULL); memset(buf, 0, FLOAT_QUADRUPLE_BYTES); for (i = 0; i < len / 2; ++i) { byte = hex_to_dec(p[len - i * 2 - 1]) + hex_to_dec(p[len - i * 2 - 2]) * 16; if (byte < 0 || byte > 255) return (NULL); #if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN buf[i] = (unsigned char)(byte); #else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ buf[FLOAT_QUADRUPLE_BYTES - i -1] = (unsigned char)(byte); #endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ } memset(&f, 0, FLOAT_EXTENED_BYTES); #if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN memcpy(&f, buf, FLOAT_EXTENED_BYTES); #else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ memcpy(&f, buf + 6, FLOAT_EXTENED_BYTES); #endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ rtn_len = 256; limit = 0; again: if ((rtn = malloc(sizeof(char) * rtn_len)) == NULL) return (NULL); if (snprintf(rtn, rtn_len, "%Lfd", f) >= (int)rtn_len) { free(rtn); if (limit++ > FLOAT_SPRINTF_TRY_LIMIT) return (NULL); rtn_len *= BUFFER_GROWFACTOR; goto again; } return (rtn); default: return (NULL); } } static char * decode_fp_to_float80(const char *p, size_t len) { long double f; size_t rtn_len, limit, i; int byte; unsigned char buf[FLOAT_EXTENED_BYTES]; char *rtn; switch(sizeof(long double)) { case FLOAT_QUADRUPLE_BYTES: if (p == NULL || len == 0 || len % 2 != 0 || len / 2 > FLOAT_EXTENED_BYTES) return (NULL); memset(buf, 0, FLOAT_EXTENED_BYTES); for (i = 0; i < len / 2; ++i) { byte = hex_to_dec(p[len - i * 2 - 1]) + hex_to_dec(p[len - i * 2 - 2]) * 16; if (byte < 0 || byte > 255) return (NULL); #if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN buf[i] = (unsigned char)(byte); #else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ buf[FLOAT_EXTENED_BYTES - i -1] = (unsigned char)(byte); #endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ } memset(&f, 0, FLOAT_QUADRUPLE_BYTES); #if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN memcpy(&f, buf, FLOAT_EXTENED_BYTES); #else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ memcpy((unsigned char *)(&f) + 6, buf, FLOAT_EXTENED_BYTES); #endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ rtn_len = 256; limit = 0; again: if ((rtn = malloc(sizeof(char) * rtn_len)) == NULL) return (NULL); if (snprintf(rtn, rtn_len, "%Lfd", f) >= (int)rtn_len) { free(rtn); if (limit++ > FLOAT_SPRINTF_TRY_LIMIT) return (NULL); rtn_len *= BUFFER_GROWFACTOR; goto again; } return (rtn); case FLOAT_EXTENED_BYTES: return (decode_fp_to_long_double(p, len)); default: return (NULL); } } static char * decode_fp_to_long_double(const char *p, size_t len) { long double f; size_t rtn_len, limit, i; int byte; char *rtn; if (p == NULL || len == 0 || len % 2 != 0 || len / 2 > sizeof(long double)) return (NULL); memset(&f, 0, sizeof(long double)); for (i = 0; i < len / 2; ++i) { byte = hex_to_dec(p[len - i * 2 - 1]) + hex_to_dec(p[len - i * 2 - 2]) * 16; if (byte < 0 || byte > 255) return (NULL); #if ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN ((unsigned char *)&f)[i] = (unsigned char)(byte); #else /* ELFTC_BYTE_ORDER != ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ ((unsigned char *)&f)[sizeof(long double) - i - 1] = (unsigned char)(byte); #endif /* ELFTC_BYTE_ORDER == ELFTC_BYTE_ORDER_LITTLE_ENDIAN */ } rtn_len = 256; limit = 0; again: if ((rtn = malloc(sizeof(char) * rtn_len)) == NULL) return (NULL); if (snprintf(rtn, rtn_len, "%Lfd", f) >= (int)rtn_len) { free(rtn); if (limit++ > FLOAT_SPRINTF_TRY_LIMIT) return (NULL); rtn_len *= BUFFER_GROWFACTOR; goto again; } return (rtn); } /* Simple hex to integer function used by decode_to_* function. */ static int hex_to_dec(char c) { switch (c) { case '0': return (0); case '1': return (1); case '2': return (2); case '3': return (3); case '4': return (4); case '5': return (5); case '6': return (6); case '7': return (7); case '8': return (8); case '9': return (9); case 'a': return (10); case 'b': return (11); case 'c': return (12); case 'd': return (13); case 'e': return (14); case 'f': return (15); default: return (-1); }; } /** * @brief Test input string is mangled by IA-64 C++ ABI style. * * Test string heads with "_Z" or "_GLOBAL__I_". * @return Return 0 at false. */ bool is_cpp_mangled_gnu3(const char *org) { size_t len; len = strlen(org); return ((len > 2 && *org == '_' && *(org + 1) == 'Z') || (len > 11 && !strncmp(org, "_GLOBAL__I_", 11))); } static void vector_read_cmd_dest(struct vector_read_cmd *v) { if (v == NULL) return; free(v->r_container); } /* return -1 at failed, 0 at not found, 1 at found. */ static int vector_read_cmd_find(struct vector_read_cmd *v, enum read_cmd dst) { size_t i; if (v == NULL || dst == READ_FAIL) return (-1); for (i = 0; i < v->size; ++i) if (v->r_container[i] == dst) return (1); return (0); } static int vector_read_cmd_init(struct vector_read_cmd *v) { if (v == NULL) return (0); v->size = 0; v->capacity = VECTOR_DEF_CAPACITY; if ((v->r_container = malloc(sizeof(enum read_cmd) * v->capacity)) == NULL) return (0); return (1); } static int vector_read_cmd_pop(struct vector_read_cmd *v) { if (v == NULL || v->size == 0) return (0); --v->size; v->r_container[v->size] = READ_FAIL; return (1); } static int vector_read_cmd_push(struct vector_read_cmd *v, enum read_cmd cmd) { enum read_cmd *tmp_r_ctn; size_t tmp_cap; size_t i; if (v == NULL) return (0); if (v->size == v->capacity) { tmp_cap = v->capacity * BUFFER_GROWFACTOR; if ((tmp_r_ctn = malloc(sizeof(enum read_cmd) * tmp_cap)) == NULL) return (0); for (i = 0; i < v->size; ++i) tmp_r_ctn[i] = v->r_container[i]; free(v->r_container); v->r_container = tmp_r_ctn; v->capacity = tmp_cap; } v->r_container[v->size] = cmd; ++v->size; return (1); } static void vector_type_qualifier_dest(struct vector_type_qualifier *v) { if (v == NULL) return; free(v->q_container); vector_str_dest(&v->ext_name); } /* size, capacity, ext_name */ static int vector_type_qualifier_init(struct vector_type_qualifier *v) { if (v == NULL) return (0); v->size = 0; v->capacity = VECTOR_DEF_CAPACITY; if ((v->q_container = malloc(sizeof(enum type_qualifier) * v->capacity)) == NULL) return (0); assert(v->q_container != NULL); if (vector_str_init(&v->ext_name) == false) { free(v->q_container); return (0); } return (1); } static int vector_type_qualifier_push(struct vector_type_qualifier *v, enum type_qualifier t) { enum type_qualifier *tmp_ctn; size_t tmp_cap; size_t i; if (v == NULL) return (0); if (v->size == v->capacity) { tmp_cap = v->capacity * BUFFER_GROWFACTOR; if ((tmp_ctn = malloc(sizeof(enum type_qualifier) * tmp_cap)) == NULL) return (0); for (i = 0; i < v->size; ++i) tmp_ctn[i] = v->q_container[i]; free(v->q_container); v->q_container = tmp_ctn; v->capacity = tmp_cap; } v->q_container[v->size] = t; ++v->size; return (1); } Index: head/contrib/elftoolchain/nm/nm.c =================================================================== --- head/contrib/elftoolchain/nm/nm.c (revision 275368) +++ head/contrib/elftoolchain/nm/nm.c (revision 275369) @@ -1,2096 +1,2096 @@ /*- * Copyright (c) 2007 Hyogeol Lee * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "_elftc.h" ELFTC_VCSID("$Id: nm.c 2484 2012-04-07 15:52:05Z kaiwang27 $"); /* symbol information list */ STAILQ_HEAD(sym_head, sym_entry); struct sym_entry { char *name; GElf_Sym *sym; STAILQ_ENTRY(sym_entry) sym_entries; }; typedef int (*fn_sort)(const void *, const void *); typedef void (*fn_elem_print)(char, const char *, const GElf_Sym *, const char *); typedef void (*fn_sym_print)(const GElf_Sym *); typedef int (*fn_filter)(char, const GElf_Sym *, const char *); /* output filter list */ -SLIST_HEAD(filter_head, filter_entry) nm_out_filter = +static SLIST_HEAD(filter_head, filter_entry) nm_out_filter = SLIST_HEAD_INITIALIZER(nm_out_filter); struct filter_entry { fn_filter fn; SLIST_ENTRY(filter_entry) filter_entries; }; struct sym_print_data { struct sym_head *headp; size_t sh_num, list_num; const char *t_table, **s_table, *filename, *objname; }; struct nm_prog_info { const char *name; const char *def_filename; }; /* List for line number information. */ struct line_info_entry { uint64_t addr; /* address */ uint64_t line; /* line number */ char *file; /* file name with path */ SLIST_ENTRY(line_info_entry) entries; }; SLIST_HEAD(line_info_head, line_info_entry); /* List for function line number information. */ struct func_info_entry { char *name; /* function name */ char *file; /* file name with path */ uint64_t lowpc; /* low address */ uint64_t highpc; /* high address */ uint64_t line; /* line number */ SLIST_ENTRY(func_info_entry) entries; }; SLIST_HEAD(func_info_head, func_info_entry); /* List for variable line number information. */ struct var_info_entry { char *name; /* variable name */ char *file; /* file name with path */ uint64_t addr; /* address */ uint64_t line; /* line number */ SLIST_ENTRY(var_info_entry) entries; }; SLIST_HEAD(var_info_head, var_info_entry); /* output numric type */ enum radix { RADIX_OCT, RADIX_HEX, RADIX_DEC }; /* output symbol type, PRINT_SYM_DYN for dynamic symbol only */ enum print_symbol { PRINT_SYM_SYM, PRINT_SYM_DYN }; /* output name type */ enum print_name { PRINT_NAME_NONE, PRINT_NAME_FULL, PRINT_NAME_MULTI }; struct nm_prog_options { enum print_symbol print_symbol; enum print_name print_name; enum radix t; int demangle_type; bool print_debug; bool print_armap; int print_size; bool debug_line; int def_only; bool undef_only; int sort_size; bool sort_reverse; int no_demangle; /* * function pointer to sort symbol list. * possible function - cmp_name, cmp_none, cmp_size, cmp_value */ fn_sort sort_fn; /* * function pointer to print symbol elem. * possible function - sym_elem_print_all * sym_elem_print_all_portable * sym_elem_print_all_sysv */ fn_elem_print elem_print_fn; fn_sym_print value_print_fn; fn_sym_print size_print_fn; }; #define CHECK_SYM_PRINT_DATA(p) (p->headp == NULL || p->sh_num == 0 || \ p->t_table == NULL || p->s_table == NULL || p->filename == NULL) #define IS_SYM_TYPE(t) ((t) == '?' || isalpha((t)) != 0) #define IS_UNDEF_SYM_TYPE(t) ((t) == 'U' || (t) == 'v' || (t) == 'w') #define UNUSED(p) ((void)p) static int cmp_name(const void *, const void *); static int cmp_none(const void *, const void *); static int cmp_size(const void *, const void *); static int cmp_value(const void *, const void *); static void filter_dest(void); static int filter_insert(fn_filter); static void get_opt(int, char **); static int get_sym(Elf *, struct sym_head *, int, size_t, size_t, const char *, const char **, int); static const char * get_sym_name(Elf *, const GElf_Sym *, size_t, const char **, int); static char get_sym_type(const GElf_Sym *, const char *); static void global_dest(void); static void global_init(void); static bool is_sec_data(GElf_Shdr *); static bool is_sec_debug(const char *); static bool is_sec_nobits(GElf_Shdr *); static bool is_sec_readonly(GElf_Shdr *); static bool is_sec_text(GElf_Shdr *); static void print_ar_index(int, Elf *); static void print_header(const char *, const char *); static void print_version(void); static int read_elf(Elf *, const char *, Elf_Kind); static int read_object(const char *); static int read_files(int, char **); static void set_opt_value_print_fn(enum radix); static int sym_elem_def(char, const GElf_Sym *, const char *); static int sym_elem_global(char, const GElf_Sym *, const char *); static int sym_elem_global_static(char, const GElf_Sym *, const char *); static int sym_elem_nondebug(char, const GElf_Sym *, const char *); static int sym_elem_nonzero_size(char, const GElf_Sym *, const char *); static void sym_elem_print_all(char, const char *, const GElf_Sym *, const char *); static void sym_elem_print_all_portable(char, const char *, const GElf_Sym *, const char *); static void sym_elem_print_all_sysv(char, const char *, const GElf_Sym *, const char *); static int sym_elem_undef(char, const GElf_Sym *, const char *); static void sym_list_dest(struct sym_head *); static int sym_list_insert(struct sym_head *, const char *, const GElf_Sym *); static void sym_list_print(struct sym_print_data *, struct func_info_head *, struct var_info_head *, struct line_info_head *); static void sym_list_print_each(struct sym_entry *, struct sym_print_data *, struct func_info_head *, struct var_info_head *, struct line_info_head *); static struct sym_entry *sym_list_sort(struct sym_print_data *); static void sym_size_oct_print(const GElf_Sym *); static void sym_size_hex_print(const GElf_Sym *); static void sym_size_dec_print(const GElf_Sym *); static void sym_value_oct_print(const GElf_Sym *); static void sym_value_hex_print(const GElf_Sym *); static void sym_value_dec_print(const GElf_Sym *); static void usage(int); static struct nm_prog_info nm_info; static struct nm_prog_options nm_opts; static int nm_elfclass; /* * Point to current sym_print_data to use portable qsort function. * (e.g. There is no qsort_r function in NetBSD.) * * Using in sym_list_sort. */ static struct sym_print_data *nm_print_data; static const struct option nm_longopts[] = { { "debug-syms", no_argument, NULL, 'a' }, { "defined-only", no_argument, &nm_opts.def_only, 1}, { "demangle", optional_argument, NULL, 'C' }, { "dynamic", no_argument, NULL, 'D' }, { "format", required_argument, NULL, 'F' }, { "help", no_argument, NULL, 'h' }, { "line-numbers", no_argument, NULL, 'l' }, { "no-demangle", no_argument, &nm_opts.no_demangle, 1}, { "no-sort", no_argument, NULL, 'p' }, { "numeric-sort", no_argument, NULL, 'v' }, { "print-armap", no_argument, NULL, 's' }, { "print-file-name", no_argument, NULL, 'A' }, { "print-size", no_argument, NULL, 'S' }, { "radix", required_argument, NULL, 't' }, { "reverse-sort", no_argument, NULL, 'r' }, { "size-sort", no_argument, &nm_opts.sort_size, 1}, { "undefined-only", no_argument, NULL, 'u' }, { "version", no_argument, NULL, 'V' }, { NULL, 0, NULL, 0 } }; #if defined(ELFTC_NEED_BYTEORDER_EXTENSIONS) static __inline uint32_t be32dec(const void *pp) { unsigned char const *p = (unsigned char const *)pp; return ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); } static __inline uint32_t le32dec(const void *pp) { unsigned char const *p = (unsigned char const *)pp; return ((p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]); } static __inline uint64_t be64dec(const void *pp) { unsigned char const *p = (unsigned char const *)pp; return (((uint64_t)be32dec(p) << 32) | be32dec(p + 4)); } static __inline uint64_t le64dec(const void *pp) { unsigned char const *p = (unsigned char const *)pp; return (((uint64_t)le32dec(p + 4) << 32) | le32dec(p)); } #endif static int cmp_name(const void *l, const void *r) { assert(l != NULL); assert(r != NULL); assert(((const struct sym_entry *)l)->name != NULL); assert(((const struct sym_entry *)r)->name != NULL); return (strcmp(((const struct sym_entry *)l)->name, ((const struct sym_entry *)r)->name)); } static int cmp_none(const void *l, const void *r) { UNUSED(l); UNUSED(r); return (0); } /* Size comparison. If l and r have same size, compare their name. */ static int cmp_size(const void *lp, const void *rp) { const struct sym_entry *l, *r; l = lp; r = rp; assert(l != NULL); assert(l->name != NULL); assert(l->sym != NULL); assert(r != NULL); assert(r->name != NULL); assert(r->sym != NULL); if (l->sym->st_size == r->sym->st_size) return (strcmp(l->name, r->name)); return (l->sym->st_size - r->sym->st_size); } /* Value comparison. Undefined symbols come first. */ static int cmp_value(const void *lp, const void *rp) { const struct sym_entry *l, *r; const char *ttable; int l_is_undef, r_is_undef; l = lp; r = rp; assert(nm_print_data != NULL); ttable = nm_print_data->t_table; assert(l != NULL); assert(l->name != NULL); assert(l->sym != NULL); assert(r != NULL); assert(r->name != NULL); assert(r->sym != NULL); assert(ttable != NULL); l_is_undef = IS_UNDEF_SYM_TYPE(get_sym_type(l->sym, ttable)) ? 1 : 0; r_is_undef = IS_UNDEF_SYM_TYPE(get_sym_type(r->sym, ttable)) ? 1 : 0; assert(l_is_undef + r_is_undef >= 0); assert(l_is_undef + r_is_undef <= 2); switch (l_is_undef + r_is_undef) { case 0: /* Both defined */ if (l->sym->st_value == r->sym->st_value) return (strcmp(l->name, r->name)); return (l->sym->st_value - r->sym->st_value); case 1: /* One undefined */ return (l_is_undef == 0 ? 1 : -1); case 2: /* Both undefined */ return (strcmp(l->name, r->name)); } /* NOTREACHED */ return (l->sym->st_value - r->sym->st_value); } static void filter_dest(void) { struct filter_entry *e; while (!SLIST_EMPTY(&nm_out_filter)) { e = SLIST_FIRST(&nm_out_filter); SLIST_REMOVE_HEAD(&nm_out_filter, filter_entries); free(e); } } static int filter_insert(fn_filter filter_fn) { struct filter_entry *e; assert(filter_fn != NULL); if ((e = malloc(sizeof(struct filter_entry))) == NULL) { warn("malloc"); return (0); } e->fn = filter_fn; SLIST_INSERT_HEAD(&nm_out_filter, e, filter_entries); return (1); } static int parse_demangle_option(const char *opt) { if (opt == NULL) return (ELFTC_DEM_UNKNOWN); else if (!strncasecmp(opt, "gnu-v2", 6)) return (ELFTC_DEM_GNU2); else if (!strncasecmp(opt, "gnu-v3", 6)) return (ELFTC_DEM_GNU3); else if (!strncasecmp(opt, "arm", 3)) return (ELFTC_DEM_ARM); else errx(EXIT_FAILURE, "unknown demangling style '%s'", opt); /* NOTREACHED */ return (0); } static void get_opt(int argc, char **argv) { int ch; bool is_posix, oflag; if (argc <= 0 || argv == NULL) return; oflag = is_posix = false; nm_opts.t = RADIX_HEX; while ((ch = getopt_long(argc, argv, "ABCDF:PSVaefghlnoprst:uvx", nm_longopts, NULL)) != -1) { switch (ch) { case 'A': nm_opts.print_name = PRINT_NAME_FULL; break; case 'B': nm_opts.elem_print_fn = &sym_elem_print_all; break; case 'C': nm_opts.demangle_type = parse_demangle_option(optarg); break; case 'D': nm_opts.print_symbol = PRINT_SYM_DYN; break; case 'F': /* sysv, bsd, posix */ switch (optarg[0]) { case 'B': case 'b': nm_opts.elem_print_fn = &sym_elem_print_all; break; case 'P': case 'p': is_posix = true; nm_opts.elem_print_fn = &sym_elem_print_all_portable; break; case 'S': case 's': nm_opts.elem_print_fn = &sym_elem_print_all_sysv; break; default: warnx("%s: Invalid format", optarg); usage(1); } break; case 'P': is_posix = true; nm_opts.elem_print_fn = &sym_elem_print_all_portable; break; case 'S': nm_opts.print_size = 1; break; case 'V': print_version(); /* NOTREACHED */ case 'a': nm_opts.print_debug = true; break; case 'e': filter_insert(sym_elem_global_static); break; case 'f': break; case 'g': filter_insert(sym_elem_global); break; case 'h': usage(0); break; case 'l': nm_opts.debug_line = true; break; case 'n': case 'v': nm_opts.sort_fn = &cmp_value; break; case 'o': oflag = true; break; case 'p': nm_opts.sort_fn = &cmp_none; break; case 'r': nm_opts.sort_reverse = true; break; case 's': nm_opts.print_armap = true; break; case 't': /* t require always argument to getopt_long */ switch (optarg[0]) { case 'd': nm_opts.t = RADIX_DEC; break; case 'o': nm_opts.t = RADIX_OCT; break; case 'x': nm_opts.t = RADIX_HEX; break; default: warnx("%s: Invalid radix", optarg); usage(1); } break; case 'u': filter_insert(sym_elem_undef); nm_opts.undef_only = true; break; /* case 'v': see case 'n' above. */ case 'x': nm_opts.t = RADIX_HEX; break; case 0: if (nm_opts.sort_size != 0) { nm_opts.sort_fn = &cmp_size; filter_insert(sym_elem_def); filter_insert(sym_elem_nonzero_size); } if (nm_opts.def_only != 0) filter_insert(sym_elem_def); if (nm_opts.no_demangle != 0) nm_opts.demangle_type = -1; break; default : usage(1); } } /* * In POSIX mode, the '-o' option controls the output radix. * In non-POSIX mode, the option is a synonym for the '-A' and * '--print-file-name' options. */ if (oflag) { if (is_posix) nm_opts.t = RADIX_OCT; else nm_opts.print_name = PRINT_NAME_FULL; } assert(nm_opts.sort_fn != NULL && "nm_opts.sort_fn is null"); assert(nm_opts.elem_print_fn != NULL && "nm_opts.elem_print_fn is null"); assert(nm_opts.value_print_fn != NULL && "nm_opts.value_print_fn is null"); set_opt_value_print_fn(nm_opts.t); if (nm_opts.undef_only == true) { if (nm_opts.sort_fn == &cmp_size) errx(EXIT_FAILURE, "--size-sort with -u is meaningless"); if (nm_opts.def_only != 0) errx(EXIT_FAILURE, "-u with --defined-only is meaningless"); } if (nm_opts.print_debug == false) filter_insert(sym_elem_nondebug); if (nm_opts.sort_reverse == true && nm_opts.sort_fn == cmp_none) nm_opts.sort_reverse = false; } /* * Get symbol information from elf. */ static int get_sym(Elf *elf, struct sym_head *headp, int shnum, size_t dynndx, size_t strndx, const char *type_table, const char **sec_table, int sec_table_size) { Elf_Scn *scn; Elf_Data *data; GElf_Shdr shdr; GElf_Sym sym; struct filter_entry *fep; size_t ndx; int rtn; const char *sym_name; char type; bool filter; int i, j; assert(elf != NULL); assert(headp != NULL); rtn = 0; for (i = 1; i < shnum; i++) { if ((scn = elf_getscn(elf, i)) == NULL) { warnx("elf_getscn failed: %s", elf_errmsg(-1)); continue; } if (gelf_getshdr(scn, &shdr) != &shdr) { warnx("gelf_getshdr failed: %s", elf_errmsg(-1)); continue; } if (shdr.sh_type == SHT_SYMTAB) { if (nm_opts.print_symbol != PRINT_SYM_SYM) continue; } else if (shdr.sh_type == SHT_DYNSYM) { if (nm_opts.print_symbol != PRINT_SYM_DYN) continue; } else continue; ndx = shdr.sh_type == SHT_DYNSYM ? dynndx : strndx; data = NULL; while ((data = elf_getdata(scn, data)) != NULL) { j = 1; while (gelf_getsym(data, j++, &sym) != NULL) { sym_name = get_sym_name(elf, &sym, ndx, sec_table, sec_table_size); filter = false; type = get_sym_type(&sym, type_table); SLIST_FOREACH(fep, &nm_out_filter, filter_entries) { if (!fep->fn(type, &sym, sym_name)) { filter = true; break; } } if (filter == false) { if (sym_list_insert(headp, sym_name, &sym) == 0) return (0); rtn++; } } } } return (rtn); } static const char * get_sym_name(Elf *elf, const GElf_Sym *sym, size_t ndx, const char **sec_table, int sec_table_size) { const char *sym_name; sym_name = NULL; /* Show section name as symbol name for STT_SECTION symbols. */ if (GELF_ST_TYPE(sym->st_info) == STT_SECTION) { if (sec_table != NULL && sym->st_shndx < sec_table_size) sym_name = sec_table[sym->st_shndx]; } else sym_name = elf_strptr(elf, ndx, sym->st_name); if (sym_name == NULL) sym_name = "(null)"; return (sym_name); } static char get_sym_type(const GElf_Sym *sym, const char *type_table) { bool is_local; if (sym == NULL || type_table == NULL) return ('?'); is_local = sym->st_info >> 4 == STB_LOCAL; if (sym->st_shndx == SHN_ABS) /* absolute */ return (is_local ? 'a' : 'A'); if (sym->st_shndx == SHN_COMMON) /* common */ return ('C'); if ((sym->st_info) >> 4 == STB_WEAK) { /* weak */ if ((sym->st_info & 0xf) == STT_OBJECT) return (sym->st_shndx == SHN_UNDEF ? 'v' : 'V'); return (sym->st_shndx == SHN_UNDEF ? 'w' : 'W'); } if (sym->st_shndx == SHN_UNDEF) /* undefined */ return ('U'); return (is_local == true && type_table[sym->st_shndx] != 'N' ? tolower((unsigned char) type_table[sym->st_shndx]) : type_table[sym->st_shndx]); } static void global_dest(void) { filter_dest(); } static void global_init(void) { if (elf_version(EV_CURRENT) == EV_NONE) errx(EXIT_FAILURE, "elf_version error"); nm_info.name = ELFTC_GETPROGNAME(); nm_info.def_filename = "a.out"; nm_opts.print_symbol = PRINT_SYM_SYM; nm_opts.print_name = PRINT_NAME_NONE; nm_opts.demangle_type = -1; nm_opts.print_debug = false; nm_opts.print_armap = false; nm_opts.print_size = 0; nm_opts.debug_line = false; nm_opts.def_only = 0; nm_opts.undef_only = false; nm_opts.sort_size = 0; nm_opts.sort_reverse = false; nm_opts.no_demangle = 0; nm_opts.sort_fn = &cmp_name; nm_opts.elem_print_fn = &sym_elem_print_all; nm_opts.value_print_fn = &sym_value_dec_print; nm_opts.size_print_fn = &sym_size_dec_print; SLIST_INIT(&nm_out_filter); } static bool is_sec_data(GElf_Shdr *s) { assert(s != NULL && "shdr is NULL"); return (((s->sh_flags & SHF_ALLOC) != 0) && s->sh_type != SHT_NOBITS); } static bool is_sec_debug(const char *shname) { const char *dbg_sec[] = { ".debug", ".gnu.linkonce.wi.", ".line", ".rel.debug", ".rela.debug", ".stab", NULL }; const char **p; assert(shname != NULL && "shname is NULL"); for (p = dbg_sec; *p; p++) { if (!strncmp(shname, *p, strlen(*p))) return (true); } return (false); } static bool is_sec_nobits(GElf_Shdr *s) { assert(s != NULL && "shdr is NULL"); return (s->sh_type == SHT_NOBITS); } static bool is_sec_readonly(GElf_Shdr *s) { assert(s != NULL && "shdr is NULL"); return ((s->sh_flags & SHF_WRITE) == 0); } static bool is_sec_text(GElf_Shdr *s) { assert(s != NULL && "shdr is NULL"); return ((s->sh_flags & SHF_EXECINSTR) != 0); } static void print_ar_index(int fd, Elf *arf) { Elf *elf; Elf_Arhdr *arhdr; Elf_Arsym *arsym; Elf_Cmd cmd; off_t start; size_t arsym_size; if (arf == NULL) return; if ((arsym = elf_getarsym(arf, &arsym_size)) == NULL) return; printf("\nArchive index:\n"); start = arsym->as_off; cmd = ELF_C_READ; while (arsym_size > 1) { if (elf_rand(arf, arsym->as_off) == arsym->as_off && (elf = elf_begin(fd, cmd, arf)) != NULL) { if ((arhdr = elf_getarhdr(elf)) != NULL) printf("%s in %s\n", arsym->as_name, arhdr->ar_name != NULL ? arhdr->ar_name : arhdr->ar_rawname); elf_end(elf); } ++arsym; --arsym_size; } elf_rand(arf, start); } #define DEMANGLED_BUFFER_SIZE (8 * 1024) #define PRINT_DEMANGLED_NAME(FORMAT, NAME) do { \ char _demangled[DEMANGLED_BUFFER_SIZE]; \ if (nm_opts.demangle_type < 0 || \ elftc_demangle((NAME), _demangled, sizeof(_demangled), \ nm_opts.demangle_type) < 0) \ printf((FORMAT), (NAME)); \ else \ printf((FORMAT), _demangled); \ } while (0) static void print_header(const char *file, const char *obj) { if (file == NULL) return; if (nm_opts.elem_print_fn == &sym_elem_print_all_sysv) { printf("\n\n%s from %s", nm_opts.undef_only == false ? "Symbols" : "Undefined symbols", file); if (obj != NULL) printf("[%s]", obj); printf(":\n\n"); printf("\ Name Value Class Type Size Line Section\n\n"); } else { /* archive file without -A option and POSIX */ if (nm_opts.print_name != PRINT_NAME_FULL && obj != NULL) { if (nm_opts.elem_print_fn == sym_elem_print_all_portable) printf("%s[%s]:\n", file, obj); else if (nm_opts.elem_print_fn == sym_elem_print_all) printf("\n%s:\n", obj); /* multiple files(not archive) without -A option */ } else if (nm_opts.print_name == PRINT_NAME_MULTI) { if (nm_opts.elem_print_fn == sym_elem_print_all) printf("\n"); printf("%s:\n", file); } } } static void print_version(void) { (void) printf("%s (%s)\n", nm_info.name, elftc_version()); exit(0); } static uint64_t get_block_value(Dwarf_Debug dbg, Dwarf_Block *block) { Elf *elf; GElf_Ehdr eh; Dwarf_Error de; if (dwarf_get_elf(dbg, &elf, &de) != DW_DLV_OK) { warnx("dwarf_get_elf failed: %s", dwarf_errmsg(de)); return (0); } if (gelf_getehdr(elf, &eh) != &eh) { warnx("gelf_getehdr failed: %s", elf_errmsg(-1)); return (0); } if (block->bl_len == 5) { if (eh.e_ident[EI_DATA] == ELFDATA2LSB) return (le32dec((uint8_t *) block->bl_data + 1)); else return (be32dec((uint8_t *) block->bl_data + 1)); } else if (block->bl_len == 9) { if (eh.e_ident[EI_DATA] == ELFDATA2LSB) return (le64dec((uint8_t *) block->bl_data + 1)); else return (be64dec((uint8_t *) block->bl_data + 1)); } return (0); } static void search_line_attr(Dwarf_Debug dbg, struct func_info_head *func_info, struct var_info_head *var_info, Dwarf_Die die, char **src_files, Dwarf_Signed filecount) { Dwarf_Attribute at; Dwarf_Unsigned udata; Dwarf_Half tag; Dwarf_Block *block; Dwarf_Bool flag; Dwarf_Die ret_die; Dwarf_Error de; struct func_info_entry *func; struct var_info_entry *var; const char *str; int ret; if (dwarf_tag(die, &tag, &de) != DW_DLV_OK) { warnx("dwarf_tag failed: %s", dwarf_errmsg(de)); goto cont_search; } /* We're interested in DIEs which define functions or variables. */ if (tag != DW_TAG_subprogram && tag != DW_TAG_entry_point && tag != DW_TAG_inlined_subroutine && tag != DW_TAG_variable) goto cont_search; if (tag == DW_TAG_variable) { /* Ignore "artificial" variable. */ if (dwarf_attrval_flag(die, DW_AT_artificial, &flag, &de) == DW_DLV_OK && flag) goto cont_search; /* Ignore pure declaration. */ if (dwarf_attrval_flag(die, DW_AT_declaration, &flag, &de) == DW_DLV_OK && flag) goto cont_search; /* Ignore stack varaibles. */ if (dwarf_attrval_flag(die, DW_AT_external, &flag, &de) != DW_DLV_OK || !flag) goto cont_search; if ((var = calloc(1, sizeof(*var))) == NULL) { warn("calloc failed"); goto cont_search; } if (dwarf_attrval_unsigned(die, DW_AT_decl_file, &udata, &de) == DW_DLV_OK && udata > 0 && (Dwarf_Signed) (udata - 1) < filecount) { var->file = strdup(src_files[udata - 1]); if (var->file == NULL) { warn("strdup"); free(var); goto cont_search; } } if (dwarf_attrval_unsigned(die, DW_AT_decl_line, &udata, &de) == DW_DLV_OK) var->line = udata; if (dwarf_attrval_string(die, DW_AT_name, &str, &de) == DW_DLV_OK) { var->name = strdup(str); if (var->name == NULL) { warn("strdup"); if (var->file) free(var->file); free(var); goto cont_search; } } if (dwarf_attr(die, DW_AT_location, &at, &de) == DW_DLV_OK && dwarf_formblock(at, &block, &de) == DW_DLV_OK) { /* * Since we ignored stack variables, the rest are the * external varaibles which should always use DW_OP_addr * operator for DW_AT_location value. */ if (*((uint8_t *)block->bl_data) == DW_OP_addr) var->addr = get_block_value(dbg, block); } SLIST_INSERT_HEAD(var_info, var, entries); } else { if ((func = calloc(1, sizeof(*func))) == NULL) { warn("calloc failed"); goto cont_search; } /* * Note that dwarf_attrval_unsigned() handles DW_AT_abstract_origin * internally, so it can retrieve DW_AT_decl_file/DW_AT_decl_line * attributes for inlined functions as well. */ if (dwarf_attrval_unsigned(die, DW_AT_decl_file, &udata, &de) == DW_DLV_OK && udata > 0 && (Dwarf_Signed) (udata - 1) < filecount) { func->file = strdup(src_files[udata - 1]); if (func->file == NULL) { warn("strdup"); free(func); goto cont_search; } } if (dwarf_attrval_unsigned(die, DW_AT_decl_line, &udata, &de) == DW_DLV_OK) func->line = udata; if (dwarf_attrval_string(die, DW_AT_name, &str, &de) == DW_DLV_OK) { func->name = strdup(str); if (func->name == NULL) { warn("strdup"); if (func->file) free(func->file); free(func); goto cont_search; } } if (dwarf_attrval_unsigned(die, DW_AT_low_pc, &udata, &de) == DW_DLV_OK) func->lowpc = udata; if (dwarf_attrval_unsigned(die, DW_AT_high_pc, &udata, &de) == DW_DLV_OK) func->highpc = udata; SLIST_INSERT_HEAD(func_info, func, entries); } cont_search: /* Search children. */ ret = dwarf_child(die, &ret_die, &de); if (ret == DW_DLV_ERROR) warnx("dwarf_child: %s", dwarf_errmsg(de)); else if (ret == DW_DLV_OK) search_line_attr(dbg, func_info, var_info, ret_die, src_files, filecount); /* Search sibling. */ ret = dwarf_siblingof(dbg, die, &ret_die, &de); if (ret == DW_DLV_ERROR) warnx("dwarf_siblingof: %s", dwarf_errmsg(de)); else if (ret == DW_DLV_OK) search_line_attr(dbg, func_info, var_info, ret_die, src_files, filecount); dwarf_dealloc(dbg, die, DW_DLA_DIE); } /* * Read elf file and collect symbol information, sort them, print. * Return 1 at failed, 0 at success. */ static int read_elf(Elf *elf, const char *filename, Elf_Kind kind) { Dwarf_Debug dbg; Dwarf_Die die; Dwarf_Error de; Dwarf_Half tag; Elf_Arhdr *arhdr; Elf_Scn *scn; GElf_Shdr shdr; GElf_Half i; Dwarf_Line *lbuf; Dwarf_Unsigned lineno; Dwarf_Signed lcount, filecount; Dwarf_Addr lineaddr; struct sym_print_data p_data; struct sym_head list_head; struct line_info_head *line_info; struct func_info_head *func_info; struct var_info_head *var_info; struct line_info_entry *lie; struct func_info_entry *func; struct var_info_entry *var; const char *shname, *objname; char *type_table, **sec_table, *sfile, **src_files; size_t shstrndx, shnum, dynndx, strndx; int ret, rtn, e_err; #define OBJNAME (objname == NULL ? filename : objname) assert(filename != NULL && "filename is null"); STAILQ_INIT(&list_head); type_table = NULL; sec_table = NULL; line_info = NULL; func_info = NULL; var_info = NULL; objname = NULL; dynndx = SHN_UNDEF; strndx = SHN_UNDEF; rtn = 0; nm_elfclass = gelf_getclass(elf); if (kind == ELF_K_AR) { if ((arhdr = elf_getarhdr(elf)) == NULL) goto next_cmd; objname = arhdr->ar_name != NULL ? arhdr->ar_name : arhdr->ar_rawname; } if (!elf_getshnum(elf, &shnum)) { if ((e_err = elf_errno()) != 0) warnx("%s: %s", OBJNAME, elf_errmsg(e_err)); else warnx("%s: cannot get section number", OBJNAME); rtn = 1; goto next_cmd; } if (shnum == 0) { warnx("%s: has no section", OBJNAME); rtn = 1; goto next_cmd; } if (!elf_getshstrndx(elf, &shstrndx)) { warnx("%s: cannot get str index", OBJNAME); rtn = 1; goto next_cmd; } /* type_table for type determine */ if ((type_table = malloc(sizeof(char) * shnum)) == NULL) { warn("%s: malloc", OBJNAME); rtn = 1; goto next_cmd; } /* sec_table for section name to display in sysv format */ if ((sec_table = calloc(shnum, sizeof(char *))) == NULL) { warn("%s: calloc", OBJNAME); rtn = 1; goto next_cmd; } type_table[0] = 'U'; if ((sec_table[0] = strdup("*UND*")) == NULL) { warn("strdup"); goto next_cmd; } for (i = 1; i < shnum; ++i) { type_table[i] = 'U'; if ((scn = elf_getscn(elf, i)) == NULL) { if ((e_err = elf_errno()) != 0) warnx("%s: %s", OBJNAME, elf_errmsg(e_err)); else warnx("%s: cannot get section", OBJNAME); rtn = 1; goto next_cmd; } if (gelf_getshdr(scn, &shdr) == NULL) goto next_cmd; /* * Cannot test by type and attribute for dynstr, strtab */ shname = elf_strptr(elf, shstrndx, (size_t) shdr.sh_name); if (shname != NULL) { if ((sec_table[i] = strdup(shname)) == NULL) { warn("strdup"); goto next_cmd; } if (!strncmp(shname, ".dynstr", 7)) { dynndx = elf_ndxscn(scn); if (dynndx == SHN_UNDEF) { warnx("%s: elf_ndxscn failed: %s", OBJNAME, elf_errmsg(-1)); goto next_cmd; } } if (!strncmp(shname, ".strtab", 7)) { strndx = elf_ndxscn(scn); if (strndx == SHN_UNDEF) { warnx("%s: elf_ndxscn failed: %s", OBJNAME, elf_errmsg(-1)); goto next_cmd; } } } else { sec_table[i] = strdup("*UND*"); if (sec_table[i] == NULL) { warn("strdup"); goto next_cmd; } } if (is_sec_text(&shdr)) type_table[i] = 'T'; else if (is_sec_data(&shdr)) { if (is_sec_readonly(&shdr)) type_table[i] = 'R'; else type_table[i] = 'D'; } else if (is_sec_nobits(&shdr)) type_table[i] = 'B'; else if (is_sec_debug(shname)) type_table[i] = 'N'; else if (is_sec_readonly(&shdr) && !is_sec_nobits(&shdr)) type_table[i] = 'n'; } print_header(filename, objname); if ((dynndx == SHN_UNDEF && nm_opts.print_symbol == PRINT_SYM_DYN) || (strndx == SHN_UNDEF && nm_opts.print_symbol == PRINT_SYM_SYM)) { warnx("%s: no symbols", OBJNAME); /* This is not an error case */ goto next_cmd; } STAILQ_INIT(&list_head); if (!nm_opts.debug_line) goto process_sym; /* * Collect dwarf line number information. */ if (dwarf_elf_init(elf, DW_DLC_READ, NULL, NULL, &dbg, &de) != DW_DLV_OK) { warnx("dwarf_elf_init failed: %s", dwarf_errmsg(de)); goto process_sym; } line_info = malloc(sizeof(struct line_info_head)); func_info = malloc(sizeof(struct func_info_head)); var_info = malloc(sizeof(struct var_info_head)); if (line_info == NULL || func_info == NULL || var_info == NULL) { warn("malloc"); (void) dwarf_finish(dbg, &de); goto process_sym; } SLIST_INIT(line_info); SLIST_INIT(func_info); SLIST_INIT(var_info); while ((ret = dwarf_next_cu_header(dbg, NULL, NULL, NULL, NULL, NULL, &de)) == DW_DLV_OK) { die = NULL; while (dwarf_siblingof(dbg, die, &die, &de) == DW_DLV_OK) { if (dwarf_tag(die, &tag, &de) != DW_DLV_OK) { warnx("dwarf_tag failed: %s", dwarf_errmsg(de)); continue; } /* XXX: What about DW_TAG_partial_unit? */ if (tag == DW_TAG_compile_unit) break; } if (die == NULL) { warnx("could not find DW_TAG_compile_unit die"); continue; } /* Retrieve source file list. */ ret = dwarf_srcfiles(die, &src_files, &filecount, &de); if (ret == DW_DLV_ERROR) warnx("dwarf_srclines: %s", dwarf_errmsg(de)); if (ret != DW_DLV_OK) continue; /* * Retrieve line number information from .debug_line section. */ ret = dwarf_srclines(die, &lbuf, &lcount, &de); if (ret == DW_DLV_ERROR) warnx("dwarf_srclines: %s", dwarf_errmsg(de)); if (ret != DW_DLV_OK) goto line_attr; for (i = 0; (Dwarf_Signed) i < lcount; i++) { if (dwarf_lineaddr(lbuf[i], &lineaddr, &de)) { warnx("dwarf_lineaddr: %s", dwarf_errmsg(de)); continue; } if (dwarf_lineno(lbuf[i], &lineno, &de)) { warnx("dwarf_lineno: %s", dwarf_errmsg(de)); continue; } if (dwarf_linesrc(lbuf[i], &sfile, &de)) { warnx("dwarf_linesrc: %s", dwarf_errmsg(de)); continue; } if ((lie = malloc(sizeof(*lie))) == NULL) { warn("malloc"); continue; } lie->addr = lineaddr; lie->line = lineno; lie->file = strdup(sfile); if (lie->file == NULL) { warn("strdup"); free(lie); continue; } SLIST_INSERT_HEAD(line_info, lie, entries); } line_attr: /* Retrieve line number information from DIEs. */ search_line_attr(dbg, func_info, var_info, die, src_files, filecount); } (void) dwarf_finish(dbg, &de); process_sym: p_data.list_num = get_sym(elf, &list_head, shnum, dynndx, strndx, type_table, (void *) sec_table, shnum); if (p_data.list_num == 0) goto next_cmd; p_data.headp = &list_head; p_data.sh_num = shnum; p_data.t_table = type_table; p_data.s_table = (void *) sec_table; p_data.filename = filename; p_data.objname = objname; sym_list_print(&p_data, func_info, var_info, line_info); next_cmd: if (nm_opts.debug_line) { if (func_info != NULL) { while (!SLIST_EMPTY(func_info)) { func = SLIST_FIRST(func_info); SLIST_REMOVE_HEAD(func_info, entries); free(func->file); free(func->name); free(func); } free(func_info); func_info = NULL; } if (var_info != NULL) { while (!SLIST_EMPTY(var_info)) { var = SLIST_FIRST(var_info); SLIST_REMOVE_HEAD(var_info, entries); free(var->file); free(var->name); free(var); } free(var_info); var_info = NULL; } if (line_info != NULL) { while (!SLIST_EMPTY(line_info)) { lie = SLIST_FIRST(line_info); SLIST_REMOVE_HEAD(line_info, entries); free(lie->file); free(lie); } free(line_info); line_info = NULL; } } if (sec_table != NULL) for (i = 0; i < shnum; ++i) free(sec_table[i]); free(sec_table); free(type_table); sym_list_dest(&list_head); return (rtn); #undef OBJNAME } static int read_object(const char *filename) { Elf *elf, *arf; Elf_Cmd elf_cmd; Elf_Kind kind; int fd, rtn, e_err; assert(filename != NULL && "filename is null"); if ((fd = open(filename, O_RDONLY)) == -1) { warn("'%s'", filename); return (1); } elf_cmd = ELF_C_READ; if ((arf = elf_begin(fd, elf_cmd, (Elf *) NULL)) == NULL) { if ((e_err = elf_errno()) != 0) warnx("elf_begin error: %s", elf_errmsg(e_err)); else warnx("elf_begin error"); close(fd); return (1); } assert(arf != NULL && "arf is null."); rtn = 0; if ((kind = elf_kind(arf)) == ELF_K_NONE) { warnx("%s: File format not recognized", filename); elf_end(arf); close(fd); return (1); } if (kind == ELF_K_AR) { if (nm_opts.print_name == PRINT_NAME_MULTI && nm_opts.elem_print_fn == sym_elem_print_all) printf("\n%s:\n", filename); if (nm_opts.print_armap == true) print_ar_index(fd, arf); } while ((elf = elf_begin(fd, elf_cmd, arf)) != NULL) { rtn |= read_elf(elf, filename, kind); /* * If file is not archive, elf_next return ELF_C_NULL and * stop the loop. */ elf_cmd = elf_next(elf); elf_end(elf); } elf_end(arf); close(fd); return (rtn); } static int read_files(int argc, char **argv) { int rtn = 0; if (argc < 0 || argv == NULL) return (1); if (argc == 0) rtn |= read_object(nm_info.def_filename); else { if (nm_opts.print_name == PRINT_NAME_NONE && argc > 1) nm_opts.print_name = PRINT_NAME_MULTI; while (argc > 0) { rtn |= read_object(*argv); --argc; ++argv; } } return (rtn); } static void print_lineno(struct sym_entry *ep, struct func_info_head *func_info, struct var_info_head *var_info, struct line_info_head *line_info) { struct func_info_entry *func; struct var_info_entry *var; struct line_info_entry *lie; /* For function symbol, search the function line information list. */ if ((ep->sym->st_info & 0xf) == STT_FUNC && func_info != NULL) { SLIST_FOREACH(func, func_info, entries) { if (!strcmp(ep->name, func->name) && ep->sym->st_value >= func->lowpc && ep->sym->st_value < func->highpc) { printf("\t%s:%" PRIu64, func->file, func->line); return; } } } /* For variable symbol, search the variable line information list. */ if ((ep->sym->st_info & 0xf) == STT_OBJECT && var_info != NULL) { SLIST_FOREACH(var, var_info, entries) { if (!strcmp(ep->name, var->name) && ep->sym->st_value == var->addr) { printf("\t%s:%" PRIu64, var->file, var->line); return; } } } /* Otherwise search line number information the .debug_line section. */ if (line_info != NULL) { SLIST_FOREACH(lie, line_info, entries) { if (ep->sym->st_value == lie->addr) { printf("\t%s:%" PRIu64, lie->file, lie->line); return; } } } } static void set_opt_value_print_fn(enum radix t) { switch (t) { case RADIX_OCT: nm_opts.value_print_fn = &sym_value_oct_print; nm_opts.size_print_fn = &sym_size_oct_print; break; case RADIX_DEC: nm_opts.value_print_fn = &sym_value_dec_print; nm_opts.size_print_fn = &sym_size_dec_print; break; case RADIX_HEX: default : nm_opts.value_print_fn = &sym_value_hex_print; nm_opts.size_print_fn = &sym_size_hex_print; } assert(nm_opts.value_print_fn != NULL && "nm_opts.value_print_fn is null"); } static void sym_elem_print_all(char type, const char *sec, const GElf_Sym *sym, const char *name) { if (sec == NULL || sym == NULL || name == NULL || nm_opts.value_print_fn == NULL) return; if (IS_UNDEF_SYM_TYPE(type)) { if (nm_opts.t == RADIX_HEX && nm_elfclass == ELFCLASS32) printf("%-8s", ""); else printf("%-16s", ""); } else { switch ((nm_opts.sort_fn == & cmp_size ? 2 : 0) + nm_opts.print_size) { case 3: if (sym->st_size != 0) { nm_opts.value_print_fn(sym); printf(" "); nm_opts.size_print_fn(sym); } break; case 2: if (sym->st_size != 0) nm_opts.size_print_fn(sym); break; case 1: nm_opts.value_print_fn(sym); if (sym->st_size != 0) { printf(" "); nm_opts.size_print_fn(sym); } break; case 0: default: nm_opts.value_print_fn(sym); } } printf(" %c ", type); PRINT_DEMANGLED_NAME("%s", name); } static void sym_elem_print_all_portable(char type, const char *sec, const GElf_Sym *sym, const char *name) { if (sec == NULL || sym == NULL || name == NULL || nm_opts.value_print_fn == NULL) return; PRINT_DEMANGLED_NAME("%s", name); printf(" %c ", type); if (!IS_UNDEF_SYM_TYPE(type)) { nm_opts.value_print_fn(sym); printf(" "); if (sym->st_size != 0) nm_opts.size_print_fn(sym); } else printf(" "); } static void sym_elem_print_all_sysv(char type, const char *sec, const GElf_Sym *sym, const char *name) { if (sec == NULL || sym == NULL || name == NULL || nm_opts.value_print_fn == NULL) return; PRINT_DEMANGLED_NAME("%-20s|", name); if (IS_UNDEF_SYM_TYPE(type)) printf(" "); else nm_opts.value_print_fn(sym); printf("| %c |", type); switch (sym->st_info & 0xf) { case STT_OBJECT: printf("%18s|", "OBJECT"); break; case STT_FUNC: printf("%18s|", "FUNC"); break; case STT_SECTION: printf("%18s|", "SECTION"); break; case STT_FILE: printf("%18s|", "FILE"); break; case STT_LOPROC: printf("%18s|", "LOPROC"); break; case STT_HIPROC: printf("%18s|", "HIPROC"); break; case STT_NOTYPE: default: printf("%18s|", "NOTYPE"); }; if (sym->st_size != 0) nm_opts.size_print_fn(sym); else printf(" "); printf("| |%s", sec); } static int sym_elem_def(char type, const GElf_Sym *sym, const char *name) { assert(IS_SYM_TYPE((unsigned char) type)); UNUSED(sym); UNUSED(name); return (!IS_UNDEF_SYM_TYPE((unsigned char) type)); } static int sym_elem_global(char type, const GElf_Sym *sym, const char *name) { assert(IS_SYM_TYPE((unsigned char) type)); UNUSED(sym); UNUSED(name); /* weak symbols resemble global. */ return (isupper((unsigned char) type) || type == 'w'); } static int sym_elem_global_static(char type, const GElf_Sym *sym, const char *name) { unsigned char info; assert(sym != NULL); UNUSED(type); UNUSED(name); info = sym->st_info >> 4; return (info == STB_LOCAL || info == STB_GLOBAL || info == STB_WEAK); } static int sym_elem_nondebug(char type, const GElf_Sym *sym, const char *name) { assert(sym != NULL); UNUSED(type); UNUSED(name); if (sym->st_value == 0 && (sym->st_info & 0xf) == STT_FILE) return (0); if (sym->st_name == 0) return (0); return (1); } static int sym_elem_nonzero_size(char type, const GElf_Sym *sym, const char *name) { assert(sym != NULL); UNUSED(type); UNUSED(name); return (sym->st_size > 0); } static int sym_elem_undef(char type, const GElf_Sym *sym, const char *name) { assert(IS_SYM_TYPE((unsigned char) type)); UNUSED(sym); UNUSED(name); return (IS_UNDEF_SYM_TYPE((unsigned char) type)); } static void sym_list_dest(struct sym_head *headp) { struct sym_entry *ep, *ep_n; if (headp == NULL) return; ep = STAILQ_FIRST(headp); while (ep != NULL) { ep_n = STAILQ_NEXT(ep, sym_entries); free(ep->sym); free(ep->name); free(ep); ep = ep_n; } } static int sym_list_insert(struct sym_head *headp, const char *name, const GElf_Sym *sym) { struct sym_entry *e; if (headp == NULL || name == NULL || sym == NULL) return (0); if ((e = malloc(sizeof(struct sym_entry))) == NULL) { warn("malloc"); return (0); } if ((e->name = strdup(name)) == NULL) { warn("strdup"); free(e); return (0); } if ((e->sym = malloc(sizeof(GElf_Sym))) == NULL) { warn("malloc"); free(e->name); free(e); return (0); } memcpy(e->sym, sym, sizeof(GElf_Sym)); /* Display size instead of value for common symbol. */ if (sym->st_shndx == SHN_COMMON) e->sym->st_value = sym->st_size; STAILQ_INSERT_TAIL(headp, e, sym_entries); return (1); } /* If file has not .debug_info, line_info will be NULL */ static void sym_list_print(struct sym_print_data *p, struct func_info_head *func_info, struct var_info_head *var_info, struct line_info_head *line_info) { struct sym_entry *e_v; size_t si; int i; if (p == NULL || CHECK_SYM_PRINT_DATA(p)) return; if ((e_v = sym_list_sort(p)) == NULL) return; if (nm_opts.sort_reverse == false) for (si = 0; si != p->list_num; ++si) sym_list_print_each(&e_v[si], p, func_info, var_info, line_info); else for (i = p->list_num - 1; i != -1; --i) sym_list_print_each(&e_v[i], p, func_info, var_info, line_info); free(e_v); } /* If file has not .debug_info, line_info will be NULL */ static void sym_list_print_each(struct sym_entry *ep, struct sym_print_data *p, struct func_info_head *func_info, struct var_info_head *var_info, struct line_info_head *line_info) { const char *sec; char type; if (ep == NULL || CHECK_SYM_PRINT_DATA(p)) return; assert(ep->name != NULL); assert(ep->sym != NULL); type = get_sym_type(ep->sym, p->t_table); if (nm_opts.print_name == PRINT_NAME_FULL) { printf("%s", p->filename); if (nm_opts.elem_print_fn == &sym_elem_print_all_portable) { if (p->objname != NULL) printf("[%s]", p->objname); printf(": "); } else { if (p->objname != NULL) printf(":%s", p->objname); printf(":"); } } switch (ep->sym->st_shndx) { case SHN_LOPROC: /* LOPROC or LORESERVE */ sec = "*LOPROC*"; break; case SHN_HIPROC: sec = "*HIPROC*"; break; case SHN_LOOS: sec = "*LOOS*"; break; case SHN_HIOS: sec = "*HIOS*"; break; case SHN_ABS: sec = "*ABS*"; break; case SHN_COMMON: sec = "*COM*"; break; case SHN_HIRESERVE: /* HIRESERVE or XINDEX */ sec = "*HIRESERVE*"; break; default: if (ep->sym->st_shndx > p->sh_num) return; sec = p->s_table[ep->sym->st_shndx]; break; }; nm_opts.elem_print_fn(type, sec, ep->sym, ep->name); if (nm_opts.debug_line == true && !IS_UNDEF_SYM_TYPE(type)) print_lineno(ep, func_info, var_info, line_info); printf("\n"); } static struct sym_entry * sym_list_sort(struct sym_print_data *p) { struct sym_entry *ep, *e_v; int idx; if (p == NULL || CHECK_SYM_PRINT_DATA(p)) return (NULL); if ((e_v = malloc(sizeof(struct sym_entry) * p->list_num)) == NULL) { warn("malloc"); return (NULL); } idx = 0; STAILQ_FOREACH(ep, p->headp, sym_entries) { if (ep->name != NULL && ep->sym != NULL) { e_v[idx].name = ep->name; e_v[idx].sym = ep->sym; ++idx; } } assert((size_t)idx == p->list_num); if (nm_opts.sort_fn != &cmp_none) { nm_print_data = p; assert(nm_print_data != NULL); qsort(e_v, p->list_num, sizeof(struct sym_entry), nm_opts.sort_fn); } return (e_v); } static void sym_size_oct_print(const GElf_Sym *sym) { assert(sym != NULL && "sym is null"); printf("%016" PRIo64, sym->st_size); } static void sym_size_hex_print(const GElf_Sym *sym) { assert(sym != NULL && "sym is null"); if (nm_elfclass == ELFCLASS32) printf("%08" PRIx64, sym->st_size); else printf("%016" PRIx64, sym->st_size); } static void sym_size_dec_print(const GElf_Sym *sym) { assert(sym != NULL && "sym is null"); printf("%016" PRId64, sym->st_size); } static void sym_value_oct_print(const GElf_Sym *sym) { assert(sym != NULL && "sym is null"); printf("%016" PRIo64, sym->st_value); } static void sym_value_hex_print(const GElf_Sym *sym) { assert(sym != NULL && "sym is null"); if (nm_elfclass == ELFCLASS32) printf("%08" PRIx64, sym->st_value); else printf("%016" PRIx64, sym->st_value); } static void sym_value_dec_print(const GElf_Sym *sym) { assert(sym != NULL && "sym is null"); printf("%016" PRId64, sym->st_value); } static void usage(int exitcode) { printf("Usage: %s [options] file ...\ \n Display symbolic information in file.\n\ \n Options: \ \n -A, --print-file-name Write the full pathname or library name of an\ \n object on each line.\ \n -a, --debug-syms Display all symbols include debugger-only\ \n symbols.", nm_info.name); printf("\ \n -B Equivalent to specifying \"--format=bsd\".\ \n -C, --demangle[=style] Decode low-level symbol names.\ \n --no-demangle Do not demangle low-level symbol names.\ \n -D, --dynamic Display only dynamic symbols.\ \n -e Display only global and static symbols."); printf("\ \n -f Produce full output (default).\ \n --format=format Display output in specific format. Allowed\ \n formats are: \"bsd\", \"posix\" and \"sysv\".\ \n -g Display only global symbol information.\ \n -h, --help Show this help message.\ \n -l, --line-numbers Display filename and linenumber using\ \n debugging information.\ \n -n, --numeric-sort Sort symbols numerically by value."); printf("\ \n -o Write numeric values in octal. Equivalent to\ \n specifying \"-t o\".\ \n -p, --no-sort Do not sort symbols.\ \n -P Write information in a portable output format.\ \n Equivalent to specifying \"--format=posix\".\ \n -r, --reverse-sort Reverse the order of the sort.\ \n -S, --print-size Print symbol sizes instead values.\ \n -s, --print-armap Include an index of archive members.\ \n --size-sort Sort symbols by size."); printf("\ \n -t, --radix=format Write each numeric value in the specified\ \n format:\ \n d In decimal,\ \n o In octal,\ \n x In hexadecimal."); printf("\ \n -u, --undefined-only Display only undefined symbols.\ \n --defined-only Display only defined symbols.\ \n -V, --version Show the version identifier for %s.\ \n -v Sort output by value.\ \n -x Write numeric values in hexadecimal.\ \n Equivalent to specifying \"-t x\".", nm_info.name); printf("\n\ \n The default options are: output in bsd format, use a hexadecimal radix,\ \n sort by symbol name, do not demangle names.\n"); exit(exitcode); } /* * Display symbolic information in file. * Return 0 at success, >0 at failed. */ int main(int argc, char **argv) { int rtn; global_init(); get_opt(argc, argv); rtn = read_files(argc - optind, argv + optind); global_dest(); exit(rtn); } Index: head/contrib/elftoolchain/strings/strings.c =================================================================== --- head/contrib/elftoolchain/strings/strings.c (revision 275368) +++ head/contrib/elftoolchain/strings/strings.c (revision 275369) @@ -1,454 +1,454 @@ /*- * Copyright (c) 2007 S.Sam Arun Raj * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "_elftc.h" ELFTC_VCSID("$Id: strings.c 2351 2011-12-19 11:20:37Z jkoshy $"); enum return_code { RETURN_OK, RETURN_NOINPUT, RETURN_SOFTWARE }; enum radix_style { RADIX_DECIMAL, RADIX_HEX, RADIX_OCTAL }; enum encoding_style { ENCODING_7BIT, ENCODING_8BIT, ENCODING_16BIT_BIG, ENCODING_16BIT_LITTLE, ENCODING_32BIT_BIG, ENCODING_32BIT_LITTLE }; #define PRINTABLE(c) \ ((c) >= 0 && (c) <= 255 && \ ((c) == '\t' || isprint((c)) || \ (encoding == ENCODING_8BIT && (c) > 127))) -int encoding_size, entire_file, min_len, show_filename, show_loc; -enum encoding_style encoding; -enum radix_style radix; +static int encoding_size, entire_file, min_len, show_filename, show_loc; +static enum encoding_style encoding; +static enum radix_style radix; static struct option strings_longopts[] = { { "all", no_argument, NULL, 'a'}, { "bytes", required_argument, NULL, 'n'}, { "encoding", required_argument, NULL, 'e'}, { "help", no_argument, NULL, 'h'}, { "print-file-name", no_argument, NULL, 'f'}, { "radix", required_argument, NULL, 't'}, { "version", no_argument, NULL, 'v'}, { NULL, 0, NULL, 0 } }; long getcharacter(void); int handle_file(const char *); int handle_elf(const char *, int); int handle_binary(const char *, int); int find_strings(const char *, off_t, off_t); void show_version(void); void usage(void); /* * strings(1) extracts text(contiguous printable characters) * from elf and binary files. */ int main(int argc, char **argv) { int ch, rc; rc = RETURN_OK; min_len = 0; encoding_size = 1; if (elf_version(EV_CURRENT) == EV_NONE) errx(EXIT_FAILURE, "ELF library initialization failed: %s", elf_errmsg(-1)); while ((ch = getopt_long(argc, argv, "1234567890ae:fhn:ot:Vv", strings_longopts, NULL)) != -1) switch((char)ch) { case 'a': entire_file = 1; break; case 'e': if (*optarg == 's') { encoding = ENCODING_7BIT; } else if (*optarg == 'S') { encoding = ENCODING_8BIT; } else if (*optarg == 'b') { encoding = ENCODING_16BIT_BIG; encoding_size = 2; } else if (*optarg == 'B') { encoding = ENCODING_32BIT_BIG; encoding_size = 4; } else if (*optarg == 'l') { encoding = ENCODING_16BIT_LITTLE; encoding_size = 2; } else if (*optarg == 'L') { encoding = ENCODING_32BIT_LITTLE; encoding_size = 4; } else usage(); /* NOTREACHED */ break; case 'f': show_filename = 1; break; case 'n': min_len = (int)strtoimax(optarg, (char**)NULL, 10); break; case 'o': show_loc = 1; radix = RADIX_OCTAL; break; case 't': show_loc = 1; if (*optarg == 'd') radix = RADIX_DECIMAL; else if (*optarg == 'o') radix = RADIX_OCTAL; else if (*optarg == 'x') radix = RADIX_HEX; else usage(); /* NOTREACHED */ break; case 'v': case 'V': show_version(); /* NOTREACHED */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': min_len *= 10; min_len += ch - '0'; break; case 'h': case '?': default: usage(); /* NOTREACHED */ } argc -= optind; argv += optind; if (!min_len) min_len = 4; if (!*argv) rc = handle_file("{standard input}"); else while (*argv) { rc = handle_file(*argv); argv++; } return (rc); } int handle_file(const char *name) { int fd, rt; if (name == NULL) return (RETURN_NOINPUT); if (strcmp("{standard input}", name) != 0) { if (freopen(name, "rb", stdin) == NULL) { warnx("'%s': %s", name, strerror(errno)); return (RETURN_NOINPUT); } } else { return (find_strings(name, (off_t)0, (off_t)0)); } fd = fileno(stdin); if (fd < 0) return (RETURN_NOINPUT); rt = handle_elf(name, fd); return (rt); } /* * Files not understood by handle_elf, will be passed off here and will * treated as a binary file. This would include text file, core dumps ... */ int handle_binary(const char *name, int fd) { struct stat buf; memset(&buf, 0, sizeof(struct stat)); (void) lseek(fd, (off_t)0, SEEK_SET); if (!fstat(fd, &buf)) return (find_strings(name, (off_t)0, buf.st_size)); return (RETURN_SOFTWARE); } /* * Will analyse a file to see if it ELF, other files including ar(1), * core dumps are passed off and treated as flat binary files. Unlike * GNU size in FreeBSD this routine will not treat ELF object from * different archs as flat binary files(has to overridden using -a). */ int handle_elf(const char *name, int fd) { GElf_Ehdr elfhdr; GElf_Shdr shdr; Elf *elf; Elf_Scn *scn; int rc; rc = RETURN_OK; /* If entire file is choosen, treat it as a binary file */ if (entire_file) return (handle_binary(name, fd)); (void) lseek(fd, (off_t)0, SEEK_SET); elf = elf_begin(fd, ELF_C_READ, NULL); if (elf_kind(elf) != ELF_K_ELF) { (void) elf_end(elf); return (handle_binary(name, fd)); } if (gelf_getehdr(elf, &elfhdr) == NULL) { (void) elf_end(elf); warnx("%s: ELF file could not be processed", name); return (RETURN_SOFTWARE); } if (elfhdr.e_shnum == 0 && elfhdr.e_type == ET_CORE) { (void) elf_end(elf); return (handle_binary(name, fd)); } else { scn = NULL; while ((scn = elf_nextscn(elf, scn)) != NULL) { if (gelf_getshdr(scn, &shdr) == NULL) continue; if (shdr.sh_type != SHT_NOBITS && (shdr.sh_flags & SHF_ALLOC) != 0) { rc = find_strings(name, shdr.sh_offset, shdr.sh_size); } } } (void) elf_end(elf); return (rc); } /* * Retrieves a character from input stream based on the encoding * type requested. */ long getcharacter(void) { long rt; int i; char buf[4], c; rt = EOF; for(i = 0; i < encoding_size; i++) { c = getc(stdin); if (feof(stdin)) return (EOF); buf[i] = c; } switch(encoding) { case ENCODING_7BIT: case ENCODING_8BIT: rt = buf[0]; break; case ENCODING_16BIT_BIG: rt = (buf[0] << 8) | buf[1]; break; case ENCODING_16BIT_LITTLE: rt = buf[0] | (buf[1] << 8); break; case ENCODING_32BIT_BIG: rt = ((long) buf[0] << 24) | ((long) buf[1] << 16) | ((long) buf[2] << 8) | buf[3]; break; case ENCODING_32BIT_LITTLE: rt = buf[0] | ((long) buf[1] << 8) | ((long) buf[2] << 16) | ((long) buf[3] << 24); break; } return (rt); } /* * Input stream stdin is read until the end of file is reached or until * the section size is reached in case of ELF files. Contiguous * characters of >= min_size(default 4) will be displayed. */ int find_strings(const char *name, off_t offset, off_t size) { off_t cur_off, start_off; char *obuf; long c; int i; if ((obuf = (char*)calloc(1, min_len + 1)) == NULL) { (void) fprintf(stderr, "Unable to allocate memory: %s\n", strerror(errno)); return (RETURN_SOFTWARE); } (void) fseeko(stdin, offset, SEEK_SET); cur_off = offset; start_off = 0; while(1) { if ((offset + size) && (cur_off >= offset + size)) break; start_off = cur_off; memset(obuf, 0, min_len+1); for(i = 0; i < min_len; i++) { c = getcharacter(); if (c == EOF && feof(stdin)) goto _exit1; if (PRINTABLE(c)) { obuf[i] = c; obuf[i+1] = 0; cur_off += encoding_size; } else { if (encoding == ENCODING_8BIT && (uint8_t)c > 127) { obuf[i] = c; obuf[i+1] = 0; cur_off += encoding_size; continue; } cur_off += encoding_size; break; } } if (i >= min_len && ((cur_off <= offset + size) || !(offset + size))) { if (show_filename) printf ("%s: ", name); if (show_loc) { switch(radix) { case RADIX_DECIMAL: (void) printf("%7ju ", (uintmax_t)start_off); break; case RADIX_HEX: (void) printf("%7jx ", (uintmax_t)start_off); break; case RADIX_OCTAL: (void) printf("%7jo ", (uintmax_t)start_off); break; } } printf("%s", obuf); while(1) { if ((offset + size) && (cur_off >= offset + size)) break; c = getcharacter(); cur_off += encoding_size; if (encoding == ENCODING_8BIT && (uint8_t)c > 127) { putchar(c); continue; } if (!PRINTABLE(c) || c == EOF) break; putchar(c); } putchar('\n'); } } _exit1: free(obuf); return (RETURN_OK); } #define USAGE_MESSAGE "\ Usage: %s [options] [file...]\n\ Print contiguous sequences of printable characters.\n\n\ Options:\n\ -a | --all Scan the entire file for strings.\n\ -e ENC | --encoding=ENC Select the character encoding to use.\n\ -f | --print-file-name Print the file name before each string.\n\ -h | --help Print a help message and exit.\n\ -n N | --bytes=N | -N Print sequences with 'N' or more characters.\n\ -o Print offsets in octal.\n\ -t R | --radix=R Print offsets using the radix named by 'R'.\n\ -v | --version Print a version identifier and exit.\n" void usage(void) { (void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME()); exit(EXIT_FAILURE); } void show_version(void) { (void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version()); exit(EXIT_SUCCESS); }