17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5ac448965Sahl * Common Development and Distribution License (the "License"). 6ac448965Sahl * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 21ac448965Sahl 227c478bd9Sstevel@tonic-gate /* 236009dbc6Sahl * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 25*b3db2570SJerry Jelinek * Copyright 2016 Mark Johnston. 267c478bd9Sstevel@tonic-gate */ 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate #define ELF_TARGET_ALL 297c478bd9Sstevel@tonic-gate #include <elf.h> 307c478bd9Sstevel@tonic-gate 317c478bd9Sstevel@tonic-gate #include <sys/types.h> 327c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 337c478bd9Sstevel@tonic-gate 347c478bd9Sstevel@tonic-gate #include <unistd.h> 357c478bd9Sstevel@tonic-gate #include <strings.h> 367c478bd9Sstevel@tonic-gate #include <alloca.h> 377c478bd9Sstevel@tonic-gate #include <limits.h> 387c478bd9Sstevel@tonic-gate #include <stddef.h> 397c478bd9Sstevel@tonic-gate #include <stdlib.h> 407c478bd9Sstevel@tonic-gate #include <stdio.h> 417c478bd9Sstevel@tonic-gate #include <fcntl.h> 427c478bd9Sstevel@tonic-gate #include <errno.h> 437c478bd9Sstevel@tonic-gate #include <wait.h> 447c478bd9Sstevel@tonic-gate #include <assert.h> 454b499cecSahl #include <sys/ipc.h> 467c478bd9Sstevel@tonic-gate 477c478bd9Sstevel@tonic-gate #include <dt_impl.h> 487c478bd9Sstevel@tonic-gate #include <dt_provider.h> 49ac448965Sahl #include <dt_program.h> 507c478bd9Sstevel@tonic-gate #include <dt_string.h> 517c478bd9Sstevel@tonic-gate 527c478bd9Sstevel@tonic-gate #define ESHDR_NULL 0 537c478bd9Sstevel@tonic-gate #define ESHDR_SHSTRTAB 1 547c478bd9Sstevel@tonic-gate #define ESHDR_DOF 2 557c478bd9Sstevel@tonic-gate #define ESHDR_STRTAB 3 567c478bd9Sstevel@tonic-gate #define ESHDR_SYMTAB 4 577c478bd9Sstevel@tonic-gate #define ESHDR_REL 5 587c478bd9Sstevel@tonic-gate #define ESHDR_NUM 6 597c478bd9Sstevel@tonic-gate 607c478bd9Sstevel@tonic-gate #define PWRITE_SCN(index, data) \ 617c478bd9Sstevel@tonic-gate (lseek64(fd, (off64_t)elf_file.shdr[(index)].sh_offset, SEEK_SET) != \ 627c478bd9Sstevel@tonic-gate (off64_t)elf_file.shdr[(index)].sh_offset || \ 637c478bd9Sstevel@tonic-gate dt_write(dtp, fd, (data), elf_file.shdr[(index)].sh_size) != \ 647c478bd9Sstevel@tonic-gate elf_file.shdr[(index)].sh_size) 657c478bd9Sstevel@tonic-gate 667c478bd9Sstevel@tonic-gate static const char DTRACE_SHSTRTAB32[] = "\0" 677c478bd9Sstevel@tonic-gate ".shstrtab\0" /* 1 */ 687c478bd9Sstevel@tonic-gate ".SUNW_dof\0" /* 11 */ 697c478bd9Sstevel@tonic-gate ".strtab\0" /* 21 */ 707c478bd9Sstevel@tonic-gate ".symtab\0" /* 29 */ 717c478bd9Sstevel@tonic-gate #ifdef __sparc 727c478bd9Sstevel@tonic-gate ".rela.SUNW_dof"; /* 37 */ 737c478bd9Sstevel@tonic-gate #else 747c478bd9Sstevel@tonic-gate ".rel.SUNW_dof"; /* 37 */ 757c478bd9Sstevel@tonic-gate #endif 767c478bd9Sstevel@tonic-gate 777c478bd9Sstevel@tonic-gate static const char DTRACE_SHSTRTAB64[] = "\0" 787c478bd9Sstevel@tonic-gate ".shstrtab\0" /* 1 */ 797c478bd9Sstevel@tonic-gate ".SUNW_dof\0" /* 11 */ 807c478bd9Sstevel@tonic-gate ".strtab\0" /* 21 */ 817c478bd9Sstevel@tonic-gate ".symtab\0" /* 29 */ 827c478bd9Sstevel@tonic-gate ".rela.SUNW_dof"; /* 37 */ 837c478bd9Sstevel@tonic-gate 847c478bd9Sstevel@tonic-gate static const char DOFSTR[] = "__SUNW_dof"; 857c478bd9Sstevel@tonic-gate static const char DOFLAZYSTR[] = "___SUNW_dof"; 867c478bd9Sstevel@tonic-gate 874b499cecSahl typedef struct dt_link_pair { 884b499cecSahl struct dt_link_pair *dlp_next; /* next pair in linked list */ 894b499cecSahl void *dlp_str; /* buffer for string table */ 904b499cecSahl void *dlp_sym; /* buffer for symbol table */ 914b499cecSahl } dt_link_pair_t; 924b499cecSahl 937c478bd9Sstevel@tonic-gate typedef struct dof_elf32 { 947c478bd9Sstevel@tonic-gate uint32_t de_nrel; /* relocation count */ 957c478bd9Sstevel@tonic-gate #ifdef __sparc 967c478bd9Sstevel@tonic-gate Elf32_Rela *de_rel; /* array of relocations for sparc */ 977c478bd9Sstevel@tonic-gate #else 987c478bd9Sstevel@tonic-gate Elf32_Rel *de_rel; /* array of relocations for x86 */ 997c478bd9Sstevel@tonic-gate #endif 1007c478bd9Sstevel@tonic-gate uint32_t de_nsym; /* symbol count */ 1017c478bd9Sstevel@tonic-gate Elf32_Sym *de_sym; /* array of symbols */ 1027c478bd9Sstevel@tonic-gate uint32_t de_strlen; /* size of of string table */ 1037c478bd9Sstevel@tonic-gate char *de_strtab; /* string table */ 1047c478bd9Sstevel@tonic-gate uint32_t de_global; /* index of the first global symbol */ 1057c478bd9Sstevel@tonic-gate } dof_elf32_t; 1067c478bd9Sstevel@tonic-gate 1077c478bd9Sstevel@tonic-gate static int 1087c478bd9Sstevel@tonic-gate prepare_elf32(dtrace_hdl_t *dtp, const dof_hdr_t *dof, dof_elf32_t *dep) 1097c478bd9Sstevel@tonic-gate { 1107c478bd9Sstevel@tonic-gate dof_sec_t *dofs, *s; 1117c478bd9Sstevel@tonic-gate dof_relohdr_t *dofrh; 1127c478bd9Sstevel@tonic-gate dof_relodesc_t *dofr; 1137c478bd9Sstevel@tonic-gate char *strtab; 1147c478bd9Sstevel@tonic-gate int i, j, nrel; 1157c478bd9Sstevel@tonic-gate size_t strtabsz = 1; 1167c478bd9Sstevel@tonic-gate uint32_t count = 0; 1177c478bd9Sstevel@tonic-gate size_t base; 1187c478bd9Sstevel@tonic-gate Elf32_Sym *sym; 1197c478bd9Sstevel@tonic-gate #ifdef __sparc 1207c478bd9Sstevel@tonic-gate Elf32_Rela *rel; 1217c478bd9Sstevel@tonic-gate #else 1227c478bd9Sstevel@tonic-gate Elf32_Rel *rel; 1237c478bd9Sstevel@tonic-gate #endif 1247c478bd9Sstevel@tonic-gate 1257c478bd9Sstevel@tonic-gate /*LINTED*/ 1267c478bd9Sstevel@tonic-gate dofs = (dof_sec_t *)((char *)dof + dof->dofh_secoff); 1277c478bd9Sstevel@tonic-gate 1287c478bd9Sstevel@tonic-gate /* 1297c478bd9Sstevel@tonic-gate * First compute the size of the string table and the number of 1307c478bd9Sstevel@tonic-gate * relocations present in the DOF. 1317c478bd9Sstevel@tonic-gate */ 1327c478bd9Sstevel@tonic-gate for (i = 0; i < dof->dofh_secnum; i++) { 1337c478bd9Sstevel@tonic-gate if (dofs[i].dofs_type != DOF_SECT_URELHDR) 1347c478bd9Sstevel@tonic-gate continue; 1357c478bd9Sstevel@tonic-gate 1367c478bd9Sstevel@tonic-gate /*LINTED*/ 1377c478bd9Sstevel@tonic-gate dofrh = (dof_relohdr_t *)((char *)dof + dofs[i].dofs_offset); 1387c478bd9Sstevel@tonic-gate 1397c478bd9Sstevel@tonic-gate s = &dofs[dofrh->dofr_strtab]; 1407c478bd9Sstevel@tonic-gate strtab = (char *)dof + s->dofs_offset; 1417c478bd9Sstevel@tonic-gate assert(strtab[0] == '\0'); 1427c478bd9Sstevel@tonic-gate strtabsz += s->dofs_size - 1; 1437c478bd9Sstevel@tonic-gate 1447c478bd9Sstevel@tonic-gate s = &dofs[dofrh->dofr_relsec]; 1457c478bd9Sstevel@tonic-gate /*LINTED*/ 1467c478bd9Sstevel@tonic-gate dofr = (dof_relodesc_t *)((char *)dof + s->dofs_offset); 1477c478bd9Sstevel@tonic-gate count += s->dofs_size / s->dofs_entsize; 1487c478bd9Sstevel@tonic-gate } 1497c478bd9Sstevel@tonic-gate 1507c478bd9Sstevel@tonic-gate dep->de_strlen = strtabsz; 1517c478bd9Sstevel@tonic-gate dep->de_nrel = count; 1527c478bd9Sstevel@tonic-gate dep->de_nsym = count + 1; /* the first symbol is always null */ 1537c478bd9Sstevel@tonic-gate 1547c478bd9Sstevel@tonic-gate if (dtp->dt_lazyload) { 1557c478bd9Sstevel@tonic-gate dep->de_strlen += sizeof (DOFLAZYSTR); 1567c478bd9Sstevel@tonic-gate dep->de_nsym++; 1577c478bd9Sstevel@tonic-gate } else { 1587c478bd9Sstevel@tonic-gate dep->de_strlen += sizeof (DOFSTR); 1597c478bd9Sstevel@tonic-gate dep->de_nsym++; 1607c478bd9Sstevel@tonic-gate } 1617c478bd9Sstevel@tonic-gate 1627c478bd9Sstevel@tonic-gate if ((dep->de_rel = calloc(dep->de_nrel, 1637c478bd9Sstevel@tonic-gate sizeof (dep->de_rel[0]))) == NULL) { 1647c478bd9Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_NOMEM)); 1657c478bd9Sstevel@tonic-gate } 1667c478bd9Sstevel@tonic-gate 1677c478bd9Sstevel@tonic-gate if ((dep->de_sym = calloc(dep->de_nsym, sizeof (Elf32_Sym))) == NULL) { 1687c478bd9Sstevel@tonic-gate free(dep->de_rel); 1697c478bd9Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_NOMEM)); 1707c478bd9Sstevel@tonic-gate } 1717c478bd9Sstevel@tonic-gate 1727c478bd9Sstevel@tonic-gate if ((dep->de_strtab = calloc(dep->de_strlen, 1)) == NULL) { 1737c478bd9Sstevel@tonic-gate free(dep->de_rel); 1747c478bd9Sstevel@tonic-gate free(dep->de_sym); 1757c478bd9Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_NOMEM)); 1767c478bd9Sstevel@tonic-gate } 1777c478bd9Sstevel@tonic-gate 1787c478bd9Sstevel@tonic-gate count = 0; 1797c478bd9Sstevel@tonic-gate strtabsz = 1; 1807c478bd9Sstevel@tonic-gate dep->de_strtab[0] = '\0'; 1817c478bd9Sstevel@tonic-gate rel = dep->de_rel; 1827c478bd9Sstevel@tonic-gate sym = dep->de_sym; 1837c478bd9Sstevel@tonic-gate dep->de_global = 1; 1847c478bd9Sstevel@tonic-gate 1857c478bd9Sstevel@tonic-gate /* 1867c478bd9Sstevel@tonic-gate * The first symbol table entry must be zeroed and is always ignored. 1877c478bd9Sstevel@tonic-gate */ 1887c478bd9Sstevel@tonic-gate bzero(sym, sizeof (Elf32_Sym)); 1897c478bd9Sstevel@tonic-gate sym++; 1907c478bd9Sstevel@tonic-gate 1917c478bd9Sstevel@tonic-gate /* 1927c478bd9Sstevel@tonic-gate * Take a second pass through the DOF sections filling in the 1937c478bd9Sstevel@tonic-gate * memory we allocated. 1947c478bd9Sstevel@tonic-gate */ 1957c478bd9Sstevel@tonic-gate for (i = 0; i < dof->dofh_secnum; i++) { 1967c478bd9Sstevel@tonic-gate if (dofs[i].dofs_type != DOF_SECT_URELHDR) 1977c478bd9Sstevel@tonic-gate continue; 1987c478bd9Sstevel@tonic-gate 1997c478bd9Sstevel@tonic-gate /*LINTED*/ 2007c478bd9Sstevel@tonic-gate dofrh = (dof_relohdr_t *)((char *)dof + dofs[i].dofs_offset); 2017c478bd9Sstevel@tonic-gate 2027c478bd9Sstevel@tonic-gate s = &dofs[dofrh->dofr_strtab]; 2037c478bd9Sstevel@tonic-gate strtab = (char *)dof + s->dofs_offset; 2047c478bd9Sstevel@tonic-gate bcopy(strtab + 1, dep->de_strtab + strtabsz, s->dofs_size); 2057c478bd9Sstevel@tonic-gate base = strtabsz; 2067c478bd9Sstevel@tonic-gate strtabsz += s->dofs_size - 1; 2077c478bd9Sstevel@tonic-gate 2087c478bd9Sstevel@tonic-gate s = &dofs[dofrh->dofr_relsec]; 2097c478bd9Sstevel@tonic-gate /*LINTED*/ 2107c478bd9Sstevel@tonic-gate dofr = (dof_relodesc_t *)((char *)dof + s->dofs_offset); 2117c478bd9Sstevel@tonic-gate nrel = s->dofs_size / s->dofs_entsize; 2127c478bd9Sstevel@tonic-gate 2137c478bd9Sstevel@tonic-gate s = &dofs[dofrh->dofr_tgtsec]; 2147c478bd9Sstevel@tonic-gate 2157c478bd9Sstevel@tonic-gate for (j = 0; j < nrel; j++) { 2167c478bd9Sstevel@tonic-gate #if defined(__i386) || defined(__amd64) 2177c478bd9Sstevel@tonic-gate rel->r_offset = s->dofs_offset + 2187c478bd9Sstevel@tonic-gate dofr[j].dofr_offset; 2197c478bd9Sstevel@tonic-gate rel->r_info = ELF32_R_INFO(count + dep->de_global, 2207c478bd9Sstevel@tonic-gate R_386_32); 2217c478bd9Sstevel@tonic-gate #elif defined(__sparc) 2227c478bd9Sstevel@tonic-gate /* 2237c478bd9Sstevel@tonic-gate * Add 4 bytes to hit the low half of this 64-bit 2247c478bd9Sstevel@tonic-gate * big-endian address. 2257c478bd9Sstevel@tonic-gate */ 2267c478bd9Sstevel@tonic-gate rel->r_offset = s->dofs_offset + 2277c478bd9Sstevel@tonic-gate dofr[j].dofr_offset + 4; 2287c478bd9Sstevel@tonic-gate rel->r_info = ELF32_R_INFO(count + dep->de_global, 2297c478bd9Sstevel@tonic-gate R_SPARC_32); 2307c478bd9Sstevel@tonic-gate #else 2317c478bd9Sstevel@tonic-gate #error unknown ISA 2327c478bd9Sstevel@tonic-gate #endif 2337c478bd9Sstevel@tonic-gate 2347c478bd9Sstevel@tonic-gate sym->st_name = base + dofr[j].dofr_name - 1; 2357c478bd9Sstevel@tonic-gate sym->st_value = 0; 2367c478bd9Sstevel@tonic-gate sym->st_size = 0; 2374b499cecSahl sym->st_info = ELF32_ST_INFO(STB_GLOBAL, STT_FUNC); 2387c478bd9Sstevel@tonic-gate sym->st_other = 0; 2397c478bd9Sstevel@tonic-gate sym->st_shndx = SHN_UNDEF; 2407c478bd9Sstevel@tonic-gate 2417c478bd9Sstevel@tonic-gate rel++; 2427c478bd9Sstevel@tonic-gate sym++; 2437c478bd9Sstevel@tonic-gate count++; 2447c478bd9Sstevel@tonic-gate } 2457c478bd9Sstevel@tonic-gate } 2467c478bd9Sstevel@tonic-gate 2477c478bd9Sstevel@tonic-gate /* 2487c478bd9Sstevel@tonic-gate * Add a symbol for the DOF itself. We use a different symbol for 2497c478bd9Sstevel@tonic-gate * lazily and actively loaded DOF to make them easy to distinguish. 2507c478bd9Sstevel@tonic-gate */ 2517c478bd9Sstevel@tonic-gate sym->st_name = strtabsz; 2527c478bd9Sstevel@tonic-gate sym->st_value = 0; 2537c478bd9Sstevel@tonic-gate sym->st_size = dof->dofh_filesz; 2547c478bd9Sstevel@tonic-gate sym->st_info = ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT); 2557c478bd9Sstevel@tonic-gate sym->st_other = 0; 2567c478bd9Sstevel@tonic-gate sym->st_shndx = ESHDR_DOF; 2577c478bd9Sstevel@tonic-gate sym++; 2587c478bd9Sstevel@tonic-gate 2597c478bd9Sstevel@tonic-gate if (dtp->dt_lazyload) { 2607c478bd9Sstevel@tonic-gate bcopy(DOFLAZYSTR, dep->de_strtab + strtabsz, 2617c478bd9Sstevel@tonic-gate sizeof (DOFLAZYSTR)); 2627c478bd9Sstevel@tonic-gate strtabsz += sizeof (DOFLAZYSTR); 2637c478bd9Sstevel@tonic-gate } else { 2647c478bd9Sstevel@tonic-gate bcopy(DOFSTR, dep->de_strtab + strtabsz, sizeof (DOFSTR)); 2657c478bd9Sstevel@tonic-gate strtabsz += sizeof (DOFSTR); 2667c478bd9Sstevel@tonic-gate } 2677c478bd9Sstevel@tonic-gate 2687c478bd9Sstevel@tonic-gate assert(count == dep->de_nrel); 2697c478bd9Sstevel@tonic-gate assert(strtabsz == dep->de_strlen); 2707c478bd9Sstevel@tonic-gate 2717c478bd9Sstevel@tonic-gate return (0); 2727c478bd9Sstevel@tonic-gate } 2737c478bd9Sstevel@tonic-gate 2747c478bd9Sstevel@tonic-gate 2757c478bd9Sstevel@tonic-gate typedef struct dof_elf64 { 2767c478bd9Sstevel@tonic-gate uint32_t de_nrel; 2777c478bd9Sstevel@tonic-gate Elf64_Rela *de_rel; 2787c478bd9Sstevel@tonic-gate uint32_t de_nsym; 2797c478bd9Sstevel@tonic-gate Elf64_Sym *de_sym; 2807c478bd9Sstevel@tonic-gate 2817c478bd9Sstevel@tonic-gate uint32_t de_strlen; 2827c478bd9Sstevel@tonic-gate char *de_strtab; 2837c478bd9Sstevel@tonic-gate 2847c478bd9Sstevel@tonic-gate uint32_t de_global; 2857c478bd9Sstevel@tonic-gate } dof_elf64_t; 2867c478bd9Sstevel@tonic-gate 2877c478bd9Sstevel@tonic-gate static int 2887c478bd9Sstevel@tonic-gate prepare_elf64(dtrace_hdl_t *dtp, const dof_hdr_t *dof, dof_elf64_t *dep) 2897c478bd9Sstevel@tonic-gate { 2907c478bd9Sstevel@tonic-gate dof_sec_t *dofs, *s; 2917c478bd9Sstevel@tonic-gate dof_relohdr_t *dofrh; 2927c478bd9Sstevel@tonic-gate dof_relodesc_t *dofr; 2937c478bd9Sstevel@tonic-gate char *strtab; 2947c478bd9Sstevel@tonic-gate int i, j, nrel; 2957c478bd9Sstevel@tonic-gate size_t strtabsz = 1; 2967c478bd9Sstevel@tonic-gate uint32_t count = 0; 2977c478bd9Sstevel@tonic-gate size_t base; 2987c478bd9Sstevel@tonic-gate Elf64_Sym *sym; 2997c478bd9Sstevel@tonic-gate Elf64_Rela *rel; 3007c478bd9Sstevel@tonic-gate 3017c478bd9Sstevel@tonic-gate /*LINTED*/ 3027c478bd9Sstevel@tonic-gate dofs = (dof_sec_t *)((char *)dof + dof->dofh_secoff); 3037c478bd9Sstevel@tonic-gate 3047c478bd9Sstevel@tonic-gate /* 3057c478bd9Sstevel@tonic-gate * First compute the size of the string table and the number of 3067c478bd9Sstevel@tonic-gate * relocations present in the DOF. 3077c478bd9Sstevel@tonic-gate */ 3087c478bd9Sstevel@tonic-gate for (i = 0; i < dof->dofh_secnum; i++) { 3097c478bd9Sstevel@tonic-gate if (dofs[i].dofs_type != DOF_SECT_URELHDR) 3107c478bd9Sstevel@tonic-gate continue; 3117c478bd9Sstevel@tonic-gate 3127c478bd9Sstevel@tonic-gate /*LINTED*/ 3137c478bd9Sstevel@tonic-gate dofrh = (dof_relohdr_t *)((char *)dof + dofs[i].dofs_offset); 3147c478bd9Sstevel@tonic-gate 3157c478bd9Sstevel@tonic-gate s = &dofs[dofrh->dofr_strtab]; 3167c478bd9Sstevel@tonic-gate strtab = (char *)dof + s->dofs_offset; 3177c478bd9Sstevel@tonic-gate assert(strtab[0] == '\0'); 3187c478bd9Sstevel@tonic-gate strtabsz += s->dofs_size - 1; 3197c478bd9Sstevel@tonic-gate 3207c478bd9Sstevel@tonic-gate s = &dofs[dofrh->dofr_relsec]; 3217c478bd9Sstevel@tonic-gate /*LINTED*/ 3227c478bd9Sstevel@tonic-gate dofr = (dof_relodesc_t *)((char *)dof + s->dofs_offset); 3237c478bd9Sstevel@tonic-gate count += s->dofs_size / s->dofs_entsize; 3247c478bd9Sstevel@tonic-gate } 3257c478bd9Sstevel@tonic-gate 3267c478bd9Sstevel@tonic-gate dep->de_strlen = strtabsz; 3277c478bd9Sstevel@tonic-gate dep->de_nrel = count; 3287c478bd9Sstevel@tonic-gate dep->de_nsym = count + 1; /* the first symbol is always null */ 3297c478bd9Sstevel@tonic-gate 3307c478bd9Sstevel@tonic-gate if (dtp->dt_lazyload) { 3317c478bd9Sstevel@tonic-gate dep->de_strlen += sizeof (DOFLAZYSTR); 3327c478bd9Sstevel@tonic-gate dep->de_nsym++; 3337c478bd9Sstevel@tonic-gate } else { 3347c478bd9Sstevel@tonic-gate dep->de_strlen += sizeof (DOFSTR); 3357c478bd9Sstevel@tonic-gate dep->de_nsym++; 3367c478bd9Sstevel@tonic-gate } 3377c478bd9Sstevel@tonic-gate 3387c478bd9Sstevel@tonic-gate if ((dep->de_rel = calloc(dep->de_nrel, 3397c478bd9Sstevel@tonic-gate sizeof (dep->de_rel[0]))) == NULL) { 3407c478bd9Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_NOMEM)); 3417c478bd9Sstevel@tonic-gate } 3427c478bd9Sstevel@tonic-gate 3437c478bd9Sstevel@tonic-gate if ((dep->de_sym = calloc(dep->de_nsym, sizeof (Elf64_Sym))) == NULL) { 3447c478bd9Sstevel@tonic-gate free(dep->de_rel); 3457c478bd9Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_NOMEM)); 3467c478bd9Sstevel@tonic-gate } 3477c478bd9Sstevel@tonic-gate 3487c478bd9Sstevel@tonic-gate if ((dep->de_strtab = calloc(dep->de_strlen, 1)) == NULL) { 3497c478bd9Sstevel@tonic-gate free(dep->de_rel); 3507c478bd9Sstevel@tonic-gate free(dep->de_sym); 3517c478bd9Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_NOMEM)); 3527c478bd9Sstevel@tonic-gate } 3537c478bd9Sstevel@tonic-gate 3547c478bd9Sstevel@tonic-gate count = 0; 3557c478bd9Sstevel@tonic-gate strtabsz = 1; 3567c478bd9Sstevel@tonic-gate dep->de_strtab[0] = '\0'; 3577c478bd9Sstevel@tonic-gate rel = dep->de_rel; 3587c478bd9Sstevel@tonic-gate sym = dep->de_sym; 3597c478bd9Sstevel@tonic-gate dep->de_global = 1; 3607c478bd9Sstevel@tonic-gate 3617c478bd9Sstevel@tonic-gate /* 3627c478bd9Sstevel@tonic-gate * The first symbol table entry must be zeroed and is always ignored. 3637c478bd9Sstevel@tonic-gate */ 3647c478bd9Sstevel@tonic-gate bzero(sym, sizeof (Elf64_Sym)); 3657c478bd9Sstevel@tonic-gate sym++; 3667c478bd9Sstevel@tonic-gate 3677c478bd9Sstevel@tonic-gate /* 3687c478bd9Sstevel@tonic-gate * Take a second pass through the DOF sections filling in the 3697c478bd9Sstevel@tonic-gate * memory we allocated. 3707c478bd9Sstevel@tonic-gate */ 3717c478bd9Sstevel@tonic-gate for (i = 0; i < dof->dofh_secnum; i++) { 3727c478bd9Sstevel@tonic-gate if (dofs[i].dofs_type != DOF_SECT_URELHDR) 3737c478bd9Sstevel@tonic-gate continue; 3747c478bd9Sstevel@tonic-gate 3757c478bd9Sstevel@tonic-gate /*LINTED*/ 3767c478bd9Sstevel@tonic-gate dofrh = (dof_relohdr_t *)((char *)dof + dofs[i].dofs_offset); 3777c478bd9Sstevel@tonic-gate 3787c478bd9Sstevel@tonic-gate s = &dofs[dofrh->dofr_strtab]; 3797c478bd9Sstevel@tonic-gate strtab = (char *)dof + s->dofs_offset; 3807c478bd9Sstevel@tonic-gate bcopy(strtab + 1, dep->de_strtab + strtabsz, s->dofs_size); 3817c478bd9Sstevel@tonic-gate base = strtabsz; 3827c478bd9Sstevel@tonic-gate strtabsz += s->dofs_size - 1; 3837c478bd9Sstevel@tonic-gate 3847c478bd9Sstevel@tonic-gate s = &dofs[dofrh->dofr_relsec]; 3857c478bd9Sstevel@tonic-gate /*LINTED*/ 3867c478bd9Sstevel@tonic-gate dofr = (dof_relodesc_t *)((char *)dof + s->dofs_offset); 3877c478bd9Sstevel@tonic-gate nrel = s->dofs_size / s->dofs_entsize; 3887c478bd9Sstevel@tonic-gate 3897c478bd9Sstevel@tonic-gate s = &dofs[dofrh->dofr_tgtsec]; 3907c478bd9Sstevel@tonic-gate 3917c478bd9Sstevel@tonic-gate for (j = 0; j < nrel; j++) { 3927c478bd9Sstevel@tonic-gate #if defined(__i386) || defined(__amd64) 3937c478bd9Sstevel@tonic-gate rel->r_offset = s->dofs_offset + 3947c478bd9Sstevel@tonic-gate dofr[j].dofr_offset; 3957c478bd9Sstevel@tonic-gate rel->r_info = ELF64_R_INFO(count + dep->de_global, 3967c478bd9Sstevel@tonic-gate R_AMD64_64); 3977c478bd9Sstevel@tonic-gate #elif defined(__sparc) 3987c478bd9Sstevel@tonic-gate rel->r_offset = s->dofs_offset + 3997c478bd9Sstevel@tonic-gate dofr[j].dofr_offset; 4007c478bd9Sstevel@tonic-gate rel->r_info = ELF64_R_INFO(count + dep->de_global, 4017c478bd9Sstevel@tonic-gate R_SPARC_64); 4027c478bd9Sstevel@tonic-gate #else 4037c478bd9Sstevel@tonic-gate #error unknown ISA 4047c478bd9Sstevel@tonic-gate #endif 4057c478bd9Sstevel@tonic-gate 4067c478bd9Sstevel@tonic-gate sym->st_name = base + dofr[j].dofr_name - 1; 4077c478bd9Sstevel@tonic-gate sym->st_value = 0; 4087c478bd9Sstevel@tonic-gate sym->st_size = 0; 4094b499cecSahl sym->st_info = GELF_ST_INFO(STB_GLOBAL, STT_FUNC); 4107c478bd9Sstevel@tonic-gate sym->st_other = 0; 4117c478bd9Sstevel@tonic-gate sym->st_shndx = SHN_UNDEF; 4127c478bd9Sstevel@tonic-gate 4137c478bd9Sstevel@tonic-gate rel++; 4147c478bd9Sstevel@tonic-gate sym++; 4157c478bd9Sstevel@tonic-gate count++; 4167c478bd9Sstevel@tonic-gate } 4177c478bd9Sstevel@tonic-gate } 4187c478bd9Sstevel@tonic-gate 4197c478bd9Sstevel@tonic-gate /* 4207c478bd9Sstevel@tonic-gate * Add a symbol for the DOF itself. We use a different symbol for 4217c478bd9Sstevel@tonic-gate * lazily and actively loaded DOF to make them easy to distinguish. 4227c478bd9Sstevel@tonic-gate */ 4237c478bd9Sstevel@tonic-gate sym->st_name = strtabsz; 4247c478bd9Sstevel@tonic-gate sym->st_value = 0; 4257c478bd9Sstevel@tonic-gate sym->st_size = dof->dofh_filesz; 4264b499cecSahl sym->st_info = GELF_ST_INFO(STB_GLOBAL, STT_OBJECT); 4277c478bd9Sstevel@tonic-gate sym->st_other = 0; 4287c478bd9Sstevel@tonic-gate sym->st_shndx = ESHDR_DOF; 4297c478bd9Sstevel@tonic-gate sym++; 4307c478bd9Sstevel@tonic-gate 4317c478bd9Sstevel@tonic-gate if (dtp->dt_lazyload) { 4327c478bd9Sstevel@tonic-gate bcopy(DOFLAZYSTR, dep->de_strtab + strtabsz, 4337c478bd9Sstevel@tonic-gate sizeof (DOFLAZYSTR)); 4347c478bd9Sstevel@tonic-gate strtabsz += sizeof (DOFLAZYSTR); 4357c478bd9Sstevel@tonic-gate } else { 4367c478bd9Sstevel@tonic-gate bcopy(DOFSTR, dep->de_strtab + strtabsz, sizeof (DOFSTR)); 4377c478bd9Sstevel@tonic-gate strtabsz += sizeof (DOFSTR); 4387c478bd9Sstevel@tonic-gate } 4397c478bd9Sstevel@tonic-gate 4407c478bd9Sstevel@tonic-gate assert(count == dep->de_nrel); 4417c478bd9Sstevel@tonic-gate assert(strtabsz == dep->de_strlen); 4427c478bd9Sstevel@tonic-gate 4437c478bd9Sstevel@tonic-gate return (0); 4447c478bd9Sstevel@tonic-gate } 4457c478bd9Sstevel@tonic-gate 4467c478bd9Sstevel@tonic-gate /* 4477c478bd9Sstevel@tonic-gate * Write out an ELF32 file prologue consisting of a header, section headers, 4487c478bd9Sstevel@tonic-gate * and a section header string table. The DOF data will follow this prologue 4497c478bd9Sstevel@tonic-gate * and complete the contents of the given ELF file. 4507c478bd9Sstevel@tonic-gate */ 4517c478bd9Sstevel@tonic-gate static int 4527c478bd9Sstevel@tonic-gate dump_elf32(dtrace_hdl_t *dtp, const dof_hdr_t *dof, int fd) 4537c478bd9Sstevel@tonic-gate { 4547c478bd9Sstevel@tonic-gate struct { 4557c478bd9Sstevel@tonic-gate Elf32_Ehdr ehdr; 4567c478bd9Sstevel@tonic-gate Elf32_Shdr shdr[ESHDR_NUM]; 4577c478bd9Sstevel@tonic-gate } elf_file; 4587c478bd9Sstevel@tonic-gate 4597c478bd9Sstevel@tonic-gate Elf32_Shdr *shp; 4607c478bd9Sstevel@tonic-gate Elf32_Off off; 4617c478bd9Sstevel@tonic-gate dof_elf32_t de; 4627c478bd9Sstevel@tonic-gate int ret = 0; 4637c478bd9Sstevel@tonic-gate uint_t nshdr; 4647c478bd9Sstevel@tonic-gate 4657c478bd9Sstevel@tonic-gate if (prepare_elf32(dtp, dof, &de) != 0) 4667c478bd9Sstevel@tonic-gate return (-1); /* errno is set for us */ 4677c478bd9Sstevel@tonic-gate 4687c478bd9Sstevel@tonic-gate /* 4697c478bd9Sstevel@tonic-gate * If there are no relocations, we only need enough sections for 4707c478bd9Sstevel@tonic-gate * the shstrtab and the DOF. 4717c478bd9Sstevel@tonic-gate */ 4727c478bd9Sstevel@tonic-gate nshdr = de.de_nrel == 0 ? ESHDR_SYMTAB + 1 : ESHDR_NUM; 4737c478bd9Sstevel@tonic-gate 4747c478bd9Sstevel@tonic-gate bzero(&elf_file, sizeof (elf_file)); 4757c478bd9Sstevel@tonic-gate 4767c478bd9Sstevel@tonic-gate elf_file.ehdr.e_ident[EI_MAG0] = ELFMAG0; 4777c478bd9Sstevel@tonic-gate elf_file.ehdr.e_ident[EI_MAG1] = ELFMAG1; 4787c478bd9Sstevel@tonic-gate elf_file.ehdr.e_ident[EI_MAG2] = ELFMAG2; 4797c478bd9Sstevel@tonic-gate elf_file.ehdr.e_ident[EI_MAG3] = ELFMAG3; 4807c478bd9Sstevel@tonic-gate elf_file.ehdr.e_ident[EI_VERSION] = EV_CURRENT; 4817c478bd9Sstevel@tonic-gate elf_file.ehdr.e_ident[EI_CLASS] = ELFCLASS32; 4827c478bd9Sstevel@tonic-gate #if defined(_BIG_ENDIAN) 4837c478bd9Sstevel@tonic-gate elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2MSB; 4847c478bd9Sstevel@tonic-gate #elif defined(_LITTLE_ENDIAN) 4857c478bd9Sstevel@tonic-gate elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2LSB; 4867c478bd9Sstevel@tonic-gate #endif 4877c478bd9Sstevel@tonic-gate elf_file.ehdr.e_type = ET_REL; 4887c478bd9Sstevel@tonic-gate #if defined(__sparc) 4897c478bd9Sstevel@tonic-gate elf_file.ehdr.e_machine = EM_SPARC; 4907c478bd9Sstevel@tonic-gate #elif defined(__i386) || defined(__amd64) 4917c478bd9Sstevel@tonic-gate elf_file.ehdr.e_machine = EM_386; 4927c478bd9Sstevel@tonic-gate #endif 4937c478bd9Sstevel@tonic-gate elf_file.ehdr.e_version = EV_CURRENT; 4947c478bd9Sstevel@tonic-gate elf_file.ehdr.e_shoff = sizeof (Elf32_Ehdr); 4957c478bd9Sstevel@tonic-gate elf_file.ehdr.e_ehsize = sizeof (Elf32_Ehdr); 4967c478bd9Sstevel@tonic-gate elf_file.ehdr.e_phentsize = sizeof (Elf32_Phdr); 4977c478bd9Sstevel@tonic-gate elf_file.ehdr.e_shentsize = sizeof (Elf32_Shdr); 4987c478bd9Sstevel@tonic-gate elf_file.ehdr.e_shnum = nshdr; 4997c478bd9Sstevel@tonic-gate elf_file.ehdr.e_shstrndx = ESHDR_SHSTRTAB; 5007c478bd9Sstevel@tonic-gate off = sizeof (elf_file) + nshdr * sizeof (Elf32_Shdr); 5017c478bd9Sstevel@tonic-gate 5027c478bd9Sstevel@tonic-gate shp = &elf_file.shdr[ESHDR_SHSTRTAB]; 5037c478bd9Sstevel@tonic-gate shp->sh_name = 1; /* DTRACE_SHSTRTAB32[1] = ".shstrtab" */ 5047c478bd9Sstevel@tonic-gate shp->sh_type = SHT_STRTAB; 5057c478bd9Sstevel@tonic-gate shp->sh_offset = off; 5067c478bd9Sstevel@tonic-gate shp->sh_size = sizeof (DTRACE_SHSTRTAB32); 5077c478bd9Sstevel@tonic-gate shp->sh_addralign = sizeof (char); 5087c478bd9Sstevel@tonic-gate off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 8); 5097c478bd9Sstevel@tonic-gate 5107c478bd9Sstevel@tonic-gate shp = &elf_file.shdr[ESHDR_DOF]; 5117c478bd9Sstevel@tonic-gate shp->sh_name = 11; /* DTRACE_SHSTRTAB32[11] = ".SUNW_dof" */ 5127c478bd9Sstevel@tonic-gate shp->sh_flags = SHF_ALLOC; 5137c478bd9Sstevel@tonic-gate shp->sh_type = SHT_SUNW_dof; 5147c478bd9Sstevel@tonic-gate shp->sh_offset = off; 5157c478bd9Sstevel@tonic-gate shp->sh_size = dof->dofh_filesz; 5167c478bd9Sstevel@tonic-gate shp->sh_addralign = 8; 5177c478bd9Sstevel@tonic-gate off = shp->sh_offset + shp->sh_size; 5187c478bd9Sstevel@tonic-gate 5197c478bd9Sstevel@tonic-gate shp = &elf_file.shdr[ESHDR_STRTAB]; 5207c478bd9Sstevel@tonic-gate shp->sh_name = 21; /* DTRACE_SHSTRTAB32[21] = ".strtab" */ 5217c478bd9Sstevel@tonic-gate shp->sh_flags = SHF_ALLOC; 5227c478bd9Sstevel@tonic-gate shp->sh_type = SHT_STRTAB; 5237c478bd9Sstevel@tonic-gate shp->sh_offset = off; 5247c478bd9Sstevel@tonic-gate shp->sh_size = de.de_strlen; 5257c478bd9Sstevel@tonic-gate shp->sh_addralign = sizeof (char); 5267c478bd9Sstevel@tonic-gate off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 4); 5277c478bd9Sstevel@tonic-gate 5287c478bd9Sstevel@tonic-gate shp = &elf_file.shdr[ESHDR_SYMTAB]; 5297c478bd9Sstevel@tonic-gate shp->sh_name = 29; /* DTRACE_SHSTRTAB32[29] = ".symtab" */ 5307c478bd9Sstevel@tonic-gate shp->sh_flags = SHF_ALLOC; 5317c478bd9Sstevel@tonic-gate shp->sh_type = SHT_SYMTAB; 5327c478bd9Sstevel@tonic-gate shp->sh_entsize = sizeof (Elf32_Sym); 5337c478bd9Sstevel@tonic-gate shp->sh_link = ESHDR_STRTAB; 5347c478bd9Sstevel@tonic-gate shp->sh_offset = off; 5357c478bd9Sstevel@tonic-gate shp->sh_info = de.de_global; 5367c478bd9Sstevel@tonic-gate shp->sh_size = de.de_nsym * sizeof (Elf32_Sym); 5377c478bd9Sstevel@tonic-gate shp->sh_addralign = 4; 5387c478bd9Sstevel@tonic-gate off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 4); 5397c478bd9Sstevel@tonic-gate 5407c478bd9Sstevel@tonic-gate if (de.de_nrel == 0) { 5417c478bd9Sstevel@tonic-gate if (dt_write(dtp, fd, &elf_file, 5427c478bd9Sstevel@tonic-gate sizeof (elf_file)) != sizeof (elf_file) || 5437c478bd9Sstevel@tonic-gate PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB32) || 5447c478bd9Sstevel@tonic-gate PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) || 5457c478bd9Sstevel@tonic-gate PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) || 5467c478bd9Sstevel@tonic-gate PWRITE_SCN(ESHDR_DOF, dof)) { 5477c478bd9Sstevel@tonic-gate ret = dt_set_errno(dtp, errno); 5487c478bd9Sstevel@tonic-gate } 5497c478bd9Sstevel@tonic-gate } else { 5507c478bd9Sstevel@tonic-gate shp = &elf_file.shdr[ESHDR_REL]; 5517c478bd9Sstevel@tonic-gate shp->sh_name = 37; /* DTRACE_SHSTRTAB32[37] = ".rel.SUNW_dof" */ 5527c478bd9Sstevel@tonic-gate shp->sh_flags = SHF_ALLOC; 5537c478bd9Sstevel@tonic-gate #ifdef __sparc 5547c478bd9Sstevel@tonic-gate shp->sh_type = SHT_RELA; 5557c478bd9Sstevel@tonic-gate #else 5567c478bd9Sstevel@tonic-gate shp->sh_type = SHT_REL; 5577c478bd9Sstevel@tonic-gate #endif 5587c478bd9Sstevel@tonic-gate shp->sh_entsize = sizeof (de.de_rel[0]); 5597c478bd9Sstevel@tonic-gate shp->sh_link = ESHDR_SYMTAB; 5607c478bd9Sstevel@tonic-gate shp->sh_info = ESHDR_DOF; 5617c478bd9Sstevel@tonic-gate shp->sh_offset = off; 5627c478bd9Sstevel@tonic-gate shp->sh_size = de.de_nrel * sizeof (de.de_rel[0]); 5637c478bd9Sstevel@tonic-gate shp->sh_addralign = 4; 5647c478bd9Sstevel@tonic-gate 5657c478bd9Sstevel@tonic-gate if (dt_write(dtp, fd, &elf_file, 5667c478bd9Sstevel@tonic-gate sizeof (elf_file)) != sizeof (elf_file) || 5677c478bd9Sstevel@tonic-gate PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB32) || 5687c478bd9Sstevel@tonic-gate PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) || 5697c478bd9Sstevel@tonic-gate PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) || 5707c478bd9Sstevel@tonic-gate PWRITE_SCN(ESHDR_REL, de.de_rel) || 5717c478bd9Sstevel@tonic-gate PWRITE_SCN(ESHDR_DOF, dof)) { 5727c478bd9Sstevel@tonic-gate ret = dt_set_errno(dtp, errno); 5737c478bd9Sstevel@tonic-gate } 5747c478bd9Sstevel@tonic-gate } 5757c478bd9Sstevel@tonic-gate 5767c478bd9Sstevel@tonic-gate free(de.de_strtab); 5777c478bd9Sstevel@tonic-gate free(de.de_sym); 5787c478bd9Sstevel@tonic-gate free(de.de_rel); 5797c478bd9Sstevel@tonic-gate 5807c478bd9Sstevel@tonic-gate return (ret); 5817c478bd9Sstevel@tonic-gate } 5827c478bd9Sstevel@tonic-gate 5837c478bd9Sstevel@tonic-gate /* 5847c478bd9Sstevel@tonic-gate * Write out an ELF64 file prologue consisting of a header, section headers, 5857c478bd9Sstevel@tonic-gate * and a section header string table. The DOF data will follow this prologue 5867c478bd9Sstevel@tonic-gate * and complete the contents of the given ELF file. 5877c478bd9Sstevel@tonic-gate */ 5887c478bd9Sstevel@tonic-gate static int 5897c478bd9Sstevel@tonic-gate dump_elf64(dtrace_hdl_t *dtp, const dof_hdr_t *dof, int fd) 5907c478bd9Sstevel@tonic-gate { 5917c478bd9Sstevel@tonic-gate struct { 5927c478bd9Sstevel@tonic-gate Elf64_Ehdr ehdr; 5937c478bd9Sstevel@tonic-gate Elf64_Shdr shdr[ESHDR_NUM]; 5947c478bd9Sstevel@tonic-gate } elf_file; 5957c478bd9Sstevel@tonic-gate 5967c478bd9Sstevel@tonic-gate Elf64_Shdr *shp; 5977c478bd9Sstevel@tonic-gate Elf64_Off off; 5987c478bd9Sstevel@tonic-gate dof_elf64_t de; 5997c478bd9Sstevel@tonic-gate int ret = 0; 6007c478bd9Sstevel@tonic-gate uint_t nshdr; 6017c478bd9Sstevel@tonic-gate 6027c478bd9Sstevel@tonic-gate if (prepare_elf64(dtp, dof, &de) != 0) 6037c478bd9Sstevel@tonic-gate return (-1); /* errno is set for us */ 6047c478bd9Sstevel@tonic-gate 6057c478bd9Sstevel@tonic-gate /* 6067c478bd9Sstevel@tonic-gate * If there are no relocations, we only need enough sections for 6077c478bd9Sstevel@tonic-gate * the shstrtab and the DOF. 6087c478bd9Sstevel@tonic-gate */ 6097c478bd9Sstevel@tonic-gate nshdr = de.de_nrel == 0 ? ESHDR_SYMTAB + 1 : ESHDR_NUM; 6107c478bd9Sstevel@tonic-gate 6117c478bd9Sstevel@tonic-gate bzero(&elf_file, sizeof (elf_file)); 6127c478bd9Sstevel@tonic-gate 6137c478bd9Sstevel@tonic-gate elf_file.ehdr.e_ident[EI_MAG0] = ELFMAG0; 6147c478bd9Sstevel@tonic-gate elf_file.ehdr.e_ident[EI_MAG1] = ELFMAG1; 6157c478bd9Sstevel@tonic-gate elf_file.ehdr.e_ident[EI_MAG2] = ELFMAG2; 6167c478bd9Sstevel@tonic-gate elf_file.ehdr.e_ident[EI_MAG3] = ELFMAG3; 6177c478bd9Sstevel@tonic-gate elf_file.ehdr.e_ident[EI_VERSION] = EV_CURRENT; 6187c478bd9Sstevel@tonic-gate elf_file.ehdr.e_ident[EI_CLASS] = ELFCLASS64; 6197c478bd9Sstevel@tonic-gate #if defined(_BIG_ENDIAN) 6207c478bd9Sstevel@tonic-gate elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2MSB; 6217c478bd9Sstevel@tonic-gate #elif defined(_LITTLE_ENDIAN) 6227c478bd9Sstevel@tonic-gate elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2LSB; 6237c478bd9Sstevel@tonic-gate #endif 6247c478bd9Sstevel@tonic-gate elf_file.ehdr.e_type = ET_REL; 6257c478bd9Sstevel@tonic-gate #if defined(__sparc) 6267c478bd9Sstevel@tonic-gate elf_file.ehdr.e_machine = EM_SPARCV9; 6277c478bd9Sstevel@tonic-gate #elif defined(__i386) || defined(__amd64) 6287c478bd9Sstevel@tonic-gate elf_file.ehdr.e_machine = EM_AMD64; 6297c478bd9Sstevel@tonic-gate #endif 6307c478bd9Sstevel@tonic-gate elf_file.ehdr.e_version = EV_CURRENT; 6317c478bd9Sstevel@tonic-gate elf_file.ehdr.e_shoff = sizeof (Elf64_Ehdr); 6327c478bd9Sstevel@tonic-gate elf_file.ehdr.e_ehsize = sizeof (Elf64_Ehdr); 6337c478bd9Sstevel@tonic-gate elf_file.ehdr.e_phentsize = sizeof (Elf64_Phdr); 6347c478bd9Sstevel@tonic-gate elf_file.ehdr.e_shentsize = sizeof (Elf64_Shdr); 6357c478bd9Sstevel@tonic-gate elf_file.ehdr.e_shnum = nshdr; 6367c478bd9Sstevel@tonic-gate elf_file.ehdr.e_shstrndx = ESHDR_SHSTRTAB; 6377c478bd9Sstevel@tonic-gate off = sizeof (elf_file) + nshdr * sizeof (Elf64_Shdr); 6387c478bd9Sstevel@tonic-gate 6397c478bd9Sstevel@tonic-gate shp = &elf_file.shdr[ESHDR_SHSTRTAB]; 6407c478bd9Sstevel@tonic-gate shp->sh_name = 1; /* DTRACE_SHSTRTAB64[1] = ".shstrtab" */ 6417c478bd9Sstevel@tonic-gate shp->sh_type = SHT_STRTAB; 6427c478bd9Sstevel@tonic-gate shp->sh_offset = off; 6437c478bd9Sstevel@tonic-gate shp->sh_size = sizeof (DTRACE_SHSTRTAB64); 6447c478bd9Sstevel@tonic-gate shp->sh_addralign = sizeof (char); 6457c478bd9Sstevel@tonic-gate off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 8); 6467c478bd9Sstevel@tonic-gate 6477c478bd9Sstevel@tonic-gate shp = &elf_file.shdr[ESHDR_DOF]; 6487c478bd9Sstevel@tonic-gate shp->sh_name = 11; /* DTRACE_SHSTRTAB64[11] = ".SUNW_dof" */ 6497c478bd9Sstevel@tonic-gate shp->sh_flags = SHF_ALLOC; 6507c478bd9Sstevel@tonic-gate shp->sh_type = SHT_SUNW_dof; 6517c478bd9Sstevel@tonic-gate shp->sh_offset = off; 6527c478bd9Sstevel@tonic-gate shp->sh_size = dof->dofh_filesz; 6537c478bd9Sstevel@tonic-gate shp->sh_addralign = 8; 6547c478bd9Sstevel@tonic-gate off = shp->sh_offset + shp->sh_size; 6557c478bd9Sstevel@tonic-gate 6567c478bd9Sstevel@tonic-gate shp = &elf_file.shdr[ESHDR_STRTAB]; 6577c478bd9Sstevel@tonic-gate shp->sh_name = 21; /* DTRACE_SHSTRTAB64[21] = ".strtab" */ 6587c478bd9Sstevel@tonic-gate shp->sh_flags = SHF_ALLOC; 6597c478bd9Sstevel@tonic-gate shp->sh_type = SHT_STRTAB; 6607c478bd9Sstevel@tonic-gate shp->sh_offset = off; 6617c478bd9Sstevel@tonic-gate shp->sh_size = de.de_strlen; 6627c478bd9Sstevel@tonic-gate shp->sh_addralign = sizeof (char); 6637c478bd9Sstevel@tonic-gate off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 8); 6647c478bd9Sstevel@tonic-gate 6657c478bd9Sstevel@tonic-gate shp = &elf_file.shdr[ESHDR_SYMTAB]; 6667c478bd9Sstevel@tonic-gate shp->sh_name = 29; /* DTRACE_SHSTRTAB64[29] = ".symtab" */ 6677c478bd9Sstevel@tonic-gate shp->sh_flags = SHF_ALLOC; 6687c478bd9Sstevel@tonic-gate shp->sh_type = SHT_SYMTAB; 6697c478bd9Sstevel@tonic-gate shp->sh_entsize = sizeof (Elf64_Sym); 6707c478bd9Sstevel@tonic-gate shp->sh_link = ESHDR_STRTAB; 6717c478bd9Sstevel@tonic-gate shp->sh_offset = off; 6727c478bd9Sstevel@tonic-gate shp->sh_info = de.de_global; 6737c478bd9Sstevel@tonic-gate shp->sh_size = de.de_nsym * sizeof (Elf64_Sym); 6747c478bd9Sstevel@tonic-gate shp->sh_addralign = 8; 6757c478bd9Sstevel@tonic-gate off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 8); 6767c478bd9Sstevel@tonic-gate 6777c478bd9Sstevel@tonic-gate if (de.de_nrel == 0) { 6787c478bd9Sstevel@tonic-gate if (dt_write(dtp, fd, &elf_file, 6797c478bd9Sstevel@tonic-gate sizeof (elf_file)) != sizeof (elf_file) || 6807c478bd9Sstevel@tonic-gate PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB64) || 6817c478bd9Sstevel@tonic-gate PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) || 6827c478bd9Sstevel@tonic-gate PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) || 6837c478bd9Sstevel@tonic-gate PWRITE_SCN(ESHDR_DOF, dof)) { 6847c478bd9Sstevel@tonic-gate ret = dt_set_errno(dtp, errno); 6857c478bd9Sstevel@tonic-gate } 6867c478bd9Sstevel@tonic-gate } else { 6877c478bd9Sstevel@tonic-gate shp = &elf_file.shdr[ESHDR_REL]; 6887c478bd9Sstevel@tonic-gate shp->sh_name = 37; /* DTRACE_SHSTRTAB64[37] = ".rel.SUNW_dof" */ 6897c478bd9Sstevel@tonic-gate shp->sh_flags = SHF_ALLOC; 6907c478bd9Sstevel@tonic-gate shp->sh_type = SHT_RELA; 6917c478bd9Sstevel@tonic-gate shp->sh_entsize = sizeof (de.de_rel[0]); 6927c478bd9Sstevel@tonic-gate shp->sh_link = ESHDR_SYMTAB; 6937c478bd9Sstevel@tonic-gate shp->sh_info = ESHDR_DOF; 6947c478bd9Sstevel@tonic-gate shp->sh_offset = off; 6957c478bd9Sstevel@tonic-gate shp->sh_size = de.de_nrel * sizeof (de.de_rel[0]); 6967c478bd9Sstevel@tonic-gate shp->sh_addralign = 8; 6977c478bd9Sstevel@tonic-gate 6987c478bd9Sstevel@tonic-gate if (dt_write(dtp, fd, &elf_file, 6997c478bd9Sstevel@tonic-gate sizeof (elf_file)) != sizeof (elf_file) || 7007c478bd9Sstevel@tonic-gate PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB64) || 7017c478bd9Sstevel@tonic-gate PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) || 7027c478bd9Sstevel@tonic-gate PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) || 7037c478bd9Sstevel@tonic-gate PWRITE_SCN(ESHDR_REL, de.de_rel) || 7047c478bd9Sstevel@tonic-gate PWRITE_SCN(ESHDR_DOF, dof)) { 7057c478bd9Sstevel@tonic-gate ret = dt_set_errno(dtp, errno); 7067c478bd9Sstevel@tonic-gate } 7077c478bd9Sstevel@tonic-gate } 7087c478bd9Sstevel@tonic-gate 7097c478bd9Sstevel@tonic-gate free(de.de_strtab); 7107c478bd9Sstevel@tonic-gate free(de.de_sym); 7117c478bd9Sstevel@tonic-gate free(de.de_rel); 7127c478bd9Sstevel@tonic-gate 7137c478bd9Sstevel@tonic-gate return (ret); 7147c478bd9Sstevel@tonic-gate } 7157c478bd9Sstevel@tonic-gate 7167c478bd9Sstevel@tonic-gate static int 71773427c57Sahl dt_symtab_lookup(Elf_Data *data_sym, int nsym, uintptr_t addr, uint_t shn, 71873427c57Sahl GElf_Sym *sym) 7197c478bd9Sstevel@tonic-gate { 7207c478bd9Sstevel@tonic-gate int i, ret = -1; 7217c478bd9Sstevel@tonic-gate GElf_Sym s; 7227c478bd9Sstevel@tonic-gate 72373427c57Sahl for (i = 0; i < nsym && gelf_getsym(data_sym, i, sym) != NULL; i++) { 7247c478bd9Sstevel@tonic-gate if (GELF_ST_TYPE(sym->st_info) == STT_FUNC && 7257c478bd9Sstevel@tonic-gate shn == sym->st_shndx && 7267c478bd9Sstevel@tonic-gate sym->st_value <= addr && 7277c478bd9Sstevel@tonic-gate addr < sym->st_value + sym->st_size) { 7287c478bd9Sstevel@tonic-gate if (GELF_ST_BIND(sym->st_info) == STB_GLOBAL) 7297c478bd9Sstevel@tonic-gate return (0); 7307c478bd9Sstevel@tonic-gate 7317c478bd9Sstevel@tonic-gate ret = 0; 7327c478bd9Sstevel@tonic-gate s = *sym; 7337c478bd9Sstevel@tonic-gate } 7347c478bd9Sstevel@tonic-gate } 7357c478bd9Sstevel@tonic-gate 7367c478bd9Sstevel@tonic-gate if (ret == 0) 7377c478bd9Sstevel@tonic-gate *sym = s; 7387c478bd9Sstevel@tonic-gate return (ret); 7397c478bd9Sstevel@tonic-gate } 7407c478bd9Sstevel@tonic-gate 7417c478bd9Sstevel@tonic-gate #if defined(__sparc) 7427c478bd9Sstevel@tonic-gate 7437c478bd9Sstevel@tonic-gate #define DT_OP_RET 0x81c7e008 7447c478bd9Sstevel@tonic-gate #define DT_OP_NOP 0x01000000 7457c478bd9Sstevel@tonic-gate #define DT_OP_CALL 0x40000000 746ac448965Sahl #define DT_OP_CLR_O0 0x90102000 7477c478bd9Sstevel@tonic-gate 7487c478bd9Sstevel@tonic-gate #define DT_IS_MOV_O7(inst) (((inst) & 0xffffe000) == 0x9e100000) 7497c478bd9Sstevel@tonic-gate #define DT_IS_RESTORE(inst) (((inst) & 0xc1f80000) == 0x81e80000) 7507c478bd9Sstevel@tonic-gate #define DT_IS_RETL(inst) (((inst) & 0xfff83fff) == 0x81c02008) 7517c478bd9Sstevel@tonic-gate 7527c478bd9Sstevel@tonic-gate #define DT_RS2(inst) ((inst) & 0x1f) 7537c478bd9Sstevel@tonic-gate #define DT_MAKE_RETL(reg) (0x81c02008 | ((reg) << 14)) 7547c478bd9Sstevel@tonic-gate 755ac448965Sahl /*ARGSUSED*/ 7567c478bd9Sstevel@tonic-gate static int 757ac448965Sahl dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela, 758ac448965Sahl uint32_t *off) 7597c478bd9Sstevel@tonic-gate { 7607c478bd9Sstevel@tonic-gate uint32_t *ip; 7617c478bd9Sstevel@tonic-gate 7627c478bd9Sstevel@tonic-gate if ((rela->r_offset & (sizeof (uint32_t) - 1)) != 0) 7637c478bd9Sstevel@tonic-gate return (-1); 7647c478bd9Sstevel@tonic-gate 7657c478bd9Sstevel@tonic-gate /*LINTED*/ 7667c478bd9Sstevel@tonic-gate ip = (uint32_t *)(p + rela->r_offset); 7677c478bd9Sstevel@tonic-gate 7687c478bd9Sstevel@tonic-gate /* 7697c478bd9Sstevel@tonic-gate * We only know about some specific relocation types. 7707c478bd9Sstevel@tonic-gate */ 7717c478bd9Sstevel@tonic-gate if (GELF_R_TYPE(rela->r_info) != R_SPARC_WDISP30 && 7727c478bd9Sstevel@tonic-gate GELF_R_TYPE(rela->r_info) != R_SPARC_WPLT30) 7737c478bd9Sstevel@tonic-gate return (-1); 7747c478bd9Sstevel@tonic-gate 7757c478bd9Sstevel@tonic-gate /* 776ac448965Sahl * We may have already processed this object file in an earlier linker 777ac448965Sahl * invocation. Check to see if the present instruction sequence matches 7780bac14eaSahl * the one we would install below. 7797c478bd9Sstevel@tonic-gate */ 780ac448965Sahl if (isenabled) { 7810bac14eaSahl if (ip[0] == DT_OP_NOP) { 7820bac14eaSahl (*off) += sizeof (ip[0]); 783ac448965Sahl return (0); 7840bac14eaSahl } 785ac448965Sahl } else { 7867c478bd9Sstevel@tonic-gate if (DT_IS_RESTORE(ip[1])) { 7870bac14eaSahl if (ip[0] == DT_OP_RET) { 7880bac14eaSahl (*off) += sizeof (ip[0]); 7897c478bd9Sstevel@tonic-gate return (0); 7900bac14eaSahl } 7917c478bd9Sstevel@tonic-gate } else if (DT_IS_MOV_O7(ip[1])) { 792ac448965Sahl if (DT_IS_RETL(ip[0])) 7937c478bd9Sstevel@tonic-gate return (0); 7947c478bd9Sstevel@tonic-gate } else { 7957c478bd9Sstevel@tonic-gate if (ip[0] == DT_OP_NOP) { 7967c478bd9Sstevel@tonic-gate (*off) += sizeof (ip[0]); 7977c478bd9Sstevel@tonic-gate return (0); 7987c478bd9Sstevel@tonic-gate } 7997c478bd9Sstevel@tonic-gate } 800ac448965Sahl } 8017c478bd9Sstevel@tonic-gate 8027c478bd9Sstevel@tonic-gate /* 8037c478bd9Sstevel@tonic-gate * We only expect call instructions with a displacement of 0. 8047c478bd9Sstevel@tonic-gate */ 8057c478bd9Sstevel@tonic-gate if (ip[0] != DT_OP_CALL) { 8067c478bd9Sstevel@tonic-gate dt_dprintf("found %x instead of a call instruction at %llx\n", 8077c478bd9Sstevel@tonic-gate ip[0], (u_longlong_t)rela->r_offset); 8087c478bd9Sstevel@tonic-gate return (-1); 8097c478bd9Sstevel@tonic-gate } 8107c478bd9Sstevel@tonic-gate 811ac448965Sahl if (isenabled) { 8127c478bd9Sstevel@tonic-gate /* 813ac448965Sahl * It would necessarily indicate incorrect usage if an is- 814ac448965Sahl * enabled probe were tail-called so flag that as an error. 815ac448965Sahl * It's also potentially (very) tricky to handle gracefully, 816ac448965Sahl * but could be done if this were a desired use scenario. 817ac448965Sahl */ 818ac448965Sahl if (DT_IS_RESTORE(ip[1]) || DT_IS_MOV_O7(ip[1])) { 819ac448965Sahl dt_dprintf("tail call to is-enabled probe at %llx\n", 820ac448965Sahl (u_longlong_t)rela->r_offset); 821ac448965Sahl return (-1); 822ac448965Sahl } 823ac448965Sahl 8240bac14eaSahl 8250bac14eaSahl /* 8260bac14eaSahl * On SPARC, we take advantage of the fact that the first 8270bac14eaSahl * argument shares the same register as for the return value. 8280bac14eaSahl * The macro handles the work of zeroing that register so we 8290bac14eaSahl * don't need to do anything special here. We instrument the 8300bac14eaSahl * instruction in the delay slot as we'll need to modify the 8310bac14eaSahl * return register after that instruction has been emulated. 8320bac14eaSahl */ 8330bac14eaSahl ip[0] = DT_OP_NOP; 8340bac14eaSahl (*off) += sizeof (ip[0]); 835ac448965Sahl } else { 836ac448965Sahl /* 837ac448965Sahl * If the call is followed by a restore, it's a tail call so 838ac448965Sahl * change the call to a ret. If the call if followed by a mov 839ac448965Sahl * of a register into %o7, it's a tail call in leaf context 840ac448965Sahl * so change the call to a retl-like instruction that returns 841ac448965Sahl * to that register value + 8 (rather than the typical %o7 + 84292e807e6Sahl * 8); the delay slot instruction is left, but should have no 8430bac14eaSahl * effect. Otherwise we change the call to be a nop. We 8440bac14eaSahl * identify the subsequent instruction as the probe point in 8450bac14eaSahl * all but the leaf tail-call case to ensure that arguments to 8460bac14eaSahl * the probe are complete and consistent. An astute, though 8470bac14eaSahl * largely hypothetical, observer would note that there is the 8480bac14eaSahl * possibility of a false-positive probe firing if the function 8490bac14eaSahl * contained a branch to the instruction in the delay slot of 8500bac14eaSahl * the call. Fixing this would require significant in-kernel 8510bac14eaSahl * modifications, and isn't worth doing until we see it in the 8520bac14eaSahl * wild. 8537c478bd9Sstevel@tonic-gate */ 8547c478bd9Sstevel@tonic-gate if (DT_IS_RESTORE(ip[1])) { 8557c478bd9Sstevel@tonic-gate ip[0] = DT_OP_RET; 85692e807e6Sahl (*off) += sizeof (ip[0]); 8577c478bd9Sstevel@tonic-gate } else if (DT_IS_MOV_O7(ip[1])) { 8587c478bd9Sstevel@tonic-gate ip[0] = DT_MAKE_RETL(DT_RS2(ip[1])); 8597c478bd9Sstevel@tonic-gate } else { 8607c478bd9Sstevel@tonic-gate ip[0] = DT_OP_NOP; 8617c478bd9Sstevel@tonic-gate (*off) += sizeof (ip[0]); 8627c478bd9Sstevel@tonic-gate } 863ac448965Sahl } 8647c478bd9Sstevel@tonic-gate 8657c478bd9Sstevel@tonic-gate return (0); 8667c478bd9Sstevel@tonic-gate } 8677c478bd9Sstevel@tonic-gate 8687c478bd9Sstevel@tonic-gate #elif defined(__i386) || defined(__amd64) 8697c478bd9Sstevel@tonic-gate 8707c478bd9Sstevel@tonic-gate #define DT_OP_NOP 0x90 8716009dbc6Sahl #define DT_OP_RET 0xc3 8727c478bd9Sstevel@tonic-gate #define DT_OP_CALL 0xe8 8736009dbc6Sahl #define DT_OP_JMP32 0xe9 874ac448965Sahl #define DT_OP_REX_RAX 0x48 875ac448965Sahl #define DT_OP_XOR_EAX_0 0x33 876ac448965Sahl #define DT_OP_XOR_EAX_1 0xc0 8777c478bd9Sstevel@tonic-gate 8787c478bd9Sstevel@tonic-gate static int 879ac448965Sahl dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela, 880ac448965Sahl uint32_t *off) 8817c478bd9Sstevel@tonic-gate { 8827c478bd9Sstevel@tonic-gate uint8_t *ip = (uint8_t *)(p + rela->r_offset - 1); 8836009dbc6Sahl uint8_t ret; 8847c478bd9Sstevel@tonic-gate 8857c478bd9Sstevel@tonic-gate /* 8867c478bd9Sstevel@tonic-gate * On x86, the first byte of the instruction is the call opcode and 8877c478bd9Sstevel@tonic-gate * the next four bytes are the 32-bit address; the relocation is for 888ac448965Sahl * the address operand. We back up the offset to the first byte of 889ac448965Sahl * the instruction. For is-enabled probes, we later advance the offset 890ac448965Sahl * so that it hits the first nop in the instruction sequence. 8917c478bd9Sstevel@tonic-gate */ 8927c478bd9Sstevel@tonic-gate (*off) -= 1; 8937c478bd9Sstevel@tonic-gate 8947c478bd9Sstevel@tonic-gate /* 8957c478bd9Sstevel@tonic-gate * We only know about some specific relocation types. Luckily 8967c478bd9Sstevel@tonic-gate * these types have the same values on both 32-bit and 64-bit 8977c478bd9Sstevel@tonic-gate * x86 architectures. 8987c478bd9Sstevel@tonic-gate */ 8997c478bd9Sstevel@tonic-gate if (GELF_R_TYPE(rela->r_info) != R_386_PC32 && 9007c478bd9Sstevel@tonic-gate GELF_R_TYPE(rela->r_info) != R_386_PLT32) 9017c478bd9Sstevel@tonic-gate return (-1); 9027c478bd9Sstevel@tonic-gate 9037c478bd9Sstevel@tonic-gate /* 904ac448965Sahl * We may have already processed this object file in an earlier linker 905ac448965Sahl * invocation. Check to see if the present instruction sequence matches 906ac448965Sahl * the one we would install. For is-enabled probes, we advance the 9076009dbc6Sahl * offset to the first nop instruction in the sequence to match the 9086009dbc6Sahl * text modification code below. 9097c478bd9Sstevel@tonic-gate */ 910ac448965Sahl if (!isenabled) { 9116009dbc6Sahl if ((ip[0] == DT_OP_NOP || ip[0] == DT_OP_RET) && 9126009dbc6Sahl ip[1] == DT_OP_NOP && ip[2] == DT_OP_NOP && 9136009dbc6Sahl ip[3] == DT_OP_NOP && ip[4] == DT_OP_NOP) 9147c478bd9Sstevel@tonic-gate return (0); 915ac448965Sahl } else if (dtp->dt_oflags & DTRACE_O_LP64) { 916ac448965Sahl if (ip[0] == DT_OP_REX_RAX && 917ac448965Sahl ip[1] == DT_OP_XOR_EAX_0 && ip[2] == DT_OP_XOR_EAX_1 && 9186009dbc6Sahl (ip[3] == DT_OP_NOP || ip[3] == DT_OP_RET) && 9196009dbc6Sahl ip[4] == DT_OP_NOP) { 920ac448965Sahl (*off) += 3; 921ac448965Sahl return (0); 922ac448965Sahl } 923ac448965Sahl } else { 924ac448965Sahl if (ip[0] == DT_OP_XOR_EAX_0 && ip[1] == DT_OP_XOR_EAX_1 && 9256009dbc6Sahl (ip[2] == DT_OP_NOP || ip[2] == DT_OP_RET) && 9266009dbc6Sahl ip[3] == DT_OP_NOP && ip[4] == DT_OP_NOP) { 927ac448965Sahl (*off) += 2; 928ac448965Sahl return (0); 929ac448965Sahl } 930ac448965Sahl } 9317c478bd9Sstevel@tonic-gate 9327c478bd9Sstevel@tonic-gate /* 9336009dbc6Sahl * We expect either a call instrution with a 32-bit displacement or a 9346009dbc6Sahl * jmp instruction with a 32-bit displacement acting as a tail-call. 9357c478bd9Sstevel@tonic-gate */ 9366009dbc6Sahl if (ip[0] != DT_OP_CALL && ip[0] != DT_OP_JMP32) { 9376009dbc6Sahl dt_dprintf("found %x instead of a call or jmp instruction at " 9386009dbc6Sahl "%llx\n", ip[0], (u_longlong_t)rela->r_offset); 9397c478bd9Sstevel@tonic-gate return (-1); 9407c478bd9Sstevel@tonic-gate } 9417c478bd9Sstevel@tonic-gate 9426009dbc6Sahl ret = (ip[0] == DT_OP_JMP32) ? DT_OP_RET : DT_OP_NOP; 9436009dbc6Sahl 944ac448965Sahl /* 945ac448965Sahl * Establish the instruction sequence -- all nops for probes, and an 946ac448965Sahl * instruction to clear the return value register (%eax/%rax) followed 947ac448965Sahl * by nops for is-enabled probes. For is-enabled probes, we advance 948ac448965Sahl * the offset to the first nop. This isn't stricly necessary but makes 949ac448965Sahl * for more readable disassembly when the probe is enabled. 950ac448965Sahl */ 951ac448965Sahl if (!isenabled) { 9526009dbc6Sahl ip[0] = ret; 9537c478bd9Sstevel@tonic-gate ip[1] = DT_OP_NOP; 9547c478bd9Sstevel@tonic-gate ip[2] = DT_OP_NOP; 9557c478bd9Sstevel@tonic-gate ip[3] = DT_OP_NOP; 9567c478bd9Sstevel@tonic-gate ip[4] = DT_OP_NOP; 957ac448965Sahl } else if (dtp->dt_oflags & DTRACE_O_LP64) { 958ac448965Sahl ip[0] = DT_OP_REX_RAX; 959ac448965Sahl ip[1] = DT_OP_XOR_EAX_0; 960ac448965Sahl ip[2] = DT_OP_XOR_EAX_1; 9616009dbc6Sahl ip[3] = ret; 962ac448965Sahl ip[4] = DT_OP_NOP; 963ac448965Sahl (*off) += 3; 964ac448965Sahl } else { 965ac448965Sahl ip[0] = DT_OP_XOR_EAX_0; 966ac448965Sahl ip[1] = DT_OP_XOR_EAX_1; 9676009dbc6Sahl ip[2] = ret; 968ac448965Sahl ip[3] = DT_OP_NOP; 969ac448965Sahl ip[4] = DT_OP_NOP; 970ac448965Sahl (*off) += 2; 971ac448965Sahl } 9727c478bd9Sstevel@tonic-gate 9737c478bd9Sstevel@tonic-gate return (0); 9747c478bd9Sstevel@tonic-gate } 9757c478bd9Sstevel@tonic-gate 9767c478bd9Sstevel@tonic-gate #else 9777c478bd9Sstevel@tonic-gate #error unknown ISA 9787c478bd9Sstevel@tonic-gate #endif 9797c478bd9Sstevel@tonic-gate 9804b499cecSahl /*PRINTFLIKE5*/ 9817c478bd9Sstevel@tonic-gate static int 9824b499cecSahl dt_link_error(dtrace_hdl_t *dtp, Elf *elf, int fd, dt_link_pair_t *bufs, 9834b499cecSahl const char *format, ...) 9847c478bd9Sstevel@tonic-gate { 9857c478bd9Sstevel@tonic-gate va_list ap; 9864b499cecSahl dt_link_pair_t *pair; 9877c478bd9Sstevel@tonic-gate 9887c478bd9Sstevel@tonic-gate va_start(ap, format); 9897c478bd9Sstevel@tonic-gate dt_set_errmsg(dtp, NULL, NULL, NULL, 0, format, ap); 9907c478bd9Sstevel@tonic-gate va_end(ap); 9917c478bd9Sstevel@tonic-gate 9921a7c1b72Smws if (elf != NULL) 9931a7c1b72Smws (void) elf_end(elf); 9941a7c1b72Smws 9954b499cecSahl if (fd >= 0) 9964b499cecSahl (void) close(fd); 9974b499cecSahl 9984b499cecSahl while ((pair = bufs) != NULL) { 9994b499cecSahl bufs = pair->dlp_next; 10004b499cecSahl dt_free(dtp, pair->dlp_str); 10014b499cecSahl dt_free(dtp, pair->dlp_sym); 10024b499cecSahl dt_free(dtp, pair); 10034b499cecSahl } 10044b499cecSahl 10057c478bd9Sstevel@tonic-gate return (dt_set_errno(dtp, EDT_COMPILER)); 10067c478bd9Sstevel@tonic-gate } 10077c478bd9Sstevel@tonic-gate 10087c478bd9Sstevel@tonic-gate static int 1009ac448965Sahl process_obj(dtrace_hdl_t *dtp, const char *obj, int *eprobesp) 10107c478bd9Sstevel@tonic-gate { 1011ac448965Sahl static const char dt_prefix[] = "__dtrace"; 1012ac448965Sahl static const char dt_enabled[] = "enabled"; 10134b499cecSahl static const char dt_symprefix[] = "$dtrace"; 10144b499cecSahl static const char dt_symfmt[] = "%s%d.%s"; 1015*b3db2570SJerry Jelinek char probename[DTRACE_NAMELEN]; 1016ac448965Sahl int fd, i, ndx, eprobe, mod = 0; 10171a7c1b72Smws Elf *elf = NULL; 10187c478bd9Sstevel@tonic-gate GElf_Ehdr ehdr; 10194b499cecSahl Elf_Scn *scn_rel, *scn_sym, *scn_str, *scn_tgt; 10204b499cecSahl Elf_Data *data_rel, *data_sym, *data_str, *data_tgt; 10214b499cecSahl GElf_Shdr shdr_rel, shdr_sym, shdr_str, shdr_tgt; 10224b499cecSahl GElf_Sym rsym, fsym, dsym; 10237c478bd9Sstevel@tonic-gate GElf_Rela rela; 10244b499cecSahl char *s, *p, *r; 10257c478bd9Sstevel@tonic-gate char pname[DTRACE_PROVNAMELEN]; 10267c478bd9Sstevel@tonic-gate dt_provider_t *pvp; 10277c478bd9Sstevel@tonic-gate dt_probe_t *prp; 10287c478bd9Sstevel@tonic-gate uint32_t off, eclass, emachine1, emachine2; 10292b6e762cSahl size_t symsize, nsym, isym, istr, len; 10304b499cecSahl key_t objkey; 10314b499cecSahl dt_link_pair_t *pair, *bufs = NULL; 10322b6e762cSahl dt_strtab_t *strtab; 10337c478bd9Sstevel@tonic-gate 10347c478bd9Sstevel@tonic-gate if ((fd = open64(obj, O_RDWR)) == -1) { 10354b499cecSahl return (dt_link_error(dtp, elf, fd, bufs, 10364b499cecSahl "failed to open %s: %s", obj, strerror(errno))); 10377c478bd9Sstevel@tonic-gate } 10387c478bd9Sstevel@tonic-gate 10391a7c1b72Smws if ((elf = elf_begin(fd, ELF_C_RDWR, NULL)) == NULL) { 10404b499cecSahl return (dt_link_error(dtp, elf, fd, bufs, 10414b499cecSahl "failed to process %s: %s", obj, elf_errmsg(elf_errno()))); 10427c478bd9Sstevel@tonic-gate } 10437c478bd9Sstevel@tonic-gate 10447c478bd9Sstevel@tonic-gate switch (elf_kind(elf)) { 10457c478bd9Sstevel@tonic-gate case ELF_K_ELF: 10467c478bd9Sstevel@tonic-gate break; 10477c478bd9Sstevel@tonic-gate case ELF_K_AR: 10484b499cecSahl return (dt_link_error(dtp, elf, fd, bufs, "archives are not " 10494b499cecSahl "permitted; use the contents of the archive instead: %s", 10504b499cecSahl obj)); 10517c478bd9Sstevel@tonic-gate default: 10524b499cecSahl return (dt_link_error(dtp, elf, fd, bufs, 10534b499cecSahl "invalid file type: %s", obj)); 10547c478bd9Sstevel@tonic-gate } 10557c478bd9Sstevel@tonic-gate 10564b499cecSahl if (gelf_getehdr(elf, &ehdr) == NULL) { 10574b499cecSahl return (dt_link_error(dtp, elf, fd, bufs, "corrupt file: %s", 10584b499cecSahl obj)); 10594b499cecSahl } 10607c478bd9Sstevel@tonic-gate 10617c478bd9Sstevel@tonic-gate if (dtp->dt_oflags & DTRACE_O_LP64) { 10627c478bd9Sstevel@tonic-gate eclass = ELFCLASS64; 10637c478bd9Sstevel@tonic-gate #if defined(__sparc) 10647c478bd9Sstevel@tonic-gate emachine1 = emachine2 = EM_SPARCV9; 10657c478bd9Sstevel@tonic-gate #elif defined(__i386) || defined(__amd64) 10667c478bd9Sstevel@tonic-gate emachine1 = emachine2 = EM_AMD64; 10677c478bd9Sstevel@tonic-gate #endif 10684b499cecSahl symsize = sizeof (Elf64_Sym); 10697c478bd9Sstevel@tonic-gate } else { 10707c478bd9Sstevel@tonic-gate eclass = ELFCLASS32; 10717c478bd9Sstevel@tonic-gate #if defined(__sparc) 10727c478bd9Sstevel@tonic-gate emachine1 = EM_SPARC; 10737c478bd9Sstevel@tonic-gate emachine2 = EM_SPARC32PLUS; 10747c478bd9Sstevel@tonic-gate #elif defined(__i386) || defined(__amd64) 10757c478bd9Sstevel@tonic-gate emachine1 = emachine2 = EM_386; 10767c478bd9Sstevel@tonic-gate #endif 10774b499cecSahl symsize = sizeof (Elf32_Sym); 10787c478bd9Sstevel@tonic-gate } 10797c478bd9Sstevel@tonic-gate 10801a7c1b72Smws if (ehdr.e_ident[EI_CLASS] != eclass) { 10814b499cecSahl return (dt_link_error(dtp, elf, fd, bufs, 10821a7c1b72Smws "incorrect ELF class for object file: %s", obj)); 10831a7c1b72Smws } 10847c478bd9Sstevel@tonic-gate 10854b499cecSahl if (ehdr.e_machine != emachine1 && ehdr.e_machine != emachine2) { 10864b499cecSahl return (dt_link_error(dtp, elf, fd, bufs, 10874b499cecSahl "incorrect ELF machine type for object file: %s", obj)); 10884b499cecSahl } 10894b499cecSahl 10904b499cecSahl /* 10914b499cecSahl * We use this token as a relatively unique handle for this file on the 10924b499cecSahl * system in order to disambiguate potential conflicts between files of 10934b499cecSahl * the same name which contain identially named local symbols. 10944b499cecSahl */ 10954b499cecSahl if ((objkey = ftok(obj, 0)) == (key_t)-1) { 10964b499cecSahl return (dt_link_error(dtp, elf, fd, bufs, 10974b499cecSahl "failed to generate unique key for object file: %s", obj)); 10984b499cecSahl } 10997c478bd9Sstevel@tonic-gate 11007c478bd9Sstevel@tonic-gate scn_rel = NULL; 11017c478bd9Sstevel@tonic-gate while ((scn_rel = elf_nextscn(elf, scn_rel)) != NULL) { 11027c478bd9Sstevel@tonic-gate if (gelf_getshdr(scn_rel, &shdr_rel) == NULL) 11037c478bd9Sstevel@tonic-gate goto err; 11047c478bd9Sstevel@tonic-gate 11054b499cecSahl /* 11064b499cecSahl * Skip any non-relocation sections. 11074b499cecSahl */ 11087c478bd9Sstevel@tonic-gate if (shdr_rel.sh_type != SHT_RELA && shdr_rel.sh_type != SHT_REL) 11097c478bd9Sstevel@tonic-gate continue; 11107c478bd9Sstevel@tonic-gate 11117c478bd9Sstevel@tonic-gate if ((data_rel = elf_getdata(scn_rel, NULL)) == NULL) 11127c478bd9Sstevel@tonic-gate goto err; 11137c478bd9Sstevel@tonic-gate 11144b499cecSahl /* 11154b499cecSahl * Grab the section, section header and section data for the 11164b499cecSahl * symbol table that this relocation section references. 11174b499cecSahl */ 11187c478bd9Sstevel@tonic-gate if ((scn_sym = elf_getscn(elf, shdr_rel.sh_link)) == NULL || 11197c478bd9Sstevel@tonic-gate gelf_getshdr(scn_sym, &shdr_sym) == NULL || 11207c478bd9Sstevel@tonic-gate (data_sym = elf_getdata(scn_sym, NULL)) == NULL) 11217c478bd9Sstevel@tonic-gate goto err; 11227c478bd9Sstevel@tonic-gate 11234b499cecSahl /* 11244b499cecSahl * Ditto for that symbol table's string table. 11254b499cecSahl */ 11264b499cecSahl if ((scn_str = elf_getscn(elf, shdr_sym.sh_link)) == NULL || 11274b499cecSahl gelf_getshdr(scn_str, &shdr_str) == NULL || 11284b499cecSahl (data_str = elf_getdata(scn_str, NULL)) == NULL) 11294b499cecSahl goto err; 11304b499cecSahl 11314b499cecSahl /* 11324b499cecSahl * Grab the section, section header and section data for the 11334b499cecSahl * target section for the relocations. For the relocations 11344b499cecSahl * we're looking for -- this will typically be the text of the 11354b499cecSahl * object file. 11364b499cecSahl */ 11377c478bd9Sstevel@tonic-gate if ((scn_tgt = elf_getscn(elf, shdr_rel.sh_info)) == NULL || 11387c478bd9Sstevel@tonic-gate gelf_getshdr(scn_tgt, &shdr_tgt) == NULL || 11397c478bd9Sstevel@tonic-gate (data_tgt = elf_getdata(scn_tgt, NULL)) == NULL) 11407c478bd9Sstevel@tonic-gate goto err; 11417c478bd9Sstevel@tonic-gate 11424b499cecSahl /* 11434b499cecSahl * We're looking for relocations to symbols matching this form: 11444b499cecSahl * 1145ac448965Sahl * __dtrace[enabled]_<prov>___<probe> 11464b499cecSahl * 11474b499cecSahl * For the generated object, we need to record the location 11484b499cecSahl * identified by the relocation, and create a new relocation 11494b499cecSahl * in the generated object that will be resolved at link time 11504b499cecSahl * to the location of the function in which the probe is 11514b499cecSahl * embedded. In the target object, we change the matched symbol 11524b499cecSahl * so that it will be ignored at link time, and we modify the 11534b499cecSahl * target (text) section to replace the call instruction with 11544b499cecSahl * one or more nops. 11554b499cecSahl * 11564b499cecSahl * If the function containing the probe is locally scoped 11574b499cecSahl * (static), we create an alias used by the relocation in the 11584b499cecSahl * generated object. The alias, a new symbol, will be global 11594b499cecSahl * (so that the relocation from the generated object can be 11604b499cecSahl * resolved), and hidden (so that it is converted to a local 11614b499cecSahl * symbol at link time). Such aliases have this form: 11624b499cecSahl * 11634b499cecSahl * $dtrace<key>.<function> 11644b499cecSahl * 11654b499cecSahl * We take a first pass through all the relocations to 11662b6e762cSahl * populate our string table and count the number of extra 11672b6e762cSahl * symbols we'll require. 11684b499cecSahl */ 11692b6e762cSahl strtab = dt_strtab_create(1); 11702b6e762cSahl nsym = 0; 117173427c57Sahl isym = data_sym->d_size / symsize; 117273427c57Sahl istr = data_str->d_size; 11732b6e762cSahl 11747c478bd9Sstevel@tonic-gate for (i = 0; i < shdr_rel.sh_size / shdr_rel.sh_entsize; i++) { 11757c478bd9Sstevel@tonic-gate 11767c478bd9Sstevel@tonic-gate if (shdr_rel.sh_type == SHT_RELA) { 11777c478bd9Sstevel@tonic-gate if (gelf_getrela(data_rel, i, &rela) == NULL) 11787c478bd9Sstevel@tonic-gate continue; 11797c478bd9Sstevel@tonic-gate } else { 11804b499cecSahl GElf_Rel rel; 11814b499cecSahl if (gelf_getrel(data_rel, i, &rel) == NULL) 11824b499cecSahl continue; 11834b499cecSahl rela.r_offset = rel.r_offset; 11844b499cecSahl rela.r_info = rel.r_info; 11854b499cecSahl rela.r_addend = 0; 11864b499cecSahl } 11874b499cecSahl 11884b499cecSahl if (gelf_getsym(data_sym, GELF_R_SYM(rela.r_info), 11892b6e762cSahl &rsym) == NULL) { 11902b6e762cSahl dt_strtab_destroy(strtab); 11914b499cecSahl goto err; 11922b6e762cSahl } 11934b499cecSahl 11944b499cecSahl s = (char *)data_str->d_buf + rsym.st_name; 11954b499cecSahl 11964b499cecSahl if (strncmp(s, dt_prefix, sizeof (dt_prefix) - 1) != 0) 11974b499cecSahl continue; 11984b499cecSahl 119973427c57Sahl if (dt_symtab_lookup(data_sym, isym, rela.r_offset, 12002b6e762cSahl shdr_rel.sh_info, &fsym) != 0) { 12012b6e762cSahl dt_strtab_destroy(strtab); 12024b499cecSahl goto err; 12032b6e762cSahl } 12044b499cecSahl 12054b499cecSahl if (GELF_ST_BIND(fsym.st_info) != STB_LOCAL) 12064b499cecSahl continue; 12074b499cecSahl 12082b6e762cSahl if (fsym.st_name > data_str->d_size) { 12092b6e762cSahl dt_strtab_destroy(strtab); 12104b499cecSahl goto err; 12112b6e762cSahl } 12124b499cecSahl 12134b499cecSahl s = (char *)data_str->d_buf + fsym.st_name; 12144b499cecSahl 12154b499cecSahl /* 12164b499cecSahl * If this symbol isn't of type function, we've really 12174b499cecSahl * driven off the rails or the object file is corrupt. 12184b499cecSahl */ 12194b499cecSahl if (GELF_ST_TYPE(fsym.st_info) != STT_FUNC) { 12202b6e762cSahl dt_strtab_destroy(strtab); 12214b499cecSahl return (dt_link_error(dtp, elf, fd, bufs, 12224b499cecSahl "expected %s to be of type function", s)); 12234b499cecSahl } 12244b499cecSahl 12252b6e762cSahl len = snprintf(NULL, 0, dt_symfmt, dt_symprefix, 12262b6e762cSahl objkey, s) + 1; 12272b6e762cSahl if ((p = dt_alloc(dtp, len)) == NULL) { 12282b6e762cSahl dt_strtab_destroy(strtab); 12292b6e762cSahl goto err; 12302b6e762cSahl } 12312b6e762cSahl (void) snprintf(p, len, dt_symfmt, dt_symprefix, 12322b6e762cSahl objkey, s); 12332b6e762cSahl 12342b6e762cSahl if (dt_strtab_index(strtab, p) == -1) { 12352b6e762cSahl nsym++; 12362b6e762cSahl (void) dt_strtab_insert(strtab, p); 12372b6e762cSahl } 12382b6e762cSahl 12392b6e762cSahl dt_free(dtp, p); 12404b499cecSahl } 12414b499cecSahl 12424b499cecSahl /* 12434b499cecSahl * If needed, allocate the additional space for the symbol 12444b499cecSahl * table and string table copying the old data into the new 12454b499cecSahl * buffers, and marking the buffers as dirty. We inject those 12464b499cecSahl * newly allocated buffers into the libelf data structures, but 12474b499cecSahl * are still responsible for freeing them once we're done with 12484b499cecSahl * the elf handle. 12494b499cecSahl */ 12502b6e762cSahl if (nsym > 0) { 12512b6e762cSahl /* 12522b6e762cSahl * The first byte of the string table is reserved for 12532b6e762cSahl * the \0 entry. 12542b6e762cSahl */ 12552b6e762cSahl len = dt_strtab_size(strtab) - 1; 12562b6e762cSahl 12572b6e762cSahl assert(len > 0); 12582b6e762cSahl assert(dt_strtab_index(strtab, "") == 0); 12592b6e762cSahl 12602b6e762cSahl dt_strtab_destroy(strtab); 12614b499cecSahl 12624b499cecSahl if ((pair = dt_alloc(dtp, sizeof (*pair))) == NULL) 12634b499cecSahl goto err; 12644b499cecSahl 12654b499cecSahl if ((pair->dlp_str = dt_alloc(dtp, data_str->d_size + 12662b6e762cSahl len)) == NULL) { 12674b499cecSahl dt_free(dtp, pair); 12684b499cecSahl goto err; 12694b499cecSahl } 12704b499cecSahl 12714b499cecSahl if ((pair->dlp_sym = dt_alloc(dtp, data_sym->d_size + 12722b6e762cSahl nsym * symsize)) == NULL) { 12734b499cecSahl dt_free(dtp, pair->dlp_str); 12744b499cecSahl dt_free(dtp, pair); 12754b499cecSahl goto err; 12764b499cecSahl } 12774b499cecSahl 12784b499cecSahl pair->dlp_next = bufs; 12794b499cecSahl bufs = pair; 12804b499cecSahl 12814b499cecSahl bcopy(data_str->d_buf, pair->dlp_str, data_str->d_size); 12824b499cecSahl data_str->d_buf = pair->dlp_str; 12832b6e762cSahl data_str->d_size += len; 12844b499cecSahl (void) elf_flagdata(data_str, ELF_C_SET, ELF_F_DIRTY); 12854b499cecSahl 12862b6e762cSahl shdr_str.sh_size += len; 12874b499cecSahl (void) gelf_update_shdr(scn_str, &shdr_str); 12884b499cecSahl 12894b499cecSahl bcopy(data_sym->d_buf, pair->dlp_sym, data_sym->d_size); 12904b499cecSahl data_sym->d_buf = pair->dlp_sym; 12912b6e762cSahl data_sym->d_size += nsym * symsize; 12924b499cecSahl (void) elf_flagdata(data_sym, ELF_C_SET, ELF_F_DIRTY); 12934b499cecSahl 12942b6e762cSahl shdr_sym.sh_size += nsym * symsize; 12954b499cecSahl (void) gelf_update_shdr(scn_sym, &shdr_sym); 12964b499cecSahl 12972b6e762cSahl nsym += isym; 12982b6e762cSahl } else { 12992b6e762cSahl dt_strtab_destroy(strtab); 13002b6e762cSahl } 13014b499cecSahl 13024b499cecSahl /* 13034b499cecSahl * Now that the tables have been allocated, perform the 13044b499cecSahl * modifications described above. 13054b499cecSahl */ 13064b499cecSahl for (i = 0; i < shdr_rel.sh_size / shdr_rel.sh_entsize; i++) { 13074b499cecSahl 13084b499cecSahl if (shdr_rel.sh_type == SHT_RELA) { 13094b499cecSahl if (gelf_getrela(data_rel, i, &rela) == NULL) 13104b499cecSahl continue; 13114b499cecSahl } else { 13124b499cecSahl GElf_Rel rel; 13137c478bd9Sstevel@tonic-gate if (gelf_getrel(data_rel, i, &rel) == NULL) 13147c478bd9Sstevel@tonic-gate continue; 13157c478bd9Sstevel@tonic-gate rela.r_offset = rel.r_offset; 13167c478bd9Sstevel@tonic-gate rela.r_info = rel.r_info; 13177c478bd9Sstevel@tonic-gate rela.r_addend = 0; 13187c478bd9Sstevel@tonic-gate } 13197c478bd9Sstevel@tonic-gate 13207c478bd9Sstevel@tonic-gate ndx = GELF_R_SYM(rela.r_info); 13217c478bd9Sstevel@tonic-gate 13227c478bd9Sstevel@tonic-gate if (gelf_getsym(data_sym, ndx, &rsym) == NULL || 13234b499cecSahl rsym.st_name > data_str->d_size) 13247c478bd9Sstevel@tonic-gate goto err; 13257c478bd9Sstevel@tonic-gate 13264b499cecSahl s = (char *)data_str->d_buf + rsym.st_name; 13274b499cecSahl 13287c478bd9Sstevel@tonic-gate if (strncmp(s, dt_prefix, sizeof (dt_prefix) - 1) != 0) 13297c478bd9Sstevel@tonic-gate continue; 13307c478bd9Sstevel@tonic-gate 13317c478bd9Sstevel@tonic-gate s += sizeof (dt_prefix) - 1; 1332ac448965Sahl 1333ac448965Sahl /* 1334ac448965Sahl * Check to see if this is an 'is-enabled' check as 1335ac448965Sahl * opposed to a normal probe. 1336ac448965Sahl */ 1337ac448965Sahl if (strncmp(s, dt_enabled, 1338ac448965Sahl sizeof (dt_enabled) - 1) == 0) { 1339ac448965Sahl s += sizeof (dt_enabled) - 1; 1340ac448965Sahl eprobe = 1; 1341ac448965Sahl *eprobesp = 1; 1342ac448965Sahl dt_dprintf("is-enabled probe\n"); 1343ac448965Sahl } else { 1344ac448965Sahl eprobe = 0; 1345ac448965Sahl dt_dprintf("normal probe\n"); 1346ac448965Sahl } 1347ac448965Sahl 1348ac448965Sahl if (*s++ != '_') 1349ac448965Sahl goto err; 1350ac448965Sahl 13517c478bd9Sstevel@tonic-gate if ((p = strstr(s, "___")) == NULL || 13527c478bd9Sstevel@tonic-gate p - s >= sizeof (pname)) 13537c478bd9Sstevel@tonic-gate goto err; 13547c478bd9Sstevel@tonic-gate 13554b499cecSahl bcopy(s, pname, p - s); 13567c478bd9Sstevel@tonic-gate pname[p - s] = '\0'; 13577c478bd9Sstevel@tonic-gate 135873427c57Sahl if (dt_symtab_lookup(data_sym, isym, rela.r_offset, 13594b499cecSahl shdr_rel.sh_info, &fsym) != 0) 13607c478bd9Sstevel@tonic-gate goto err; 13617c478bd9Sstevel@tonic-gate 13624b499cecSahl if (fsym.st_name > data_str->d_size) 13634b499cecSahl goto err; 13644b499cecSahl 13654b499cecSahl assert(GELF_ST_TYPE(fsym.st_info) == STT_FUNC); 13664b499cecSahl 13674b499cecSahl /* 13684b499cecSahl * If a NULL relocation name is passed to 13694b499cecSahl * dt_probe_define(), the function name is used for the 13704b499cecSahl * relocation. The relocation needs to use a mangled 13714b499cecSahl * name if the symbol is locally scoped; the function 13724b499cecSahl * name may need to change if we've found the global 13734b499cecSahl * alias for the locally scoped symbol (we prefer 13744b499cecSahl * global symbols to locals in dt_symtab_lookup()). 13754b499cecSahl */ 13764b499cecSahl s = (char *)data_str->d_buf + fsym.st_name; 13774b499cecSahl r = NULL; 13784b499cecSahl 13794b499cecSahl if (GELF_ST_BIND(fsym.st_info) == STB_LOCAL) { 13804b499cecSahl dsym = fsym; 13812b6e762cSahl dsym.st_name = istr; 13824b499cecSahl dsym.st_info = GELF_ST_INFO(STB_GLOBAL, 13834b499cecSahl STT_FUNC); 1384823b63e1Sahl dsym.st_other = 1385823b63e1Sahl ELF64_ST_VISIBILITY(STV_ELIMINATE); 13862b6e762cSahl (void) gelf_update_sym(data_sym, isym, &dsym); 13874b499cecSahl 13882b6e762cSahl r = (char *)data_str->d_buf + istr; 13892b6e762cSahl istr += 1 + sprintf(r, dt_symfmt, 13904b499cecSahl dt_symprefix, objkey, s); 13912b6e762cSahl isym++; 13922b6e762cSahl assert(isym <= nsym); 13934b499cecSahl 13944b499cecSahl } else if (strncmp(s, dt_symprefix, 13954b499cecSahl strlen(dt_symprefix)) == 0) { 13964b499cecSahl r = s; 13974b499cecSahl if ((s = strchr(s, '.')) == NULL) 13984b499cecSahl goto err; 13994b499cecSahl s++; 14004b499cecSahl } 14014b499cecSahl 14027c478bd9Sstevel@tonic-gate if ((pvp = dt_provider_lookup(dtp, pname)) == NULL) { 14034b499cecSahl return (dt_link_error(dtp, elf, fd, bufs, 14047c478bd9Sstevel@tonic-gate "no such provider %s", pname)); 14057c478bd9Sstevel@tonic-gate } 14067c478bd9Sstevel@tonic-gate 1407*b3db2570SJerry Jelinek /* strlen("___") */ 1408*b3db2570SJerry Jelinek if (strlcpy(probename, p + 3, sizeof (probename)) >= 1409*b3db2570SJerry Jelinek sizeof (probename)) 14104b499cecSahl return (dt_link_error(dtp, elf, fd, bufs, 1411*b3db2570SJerry Jelinek "probe name too long %s", probename)); 1412*b3db2570SJerry Jelinek 1413*b3db2570SJerry Jelinek (void) strhyphenate(probename); 1414*b3db2570SJerry Jelinek 1415*b3db2570SJerry Jelinek if ((prp = dt_probe_lookup(pvp, probename)) == NULL) { 1416*b3db2570SJerry Jelinek return (dt_link_error(dtp, elf, fd, bufs, 1417*b3db2570SJerry Jelinek "no such probe %s", probename)); 14187c478bd9Sstevel@tonic-gate } 14197c478bd9Sstevel@tonic-gate 14207c478bd9Sstevel@tonic-gate assert(fsym.st_value <= rela.r_offset); 14217c478bd9Sstevel@tonic-gate 14227c478bd9Sstevel@tonic-gate off = rela.r_offset - fsym.st_value; 1423ac448965Sahl if (dt_modtext(dtp, data_tgt->d_buf, eprobe, 1424ac448965Sahl &rela, &off) != 0) { 14257c478bd9Sstevel@tonic-gate goto err; 1426ac448965Sahl } 14277c478bd9Sstevel@tonic-gate 1428ac448965Sahl if (dt_probe_define(pvp, prp, s, r, off, eprobe) != 0) { 14294b499cecSahl return (dt_link_error(dtp, elf, fd, bufs, 14304b499cecSahl "failed to allocate space for probe")); 14314b499cecSahl } 14327c478bd9Sstevel@tonic-gate 14337c478bd9Sstevel@tonic-gate mod = 1; 14344b499cecSahl (void) elf_flagdata(data_tgt, ELF_C_SET, ELF_F_DIRTY); 14357c478bd9Sstevel@tonic-gate 14367c478bd9Sstevel@tonic-gate /* 14377c478bd9Sstevel@tonic-gate * This symbol may already have been marked to 14387c478bd9Sstevel@tonic-gate * be ignored by another relocation referencing 14397c478bd9Sstevel@tonic-gate * the same symbol or if this object file has 14407c478bd9Sstevel@tonic-gate * already been processed by an earlier link 14417c478bd9Sstevel@tonic-gate * invocation. 14427c478bd9Sstevel@tonic-gate */ 14437c478bd9Sstevel@tonic-gate if (rsym.st_shndx != SHN_SUNW_IGNORE) { 14447c478bd9Sstevel@tonic-gate rsym.st_shndx = SHN_SUNW_IGNORE; 14457c478bd9Sstevel@tonic-gate (void) gelf_update_sym(data_sym, ndx, &rsym); 14467c478bd9Sstevel@tonic-gate } 14477c478bd9Sstevel@tonic-gate } 14487c478bd9Sstevel@tonic-gate } 14497c478bd9Sstevel@tonic-gate 14507c478bd9Sstevel@tonic-gate if (mod && elf_update(elf, ELF_C_WRITE) == -1) 14517c478bd9Sstevel@tonic-gate goto err; 14527c478bd9Sstevel@tonic-gate 14531a7c1b72Smws (void) elf_end(elf); 14544b499cecSahl (void) close(fd); 14554b499cecSahl 14564b499cecSahl while ((pair = bufs) != NULL) { 14574b499cecSahl bufs = pair->dlp_next; 14584b499cecSahl dt_free(dtp, pair->dlp_str); 14594b499cecSahl dt_free(dtp, pair->dlp_sym); 14604b499cecSahl dt_free(dtp, pair); 14614b499cecSahl } 14624b499cecSahl 14637c478bd9Sstevel@tonic-gate return (0); 14647c478bd9Sstevel@tonic-gate 14657c478bd9Sstevel@tonic-gate err: 14664b499cecSahl return (dt_link_error(dtp, elf, fd, bufs, 14677c478bd9Sstevel@tonic-gate "an error was encountered while processing %s", obj)); 14687c478bd9Sstevel@tonic-gate } 14697c478bd9Sstevel@tonic-gate 14707c478bd9Sstevel@tonic-gate int 14717c478bd9Sstevel@tonic-gate dtrace_program_link(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, uint_t dflags, 14727c478bd9Sstevel@tonic-gate const char *file, int objc, char *const objv[]) 14737c478bd9Sstevel@tonic-gate { 14747c478bd9Sstevel@tonic-gate char drti[PATH_MAX]; 14757c478bd9Sstevel@tonic-gate dof_hdr_t *dof; 14760b38a8bdSahl int fd, status, i, cur; 14777c478bd9Sstevel@tonic-gate char *cmd, tmp; 14787c478bd9Sstevel@tonic-gate size_t len; 1479ac448965Sahl int eprobes = 0, ret = 0; 14807c478bd9Sstevel@tonic-gate 14810b38a8bdSahl /* 14820b38a8bdSahl * A NULL program indicates a special use in which we just link 14830b38a8bdSahl * together a bunch of object files specified in objv and then 14840b38a8bdSahl * unlink(2) those object files. 14850b38a8bdSahl */ 14860b38a8bdSahl if (pgp == NULL) { 14870b38a8bdSahl const char *fmt = "%s -o %s -r"; 14880b38a8bdSahl 14890b38a8bdSahl len = snprintf(&tmp, 1, fmt, dtp->dt_ld_path, file) + 1; 14900b38a8bdSahl 14910b38a8bdSahl for (i = 0; i < objc; i++) 14920b38a8bdSahl len += strlen(objv[i]) + 1; 14930b38a8bdSahl 14940b38a8bdSahl cmd = alloca(len); 14950b38a8bdSahl 14960b38a8bdSahl cur = snprintf(cmd, len, fmt, dtp->dt_ld_path, file); 14970b38a8bdSahl 14980b38a8bdSahl for (i = 0; i < objc; i++) 14990b38a8bdSahl cur += snprintf(cmd + cur, len - cur, " %s", objv[i]); 15000b38a8bdSahl 15010b38a8bdSahl if ((status = system(cmd)) == -1) { 15024b499cecSahl return (dt_link_error(dtp, NULL, -1, NULL, 15034b499cecSahl "failed to run %s: %s", dtp->dt_ld_path, 15044b499cecSahl strerror(errno))); 15050b38a8bdSahl } 15060b38a8bdSahl 15070b38a8bdSahl if (WIFSIGNALED(status)) { 15084b499cecSahl return (dt_link_error(dtp, NULL, -1, NULL, 15090b38a8bdSahl "failed to link %s: %s failed due to signal %d", 15100b38a8bdSahl file, dtp->dt_ld_path, WTERMSIG(status))); 15110b38a8bdSahl } 15120b38a8bdSahl 15130b38a8bdSahl if (WEXITSTATUS(status) != 0) { 15144b499cecSahl return (dt_link_error(dtp, NULL, -1, NULL, 15150b38a8bdSahl "failed to link %s: %s exited with status %d\n", 15160b38a8bdSahl file, dtp->dt_ld_path, WEXITSTATUS(status))); 15170b38a8bdSahl } 15180b38a8bdSahl 15190b38a8bdSahl for (i = 0; i < objc; i++) { 15200b38a8bdSahl if (strcmp(objv[i], file) != 0) 15210b38a8bdSahl (void) unlink(objv[i]); 15220b38a8bdSahl } 15230b38a8bdSahl 15240b38a8bdSahl return (0); 15250b38a8bdSahl } 15260b38a8bdSahl 15277c478bd9Sstevel@tonic-gate for (i = 0; i < objc; i++) { 1528ac448965Sahl if (process_obj(dtp, objv[i], &eprobes) != 0) 15297c478bd9Sstevel@tonic-gate return (-1); /* errno is set for us */ 15307c478bd9Sstevel@tonic-gate } 15317c478bd9Sstevel@tonic-gate 1532ac448965Sahl /* 1533ac448965Sahl * If there are is-enabled probes then we need to force use of DOF 1534ac448965Sahl * version 2. 1535ac448965Sahl */ 1536ac448965Sahl if (eprobes && pgp->dp_dofversion < DOF_VERSION_2) 1537ac448965Sahl pgp->dp_dofversion = DOF_VERSION_2; 1538ac448965Sahl 15397c478bd9Sstevel@tonic-gate if ((dof = dtrace_dof_create(dtp, pgp, dflags)) == NULL) 15407c478bd9Sstevel@tonic-gate return (-1); /* errno is set for us */ 15417c478bd9Sstevel@tonic-gate 15427c478bd9Sstevel@tonic-gate /* 15437c478bd9Sstevel@tonic-gate * Create a temporary file and then unlink it if we're going to 15447c478bd9Sstevel@tonic-gate * combine it with drti.o later. We can still refer to it in child 15457c478bd9Sstevel@tonic-gate * processes as /dev/fd/<fd>. 15467c478bd9Sstevel@tonic-gate */ 15477c478bd9Sstevel@tonic-gate if ((fd = open64(file, O_RDWR | O_CREAT | O_TRUNC, 0666)) == -1) { 15484b499cecSahl return (dt_link_error(dtp, NULL, -1, NULL, 15497c478bd9Sstevel@tonic-gate "failed to open %s: %s", file, strerror(errno))); 15507c478bd9Sstevel@tonic-gate } 15517c478bd9Sstevel@tonic-gate 15527c478bd9Sstevel@tonic-gate /* 15537c478bd9Sstevel@tonic-gate * If -xlinktype=DOF has been selected, just write out the DOF. 15547c478bd9Sstevel@tonic-gate * Otherwise proceed to the default of generating and linking ELF. 15557c478bd9Sstevel@tonic-gate */ 15567c478bd9Sstevel@tonic-gate switch (dtp->dt_linktype) { 15577c478bd9Sstevel@tonic-gate case DT_LTYP_DOF: 15587c478bd9Sstevel@tonic-gate if (dt_write(dtp, fd, dof, dof->dofh_filesz) < dof->dofh_filesz) 15597c478bd9Sstevel@tonic-gate ret = errno; 15607c478bd9Sstevel@tonic-gate 15617c478bd9Sstevel@tonic-gate if (close(fd) != 0 && ret == 0) 15627c478bd9Sstevel@tonic-gate ret = errno; 15637c478bd9Sstevel@tonic-gate 15647c478bd9Sstevel@tonic-gate if (ret != 0) { 15654b499cecSahl return (dt_link_error(dtp, NULL, -1, NULL, 15667c478bd9Sstevel@tonic-gate "failed to write %s: %s", file, strerror(ret))); 15677c478bd9Sstevel@tonic-gate } 15687c478bd9Sstevel@tonic-gate 15697c478bd9Sstevel@tonic-gate return (0); 15707c478bd9Sstevel@tonic-gate 15717c478bd9Sstevel@tonic-gate case DT_LTYP_ELF: 15727c478bd9Sstevel@tonic-gate break; /* fall through to the rest of dtrace_program_link() */ 15737c478bd9Sstevel@tonic-gate 15747c478bd9Sstevel@tonic-gate default: 15754b499cecSahl return (dt_link_error(dtp, NULL, -1, NULL, 15767c478bd9Sstevel@tonic-gate "invalid link type %u\n", dtp->dt_linktype)); 15777c478bd9Sstevel@tonic-gate } 15787c478bd9Sstevel@tonic-gate 15797c478bd9Sstevel@tonic-gate 15807c478bd9Sstevel@tonic-gate if (!dtp->dt_lazyload) 15817c478bd9Sstevel@tonic-gate (void) unlink(file); 15827c478bd9Sstevel@tonic-gate 15837c478bd9Sstevel@tonic-gate if (dtp->dt_oflags & DTRACE_O_LP64) 15847c478bd9Sstevel@tonic-gate status = dump_elf64(dtp, dof, fd); 15857c478bd9Sstevel@tonic-gate else 15867c478bd9Sstevel@tonic-gate status = dump_elf32(dtp, dof, fd); 15877c478bd9Sstevel@tonic-gate 15887c478bd9Sstevel@tonic-gate if (status != 0 || lseek(fd, 0, SEEK_SET) != 0) { 15894b499cecSahl return (dt_link_error(dtp, NULL, -1, NULL, 15907c478bd9Sstevel@tonic-gate "failed to write %s: %s", file, strerror(errno))); 15917c478bd9Sstevel@tonic-gate } 15927c478bd9Sstevel@tonic-gate 15937c478bd9Sstevel@tonic-gate if (!dtp->dt_lazyload) { 15940b38a8bdSahl const char *fmt = "%s -o %s -r -Blocal -Breduce /dev/fd/%d %s"; 15950b38a8bdSahl 15967c478bd9Sstevel@tonic-gate if (dtp->dt_oflags & DTRACE_O_LP64) { 15977c478bd9Sstevel@tonic-gate (void) snprintf(drti, sizeof (drti), 15987c478bd9Sstevel@tonic-gate "%s/64/drti.o", _dtrace_libdir); 15997c478bd9Sstevel@tonic-gate } else { 16007c478bd9Sstevel@tonic-gate (void) snprintf(drti, sizeof (drti), 16017c478bd9Sstevel@tonic-gate "%s/drti.o", _dtrace_libdir); 16027c478bd9Sstevel@tonic-gate } 16037c478bd9Sstevel@tonic-gate 16040b38a8bdSahl len = snprintf(&tmp, 1, fmt, dtp->dt_ld_path, file, fd, 16050b38a8bdSahl drti) + 1; 16067c478bd9Sstevel@tonic-gate 16077c478bd9Sstevel@tonic-gate cmd = alloca(len); 16087c478bd9Sstevel@tonic-gate 16090b38a8bdSahl (void) snprintf(cmd, len, fmt, dtp->dt_ld_path, file, fd, drti); 16107c478bd9Sstevel@tonic-gate 16117c478bd9Sstevel@tonic-gate if ((status = system(cmd)) == -1) { 16124b499cecSahl ret = dt_link_error(dtp, NULL, -1, NULL, 16134b499cecSahl "failed to run %s: %s", dtp->dt_ld_path, 16144b499cecSahl strerror(errno)); 16157c478bd9Sstevel@tonic-gate goto done; 16167c478bd9Sstevel@tonic-gate } 16177c478bd9Sstevel@tonic-gate 16187c478bd9Sstevel@tonic-gate (void) close(fd); /* release temporary file */ 16197c478bd9Sstevel@tonic-gate 16207c478bd9Sstevel@tonic-gate if (WIFSIGNALED(status)) { 16214b499cecSahl ret = dt_link_error(dtp, NULL, -1, NULL, 16227c478bd9Sstevel@tonic-gate "failed to link %s: %s failed due to signal %d", 16237c478bd9Sstevel@tonic-gate file, dtp->dt_ld_path, WTERMSIG(status)); 16247c478bd9Sstevel@tonic-gate goto done; 16257c478bd9Sstevel@tonic-gate } 16267c478bd9Sstevel@tonic-gate 16277c478bd9Sstevel@tonic-gate if (WEXITSTATUS(status) != 0) { 16284b499cecSahl ret = dt_link_error(dtp, NULL, -1, NULL, 16297c478bd9Sstevel@tonic-gate "failed to link %s: %s exited with status %d\n", 16307c478bd9Sstevel@tonic-gate file, dtp->dt_ld_path, WEXITSTATUS(status)); 16317c478bd9Sstevel@tonic-gate goto done; 16327c478bd9Sstevel@tonic-gate } 16337c478bd9Sstevel@tonic-gate } else { 16347c478bd9Sstevel@tonic-gate (void) close(fd); 16357c478bd9Sstevel@tonic-gate } 16367c478bd9Sstevel@tonic-gate 16377c478bd9Sstevel@tonic-gate done: 16387c478bd9Sstevel@tonic-gate dtrace_dof_destroy(dtp, dof); 16397c478bd9Sstevel@tonic-gate return (ret); 16407c478bd9Sstevel@tonic-gate } 1641