Changeset View
Changeset View
Standalone View
Standalone View
cddl/contrib/opensolaris/lib/libdtrace/common/dt_module.c
Show First 20 Lines • Show All 1,123 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Update our module cache by adding an entry for the specified module 'name'. | * Update our module cache by adding an entry for the specified module 'name'. | ||||
* We create the dt_module_t and populate it using /system/object/<name>/. | * We create the dt_module_t and populate it using /system/object/<name>/. | ||||
* | * | ||||
* On FreeBSD, the module name is passed as the full module file name, | * On FreeBSD, the module name is passed as the full module file name, | ||||
* including the path. | * including the path. | ||||
*/ | */ | ||||
static void | static void | ||||
#ifdef illumos | |||||
dt_module_update(dtrace_hdl_t *dtp, const char *name) | |||||
#else | |||||
dt_module_update(dtrace_hdl_t *dtp, struct kld_file_stat *k_stat) | dt_module_update(dtrace_hdl_t *dtp, struct kld_file_stat *k_stat) | ||||
#endif | |||||
{ | { | ||||
char fname[MAXPATHLEN]; | char fname[MAXPATHLEN]; | ||||
struct stat64 st; | struct stat64 st; | ||||
int fd, err, bits; | int fd, err, bits; | ||||
#ifdef __FreeBSD__ | |||||
struct module_stat ms; | struct module_stat ms; | ||||
dt_kmodule_t *dkmp; | dt_kmodule_t *dkmp; | ||||
uint_t h; | uint_t h; | ||||
int modid; | int modid; | ||||
#endif | |||||
dt_module_t *dmp; | dt_module_t *dmp; | ||||
const char *s; | const char *s; | ||||
size_t shstrs; | size_t shstrs; | ||||
GElf_Shdr sh; | GElf_Shdr sh; | ||||
Elf_Data *dp; | Elf_Data *dp; | ||||
Elf_Scn *sp; | Elf_Scn *sp; | ||||
#ifdef illumos | |||||
(void) snprintf(fname, sizeof (fname), | |||||
"%s/%s/object", OBJFS_ROOT, name); | |||||
#else | |||||
GElf_Ehdr ehdr; | GElf_Ehdr ehdr; | ||||
GElf_Phdr ph; | GElf_Phdr ph; | ||||
char name[MAXPATHLEN]; | char name[MAXPATHLEN]; | ||||
uintptr_t mapbase, alignmask; | uintptr_t mapbase, alignmask; | ||||
int i = 0; | int i = 0; | ||||
int is_elf_obj; | int is_elf_obj; | ||||
(void) strlcpy(name, k_stat->name, sizeof(name)); | (void) strlcpy(name, k_stat->name, sizeof(name)); | ||||
(void) strlcpy(fname, k_stat->pathname, sizeof(fname)); | (void) strlcpy(fname, k_stat->pathname, sizeof(fname)); | ||||
#endif | |||||
if ((fd = open(fname, O_RDONLY)) == -1 || fstat64(fd, &st) == -1 || | if ((fd = open(fname, O_RDONLY)) == -1 || fstat64(fd, &st) == -1 || | ||||
(dmp = dt_module_create(dtp, name)) == NULL) { | (dmp = dt_module_create(dtp, name)) == NULL) { | ||||
dt_dprintf("failed to open %s: %s\n", fname, strerror(errno)); | dt_dprintf("failed to open %s: %s\n", fname, strerror(errno)); | ||||
(void) close(fd); | (void) close(fd); | ||||
return; | return; | ||||
} | } | ||||
(void) strlcpy(dmp->dm_file, fname, sizeof(dmp->dm_file)); | |||||
dmp->dm_modid = k_stat->id; | |||||
/* | /* | ||||
* Since the module can unload out from under us (and /system/object | * Since the module can unload out from under us (and /system/object | ||||
* will return ENOENT), tell libelf to cook the entire file now and | * will return ENOENT), tell libelf to cook the entire file now and | ||||
* then close the underlying file descriptor immediately. If this | * then close the underlying file descriptor immediately. If this | ||||
* succeeds, we know that we can continue safely using dmp->dm_elf. | * succeeds, we know that we can continue safely using dmp->dm_elf. | ||||
*/ | */ | ||||
dmp->dm_elf = elf_begin(fd, ELF_C_READ, NULL); | dmp->dm_elf = elf_begin(fd, ELF_C_READ, NULL); | ||||
err = elf_cntl(dmp->dm_elf, ELF_C_FDREAD); | err = elf_cntl(dmp->dm_elf, ELF_C_FDREAD); | ||||
Show All 16 Lines | case ELFCLASS64: | ||||
dmp->dm_ops = &dt_modops_64; | dmp->dm_ops = &dt_modops_64; | ||||
bits = 64; | bits = 64; | ||||
break; | break; | ||||
default: | default: | ||||
dt_dprintf("failed to load %s: unknown ELF class\n", fname); | dt_dprintf("failed to load %s: unknown ELF class\n", fname); | ||||
dt_module_destroy(dtp, dmp); | dt_module_destroy(dtp, dmp); | ||||
return; | return; | ||||
} | } | ||||
#if defined(__FreeBSD__) | |||||
mapbase = (uintptr_t)k_stat->address; | mapbase = (uintptr_t)k_stat->address; | ||||
gelf_getehdr(dmp->dm_elf, &ehdr); | gelf_getehdr(dmp->dm_elf, &ehdr); | ||||
is_elf_obj = (ehdr.e_type == ET_REL); | is_elf_obj = (ehdr.e_type == ET_REL); | ||||
if (is_elf_obj) { | if (is_elf_obj) { | ||||
dmp->dm_sec_offsets = | dmp->dm_sec_offsets = | ||||
malloc(ehdr.e_shnum * sizeof(*dmp->dm_sec_offsets)); | malloc(ehdr.e_shnum * sizeof(*dmp->dm_sec_offsets)); | ||||
if (dmp->dm_sec_offsets == NULL) { | if (dmp->dm_sec_offsets == NULL) { | ||||
dt_dprintf("failed to allocate memory\n"); | dt_dprintf("failed to allocate memory\n"); | ||||
dt_module_destroy(dtp, dmp); | dt_module_destroy(dtp, dmp); | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
#endif | |||||
/* | /* | ||||
* Iterate over the section headers locating various sections of | * Iterate over the section headers locating various sections of | ||||
* interest and use their attributes to flesh out the dt_module_t. | * interest and use their attributes to flesh out the dt_module_t. | ||||
*/ | */ | ||||
for (sp = NULL; (sp = elf_nextscn(dmp->dm_elf, sp)) != NULL; ) { | for (sp = NULL; (sp = elf_nextscn(dmp->dm_elf, sp)) != NULL; ) { | ||||
if (gelf_getshdr(sp, &sh) == NULL || sh.sh_type == SHT_NULL || | if (gelf_getshdr(sp, &sh) == NULL || sh.sh_type == SHT_NULL || | ||||
(s = elf_strptr(dmp->dm_elf, shstrs, sh.sh_name)) == NULL) | (s = elf_strptr(dmp->dm_elf, shstrs, sh.sh_name)) == NULL) | ||||
continue; /* skip any malformed sections */ | continue; /* skip any malformed sections */ | ||||
#if defined(__FreeBSD__) | |||||
if (sh.sh_size == 0) | if (sh.sh_size == 0) | ||||
continue; | continue; | ||||
if (sh.sh_type == SHT_PROGBITS || sh.sh_type == SHT_NOBITS) { | if (sh.sh_type == SHT_PROGBITS || sh.sh_type == SHT_NOBITS) { | ||||
alignmask = sh.sh_addralign - 1; | alignmask = sh.sh_addralign - 1; | ||||
mapbase += alignmask; | mapbase += alignmask; | ||||
mapbase &= ~alignmask; | mapbase &= ~alignmask; | ||||
sh.sh_addr = mapbase; | sh.sh_addr = mapbase; | ||||
if (is_elf_obj) | if (is_elf_obj) | ||||
dmp->dm_sec_offsets[elf_ndxscn(sp)] = sh.sh_addr; | dmp->dm_sec_offsets[elf_ndxscn(sp)] = sh.sh_addr; | ||||
mapbase += sh.sh_size; | mapbase += sh.sh_size; | ||||
} | } | ||||
#endif | |||||
if (strcmp(s, ".text") == 0) { | if (strcmp(s, ".text") == 0) { | ||||
dmp->dm_text_size = sh.sh_size; | dmp->dm_text_size = sh.sh_size; | ||||
dmp->dm_text_va = sh.sh_addr; | dmp->dm_text_va = sh.sh_addr; | ||||
} else if (strcmp(s, ".data") == 0) { | } else if (strcmp(s, ".data") == 0) { | ||||
dmp->dm_data_size = sh.sh_size; | dmp->dm_data_size = sh.sh_size; | ||||
dmp->dm_data_va = sh.sh_addr; | dmp->dm_data_va = sh.sh_addr; | ||||
} else if (strcmp(s, ".bss") == 0) { | } else if (strcmp(s, ".bss") == 0) { | ||||
dmp->dm_bss_size = sh.sh_size; | dmp->dm_bss_size = sh.sh_size; | ||||
dmp->dm_bss_va = sh.sh_addr; | dmp->dm_bss_va = sh.sh_addr; | ||||
} else if (strcmp(s, ".info") == 0 && | } else if (strcmp(s, ".info") == 0 && | ||||
(dp = elf_getdata(sp, NULL)) != NULL) { | (dp = elf_getdata(sp, NULL)) != NULL) { | ||||
bcopy(dp->d_buf, &dmp->dm_info, | bcopy(dp->d_buf, &dmp->dm_info, | ||||
MIN(sh.sh_size, sizeof (dmp->dm_info))); | MIN(sh.sh_size, sizeof (dmp->dm_info))); | ||||
} else if (strcmp(s, ".filename") == 0 && | |||||
(dp = elf_getdata(sp, NULL)) != NULL) { | |||||
(void) strlcpy(dmp->dm_file, | |||||
dp->d_buf, sizeof (dmp->dm_file)); | |||||
} | } | ||||
} | } | ||||
dmp->dm_flags |= DT_DM_KERNEL; | dmp->dm_flags |= DT_DM_KERNEL; | ||||
#ifdef illumos | |||||
dmp->dm_modid = (int)OBJFS_MODID(st.st_ino); | |||||
#else | |||||
/* | /* | ||||
* Include .rodata and special sections into .text. | * Include .rodata and special sections into .text. | ||||
* This depends on default section layout produced by GNU ld | * This depends on default section layout produced by GNU ld | ||||
* for ELF objects and libraries: | * for ELF objects and libraries: | ||||
* [Text][R/O data][R/W data][Dynamic][BSS][Non loadable] | * [Text][R/O data][R/W data][Dynamic][BSS][Non loadable] | ||||
*/ | */ | ||||
dmp->dm_text_size = dmp->dm_data_va - dmp->dm_text_va; | dmp->dm_text_size = dmp->dm_data_va - dmp->dm_text_va; | ||||
#if defined(__i386__) | #if defined(__i386__) | ||||
/* | /* | ||||
* Find the first load section and figure out the relocation | * Find the first load section and figure out the relocation | ||||
* offset for the symbols. The kernel module will not need | * offset for the symbols. The kernel module will not need | ||||
* relocation, but the kernel linker modules will. | * relocation, but the kernel linker modules will. | ||||
*/ | */ | ||||
for (i = 0; gelf_getphdr(dmp->dm_elf, i, &ph) != NULL; i++) { | for (i = 0; gelf_getphdr(dmp->dm_elf, i, &ph) != NULL; i++) { | ||||
if (ph.p_type == PT_LOAD) { | if (ph.p_type == PT_LOAD) { | ||||
dmp->dm_reloc_offset = k_stat->address - ph.p_vaddr; | dmp->dm_reloc_offset = k_stat->address - ph.p_vaddr; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
#endif | #endif | ||||
#endif /* illumos */ | |||||
if (dmp->dm_info.objfs_info_primary) | if (dmp->dm_info.objfs_info_primary) | ||||
dmp->dm_flags |= DT_DM_PRIMARY; | dmp->dm_flags |= DT_DM_PRIMARY; | ||||
#ifdef __FreeBSD__ | |||||
ms.version = sizeof(ms); | ms.version = sizeof(ms); | ||||
for (modid = kldfirstmod(k_stat->id); modid > 0; | for (modid = kldfirstmod(k_stat->id); modid > 0; | ||||
modid = modnext(modid)) { | modid = modnext(modid)) { | ||||
if (modstat(modid, &ms) != 0) { | if (modstat(modid, &ms) != 0) { | ||||
dt_dprintf("modstat failed for id %d in %s: %s\n", | dt_dprintf("modstat failed for id %d in %s: %s\n", | ||||
modid, k_stat->name, strerror(errno)); | modid, k_stat->name, strerror(errno)); | ||||
continue; | continue; | ||||
} | } | ||||
if (dt_kmodule_lookup(dtp, ms.name) != NULL) | if (dt_kmodule_lookup(dtp, ms.name) != NULL) | ||||
continue; | continue; | ||||
dkmp = malloc(sizeof (*dkmp)); | dkmp = malloc(sizeof (*dkmp)); | ||||
if (dkmp == NULL) { | if (dkmp == NULL) { | ||||
dt_dprintf("failed to allocate memory\n"); | dt_dprintf("failed to allocate memory\n"); | ||||
dt_module_destroy(dtp, dmp); | dt_module_destroy(dtp, dmp); | ||||
return; | return; | ||||
} | } | ||||
h = dt_strtab_hash(ms.name, NULL) % dtp->dt_modbuckets; | h = dt_strtab_hash(ms.name, NULL) % dtp->dt_modbuckets; | ||||
dkmp->dkm_next = dtp->dt_kmods[h]; | dkmp->dkm_next = dtp->dt_kmods[h]; | ||||
dkmp->dkm_name = strdup(ms.name); | dkmp->dkm_name = strdup(ms.name); | ||||
dkmp->dkm_module = dmp; | dkmp->dkm_module = dmp; | ||||
dtp->dt_kmods[h] = dkmp; | dtp->dt_kmods[h] = dkmp; | ||||
} | } | ||||
#endif | |||||
dt_dprintf("opened %d-bit module %s (%s) [%d]\n", | dt_dprintf("opened %d-bit module %s (%s) [%d]\n", | ||||
bits, dmp->dm_name, dmp->dm_file, dmp->dm_modid); | bits, dmp->dm_name, dmp->dm_file, dmp->dm_modid); | ||||
} | } | ||||
/* | /* | ||||
* Unload all the loaded modules and then refresh the module cache with the | * Unload all the loaded modules and then refresh the module cache with the | ||||
* latest list of loaded modules and their address ranges. | * latest list of loaded modules and their address ranges. | ||||
▲ Show 20 Lines • Show All 456 Lines • Show Last 20 Lines |