| 1 | /* Copyright (C) 1999-2020 Free Software Foundation, Inc. | 
|---|
| 2 | This file is part of the GNU C Library. | 
|---|
| 3 | Contributed by Andreas Jaeger <aj@suse.de>, 1999 and | 
|---|
| 4 | Jakub Jelinek <jakub@redhat.com>, 1999. | 
|---|
| 5 |  | 
|---|
| 6 | The GNU C Library is free software; you can redistribute it and/or | 
|---|
| 7 | modify it under the terms of the GNU Lesser General Public | 
|---|
| 8 | License as published by the Free Software Foundation; either | 
|---|
| 9 | version 2.1 of the License, or (at your option) any later version. | 
|---|
| 10 |  | 
|---|
| 11 | The GNU C Library is distributed in the hope that it will be useful, | 
|---|
| 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
| 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|---|
| 14 | Lesser General Public License for more details. | 
|---|
| 15 |  | 
|---|
| 16 | You should have received a copy of the GNU Lesser General Public | 
|---|
| 17 | License along with the GNU C Library; if not, see | 
|---|
| 18 | <https://www.gnu.org/licenses/>.  */ | 
|---|
| 19 |  | 
|---|
| 20 | /* This code is a heavily simplified version of the readelf program | 
|---|
| 21 | that's part of the current binutils development version.  For architectures | 
|---|
| 22 | which need to handle both 32bit and 64bit ELF libraries,  this file is | 
|---|
| 23 | included twice for each arch size.  */ | 
|---|
| 24 |  | 
|---|
| 25 | /* check_ptr checks that a pointer is in the mmaped file and doesn't | 
|---|
| 26 | point outside it.  */ | 
|---|
| 27 | #undef check_ptr | 
|---|
| 28 | #define check_ptr(ptr)						\ | 
|---|
| 29 | do								\ | 
|---|
| 30 | {								\ | 
|---|
| 31 | if ((void *)(ptr) < file_contents				\ | 
|---|
| 32 | || (void *)(ptr) > (file_contents+file_length))		\ | 
|---|
| 33 | {								\ | 
|---|
| 34 | error (0, 0, _("file %s is truncated\n"), file_name);	\ | 
|---|
| 35 | return 1;						\ | 
|---|
| 36 | }								\ | 
|---|
| 37 | }								\ | 
|---|
| 38 | while (0); | 
|---|
| 39 |  | 
|---|
| 40 | /* Returns 0 if everything is ok, != 0 in case of error.  */ | 
|---|
| 41 | int | 
|---|
| 42 | process_elf_file (const char *file_name, const char *lib, int *flag, | 
|---|
| 43 | unsigned int *osversion, char **soname, void *file_contents, | 
|---|
| 44 | size_t file_length) | 
|---|
| 45 | { | 
|---|
| 46 | int i; | 
|---|
| 47 | unsigned int j; | 
|---|
| 48 | unsigned int dynamic_addr; | 
|---|
| 49 | size_t dynamic_size; | 
|---|
| 50 | char *program_interpreter; | 
|---|
| 51 |  | 
|---|
| 52 | ElfW(Ehdr) *; | 
|---|
| 53 | ElfW(Phdr) *, *segment; | 
|---|
| 54 | ElfW(Dyn) *dynamic_segment, *dyn_entry; | 
|---|
| 55 | char *dynamic_strings; | 
|---|
| 56 |  | 
|---|
| 57 | elf_header = (ElfW(Ehdr) *) file_contents; | 
|---|
| 58 | *osversion = 0; | 
|---|
| 59 |  | 
|---|
| 60 | if (elf_header->e_ident [EI_CLASS] != ElfW (CLASS)) | 
|---|
| 61 | { | 
|---|
| 62 | if (opt_verbose) | 
|---|
| 63 | { | 
|---|
| 64 | if (elf_header->e_ident [EI_CLASS] == ELFCLASS32) | 
|---|
| 65 | error (0, 0, _( "%s is a 32 bit ELF file.\n"), file_name); | 
|---|
| 66 | else if (elf_header->e_ident [EI_CLASS] == ELFCLASS64) | 
|---|
| 67 | error (0, 0, _( "%s is a 64 bit ELF file.\n"), file_name); | 
|---|
| 68 | else | 
|---|
| 69 | error (0, 0, _( "Unknown ELFCLASS in file %s.\n"), file_name); | 
|---|
| 70 | } | 
|---|
| 71 | return 1; | 
|---|
| 72 | } | 
|---|
| 73 |  | 
|---|
| 74 | if (elf_header->e_type != ET_DYN) | 
|---|
| 75 | { | 
|---|
| 76 | error (0, 0, _( "%s is not a shared object file (Type: %d).\n"), file_name, | 
|---|
| 77 | elf_header->e_type); | 
|---|
| 78 | return 1; | 
|---|
| 79 | } | 
|---|
| 80 |  | 
|---|
| 81 | /* Get information from elf program header.  */ | 
|---|
| 82 | elf_pheader = (ElfW(Phdr) *) (elf_header->e_phoff + file_contents); | 
|---|
| 83 | check_ptr (elf_pheader); | 
|---|
| 84 |  | 
|---|
| 85 | /* The library is an elf library, now search for soname and | 
|---|
| 86 | libc5/libc6.  */ | 
|---|
| 87 | *flag = FLAG_ELF; | 
|---|
| 88 |  | 
|---|
| 89 | dynamic_addr = 0; | 
|---|
| 90 | dynamic_size = 0; | 
|---|
| 91 | program_interpreter = NULL; | 
|---|
| 92 | for (i = 0, segment = elf_pheader; | 
|---|
| 93 | i < elf_header->e_phnum; i++, segment++) | 
|---|
| 94 | { | 
|---|
| 95 | check_ptr (segment); | 
|---|
| 96 |  | 
|---|
| 97 | switch (segment->p_type) | 
|---|
| 98 | { | 
|---|
| 99 | case PT_DYNAMIC: | 
|---|
| 100 | if (dynamic_addr) | 
|---|
| 101 | error (0, 0, _( "more than one dynamic segment\n")); | 
|---|
| 102 |  | 
|---|
| 103 | dynamic_addr = segment->p_offset; | 
|---|
| 104 | dynamic_size = segment->p_filesz; | 
|---|
| 105 | break; | 
|---|
| 106 |  | 
|---|
| 107 | case PT_INTERP: | 
|---|
| 108 | program_interpreter = (char *) (file_contents + segment->p_offset); | 
|---|
| 109 | check_ptr (program_interpreter); | 
|---|
| 110 |  | 
|---|
| 111 | /* Check if this is enough to classify the binary.  */ | 
|---|
| 112 | for (j = 0; j < sizeof (interpreters) / sizeof (interpreters [0]); | 
|---|
| 113 | ++j) | 
|---|
| 114 | if (strcmp (program_interpreter, interpreters[j].soname) == 0) | 
|---|
| 115 | { | 
|---|
| 116 | *flag = interpreters[j].flag; | 
|---|
| 117 | break; | 
|---|
| 118 | } | 
|---|
| 119 | break; | 
|---|
| 120 |  | 
|---|
| 121 | case PT_NOTE: | 
|---|
| 122 | if (!*osversion && segment->p_filesz >= 32 && segment->p_align >= 4) | 
|---|
| 123 | { | 
|---|
| 124 | ElfW(Word) *abi_note = (ElfW(Word) *) (file_contents | 
|---|
| 125 | + segment->p_offset); | 
|---|
| 126 | ElfW(Addr) size = segment->p_filesz; | 
|---|
| 127 | /* NB: Some PT_NOTE segment may have alignment value of 0 | 
|---|
| 128 | or 1.  gABI specifies that PT_NOTE segments should be | 
|---|
| 129 | aligned to 4 bytes in 32-bit objects and to 8 bytes in | 
|---|
| 130 | 64-bit objects.  As a Linux extension, we also support | 
|---|
| 131 | 4 byte alignment in 64-bit objects.  If p_align is less | 
|---|
| 132 | than 4, we treate alignment as 4 bytes since some note | 
|---|
| 133 | segments have 0 or 1 byte alignment.   */ | 
|---|
| 134 | ElfW(Addr) align = segment->p_align; | 
|---|
| 135 | if (align < 4) | 
|---|
| 136 | align = 4; | 
|---|
| 137 | else if (align != 4 && align != 8) | 
|---|
| 138 | continue; | 
|---|
| 139 |  | 
|---|
| 140 | while (abi_note [0] != 4 || abi_note [1] != 16 | 
|---|
| 141 | || abi_note [2] != 1 | 
|---|
| 142 | || memcmp (abi_note + 3, "GNU", 4) != 0) | 
|---|
| 143 | { | 
|---|
| 144 | ElfW(Addr) note_size | 
|---|
| 145 | = ELF_NOTE_NEXT_OFFSET (abi_note[0], abi_note[1], | 
|---|
| 146 | align); | 
|---|
| 147 |  | 
|---|
| 148 | if (size - 32 < note_size || note_size == 0) | 
|---|
| 149 | { | 
|---|
| 150 | size = 0; | 
|---|
| 151 | break; | 
|---|
| 152 | } | 
|---|
| 153 | size -= note_size; | 
|---|
| 154 | abi_note = (void *) abi_note + note_size; | 
|---|
| 155 | } | 
|---|
| 156 |  | 
|---|
| 157 | if (size == 0) | 
|---|
| 158 | break; | 
|---|
| 159 |  | 
|---|
| 160 | *osversion = ((abi_note [4] << 24) | 
|---|
| 161 | | ((abi_note [5] & 0xff) << 16) | 
|---|
| 162 | | ((abi_note [6] & 0xff) << 8) | 
|---|
| 163 | | (abi_note [7] & 0xff)); | 
|---|
| 164 | } | 
|---|
| 165 | break; | 
|---|
| 166 |  | 
|---|
| 167 | default: | 
|---|
| 168 | break; | 
|---|
| 169 | } | 
|---|
| 170 |  | 
|---|
| 171 | } | 
|---|
| 172 |  | 
|---|
| 173 | /* Now we can read the dynamic sections.  */ | 
|---|
| 174 | if (dynamic_size == 0) | 
|---|
| 175 | return 1; | 
|---|
| 176 |  | 
|---|
| 177 | dynamic_segment = (ElfW(Dyn) *) (file_contents + dynamic_addr); | 
|---|
| 178 | check_ptr (dynamic_segment); | 
|---|
| 179 |  | 
|---|
| 180 | /* Find the string table.  */ | 
|---|
| 181 | dynamic_strings = NULL; | 
|---|
| 182 | for (dyn_entry = dynamic_segment; dyn_entry->d_tag != DT_NULL; | 
|---|
| 183 | ++dyn_entry) | 
|---|
| 184 | { | 
|---|
| 185 | check_ptr (dyn_entry); | 
|---|
| 186 | if (dyn_entry->d_tag == DT_STRTAB) | 
|---|
| 187 | { | 
|---|
| 188 | /* Find the file offset of the segment containing the dynamic | 
|---|
| 189 | string table.  */ | 
|---|
| 190 | ElfW(Off) loadoff = -1; | 
|---|
| 191 | for (i = 0, segment = elf_pheader; | 
|---|
| 192 | i < elf_header->e_phnum; i++, segment++) | 
|---|
| 193 | { | 
|---|
| 194 | if (segment->p_type == PT_LOAD | 
|---|
| 195 | && dyn_entry->d_un.d_val >= segment->p_vaddr | 
|---|
| 196 | && (dyn_entry->d_un.d_val - segment->p_vaddr | 
|---|
| 197 | < segment->p_filesz)) | 
|---|
| 198 | { | 
|---|
| 199 | loadoff = segment->p_vaddr - segment->p_offset; | 
|---|
| 200 | break; | 
|---|
| 201 | } | 
|---|
| 202 | } | 
|---|
| 203 | if (loadoff == (ElfW(Off)) -1) | 
|---|
| 204 | { | 
|---|
| 205 | /* Very strange. */ | 
|---|
| 206 | loadoff = 0; | 
|---|
| 207 | } | 
|---|
| 208 |  | 
|---|
| 209 | dynamic_strings = (char *) (file_contents + dyn_entry->d_un.d_val | 
|---|
| 210 | - loadoff); | 
|---|
| 211 | check_ptr (dynamic_strings); | 
|---|
| 212 | break; | 
|---|
| 213 | } | 
|---|
| 214 | } | 
|---|
| 215 |  | 
|---|
| 216 | if (dynamic_strings == NULL) | 
|---|
| 217 | return 1; | 
|---|
| 218 |  | 
|---|
| 219 | /* Now read the DT_NEEDED and DT_SONAME entries.  */ | 
|---|
| 220 | for (dyn_entry = dynamic_segment; dyn_entry->d_tag != DT_NULL; | 
|---|
| 221 | ++dyn_entry) | 
|---|
| 222 | { | 
|---|
| 223 | if (dyn_entry->d_tag == DT_NEEDED || dyn_entry->d_tag == DT_SONAME) | 
|---|
| 224 | { | 
|---|
| 225 | char *name = dynamic_strings + dyn_entry->d_un.d_val; | 
|---|
| 226 | check_ptr (name); | 
|---|
| 227 |  | 
|---|
| 228 | if (dyn_entry->d_tag == DT_NEEDED) | 
|---|
| 229 | { | 
|---|
| 230 |  | 
|---|
| 231 | if (*flag == FLAG_ELF) | 
|---|
| 232 | { | 
|---|
| 233 | /* Check if this is enough to classify the binary.  */ | 
|---|
| 234 | for (j = 0; | 
|---|
| 235 | j < sizeof (known_libs) / sizeof (known_libs [0]); | 
|---|
| 236 | ++j) | 
|---|
| 237 | if (strcmp (name, known_libs [j].soname) == 0) | 
|---|
| 238 | { | 
|---|
| 239 | *flag = known_libs [j].flag; | 
|---|
| 240 | break; | 
|---|
| 241 | } | 
|---|
| 242 | } | 
|---|
| 243 | } | 
|---|
| 244 |  | 
|---|
| 245 | else if (dyn_entry->d_tag == DT_SONAME) | 
|---|
| 246 | *soname = xstrdup (name); | 
|---|
| 247 |  | 
|---|
| 248 | /* Do we have everything we need?  */ | 
|---|
| 249 | if (*soname && *flag != FLAG_ELF) | 
|---|
| 250 | return 0; | 
|---|
| 251 | } | 
|---|
| 252 | } | 
|---|
| 253 |  | 
|---|
| 254 | return 0; | 
|---|
| 255 | } | 
|---|
| 256 |  | 
|---|