1 
2 /*--------------------------------------------------------------------*/
3 /*--- Startup: create initial process image on Darwin              ---*/
4 /*---                                             initimg-darwin.c ---*/
5 /*--------------------------------------------------------------------*/
6 
7 /*
8    This file is part of Valgrind, a dynamic binary instrumentation
9    framework.
10 
11    Copyright (C) 2000-2015 Julian Seward
12       jseward@acm.org
13 
14    This program is free software; you can redistribute it and/or
15    modify it under the terms of the GNU General Public License as
16    published by the Free Software Foundation; either version 2 of the
17    License, or (at your option) any later version.
18 
19    This program is distributed in the hope that it will be useful, but
20    WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22    General Public License for more details.
23 
24    You should have received a copy of the GNU General Public License
25    along with this program; if not, write to the Free Software
26    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
27    02111-1307, USA.
28 
29    The GNU General Public License is contained in the file COPYING.
30 */
31 
32 #if defined(VGO_darwin)
33 
34 #include "pub_core_basics.h"
35 #include "pub_core_vki.h"
36 #include "pub_core_debuglog.h"
37 #include "pub_core_libcbase.h"
38 #include "pub_core_libcassert.h"
39 #include "pub_core_libcfile.h"
40 #include "pub_core_libcproc.h"
41 #include "pub_core_libcprint.h"
42 #include "pub_core_xarray.h"
43 #include "pub_core_clientstate.h"
44 #include "pub_core_aspacemgr.h"
45 #include "pub_core_mallocfree.h"
46 #include "pub_core_machine.h"
47 #include "pub_core_ume.h"
48 #include "pub_core_options.h"
49 #include "pub_core_tooliface.h"       /* VG_TRACK */
50 #include "pub_core_threadstate.h"     /* ThreadArchState */
51 #include "priv_initimg_pathscan.h"
52 #include "pub_core_initimg.h"         /* self */
53 
54 
55 /*====================================================================*/
56 /*=== Loading the client                                           ===*/
57 /*====================================================================*/
58 
59 /* Load the client whose name is VG_(argv_the_exename). */
60 
load_client(ExeInfo * info,Addr * client_ip)61 static void load_client ( /*OUT*/ExeInfo* info,
62                           /*OUT*/Addr*    client_ip)
63 {
64    const HChar* exe_name;
65    Int    ret;
66    SysRes res;
67 
68    vg_assert( VG_(args_the_exename) != NULL);
69    exe_name = ML_(find_executable)( VG_(args_the_exename) );
70 
71    if (!exe_name) {
72       VG_(printf)("valgrind: %s: command not found\n", VG_(args_the_exename));
73       VG_(exit)(127);      // 127 is Posix NOTFOUND
74    }
75 
76    VG_(memset)(info, 0, sizeof(*info));
77    ret = VG_(do_exec)(exe_name, info);
78 
79    // The client was successfully loaded!  Continue.
80 
81    /* Get hold of a file descriptor which refers to the client
82       executable.  This is needed for attaching to GDB. */
83    res = VG_(open)(exe_name, VKI_O_RDONLY, VKI_S_IRUSR);
84    if (!sr_isError(res))
85       VG_(cl_exec_fd) = sr_Res(res);
86 
87    /* Copy necessary bits of 'info' that were filled in */
88    *client_ip  = info->init_ip;
89 }
90 
91 
92 /*====================================================================*/
93 /*=== Setting up the client's environment                          ===*/
94 /*====================================================================*/
95 
96 /* Prepare the client's environment.  This is basically a copy of our
97    environment, except:
98 
99      DYLD_INSERT_LIBRARIES=$VALGRIND_LIB/vgpreload_core-PLATFORM.so:
100                 ($VALGRIND_LIB/vgpreload_TOOL-PLATFORM.so:)?
101                 DYLD_INSERT_LIBRARIES
102 
103    If this is missing, then it is added.
104 
105    Also, remove any binding for VALGRIND_LAUNCHER=.  The client should
106    not be able to see this.
107 
108    Also, add DYLD_SHARED_REGION=avoid, because V doesn't know how
109    to process the dyld shared cache file.
110 
111    Also, change VYLD_* (mangled by launcher) back to DYLD_*.
112 
113    If this needs to handle any more variables it should be hacked
114    into something table driven.  The copy is VG_(malloc)'d space.
115 */
setup_client_env(HChar ** origenv,const HChar * toolname)116 static HChar** setup_client_env ( HChar** origenv, const HChar* toolname)
117 {
118    const HChar* preload_core    = "vgpreload_core";
119    const HChar* ld_preload      = "DYLD_INSERT_LIBRARIES=";
120    const HChar* dyld_cache      = "DYLD_SHARED_REGION=";
121    const HChar* dyld_cache_value= "avoid";
122    const HChar* v_launcher      = VALGRIND_LAUNCHER "=";
123    Int    ld_preload_len  = VG_(strlen)( ld_preload );
124    Int    dyld_cache_len  = VG_(strlen)( dyld_cache );
125    Int    v_launcher_len  = VG_(strlen)( v_launcher );
126    Bool   ld_preload_done = False;
127    Bool   dyld_cache_done = False;
128    Int    vglib_len       = VG_(strlen)(VG_(libdir));
129 
130    HChar** cpp;
131    HChar** ret;
132    HChar*  preload_tool_path;
133    Int     envc, i;
134 
135    /* Alloc space for the vgpreload_core.so path and vgpreload_<tool>.so
136       paths.  We might not need the space for vgpreload_<tool>.so, but it
137       doesn't hurt to over-allocate briefly.  The 16s are just cautious
138       slop. */
139    Int preload_core_path_len = vglib_len + sizeof(preload_core)
140                                          + sizeof(VG_PLATFORM) + 16;
141    Int preload_tool_path_len = vglib_len + VG_(strlen)(toolname)
142                                          + sizeof(VG_PLATFORM) + 16;
143    Int preload_string_len    = preload_core_path_len + preload_tool_path_len;
144    HChar* preload_string     = VG_(malloc)("initimg-darwin.sce.1", preload_string_len);
145 
146    /* Determine if there's a vgpreload_<tool>_<platform>.so file, and setup
147       preload_string. */
148    preload_tool_path = VG_(malloc)("initimg-darwin.sce.2", preload_tool_path_len);
149    VG_(snprintf)(preload_tool_path, preload_tool_path_len,
150                  "%s/vgpreload_%s-%s.so", VG_(libdir), toolname, VG_PLATFORM);
151    if (VG_(access)(preload_tool_path, True/*r*/, False/*w*/, False/*x*/) == 0) {
152       VG_(snprintf)(preload_string, preload_string_len, "%s/%s-%s.so:%s",
153                     VG_(libdir), preload_core, VG_PLATFORM, preload_tool_path);
154    } else {
155       VG_(snprintf)(preload_string, preload_string_len, "%s/%s-%s.so",
156                     VG_(libdir), preload_core, VG_PLATFORM);
157    }
158    VG_(free)(preload_tool_path);
159 
160    VG_(debugLog)(2, "initimg", "preload_string:\n");
161    VG_(debugLog)(2, "initimg", "  \"%s\"\n", preload_string);
162 
163    /* Count the original size of the env */
164    envc = 0;
165    for (cpp = origenv; cpp && *cpp; cpp++)
166       envc++;
167 
168    /* Allocate a new space */
169    ret = VG_(malloc) ("initimg-darwin.sce.3",
170                       sizeof(HChar *) * (envc+2+1)); /* 2 new entries + NULL */
171 
172    /* copy it over */
173    for (cpp = ret; *origenv; )
174       *cpp++ = *origenv++;
175    *cpp = NULL;
176 
177    vg_assert(envc == (cpp - ret));
178 
179    /* Walk over the new environment, mashing as we go */
180    for (cpp = ret; cpp && *cpp; cpp++) {
181       if (VG_(memcmp)(*cpp, ld_preload, ld_preload_len) == 0) {
182          Int len = VG_(strlen)(*cpp) + preload_string_len;
183          HChar *cp = VG_(malloc)("initimg-darwin.sce.4", len);
184 
185          VG_(snprintf)(cp, len, "%s%s:%s",
186                        ld_preload, preload_string, (*cpp)+ld_preload_len);
187 
188          *cpp = cp;
189 
190          ld_preload_done = True;
191       }
192       if (VG_(memcmp)(*cpp, dyld_cache, dyld_cache_len) == 0) {
193          Int len = dyld_cache_len + VG_(strlen)(dyld_cache_value) + 1;
194          HChar *cp = VG_(malloc)("initimg-darwin.sce.4.2", len);
195 
196          VG_(snprintf)(cp, len, "%s%s", dyld_cache, dyld_cache_value);
197 
198          *cpp = cp;
199 
200          ld_preload_done = True;
201       }
202    }
203 
204    /* Add the missing bits */
205    if (!ld_preload_done) {
206       Int len = ld_preload_len + preload_string_len;
207       HChar *cp = VG_(malloc) ("initimg-darwin.sce.5", len);
208 
209       VG_(snprintf)(cp, len, "%s%s", ld_preload, preload_string);
210 
211       ret[envc++] = cp;
212    }
213    if (!dyld_cache_done) {
214       Int len = dyld_cache_len + VG_(strlen)(dyld_cache_value) + 1;
215       HChar *cp = VG_(malloc) ("initimg-darwin.sce.5.2", len);
216 
217       VG_(snprintf)(cp, len, "%s%s", dyld_cache, dyld_cache_value);
218 
219       ret[envc++] = cp;
220    }
221 
222 
223    /* ret[0 .. envc-1] is live now. */
224    /* Find and remove a binding for VALGRIND_LAUNCHER. */
225    for (i = 0; i < envc; i++)
226       if (0 == VG_(memcmp)(ret[i], v_launcher, v_launcher_len))
227          break;
228 
229    if (i < envc) {
230       for (; i < envc-1; i++)
231          ret[i] = ret[i+1];
232       envc--;
233    }
234 
235    /* Change VYLD_ to DYLD */
236    for (i = 0; i < envc; i++) {
237       if (0 == VG_(strncmp)(ret[i], "VYLD_", 5)) {
238          ret[i][0] = 'D';
239       }
240    }
241 
242 
243    VG_(free)(preload_string);
244    ret[envc] = NULL;
245    return ret;
246 }
247 
248 
249 /*====================================================================*/
250 /*=== Setting up the client's stack                                ===*/
251 /*====================================================================*/
252 
253 /* Add a string onto the string table, and return its address */
copy_str(HChar ** tab,const HChar * str)254 static HChar *copy_str(HChar **tab, const HChar *str)
255 {
256    HChar *cp = *tab;
257    HChar *orig = cp;
258 
259    while(*str)
260       *cp++ = *str++;
261    *cp++ = '\0';
262 
263    if (0)
264       VG_(printf)("copied %p \"%s\" len %lld\n", orig, orig, (Long)(cp-orig));
265 
266    *tab = cp;
267 
268    return orig;
269 }
270 
271 
272 /* ----------------------------------------------------------------
273 
274    This sets up the client's initial stack, containing the args,
275    environment and aux vector.
276 
277    The format of the stack on Darwin is:
278 
279    higher address +-----------------+ <- clstack_end
280                   |                 |
281                   : string table    :
282                   |                 |
283                   +-----------------+
284                   | NULL            |
285                   +-----------------+
286                   | executable_path | (first arg to execve())
287                   +-----------------+
288                   | NULL            |
289                   -                 -
290                   | envp            |
291                   +-----------------+
292                   | NULL            |
293                   -                 -
294                   | argv            |
295                   +-----------------+
296                   | argc            |
297                   +-----------------+
298                   | mach_header *   | (dynamic only)
299    lower address  +-----------------+ <- sp
300                   | undefined       |
301                   :                 :
302 
303    Allocate and create the initial client stack.  It is allocated down
304    from clstack_end, which was previously determined by the address
305    space manager.  The returned value is the SP value for the client.
306 
307    ---------------------------------------------------------------- */
308 
309 static
setup_client_stack(void * init_sp,HChar ** orig_envp,const ExeInfo * info,Addr clstack_end,SizeT clstack_max_size,const VexArchInfo * vex_archinfo)310 Addr setup_client_stack( void*  init_sp,
311                          HChar** orig_envp,
312                          const ExeInfo* info,
313                          Addr   clstack_end,
314                          SizeT  clstack_max_size,
315                          const VexArchInfo* vex_archinfo )
316 {
317    HChar **cpp;
318    HChar *strtab;		/* string table */
319    HChar *stringbase;
320    Addr *ptr;
321    unsigned stringsize;		/* total size of strings in bytes */
322    unsigned auxsize;		/* total size of auxv in bytes */
323    Int argc;			/* total argc */
324    Int envc;			/* total number of env vars */
325    unsigned stacksize;		/* total client stack size */
326    Addr client_SP;	        /* client stack base (initial SP) */
327    Addr clstack_start;
328    Int i;
329 
330    vg_assert(VG_IS_PAGE_ALIGNED(clstack_end+1));
331    vg_assert( VG_(args_for_client) );
332 
333    /* ==================== compute sizes ==================== */
334 
335    /* first of all, work out how big the client stack will be */
336    stringsize   = 0;
337    auxsize = 0;
338 
339    /* paste on the extra args if the loader needs them (ie, the #!
340       interpreter and its argument) */
341    argc = 0;
342    if (info->interp_name != NULL) {
343       argc++;
344       stringsize += VG_(strlen)(info->interp_name) + 1;
345    }
346    if (info->interp_args != NULL) {
347       argc++;
348       stringsize += VG_(strlen)(info->interp_args) + 1;
349    }
350 
351    /* now scan the args we're given... */
352    stringsize += VG_(strlen)( VG_(args_the_exename) ) + 1;
353 
354    for (i = 0; i < VG_(sizeXA)( VG_(args_for_client) ); i++) {
355       argc++;
356       stringsize += VG_(strlen)( * (HChar**)
357                                    VG_(indexXA)( VG_(args_for_client), i ))
358                     + 1;
359    }
360 
361    /* ...and the environment */
362    envc = 0;
363    for (cpp = orig_envp; cpp && *cpp; cpp++) {
364       envc++;
365       stringsize += VG_(strlen)(*cpp) + 1;
366    }
367 
368    /* Darwin executable_path + NULL */
369    auxsize += 2 * sizeof(Word);
370    if (info->executable_path) {
371        stringsize += 1 + VG_(strlen)(info->executable_path);
372    }
373 
374    /* Darwin mach_header */
375    if (info->dynamic) auxsize += sizeof(Word);
376 
377    /* OK, now we know how big the client stack is */
378    stacksize =
379       sizeof(Word) +                          /* argc */
380       sizeof(HChar **) +                      /* argc[0] == exename */
381       sizeof(HChar **)*argc +                 /* argv */
382       sizeof(HChar **) +                      /* terminal NULL */
383       sizeof(HChar **)*envc +                 /* envp */
384       sizeof(HChar **) +                      /* terminal NULL */
385       auxsize +                               /* auxv */
386       VG_ROUNDUP(stringsize, sizeof(Word));   /* strings (aligned) */
387 
388    if (0) VG_(printf)("stacksize = %d\n", stacksize);
389 
390    /* client_SP is the client's stack pointer */
391    client_SP = clstack_end + 1 - stacksize;
392    client_SP = VG_ROUNDDN(client_SP, 32); /* make stack 32 byte aligned */
393 
394    /* base of the string table (aligned) */
395    stringbase = strtab = (HChar *)clstack_end
396                          - VG_ROUNDUP(stringsize, sizeof(int));
397 
398    /* The max stack size */
399    clstack_max_size = VG_PGROUNDUP(clstack_max_size);
400 
401    /* Darwin stack is chosen by the ume loader */
402    clstack_start = clstack_end + 1 - clstack_max_size;
403 
404    /* Record stack extent -- needed for stack-change code. */
405    /* GrP fixme really? */
406    VG_(clstk_start_base) = clstack_start;
407    VG_(clstk_end)  = clstack_end;
408 
409    if (0)
410       VG_(printf)("stringsize=%d auxsize=%d stacksize=%d maxsize=0x%x\n"
411                   "clstack_start %p\n"
412                   "clstack_end   %p\n",
413 	          stringsize, auxsize, stacksize, (Int)clstack_max_size,
414                   (void*)clstack_start, (void*)clstack_end);
415 
416    /* ==================== allocate space ==================== */
417 
418    /* Stack was allocated by the ume loader. */
419 
420    /* ==================== create client stack ==================== */
421 
422    ptr = (Addr*)client_SP;
423 
424    /* --- mach_header --- */
425    if (info->dynamic) *ptr++ = info->text;
426 
427    /* --- client argc --- */
428    *ptr++ = (Addr)(argc + 1);
429 
430    /* --- client argv --- */
431    if (info->interp_name) {
432       *ptr++ = (Addr)copy_str(&strtab, info->interp_name);
433       VG_(free)(info->interp_name);
434    }
435    if (info->interp_args) {
436       *ptr++ = (Addr)copy_str(&strtab, info->interp_args);
437       VG_(free)(info->interp_args);
438    }
439 
440    *ptr++ = (Addr)copy_str(&strtab, VG_(args_the_exename));
441 
442    for (i = 0; i < VG_(sizeXA)( VG_(args_for_client) ); i++) {
443       *ptr++ = (Addr)copy_str(
444                        &strtab,
445                        * (HChar**) VG_(indexXA)( VG_(args_for_client), i )
446                      );
447    }
448    *ptr++ = 0;
449 
450    /* --- envp --- */
451    VG_(client_envp) = (HChar **)ptr;
452    for (cpp = orig_envp; cpp && *cpp; ptr++, cpp++)
453       *ptr = (Addr)copy_str(&strtab, *cpp);
454    *ptr++ = 0;
455 
456    /* --- executable_path + NULL --- */
457    if (info->executable_path)
458        *ptr++ = (Addr)copy_str(&strtab, info->executable_path);
459    else
460        *ptr++ = 0;
461    *ptr++ = 0;
462 
463    vg_assert((strtab-stringbase) == stringsize);
464 
465    /* client_SP is pointing at client's argc/argv */
466 
467    if (0) VG_(printf)("startup SP = %#lx\n", client_SP);
468    return client_SP;
469 }
470 
471 
472 /*====================================================================*/
473 /*=== Record system memory regions                                 ===*/
474 /*====================================================================*/
475 
record_system_memory(void)476 static void record_system_memory(void)
477 {
478   /* JRS 2014-Jul-08: this messes up the sync checker, because the
479      information that the kernel gives us doesn't include anything
480      about the commpage mapping.  This functionality has therefore
481      been moved to m_main.c, valgrind_main(), section "Tell the tool
482      about the initial client memory permissions".  See comments there
483      for rationale. */
484    return;
485    /*NOTREACHED*/
486 
487    /* Tell aspacem where the client's kernel commpage is */
488 #if defined(VGA_amd64)
489    /* commpage 0x7fff:ffe00000+ - not in vm_region */
490    // GrP fixme check again
491    VG_(am_notify_client_mmap)(0x7fffffe00000, 0x7ffffffff000-0x7fffffe00000,
492                               VKI_PROT_READ|VKI_PROT_EXEC, 0, -1, 0);
493 
494 #elif defined(VGA_x86)
495    /* commpage 0xfffec000+ - not in vm_region */
496    // GrP fixme check again
497    VG_(am_notify_client_mmap)(0xfffec000, 0xfffff000-0xfffec000,
498                               VKI_PROT_READ|VKI_PROT_EXEC, 0, -1, 0);
499 
500 #else
501 #  error unknown architecture
502 #endif
503 }
504 
505 
506 /*====================================================================*/
507 /*=== TOP-LEVEL: VG_(ii_create_image)                              ===*/
508 /*====================================================================*/
509 
510 /* Create the client's initial memory image. */
VG_(ii_create_image)511 IIFinaliseImageInfo VG_(ii_create_image)( IICreateImageInfo iicii,
512                                           const VexArchInfo* vex_archinfo )
513 {
514    ExeInfo info;
515    VG_(memset)( &info, 0, sizeof(info) );
516 
517    HChar** env = NULL;
518 
519    IIFinaliseImageInfo iifii;
520    VG_(memset)( &iifii, 0, sizeof(iifii) );
521 
522    //--------------------------------------------------------------
523    // Load client executable, finding in $PATH if necessary
524    //   p: get_helprequest_and_toolname()  [for 'exec', 'need_help']
525    //   p: layout_remaining_space          [so there's space]
526    //--------------------------------------------------------------
527    VG_(debugLog)(1, "initimg", "Loading client\n");
528 
529    if (VG_(args_the_exename) == NULL)
530       VG_(err_missing_prog)();
531 
532    load_client(&info, &iifii.initial_client_IP);
533 
534    //--------------------------------------------------------------
535    // Set up client's environment
536    //   p: set-libdir                   [for VG_(libdir)]
537    //   p: get_helprequest_and_toolname [for toolname]
538    //--------------------------------------------------------------
539    VG_(debugLog)(1, "initimg", "Setup client env\n");
540    env = setup_client_env(iicii.envp, iicii.toolname);
541 
542    //--------------------------------------------------------------
543    // Setup client stack, eip, and VG_(client_arg[cv])
544    //   p: load_client()     [for 'info']
545    //   p: fix_environment() [for 'env']
546    //--------------------------------------------------------------
547    iicii.clstack_end = info.stack_end;
548    iifii.clstack_max_size = info.stack_end - info.stack_start + 1;
549 
550    iifii.initial_client_SP =
551        setup_client_stack( iicii.argv - 1, env, &info,
552                            iicii.clstack_end, iifii.clstack_max_size,
553                            vex_archinfo );
554 
555    VG_(free)(env);
556 
557    VG_(debugLog)(2, "initimg",
558                  "Client info: "
559                  "initial_IP=%p initial_SP=%p stack=[%p..%p]\n",
560                  (void*)(iifii.initial_client_IP),
561                  (void*)(iifii.initial_client_SP),
562                  (void*)(info.stack_start),
563                  (void*)(info.stack_end));
564 
565 
566    // Tell aspacem about commpage, etc
567    record_system_memory();
568 
569    return iifii;
570 }
571 
572 
573 /*====================================================================*/
574 /*=== TOP-LEVEL: VG_(ii_finalise_image)                            ===*/
575 /*====================================================================*/
576 
577 /* Just before starting the client, we may need to make final
578    adjustments to its initial image.  Also we need to set up the VEX
579    guest state for thread 1 (the root thread) and copy in essential
580    starting values.  This is handed the IIFinaliseImageInfo created by
581    VG_(ii_create_image).
582 */
VG_(ii_finalise_image)583 void VG_(ii_finalise_image)( IIFinaliseImageInfo iifii )
584 {
585    ThreadArchState* arch = &VG_(threads)[1].arch;
586 
587    /* GrP fixme doesn't handle all registers from LC_THREAD or LC_UNIXTHREAD */
588 
589 #  if defined(VGP_x86_darwin)
590    vg_assert(0 == sizeof(VexGuestX86State) % 16);
591 
592    /* Zero out the initial state, and set up the simulated FPU in a
593       sane way. */
594    LibVEX_GuestX86_initialise(&arch->vex);
595 
596    /* Zero out the shadow areas. */
597    VG_(memset)(&arch->vex_shadow1, 0, sizeof(VexGuestX86State));
598    VG_(memset)(&arch->vex_shadow2, 0, sizeof(VexGuestX86State));
599 
600    /* Put essential stuff into the new state. */
601    arch->vex.guest_ESP = iifii.initial_client_SP;
602    arch->vex.guest_EIP = iifii.initial_client_IP;
603 
604 #  elif defined(VGP_amd64_darwin)
605    vg_assert(0 == sizeof(VexGuestAMD64State) % 16);
606 
607    /* Zero out the initial state, and set up the simulated FPU in a
608       sane way. */
609    LibVEX_GuestAMD64_initialise(&arch->vex);
610 
611    /* Zero out the shadow areas. */
612    VG_(memset)(&arch->vex_shadow1, 0, sizeof(VexGuestAMD64State));
613    VG_(memset)(&arch->vex_shadow2, 0, sizeof(VexGuestAMD64State));
614 
615    /* Put essential stuff into the new state. */
616    arch->vex.guest_RSP = iifii.initial_client_SP;
617    arch->vex.guest_RIP = iifii.initial_client_IP;
618 
619 #  else
620 #    error Unknown platform
621 #  endif
622 
623    /* Tell the tool that we just wrote to the registers. */
624    VG_TRACK( post_reg_write, Vg_CoreStartup, /*tid*/1, /*offset*/0,
625              sizeof(VexGuestArchState));
626 }
627 
628 #endif // defined(VGO_darwin)
629 
630 /*--------------------------------------------------------------------*/
631 /*--- end                                                          ---*/
632 /*--------------------------------------------------------------------*/
633