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

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

Fix several bugs in VFS.

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] == 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_X86_64))
49           && (header->e_type == ET_EXEC))
50                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_X86_64) )
67                printk("\n[ERROR] in %s : unexpected core / accept only MIPS or x86_64\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 loads the .elf header in the buffer allocated by the caller.
77///////////////////////////////////////////////////////////////////////////////////////
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///////////////////////////////////////////////////////////////////////////////////////
82static error_t elf_header_load( xptr_t   file_xp,
83                                void   * buffer,
84                                uint32_t size )
85{
86        uint32_t  count;
87
88        // load .elf header
89        count = vfs_move( true,     // to_buffer
90                      false,    // is_user
91                          file_xp,
92                          buffer,
93                          size );
94
95        if( count != size )
96        {
97                printk("\n[ERROR] in %s : cannot read ELF header size : %d / done = %d\n",
98               __FUNCTION__ , size , count );
99                return -1;
100        }
101
102        Elf_Ehdr * header = (Elf_Ehdr *)buffer;
103
104        if( (header->e_ident[EI_MAG0] != ELFMAG0) ||
105            (header->e_ident[EI_MAG1] != ELFMAG1) ||
106            (header->e_ident[EI_MAG2] != ELFMAG2) ||
107            (header->e_ident[EI_MAG3] != ELFMAG3) )
108        {
109                printk("\n[ERROR] in %s : file %s not in ELF format\n", __FUNCTION__ );
110                return -1;
111        }
112
113        if( !(elf_isValidHeader( header ) ) )
114        {
115                printk("\n[ERROR] in %s : not supported Elf\n", __FUNCTION__ );
116                return -1;
117        }
118        return 0;
119
120} // end elf_header_load()
121
122///////////////////////////////////////////////////////////////////////////////////////
123// This function registers in the process VMM the CODE and DATA segments.
124///////////////////////////////////////////////////////////////////////////////////////
125// @ file      : extended pointer on the remote file descriptor.
126// @ segs_base : local pointer on buffer containing the segments descriptors array
127// @ segs_nr   : number of segments in segment descriptors array.
128// @ process   : local pointer on process descriptor.
129///////////////////////////////////////////////////////////////////////////////////////
130static error_t elf_segments_load( xptr_t       file_xp,
131                                  void       * segs_base,
132                                  uint32_t     nb_segs,
133                                  process_t  * process )
134{
135        error_t      error;
136        uint32_t     index;
137        uint32_t     file_size;
138        uint32_t     mem_size;
139        intptr_t     start;
140        uint32_t     type;
141        uint32_t     flags;
142        uint32_t     offset;
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                start     = seg_ptr->p_vaddr;
155                offset    = seg_ptr->p_offset;
156                file_size = seg_ptr->p_filesz;
157                mem_size  = seg_ptr->p_memsz;
158                flags     = seg_ptr->p_flags;
159
160                // check alignment
161                if( start & CONFIG_PPM_PAGE_MASK )
162                {
163                        printk("\n[WARNING] in %s : segment base not aligned = %x\n",
164                               __FUNCTION__, start );
165                }
166
167                // check size
168                if( file_size != mem_size )
169                {
170                        printk("\n[WARNING] in %s : base = %x / mem_size = %x / file_size = %x\n",
171                               __FUNCTION__, start , mem_size , file_size);
172                }
173
174                // set seek on segment base in file
175                error = vfs_lseek( file_xp,
176                                   offset,
177                                   SEEK_SET,
178                                   NULL );
179
180                if( error )
181                {
182                        printk("\n[ERROR] in %s : failed to seek\n", __FUNCTION__ );
183                        return -1;
184                }
185
186                if( flags & PF_X ) // found CODE segment
187                {
188                        type                       = VSEG_TYPE_CODE;
189                        process->vmm.code_vpn_base = start >> CONFIG_PPM_PAGE_SHIFT;
190
191                        elf_dmsg("\n[INFO] %s found CODE vseg / base = %x / size = %x\n",
192                                 __FUNCTION__ , start , mem_size );
193                }
194                else               // found DATA segment
195                {
196                        type                       = VSEG_TYPE_DATA;
197                        process->vmm.data_vpn_base = start >> CONFIG_PPM_PAGE_SHIFT;
198
199                        elf_dmsg("\n[INFO] %s found DATA vseg / base = %x / size = %x\n",
200                                 __FUNCTION__, start , mem_size );
201                }
202
203                // register vseg in VMM
204                vseg = (vseg_t *)vmm_create_vseg( process,
205                                                  start,
206                                                  mem_size,
207                                                  type );
208                if( vseg == NULL )
209                {
210                        printk("\n[ERROR] in %s : cannot map segment / base = %x / size = %x\n",
211                               __FUNCTION__ , start , mem_size );
212                        return -1;
213                }
214
215                vfs_file_count_up( file_xp );
216        }
217
218        return 0;
219
220} // end elf_segments_load()
221
222///////////////////////////////////////////////
223error_t elf_load_process( char      * pathname,
224                          process_t * process)
225{
226        kmem_req_t   req;              // kmem request for program header
227        Elf_Ehdr     header;           // local buffer for .elf header
228        void       * segs_base;        // pointer on buffer for segment descriptors array
229        uint32_t     segs_size;        // size of buffer for segment descriptors array
230        xptr_t       file_xp;          // extended pointer on created file descriptor
231        uint32_t     file_id;          // file descriptor index (unused)
232        uint32_t     count;            // bytes counter
233        error_t      error;
234
235    elf_dmsg("\n[INFO] %s : enters for <%s>\n", __FUNCTION__ , pathname );
236
237    // avoid GCC warning
238        file_xp = XPTR_NULL; 
239        file_id = -1;
240
241        // open file
242        error = vfs_open( process->vfs_cwd_xp,
243                          pathname,
244                          O_RDONLY,
245                          0,
246                          &file_xp,
247                          &file_id );
248        if( error )
249        {
250                printk("\n[ERROR] in %s : failed to open file %s\n", __FUNCTION__ , pathname );
251                return -1;
252        }
253
254    elf_dmsg("\n[INFO] %s : file <%s> open\n", __FUNCTION__ , pathname );
255
256        // load header in local buffer
257        error = elf_header_load( file_xp ,
258                                 &header,
259                                 sizeof(Elf_Ehdr) );
260        if( error )
261        {
262                printk("\n[ERROR] in %s : cannot get header file %s\n", __FUNCTION__ , pathname );
263                vfs_close( file_xp , file_id );
264                return -1;
265        }
266
267        elf_dmsg("\n[INFO] %s : loaded elf header for %s\n", __FUNCTION__ , pathname );
268
269        if( header.e_phnum == 0 )
270        {
271                printk("\n[ERROR] in %s : no segments found\n", __FUNCTION__ );
272                vfs_close( file_xp , file_id );
273                return -1;
274        }
275
276        // compute buffer size for segment descriptors array
277        segs_size = sizeof(Elf_Phdr) * header.e_phnum;
278
279        // allocate memory for segment descriptors array
280        req.type  = KMEM_GENERIC;
281        req.size  = segs_size;
282        req.flags = AF_KERNEL;
283        segs_base = kmem_alloc( &req );
284
285        if( segs_base == NULL )
286        {
287                printk("\n[ERROR] in %s : no memory for segment descriptors\n", __FUNCTION__ );
288                vfs_close( file_xp , file_id );
289                return -1;
290        }
291
292        // set seek pointer in file descriptor to access segment descriptors array
293        error = vfs_lseek( file_xp , header.e_phoff, SEEK_SET , NULL );
294
295        if( error )
296        {
297                printk("\n[ERROR] in %s : cannot seek for descriptors array\n", __FUNCTION__ );
298                vfs_close( file_xp , file_id );
299                req.ptr = segs_base;
300                kmem_free( &req );
301                return -1;
302        }
303
304        // load seg descriptors array to local buffer
305        count = vfs_move( true,       // to_buffer
306                      false,      // is_user
307                          file_xp,
308                          segs_base,
309                          segs_size );
310
311        if( count != segs_size )
312        {
313                printk("\n[ERROR] in %s : cannot read segments descriptors\n", __FUNCTION__ );
314                vfs_close( file_xp , file_id );
315                req.ptr = segs_base;
316                kmem_free( &req );
317                return -1;
318        }
319
320        elf_dmsg("\n[INFO] %s loaded segments descriptors for %s \n", __FUNCTION__ , pathname );
321
322        // register loadable segments in process VMM
323        error = elf_segments_load( file_xp,
324                                   segs_base,
325                                   header.e_phnum,
326                                   process );
327        if( error )
328        {
329                vfs_close( file_xp , file_id );
330                req.ptr = segs_base;
331                kmem_free( &req );
332                return -1;
333        }
334
335        // register process entry point in VMM
336        process->vmm.entry_point = (intptr_t)header.e_entry;
337
338        // register extended pointer on .elf file descriptor
339        process->vfs_bin_xp = file_xp;
340
341        // release allocated memory for program header
342        req.ptr = segs_base;
343        kmem_free(&req);
344
345        elf_dmsg("\n[INFO] %s successfully completed / entry point = %x for %s]\n",
346                 __FUNCTION__, (uint32_t) header.e_entry , pathname );
347
348        return 0;
349
350}  // end elf_load_process()
351
Note: See TracBrowser for help on using the repository browser.