/* Fix and Continue support for gdb Copyright 2003, 2004 Free Software Foundation, Inc. Contributed by Apple Computer, Inc. Written by Jason Molenda. Inspired by Fix & Continue implementation in wdb by Hewlett-Packard Company. 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 #include #include #include #include #include #include #include "defs.h" #include "value.h" #include "gdbtypes.h" #include "objfiles.h" #include "command.h" #include "completer.h" #include "frame.h" #include "target.h" #include "gdbcore.h" #include "inferior.h" #include "symfile.h" #include "gdbthread.h" #include "gdb.h" #include "ui-out.h" #include "cli-out.h" #include "symtab.h" #include "regcache.h" #include "gdbcmd.h" #include "language.h" #include "dictionary.h" #include "block.h" #include #include "osabi.h" #if defined (TARGET_POWERPC) #include "ppc-macosx-frameinfo.h" #include "ppc-macosx-tdep.h" #endif #if defined (TARGET_I386) #include "i386-tdep.h" #endif #ifdef NM_NEXTSTEP #include "macosx-nat-dyld-process.h" #endif /* A list of all active threads, and the functions those threads have currently executing which are in the fixed object file. This information is only useful at the point of fix-up, where we're looking for restriction violations and reporting the state of the stack to the UI. */ struct active_threads { int num; struct active_func *active_func_chain; CORE_ADDR pc; struct active_threads *next; }; /* The structure for a single function that is active at the time of the fix request. */ struct active_func { struct active_func * next; struct symbol * sym; // The following are right out of the struct frame_info int level, line; CORE_ADDR addr, fp; const char *file, *dir; }; /* Keep track of all inferior data we change while adding in a fixed .o file, so that we can restore the state of the program if a fix is aborted half way (due to a restriction violation, for example). The Apple implementation of this does all of its syntax checking before loading the file, so this isn't nearly as necessary. In fact, I don't think it's of any use to me at all. */ struct fixeddatum { CORE_ADDR addr; int size; int oldval, newval; /* old and new values of the datum */ struct fixeddatum * next; }; /* Keep track of all symbols we mark obsolete while adding in a fixed .o file, so that we can restore the state of the program if a fix is aborted half way (due to a restriction violation, for example). The Apple implementation of this does all of its syntax checking before loading the file, so this isn't nearly as necessary. In fact, I don't think it's of any use to me at all. */ struct obsoletedsym { struct minimal_symbol * oldmsym, *newmsym; struct symbol * oldsym, *newsym; struct obsoletedsym * next; }; /* Data structure to keep track of files being fixed. This primarily acts as the token o' data that is passed around all the functions as we're handling a fix request. */ struct fixinfo { /* Source, bundle, and object filenames. Probably unnecessary to store both full name and the basename, but we seem to be recomputing that in several places, so I'll stash it here. The "basename" variants are just pointers into the middle of the "filename" malloced memory; only free the "filename" versions. The object filenames ("foo.o") are used only to communicate with ZeroLink, which only knows about object files. Note that we have one FIXINFO struct for each *SOURCE* file, but each fixed bundl3 will have a different name. The bundle_filename will contain the most recently seen bundle filename. */ const char *src_filename; const char *src_basename; const char *bundle_filename; const char *bundle_basename; const char *object_filename; /* The original objfile (original_objfile_filename) and source file (canonical_source_filename) that this fixinfo structure represents. canonical_source_filename is a pointer to either src_filename or src_basename, depending on which name is found in the executable objfile's psymtabs/symtabs. */ const char *original_objfile_filename; const char *canonical_source_filename; /* The list of active functions is only useful at the point where the fix request comes in -- once that request has been completed, this can be xfree()'ed. */ struct active_threads *active_functions; /* The chain of fixed versions of this object file (when the user fixes the same .o multiple times.) */ struct fixedobj *fixed_object_files; /* The most recently fixed .o file we've loaded for this file (i.e. a user "fixes" a single source file multiple times; this stores the current version.) NB: This is really just the tail of the fixed_object_files linked list, but the HP implementation did their link lists the opposite way so I want to make it clear where the most recent objfile is. */ struct fixedobj *most_recent_fix; /* The fixinfo structure is built up as we're doing the initial scan of the proposed object file, and if we error out half-way through, we need to recognize a half-finished structure. FIXME: I should do something cleaner here, but a simple cleanup to remove the fixinfo struct in the case of an error would be incorrect if the fixinfo structure had already existed. Note the directly related function free_half_finished_fixinfo (). */ int complete; /* One of these structures for each object file the user tries to fix. */ struct fixinfo *next; }; /* Data structure to track each fixed object file (.o) that we pull in. There may be man copies of a single object file loaded into a program; each of them will have one of these structures and a single struct fixinfo to hold all fixes to that object file. */ struct fixedobj { /* Bundle file name, including path */ const char *bundle_filename; /* Maintain a chain of inferior data we modified in the process of installing this object file so we can, in theory, back the fix out. */ struct fixeddatum *firstdatum, *lastdatum; /* Maintain a list of symbols we declared obsolete while installing this object file so we can, in theory, back the fix out. */ struct obsoletedsym *obsoletedsym; struct fixedobj *next; }; static struct fixinfo *fixinfo_chain = NULL; /* References to static/global data allocated in the new CU need to be redirected to the original locations. An array of these structures are populated with the address of an entry in the indirection table of the just-loaded CU, the destination address that that entry originally held (and which gdb will be redirecting), a pointer to the new symbol it is pointing to, and a pointer to the original symbol that it needs to be pointing to. */ struct file_static_fixups { CORE_ADDR addr; CORE_ADDR value; struct symbol *new_sym; struct minimal_symbol *new_msym; struct symbol *original_sym; struct minimal_symbol *original_msym; }; static void get_fixed_file (struct fixinfo *); static int load_fixed_objfile (const char *); static void do_final_fix_fixups (struct fixinfo *curfixinfo); static void redirect_file_statics (struct fixinfo *); static void find_new_static_symbols (struct fixinfo *, struct file_static_fixups *, int); static void find_orig_static_symbols (struct fixinfo *, struct file_static_fixups *, int); static void redirect_statics (struct file_static_fixups *, int); static int find_and_parse_nonlazy_ptr_sect (struct fixinfo *, struct file_static_fixups **); static struct objfile ** build_list_of_objfiles_to_update (struct fixinfo *); static void redirect_old_function (struct fixinfo *, struct symbol *, struct symbol *, int); CORE_ADDR decode_fix_and_continue_trampoline (CORE_ADDR); static uint16_t encode_lo16 (CORE_ADDR); static uint16_t encode_hi16 (CORE_ADDR); static CORE_ADDR decode_hi16_lo16 (uint16_t, uint16_t); static int in_active_func (const char *, struct active_threads *); static struct active_func *create_current_active_funcs_list (const char *); void do_final_fix_fixups_global_syms (struct block *newglobals, struct objfile *oldobj, struct fixinfo *curfixinfo); static void do_final_fix_fixups_static_syms (struct block *newstatics, struct objfile *oldobj, struct fixinfo *curfixinfo); static void pre_load_and_check_file (struct fixinfo *); static void force_psymtab_expansion (struct objfile *, const char *, const char *); static void expand_all_objfile_psymtabs (struct objfile *); static void free_active_threads_struct (struct active_threads *); static struct active_threads * create_current_threads_list (const char *); static struct fixinfo *get_fixinfo_for_new_request (const char *); static int file_exists_p (const char *); static void do_pre_load_checks (struct fixinfo *, struct objfile *); static void check_restrictions_globals (struct fixinfo *, struct objfile *); static void check_restrictions_statics (struct fixinfo *, struct objfile *); static void check_restrictions_locals (struct fixinfo *, struct objfile *); static void check_restrictions_function (const char *, int, struct block *, struct block *); static void check_restriction_cxx_zerolink (struct objfile *); static int sym_is_argument (struct symbol *); static int sym_is_local (struct symbol *); static void free_half_finished_fixinfo (struct fixinfo *); static void mark_previous_fixes_obsolete (struct fixinfo *); static const char *getbasename (const char *); static int inferior_is_zerolinked_p (void); static void tell_zerolink (struct fixinfo *); static void print_active_functions (struct fixinfo *); static struct objfile *find_objfile_by_name (const char *); static struct symtab *find_symtab_by_name (struct objfile *, const char *); static struct symbol *search_for_coalesced_symbol (struct objfile *, struct symbol *); static void restore_language (void *); static struct cleanup *set_current_language (const char *); static void find_original_object_file_name (struct fixinfo *); static struct objfile *find_original_object_file (struct fixinfo *); static struct symtab *find_original_symtab (struct fixinfo *); static struct partial_symtab *find_original_psymtab (struct fixinfo *); static struct objfile *raise_objfile_load_level (struct objfile *); int fix_and_continue_debug_flag = 0; #ifndef TARGET_ADDRESS_BYTES #define TARGET_ADDRESS_BYTES (TARGET_LONG_BIT / TARGET_CHAR_BIT) #endif static void fix_command (char *args, int from_tty) { char **argv; char *object_filename, *source_filename, *bundle_filename; struct cleanup *cleanups; const char *usage = "Usage: fix bundle-filename source-filename [object-filename]"; if (!args || args[0] == '\0') error ("%s", usage); argv = buildargv (args); cleanups = make_cleanup_freeargv (argv); /* Two required arguments. */ if (argv[0] == NULL || strlen (argv[0]) == 0 || argv[1] == NULL || strlen (argv[1]) == 0) error ("%s", usage); /* An optional third argument. */ if (argv[2] != NULL && (strlen (argv[2]) == 0 || argv[3] != NULL)) error ("%s", usage); bundle_filename = argv[0]; source_filename = argv[1]; if (!source_filename || strlen (source_filename) == 0 || !bundle_filename || strlen (bundle_filename) == 0) error ("%s", usage); /* Get third argument: Object file name (only needed for ZeroLink) */ object_filename = argv[2]; fix_command_1 (source_filename, bundle_filename, object_filename, NULL); if (!ui_out_is_mi_like_p (uiout) && from_tty) { printf_filtered ("Fix succeeded.\n"); } do_cleanups (cleanups); } /* All filename arguments should be tilde expanded, and the bundle filename should be run through realpath() before getting here so it's the same form that dyld will report. */ void fix_command_1 (const char *source_filename, const char *bundle_filename, const char *object_filename, const char *solib_filename) { struct fixinfo *cur; struct cleanup *wipe; char tmpbuf[MAXPATHLEN]; char *fn; /* source_filename and bundle_filename must be set. */ if (source_filename == NULL || *source_filename == '\0' || bundle_filename == NULL || *bundle_filename == '\0') error ("Source or bundle filename not provided."); /* object_filename and bundle_filename are needed in certain circumstances; F&C can potentially work without them. Canonicalize them to NULL if they're empty. */ if (object_filename && *object_filename == '\0') object_filename = NULL; if (bundle_filename && *bundle_filename == '\0') bundle_filename = NULL; /* Don't realpath() the source filename or the object filename. In the case of the source filename, this has to match the stabs generated by gcc, which aren't realpathed. In the case of the object filename, this has to match what ZeroLink knows, which isn't realpathed. To text, copy a project like Sketch into /tmp and try fixing it over there. (with /tmp really being a soft-link to /private/tmp). */ /* tilde_expand () from readline returns xmalloc'ed memory. */ source_filename = tilde_expand (source_filename); if (source_filename == NULL) error ("Source filename not found."); fn = tilde_expand (bundle_filename); if (fn) { char *ret = realpath (fn, tmpbuf); if (ret) bundle_filename = xstrdup (ret); else bundle_filename = NULL; xfree (fn); } else error ("Bundle filename '%s' not found.", bundle_filename); if (bundle_filename == NULL) error ("Bundle filename not found."); if (object_filename && *object_filename != '\0') object_filename = tilde_expand (object_filename); else object_filename = NULL; if (solib_filename) { fn = tilde_expand (solib_filename); if (fn) { char *ret = realpath (fn, tmpbuf); if (ret) solib_filename = xstrdup (ret); else solib_filename = NULL; xfree (fn); } else error ("Dylib filename '%s' not found.", solib_filename); if (solib_filename == NULL) error ("Dylib not found."); } if (!file_exists_p (source_filename)) error ("Source file '%s' not found.", source_filename); if (!file_exists_p (bundle_filename)) error ("Bundle '%s' not found.", bundle_filename); if (object_filename && !file_exists_p (object_filename)) error ("Object '%s' not found.", object_filename); if (solib_filename && !file_exists_p (solib_filename)) error ("Dylib/executable '%s' not found.", solib_filename); if (find_objfile_by_name (bundle_filename)) error ("Bundle '%s' has already been loaded.", bundle_filename); wipe = set_current_language (source_filename); /* FIXME: Should use a cleanup to free cur if it's a newly allocated fixinfo and we bail before the end. cf the documentation around the 'complete' field and free_half_finished_fixinfo(). */ cur = get_fixinfo_for_new_request (source_filename); cur->bundle_filename = bundle_filename; cur->bundle_basename = getbasename (bundle_filename); cur->object_filename = object_filename; tell_zerolink (cur); /* Make sure the original objfile that we're 'fixing' has its load level set so debug info is being read in.. */ if (solib_filename) raise_objfile_load_level (find_objfile_by_name (solib_filename)); find_original_object_file_name (cur); pre_load_and_check_file (cur); get_fixed_file (cur); mark_previous_fixes_obsolete (cur); do_final_fix_fixups (cur); print_active_functions (cur); do_cleanups (wipe); } /* If the inferior process is a zerolinked executible, and the object file that we're about to replace hasn't yet been loaded in, we need to reference a symbol from the file and get ZL to map in the original objfile before we load the fixed version. */ /* SPI for __zero_link_force_link_object_file() return value: */ enum ZLObjectFileResult { zlObjectFileUnknown = 0, zlObjectFileBeingLinked = 1, zlObjectFileAlreadyLinked = 2, zlObjectFileJustLinked = 3, }; static void tell_zerolink (struct fixinfo *cur) { static struct cached_value *cached_zl_force_link_object_file = NULL; struct value **obj_name_vec, **args, *val; const char *obj_name = cur->object_filename; int obj_name_len, i, retval; /* Is this source file already been fixed in the past? */ if (cur->fixed_object_files != NULL) return; /* Is the inferior using ZeroLink? */ if (!inferior_is_zerolinked_p ()) return; if (obj_name == NULL) { warning ("Inferior is a ZeroLinked, but no .o file was provided."); return; } if (!lookup_minimal_symbol ("__zero_link_force_link_object_file", NULL, NULL)) { warning ("Inferior is apparently a ZeroLink app, but " "__zero_link_force_link_object_file not found."); return; } if (cached_zl_force_link_object_file == NULL) cached_zl_force_link_object_file = create_cached_function ("__zero_link_force_link_object_file", builtin_type_int); obj_name_len = strlen (obj_name); obj_name_vec = (struct value **) alloca (sizeof (struct value *) * (obj_name_len + 2)); for (i = 0; i < obj_name_len + 1; i++) obj_name_vec[i] = value_from_longest (builtin_type_char, obj_name[i]); args = (struct value **) alloca (sizeof (struct value *) * 3); args[0] = value_array (0, obj_name_len, obj_name_vec); args[1] = value_from_longest (builtin_type_int, 0); val = call_function_by_hand_expecting_type (lookup_cached_function (cached_zl_force_link_object_file), builtin_type_int, 1, args, 1); retval = value_as_long (val); if (fix_and_continue_debug_flag) switch (retval) { case zlObjectFileUnknown: printf_filtered ("DEBUG: zlObjectFileUnknown result from ZL.\n"); break; case zlObjectFileBeingLinked: printf_filtered ("DEBUG: zlObjectFileBeingLinked result from ZL.\n"); break; case zlObjectFileAlreadyLinked: printf_filtered ("DEBUG: zlObjectFileAlreadyLinked result from ZL.\n"); break; case zlObjectFileJustLinked: printf_filtered ("DEBUG: zlObjectFileJustLinked result from ZL.\n"); break; default: printf_filtered ("DEBUG: Got unknown result from ZeroLink!"); } if (retval == zlObjectFileAlreadyLinked || retval == zlObjectFileJustLinked) return; else if (retval == zlObjectFileUnknown) warning ("ZeroLink says object file '%s' is unknown.", obj_name); else if (retval == zlObjectFileBeingLinked) warning ("ZeroLink says object file '%s' is mid-load.", obj_name); else warning ("Unrecognized result code from ZeroLink for obj file '%s'.", obj_name); } static int inferior_is_zerolinked_p (void) { int is_zl_executable = 0; if (find_objfile_by_name ( "/System/Library/PrivateFrameworks/ZeroLink.framework/Versions/A/ZeroLink")) { is_zl_executable = 1; } if (fix_and_continue_debug_flag && is_zl_executable) printf_filtered ("DEBUG: Inferior is a ZeroLink executable.\n"); return (is_zl_executable); } /* Step through all previously fixed versions of this .o file and make sure their msymbols and symbols are marked obsolete. */ static void mark_previous_fixes_obsolete (struct fixinfo *cur) { struct fixedobj *fo; struct minimal_symbol *msym; struct objfile *fo_objfile; for (fo = cur->fixed_object_files; fo != NULL; fo = fo->next) { fo_objfile = find_objfile_by_name (fo->bundle_filename); if (fo_objfile == NULL) { warning ("fixed object file entry for '%s' has a NULL objfile ptr! " "Will try continuing", fo->bundle_filename); continue; } /* Don't mark the file we just loaded as obsolete. This means that mark_previous_fixes_obsolete() must be run after get_fixed_file(). */ if (fo == cur->most_recent_fix) continue; /* Mark all minsyms obsolete except for global data - we'll leave that up to the global-data-redirecting code to handle over in redirect_statics(). */ ALL_OBJFILE_MSYMBOLS (fo_objfile, msym) if (MSYMBOL_TYPE (msym) != mst_data && MSYMBOL_TYPE (msym) != mst_bss && MSYMBOL_TYPE (msym) != mst_file_data && MSYMBOL_TYPE (msym) != mst_file_bss) { MSYMBOL_OBSOLETED (msym) = 1; } int i; struct dict_iterator j; struct symbol *sym; struct symtab *st; struct partial_symtab *pst; ALL_OBJFILE_SYMTABS_INCL_OBSOLETED (fo_objfile, st) { if (st->primary == 1) for (i = 0; i < BLOCKVECTOR_NBLOCKS (BLOCKVECTOR (st)); i++) ALL_BLOCK_SYMBOLS (BLOCKVECTOR_BLOCK (BLOCKVECTOR (st), i), j, sym) if (SYMBOL_DOMAIN (sym) != VAR_DOMAIN || SYMBOL_CLASS (sym) != LOC_STATIC) SYMTAB_OBSOLETED (st) = 51; } ALL_OBJFILE_PSYMTABS (fo_objfile, pst) { PSYMTAB_OBSOLETED (pst) = 51; } } PSYMTAB_OBSOLETED (find_original_psymtab (cur)) = 51; SYMTAB_OBSOLETED (find_original_symtab (cur)) = 51; } /* Given a source filename, either find an existing fixinfo record detailing a previous fix, or create a new one and attach it to the chain. */ static struct fixinfo * get_fixinfo_for_new_request (const char *source_filename) { struct fixinfo *i, *prev = NULL; /* Let's scan over the list to make sure there aren't any half-allocated fixinfo structures. FIXME, this is obviously a hack; see the documentation for the fixinfo->complete variable at the top of the file about why I haven't solved this yet. */ i = fixinfo_chain; while (i != NULL) { if (i->complete == 0 && prev == NULL) { fixinfo_chain = i->next; free_half_finished_fixinfo (i); i = fixinfo_chain; continue; } if (i->complete == 0) { prev->next = i->next; free_half_finished_fixinfo (i); i = fixinfo_chain; continue; } prev = i; i = i->next; } for (i = fixinfo_chain; i != NULL; i = i->next) { if (!strcmp (i->src_filename, source_filename) && i->complete) return (i); } /* Either no match or no fixinfo entries */ i = (struct fixinfo *) xmalloc (sizeof (struct fixinfo)); memset (i, 0, sizeof (struct fixinfo)); if (fixinfo_chain == NULL) fixinfo_chain = i; else i->next = fixinfo_chain; i->src_filename = source_filename; i->src_basename = getbasename (source_filename); i->complete = 0; return (i); } static void free_half_finished_fixinfo (struct fixinfo *f) { if (f->fixed_object_files != NULL) { warning ("free_half_finished_fixinfo: incomplete fixinfo was too complete"); return; } if (f->src_filename != NULL) xfree ((char *) f->src_filename); if (f->bundle_filename != NULL) xfree ((char *) f->bundle_filename); if (f->object_filename != NULL) xfree ((char *) f->object_filename); if (f->active_functions != NULL) free_active_threads_struct (f->active_functions); if (f->original_objfile_filename != NULL) xfree ((char *) f->original_objfile_filename); xfree (f); } /* Given a mostly-populated CUR, load the named object file into the program via dyld and complete the fixinfo struct, CUR. */ static void get_fixed_file (struct fixinfo *cur) { struct partial_symtab *ps = NULL; struct fixedobj *flp; struct objfile *fixedobj_objfile; struct fixedobj *fixedobj; int loaded_ok; /* Allocate a new fixedobj object for the .o file we're about to load, add it to the end of the CUR list of .o files. */ fixedobj = xmalloc (sizeof (struct fixedobj)); fixedobj->bundle_filename = cur->bundle_filename; fixedobj->firstdatum = NULL; fixedobj->lastdatum = NULL; fixedobj->obsoletedsym = NULL; fixedobj->next = NULL; /* FIXME: leak of fixedobj if load_fixed_objfile() errors out. */ loaded_ok = load_fixed_objfile (fixedobj->bundle_filename); fixedobj_objfile = find_objfile_by_name (fixedobj->bundle_filename); /* Even if the load_fixed_objfile() eventually failed, gdb may still believe a new solib was loaded successfully -- clear that out. */ if (loaded_ok != 1) { #ifdef NM_NEXTSTEP if (fixedobj_objfile != NULL) remove_objfile_from_dyld_records (fixedobj_objfile); #endif error ("NSLinkModule was not able to correctly load the Fix bundle, " "most likely due to unresolved external references."); } if (fixedobj_objfile == NULL) error ("Unable to load fixed object file '%s'.", fixedobj->bundle_filename); /* We need all of the debug symbols for the loaded objfile; override any load-rules that might have affected it. */ fixedobj_objfile = raise_objfile_load_level (fixedobj_objfile); /* Throw fixedobj on to the cur->fixed_object_files linked list. */ if (cur->fixed_object_files == NULL) cur->fixed_object_files = fixedobj; else { for (flp = cur->fixed_object_files; flp->next != NULL; flp = flp->next) ; flp->next = fixedobj; } cur->most_recent_fix = fixedobj; /* Should this psymtab expansion just be limited to the source file that we've just fixed? It wouldn't catch changes in .h files, but then again I'm afraid we could have a lot of unnecessary psymtab expansion in an environment with a lot of header files.. */ ALL_OBJFILE_PSYMTABS (fixedobj_objfile, ps) PSYMTAB_TO_SYMTAB (ps); cur->complete = 1; } /* Get the final filename component of a pathname. Returns a pointer into the middle of NAME, or a pointer to NAME. So don't go around trying to deallocate the value returned here. */ static const char * getbasename (const char *name) { const char *p = strrchr (name, '/'); if (p) return (p + 1); else return name; } /* Returns 1 if the bundle loads correctly; 0 if it did not. If 0 is returned, the objfile linked list must be pruned of this aborted objfile load. This cleanup is the responsibility of the caller. Do inferior function calls as if the inferior had done this: char *fn = "b2.o"; NSObjectFileImage objfile_ref; NSObjectFileImageReturnCode retval1; NSModule retval2; retval1 = NSCreateObjectFileImageFromFile (fn, &objfile_ref); retval2 = NSLinkModule (objfile_ref, fn, NSLINKMODULE_OPTION_PRIVATE | NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES |NSLINKMODULE_OPTION_RETURN_ON_ERROR |NSLINKMODULE_OPTION_BINDNOW); Note that objfile_ref is first passed by reference, then by value, so we need to allocate space in the inferior for that ahead of time. */ static int load_fixed_objfile (const char *name) { struct value **libraryvec, *val, **args; int i, librarylen; struct value *objfile_image_ref_memory, *objfile_image_ref; struct value *ref_to_NSCreateObjectFileImageFromFile, *ref_to_NSLinkModule; NSObjectFileImageReturnCode retval; unsigned long nslink_options; /* NSCreateObjectFileImageFromFile (NSObjectFileImage(3)) returns a pointer to an opaque structure via the 2nd argument you pass to it, which is a reference to a word of memory. */ objfile_image_ref_memory = value_allocate_space_in_inferior (sizeof (NSObjectFileImage)); ref_to_NSCreateObjectFileImageFromFile = find_function_in_inferior ("NSCreateObjectFileImageFromFile", builtin_type_int); ref_to_NSLinkModule = find_function_in_inferior ("NSLinkModule", builtin_type_int); librarylen = strlen (name); libraryvec = (struct value **) alloca (sizeof (struct value *) * (librarylen + 2)); for (i = 0; i < librarylen + 1; i++) libraryvec [i] = value_from_longest (builtin_type_char, name[i]); /* Call to NSCreateObjectFileImageFromFile() */ args = (struct value **) alloca (sizeof (struct value *) * 4); args[0] = value_array (0, librarylen, libraryvec); args[1] = objfile_image_ref_memory; args[2] = value_from_longest (builtin_type_int, 0); val = call_function_by_hand_expecting_type (ref_to_NSCreateObjectFileImageFromFile, builtin_type_int, 3, args, 1); retval = value_as_long (val); if (retval != NSObjectFileImageSuccess) { error ("NSCreateObjectFileImageFromFile failed. " "This can happen if certain QuickDraw calls are happening in a " "run loop. Stop your program with a normal breakpoint and " "re-try fix while stopped in your code."); } /* Call to NSLinkModule */ nslink_options = NSLINKMODULE_OPTION_PRIVATE | NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES | NSLINKMODULE_OPTION_RETURN_ON_ERROR; if (!inferior_is_zerolinked_p ()) nslink_options |= NSLINKMODULE_OPTION_BINDNOW; objfile_image_ref = value_at (builtin_type_CORE_ADDR, value_as_long (objfile_image_ref_memory), NULL); args[0] = objfile_image_ref; args[1] = value_array (0, librarylen, libraryvec); args[2] = value_from_longest (builtin_type_int, nslink_options); args[3] = value_from_longest (builtin_type_int, 0); val = call_function_by_hand_expecting_type (ref_to_NSLinkModule, builtin_type_int, 4, args, 1); retval = value_as_long (val); if (retval == 0) /* NSLinkModule returns NULL on failed load. */ return 0; return 1; } /* Step through a newly loaded object file's symbols looking for functions that need to be redirected and such. FIXME: Do something with file-static indirect data in the new .o file to point to the original objfile. Somehow. */ static void do_final_fix_fixups (struct fixinfo *cur) { struct objfile *most_recent_fix_objfile; struct symtab *newsymtab = NULL; struct blockvector *newbv; struct block *newblock; struct objfile **objfiles_to_update; struct cleanup *cleanups; int i; most_recent_fix_objfile = find_objfile_by_name (cur->most_recent_fix->bundle_filename); objfiles_to_update = build_list_of_objfiles_to_update (cur); cleanups = make_cleanup (xfree, objfiles_to_update); for (i = 0; objfiles_to_update[i] != NULL; i++) { ALL_OBJFILE_SYMTABS_INCL_OBSOLETED (most_recent_fix_objfile, newsymtab) { /* All code-less symtabs will have links to a single codeful symtab's blockvector. We only need to scan that blockvector once, so skip all the copy-cats. */ if (newsymtab->primary != 1) continue; /* global symbols */ newbv = BLOCKVECTOR (newsymtab); newblock = BLOCKVECTOR_BLOCK (newbv, GLOBAL_BLOCK); do_final_fix_fixups_global_syms (newblock, objfiles_to_update[i], cur); /* Static symbols (incl ObjC class functions) */ newblock = BLOCKVECTOR_BLOCK (newbv, STATIC_BLOCK); do_final_fix_fixups_static_syms (newblock, objfiles_to_update[i], cur); } } redirect_file_statics (cur); do_cleanups (cleanups); } /* With the -findirect-data compiler flag, references to file static data (globals allocated in this compilation unit, actual static data) are made indirect (which is not normally necessary). When the compilation unit is fixed, the target of these indirect pointers are redirected (by gdb) to the original static/global address locations. */ static void redirect_file_statics (struct fixinfo *cur) { struct file_static_fixups *indirect_entries; struct cleanup *wipe; int indirect_entry_count; indirect_entry_count = find_and_parse_nonlazy_ptr_sect (cur, &indirect_entries); if (indirect_entries == NULL || indirect_entry_count == 0) return; wipe = make_cleanup (xfree, indirect_entries); find_new_static_symbols (cur, indirect_entries, indirect_entry_count); find_orig_static_symbols (cur, indirect_entries, indirect_entry_count); redirect_statics (indirect_entries, indirect_entry_count); do_cleanups (wipe); } static void find_new_static_symbols (struct fixinfo *cur, struct file_static_fixups *indirect_entries, int indirect_entry_count) { struct symtab *symtab; struct block *b; struct symbol *sym; int j; struct dict_iterator i; struct objfile *most_recent_fix_objfile; most_recent_fix_objfile = find_objfile_by_name (cur->most_recent_fix->bundle_filename); for (j = 0; j < indirect_entry_count; j++) { CORE_ADDR addr = indirect_entries[j].value; int matched = 0; ALL_OBJFILE_SYMTABS (most_recent_fix_objfile, symtab) { if (symtab->primary != 1) continue; b = BLOCKVECTOR_BLOCK (BLOCKVECTOR (symtab), STATIC_BLOCK); ALL_BLOCK_SYMBOLS (b, i, sym) { if (SYMBOL_CLASS (sym) == LOC_STATIC && SYMBOL_VALUE_ADDRESS (sym) == addr) { indirect_entries[j].new_sym = sym; matched = 1; break; } } if (!matched) { b = BLOCKVECTOR_BLOCK (BLOCKVECTOR (symtab), GLOBAL_BLOCK); ALL_BLOCK_SYMBOLS (b, i, sym) { if (SYMBOL_CLASS (sym) == LOC_STATIC && SYMBOL_VALUE_ADDRESS (sym) == addr) { indirect_entries[j].new_sym = sym; matched = 1; break; } } } if (matched) { indirect_entries[j].new_msym = lookup_minimal_symbol (SYMBOL_LINKAGE_NAME (indirect_entries[j].new_sym), NULL, most_recent_fix_objfile); } } } } static void find_orig_static_symbols (struct fixinfo *cur, struct file_static_fixups *indirect_entries, int indirect_entry_count) { int i; struct symbol *new_sym, *orig_sym; struct block *static_bl, *global_bl; struct fixedobj original_file; struct fixedobj *f; struct objfile *f_objfile; struct symtab *f_symtab; original_file.bundle_filename = cur->original_objfile_filename; original_file.next = cur->fixed_object_files; f = &original_file; while (f != NULL) { /* Don't "find" the original symbol in the currently-being-loaded objfile. */ if (!strcmp (f->bundle_filename, cur->most_recent_fix->bundle_filename)) { f = f->next; continue; } f_objfile = find_objfile_by_name (f->bundle_filename); f_symtab = find_symtab_by_name (f_objfile, cur->canonical_source_filename); static_bl = BLOCKVECTOR_BLOCK (BLOCKVECTOR (f_symtab), STATIC_BLOCK); global_bl = BLOCKVECTOR_BLOCK (BLOCKVECTOR (f_symtab), GLOBAL_BLOCK); for (i = 0; i < indirect_entry_count; i++) { /* Stop searching for a variable after the first match. */ if (indirect_entries[i].original_sym != NULL) continue; new_sym = indirect_entries[i].new_sym; if (new_sym == NULL) continue; orig_sym = lookup_block_symbol (static_bl, SYMBOL_PRINT_NAME (new_sym), SYMBOL_LINKAGE_NAME (new_sym), SYMBOL_DOMAIN (new_sym)); if (orig_sym == NULL) orig_sym = lookup_block_symbol (global_bl, SYMBOL_PRINT_NAME (new_sym), SYMBOL_LINKAGE_NAME (new_sym), SYMBOL_DOMAIN (new_sym)); /* For C++ coalesced symbols, expand the scope of the search to other symtabs within this objfile. */ if (orig_sym == NULL) orig_sym = search_for_coalesced_symbol (f_objfile, new_sym); if (orig_sym) { indirect_entries[i].original_sym = orig_sym; indirect_entries[i].original_msym = lookup_minimal_symbol (SYMBOL_LINKAGE_NAME (orig_sym), NULL, f_objfile); if (indirect_entries[i].original_msym == NULL && fix_and_continue_debug_flag) { printf_unfiltered ("DEBUG: Found orig sym but unable to find orig msym for %s\n", SYMBOL_LINKAGE_NAME (orig_sym)); } } } f = f->next; } } /* This function does the actual overwriting of the indirect pointers for file statics in the module just loaded (just fixed). It changes the pointers to point to the original file's version, obsoletes the fixed file's symbol/msymbol for that static, and unobsoletes the original file's symbol/msymbol. */ static void redirect_statics (struct file_static_fixups *indirect_entries, int indirect_entry_count) { int i; char buf[TARGET_ADDRESS_BYTES]; for (i = 0; i < indirect_entry_count; i++) { if (fix_and_continue_debug_flag) { if (indirect_entries[i].addr == (CORE_ADDR) NULL) printf_filtered ("DEBUG: Static entry addr for file static #%d was zero.\n", i); if (indirect_entries[i].value == (CORE_ADDR) NULL) printf_filtered ("DEBUG: Destination addr for file static #%d was zero.\n", i); if (indirect_entries[i].new_sym == NULL) printf_filtered ("DEBUG: Could not find new symbol for static #%d\n", i); if (indirect_entries[i].new_msym == NULL) printf_filtered ("DEBUG: Could not find new msymbol for static #%d\n", i); if (indirect_entries[i].original_sym == NULL) printf_filtered ("DEBUG: Could not find original symbol for static #%d\n", i); if (indirect_entries[i].original_msym == NULL) printf_filtered ("DEBUG: Could not find original msymbol for static #%d\n", i); } if (indirect_entries[i].new_sym == NULL || indirect_entries[i].original_sym == NULL || indirect_entries[i].new_msym == NULL || indirect_entries[i].original_msym == NULL) continue; if (indirect_entries[i].value == (CORE_ADDR) NULL || indirect_entries[i].addr == (CORE_ADDR) NULL) continue; store_unsigned_integer (buf, TARGET_ADDRESS_BYTES, SYMBOL_VALUE_ADDRESS (indirect_entries[i].original_sym)); write_memory (indirect_entries[i].addr, buf, TARGET_ADDRESS_BYTES); SYMBOL_OBSOLETED (indirect_entries[i].original_sym) = 0; MSYMBOL_OBSOLETED (indirect_entries[i].original_msym) = 0; SYMBOL_OBSOLETED (indirect_entries[i].new_sym) = 1; MSYMBOL_OBSOLETED (indirect_entries[i].new_msym) = 1; if (fix_and_continue_debug_flag) printf_filtered ("DEBUG: Redirected file static %s from 0x%s to 0x%s\n", SYMBOL_PRINT_NAME (indirect_entries[i].original_sym), paddr_nz (SYMBOL_VALUE_ADDRESS (indirect_entries[i].new_sym)), paddr_nz (SYMBOL_VALUE_ADDRESS (indirect_entries[i].original_sym))); } } /* The indirect addresses are in a separate segment/section, (__IMPORT, __pointers on x86; __DATA, __nl_symbol_ptr on ppc). Find them, put them in an (xmalloced) array of file_static_fixups, and return the number of them. */ static int find_and_parse_nonlazy_ptr_sect (struct fixinfo *cur, struct file_static_fixups **indirect_entries) { struct objfile *most_recent_fix_objfile; struct obj_section *indirect_ptr_section, *j; CORE_ADDR indirect_ptr_section_start; bfd_size_type indirect_ptr_section_size; int nl_symbol_ptr_count = 0; int actual_entry_count; char *buf; struct cleanup *wipe; int i; *indirect_entries = NULL; indirect_ptr_section = NULL; most_recent_fix_objfile = find_objfile_by_name (cur->most_recent_fix->bundle_filename); ALL_OBJFILE_OSECTIONS (most_recent_fix_objfile, j) if (strcmp ("LC_SEGMENT.__IMPORT.__pointers", bfd_section_name (most_recent_fix_objfile->obfd, j->the_bfd_section)) == 0 || strcmp ("LC_SEGMENT.__DATA.__nl_symbol_ptr", bfd_section_name (most_recent_fix_objfile->obfd, j->the_bfd_section)) == 0) { indirect_ptr_section = j; break; } if (indirect_ptr_section == NULL) return 0; indirect_ptr_section_start = indirect_ptr_section->addr; indirect_ptr_section_size = indirect_ptr_section->endaddr - indirect_ptr_section->addr; if (indirect_ptr_section_size == 0) return 0; if (indirect_ptr_section_size % TARGET_ADDRESS_BYTES != 0) error ("Incorrect non-lazy symbol pointer section size!"); nl_symbol_ptr_count = indirect_ptr_section_size / TARGET_ADDRESS_BYTES; *indirect_entries = (struct file_static_fixups *) xmalloc (sizeof (struct file_static_fixups) * nl_symbol_ptr_count); buf = xmalloc (nl_symbol_ptr_count * TARGET_ADDRESS_BYTES); wipe = make_cleanup (xfree, buf); /* The following code to read an array of ints from the target and convert them to host order is from symfile.c:read_target_long_array. */ read_memory (indirect_ptr_section_start, buf, nl_symbol_ptr_count * TARGET_ADDRESS_BYTES); /* Some of these entries will point to objects outside the current object file, in which case we're not interested in them. */ actual_entry_count = 0; for (i = 0; i < nl_symbol_ptr_count; i++) { CORE_ADDR destination_address; destination_address = extract_unsigned_integer (buf + i * TARGET_ADDRESS_BYTES, TARGET_ADDRESS_BYTES); if (destination_address == 0 || find_pc_section (destination_address)->objfile != most_recent_fix_objfile) continue; (*indirect_entries)[actual_entry_count].addr = indirect_ptr_section_start + i * TARGET_ADDRESS_BYTES; (*indirect_entries)[actual_entry_count].value = destination_address; (*indirect_entries)[actual_entry_count].new_sym = NULL; (*indirect_entries)[actual_entry_count].new_msym = NULL; (*indirect_entries)[actual_entry_count].original_sym = NULL; (*indirect_entries)[actual_entry_count].original_msym = NULL; actual_entry_count++; } do_cleanups (wipe); return (actual_entry_count); } /* Build up a list of object files we need to scan to redirect old functions to the new versions. */ static struct objfile ** build_list_of_objfiles_to_update (struct fixinfo *cur) { struct fixedobj *i; struct objfile **old_objfiles; int count, j; count = 2; /* 1 for original_objfile, 1 for NULL */ for (i = cur->fixed_object_files; i != NULL; i = i->next) { if (i == cur->most_recent_fix) continue; count++; } old_objfiles = (struct objfile **) xmalloc (sizeof (struct objfile *) * count); old_objfiles[0] = find_original_object_file (cur); j = 1; for (i = cur->fixed_object_files; i != NULL; i = i->next) { if (i == cur->most_recent_fix) continue; old_objfiles[j++] = find_objfile_by_name (i->bundle_filename); } old_objfiles[j] = NULL; return (old_objfiles); } /* Look for function names in the global scope of a just-loaded object file. When found, try to find that same function name in the old object file, and stomp on that function's prologue if found. */ void do_final_fix_fixups_global_syms (struct block *newglobals, struct objfile *oldobj, struct fixinfo *curfixinfo) { struct symtab *oldsymtab; struct blockvector *oldbv; struct block *oldblock; struct symbol *oldsym = NULL; struct dict_iterator j; struct symbol *cursym, *newsym; ALL_BLOCK_SYMBOLS (newglobals, j, cursym) { newsym = lookup_block_symbol (newglobals, SYMBOL_PRINT_NAME (cursym), SYMBOL_LINKAGE_NAME (cursym), VAR_DOMAIN); /* Ignore type definitions. */ if (!newsym || SYMBOL_CLASS (newsym) == LOC_TYPEDEF) continue; if (!oldobj) continue; ALL_OBJFILE_SYMTABS_INCL_OBSOLETED (oldobj, oldsymtab) { /* All code-less symtabs will have links to a single codeful symtab's blockvector. We only need to scan that blockvector once, so skip all the copy-cats. */ if (oldsymtab->primary != 1) continue; oldbv = BLOCKVECTOR (oldsymtab); oldblock = BLOCKVECTOR_BLOCK (oldbv, GLOBAL_BLOCK); if (oldblock != newglobals) { oldsym = lookup_block_symbol (oldblock, SYMBOL_PRINT_NAME (cursym), SYMBOL_LINKAGE_NAME (cursym), VAR_DOMAIN); if (oldsym) break; } } if (!oldsym) continue; /* Fixup a function. */ if (TYPE_CODE (SYMBOL_TYPE (newsym)) == TYPE_CODE_FUNC) { if (fix_and_continue_debug_flag) printf_filtered ("DEBUG: fixed up global %s " "(newaddr 0x%s, oldaddr 0x%s)\n", SYMBOL_PRINT_NAME (newsym), paddr_nz (BLOCK_START (SYMBOL_BLOCK_VALUE (newsym))), paddr_nz (BLOCK_START (SYMBOL_BLOCK_VALUE (oldsym)))); redirect_old_function (curfixinfo, newsym, oldsym, in_active_func (SYMBOL_LINKAGE_NAME (cursym), curfixinfo->active_functions)); } } } static void do_final_fix_fixups_static_syms (struct block *newstatics, struct objfile *oldobj, struct fixinfo *curfixinfo) { struct symtab *oldsymtab; struct blockvector *oldbv; struct block *oldblock; struct symbol *oldsym = NULL; struct dict_iterator j; struct symbol *cursym, *newsym; struct objfile *original_objfile = find_original_object_file (curfixinfo); if (!oldobj) return; ALL_BLOCK_SYMBOLS (newstatics, j, cursym) { newsym = lookup_block_symbol (newstatics, SYMBOL_PRINT_NAME (cursym), SYMBOL_LINKAGE_NAME (cursym), VAR_DOMAIN); /* Ignore type definitions. */ if (!newsym || SYMBOL_CLASS (newsym) == LOC_TYPEDEF) continue; /* FIXME - skip over non-function syms */ if (TYPE_CODE (SYMBOL_TYPE (newsym)) != TYPE_CODE_FUNC) continue; ALL_OBJFILE_SYMTABS_INCL_OBSOLETED (oldobj, oldsymtab) { /* All code-less symtabs will have links to a single codeful symtab's blockvector. We only need to scan that blockvector once, so skip all the copy-cats. */ if (oldsymtab->primary != 1) continue; oldbv = BLOCKVECTOR (oldsymtab); oldblock = BLOCKVECTOR_BLOCK (oldbv, STATIC_BLOCK); if (oldblock != newstatics) { oldsym = lookup_block_symbol (oldblock, SYMBOL_PRINT_NAME (cursym), SYMBOL_LINKAGE_NAME (cursym), VAR_DOMAIN); if (oldsym) break; } } /* Maybe this static is a C++ coalesced symbol that resides in some arbitrary symtab. Try expanding the search scope a bit. */ if (!oldsym) { oldsym = search_for_coalesced_symbol (original_objfile, newsym); if (oldsym == newsym) oldsym = NULL; } if (!oldsym) continue; /* Fixup a function. */ if (TYPE_CODE (SYMBOL_TYPE (newsym)) == TYPE_CODE_FUNC) { if (fix_and_continue_debug_flag) printf_filtered ("DEBUG: fixed up static %s " "(newaddr 0x%s, oldaddr 0x%s)\n", SYMBOL_PRINT_NAME (newsym), paddr_nz (BLOCK_START (SYMBOL_BLOCK_VALUE (newsym))), paddr_nz (BLOCK_START (SYMBOL_BLOCK_VALUE (oldsym)))); redirect_old_function (curfixinfo, newsym, oldsym, in_active_func (SYMBOL_LINKAGE_NAME (cursym), curfixinfo->active_functions)); } } } /* The instructions to put a 32 bit address into a register will sign extend the value of the lower 16 bits. You put the higher 16 bits into the register with an 'addis' instructions so you need to add 1 to the higher 16 bits to arrive at the correct value. This corresponds to the "hi16()" and "lo16()" address transforms you see in assembly output. */ static uint16_t encode_lo16 (CORE_ADDR addr) { return addr & 0xffff; } static uint16_t encode_hi16 (CORE_ADDR addr) { uint16_t i; i = addr >> 16; /* is bit 15 set? */ if (addr & 0x8000) i++; return i; } static CORE_ADDR decode_hi16_lo16 (uint16_t hi16, uint16_t lo16) { CORE_ADDR addr = 0; addr = lo16; /* If the high bit of lo16 was set, we need to decrement hi16 by one. */ if (lo16 & 0x8000) hi16--; addr = addr | ((CORE_ADDR) hi16 << 16); return addr; } /* FIXME: This is a copy of objfiles.c:do_free_objfile_cleanup which is static. Maybe it shouldn't be. */ static void free_objfile_cleanup (void *obj) { free_objfile (obj); } /* Before we load an objfile via dyld, load it into gdb and check that it doesn't violate any of the easy-to-check restrictions. We fill in a good bit of the fixinfo structure as we do our job. */ static void pre_load_and_check_file (struct fixinfo *cur) { bfd *object_bfd; struct objfile *new_objfile; struct cleanup *cleanups; object_bfd = symfile_bfd_open_safe (cur->bundle_filename, 0); new_objfile = symbol_file_add_bfd_safe (object_bfd, 0, 0, 0, 0, OBJF_SYM_ALL, (CORE_ADDR) NULL, NULL); /* We need all of the debug symbols for the pre-loaded objfile; override any load-rules that might have affected it. */ new_objfile = raise_objfile_load_level (new_objfile); cleanups = make_cleanup (free_objfile_cleanup, new_objfile); force_psymtab_expansion (new_objfile, cur->src_filename, cur->src_basename); cur->active_functions = create_current_threads_list (cur->src_filename); do_pre_load_checks (cur, new_objfile); do_cleanups (cleanups); /* De-allocate the objfile we pre-loaded */ } /* Iterate through the threads linked list and all the functions in each thread's stack, to make an exhaustive list of all currently-executing functions which are being replaced by the fix command. */ static struct active_threads * create_current_threads_list (const char *source_filename) { struct thread_info *tp; struct active_threads *i, *head; struct active_func *k; struct cleanup *inferior_ptid; struct ui_out *null_uiout = NULL; char buf[80]; enum gdb_rc rc; i = head = NULL; /* FIXME: gdb_thread_select has gained a "print" parameter, so this UI redirection is superfluous. molenda/2003-04-23 */ inferior_ptid = save_inferior_ptid (); null_uiout = cli_out_new (gdb_null); if (null_uiout == NULL) { error ("Unable to open null uiout in fix-and-continue.c"); } make_cleanup_ui_out_delete (null_uiout); /* FIXME: I should use the iterate_over_threads () call. */ for (tp = thread_list; tp; tp = tp->next) { snprintf (buf, 79, "%d", tp->num); rc = gdb_thread_select (null_uiout, buf, 0); if (((int) rc < 0 && (enum return_reason) rc == RETURN_ERROR) || ((int) rc >= 0 && rc == GDB_RC_FAIL)) { /* Thread's dead, Jed. Silently continue on our way. */ continue; } k = create_current_active_funcs_list (source_filename); /* Any functions in this thread being replaced? */ if (k != NULL) { if (head == NULL) { i = head = (struct active_threads *) xmalloc (sizeof (struct active_threads)); } else { i->next = (struct active_threads *) xmalloc (sizeof (struct active_threads)); i = i->next; } i->num = tp->num; i->active_func_chain = k; i->pc = read_pc (); i->next = NULL; } } do_cleanups (inferior_ptid); return (head); } /* Check out the object file for really obvious violations, like adding a parameter to a function that is currently on the stack. When this is called, the following things should already have been done: cur->original_objfile_filename and cur->canonical_source_filename are initialized cur->active_functions is initialized for all threads the psymtab in the original and new objfiles have been expanded source_filename and bundle_filename are correct Most notably, we don't make any assumption that cur->fixed_object_files has anything in it yet (these checks are done before the object file is actually loaded into memory via dyld.) */ /* FIXME: Some of the things I need to check here: Are any functions that will be rewritten too small to have the F&C trampoline written? Is the PC in the area where I need to write a trampoline? Have the number of arguments of any functions on the stack changed? Were any of the types of those arguments changed? Have the global/statics been changed? The statics/globals in the old original file should be identical to this one. Have any types been changed? Things added to a struct, a struct turning into an int, etc. Maybe I can do a few others, but that's about what the wdb impl does. */ static void do_pre_load_checks (struct fixinfo *cur, struct objfile *new_objfile) { if (cur->original_objfile_filename == NULL || cur->canonical_source_filename == NULL) { internal_error (__FILE__, __LINE__, "do_pre_load_checks: " "Original objfile or canonical source filename not set"); } if (cur->src_filename == NULL || cur->bundle_filename == NULL) internal_error (__FILE__, __LINE__, "do_pre_load_checks: src_filename or bundle_filename not set"); /* FIXME: We're going to error() out of these funcs if there is a problem; we need a cleanup (probably at the caller of this func) to clean up the pre-loaded objfile. */ check_restrictions_globals (cur, new_objfile); check_restrictions_statics (cur, new_objfile); check_restrictions_locals (cur, new_objfile); check_restriction_cxx_zerolink (new_objfile); } static void check_restrictions_globals (struct fixinfo *cur, struct objfile *newobj) { struct objfile *oldobj = find_original_object_file (cur); struct block *oldblock, *newblock; struct symtab *oldsymtab, *newsymtab; struct symbol *oldsym, *newsym, *sym; const char *sym_source_name, *sym_linkage_name; char *old_type, *new_type; struct cleanup *wipe; ALL_OBJFILE_SYMTABS_INCL_OBSOLETED (newobj, newsymtab) { if (newsymtab->primary != 1) continue; struct dict_iterator i; newblock = BLOCKVECTOR_BLOCK (BLOCKVECTOR (newsymtab), GLOBAL_BLOCK); ALL_BLOCK_SYMBOLS (newblock, i, sym) { sym_source_name = SYMBOL_PRINT_NAME (sym); sym_linkage_name = SYMBOL_LINKAGE_NAME (sym); newsym = lookup_block_symbol (newblock, sym_source_name, sym_linkage_name, VAR_DOMAIN); oldsym = NULL; if (newsym && (SYMBOL_CLASS (newsym) != LOC_TYPEDEF)) { ALL_OBJFILE_SYMTABS_INCL_OBSOLETED (oldobj, oldsymtab) { oldblock = BLOCKVECTOR_BLOCK (BLOCKVECTOR (oldsymtab), GLOBAL_BLOCK); oldsym = lookup_block_symbol (oldblock, sym_source_name, sym_linkage_name, VAR_DOMAIN); if (oldsym) break; } } /* FIXME: Should we bail if new globals are added? */ if (!oldsym) continue; /* Functions have class LOC_BLOCK. */ if (TYPE_CODE (SYMBOL_TYPE (oldsym)) == TYPE_CODE_FUNC && TYPE_CODE (SYMBOL_TYPE (newsym)) != TYPE_CODE_FUNC) error ("Changing function '%s' to a variable is not supported.", SYMBOL_PRINT_NAME (oldsym)); if (TYPE_CODE (SYMBOL_TYPE (oldsym)) != TYPE_CODE_FUNC && TYPE_CODE (SYMBOL_TYPE (newsym)) == TYPE_CODE_FUNC) error ("Changing variable '%s' to a function is not supported.", SYMBOL_PRINT_NAME (oldsym)); if (TYPE_CODE (SYMBOL_TYPE (oldsym)) == TYPE_CODE_FUNC && TYPE_CODE (SYMBOL_TYPE (newsym)) == TYPE_CODE_FUNC) continue; old_type = type_sprint (SYMBOL_TYPE (oldsym), NULL, 0); wipe = make_cleanup (xfree, old_type); new_type = type_sprint (SYMBOL_TYPE (newsym), NULL, 0); make_cleanup (xfree, new_type); if (strcmp (old_type, new_type) != 0) error ("Changing the type of global variable '%s'" " from '%s' to '%s' is not supported.", SYMBOL_PRINT_NAME (oldsym), old_type, new_type); do_cleanups (wipe); } } } static void check_restrictions_statics (struct fixinfo *cur, struct objfile *newobj) { struct block *newblock; struct symtab *newsymtab; struct symbol *oldsym, *newsym, *sym; const char *sym_source_name, *sym_linkage_name; struct objfile *original_objfile = find_original_object_file (cur); ALL_OBJFILE_SYMTABS_INCL_OBSOLETED (newobj, newsymtab) { if (newsymtab->primary != 1) continue; struct dict_iterator i; newblock = BLOCKVECTOR_BLOCK (BLOCKVECTOR (newsymtab), STATIC_BLOCK); ALL_BLOCK_SYMBOLS (newblock, i, sym) { /* Notably: Skip STRUCT_DOMAIN until I can think of checks for it. */ if (SYMBOL_DOMAIN (sym) != VAR_DOMAIN && SYMBOL_DOMAIN (sym) != METHODS_DOMAIN) continue; sym_source_name = SYMBOL_PRINT_NAME (sym); sym_linkage_name = SYMBOL_LINKAGE_NAME (sym); newsym = lookup_block_symbol (newblock, sym_source_name, sym_linkage_name, SYMBOL_DOMAIN (sym)); /* This should be impossible. */ /* Actually, it can happen. If you're fixing a C++ program but some of the files don't end in ".cp" or ".cxx" or ".C", gdb's mangling will be disabled. The mangled sym name is passed to lookup_symbol() and it won't match anything. A reasonable workaround at this point would be to try setting the current language to language_cplus and re-executing this function... */ /* FIXME: This error message is a hack. */ if (newsym == NULL) error ("No symbol found for '%s'. " "Could this be a C++ application whose source filenames end in '.c'?", sym_source_name); if (SYMBOL_CLASS (newsym) == LOC_CONST) continue; if (TYPE_CODE (SYMBOL_TYPE (newsym)) == TYPE_CODE_FUNC) continue; /* For now, ignore all of the OBJC internal labels (_OBJC_CLASS, _OBJC_CLASS_METHODS, _OBJC_CLASS_NAME, _OBJC_CLASS_REFERENCES, _OBJC_INSTANCE_METHODS, _OBJC_INSTANCE_VARIABLES, _OBJC_METACLASS, _OBJC_METH_VAR_NAME, _OBJC_METH_VAR_TYPE, _OBJC_SELECTOR_REFERENCES, et al) These may prove to be useful to check later on, but I haven't thought this through yet, and I suspect only _OBJC_INSTANCE_METHODS and _OBJC_INSTANCE_VARIABLES will be of use. */ if (strncmp (sym_linkage_name, "_OBJC_", 6) == 0) continue; oldsym = lookup_symbol (SYMBOL_LINKAGE_NAME (newsym), NULL, SYMBOL_DOMAIN (newsym), NULL, NULL); /* oldsym == newsym, so we didn't find the symbol in the symtabs. Try a bit more searching before we assume it's a new symbol. This can easily happen in C++ where the symbol may be a coalesced sym in a symtab that hasn't been expanded from a psymtab yet. */ if (oldsym == newsym && (SYMBOL_CLASS (newsym) == LOC_STATIC || SYMBOL_CLASS (newsym) == LOC_INDIRECT)) { oldsym = search_for_coalesced_symbol (original_objfile, newsym); /* We didn't find a matching minsym in the objfile (app, library, etc.) that we're fixing. This should be a reliable indication that a new static is being added by the user. Which we'll handle by ignoring for now. */ if (oldsym == newsym || oldsym == NULL) continue; } /* A new symbol if I'm not mistaken.. Let it pass. */ if (oldsym == NULL) continue; if (SYMBOL_CLASS (oldsym) != LOC_CONST && SYMBOL_CLASS (oldsym) != LOC_TYPEDEF) { char *old_type, *new_type; struct cleanup *wipe; /* Hacky: In some programs the original static symbol type might not have resolved correctly when the original objfile was read in. So in that case, we'll give the user the benefit of the doubt and just skip the type change checks. */ if (TYPE_CODE (SYMBOL_TYPE (oldsym)) == TYPE_CODE_ERROR || TYPE_CODE (SYMBOL_TYPE (oldsym)) == TYPE_CODE_UNDEF) { warning ("Type code for '%s' unresolvable, " "skipping type change checks.", SYMBOL_PRINT_NAME (oldsym)); continue; } if (TYPE_CODE (SYMBOL_TYPE (newsym)) == TYPE_CODE_ERROR || TYPE_CODE (SYMBOL_TYPE (newsym)) == TYPE_CODE_UNDEF) { warning ("Type code for '%s' unresolvable, " "skipping type change checks.", SYMBOL_PRINT_NAME (newsym)); continue; } old_type = type_sprint (SYMBOL_TYPE (oldsym), NULL, 0); wipe = make_cleanup (xfree, old_type); new_type = type_sprint (SYMBOL_TYPE (newsym), NULL, 0); make_cleanup (xfree, new_type); if (strcmp (old_type, new_type) != 0) error ("Changing the type of file static variable '%s'" " from '%s' to '%s' is not supported.", SYMBOL_PRINT_NAME (oldsym), old_type, new_type); do_cleanups (wipe); } } } } static void check_restrictions_locals (struct fixinfo *cur, struct objfile *newobj) { struct objfile *oldobj = find_original_object_file (cur); struct block *oldblock, *newblock; struct symtab *oldsymtab, *newsymtab; struct blockvector *oldbv, *newbv; struct symbol *oldsym; const char *funcname; int active, i, j; int foundmatch; ALL_OBJFILE_SYMTABS_INCL_OBSOLETED (newobj, newsymtab) { if (newsymtab->primary != 1) continue; newbv = BLOCKVECTOR (newsymtab); for (i = FIRST_LOCAL_BLOCK; i < BLOCKVECTOR_NBLOCKS (newbv); i++) { newblock = BLOCKVECTOR_BLOCK (newbv, i); if (BLOCK_FUNCTION (newblock) == NULL) continue; funcname = SYMBOL_LINKAGE_NAME (BLOCK_FUNCTION (newblock)); if (funcname == NULL) continue; active = in_active_func (funcname, cur->active_functions); foundmatch = 0; ALL_OBJFILE_SYMTABS_INCL_OBSOLETED (oldobj, oldsymtab) { if (oldsymtab->primary != 1) continue; oldbv = BLOCKVECTOR (oldsymtab); for (j = FIRST_LOCAL_BLOCK; j < BLOCKVECTOR_NBLOCKS (oldbv); j++) { oldblock = BLOCKVECTOR_BLOCK (oldbv, j); if (BLOCK_FUNCTION (oldblock) != NULL && SYMBOL_MATCHES_NATURAL_NAME (BLOCK_FUNCTION (oldblock), funcname)) { check_restrictions_function (funcname, active, oldblock, newblock); foundmatch = 1; break; } } if (foundmatch) break; } /* This picks up the case where the function was coalesced into another symtab within the same objfile ("C++"). */ if (!foundmatch) { oldsym = search_for_coalesced_symbol (oldobj, BLOCK_FUNCTION (newblock)); if (oldsym) { oldblock = SYMBOL_BLOCK_VALUE (oldsym); if (oldblock != newblock) check_restrictions_function (funcname, active, oldblock, newblock); } } } } } static void check_restrictions_function (const char *funcname, int active, struct block *oldblock, struct block *newblock) { int newfunc_args, oldfunc_args; int newfunc_locals, oldfunc_locals; struct dict_iterator i, j; char *old_type_name, *new_type_name; struct symbol *oldsym, *newsym; struct cleanup *wipe; wipe = make_cleanup (null_cleanup, NULL); /* NB: The way I use step through the newblock and oldblock assume that the block is not sorted and is not a hashtable. I believe this is currently an accurate assertion for function blocks. */ /* Check to see that the function return type matches. */ old_type_name = type_sprint (SYMBOL_TYPE (BLOCK_FUNCTION (oldblock)), NULL, 0); make_cleanup (xfree, old_type_name); new_type_name = type_sprint (SYMBOL_TYPE (BLOCK_FUNCTION (newblock)), NULL, 0); make_cleanup (xfree, new_type_name); if (strcmp (old_type_name, new_type_name) != 0) error ("Function '%s' was changed from returning '%s' to '%s', which is " "not supported.", funcname, old_type_name, new_type_name); /* Count # of args, locals in old and new blocks. */ oldfunc_args = oldfunc_locals = 0; ALL_BLOCK_SYMBOLS (oldblock, i, oldsym) { if (sym_is_argument (oldsym)) oldfunc_args++; else if (sym_is_local (oldsym)) oldfunc_locals++; } newfunc_args = newfunc_locals = 0; ALL_BLOCK_SYMBOLS (newblock, i, newsym) { if (sym_is_argument (newsym)) newfunc_args++; else if (sym_is_local (newsym)) newfunc_locals++; } if (oldfunc_args != newfunc_args) error ("Changing number of arguments from %d to %d for function '%s' is " "not supported.", oldfunc_args, newfunc_args, funcname); if (active && oldfunc_locals != newfunc_locals) error ("Changing number of local variables from %d to %d for function '%s'" " while active on the stack is not supported.", oldfunc_locals, newfunc_locals, funcname); /* Check that arguments have matching types. */ /* In the following loop, we can access the symbols in the block structure directly, but if the block structure is ever replaced with the proposed 'dictionary' structure, we'll need to maintain concurrent accessor iterators to step through both the old and new blocks in tandem. So I approximate that behavior with the two integers i and j. molenda 2003-04-16. */ for (oldsym = dict_iterator_first (BLOCK_DICT (oldblock), &i), newsym = dict_iterator_first (BLOCK_DICT (newblock), &j); oldsym && newsym; oldsym = dict_iterator_next (&i), newsym = dict_iterator_next (&j)) { /* FIXME: Still need to think through the "type equivalency" checks here. Check by type, or by memory size? Not sure yet. */ old_type_name = type_sprint (SYMBOL_TYPE (oldsym), NULL, 0); make_cleanup (xfree, old_type_name); new_type_name = type_sprint (SYMBOL_TYPE (newsym), NULL, 0); make_cleanup (xfree, new_type_name); if (sym_is_argument (oldsym) && strcmp (old_type_name, new_type_name) != 0) { error ("In function '%s', argument '%s' changed from " "type '%s' to type '%s', which is not supported.", funcname, SYMBOL_PRINT_NAME (oldsym), old_type_name, new_type_name); } } do_cleanups (wipe); } /* C++ programs must use ZeroLink, which implies using a shared libstdc++. The static libstdc++ private extern functions cannot be found by dyld after the program is linked together in a traditional link, so the fixed bundle cannot bind to them. ZeroLink has a shared libstdc++ to deal with these very issues. */ static void check_restriction_cxx_zerolink (struct objfile *obj) { struct symtab *s; if (inferior_is_zerolinked_p ()) return; ALL_OBJFILE_SYMTABS (obj, s) if (s->primary && (s->language == language_cplus || s->language == language_objcplus)) error ("Target is a C++ program that is not using ZeroLink. " "This is not supported. To use Fix and Continue on a C++ program, " "enable ZeroLink."); } static int sym_is_argument (struct symbol *s) { return (SYMBOL_CLASS (s) == LOC_ARG || SYMBOL_CLASS (s) == LOC_REF_ARG || SYMBOL_CLASS (s) == LOC_REGPARM || SYMBOL_CLASS (s) == LOC_REGPARM_ADDR || SYMBOL_CLASS (s) == LOC_BASEREG_ARG); } static int sym_is_local (struct symbol *s) { return (SYMBOL_CLASS (s) == LOC_LOCAL || SYMBOL_CLASS (s) == LOC_REGISTER || SYMBOL_CLASS (s) == LOC_BASEREG); } /* Expand the partial symtabs for the named source file in the given objfile. If an alternate source filename is provided, that one is searched for as well. */ static void force_psymtab_expansion (struct objfile *obj, const char *source_fn, const char *alt_source_fn) { struct partial_symtab *ps; /* Iterate over the objfile, expanding anything that looks like it might be the file we're interested in. Expand anything that looks like a match--expanding too many isn't a travesty, but expanding none would be Bad. */ ALL_OBJFILE_PSYMTABS_INCL_OBSOLETED (obj, ps) if (!strcmp (source_fn, ps->filename)) PSYMTAB_TO_SYMTAB (ps); else if (ps->fullname != NULL && !strcmp (source_fn, ps->fullname)) PSYMTAB_TO_SYMTAB (ps); else if (alt_source_fn != NULL && !strcmp (alt_source_fn, ps->filename)) PSYMTAB_TO_SYMTAB (ps); else if (alt_source_fn != NULL && ps->fullname != NULL && !strcmp (alt_source_fn, ps->filename)) PSYMTAB_TO_SYMTAB (ps); } /* Expand all partial symtabs for all source files in an objfile (application, library). In C++, coalesced symbols will end up in an arbitrary symtab, so we'll need to expand all of them to find it. */ static void expand_all_objfile_psymtabs (struct objfile *obj) { struct partial_symtab *pst = obj->psymtabs; ALL_OBJFILE_PSYMTABS_INCL_OBSOLETED (obj, pst) PSYMTAB_TO_SYMTAB (pst); } /* Returns 1 if the file is found. 0 if error or not found. */ static int file_exists_p (const char *filename) { struct stat sb; if (stat (filename, &sb) != 0) return 0; if ((sb.st_mode & S_IFMT) | S_IFREG || (sb.st_mode & S_IFMT) | S_IFLNK) return 1; else return 0; } /* Free all active_func structures in every active_threads structure */ static void free_active_threads_struct (struct active_threads *head) { struct active_threads *i = head, *j; struct active_func *k, *l; while (i != NULL) { k = head->active_func_chain; while (k != NULL) { xfree (SYMBOL_LINKAGE_NAME (k->sym)); if (SYMBOL_CPLUS_DEMANGLED_NAME (k->sym)) xfree (SYMBOL_CPLUS_DEMANGLED_NAME (k->sym)); xfree (k->sym); if (k->file != NULL) xfree ((void *)k->file); if (k->dir != NULL) xfree ((void *)k->dir); l = k->next; xfree (k); k = l; } j = i->next; xfree (i); i = j; } } /* Find the chain of active functions the current thread. */ static struct active_func * create_current_active_funcs_list (const char *source_filename) { struct active_func * function_chain = NULL; struct frame_info *fi; struct symbol *sym; struct active_func *func; struct symtab_and_line sal; for (fi = get_current_frame (); fi != NULL; fi = get_prev_frame (fi)) { sal = find_pc_line (get_frame_pc (fi), 0); if (sal.symtab == NULL) continue; if ((!strcmp (source_filename, sal.symtab->filename)) || (!strcmp (getbasename (source_filename), getbasename (sal.symtab->filename)))) { sym = find_pc_function (get_frame_pc (fi)); if (sym != 0) { func = xmalloc (sizeof (struct active_func)); func->level = frame_relative_level (fi); func->line = sal.line; func->file = xstrdup (sal.symtab->filename); func->dir = xstrdup (sal.symtab->dirname); func->fp = get_frame_base (fi); func->addr = get_frame_pc (fi); /* The following copies (and the related free()s in free_active_threads_struct()) should not be necessary, except that in some circumstances we seem to be accidentally picking up the pre-loaded test objfile, which gets freed shortly hereafter... */ func->sym = (struct symbol *) xmalloc (sizeof (struct symbol)); memcpy (func->sym, sym, sizeof (struct symbol)); SYMBOL_LINKAGE_NAME (func->sym) = xstrdup (SYMBOL_LINKAGE_NAME (sym)); if (SYMBOL_CPLUS_DEMANGLED_NAME(sym)) SYMBOL_CPLUS_DEMANGLED_NAME (func->sym) = xstrdup (SYMBOL_CPLUS_DEMANGLED_NAME (sym)); if (function_chain == NULL) func->next = NULL; else func->next = function_chain; function_chain = func; } } } return (function_chain); } /* Is a function NAME currently executing? */ static int in_active_func (const char *name, struct active_threads *threads) { struct active_func *func; for (; threads != NULL; threads = threads->next) { for (func = threads->active_func_chain; func != NULL; func = func->next) { if (SYMBOL_MATCHES_NATURAL_NAME (func->sym, name)) return 1; } } return 0; } /* Record the value of a memory location, and update it with the new value. */ static void updatedatum (struct fixinfo *fixinfo, CORE_ADDR addr, char *newval, int size) { struct fixeddatum * fixeddatum; char buf [8]; int oldval; target_read_memory (addr, buf, size); oldval = extract_unsigned_integer (buf, size); if (target_write_memory (addr, newval, size)) error ("Can't redirect function"); fixeddatum = xmalloc (sizeof (struct fixeddatum)); fixeddatum->next = NULL; fixeddatum->addr = addr; fixeddatum->size = size; fixeddatum->oldval = oldval; fixeddatum->newval = *newval; if (fixinfo->most_recent_fix->firstdatum == NULL) fixinfo->most_recent_fix->firstdatum = fixinfo->most_recent_fix->lastdatum = fixeddatum; else { fixinfo->most_recent_fix->lastdatum->next = fixeddatum; fixinfo->most_recent_fix->lastdatum = fixeddatum; } } /* Redirect a function to its new definition, update the gdb symbols so the now-obsolete ones are marked as such. This function assumes that checks have already been made to assure that the function is large enough to contain the trampoline, and that the PC isn't presently in the middle of the code we're overwriting. */ static void redirect_old_function (struct fixinfo *fixinfo, struct symbol *new_sym, struct symbol *old_sym, int active) { int inst; CORE_ADDR oldfuncstart, oldfuncend, newfuncstart, fixup_addr; struct minimal_symbol *msym; struct obsoletedsym *obsoletedsym; oldfuncstart = BLOCK_START (SYMBOL_BLOCK_VALUE (old_sym)); oldfuncend = BLOCK_END (SYMBOL_BLOCK_VALUE (old_sym)); newfuncstart = BLOCK_START (SYMBOL_BLOCK_VALUE (new_sym)); fixup_addr = oldfuncstart; #if defined (TARGET_POWERPC) /* li r12,lo16(newfuncstart) */ inst = 0x39800000 | encode_lo16 (newfuncstart); updatedatum (fixinfo, fixup_addr, (char *)&inst, 4); /* addis r12,r12,hi16(newfuncstart) */ inst = 0x3d8c0000 | encode_hi16 (newfuncstart); updatedatum (fixinfo, fixup_addr + 4, (char *)&inst, 4); /* mtctr r12 - move contents of r12 (newfuncstart) to count register */ inst = 0x7d8903a6; updatedatum (fixinfo, fixup_addr + 8, (char *)&inst, 4); /* bctr - branch unconditionally to count reg, don't update link reg */ inst = 0x4e800420; updatedatum (fixinfo, fixup_addr + 12, (char *)&inst, 4); /* .long 0 - Illegal instruction for trampoline detection */ inst = 0x0; updatedatum (fixinfo, fixup_addr + 16, (char *)&inst, 4); #endif #if defined (TARGET_I386) unsigned char buf[6]; uint32_t relative_offset; buf[0] = 0xe9; /* jmp */ buf[5] = 0xcc; /* int 3 */ relative_offset = (uint32_t) newfuncstart - (oldfuncstart + 5); store_unsigned_integer (buf + 1, TARGET_ADDRESS_BYTES, relative_offset); target_write_memory (oldfuncstart, buf, 6); #endif SYMBOL_OBSOLETED (old_sym) = 1; msym = lookup_minimal_symbol_by_pc (oldfuncstart); if (msym) MSYMBOL_OBSOLETED (msym) = 1; obsoletedsym = xmalloc (sizeof (struct obsoletedsym)); obsoletedsym->oldsym = old_sym; obsoletedsym->newsym = new_sym; obsoletedsym->oldmsym = msym; msym = lookup_minimal_symbol_by_pc (newfuncstart); obsoletedsym->newmsym = msym; if (fixinfo->most_recent_fix->obsoletedsym == NULL) obsoletedsym->next = NULL; else obsoletedsym->next = fixinfo->most_recent_fix->obsoletedsym; fixinfo->most_recent_fix->obsoletedsym = obsoletedsym; } /* Detect a Fix and Continue trampoline. Returns 0 if this is not a F&C trampoline; returns the destination address if it is. */ CORE_ADDR decode_fix_and_continue_trampoline (CORE_ADDR pc) { #if defined (TARGET_POWERPC) uint16_t newpc_lo16, newpc_hi16; int buf; /* li r12,lo16(destination-address) */ buf = read_memory_unsigned_integer (pc, 4); if ((buf & 0x39800000) != 0x39800000) return 0; newpc_lo16 = buf & 0xffff; /* addis r12,r12,hi16(destination-address) */ buf = read_memory_unsigned_integer (pc + 4, 4); if ((buf & 0x3d8c0000) != 0x3d8c0000) return 0; newpc_hi16 = buf & 0xffff; /* mtctr r12 */ buf = read_memory_unsigned_integer (pc + 8, 4); if (buf != 0x7d8903a6) return 0; /* bctr */ buf = read_memory_unsigned_integer (pc + 12, 4); if (buf != 0x4e800420) return 0; /* .long 0 */ buf = read_memory_unsigned_integer (pc + 16, 4); if (buf != 0x0) return 0; return decode_hi16_lo16 (newpc_hi16, newpc_lo16); #endif #if defined (TARGET_I386) /* Detect the x86 F&C trampoline sequence. */ unsigned char buf[5]; uint32_t current_pc, relative_offset; target_read_memory (pc, buf, 5); /* jmp <32-bit-relative-addr> */ if (buf[0] != 0xe9) return 0; /* int 3 */ if (buf[5] != 0xcc) return 0; relative_offset = extract_unsigned_integer (buf + 1, 4); current_pc = pc; /* 64bit x86 unsafe, we're truncating the top 32 bits */ current_pc += 5; /* Offset is from next instruction */ return (uint32_t) current_pc + relative_offset; #endif } /* Print all of the functions that are currently on the stack which were just replaced, across all threads. This is only intended for MI outputs where the UI can use this list to indicate things to the user. */ static void print_active_functions (struct fixinfo *cur) { struct active_threads *th; struct active_func *fn; struct cleanup *uiout_cleanup; if (!ui_out_is_mi_like_p (uiout)) return; uiout_cleanup = make_cleanup_ui_out_list_begin_end (uiout, "replaced-functions"); for (th = cur->active_functions; th != NULL; th = th->next) { struct cleanup *uiout_one_thread_cleanup; uiout_one_thread_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout, NULL); ui_out_field_int (uiout, "thread-id", th->num); make_cleanup_ui_out_list_begin_end (uiout, "replaced"); for (fn = th->active_func_chain; fn != NULL; fn = fn->next) { struct cleanup *uiout_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout, "frame"); ui_out_field_int (uiout, "level", fn->level); ui_out_field_core_addr (uiout, "addr", fn->addr); ui_out_field_core_addr (uiout, "fp", fn->fp); ui_out_field_string (uiout, "func", SYMBOL_PRINT_NAME (fn->sym)); ui_out_field_string (uiout, "file", fn->file); ui_out_field_int (uiout, "line", fn->line); ui_out_field_string (uiout, "dir", fn->dir); do_cleanups (uiout_cleanup); } do_cleanups (uiout_one_thread_cleanup); } do_cleanups (uiout_cleanup); } /* When doing a fix-and-continue and a replaced function is at frame 0, the PC is being moved from an old version of a function to a new version via a -thread-set-pc MI command. Within a function, gcc assumes that the PIC base, once set in the prologue, is constant and usable throughout the lifetime of the function. If we change the $pc to point to the new version of the function, but do not update the PIC base register, the function will soon crash. The function that contains the new $pc setting is passed to update_picbase_register as NEW_FUN. The correct thread should be selected before update_picbase_register is called. This is entirely MacOS X specific. */ void update_picbase_register (struct symbol *new_fun) { #if defined (TARGET_POWERPC) ppc_function_properties props; CORE_ADDR ret; int pic_base_reg; CORE_ADDR pic_base_value; ppc_clear_function_properties (&props); ret = ppc_parse_instructions (BLOCK_START (SYMBOL_BLOCK_VALUE (new_fun)), BLOCK_END (SYMBOL_BLOCK_VALUE (new_fun)), &props); pic_base_reg = props.pic_base_reg; pic_base_value = props.pic_base_address; /* FIXME: It is possible to have a function without any PIC base used (an empty stub function, like slurry() is in the current fix-small-c test case), so for now I'll silently do nothing. This may not be a good choice -- I'm not distinguishing between a function that doesn't have a PIC base and a failure to find the PIC base. */ if (pic_base_reg == 0 || pic_base_value == INVALID_ADDRESS) return; write_register (pic_base_reg, pic_base_value); #endif #if defined (TARGET_I386) /* Find & update the x86 PIC base register if one is used. */ enum i386_regnum pic_base_reg; CORE_ADDR pic_base_value; if (i386_find_picbase_setup (BLOCK_START (SYMBOL_BLOCK_VALUE (new_fun)), &pic_base_value, &pic_base_reg)) { if (fix_and_continue_debug_flag) printf_filtered ("DEBUG: updating picbase in register %d to 0x%s\n", pic_base_reg, paddr_nz (pic_base_value)); write_register (pic_base_reg, pic_base_value); } #endif } static struct objfile * find_objfile_by_name (const char *name) { struct objfile *obj; ALL_OBJFILES (obj) if (!strcmp (name, obj->name)) return obj; /* In a cached symfile case, the objfile 'name' member will be the name of the cached symfile, not the object file. The objfile's bfd's filename, however, will be the name of the actual object file. So we'll search those as a back-up. */ ALL_OBJFILES (obj) if (obj->obfd && !strcmp (name, obj->obfd->filename)) return obj; return NULL; } static struct symtab * find_symtab_by_name (struct objfile *obj, const char *name) { struct symtab *s; struct partial_symtab *ps; ALL_SYMTABS_INCL_OBSOLETED (obj, s) if (!strcmp (name, s->filename)) return s; ALL_PSYMTABS (obj, ps) if (!strcmp (name, ps->filename)) return (PSYMTAB_TO_SYMTAB (ps)); return NULL; } /* When we do symbol lookup for a sym in the newly fixed file, usually we can find the matching symbol in the symtab (the file) that we're replacing/fixing. With C++, some symbols (inlined functions or template functions for example) are emitted in each .o file, and the linker coalesces them into one symbol which is associated with an arbitrary .o file in the executable. So when searching for a symbol with static visibility, if we don't find it in the original symtab, it might be one of these coalesced symbols and we need to search all the symtabs in the objfile. No special concern is needed for the ZeroLink case -- in that case, each source file symtab is its own objfile, and each one will have its own copy of all these coalesced items. FIXME: I'm searching the minsyms right now, but it would be more reliable to base this off of the partial symtabs. symtab.c doesn't expose either of the two psymtab-searching functions globally, though, so for now I'll just use minsyms. */ static struct symbol * search_for_coalesced_symbol (struct objfile *obj, struct symbol *sym) { struct minimal_symbol *minsym; minsym = lookup_minimal_symbol (SYMBOL_LINKAGE_NAME (sym), 0, obj); if (minsym) { /* It's in there somewhere... expand symtabs and re-search. */ expand_all_objfile_psymtabs (obj); return (lookup_symbol (SYMBOL_LINKAGE_NAME (sym), NULL, SYMBOL_DOMAIN (sym), NULL, NULL)); } return (NULL); } static void restore_language (void *arg) { enum language l = (enum language) arg; set_language (l); } static struct cleanup * set_current_language (const char *filename) { enum language save_language; enum language new_language; save_language = current_language->la_language; new_language = deduce_language_from_filename (filename); if (new_language == save_language) return (make_cleanup (null_cleanup, 0)); set_language (new_language); return (make_cleanup (restore_language, (void *) save_language)); } /* Given the SRC_FILENAME and SRC_BASENAME fields of the CUR structure being set already, scan through all known source files to determine which object file contains the *original* version of this source file. Updates the CUR structure with the correct filename. The ORIGINAL_OBJFILE_FILENAME is malloc()'ed here, so it should be freed if the structure is ever freed. CANONICAL_SOURCE_FILENAME is just a pointer to one of the other filenames so it should not be freed. */ static void find_original_object_file_name (struct fixinfo *cur) { struct objfile *obj; struct partial_symtab *ps; if (cur->original_objfile_filename != NULL && cur->canonical_source_filename != NULL) return; ALL_PSYMTABS (obj, ps) if (!strcmp (ps->filename, cur->src_filename) || (ps->fullname != NULL && !strcmp (ps->fullname, cur->src_filename))) { /* FIXME: The cond is to guard against a probably bug in the Apple gdb sources where we have two psymtabs, 2003-03-17/jmolenda */ if (ps->texthigh != 0 && (strcmp (ps->objfile->name, cur->bundle_filename) != 0)) { PSYMTAB_TO_SYMTAB (ps); cur->original_objfile_filename = xstrdup (obj->name); cur->canonical_source_filename = cur->src_filename; return; } } /* Now try searching for just the filename without the path. There is a good chance that this could match the wrong file, but we'll use it as a backup scheme. */ ALL_PSYMTABS (obj, ps) if (!strcmp (ps->filename, cur->src_basename) || (ps->fullname != NULL && !strcmp (ps->fullname, cur->src_basename))) { /* FIXME: The cond is to guard against a probably bug in the Apple gdb sources where we have two psymtabs, 2003-03-17/jmolenda */ if (ps->texthigh != 0 && (strcmp (ps->objfile->name, cur->bundle_filename) != 0)) { PSYMTAB_TO_SYMTAB (ps); cur->original_objfile_filename = xstrdup (obj->name); cur->canonical_source_filename = cur->src_basename; return; } } cur->original_objfile_filename = NULL; cur->canonical_source_filename = NULL; error ("Unable to find original source file %s. " "Target built without debugging symbols?", cur->src_basename); } static struct objfile * find_original_object_file (struct fixinfo *cur) { if (cur->original_objfile_filename == NULL) error ("find_original_object_file() called with an empty filename!"); return find_objfile_by_name (cur->original_objfile_filename); } static struct symtab * find_original_symtab (struct fixinfo *cur) { return PSYMTAB_TO_SYMTAB (find_original_psymtab (cur)); } static struct partial_symtab * find_original_psymtab (struct fixinfo *cur) { struct objfile *original_objfile; struct partial_symtab *pst; original_objfile = find_original_object_file (cur); if (original_objfile == NULL) error ("Unable to find original object file!"); ALL_OBJFILE_PSYMTABS_INCL_OBSOLETED (original_objfile, pst) if (!strcmp (pst->filename, cur->canonical_source_filename)) return pst; error ("Unable to find original source file '%s'! " "Target compiled without debug information?", cur->canonical_source_filename); } /* Raise the load level of a struct objfile to OBJF_SYM_ALL if it isn't already, and return a pointer to the (potentially new) objfile. When the symbol load level is raised a new objfile is created, so be sure to not hold on to a pointer to the old one. Either re-find the objfile, or use the return value of this function. When the objfile's load level is raised from e.g. OBJF_SYM_EXTERN, the new OBJF_SYM_ALL objfile will have the usual psymtabs set up and ready for the usual expansions. */ static struct objfile * raise_objfile_load_level (struct objfile *obj) { const char *name; struct cleanup *wipe; if (obj == NULL || obj->symflags == OBJF_SYM_ALL) return obj; name = xstrdup (obj->name); wipe = make_cleanup (xfree, (char *) name); objfile_set_load_state (obj, OBJF_SYM_ALL, 1); obj = find_objfile_by_name (name); do_cleanups (wipe); return (obj); } /* This function returns 1 if fix and continue is supported, 0 if it isn't supported, and -1 if it is unable to make the determination. */ int fix_and_continue_supported (void) { /* Don't have a binary specified OR we've attached to a process and exec_bfd hasn't been set up for us. */ if (exec_bfd == NULL) return -1; /* F&C is not supported on ppc64 yet. */ if (gdbarch_lookup_osabi (exec_bfd) == GDB_OSABI_DARWIN64) return 0; return 1; } void _initialize_fix (void) { struct cmd_list_element *c; c = add_com ("fix", class_files, fix_command, "Bring in a fixed objfile."); set_cmd_completer (c, filename_completer); c = add_set_cmd ("fix-and-continue", class_obscure, var_boolean, (char *) &fix_and_continue_debug_flag, "Set if GDB prints debug information while Fix and Continuing.", &setdebuglist); add_show_from_set (c, &showdebuglist); }