source: trunk/kernel/libk/elf.c @ 255

Last change on this file since 255 was 227, checked in by max@…, 7 years ago

use Elf64 for 64bit architectures (x86_64)

File size: 10.5 KB
RevLine 
[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///////////////////////////////////////////////////////////////////
[227]40static bool_t elf_isValidHeader(Elf_Ehdr *header)
[1]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]82static 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
[227]100        Elf_Ehdr * header = (Elf_Ehdr *)buffer;
[156]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///////////////////////////////////////////////////////////////////////////////////////
128static 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
[227]143        Elf_Phdr * seg_ptr = (Elf_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///////////////////////////////////////////////
221error_t elf_load_process( char      * pathname,
222                          process_t * process)
223{
224        kmem_req_t   req;              // kmem request for program header
[227]225        Elf_Ehdr     header;           // local buffer for .elf header
[1]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,
[227]257                                 sizeof(Elf_Ehdr) );
[156]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
[227]275        segs_size = sizeof(Elf_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
Note: See TracBrowser for help on using the repository browser.