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

Last change on this file since 99 was 23, checked in by alain, 7 years ago

Introduce syscalls.

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