16ff6d951SJohn Birrell /* 26ff6d951SJohn Birrell * CDDL HEADER START 36ff6d951SJohn Birrell * 46ff6d951SJohn Birrell * The contents of this file are subject to the terms of the 56ff6d951SJohn Birrell * Common Development and Distribution License (the "License"). 66ff6d951SJohn Birrell * You may not use this file except in compliance with the License. 76ff6d951SJohn Birrell * 86ff6d951SJohn Birrell * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 96ff6d951SJohn Birrell * or http://www.opensolaris.org/os/licensing. 106ff6d951SJohn Birrell * See the License for the specific language governing permissions 116ff6d951SJohn Birrell * and limitations under the License. 126ff6d951SJohn Birrell * 136ff6d951SJohn Birrell * When distributing Covered Code, include this CDDL HEADER in each 146ff6d951SJohn Birrell * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 156ff6d951SJohn Birrell * If applicable, add the following below this CDDL HEADER, with the 166ff6d951SJohn Birrell * fields enclosed by brackets "[]" replaced with your own identifying 176ff6d951SJohn Birrell * information: Portions Copyright [yyyy] [name of copyright owner] 186ff6d951SJohn Birrell * 196ff6d951SJohn Birrell * CDDL HEADER END 206ff6d951SJohn Birrell */ 216ff6d951SJohn Birrell 226ff6d951SJohn Birrell /* 232693feb4SJohn Birrell * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 246ff6d951SJohn Birrell * Use is subject to license terms. 256ff6d951SJohn Birrell */ 266ff6d951SJohn Birrell 276ff6d951SJohn Birrell #pragma ident "%Z%%M% %I% %E% SMI" 286ff6d951SJohn Birrell 296ff6d951SJohn Birrell #define ELF_TARGET_ALL 306ff6d951SJohn Birrell #include <elf.h> 316ff6d951SJohn Birrell 326ff6d951SJohn Birrell #include <sys/types.h> 332693feb4SJohn Birrell #if defined(sun) 346ff6d951SJohn Birrell #include <sys/sysmacros.h> 352693feb4SJohn Birrell #else 362693feb4SJohn Birrell #define P2ROUNDUP(x, align) (-(-(x) & -(align))) 372693feb4SJohn Birrell #endif 386ff6d951SJohn Birrell 396ff6d951SJohn Birrell #include <unistd.h> 406ff6d951SJohn Birrell #include <strings.h> 412693feb4SJohn Birrell #if defined(sun) 426ff6d951SJohn Birrell #include <alloca.h> 432693feb4SJohn Birrell #endif 446ff6d951SJohn Birrell #include <limits.h> 456ff6d951SJohn Birrell #include <stddef.h> 466ff6d951SJohn Birrell #include <stdlib.h> 476ff6d951SJohn Birrell #include <stdio.h> 486ff6d951SJohn Birrell #include <fcntl.h> 496ff6d951SJohn Birrell #include <errno.h> 502693feb4SJohn Birrell #if defined(sun) 516ff6d951SJohn Birrell #include <wait.h> 522693feb4SJohn Birrell #else 532693feb4SJohn Birrell #include <sys/wait.h> 540f2bd1e8SRui Paulo #include <libelf.h> 550f2bd1e8SRui Paulo #include <gelf.h> 560f2bd1e8SRui Paulo #include <sys/mman.h> 572693feb4SJohn Birrell #endif 586ff6d951SJohn Birrell #include <assert.h> 596ff6d951SJohn Birrell #include <sys/ipc.h> 606ff6d951SJohn Birrell 616ff6d951SJohn Birrell #include <dt_impl.h> 626ff6d951SJohn Birrell #include <dt_provider.h> 636ff6d951SJohn Birrell #include <dt_program.h> 646ff6d951SJohn Birrell #include <dt_string.h> 656ff6d951SJohn Birrell 666ff6d951SJohn Birrell #define ESHDR_NULL 0 676ff6d951SJohn Birrell #define ESHDR_SHSTRTAB 1 686ff6d951SJohn Birrell #define ESHDR_DOF 2 696ff6d951SJohn Birrell #define ESHDR_STRTAB 3 706ff6d951SJohn Birrell #define ESHDR_SYMTAB 4 716ff6d951SJohn Birrell #define ESHDR_REL 5 726ff6d951SJohn Birrell #define ESHDR_NUM 6 736ff6d951SJohn Birrell 746ff6d951SJohn Birrell #define PWRITE_SCN(index, data) \ 756ff6d951SJohn Birrell (lseek64(fd, (off64_t)elf_file.shdr[(index)].sh_offset, SEEK_SET) != \ 766ff6d951SJohn Birrell (off64_t)elf_file.shdr[(index)].sh_offset || \ 776ff6d951SJohn Birrell dt_write(dtp, fd, (data), elf_file.shdr[(index)].sh_size) != \ 786ff6d951SJohn Birrell elf_file.shdr[(index)].sh_size) 796ff6d951SJohn Birrell 806ff6d951SJohn Birrell static const char DTRACE_SHSTRTAB32[] = "\0" 816ff6d951SJohn Birrell ".shstrtab\0" /* 1 */ 826ff6d951SJohn Birrell ".SUNW_dof\0" /* 11 */ 836ff6d951SJohn Birrell ".strtab\0" /* 21 */ 846ff6d951SJohn Birrell ".symtab\0" /* 29 */ 856ff6d951SJohn Birrell #ifdef __sparc 866ff6d951SJohn Birrell ".rela.SUNW_dof"; /* 37 */ 876ff6d951SJohn Birrell #else 886ff6d951SJohn Birrell ".rel.SUNW_dof"; /* 37 */ 896ff6d951SJohn Birrell #endif 906ff6d951SJohn Birrell 916ff6d951SJohn Birrell static const char DTRACE_SHSTRTAB64[] = "\0" 926ff6d951SJohn Birrell ".shstrtab\0" /* 1 */ 936ff6d951SJohn Birrell ".SUNW_dof\0" /* 11 */ 946ff6d951SJohn Birrell ".strtab\0" /* 21 */ 956ff6d951SJohn Birrell ".symtab\0" /* 29 */ 966ff6d951SJohn Birrell ".rela.SUNW_dof"; /* 37 */ 976ff6d951SJohn Birrell 986ff6d951SJohn Birrell static const char DOFSTR[] = "__SUNW_dof"; 996ff6d951SJohn Birrell static const char DOFLAZYSTR[] = "___SUNW_dof"; 1006ff6d951SJohn Birrell 1016ff6d951SJohn Birrell typedef struct dt_link_pair { 1026ff6d951SJohn Birrell struct dt_link_pair *dlp_next; /* next pair in linked list */ 1036ff6d951SJohn Birrell void *dlp_str; /* buffer for string table */ 1046ff6d951SJohn Birrell void *dlp_sym; /* buffer for symbol table */ 1056ff6d951SJohn Birrell } dt_link_pair_t; 1066ff6d951SJohn Birrell 1076ff6d951SJohn Birrell typedef struct dof_elf32 { 1086ff6d951SJohn Birrell uint32_t de_nrel; /* relocation count */ 1096ff6d951SJohn Birrell #ifdef __sparc 1106ff6d951SJohn Birrell Elf32_Rela *de_rel; /* array of relocations for sparc */ 1116ff6d951SJohn Birrell #else 1126ff6d951SJohn Birrell Elf32_Rel *de_rel; /* array of relocations for x86 */ 1136ff6d951SJohn Birrell #endif 1146ff6d951SJohn Birrell uint32_t de_nsym; /* symbol count */ 1156ff6d951SJohn Birrell Elf32_Sym *de_sym; /* array of symbols */ 1166ff6d951SJohn Birrell uint32_t de_strlen; /* size of of string table */ 1176ff6d951SJohn Birrell char *de_strtab; /* string table */ 1186ff6d951SJohn Birrell uint32_t de_global; /* index of the first global symbol */ 1196ff6d951SJohn Birrell } dof_elf32_t; 1206ff6d951SJohn Birrell 1216ff6d951SJohn Birrell static int 1226ff6d951SJohn Birrell prepare_elf32(dtrace_hdl_t *dtp, const dof_hdr_t *dof, dof_elf32_t *dep) 1236ff6d951SJohn Birrell { 1246ff6d951SJohn Birrell dof_sec_t *dofs, *s; 1256ff6d951SJohn Birrell dof_relohdr_t *dofrh; 1266ff6d951SJohn Birrell dof_relodesc_t *dofr; 1276ff6d951SJohn Birrell char *strtab; 1286ff6d951SJohn Birrell int i, j, nrel; 1296ff6d951SJohn Birrell size_t strtabsz = 1; 1306ff6d951SJohn Birrell uint32_t count = 0; 1316ff6d951SJohn Birrell size_t base; 1326ff6d951SJohn Birrell Elf32_Sym *sym; 1336ff6d951SJohn Birrell #ifdef __sparc 1346ff6d951SJohn Birrell Elf32_Rela *rel; 1356ff6d951SJohn Birrell #else 1366ff6d951SJohn Birrell Elf32_Rel *rel; 1376ff6d951SJohn Birrell #endif 1386ff6d951SJohn Birrell 1396ff6d951SJohn Birrell /*LINTED*/ 1406ff6d951SJohn Birrell dofs = (dof_sec_t *)((char *)dof + dof->dofh_secoff); 1416ff6d951SJohn Birrell 1426ff6d951SJohn Birrell /* 1436ff6d951SJohn Birrell * First compute the size of the string table and the number of 1446ff6d951SJohn Birrell * relocations present in the DOF. 1456ff6d951SJohn Birrell */ 1466ff6d951SJohn Birrell for (i = 0; i < dof->dofh_secnum; i++) { 1476ff6d951SJohn Birrell if (dofs[i].dofs_type != DOF_SECT_URELHDR) 1486ff6d951SJohn Birrell continue; 1496ff6d951SJohn Birrell 1506ff6d951SJohn Birrell /*LINTED*/ 1516ff6d951SJohn Birrell dofrh = (dof_relohdr_t *)((char *)dof + dofs[i].dofs_offset); 1526ff6d951SJohn Birrell 1536ff6d951SJohn Birrell s = &dofs[dofrh->dofr_strtab]; 1546ff6d951SJohn Birrell strtab = (char *)dof + s->dofs_offset; 1556ff6d951SJohn Birrell assert(strtab[0] == '\0'); 1566ff6d951SJohn Birrell strtabsz += s->dofs_size - 1; 1576ff6d951SJohn Birrell 1586ff6d951SJohn Birrell s = &dofs[dofrh->dofr_relsec]; 1596ff6d951SJohn Birrell /*LINTED*/ 1606ff6d951SJohn Birrell dofr = (dof_relodesc_t *)((char *)dof + s->dofs_offset); 1616ff6d951SJohn Birrell count += s->dofs_size / s->dofs_entsize; 1626ff6d951SJohn Birrell } 1636ff6d951SJohn Birrell 1646ff6d951SJohn Birrell dep->de_strlen = strtabsz; 1656ff6d951SJohn Birrell dep->de_nrel = count; 1666ff6d951SJohn Birrell dep->de_nsym = count + 1; /* the first symbol is always null */ 1676ff6d951SJohn Birrell 1686ff6d951SJohn Birrell if (dtp->dt_lazyload) { 1696ff6d951SJohn Birrell dep->de_strlen += sizeof (DOFLAZYSTR); 1706ff6d951SJohn Birrell dep->de_nsym++; 1716ff6d951SJohn Birrell } else { 1726ff6d951SJohn Birrell dep->de_strlen += sizeof (DOFSTR); 1736ff6d951SJohn Birrell dep->de_nsym++; 1746ff6d951SJohn Birrell } 1756ff6d951SJohn Birrell 1766ff6d951SJohn Birrell if ((dep->de_rel = calloc(dep->de_nrel, 1776ff6d951SJohn Birrell sizeof (dep->de_rel[0]))) == NULL) { 1786ff6d951SJohn Birrell return (dt_set_errno(dtp, EDT_NOMEM)); 1796ff6d951SJohn Birrell } 1806ff6d951SJohn Birrell 1816ff6d951SJohn Birrell if ((dep->de_sym = calloc(dep->de_nsym, sizeof (Elf32_Sym))) == NULL) { 1826ff6d951SJohn Birrell free(dep->de_rel); 1836ff6d951SJohn Birrell return (dt_set_errno(dtp, EDT_NOMEM)); 1846ff6d951SJohn Birrell } 1856ff6d951SJohn Birrell 1866ff6d951SJohn Birrell if ((dep->de_strtab = calloc(dep->de_strlen, 1)) == NULL) { 1876ff6d951SJohn Birrell free(dep->de_rel); 1886ff6d951SJohn Birrell free(dep->de_sym); 1896ff6d951SJohn Birrell return (dt_set_errno(dtp, EDT_NOMEM)); 1906ff6d951SJohn Birrell } 1916ff6d951SJohn Birrell 1926ff6d951SJohn Birrell count = 0; 1936ff6d951SJohn Birrell strtabsz = 1; 1946ff6d951SJohn Birrell dep->de_strtab[0] = '\0'; 1956ff6d951SJohn Birrell rel = dep->de_rel; 1966ff6d951SJohn Birrell sym = dep->de_sym; 1976ff6d951SJohn Birrell dep->de_global = 1; 1986ff6d951SJohn Birrell 1996ff6d951SJohn Birrell /* 2006ff6d951SJohn Birrell * The first symbol table entry must be zeroed and is always ignored. 2016ff6d951SJohn Birrell */ 2026ff6d951SJohn Birrell bzero(sym, sizeof (Elf32_Sym)); 2036ff6d951SJohn Birrell sym++; 2046ff6d951SJohn Birrell 2056ff6d951SJohn Birrell /* 2066ff6d951SJohn Birrell * Take a second pass through the DOF sections filling in the 2076ff6d951SJohn Birrell * memory we allocated. 2086ff6d951SJohn Birrell */ 2096ff6d951SJohn Birrell for (i = 0; i < dof->dofh_secnum; i++) { 2106ff6d951SJohn Birrell if (dofs[i].dofs_type != DOF_SECT_URELHDR) 2116ff6d951SJohn Birrell continue; 2126ff6d951SJohn Birrell 2136ff6d951SJohn Birrell /*LINTED*/ 2146ff6d951SJohn Birrell dofrh = (dof_relohdr_t *)((char *)dof + dofs[i].dofs_offset); 2156ff6d951SJohn Birrell 2166ff6d951SJohn Birrell s = &dofs[dofrh->dofr_strtab]; 2176ff6d951SJohn Birrell strtab = (char *)dof + s->dofs_offset; 2186ff6d951SJohn Birrell bcopy(strtab + 1, dep->de_strtab + strtabsz, s->dofs_size); 2196ff6d951SJohn Birrell base = strtabsz; 2206ff6d951SJohn Birrell strtabsz += s->dofs_size - 1; 2216ff6d951SJohn Birrell 2226ff6d951SJohn Birrell s = &dofs[dofrh->dofr_relsec]; 2236ff6d951SJohn Birrell /*LINTED*/ 2246ff6d951SJohn Birrell dofr = (dof_relodesc_t *)((char *)dof + s->dofs_offset); 2256ff6d951SJohn Birrell nrel = s->dofs_size / s->dofs_entsize; 2266ff6d951SJohn Birrell 2276ff6d951SJohn Birrell s = &dofs[dofrh->dofr_tgtsec]; 2286ff6d951SJohn Birrell 2296ff6d951SJohn Birrell for (j = 0; j < nrel; j++) { 2302693feb4SJohn Birrell #if defined(__arm__) 2312693feb4SJohn Birrell /* XXX */ 2322693feb4SJohn Birrell printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); 2332693feb4SJohn Birrell #elif defined(__i386) || defined(__amd64) 2346ff6d951SJohn Birrell rel->r_offset = s->dofs_offset + 2356ff6d951SJohn Birrell dofr[j].dofr_offset; 2366ff6d951SJohn Birrell rel->r_info = ELF32_R_INFO(count + dep->de_global, 2376ff6d951SJohn Birrell R_386_32); 2382693feb4SJohn Birrell #elif defined(__mips__) 2392693feb4SJohn Birrell /* XXX */ 2402693feb4SJohn Birrell printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); 2412693feb4SJohn Birrell #elif defined(__powerpc__) 24230b318b9SJustin Hibbits /* 24330b318b9SJustin Hibbits * Add 4 bytes to hit the low half of this 64-bit 24430b318b9SJustin Hibbits * big-endian address. 24530b318b9SJustin Hibbits */ 24630b318b9SJustin Hibbits rel->r_offset = s->dofs_offset + 24730b318b9SJustin Hibbits dofr[j].dofr_offset + 4; 24830b318b9SJustin Hibbits rel->r_info = ELF32_R_INFO(count + dep->de_global, 24930b318b9SJustin Hibbits R_PPC_REL32); 2506ff6d951SJohn Birrell #elif defined(__sparc) 2516ff6d951SJohn Birrell /* 2526ff6d951SJohn Birrell * Add 4 bytes to hit the low half of this 64-bit 2536ff6d951SJohn Birrell * big-endian address. 2546ff6d951SJohn Birrell */ 2556ff6d951SJohn Birrell rel->r_offset = s->dofs_offset + 2566ff6d951SJohn Birrell dofr[j].dofr_offset + 4; 2576ff6d951SJohn Birrell rel->r_info = ELF32_R_INFO(count + dep->de_global, 2586ff6d951SJohn Birrell R_SPARC_32); 2596ff6d951SJohn Birrell #else 2606ff6d951SJohn Birrell #error unknown ISA 2616ff6d951SJohn Birrell #endif 2626ff6d951SJohn Birrell 2636ff6d951SJohn Birrell sym->st_name = base + dofr[j].dofr_name - 1; 2646ff6d951SJohn Birrell sym->st_value = 0; 2656ff6d951SJohn Birrell sym->st_size = 0; 2666ff6d951SJohn Birrell sym->st_info = ELF32_ST_INFO(STB_GLOBAL, STT_FUNC); 2676ff6d951SJohn Birrell sym->st_other = 0; 2686ff6d951SJohn Birrell sym->st_shndx = SHN_UNDEF; 2696ff6d951SJohn Birrell 2706ff6d951SJohn Birrell rel++; 2716ff6d951SJohn Birrell sym++; 2726ff6d951SJohn Birrell count++; 2736ff6d951SJohn Birrell } 2746ff6d951SJohn Birrell } 2756ff6d951SJohn Birrell 2766ff6d951SJohn Birrell /* 2776ff6d951SJohn Birrell * Add a symbol for the DOF itself. We use a different symbol for 2786ff6d951SJohn Birrell * lazily and actively loaded DOF to make them easy to distinguish. 2796ff6d951SJohn Birrell */ 2806ff6d951SJohn Birrell sym->st_name = strtabsz; 2816ff6d951SJohn Birrell sym->st_value = 0; 2826ff6d951SJohn Birrell sym->st_size = dof->dofh_filesz; 2836ff6d951SJohn Birrell sym->st_info = ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT); 2846ff6d951SJohn Birrell sym->st_other = 0; 2856ff6d951SJohn Birrell sym->st_shndx = ESHDR_DOF; 2866ff6d951SJohn Birrell sym++; 2876ff6d951SJohn Birrell 2886ff6d951SJohn Birrell if (dtp->dt_lazyload) { 2896ff6d951SJohn Birrell bcopy(DOFLAZYSTR, dep->de_strtab + strtabsz, 2906ff6d951SJohn Birrell sizeof (DOFLAZYSTR)); 2916ff6d951SJohn Birrell strtabsz += sizeof (DOFLAZYSTR); 2926ff6d951SJohn Birrell } else { 2936ff6d951SJohn Birrell bcopy(DOFSTR, dep->de_strtab + strtabsz, sizeof (DOFSTR)); 2946ff6d951SJohn Birrell strtabsz += sizeof (DOFSTR); 2956ff6d951SJohn Birrell } 2966ff6d951SJohn Birrell 2976ff6d951SJohn Birrell assert(count == dep->de_nrel); 2986ff6d951SJohn Birrell assert(strtabsz == dep->de_strlen); 2996ff6d951SJohn Birrell 3006ff6d951SJohn Birrell return (0); 3016ff6d951SJohn Birrell } 3026ff6d951SJohn Birrell 3036ff6d951SJohn Birrell 3046ff6d951SJohn Birrell typedef struct dof_elf64 { 3056ff6d951SJohn Birrell uint32_t de_nrel; 3066ff6d951SJohn Birrell Elf64_Rela *de_rel; 3076ff6d951SJohn Birrell uint32_t de_nsym; 3086ff6d951SJohn Birrell Elf64_Sym *de_sym; 3096ff6d951SJohn Birrell 3106ff6d951SJohn Birrell uint32_t de_strlen; 3116ff6d951SJohn Birrell char *de_strtab; 3126ff6d951SJohn Birrell 3136ff6d951SJohn Birrell uint32_t de_global; 3146ff6d951SJohn Birrell } dof_elf64_t; 3156ff6d951SJohn Birrell 3166ff6d951SJohn Birrell static int 3176ff6d951SJohn Birrell prepare_elf64(dtrace_hdl_t *dtp, const dof_hdr_t *dof, dof_elf64_t *dep) 3186ff6d951SJohn Birrell { 3196ff6d951SJohn Birrell dof_sec_t *dofs, *s; 3206ff6d951SJohn Birrell dof_relohdr_t *dofrh; 3216ff6d951SJohn Birrell dof_relodesc_t *dofr; 3226ff6d951SJohn Birrell char *strtab; 3236ff6d951SJohn Birrell int i, j, nrel; 3246ff6d951SJohn Birrell size_t strtabsz = 1; 3256ff6d951SJohn Birrell uint32_t count = 0; 3266ff6d951SJohn Birrell size_t base; 3276ff6d951SJohn Birrell Elf64_Sym *sym; 3286ff6d951SJohn Birrell Elf64_Rela *rel; 3296ff6d951SJohn Birrell 3306ff6d951SJohn Birrell /*LINTED*/ 3316ff6d951SJohn Birrell dofs = (dof_sec_t *)((char *)dof + dof->dofh_secoff); 3326ff6d951SJohn Birrell 3336ff6d951SJohn Birrell /* 3346ff6d951SJohn Birrell * First compute the size of the string table and the number of 3356ff6d951SJohn Birrell * relocations present in the DOF. 3366ff6d951SJohn Birrell */ 3376ff6d951SJohn Birrell for (i = 0; i < dof->dofh_secnum; i++) { 3386ff6d951SJohn Birrell if (dofs[i].dofs_type != DOF_SECT_URELHDR) 3396ff6d951SJohn Birrell continue; 3406ff6d951SJohn Birrell 3416ff6d951SJohn Birrell /*LINTED*/ 3426ff6d951SJohn Birrell dofrh = (dof_relohdr_t *)((char *)dof + dofs[i].dofs_offset); 3436ff6d951SJohn Birrell 3446ff6d951SJohn Birrell s = &dofs[dofrh->dofr_strtab]; 3456ff6d951SJohn Birrell strtab = (char *)dof + s->dofs_offset; 3466ff6d951SJohn Birrell assert(strtab[0] == '\0'); 3476ff6d951SJohn Birrell strtabsz += s->dofs_size - 1; 3486ff6d951SJohn Birrell 3496ff6d951SJohn Birrell s = &dofs[dofrh->dofr_relsec]; 3506ff6d951SJohn Birrell /*LINTED*/ 3516ff6d951SJohn Birrell dofr = (dof_relodesc_t *)((char *)dof + s->dofs_offset); 3526ff6d951SJohn Birrell count += s->dofs_size / s->dofs_entsize; 3536ff6d951SJohn Birrell } 3546ff6d951SJohn Birrell 3556ff6d951SJohn Birrell dep->de_strlen = strtabsz; 3566ff6d951SJohn Birrell dep->de_nrel = count; 3576ff6d951SJohn Birrell dep->de_nsym = count + 1; /* the first symbol is always null */ 3586ff6d951SJohn Birrell 3596ff6d951SJohn Birrell if (dtp->dt_lazyload) { 3606ff6d951SJohn Birrell dep->de_strlen += sizeof (DOFLAZYSTR); 3616ff6d951SJohn Birrell dep->de_nsym++; 3626ff6d951SJohn Birrell } else { 3636ff6d951SJohn Birrell dep->de_strlen += sizeof (DOFSTR); 3646ff6d951SJohn Birrell dep->de_nsym++; 3656ff6d951SJohn Birrell } 3666ff6d951SJohn Birrell 3676ff6d951SJohn Birrell if ((dep->de_rel = calloc(dep->de_nrel, 3686ff6d951SJohn Birrell sizeof (dep->de_rel[0]))) == NULL) { 3696ff6d951SJohn Birrell return (dt_set_errno(dtp, EDT_NOMEM)); 3706ff6d951SJohn Birrell } 3716ff6d951SJohn Birrell 3726ff6d951SJohn Birrell if ((dep->de_sym = calloc(dep->de_nsym, sizeof (Elf64_Sym))) == NULL) { 3736ff6d951SJohn Birrell free(dep->de_rel); 3746ff6d951SJohn Birrell return (dt_set_errno(dtp, EDT_NOMEM)); 3756ff6d951SJohn Birrell } 3766ff6d951SJohn Birrell 3776ff6d951SJohn Birrell if ((dep->de_strtab = calloc(dep->de_strlen, 1)) == NULL) { 3786ff6d951SJohn Birrell free(dep->de_rel); 3796ff6d951SJohn Birrell free(dep->de_sym); 3806ff6d951SJohn Birrell return (dt_set_errno(dtp, EDT_NOMEM)); 3816ff6d951SJohn Birrell } 3826ff6d951SJohn Birrell 3836ff6d951SJohn Birrell count = 0; 3846ff6d951SJohn Birrell strtabsz = 1; 3856ff6d951SJohn Birrell dep->de_strtab[0] = '\0'; 3866ff6d951SJohn Birrell rel = dep->de_rel; 3876ff6d951SJohn Birrell sym = dep->de_sym; 3886ff6d951SJohn Birrell dep->de_global = 1; 3896ff6d951SJohn Birrell 3906ff6d951SJohn Birrell /* 3916ff6d951SJohn Birrell * The first symbol table entry must be zeroed and is always ignored. 3926ff6d951SJohn Birrell */ 3936ff6d951SJohn Birrell bzero(sym, sizeof (Elf64_Sym)); 3946ff6d951SJohn Birrell sym++; 3956ff6d951SJohn Birrell 3966ff6d951SJohn Birrell /* 3976ff6d951SJohn Birrell * Take a second pass through the DOF sections filling in the 3986ff6d951SJohn Birrell * memory we allocated. 3996ff6d951SJohn Birrell */ 4006ff6d951SJohn Birrell for (i = 0; i < dof->dofh_secnum; i++) { 4016ff6d951SJohn Birrell if (dofs[i].dofs_type != DOF_SECT_URELHDR) 4026ff6d951SJohn Birrell continue; 4036ff6d951SJohn Birrell 4046ff6d951SJohn Birrell /*LINTED*/ 4056ff6d951SJohn Birrell dofrh = (dof_relohdr_t *)((char *)dof + dofs[i].dofs_offset); 4066ff6d951SJohn Birrell 4076ff6d951SJohn Birrell s = &dofs[dofrh->dofr_strtab]; 4086ff6d951SJohn Birrell strtab = (char *)dof + s->dofs_offset; 4096ff6d951SJohn Birrell bcopy(strtab + 1, dep->de_strtab + strtabsz, s->dofs_size); 4106ff6d951SJohn Birrell base = strtabsz; 4116ff6d951SJohn Birrell strtabsz += s->dofs_size - 1; 4126ff6d951SJohn Birrell 4136ff6d951SJohn Birrell s = &dofs[dofrh->dofr_relsec]; 4146ff6d951SJohn Birrell /*LINTED*/ 4156ff6d951SJohn Birrell dofr = (dof_relodesc_t *)((char *)dof + s->dofs_offset); 4166ff6d951SJohn Birrell nrel = s->dofs_size / s->dofs_entsize; 4176ff6d951SJohn Birrell 4186ff6d951SJohn Birrell s = &dofs[dofrh->dofr_tgtsec]; 4196ff6d951SJohn Birrell 4206ff6d951SJohn Birrell for (j = 0; j < nrel; j++) { 4212693feb4SJohn Birrell #ifdef DOODAD 4222693feb4SJohn Birrell #if defined(__arm__) 4232693feb4SJohn Birrell /* XXX */ 4242693feb4SJohn Birrell #elif defined(__mips__) 4252693feb4SJohn Birrell /* XXX */ 4262693feb4SJohn Birrell #elif defined(__powerpc__) 42730b318b9SJustin Hibbits rel->r_offset = s->dofs_offset + 42830b318b9SJustin Hibbits dofr[j].dofr_offset; 42930b318b9SJustin Hibbits rel->r_info = ELF64_R_INFO(count + dep->de_global, 43030b318b9SJustin Hibbits R_PPC64_REL64); 4312693feb4SJohn Birrell #elif defined(__i386) || defined(__amd64) 4326ff6d951SJohn Birrell rel->r_offset = s->dofs_offset + 4336ff6d951SJohn Birrell dofr[j].dofr_offset; 4346ff6d951SJohn Birrell rel->r_info = ELF64_R_INFO(count + dep->de_global, 4356ff6d951SJohn Birrell R_AMD64_64); 4366ff6d951SJohn Birrell #elif defined(__sparc) 4376ff6d951SJohn Birrell rel->r_offset = s->dofs_offset + 4386ff6d951SJohn Birrell dofr[j].dofr_offset; 4396ff6d951SJohn Birrell rel->r_info = ELF64_R_INFO(count + dep->de_global, 4406ff6d951SJohn Birrell R_SPARC_64); 4416ff6d951SJohn Birrell #else 4426ff6d951SJohn Birrell #error unknown ISA 4436ff6d951SJohn Birrell #endif 4442693feb4SJohn Birrell #endif 4456ff6d951SJohn Birrell 4466ff6d951SJohn Birrell sym->st_name = base + dofr[j].dofr_name - 1; 4476ff6d951SJohn Birrell sym->st_value = 0; 4486ff6d951SJohn Birrell sym->st_size = 0; 4496ff6d951SJohn Birrell sym->st_info = GELF_ST_INFO(STB_GLOBAL, STT_FUNC); 4506ff6d951SJohn Birrell sym->st_other = 0; 4516ff6d951SJohn Birrell sym->st_shndx = SHN_UNDEF; 4526ff6d951SJohn Birrell 4536ff6d951SJohn Birrell rel++; 4546ff6d951SJohn Birrell sym++; 4556ff6d951SJohn Birrell count++; 4566ff6d951SJohn Birrell } 4576ff6d951SJohn Birrell } 4586ff6d951SJohn Birrell 4596ff6d951SJohn Birrell /* 4606ff6d951SJohn Birrell * Add a symbol for the DOF itself. We use a different symbol for 4616ff6d951SJohn Birrell * lazily and actively loaded DOF to make them easy to distinguish. 4626ff6d951SJohn Birrell */ 4636ff6d951SJohn Birrell sym->st_name = strtabsz; 4646ff6d951SJohn Birrell sym->st_value = 0; 4656ff6d951SJohn Birrell sym->st_size = dof->dofh_filesz; 4666ff6d951SJohn Birrell sym->st_info = GELF_ST_INFO(STB_GLOBAL, STT_OBJECT); 4676ff6d951SJohn Birrell sym->st_other = 0; 4686ff6d951SJohn Birrell sym->st_shndx = ESHDR_DOF; 4696ff6d951SJohn Birrell sym++; 4706ff6d951SJohn Birrell 4716ff6d951SJohn Birrell if (dtp->dt_lazyload) { 4726ff6d951SJohn Birrell bcopy(DOFLAZYSTR, dep->de_strtab + strtabsz, 4736ff6d951SJohn Birrell sizeof (DOFLAZYSTR)); 4746ff6d951SJohn Birrell strtabsz += sizeof (DOFLAZYSTR); 4756ff6d951SJohn Birrell } else { 4766ff6d951SJohn Birrell bcopy(DOFSTR, dep->de_strtab + strtabsz, sizeof (DOFSTR)); 4776ff6d951SJohn Birrell strtabsz += sizeof (DOFSTR); 4786ff6d951SJohn Birrell } 4796ff6d951SJohn Birrell 4806ff6d951SJohn Birrell assert(count == dep->de_nrel); 4816ff6d951SJohn Birrell assert(strtabsz == dep->de_strlen); 4826ff6d951SJohn Birrell 4836ff6d951SJohn Birrell return (0); 4846ff6d951SJohn Birrell } 4856ff6d951SJohn Birrell 4866ff6d951SJohn Birrell /* 4876ff6d951SJohn Birrell * Write out an ELF32 file prologue consisting of a header, section headers, 4886ff6d951SJohn Birrell * and a section header string table. The DOF data will follow this prologue 4896ff6d951SJohn Birrell * and complete the contents of the given ELF file. 4906ff6d951SJohn Birrell */ 4916ff6d951SJohn Birrell static int 4926ff6d951SJohn Birrell dump_elf32(dtrace_hdl_t *dtp, const dof_hdr_t *dof, int fd) 4936ff6d951SJohn Birrell { 4946ff6d951SJohn Birrell struct { 4956ff6d951SJohn Birrell Elf32_Ehdr ehdr; 4966ff6d951SJohn Birrell Elf32_Shdr shdr[ESHDR_NUM]; 4976ff6d951SJohn Birrell } elf_file; 4986ff6d951SJohn Birrell 4996ff6d951SJohn Birrell Elf32_Shdr *shp; 5006ff6d951SJohn Birrell Elf32_Off off; 5016ff6d951SJohn Birrell dof_elf32_t de; 5026ff6d951SJohn Birrell int ret = 0; 5036ff6d951SJohn Birrell uint_t nshdr; 5046ff6d951SJohn Birrell 5056ff6d951SJohn Birrell if (prepare_elf32(dtp, dof, &de) != 0) 5066ff6d951SJohn Birrell return (-1); /* errno is set for us */ 5076ff6d951SJohn Birrell 5086ff6d951SJohn Birrell /* 5096ff6d951SJohn Birrell * If there are no relocations, we only need enough sections for 5106ff6d951SJohn Birrell * the shstrtab and the DOF. 5116ff6d951SJohn Birrell */ 5126ff6d951SJohn Birrell nshdr = de.de_nrel == 0 ? ESHDR_SYMTAB + 1 : ESHDR_NUM; 5136ff6d951SJohn Birrell 5146ff6d951SJohn Birrell bzero(&elf_file, sizeof (elf_file)); 5156ff6d951SJohn Birrell 5166ff6d951SJohn Birrell elf_file.ehdr.e_ident[EI_MAG0] = ELFMAG0; 5176ff6d951SJohn Birrell elf_file.ehdr.e_ident[EI_MAG1] = ELFMAG1; 5186ff6d951SJohn Birrell elf_file.ehdr.e_ident[EI_MAG2] = ELFMAG2; 5196ff6d951SJohn Birrell elf_file.ehdr.e_ident[EI_MAG3] = ELFMAG3; 5206ff6d951SJohn Birrell elf_file.ehdr.e_ident[EI_VERSION] = EV_CURRENT; 5216ff6d951SJohn Birrell elf_file.ehdr.e_ident[EI_CLASS] = ELFCLASS32; 5222693feb4SJohn Birrell #if BYTE_ORDER == _BIG_ENDIAN 5236ff6d951SJohn Birrell elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2MSB; 5242693feb4SJohn Birrell #else 5256ff6d951SJohn Birrell elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2LSB; 5266ff6d951SJohn Birrell #endif 5272693feb4SJohn Birrell #if defined(__FreeBSD__) 5282693feb4SJohn Birrell elf_file.ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD; 5292693feb4SJohn Birrell #endif 5306ff6d951SJohn Birrell elf_file.ehdr.e_type = ET_REL; 5312693feb4SJohn Birrell #if defined(__arm__) 5322693feb4SJohn Birrell elf_file.ehdr.e_machine = EM_ARM; 5332693feb4SJohn Birrell #elif defined(__mips__) 5342693feb4SJohn Birrell elf_file.ehdr.e_machine = EM_MIPS; 5352693feb4SJohn Birrell #elif defined(__powerpc__) 5362693feb4SJohn Birrell elf_file.ehdr.e_machine = EM_PPC; 5372693feb4SJohn Birrell #elif defined(__sparc) 5386ff6d951SJohn Birrell elf_file.ehdr.e_machine = EM_SPARC; 5396ff6d951SJohn Birrell #elif defined(__i386) || defined(__amd64) 5406ff6d951SJohn Birrell elf_file.ehdr.e_machine = EM_386; 5416ff6d951SJohn Birrell #endif 5426ff6d951SJohn Birrell elf_file.ehdr.e_version = EV_CURRENT; 5436ff6d951SJohn Birrell elf_file.ehdr.e_shoff = sizeof (Elf32_Ehdr); 5446ff6d951SJohn Birrell elf_file.ehdr.e_ehsize = sizeof (Elf32_Ehdr); 5456ff6d951SJohn Birrell elf_file.ehdr.e_phentsize = sizeof (Elf32_Phdr); 5466ff6d951SJohn Birrell elf_file.ehdr.e_shentsize = sizeof (Elf32_Shdr); 5476ff6d951SJohn Birrell elf_file.ehdr.e_shnum = nshdr; 5486ff6d951SJohn Birrell elf_file.ehdr.e_shstrndx = ESHDR_SHSTRTAB; 5496ff6d951SJohn Birrell off = sizeof (elf_file) + nshdr * sizeof (Elf32_Shdr); 5506ff6d951SJohn Birrell 5516ff6d951SJohn Birrell shp = &elf_file.shdr[ESHDR_SHSTRTAB]; 5526ff6d951SJohn Birrell shp->sh_name = 1; /* DTRACE_SHSTRTAB32[1] = ".shstrtab" */ 5536ff6d951SJohn Birrell shp->sh_type = SHT_STRTAB; 5546ff6d951SJohn Birrell shp->sh_offset = off; 5556ff6d951SJohn Birrell shp->sh_size = sizeof (DTRACE_SHSTRTAB32); 5566ff6d951SJohn Birrell shp->sh_addralign = sizeof (char); 5576ff6d951SJohn Birrell off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 8); 5586ff6d951SJohn Birrell 5596ff6d951SJohn Birrell shp = &elf_file.shdr[ESHDR_DOF]; 5606ff6d951SJohn Birrell shp->sh_name = 11; /* DTRACE_SHSTRTAB32[11] = ".SUNW_dof" */ 5616ff6d951SJohn Birrell shp->sh_flags = SHF_ALLOC; 5626ff6d951SJohn Birrell shp->sh_type = SHT_SUNW_dof; 5636ff6d951SJohn Birrell shp->sh_offset = off; 5646ff6d951SJohn Birrell shp->sh_size = dof->dofh_filesz; 5656ff6d951SJohn Birrell shp->sh_addralign = 8; 5666ff6d951SJohn Birrell off = shp->sh_offset + shp->sh_size; 5676ff6d951SJohn Birrell 5686ff6d951SJohn Birrell shp = &elf_file.shdr[ESHDR_STRTAB]; 5696ff6d951SJohn Birrell shp->sh_name = 21; /* DTRACE_SHSTRTAB32[21] = ".strtab" */ 5706ff6d951SJohn Birrell shp->sh_flags = SHF_ALLOC; 5716ff6d951SJohn Birrell shp->sh_type = SHT_STRTAB; 5726ff6d951SJohn Birrell shp->sh_offset = off; 5736ff6d951SJohn Birrell shp->sh_size = de.de_strlen; 5746ff6d951SJohn Birrell shp->sh_addralign = sizeof (char); 5756ff6d951SJohn Birrell off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 4); 5766ff6d951SJohn Birrell 5776ff6d951SJohn Birrell shp = &elf_file.shdr[ESHDR_SYMTAB]; 5786ff6d951SJohn Birrell shp->sh_name = 29; /* DTRACE_SHSTRTAB32[29] = ".symtab" */ 5796ff6d951SJohn Birrell shp->sh_flags = SHF_ALLOC; 5806ff6d951SJohn Birrell shp->sh_type = SHT_SYMTAB; 5816ff6d951SJohn Birrell shp->sh_entsize = sizeof (Elf32_Sym); 5826ff6d951SJohn Birrell shp->sh_link = ESHDR_STRTAB; 5836ff6d951SJohn Birrell shp->sh_offset = off; 5846ff6d951SJohn Birrell shp->sh_info = de.de_global; 5856ff6d951SJohn Birrell shp->sh_size = de.de_nsym * sizeof (Elf32_Sym); 5866ff6d951SJohn Birrell shp->sh_addralign = 4; 5876ff6d951SJohn Birrell off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 4); 5886ff6d951SJohn Birrell 5896ff6d951SJohn Birrell if (de.de_nrel == 0) { 5906ff6d951SJohn Birrell if (dt_write(dtp, fd, &elf_file, 5916ff6d951SJohn Birrell sizeof (elf_file)) != sizeof (elf_file) || 5926ff6d951SJohn Birrell PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB32) || 5936ff6d951SJohn Birrell PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) || 5946ff6d951SJohn Birrell PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) || 5956ff6d951SJohn Birrell PWRITE_SCN(ESHDR_DOF, dof)) { 5966ff6d951SJohn Birrell ret = dt_set_errno(dtp, errno); 5976ff6d951SJohn Birrell } 5986ff6d951SJohn Birrell } else { 5996ff6d951SJohn Birrell shp = &elf_file.shdr[ESHDR_REL]; 6006ff6d951SJohn Birrell shp->sh_name = 37; /* DTRACE_SHSTRTAB32[37] = ".rel.SUNW_dof" */ 6016ff6d951SJohn Birrell shp->sh_flags = SHF_ALLOC; 6026ff6d951SJohn Birrell #ifdef __sparc 6036ff6d951SJohn Birrell shp->sh_type = SHT_RELA; 6046ff6d951SJohn Birrell #else 6056ff6d951SJohn Birrell shp->sh_type = SHT_REL; 6066ff6d951SJohn Birrell #endif 6076ff6d951SJohn Birrell shp->sh_entsize = sizeof (de.de_rel[0]); 6086ff6d951SJohn Birrell shp->sh_link = ESHDR_SYMTAB; 6096ff6d951SJohn Birrell shp->sh_info = ESHDR_DOF; 6106ff6d951SJohn Birrell shp->sh_offset = off; 6116ff6d951SJohn Birrell shp->sh_size = de.de_nrel * sizeof (de.de_rel[0]); 6126ff6d951SJohn Birrell shp->sh_addralign = 4; 6136ff6d951SJohn Birrell 6146ff6d951SJohn Birrell if (dt_write(dtp, fd, &elf_file, 6156ff6d951SJohn Birrell sizeof (elf_file)) != sizeof (elf_file) || 6166ff6d951SJohn Birrell PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB32) || 6176ff6d951SJohn Birrell PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) || 6186ff6d951SJohn Birrell PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) || 6196ff6d951SJohn Birrell PWRITE_SCN(ESHDR_REL, de.de_rel) || 6206ff6d951SJohn Birrell PWRITE_SCN(ESHDR_DOF, dof)) { 6216ff6d951SJohn Birrell ret = dt_set_errno(dtp, errno); 6226ff6d951SJohn Birrell } 6236ff6d951SJohn Birrell } 6246ff6d951SJohn Birrell 6256ff6d951SJohn Birrell free(de.de_strtab); 6266ff6d951SJohn Birrell free(de.de_sym); 6276ff6d951SJohn Birrell free(de.de_rel); 6286ff6d951SJohn Birrell 6296ff6d951SJohn Birrell return (ret); 6306ff6d951SJohn Birrell } 6316ff6d951SJohn Birrell 6326ff6d951SJohn Birrell /* 6336ff6d951SJohn Birrell * Write out an ELF64 file prologue consisting of a header, section headers, 6346ff6d951SJohn Birrell * and a section header string table. The DOF data will follow this prologue 6356ff6d951SJohn Birrell * and complete the contents of the given ELF file. 6366ff6d951SJohn Birrell */ 6376ff6d951SJohn Birrell static int 6386ff6d951SJohn Birrell dump_elf64(dtrace_hdl_t *dtp, const dof_hdr_t *dof, int fd) 6396ff6d951SJohn Birrell { 6406ff6d951SJohn Birrell struct { 6416ff6d951SJohn Birrell Elf64_Ehdr ehdr; 6426ff6d951SJohn Birrell Elf64_Shdr shdr[ESHDR_NUM]; 6436ff6d951SJohn Birrell } elf_file; 6446ff6d951SJohn Birrell 6456ff6d951SJohn Birrell Elf64_Shdr *shp; 6466ff6d951SJohn Birrell Elf64_Off off; 6476ff6d951SJohn Birrell dof_elf64_t de; 6486ff6d951SJohn Birrell int ret = 0; 6496ff6d951SJohn Birrell uint_t nshdr; 6506ff6d951SJohn Birrell 6516ff6d951SJohn Birrell if (prepare_elf64(dtp, dof, &de) != 0) 6526ff6d951SJohn Birrell return (-1); /* errno is set for us */ 6536ff6d951SJohn Birrell 6546ff6d951SJohn Birrell /* 6556ff6d951SJohn Birrell * If there are no relocations, we only need enough sections for 6566ff6d951SJohn Birrell * the shstrtab and the DOF. 6576ff6d951SJohn Birrell */ 6586ff6d951SJohn Birrell nshdr = de.de_nrel == 0 ? ESHDR_SYMTAB + 1 : ESHDR_NUM; 6596ff6d951SJohn Birrell 6606ff6d951SJohn Birrell bzero(&elf_file, sizeof (elf_file)); 6616ff6d951SJohn Birrell 6626ff6d951SJohn Birrell elf_file.ehdr.e_ident[EI_MAG0] = ELFMAG0; 6636ff6d951SJohn Birrell elf_file.ehdr.e_ident[EI_MAG1] = ELFMAG1; 6646ff6d951SJohn Birrell elf_file.ehdr.e_ident[EI_MAG2] = ELFMAG2; 6656ff6d951SJohn Birrell elf_file.ehdr.e_ident[EI_MAG3] = ELFMAG3; 6666ff6d951SJohn Birrell elf_file.ehdr.e_ident[EI_VERSION] = EV_CURRENT; 6676ff6d951SJohn Birrell elf_file.ehdr.e_ident[EI_CLASS] = ELFCLASS64; 6682693feb4SJohn Birrell #if BYTE_ORDER == _BIG_ENDIAN 6696ff6d951SJohn Birrell elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2MSB; 6702693feb4SJohn Birrell #else 6716ff6d951SJohn Birrell elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2LSB; 6726ff6d951SJohn Birrell #endif 6732693feb4SJohn Birrell #if defined(__FreeBSD__) 6742693feb4SJohn Birrell elf_file.ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD; 6752693feb4SJohn Birrell #endif 6766ff6d951SJohn Birrell elf_file.ehdr.e_type = ET_REL; 6772693feb4SJohn Birrell #if defined(__arm__) 6782693feb4SJohn Birrell elf_file.ehdr.e_machine = EM_ARM; 6792693feb4SJohn Birrell #elif defined(__mips__) 6802693feb4SJohn Birrell elf_file.ehdr.e_machine = EM_MIPS; 6812693feb4SJohn Birrell #elif defined(__powerpc__) 6822693feb4SJohn Birrell elf_file.ehdr.e_machine = EM_PPC; 6832693feb4SJohn Birrell #elif defined(__sparc) 6846ff6d951SJohn Birrell elf_file.ehdr.e_machine = EM_SPARCV9; 6856ff6d951SJohn Birrell #elif defined(__i386) || defined(__amd64) 6866ff6d951SJohn Birrell elf_file.ehdr.e_machine = EM_AMD64; 6876ff6d951SJohn Birrell #endif 6886ff6d951SJohn Birrell elf_file.ehdr.e_version = EV_CURRENT; 6896ff6d951SJohn Birrell elf_file.ehdr.e_shoff = sizeof (Elf64_Ehdr); 6906ff6d951SJohn Birrell elf_file.ehdr.e_ehsize = sizeof (Elf64_Ehdr); 6916ff6d951SJohn Birrell elf_file.ehdr.e_phentsize = sizeof (Elf64_Phdr); 6926ff6d951SJohn Birrell elf_file.ehdr.e_shentsize = sizeof (Elf64_Shdr); 6936ff6d951SJohn Birrell elf_file.ehdr.e_shnum = nshdr; 6946ff6d951SJohn Birrell elf_file.ehdr.e_shstrndx = ESHDR_SHSTRTAB; 6956ff6d951SJohn Birrell off = sizeof (elf_file) + nshdr * sizeof (Elf64_Shdr); 6966ff6d951SJohn Birrell 6976ff6d951SJohn Birrell shp = &elf_file.shdr[ESHDR_SHSTRTAB]; 6986ff6d951SJohn Birrell shp->sh_name = 1; /* DTRACE_SHSTRTAB64[1] = ".shstrtab" */ 6996ff6d951SJohn Birrell shp->sh_type = SHT_STRTAB; 7006ff6d951SJohn Birrell shp->sh_offset = off; 7016ff6d951SJohn Birrell shp->sh_size = sizeof (DTRACE_SHSTRTAB64); 7026ff6d951SJohn Birrell shp->sh_addralign = sizeof (char); 7036ff6d951SJohn Birrell off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 8); 7046ff6d951SJohn Birrell 7056ff6d951SJohn Birrell shp = &elf_file.shdr[ESHDR_DOF]; 7066ff6d951SJohn Birrell shp->sh_name = 11; /* DTRACE_SHSTRTAB64[11] = ".SUNW_dof" */ 7076ff6d951SJohn Birrell shp->sh_flags = SHF_ALLOC; 7086ff6d951SJohn Birrell shp->sh_type = SHT_SUNW_dof; 7096ff6d951SJohn Birrell shp->sh_offset = off; 7106ff6d951SJohn Birrell shp->sh_size = dof->dofh_filesz; 7116ff6d951SJohn Birrell shp->sh_addralign = 8; 7126ff6d951SJohn Birrell off = shp->sh_offset + shp->sh_size; 7136ff6d951SJohn Birrell 7146ff6d951SJohn Birrell shp = &elf_file.shdr[ESHDR_STRTAB]; 7156ff6d951SJohn Birrell shp->sh_name = 21; /* DTRACE_SHSTRTAB64[21] = ".strtab" */ 7166ff6d951SJohn Birrell shp->sh_flags = SHF_ALLOC; 7176ff6d951SJohn Birrell shp->sh_type = SHT_STRTAB; 7186ff6d951SJohn Birrell shp->sh_offset = off; 7196ff6d951SJohn Birrell shp->sh_size = de.de_strlen; 7206ff6d951SJohn Birrell shp->sh_addralign = sizeof (char); 7216ff6d951SJohn Birrell off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 8); 7226ff6d951SJohn Birrell 7236ff6d951SJohn Birrell shp = &elf_file.shdr[ESHDR_SYMTAB]; 7246ff6d951SJohn Birrell shp->sh_name = 29; /* DTRACE_SHSTRTAB64[29] = ".symtab" */ 7256ff6d951SJohn Birrell shp->sh_flags = SHF_ALLOC; 7266ff6d951SJohn Birrell shp->sh_type = SHT_SYMTAB; 7276ff6d951SJohn Birrell shp->sh_entsize = sizeof (Elf64_Sym); 7286ff6d951SJohn Birrell shp->sh_link = ESHDR_STRTAB; 7296ff6d951SJohn Birrell shp->sh_offset = off; 7306ff6d951SJohn Birrell shp->sh_info = de.de_global; 7316ff6d951SJohn Birrell shp->sh_size = de.de_nsym * sizeof (Elf64_Sym); 7326ff6d951SJohn Birrell shp->sh_addralign = 8; 7336ff6d951SJohn Birrell off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 8); 7346ff6d951SJohn Birrell 7356ff6d951SJohn Birrell if (de.de_nrel == 0) { 7366ff6d951SJohn Birrell if (dt_write(dtp, fd, &elf_file, 7376ff6d951SJohn Birrell sizeof (elf_file)) != sizeof (elf_file) || 7386ff6d951SJohn Birrell PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB64) || 7396ff6d951SJohn Birrell PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) || 7406ff6d951SJohn Birrell PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) || 7416ff6d951SJohn Birrell PWRITE_SCN(ESHDR_DOF, dof)) { 7426ff6d951SJohn Birrell ret = dt_set_errno(dtp, errno); 7436ff6d951SJohn Birrell } 7446ff6d951SJohn Birrell } else { 7456ff6d951SJohn Birrell shp = &elf_file.shdr[ESHDR_REL]; 7466ff6d951SJohn Birrell shp->sh_name = 37; /* DTRACE_SHSTRTAB64[37] = ".rel.SUNW_dof" */ 7476ff6d951SJohn Birrell shp->sh_flags = SHF_ALLOC; 7486ff6d951SJohn Birrell shp->sh_type = SHT_RELA; 7496ff6d951SJohn Birrell shp->sh_entsize = sizeof (de.de_rel[0]); 7506ff6d951SJohn Birrell shp->sh_link = ESHDR_SYMTAB; 7516ff6d951SJohn Birrell shp->sh_info = ESHDR_DOF; 7526ff6d951SJohn Birrell shp->sh_offset = off; 7536ff6d951SJohn Birrell shp->sh_size = de.de_nrel * sizeof (de.de_rel[0]); 7546ff6d951SJohn Birrell shp->sh_addralign = 8; 7556ff6d951SJohn Birrell 7566ff6d951SJohn Birrell if (dt_write(dtp, fd, &elf_file, 7576ff6d951SJohn Birrell sizeof (elf_file)) != sizeof (elf_file) || 7586ff6d951SJohn Birrell PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB64) || 7596ff6d951SJohn Birrell PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) || 7606ff6d951SJohn Birrell PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) || 7616ff6d951SJohn Birrell PWRITE_SCN(ESHDR_REL, de.de_rel) || 7626ff6d951SJohn Birrell PWRITE_SCN(ESHDR_DOF, dof)) { 7636ff6d951SJohn Birrell ret = dt_set_errno(dtp, errno); 7646ff6d951SJohn Birrell } 7656ff6d951SJohn Birrell } 7666ff6d951SJohn Birrell 7676ff6d951SJohn Birrell free(de.de_strtab); 7686ff6d951SJohn Birrell free(de.de_sym); 7696ff6d951SJohn Birrell free(de.de_rel); 7706ff6d951SJohn Birrell 7716ff6d951SJohn Birrell return (ret); 7726ff6d951SJohn Birrell } 7736ff6d951SJohn Birrell 7746ff6d951SJohn Birrell static int 7756ff6d951SJohn Birrell dt_symtab_lookup(Elf_Data *data_sym, int nsym, uintptr_t addr, uint_t shn, 7766ff6d951SJohn Birrell GElf_Sym *sym) 7776ff6d951SJohn Birrell { 7786ff6d951SJohn Birrell int i, ret = -1; 7796ff6d951SJohn Birrell GElf_Sym s; 7806ff6d951SJohn Birrell 7816ff6d951SJohn Birrell for (i = 0; i < nsym && gelf_getsym(data_sym, i, sym) != NULL; i++) { 7826ff6d951SJohn Birrell if (GELF_ST_TYPE(sym->st_info) == STT_FUNC && 7836ff6d951SJohn Birrell shn == sym->st_shndx && 7846ff6d951SJohn Birrell sym->st_value <= addr && 7856ff6d951SJohn Birrell addr < sym->st_value + sym->st_size) { 7866ff6d951SJohn Birrell if (GELF_ST_BIND(sym->st_info) == STB_GLOBAL) 7876ff6d951SJohn Birrell return (0); 7886ff6d951SJohn Birrell 7896ff6d951SJohn Birrell ret = 0; 7906ff6d951SJohn Birrell s = *sym; 7916ff6d951SJohn Birrell } 7926ff6d951SJohn Birrell } 7936ff6d951SJohn Birrell 7946ff6d951SJohn Birrell if (ret == 0) 7956ff6d951SJohn Birrell *sym = s; 7966ff6d951SJohn Birrell return (ret); 7976ff6d951SJohn Birrell } 7986ff6d951SJohn Birrell 7992693feb4SJohn Birrell #if defined(__arm__) 8002693feb4SJohn Birrell /* XXX */ 8012693feb4SJohn Birrell static int 8022693feb4SJohn Birrell dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela, 8032693feb4SJohn Birrell uint32_t *off) 8042693feb4SJohn Birrell { 8052693feb4SJohn Birrell printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); 8062693feb4SJohn Birrell return (0); 8072693feb4SJohn Birrell } 8082693feb4SJohn Birrell #elif defined(__mips__) 8092693feb4SJohn Birrell /* XXX */ 8102693feb4SJohn Birrell static int 8112693feb4SJohn Birrell dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela, 8122693feb4SJohn Birrell uint32_t *off) 8132693feb4SJohn Birrell { 8142693feb4SJohn Birrell printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__); 8152693feb4SJohn Birrell return (0); 8162693feb4SJohn Birrell } 8172693feb4SJohn Birrell #elif defined(__powerpc__) 81830b318b9SJustin Hibbits /* The sentinel is 'xor r3,r3,r3'. */ 81930b318b9SJustin Hibbits #define DT_OP_XOR_R3 0x7c631a78 82030b318b9SJustin Hibbits 82130b318b9SJustin Hibbits #define DT_OP_NOP 0x60000000 82230b318b9SJustin Hibbits #define DT_OP_BLR 0x4e800020 82330b318b9SJustin Hibbits 82430b318b9SJustin Hibbits /* This captures all forms of branching to address. */ 82530b318b9SJustin Hibbits #define DT_IS_BRANCH(inst) ((inst & 0xfc000000) == 0x48000000) 82630b318b9SJustin Hibbits #define DT_IS_BL(inst) (DT_IS_BRANCH(inst) && (inst & 0x01)) 82730b318b9SJustin Hibbits 8282693feb4SJohn Birrell /* XXX */ 8292693feb4SJohn Birrell static int 8302693feb4SJohn Birrell dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela, 8312693feb4SJohn Birrell uint32_t *off) 8322693feb4SJohn Birrell { 83330b318b9SJustin Hibbits uint32_t *ip; 83430b318b9SJustin Hibbits 83530b318b9SJustin Hibbits if ((rela->r_offset & (sizeof (uint32_t) - 1)) != 0) 83630b318b9SJustin Hibbits return (-1); 83730b318b9SJustin Hibbits 83830b318b9SJustin Hibbits /*LINTED*/ 83930b318b9SJustin Hibbits ip = (uint32_t *)(p + rela->r_offset); 84030b318b9SJustin Hibbits 84130b318b9SJustin Hibbits /* 84230b318b9SJustin Hibbits * We only know about some specific relocation types. 84330b318b9SJustin Hibbits */ 84430b318b9SJustin Hibbits if (GELF_R_TYPE(rela->r_info) != R_PPC_REL24 && 84530b318b9SJustin Hibbits GELF_R_TYPE(rela->r_info) != R_PPC_PLTREL24) 84630b318b9SJustin Hibbits return (-1); 84730b318b9SJustin Hibbits 84830b318b9SJustin Hibbits /* 84930b318b9SJustin Hibbits * We may have already processed this object file in an earlier linker 85030b318b9SJustin Hibbits * invocation. Check to see if the present instruction sequence matches 85130b318b9SJustin Hibbits * the one we would install below. 85230b318b9SJustin Hibbits */ 85330b318b9SJustin Hibbits if (isenabled) { 85430b318b9SJustin Hibbits if (ip[0] == DT_OP_XOR_R3) { 85530b318b9SJustin Hibbits (*off) += sizeof (ip[0]); 85630b318b9SJustin Hibbits return (0); 85730b318b9SJustin Hibbits } 85830b318b9SJustin Hibbits } else { 85930b318b9SJustin Hibbits if (ip[0] == DT_OP_NOP) { 86030b318b9SJustin Hibbits (*off) += sizeof (ip[0]); 86130b318b9SJustin Hibbits return (0); 86230b318b9SJustin Hibbits } 86330b318b9SJustin Hibbits } 86430b318b9SJustin Hibbits 86530b318b9SJustin Hibbits /* 86630b318b9SJustin Hibbits * We only expect branch to address instructions. 86730b318b9SJustin Hibbits */ 86830b318b9SJustin Hibbits if (!DT_IS_BRANCH(ip[0])) { 86930b318b9SJustin Hibbits dt_dprintf("found %x instead of a branch instruction at %llx\n", 87030b318b9SJustin Hibbits ip[0], (u_longlong_t)rela->r_offset); 87130b318b9SJustin Hibbits return (-1); 87230b318b9SJustin Hibbits } 87330b318b9SJustin Hibbits 87430b318b9SJustin Hibbits if (isenabled) { 87530b318b9SJustin Hibbits /* 87630b318b9SJustin Hibbits * It would necessarily indicate incorrect usage if an is- 87730b318b9SJustin Hibbits * enabled probe were tail-called so flag that as an error. 87830b318b9SJustin Hibbits * It's also potentially (very) tricky to handle gracefully, 87930b318b9SJustin Hibbits * but could be done if this were a desired use scenario. 88030b318b9SJustin Hibbits */ 88130b318b9SJustin Hibbits if (!DT_IS_BL(ip[0])) { 88230b318b9SJustin Hibbits dt_dprintf("tail call to is-enabled probe at %llx\n", 88330b318b9SJustin Hibbits (u_longlong_t)rela->r_offset); 88430b318b9SJustin Hibbits return (-1); 88530b318b9SJustin Hibbits } 88630b318b9SJustin Hibbits 88730b318b9SJustin Hibbits ip[0] = DT_OP_XOR_R3; 88830b318b9SJustin Hibbits (*off) += sizeof (ip[0]); 88930b318b9SJustin Hibbits } else { 89030b318b9SJustin Hibbits if (DT_IS_BL(ip[0])) 89130b318b9SJustin Hibbits ip[0] = DT_OP_NOP; 89230b318b9SJustin Hibbits else 89330b318b9SJustin Hibbits ip[0] = DT_OP_BLR; 89430b318b9SJustin Hibbits } 89530b318b9SJustin Hibbits 8962693feb4SJohn Birrell return (0); 8972693feb4SJohn Birrell } 8982693feb4SJohn Birrell 8992693feb4SJohn Birrell #elif defined(__sparc) 9006ff6d951SJohn Birrell 9016ff6d951SJohn Birrell #define DT_OP_RET 0x81c7e008 9026ff6d951SJohn Birrell #define DT_OP_NOP 0x01000000 9036ff6d951SJohn Birrell #define DT_OP_CALL 0x40000000 9046ff6d951SJohn Birrell #define DT_OP_CLR_O0 0x90102000 9056ff6d951SJohn Birrell 9066ff6d951SJohn Birrell #define DT_IS_MOV_O7(inst) (((inst) & 0xffffe000) == 0x9e100000) 9076ff6d951SJohn Birrell #define DT_IS_RESTORE(inst) (((inst) & 0xc1f80000) == 0x81e80000) 9086ff6d951SJohn Birrell #define DT_IS_RETL(inst) (((inst) & 0xfff83fff) == 0x81c02008) 9096ff6d951SJohn Birrell 9106ff6d951SJohn Birrell #define DT_RS2(inst) ((inst) & 0x1f) 9116ff6d951SJohn Birrell #define DT_MAKE_RETL(reg) (0x81c02008 | ((reg) << 14)) 9126ff6d951SJohn Birrell 9136ff6d951SJohn Birrell /*ARGSUSED*/ 9146ff6d951SJohn Birrell static int 9156ff6d951SJohn Birrell dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela, 9166ff6d951SJohn Birrell uint32_t *off) 9176ff6d951SJohn Birrell { 9186ff6d951SJohn Birrell uint32_t *ip; 9196ff6d951SJohn Birrell 9206ff6d951SJohn Birrell if ((rela->r_offset & (sizeof (uint32_t) - 1)) != 0) 9216ff6d951SJohn Birrell return (-1); 9226ff6d951SJohn Birrell 9236ff6d951SJohn Birrell /*LINTED*/ 9246ff6d951SJohn Birrell ip = (uint32_t *)(p + rela->r_offset); 9256ff6d951SJohn Birrell 9266ff6d951SJohn Birrell /* 9276ff6d951SJohn Birrell * We only know about some specific relocation types. 9286ff6d951SJohn Birrell */ 9296ff6d951SJohn Birrell if (GELF_R_TYPE(rela->r_info) != R_SPARC_WDISP30 && 9306ff6d951SJohn Birrell GELF_R_TYPE(rela->r_info) != R_SPARC_WPLT30) 9316ff6d951SJohn Birrell return (-1); 9326ff6d951SJohn Birrell 9336ff6d951SJohn Birrell /* 9346ff6d951SJohn Birrell * We may have already processed this object file in an earlier linker 9356ff6d951SJohn Birrell * invocation. Check to see if the present instruction sequence matches 93656b35563SCraig Rodrigues * the one we would install below. 9376ff6d951SJohn Birrell */ 9386ff6d951SJohn Birrell if (isenabled) { 93956b35563SCraig Rodrigues if (ip[0] == DT_OP_NOP) { 94056b35563SCraig Rodrigues (*off) += sizeof (ip[0]); 9416ff6d951SJohn Birrell return (0); 94256b35563SCraig Rodrigues } 9436ff6d951SJohn Birrell } else { 9446ff6d951SJohn Birrell if (DT_IS_RESTORE(ip[1])) { 94556b35563SCraig Rodrigues if (ip[0] == DT_OP_RET) { 94656b35563SCraig Rodrigues (*off) += sizeof (ip[0]); 9476ff6d951SJohn Birrell return (0); 94856b35563SCraig Rodrigues } 9496ff6d951SJohn Birrell } else if (DT_IS_MOV_O7(ip[1])) { 9506ff6d951SJohn Birrell if (DT_IS_RETL(ip[0])) 9516ff6d951SJohn Birrell return (0); 9526ff6d951SJohn Birrell } else { 9536ff6d951SJohn Birrell if (ip[0] == DT_OP_NOP) { 9546ff6d951SJohn Birrell (*off) += sizeof (ip[0]); 9556ff6d951SJohn Birrell return (0); 9566ff6d951SJohn Birrell } 9576ff6d951SJohn Birrell } 9586ff6d951SJohn Birrell } 9596ff6d951SJohn Birrell 9606ff6d951SJohn Birrell /* 9616ff6d951SJohn Birrell * We only expect call instructions with a displacement of 0. 9626ff6d951SJohn Birrell */ 9636ff6d951SJohn Birrell if (ip[0] != DT_OP_CALL) { 9646ff6d951SJohn Birrell dt_dprintf("found %x instead of a call instruction at %llx\n", 9656ff6d951SJohn Birrell ip[0], (u_longlong_t)rela->r_offset); 9666ff6d951SJohn Birrell return (-1); 9676ff6d951SJohn Birrell } 9686ff6d951SJohn Birrell 9696ff6d951SJohn Birrell if (isenabled) { 9706ff6d951SJohn Birrell /* 9716ff6d951SJohn Birrell * It would necessarily indicate incorrect usage if an is- 9726ff6d951SJohn Birrell * enabled probe were tail-called so flag that as an error. 9736ff6d951SJohn Birrell * It's also potentially (very) tricky to handle gracefully, 9746ff6d951SJohn Birrell * but could be done if this were a desired use scenario. 9756ff6d951SJohn Birrell */ 9766ff6d951SJohn Birrell if (DT_IS_RESTORE(ip[1]) || DT_IS_MOV_O7(ip[1])) { 9776ff6d951SJohn Birrell dt_dprintf("tail call to is-enabled probe at %llx\n", 9786ff6d951SJohn Birrell (u_longlong_t)rela->r_offset); 9796ff6d951SJohn Birrell return (-1); 9806ff6d951SJohn Birrell } 9816ff6d951SJohn Birrell 98256b35563SCraig Rodrigues 98356b35563SCraig Rodrigues /* 98456b35563SCraig Rodrigues * On SPARC, we take advantage of the fact that the first 98556b35563SCraig Rodrigues * argument shares the same register as for the return value. 98656b35563SCraig Rodrigues * The macro handles the work of zeroing that register so we 98756b35563SCraig Rodrigues * don't need to do anything special here. We instrument the 98856b35563SCraig Rodrigues * instruction in the delay slot as we'll need to modify the 98956b35563SCraig Rodrigues * return register after that instruction has been emulated. 99056b35563SCraig Rodrigues */ 99156b35563SCraig Rodrigues ip[0] = DT_OP_NOP; 99256b35563SCraig Rodrigues (*off) += sizeof (ip[0]); 9936ff6d951SJohn Birrell } else { 9946ff6d951SJohn Birrell /* 9956ff6d951SJohn Birrell * If the call is followed by a restore, it's a tail call so 9966ff6d951SJohn Birrell * change the call to a ret. If the call if followed by a mov 9976ff6d951SJohn Birrell * of a register into %o7, it's a tail call in leaf context 9986ff6d951SJohn Birrell * so change the call to a retl-like instruction that returns 9996ff6d951SJohn Birrell * to that register value + 8 (rather than the typical %o7 + 10006ff6d951SJohn Birrell * 8); the delay slot instruction is left, but should have no 100156b35563SCraig Rodrigues * effect. Otherwise we change the call to be a nop. We 100256b35563SCraig Rodrigues * identify the subsequent instruction as the probe point in 100356b35563SCraig Rodrigues * all but the leaf tail-call case to ensure that arguments to 100456b35563SCraig Rodrigues * the probe are complete and consistent. An astute, though 100556b35563SCraig Rodrigues * largely hypothetical, observer would note that there is the 100656b35563SCraig Rodrigues * possibility of a false-positive probe firing if the function 100756b35563SCraig Rodrigues * contained a branch to the instruction in the delay slot of 100856b35563SCraig Rodrigues * the call. Fixing this would require significant in-kernel 100956b35563SCraig Rodrigues * modifications, and isn't worth doing until we see it in the 101056b35563SCraig Rodrigues * wild. 10116ff6d951SJohn Birrell */ 10126ff6d951SJohn Birrell if (DT_IS_RESTORE(ip[1])) { 10136ff6d951SJohn Birrell ip[0] = DT_OP_RET; 10146ff6d951SJohn Birrell (*off) += sizeof (ip[0]); 10156ff6d951SJohn Birrell } else if (DT_IS_MOV_O7(ip[1])) { 10166ff6d951SJohn Birrell ip[0] = DT_MAKE_RETL(DT_RS2(ip[1])); 10176ff6d951SJohn Birrell } else { 10186ff6d951SJohn Birrell ip[0] = DT_OP_NOP; 10196ff6d951SJohn Birrell (*off) += sizeof (ip[0]); 10206ff6d951SJohn Birrell } 10216ff6d951SJohn Birrell } 10226ff6d951SJohn Birrell 10236ff6d951SJohn Birrell return (0); 10246ff6d951SJohn Birrell } 10256ff6d951SJohn Birrell 10266ff6d951SJohn Birrell #elif defined(__i386) || defined(__amd64) 10276ff6d951SJohn Birrell 10286ff6d951SJohn Birrell #define DT_OP_NOP 0x90 10292693feb4SJohn Birrell #define DT_OP_RET 0xc3 10306ff6d951SJohn Birrell #define DT_OP_CALL 0xe8 10312693feb4SJohn Birrell #define DT_OP_JMP32 0xe9 10326ff6d951SJohn Birrell #define DT_OP_REX_RAX 0x48 10336ff6d951SJohn Birrell #define DT_OP_XOR_EAX_0 0x33 10346ff6d951SJohn Birrell #define DT_OP_XOR_EAX_1 0xc0 10356ff6d951SJohn Birrell 10366ff6d951SJohn Birrell static int 10376ff6d951SJohn Birrell dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela, 10386ff6d951SJohn Birrell uint32_t *off) 10396ff6d951SJohn Birrell { 10406ff6d951SJohn Birrell uint8_t *ip = (uint8_t *)(p + rela->r_offset - 1); 10412693feb4SJohn Birrell uint8_t ret; 10426ff6d951SJohn Birrell 10436ff6d951SJohn Birrell /* 10446ff6d951SJohn Birrell * On x86, the first byte of the instruction is the call opcode and 10456ff6d951SJohn Birrell * the next four bytes are the 32-bit address; the relocation is for 10466ff6d951SJohn Birrell * the address operand. We back up the offset to the first byte of 10476ff6d951SJohn Birrell * the instruction. For is-enabled probes, we later advance the offset 10486ff6d951SJohn Birrell * so that it hits the first nop in the instruction sequence. 10496ff6d951SJohn Birrell */ 10506ff6d951SJohn Birrell (*off) -= 1; 10516ff6d951SJohn Birrell 10526ff6d951SJohn Birrell /* 10536ff6d951SJohn Birrell * We only know about some specific relocation types. Luckily 10546ff6d951SJohn Birrell * these types have the same values on both 32-bit and 64-bit 10556ff6d951SJohn Birrell * x86 architectures. 10566ff6d951SJohn Birrell */ 10576ff6d951SJohn Birrell if (GELF_R_TYPE(rela->r_info) != R_386_PC32 && 10586ff6d951SJohn Birrell GELF_R_TYPE(rela->r_info) != R_386_PLT32) 10596ff6d951SJohn Birrell return (-1); 10606ff6d951SJohn Birrell 10616ff6d951SJohn Birrell /* 10626ff6d951SJohn Birrell * We may have already processed this object file in an earlier linker 10636ff6d951SJohn Birrell * invocation. Check to see if the present instruction sequence matches 10646ff6d951SJohn Birrell * the one we would install. For is-enabled probes, we advance the 10652693feb4SJohn Birrell * offset to the first nop instruction in the sequence to match the 10662693feb4SJohn Birrell * text modification code below. 10676ff6d951SJohn Birrell */ 10686ff6d951SJohn Birrell if (!isenabled) { 10692693feb4SJohn Birrell if ((ip[0] == DT_OP_NOP || ip[0] == DT_OP_RET) && 10702693feb4SJohn Birrell ip[1] == DT_OP_NOP && ip[2] == DT_OP_NOP && 10712693feb4SJohn Birrell ip[3] == DT_OP_NOP && ip[4] == DT_OP_NOP) 10726ff6d951SJohn Birrell return (0); 10736ff6d951SJohn Birrell } else if (dtp->dt_oflags & DTRACE_O_LP64) { 10746ff6d951SJohn Birrell if (ip[0] == DT_OP_REX_RAX && 10756ff6d951SJohn Birrell ip[1] == DT_OP_XOR_EAX_0 && ip[2] == DT_OP_XOR_EAX_1 && 10762693feb4SJohn Birrell (ip[3] == DT_OP_NOP || ip[3] == DT_OP_RET) && 10772693feb4SJohn Birrell ip[4] == DT_OP_NOP) { 10786ff6d951SJohn Birrell (*off) += 3; 10796ff6d951SJohn Birrell return (0); 10806ff6d951SJohn Birrell } 10816ff6d951SJohn Birrell } else { 10826ff6d951SJohn Birrell if (ip[0] == DT_OP_XOR_EAX_0 && ip[1] == DT_OP_XOR_EAX_1 && 10832693feb4SJohn Birrell (ip[2] == DT_OP_NOP || ip[2] == DT_OP_RET) && 10842693feb4SJohn Birrell ip[3] == DT_OP_NOP && ip[4] == DT_OP_NOP) { 10856ff6d951SJohn Birrell (*off) += 2; 10866ff6d951SJohn Birrell return (0); 10876ff6d951SJohn Birrell } 10886ff6d951SJohn Birrell } 10896ff6d951SJohn Birrell 10906ff6d951SJohn Birrell /* 10912693feb4SJohn Birrell * We expect either a call instrution with a 32-bit displacement or a 10922693feb4SJohn Birrell * jmp instruction with a 32-bit displacement acting as a tail-call. 10936ff6d951SJohn Birrell */ 10942693feb4SJohn Birrell if (ip[0] != DT_OP_CALL && ip[0] != DT_OP_JMP32) { 10952693feb4SJohn Birrell dt_dprintf("found %x instead of a call or jmp instruction at " 10962693feb4SJohn Birrell "%llx\n", ip[0], (u_longlong_t)rela->r_offset); 10976ff6d951SJohn Birrell return (-1); 10986ff6d951SJohn Birrell } 10996ff6d951SJohn Birrell 11002693feb4SJohn Birrell ret = (ip[0] == DT_OP_JMP32) ? DT_OP_RET : DT_OP_NOP; 11012693feb4SJohn Birrell 11026ff6d951SJohn Birrell /* 11036ff6d951SJohn Birrell * Establish the instruction sequence -- all nops for probes, and an 11046ff6d951SJohn Birrell * instruction to clear the return value register (%eax/%rax) followed 11056ff6d951SJohn Birrell * by nops for is-enabled probes. For is-enabled probes, we advance 11066ff6d951SJohn Birrell * the offset to the first nop. This isn't stricly necessary but makes 11076ff6d951SJohn Birrell * for more readable disassembly when the probe is enabled. 11086ff6d951SJohn Birrell */ 11096ff6d951SJohn Birrell if (!isenabled) { 11102693feb4SJohn Birrell ip[0] = ret; 11116ff6d951SJohn Birrell ip[1] = DT_OP_NOP; 11126ff6d951SJohn Birrell ip[2] = DT_OP_NOP; 11136ff6d951SJohn Birrell ip[3] = DT_OP_NOP; 11146ff6d951SJohn Birrell ip[4] = DT_OP_NOP; 11156ff6d951SJohn Birrell } else if (dtp->dt_oflags & DTRACE_O_LP64) { 11166ff6d951SJohn Birrell ip[0] = DT_OP_REX_RAX; 11176ff6d951SJohn Birrell ip[1] = DT_OP_XOR_EAX_0; 11186ff6d951SJohn Birrell ip[2] = DT_OP_XOR_EAX_1; 11192693feb4SJohn Birrell ip[3] = ret; 11206ff6d951SJohn Birrell ip[4] = DT_OP_NOP; 11216ff6d951SJohn Birrell (*off) += 3; 11226ff6d951SJohn Birrell } else { 11236ff6d951SJohn Birrell ip[0] = DT_OP_XOR_EAX_0; 11246ff6d951SJohn Birrell ip[1] = DT_OP_XOR_EAX_1; 11252693feb4SJohn Birrell ip[2] = ret; 11266ff6d951SJohn Birrell ip[3] = DT_OP_NOP; 11276ff6d951SJohn Birrell ip[4] = DT_OP_NOP; 11286ff6d951SJohn Birrell (*off) += 2; 11296ff6d951SJohn Birrell } 11306ff6d951SJohn Birrell 11316ff6d951SJohn Birrell return (0); 11326ff6d951SJohn Birrell } 11336ff6d951SJohn Birrell 11346ff6d951SJohn Birrell #else 11356ff6d951SJohn Birrell #error unknown ISA 11366ff6d951SJohn Birrell #endif 11376ff6d951SJohn Birrell 11386ff6d951SJohn Birrell /*PRINTFLIKE5*/ 11396ff6d951SJohn Birrell static int 11406ff6d951SJohn Birrell dt_link_error(dtrace_hdl_t *dtp, Elf *elf, int fd, dt_link_pair_t *bufs, 11416ff6d951SJohn Birrell const char *format, ...) 11426ff6d951SJohn Birrell { 11436ff6d951SJohn Birrell va_list ap; 11446ff6d951SJohn Birrell dt_link_pair_t *pair; 11456ff6d951SJohn Birrell 11466ff6d951SJohn Birrell va_start(ap, format); 11476ff6d951SJohn Birrell dt_set_errmsg(dtp, NULL, NULL, NULL, 0, format, ap); 11486ff6d951SJohn Birrell va_end(ap); 11496ff6d951SJohn Birrell 11506ff6d951SJohn Birrell if (elf != NULL) 11516ff6d951SJohn Birrell (void) elf_end(elf); 11526ff6d951SJohn Birrell 11536ff6d951SJohn Birrell if (fd >= 0) 11546ff6d951SJohn Birrell (void) close(fd); 11556ff6d951SJohn Birrell 11566ff6d951SJohn Birrell while ((pair = bufs) != NULL) { 11576ff6d951SJohn Birrell bufs = pair->dlp_next; 11586ff6d951SJohn Birrell dt_free(dtp, pair->dlp_str); 11596ff6d951SJohn Birrell dt_free(dtp, pair->dlp_sym); 11606ff6d951SJohn Birrell dt_free(dtp, pair); 11616ff6d951SJohn Birrell } 11626ff6d951SJohn Birrell 11636ff6d951SJohn Birrell return (dt_set_errno(dtp, EDT_COMPILER)); 11646ff6d951SJohn Birrell } 11656ff6d951SJohn Birrell 11666ff6d951SJohn Birrell static int 11676ff6d951SJohn Birrell process_obj(dtrace_hdl_t *dtp, const char *obj, int *eprobesp) 11686ff6d951SJohn Birrell { 11696ff6d951SJohn Birrell static const char dt_prefix[] = "__dtrace"; 11706ff6d951SJohn Birrell static const char dt_enabled[] = "enabled"; 11716ff6d951SJohn Birrell static const char dt_symprefix[] = "$dtrace"; 11728b6b79ceSDimitry Andric static const char dt_symfmt[] = "%s%ld.%s"; 11736ff6d951SJohn Birrell int fd, i, ndx, eprobe, mod = 0; 11746ff6d951SJohn Birrell Elf *elf = NULL; 11756ff6d951SJohn Birrell GElf_Ehdr ehdr; 11766ff6d951SJohn Birrell Elf_Scn *scn_rel, *scn_sym, *scn_str, *scn_tgt; 11776ff6d951SJohn Birrell Elf_Data *data_rel, *data_sym, *data_str, *data_tgt; 11786ff6d951SJohn Birrell GElf_Shdr shdr_rel, shdr_sym, shdr_str, shdr_tgt; 11796ff6d951SJohn Birrell GElf_Sym rsym, fsym, dsym; 11806ff6d951SJohn Birrell GElf_Rela rela; 11816ff6d951SJohn Birrell char *s, *p, *r; 11826ff6d951SJohn Birrell char pname[DTRACE_PROVNAMELEN]; 11836ff6d951SJohn Birrell dt_provider_t *pvp; 11846ff6d951SJohn Birrell dt_probe_t *prp; 11856ff6d951SJohn Birrell uint32_t off, eclass, emachine1, emachine2; 11866ff6d951SJohn Birrell size_t symsize, nsym, isym, istr, len; 11876ff6d951SJohn Birrell key_t objkey; 11886ff6d951SJohn Birrell dt_link_pair_t *pair, *bufs = NULL; 11896ff6d951SJohn Birrell dt_strtab_t *strtab; 11906ff6d951SJohn Birrell 11916ff6d951SJohn Birrell if ((fd = open64(obj, O_RDWR)) == -1) { 11926ff6d951SJohn Birrell return (dt_link_error(dtp, elf, fd, bufs, 11936ff6d951SJohn Birrell "failed to open %s: %s", obj, strerror(errno))); 11946ff6d951SJohn Birrell } 11956ff6d951SJohn Birrell 11966ff6d951SJohn Birrell if ((elf = elf_begin(fd, ELF_C_RDWR, NULL)) == NULL) { 11976ff6d951SJohn Birrell return (dt_link_error(dtp, elf, fd, bufs, 11986ff6d951SJohn Birrell "failed to process %s: %s", obj, elf_errmsg(elf_errno()))); 11996ff6d951SJohn Birrell } 12006ff6d951SJohn Birrell 12016ff6d951SJohn Birrell switch (elf_kind(elf)) { 12026ff6d951SJohn Birrell case ELF_K_ELF: 12036ff6d951SJohn Birrell break; 12046ff6d951SJohn Birrell case ELF_K_AR: 12056ff6d951SJohn Birrell return (dt_link_error(dtp, elf, fd, bufs, "archives are not " 12066ff6d951SJohn Birrell "permitted; use the contents of the archive instead: %s", 12076ff6d951SJohn Birrell obj)); 12086ff6d951SJohn Birrell default: 12096ff6d951SJohn Birrell return (dt_link_error(dtp, elf, fd, bufs, 12106ff6d951SJohn Birrell "invalid file type: %s", obj)); 12116ff6d951SJohn Birrell } 12126ff6d951SJohn Birrell 12136ff6d951SJohn Birrell if (gelf_getehdr(elf, &ehdr) == NULL) { 12146ff6d951SJohn Birrell return (dt_link_error(dtp, elf, fd, bufs, "corrupt file: %s", 12156ff6d951SJohn Birrell obj)); 12166ff6d951SJohn Birrell } 12176ff6d951SJohn Birrell 12186ff6d951SJohn Birrell if (dtp->dt_oflags & DTRACE_O_LP64) { 12196ff6d951SJohn Birrell eclass = ELFCLASS64; 1220e7d939bdSMarcel Moolenaar #if defined(__mips__) 12212693feb4SJohn Birrell emachine1 = emachine2 = EM_MIPS; 12222693feb4SJohn Birrell #elif defined(__powerpc__) 12232693feb4SJohn Birrell emachine1 = emachine2 = EM_PPC64; 12242693feb4SJohn Birrell #elif defined(__sparc) 12256ff6d951SJohn Birrell emachine1 = emachine2 = EM_SPARCV9; 12266ff6d951SJohn Birrell #elif defined(__i386) || defined(__amd64) 12276ff6d951SJohn Birrell emachine1 = emachine2 = EM_AMD64; 12286ff6d951SJohn Birrell #endif 12296ff6d951SJohn Birrell symsize = sizeof (Elf64_Sym); 12306ff6d951SJohn Birrell } else { 12316ff6d951SJohn Birrell eclass = ELFCLASS32; 12322693feb4SJohn Birrell #if defined(__arm__) 12332693feb4SJohn Birrell emachine1 = emachine2 = EM_ARM; 12342693feb4SJohn Birrell #elif defined(__mips__) 12352693feb4SJohn Birrell emachine1 = emachine2 = EM_MIPS; 12362693feb4SJohn Birrell #elif defined(__powerpc__) 12372693feb4SJohn Birrell emachine1 = emachine2 = EM_PPC; 12382693feb4SJohn Birrell #elif defined(__sparc) 12396ff6d951SJohn Birrell emachine1 = EM_SPARC; 12406ff6d951SJohn Birrell emachine2 = EM_SPARC32PLUS; 1241e7d939bdSMarcel Moolenaar #elif defined(__i386) || defined(__amd64) 12426ff6d951SJohn Birrell emachine1 = emachine2 = EM_386; 12436ff6d951SJohn Birrell #endif 12446ff6d951SJohn Birrell symsize = sizeof (Elf32_Sym); 12456ff6d951SJohn Birrell } 12466ff6d951SJohn Birrell 12476ff6d951SJohn Birrell if (ehdr.e_ident[EI_CLASS] != eclass) { 12486ff6d951SJohn Birrell return (dt_link_error(dtp, elf, fd, bufs, 12496ff6d951SJohn Birrell "incorrect ELF class for object file: %s", obj)); 12506ff6d951SJohn Birrell } 12516ff6d951SJohn Birrell 12526ff6d951SJohn Birrell if (ehdr.e_machine != emachine1 && ehdr.e_machine != emachine2) { 12536ff6d951SJohn Birrell return (dt_link_error(dtp, elf, fd, bufs, 12546ff6d951SJohn Birrell "incorrect ELF machine type for object file: %s", obj)); 12556ff6d951SJohn Birrell } 12566ff6d951SJohn Birrell 12576ff6d951SJohn Birrell /* 12586ff6d951SJohn Birrell * We use this token as a relatively unique handle for this file on the 12596ff6d951SJohn Birrell * system in order to disambiguate potential conflicts between files of 12606ff6d951SJohn Birrell * the same name which contain identially named local symbols. 12616ff6d951SJohn Birrell */ 12626ff6d951SJohn Birrell if ((objkey = ftok(obj, 0)) == (key_t)-1) { 12636ff6d951SJohn Birrell return (dt_link_error(dtp, elf, fd, bufs, 12646ff6d951SJohn Birrell "failed to generate unique key for object file: %s", obj)); 12656ff6d951SJohn Birrell } 12666ff6d951SJohn Birrell 12676ff6d951SJohn Birrell scn_rel = NULL; 12686ff6d951SJohn Birrell while ((scn_rel = elf_nextscn(elf, scn_rel)) != NULL) { 12696ff6d951SJohn Birrell if (gelf_getshdr(scn_rel, &shdr_rel) == NULL) 12706ff6d951SJohn Birrell goto err; 12716ff6d951SJohn Birrell 12726ff6d951SJohn Birrell /* 12736ff6d951SJohn Birrell * Skip any non-relocation sections. 12746ff6d951SJohn Birrell */ 12756ff6d951SJohn Birrell if (shdr_rel.sh_type != SHT_RELA && shdr_rel.sh_type != SHT_REL) 12766ff6d951SJohn Birrell continue; 12776ff6d951SJohn Birrell 12786ff6d951SJohn Birrell if ((data_rel = elf_getdata(scn_rel, NULL)) == NULL) 12796ff6d951SJohn Birrell goto err; 12806ff6d951SJohn Birrell 12816ff6d951SJohn Birrell /* 12826ff6d951SJohn Birrell * Grab the section, section header and section data for the 12836ff6d951SJohn Birrell * symbol table that this relocation section references. 12846ff6d951SJohn Birrell */ 12856ff6d951SJohn Birrell if ((scn_sym = elf_getscn(elf, shdr_rel.sh_link)) == NULL || 12866ff6d951SJohn Birrell gelf_getshdr(scn_sym, &shdr_sym) == NULL || 12876ff6d951SJohn Birrell (data_sym = elf_getdata(scn_sym, NULL)) == NULL) 12886ff6d951SJohn Birrell goto err; 12896ff6d951SJohn Birrell 12906ff6d951SJohn Birrell /* 12916ff6d951SJohn Birrell * Ditto for that symbol table's string table. 12926ff6d951SJohn Birrell */ 12936ff6d951SJohn Birrell if ((scn_str = elf_getscn(elf, shdr_sym.sh_link)) == NULL || 12946ff6d951SJohn Birrell gelf_getshdr(scn_str, &shdr_str) == NULL || 12956ff6d951SJohn Birrell (data_str = elf_getdata(scn_str, NULL)) == NULL) 12966ff6d951SJohn Birrell goto err; 12976ff6d951SJohn Birrell 12986ff6d951SJohn Birrell /* 12996ff6d951SJohn Birrell * Grab the section, section header and section data for the 13006ff6d951SJohn Birrell * target section for the relocations. For the relocations 13016ff6d951SJohn Birrell * we're looking for -- this will typically be the text of the 13026ff6d951SJohn Birrell * object file. 13036ff6d951SJohn Birrell */ 13046ff6d951SJohn Birrell if ((scn_tgt = elf_getscn(elf, shdr_rel.sh_info)) == NULL || 13056ff6d951SJohn Birrell gelf_getshdr(scn_tgt, &shdr_tgt) == NULL || 13066ff6d951SJohn Birrell (data_tgt = elf_getdata(scn_tgt, NULL)) == NULL) 13076ff6d951SJohn Birrell goto err; 13086ff6d951SJohn Birrell 13096ff6d951SJohn Birrell /* 13106ff6d951SJohn Birrell * We're looking for relocations to symbols matching this form: 13116ff6d951SJohn Birrell * 13126ff6d951SJohn Birrell * __dtrace[enabled]_<prov>___<probe> 13136ff6d951SJohn Birrell * 13146ff6d951SJohn Birrell * For the generated object, we need to record the location 13156ff6d951SJohn Birrell * identified by the relocation, and create a new relocation 13166ff6d951SJohn Birrell * in the generated object that will be resolved at link time 13176ff6d951SJohn Birrell * to the location of the function in which the probe is 13186ff6d951SJohn Birrell * embedded. In the target object, we change the matched symbol 13196ff6d951SJohn Birrell * so that it will be ignored at link time, and we modify the 13206ff6d951SJohn Birrell * target (text) section to replace the call instruction with 13216ff6d951SJohn Birrell * one or more nops. 13226ff6d951SJohn Birrell * 13236ff6d951SJohn Birrell * If the function containing the probe is locally scoped 13246ff6d951SJohn Birrell * (static), we create an alias used by the relocation in the 13256ff6d951SJohn Birrell * generated object. The alias, a new symbol, will be global 13266ff6d951SJohn Birrell * (so that the relocation from the generated object can be 13276ff6d951SJohn Birrell * resolved), and hidden (so that it is converted to a local 13286ff6d951SJohn Birrell * symbol at link time). Such aliases have this form: 13296ff6d951SJohn Birrell * 13306ff6d951SJohn Birrell * $dtrace<key>.<function> 13316ff6d951SJohn Birrell * 13326ff6d951SJohn Birrell * We take a first pass through all the relocations to 13336ff6d951SJohn Birrell * populate our string table and count the number of extra 13346ff6d951SJohn Birrell * symbols we'll require. 13356ff6d951SJohn Birrell */ 13366ff6d951SJohn Birrell strtab = dt_strtab_create(1); 13376ff6d951SJohn Birrell nsym = 0; 13386ff6d951SJohn Birrell isym = data_sym->d_size / symsize; 13396ff6d951SJohn Birrell istr = data_str->d_size; 13406ff6d951SJohn Birrell 13416ff6d951SJohn Birrell for (i = 0; i < shdr_rel.sh_size / shdr_rel.sh_entsize; i++) { 13426ff6d951SJohn Birrell 13436ff6d951SJohn Birrell if (shdr_rel.sh_type == SHT_RELA) { 13446ff6d951SJohn Birrell if (gelf_getrela(data_rel, i, &rela) == NULL) 13456ff6d951SJohn Birrell continue; 13466ff6d951SJohn Birrell } else { 13476ff6d951SJohn Birrell GElf_Rel rel; 13486ff6d951SJohn Birrell if (gelf_getrel(data_rel, i, &rel) == NULL) 13496ff6d951SJohn Birrell continue; 13506ff6d951SJohn Birrell rela.r_offset = rel.r_offset; 13516ff6d951SJohn Birrell rela.r_info = rel.r_info; 13526ff6d951SJohn Birrell rela.r_addend = 0; 13536ff6d951SJohn Birrell } 13546ff6d951SJohn Birrell 13556ff6d951SJohn Birrell if (gelf_getsym(data_sym, GELF_R_SYM(rela.r_info), 13566ff6d951SJohn Birrell &rsym) == NULL) { 13576ff6d951SJohn Birrell dt_strtab_destroy(strtab); 13586ff6d951SJohn Birrell goto err; 13596ff6d951SJohn Birrell } 13606ff6d951SJohn Birrell 13616ff6d951SJohn Birrell s = (char *)data_str->d_buf + rsym.st_name; 13626ff6d951SJohn Birrell 13636ff6d951SJohn Birrell if (strncmp(s, dt_prefix, sizeof (dt_prefix) - 1) != 0) 13646ff6d951SJohn Birrell continue; 13656ff6d951SJohn Birrell 13666ff6d951SJohn Birrell if (dt_symtab_lookup(data_sym, isym, rela.r_offset, 13676ff6d951SJohn Birrell shdr_rel.sh_info, &fsym) != 0) { 13686ff6d951SJohn Birrell dt_strtab_destroy(strtab); 13696ff6d951SJohn Birrell goto err; 13706ff6d951SJohn Birrell } 13716ff6d951SJohn Birrell 13726ff6d951SJohn Birrell if (GELF_ST_BIND(fsym.st_info) != STB_LOCAL) 13736ff6d951SJohn Birrell continue; 13746ff6d951SJohn Birrell 13756ff6d951SJohn Birrell if (fsym.st_name > data_str->d_size) { 13766ff6d951SJohn Birrell dt_strtab_destroy(strtab); 13776ff6d951SJohn Birrell goto err; 13786ff6d951SJohn Birrell } 13796ff6d951SJohn Birrell 13806ff6d951SJohn Birrell s = (char *)data_str->d_buf + fsym.st_name; 13816ff6d951SJohn Birrell 13826ff6d951SJohn Birrell /* 13836ff6d951SJohn Birrell * If this symbol isn't of type function, we've really 13846ff6d951SJohn Birrell * driven off the rails or the object file is corrupt. 13856ff6d951SJohn Birrell */ 13866ff6d951SJohn Birrell if (GELF_ST_TYPE(fsym.st_info) != STT_FUNC) { 13876ff6d951SJohn Birrell dt_strtab_destroy(strtab); 13886ff6d951SJohn Birrell return (dt_link_error(dtp, elf, fd, bufs, 13896ff6d951SJohn Birrell "expected %s to be of type function", s)); 13906ff6d951SJohn Birrell } 13916ff6d951SJohn Birrell 13926ff6d951SJohn Birrell len = snprintf(NULL, 0, dt_symfmt, dt_symprefix, 13936ff6d951SJohn Birrell objkey, s) + 1; 13946ff6d951SJohn Birrell if ((p = dt_alloc(dtp, len)) == NULL) { 13956ff6d951SJohn Birrell dt_strtab_destroy(strtab); 13966ff6d951SJohn Birrell goto err; 13976ff6d951SJohn Birrell } 13986ff6d951SJohn Birrell (void) snprintf(p, len, dt_symfmt, dt_symprefix, 13996ff6d951SJohn Birrell objkey, s); 14006ff6d951SJohn Birrell 14016ff6d951SJohn Birrell if (dt_strtab_index(strtab, p) == -1) { 14026ff6d951SJohn Birrell nsym++; 14036ff6d951SJohn Birrell (void) dt_strtab_insert(strtab, p); 14046ff6d951SJohn Birrell } 14056ff6d951SJohn Birrell 14066ff6d951SJohn Birrell dt_free(dtp, p); 14076ff6d951SJohn Birrell } 14086ff6d951SJohn Birrell 14096ff6d951SJohn Birrell /* 14106ff6d951SJohn Birrell * If needed, allocate the additional space for the symbol 14116ff6d951SJohn Birrell * table and string table copying the old data into the new 14126ff6d951SJohn Birrell * buffers, and marking the buffers as dirty. We inject those 14136ff6d951SJohn Birrell * newly allocated buffers into the libelf data structures, but 14146ff6d951SJohn Birrell * are still responsible for freeing them once we're done with 14156ff6d951SJohn Birrell * the elf handle. 14166ff6d951SJohn Birrell */ 14176ff6d951SJohn Birrell if (nsym > 0) { 14186ff6d951SJohn Birrell /* 14196ff6d951SJohn Birrell * The first byte of the string table is reserved for 14206ff6d951SJohn Birrell * the \0 entry. 14216ff6d951SJohn Birrell */ 14226ff6d951SJohn Birrell len = dt_strtab_size(strtab) - 1; 14236ff6d951SJohn Birrell 14246ff6d951SJohn Birrell assert(len > 0); 14256ff6d951SJohn Birrell assert(dt_strtab_index(strtab, "") == 0); 14266ff6d951SJohn Birrell 14276ff6d951SJohn Birrell dt_strtab_destroy(strtab); 14286ff6d951SJohn Birrell 14296ff6d951SJohn Birrell if ((pair = dt_alloc(dtp, sizeof (*pair))) == NULL) 14306ff6d951SJohn Birrell goto err; 14316ff6d951SJohn Birrell 14326ff6d951SJohn Birrell if ((pair->dlp_str = dt_alloc(dtp, data_str->d_size + 14336ff6d951SJohn Birrell len)) == NULL) { 14346ff6d951SJohn Birrell dt_free(dtp, pair); 14356ff6d951SJohn Birrell goto err; 14366ff6d951SJohn Birrell } 14376ff6d951SJohn Birrell 14386ff6d951SJohn Birrell if ((pair->dlp_sym = dt_alloc(dtp, data_sym->d_size + 14396ff6d951SJohn Birrell nsym * symsize)) == NULL) { 14406ff6d951SJohn Birrell dt_free(dtp, pair->dlp_str); 14416ff6d951SJohn Birrell dt_free(dtp, pair); 14426ff6d951SJohn Birrell goto err; 14436ff6d951SJohn Birrell } 14446ff6d951SJohn Birrell 14456ff6d951SJohn Birrell pair->dlp_next = bufs; 14466ff6d951SJohn Birrell bufs = pair; 14476ff6d951SJohn Birrell 14486ff6d951SJohn Birrell bcopy(data_str->d_buf, pair->dlp_str, data_str->d_size); 14496ff6d951SJohn Birrell data_str->d_buf = pair->dlp_str; 14506ff6d951SJohn Birrell data_str->d_size += len; 14516ff6d951SJohn Birrell (void) elf_flagdata(data_str, ELF_C_SET, ELF_F_DIRTY); 14526ff6d951SJohn Birrell 14536ff6d951SJohn Birrell shdr_str.sh_size += len; 14546ff6d951SJohn Birrell (void) gelf_update_shdr(scn_str, &shdr_str); 14556ff6d951SJohn Birrell 14566ff6d951SJohn Birrell bcopy(data_sym->d_buf, pair->dlp_sym, data_sym->d_size); 14576ff6d951SJohn Birrell data_sym->d_buf = pair->dlp_sym; 14586ff6d951SJohn Birrell data_sym->d_size += nsym * symsize; 14596ff6d951SJohn Birrell (void) elf_flagdata(data_sym, ELF_C_SET, ELF_F_DIRTY); 14606ff6d951SJohn Birrell 14616ff6d951SJohn Birrell shdr_sym.sh_size += nsym * symsize; 14626ff6d951SJohn Birrell (void) gelf_update_shdr(scn_sym, &shdr_sym); 14636ff6d951SJohn Birrell 14646ff6d951SJohn Birrell nsym += isym; 14656ff6d951SJohn Birrell } else { 14666ff6d951SJohn Birrell dt_strtab_destroy(strtab); 14676ff6d951SJohn Birrell } 14686ff6d951SJohn Birrell 14696ff6d951SJohn Birrell /* 14706ff6d951SJohn Birrell * Now that the tables have been allocated, perform the 14716ff6d951SJohn Birrell * modifications described above. 14726ff6d951SJohn Birrell */ 14736ff6d951SJohn Birrell for (i = 0; i < shdr_rel.sh_size / shdr_rel.sh_entsize; i++) { 14746ff6d951SJohn Birrell 14756ff6d951SJohn Birrell if (shdr_rel.sh_type == SHT_RELA) { 14766ff6d951SJohn Birrell if (gelf_getrela(data_rel, i, &rela) == NULL) 14776ff6d951SJohn Birrell continue; 14786ff6d951SJohn Birrell } else { 14796ff6d951SJohn Birrell GElf_Rel rel; 14806ff6d951SJohn Birrell if (gelf_getrel(data_rel, i, &rel) == NULL) 14816ff6d951SJohn Birrell continue; 14826ff6d951SJohn Birrell rela.r_offset = rel.r_offset; 14836ff6d951SJohn Birrell rela.r_info = rel.r_info; 14846ff6d951SJohn Birrell rela.r_addend = 0; 14856ff6d951SJohn Birrell } 14866ff6d951SJohn Birrell 14876ff6d951SJohn Birrell ndx = GELF_R_SYM(rela.r_info); 14886ff6d951SJohn Birrell 14896ff6d951SJohn Birrell if (gelf_getsym(data_sym, ndx, &rsym) == NULL || 14906ff6d951SJohn Birrell rsym.st_name > data_str->d_size) 14916ff6d951SJohn Birrell goto err; 14926ff6d951SJohn Birrell 14936ff6d951SJohn Birrell s = (char *)data_str->d_buf + rsym.st_name; 14946ff6d951SJohn Birrell 14956ff6d951SJohn Birrell if (strncmp(s, dt_prefix, sizeof (dt_prefix) - 1) != 0) 14966ff6d951SJohn Birrell continue; 14976ff6d951SJohn Birrell 14986ff6d951SJohn Birrell s += sizeof (dt_prefix) - 1; 14996ff6d951SJohn Birrell 15006ff6d951SJohn Birrell /* 15016ff6d951SJohn Birrell * Check to see if this is an 'is-enabled' check as 15026ff6d951SJohn Birrell * opposed to a normal probe. 15036ff6d951SJohn Birrell */ 15046ff6d951SJohn Birrell if (strncmp(s, dt_enabled, 15056ff6d951SJohn Birrell sizeof (dt_enabled) - 1) == 0) { 15066ff6d951SJohn Birrell s += sizeof (dt_enabled) - 1; 15076ff6d951SJohn Birrell eprobe = 1; 15086ff6d951SJohn Birrell *eprobesp = 1; 15096ff6d951SJohn Birrell dt_dprintf("is-enabled probe\n"); 15106ff6d951SJohn Birrell } else { 15116ff6d951SJohn Birrell eprobe = 0; 15126ff6d951SJohn Birrell dt_dprintf("normal probe\n"); 15136ff6d951SJohn Birrell } 15146ff6d951SJohn Birrell 15156ff6d951SJohn Birrell if (*s++ != '_') 15166ff6d951SJohn Birrell goto err; 15176ff6d951SJohn Birrell 15186ff6d951SJohn Birrell if ((p = strstr(s, "___")) == NULL || 15196ff6d951SJohn Birrell p - s >= sizeof (pname)) 15206ff6d951SJohn Birrell goto err; 15216ff6d951SJohn Birrell 15226ff6d951SJohn Birrell bcopy(s, pname, p - s); 15236ff6d951SJohn Birrell pname[p - s] = '\0'; 15246ff6d951SJohn Birrell 15256ff6d951SJohn Birrell p = strhyphenate(p + 3); /* strlen("___") */ 15266ff6d951SJohn Birrell 15276ff6d951SJohn Birrell if (dt_symtab_lookup(data_sym, isym, rela.r_offset, 15286ff6d951SJohn Birrell shdr_rel.sh_info, &fsym) != 0) 15296ff6d951SJohn Birrell goto err; 15306ff6d951SJohn Birrell 15316ff6d951SJohn Birrell if (fsym.st_name > data_str->d_size) 15326ff6d951SJohn Birrell goto err; 15336ff6d951SJohn Birrell 15346ff6d951SJohn Birrell assert(GELF_ST_TYPE(fsym.st_info) == STT_FUNC); 15356ff6d951SJohn Birrell 15366ff6d951SJohn Birrell /* 15376ff6d951SJohn Birrell * If a NULL relocation name is passed to 15386ff6d951SJohn Birrell * dt_probe_define(), the function name is used for the 15396ff6d951SJohn Birrell * relocation. The relocation needs to use a mangled 15406ff6d951SJohn Birrell * name if the symbol is locally scoped; the function 15416ff6d951SJohn Birrell * name may need to change if we've found the global 15426ff6d951SJohn Birrell * alias for the locally scoped symbol (we prefer 15436ff6d951SJohn Birrell * global symbols to locals in dt_symtab_lookup()). 15446ff6d951SJohn Birrell */ 15456ff6d951SJohn Birrell s = (char *)data_str->d_buf + fsym.st_name; 15466ff6d951SJohn Birrell r = NULL; 15476ff6d951SJohn Birrell 15486ff6d951SJohn Birrell if (GELF_ST_BIND(fsym.st_info) == STB_LOCAL) { 15496ff6d951SJohn Birrell dsym = fsym; 15506ff6d951SJohn Birrell dsym.st_name = istr; 15516ff6d951SJohn Birrell dsym.st_info = GELF_ST_INFO(STB_GLOBAL, 15526ff6d951SJohn Birrell STT_FUNC); 15536ff6d951SJohn Birrell dsym.st_other = 15546ff6d951SJohn Birrell ELF64_ST_VISIBILITY(STV_ELIMINATE); 15556ff6d951SJohn Birrell (void) gelf_update_sym(data_sym, isym, &dsym); 15566ff6d951SJohn Birrell 15576ff6d951SJohn Birrell r = (char *)data_str->d_buf + istr; 15586ff6d951SJohn Birrell istr += 1 + sprintf(r, dt_symfmt, 15596ff6d951SJohn Birrell dt_symprefix, objkey, s); 15606ff6d951SJohn Birrell isym++; 15616ff6d951SJohn Birrell assert(isym <= nsym); 15626ff6d951SJohn Birrell 15636ff6d951SJohn Birrell } else if (strncmp(s, dt_symprefix, 15646ff6d951SJohn Birrell strlen(dt_symprefix)) == 0) { 15656ff6d951SJohn Birrell r = s; 15666ff6d951SJohn Birrell if ((s = strchr(s, '.')) == NULL) 15676ff6d951SJohn Birrell goto err; 15686ff6d951SJohn Birrell s++; 15696ff6d951SJohn Birrell } 15706ff6d951SJohn Birrell 15716ff6d951SJohn Birrell if ((pvp = dt_provider_lookup(dtp, pname)) == NULL) { 15726ff6d951SJohn Birrell return (dt_link_error(dtp, elf, fd, bufs, 15736ff6d951SJohn Birrell "no such provider %s", pname)); 15746ff6d951SJohn Birrell } 15756ff6d951SJohn Birrell 15766ff6d951SJohn Birrell if ((prp = dt_probe_lookup(pvp, p)) == NULL) { 15776ff6d951SJohn Birrell return (dt_link_error(dtp, elf, fd, bufs, 15786ff6d951SJohn Birrell "no such probe %s", p)); 15796ff6d951SJohn Birrell } 15806ff6d951SJohn Birrell 15816ff6d951SJohn Birrell assert(fsym.st_value <= rela.r_offset); 15826ff6d951SJohn Birrell 15836ff6d951SJohn Birrell off = rela.r_offset - fsym.st_value; 15846ff6d951SJohn Birrell if (dt_modtext(dtp, data_tgt->d_buf, eprobe, 15850f2bd1e8SRui Paulo &rela, &off) != 0) 15866ff6d951SJohn Birrell goto err; 15876ff6d951SJohn Birrell 15886ff6d951SJohn Birrell if (dt_probe_define(pvp, prp, s, r, off, eprobe) != 0) { 15896ff6d951SJohn Birrell return (dt_link_error(dtp, elf, fd, bufs, 15906ff6d951SJohn Birrell "failed to allocate space for probe")); 15916ff6d951SJohn Birrell } 15920f2bd1e8SRui Paulo #if !defined(sun) 15930f2bd1e8SRui Paulo /* 15940f2bd1e8SRui Paulo * Our linker doesn't understand the SUNW_IGNORE ndx and 15950f2bd1e8SRui Paulo * will try to use this relocation when we build the 15960f2bd1e8SRui Paulo * final executable. Since we are done processing this 15970f2bd1e8SRui Paulo * relocation, mark it as inexistant and let libelf 15980f2bd1e8SRui Paulo * remove it from the file. 15990f2bd1e8SRui Paulo * If this wasn't done, we would have garbage added to 16000f2bd1e8SRui Paulo * the executable file as the symbol is going to be 16010f2bd1e8SRui Paulo * change from UND to ABS. 16020f2bd1e8SRui Paulo */ 16032b374230SMark Johnston if (shdr_rel.sh_type == SHT_RELA) { 16040f2bd1e8SRui Paulo rela.r_offset = 0; 16050f2bd1e8SRui Paulo rela.r_info = 0; 16060f2bd1e8SRui Paulo rela.r_addend = 0; 16070f2bd1e8SRui Paulo (void) gelf_update_rela(data_rel, i, &rela); 16082b374230SMark Johnston } else { 16092b374230SMark Johnston GElf_Rel rel; 16102b374230SMark Johnston rel.r_offset = 0; 16112b374230SMark Johnston rel.r_info = 0; 16122b374230SMark Johnston (void) gelf_update_rel(data_rel, i, &rel); 16132b374230SMark Johnston } 16140f2bd1e8SRui Paulo #endif 16156ff6d951SJohn Birrell 16166ff6d951SJohn Birrell mod = 1; 16176ff6d951SJohn Birrell (void) elf_flagdata(data_tgt, ELF_C_SET, ELF_F_DIRTY); 16186ff6d951SJohn Birrell 16196ff6d951SJohn Birrell /* 16206ff6d951SJohn Birrell * This symbol may already have been marked to 16216ff6d951SJohn Birrell * be ignored by another relocation referencing 16226ff6d951SJohn Birrell * the same symbol or if this object file has 16236ff6d951SJohn Birrell * already been processed by an earlier link 16246ff6d951SJohn Birrell * invocation. 16256ff6d951SJohn Birrell */ 16260f2bd1e8SRui Paulo #if !defined(sun) 16270f2bd1e8SRui Paulo #define SHN_SUNW_IGNORE SHN_ABS 16280f2bd1e8SRui Paulo #endif 16296ff6d951SJohn Birrell if (rsym.st_shndx != SHN_SUNW_IGNORE) { 16306ff6d951SJohn Birrell rsym.st_shndx = SHN_SUNW_IGNORE; 16316ff6d951SJohn Birrell (void) gelf_update_sym(data_sym, ndx, &rsym); 16326ff6d951SJohn Birrell } 16336ff6d951SJohn Birrell } 16346ff6d951SJohn Birrell } 16356ff6d951SJohn Birrell 16366ff6d951SJohn Birrell if (mod && elf_update(elf, ELF_C_WRITE) == -1) 16376ff6d951SJohn Birrell goto err; 16386ff6d951SJohn Birrell 16396ff6d951SJohn Birrell (void) elf_end(elf); 16406ff6d951SJohn Birrell (void) close(fd); 16416ff6d951SJohn Birrell 16420f2bd1e8SRui Paulo #if !defined(sun) 16430f2bd1e8SRui Paulo if (nsym > 0) 16440f2bd1e8SRui Paulo #endif 16456ff6d951SJohn Birrell while ((pair = bufs) != NULL) { 16466ff6d951SJohn Birrell bufs = pair->dlp_next; 16476ff6d951SJohn Birrell dt_free(dtp, pair->dlp_str); 16486ff6d951SJohn Birrell dt_free(dtp, pair->dlp_sym); 16496ff6d951SJohn Birrell dt_free(dtp, pair); 16506ff6d951SJohn Birrell } 16516ff6d951SJohn Birrell 16526ff6d951SJohn Birrell return (0); 16536ff6d951SJohn Birrell 16546ff6d951SJohn Birrell err: 16556ff6d951SJohn Birrell return (dt_link_error(dtp, elf, fd, bufs, 16566ff6d951SJohn Birrell "an error was encountered while processing %s", obj)); 16576ff6d951SJohn Birrell } 16586ff6d951SJohn Birrell 16596ff6d951SJohn Birrell int 16606ff6d951SJohn Birrell dtrace_program_link(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, uint_t dflags, 16616ff6d951SJohn Birrell const char *file, int objc, char *const objv[]) 16626ff6d951SJohn Birrell { 16632693feb4SJohn Birrell #if !defined(sun) 16642693feb4SJohn Birrell char tfile[PATH_MAX]; 16650f2bd1e8SRui Paulo Elf *e; 16660f2bd1e8SRui Paulo Elf_Scn *scn; 16670f2bd1e8SRui Paulo Elf_Data *data; 16680f2bd1e8SRui Paulo GElf_Shdr shdr; 16690f2bd1e8SRui Paulo int efd; 16700f2bd1e8SRui Paulo size_t stridx; 16710f2bd1e8SRui Paulo unsigned char *buf; 16720f2bd1e8SRui Paulo char *s; 16730f2bd1e8SRui Paulo int loc; 16740f2bd1e8SRui Paulo GElf_Ehdr ehdr; 16750f2bd1e8SRui Paulo Elf_Scn *scn0; 16760f2bd1e8SRui Paulo GElf_Shdr shdr0; 16770f2bd1e8SRui Paulo uint64_t off, rc; 16782693feb4SJohn Birrell #endif 16796ff6d951SJohn Birrell char drti[PATH_MAX]; 16806ff6d951SJohn Birrell dof_hdr_t *dof; 16816ff6d951SJohn Birrell int fd, status, i, cur; 16826ff6d951SJohn Birrell char *cmd, tmp; 16836ff6d951SJohn Birrell size_t len; 16846ff6d951SJohn Birrell int eprobes = 0, ret = 0; 16856ff6d951SJohn Birrell 16862693feb4SJohn Birrell #if !defined(sun) 168737c380fbSRui Paulo if (access(file, R_OK) == 0) { 168837c380fbSRui Paulo fprintf(stderr, "dtrace: target object (%s) already exists. " 168937c380fbSRui Paulo "Please remove the target\ndtrace: object and rebuild all " 169037c380fbSRui Paulo "the source objects if you wish to run the DTrace\n" 169137c380fbSRui Paulo "dtrace: linking process again\n", file); 169237c380fbSRui Paulo /* 169337c380fbSRui Paulo * Several build infrastructures run DTrace twice (e.g. 169437c380fbSRui Paulo * postgres) and we don't want the build to fail. Return 169537c380fbSRui Paulo * 0 here since this isn't really a fatal error. 169637c380fbSRui Paulo */ 169737c380fbSRui Paulo return (0); 169837c380fbSRui Paulo } 16992693feb4SJohn Birrell #endif 17002693feb4SJohn Birrell 17016ff6d951SJohn Birrell /* 17026ff6d951SJohn Birrell * A NULL program indicates a special use in which we just link 17036ff6d951SJohn Birrell * together a bunch of object files specified in objv and then 17046ff6d951SJohn Birrell * unlink(2) those object files. 17056ff6d951SJohn Birrell */ 17066ff6d951SJohn Birrell if (pgp == NULL) { 17076ff6d951SJohn Birrell const char *fmt = "%s -o %s -r"; 17086ff6d951SJohn Birrell 17096ff6d951SJohn Birrell len = snprintf(&tmp, 1, fmt, dtp->dt_ld_path, file) + 1; 17106ff6d951SJohn Birrell 17116ff6d951SJohn Birrell for (i = 0; i < objc; i++) 17126ff6d951SJohn Birrell len += strlen(objv[i]) + 1; 17136ff6d951SJohn Birrell 17146ff6d951SJohn Birrell cmd = alloca(len); 17156ff6d951SJohn Birrell 17166ff6d951SJohn Birrell cur = snprintf(cmd, len, fmt, dtp->dt_ld_path, file); 17176ff6d951SJohn Birrell 17186ff6d951SJohn Birrell for (i = 0; i < objc; i++) 17196ff6d951SJohn Birrell cur += snprintf(cmd + cur, len - cur, " %s", objv[i]); 17206ff6d951SJohn Birrell 17216ff6d951SJohn Birrell if ((status = system(cmd)) == -1) { 17226ff6d951SJohn Birrell return (dt_link_error(dtp, NULL, -1, NULL, 17236ff6d951SJohn Birrell "failed to run %s: %s", dtp->dt_ld_path, 17246ff6d951SJohn Birrell strerror(errno))); 17256ff6d951SJohn Birrell } 17266ff6d951SJohn Birrell 17276ff6d951SJohn Birrell if (WIFSIGNALED(status)) { 17286ff6d951SJohn Birrell return (dt_link_error(dtp, NULL, -1, NULL, 17296ff6d951SJohn Birrell "failed to link %s: %s failed due to signal %d", 17306ff6d951SJohn Birrell file, dtp->dt_ld_path, WTERMSIG(status))); 17316ff6d951SJohn Birrell } 17326ff6d951SJohn Birrell 17336ff6d951SJohn Birrell if (WEXITSTATUS(status) != 0) { 17346ff6d951SJohn Birrell return (dt_link_error(dtp, NULL, -1, NULL, 17356ff6d951SJohn Birrell "failed to link %s: %s exited with status %d\n", 17366ff6d951SJohn Birrell file, dtp->dt_ld_path, WEXITSTATUS(status))); 17376ff6d951SJohn Birrell } 17386ff6d951SJohn Birrell 17396ff6d951SJohn Birrell for (i = 0; i < objc; i++) { 17406ff6d951SJohn Birrell if (strcmp(objv[i], file) != 0) 17416ff6d951SJohn Birrell (void) unlink(objv[i]); 17426ff6d951SJohn Birrell } 17436ff6d951SJohn Birrell 17446ff6d951SJohn Birrell return (0); 17456ff6d951SJohn Birrell } 17466ff6d951SJohn Birrell 17476ff6d951SJohn Birrell for (i = 0; i < objc; i++) { 17486ff6d951SJohn Birrell if (process_obj(dtp, objv[i], &eprobes) != 0) 17496ff6d951SJohn Birrell return (-1); /* errno is set for us */ 17506ff6d951SJohn Birrell } 17516ff6d951SJohn Birrell 17526ff6d951SJohn Birrell /* 17536ff6d951SJohn Birrell * If there are is-enabled probes then we need to force use of DOF 17546ff6d951SJohn Birrell * version 2. 17556ff6d951SJohn Birrell */ 17566ff6d951SJohn Birrell if (eprobes && pgp->dp_dofversion < DOF_VERSION_2) 17576ff6d951SJohn Birrell pgp->dp_dofversion = DOF_VERSION_2; 17586ff6d951SJohn Birrell 17596ff6d951SJohn Birrell if ((dof = dtrace_dof_create(dtp, pgp, dflags)) == NULL) 17606ff6d951SJohn Birrell return (-1); /* errno is set for us */ 17616ff6d951SJohn Birrell 17622693feb4SJohn Birrell #if defined(sun) 17636ff6d951SJohn Birrell /* 17646ff6d951SJohn Birrell * Create a temporary file and then unlink it if we're going to 17656ff6d951SJohn Birrell * combine it with drti.o later. We can still refer to it in child 17666ff6d951SJohn Birrell * processes as /dev/fd/<fd>. 17676ff6d951SJohn Birrell */ 17686ff6d951SJohn Birrell if ((fd = open64(file, O_RDWR | O_CREAT | O_TRUNC, 0666)) == -1) { 17696ff6d951SJohn Birrell return (dt_link_error(dtp, NULL, -1, NULL, 17706ff6d951SJohn Birrell "failed to open %s: %s", file, strerror(errno))); 17716ff6d951SJohn Birrell } 17722693feb4SJohn Birrell #else 177333d84250SMark Johnston snprintf(tfile, sizeof(tfile), "%s.XXXXXX", file); 177433d84250SMark Johnston if ((fd = mkstemp(tfile)) == -1) 17752693feb4SJohn Birrell return (dt_link_error(dtp, NULL, -1, NULL, 177633d84250SMark Johnston "failed to create temporary file %s: %s", 177733d84250SMark Johnston tfile, strerror(errno))); 17782693feb4SJohn Birrell #endif 17796ff6d951SJohn Birrell 17806ff6d951SJohn Birrell /* 17816ff6d951SJohn Birrell * If -xlinktype=DOF has been selected, just write out the DOF. 17826ff6d951SJohn Birrell * Otherwise proceed to the default of generating and linking ELF. 17836ff6d951SJohn Birrell */ 17846ff6d951SJohn Birrell switch (dtp->dt_linktype) { 17856ff6d951SJohn Birrell case DT_LTYP_DOF: 17866ff6d951SJohn Birrell if (dt_write(dtp, fd, dof, dof->dofh_filesz) < dof->dofh_filesz) 17876ff6d951SJohn Birrell ret = errno; 17886ff6d951SJohn Birrell 17896ff6d951SJohn Birrell if (close(fd) != 0 && ret == 0) 17906ff6d951SJohn Birrell ret = errno; 17916ff6d951SJohn Birrell 17926ff6d951SJohn Birrell if (ret != 0) { 17936ff6d951SJohn Birrell return (dt_link_error(dtp, NULL, -1, NULL, 17946ff6d951SJohn Birrell "failed to write %s: %s", file, strerror(ret))); 17956ff6d951SJohn Birrell } 17966ff6d951SJohn Birrell 17976ff6d951SJohn Birrell return (0); 17986ff6d951SJohn Birrell 17996ff6d951SJohn Birrell case DT_LTYP_ELF: 18006ff6d951SJohn Birrell break; /* fall through to the rest of dtrace_program_link() */ 18016ff6d951SJohn Birrell 18026ff6d951SJohn Birrell default: 18036ff6d951SJohn Birrell return (dt_link_error(dtp, NULL, -1, NULL, 18046ff6d951SJohn Birrell "invalid link type %u\n", dtp->dt_linktype)); 18056ff6d951SJohn Birrell } 18066ff6d951SJohn Birrell 18076ff6d951SJohn Birrell 18082693feb4SJohn Birrell #if defined(sun) 18096ff6d951SJohn Birrell if (!dtp->dt_lazyload) 18106ff6d951SJohn Birrell (void) unlink(file); 18112693feb4SJohn Birrell #endif 18126ff6d951SJohn Birrell 18130f2bd1e8SRui Paulo #if defined(sun) 18146ff6d951SJohn Birrell if (dtp->dt_oflags & DTRACE_O_LP64) 18156ff6d951SJohn Birrell status = dump_elf64(dtp, dof, fd); 18166ff6d951SJohn Birrell else 18176ff6d951SJohn Birrell status = dump_elf32(dtp, dof, fd); 18186ff6d951SJohn Birrell 18196ff6d951SJohn Birrell if (status != 0 || lseek(fd, 0, SEEK_SET) != 0) { 18206ff6d951SJohn Birrell return (dt_link_error(dtp, NULL, -1, NULL, 18216ff6d951SJohn Birrell "failed to write %s: %s", file, strerror(errno))); 18226ff6d951SJohn Birrell } 182333d84250SMark Johnston #else 182433d84250SMark Johnston /* We don't write the ELF header, just the DOF section */ 182533d84250SMark Johnston if (dt_write(dtp, fd, dof, dof->dofh_filesz) < dof->dofh_filesz) 182633d84250SMark Johnston return (dt_link_error(dtp, NULL, -1, NULL, 182733d84250SMark Johnston "failed to write %s: %s", tfile, strerror(errno))); 182833d84250SMark Johnston #endif 18296ff6d951SJohn Birrell 18306ff6d951SJohn Birrell if (!dtp->dt_lazyload) { 18312693feb4SJohn Birrell #if defined(sun) 18326ff6d951SJohn Birrell const char *fmt = "%s -o %s -r -Blocal -Breduce /dev/fd/%d %s"; 18336ff6d951SJohn Birrell 18346ff6d951SJohn Birrell if (dtp->dt_oflags & DTRACE_O_LP64) { 18356ff6d951SJohn Birrell (void) snprintf(drti, sizeof (drti), 18366ff6d951SJohn Birrell "%s/64/drti.o", _dtrace_libdir); 18376ff6d951SJohn Birrell } else { 18386ff6d951SJohn Birrell (void) snprintf(drti, sizeof (drti), 18396ff6d951SJohn Birrell "%s/drti.o", _dtrace_libdir); 18406ff6d951SJohn Birrell } 18416ff6d951SJohn Birrell 18426ff6d951SJohn Birrell len = snprintf(&tmp, 1, fmt, dtp->dt_ld_path, file, fd, 18436ff6d951SJohn Birrell drti) + 1; 18446ff6d951SJohn Birrell 18456ff6d951SJohn Birrell cmd = alloca(len); 18466ff6d951SJohn Birrell 18476ff6d951SJohn Birrell (void) snprintf(cmd, len, fmt, dtp->dt_ld_path, file, fd, drti); 18482693feb4SJohn Birrell #else 18490f2bd1e8SRui Paulo const char *fmt = "%s -o %s -r %s"; 18502693feb4SJohn Birrell 18512693feb4SJohn Birrell #if defined(__amd64__) 18522693feb4SJohn Birrell /* 18532693feb4SJohn Birrell * Arches which default to 64-bit need to explicitly use 18542693feb4SJohn Birrell * the 32-bit library path. 18552693feb4SJohn Birrell */ 1856ee765c2dSMark Johnston int use_32 = (dtp->dt_oflags & DTRACE_O_ILP32); 18572693feb4SJohn Birrell #else 18582693feb4SJohn Birrell /* 18592693feb4SJohn Birrell * Arches which are 32-bit only just use the normal 18602693feb4SJohn Birrell * library path. 18612693feb4SJohn Birrell */ 18622693feb4SJohn Birrell int use_32 = 0; 18632693feb4SJohn Birrell #endif 18642693feb4SJohn Birrell 18652693feb4SJohn Birrell (void) snprintf(drti, sizeof (drti), "/usr/lib%s/dtrace/drti.o", 18661149b79dSJohn Baldwin use_32 ? "32":""); 18672693feb4SJohn Birrell 18682693feb4SJohn Birrell len = snprintf(&tmp, 1, fmt, dtp->dt_ld_path, file, tfile, 18692693feb4SJohn Birrell drti) + 1; 18702693feb4SJohn Birrell 18710f2bd1e8SRui Paulo len *= 2; 18722693feb4SJohn Birrell cmd = alloca(len); 18732693feb4SJohn Birrell 18740f2bd1e8SRui Paulo (void) snprintf(cmd, len, fmt, dtp->dt_ld_path, file, 18750f2bd1e8SRui Paulo drti); 18762693feb4SJohn Birrell #endif 18776ff6d951SJohn Birrell if ((status = system(cmd)) == -1) { 18786ff6d951SJohn Birrell ret = dt_link_error(dtp, NULL, -1, NULL, 18796ff6d951SJohn Birrell "failed to run %s: %s", dtp->dt_ld_path, 18806ff6d951SJohn Birrell strerror(errno)); 18816ff6d951SJohn Birrell goto done; 18826ff6d951SJohn Birrell } 18836ff6d951SJohn Birrell 18846ff6d951SJohn Birrell if (WIFSIGNALED(status)) { 18856ff6d951SJohn Birrell ret = dt_link_error(dtp, NULL, -1, NULL, 18866ff6d951SJohn Birrell "failed to link %s: %s failed due to signal %d", 18876ff6d951SJohn Birrell file, dtp->dt_ld_path, WTERMSIG(status)); 18886ff6d951SJohn Birrell goto done; 18896ff6d951SJohn Birrell } 18906ff6d951SJohn Birrell 18916ff6d951SJohn Birrell if (WEXITSTATUS(status) != 0) { 18926ff6d951SJohn Birrell ret = dt_link_error(dtp, NULL, -1, NULL, 18936ff6d951SJohn Birrell "failed to link %s: %s exited with status %d\n", 18946ff6d951SJohn Birrell file, dtp->dt_ld_path, WEXITSTATUS(status)); 18956ff6d951SJohn Birrell goto done; 18966ff6d951SJohn Birrell } 18970f2bd1e8SRui Paulo #if !defined(sun) 18980f2bd1e8SRui Paulo #define BROKEN_LIBELF 18990f2bd1e8SRui Paulo /* 19000f2bd1e8SRui Paulo * FreeBSD's ld(1) is not instructed to interpret and add 19010f2bd1e8SRui Paulo * correctly the SUNW_dof section present in tfile. 19020f2bd1e8SRui Paulo * We use libelf to add this section manually and hope the next 19030f2bd1e8SRui Paulo * ld invocation won't remove it. 19040f2bd1e8SRui Paulo */ 19050f2bd1e8SRui Paulo elf_version(EV_CURRENT); 19060f2bd1e8SRui Paulo if ((efd = open(file, O_RDWR, 0)) < 0) { 19070f2bd1e8SRui Paulo ret = dt_link_error(dtp, NULL, -1, NULL, 19080f2bd1e8SRui Paulo "failed to open file %s: %s", 19090f2bd1e8SRui Paulo file, strerror(errno)); 19100f2bd1e8SRui Paulo goto done; 19110f2bd1e8SRui Paulo } 19120f2bd1e8SRui Paulo if ((e = elf_begin(efd, ELF_C_RDWR, NULL)) == NULL) { 19130f2bd1e8SRui Paulo close(efd); 19140f2bd1e8SRui Paulo ret = dt_link_error(dtp, NULL, -1, NULL, 19150f2bd1e8SRui Paulo "failed to open elf file: %s", 19160f2bd1e8SRui Paulo elf_errmsg(elf_errno())); 19170f2bd1e8SRui Paulo goto done; 19180f2bd1e8SRui Paulo } 19190f2bd1e8SRui Paulo /* 19200f2bd1e8SRui Paulo * Add the string '.SUWN_dof' to the shstrtab section. 19210f2bd1e8SRui Paulo */ 19220f2bd1e8SRui Paulo #ifdef BROKEN_LIBELF 19230f2bd1e8SRui Paulo elf_flagelf(e, ELF_C_SET, ELF_F_LAYOUT); 19240f2bd1e8SRui Paulo #endif 19250f2bd1e8SRui Paulo elf_getshdrstrndx(e, &stridx); 19260f2bd1e8SRui Paulo scn = elf_getscn(e, stridx); 19270f2bd1e8SRui Paulo gelf_getshdr(scn, &shdr); 19280f2bd1e8SRui Paulo data = elf_newdata(scn); 19290f2bd1e8SRui Paulo data->d_off = shdr.sh_size; 19300f2bd1e8SRui Paulo data->d_buf = ".SUNW_dof"; 19310f2bd1e8SRui Paulo data->d_size = 10; 19320f2bd1e8SRui Paulo data->d_type = ELF_T_BYTE; 19330f2bd1e8SRui Paulo loc = shdr.sh_size; 19340f2bd1e8SRui Paulo shdr.sh_size += data->d_size; 19350f2bd1e8SRui Paulo gelf_update_shdr(scn, &shdr); 19360f2bd1e8SRui Paulo #ifdef BROKEN_LIBELF 19370f2bd1e8SRui Paulo off = shdr.sh_offset; 19380f2bd1e8SRui Paulo rc = shdr.sh_offset + shdr.sh_size; 19390f2bd1e8SRui Paulo gelf_getehdr(e, &ehdr); 19400f2bd1e8SRui Paulo if (ehdr.e_shoff > off) { 19410f2bd1e8SRui Paulo off = ehdr.e_shoff + ehdr.e_shnum * ehdr.e_shentsize; 19420f2bd1e8SRui Paulo rc = roundup(rc, 8); 19430f2bd1e8SRui Paulo ehdr.e_shoff = rc; 19440f2bd1e8SRui Paulo gelf_update_ehdr(e, &ehdr); 19450f2bd1e8SRui Paulo rc += ehdr.e_shnum * ehdr.e_shentsize; 19460f2bd1e8SRui Paulo } 19470f2bd1e8SRui Paulo for (;;) { 19480f2bd1e8SRui Paulo scn0 = NULL; 19490f2bd1e8SRui Paulo scn = NULL; 19500f2bd1e8SRui Paulo while ((scn = elf_nextscn(e, scn)) != NULL) { 19510f2bd1e8SRui Paulo gelf_getshdr(scn, &shdr); 19520f2bd1e8SRui Paulo if (shdr.sh_type == SHT_NOBITS || 19530f2bd1e8SRui Paulo shdr.sh_offset < off) 19540f2bd1e8SRui Paulo continue; 19550f2bd1e8SRui Paulo /* Find the immediately adjcent section. */ 19560f2bd1e8SRui Paulo if (scn0 == NULL || 19570f2bd1e8SRui Paulo shdr.sh_offset < shdr0.sh_offset) { 19580f2bd1e8SRui Paulo scn0 = scn; 19590f2bd1e8SRui Paulo gelf_getshdr(scn0, &shdr0); 19600f2bd1e8SRui Paulo } 19610f2bd1e8SRui Paulo } 19620f2bd1e8SRui Paulo if (scn0 == NULL) 19630f2bd1e8SRui Paulo break; 19640f2bd1e8SRui Paulo /* Load section data to work around another bug */ 19650f2bd1e8SRui Paulo elf_getdata(scn0, NULL); 19660f2bd1e8SRui Paulo /* Update section header, assure section alignment */ 19670f2bd1e8SRui Paulo off = shdr0.sh_offset + shdr0.sh_size; 19680f2bd1e8SRui Paulo rc = roundup(rc, shdr0.sh_addralign); 19690f2bd1e8SRui Paulo shdr0.sh_offset = rc; 19700f2bd1e8SRui Paulo gelf_update_shdr(scn0, &shdr0); 19710f2bd1e8SRui Paulo rc += shdr0.sh_size; 19720f2bd1e8SRui Paulo } 19730f2bd1e8SRui Paulo if (elf_update(e, ELF_C_WRITE) < 0) { 19740f2bd1e8SRui Paulo ret = dt_link_error(dtp, NULL, -1, NULL, 19750f2bd1e8SRui Paulo "failed to add append the shstrtab section: %s", 19760f2bd1e8SRui Paulo elf_errmsg(elf_errno())); 19770f2bd1e8SRui Paulo elf_end(e); 19780f2bd1e8SRui Paulo close(efd); 19790f2bd1e8SRui Paulo goto done; 19800f2bd1e8SRui Paulo } 19810f2bd1e8SRui Paulo elf_end(e); 19820f2bd1e8SRui Paulo e = elf_begin(efd, ELF_C_RDWR, NULL); 19830f2bd1e8SRui Paulo #endif 19840f2bd1e8SRui Paulo /* 19850f2bd1e8SRui Paulo * Construct the .SUNW_dof section. 19860f2bd1e8SRui Paulo */ 19870f2bd1e8SRui Paulo scn = elf_newscn(e); 19880f2bd1e8SRui Paulo data = elf_newdata(scn); 19890f2bd1e8SRui Paulo buf = mmap(NULL, dof->dofh_filesz, PROT_READ, MAP_SHARED, 19900f2bd1e8SRui Paulo fd, 0); 19910f2bd1e8SRui Paulo if (buf == MAP_FAILED) { 19920f2bd1e8SRui Paulo ret = dt_link_error(dtp, NULL, -1, NULL, 19930f2bd1e8SRui Paulo "failed to mmap buffer %s", strerror(errno)); 19940f2bd1e8SRui Paulo elf_end(e); 19950f2bd1e8SRui Paulo close(efd); 19960f2bd1e8SRui Paulo goto done; 19970f2bd1e8SRui Paulo } 19980f2bd1e8SRui Paulo data->d_buf = buf; 19990f2bd1e8SRui Paulo data->d_align = 4; 20000f2bd1e8SRui Paulo data->d_size = dof->dofh_filesz; 20010f2bd1e8SRui Paulo data->d_version = EV_CURRENT; 20020f2bd1e8SRui Paulo gelf_getshdr(scn, &shdr); 20030f2bd1e8SRui Paulo shdr.sh_name = loc; 20040f2bd1e8SRui Paulo shdr.sh_flags = SHF_ALLOC; 20050f2bd1e8SRui Paulo /* 20060f2bd1e8SRui Paulo * Actually this should be SHT_SUNW_dof, but FreeBSD's ld(1) 20070f2bd1e8SRui Paulo * will remove this 'unknown' section when we try to create an 20080f2bd1e8SRui Paulo * executable using the object we are modifying, so we stop 20090f2bd1e8SRui Paulo * playing by the rules and use SHT_PROGBITS. 20100f2bd1e8SRui Paulo * Also, note that our drti has modifications to handle this. 20110f2bd1e8SRui Paulo */ 20120f2bd1e8SRui Paulo shdr.sh_type = SHT_PROGBITS; 20130f2bd1e8SRui Paulo shdr.sh_addralign = 4; 20140f2bd1e8SRui Paulo gelf_update_shdr(scn, &shdr); 20150f2bd1e8SRui Paulo if (elf_update(e, ELF_C_WRITE) < 0) { 20160f2bd1e8SRui Paulo ret = dt_link_error(dtp, NULL, -1, NULL, 20170f2bd1e8SRui Paulo "failed to add the SUNW_dof section: %s", 20180f2bd1e8SRui Paulo elf_errmsg(elf_errno())); 20190f2bd1e8SRui Paulo munmap(buf, dof->dofh_filesz); 20200f2bd1e8SRui Paulo elf_end(e); 20210f2bd1e8SRui Paulo close(efd); 20220f2bd1e8SRui Paulo goto done; 20230f2bd1e8SRui Paulo } 20240f2bd1e8SRui Paulo munmap(buf, dof->dofh_filesz); 20250f2bd1e8SRui Paulo elf_end(e); 20260f2bd1e8SRui Paulo close(efd); 20270f2bd1e8SRui Paulo #endif 20280f2bd1e8SRui Paulo (void) close(fd); /* release temporary file */ 20296ff6d951SJohn Birrell } else { 20306ff6d951SJohn Birrell (void) close(fd); 20316ff6d951SJohn Birrell } 20326ff6d951SJohn Birrell 20336ff6d951SJohn Birrell done: 20346ff6d951SJohn Birrell dtrace_dof_destroy(dtp, dof); 20352693feb4SJohn Birrell 20362693feb4SJohn Birrell #if !defined(sun) 20372693feb4SJohn Birrell unlink(tfile); 20382693feb4SJohn Birrell #endif 20396ff6d951SJohn Birrell return (ret); 20406ff6d951SJohn Birrell } 2041