int pmelf_getsym32(Elf_Stream s, Elf32_Sym *psym) { if ( 1 != SREAD(psym, sizeof(*psym), 1, s) ) { return -1; } if ( s->needswap ) { #ifdef PMELF_CONFIG_NO_SWAPSUPPORT return -2; #else elf_swap32( &psym->st_name); elf_swap32( &psym->st_value); elf_swap32( &psym->st_size); elf_swap16( &psym->st_shndx); #endif } #ifdef PARANOIA_ON { int x; if ( (x = ELF32_ST_TYPE( psym->st_info )) > STT_MAXSUP ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_getsym - paranoia: unsupported type %i\n", x); return -1; } if ( (x = ELF32_ST_BIND( psym->st_info )) > STB_MAXSUP ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_getsym - paranoia: unsupported binding %i\n", x); return -1; } } #endif return 0; }
int pmelf_putehdr32(Elf_Stream s, Elf32_Ehdr *pehdr) { uint8_t magic[4] = { ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3 }; #ifndef PMELF_CONFIG_NO_SWAPSUPPORT Elf32_Ehdr nehdr; #endif if ( memcmp(magic, pehdr->e_ident+EI_MAG0, sizeof(magic)) ) { PMELF_PRINTF(pmelf_err, PMELF_PRE"error: not an ELF file\n"); return -1; } if ( pehdr->e_ident[EI_CLASS] != ELFCLASS32 ) { PMELF_PRINTF(pmelf_err, PMELF_PRE"error: not an 32-bit ELF file\n"); return -1; } if ( pehdr->e_ident[EI_VERSION] != EV_CURRENT ) { PMELF_PRINTF(pmelf_err, PMELF_PRE"error: not a version %i ELF file\n", EV_CURRENT); return -1; } s->needswap = iamlsb() ^ (pehdr->e_ident[EI_DATA] == ELFDATA2LSB ? 1 : 0); #ifdef PMELF_CONFIG_NO_SWAPSUPPORT if ( s->needswap ) { PMELF_PRINTF(pmelf_err, PMELF_PRE"error: host/target byte order mismatch but pmelf was configured w/o support for byte-swapping\n"); return -2; } #else if ( s->needswap ) { nehdr = *pehdr; pehdr = &nehdr; elf_swap16( &pehdr->e_type ); elf_swap16( &pehdr->e_machine ); elf_swap32( &pehdr->e_version ); elf_swap32( &pehdr->e_entry ); elf_swap32( &pehdr->e_phoff ); elf_swap32( &pehdr->e_shoff ); elf_swap32( &pehdr->e_flags ); elf_swap16( &pehdr->e_ehsize ); elf_swap16( &pehdr->e_phentsize ); elf_swap16( &pehdr->e_phnum ); elf_swap16( &pehdr->e_shentsize ); elf_swap16( &pehdr->e_shnum ); elf_swap16( &pehdr->e_shstrndx ); } #endif return s->write && 1 == SWRITE(pehdr, sizeof(*pehdr), 1, s) ? 0 : -1; }
/* * Scan attribute sets 'pa' and 'pb' for common vendors and * call their file_attributes_match() methods to find incompatibilities. * * If the vendor sets in 'pa' and 'pb' do not match or if only one set * exists (the other being NULL) this is treated as a mismatch. * * RETURNS: zero if no incompatibility is found, a value < 0 otherwise. * * NOTE: it is legal to pass 'pa==NULL', 'pb==NULL' in which case * the routine returns 0 (SUCCESS). */ int pmelf_match_attribute_set(Pmelf_attribute_set *pa, Pmelf_attribute_set *pb) { int i,j; Pmelf_attribute_vendor *pv; int vendor_set_a, vendor_set_b; int rval = 0; if ( !pa && !pb ) return 0; if ( ! (pa && pb) ) return -1; if ( (vendor_set_a = vset(pa)) < 0 ) { return -1; } if ( (vendor_set_b = vset(pb)) < 0 ) { return -1; } if ( vendor_set_a != vendor_set_b ) { PMELF_PRINTF(pmelf_err, PMELF_PRE"pmelf_match_attribute_set(): vendor set mismatch (objects %s, %s)\n", pa->obj_name, pb->obj_name); return -2; } for ( i=0; i<ATTR_MAX_VENDORS; i++ ) { if ( pa->attributes[i].file_attributes ) { pv = pa->attributes[i].file_attributes->pv; for ( j=0; j<ATTR_MAX_VENDORS; j++ ) { if ( pb->attributes[j].file_attributes ) { if ( pv == pb->attributes[j].file_attributes->pv ) { if ( ! pv->file_attributes_match ) { PMELF_PRINTF(pmelf_err, PMELF_PRE"pmelf_match_attribute_set(): vendor %s has no 'match' handler\n", pv->name); return -1; } rval |= pv->file_attributes_match( pa->attributes[i].file_attributes, pb->attributes[j].file_attributes ); } } } } } return rval; }
/* * Read attribute set from ELF stream (see header file for more information). */ Pmelf_attribute_set * pmelf_create_attribute_set(Elf_Stream s, Elf_Shdr *psect) { Pmelf_attribute_set *rval = 0; uint8_t *b = 0; Pmelf_Off sh_size; /* Hack; accessing sh_type as ELF32 should work for both, ELF32 and ELF64 */ if ( psect->s32.sh_type != SHT_GNU_ATTRIBUTES ) { PMELF_PRINTF(pmelf_err, PMELF_PRE"pmelf_create_attribute_set(): not a .gnu_attributes section\n"); return 0; } if ( ! (b = pmelf_getscn(s, psect, 0, 0, 0)) ) goto cleanup; switch ( s->clss ) { #ifdef PMELF_CONFIG_ELF64SUPPORT case ELFCLASS64: sh_size = psect->s64.sh_size; break; #endif case ELFCLASS32: sh_size = psect->s32.sh_size; break; default: PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_create_attribute_set(): unsupported ELF class (64?)\n"); goto cleanup; } rval = pmelf_read_attribute_set(b, sh_size, s->needswap, s->name); if ( !rval ) goto cleanup; rval->scn_data = b; b = 0; cleanup: free( b ); return rval; }
/* * Compute bitmask of all vendors present in an attribute set 'pa' * * E.g., if 'pa' contains attributes associated with vendors "gnu" * and "foo" and the vendors "gnu", "boo" and "foo" are * registered/known then 'vset(pa)' returns 0x5 since it * contains vendors #0 ("gnu") and #2 ("foo"). */ static int vset(Pmelf_attribute_set *pa) { int i,j; unsigned rval = 0; for ( i=0; i<ATTR_MAX_VENDORS; i++ ) { if ( pa->attributes[i].file_attributes ) { j = vidx(pa->attributes[i].file_attributes->pv); if ( j < 0 ) { PMELF_PRINTF(pmelf_err, PMELF_PRE"pmelf_match_attribute_set(): invalid vendor index ?? (object %s)\n", pa->obj_name); return -1; } rval |= (1<<j); } } return rval; }
/* * Read attribute set from memory buffer (see header file for more information). */ Pmelf_attribute_set * pmelf_read_attribute_set(const uint8_t *b, unsigned bsize, int needswap, const char *obj_name) { Pmelf_attribute_set *rval = 0; Pmelf_attribute_vendor *pv; Pmelf_Off n,nbeg; int l; uint8_t v; const char *vendor_name; Pmelf_attribute_tbl *patbl; Elf32_Word len,tag,sublen; if ( ! known_vendors ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_read_attribute_set(): no vendors registered\n"); /* No vendors registered; save us the trouble */ return 0; } if ( ! (rval = calloc(sizeof(*rval),1)) ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_read_attribute_set(): no memory\n"); return 0; } if ( ! (rval->obj_name = strdup(obj_name)) ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_read_attribute_set(): no memory (for obj_name)\n"); goto cleanup; } n = 0; while ( n < bsize ) { nbeg = n; if ( FORMAT_VERSION_A != (v = b[n++]) ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_read_attribute_set(): unknown attribute format version %i\n", v); goto cleanup; } if ( pmelf_getword(needswap, b+n, bsize-n, &len, "subsection") ) goto cleanup; n += sizeof(len); if ( nbeg + len + 1 > bsize ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_read_attribute_set(): vendor subsection length > section length\n"); goto cleanup; } for ( l = n; b[l]; ) { if ( ++l >= bsize ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_read_attribute_set(): reading beyond section (getting vendor name)\n"); goto cleanup; } } vendor_name = (const char*)(b + n); n = l+1; if ( (l = pmelf_guleb128(b+n, &tag, bsize - n)) < 0 ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_read_attribute_size() reading beyond subsubsection (getting tag)\n"); goto cleanup; } n += l; if ( pmelf_getword(needswap, b + n, bsize - n, &sublen, "subsubsection") ) goto cleanup; n += sizeof(sublen); /* subsubsection length includes byte count for tag and length itself */ sublen -= sizeof(sublen) + l; switch ( tag ) { case Tag_File: for ( pv = known_vendors; pv && strcmp(pv->name, vendor_name); ) pv = pv->next; if ( !pv ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_read_attribute_set(): no handlers for vendor '%s' found\n", vendor_name); goto cleanup; } for ( patbl=0, l=0; l<ATTR_MAX_VENDORS; l++ ) { if ( ! rval->attributes[l].file_attributes || rval->attributes[l].file_attributes->pv == pv ) { /* setting a covers the case where we use a new empty slot */ patbl = rval->attributes[l].file_attributes; break; } } if ( ! patbl ) { if ( l >= ATTR_MAX_VENDORS ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_read_attribute_set(): not enough vendor slots\n"); goto cleanup; } /* create attribute table */ if ( !(patbl = calloc(sizeof(*patbl) + sizeof(patbl->map[0])*(pv->max_tag+1), 1)) ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_read_attribute_set(): no memory\n"); goto cleanup; } patbl->vals = 0; patbl->avail = 0; patbl->idx = 1; /* first entry is empty/unused */ patbl->pv = pv; patbl->aset = rval; rval->attributes[l].file_attributes = patbl; } /* read attributes into table */ if ( pv->file_attributes_read(patbl, b+n, sublen) ) goto cleanup; break; case Tag_Section: PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_read_attribute_set(): Tag_Section no yet supported\n"); goto cleanup; case Tag_Symbol: PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_read_attribute_set(): Tag_Symbol no yet supported\n"); goto cleanup; default: PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_read_attribute_set(): unsupported tag %"PRIi32"\n", tag); goto cleanup; } n = nbeg + len + 1; } return rval; cleanup: pmelf_destroy_attribute_set(rval); return 0; }
/* * Decode attributes from byte stream in memory (public format; see header for * more detailed description. */ int pmelf_pub_file_attributes_read(Pmelf_attribute_tbl *patbl, const uint8_t *buf, unsigned size) { Elf32_Word tag; int l,n; Pmelf_pub_attribute_t tagt; Elf32_Word ival; const char * sval; Pmelf_attribute_list *el,**pn,**pp; unsigned csz; if ( !size ) return 0; if ( ! patbl ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_pub_file_attributes_read(): no attribute table (internal error)\n"); return -1; } while ( size ) { if ( (n = pmelf_guleb128(buf, &tag, size)) < 0 ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_pub_file_attributes_read() reading beyond subsubsection (getting tag)\n"); goto cleanup; } buf += n; size -= n; if ( patbl->avail == 0 && (tag <= patbl->pv->max_tag && Tag_Compat != tag) ) { csz = CHUNKSZ < patbl->pv->max_tag ? CHUNKSZ : patbl->pv->max_tag; if ( !(patbl->vals = realloc(patbl->vals, sizeof(*patbl->vals)*(patbl->idx + csz))) ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_pub_file_attributes_read(): no memory\n"); goto cleanup; } patbl->avail += csz; } ival = 0; sval = 0; /* FIXME: we cannot have multiple identical tags in the array (but could in the linked list part) */ if ( (tag <= patbl->pv->max_tag && Tag_Compat != tag) && patbl->map[tag] ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_pub_file_attributes_read() tag %"PRIi32" already defined\n", tag); goto cleanup; } if ( Tag_Compat == tag ) { tagt = Pmelf_Attribute_Type_both; } else if ( ! patbl->pv->file_attributes_tag_type ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_pub_file_attributes_read() vendor %s has no type handler!\n", patbl->pv->name ); tagt = Pmelf_Attribute_Type_unknown; } else { tagt = patbl->pv->file_attributes_tag_type(patbl, tag); } switch ( tagt ) { default: PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_pub_file_attributes_read() unknown tag %"PRIi32"\n", tag ); goto cleanup; case Pmelf_Attribute_Type_none: continue; case Pmelf_Attribute_Type_num: case Pmelf_Attribute_Type_both: if ( (n = pmelf_guleb128(buf, &ival, size)) < 0 ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_pub_file_attributes_read() reading beyond subsubsection (getting (i)value)\n"); goto cleanup; } buf += n; size -= n; if ( Pmelf_Attribute_Type_num == tagt ) break; /* else fall thru */ case Pmelf_Attribute_Type_str: for ( l=0; buf[l++]; ) { if ( l >= size ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_pub_file_attributes_read() reading beyond subsubsection (getting (s)value)\n"); goto cleanup; } } sval = (const char*)buf; buf += l; size -= l; break; } /* Tag_Compat always go to the list */ if ( tag > patbl->pv->max_tag || Tag_Compat == tag ) { if ( ! (el = calloc( sizeof(*el), 1 ) ) ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_pub_file_attributes_read() no memory for attribute list element\n"); goto cleanup; } /* append to possibly existing instances of 'tag' */ for ( pp = &patbl->lst; *pp; pp = &(*pp)->next ) { if ( (*pp)->att.tag == tag ) { for ( pn = &(*pp)->next; *pn && (*pn)->att.tag == tag; pp = pn) ; break; } } el->next = *pp; *pp = el; el->att.tag = tag; el->att.val.pub.i = ival; el->att.val.pub.s = sval; } else { patbl->map[tag] = patbl->idx; patbl->vals[patbl->idx].pub.i = ival; patbl->vals[patbl->idx].pub.s = sval; patbl->idx++; } } return 0; cleanup: return -1; }
static Pmelf_Long _pmelf_find_symhdrs(Elf_Stream s, Pmelf_Shtab shtab, Elf_Shdr **psymsh, Elf_Shdr **pstrsh, int dynamic) { Elf_Shdr *shdr; uint32_t i; Elf_Shdr *symsh = 0; Elf_Shdr *strsh = 0; const char *name; uint8_t *p; uint32_t shdrsz = get_shdrsz(shtab); uint32_t symsz; Pmelf_Size sh_size, sh_entsize; Pmelf_Off sh_offset; uint32_t type, destype; const char *desname; #ifndef USE_SYM_SH_LINK const char *desstrn; #endif if ( dynamic ) { destype = SHT_DYNSYM; desname = ".dynsym"; #ifndef USE_SYM_SH_LINK desstrn = ".dynstr"; #endif } else { destype = SHT_SYMTAB; desname = ".symtab"; #ifndef USE_SYM_SH_LINK desstrn = ".strtab"; #endif } destype = dynamic ? SHT_DYNSYM : SHT_SYMTAB; for ( i = 0, p = shtab->shdrs.p_raw; i<shtab->nshdrs; i++, p+=shdrsz ) { shdr = (Elf_Shdr*)p; type = get_sh_type(shtab->clss, shdr); if ( type == destype ) { if ( ! (name = pmelf_sec_name(shtab, shdr)) ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_find_symhdrs: symtab section name out of bounds\n"); return -1; } if ( !strcmp(desname, name) ) { if ( symsh ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_find_symhdrs: multiple symtabs\n"); return -1; } symsh = shdr; #ifdef USE_SYM_SH_LINK strsh = get_shtabN(shtab,get_sh_link(shtab->clss, symsh)); #endif } } #ifndef USE_SYM_SH_LINK else if ( SHT_STRTAB == type ) { if ( ! (name = pmelf_sec_name(shtab, shdr)) ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_find_symhdrs: strtab section name out of bounds\n"); return -1; } if ( !strcmp(desstrn, name) ) { if ( strsh ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_find_symhdrs: multiple strtabs\n"); return -1; } strsh = shdr; } } #endif } if ( !symsh ) { #if 0 /* let the caller barf it they think it is appropriate */ PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_find_symhdrs: no symtab found\n"); #endif return -1; } #ifdef PMELF_CONFIG_ELF64SUPPORT if ( ELFCLASS64 == shtab->clss ) { sh_size = symsh->s64.sh_size; sh_entsize = symsh->s64.sh_entsize; sh_offset = symsh->s64.sh_offset; symsz = sizeof(Elf64_Sym); } else #endif { sh_size = symsh->s32.sh_size; sh_entsize = symsh->s32.sh_entsize; sh_offset = symsh->s32.sh_offset; symsz = sizeof(Elf32_Sym); } if ( !strsh ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_find_symhdrs: no strtab found\n"); return -1; } if ( 0 == sh_size ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_find_symhdrs: zero size symtab\n"); return -1; } if ( sh_entsize && sh_entsize != symsz ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_find_symhdrs: symbol size mismatch (sh_entsize %lu\n", (unsigned long)sh_entsize); return -1; } if ( (sh_size % symsz) != 0 ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_find_symhdrs: sh_size of symtab not a multiple of sizeof(Elf32_Sym)\n"); return -1; } if ( pmelf_seek(s, sh_offset) ) { PMELF_PRINTF( pmelf_err, PMELF_PRE"pmelf_find_symhdrs unable to seek to symtab %s\n", strerror(errno)); return -1; } if ( psymsh ) *psymsh = symsh; if ( pstrsh ) *pstrsh = strsh; return sh_size/symsz; }