/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1988 AT&T */ /* All Rights Reserved */ #pragma ident "@(#)clscook.c 1.11 08/05/31 SMI" /* * This stuff used to live in cook.c, but was moved out to * facilitate dual (Elf32 and Elf64) compilation. See block * comment in cook.c for more info. */ #include #include #include #include #include "decl.h" #include "member.h" #include "msg.h" #include #include #include #include #include static struct xSectName { const char *MachoName; const char *PrimaryElfName; const char *SecondaryElfName; } xTab[] = { {SECT_TEXT, ".text", "non-SEG_TEXT .text" }, {SECT_DATA, ".data", "non-SEG_DATA .data" }, {SECT_BSS, ".bss", "non-SEG_DATA .bss" }, {SECT_CTF, ".SUNW_ctf", 0}, {"__symbol_table", ".symtab", 0 }, {"__debug_info", ".debug_info", 0 }, {"__debug_abbrev", ".debug_abbrev", 0 }, {"__debug_aranges", ".debug_aranges", 0 }, {"__debug_ranges", ".debug_ranges", 0 }, {"__debug_line", ".debug_line", 0 }, {"__debug_frame", ".debug_frame", 0 }, {"__debug_loc", ".debug_loc", 0 }, {"__debug_pubnames", ".debug_pubnames", 0 }, {"__debug_str", ".debug_str", 0 }, {"__debug_funcnames", ".debug_funcnames", 0 }, {"__debug_typenames", ".debug_typenames", 0 }, {"__debug_pubtypes", ".debug_pubtypes", 0 }, {"__debug_varnames", ".debug_varnames", 0 }, {"__debug_weaknames", ".debug_weaknames", 0 }, {"__debug_macinfo", ".debug_macinfo", 0 }, {"__eh_frame", ".eh_frame", 0 }, {"__dir_str_table", ".dir_str_table", 0 } /* Directly addressed Mach-o string table. */ }; #define SIZE_XTAB (sizeof(xTab)/sizeof(xTab[0])) #define LOADER_H_NAME_LENGTH 16 static int elf_macho_str_cookie(const char *name, int primary) { int j; int ret = SIZE_XTAB << 1; for (j = 0; j < SIZE_XTAB && name; j++) { if (0 == strncmp(name, xTab[j].MachoName, LOADER_H_NAME_LENGTH)) { if (!primary && xTab[j].SecondaryElfName) ret = (j << 1) + 1; else ret = j << 1; break; } } return ret; } #if defined(_ELF64) /* Want just one definition, so compile only under -D_ELF64 */ const char *elf_macho_str_off(size_t off) { if (off >= (SIZE_XTAB << 1)) return ".unknown mach-o"; else if (off & 1) return xTab[off>>1].SecondaryElfName; else return xTab[off>>1].PrimaryElfName; } #endif static Elf32_Word STTSect(const char *sectname) { if (0 == strcmp(sectname, SECT_TEXT)) return STT_FUNC; else if (0 == strcmp(sectname, SECT_DATA)) return STT_OBJECT; else if (0 == strcmp(sectname, SECT_BSS)) return STT_OBJECT; else return STT_OBJECT; } /* * This module is compiled twice, the second time having * -D_ELF64 defined. The following set of macros, along * with machelf.h, represent the differences between the * two compilations. Be careful *not* to add any class- * dependent code (anything that has elf32 or elf64 in the * name) to this code without hiding it behind a switch- * able macro like these. */ #if defined(_ELF64) #define Snode Snode64 #define ELFCLASS ELFCLASS64 #define ElfField Elf64 #define _elf_snode_init _elf64_snode_init #define _elf_prepscan _elf64_prepscan #define _elf_cookscn _elf64_cookscn #define _elf_mtype _elf64_mtype #define _elf_msize _elf64_msize #define elf_fsize elf64_fsize #define _elf_snode _elf64_snode #define _elf_ehdr _elf64_ehdr #define elf_xlatetom elf64_xlatetom #define _elf_phdr _elf64_phdr #define _elf_shdr _elf64_shdr #define _elf_prepscn _elf64_prepscn #define _mach_header mach_header_64 #define _MH_CIGAM MH_CIGAM_64 #define _swap_mh __swap_mach_header_64 #define _elf_word Elf64_Word #define _elf_addr Elf64_Addr #define _elf_off Elf64_Off #define _SHN_MACHO SHN_MACHO_64 #define _nlist nlist_64 #define _addralign 8 #define _SWAPVAL SWAP64 #define _LC_SEGMENT LC_SEGMENT_64 #define _segcmd segment_command_64 #define _sect section_64 #define _swapsegcmd __swap_segment_command_64 #define _swapsect __swap_section_64 #else /* Elf32 */ #define Snode Snode32 #define ELFCLASS ELFCLASS32 #define ElfField Elf32 #define _elf_snode_init _elf32_snode_init #define _elf_prepscan _elf32_prepscan #define _elf_cookscn _elf32_cookscn #define _elf_mtype _elf32_mtype #define _elf_msize _elf32_msize #define elf_fsize elf32_fsize #define _elf_snode _elf32_snode #define _elf_ehdr _elf32_ehdr #define elf_xlatetom elf32_xlatetom #define _elf_phdr _elf32_phdr #define _elf_shdr _elf32_shdr #define _elf_prepscn _elf32_prepscn #define _mach_header mach_header #define _MH_CIGAM MH_CIGAM #define _swap_mh __swap_mach_header #define _elf_word Elf32_Word #define _elf_addr Elf32_Addr #define _elf_off Elf32_Off #define _SHN_MACHO SHN_MACHO #define _nlist nlist #define _addralign 4 #define _SWAPVAL SWAP32 #define _LC_SEGMENT LC_SEGMENT #define _segcmd segment_command #define _sect section #define _swapsegcmd __swap_segment_command #define _swapsect __swap_section #endif /* _ELF64 */ static Okay _elf_prepscn(Elf *elf, size_t cnt) { NOTE(ASSUMING_PROTECTED(*elf)) Elf_Scn * s; Elf_Scn * end; if (cnt == 0) return (OK_YES); if ((s = malloc(cnt * sizeof (Elf_Scn))) == 0) { _elf_seterr(EMEM_SCN, errno); return (OK_NO); } NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*s)) elf->ed_scntabsz = cnt; end = s + cnt; elf->ed_hdscn = s; do { *s = _elf_snode_init.sb_scn; s->s_elf = elf; s->s_next = s + 1; s->s_index = s - elf->ed_hdscn; s->s_shdr = (Shdr*)s->s_elf->ed_shdr + s->s_index; ELFMUTEXINIT(&s->s_mutex); /* * Section has not yet been cooked! * * We don't cook a section until it's data is actually * referenced. */ s->s_myflags = 0; } while (++s < end); elf->ed_tlscn = --s; s->s_next = 0; /* * Section index SHN_UNDEF (0) does not and cannot * have a data buffer. Fix it here. Also mark the * initial section as being allocated for the block */ s = elf->ed_hdscn; s->s_myflags = SF_ALLOC; s->s_hdnode = 0; s->s_tlnode = 0; NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*s)) return (OK_YES); } Okay _elf_cookscn(Elf_Scn * s) { NOTE(ASSUMING_PROTECTED(*s, *(s->s_elf))) Elf * elf; Shdr * sh; register Dnode * d = &s->s_dnode; size_t fsz, msz; unsigned work; NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*d)) s->s_hdnode = s->s_tlnode = d; s->s_err = 0; s->s_shflags = 0; s->s_uflags = 0; /* * Prepare d_data for inspection, but don't actually * translate data until needed. Leave the READY * flag off. NOBITS sections see zero size. */ elf = s->s_elf; sh = s->s_shdr; d->db_scn = s; d->db_off = sh->sh_offset; d->db_data.d_align = sh->sh_addralign; d->db_data.d_version = elf->ed_version; ELFACCESSDATA(work, _elf_work) d->db_data.d_type = _elf_mtype(elf, sh->sh_type, work); d->db_data.d_buf = 0; d->db_data.d_off = 0; fsz = elf_fsize(d->db_data.d_type, 1, elf->ed_version); msz = _elf_msize(d->db_data.d_type, elf->ed_version); d->db_data.d_size = (sh->sh_size / fsz) * msz; d->db_shsz = sh->sh_size; d->db_raw = 0; d->db_buf = 0; d->db_uflags = 0; d->db_myflags = 0; d->db_next = 0; if (sh->sh_type != SHT_NOBITS) d->db_fsz = sh->sh_size; else d->db_fsz = 0; s->s_myflags |= SF_READY; NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*d)) return (OK_YES); } Snode * _elf_snode() { register Snode *s; if ((s = malloc(sizeof (Snode))) == 0) { _elf_seterr(EMEM_SNODE, errno); return (0); } *s = _elf_snode_init; ELFMUTEXINIT(&s->sb_scn.s_mutex); s->sb_scn.s_myflags = SF_ALLOC | SF_READY; s->sb_scn.s_shdr = &s->sb_shdr; return (s); } int _elf_ehdr(Elf * elf, int inplace) { NOTE(ASSUMING_PROTECTED(*elf)) register size_t fsz; /* field size */ Elf_Data dst, src; fsz = elf_fsize(ELF_T_EHDR, 1, elf->ed_version); if (fsz > elf->ed_fsz) { _elf_seterr(EFMT_EHDRSZ, 0); return (-1); } if (inplace && (fsz >= sizeof (Ehdr))) { /* * The translated Ehdr will fit over the original Ehdr. */ /* LINTED */ elf->ed_ehdr = (Ehdr *)elf->ed_ident; elf->ed_status = ES_COOKED; } else { elf->ed_ehdr = malloc(sizeof (Ehdr)); if (elf->ed_ehdr == 0) { _elf_seterr(EMEM_EHDR, errno); return (-1); } elf->ed_myflags |= EDF_EHALLOC; } /* * Memory size >= fsz, because otherwise the memory version * loses information and cannot accurately implement the * file. */ src.d_buf = (Elf_Void *)elf->ed_ident; src.d_type = ELF_T_EHDR; src.d_size = fsz; src.d_version = elf->ed_version; dst.d_buf = (Elf_Void *)elf->ed_ehdr; dst.d_size = sizeof (Ehdr); dst.d_version = EV_CURRENT; if ((_elf_vm(elf, (size_t)0, fsz) != OK_YES) || (elf_xlatetom(&dst, &src, elf->ed_encode) == 0)) { if (elf->ed_myflags & EDF_EHALLOC) { elf->ed_myflags &= ~EDF_EHALLOC; free(elf->ed_ehdr); } elf->ed_ehdr = 0; return (-1); } if (((Ehdr*)elf->ed_ehdr)->e_ident[EI_CLASS] != ELFCLASS) { _elf_seterr(EREQ_CLASS, 0); if (elf->ed_myflags & EDF_EHALLOC) { elf->ed_myflags &= ~EDF_EHALLOC; free(elf->ed_ehdr); } elf->ed_ehdr = 0; return (-1); } if (((Ehdr*)elf->ed_ehdr)->e_version != elf->ed_version) { _elf_seterr(EFMT_VER2, 0); if (elf->ed_myflags & EDF_EHALLOC) { elf->ed_myflags &= ~EDF_EHALLOC; free(elf->ed_ehdr); } elf->ed_ehdr = 0; return (-1); } return (0); } int _elf_phdr(Elf * elf, int inplace) { NOTE(ASSUMING_PROTECTED(*elf)) register size_t fsz, msz; Elf_Data dst, src; Ehdr * eh = elf->ed_ehdr; /* must be present */ unsigned work; if (eh->e_phnum == 0) return (0); fsz = elf_fsize(ELF_T_PHDR, 1, elf->ed_version); if (eh->e_phentsize != fsz) { _elf_seterr(EFMT_PHDRSZ, 0); return (-1); } fsz *= eh->e_phnum; ELFACCESSDATA(work, _elf_work) msz = _elf_msize(ELF_T_PHDR, work) * eh->e_phnum; if ((eh->e_phoff == 0) || ((fsz + eh->e_phoff) > elf->ed_fsz)) { _elf_seterr(EFMT_PHTAB, 0); return (-1); } if (inplace && fsz >= msz && eh->e_phoff % sizeof (ElfField) == 0) { elf->ed_phdr = (Elf_Void *)(elf->ed_ident + eh->e_phoff); elf->ed_status = ES_COOKED; } else { if ((elf->ed_phdr = malloc(msz)) == 0) { _elf_seterr(EMEM_PHDR, errno); return (-1); } elf->ed_myflags |= EDF_PHALLOC; } src.d_buf = (Elf_Void *)(elf->ed_ident + eh->e_phoff); src.d_type = ELF_T_PHDR; src.d_size = fsz; src.d_version = elf->ed_version; dst.d_buf = elf->ed_phdr; dst.d_size = msz; dst.d_version = work; if ((_elf_vm(elf, (size_t)eh->e_phoff, fsz) != OK_YES) || (elf_xlatetom(&dst, &src, elf->ed_encode) == 0)) { if (elf->ed_myflags & EDF_PHALLOC) { elf->ed_myflags &= ~EDF_PHALLOC; free(elf->ed_phdr); } elf->ed_phdr = 0; return (-1); } elf->ed_phdrsz = msz; return (0); } int _elf_shdr(Elf * elf, int inplace) { NOTE(ASSUMING_PROTECTED(*elf)) register size_t fsz, msz; size_t scncnt; Elf_Data dst, src; register Ehdr *eh = elf->ed_ehdr; /* must be present */ if ((eh->e_shnum == 0) && (eh->e_shoff == 0)) return (0); fsz = elf_fsize(ELF_T_SHDR, 1, elf->ed_version); if (eh->e_shentsize != fsz) { _elf_seterr(EFMT_SHDRSZ, 0); return (-1); } /* * If we are dealing with a file with 'extended section * indexes' - then we need to load the first section * header. The actual section count is stored in * Shdr[0].sh_size. */ if ((scncnt = eh->e_shnum) == 0) { Shdr sh; if ((eh->e_shoff == 0) || (elf->ed_fsz <= eh->e_shoff) || (elf->ed_fsz - eh->e_shoff < fsz)) { _elf_seterr(EFMT_SHTAB, 0); return (-1); } src.d_buf = (Elf_Void *)(elf->ed_ident + eh->e_shoff); src.d_type = ELF_T_SHDR; src.d_size = fsz; src.d_version = elf->ed_version; dst.d_buf = (Elf_Void *)&sh; dst.d_size = sizeof (Shdr); dst.d_version = EV_CURRENT; if ((_elf_vm(elf, (size_t)eh->e_shoff, fsz) != OK_YES) || (elf_xlatetom(&dst, &src, elf->ed_encode) == 0)) { return (-1); } scncnt = sh.sh_size; } fsz *= scncnt; msz = scncnt * sizeof (Shdr); if ((eh->e_shoff == 0) || (elf->ed_fsz <= eh->e_shoff) || (elf->ed_fsz - eh->e_shoff < fsz)) { _elf_seterr(EFMT_SHTAB, 0); return (-1); } if (inplace && (fsz >= msz) && ((eh->e_shoff % sizeof (ElfField)) == 0)) { /* LINTED */ elf->ed_shdr = (Shdr *)(elf->ed_ident + eh->e_shoff); elf->ed_status = ES_COOKED; } else { if ((elf->ed_shdr = malloc(msz)) == 0) { _elf_seterr(EMEM_SHDR, errno); return (-1); } elf->ed_myflags |= EDF_SHALLOC; } src.d_buf = (Elf_Void *)(elf->ed_ident + eh->e_shoff); src.d_type = ELF_T_SHDR; src.d_size = fsz; src.d_version = elf->ed_version; dst.d_buf = (Elf_Void *)elf->ed_shdr; dst.d_size = msz; dst.d_version = EV_CURRENT; if (elf->ed_kind == ELF_K_MACHO) { struct _mach_header hdr, *mh = (struct _mach_header *)(elf->ed_image); struct load_command *thisLC = (struct load_command *)(&(mh[1])); int needSwap = (_MH_CIGAM == mh->magic); int i,j; Shdr *pShdr = (Shdr *)(elf->ed_shdr); Shdr *SectionToShdrMap[scncnt]; /* small stack allocated array */ Shdr **pMap = SectionToShdrMap; Shdr *pSymTab = NULL; bzero(pShdr, sizeof(Shdr)); SectionToShdrMap[0] = pShdr; pMap++; /* By Mach-o convention the n_sect ordinal index is one-based */ pShdr++; /* By ELF convention the first section header is unused */ if (needSwap) { hdr = *mh; mh = &hdr; _swap_mh(mh); } for (i = 0; i < mh->ncmds; i++) { int cmd = thisLC->cmd, cmdsize = thisLC->cmdsize; if (needSwap) { SWAP32(cmd); SWAP32(cmdsize); } switch(cmd) { case _LC_SEGMENT: { struct _segcmd seg, *thisSG = (struct _segcmd *)thisLC; struct _sect sect, *section_ptr, *thisSect = (struct _sect *)(&(thisSG[1])); if (needSwap) { seg = *thisSG; thisSG = &seg; _swapsegcmd(thisSG); } for (j = 0; j < thisSG->nsects; ++j) { int primary; section_ptr = thisSect + j; if (needSwap) { sect = *section_ptr; section_ptr = § _swapsect(section_ptr); } primary = (0 == strcmp(thisSG->segname, SEG_TEXT)) || (0 == strcmp(thisSG->segname, SEG_DATA)); pShdr->sh_name = (_elf_word)elf_macho_str_cookie(section_ptr->sectname, primary); pShdr->sh_type = SHT_UNKNOWN12; pShdr->sh_flags = 0; pShdr->sh_addr = (_elf_addr)section_ptr->addr; pShdr->sh_offset = (_elf_off)section_ptr->offset; pShdr->sh_size = section_ptr->size; pShdr->sh_link = _SHN_MACHO; pShdr->sh_info = STTSect(section_ptr->sectname); pShdr->sh_addralign = section_ptr->align; pShdr->sh_entsize = 0; *pMap = pShdr; pMap++; pShdr++; } break; } case LC_SYMTAB: { struct symtab_command symt, *thisST = (struct symtab_command *)thisLC; if (needSwap) { symt = *thisST; thisST = &symt; __swap_symtab_command(thisST); } pShdr->sh_name = (_elf_word)elf_macho_str_cookie("__symbol_table", 1); pShdr->sh_type = SHT_STRTAB; /* Must yield ELF_T_BYTE to allow the sh_entsize used below! */ pShdr->sh_flags = 0; pShdr->sh_addr = (_elf_addr)(thisST->symoff); pShdr->sh_offset = (_elf_off)(thisST->symoff); pShdr->sh_size = thisST->nsyms * sizeof (struct _nlist); pShdr->sh_link = _SHN_MACHO; pShdr->sh_info = 0; pShdr->sh_addralign = _addralign; pShdr->sh_entsize = sizeof(struct _nlist); pSymTab = pShdr; pShdr++; pShdr->sh_name = (_elf_word)elf_macho_str_cookie("__dir_str_table", 1); pShdr->sh_type = SHT_STRTAB; /* Must yield ELF_T_BYTE to allow the sh_entsize used below! */ pShdr->sh_flags = 0; pShdr->sh_addr = (_elf_addr)(thisST->stroff); pShdr->sh_offset = (_elf_off)(thisST->stroff); pShdr->sh_size = thisST->strsize; pShdr->sh_link = _SHN_MACHO; pShdr->sh_info = 0; pShdr->sh_addralign = 1; pShdr->sh_entsize = 0; pShdr++; break; } default: break; } thisLC = (struct load_command *) ((caddr_t) thisLC + cmdsize); } src.d_buf = (Elf_Void *)elf->ed_shdr; src.d_type = ELF_T_SHDR; src.d_size = fsz; if (NULL != pSymTab) { struct _nlist *nsym = (struct _nlist *)(elf->ed_image + pSymTab->sh_addr); struct _nlist *pEnd = nsym + (pSymTab->sh_size/pSymTab->sh_entsize); pMap = SectionToShdrMap; if (mprotect((char *)elf->ed_image, elf->ed_imagesz, PROT_READ|PROT_WRITE) == -1) { _elf_seterr(EIO_VM, errno); return (-1); } while (nsym < pEnd) { if (needSwap) { SWAP32(nsym->n_un.n_strx); SWAP16(nsym->n_desc); _SWAPVAL(nsym->n_value); } if (nsym->n_type & N_STAB) { /* Detect C++ methods */ switch(nsym->n_type) { case N_FUN: nsym->n_desc = STT_FUNC; break; case N_GSYM: nsym->n_desc = STT_OBJECT; break; default: break; } } else if ((N_ABS | N_EXT) == (nsym->n_type & (N_TYPE | N_EXT)) || (N_SECT | N_EXT) == (nsym->n_type & (N_TYPE | N_EXT))) { nsym->n_desc = SectionToShdrMap[nsym->n_sect]->sh_info; } else if ((N_UNDF | N_EXT) == (nsym->n_type & (N_TYPE | N_EXT)) && nsym->n_sect == NO_SECT) { nsym->n_desc = STT_OBJECT; /* Common symbol */ } else { /* NOTHING don't mess with other types of symbols */ } nsym++; } } } if ((_elf_vm(elf, (size_t)eh->e_shoff, fsz) != OK_YES) || (elf_xlatetom(&dst, &src, elf->ed_encode) == 0) || (_elf_prepscn(elf, scncnt) != OK_YES)) { if (elf->ed_myflags & EDF_SHALLOC) { elf->ed_myflags &= ~EDF_SHALLOC; free(elf->ed_shdr); } elf->ed_shdr = 0; return (-1); } return (0); }