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

Last change on this file since 671 was 671, checked in by alain, 3 years ago

Cosmetic.

File size: 11.5 KB
RevLine 
[1]1/*
2 * elf.c - elf parser: find and map process CODE and DATA segments
3 *
[671]4 * Authors   Alain Greiner    (2016,2017,2018,2019,2020)
[1]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
[14]24#include <kernel_config.h>
[457]25#include <hal_kernel_types.h>
[406]26#include <hal_special.h>
[1]27#include <hal_uspace.h>
28#include <printk.h>
29#include <process.h>
[406]30#include <thread.h>
31#include <mapper.h>
[1]32#include <vseg.h>
33#include <kmem.h>
34#include <vfs.h>
35#include <elf.h>
[23]36#include <syscalls.h>
[1]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///////////////////////////////////////////////////////////////////
[227]43static bool_t elf_isValidHeader(Elf_Ehdr *header)
[1]44{
[671]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) )
[156]52                return true;
53
[270]54        if( header->e_ident[EI_CLASS] != ELFCLASS )
55                printk("\n[ERROR] in %s : Elf is not 32/64-Binary\n", __FUNCTION__ );
[1]56
57        if( header->e_ident[EI_DATA] != ELFDATA2LSB )
[156]58                printk("\n[ERROR] in %s : Elf is not 2's complement, little endian\n", __FUNCTION__ );
[1]59
[156]60        if( header->e_ident[EI_VERSION] != EV_CURRENT )
61                printk("\n[ERROR] in %s : Elf is not in Current Version\n", __FUNCTION__);
[441]62
[270]63        if( (header->e_machine != EM_MIPS) &&
64            (header->e_machine != EM_MIPS_RS3_LE) &&
65            (header->e_machine != EM_X86_64) )
[157]66                printk("\n[ERROR] in %s : unexpected core / accept only MIPS or x86_64\n", __FUNCTION__ );
[1]67
[270]68        if( header->e_type != ET_EXEC )
[156]69                printk("\n[ERROR] in %s : Elf is not executable binary\n", __FUNCTION__ );
[1]70
71        return false;
72}
73
74///////////////////////////////////////////////////////////////////////////////////////
[671]75// This static function loads the .elf header in the buffer allocated by the caller.
[204]76///////////////////////////////////////////////////////////////////////////////////////
[1]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///////////////////////////////////////////////////////////////////////////////////////
[204]81static error_t elf_header_load( xptr_t   file_xp,
[1]82                                void   * buffer,
83                                uint32_t size )
[156]84{
[332]85        error_t   error;
[328]86        xptr_t    buf_xp;
[1]87
[337]88        buf_xp = XPTR( local_cxy , buffer );
[328]89
[156]90        // load .elf header
[332]91        error = vfs_kernel_move( true,     // to_buffer
[328]92                                 file_xp,
93                                 buf_xp,
94                                 size );
[1]95
[332]96        if( error )
[1]97        {
[332]98                printk("\n[ERROR] in %s : cannot read ELF header size : %d\n",
99               __FUNCTION__ , size );
[1]100                return -1;
101        }
102
[227]103        Elf_Ehdr * header = (Elf_Ehdr *)buffer;
[156]104
[1]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        {
[332]110                printk("\n[ERROR] in %s : file not in ELF format\n", __FUNCTION__ );
[1]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
[204]121} // end elf_header_load()
122
[1]123///////////////////////////////////////////////////////////////////////////////////////
124// This function registers in the process VMM the CODE and DATA segments.
[204]125///////////////////////////////////////////////////////////////////////////////////////
[1]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///////////////////////////////////////////////////////////////////////////////////////
[313]131static error_t elf_segments_register( xptr_t       file_xp,
132                                      void       * segs_base,
133                                      uint32_t     nb_segs,
134                                      process_t  * process )
[1]135{
136        uint32_t     index;
[313]137        intptr_t     file_size;
138        intptr_t     mem_size;
139        intptr_t     file_offset;
140        intptr_t     vbase;
[1]141        uint32_t     type;
142        uint32_t     flags;
[156]143        vseg_t     * vseg;
[1]144
[227]145        Elf_Phdr * seg_ptr = (Elf_Phdr *)segs_base;
[1]146
[156]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
[313]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;
[1]159
160                if( flags & PF_X ) // found CODE segment
161                {
[156]162                        type                       = VSEG_TYPE_CODE;
[315]163                        process->vmm.code_vpn_base = vbase >> CONFIG_PPM_PAGE_SHIFT;
[1]164                }
[156]165                else               // found DATA segment
[1]166                {
[156]167                        type                       = VSEG_TYPE_DATA;
[315]168                        process->vmm.data_vpn_base = vbase >> CONFIG_PPM_PAGE_SHIFT;
[1]169                }
170
[407]171        // get .elf file descriptor cluster and local pointer
172        cxy_t        file_cxy = GET_CXY( file_xp );
[671]173        vfs_file_t * file_ptr = GET_PTR( file_xp );
[407]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 ) );
[156]178                // register vseg in VMM
179                vseg = (vseg_t *)vmm_create_vseg( process,
[407]180                                          type,
[313]181                                                  vbase,
[156]182                                                  mem_size,
[407]183                                          file_offset,
184                                          file_size,
185                                          XPTR( file_cxy , mapper_ptr ),
186                                                  local_cxy ); 
[1]187                if( vseg == NULL )
188                {
189                        printk("\n[ERROR] in %s : cannot map segment / base = %x / size = %x\n",
[313]190                               __FUNCTION__ , vbase , mem_size );
[1]191                        return -1;
192                }
193
[438]194#if DEBUG_ELF_LOAD
[625]195uint32_t   cycle = (uint32_t)hal_get_cycles();
196thread_t * this  = CURRENT_THREAD;
[438]197if( DEBUG_ELF_LOAD < cycle )
[625]198printk("\n[%s] thread[%x,%x] found %s vseg / base %x / size %x\n"
[671]199"  file_size %x / file_offset %x / mapper[%x,%x] / cycle %d\n",
[635]200__FUNCTION__ , this->process->pid, this->trdid, 
[625]201vseg_type_str(vseg->type) , vseg->min , vseg->max - vseg->min ,
[671]202vseg->file_size , vseg->file_offset , 
203GET_CXY(vseg->mapper_xp), GET_PTR(vseg->mapper_xp), cycle );
[433]204#endif
205
[1]206        }
207
208        return 0;
209
[313]210} // end elf_segments_register()
[204]211
[457]212//////////////////////////////////////////////
213error_t elf_load_process( xptr_t      file_xp,
214                          process_t * process )
[1]215{
[651]216    uint32_t     new_offset;       // unused, required by vfs_lseek()
[1]217        kmem_req_t   req;              // kmem request for program header
[227]218        Elf_Ehdr     header;           // local buffer for .elf header
[1]219        void       * segs_base;        // pointer on buffer for segment descriptors array
[156]220        uint32_t     segs_size;        // size of buffer for segment descriptors array
[457]221    char         name[CONFIG_VFS_MAX_NAME_LENGTH];
[1]222        error_t      error;
223
[657]224    // get file cluster and local pointer
[671]225    cxy_t        file_cxy = GET_CXY( file_xp );
226    vfs_file_t * file_ptr = GET_PTR( file_xp );
[657]227
[671]228    // get inode pointer
229    vfs_inode_t * inode_ptr = hal_remote_lpt( XPTR( file_cxy , &file_ptr->inode ) );
230 
[657]231    // get file name for error reporting and debug
[671]232    vfs_inode_get_name( XPTR( file_cxy , inode_ptr ) , name );
[457]233   
[438]234#if DEBUG_ELF_LOAD
[603]235uint32_t   cycle = (uint32_t)hal_get_cycles();
236thread_t * this  = CURRENT_THREAD;
[438]237if( DEBUG_ELF_LOAD < cycle )
[603]238printk("\n[%s] thread[%x,%x] enter for <%s> / cycle %d\n",
239__FUNCTION__, this->process->pid, this->trdid, name, cycle );
[433]240#endif
[1]241
[156]242        // load header in local buffer
[204]243        error = elf_header_load( file_xp ,
[156]244                                 &header,
[227]245                                 sizeof(Elf_Ehdr) );
[156]246        if( error )
247        {
[457]248                printk("\n[ERROR] in %s : cannot get header for <%s>\n", __FUNCTION__ , name );
[156]249                return -1;
250        }
[1]251
[438]252#if (DEBUG_ELF_LOAD & 1)
253if( DEBUG_ELF_LOAD < cycle )
[671]254printk("\n[%s] loaded elf header for <%s>\n"
255       "      - size    = %x\n"
256       "      - version = %x\n"
257       "      - entry   = %x\n"
258       "      - machine = %x\n"
259       "      - offset  = %x\n"
260       "      - segnum  = %x\n",
261       __FUNCTION__, name,
262       header.e_ehsize,
263       header.e_version,
264       header.e_entry,
265       header.e_machine,
266       header.e_phoff,
267       header.e_phnum );
[433]268#endif
[1]269
270        if( header.e_phnum == 0 )
271        {
272                printk("\n[ERROR] in %s : no segments found\n", __FUNCTION__ );
273                return -1;
274        }
275
[156]276        // compute buffer size for segment descriptors array
[227]277        segs_size = sizeof(Elf_Phdr) * header.e_phnum;
[156]278
279        // allocate memory for segment descriptors array
[635]280        req.type  = KMEM_KCM;
281        req.order = bits_log2(segs_size);
[1]282        req.flags = AF_KERNEL;
283        segs_base = kmem_alloc( &req );
284
285        if( segs_base == NULL )
[156]286        {
[1]287                printk("\n[ERROR] in %s : no memory for segment descriptors\n", __FUNCTION__ );
[156]288                return -1;
289        }
[1]290
[156]291        // set seek pointer in file descriptor to access segment descriptors array
[651]292        error = vfs_lseek( file_xp , header.e_phoff, SEEK_SET , &new_offset );
[1]293
294        if( error )
295        {
296                printk("\n[ERROR] in %s : cannot seek for descriptors array\n", __FUNCTION__ );
[156]297                req.ptr = segs_base;
298                kmem_free( &req );
[1]299                return -1;
300        }
301
[438]302#if (DEBUG_ELF_LOAD & 1)
303if( DEBUG_ELF_LOAD < cycle )
[603]304printk("\n[%s] segments array allocated for <%s>\n", __FUNCTION__ , name );
[433]305#endif
[367]306
[156]307        // load seg descriptors array to local buffer
[367]308        error = vfs_kernel_move( true,                  // to_buffer
309                                 file_xp,
310                                 XPTR( local_cxy , segs_base ),
311                                 segs_size );
[1]312
[333]313        if( error )
[1]314        {
315                printk("\n[ERROR] in %s : cannot read segments descriptors\n", __FUNCTION__ );
[156]316                req.ptr = segs_base;
317                kmem_free( &req );
[1]318                return -1;
319        }
320
[438]321#if (DEBUG_ELF_LOAD & 1)
322if( DEBUG_ELF_LOAD < cycle )
[603]323printk("\n[%s] loaded segments descriptors for <%s>\n", __FUNCTION__ , name );
[433]324#endif
[1]325
[156]326        // register loadable segments in process VMM
[313]327        error = elf_segments_register( file_xp,
328                                       segs_base,
329                                       header.e_phnum,
330                                       process );
[1]331        if( error )
[156]332        {
333                req.ptr = segs_base;
334                kmem_free( &req );
[1]335                return -1;
[156]336        }
[1]337
[156]338        // register process entry point in VMM
[1]339        process->vmm.entry_point = (intptr_t)header.e_entry;
340
[156]341        // register extended pointer on .elf file descriptor
342        process->vfs_bin_xp = file_xp;
[1]343
[156]344        // release allocated memory for program header
[1]345        req.ptr = segs_base;
346        kmem_free(&req);
347
[438]348#if DEBUG_ELF_LOAD
[433]349cycle = (uint32_t)hal_get_cycles();
[438]350if( DEBUG_ELF_LOAD < cycle )
[603]351printk("\n[%s] thread[%x,%x] exit for <%s> / entry_point %x / cycle %d\n",
352__FUNCTION__, this->process->pid, this->trdid, name, header.e_entry, cycle );
[433]353#endif
[1]354
355        return 0;
356
[204]357}  // end elf_load_process()
358
Note: See TracBrowser for help on using the repository browser.