/* * 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. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include <_libelf.h> #include <_elfwrap.h> /* * This module is compiled to support 32-bit and 64-bit class objects. Define * the necessary interfaces for these classes. */ #if defined(_ELF64) #define input input64 #define output output64 #else #define input input32 #define output output32 #endif static StdSec_t StdSecs[] = { { MSG_ORIG(MSG_SCN_SYMTAB), SHT_SYMTAB, 0 }, { MSG_ORIG(MSG_SCN_STRTAB), SHT_STRTAB, SHF_STRINGS}, { MSG_ORIG(MSG_SCN_SHSTRTAB), SHT_STRTAB, SHF_STRINGS}, { NULL, 0, 0 } }; /* * Process all input files. These contain the data that will be assigned to a * new ELF section. */ int input(int argc, char **argv, const char *prog, const char *ofile, ObjDesc_t *odp) { OutSec_t outsec; StdSec_t *stdsecs; size_t ndx, cnt; int ret = 0, fd = -1; /* * Make sure we have access to read each input file, and prepare an * output section descriptor for each. Note, we assign section indexes * starting at 1, as section index 0 is special, and is created by * libelf. */ for (ndx = 1; argc; argc--, argv++, ndx++) { char *file = *argv; struct stat status; size_t namesz; /* * Close any previously opened file. */ if (fd != -1) (void) close(fd); /* * Identify the section. */ outsec.os_name = basename(file); outsec.os_type = SHT_PROGBITS; outsec.os_flags = SHF_ALLOC; outsec.os_ndx = ndx; if ((fd = open(file, O_RDONLY)) == -1) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_ERR_OPEN), prog, file, strerror(err)); ret = 1; continue; } if (fstat(fd, &status) == -1) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_ERR_FSTAT), prog, file, strerror(err)); ret = 1; continue; } if ((outsec.os_size = status.st_size) == 0) { (void) fprintf(stderr, MSG_INTL(MSG_WARN_ZERO), prog, file); continue; } if ((outsec.os_addr = mmap(0, outsec.os_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_ERR_MMAP), prog, file, strerror(err)); ret = 1; continue; } if (alist_append(&(odp->od_outsecs), &outsec, sizeof (OutSec_t), AL_CNT_WOSECS) == 0) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_ERR_ALLOC), prog, file, strerror(err)); return (1); } /* * Each data section contributes: * * i. its basename, prefixed with a "dot", to the .shstrtab. * ii. a section symbol. * iii. a data symbol, using the basename, with an * appended "_data" string. * iv. a data size symbol, using the basename with an * appended "_size" string. */ namesz = strlen(outsec.os_name) + 1; odp->od_symtabno += 3; odp->od_strtabsz += (namesz + MSG_STR_START_SIZE); odp->od_strtabsz += (namesz + MSG_STR_END_SIZE); odp->od_shstrtabsz += (namesz + MSG_STR_DOT_SIZE); } if (fd != -1) (void) close(fd); /* * If an error occurred, or no input files contributed data, bail now. */ if (ret || (odp->od_outsecs == NULL)) return (1); /* * Create section descriptors for .symtab, .strtab, and .shstrtab. */ for (cnt = 0, stdsecs = &StdSecs[cnt]; stdsecs->ss_name; cnt++, ndx++, stdsecs = &StdSecs[cnt]) { /* * Identify the section. */ outsec.os_name = stdsecs->ss_name; outsec.os_type = stdsecs->ss_type; outsec.os_flags = stdsecs->ss_flags; outsec.os_ndx = ndx; outsec.os_size = 0; outsec.os_addr = 0; if (alist_append(&(odp->od_outsecs), &outsec, sizeof (OutSec_t), AL_CNT_WOSECS) == 0) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_ERR_ALLOC), prog, outsec.os_name, strerror(err)); return (1); } /* * Each standard section contributes: * * i. its section name to the .shstrtab. * ii. a section symbol. */ odp->od_symtabno++; odp->od_shstrtabsz += (strlen(outsec.os_name) + 1); } /* * The symbol table requires an initial NULL entry and a following * FILE entry. Both string tables require an initial NULL byte. * The .strtab requires room for the output file name (STT_FILE). */ odp->od_symtabno += 2; odp->od_strtabsz += strlen(ofile) + 2; odp->od_shstrtabsz++; return (0); } /* * Having captured all input data, create the output file. */ int output(const char *prog, int fd, const char *ofile, ushort_t mach, ObjDesc_t *odp) { Aliste off; Elf *melf, *oelf; Ehdr *ehdr; Sym *symtab, *secsymtabent, *glbsymtabent; char *strtab, *strtabent, *shstrtab, *shstrtabent; OutSec_t *outsec, *outsymtab, *outstrtab, *outshstrtab; size_t len; TargDesc_t tdesc; /* * Obtain any target specific ELF information. */ if (mach == 0) mach = M_MACH; switch (mach) { #if !defined(lint) case EM_SPARC: target_init_sparc(&tdesc); break; case EM_SPARCV9: target_init_sparcv9(&tdesc); break; case EM_386: target_init_i386(&tdesc); break; case EM_AMD64: target_init_amd64(&tdesc); break; #else default: target_init(&tdesc); break; #endif } /* * Create a new ELF descriptor for the new output file. */ if ((oelf = elf_begin(fd, ELF_C_WRITE, 0)) == NULL) { (void) fprintf(stderr, MSG_INTL(MSG_ELF_BEGIN), prog, elf_errmsg(elf_errno())); return (1); } /* * Create and initialize the new ELF header. */ if ((ehdr = elf_newehdr(oelf)) == NULL) { (void) fprintf(stderr, MSG_INTL(MSG_ELF_NEWEHDR), prog, elf_errmsg(elf_errno())); return (1); } /* * Note, the ELF header is initialized to reflect the host running * elfwrap(1) rather than the target. Using host byte order allows * elfwrap(1) to create the object data. Prior to the final update, * the output ELF header is modified to reflect the target, causing * libelf to produce the output object using the correct byte order * and other target information. */ ehdr->e_ident[EI_DATA] = M_DATA; ehdr->e_type = ET_REL; ehdr->e_version = EV_CURRENT; /* * Create the required number of new sections, their associated section * header, and an initial data buffer. */ for (ALIST_TRAVERSE(odp->od_outsecs, off, outsec)) { Elf_Scn *scn; Elf_Data *data; Shdr *shdr; if ((scn = elf_newscn(oelf)) == NULL) { (void) fprintf(stderr, MSG_INTL(MSG_ELF_NEWSCN), prog, outsec->os_name, elf_errmsg(elf_errno())); return (1); } if ((shdr = elf_getshdr(scn)) == NULL) { (void) fprintf(stderr, MSG_INTL(MSG_ELF_GETSHDR), prog, outsec->os_name, elf_errmsg(elf_errno())); return (1); } /* * Assign the section type and flags. */ shdr->sh_type = outsec->os_type; shdr->sh_flags = outsec->os_flags; if ((data = elf_newdata(scn)) == NULL) { (void) fprintf(stderr, MSG_INTL(MSG_ELF_NEWDATA), prog, outsec->os_name, elf_errmsg(elf_errno())); return (1); } switch (shdr->sh_type) { case SHT_PROGBITS: /* * If this is a PROGBITS section, then the data * originates from an input file. Assign the data * buffer to this input file and provide a default * alignment. */ data->d_buf = outsec->os_addr; data->d_type = ELF_T_BYTE; data->d_size = outsec->os_size; data->d_align = tdesc.td_align; break; case SHT_SYMTAB: /* * If this is the symbol table, use the symbol count to * reserve sufficient space for the symbols we need. */ data->d_buf = 0; data->d_type = ELF_T_SYM; data->d_size = (odp->od_symtabno * tdesc.td_symsz); data->d_align = tdesc.td_align; break; case SHT_STRTAB: /* * If this is a string table, use the table size to * reserve sufficient space for the strings we need. */ data->d_buf = 0; data->d_type = ELF_T_BYTE; if (strcmp(outsec->os_name, MSG_ORIG(MSG_SCN_STRTAB))) data->d_size = odp->od_shstrtabsz; else data->d_size = odp->od_strtabsz; data->d_align = 1; break; } } /* * Write the ELF data into a memory image. */ if ((elf_update(oelf, ELF_C_WRIMAGE)) == -1) { (void) fprintf(stderr, MSG_INTL(MSG_ELF_UPDATE), prog, elf_errmsg(elf_errno())); return (1); } /* * Assign an ELF descriptor to the memory image. */ if ((melf = elf_begin(0, ELF_C_IMAGE, oelf)) == NULL) { (void) fprintf(stderr, MSG_INTL(MSG_ELF_BEGIN), prog, elf_errmsg(elf_errno())); return (1); } /* * Get the ELF header from the memory image. */ if ((ehdr = elf_getehdr(melf)) == NULL) { (void) fprintf(stderr, MSG_INTL(MSG_ELF_GETEHDR), prog, elf_errmsg(elf_errno())); return (1); } /* * Read the section header and data from the new sections of the * memory image. */ for (ALIST_TRAVERSE(odp->od_outsecs, off, outsec)) { Elf_Scn *scn; Shdr *shdr; if ((scn = elf_getscn(melf, outsec->os_ndx)) == NULL) { (void) fprintf(stderr, MSG_INTL(MSG_ELF_GETSCN), prog, outsec->os_name, elf_errmsg(elf_errno())); return (1); } if ((outsec->os_shdr = shdr = elf_getshdr(scn)) == NULL) { (void) fprintf(stderr, MSG_INTL(MSG_ELF_GETSHDR), prog, outsec->os_name, elf_errmsg(elf_errno())); return (1); } if ((outsec->os_data = elf_getdata(scn, NULL)) == NULL) { (void) fprintf(stderr, MSG_INTL(MSG_ELF_GETDATA), prog, outsec->os_name, elf_errmsg(elf_errno())); return (1); } if (shdr->sh_type == SHT_PROGBITS) continue; /* * Remember the symbol table and string tables, so that they * can be filled in later. */ if (shdr->sh_type == SHT_SYMTAB) { outsymtab = outsec; symtab = (Sym *)outsec->os_data->d_buf; } else if (shdr->sh_type == SHT_STRTAB) { if (strcmp(outsec->os_name, MSG_ORIG(MSG_SCN_STRTAB))) { outshstrtab = outsec; shstrtab = (char *)outsec->os_data->d_buf; } else { outstrtab = outsec; strtab = (char *)outsec->os_data->d_buf; } } } /* * Update the ELF header with the .shstrtab index. */ ehdr->e_shstrndx = outshstrtab->os_ndx; /* * Set up the string table entries, and skip the first byte. */ strtabent = strtab; strtabent++; shstrtabent = shstrtab; shstrtabent++; /* * Skip the first symbol table entry. Write a FILE entry, and set * up for adding sections and data symbols. Associate the symbol * table with the string table. */ secsymtabent = symtab; secsymtabent++; secsymtabent->st_name = (strtabent - strtab); secsymtabent->st_info = ELF_ST_INFO(STB_LOCAL, STT_NOTYPE); secsymtabent->st_shndx = SHN_ABS; secsymtabent++; glbsymtabent = secsymtabent; glbsymtabent += alist_nitems(odp->od_outsecs); outsymtab->os_shdr->sh_link = outstrtab->os_ndx; /* * Write the output file name to the .strtab. */ len = strlen(ofile) + 1; (void) memcpy(strtabent, ofile, len); strtabent += len; /* * Rescan all the new sections, adding symbols and strings as required. */ for (ALIST_TRAVERSE(odp->od_outsecs, off, outsec)) { size_t alen; /* * Create a section symbol. */ secsymtabent->st_info = ELF_ST_INFO(STB_LOCAL, STT_SECTION); secsymtabent->st_shndx = outsec->os_ndx; secsymtabent++; /* * Store the section name, (with an appended "." if the section * name is derived from the input file name), and point the * section header to this name. */ outsec->os_shdr->sh_name = (shstrtabent - shstrtab); if (outsec->os_shdr->sh_type == SHT_PROGBITS) { (void) memcpy(shstrtabent, MSG_ORIG(MSG_STR_DOT), MSG_STR_DOT_SIZE); shstrtabent += MSG_STR_DOT_SIZE; } len = strlen(outsec->os_name) + 1; (void) memcpy(shstrtabent, outsec->os_name, len); shstrtabent += len; if (outsec->os_shdr->sh_type != SHT_PROGBITS) continue; /* * Add a symbol pointing to this PROGBITS section. The value * is the base offset of this section, which can only be 0. * The size of the symbol can be taken straight from the section * header information (that libelf generated). */ glbsymtabent->st_name = (strtabent - strtab); glbsymtabent->st_info = ELF_ST_INFO(STB_GLOBAL, STT_OBJECT); glbsymtabent->st_shndx = outsec->os_ndx; glbsymtabent->st_size = outsec->os_shdr->sh_size; glbsymtabent++; /* * Store this symbol name (with an appended "_data") in the * string table. */ len--; (void) memcpy(strtabent, outsec->os_name, len); strtabent += len; alen = (MSG_STR_START_SIZE + 1); (void) memcpy(strtabent, MSG_ORIG(MSG_STR_START), alen); strtabent += alen; /* * Add a symbol indicating the size of this PROGBITS section. */ glbsymtabent->st_name = (strtabent - strtab); glbsymtabent->st_info = ELF_ST_INFO(STB_GLOBAL, STT_OBJECT); glbsymtabent->st_shndx = outsec->os_ndx; glbsymtabent->st_value = outsec->os_shdr->sh_size; glbsymtabent++; /* * Store this symbol name (with an appended "_end") in the * string table. */ (void) memcpy(strtabent, outsec->os_name, len); strtabent += len; alen = (MSG_STR_END_SIZE + 1); (void) memcpy(strtabent, MSG_ORIG(MSG_STR_END), alen); strtabent += alen; } /* * Update the .symtab section header with the index of the first * non-local symbol. The only locals written are the section symbols. */ outsymtab->os_shdr->sh_info = (secsymtabent - symtab); /* * Having updated the image following the byte order of elfwrap(), seed * the ELF header with the appropriate target information. */ ehdr->e_ident[EI_CLASS] = tdesc.td_class; ehdr->e_ident[EI_DATA] = tdesc.td_data; ehdr->e_machine = tdesc.td_mach; /* * If the output relocatable object is targeted to a machine with a * different byte order than the host running elfwrap(1), swap the data * to the target byte order. */ if ((_elf_sys_encoding() != ehdr->e_ident[EI_DATA]) && (_elf_swap_wrimage(melf) != 0)) { (void) fprintf(stderr, MSG_INTL(MSG_ELF_SWAP_WRIMAGE), prog, elf_errmsg(elf_errno())); return (1); } (void) elf_end(melf); /* * Finally, write the updated memory image out to disc. */ if ((elf_update(oelf, ELF_C_WRITE)) == -1) { (void) fprintf(stderr, MSG_INTL(MSG_ELF_UPDATE), prog, elf_errmsg(elf_errno())); return (1); } (void) elf_end(oelf); return (0); }