Handle invalid accesses as SIGILL for mips/mipsel userland emulation.
[qemu] / linux-user / linuxload.c
1 /* Code for loading Linux executables.  Mostly linux kenrel code.  */
2
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6 #include <errno.h>
7 #include <unistd.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10
11 #include "qemu.h"
12
13 #define NGROUPS 32
14
15 /* ??? This should really be somewhere else.  */
16 void memcpy_to_target(target_ulong dest, const void *src,
17                       unsigned long len)
18 {
19     void *host_ptr;
20
21     host_ptr = lock_user(dest, len, 0);
22     memcpy(host_ptr, src, len);
23     unlock_user(host_ptr, dest, 1);
24 }
25
26 static int in_group_p(gid_t g)
27 {
28     /* return TRUE if we're in the specified group, FALSE otherwise */
29     int         ngroup;
30     int         i;
31     gid_t       grouplist[NGROUPS];
32
33     ngroup = getgroups(NGROUPS, grouplist);
34     for(i = 0; i < ngroup; i++) {
35         if(grouplist[i] == g) {
36             return 1;
37         }
38     }
39     return 0;
40 }
41
42 static int count(char ** vec)
43 {
44     int         i;
45
46     for(i = 0; *vec; i++) {
47         vec++;
48     }
49
50     return(i);
51 }
52
53 static int prepare_binprm(struct linux_binprm *bprm)
54 {
55     struct stat         st;
56     int mode;
57     int retval, id_change;
58
59     if(fstat(bprm->fd, &st) < 0) {
60         return(-errno);
61     }
62
63     mode = st.st_mode;
64     if(!S_ISREG(mode)) {        /* Must be regular file */
65         return(-EACCES);
66     }
67     if(!(mode & 0111)) {        /* Must have at least one execute bit set */
68         return(-EACCES);
69     }
70
71     bprm->e_uid = geteuid();
72     bprm->e_gid = getegid();
73     id_change = 0;
74
75     /* Set-uid? */
76     if(mode & S_ISUID) {
77         bprm->e_uid = st.st_uid;
78         if(bprm->e_uid != geteuid()) {
79             id_change = 1;
80         }
81     }
82
83     /* Set-gid? */
84     /*
85      * If setgid is set but no group execute bit then this
86      * is a candidate for mandatory locking, not a setgid
87      * executable.
88      */
89     if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
90         bprm->e_gid = st.st_gid;
91         if (!in_group_p(bprm->e_gid)) {
92                 id_change = 1;
93         }
94     }
95
96     memset(bprm->buf, 0, sizeof(bprm->buf));
97     retval = lseek(bprm->fd, 0L, SEEK_SET);
98     if(retval >= 0) {
99         retval = read(bprm->fd, bprm->buf, 128);
100     }
101     if(retval < 0) {
102         perror("prepare_binprm");
103         exit(-1);
104         /* return(-errno); */
105     }
106     else {
107         return(retval);
108     }
109 }
110
111 /* Construct the envp and argv tables on the target stack.  */
112 target_ulong loader_build_argptr(int envc, int argc, target_ulong sp,
113                                  target_ulong stringp, int push_ptr)
114 {
115     int n = sizeof(target_ulong);
116     target_ulong envp;
117     target_ulong argv;
118
119     sp -= (envc + 1) * n;
120     envp = sp;
121     sp -= (argc + 1) * n;
122     argv = sp;
123     if (push_ptr) {
124         sp -= n; tputl(sp, envp);
125         sp -= n; tputl(sp, argv);
126     }
127     sp -= n; tputl(sp, argc);
128
129     while (argc-- > 0) {
130         tputl(argv, stringp); argv += n;
131         stringp += target_strlen(stringp) + 1;
132     }
133     tputl(argv, 0);
134     while (envc-- > 0) {
135         tputl(envp, stringp); envp += n;
136         stringp += target_strlen(stringp) + 1;
137     }
138     tputl(envp, 0);
139
140     return sp;
141 }
142
143 int loader_exec(const char * filename, char ** argv, char ** envp, 
144              struct target_pt_regs * regs, struct image_info *infop)
145 {
146     struct linux_binprm bprm;
147     int retval;
148     int i;
149
150     bprm.p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int);
151     for (i=0 ; i<MAX_ARG_PAGES ; i++)       /* clear page-table */
152             bprm.page[i] = 0;
153     retval = open(filename, O_RDONLY);
154     if (retval < 0)
155         return retval;
156     bprm.fd = retval;
157     bprm.filename = (char *)filename;
158     bprm.argc = count(argv);
159     bprm.argv = argv;
160     bprm.envc = count(envp);
161     bprm.envp = envp;
162
163     retval = prepare_binprm(&bprm);
164
165     infop->host_argv = argv;
166
167     if(retval>=0) {
168         if (bprm.buf[0] == 0x7f
169                 && bprm.buf[1] == 'E'
170                 && bprm.buf[2] == 'L'
171                 && bprm.buf[3] == 'F') {
172             retval = load_elf_binary(&bprm,regs,infop);
173 #if defined(TARGET_HAS_BFLT)
174         } else if (bprm.buf[0] == 'b'
175                 && bprm.buf[1] == 'F'
176                 && bprm.buf[2] == 'L'
177                 && bprm.buf[3] == 'T') {
178             retval = load_flt_binary(&bprm,regs,infop);
179 #endif
180         } else {
181             fprintf(stderr, "Unknown binary format\n");
182             return -1;
183         }
184     }
185     
186     if(retval>=0) {
187         /* success.  Initialize important registers */
188         do_init_thread(regs, infop);
189         return retval;
190     }
191
192     /* Something went wrong, return the inode and free the argument pages*/
193     for (i=0 ; i<MAX_ARG_PAGES ; i++) {
194         free(bprm.page[i]);
195     }
196     return(retval);
197 }