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

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

vfs_user_move() returns an error_t, so don't return a count.

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