xref: /qemu/linux-user/linuxload.c (revision e4e5cb4a)
10458df24Sths /* Code for loading Linux executables.  Mostly linux kernel code.  */
2e5fe0c52Spbrook 
3d39594e9SPeter Maydell #include "qemu/osdep.h"
4e5fe0c52Spbrook #include "qemu.h"
53b249d26SPeter Maydell #include "user-internals.h"
67d2c5526SRichard Henderson #include "user-mmap.h"
73ad0a769SPeter Maydell #include "loader.h"
87d2c5526SRichard Henderson #include "qapi/error.h"
9e5fe0c52Spbrook 
10e5fe0c52Spbrook #define NGROUPS 32
11e5fe0c52Spbrook 
12e5fe0c52Spbrook /* ??? This should really be somewhere else.  */
memcpy_to_target(abi_ulong dest,const void * src,unsigned long len)13a46955ffSRichard Henderson abi_long memcpy_to_target(abi_ulong dest, const void *src, unsigned long len)
14e5fe0c52Spbrook {
15e5fe0c52Spbrook     void *host_ptr;
16e5fe0c52Spbrook 
17579a97f7Sbellard     host_ptr = lock_user(VERIFY_WRITE, dest, len, 0);
18a46955ffSRichard Henderson     if (!host_ptr) {
19579a97f7Sbellard         return -TARGET_EFAULT;
20a46955ffSRichard Henderson     }
21e5fe0c52Spbrook     memcpy(host_ptr, src, len);
22e5fe0c52Spbrook     unlock_user(host_ptr, dest, 1);
23579a97f7Sbellard     return 0;
24e5fe0c52Spbrook }
25e5fe0c52Spbrook 
count(char ** vec)26e5fe0c52Spbrook static int count(char **vec)
27e5fe0c52Spbrook {
28e5fe0c52Spbrook     int i;
29e5fe0c52Spbrook 
30e5fe0c52Spbrook     for (i = 0; *vec; i++) {
31e5fe0c52Spbrook         vec++;
32e5fe0c52Spbrook     }
33a46955ffSRichard Henderson     return i;
34e5fe0c52Spbrook }
35e5fe0c52Spbrook 
prepare_binprm(struct linux_binprm * bprm)36e5fe0c52Spbrook static int prepare_binprm(struct linux_binprm *bprm)
37e5fe0c52Spbrook {
38e5fe0c52Spbrook     struct stat st;
39e5fe0c52Spbrook     int mode;
40331c23b5SJuan Quintela     int retval;
41e5fe0c52Spbrook 
42d0b6b793SRichard Henderson     if (fstat(bprm->src.fd, &st) < 0) {
43a46955ffSRichard Henderson         return -errno;
44e5fe0c52Spbrook     }
45e5fe0c52Spbrook 
46e5fe0c52Spbrook     mode = st.st_mode;
47e5fe0c52Spbrook     if (!S_ISREG(mode)) {   /* Must be regular file */
48a46955ffSRichard Henderson         return -EACCES;
49e5fe0c52Spbrook     }
50e5fe0c52Spbrook     if (!(mode & 0111)) {   /* Must have at least one execute bit set */
51a46955ffSRichard Henderson         return -EACCES;
52e5fe0c52Spbrook     }
53e5fe0c52Spbrook 
54e5fe0c52Spbrook     bprm->e_uid = geteuid();
55e5fe0c52Spbrook     bprm->e_gid = getegid();
56e5fe0c52Spbrook 
57e5fe0c52Spbrook     /* Set-uid? */
58e5fe0c52Spbrook     if (mode & S_ISUID) {
59e5fe0c52Spbrook         bprm->e_uid = st.st_uid;
60e5fe0c52Spbrook     }
61e5fe0c52Spbrook 
62e5fe0c52Spbrook     /* Set-gid? */
63e5fe0c52Spbrook     /*
64e5fe0c52Spbrook      * If setgid is set but no group execute bit then this
65e5fe0c52Spbrook      * is a candidate for mandatory locking, not a setgid
66e5fe0c52Spbrook      * executable.
67e5fe0c52Spbrook      */
68e5fe0c52Spbrook     if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
69e5fe0c52Spbrook         bprm->e_gid = st.st_gid;
70e5fe0c52Spbrook     }
71e5fe0c52Spbrook 
72d0b6b793SRichard Henderson     retval = read(bprm->src.fd, bprm->buf, BPRM_BUF_SIZE);
73e5fe0c52Spbrook     if (retval < 0) {
74e5fe0c52Spbrook         perror("prepare_binprm");
75e5fe0c52Spbrook         exit(-1);
76e5fe0c52Spbrook     }
779955ffacSRichard Henderson     if (retval < BPRM_BUF_SIZE) {
789955ffacSRichard Henderson         /* Make sure the rest of the loader won't read garbage.  */
799955ffacSRichard Henderson         memset(bprm->buf + retval, 0, BPRM_BUF_SIZE - retval);
80e5fe0c52Spbrook     }
817d2c5526SRichard Henderson 
827d2c5526SRichard Henderson     bprm->src.cache = bprm->buf;
837d2c5526SRichard Henderson     bprm->src.cache_size = retval;
847d2c5526SRichard Henderson 
859955ffacSRichard Henderson     return retval;
86e5fe0c52Spbrook }
87e5fe0c52Spbrook 
88e5fe0c52Spbrook /* Construct the envp and argv tables on the target stack.  */
loader_build_argptr(int envc,int argc,abi_ulong sp,abi_ulong stringp,int push_ptr)89992f48a0Sblueswir1 abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp,
90992f48a0Sblueswir1                               abi_ulong stringp, int push_ptr)
91e5fe0c52Spbrook {
92*e4e5cb4aSIlya Leoshkevich     TaskState *ts = get_task_state(thread_cpu);
93992f48a0Sblueswir1     int n = sizeof(abi_ulong);
94992f48a0Sblueswir1     abi_ulong envp;
95992f48a0Sblueswir1     abi_ulong argv;
96e5fe0c52Spbrook 
97e5fe0c52Spbrook     sp -= (envc + 1) * n;
98e5fe0c52Spbrook     envp = sp;
99e5fe0c52Spbrook     sp -= (argc + 1) * n;
100e5fe0c52Spbrook     argv = sp;
10160f1c801SRichard Henderson     ts->info->envp = envp;
10260f1c801SRichard Henderson     ts->info->envc = envc;
10360f1c801SRichard Henderson     ts->info->argv = argv;
10460f1c801SRichard Henderson     ts->info->argc = argc;
10560f1c801SRichard Henderson 
106e5fe0c52Spbrook     if (push_ptr) {
1072f619698Sbellard         /* FIXME - handle put_user() failures */
1082f619698Sbellard         sp -= n;
1092f619698Sbellard         put_user_ual(envp, sp);
1102f619698Sbellard         sp -= n;
1112f619698Sbellard         put_user_ual(argv, sp);
112e5fe0c52Spbrook     }
11360f1c801SRichard Henderson 
1142f619698Sbellard     sp -= n;
1152f619698Sbellard     /* FIXME - handle put_user() failures */
1162f619698Sbellard     put_user_ual(argc, sp);
11760f1c801SRichard Henderson 
11860f1c801SRichard Henderson     ts->info->arg_strings = stringp;
119e5fe0c52Spbrook     while (argc-- > 0) {
1202f619698Sbellard         /* FIXME - handle put_user() failures */
1212f619698Sbellard         put_user_ual(stringp, argv);
1222f619698Sbellard         argv += n;
123e5fe0c52Spbrook         stringp += target_strlen(stringp) + 1;
124e5fe0c52Spbrook     }
1252f619698Sbellard     /* FIXME - handle put_user() failures */
1262f619698Sbellard     put_user_ual(0, argv);
12760f1c801SRichard Henderson 
12860f1c801SRichard Henderson     ts->info->env_strings = stringp;
129e5fe0c52Spbrook     while (envc-- > 0) {
1302f619698Sbellard         /* FIXME - handle put_user() failures */
1312f619698Sbellard         put_user_ual(stringp, envp);
1322f619698Sbellard         envp += n;
133e5fe0c52Spbrook         stringp += target_strlen(stringp) + 1;
134e5fe0c52Spbrook     }
1352f619698Sbellard     /* FIXME - handle put_user() failures */
1362f619698Sbellard     put_user_ual(0, envp);
137e5fe0c52Spbrook 
138e5fe0c52Spbrook     return sp;
139e5fe0c52Spbrook }
140e5fe0c52Spbrook 
loader_exec(int fdexec,const char * filename,char ** argv,char ** envp,struct target_pt_regs * regs,struct image_info * infop,struct linux_binprm * bprm)14103cfd8faSLaurent Vivier int loader_exec(int fdexec, const char *filename, char **argv, char **envp,
142edf8e2afSMika Westerberg                 struct target_pt_regs *regs, struct image_info *infop,
143edf8e2afSMika Westerberg                 struct linux_binprm *bprm)
144e5fe0c52Spbrook {
145e5fe0c52Spbrook     int retval;
146e5fe0c52Spbrook 
1477d2c5526SRichard Henderson     bprm->src.fd = fdexec;
148edf8e2afSMika Westerberg     bprm->filename = (char *)filename;
149edf8e2afSMika Westerberg     bprm->argc = count(argv);
150edf8e2afSMika Westerberg     bprm->argv = argv;
151edf8e2afSMika Westerberg     bprm->envc = count(envp);
152edf8e2afSMika Westerberg     bprm->envp = envp;
153e5fe0c52Spbrook 
154edf8e2afSMika Westerberg     retval = prepare_binprm(bprm);
155e5fe0c52Spbrook 
156f485be72SRichard Henderson     if (retval < 4) {
157f485be72SRichard Henderson         return -ENOEXEC;
158f485be72SRichard Henderson     }
159edf8e2afSMika Westerberg     if (bprm->buf[0] == 0x7f
160edf8e2afSMika Westerberg         && bprm->buf[1] == 'E'
161edf8e2afSMika Westerberg         && bprm->buf[2] == 'L'
162edf8e2afSMika Westerberg         && bprm->buf[3] == 'F') {
163f0116c54SWill Newton         retval = load_elf_binary(bprm, infop);
164e5fe0c52Spbrook #if defined(TARGET_HAS_BFLT)
165edf8e2afSMika Westerberg     } else if (bprm->buf[0] == 'b'
166edf8e2afSMika Westerberg                && bprm->buf[1] == 'F'
167edf8e2afSMika Westerberg                && bprm->buf[2] == 'L'
168edf8e2afSMika Westerberg                && bprm->buf[3] == 'T') {
169f0116c54SWill Newton         retval = load_flt_binary(bprm, infop);
170e5fe0c52Spbrook #endif
171e5fe0c52Spbrook     } else {
172885c1d10SPeter Maydell         return -ENOEXEC;
173e5fe0c52Spbrook     }
174f485be72SRichard Henderson     if (retval < 0) {
175f485be72SRichard Henderson         return retval;
176e5fe0c52Spbrook     }
177e5fe0c52Spbrook 
178f485be72SRichard Henderson     /* Success.  Initialize important registers. */
179e5fe0c52Spbrook     do_init_thread(regs, infop);
180f485be72SRichard Henderson     return 0;
181e5fe0c52Spbrook }
1827d2c5526SRichard Henderson 
imgsrc_read(void * dst,off_t offset,size_t len,const ImageSource * img,Error ** errp)1837d2c5526SRichard Henderson bool imgsrc_read(void *dst, off_t offset, size_t len,
1847d2c5526SRichard Henderson                  const ImageSource *img, Error **errp)
1857d2c5526SRichard Henderson {
1867d2c5526SRichard Henderson     ssize_t ret;
1877d2c5526SRichard Henderson 
1887d2c5526SRichard Henderson     if (offset + len <= img->cache_size) {
1897d2c5526SRichard Henderson         memcpy(dst, img->cache + offset, len);
1907d2c5526SRichard Henderson         return true;
1917d2c5526SRichard Henderson     }
1927d2c5526SRichard Henderson 
1937d2c5526SRichard Henderson     if (img->fd < 0) {
1947d2c5526SRichard Henderson         error_setg(errp, "read past end of buffer");
1957d2c5526SRichard Henderson         return false;
1967d2c5526SRichard Henderson     }
1977d2c5526SRichard Henderson 
1987d2c5526SRichard Henderson     ret = pread(img->fd, dst, len, offset);
1997d2c5526SRichard Henderson     if (ret == len) {
2007d2c5526SRichard Henderson         return true;
2017d2c5526SRichard Henderson     }
2027d2c5526SRichard Henderson     if (ret < 0) {
2037d2c5526SRichard Henderson         error_setg_errno(errp, errno, "Error reading file header");
2047d2c5526SRichard Henderson     } else {
2057d2c5526SRichard Henderson         error_setg(errp, "Incomplete read of file header");
2067d2c5526SRichard Henderson     }
2077d2c5526SRichard Henderson     return false;
2087d2c5526SRichard Henderson }
2097d2c5526SRichard Henderson 
imgsrc_read_alloc(off_t offset,size_t len,const ImageSource * img,Error ** errp)2107d2c5526SRichard Henderson void *imgsrc_read_alloc(off_t offset, size_t len,
2117d2c5526SRichard Henderson                         const ImageSource *img, Error **errp)
2127d2c5526SRichard Henderson {
2137d2c5526SRichard Henderson     void *alloc = g_malloc(len);
2147d2c5526SRichard Henderson     bool ok = imgsrc_read(alloc, offset, len, img, errp);
2157d2c5526SRichard Henderson 
2167d2c5526SRichard Henderson     if (!ok) {
2177d2c5526SRichard Henderson         g_free(alloc);
2187d2c5526SRichard Henderson         alloc = NULL;
2197d2c5526SRichard Henderson     }
2207d2c5526SRichard Henderson     return alloc;
2217d2c5526SRichard Henderson }
2227d2c5526SRichard Henderson 
imgsrc_mmap(abi_ulong start,abi_ulong len,int prot,int flags,const ImageSource * src,abi_ulong offset)2237d2c5526SRichard Henderson abi_long imgsrc_mmap(abi_ulong start, abi_ulong len, int prot,
2247d2c5526SRichard Henderson                      int flags, const ImageSource *src, abi_ulong offset)
2257d2c5526SRichard Henderson {
2267d2c5526SRichard Henderson     const int prot_write = PROT_READ | PROT_WRITE;
2277d2c5526SRichard Henderson     abi_long ret;
2287d2c5526SRichard Henderson     void *haddr;
2297d2c5526SRichard Henderson 
2307d2c5526SRichard Henderson     assert(flags == (MAP_PRIVATE | MAP_FIXED));
2317d2c5526SRichard Henderson 
2327d2c5526SRichard Henderson     if (src->fd >= 0) {
2337d2c5526SRichard Henderson         return target_mmap(start, len, prot, flags, src->fd, offset);
2347d2c5526SRichard Henderson     }
2357d2c5526SRichard Henderson 
2367d2c5526SRichard Henderson     /*
2377d2c5526SRichard Henderson      * This case is for the vdso; we don't expect bad images.
2387d2c5526SRichard Henderson      * The mmap may extend beyond the end of the image, especially
2397d2c5526SRichard Henderson      * to the end of the page.  Zero fill.
2407d2c5526SRichard Henderson      */
2417d2c5526SRichard Henderson     assert(offset < src->cache_size);
2427d2c5526SRichard Henderson 
2437d2c5526SRichard Henderson     ret = target_mmap(start, len, prot_write, flags | MAP_ANON, -1, 0);
2447d2c5526SRichard Henderson     if (ret == -1) {
2457d2c5526SRichard Henderson         return ret;
2467d2c5526SRichard Henderson     }
2477d2c5526SRichard Henderson 
2487d2c5526SRichard Henderson     haddr = lock_user(VERIFY_WRITE, start, len, 0);
2497d2c5526SRichard Henderson     assert(haddr != NULL);
2507d2c5526SRichard Henderson     if (offset + len <= src->cache_size) {
2517d2c5526SRichard Henderson         memcpy(haddr, src->cache + offset, len);
2527d2c5526SRichard Henderson     } else {
2537d2c5526SRichard Henderson         size_t rest = src->cache_size - offset;
2547d2c5526SRichard Henderson         memcpy(haddr, src->cache + offset, rest);
2557d2c5526SRichard Henderson         memset(haddr + rest, 0, len - rest);
2567d2c5526SRichard Henderson     }
2577d2c5526SRichard Henderson     unlock_user(haddr, start, len);
2587d2c5526SRichard Henderson 
2597d2c5526SRichard Henderson     if (prot != prot_write) {
2607d2c5526SRichard Henderson         target_mprotect(start, len, prot);
2617d2c5526SRichard Henderson     }
2627d2c5526SRichard Henderson 
2637d2c5526SRichard Henderson     return ret;
2647d2c5526SRichard Henderson }
265