| [1] | 1 | /* | 
|---|
 | 2 |  * elf.c - elf parser: find and map process CODE and DATA segments | 
|---|
 | 3 |  * | 
|---|
 | 4 |  * Authors   Alain Greiner    (2016) | 
|---|
 | 5 |  * | 
|---|
 | 6 |  * Copyright (c) UPMC Sorbonne Universites | 
|---|
 | 7 |  * | 
|---|
 | 8 |  * This file is part of ALMOS-MKH. | 
|---|
 | 9 |  * | 
|---|
 | 10 |  * ALMOS-MKH is free software; you can redistribute it and/or modify it | 
|---|
 | 11 |  * under the terms of the GNU General Public License as published by | 
|---|
 | 12 |  * the Free Software Foundation; version 2.0 of the License. | 
|---|
 | 13 |  * | 
|---|
 | 14 |  * ALMOS-MKH is distributed in the hope that it will be useful, but | 
|---|
 | 15 |  * WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
 | 16 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|---|
 | 17 |  * General Public License for more details. | 
|---|
 | 18 |  * | 
|---|
 | 19 |  * You should have received a copy of the GNU General Public License | 
|---|
 | 20 |  * along with ALMOS-MKH; if not, write to the Free Software Foundation, | 
|---|
 | 21 |  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | 
|---|
 | 22 |  */ | 
|---|
 | 23 |  | 
|---|
| [14] | 24 | #include <kernel_config.h> | 
|---|
| [1] | 25 | #include <hal_types.h> | 
|---|
 | 26 | #include <hal_uspace.h> | 
|---|
 | 27 | #include <printk.h> | 
|---|
 | 28 | #include <process.h> | 
|---|
 | 29 | #include <vseg.h> | 
|---|
 | 30 | #include <kmem.h> | 
|---|
 | 31 | #include <vfs.h> | 
|---|
 | 32 | #include <elf.h> | 
|---|
| [23] | 33 | #include <syscalls.h> | 
|---|
| [1] | 34 |  | 
|---|
 | 35 | /////////////////////////////////////////////////////////////////// | 
|---|
 | 36 | // This static function checks the .elf header. | 
|---|
 | 37 | // - return true if legal header. | 
|---|
 | 38 | // - return false with an error message if illegal header. | 
|---|
 | 39 | /////////////////////////////////////////////////////////////////// | 
|---|
 | 40 | static bool_t elf_isValidHeader(Elf32_Ehdr *header) | 
|---|
 | 41 | { | 
|---|
| [156] | 42 |         if((header->e_ident[EI_CLASS] == ELFCLASS32) | 
|---|
 | 43 |            && (header->e_ident[EI_DATA] == ELFDATA2LSB) | 
|---|
| [1] | 44 |            && (header->e_ident[EI_VERSION] == EV_CURRENT) | 
|---|
 | 45 |            && (header->e_ident[EI_OSABI] == ELFOSABI_NONE) | 
|---|
| [156] | 46 |            && ((header->e_machine == EM_MIPS) || | 
|---|
| [1] | 47 |                (header->e_machine == EM_MIPS_RS3_LE) || | 
|---|
| [157] | 48 |                (header->e_machine == EM_X86_64)) | 
|---|
| [156] | 49 |            && (header->e_type == ET_EXEC)) | 
|---|
 | 50 |                 return true; | 
|---|
 | 51 |  | 
|---|
| [1] | 52 |         if( header->e_ident[EI_CLASS] != ELFCLASS32 ) | 
|---|
| [156] | 53 |                 printk("\n[ERROR] in %s : Elf is not 32-Binary\n", __FUNCTION__ ); | 
|---|
| [1] | 54 |  | 
|---|
 | 55 |         if( header->e_ident[EI_DATA] != ELFDATA2LSB ) | 
|---|
| [156] | 56 |                 printk("\n[ERROR] in %s : Elf is not 2's complement, little endian\n", __FUNCTION__ ); | 
|---|
| [1] | 57 |  | 
|---|
| [156] | 58 |         if( header->e_ident[EI_VERSION] != EV_CURRENT ) | 
|---|
 | 59 |                 printk("\n[ERROR] in %s : Elf is not in Current Version\n", __FUNCTION__); | 
|---|
| [1] | 60 |  | 
|---|
 | 61 |         if( header->e_ident[EI_OSABI] != ELFOSABI_NONE ) | 
|---|
| [156] | 62 |                 printk("\n[ERROR] in %s : Unexpected Elf ABI, need UNIX System V ABI\n", __FUNCTION__ ); | 
|---|
| [1] | 63 |  | 
|---|
| [156] | 64 |         if( (header->e_machine == EM_MIPS) || | 
|---|
 | 65 |             (header->e_machine == EM_MIPS_RS3_LE) || | 
|---|
| [157] | 66 |             (header->e_machine == EM_X86_64) ) | 
|---|
 | 67 |                 printk("\n[ERROR] in %s : unexpected core / accept only MIPS or x86_64\n", __FUNCTION__ ); | 
|---|
| [1] | 68 |  | 
|---|
 | 69 |         if( header->e_type == ET_EXEC ) | 
|---|
| [156] | 70 |                 printk("\n[ERROR] in %s : Elf is not executable binary\n", __FUNCTION__ ); | 
|---|
| [1] | 71 |  | 
|---|
 | 72 |         return false; | 
|---|
 | 73 | } | 
