/* Mac OS X support for GDB, the GNU debugger. Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2004 Free Software Foundation, Inc. Contributed by Apple Computer, Inc. This file is part of GDB. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "macosx-nat-dyld-path.h" #include "macosx-nat-dyld-info.h" #include "macosx-nat-dyld.h" #include "macosx-nat-inferior.h" #include #include #include #include #include #include "defs.h" #include "inferior.h" #include "environ.h" #include "gdbcore.h" extern macosx_inferior_status *macosx_status; #define assert CHECK_FATAL /* Declarations of functions used only in this file. */ static char *build_suffix_name (const char *name, const char *suffix); static char *search_for_name_in_path (const char *name, const char *path, const char *suffix); static const char *look_back_for_slash (const char *name, const char *p); static const char *get_framework_pathname (const char *name, const char *type, int with_suffix); /* look_back_for_slash() is passed a string NAME and an end point P in NAME to start looking for '/' before the end point. It returns a pointer to the '/' back from the end point or NULL if there is none. */ static const char * look_back_for_slash (const char *name, const char *p) { for (p = p - 1; p >= name; p--) { if (*p == '/') return p; } return NULL; } /* build_suffix_name returns the proper suffix'ed name for NAME, putting SUFFIX before .dylib, if it is the suffix for NAME, and just appending it otherwise. The return value is malloc'ed, and it is up to the caller to free it. If SUFFIX is NULL, then this returns NULL. */ static char * build_suffix_name (const char *name, const char *suffix) { int suffixlen = strlen (suffix); int namelen = strlen (name); char *name_with_suffix; if (suffixlen > 0) { char *tmp; name_with_suffix = xmalloc (namelen + suffixlen + 1); if (namelen < 7) tmp = NULL; else tmp = strrchr (name, '.'); if (tmp != NULL && strcmp (tmp, ".dylib") == 0) { int baselen = namelen - 6; memcpy (name_with_suffix, name, baselen); tmp = name_with_suffix + baselen; memcpy (tmp, suffix, suffixlen); tmp += suffixlen; memcpy (tmp, ".dylib", 6); *(tmp + 6) = '\0'; } else { memcpy (name_with_suffix, name, namelen); tmp = name_with_suffix + namelen; memcpy (tmp, suffix, suffixlen); *(tmp + suffixlen) = '\0'; } return name_with_suffix; } else { return NULL; } } /* search_for_name_in_path() is used in searching for name in the DYLD_LIBRARY_PATH or the DYLD_FRAMEWORK_PATH. It is passed a name and a path and returns the name of the first combination that exist or NULL if none exists. */ static char * search_for_name_in_path (const char *name, const char *path, const char *suffix) { char *dylib_name; char *name_with_suffix; int name_with_suffix_len; const char *p, *cur; int curlen; int namelen; int pathlen; struct stat stat_buf; namelen = strlen (name); pathlen = strlen (path); /* Prebuild the name with suffix */ if (suffix) { name_with_suffix = build_suffix_name (name, suffix); name_with_suffix_len = strlen (name_with_suffix); dylib_name = xmalloc (name_with_suffix_len + pathlen + 2); } else { name_with_suffix = NULL; name_with_suffix_len = 0; dylib_name = xmalloc (namelen + pathlen + 2); } /* Now cruise on through the path, trying the name_with_suffix, and then the name, with each path element */ cur = path; for (;;) { p = strchr (cur, ':'); if (p == NULL) { p = strchr (cur, '\0'); } assert (p != NULL); curlen = p - cur; /* Skip empty path elements... */ if (curlen != 0) { memcpy (dylib_name, cur, curlen); dylib_name[curlen] = '/'; if (name_with_suffix != NULL) { memcpy (dylib_name + curlen + 1, name_with_suffix, name_with_suffix_len); dylib_name[curlen + 1 + name_with_suffix_len] = '\0'; if (stat (dylib_name, &stat_buf) == 0) { xfree (name_with_suffix); return dylib_name; } } memcpy (dylib_name + curlen + 1, name, namelen); dylib_name[curlen + 1 + namelen] = '\0'; if (stat (dylib_name, &stat_buf) == 0) { if (name_with_suffix) xfree (name_with_suffix); return dylib_name; } } if (*p == '\0') { break; } cur = p + 1; if (*cur == '\0') { break; } } xfree (dylib_name); if (name_with_suffix) xfree (name_with_suffix); return NULL; } /* get_framework_pathname() is passed a name of a dynamic library and returns a pointer to the start of the framework name if one exist or NULL if none exists. A framework name can take one of the following two forms: Foo.framework/Versions/A/Foo Foo.framework/Foo Where 'A' and 'Foo' can be any string. NAME is the pathname of the file. TYPE is something like ".framework" or ".bundle". If WITH_SUFFIX is set, suffixes like _debug are omitted. */ static const char * get_framework_pathname (const char *name, const char *type, int with_suffix) { const char *basename, *a, *b, *c, *d, *suffix; int baselen, s; /* pull off the last component and make basename point to it A will point to the last / character in NAME. */ a = strrchr (name, '/'); if (a == NULL) return (NULL); if (a == name) return (NULL); basename = a + 1; baselen = strlen (basename); /* look for suffix starting with a '_', e.g. ...Versions/A/Carbon_debug */ if (with_suffix) { suffix = strrchr (basename, '_'); if (suffix != NULL) { s = strlen (suffix); if (suffix == basename || s < 2) suffix = NULL; else baselen -= s; } } /* First look for the form "Foo.framework/Foo" A points to the last '/' character in NAME. B will point to the pentultimate '/' character in NAME. */ b = look_back_for_slash (name, a); if (b == NULL) { if (strncmp (name, basename, baselen) == 0 && strncmp (name + baselen, type, sizeof (type) - 1) == 0) return (name); else return (NULL); } else { if (strncmp (b + 1, basename, baselen) == 0 && strncmp (b + 1 + baselen, type, sizeof (type) - 1) == 0) return (b + 1); } /* Next look for the form "Foo.framework/Versions/A/Foo" A points to the last '/' character in NAME. B points to the pentultimate '/' character in NAME. C will point to the '/' character before B in NAME. */ if (b == name) return (NULL); c = look_back_for_slash (name, b); if (c == NULL) return NULL; if (c == name) { if (c == NULL) return (NULL); if (strncmp (c + 1, "Versions/", sizeof ("Versions/") - 1) != 0) { /* Look for the form "Foo.bundle/Contents/MacOS/Foo" */ if (strncmp (c + 1, "Contents/MacOS/", sizeof ("Contents/MacOS/") - 1) == 0) { if (strncmp (c + 1, basename, baselen) == 0 && strncmp (c + 1 + baselen, type, sizeof (type) - 1) == 0) return (c + 1); } return (NULL); } } /* Next look for the form "Foo.framework/Versions/A/Foo" A points to the last '/' character in NAME. B points to the pentultimate '/' character in NAME. C points to the '/' character before B in NAME. D will point to the '/' character before C in NAME.*/ d = look_back_for_slash (name, c); if (d == NULL) { if (strncmp (name, basename, baselen) == 0 && strncmp (name + baselen, type, sizeof (type) - 1) == 0) return (name); else return (NULL); } else { if (strncmp (d + 1, basename, baselen) == 0 && strncmp (d + 1 + baselen, type, sizeof (type) - 1) == 0) return (d + 1); else return (NULL); } } /* Given a pathname to a library/bundle/framework, come up with its "short name", or "basename". Directories are stripped; any suffixes (_debug) are removed, and so forth. PATH is the filename to examine. S is set to the base name string, which is xmalloc()'ed and freeing is the duty of the caller. LEN is the length of the basename. The string S may actually be longer than LEN, for instance if there is a suffix which we should ignore. IS_FRAMEWORK is true if PATH appears to be a framework. IS_BUNDLE is true if PATH appears to be a bundle. */ void dyld_library_basename (const char *path, const char **s, int *len, int *is_framework, int *is_bundle) { const char *p = NULL; const char *q = NULL; const char *dyld_image_suffix = NULL; /* If the user specified a DYLD_IMAGE_SUFFIX, get a pointer to that string. */ if (macosx_status != NULL && macosx_status->dyld_status.path_info.image_suffix != NULL) { dyld_image_suffix = macosx_status->dyld_status.path_info.image_suffix; } if (is_framework != NULL) { *is_framework = 0; } if (is_bundle != NULL) { *is_bundle = 0; } p = get_framework_pathname (path, ".framework/", 1); if (p != NULL) { q = strrchr (path, '/'); assert (q != NULL); assert (*q++ == '/'); *s = xstrdup (q); *len = strlen (q); if (is_framework != NULL) { *is_framework = 1; } if (is_bundle != NULL) { *is_bundle = 0; } return; } p = get_framework_pathname (path, ".bundle/", 1); if (p != NULL) { q = strrchr (path, '/'); assert (q != NULL); assert (*q++ == '/'); *s = xstrdup (q); *len = strlen (q); if (is_framework != NULL) { *is_framework = 0; } if (is_bundle != NULL) { *is_bundle = 1; } return; } /* Not a bundle, not a framework, just a normal dylib/bundle pathname. If it's something like /usr/lib/libSystem.B_debug.dylib, we want to return libSystem.B.dylib. We'll need to copy the basename const string to a writable memory buffer and move that _debug out of the way. */ q = strrchr (path, '/'); if (q != NULL) path = ++q; char *newstr = xstrdup (path); if (dyld_image_suffix != NULL) { char *suffixptr = strstr (newstr, dyld_image_suffix); /* If we have a suffix, copy anything AFTER the suffix ("_debug") on top of the suffix. */ /* Copy anything after the suffix to a scratch buffer, then the contents of the scratch buffer on top of the suffix. This is me being paranoid where the stuff after suffix could be longer than the suffix ("_debug.dylibbbber") and a straight memcpy could have overlap. */ if (suffixptr != NULL) { char *tmpbuf = xstrdup (suffixptr + strlen (dyld_image_suffix)); strcpy (suffixptr, tmpbuf); xfree (tmpbuf); } } *s = (const char *) newstr; *len = strlen (newstr); return; } char * dyld_resolve_image (const struct dyld_path_info *d, const char *dylib_name) { struct stat stat_buf; const char *framework_name = NULL; const char *framework_name_suffix = NULL; const char *library_name = NULL; char *framework_path = NULL; if (dylib_name == NULL) return NULL; if (dylib_name[0] == '@' && strstr (dylib_name, "@executable_path") == dylib_name) { /* Handle the @executable_path name here... */ int cookie_len = strlen ("@executable_path"); const char *relative_name = dylib_name + cookie_len; if (exec_bfd != NULL && exec_bfd->filename != NULL) { int relative_name_len = strlen (relative_name); char *executable_path_end = strrchr (exec_bfd->filename, '/'); if (executable_path_end != NULL) { int executable_path_len = executable_path_end - exec_bfd->filename; char *final_name = xmalloc (relative_name_len + executable_path_len + 1); memcpy (final_name, exec_bfd->filename, executable_path_len); memcpy (final_name + executable_path_len, relative_name, relative_name_len); final_name[executable_path_len + relative_name_len] = '\0'; if (stat (final_name, &stat_buf) == 0) return final_name; else xfree (final_name); } else { warning ("Executable filename not a path, " "can't resolve \"@executable_path load command."); return NULL; } } else { warning ("Couldn't find executable filename while trying to" " resolve \"@executable_path\" load command."); } } framework_name = get_framework_pathname (dylib_name, ".framework/", 0); framework_name_suffix = get_framework_pathname (dylib_name, ".framework/", 1); library_name = strrchr (dylib_name, '/'); if (library_name != NULL && library_name[1] != '\0') library_name++; else library_name = dylib_name; /* If d->framework_path is set and this dylib_name is a framework name, use the first file that exists in the framework path, if any. If none exist, go on to search the d->library_path if any. The first call to get_framework_pathname() tries to get a name without a suffix, the second call tries with a suffix. */ if (d->framework_path != NULL) { if (framework_name != NULL) { framework_path = search_for_name_in_path (framework_name, d->framework_path, d->image_suffix); if (framework_path != NULL) return framework_path; } if (framework_name_suffix != NULL) { framework_path = search_for_name_in_path (framework_name_suffix, d->framework_path, d->image_suffix); if (framework_path != NULL) return framework_path; } } /* If d->library_path is set, then use the first file that exists in the path. If none exist, use the original name. The string d->library_path points to is "path1:path2:path3" and comes from the enviroment variable DYLD_LIBRARY_PATH. */ if (d->library_path != NULL) { framework_path = search_for_name_in_path (library_name, d->library_path, d->image_suffix); if (framework_path != NULL) return framework_path; } /* Now try to open the dylib_name (remembering to try the suffix first). If it fails and we have not previously tried to search for a name then try searching the fall back paths (including the default fall back framework path). */ if (d->image_suffix) { char *suffix_name; suffix_name = build_suffix_name (dylib_name, d->image_suffix); if (stat (suffix_name, &stat_buf) == 0) return suffix_name; else xfree (suffix_name); } if (stat (dylib_name, &stat_buf) == 0) { return xstrdup (dylib_name); } /* First try the the d->fallback_framework_path if that has been set (first without a suffix and then with a suffix). */ if (d->fallback_framework_path != NULL) { if (framework_name != NULL) { framework_path = search_for_name_in_path (framework_name, d->fallback_framework_path, d->image_suffix); if (framework_path != NULL) { return framework_path; } } if (framework_name_suffix != NULL) { framework_path = search_for_name_in_path (framework_name_suffix, d->fallback_framework_path, d->image_suffix); if (framework_path != NULL) { return framework_path; } } } /* If no new name is still found try d->fallback_library_path if that was set. */ if (d->fallback_library_path != NULL) { framework_path = search_for_name_in_path (library_name, d->fallback_library_path, d->image_suffix); if (framework_path != NULL) { return framework_path; } } return NULL; } /* This function ensures that we have all zero's in our path_info structure D so that dyld_init_paths() doesn't try to xfree() a pointer that is random garbage sitting in memory. */ void dyld_zero_path_info (dyld_path_info *d) { d->framework_path = NULL; d->library_path = NULL; d->image_suffix = NULL; d->fallback_framework_path = NULL; d->fallback_library_path = NULL; d->insert_libraries = NULL; } void dyld_init_paths (dyld_path_info * d) { char *home; const char *default_fallback_framework_path = "%s/Library/Frameworks:" "/Local/Library/Frameworks:" "/Network/Library/Frameworks:" "/System/Library/Frameworks"; const char *default_fallback_library_path = "%s/lib:" "/usr/local/lib:" "/lib:" "/usr/lib"; /* Neither framework path searching nor library insertion is done for setuid programs which are not run by the real user. */ if (getuid () == geteuid () && getgid () == getegid ()) { if (d->framework_path != NULL) xfree (d->framework_path); if (d->library_path != NULL) xfree (d->library_path); if (d->fallback_framework_path != NULL) xfree (d->fallback_framework_path); if (d->fallback_library_path != NULL) xfree (d->fallback_library_path); if (d->image_suffix != NULL) xfree (d->image_suffix); if (d->insert_libraries != NULL) xfree (d->insert_libraries); d->framework_path = get_in_environ (inferior_environ, "DYLD_FRAMEWORK_PATH"); if (d->framework_path != NULL) d->framework_path = xstrdup (d->framework_path); d->library_path = get_in_environ (inferior_environ, "DYLD_LIBRARY_PATH"); if (d->library_path != NULL) d->library_path = xstrdup (d->library_path); d->fallback_framework_path = get_in_environ (inferior_environ, "DYLD_FALLBACK_FRAMEWORK_PATH"); if (d->fallback_framework_path != NULL) d->fallback_framework_path = xstrdup (d->fallback_framework_path); d->fallback_library_path = get_in_environ (inferior_environ, "DYLD_FALLBACK_LIBRARY_PATH"); if (d->fallback_library_path != NULL) d->fallback_library_path = xstrdup (d->fallback_library_path); d->image_suffix = get_in_environ (inferior_environ, "DYLD_IMAGE_SUFFIX"); if (d->image_suffix != NULL) d->image_suffix = xstrdup (d->image_suffix); d->insert_libraries = get_in_environ (inferior_environ, "DYLD_INSERT_LIBRARIES"); if (d->insert_libraries != NULL) d->insert_libraries = xstrdup (d->insert_libraries); } home = get_in_environ (inferior_environ, "HOME"); if (home != NULL) home = xstrdup (home); if (home == NULL) home = xstrdup ("/"); if (d->fallback_framework_path == NULL) { d->fallback_framework_path = xmalloc (strlen (default_fallback_framework_path) + strlen (home) + 1); sprintf (d->fallback_framework_path, default_fallback_framework_path, home); } if (d->fallback_library_path == NULL) { d->fallback_library_path = xmalloc (strlen (default_fallback_library_path) + strlen (home) + 1); sprintf (d->fallback_library_path, default_fallback_library_path, home); } xfree (home); }