dt_link.c
来自「Sun Solaris 10 中的 DTrace 组件的源代码。请参看: htt」· C语言 代码 · 共 1,111 行 · 第 1/2 页
C
1,111 行
}/* * Write out an ELF64 file prologue consisting of a header, section headers, * and a section header string table. The DOF data will follow this prologue * and complete the contents of the given ELF file. */static intdump_elf64(dtrace_hdl_t *dtp, const dof_hdr_t *dof, int fd){ struct { Elf64_Ehdr ehdr; Elf64_Shdr shdr[ESHDR_NUM]; } elf_file; Elf64_Shdr *shp; Elf64_Off off; dof_elf64_t de; int ret = 0; uint_t nshdr; if (prepare_elf64(dtp, dof, &de) != 0) return (-1); /* errno is set for us */ /* * If there are no relocations, we only need enough sections for * the shstrtab and the DOF. */ nshdr = de.de_nrel == 0 ? ESHDR_SYMTAB + 1 : ESHDR_NUM; bzero(&elf_file, sizeof (elf_file)); elf_file.ehdr.e_ident[EI_MAG0] = ELFMAG0; elf_file.ehdr.e_ident[EI_MAG1] = ELFMAG1; elf_file.ehdr.e_ident[EI_MAG2] = ELFMAG2; elf_file.ehdr.e_ident[EI_MAG3] = ELFMAG3; elf_file.ehdr.e_ident[EI_VERSION] = EV_CURRENT; elf_file.ehdr.e_ident[EI_CLASS] = ELFCLASS64;#if defined(_BIG_ENDIAN) elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2MSB;#elif defined(_LITTLE_ENDIAN) elf_file.ehdr.e_ident[EI_DATA] = ELFDATA2LSB;#endif elf_file.ehdr.e_type = ET_REL;#if defined(__sparc) elf_file.ehdr.e_machine = EM_SPARCV9;#elif defined(__i386) || defined(__amd64) elf_file.ehdr.e_machine = EM_AMD64;#endif elf_file.ehdr.e_version = EV_CURRENT; elf_file.ehdr.e_shoff = sizeof (Elf64_Ehdr); elf_file.ehdr.e_ehsize = sizeof (Elf64_Ehdr); elf_file.ehdr.e_phentsize = sizeof (Elf64_Phdr); elf_file.ehdr.e_shentsize = sizeof (Elf64_Shdr); elf_file.ehdr.e_shnum = nshdr; elf_file.ehdr.e_shstrndx = ESHDR_SHSTRTAB; off = sizeof (elf_file) + nshdr * sizeof (Elf64_Shdr); shp = &elf_file.shdr[ESHDR_SHSTRTAB]; shp->sh_name = 1; /* DTRACE_SHSTRTAB64[1] = ".shstrtab" */ shp->sh_type = SHT_STRTAB; shp->sh_offset = off; shp->sh_size = sizeof (DTRACE_SHSTRTAB64); shp->sh_addralign = sizeof (char); off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 8); shp = &elf_file.shdr[ESHDR_DOF]; shp->sh_name = 11; /* DTRACE_SHSTRTAB64[11] = ".SUNW_dof" */ shp->sh_flags = SHF_ALLOC; shp->sh_type = SHT_SUNW_dof; shp->sh_offset = off; shp->sh_size = dof->dofh_filesz; shp->sh_addralign = 8; off = shp->sh_offset + shp->sh_size; shp = &elf_file.shdr[ESHDR_STRTAB]; shp->sh_name = 21; /* DTRACE_SHSTRTAB64[21] = ".strtab" */ shp->sh_flags = SHF_ALLOC; shp->sh_type = SHT_STRTAB; shp->sh_offset = off; shp->sh_size = de.de_strlen; shp->sh_addralign = sizeof (char); off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 8); shp = &elf_file.shdr[ESHDR_SYMTAB]; shp->sh_name = 29; /* DTRACE_SHSTRTAB64[29] = ".symtab" */ shp->sh_flags = SHF_ALLOC; shp->sh_type = SHT_SYMTAB; shp->sh_entsize = sizeof (Elf64_Sym); shp->sh_link = ESHDR_STRTAB; shp->sh_offset = off; shp->sh_info = de.de_global; shp->sh_size = de.de_nsym * sizeof (Elf64_Sym); shp->sh_addralign = 8; off = P2ROUNDUP(shp->sh_offset + shp->sh_size, 8); if (de.de_nrel == 0) { if (dt_write(dtp, fd, &elf_file, sizeof (elf_file)) != sizeof (elf_file) || PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB64) || PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) || PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) || PWRITE_SCN(ESHDR_DOF, dof)) { ret = dt_set_errno(dtp, errno); } } else { shp = &elf_file.shdr[ESHDR_REL]; shp->sh_name = 37; /* DTRACE_SHSTRTAB64[37] = ".rel.SUNW_dof" */ shp->sh_flags = SHF_ALLOC; shp->sh_type = SHT_RELA; shp->sh_entsize = sizeof (de.de_rel[0]); shp->sh_link = ESHDR_SYMTAB; shp->sh_info = ESHDR_DOF; shp->sh_offset = off; shp->sh_size = de.de_nrel * sizeof (de.de_rel[0]); shp->sh_addralign = 8; if (dt_write(dtp, fd, &elf_file, sizeof (elf_file)) != sizeof (elf_file) || PWRITE_SCN(ESHDR_SHSTRTAB, DTRACE_SHSTRTAB64) || PWRITE_SCN(ESHDR_STRTAB, de.de_strtab) || PWRITE_SCN(ESHDR_SYMTAB, de.de_sym) || PWRITE_SCN(ESHDR_REL, de.de_rel) || PWRITE_SCN(ESHDR_DOF, dof)) { ret = dt_set_errno(dtp, errno); } } free(de.de_strtab); free(de.de_sym); free(de.de_rel); return (ret);}static intdt_symtab_lookup(Elf_Data *data_sym, uintptr_t addr, uint_t shn, GElf_Sym *sym){ int i, ret = -1; GElf_Sym s; for (i = 0; gelf_getsym(data_sym, i, sym) != NULL; i++) { if (GELF_ST_TYPE(sym->st_info) == STT_FUNC && shn == sym->st_shndx && sym->st_value <= addr && addr < sym->st_value + sym->st_size) { if (GELF_ST_BIND(sym->st_info) == STB_GLOBAL) return (0); ret = 0; s = *sym; } } if (ret == 0) *sym = s; return (ret);}#if defined(__sparc)#define DT_OP_RET 0x81c7e008#define DT_OP_NOP 0x01000000#define DT_OP_CALL 0x40000000#define DT_IS_RESTORE(inst) (((inst) & 0xc1f80000) == 0x81e80000)static intdt_modtext(char *p, GElf_Rela *rela, uint32_t *off){ uint32_t *ip; if ((rela->r_offset & (sizeof (uint32_t) - 1)) != 0) return (-1); /*LINTED*/ ip = (uint32_t *)(p + rela->r_offset); /* * We only know about some specific relocation types. */ if (GELF_R_TYPE(rela->r_info) != R_SPARC_WDISP30 && GELF_R_TYPE(rela->r_info) != R_SPARC_WPLT30) return (-1); /* * We may have already processed this object file in an earlier * linker invocation in which case we'd expect to see either * a ret/restore pair or a nop; return success in that case. */ if (DT_IS_RESTORE(ip[1])) { if (ip[0] == DT_OP_RET) { return (0); } } else { if (ip[0] == DT_OP_NOP) { (*off) += sizeof (ip[0]); return (0); } } /* * We only expect call instructions with a displacement of 0. */ assert(ip[0] == DT_OP_CALL); /* * If the call is followed by a restore, it's a tail call so change * the call to a ret. Otherwise we adjust the offset to land on what * was once the delay slot of the call so we correctly get all the * arguments. */ if (DT_IS_RESTORE(ip[1])) { ip[0] = DT_OP_RET; /* ret */ } else { ip[0] = DT_OP_NOP; /* nop */ (*off) += sizeof (ip[0]); } return (0);}#elif defined(__i386) || defined(__amd64)#define DT_OP_NOP 0x90#define DT_OP_CALL 0xe8static intdt_modtext(char *p, GElf_Rela *rela, uint32_t *off){ uint8_t *ip = (uint8_t *)(p + rela->r_offset - 1); /* * On x86, the first byte of the instruction is the call opcode and * the next four bytes are the 32-bit address; the relocation is for * the address so we back up one byte to land on the opcode. */ (*off) -= 1; /* * We only know about some specific relocation types. Luckily * these types have the same values on both 32-bit and 64-bit * x86 architectures. */ if (GELF_R_TYPE(rela->r_info) != R_386_PC32 && GELF_R_TYPE(rela->r_info) != R_386_PLT32) return (-1); /* * We may have already processed this object file in an earlier * linker invocation in which case we'd expect to see a bunch * of nops; return success in that case. */ if (ip[0] == DT_OP_NOP && ip[1] == DT_OP_NOP && ip[2] == DT_OP_NOP && ip[3] == DT_OP_NOP && ip[4] == DT_OP_NOP) return (0); /* * We only expect a call instrution with a 32-bit displacement. */ assert(ip[0] == DT_OP_CALL); ip[0] = DT_OP_NOP; ip[1] = DT_OP_NOP; ip[2] = DT_OP_NOP; ip[3] = DT_OP_NOP; ip[4] = DT_OP_NOP; return (0);}#else#error unknown ISA#endif/*PRINTFLIKE2*/static intdt_link_error(dtrace_hdl_t *dtp, const char *format, ...){ va_list ap; va_start(ap, format); dt_set_errmsg(dtp, NULL, NULL, NULL, 0, format, ap); va_end(ap); return (dt_set_errno(dtp, EDT_COMPILER));}static intprocess_obj(dtrace_hdl_t *dtp, const char *obj){ static const char dt_prefix[] = "__dtrace_"; int fd, i, ndx, mod = 0; Elf *elf; GElf_Ehdr ehdr; Elf_Scn *scn_rel, *scn_sym, *scn_tgt; Elf_Data *data_rel, *data_sym, *data_tgt; GElf_Shdr shdr_rel, shdr_sym, shdr_tgt; GElf_Sym rsym, fsym; GElf_Rela rela; GElf_Rel rel; char *s, *p; char pname[DTRACE_PROVNAMELEN]; dt_provider_t *pvp; dt_probe_t *prp; uint32_t off, eclass, emachine1, emachine2; if ((fd = open64(obj, O_RDWR)) == -1) { return (dt_link_error(dtp, "failed to open %s: %s", obj, strerror(errno))); } if (elf_version(EV_CURRENT) == EV_NONE || (elf = elf_begin(fd, ELF_C_RDWR, NULL)) == NULL) { return (dt_link_error(dtp, "failed to process %s: %s", obj, elf_errmsg(elf_errno()))); } switch (elf_kind(elf)) { case ELF_K_ELF: break; case ELF_K_AR: return (dt_link_error(dtp, "archive files are not permitted %s;" " use the contents of the archive instead", obj)); default: return (dt_link_error(dtp, "invalid file type for %s", obj)); } if (gelf_getehdr(elf, &ehdr) == NULL) return (dt_link_error(dtp, "corrupt object file %s", obj)); if (dtp->dt_oflags & DTRACE_O_LP64) { eclass = ELFCLASS64;#if defined(__sparc) emachine1 = emachine2 = EM_SPARCV9;#elif defined(__i386) || defined(__amd64) emachine1 = emachine2 = EM_AMD64;#endif } else { eclass = ELFCLASS32;#if defined(__sparc) emachine1 = EM_SPARC; emachine2 = EM_SPARC32PLUS;#elif defined(__i386) || defined(__amd64) emachine1 = emachine2 = EM_386;#endif } if (ehdr.e_ident[EI_CLASS] != eclass) return (dt_link_error(dtp, "incorrect ELF class for object " "file %s", obj)); if (ehdr.e_machine != emachine1 && ehdr.e_machine != emachine2) return (dt_link_error(dtp, "incorrect ELF machine type for " "object file %s", obj)); scn_rel = NULL; while ((scn_rel = elf_nextscn(elf, scn_rel)) != NULL) { if (gelf_getshdr(scn_rel, &shdr_rel) == NULL) goto err; if (shdr_rel.sh_type != SHT_RELA && shdr_rel.sh_type != SHT_REL) continue; if ((data_rel = elf_getdata(scn_rel, NULL)) == NULL) goto err; if ((scn_sym = elf_getscn(elf, shdr_rel.sh_link)) == NULL || gelf_getshdr(scn_sym, &shdr_sym) == NULL || (data_sym = elf_getdata(scn_sym, NULL)) == NULL) goto err; if ((scn_tgt = elf_getscn(elf, shdr_rel.sh_info)) == NULL || gelf_getshdr(scn_tgt, &shdr_tgt) == NULL || (data_tgt = elf_getdata(scn_tgt, NULL)) == NULL) goto err; for (i = 0; i < shdr_rel.sh_size / shdr_rel.sh_entsize; i++) { if (shdr_rel.sh_type == SHT_RELA) { if (gelf_getrela(data_rel, i, &rela) == NULL) continue; } else { if (gelf_getrel(data_rel, i, &rel) == NULL) continue; rela.r_offset = rel.r_offset; rela.r_info = rel.r_info; rela.r_addend = 0; } ndx = GELF_R_SYM(rela.r_info); if (gelf_getsym(data_sym, ndx, &rsym) == NULL || (s = elf_strptr(elf, shdr_sym.sh_link, rsym.st_name)) == NULL) goto err; if (strncmp(s, dt_prefix, sizeof (dt_prefix) - 1) != 0) continue; if (dt_symtab_lookup(data_sym, rela.r_offset, shdr_rel.sh_info, &fsym) != 0) goto err; s += sizeof (dt_prefix) - 1; if ((p = strstr(s, "___")) == NULL || p - s >= sizeof (pname)) goto err; (void) memcpy(pname, s, p - s); pname[p - s] = '\0'; p = strhyphenate(p + 3); /* strlen("___") */ if ((s = elf_strptr(elf, shdr_sym.sh_link, fsym.st_name)) == NULL) goto err; if ((pvp = dt_provider_lookup(dtp, pname)) == NULL) { return (dt_link_error(dtp, "no such provider %s", pname)); } if ((prp = dt_probe_lookup(pvp, p)) == NULL) { return (dt_link_error(dtp, "no such probe %s", p)); } assert(fsym.st_value < rela.r_offset); off = rela.r_offset - fsym.st_value; if (dt_modtext(data_tgt->d_buf, &rela, &off) != 0) goto err; dt_dprintf("added instance %s:%s %x\n", p, s, off); if (dt_probe_add(dtp, prp, s, off) != 0) return (-1); /* errno is set for us */ mod = 1; /* * This symbol may already have been marked to * be ignored by another relocation referencing * the same symbol or if this object file has * already been processed by an earlier link * invocation. */ if (rsym.st_shndx != SHN_SUNW_IGNORE) { rsym.st_shndx = SHN_SUNW_IGNORE; (void) gelf_update_sym(data_sym, ndx, &rsym); } } } if (mod && elf_update(elf, ELF_C_WRITE) == -1) goto err; return (0);err: return (dt_link_error(dtp, "an error was encountered while processing %s", obj));}intdtrace_program_link(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, uint_t dflags, const char *file, int objc, char *const objv[]){ char drti[PATH_MAX]; dof_hdr_t *dof; int fd, status, i; char *cmd, tmp; size_t len; int ret = 0; for (i = 0; i < objc; i++) { if (process_obj(dtp, objv[i]) != 0) return (-1); /* errno is set for us */ } dflags |= DTRACE_D_PROBES; if ((dof = dtrace_dof_create(dtp, pgp, dflags)) == NULL) return (-1); /* errno is set for us */ /* * Create a temporary file and then unlink it if we're going to * combine it with drti.o later. We can still refer to it in child * processes as /dev/fd/<fd>. */ if ((fd = open64(file, O_RDWR | O_CREAT | O_TRUNC, 0666)) == -1) { return (dt_link_error(dtp, "failed to open %s: %s", file, strerror(errno))); } if (!dtp->dt_lazyload) (void) unlink(file); if (dtp->dt_oflags & DTRACE_O_LP64) status = dump_elf64(dtp, dof, fd); else status = dump_elf32(dtp, dof, fd); if (status != 0 || lseek(fd, 0, SEEK_SET) != 0) { return (dt_link_error(dtp, "failed to write %s: %s", file, strerror(errno))); } if (!dtp->dt_lazyload) { if (dtp->dt_oflags & DTRACE_O_LP64) { (void) snprintf(drti, sizeof (drti), "%s/64/drti.o", _dtrace_libdir); } else { (void) snprintf(drti, sizeof (drti), "%s/drti.o", _dtrace_libdir); } len = snprintf(&tmp, 1, "%s -o %s -r /dev/fd/%d %s", dtp->dt_ld_path, file, fd, drti) + 1; cmd = alloca(len); (void) snprintf(cmd, len, "%s -o %s -r /dev/fd/%d %s", dtp->dt_ld_path, file, fd, drti); if ((status = system(cmd)) == -1) { ret = dt_link_error(dtp, "failed to run %s: %s", dtp->dt_ld_path, strerror(errno)); goto done; } (void) close(fd); /* release temporary file */ if (WIFSIGNALED(status)) { ret = dt_link_error(dtp, "failed to link %s: %s failed due to signal %d", file, dtp->dt_ld_path, WTERMSIG(status)); goto done; } if (WEXITSTATUS(status) != 0) { ret = dt_link_error(dtp, "failed to link %s: %s exited with status %d\n", file, dtp->dt_ld_path, WEXITSTATUS(status)); goto done; } } else { (void) close(fd); }done: dtrace_dof_destroy(dof); return (ret);}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?