|---|
 | 74 |  | 
|---|
 | 75 | /////////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [156] | 76 | // This function loads the .elf header in the buffer allocated by the caller. | 
|---|
| [204] | 77 | /////////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [1] | 78 | // @ file   : extended pointer on the remote file descriptor. | 
|---|
 | 79 | // @ buffer : pointer on buffer allocated by the caller. | 
|---|
 | 80 | // @ size   : number of bytes to read. | 
|---|
 | 81 | /////////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [204] | 82 | static error_t elf_header_load( xptr_t   file_xp, | 
|---|
| [1] | 83 |                                 void   * buffer, | 
|---|
 | 84 |                                 uint32_t size ) | 
|---|
| [156] | 85 | { | 
|---|
 | 86 |         uint32_t  count; | 
|---|
| [1] | 87 |  | 
|---|
| [156] | 88 |         // load .elf header | 
|---|
| [23] | 89 |         count = vfs_move( true , | 
|---|
| [156] | 90 |                           file_xp, | 
|---|
 | 91 |                           buffer, | 
|---|
 | 92 |                           size ); | 
|---|
| [1] | 93 |  | 
|---|
 | 94 |         if( count != size ) | 
|---|
 | 95 |         { | 
|---|
 | 96 |                 printk("\n[ERROR] in %s : failed to read ELF header\n", __FUNCTION__ ); | 
|---|
 | 97 |                 return -1; | 
|---|
 | 98 |         } | 
|---|
 | 99 |  | 
|---|
| [156] | 100 |         Elf32_Ehdr * header = (Elf32_Ehdr *)buffer; | 
|---|
 | 101 |  | 
|---|
| [1] | 102 |         if( (header->e_ident[EI_MAG0] != ELFMAG0) || | 
|---|
 | 103 |             (header->e_ident[EI_MAG1] != ELFMAG1) || | 
|---|
 | 104 |             (header->e_ident[EI_MAG2] != ELFMAG2) || | 
|---|
 | 105 |             (header->e_ident[EI_MAG3] != ELFMAG3) ) | 
|---|
 | 106 |         { | 
|---|
 | 107 |                 printk("\n[ERROR] in %s : file %s not in ELF format\n", __FUNCTION__ ); | 
|---|
 | 108 |                 return -1; | 
|---|
 | 109 |         } | 
|---|
 | 110 |  | 
|---|
 | 111 |         if( !(elf_isValidHeader( header ) ) ) | 
|---|
 | 112 |         { | 
|---|
 | 113 |                 printk("\n[ERROR] in %s : not supported Elf\n", __FUNCTION__ ); | 
|---|
 | 114 |                 return -1; | 
|---|
 | 115 |         } | 
|---|
 | 116 |         return 0; | 
|---|
 | 117 |  | 
|---|
| [204] | 118 | } // end elf_header_load() | 
|---|
 | 119 |  | 
