| /* ******************************************************************************* |
| * Copyright (c) 2012-2021 Google, Inc. All rights reserved. |
| * Copyright (c) 2011 Massachusetts Institute of Technology All rights reserved. |
| * Copyright (c) 2008-2010 VMware, Inc. All rights reserved. |
| * *******************************************************************************/ |
| |
| /* |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * * Neither the name of VMware, Inc. nor the names of its contributors may be |
| * used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| */ |
| |
| /* ELF module analysis routines shared between core and non-core. */ |
| |
| #include "../globals.h" |
| #include "../module_shared.h" |
| #include "drlibc_unix.h" |
| #include "module_private.h" |
| #include "../utils.h" |
| #include "instrument.h" |
| #include <stddef.h> /* offsetof */ |
| #include <link.h> /* Elf_Symndx */ |
| |
| typedef union _elf_generic_header_t { |
| Elf64_Ehdr elf64; |
| Elf32_Ehdr elf32; |
| } elf_generic_header_t; |
| |
| /* This routine is duplicated in privload_mem_is_elf_so_header. Any update here |
| * should be updated in privload_mem_is_elf_so_header. |
| */ |
| /* Is there an ELF header for a shared object at address 'base'? |
| * If size == 0 then checks for header readability else assumes that size bytes from |
| * base are readable (unmap races are then callers responsibility). |
| */ |
| static bool |
| is_elf_so_header_common(app_pc base, size_t size, bool memory) |
| { |
| /* FIXME We could check more fields in the header just as the |
| * dlopen() does. */ |
| static const unsigned char ei_expected[SELFMAG] = { |
| [EI_MAG0] = ELFMAG0, [EI_MAG1] = ELFMAG1, [EI_MAG2] = ELFMAG2, [EI_MAG3] = ELFMAG3 |
| }; |
| ELF_HEADER_TYPE elf_header; |
| |
| if (base == NULL) { |
| ASSERT(false && "is_elf_so_header(): NULL base"); |
| return false; |
| } |
| |
| /* Read the header. We used to directly deref if size >= sizeof(ELF_HEADER_TYPE) |
| * but given that we now have safe_read_fast() it's best to always use it and |
| * avoid races (like i#2113). However, the non-fast version hits deadlock on |
| * memquery during client init, so we use a special routine safe_read_if_fast(). |
| */ |
| if (size >= sizeof(ELF_HEADER_TYPE)) { |
| if (!safe_read_if_fast(base, sizeof(ELF_HEADER_TYPE), &elf_header)) |
| return false; |
| } else if (size == 0) { |
| if (!d_r_safe_read(base, sizeof(ELF_HEADER_TYPE), &elf_header)) |
| return false; |
| } else { |
| return false; |
| } |
| |
| /* We check the first 4 bytes which is the magic number. */ |
| if ((size == 0 || size >= sizeof(ELF_HEADER_TYPE)) && |
| elf_header.e_ident[EI_MAG0] == ei_expected[EI_MAG0] && |
| elf_header.e_ident[EI_MAG1] == ei_expected[EI_MAG1] && |
| elf_header.e_ident[EI_MAG2] == ei_expected[EI_MAG2] && |
| elf_header.e_ident[EI_MAG3] == ei_expected[EI_MAG3] && |
| /* PR 475158: if an app loads a linkable but not loadable |
| * file (e.g., .o file) we don't want to treat as a module |
| */ |
| (elf_header.e_type == ET_DYN || elf_header.e_type == ET_EXEC)) { |
| /* i#157, we do more check to make sure we load the right modules, |
| * i.e. 32/64-bit libraries. |
| * We check again in privload_map_and_relocate() in loader for nice |
| * error message. |
| * Xref i#1345 for supporting mixed libs, which makes more sense for |
| * standalone mode tools like those using drsyms (i#1532) or |
| * dr_map_executable_file, but we just don't support that yet until we |
| * remove our hardcoded type defines in module_elf.h. |
| * |
| * i#1684: We do allow mixing arches of the same bitwidth to better support |
| * drdecode tools. We have no standalone_library var access here to limit |
| * this relaxation to tools; we assume DR managed code will hit other |
| * problems later for the wrong arch and that recognizing an other-arch |
| * file as an ELF won't cause problems. |
| */ |
| if ((elf_header.e_version != 1) || |
| (memory && elf_header.e_ehsize != sizeof(ELF_HEADER_TYPE)) || |
| (memory && |
| #ifdef X64 |
| elf_header.e_machine != EM_X86_64 && elf_header.e_machine != EM_AARCH64 && |
| elf_header.e_machine != EM_RISCV |
| #else |
| elf_header.e_machine != EM_386 && elf_header.e_machine != EM_ARM |
| #endif |
| )) |
| return false; |
| /* FIXME - should we add any of these to the check? For real |
| * modules all of these should hold. */ |
| ASSERT_CURIOSITY(elf_header.e_version == 1); |
| ASSERT_CURIOSITY(!memory || elf_header.e_ehsize == sizeof(ELF_HEADER_TYPE)); |
| ASSERT_CURIOSITY(elf_header.e_ident[EI_OSABI] == ELFOSABI_SYSV || |
| elf_header.e_ident[EI_OSABI] == ELFOSABI_LINUX); |
| ASSERT_CURIOSITY(!memory || |
| #ifdef X64 |
| elf_header.e_machine == EM_X86_64 || |
| elf_header.e_machine == EM_AARCH64 || |
| elf_header.e_machine == EM_RISCV |
| #else |
| elf_header.e_machine == EM_386 || elf_header.e_machine == EM_ARM |
| #endif |
| ); |
| return true; |
| } |
| return false; |
| } |
| |
| /* i#727: Recommend passing 0 as size if not known if the header can be safely |
| * read. |
| */ |
| bool |
| is_elf_so_header(app_pc base, size_t size) |
| { |
| return is_elf_so_header_common(base, size, true); |
| } |
| |
| uint |
| module_segment_prot_to_osprot(ELF_PROGRAM_HEADER_TYPE *prog_hdr) |
| { |
| uint segment_prot = 0; |
| if (TEST(PF_X, prog_hdr->p_flags)) |
| segment_prot |= MEMPROT_EXEC; |
| if (TEST(PF_W, prog_hdr->p_flags)) |
| segment_prot |= MEMPROT_WRITE; |
| if (TEST(PF_R, prog_hdr->p_flags)) |
| segment_prot |= MEMPROT_READ; |
| return segment_prot; |
| } |
| |
| /* XXX: This routine may be called before dynamorio relocation when we are |
| * in a fragile state and thus no globals access or use of ASSERT/LOG/STATS! |
| */ |
| /* Returns the minimum p_vaddr field, aligned to page boundaries, in |
| * the loadable segments in the prog_header array, or POINTER_MAX if |
| * there are no loadable segments. |
| */ |
| app_pc |
| module_vaddr_from_prog_header(app_pc prog_header, uint num_segments, |
| DR_PARAM_OUT app_pc *out_first_end, |
| DR_PARAM_OUT app_pc *out_max_end) |
| { |
| uint i; |
| app_pc min_vaddr = (app_pc)POINTER_MAX; |
| app_pc max_end = (app_pc)PTR_UINT_0; |
| app_pc first_end = NULL; |
| for (i = 0; i < num_segments; i++) { |
| /* Without the ELF header we use sizeof instead of elf_hdr->e_phentsize |
| * which must be a reliable assumption as dl_iterate_phdr() doesn't |
| * bother to deliver the entry size. |
| */ |
| ELF_PROGRAM_HEADER_TYPE *prog_hdr = |
| (ELF_PROGRAM_HEADER_TYPE *)(prog_header + |
| i * sizeof(ELF_PROGRAM_HEADER_TYPE)); |
| if (prog_hdr->p_type == PT_LOAD) { |
| /* ELF requires p_vaddr to already be aligned to p_align */ |
| /* XXX i#4737: Our PAGE_SIZE may not match the size on a cross-arch file |
| * that was loaded on another machine. We're also ignoring |
| * prog_hdr->p_align here as it is actually complex to use: some loaders |
| * (notably some kernels) seem to ignore it. These corner cases are left |
| * as unsolved for now. |
| */ |
| min_vaddr = |
| MIN(min_vaddr, (app_pc)ALIGN_BACKWARD(prog_hdr->p_vaddr, PAGE_SIZE)); |
| if (min_vaddr == (app_pc)prog_hdr->p_vaddr) |
| first_end = (app_pc)prog_hdr->p_vaddr + prog_hdr->p_memsz; |
| max_end = MAX( |
| max_end, |
| (app_pc)ALIGN_FORWARD(prog_hdr->p_vaddr + prog_hdr->p_memsz, PAGE_SIZE)); |
| } |
| } |
| if (out_first_end != NULL) |
| *out_first_end = first_end; |
| if (out_max_end != NULL) |
| *out_max_end = max_end; |
| return min_vaddr; |
| } |
| |
| bool |
| module_get_platform(file_t f, dr_platform_t *platform, dr_platform_t *alt_platform) |
| { |
| elf_generic_header_t elf_header; |
| if (alt_platform != NULL) |
| *alt_platform = DR_PLATFORM_NONE; |
| if (os_read(f, &elf_header, sizeof(elf_header)) != sizeof(elf_header)) |
| return false; |
| if (!is_elf_so_header_common((app_pc)&elf_header, sizeof(elf_header), false)) |
| return false; |
| ASSERT(offsetof(Elf64_Ehdr, e_machine) == offsetof(Elf32_Ehdr, e_machine)); |
| switch (elf_header.elf64.e_machine) { |
| case EM_X86_64: |
| #ifdef EM_AARCH64 |
| case EM_AARCH64: |
| #endif |
| *platform = DR_PLATFORM_64BIT; |
| break; |
| case EM_386: |
| case EM_ARM: *platform = DR_PLATFORM_32BIT; break; |
| default: return false; |
| } |
| return true; |
| } |
| |
| /* Get the module text section from the mapped image file, |
| * Note that it must be the image file, not the loaded module. |
| */ |
| ELF_ADDR |
| module_get_text_section(app_pc file_map, size_t file_size) |
| { |
| ELF_HEADER_TYPE *elf_hdr = (ELF_HEADER_TYPE *)file_map; |
| ELF_SECTION_HEADER_TYPE *sec_hdr; |
| char *strtab; |
| uint i; |
| ASSERT(is_elf_so_header(file_map, file_size)); |
| ASSERT(elf_hdr->e_shoff < file_size); |
| ASSERT(elf_hdr->e_shentsize == sizeof(ELF_SECTION_HEADER_TYPE)); |
| ASSERT(elf_hdr->e_shoff + elf_hdr->e_shentsize * elf_hdr->e_shnum <= file_size); |
| sec_hdr = (ELF_SECTION_HEADER_TYPE *)(file_map + elf_hdr->e_shoff); |
| strtab = (char *)(file_map + sec_hdr[elf_hdr->e_shstrndx].sh_offset); |
| for (i = 0; i < elf_hdr->e_shnum; i++) { |
| if (strcmp(".text", strtab + sec_hdr->sh_name) == 0) |
| return sec_hdr->sh_addr; |
| ++sec_hdr; |
| } |
| /* ELF doesn't require that there's a section named ".text". */ |
| ASSERT_CURIOSITY(false); |
| return 0; |
| } |
| |
| /* Read until EOF or error. Return number of bytes read. */ |
| static size_t |
| os_read_until(file_t fd, void *buf, size_t toread) |
| { |
| size_t orig_toread = toread; |
| ssize_t nread; |
| while (toread > 0) { |
| nread = os_read(fd, buf, toread); |
| if (nread <= 0) |
| break; |
| toread -= nread; |
| buf = (app_pc)buf + nread; |
| } |
| return orig_toread - toread; |
| } |
| |
| bool |
| elf_loader_init(elf_loader_t *elf, const char *filename) |
| { |
| memset(elf, 0, sizeof(*elf)); |
| elf->filename = filename; |
| elf->fd = os_open(filename, OS_OPEN_READ); |
| return elf->fd != INVALID_FILE; |
| } |
| |
| void |
| elf_loader_destroy(elf_loader_t *elf) |
| { |
| if (elf->fd != INVALID_FILE) { |
| os_close(elf->fd); |
| } |
| if (elf->file_map != NULL) { |
| os_unmap_file(elf->file_map, elf->file_size); |
| } |
| memset(elf, 0, sizeof(*elf)); |
| } |
| |
| ELF_HEADER_TYPE * |
| elf_loader_read_ehdr(elf_loader_t *elf) |
| { |
| /* The initial read is sized to read both ehdr and all phdrs. */ |
| if (elf->fd == INVALID_FILE) |
| return NULL; |
| if (elf->file_map != NULL) { |
| /* The user mapped the entire file up front, so use it. */ |
| elf->ehdr = (ELF_HEADER_TYPE *)elf->file_map; |
| } else { |
| size_t size = os_read_until(elf->fd, elf->buf, sizeof(elf->buf)); |
| if (size == 0) |
| return NULL; |
| if (!is_elf_so_header(elf->buf, size)) |
| return NULL; |
| elf->ehdr = (ELF_HEADER_TYPE *)elf->buf; |
| } |
| return elf->ehdr; |
| } |
| |
| app_pc |
| elf_loader_map_file(elf_loader_t *elf, bool reachable) |
| { |
| uint64 size64; |
| if (elf->file_map != NULL) |
| return elf->file_map; |
| if (elf->fd == INVALID_FILE) |
| return NULL; |
| if (!os_get_file_size_by_handle(elf->fd, &size64)) |
| return NULL; |
| ASSERT_TRUNCATE(elf->file_size, size_t, size64); |
| elf->file_size = (size_t)size64; /* truncate */ |
| /* We use os_map_file instead of map_file since this mapping is temporary. |
| * We don't need to add and remove it from dynamo_areas. |
| */ |
| elf->file_map = |
| os_map_file(elf->fd, &elf->file_size, 0, NULL, MEMPROT_READ, |
| MAP_FILE_COPY_ON_WRITE | (reachable ? MAP_FILE_REACHABLE : 0)); |
| return elf->file_map; |
| } |
| |
| ELF_PROGRAM_HEADER_TYPE * |
| elf_loader_read_phdrs(elf_loader_t *elf) |
| { |
| size_t ph_off; |
| size_t ph_size; |
| if (elf->ehdr == NULL) |
| return NULL; |
| ph_off = elf->ehdr->e_phoff; |
| ph_size = elf->ehdr->e_phnum * elf->ehdr->e_phentsize; |
| if (elf->file_map == NULL && ph_off + ph_size < sizeof(elf->buf)) { |
| /* We already read phdrs, and they are in buf. */ |
| elf->phdrs = (ELF_PROGRAM_HEADER_TYPE *)(elf->buf + elf->ehdr->e_phoff); |
| } else { |
| /* We have large or distant phdrs, so map the whole file. We could |
| * seek and read just the phdrs to avoid disturbing the address space, |
| * but that would introduce a dependency on DR's heap. |
| */ |
| if (elf_loader_map_file(elf, false /*!reachable*/) == NULL) |
| return NULL; |
| elf->phdrs = (ELF_PROGRAM_HEADER_TYPE *)(elf->file_map + elf->ehdr->e_phoff); |
| } |
| return elf->phdrs; |
| } |
| |
| bool |
| elf_loader_read_headers(elf_loader_t *elf, const char *filename) |
| { |
| if (!elf_loader_init(elf, filename)) |
| return false; |
| if (elf_loader_read_ehdr(elf) == NULL) |
| return false; |
| if (elf_loader_read_phdrs(elf) == NULL) |
| return false; |
| return true; |
| } |
| |
| app_pc |
| elf_loader_map_phdrs(elf_loader_t *elf, bool fixed, map_fn_t map_func, |
| unmap_fn_t unmap_func, prot_fn_t prot_func, |
| check_bounds_fn_t check_bounds_func, memset_fn_t memset_func, |
| modload_flags_t flags) |
| { |
| app_pc lib_base, lib_end, last_end; |
| ELF_HEADER_TYPE *elf_hdr = elf->ehdr; |
| app_pc map_base, map_end; |
| reg_t pg_offs; |
| uint seg_prot, i; |
| ptr_int_t delta; |
| size_t initial_map_size; |
| |
| ASSERT(elf->phdrs != NULL && "call elf_loader_read_phdrs() first"); |
| if (elf->phdrs == NULL) |
| return NULL; |
| |
| map_base = module_vaddr_from_prog_header((app_pc)elf->phdrs, elf->ehdr->e_phnum, NULL, |
| &map_end); |
| |
| if (fixed && check_bounds_func != NULL) |
| (*check_bounds_func)(elf, map_base, map_end); |
| |
| elf->image_size = map_end - map_base; |
| |
| /* reserve the memory from os for library */ |
| initial_map_size = elf->image_size; |
| if (TEST(MODLOAD_SEPARATE_BSS, flags)) { |
| /* place an extra no-access page after .bss */ |
| initial_map_size += PAGE_SIZE; |
| } |
| lib_base = (*map_func)(-1, &initial_map_size, 0, map_base, |
| MEMPROT_NONE, /* so the separating page is no-access */ |
| MAP_FILE_COPY_ON_WRITE | MAP_FILE_IMAGE | |
| /* i#1001: a PIE executable may have NULL as preferred |
| * base, in which case the map can be anywhere |
| */ |
| ((fixed && map_base != NULL) ? MAP_FILE_FIXED : 0) | |
| (TEST(MODLOAD_REACHABLE, flags) ? MAP_FILE_REACHABLE : 0) | |
| (TEST(MODLOAD_IS_APP, flags) ? MAP_FILE_APP : 0)); |
| if (lib_base == NULL) |
| return NULL; |
| LOG(GLOBAL, LOG_LOADER, 3, |
| "%s: initial reservation " PFX "-" PFX " vs preferred " PFX "\n", __FUNCTION__, |
| lib_base, lib_base + initial_map_size, map_base); |
| if (TEST(MODLOAD_SEPARATE_BSS, flags) && initial_map_size > elf->image_size) |
| elf->image_size = initial_map_size - PAGE_SIZE; |
| else |
| elf->image_size = initial_map_size; |
| lib_end = lib_base + elf->image_size; |
| elf->load_base = lib_base; |
| ASSERT(elf->load_delta == 0 || map_base == NULL); |
| |
| if (map_base != NULL && map_base != lib_base) { |
| /* the mapped memory is not at preferred address, |
| * should be ok if it is still reachable for X64, |
| * which will be checked later. |
| */ |
| LOG(GLOBAL, LOG_LOADER, 1, "%s: module not loaded at preferred address\n", |
| __FUNCTION__); |
| } |
| delta = lib_base - map_base; |
| elf->load_delta = delta; |
| |
| /* walk over the program header to load the individual segments */ |
| last_end = lib_base; |
| for (i = 0; i < elf_hdr->e_phnum; i++) { |
| app_pc seg_base, seg_end, map, file_end; |
| size_t seg_size; |
| ELF_PROGRAM_HEADER_TYPE *prog_hdr = |
| (ELF_PROGRAM_HEADER_TYPE *)((byte *)elf->phdrs + i * elf_hdr->e_phentsize); |
| if (prog_hdr->p_type == PT_LOAD) { |
| bool do_mmap = true; |
| /* XXX i#4737: Our PAGE_SIZE may not match the size on a cross-arch file |
| * that was loaded on another machine. We're also ignoring |
| * prog_hdr->p_align here as it is actually complex to use: some loaders |
| * (notably some kernels) seem to ignore it. These corner cases are left |
| * as unsolved for now. |
| */ |
| seg_base = (app_pc)ALIGN_BACKWARD(prog_hdr->p_vaddr, PAGE_SIZE) + delta; |
| seg_end = |
| (app_pc)ALIGN_FORWARD(prog_hdr->p_vaddr + prog_hdr->p_filesz, PAGE_SIZE) + |
| delta; |
| app_pc mem_end = |
| (app_pc)ALIGN_FORWARD(prog_hdr->p_vaddr + prog_hdr->p_memsz, PAGE_SIZE) + |
| delta; |
| seg_size = seg_end - seg_base; |
| if (seg_base != last_end) { |
| /* XXX: a hole, I reserve this space instead of unmap it */ |
| size_t hole_size = seg_base - last_end; |
| (*prot_func)(last_end, hole_size, MEMPROT_NONE); |
| } |
| seg_prot = module_segment_prot_to_osprot(prog_hdr); |
| pg_offs = ALIGN_BACKWARD(prog_hdr->p_offset, PAGE_SIZE); |
| if (TEST(MODLOAD_SKIP_WRITABLE, flags) && TEST(MEMPROT_WRITE, seg_prot) && |
| mem_end == lib_end) { |
| /* We only actually skip if it's the final segment, to allow |
| * unmapping with a single mmap and not worrying about sthg |
| * else having been unmapped at the end in the meantime. |
| */ |
| do_mmap = false; |
| elf->image_size = last_end - lib_base; |
| } |
| /* XXX: |
| * This function can be called after dynamo_heap_initialized, |
| * and we will use map_file instead of os_map_file. |
| * However, map_file does not allow mmap with overlapped memory, |
| * so we have to unmap the old memory first. |
| * This might be a problem, e.g. |
| * one thread unmaps the memory and before mapping the actual file, |
| * another thread requests memory via mmap takes the memory here, |
| * a racy condition. |
| */ |
| if (seg_size > 0) { /* i#1872: handle empty segments */ |
| if (do_mmap) { |
| (*unmap_func)(seg_base, seg_size); |
| map = (*map_func)( |
| elf->fd, &seg_size, pg_offs, seg_base /* base */, |
| seg_prot | MEMPROT_WRITE /* prot */, |
| MAP_FILE_COPY_ON_WRITE /*writes should not change file*/ | |
| MAP_FILE_IMAGE | |
| /* we don't need MAP_FILE_REACHABLE b/c we're fixed */ |
| MAP_FILE_FIXED); |
| ASSERT(map != NULL); |
| /* fill zeros at extend size */ |
| file_end = (app_pc)prog_hdr->p_vaddr + prog_hdr->p_filesz; |
| if (seg_end > file_end + delta) { |
| /* There is typically one RW PT_LOAD segment for .data and |
| * .bss. If .data ends and .bss starts before filesz bytes, |
| * we need to zero the .bss bytes manually. |
| */ |
| (*memset_func)(file_end + delta, 0, seg_end - (file_end + delta)); |
| } |
| } |
| } |
| seg_end = |
| (app_pc)ALIGN_FORWARD(prog_hdr->p_vaddr + prog_hdr->p_memsz, PAGE_SIZE) + |
| delta; |
| seg_size = seg_end - seg_base; |
| if (seg_size > 0 && do_mmap) |
| (*prot_func)(seg_base, seg_size, seg_prot); |
| last_end = seg_end; |
| } |
| } |
| ASSERT(last_end == lib_end); |
| /* FIXME: recover from map failure rather than relying on asserts. */ |
| |
| return lib_base; |
| } |
| |
| /* Iterate program headers of a mapped ELF image and find the string that |
| * PT_INTERP points to. Typically this comes early in the file and is always |
| * included in PT_LOAD segments, so we safely do this after the initial |
| * mapping. |
| */ |
| const char * |
| elf_loader_find_pt_interp(elf_loader_t *elf) |
| { |
| int i; |
| ELF_HEADER_TYPE *ehdr = elf->ehdr; |
| ELF_PROGRAM_HEADER_TYPE *phdrs = elf->phdrs; |
| |
| ASSERT(elf->load_base != NULL && "call elf_loader_map_phdrs() first"); |
| if (ehdr == NULL || phdrs == NULL || elf->load_base == NULL) |
| return NULL; |
| for (i = 0; i < ehdr->e_phnum; i++) { |
| if (phdrs[i].p_type == PT_INTERP) { |
| return (const char *)(phdrs[i].p_vaddr + elf->load_delta); |
| } |
| } |
| |
| return NULL; |
| } |