1ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
2ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*--------------------------------------------------------------------*/
3ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*--- User-mode execve() for Mach-O executables      m_ume_macho.c ---*/
4ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*--------------------------------------------------------------------*/
5ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
6ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*
7ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   This file is part of Valgrind, a dynamic binary instrumentation
8ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   framework.
9ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
10436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov   Copyright (C) 2005-2013 Apple Inc.
11ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      Greg Parker  gparker@apple.com
12ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
13ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   This program is free software; you can redistribute it and/or
14ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   modify it under the terms of the GNU General Public License as
15ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   published by the Free Software Foundation; either version 2 of the
16ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   License, or (at your option) any later version.
17ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
18ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   This program is distributed in the hope that it will be useful, but
19ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   WITHOUT ANY WARRANTY; without even the implied warranty of
20ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   General Public License for more details.
22ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
23ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   You should have received a copy of the GNU General Public License
24ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   along with this program; if not, write to the Free Software
25ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   02111-1307, USA.
27ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
28ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   The GNU General Public License is contained in the file COPYING.
29ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown*/
30ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
31ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#if defined(VGO_darwin)
32ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
33ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_core_basics.h"
34ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_core_vki.h"
35ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
36ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_core_aspacemgr.h"     // various mapping fns
37ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_core_debuglog.h"
38ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_core_libcassert.h"    // VG_(exit), vg_assert
39ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_core_libcbase.h"      // VG_(memcmp), etc
40ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_core_libcfile.h"      // VG_(open) et al
41ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_core_libcprint.h"
42ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_core_libcproc.h"
43ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_core_machine.h"       // VG_ELF_CLASS (XXX: which should be moved)
44ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_core_mallocfree.h"    // VG_(malloc), VG_(free)
45ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_core_syscall.h"       // VG_(strerror)
46ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "pub_core_ume.h"           // self
47ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
48ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include "priv_ume.h"
49ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
50ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <mach/mach.h>
51ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
52ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <mach-o/dyld.h>
53ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <mach-o/fat.h>
54ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <mach-o/loader.h>
55ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
56ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#if VG_WORDSIZE == 4
57ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define MAGIC MH_MAGIC
58ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define MACH_HEADER mach_header
59ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define LC_SEGMENT_CMD LC_SEGMENT
60ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define SEGMENT_COMMAND segment_command
61ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define SECTION section
62ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#else
63ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define MAGIC MH_MAGIC_64
64ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define MACH_HEADER mach_header_64
65ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define LC_SEGMENT_CMD LC_SEGMENT_64
66ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define SEGMENT_COMMAND segment_command_64
67ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define SECTION section_64
68ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#endif
69ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
70ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
71436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanovstatic void print(const HChar *str)
72ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
73ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   VG_(printf)("%s", str);
74ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
75ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
76436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanovstatic void check_mmap(SysRes res, Addr base, SizeT len, const HChar* who)
77ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
78ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (sr_isError(res)) {
79663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng      VG_(printf)("valgrind: mmap-FIXED(0x%llx, %lld) failed in UME (%s).\n",
80ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                  (ULong)base, (Long)len, who);
81ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      VG_(exit)(1);
82ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
83ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
84ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
85663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng#if DARWIN_VERS == DARWIN_10_8
86436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanovstatic void check_mmap_float(SysRes res, SizeT len, const HChar* who)
87663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng{
88663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   if (sr_isError(res)) {
89663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng      VG_(printf)("valgrind: mmap-FLOAT(size=%lld) failed in UME (%s).\n",
90663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng                  (Long)len, who);
91663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng      VG_(exit)(1);
92663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   }
93663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng}
94663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng#endif
95ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
96ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic int
97ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownload_thin_file(int fd, vki_off_t offset, vki_off_t size, unsigned long filetype,
98436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov               const HChar *filename,
99ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown               vki_uint8_t **out_stack_start, vki_uint8_t **out_stack_end,
100ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown               vki_uint8_t **out_text, vki_uint8_t **out_entry, vki_uint8_t **out_linker_entry);
101ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
102ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic int
103ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownload_fat_file(int fd, vki_off_t offset, vki_off_t size, unsigned long filetype,
104436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov              const HChar *filename,
105ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown              vki_uint8_t **out_stack_start, vki_uint8_t **out_stack_end,
106ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown              vki_uint8_t **out_text, vki_uint8_t **out_entry, vki_uint8_t **out_linker_entry);
107ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
108ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic int
109ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownload_mach_file(int fd, vki_off_t offset, vki_off_t size, unsigned long filetype,
110436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov               const HChar *filename,
111ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown               vki_uint8_t **out_stack_start, vki_uint8_t **out_stack_end,
112ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown               vki_uint8_t **out_text, vki_uint8_t **out_entry, vki_uint8_t **out_linker_entry);
113ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
114ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
115ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/* Open and map a dylinker file.
116ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Returns 0 on success, -1 on any failure.
117ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   filename must be an absolute path.
118ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   The dylinker's entry point is returned in *out_linker_entry.
119ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown */
120ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic int
121436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanovopen_dylinker(const HChar *filename, vki_uint8_t **out_linker_entry)
122ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
123ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   struct vg_stat sb;
124ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   vki_size_t filesize;
125ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   SysRes res;
126ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   int fd;
127ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   int err;
128ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
129ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (filename[0] != '/') {
130ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("bad executable (dylinker name is not an absolute path)\n");
131ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return -1;
132ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
133ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
134ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   res = VG_(open)(filename, VKI_O_RDONLY, 0);
135ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   fd = sr_Res(res);
136ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (sr_isError(res)) {
137ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("couldn't open dylinker: ");
138ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print(filename);
139ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("\n");
140ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return -1;
141ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
142ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   err = VG_(fstat)(fd, &sb);
143ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (err) {
144ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("couldn't stat dylinker: ");
145ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print(filename);
146ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("\n");
147ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      VG_(close)(fd);
148ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return -1;
149ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
150ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   filesize = sb.size;
151ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
152ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   err = load_mach_file(fd, 0, filesize, MH_DYLINKER, filename,
153ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                        NULL, NULL, NULL, out_linker_entry, NULL);
154ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (err) {
155ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("...while loading dylinker: ");
156ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print(filename);
157ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("\n");
158ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
159ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   VG_(close)(fd);
160ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   return err;
161ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
162ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
163ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
164ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*
165ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Process an LC_SEGMENT command, mapping it into memory if appropriate.
166ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   fd[offset..size) is a Mach-O thin file.
167ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Returns 0 on success, -1 on any failure.
168ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   If this segment contains the executable's Mach headers, their
169ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown     loaded address is returned in *text.
170ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   If this segment is a __UNIXSTACK, its start address is returned in
171ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown     *stack_start.
172ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown*/
173ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic int
174ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownload_segment(int fd, vki_off_t offset, vki_off_t size,
175ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown             vki_uint8_t **text, vki_uint8_t **stack_start,
176ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown             struct SEGMENT_COMMAND *segcmd, const HChar *filename)
177ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
178ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   SysRes res;
179ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Addr addr;
180ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   vki_size_t filesize; // page-aligned
181ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   vki_size_t vmsize;   // page-aligned
182ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   unsigned int prot;
183ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
184ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // GrP fixme mark __UNIXSTACK as SF_STACK
185ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
186ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Don't honour the client's request to map PAGEZERO.  Why not?
187ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Because when the kernel loaded the valgrind tool executable,
188ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // it will have mapped pagezero itself.  So further attempts
189ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // to map it when loading the client are guaranteed to fail.
190ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#if VG_WORDSIZE == 4
191ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (segcmd->vmaddr == 0 && 0 == VG_(strcmp)(segcmd->segname, SEG_PAGEZERO)) {
192ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (segcmd->vmsize != 0x1000) {
193ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         print("bad executable (__PAGEZERO is not 4 KB)\n");
194ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         return -1;
195ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
196ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return 0;
197ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
198ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#endif
199ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#if VG_WORDSIZE == 8
200ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (segcmd->vmaddr == 0 && 0 == VG_(strcmp)(segcmd->segname, SEG_PAGEZERO)) {
201ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (segcmd->vmsize != 0x100000000) {
202ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         print("bad executable (__PAGEZERO is not 4 GB)\n");
203ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         return -1;
204ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
205ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return 0;
206ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
207ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#endif
208ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
209ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Record the segment containing the Mach headers themselves
210ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (segcmd->fileoff == 0  &&  segcmd->filesize != 0) {
211ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (text) *text = (vki_uint8_t *)segcmd->vmaddr;
212ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
213ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
214ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Record the __UNIXSTACK start
215ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (0 == VG_(strcmp)(segcmd->segname, SEG_UNIXSTACK)) {
216ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (stack_start) *stack_start = (vki_uint8_t *)segcmd->vmaddr;
217ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
218ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
219ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Sanity-check the segment
220ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (segcmd->fileoff + segcmd->filesize > size) {
221ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("bad executable (invalid segment command)\n");
222ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return -1;
223ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
224ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (segcmd->vmsize == 0) {
225ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return 0;  // nothing to map - ok
226ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
227ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
228ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Get desired memory protection
229ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // GrP fixme need maxprot too
230ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   prot = (((segcmd->initprot & VM_PROT_READ) ? VKI_PROT_READ : 0) |
231ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown           ((segcmd->initprot & VM_PROT_WRITE) ? VKI_PROT_WRITE : 0) |
232ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown           ((segcmd->initprot & VM_PROT_EXECUTE) ? VKI_PROT_EXEC : 0));
233ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
234ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Map the segment
235ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   filesize = VG_PGROUNDUP(segcmd->filesize);
236ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   vmsize = VG_PGROUNDUP(segcmd->vmsize);
237ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (filesize > 0) {
238ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      addr = (Addr)segcmd->vmaddr;
239ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      VG_(debugLog)(2, "ume", "mmap fixed (file) (%#lx, %lu)\n", addr, filesize);
240ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      res = VG_(am_mmap_named_file_fixed_client)(addr, filesize, prot, fd,
241ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                                                 offset + segcmd->fileoff,
242ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                                                 filename);
243ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      check_mmap(res, addr, filesize, "load_segment1");
244ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
245ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
246ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Zero-fill the remainder of the segment, if any
247ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (segcmd->filesize != filesize) {
248ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // non-page-aligned part
249ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // GrP fixme kernel doesn't do this?
250ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      //bzero(segcmd->filesize+(vki_uint8_t *)addr, filesize-segcmd->filesize);
251ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
252ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (filesize != vmsize) {
253ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // page-aligned part
254ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      SizeT length = vmsize - filesize;
255ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      addr = (Addr)(filesize + segcmd->vmaddr);
256ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      VG_(debugLog)(2, "ume", "mmap fixed (anon) (%#lx, %lu)\n", addr, length);
257ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      res = VG_(am_mmap_anon_fixed_client)(addr, length, prot);
258ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      check_mmap(res, addr, length, "load_segment2");
259ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
260ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
261ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   return 0;
262ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
263ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
264ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
265ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*
266ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Parse a LC_THREAD or LC_UNIXTHREAD command.
267ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Return 0 on success, -1 on any failure.
268ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   The stack address is returned in *stack. If the executable requested
269ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   a non-default stack address, *customstack is set to TRUE. The thread's
270ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   entry point is returned in *entry.
271ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   The stack itself (if any) is not mapped.
272ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Other custom register settings are silently ignored (GrP fixme).
273ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown*/
274ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic int
275ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownload_genericthread(vki_uint8_t **stack_end,
276ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                   int *customstack, vki_uint8_t **entry,
277ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                   struct thread_command *threadcmd)
278ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
279ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   unsigned int flavor;
280ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   unsigned int count;
281ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   unsigned int *p;
282ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   unsigned int left;
283ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
284ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   p = (unsigned int *)(threadcmd + 1);
285ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   left = (threadcmd->cmdsize - sizeof(struct thread_command)) / sizeof(*p);
286ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
287ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   while (left > 0) {
288ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (left < 2) {
289ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         print("bad executable (invalid thread command)\n");
290ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         return -1;
291ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
292ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      flavor = *p++; left--;
293ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      count = *p++; left--;
294ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
295ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (left < count) {
296ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         print("bad executable (invalid thread command 2)\n");
297ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         return -1;
298ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
299ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
300ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#if defined(VGA_x86)
301ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (flavor == i386_THREAD_STATE && count == i386_THREAD_STATE_COUNT) {
302ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         i386_thread_state_t *state = (i386_thread_state_t *)p;
303ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (entry) *entry = (vki_uint8_t *)state->__eip;
304ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (stack_end) *stack_end = (vki_uint8_t *)(state->__esp ? state->__esp : VKI_USRSTACK);
305ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (customstack) *customstack = state->__esp;
306ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         return 0;
307ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
308ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
309ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#elif defined(VGA_amd64)
310ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (flavor == x86_THREAD_STATE64 && count == x86_THREAD_STATE64_COUNT){
311ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         x86_thread_state64_t *state = (x86_thread_state64_t *)p;
312ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (entry) *entry = (vki_uint8_t *)state->__rip;
313ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (stack_end) *stack_end = (vki_uint8_t *)(state->__rsp ? state->__rsp : VKI_USRSTACK64);
314ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (customstack) *customstack = state->__rsp;
315ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         return 0;
316ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
317ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
318ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#else
319ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown# error unknown platform
320ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#endif
321ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      p += count;
322ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      left -= count;
323ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
324ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
325ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   print("bad executable (no arch-compatible thread state)\n");
326ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   return -1;
327ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
328ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
329ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
330ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/* Returns the main stack size on this platform,
331ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   using getrlimit or a fixed size.
332ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   GrP fixme 64-bit? */
333ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic vki_size_t default_stack_size(void)
334ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
335ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   struct vki_rlimit lim;
336ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   int err = VG_(getrlimit)(VKI_RLIMIT_STACK, &lim);
337ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (err) return 8*1024*1024; // 8 MB
338ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   else return lim.rlim_cur;
339ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
340ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
341ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
342ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*
343ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Processes a LC_UNIXTHREAD command.
344ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Returns 0 on success, -1 on any failure.
345ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   The stack is mapped in and returned in *out_stack.
346ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   The thread's entry point is returned in *out_entry.
347ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown*/
348ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic int
349ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownload_unixthread(vki_uint8_t **out_stack_start, vki_uint8_t **out_stack_end,
350ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                vki_uint8_t **out_entry, struct thread_command *threadcmd)
351ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
352ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   int err;
353ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   vki_uint8_t *stack_end;
354ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   int customstack;
355ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
356ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   err = load_genericthread(&stack_end, &customstack, out_entry, threadcmd);
357ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (err) return -1;
358ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
359ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (!stack_end) {
360ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("bad executable (no thread stack)\n");
361ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return -1;
362ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
363ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
364ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (!customstack) {
365ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // Map the stack
366ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      vki_size_t stacksize = VG_PGROUNDUP(default_stack_size());
367ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      vm_address_t stackbase = VG_PGROUNDDN(stack_end-stacksize);
368ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      SysRes res;
369ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
370ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      res = VG_(am_mmap_anon_fixed_client)(stackbase, stacksize, VKI_PROT_READ|VKI_PROT_WRITE|VKI_PROT_EXEC);
371ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      check_mmap(res, stackbase, stacksize, "load_unixthread1");
372ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (out_stack_start) *out_stack_start = (vki_uint8_t *)stackbase;
373ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   } else {
374ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // custom stack - mapped via __UNIXTHREAD segment
375ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
376ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
377ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (out_stack_end) *out_stack_end = stack_end;
378ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
379ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   return 0;
380ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
381ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
382ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
383663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng/* Allocates a stack mapping at a V-chosen address.  Pertains to
384663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   LC_MAIN commands, which seem to have appeared in OSX 10.8.
385663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng
386663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   This is a really nasty hack -- allocates 64M+stack size, then
387663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   deallocates the 64M, to guarantee that the stack is at least 64M
388663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   above zero. */
389663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng#if DARWIN_VERS == DARWIN_10_8
390663860b1408516d02ebfcb3a9999a134e6cfb223Ben Chengstatic int
391663860b1408516d02ebfcb3a9999a134e6cfb223Ben Chenghandle_lcmain ( vki_uint8_t **out_stack_start,
392663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng                vki_uint8_t **out_stack_end,
393663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng                vki_size_t requested_size )
394663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng{
395663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   if (requested_size == 0) {
396663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng      requested_size = default_stack_size();
397663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   }
398663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   requested_size = VG_PGROUNDUP(requested_size);
399663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng
400663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   const vki_size_t HACK = 64 * 1024 * 1024;
401663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   requested_size += HACK;
402663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng
403663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   SysRes res = VG_(am_mmap_anon_float_client)(requested_size,
404663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng                   VKI_PROT_READ|VKI_PROT_WRITE|VKI_PROT_EXEC);
405663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   check_mmap_float(res, requested_size, "handle_lcmain");
406663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   vg_assert(!sr_isError(res));
407663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   *out_stack_start = (vki_uint8_t*)sr_Res(res);
408663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   *out_stack_end   = *out_stack_start + requested_size;
409663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng
410663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   Bool need_discard = False;
411663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   res = VG_(am_munmap_client)(&need_discard, (Addr)*out_stack_start, HACK);
412663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   if (sr_isError(res)) return -1;
413663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   vg_assert(!need_discard); // True == wtf?
414663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng
415663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   *out_stack_start += HACK;
416663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng
417663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   return 0;
418663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng}
419663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng#endif /* DARWIN_VERS == DARWIN_10_8 */
420663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng
421663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng
422663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng
423ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*
424ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Processes an LC_LOAD_DYLINKER command.
425ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   Returns 0 on success, -1 on any error.
426ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   The linker itself is mapped into memory.
427ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   The linker's entry point is returned in *linker_entry.
428ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown*/
429ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic int
430ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownload_dylinker(vki_uint8_t **linker_entry, struct dylinker_command *dycmd)
431ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
432436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov   const HChar *name;
433ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
434ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (dycmd->name.offset >= dycmd->cmdsize) {
435ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("bad executable (invalid dylinker command)\n");
436ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return -1;
437ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
438ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
439436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov   name = dycmd->name.offset + (HChar *)dycmd;
440ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
441ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // GrP fixme assumes name is terminated somewhere
442ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   return open_dylinker(name, linker_entry);
443ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
444ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
445ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
446ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*
447ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    Process an LC_THREAD command.
448ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    Returns 0 on success, -1 on any failure.
449ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown    The thread's entry point is returned in *out_entry.
450ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown*/
451ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic int
452ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownload_thread(vki_uint8_t **out_entry, struct thread_command *threadcmd)
453ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
454ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   int customstack;
455ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   int err;
456ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
457ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   err = load_genericthread(NULL, &customstack, out_entry, threadcmd);
458ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (err) return -1;
459ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (customstack) {
460ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("bad executable (stackless thread has stack)\n");
461ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return -1;
462ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
463ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   return 0;
464ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
465ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
466ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
467ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*
468ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  Loads a Mach-O executable into memory, along with any threads,
469ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  stacks, and dylinker.
470ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  Returns 0 on success, -1 on any failure.
471ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  fd[offset..offset+size) is a Mach-O thin file.
472ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  filetype is MH_EXECUTE or MH_DYLINKER.
473ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  The mapped but empty stack is returned in *out_stack.
474ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  The executable's Mach headers are returned in *out_text.
475ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  The executable's entry point is returned in *out_entry.
476ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  The dylinker's entry point (if any) is returned in *out_linker_entry.
477ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown  GrP fixme need to return whether dylinker was found - stack layout is different
478ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown*/
479ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic int
480ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownload_thin_file(int fd, vki_off_t offset, vki_off_t size, unsigned long filetype,
481436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov               const HChar *filename,
482ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown               vki_uint8_t **out_stack_start, vki_uint8_t **out_stack_end,
483ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown               vki_uint8_t **out_text, vki_uint8_t **out_entry, vki_uint8_t **out_linker_entry)
484ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
485663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   VG_(debugLog)(1, "ume", "load_thin_file: begin:   %s\n", filename);
486ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   struct MACH_HEADER mh;
487ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   vki_uint8_t *headers;
488ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   vki_uint8_t *headers_end;
489ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   struct load_command *lc;
490ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   struct load_command *lcend;
491ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   struct SEGMENT_COMMAND *segcmd;
492ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   struct thread_command *threadcmd;
493ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   struct dylinker_command *dycmd;
494ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   int err;
495ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   SysRes res;
496ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   vki_size_t len;
497ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
498ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   vki_uint8_t *stack_start = NULL;   // allocated thread stack (hot end)
499ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   vki_uint8_t *stack_end = NULL;   // allocated thread stack (cold end)
500ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   vki_uint8_t *entry = NULL;   // static entry point
501ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   vki_uint8_t *text = NULL;    // start of text segment (i.e. the mach headers)
502ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   vki_uint8_t *linker_entry = NULL; // dylinker entry point
503ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
504ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Read Mach-O header
505ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (sizeof(mh) > size) {
506ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("bad executable (no Mach-O header)\n");
507ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
508ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   res = VG_(pread)(fd, &mh, sizeof(mh), offset);
509ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (sr_isError(res)  ||  sr_Res(res) != sizeof(mh)) {
510ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("bad executable (no Mach-O header)\n");
511ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return -1;
512ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
513ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
514ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
515ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Sanity-check the header itself
516ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (mh.magic != MAGIC) {
517ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("bad executable (no Mach-O magic)\n");
518ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return -1;
519ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
520ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
521ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (mh.filetype != filetype) {
522ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // expecting MH_EXECUTE or MH_DYLINKER
523ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("bad executable (wrong file type)\n");
524ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return -1;
525ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
526ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
527ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
528ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Map all headers into memory
529ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   len = sizeof(mh) + mh.sizeofcmds;
530ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (len > size) {
531ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("bad executable (missing load commands)\n");
532ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return -1;
533ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
534ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
535ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   headers = VG_(malloc)("ume.macho.headers", len);
536ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   res = VG_(pread)(fd, headers, len, offset);
537ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (sr_isError(res)) {
538ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("couldn't read load commands from executable\n");
539ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return -1;
540ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
541663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   headers_end = headers + len;
542ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
543ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
544ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Map some segments into client memory:
545ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // LC_SEGMENT    (text, data, etc)
546ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // UNIXSTACK     (stack)
547ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // LOAD_DYLINKER (dyld)
548ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   lcend = (struct load_command *)(headers + mh.sizeofcmds + sizeof(mh));
549ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   for (lc = (struct load_command *)(headers + sizeof(mh));
550ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown        lc < lcend;
551ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown        lc = (struct load_command *)(lc->cmdsize + (vki_uint8_t *)lc))
552ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   {
553ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if ((vki_uint8_t *)lc < headers  ||
554ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown          lc->cmdsize+(vki_uint8_t *)lc > headers_end) {
555ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown          print("bad executable (invalid load commands)\n");
556ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown          return -1;
557ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
558ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
559ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      switch (lc->cmd) {
560663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng
561663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng#if   DARWIN_VERS == DARWIN_10_8
562663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng      case LC_MAIN: { /* New in 10.8 */
563663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng         struct entry_point_command* epcmd
564663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng            = (struct entry_point_command*)lc;
565663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng         if (stack_start || stack_end) {
566663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng            print("bad executable (multiple indications of stack)");
567663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng            return -1;
568663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng         }
569663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng         err = handle_lcmain ( &stack_start, &stack_end, epcmd->stacksize );
570663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng         if (err) return -1;
571663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng         VG_(debugLog)(2, "ume", "lc_main: created stack %p-%p\n",
572663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng	               stack_start, stack_end);
573663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng         break;
574663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng      }
575663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng#     endif
576663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng
577ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      case LC_SEGMENT_CMD:
578ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (lc->cmdsize < sizeof(struct SEGMENT_COMMAND)) {
579ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            print("bad executable (invalid load commands)\n");
580ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            return -1;
581ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         }
582ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         segcmd = (struct SEGMENT_COMMAND *)lc;
583ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         err = load_segment(fd, offset, size, &text, &stack_start,
584ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                            segcmd, filename);
585ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (err) return -1;
586ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
587ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         break;
588ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
589ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      case LC_UNIXTHREAD:
590ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (stack_end  ||  entry) {
591ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            print("bad executable (multiple thread commands)\n");
592ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            return -1;
593ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         }
594ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (lc->cmdsize < sizeof(struct thread_command)) {
595ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            print("bad executable (invalid load commands)\n");
596ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            return -1;
597ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         }
598ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         threadcmd = (struct thread_command *)lc;
599ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         err = load_unixthread(&stack_start, &stack_end, &entry, threadcmd);
600ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (err) return -1;
601ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         break;
602ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
603ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      case LC_LOAD_DYLINKER:
604ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (filetype == MH_DYLINKER) {
605ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            print("bad executable (dylinker needs a dylinker)\n");
606ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            return -1;
607ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         }
608ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (linker_entry) {
609ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            print("bad executable (multiple dylinker commands)\n");
610ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         }
611ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (lc->cmdsize < sizeof(struct dylinker_command)) {
612ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            print("bad executable (invalid load commands)\n");
613ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            return -1;
614ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         }
615ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         dycmd = (struct dylinker_command *)lc;
616ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         err = load_dylinker(&linker_entry, dycmd);
617ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (err) return -1;
618ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         break;
619ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
620ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      case LC_THREAD:
621ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (filetype == MH_EXECUTE) {
622ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            print("bad executable (stackless thread)\n");
623ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            return -1;
624ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         }
625ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (stack_end  ||  entry) {
626ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            print("bad executable (multiple thread commands)\n");
627ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            return -1;
628ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         }
629ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (lc->cmdsize < sizeof(struct thread_command)) {
630ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            print("bad executable (invalid load commands)\n");
631ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            return -1;
632ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         }
633ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         threadcmd = (struct thread_command *)lc;
634ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         err = load_thread(&entry, threadcmd);
635ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (err) return -1;
636ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         break;
637ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
638ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      default:
639ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         break;
640ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
641ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
642ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
643ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
644ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Done with the headers
645ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   VG_(free)(headers);
646ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
647ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (filetype == MH_EXECUTE) {
648ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // Verify the necessary pieces for an executable:
649ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // a stack
650ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // a text segment
651ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // an entry point (static or linker)
652ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (!stack_end || !stack_start) {
653663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng         VG_(printf)("bad executable %s (no stack)\n", filename);
654ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         return -1;
655ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
656ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (!text) {
657ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         print("bad executable (no text segment)\n");
658ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         return -1;
659ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
660ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (!entry  &&  !linker_entry) {
661ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         print("bad executable (no entry point)\n");
662ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         return -1;
663ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
664ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
665ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   else if (filetype == MH_DYLINKER) {
666ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // Verify the necessary pieces for a dylinker:
667ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // an entry point
668ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (!entry) {
669ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         print("bad executable (no entry point)\n");
670ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         return -1;
671ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
672ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
673ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
674ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (out_stack_start) *out_stack_start = stack_start;
675ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (out_stack_end) *out_stack_end = stack_end;
676ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (out_text)  *out_text = text;
677ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (out_entry) *out_entry = entry;
678ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (out_linker_entry) *out_linker_entry = linker_entry;
679ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
680663860b1408516d02ebfcb3a9999a134e6cfb223Ben Cheng   VG_(debugLog)(1, "ume", "load_thin_file: success: %s\n", filename);
681ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   return 0;
682ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
683ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
684ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
685ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*
686ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown Load a fat Mach-O executable.
687ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown*/
688ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic int
689ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownload_fat_file(int fd, vki_off_t offset, vki_off_t size, unsigned long filetype,
690436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov             const HChar *filename,
691ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown             vki_uint8_t **out_stack_start, vki_uint8_t **out_stack_end,
692ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown             vki_uint8_t **out_text, vki_uint8_t **out_entry, vki_uint8_t **out_linker_entry)
693ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
694ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   struct fat_header fh;
695ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   vki_off_t arch_offset;
696ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   int i;
697ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   cpu_type_t good_arch;
698ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   SysRes res;
699ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
700ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#if defined(VGA_ppc32)
701ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   good_arch = CPU_TYPE_POWERPC;
702ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#elif defined(VGA_ppc64)
703ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   good_arch = CPU_TYPE_POWERPC64;
704ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#elif defined(VGA_x86)
705ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   good_arch = CPU_TYPE_I386;
706ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#elif defined(VGA_amd64)
707ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   good_arch = CPU_TYPE_X86_64;
708ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#else
709ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown# error unknown architecture
710ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#endif
711ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
712ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Read fat header
713ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // All fat contents are BIG-ENDIAN
714ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (size < sizeof(fh)) {
715ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("bad executable (bad fat header)\n");
716ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return -1;
717ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
718ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   res = VG_(pread)(fd, &fh, sizeof(fh), offset);
719ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (sr_isError(res)  ||  sr_Res(res) != sizeof(fh)) {
720ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("bad executable (bad fat header)\n");
721ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return -1;
722ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
723ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
724ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // Scan arch headers looking for a good one
725ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   arch_offset = offset + sizeof(fh);
726ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   fh.nfat_arch = VG_(ntohl)(fh.nfat_arch);
727ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   for (i = 0; i < fh.nfat_arch; i++) {
728ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      struct fat_arch arch;
729ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (arch_offset + sizeof(arch) > size) {
730ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown          print("bad executable (corrupt fat archs)\n");
731ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown          return -1;
732ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
733ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
734ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      res = VG_(pread)(fd, &arch, sizeof(arch), arch_offset);
735ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      arch_offset += sizeof(arch);
736ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (sr_isError(res)  ||  sr_Res(res) != sizeof(arch)) {
737ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         VG_(printf)("bad executable (corrupt fat arch) %x %llu\n",
738ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                     arch.cputype, (ULong)arch_offset);
739ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         return -1;
740ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
741ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
742ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      arch.cputype = VG_(ntohl)(arch.cputype);
743ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      arch.cpusubtype = VG_(ntohl)(arch.cpusubtype);
744ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      arch.offset = VG_(ntohl)(arch.offset);
745ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      arch.size = VG_(ntohl)(arch.size);
746ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      arch.align = VG_(ntohl)(arch.align);
747ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      if (arch.cputype == good_arch) {
748ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         // use this arch
749ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         if (arch.offset > size  ||  arch.offset + arch.size > size) {
750ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            print("bad executable (corrupt fat arch 2)\n");
751ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown            return -1;
752ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         }
753ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown         return load_mach_file(fd, offset+arch.offset, arch.size, filetype,
754ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                               filename, out_stack_start, out_stack_end,
755ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                               out_text, out_entry, out_linker_entry);
756ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      }
757ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
758ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
759ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   print("bad executable (can't run on this machine)\n");
760ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   return -1;
761ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
762ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
763ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*
764ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown Load a Mach-O executable or dylinker.
765ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown The file may be fat or thin.
766ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown*/
767ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic int
768ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownload_mach_file(int fd, vki_off_t offset, vki_off_t size, unsigned long filetype,
769436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov              const HChar *filename,
770ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown              vki_uint8_t **out_stack_start, vki_uint8_t **out_stack_end,
771ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown              vki_uint8_t **out_text, vki_uint8_t **out_entry, vki_uint8_t **out_linker_entry)
772ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
773ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   vki_uint32_t magic;
774ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   SysRes res;
775ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
776ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (size < sizeof(magic)) {
777ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("bad executable (no Mach-O magic)\n");
778ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return -1;
779ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
780ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   res = VG_(pread)(fd, &magic, sizeof(magic), offset);
781ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (sr_isError(res)  ||  sr_Res(res) != sizeof(magic)) {
782ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("bad executable (no Mach-O magic)\n");
783ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return -1;
784ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
785ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
786ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (magic == MAGIC) {
787ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // thin
788ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return load_thin_file(fd, offset, size, filetype, filename,
789ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                            out_stack_start, out_stack_end,
790ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                            out_text, out_entry, out_linker_entry);
791ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   } else if (magic == VG_(htonl)(FAT_MAGIC)) {
792ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // fat
793ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return load_fat_file(fd, offset, size, filetype, filename,
794ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                           out_stack_start, out_stack_end,
795ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                           out_text, out_entry, out_linker_entry);
796ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   } else {
797ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      // huh?
798ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("bad executable (bad Mach-O magic)\n");
799ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return -1;
800ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
801ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
802ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
803ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
804436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy IvanovBool VG_(match_macho)(const void *hdr, Int len)
805ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
806436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov   const vki_uint32_t *magic = hdr;
807ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
808ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // GrP fixme check more carefully for matching fat arch?
809ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
810ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   return (len >= VKI_PAGE_SIZE  &&
811ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown           (*magic == MAGIC  ||  *magic == VG_(ntohl)(FAT_MAGIC)))
812ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      ? True : False;
813ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
814ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
815ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
816ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff BrownInt VG_(load_macho)(Int fd, const HChar *name, ExeInfo *info)
817ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{
818ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   int err;
819ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   struct vg_stat sb;
820ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   vki_uint8_t *stack_start;
821ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   vki_uint8_t *stack_end;
822ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   vki_uint8_t *text;
823ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   vki_uint8_t *entry;
824ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   vki_uint8_t *linker_entry;
825ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
826ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   err = VG_(fstat)(fd, &sb);
827ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (err) {
828ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      print("couldn't stat executable\n");
829ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown      return VKI_ENOEXEC;
830ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   }
831ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
832ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   err = load_mach_file(fd, 0, sb.size, MH_EXECUTE, name,
833ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                        &stack_start, &stack_end,
834ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown                        &text, &entry, &linker_entry);
835ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   if (err) return VKI_ENOEXEC;
836ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
837ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // GrP fixme exe_base
838ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   // GrP fixme exe_end
839ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   info->entry = (Addr)entry;
840ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   info->init_ip = (Addr)(linker_entry ? linker_entry : entry);
841ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   info->brkbase = 0xffffffff; // GrP fixme hack
842ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   info->init_toc = 0; // GrP fixme unused
843ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
844ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   info->stack_start = (Addr)stack_start;
845ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   info->stack_end = (Addr)stack_end;
846ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   info->text = (Addr)text;
847ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   info->dynamic = linker_entry ? True : False;
848ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
849ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   info->executable_path = VG_(strdup)("ume.macho.executable_path", name);
850ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
851ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown   return 0;
852ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown}
853ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
854ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#endif // defined(VGO_darwin)
855ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
856ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*--------------------------------------------------------------------*/
857ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*--- end                                                          ---*/
858ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown/*--------------------------------------------------------------------*/
859ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown
860