|---|
| [1] | 120 | /////////////////////////////////////////////////////////////////////////////////////// | 
|---|
 | 121 | // This function registers in the process VMM the CODE and DATA segments. | 
|---|
| [204] | 122 | /////////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [1] | 123 | // @ file      : extended pointer on the remote file descriptor. | 
|---|
 | 124 | // @ segs_base : local pointer on buffer containing the segments descriptors array | 
|---|
 | 125 | // @ segs_nr   : number of segments in segment descriptors array. | 
|---|
 | 126 | // @ process   : local pointer on process descriptor. | 
|---|
 | 127 | /////////////////////////////////////////////////////////////////////////////////////// | 
|---|
 | 128 | static error_t elf_segments_load( xptr_t       file_xp, | 
|---|
| [156] | 129 |                                   void       * segs_base, | 
|---|
| [1] | 130 |                                   uint32_t     nb_segs, | 
|---|
| [156] | 131 |                                   process_t  * process ) | 
|---|
| [1] | 132 | { | 
|---|
 | 133 |         error_t      error; | 
|---|
 | 134 |         uint32_t     index; | 
|---|
 | 135 |         uint32_t     file_size; | 
|---|
| [156] | 136 |         uint32_t     mem_size; | 
|---|
| [1] | 137 |         intptr_t     start; | 
|---|
 | 138 |         uint32_t     type; | 
|---|
 | 139 |         uint32_t     flags; | 
|---|
 | 140 |         uint32_t     offset; | 
|---|
| [156] | 141 |         vseg_t     * vseg; | 
|---|
| [1] | 142 |  | 
|---|
| [156] | 143 |         Elf32_Phdr * seg_ptr = (Elf32_Phdr *)segs_base; | 
|---|
| [1] | 144 |  | 
|---|
| [156] | 145 |         // loop on segments | 
|---|
 | 146 |         for( index = 0 ; index < nb_segs ; index++ , seg_ptr++ ) | 
|---|
 | 147 |         { | 
|---|
 | 148 |                 if( seg_ptr->p_type != PT_LOAD) | 
|---|
 | 149 |                         continue; | 
|---|
 | 150 |  | 
|---|
 | 151 |                 // get segment attributes | 
|---|
| [1] | 152 |                 start     = seg_ptr->p_vaddr; | 
|---|
| [156] | 153 |                 offset    = seg_ptr->p_offset; | 
|---|
 | 154 |                 file_size = seg_ptr->p_filesz; | 
|---|
 | 155 |                 mem_size  = seg_ptr->p_memsz; | 
|---|
 | 156 |                 flags     = seg_ptr->p_flags; | 
|---|
| [1] | 157 |  | 
|---|
| [156] | 158 |                 // check alignment | 
|---|
| [1] | 159 |                 if( start & CONFIG_PPM_PAGE_MASK ) | 
|---|
 | 160 |                 { | 
|---|
 | 161 |                         printk("\n[WARNING] in %s : segment base not aligned = %x\n", | 
|---|
 | 162 |                                __FUNCTION__, start ); | 
|---|
 | 163 |                 } | 
|---|
 | 164 |  | 
|---|
| [156] | 165 |                 // check size | 
|---|
 | 166 |                 if( file_size != mem_size ) | 
|---|
| [1] | 167 |                 { | 
|---|
 | 168 |                         printk("\n[WARNING] in %s : base = %x / mem_size = %x / file_size = %x\n", | 
|---|
 | 169 |                                __FUNCTION__, start , mem_size , file_size); | 
|---|
 | 170 |                 } | 
|---|
 | 171 |  | 
|---|
| [156] | 172 |                 // set seek on segment base in file | 
|---|
| [23] | 173 |                 error = vfs_lseek( file_xp, | 
|---|
| [156] | 174 |                                    offset, | 
|---|
 | 175 |                                    SEEK_SET, | 
|---|
 | 176 |                                    NULL ); | 
|---|
| [1] | 177 |  | 
|---|
 | 178 |                 if( error ) | 
|---|
 | 179 |                 { | 
|---|
 | 180 |                         printk("\n[ERROR] in %s : failed to seek\n", __FUNCTION__ ); | 
|---|
 | 181 |                         return -1; | 
|---|
 | 182 |                 } | 
|---|
 | 183 |  | 
|---|
 | 184 |                 if( flags & PF_X ) // found CODE segment | 
|---|
 | 185 |                 { | 
|---|
| [156] | 186 |                         type                       = VSEG_TYPE_CODE; | 
|---|
| [1] | 187 |                         process->vmm.code_vpn_base = start >> CONFIG_PPM_PAGE_SHIFT; | 
|---|
 | 188 |  | 
|---|
 | 189 |                         elf_dmsg("\n[INFO] %s found CODE vseg / base = %x / size = %x\n", | 
|---|
| [156] | 190 |                                  __FUNCTION__ , start , mem_size ); | 
|---|
| [1] | 191 |                 } | 
|---|
| [156] | 192 |                 else               // found DATA segment | 
|---|
| [1] | 193 |                 { | 
|---|
| [156] | 194 |                         type                       = VSEG_TYPE_DATA; | 
|---|
| [1] | 195 |                         process->vmm.data_vpn_base = start >> CONFIG_PPM_PAGE_SHIFT; | 
|---|
 | 196 |  | 
|---|
 | 197 |                         elf_dmsg("\n[INFO] %s found DATA vseg / base = %x / size = %x\n", | 
|---|
| [156] | 198 |                                  __FUNCTION__, start , mem_size ); | 
|---|
| [1] | 199 |                 } | 
|---|
 | 200 |  | 
|---|
| [156] | 201 |                 // register vseg in VMM | 
|---|
 | 202 |                 vseg = (vseg_t *)vmm_create_vseg( process, | 
|---|
 | 203 |                                                   start, | 
|---|
 | 204 |                                                   mem_size, | 
|---|
 | 205 |                                                   type ); | 
|---|
| [1] | 206 |                 if( vseg == NULL ) | 
|---|
 | 207 |                 { | 
|---|
 | 208 |                         printk("\n[ERROR] in %s : cannot map segment / base = %x / size = %x\n", | 
|---|
 | 209 |                                __FUNCTION__ , start , mem_size ); | 
|---|
 | 210 |                         return -1; | 
|---|
 | 211 |                 } | 
|---|
 | 212 |  | 
|---|
 | 213 |                 vfs_file_count_up( file_xp ); | 
|---|
 | 214 |         } | 
|---|
 | 215 |  | 
|---|
 | 216 |         return 0; | 
|---|
 | 217 |  | 
|---|
| [204] | 218 | } // end elf_segments_load() | 
|---|
 | 219 |  | 
