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

Last change on this file since 631 was 625, checked in by alain, 6 years ago

Fix a bug in the vmm_remove_vseg() function: the physical pages
associated to an user DATA vseg were released to the kernel when
the target process descriptor was in the reference cluster.
This physical pages release should be done only when the page
forks counter value is zero.
All other modifications are cosmetic.

File size: 11.0 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_kernel_types.h>
26#include <hal_special.h>
27#include <hal_uspace.h>
28#include <printk.h>
29#include <process.h>
30#include <thread.h>
31#include <mapper.h>
32#include <vseg.h>
33#include <kmem.h>
34#include <vfs.h>
35#include <elf.h>
36#include <syscalls.h>
37
38///////////////////////////////////////////////////////////////////
39// This static function checks the .elf header.
40// - return true if legal header.
41// - return false with an error message if illegal header.
42///////////////////////////////////////////////////////////////////
43static bool_t elf_isValidHeader(Elf_Ehdr *header)
44{
45        if((header->e_ident[EI_CLASS] == ELFCLASS)
46           && (header->e_ident[EI_DATA] == ELFDATA2LSB)
47           && (header->e_ident[EI_VERSION] == EV_CURRENT)
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_machine != EM_MIPS) &&
64            (header->e_machine != EM_MIPS_RS3_LE) &&
65            (header->e_machine != EM_X86_64) )
66                printk("\n[ERROR] in %s : unexpected core / accept only MIPS or x86_64\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 loads the .elf header in the buffer allocated by the caller.
76///////////////////////////////////////////////////////////////////////////////////////
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_load( xptr_t   file_xp,
82                                void   * buffer,
83                                uint32_t size )
84{
85        error_t   error;
86        xptr_t    buf_xp;
87
88        buf_xp = XPTR( local_cxy , buffer );
89
90        // load .elf header
91        error = vfs_kernel_move( true,     // to_buffer
92                                 file_xp,
93                                 buf_xp,
94                                 size );
95
96        if( error )
97        {
98                printk("\n[ERROR] in %s : cannot read ELF header size : %d\n",
99               __FUNCTION__ , size );
100                return -1;
101        }
102
103        Elf_Ehdr * header = (Elf_Ehdr *)buffer;
104
105        if( (header->e_ident[EI_MAG0] != ELFMAG0) ||
106            (header->e_ident[EI_MAG1] != ELFMAG1) ||
107            (header->e_ident[EI_MAG2] != ELFMAG2) ||
108            (header->e_ident[EI_MAG3] != ELFMAG3) )
109        {
110                printk("\n[ERROR] in %s : file not in ELF format\n", __FUNCTION__ );
111                return -1;
112        }
113
114        if( !(elf_isValidHeader( header ) ) )
115        {
116                printk("\n[ERROR] in %s : not supported Elf\n", __FUNCTION__ );
117                return -1;
118        }
119        return 0;
120
121} // end elf_header_load()
122
123///////////////////////////////////////////////////////////////////////////////////////
124// This function registers in the process VMM the CODE and DATA segments.
125///////////////////////////////////////////////////////////////////////////////////////
126// @ file      : extended pointer on the remote file descriptor.
127// @ segs_base : local pointer on buffer containing the segments descriptors array
128// @ segs_nr   : number of segments in segment descriptors array.
129// @ process   : local pointer on process descriptor.
130///////////////////////////////////////////////////////////////////////////////////////
131static error_t elf_segments_register( xptr_t       file_xp,
132                                      void       * segs_base,
133                                      uint32_t     nb_segs,
134                                      process_t  * process )
135{
136        uint32_t     index;
137        intptr_t     file_size;
138        intptr_t     mem_size;
139        intptr_t     file_offset;
140        intptr_t     vbase;
141        uint32_t     type;
142        uint32_t     flags;
143        vseg_t     * vseg;
144
145        Elf_Phdr * seg_ptr = (Elf_Phdr *)segs_base;
146
147        // loop on segments
148        for( index = 0 ; index < nb_segs ; index++ , seg_ptr++ )
149        {
150                if( seg_ptr->p_type != PT_LOAD)
151                        continue;
152
153                // get segment attributes
154                vbase       = seg_ptr->p_vaddr;     // vseg base vaddr
155                mem_size    = seg_ptr->p_memsz;     // actual vseg size
156                file_offset = seg_ptr->p_offset;    // vseg offset in .elf file
157                file_size   = seg_ptr->p_filesz;    // vseg size in .elf file
158                flags       = seg_ptr->p_flags;
159
160                if( flags & PF_X ) // found CODE segment
161                {
162                        type                       = VSEG_TYPE_CODE;
163                        process->vmm.code_vpn_base = vbase >> CONFIG_PPM_PAGE_SHIFT;
164                }
165                else               // found DATA segment
166                {
167                        type                       = VSEG_TYPE_DATA;
168                        process->vmm.data_vpn_base = vbase >> CONFIG_PPM_PAGE_SHIFT;
169                }
170
171        // get .elf file descriptor cluster and local pointer
172        cxy_t        file_cxy = GET_CXY( file_xp );
173        vfs_file_t * file_ptr = (vfs_file_t *)GET_PTR( file_xp );
174
175        // get local pointer on .elf file mapper
176        mapper_t * mapper_ptr = (mapper_t *)hal_remote_lpt( XPTR( file_cxy , 
177                                                                  &file_ptr->mapper ) );
178                // register vseg in VMM
179                vseg = (vseg_t *)vmm_create_vseg( process,
180                                          type,
181                                                  vbase,
182                                                  mem_size,
183                                          file_offset,
184                                          file_size,
185                                          XPTR( file_cxy , mapper_ptr ),
186                                                  local_cxy ); 
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        // update reference counter in file descriptor
195                vfs_file_count_up( file_xp );
196
197#if DEBUG_ELF_LOAD
198uint32_t   cycle = (uint32_t)hal_get_cycles();
199thread_t * this  = CURRENT_THREAD;
200if( DEBUG_ELF_LOAD < cycle )
201printk("\n[%s] thread[%x,%x] found %s vseg / base %x / size %x\n"
202"  file_size %x / file_offset %x / mapper_xp %l / cycle %d\n",
203__FUNCTION__ , this->process_pid, this->trdid, 
204vseg_type_str(vseg->type) , vseg->min , vseg->max - vseg->min ,
205vseg->file_size , vseg->file_offset , vseg->mapper_xp );
206#endif
207
208        }
209
210        return 0;
211
212} // end elf_segments_register()
213
214//////////////////////////////////////////////
215error_t elf_load_process( xptr_t      file_xp,
216                          process_t * process )
217{
218        kmem_req_t   req;              // kmem request for program header
219        Elf_Ehdr     header;           // local buffer for .elf header
220        void       * segs_base;        // pointer on buffer for segment descriptors array
221        uint32_t     segs_size;        // size of buffer for segment descriptors array
222    char         name[CONFIG_VFS_MAX_NAME_LENGTH];
223        error_t      error;
224
225    // get file name for error reporting and debug
226    cxy_t         file_cxy = GET_CXY( file_xp );
227    vfs_file_t  * file_ptr = GET_PTR( file_xp );
228    vfs_inode_t * inode    = hal_remote_lpt( XPTR( file_cxy , &file_ptr->inode ) );
229    vfs_inode_get_name( XPTR( file_cxy , inode ) , name );
230   
231#if DEBUG_ELF_LOAD
232uint32_t   cycle = (uint32_t)hal_get_cycles();
233thread_t * this  = CURRENT_THREAD;
234if( DEBUG_ELF_LOAD < cycle )
235printk("\n[%s] thread[%x,%x] enter for <%s> / cycle %d\n",
236__FUNCTION__, this->process->pid, this->trdid, name, cycle );
237#endif
238
239        // load header in local buffer
240        error = elf_header_load( file_xp ,
241                                 &header,
242                                 sizeof(Elf_Ehdr) );
243        if( error )
244        {
245                printk("\n[ERROR] in %s : cannot get header for <%s>\n", __FUNCTION__ , name );
246                return -1;
247        }
248
249#if (DEBUG_ELF_LOAD & 1)
250if( DEBUG_ELF_LOAD < cycle )
251printk("\n[%s] loaded elf header for <%s>\n", __FUNCTION__ , name );
252#endif
253
254        if( header.e_phnum == 0 )
255        {
256                printk("\n[ERROR] in %s : no segments found\n", __FUNCTION__ );
257                return -1;
258        }
259
260        // compute buffer size for segment descriptors array
261        segs_size = sizeof(Elf_Phdr) * header.e_phnum;
262
263        // allocate memory for segment descriptors array
264        req.type  = KMEM_GENERIC;
265        req.size  = segs_size;
266        req.flags = AF_KERNEL;
267        segs_base = kmem_alloc( &req );
268
269        if( segs_base == NULL )
270        {
271                printk("\n[ERROR] in %s : no memory for segment descriptors\n", __FUNCTION__ );
272                return -1;
273        }
274
275        // set seek pointer in file descriptor to access segment descriptors array
276        error = vfs_lseek( file_xp , header.e_phoff, SEEK_SET , NULL );
277
278        if( error )
279        {
280                printk("\n[ERROR] in %s : cannot seek for descriptors array\n", __FUNCTION__ );
281                req.ptr = segs_base;
282                kmem_free( &req );
283                return -1;
284        }
285
286#if (DEBUG_ELF_LOAD & 1)
287if( DEBUG_ELF_LOAD < cycle )
288printk("\n[%s] segments array allocated for <%s>\n", __FUNCTION__ , name );
289#endif
290
291        // load seg descriptors array to local buffer
292        error = vfs_kernel_move( true,                  // to_buffer
293                                 file_xp,
294                                 XPTR( local_cxy , segs_base ),
295                                 segs_size );
296
297        if( error )
298        {
299                printk("\n[ERROR] in %s : cannot read segments descriptors\n", __FUNCTION__ );
300                req.ptr = segs_base;
301                kmem_free( &req );
302                return -1;
303        }
304
305#if (DEBUG_ELF_LOAD & 1)
306if( DEBUG_ELF_LOAD < cycle )
307printk("\n[%s] loaded segments descriptors for <%s>\n", __FUNCTION__ , name );
308#endif
309
310        // register loadable segments in process VMM
311        error = elf_segments_register( file_xp,
312                                       segs_base,
313                                       header.e_phnum,
314                                       process );
315        if( error )
316        {
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#if DEBUG_ELF_LOAD
333cycle = (uint32_t)hal_get_cycles();
334if( DEBUG_ELF_LOAD < cycle )
335printk("\n[%s] thread[%x,%x] exit for <%s> / entry_point %x / cycle %d\n",
336__FUNCTION__, this->process->pid, this->trdid, name, header.e_entry, cycle );
337#endif
338
339        return 0;
340
341}  // end elf_load_process()
342
Note: See TracBrowser for help on using the repository browser.