Path: blob/main/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c
39562 views
/*1* CDDL HEADER START2*3* The contents of this file are subject to the terms of the4* Common Development and Distribution License (the "License").5* You may not use this file except in compliance with the License.6*7* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE8* or http://www.opensolaris.org/os/licensing.9* See the License for the specific language governing permissions10* and limitations under the License.11*12* When distributing Covered Code, include this CDDL HEADER in each13* file and include the License file at usr/src/OPENSOLARIS.LICENSE.14* If applicable, add the following below this CDDL HEADER, with the15* fields enclosed by brackets "[]" replaced with your own identifying16* information: Portions Copyright [yyyy] [name of copyright owner]17*18* CDDL HEADER END19*/2021/*22* Copyright 2008 Sun Microsystems, Inc. All rights reserved.23* Use is subject to license terms.24* Copyright 2017-2018 Mark Johnston <[email protected]>25*/2627#include <sys/param.h>28#include <sys/mman.h>29#include <sys/wait.h>3031#include <assert.h>32#include <elf.h>33#include <sys/types.h>34#include <fcntl.h>35#include <gelf.h>36#include <limits.h>37#include <stddef.h>38#include <stdio.h>39#include <stdlib.h>40#include <strings.h>41#include <errno.h>42#include <unistd.h>4344#include <libelf.h>4546#include <dt_impl.h>47#include <dt_provider.h>48#include <dt_program.h>49#include <dt_string.h>5051#define ESHDR_NULL 052#define ESHDR_SHSTRTAB 153#define ESHDR_DOF 254#define ESHDR_STRTAB 355#define ESHDR_SYMTAB 456#define ESHDR_REL 557#define ESHDR_NUM 65859#define PWRITE_SCN(index, data) \60(lseek64(fd, (off64_t)elf_file.shdr[(index)].sh_offset, SEEK_SET) != \61(off64_t)elf_file.shdr[(index)].sh_offset || \62dt_write(dtp, fd, (data), elf_file.shdr[(index)].sh_size) != \63elf_file.shdr[(index)].sh_size)6465static const char DTRACE_SHSTRTAB32[] = "\0"66".shstrtab\0" /* 1 */67".SUNW_dof\0" /* 11 */68".strtab\0" /* 21 */69".symtab\0" /* 29 */70".rel.SUNW_dof"; /* 37 */7172static const char DTRACE_SHSTRTAB64[] = "\0"73".shstrtab\0" /* 1 */74".SUNW_dof\0" /* 11 */75".strtab\0" /* 21 */76".symtab\0" /* 29 */77".rela.SUNW_dof"; /* 37 */7879static const char DOFSTR[] = "__SUNW_dof";80static const char DOFLAZYSTR[] = "___SUNW_dof";8182typedef struct dt_link_pair {83struct dt_link_pair *dlp_next; /* next pair in linked list */84void *dlp_str; /* buffer for string table */85void *dlp_sym; /* buffer for symbol table */86} dt_link_pair_t;8788typedef struct dof_elf32 {89uint32_t de_nrel; /* relocation count */90Elf32_Rel *de_rel; /* array of relocations for x86 */91uint32_t de_nsym; /* symbol count */92Elf32_Sym *de_sym; /* array of symbols */93uint32_t de_strlen; /* size of of string table */94char *de_strtab; /* string table */95uint32_t de_global; /* index of the first global symbol */96} dof_elf32_t;9798static int99prepare_elf32(dtrace_hdl_t *dtp, const dof_hdr_t *dof, dof_elf32_t *dep)100{101dof_sec_t *dofs, *s;102dof_relohdr_t *dofrh;103dof_relodesc_t *dofr;104char *strtab;105int i, j, nrel;106size_t strtabsz = 1;107uint32_t count = 0;108size_t base;109Elf32_Sym *sym;110Elf32_Rel *rel;111112/*LINTED*/113dofs = (dof_sec_t *)((char *)dof + dof->dofh_secoff);114115/*116* First compute the size of the string table and the number of117* relocations present in the DOF.118*/119for (i = 0; i < dof->dofh_secnum; i++) {120if (dofs[i].dofs_type != DOF_SECT_URELHDR)121continue;122123/*LINTED*/124dofrh = (dof_relohdr_t *)((char *)dof + dofs[i].dofs_offset);125126s = &dofs[dofrh->dofr_strtab];127strtab = (char *)dof + s->dofs_offset;128assert(strtab[0] == '\0');129strtabsz += s->dofs_size - 1;130131s = &dofs[dofrh->dofr_relsec];132/*LINTED*/133dofr = (dof_relodesc_t *)((char *)dof + s->dofs_offset);134count += s->dofs_size / s->dofs_entsize;135}136137dep->de_strlen = strtabsz;138dep->de_nrel = count;139dep->de_nsym = count + 1; /* the first symbol is always null */140141if (dtp->dt_lazyload) {142dep->de_strlen += sizeof (DOFLAZYSTR);143dep->de_nsym++;144} else {145dep->de_strlen += sizeof (DOFSTR);146dep->de_nsym++;147}148149if ((dep->de_rel = calloc(dep->de_nrel,150sizeof (dep->de_rel[0]))) == NULL) {151return (dt_set_errno(dtp, EDT_NOMEM));152}153154if ((dep->de_sym = calloc(dep->de_nsym, sizeof (Elf32_Sym))) == NULL) {155free(dep->de_rel);156return (dt_set_errno(dtp, EDT_NOMEM));157}158159if ((dep->de_strtab = calloc(dep->de_strlen, 1)) == NULL) {160free(dep->de_rel);161free(dep->de_sym);162return (dt_set_errno(dtp, EDT_NOMEM));163}164165count = 0;166strtabsz = 1;167dep->de_strtab[0] = '\0';168rel = dep->de_rel;169sym = dep->de_sym;170dep->de_global = 1;171172/*173* The first symbol table entry must be zeroed and is always ignored.174*/175bzero(sym, sizeof (Elf32_Sym));176sym++;177178/*179* Take a second pass through the DOF sections filling in the180* memory we allocated.181*/182for (i = 0; i < dof->dofh_secnum; i++) {183if (dofs[i].dofs_type != DOF_SECT_URELHDR)184continue;185186/*LINTED*/187dofrh = (dof_relohdr_t *)((char *)dof + dofs[i].dofs_offset);188189s = &dofs[dofrh->dofr_strtab];190strtab = (char *)dof + s->dofs_offset;191bcopy(strtab + 1, dep->de_strtab + strtabsz, s->dofs_size);192base = strtabsz;193strtabsz += s->dofs_size - 1;194195s = &dofs[dofrh->dofr_relsec];196/*LINTED*/197dofr = (dof_relodesc_t *)((char *)dof + s->dofs_offset);198nrel = s->dofs_size / s->dofs_entsize;199200s = &dofs[dofrh->dofr_tgtsec];201202for (j = 0; j < nrel; j++) {203#if defined(__aarch64__)204rel->r_offset = s->dofs_offset +205dofr[j].dofr_offset;206rel->r_info = ELF32_R_INFO(count + dep->de_global,207R_ARM_REL32);208#elif defined(__arm__)209/* XXX */210printf("%s:%s(%d): arm not implemented\n",211__FUNCTION__, __FILE__, __LINE__);212#elif defined(__i386) || defined(__amd64)213rel->r_offset = s->dofs_offset +214dofr[j].dofr_offset;215rel->r_info = ELF32_R_INFO(count + dep->de_global,216R_386_PC32);217#elif defined(__powerpc__)218/*219* Add 4 bytes to hit the low half of this 64-bit220* big-endian address.221*/222rel->r_offset = s->dofs_offset +223dofr[j].dofr_offset + 4;224rel->r_info = ELF32_R_INFO(count + dep->de_global,225R_PPC_REL32);226#elif defined(__riscv)227rel->r_offset = s->dofs_offset + dofr[j].dofr_offset;228rel->r_info = ELF32_R_INFO(count + dep->de_global,229R_RISCV_32_PCREL);230#else231#error unknown ISA232#endif233234sym->st_name = base + dofr[j].dofr_name - 1;235sym->st_value = 0;236sym->st_size = 0;237sym->st_info = ELF32_ST_INFO(STB_GLOBAL, STT_FUNC);238sym->st_other = ELF32_ST_VISIBILITY(STV_HIDDEN);239sym->st_shndx = SHN_UNDEF;240241rel++;242sym++;243count++;244}245}246247/*248* Add a symbol for the DOF itself. We use a different symbol for249* lazily and actively loaded DOF to make them easy to distinguish.250*/251sym->st_name = strtabsz;252sym->st_value = 0;253sym->st_size = dof->dofh_filesz;254sym->st_info = ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT);255sym->st_other = ELF32_ST_VISIBILITY(STV_HIDDEN);256sym->st_shndx = ESHDR_DOF;257sym++;258259if (dtp->dt_lazyload) {260bcopy(DOFLAZYSTR, dep->de_strtab + strtabsz,261sizeof (DOFLAZYSTR));262strtabsz += sizeof (DOFLAZYSTR);263} else {264bcopy(DOFSTR, dep->de_strtab + strtabsz, sizeof (DOFSTR));265strtabsz += sizeof (DOFSTR);266}267268assert(count == dep->de_nrel);269assert(strtabsz == dep->de_strlen);270271return (0);272}273274275typedef struct dof_elf64 {276uint32_t de_nrel;277Elf64_Rela *de_rel;278uint32_t de_nsym;279Elf64_Sym *de_sym;280281uint32_t de_strlen;282char *de_strtab;283284uint32_t de_global;285} dof_elf64_t;286287static int288prepare_elf64(dtrace_hdl_t *dtp, const dof_hdr_t *dof, dof_elf64_t *dep)289{290dof_sec_t *dofs, *s;291dof_relohdr_t *dofrh;292dof_relodesc_t *dofr;293char *strtab;294int i, j, nrel;295size_t strtabsz = 1;296uint64_t count = 0;297size_t base;298Elf64_Sym *sym;299Elf64_Rela *rel;300301/*LINTED*/302dofs = (dof_sec_t *)((char *)dof + dof->dofh_secoff);303304/*305* First compute the size of the string table and the number of306* relocations present in the DOF.307*/308for (i = 0; i < dof->dofh_secnum; i++) {309if (dofs[i].dofs_type != DOF_SECT_URELHDR)310continue;311312/*LINTED*/313dofrh = (dof_relohdr_t *)((char *)dof + dofs[i].dofs_offset);314315s = &dofs[dofrh->dofr_strtab];316strtab = (char *)dof + s->dofs_offset;317assert(strtab[0] == '\0');318strtabsz += s->dofs_size - 1;319320s = &dofs[dofrh->dofr_relsec];321/*LINTED*/322dofr = (dof_relodesc_t *)((char *)dof + s->dofs_offset);323count += s->dofs_size / s->dofs_entsize;324}325326dep->de_strlen = strtabsz;327dep->de_nrel = count;328dep->de_nsym = count + 1; /* the first symbol is always null */329330if (dtp->dt_lazyload) {331dep->de_strlen += sizeof (DOFLAZYSTR);332dep->de_nsym++;333} else {334dep->de_strlen += sizeof (DOFSTR);335dep->de_nsym++;336}337338if ((dep->de_rel = calloc(dep->de_nrel,339sizeof (dep->de_rel[0]))) == NULL) {340return (dt_set_errno(dtp, EDT_NOMEM));341}342343if ((dep->de_sym = calloc(dep->de_nsym, sizeof (Elf64_Sym))) == NULL) {344free(dep->de_rel);345return (dt_set_errno(dtp, EDT_NOMEM));346}347348if ((dep->de_strtab = calloc(dep->de_strlen, 1)) == NULL) {349free(dep->de_rel);350free(dep->de_sym);351return (dt_set_errno(dtp, EDT_NOMEM));352}353354count = 0;355strtabsz = 1;356dep->de_strtab[0] = '\0';357rel = dep->de_rel;358sym = dep->de_sym;359dep->de_global = 1;360361/*362* The first symbol table entry must be zeroed and is always ignored.363*/364bzero(sym, sizeof (Elf64_Sym));365sym++;366367/*368* Take a second pass through the DOF sections filling in the369* memory we allocated.370*/371for (i = 0; i < dof->dofh_secnum; i++) {372if (dofs[i].dofs_type != DOF_SECT_URELHDR)373continue;374375/*LINTED*/376dofrh = (dof_relohdr_t *)((char *)dof + dofs[i].dofs_offset);377378s = &dofs[dofrh->dofr_strtab];379strtab = (char *)dof + s->dofs_offset;380bcopy(strtab + 1, dep->de_strtab + strtabsz, s->dofs_size);381base = strtabsz;382strtabsz += s->dofs_size - 1;383384s = &dofs[dofrh->dofr_relsec];385/*LINTED*/386dofr = (dof_relodesc_t *)((char *)dof + s->dofs_offset);387nrel = s->dofs_size / s->dofs_entsize;388389s = &dofs[dofrh->dofr_tgtsec];390391for (j = 0; j < nrel; j++) {392#if defined(__aarch64__)393rel->r_offset = s->dofs_offset +394dofr[j].dofr_offset;395rel->r_info = ELF64_R_INFO(count + dep->de_global,396R_AARCH64_PREL64);397#elif defined(__arm__)398/* XXX */399#elif defined(__powerpc__)400rel->r_offset = s->dofs_offset +401dofr[j].dofr_offset;402rel->r_info = ELF64_R_INFO(count + dep->de_global,403R_PPC64_REL64);404#elif defined(__riscv)405rel->r_offset = s->dofs_offset + dofr[j].dofr_offset;406rel->r_info = ELF64_R_INFO(count + dep->de_global,407R_RISCV_32_PCREL);408#elif defined(__i386) || defined(__amd64)409rel->r_offset = s->dofs_offset +410dofr[j].dofr_offset;411rel->r_info = ELF64_R_INFO(count + dep->de_global,412R_X86_64_PC64);413#else414#error unknown ISA415#endif416417sym->st_name = base + dofr[j].dofr_name - 1;418sym->st_value = 0;419sym->st_size = 0;420sym->st_info = GELF_ST_INFO(STB_GLOBAL, STT_FUNC);421sym->st_other = ELF64_ST_VISIBILITY(STV_HIDDEN);422sym->st_shndx = SHN_UNDEF;423424rel++;425sym++;426count++;427}428}429430/*431* Add a symbol for the DOF itself. We use a different symbol for432* lazily and actively loaded DOF to make them easy to distinguish.433*/434sym->st_name = strtabsz;435sym->st_value = 0;436sym->st_size = dof->dofh_filesz;437sym->st_info = GELF_ST_INFO(STB_GLOBAL, STT_OBJECT);438sym->st_other = ELF64_ST_VISIBILITY(STV_HIDDEN);439sym->st_shndx = ESHDR_DOF;440sym++;441442if (dtp->dt_lazyload) {443bcopy(DOFLAZYSTR, dep->de_strtab + strtabsz,444sizeof (DOFLAZYSTR));445strtabsz += sizeof (DOFLAZYSTR);446} else {447bcopy(DOFSTR, dep->de_strtab + strtabsz, sizeof (DOFSTR));448strtabsz += sizeof (DOFSTR);449}450451assert(count == dep->de_nrel);452assert(strtabsz == dep->de_strlen);453454return (0);455}456457/*458* Write out an ELF32 file prologue consisting of a header, section headers,459* and a section header string table. The DOF data will follow this prologue460* and complete the contents of the given ELF file.461*/462static int463dump_elf32(dtrace_hdl_t *dtp, const dof_hdr_t *dof, int fd)464{465struct {466Elf32_Ehdr ehdr;467Elf32_Shdr shdr[ESHDR_NUM];468} elf_file;469470Elf32_Shdr *shp;471Elf32_Off off;472dof_elf32_t de;473int ret = 0;474uint_t nshdr;475476if (prepare_elf32(dtp, dof, &de) != 0)477return (-1); /* errno is set for us */478479/*480* If there are no relocations, we only need enough sections for481* the shstrtab and the DOF.482*/483nshdr = de.de_nrel == 0 ? ESHDR_SYMTAB + 1 : ESHDR_NUM;484485bzero(&elf_file, sizeof (elf_file));486487elf_file.ehdr.e_ident[EI_MAG0] = ELFMAG0;488elf_file.ehdr.e_ident[EI_MAG1] = ELFMAG1;489elf_file.ehdr.e_ident[EI_MAG2] = ELFMAG2;490elf_file.ehdr.e_ident[EI_MAG3] = ELFMAG3;491elf_file.ehdr.e_ident[EI_VERSION] = EV_CURRENT;492elf_file.ehdr.e_ident[EI_CLASS] = ELFCLASS32;493#if BYTE_ORDER == _BIG_ENDIAN494elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2MSB;495#else496elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2LSB;497#endif498elf_file.ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD;499elf_file.ehdr.e_type = ET_REL;500#if defined(__arm__)501elf_file.ehdr.e_machine = EM_ARM;502#elif defined(__powerpc__)503elf_file.ehdr.e_machine = EM_PPC;504#elif defined(__i386) || defined(__amd64)505elf_file.ehdr.e_machine = EM_386;506#elif defined(__aarch64__)507elf_file.ehdr.e_machine = EM_AARCH64;508#elif defined(__riscv)509elf_file.ehdr.e_machine = EM_RISCV;510511/* Set the ELF flags according to our current ABI */512#if defined(__riscv_compressed)513elf_file.ehdr.e_flags |= EF_RISCV_RVC;514#endif515#if defined(__riscv_float_abi_soft)516elf_file.ehdr.e_flags |= EF_RISCV_FLOAT_ABI_SOFT;517#endif518#if defined(__riscv_float_abi_single)519elf_file.ehdr.e_flags |= EF_RISCV_FLOAT_ABI_SINGLE;520#endif521#if defined(__riscv_float_abi_double)522elf_file.ehdr.e_flags |= EF_RISCV_FLOAT_ABI_DOUBLE;523#endif524#endif525elf_file.ehdr.e_version = EV_CURRENT;526elf_file.ehdr.e_shoff = sizeof (Elf32_Ehdr);527elf_file.ehdr.e_ehsize = sizeof (Elf32_Ehdr);528elf_file.ehdr.e_phentsize = sizeof (Elf32_Phdr);529elf_file.ehdr.e_shentsize = sizeof (Elf32_Shdr);530elf_file.ehdr.e_shnum = nshdr;531elf_file.ehdr.e_shstrndx = ESHDR_SHSTRTAB;532off = sizeof (elf_file) + nshdr * sizeof (Elf32_Shdr);533534shp = &elf_file.shdr[ESHDR_SHSTRTAB];535shp->sh_name = 1; /* DTRACE_SHSTRTAB32[1] = ".shstrtab" */536shp->sh_type = SHT_STRTAB;537shp->sh_offset = off;538shp->sh_size = sizeof (DTRACE_SHSTRTAB32);539shp->sh_addralign = sizeof (char);540off = roundup2(shp->sh_offset + shp->sh_size, 8);541542shp = &elf_file.shdr[ESHDR_DOF];543shp->sh_name = 11; /* DTRACE_SHSTRTAB32[11] = ".SUNW_dof" */544shp->sh_flags = SHF_ALLOC;545shp->sh_type = SHT_SUNW_dof;546shp->sh_offset = off;547shp->sh_size = dof->dofh_filesz;548shp->sh_addralign = 8;549off = shp->sh_offset + shp->sh_size;550551shp = &elf_file.shdr[ESHDR_STRTAB];552shp->sh_name = 21; /* DTRACE_SHSTRTAB32[21] = ".strtab" */553shp->sh_flags = SHF_ALLOC;554shp->sh_type = SHT_STRTAB;555shp->sh_offset = off;556shp->sh_size = de.de_strlen;557shp->sh_addralign = sizeof (char);558off = roundup2(shp->sh_offset + shp->sh_size, 4);559560shp = &elf_file.shdr[ESHDR_SYMTAB];561shp->sh_name = 29; /* DTRACE_SHSTRTAB32[29] = ".symtab" */562shp->sh_flags = SHF_ALLOC;563shp->sh_type = SHT_SYMTAB;564shp->sh_entsize = sizeof (Elf32_Sym);565shp->sh_link = ESHDR_STRTAB;566shp->sh_offset = off;567shp->sh_info = de.de_global;568shp->sh_size = de.de_nsym * sizeof (Elf32_Sym);569shp->sh_addralign = 4;570off = roundup2(shp->sh_offset + shp->sh_size, 4);571572if (de.de_nrel == 0) {573if (dt_write(dtp, fd, &elf_file,574sizeof (elf_file)) != sizeof (elf_file) ||575PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB32) ||576PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) ||577PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) ||578PWRITE_SCN(ESHDR_DOF, dof)) {579ret = dt_set_errno(dtp, errno);580}581} else {582shp = &elf_file.shdr[ESHDR_REL];583shp->sh_name = 37; /* DTRACE_SHSTRTAB32[37] = ".rel.SUNW_dof" */584shp->sh_flags = 0;585shp->sh_type = SHT_REL;586shp->sh_entsize = sizeof (de.de_rel[0]);587shp->sh_link = ESHDR_SYMTAB;588shp->sh_info = ESHDR_DOF;589shp->sh_offset = off;590shp->sh_size = de.de_nrel * sizeof (de.de_rel[0]);591shp->sh_addralign = 4;592593if (dt_write(dtp, fd, &elf_file,594sizeof (elf_file)) != sizeof (elf_file) ||595PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB32) ||596PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) ||597PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) ||598PWRITE_SCN(ESHDR_REL, de.de_rel) ||599PWRITE_SCN(ESHDR_DOF, dof)) {600ret = dt_set_errno(dtp, errno);601}602}603604free(de.de_strtab);605free(de.de_sym);606free(de.de_rel);607608return (ret);609}610611/*612* Write out an ELF64 file prologue consisting of a header, section headers,613* and a section header string table. The DOF data will follow this prologue614* and complete the contents of the given ELF file.615*/616static int617dump_elf64(dtrace_hdl_t *dtp, const dof_hdr_t *dof, int fd)618{619struct {620Elf64_Ehdr ehdr;621Elf64_Shdr shdr[ESHDR_NUM];622} elf_file;623624Elf64_Shdr *shp;625Elf64_Off off;626dof_elf64_t de;627int ret = 0;628uint_t nshdr;629630if (prepare_elf64(dtp, dof, &de) != 0)631return (-1); /* errno is set for us */632633/*634* If there are no relocations, we only need enough sections for635* the shstrtab and the DOF.636*/637nshdr = de.de_nrel == 0 ? ESHDR_SYMTAB + 1 : ESHDR_NUM;638639bzero(&elf_file, sizeof (elf_file));640641elf_file.ehdr.e_ident[EI_MAG0] = ELFMAG0;642elf_file.ehdr.e_ident[EI_MAG1] = ELFMAG1;643elf_file.ehdr.e_ident[EI_MAG2] = ELFMAG2;644elf_file.ehdr.e_ident[EI_MAG3] = ELFMAG3;645elf_file.ehdr.e_ident[EI_VERSION] = EV_CURRENT;646elf_file.ehdr.e_ident[EI_CLASS] = ELFCLASS64;647#if BYTE_ORDER == _BIG_ENDIAN648elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2MSB;649#else650elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2LSB;651#endif652elf_file.ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD;653elf_file.ehdr.e_type = ET_REL;654#if defined(__arm__)655elf_file.ehdr.e_machine = EM_ARM;656#elif defined(__powerpc64__)657#if defined(_CALL_ELF) && _CALL_ELF == 2658elf_file.ehdr.e_flags = 2;659#endif660elf_file.ehdr.e_machine = EM_PPC64;661#elif defined(__i386) || defined(__amd64)662elf_file.ehdr.e_machine = EM_AMD64;663#elif defined(__aarch64__)664elf_file.ehdr.e_machine = EM_AARCH64;665#elif defined(__riscv)666elf_file.ehdr.e_machine = EM_RISCV;667668/* Set the ELF flags according to our current ABI */669#if defined(__riscv_compressed)670elf_file.ehdr.e_flags |= EF_RISCV_RVC;671#endif672#if defined(__riscv_float_abi_soft)673elf_file.ehdr.e_flags |= EF_RISCV_FLOAT_ABI_SOFT;674#endif675#if defined(__riscv_float_abi_single)676elf_file.ehdr.e_flags |= EF_RISCV_FLOAT_ABI_SINGLE;677#endif678#if defined(__riscv_float_abi_double)679elf_file.ehdr.e_flags |= EF_RISCV_FLOAT_ABI_DOUBLE;680#endif681#endif682elf_file.ehdr.e_version = EV_CURRENT;683elf_file.ehdr.e_shoff = sizeof (Elf64_Ehdr);684elf_file.ehdr.e_ehsize = sizeof (Elf64_Ehdr);685elf_file.ehdr.e_phentsize = sizeof (Elf64_Phdr);686elf_file.ehdr.e_shentsize = sizeof (Elf64_Shdr);687elf_file.ehdr.e_shnum = nshdr;688elf_file.ehdr.e_shstrndx = ESHDR_SHSTRTAB;689off = sizeof (elf_file) + nshdr * sizeof (Elf64_Shdr);690691shp = &elf_file.shdr[ESHDR_SHSTRTAB];692shp->sh_name = 1; /* DTRACE_SHSTRTAB64[1] = ".shstrtab" */693shp->sh_type = SHT_STRTAB;694shp->sh_offset = off;695shp->sh_size = sizeof (DTRACE_SHSTRTAB64);696shp->sh_addralign = sizeof (char);697off = roundup2(shp->sh_offset + shp->sh_size, 8);698699shp = &elf_file.shdr[ESHDR_DOF];700shp->sh_name = 11; /* DTRACE_SHSTRTAB64[11] = ".SUNW_dof" */701shp->sh_flags = SHF_ALLOC;702shp->sh_type = SHT_SUNW_dof;703shp->sh_offset = off;704shp->sh_size = dof->dofh_filesz;705shp->sh_addralign = 8;706off = shp->sh_offset + shp->sh_size;707708shp = &elf_file.shdr[ESHDR_STRTAB];709shp->sh_name = 21; /* DTRACE_SHSTRTAB64[21] = ".strtab" */710shp->sh_flags = SHF_ALLOC;711shp->sh_type = SHT_STRTAB;712shp->sh_offset = off;713shp->sh_size = de.de_strlen;714shp->sh_addralign = sizeof (char);715off = roundup2(shp->sh_offset + shp->sh_size, 8);716717shp = &elf_file.shdr[ESHDR_SYMTAB];718shp->sh_name = 29; /* DTRACE_SHSTRTAB64[29] = ".symtab" */719shp->sh_flags = SHF_ALLOC;720shp->sh_type = SHT_SYMTAB;721shp->sh_entsize = sizeof (Elf64_Sym);722shp->sh_link = ESHDR_STRTAB;723shp->sh_offset = off;724shp->sh_info = de.de_global;725shp->sh_size = de.de_nsym * sizeof (Elf64_Sym);726shp->sh_addralign = 8;727off = roundup2(shp->sh_offset + shp->sh_size, 8);728729if (de.de_nrel == 0) {730if (dt_write(dtp, fd, &elf_file,731sizeof (elf_file)) != sizeof (elf_file) ||732PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB64) ||733PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) ||734PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) ||735PWRITE_SCN(ESHDR_DOF, dof)) {736ret = dt_set_errno(dtp, errno);737}738} else {739shp = &elf_file.shdr[ESHDR_REL];740shp->sh_name = 37; /* DTRACE_SHSTRTAB64[37] = ".rel.SUNW_dof" */741shp->sh_flags = 0;742shp->sh_type = SHT_RELA;743shp->sh_entsize = sizeof (de.de_rel[0]);744shp->sh_link = ESHDR_SYMTAB;745shp->sh_info = ESHDR_DOF;746shp->sh_offset = off;747shp->sh_size = de.de_nrel * sizeof (de.de_rel[0]);748shp->sh_addralign = 8;749750if (dt_write(dtp, fd, &elf_file,751sizeof (elf_file)) != sizeof (elf_file) ||752PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB64) ||753PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) ||754PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) ||755PWRITE_SCN(ESHDR_REL, de.de_rel) ||756PWRITE_SCN(ESHDR_DOF, dof)) {757ret = dt_set_errno(dtp, errno);758}759}760761free(de.de_strtab);762free(de.de_sym);763free(de.de_rel);764765return (ret);766}767768static int769dt_symtab_lookup(Elf_Data *data_sym, int start, int end, uintptr_t addr,770uint_t shn, GElf_Sym *sym, int uses_funcdesc, Elf *elf)771{772Elf64_Addr symval;773Elf_Scn *opd_scn;774Elf_Data *opd_desc;775int i;776777for (i = start; i < end && gelf_getsym(data_sym, i, sym) != NULL; i++) {778if (GELF_ST_TYPE(sym->st_info) == STT_FUNC) {779symval = sym->st_value;780if (uses_funcdesc) {781opd_scn = elf_getscn(elf, sym->st_shndx);782opd_desc = elf_rawdata(opd_scn, NULL);783symval =784*(uint64_t*)((char *)opd_desc->d_buf + symval);785}786if ((uses_funcdesc || shn == sym->st_shndx) &&787symval <= addr && addr < symval + sym->st_size)788return (0);789}790}791792return (-1);793}794795#if defined(__aarch64__)796#define DT_OP_NOP 0xd503201f797#define DT_OP_RET 0xd65f03c0798#define DT_OP_CALL26 0x94000000799#define DT_OP_JUMP26 0x14000000800#define DT_REL_NONE R_AARCH64_NONE801802static int803dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela,804uint32_t *off)805{806uint32_t *ip;807808/*809* Ensure that the offset is aligned on an instruction boundary.810*/811if ((rela->r_offset & (sizeof (uint32_t) - 1)) != 0)812return (-1);813814/*815* We only know about some specific relocation types.816* We also recognize relocation type NONE, since that gets used for817* relocations of USDT probes, and we might be re-processing a file.818*/819if (GELF_R_TYPE(rela->r_info) != R_AARCH64_CALL26 &&820GELF_R_TYPE(rela->r_info) != R_AARCH64_JUMP26 &&821GELF_R_TYPE(rela->r_info) != R_AARCH64_NONE)822return (-1);823824ip = (uint32_t *)(p + rela->r_offset);825826/*827* We may have already processed this object file in an earlier linker828* invocation. Check to see if the present instruction sequence matches829* the one we would install below.830*/831if (ip[0] == DT_OP_NOP || ip[0] == DT_OP_RET)832return (0);833834/*835* We only expect call instructions with a displacement of 0, or a jump836* instruction acting as a tail call.837*/838if (ip[0] != DT_OP_CALL26 && ip[0] != DT_OP_JUMP26) {839dt_dprintf("found %x instead of a call or jmp instruction at "840"%llx\n", ip[0], (u_longlong_t)rela->r_offset);841return (-1);842}843844/*845* On arm64, we do not have to differentiate between regular probes and846* is-enabled probes. Both cases are encoded as a regular branch for847* non-tail call locations, and a jump for tail call locations. Calls848* are to be converted into a no-op whereas jumps should become a849* return.850*/851if (ip[0] == DT_OP_CALL26)852ip[0] = DT_OP_NOP;853else854ip[0] = DT_OP_RET;855856return (0);857}858#elif defined(__arm__)859#define DT_REL_NONE R_ARM_NONE860861static int862dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela,863uint32_t *off)864{865printf("%s:%s(%d): arm not implemented\n", __FUNCTION__, __FILE__,866__LINE__);867return (-1);868}869#elif defined(__powerpc__)870/* The sentinel is 'xor r3,r3,r3'. */871#define DT_OP_XOR_R3 0x7c631a78872873#define DT_OP_NOP 0x60000000874#define DT_OP_BLR 0x4e800020875876/* This captures all forms of branching to address. */877#define DT_IS_BRANCH(inst) ((inst & 0xfc000000) == 0x48000000)878#define DT_IS_BL(inst) (DT_IS_BRANCH(inst) && (inst & 0x01))879880#define DT_REL_NONE R_PPC_NONE881882static int883dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela,884uint32_t *off)885{886uint32_t *ip;887888if ((rela->r_offset & (sizeof (uint32_t) - 1)) != 0)889return (-1);890891/*LINTED*/892ip = (uint32_t *)(p + rela->r_offset);893894/*895* We only know about some specific relocation types.896*/897if (GELF_R_TYPE(rela->r_info) != R_PPC_REL24 &&898GELF_R_TYPE(rela->r_info) != R_PPC_PLTREL24 &&899GELF_R_TYPE(rela->r_info) != R_PPC_NONE)900return (-1);901902/*903* We may have already processed this object file in an earlier linker904* invocation. Check to see if the present instruction sequence matches905* the one we would install below.906*/907if (isenabled) {908if (ip[0] == DT_OP_XOR_R3) {909(*off) += sizeof (ip[0]);910return (0);911}912} else {913if (ip[0] == DT_OP_NOP) {914(*off) += sizeof (ip[0]);915return (0);916}917}918919/*920* We only expect branch to address instructions.921*/922if (!DT_IS_BRANCH(ip[0])) {923dt_dprintf("found %x instead of a branch instruction at %llx\n",924ip[0], (u_longlong_t)rela->r_offset);925return (-1);926}927928if (isenabled) {929/*930* It would necessarily indicate incorrect usage if an is-931* enabled probe were tail-called so flag that as an error.932* It's also potentially (very) tricky to handle gracefully,933* but could be done if this were a desired use scenario.934*/935if (!DT_IS_BL(ip[0])) {936dt_dprintf("tail call to is-enabled probe at %llx\n",937(u_longlong_t)rela->r_offset);938return (-1);939}940941ip[0] = DT_OP_XOR_R3;942(*off) += sizeof (ip[0]);943} else {944if (DT_IS_BL(ip[0]))945ip[0] = DT_OP_NOP;946else947ip[0] = DT_OP_BLR;948}949950return (0);951}952#elif defined(__riscv)953#define DT_OP_NOP 0x00000013 /* addi x0, x0, 0 */954#define DT_OP_RET 0x00008067 /* jalr x0, x1, 0 */955#define DT_OP_IS_AUIPC(op) (((op) & 0x7f) == 0x17)956#define DT_OP_IS_JALR(op) (((op) & 0x707f) == 0x67)957#define DT_OP_JALR_CALL 0x000080e7 /* jalr x1, x1, 0 */958#define DT_OP_JALR_TAIL 0x00030067 /* jalr x0, x6, 0 */959#define DT_REL_NONE R_RISCV_NONE960961static int962dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela,963uint32_t *off)964{965uint32_t *ip;966967/*968* XXX: this implementation is untested, but should serve as a decent969* starting point.970*/971972/*973* Ensure that the offset is aligned on a compressed-instruction974* boundary.975*/976if ((rela->r_offset & (sizeof (uint16_t) - 1)) != 0)977return (-1);978979/*980* We only know about some specific relocation types.981* We also recognize relocation type NONE, since that gets used for982* relocations of USDT probes, and we might be re-processing a file.983*/984if (GELF_R_TYPE(rela->r_info) != R_RISCV_CALL &&985GELF_R_TYPE(rela->r_info) != R_RISCV_CALL_PLT &&986GELF_R_TYPE(rela->r_info) != R_RISCV_NONE)987return (-1);988989ip = (uint32_t *)(p + rela->r_offset);990991/*992* We may have already processed this object file in an earlier linker993* invocation. Check to see if the present instruction sequence matches994* the one we would install below.995*/996if (ip[0] == DT_OP_NOP && (ip[1] == DT_OP_NOP || ip[1] == DT_OP_RET))997return (0);998999/*1000* We expect a auipc+jalr pair, either from a call or a tail.1001* - call: auipc x1 0; jalr x1, x1, 01002* - tail: auipc x6 0; jalr x0, x6, 01003*/1004if (!DT_OP_IS_AUIPC(ip[0]) || !DT_OP_IS_JALR(ip[1]))1005return (-1);10061007/*1008* On riscv, we do not have to differentiate between regular probes and1009* is-enabled probes. Calls are to be converted into a no-op whereas1010* tail calls should become a return.1011*/1012if (ip[1] == DT_OP_JALR_CALL) {1013ip[0] = DT_OP_NOP;1014ip[1] = DT_OP_NOP;1015} else {1016ip[0] = DT_OP_NOP;1017ip[1] = DT_OP_RET;1018}10191020return (0);1021}10221023#elif defined(__i386) || defined(__amd64)10241025#define DT_OP_NOP 0x901026#define DT_OP_RET 0xc31027#define DT_OP_CALL 0xe81028#define DT_OP_JMP32 0xe91029#define DT_OP_REX_RAX 0x481030#define DT_OP_XOR_EAX_0 0x331031#define DT_OP_XOR_EAX_1 0xc010321033#define DT_REL_NONE R_386_NONE10341035static int1036dt_modtext(dtrace_hdl_t *dtp, char *p, int isenabled, GElf_Rela *rela,1037uint32_t *off)1038{1039uint8_t *ip = (uint8_t *)(p + rela->r_offset - 1);1040uint8_t ret;10411042/*1043* On x86, the first byte of the instruction is the call opcode and1044* the next four bytes are the 32-bit address; the relocation is for1045* the address operand. We back up the offset to the first byte of1046* the instruction. For is-enabled probes, we later advance the offset1047* so that it hits the first nop in the instruction sequence.1048*/1049(*off) -= 1;10501051/*1052* We only know about some specific relocation types. Luckily1053* these types have the same values on both 32-bit and 64-bit1054* x86 architectures.1055*/1056if (GELF_R_TYPE(rela->r_info) != R_386_PC32 &&1057GELF_R_TYPE(rela->r_info) != R_386_PLT32 &&1058GELF_R_TYPE(rela->r_info) != R_386_NONE)1059return (-1);10601061/*1062* We may have already processed this object file in an earlier linker1063* invocation. Check to see if the present instruction sequence matches1064* the one we would install. For is-enabled probes, we advance the1065* offset to the first nop instruction in the sequence to match the1066* text modification code below.1067*/1068if (!isenabled) {1069if ((ip[0] == DT_OP_NOP || ip[0] == DT_OP_RET) &&1070ip[1] == DT_OP_NOP && ip[2] == DT_OP_NOP &&1071ip[3] == DT_OP_NOP && ip[4] == DT_OP_NOP)1072return (0);1073} else if (dtp->dt_oflags & DTRACE_O_LP64) {1074if (ip[0] == DT_OP_REX_RAX &&1075ip[1] == DT_OP_XOR_EAX_0 && ip[2] == DT_OP_XOR_EAX_1 &&1076(ip[3] == DT_OP_NOP || ip[3] == DT_OP_RET) &&1077ip[4] == DT_OP_NOP) {1078(*off) += 3;1079return (0);1080}1081} else {1082if (ip[0] == DT_OP_XOR_EAX_0 && ip[1] == DT_OP_XOR_EAX_1 &&1083(ip[2] == DT_OP_NOP || ip[2] == DT_OP_RET) &&1084ip[3] == DT_OP_NOP && ip[4] == DT_OP_NOP) {1085(*off) += 2;1086return (0);1087}1088}10891090/*1091* We expect either a call instrution with a 32-bit displacement or a1092* jmp instruction with a 32-bit displacement acting as a tail-call.1093*/1094if (ip[0] != DT_OP_CALL && ip[0] != DT_OP_JMP32) {1095dt_dprintf("found %x instead of a call or jmp instruction at "1096"%llx\n", ip[0], (u_longlong_t)rela->r_offset);1097return (-1);1098}10991100ret = (ip[0] == DT_OP_JMP32) ? DT_OP_RET : DT_OP_NOP;11011102/*1103* Establish the instruction sequence -- all nops for probes, and an1104* instruction to clear the return value register (%eax/%rax) followed1105* by nops for is-enabled probes. For is-enabled probes, we advance1106* the offset to the first nop. This isn't stricly necessary but makes1107* for more readable disassembly when the probe is enabled.1108*/1109if (!isenabled) {1110ip[0] = ret;1111ip[1] = DT_OP_NOP;1112ip[2] = DT_OP_NOP;1113ip[3] = DT_OP_NOP;1114ip[4] = DT_OP_NOP;1115} else if (dtp->dt_oflags & DTRACE_O_LP64) {1116ip[0] = DT_OP_REX_RAX;1117ip[1] = DT_OP_XOR_EAX_0;1118ip[2] = DT_OP_XOR_EAX_1;1119ip[3] = ret;1120ip[4] = DT_OP_NOP;1121(*off) += 3;1122} else {1123ip[0] = DT_OP_XOR_EAX_0;1124ip[1] = DT_OP_XOR_EAX_1;1125ip[2] = ret;1126ip[3] = DT_OP_NOP;1127ip[4] = DT_OP_NOP;1128(*off) += 2;1129}11301131return (0);1132}11331134#else1135#error unknown ISA1136#endif11371138/*PRINTFLIKE5*/1139static int1140dt_link_error(dtrace_hdl_t *dtp, Elf *elf, int fd, dt_link_pair_t *bufs,1141const char *format, ...)1142{1143va_list ap;1144dt_link_pair_t *pair;11451146va_start(ap, format);1147dt_set_errmsg(dtp, NULL, NULL, NULL, 0, format, ap);1148va_end(ap);11491150if (elf != NULL)1151(void) elf_end(elf);11521153if (fd >= 0)1154(void) close(fd);11551156while ((pair = bufs) != NULL) {1157bufs = pair->dlp_next;1158dt_free(dtp, pair->dlp_str);1159dt_free(dtp, pair->dlp_sym);1160dt_free(dtp, pair);1161}11621163return (dt_set_errno(dtp, EDT_COMPILER));1164}11651166/*1167* Provide a unique identifier used when adding global symbols to an object.1168* This is the FNV-1a hash of an absolute path for the file.1169*/1170static unsigned int1171hash_obj(const char *obj, int fd)1172{1173char path[PATH_MAX];1174unsigned int h;11751176if (realpath(obj, path) == NULL)1177return (-1);11781179for (h = 2166136261u, obj = &path[0]; *obj != '\0'; obj++)1180h = (h ^ *obj) * 16777619;1181h &= 0x7fffffff;1182return (h);1183}11841185static int1186process_obj(dtrace_hdl_t *dtp, const char *obj, int *eprobesp)1187{1188static const char dt_prefix[] = "__dtrace";1189static const char dt_enabled[] = "enabled";1190static const char dt_symprefix[] = "$dtrace";1191static const char dt_symfmt[] = "%s%u.%s";1192static const char dt_weaksymfmt[] = "%s.%s";1193char probename[DTRACE_NAMELEN];1194int fd, i, ndx, eprobe, uses_funcdesc = 0, mod = 0;1195Elf *elf = NULL;1196GElf_Ehdr ehdr;1197Elf_Scn *scn_rel, *scn_sym, *scn_str, *scn_tgt;1198Elf_Data *data_rel, *data_sym, *data_str, *data_tgt;1199GElf_Shdr shdr_rel, shdr_sym, shdr_str, shdr_tgt;1200GElf_Sym rsym, fsym, dsym;1201GElf_Rela rela;1202char *s, *p, *r;1203char pname[DTRACE_PROVNAMELEN];1204dt_provider_t *pvp;1205dt_probe_t *prp;1206uint32_t off, eclass, emachine1, emachine2;1207size_t symsize, osym, nsym, isym, istr, len;1208unsigned int objkey;1209dt_link_pair_t *pair, *bufs = NULL;1210dt_strtab_t *strtab;1211void *tmp;12121213if ((fd = open64(obj, O_RDWR)) == -1) {1214return (dt_link_error(dtp, elf, fd, bufs,1215"failed to open %s: %s", obj, strerror(errno)));1216}12171218if ((elf = elf_begin(fd, ELF_C_RDWR, NULL)) == NULL) {1219return (dt_link_error(dtp, elf, fd, bufs,1220"failed to process %s: %s", obj, elf_errmsg(elf_errno())));1221}12221223switch (elf_kind(elf)) {1224case ELF_K_ELF:1225break;1226case ELF_K_AR:1227return (dt_link_error(dtp, elf, fd, bufs, "archives are not "1228"permitted; use the contents of the archive instead: %s",1229obj));1230default:1231return (dt_link_error(dtp, elf, fd, bufs,1232"invalid file type: %s", obj));1233}12341235if (gelf_getehdr(elf, &ehdr) == NULL) {1236return (dt_link_error(dtp, elf, fd, bufs, "corrupt file: %s",1237obj));1238}12391240if (dtp->dt_oflags & DTRACE_O_LP64) {1241eclass = ELFCLASS64;1242#if defined(__powerpc__)1243emachine1 = emachine2 = EM_PPC64;1244#if !defined(_CALL_ELF) || _CALL_ELF == 11245uses_funcdesc = 1;1246#endif1247#elif defined(__i386) || defined(__amd64)1248emachine1 = emachine2 = EM_AMD64;1249#elif defined(__aarch64__)1250emachine1 = emachine2 = EM_AARCH64;1251#elif defined(__riscv)1252emachine1 = emachine2 = EM_RISCV;1253#endif1254symsize = sizeof (Elf64_Sym);1255} else {1256eclass = ELFCLASS32;1257#if defined(__arm__)1258emachine1 = emachine2 = EM_ARM;1259#elif defined(__powerpc__)1260emachine1 = emachine2 = EM_PPC;1261#elif defined(__i386) || defined(__amd64)1262emachine1 = emachine2 = EM_386;1263#endif1264symsize = sizeof (Elf32_Sym);1265}12661267if (ehdr.e_ident[EI_CLASS] != eclass) {1268return (dt_link_error(dtp, elf, fd, bufs,1269"incorrect ELF class for object file: %s", obj));1270}12711272if (ehdr.e_machine != emachine1 && ehdr.e_machine != emachine2) {1273return (dt_link_error(dtp, elf, fd, bufs,1274"incorrect ELF machine type for object file: %s", obj));1275}12761277/*1278* We use this token as a relatively unique handle for this file on the1279* system in order to disambiguate potential conflicts between files of1280* the same name which contain identially named local symbols.1281*/1282if ((objkey = hash_obj(obj, fd)) == (unsigned int)-1)1283return (dt_link_error(dtp, elf, fd, bufs,1284"failed to generate unique key for object file: %s", obj));12851286scn_rel = NULL;1287while ((scn_rel = elf_nextscn(elf, scn_rel)) != NULL) {1288if (gelf_getshdr(scn_rel, &shdr_rel) == NULL)1289goto err;12901291/*1292* Skip any non-relocation sections.1293*/1294if (shdr_rel.sh_type != SHT_RELA && shdr_rel.sh_type != SHT_REL)1295continue;12961297if ((data_rel = elf_getdata(scn_rel, NULL)) == NULL)1298goto err;12991300/*1301* Grab the section, section header and section data for the1302* symbol table that this relocation section references.1303*/1304if ((scn_sym = elf_getscn(elf, shdr_rel.sh_link)) == NULL ||1305gelf_getshdr(scn_sym, &shdr_sym) == NULL ||1306(data_sym = elf_getdata(scn_sym, NULL)) == NULL)1307goto err;13081309/*1310* Ditto for that symbol table's string table.1311*/1312if ((scn_str = elf_getscn(elf, shdr_sym.sh_link)) == NULL ||1313gelf_getshdr(scn_str, &shdr_str) == NULL ||1314(data_str = elf_getdata(scn_str, NULL)) == NULL)1315goto err;13161317/*1318* Grab the section, section header and section data for the1319* target section for the relocations. For the relocations1320* we're looking for -- this will typically be the text of the1321* object file.1322*/1323if ((scn_tgt = elf_getscn(elf, shdr_rel.sh_info)) == NULL ||1324gelf_getshdr(scn_tgt, &shdr_tgt) == NULL ||1325(data_tgt = elf_getdata(scn_tgt, NULL)) == NULL)1326goto err;13271328/*1329* We're looking for relocations to symbols matching this form:1330*1331* __dtrace[enabled]_<prov>___<probe>1332*1333* For the generated object, we need to record the location1334* identified by the relocation, and create a new relocation1335* in the generated object that will be resolved at link time1336* to the location of the function in which the probe is1337* embedded. In the target object, we change the matched symbol1338* so that it will be ignored at link time, and we modify the1339* target (text) section to replace the call instruction with1340* one or more nops.1341*1342* To avoid runtime overhead, the relocations added to the1343* generated object should be resolved at static link time. We1344* therefore create aliases for the functions that contain1345* probes. An alias is global (so that the relocation from the1346* generated object can be resolved), and hidden (so that its1347* address is known at static link time). Such aliases have this1348* form:1349*1350* $dtrace<key>.<function>1351*1352* We take a first pass through all the relocations to1353* populate our string table and count the number of extra1354* symbols we'll require.1355*1356* We also handle the case where the object has already been1357* processed, to support incremental rebuilds. Relocations1358* of interest are converted to type NONE, but all information1359* needed to reconstruct the output DOF is retained.1360*/1361strtab = dt_strtab_create(1);1362nsym = 0;1363isym = data_sym->d_size / symsize;1364istr = data_str->d_size;13651366for (i = 0; i < shdr_rel.sh_size / shdr_rel.sh_entsize; i++) {1367if (shdr_rel.sh_type == SHT_RELA) {1368if (gelf_getrela(data_rel, i, &rela) == NULL)1369continue;1370} else {1371GElf_Rel rel;1372if (gelf_getrel(data_rel, i, &rel) == NULL)1373continue;1374rela.r_offset = rel.r_offset;1375rela.r_info = rel.r_info;1376rela.r_addend = 0;1377}13781379if (gelf_getsym(data_sym, GELF_R_SYM(rela.r_info),1380&rsym) == NULL) {1381dt_strtab_destroy(strtab);1382goto err;1383}13841385s = (char *)data_str->d_buf + rsym.st_name;13861387if (strncmp(s, dt_prefix, sizeof (dt_prefix) - 1) != 0)1388continue;13891390if (dt_symtab_lookup(data_sym, 0, isym, rela.r_offset,1391shdr_rel.sh_info, &fsym, uses_funcdesc,1392elf) != 0) {1393dt_strtab_destroy(strtab);1394goto err;1395}13961397if (fsym.st_name > data_str->d_size) {1398dt_strtab_destroy(strtab);1399goto err;1400}14011402s = (char *)data_str->d_buf + fsym.st_name;14031404/*1405* If this symbol isn't of type function, we've really1406* driven off the rails or the object file is corrupt.1407*/1408if (GELF_ST_TYPE(fsym.st_info) != STT_FUNC) {1409dt_strtab_destroy(strtab);1410return (dt_link_error(dtp, elf, fd, bufs,1411"expected %s to be of type function", s));1412}14131414/*1415* Aliases of weak symbols don't get a uniquifier.1416*/1417if (GELF_ST_BIND(fsym.st_info) == STB_WEAK) {1418len = snprintf(NULL, 0, dt_weaksymfmt,1419dt_symprefix, s) + 1;1420} else {1421len = snprintf(NULL, 0, dt_symfmt, dt_symprefix,1422objkey, s) + 1;1423}1424if ((p = dt_alloc(dtp, len)) == NULL) {1425dt_strtab_destroy(strtab);1426goto err;1427}1428if (GELF_ST_BIND(fsym.st_info) == STB_WEAK) {1429(void) snprintf(p, len, dt_weaksymfmt,1430dt_symprefix, s);1431} else {1432(void) snprintf(p, len, dt_symfmt, dt_symprefix,1433objkey, s);1434}14351436if (dt_strtab_index(strtab, p) == -1) {1437/*1438* Do not add new symbols if this object file1439* has already been processed.1440*/1441if (GELF_R_TYPE(rela.r_info) != DT_REL_NONE)1442nsym++;1443(void) dt_strtab_insert(strtab, p);1444}14451446dt_free(dtp, p);1447}14481449/*1450* If any new probes were found, allocate the additional space1451* for the symbol table and string table, copying the old data1452* into the new buffers, and marking the buffers as dirty. We1453* inject those newly allocated buffers into the libelf data1454* structures, but are still responsible for freeing them once1455* we're done with the elf handle.1456*/1457osym = isym;1458if (nsym > 0) {1459/*1460* The first byte of the string table is reserved for1461* the \0 entry.1462*/1463len = dt_strtab_size(strtab) - 1;14641465assert(len > 0);1466assert(dt_strtab_index(strtab, "") == 0);14671468dt_strtab_destroy(strtab);14691470if ((pair = dt_alloc(dtp, sizeof (*pair))) == NULL)1471goto err;14721473if ((pair->dlp_str = dt_alloc(dtp, data_str->d_size +1474len)) == NULL) {1475dt_free(dtp, pair);1476goto err;1477}14781479if ((pair->dlp_sym = dt_alloc(dtp, data_sym->d_size +1480nsym * symsize)) == NULL) {1481dt_free(dtp, pair->dlp_str);1482dt_free(dtp, pair);1483goto err;1484}14851486pair->dlp_next = bufs;1487bufs = pair;14881489bcopy(data_str->d_buf, pair->dlp_str, data_str->d_size);1490tmp = data_str->d_buf;1491data_str->d_buf = pair->dlp_str;1492pair->dlp_str = tmp;1493data_str->d_size += len;1494(void) elf_flagdata(data_str, ELF_C_SET, ELF_F_DIRTY);14951496shdr_str.sh_size += len;1497(void) gelf_update_shdr(scn_str, &shdr_str);14981499bcopy(data_sym->d_buf, pair->dlp_sym, data_sym->d_size);1500tmp = data_sym->d_buf;1501data_sym->d_buf = pair->dlp_sym;1502pair->dlp_sym = tmp;1503data_sym->d_size += nsym * symsize;1504(void) elf_flagdata(data_sym, ELF_C_SET, ELF_F_DIRTY);15051506shdr_sym.sh_size += nsym * symsize;1507(void) gelf_update_shdr(scn_sym, &shdr_sym);15081509nsym += isym;1510} else if (dt_strtab_empty(strtab)) {1511dt_strtab_destroy(strtab);1512continue;1513}15141515/*1516* Now that the tables have been allocated, perform the1517* modifications described above.1518*/1519for (i = 0; i < shdr_rel.sh_size / shdr_rel.sh_entsize; i++) {1520if (shdr_rel.sh_type == SHT_RELA) {1521if (gelf_getrela(data_rel, i, &rela) == NULL)1522continue;1523} else {1524GElf_Rel rel;1525if (gelf_getrel(data_rel, i, &rel) == NULL)1526continue;1527rela.r_offset = rel.r_offset;1528rela.r_info = rel.r_info;1529rela.r_addend = 0;1530}15311532ndx = GELF_R_SYM(rela.r_info);15331534if (gelf_getsym(data_sym, ndx, &rsym) == NULL ||1535rsym.st_name > data_str->d_size)1536goto err;15371538s = (char *)data_str->d_buf + rsym.st_name;15391540if (strncmp(s, dt_prefix, sizeof (dt_prefix) - 1) != 0)1541continue;15421543s += sizeof (dt_prefix) - 1;15441545/*1546* Check to see if this is an 'is-enabled' check as1547* opposed to a normal probe.1548*/1549if (strncmp(s, dt_enabled,1550sizeof (dt_enabled) - 1) == 0) {1551s += sizeof (dt_enabled) - 1;1552eprobe = 1;1553*eprobesp = 1;1554dt_dprintf("is-enabled probe\n");1555} else {1556eprobe = 0;1557dt_dprintf("normal probe\n");1558}15591560if (*s++ != '_')1561goto err;15621563if ((p = strstr(s, "___")) == NULL ||1564p - s >= sizeof (pname))1565goto err;15661567bcopy(s, pname, p - s);1568pname[p - s] = '\0';15691570if (dt_symtab_lookup(data_sym, osym, isym,1571rela.r_offset, shdr_rel.sh_info, &fsym,1572uses_funcdesc, elf) == 0) {1573if (fsym.st_name > data_str->d_size)1574goto err;15751576r = s = (char *) data_str->d_buf + fsym.st_name;1577assert(strstr(s, dt_symprefix) == s);1578s = strchr(s, '.') + 1;1579} else if (dt_symtab_lookup(data_sym, 0, osym,1580rela.r_offset, shdr_rel.sh_info, &fsym,1581uses_funcdesc, elf) == 0) {1582u_int bind;15831584bind = GELF_ST_BIND(fsym.st_info) == STB_WEAK ?1585STB_WEAK : STB_GLOBAL;1586s = (char *) data_str->d_buf + fsym.st_name;1587if (GELF_R_TYPE(rela.r_info) != DT_REL_NONE) {1588/*1589* Emit an alias for the symbol. It1590* needs to be non-preemptible so that1591* .SUNW_dof relocations may be resolved1592* at static link time. Aliases of weak1593* symbols are given a non-unique name1594* so that they may be merged by the1595* linker.1596*/1597dsym = fsym;1598dsym.st_name = istr;1599dsym.st_info = GELF_ST_INFO(bind,1600STT_FUNC);1601dsym.st_other =1602GELF_ST_VISIBILITY(STV_HIDDEN);1603(void) gelf_update_sym(data_sym, isym,1604&dsym);1605isym++;1606assert(isym <= nsym);16071608r = (char *) data_str->d_buf + istr;1609if (bind == STB_WEAK) {1610istr += sprintf(r,1611dt_weaksymfmt, dt_symprefix,1612s);1613} else {1614istr += sprintf(r, dt_symfmt,1615dt_symprefix, objkey, s);1616}1617istr++;1618} else {1619if (bind == STB_WEAK) {1620(void) asprintf(&r,1621dt_weaksymfmt, dt_symprefix,1622s);1623} else {1624(void) asprintf(&r, dt_symfmt,1625dt_symprefix, objkey, s);1626}1627}1628} else {1629goto err;1630}16311632if ((pvp = dt_provider_lookup(dtp, pname)) == NULL) {1633return (dt_link_error(dtp, elf, fd, bufs,1634"no such provider %s", pname));1635}16361637if (strlcpy(probename, p + 3, sizeof (probename)) >=1638sizeof (probename))1639return (dt_link_error(dtp, elf, fd, bufs,1640"invalid probe name %s", probename));1641(void) strhyphenate(probename);1642if ((prp = dt_probe_lookup(pvp, probename)) == NULL)1643return (dt_link_error(dtp, elf, fd, bufs,1644"no such probe %s", probename));16451646assert(fsym.st_value <= rela.r_offset);16471648off = rela.r_offset - fsym.st_value;1649if (dt_modtext(dtp, data_tgt->d_buf, eprobe,1650&rela, &off) != 0)1651goto err;16521653if (dt_probe_define(pvp, prp, s, r, off, eprobe) != 0) {1654return (dt_link_error(dtp, elf, fd, bufs,1655"failed to allocate space for probe"));1656}16571658/*1659* We are done with this relocation, but it must be1660* preserved in order to support incremental rebuilds.1661*/1662if (shdr_rel.sh_type == SHT_RELA) {1663rela.r_info = GELF_R_INFO(1664GELF_R_SYM(rela.r_info), DT_REL_NONE);1665(void) gelf_update_rela(data_rel, i, &rela);1666} else {1667GElf_Rel rel;1668rel.r_offset = rela.r_offset;1669rel.r_info = GELF_R_INFO(1670GELF_R_SYM(rela.r_info), DT_REL_NONE);1671(void) gelf_update_rel(data_rel, i, &rel);1672}16731674mod = 1;1675(void) elf_flagdata(data_tgt, ELF_C_SET, ELF_F_DIRTY);16761677/*1678* This symbol may already have been marked to1679* be ignored by another relocation referencing1680* the same symbol or if this object file has1681* already been processed by an earlier link1682* invocation.1683*/1684if (rsym.st_shndx != SHN_ABS) {1685rsym.st_info = GELF_ST_INFO(STB_WEAK, STT_FUNC);1686rsym.st_shndx = SHN_ABS;1687(void) gelf_update_sym(data_sym, ndx, &rsym);1688}1689}1690}16911692if (mod && elf_update(elf, ELF_C_WRITE) == -1)1693goto err;16941695(void) elf_end(elf);1696(void) close(fd);16971698while ((pair = bufs) != NULL) {1699bufs = pair->dlp_next;1700dt_free(dtp, pair->dlp_str);1701dt_free(dtp, pair->dlp_sym);1702dt_free(dtp, pair);1703}17041705return (0);17061707err:1708return (dt_link_error(dtp, elf, fd, bufs,1709"an error was encountered while processing %s", obj));1710}17111712int1713dtrace_program_link(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, uint_t dflags,1714const char *file, int objc, char *const objv[])1715{1716char tfile[PATH_MAX];1717char drti[PATH_MAX];1718dof_hdr_t *dof;1719int fd, status, i, cur;1720char *cmd, tmp;1721size_t len;1722int eprobes = 0, ret = 0;17231724/*1725* A NULL program indicates a special use in which we just link1726* together a bunch of object files specified in objv and then1727* unlink(2) those object files.1728*/1729if (pgp == NULL) {1730const char *fmt = "%s -o %s -r";17311732len = snprintf(&tmp, 1, fmt, dtp->dt_ld_path, file) + 1;17331734for (i = 0; i < objc; i++)1735len += strlen(objv[i]) + 1;17361737cmd = alloca(len);17381739cur = snprintf(cmd, len, fmt, dtp->dt_ld_path, file);17401741for (i = 0; i < objc; i++)1742cur += snprintf(cmd + cur, len - cur, " %s", objv[i]);17431744if ((status = system(cmd)) == -1) {1745return (dt_link_error(dtp, NULL, -1, NULL,1746"failed to run %s: %s", dtp->dt_ld_path,1747strerror(errno)));1748}17491750if (WIFSIGNALED(status)) {1751return (dt_link_error(dtp, NULL, -1, NULL,1752"failed to link %s: %s failed due to signal %d",1753file, dtp->dt_ld_path, WTERMSIG(status)));1754}17551756if (WEXITSTATUS(status) != 0) {1757return (dt_link_error(dtp, NULL, -1, NULL,1758"failed to link %s: %s exited with status %d\n",1759file, dtp->dt_ld_path, WEXITSTATUS(status)));1760}17611762for (i = 0; i < objc; i++) {1763if (strcmp(objv[i], file) != 0)1764(void) unlink(objv[i]);1765}17661767return (0);1768}17691770for (i = 0; i < objc; i++) {1771if (process_obj(dtp, objv[i], &eprobes) != 0)1772return (-1); /* errno is set for us */1773}17741775/*1776* If there are is-enabled probes then we need to force use of DOF1777* version 2.1778*/1779if (eprobes && pgp->dp_dofversion < DOF_VERSION_2)1780pgp->dp_dofversion = DOF_VERSION_2;17811782if ((dof = dtrace_dof_create(dtp, pgp, dflags)) == NULL)1783return (-1); /* errno is set for us */17841785snprintf(tfile, sizeof(tfile), "%s.XXXXXX", file);1786if ((fd = mkostemp(tfile, O_CLOEXEC)) == -1)1787return (dt_link_error(dtp, NULL, -1, NULL,1788"failed to create temporary file %s: %s",1789tfile, strerror(errno)));17901791/*1792* If -xlinktype=DOF has been selected, just write out the DOF.1793* Otherwise proceed to the default of generating and linking ELF.1794*/1795switch (dtp->dt_linktype) {1796case DT_LTYP_DOF:1797if (dt_write(dtp, fd, dof, dof->dofh_filesz) < dof->dofh_filesz)1798ret = errno;17991800if (close(fd) != 0 && ret == 0)1801ret = errno;18021803if (ret != 0) {1804return (dt_link_error(dtp, NULL, -1, NULL,1805"failed to write %s: %s", file, strerror(ret)));1806}18071808return (0);18091810case DT_LTYP_ELF:1811break; /* fall through to the rest of dtrace_program_link() */18121813default:1814return (dt_link_error(dtp, NULL, -1, NULL,1815"invalid link type %u\n", dtp->dt_linktype));1816}181718181819if (dtp->dt_oflags & DTRACE_O_LP64)1820status = dump_elf64(dtp, dof, fd);1821else1822status = dump_elf32(dtp, dof, fd);18231824if (status != 0)1825return (dt_link_error(dtp, NULL, -1, NULL,1826"failed to write %s: %s", tfile,1827strerror(dtrace_errno(dtp))));18281829if (!dtp->dt_lazyload) {1830const char *fmt = "%s -o %s -r %s %s";1831dt_dirpath_t *dp = dt_list_next(&dtp->dt_lib_path);18321833(void) snprintf(drti, sizeof (drti), "%s/drti.o", dp->dir_path);18341835len = snprintf(&tmp, 1, fmt, dtp->dt_ld_path, file, tfile,1836drti) + 1;18371838cmd = alloca(len);18391840(void) snprintf(cmd, len, fmt, dtp->dt_ld_path, file, tfile,1841drti);1842if ((status = system(cmd)) == -1) {1843ret = dt_link_error(dtp, NULL, fd, NULL,1844"failed to run %s: %s", dtp->dt_ld_path,1845strerror(errno));1846goto done;1847}18481849if (WIFSIGNALED(status)) {1850ret = dt_link_error(dtp, NULL, fd, NULL,1851"failed to link %s: %s failed due to signal %d",1852file, dtp->dt_ld_path, WTERMSIG(status));1853goto done;1854}18551856if (WEXITSTATUS(status) != 0) {1857ret = dt_link_error(dtp, NULL, fd, NULL,1858"failed to link %s: %s exited with status %d\n",1859file, dtp->dt_ld_path, WEXITSTATUS(status));1860goto done;1861}1862(void) close(fd); /* release temporary file */18631864/*1865* Now that we've linked drti.o, reduce the global __SUNW_dof1866* symbol to a local symbol. This is needed to so that multiple1867* generated object files (for different providers, for1868* instance) can be linked together. This is accomplished using1869* the -Blocal flag with Sun's linker, but GNU ld doesn't appear1870* to have an equivalent option.1871*/1872asprintf(&cmd, "%s --localize-hidden %s", dtp->dt_objcopy_path,1873file);1874if ((status = system(cmd)) == -1) {1875ret = dt_link_error(dtp, NULL, -1, NULL,1876"failed to run %s: %s", dtp->dt_objcopy_path,1877strerror(errno));1878free(cmd);1879goto done;1880}1881free(cmd);18821883if (WIFSIGNALED(status)) {1884ret = dt_link_error(dtp, NULL, -1, NULL,1885"failed to link %s: %s failed due to signal %d",1886file, dtp->dt_objcopy_path, WTERMSIG(status));1887goto done;1888}18891890if (WEXITSTATUS(status) != 0) {1891ret = dt_link_error(dtp, NULL, -1, NULL,1892"failed to link %s: %s exited with status %d\n",1893file, dtp->dt_objcopy_path, WEXITSTATUS(status));1894goto done;1895}1896} else {1897if (rename(tfile, file) != 0) {1898ret = dt_link_error(dtp, NULL, fd, NULL,1899"failed to rename %s to %s: %s", tfile, file,1900strerror(errno));1901goto done;1902}1903(void) close(fd);1904}19051906done:1907dtrace_dof_destroy(dtp, dof);19081909if (!dtp->dt_lazyload)1910(void) unlink(tfile);1911return (ret);1912}191319141915