|---|
| [1] | 220 | /////////////////////////////////////////////// | 
|---|
 | 221 | error_t elf_load_process( char      * pathname, | 
|---|
 | 222 |                           process_t * process) | 
|---|
 | 223 | { | 
|---|
 | 224 |         kmem_req_t   req;              // kmem request for program header | 
|---|
 | 225 |         Elf32_Ehdr   header;           // local buffer for .elf header | 
|---|
 | 226 |         void       * segs_base;        // pointer on buffer for segment descriptors array | 
|---|
| [156] | 227 |         uint32_t     segs_size;        // size of buffer for segment descriptors array | 
|---|
| [1] | 228 |         xptr_t       file_xp;          // extended pointer on created file descriptor | 
|---|
| [156] | 229 |         uint32_t     file_id;          // file descriptor index (unused) | 
|---|
 | 230 |         uint32_t     count;            // bytes counter | 
|---|
| [1] | 231 |         error_t      error; | 
|---|
 | 232 |  | 
|---|
| [204] | 233 |     elf_dmsg("\n[INFO] %s : enter for %s\n", __FUNCTION__ , pathname ); | 
|---|
| [1] | 234 |  | 
|---|
| [204] | 235 |     // avoid GCC warning | 
|---|
 | 236 |         file_xp = XPTR_NULL;   | 
|---|
 | 237 |         file_id = -1; | 
|---|
| [1] | 238 |  | 
|---|
| [156] | 239 |         // open file | 
|---|
 | 240 |         error = vfs_open( process->vfs_cwd_xp, | 
|---|
| [204] | 241 |                           pathname, | 
|---|
| [156] | 242 |                           O_RDONLY, | 
|---|
 | 243 |                           0, | 
|---|
 | 244 |                           &file_xp, | 
|---|
 | 245 |                           &file_id ); | 
|---|
| [1] | 246 |         if( error ) | 
|---|
 | 247 |         { | 
|---|
| [204] | 248 |                 printk("\n[ERROR] in %s : failed to open file %s\n", __FUNCTION__ , pathname ); | 
|---|
| [1] | 249 |                 return -1; | 
|---|
 | 250 |         } | 
|---|
 | 251 |  | 
|---|
| [204] | 252 |     elf_dmsg("\n[INFO] %s : file %s open\n", __FUNCTION__ , pathname ); | 
|---|
 | 253 |  | 
|---|
| [156] | 254 |         // load header in local buffer | 
|---|
| [204] | 255 |         error = elf_header_load( file_xp , | 
|---|
| [156] | 256 |                                  &header, | 
|---|
 | 257 |                                  sizeof(Elf32_Ehdr) ); | 
|---|
 | 258 |         if( error ) | 
|---|
 | 259 |         { | 
|---|
| [204] | 260 |                 printk("\n[ERROR] in %s : cannot get header file %s\n", __FUNCTION__ , pathname ); | 
|---|
| [156] | 261 |                 vfs_close( file_xp , file_id ); | 
|---|
 | 262 |                 return -1; | 
|---|
 | 263 |         } | 
|---|
| [1] | 264 |  | 
|---|
| [204] | 265 |         elf_dmsg("\n[INFO] %s : loaded elf header for %s\n", __FUNCTION__ , pathname ); | 
|---|
| [1] | 266 |  | 
|---|
 | 267 |         if( header.e_phnum == 0 ) | 
|---|
 | 268 |         { | 
|---|
 | 269 |                 printk("\n[ERROR] in %s : no segments found\n", __FUNCTION__ ); | 
|---|
| [156] | 270 |                 vfs_close( file_xp , file_id ); | 
|---|
| [1] | 271 |                 return -1; | 
|---|
 | 272 |         } | 
|---|
 | 273 |  | 
|---|
| [156] | 274 |         // compute buffer size for segment descriptors array | 
|---|
| [1] | 275 |         segs_size = sizeof(Elf32_Phdr) * header.e_phnum; | 
|---|
| [156] | 276 |  | 
|---|
 | 277 |         // allocate memory for segment descriptors array | 
|---|
| [1] | 278 |         req.type  = KMEM_GENERIC; | 
|---|
 | 279 |         req.size  = segs_size; | 
|---|
 | 280 |         req.flags = AF_KERNEL; | 
|---|
 | 281 |         segs_base = kmem_alloc( &req ); | 
|---|
 | 282 |  | 
|---|
 | 283 |         if( segs_base == NULL ) | 
|---|
| [156] | 284 |         { | 
|---|
| [1] | 285 |                 printk("\n[ERROR] in %s : no memory for segment descriptors\n", __FUNCTION__ ); | 
|---|
| [156] | 286 |                 vfs_close( file_xp , file_id ); | 
|---|
 | 287 |                 return -1; | 
|---|
 | 288 |         } | 
|---|
| [1] | 289 |  | 
|---|
| [156] | 290 |         // set seek pointer in file descriptor to access segment descriptors array | 
|---|
| [23] | 291 |         error = vfs_lseek( file_xp , header.e_phoff, SEEK_SET , NULL ); | 
|---|
| [1] | 292 |  | 
|---|
 | 293 |         if( error ) | 
|---|
 | 294 |         { | 
|---|
 | 295 |                 printk("\n[ERROR] in %s : cannot seek for descriptors array\n", __FUNCTION__ ); | 
|---|
| [156] | 296 |                 vfs_close( file_xp , file_id ); | 
|---|
 | 297 |                 req.ptr = segs_base; | 
|---|
 | 298 |                 kmem_free( &req ); | 
|---|
| [1] | 299 |                 return -1; | 
|---|
 | 300 |         } | 
|---|
 | 301 |  | 
|---|
| [156] | 302 |         // load seg descriptors array to local buffer | 
|---|
| [23] | 303 |         count = vfs_move( true, | 
|---|
| [156] | 304 |                           file_xp, | 
|---|
 | 305 |                           segs_base, | 
|---|
 | 306 |                           segs_size ); | 
|---|
| [1] | 307 |  | 
|---|
 | 308 |         if( count != segs_size ) | 
|---|
 | 309 |         { | 
|---|
 | 310 |                 printk("\n[ERROR] in %s : cannot read segments descriptors\n", __FUNCTION__ ); | 
|---|
| [156] | 311 |                 vfs_close( file_xp , file_id ); | 
|---|
 | 312 |                 req.ptr = segs_base; | 
|---|
 | 313 |                 kmem_free( &req ); | 
|---|
| [1] | 314 |                 return -1; | 
|---|
 | 315 |         } | 
|---|
 | 316 |  | 
|---|
| [204] | 317 |         elf_dmsg("\n[INFO] %s loaded segments descriptors for %s \n", __FUNCTION__ , pathname ); | 
|---|
| [1] | 318 |  | 
|---|
| [156] | 319 |         // register loadable segments in process VMM | 
|---|
 | 320 |         error = elf_segments_load( file_xp, | 
|---|
 | 321 |                                    segs_base, | 
|---|
 | 322 |                                    header.e_phnum, | 
|---|
 | 323 |                                    process ); | 
|---|
| [1] | 324 |         if( error ) | 
|---|
| [156] | 325 |         { | 
|---|
 | 326 |                 vfs_close( file_xp , file_id ); | 
|---|
 | 327 |                 req.ptr = segs_base; | 
|---|
 | 328 |                 kmem_free( &req ); | 
|---|
| [1] | 329 |                 return -1; | 
|---|
| [156] | 330 |         } | 
|---|
| [1] | 331 |  | 
|---|
| [156] | 332 |         // register process entry point in VMM | 
|---|
| [1] | 333 |         process->vmm.entry_point = (intptr_t)header.e_entry; | 
|---|
 | 334 |  | 
|---|
| [156] | 335 |         // register extended pointer on .elf file descriptor | 
|---|
 | 336 |         process->vfs_bin_xp = file_xp; | 
|---|
| [1] | 337 |  | 
|---|
| [156] | 338 |         // release allocated memory for program header | 
|---|
| [1] | 339 |         req.ptr = segs_base; | 
|---|
 | 340 |         kmem_free(&req); | 
|---|
 | 341 |  | 
|---|
| [156] | 342 |         elf_dmsg("\n[INFO] %s successfully completed / entry point = %x for %s]\n", | 
|---|
| [204] | 343 |                  __FUNCTION__, (uint32_t) header.e_entry , pathname ); | 
|---|
| [1] | 344 |  | 
|---|
 | 345 |         return 0; | 
|---|
 | 346 |  | 
|---|
| [204] | 347 | }  // end elf_load_process() | 
|---|
 | 348 |  | 
|---|