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

Last change on this file since 14 was 14, checked in by alain, 4 years ago

Bugs fix.

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