/* Copyright (C) 1999, 2000, 2001, 2002, 2007 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Andreas Jaeger , 1999 and Jakub Jelinek , 1999. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ /* This code is a heavily simplified version of the readelf program that's part of the current binutils development version. For architectures which need to handle both 32bit and 64bit ELF libraries, this file is included twice for each arch size. */ /* check_ptr checks that a pointer is in the mmaped file and doesn't point outside it. */ #include "endian_extra.h" /* Work-around for old host that does not have AArch64 defined in elf.h. */ #ifndef EM_AARCH64 #define EM_AARCH64 183 /* ARM AARCH64 */ #endif #undef check_ptr #define check_ptr(ptr) \ do \ { \ if ((void *)(ptr) < file_contents \ || (void *)(ptr) > (file_contents+file_length)) \ { \ error (0, 0, _("file %s is truncated\n"), file_name); \ return 1; \ } \ } \ while (0); int be; static uint16_t read16(uint16_t x, int be) { if (be) return be16toh(x); return le16toh(x); } static uint32_t read32(uint32_t x, int be) { if (be) return be32toh(x); return le32toh(x); } static uint64_t read64(uint64_t x, int be) { if (be) return be64toh(x); return le64toh(x); } /* Returns 0 if everything is ok, != 0 in case of error. */ int process_elf_file32 (const char *file_name, const char *lib, int *flag, unsigned int *osversion, char **soname, void *file_contents, size_t file_length) { int i; unsigned int j; Elf32_Addr loadaddr; unsigned int dynamic_addr; size_t dynamic_size; char *program_interpreter; Elf32_Ehdr *elf_header; Elf32_Phdr *elf_pheader, *segment; Elf32_Dyn *dynamic_segment, *dyn_entry; char *dynamic_strings; elf_header = (Elf32_Ehdr *) file_contents; *osversion = 0; be = (elf_header->e_ident[EI_DATA] == ELFDATA2MSB); if (read16(elf_header->e_type, be) != ET_DYN) { error (0, 0, _("%s is not a shared object file (Type: %d).\n"), file_name, read16(elf_header->e_type, be)); return 1; } /* Get information from elf program header. */ elf_pheader = (Elf32_Phdr *) (read32(elf_header->e_phoff, be) + file_contents); check_ptr (elf_pheader); /* The library is an elf library, now search for soname and libc5/libc6. */ *flag = FLAG_ELF; loadaddr = -1; dynamic_addr = 0; dynamic_size = 0; program_interpreter = NULL; for (i = 0, segment = elf_pheader; i < read16(elf_header->e_phnum, be); i++, segment++) { check_ptr (segment); switch (read32(segment->p_type, be)) { case PT_LOAD: if (loadaddr == (Elf32_Addr) -1) loadaddr = read32(segment->p_vaddr, be) - read32(segment->p_offset, be); break; case PT_DYNAMIC: if (dynamic_addr) error (0, 0, _("more than one dynamic segment\n")); dynamic_addr = read32(segment->p_offset, be); dynamic_size = read32(segment->p_filesz, be); break; case PT_INTERP: program_interpreter = (char *) (file_contents + read32(segment->p_offset, be)); check_ptr (program_interpreter); /* Check if this is enough to classify the binary. */ for (j = 0; j < sizeof (interpreters) / sizeof (interpreters [0]); ++j) if (strcmp (program_interpreter, interpreters[j].soname) == 0) { *flag = interpreters[j].flag; break; } break; case PT_NOTE: if (!*osversion && read32(segment->p_filesz, be) >= 32 && segment->p_align >= 4) { Elf32_Word *abi_note = (Elf32_Word *) (file_contents + read32(segment->p_offset, be)); Elf32_Addr size = read32(segment->p_filesz, be); while (read32(abi_note [0], be) != 4 || read32(abi_note [1], be) != 16 || read32(abi_note [2], be) != 1 || memcmp (abi_note + 3, "GNU", 4) != 0) { #define ROUND(len) (((len) + sizeof (Elf32_Word) - 1) & -sizeof (Elf32_Word)) Elf32_Addr note_size = 3 * sizeof (Elf32_Word) + ROUND (read32(abi_note[0], be)) + ROUND (read32(abi_note[1], be)); if (size - 32 < note_size || note_size == 0) { size = 0; break; } size -= note_size; abi_note = (void *) abi_note + note_size; } if (size == 0) break; *osversion = (read32(abi_note [4], be) << 24) | ((read32(abi_note [5], be) & 0xff) << 16) | ((read32(abi_note [6], be) & 0xff) << 8) | (read32(abi_note [7], be) & 0xff); } break; default: break; } } if (loadaddr == (Elf32_Addr) -1) { /* Very strange. */ loadaddr = 0; } /* Now we can read the dynamic sections. */ if (dynamic_size == 0) return 1; dynamic_segment = (Elf32_Dyn *) (file_contents + dynamic_addr); check_ptr (dynamic_segment); /* Find the string table. */ dynamic_strings = NULL; for (dyn_entry = dynamic_segment; read32(dyn_entry->d_tag, be) != DT_NULL; ++dyn_entry) { check_ptr (dyn_entry); if (read32(dyn_entry->d_tag, be) == DT_STRTAB) { dynamic_strings = (char *) (file_contents + read32(dyn_entry->d_un.d_val, be) - loadaddr); check_ptr (dynamic_strings); break; } } if (dynamic_strings == NULL) return 1; /* Now read the DT_NEEDED and DT_SONAME entries. */ for (dyn_entry = dynamic_segment; read32(dyn_entry->d_tag, be) != DT_NULL; ++dyn_entry) { if (read32(dyn_entry->d_tag, be) == DT_NEEDED || read32(dyn_entry->d_tag, be) == DT_SONAME) { char *name = dynamic_strings + read32(dyn_entry->d_un.d_val, be); check_ptr (name); if (read32(dyn_entry->d_tag, be) == DT_NEEDED) { if (*flag == FLAG_ELF) { /* Check if this is enough to classify the binary. */ for (j = 0; j < sizeof (known_libs) / sizeof (known_libs [0]); ++j) if (strcmp (name, known_libs [j].soname) == 0) { *flag = known_libs [j].flag; break; } } } else if (read32(dyn_entry->d_tag, be) == DT_SONAME) *soname = xstrdup (name); /* Do we have everything we need? */ if (*soname && *flag != FLAG_ELF) return 0; } } /* We reach this point only if the file doesn't contain a DT_SONAME or if we can't classify the library. If it doesn't have a soname, return the name of the library. */ if (*soname == NULL) *soname = xstrdup (lib); return 0; } int process_elf_file64 (const char *file_name, const char *lib, int *flag, unsigned int *osversion, char **soname, void *file_contents, size_t file_length) { int i; unsigned int j; Elf64_Addr loadaddr; Elf64_Addr dynamic_addr; Elf64_Xword dynamic_size; char *program_interpreter; Elf64_Ehdr *elf_header; Elf64_Phdr *elf_pheader, *segment; Elf64_Dyn *dynamic_segment, *dyn_entry; char *dynamic_strings; elf_header = (Elf64_Ehdr *) file_contents; *osversion = 0; be = (elf_header->e_ident[EI_DATA] == ELFDATA2MSB); if (read16(elf_header->e_type, be) != ET_DYN) { error (0, 0, _("%s is not a shared object file (Type: %d).\n"), file_name, read16(elf_header->e_type, be)); return 1; } /* Get information from elf program header. */ elf_pheader = (Elf64_Phdr *) (read64(elf_header->e_phoff, be) + file_contents); check_ptr (elf_pheader); /* The library is an elf library, now search for soname and libc5/libc6. */ *flag = FLAG_ELF; /* Set flags according to information in ELF header to align with target ldconfig */ switch (elf_header->e_machine) { case EM_IA_64: /* Intel 64bit libraries are always libc.so.6+. */ /* see sysdeps/unix/sysv/linux/ia64/readelflib.c */ *flag |= FLAG_IA64_LIB64|FLAG_ELF_LIBC6; break; case EM_X86_64: /* X86-64 64bit libraries are always libc.so.6+. */ /* see sysdeps/unix/sysv/linux/i386/readelflib.c */ *flag |= FLAG_X8664_LIB64|FLAG_ELF_LIBC6; break; case EM_S390: /* S/390 64bit libraries are always libc.so.6+. */ /* see sysdeps/unix/sysv/linux/s390/readelflib.c */ *flag |= FLAG_S390_LIB64|FLAG_ELF_LIBC6; break; case EM_PPC64: /* PowerPC 64bit libraries are always libc.so.6+. */ /* see sysdeps/unix/sysv/linux/powerpc/readelflib.c */ *flag |= FLAG_POWERPC_LIB64|FLAG_ELF_LIBC6; break; case EM_MIPS: case EM_MIPS_RS3_LE: /* n64 libraries are always libc.so.6+. */ /* NOTE: This does not correctly distinguish NAN2008 binaries and is possibly broken */ /* see sysdeps/unix/sysv/linux/mips/readelflib.c */ *flag |= FLAG_MIPS64_LIBN64|FLAG_ELF_LIBC6; break; case EM_AARCH64: /* AArch64 libraries are always libc.so.6+. */ /* see sysdeps/unix/sysv/linux/arm/readelflib.c */ *flag |= FLAG_AARCH64_LIB64|FLAG_ELF_LIBC6; break; default: error(0, 0, "%s is a 64-bit ELF for unknown machine %lx\n", file_name, (long)elf_header->e_machine); break; } loadaddr = -1; dynamic_addr = 0; dynamic_size = 0; program_interpreter = NULL; for (i = 0, segment = elf_pheader; i < read16(elf_header->e_phnum, be); i++, segment++) { check_ptr (segment); switch (read32(segment->p_type, be)) { case PT_LOAD: if (loadaddr == (Elf64_Addr) -1) loadaddr = read64(segment->p_vaddr, be) - read64(segment->p_offset, be); break; case PT_DYNAMIC: if (dynamic_addr) error (0, 0, _("more than one dynamic segment\n")); dynamic_addr = read64(segment->p_offset, be); dynamic_size = read64(segment->p_filesz, be); break; case PT_INTERP: program_interpreter = (char *) (file_contents + read64(segment->p_offset, be)); check_ptr (program_interpreter); /* Check if this is enough to classify the binary. */ for (j = 0; j < sizeof (interpreters) / sizeof (interpreters [0]); ++j) if (strcmp (program_interpreter, interpreters[j].soname) == 0) { *flag = interpreters[j].flag; break; } break; case PT_NOTE: if (!*osversion && read64(segment->p_filesz, be) >= 32 && read64(segment->p_align, be) >= 4) { Elf64_Word *abi_note = (Elf64_Word *) (file_contents + read64(segment->p_offset, be)); Elf64_Xword size = read64(segment->p_filesz, be); while (read32(abi_note [0], be) != 4 || read32(abi_note [1], be) != 16 || read32(abi_note [2], be) != 1 || memcmp (abi_note + 3, "GNU", 4) != 0) { #undef ROUND #define ROUND(len) (((len) + sizeof (Elf64_Word) - 1) & -sizeof (Elf64_Word)) Elf64_Addr note_size = 3 * sizeof (Elf64_Word) + ROUND (read32(abi_note[0], be)) + ROUND (read32(abi_note[1], be)); if (size - 32 < note_size || note_size == 0) { size = 0; break; } size -= note_size; abi_note = (void *) abi_note + note_size; } if (size == 0) break; *osversion = (read32(abi_note [4], be) << 24) | ((read32(abi_note [5], be) & 0xff) << 16) | ((read32(abi_note [6], be) & 0xff) << 8) | (read32(abi_note [7], be) & 0xff); } break; default: break; } } if (loadaddr == (Elf64_Addr) -1) { /* Very strange. */ loadaddr = 0; } /* Now we can read the dynamic sections. */ if (dynamic_size == 0) return 1; dynamic_segment = (Elf64_Dyn *) (file_contents + dynamic_addr); check_ptr (dynamic_segment); /* Find the string table. */ dynamic_strings = NULL; for (dyn_entry = dynamic_segment; read64(dyn_entry->d_tag, be) != DT_NULL; ++dyn_entry) { check_ptr (dyn_entry); if (read64(dyn_entry->d_tag, be) == DT_STRTAB) { dynamic_strings = (char *) (file_contents + read64(dyn_entry->d_un.d_val, be) - loadaddr); check_ptr (dynamic_strings); break; } } if (dynamic_strings == NULL) return 1; /* Now read the DT_NEEDED and DT_SONAME entries. */ for (dyn_entry = dynamic_segment; read64(dyn_entry->d_tag, be) != DT_NULL; ++dyn_entry) { if (read64(dyn_entry->d_tag, be) == DT_NEEDED || read64(dyn_entry->d_tag, be) == DT_SONAME) { char *name = dynamic_strings + read64(dyn_entry->d_un.d_val, be); check_ptr (name); if (read64(dyn_entry->d_tag, be) == DT_NEEDED) { if (*flag == FLAG_ELF) { /* Check if this is enough to classify the binary. */ for (j = 0; j < sizeof (known_libs) / sizeof (known_libs [0]); ++j) if (strcmp (name, known_libs [j].soname) == 0) { *flag = known_libs [j].flag; break; } } } else if (read64(dyn_entry->d_tag, be) == DT_SONAME) *soname = xstrdup (name); /* Do we have everything we need? */ if (*soname && *flag != FLAG_ELF) return 0; } } return 0; } /* Returns 0 if everything is ok, != 0 in case of error. */ int process_elf_file (const char *file_name, const char *lib, int *flag, unsigned int *osversion, char **soname, void *file_contents, size_t file_length) { int i; unsigned int j; ElfW(Addr) loadaddr; unsigned int dynamic_addr; size_t dynamic_size; char *program_interpreter; ElfW(Ehdr) *elf_header; ElfW(Phdr) *elf_pheader, *segment; ElfW(Dyn) *dynamic_segment, *dyn_entry; char *dynamic_strings; elf_header = (ElfW(Ehdr) *) file_contents; *osversion = 0; if (elf_header->e_ident [EI_CLASS] == ELFCLASS32) return process_elf_file32(file_name, lib,flag, osversion, soname, file_contents, file_length); else if (elf_header->e_ident [EI_CLASS] == ELFCLASS64) return process_elf_file64(file_name, lib,flag, osversion, soname, file_contents, file_length); error (0, 0, _("Unknown ELFCLASS in file %s.\n"), file_name); return 1; }