[RFA,6/9] Explicit locations v2 - Add explicit locations

Message ID 54076CD1.9090505@redhat.com
State New, archived
Headers

Commit Message

Keith Seitz Sept. 3, 2014, 7:32 p.m. UTC
  On 08/02/2014 06:49 PM, Doug Evans wrote:
> Keith Seitz writes:
>
>   > @@ -10010,6 +10022,20 @@ create_breakpoint (struct gdbarch *gdbarch,
>   >        init_raw_breakpoint_without_location (b, gdbarch, type_wanted, ops);
>   >        b->location = copy_event_location (location);
>   >
>   > +      /* If the location has a string representation,
>   > +	 save it to the breakpoint's location string cache, since this
>   > +	 may be used to save the breakpoint to a file.  */
>   > +      if (EVENT_LOCATION_TYPE (b->location) == EXPLICIT_LOCATION)
>   > +	{
>   > +	  char *old = event_location_to_string_const (location);
>   > +
>   > +	  b->location->as_string
>   > +	    = xstrprintf ("%s%s%s", old,
>   > +			  (extra_string == NULL ? "" : " "),
>   > +			  (extra_string == NULL ? "" : extra_string));
>
> Here's another case where a file outside of the event-location-specific code
> is reaching into the internal implementation details of an event location.
>

I've added a function for this.

> Plus it's a bit confusing to do this:
>
>        b->location = copy_event_location (location);
>
> and then change the as_string representation of the location we just copied,
> but only for EXPLICIT_LOCATION.  E.g., As the reader I'm left wondering
> what happens to extra_string if the event location was a different kind.
>
> Can you elaborate on what's going on here?

I've changed this a bit... If extra_string is not NULL, the code now 
ALWAYS appends this to the string representation it saves. This block of 
code was moved earlier where the pending breakpoint was first detected 
(in the big switch block after TRY_CATCH).

However, this string representation must still be saved to the (pending) 
breakpoint's location. Since we don't have a resolved 
breakpoint/location, we need a way to serialize the given location. That 
was computed earlier (again the big switch statement). So now, the code 
uses that.

>   > +
>   > +      /* For explicit locations, EXTRA_STRING may contain breakpoint
>   > +	 condition information.  Make a private copy of this for the
>   > +	 breakpoint.  */
>
> The comment predicates itself on being specific to explicit locations,
> but the code doesn't check for EXPLICIT_LOCATION.
>
> Can you elaborate on what's going on here?

This was removed. [Or more properly, it was moved, as I discuss right 
above.]

>   > @@ -14253,11 +14290,12 @@ update_static_tracepoint (struct breakpoint *b, struct symtab_and_line sal)
>   >  	  b->loc->symtab = sym != NULL ? sal2.symtab : NULL;
>   >
>   >  	  delete_event_location (b->location);
>   > -	  b->location = new_linespec_location (NULL);
>   > -	  EVENT_LOCATION_LINESPEC (b->location)
>   > -	    = xstrprintf ("%s:%d",
>   > -			  symtab_to_filename_for_display (sal2.symtab),
>   > -			  b->loc->line_number);
>   > +	  initialize_explicit_location (&explicit);
>   > +	  explicit.source_filename
>   > +	    = ASTRDUP (symtab_to_filename_for_display (sal2.symtab));
>   > +	  explicit.line_offset.offset = b->loc->line_number;
>   > +	  explicit.line_offset.sign = LINE_OFFSET_NONE;
>   > +	  b->location = new_explicit_location (&explicit);
>
> Hmmm, the name new_explicit_location is ambiguous.
> Does it return an explicit_location* or an event_location* ?
> I like the consistency with new_linespec_location, new_address_location, etc.
> so I understand why it's the latter, though my first guess would be the former
> (though that would be confusing given that we're passing it an
> explicit_location*).
> No need to change anything, just thinking out loud.

Yeah, there's a lot of unfortunate naming fallout. As you correctly 
deduce, I kept the ctors names consistent. Likewise with the 
get_*_locations:

function                 takes                returns
--------                 -----                -------
new_linespec_location    char*                event_location*
get_linespec_location    event_location*      char*
new_address_location     CORE_ADDR            event_location*
get_address_location     event_location*      CORE_ADDR
new_probe_location       char*                event_location*
get_probe_location       event_location*      char*
new_explicit_location    explicit_location*   event_location*
get_explicit_location    event_location*      explicit_location*

>   > -  /* The user-supplied source filename or NULL if none was specified.  */
>   > -  const char *source_filename;
>   > +  /* An explicit location describing the SaLs.  */
>   > +  struct explicit_location explicit;
>
> Does this read better to you? [here, and at all uses]
>
>    struct explicit_location location;
>
> Dunno.

My convention has been to reserve the generic "location" as meaning an 
(struct) event_location.

> When I read "explicit" I'm left wondering "Does that mean there's an implicit
> or some other possibility for the location that I have to think about?"

In this context, of course, the answer is "no." :-) An explicit location 
is an explicit location. It can be nothing else. If it said (instead) 
"struct event_location explicit;" I would agree -- that *does/would* 
leave me wondering about other possibilities. But then I've been staring 
at this for so long, I probably take (too much) for granted!

> The struct definition is local to linespec.c so the scope of any confusion
> is nicely confined, so I wouldn't make any change unless you think
> it's worth it.

I could change it to "explicit_location"?

>   > @@ -1761,42 +1739,19 @@ linespec_parse_basic (linespec_parser *parser)
>   >  }
>   >
>   >  /* Canonicalize the linespec contained in LS.  The result is saved into
>   > -   STATE->canonical.  */
>   > +   STATE->canonical.  This function handles both linespec and explicit
>   > +   locations.  */
>   >
>   >  static void
>   >  canonicalize_linespec (struct linespec_state *state, linespec_p ls)
>   >  {
>   > -  struct ui_file *buf;
>   > -  int need_colon = 0;
>   > -
>   >    /* If canonicalization was not requested, no need to do anything.  */
>   >    if (!state->canonical)
>
> No requested change here.
> I just wanted to note that here's a good example where
> "if (state->canonical == NULL)" would be clearer.
> At this point in my reading for half a second I wondered if canonical was
> a boolean, and was confused because the function comment said the result
> was stored there.

FYI, I didn't change that line. I've been called-out many times on 
making unrelated/superfluous changes, so I've left it (and other similar 
places) alone.

>   > @@ -1805,29 +1760,22 @@ canonicalize_linespec (struct linespec_state *state, linespec_p ls)
>   >  		      && (VEC_length (symbolp, ls->labels.function_symbols)
>   >  			  == 1));
>   >  	  s = VEC_index (symbolp, ls->labels.function_symbols, 0);
>   > -	  fputs_unfiltered (SYMBOL_NATURAL_NAME (s), buf);
>   > -	  fputc_unfiltered (':', buf);
>   > +	  ls->explicit.function_name = xstrdup (SYMBOL_NATURAL_NAME (s));
>
> This is a side-effect not adequately explained in the function comment.
> It's not clear from the function comment that LS is modified.
> Is the side-effect necessary (in which case let's make the function
> comment clearer), or can we avoid it?

I agree. I don't know why I did that. To be more strict, I've changed 
this function to take a const linespec_p. Obviously, what I meant to do 
(or should have done) is canonicalize the canonical location in STATE.

>   > -  EVENT_LOCATION_LINESPEC (state->canonical->location)
>   > -    = ui_file_xstrdup (buf, NULL);
>   > -  ui_file_delete (buf);
>   > +  /* Save a string representation of this linespec.  */
>   > +  if (state->is_linespec)
>   > +    state->canonical->location->as_string
>   > +      = explicit_location_to_linespec (&ls->explicit);
>   > +  else
>   > +    state->canonical->location->as_string
>   > +      = explicit_location_to_string (&ls->explicit);
>
> Why do we need to set as_string here again?
> Can users of it call event_location_to_string themselves?
> There's still the state->is_linespec distinction of course,
> feels odd to have it though: something outside of an event_location
> is deciding what to store there, feels like an abstraction violation.

Yes, this is one of the scenarios I discussed in a previous revision 
(#2/9). Since all linespecs are converted to explicit form /and/ we 
compute string serializations of event_locations on demand, we need a 
way to discern between explicit and linespec locations.

There are two ways to do this:
1) Add a flag to event_location to indicate that this location really 
represents a linespec (and use that flag later when computing the string)
2) Save the linespec representation when the linespec is converted to 
explicit.

Not seeing any immediate advantage, I've chosen #2.

>   > +/* Convert the explicit location EXPLICIT into SaLs.  */
>   > +
>   > +static struct symtabs_and_lines
>   > +convert_explicit_location_to_sals (struct linespec_state *self,
>   > +				   linespec_p result,
>   > +				   struct explicit_location *explicit)
>
> How many of these parameters can be const?

Only the last. [I've changed that in this revision.] Obviously we need 
to be able to manipulate RESULT, but other functions need to use SELF, 
too, for storing symbol search iterator information.

>   > @@ -82,6 +93,49 @@ new_probe_location (const char *probe)
>   >    return location;
>   >  }
>   >
>   > +/* Create a new explicit location.  If not NULL, EXPLICIT is checked for
>   > +   validity.  If invalid, an exception is thrown.
>   > +
>   > +   The return result is malloc'd and should be freed with
>   > +   delete_event_location.  */
>   > +
>   > +struct event_location *
>   > +new_explicit_location (const struct explicit_location *explicit)
>   > +{
>   > +  struct event_location tmp;
>   > +
>   > +  initialize_event_location (&tmp, EXPLICIT_LOCATION);
>   > +
>   > +  if (explicit != NULL)
>   > +    {
>   > +      if (explicit->source_filename != NULL)
>   > +	{
>   > +	  /* Error check -- we must have one of the other
>   > +	     parameters specified.  */
>   > +	  if (explicit->function_name == NULL
>   > +	      && explicit->label_name == NULL
>   > +	      && explicit->line_offset.sign == LINE_OFFSET_UNKNOWN)
>   > +	    error (_("Source filename requires function, label, or "
>   > +		     "line offset."));
>
> How about moving this error check into the caller(s)
> and have an assert here instead?  One could write a utility to
> error check an explicit_location, and the callers could call that.

Ha. Great idea. I can only guess this was necessary at some time. I've 
double-checked it all, and it is /not/ necessary for the CLI. I have 
added something to MI to check this.

>   > @@ -108,6 +175,15 @@ extern struct event_location *
>   >  extern struct event_location *
>   >    new_probe_location (const char *probe);
>   >
>   > +/* Create a new explicit location.  If not NULL, EXPLICIT is checked for
>   > +   validity.  If invalid, an exception is thrown.
>   > +
>   > +   The return result is malloc'd and should be freed with
>   > +   delete_event_location.  */
>
> Copy of function comment in .c file.  Delete one.

I've removed all the duplicate function comments from location.c and 
replaced them with "See description in location.h."

Updated patch attached.

Keith

Changes since last revision:
   - Save extra_string for all locations (pending bps)
   - canonicalize_linespec: have explicit label but no function,
     set function; We need it for:
    (gdb) start
    (gdb) b -label done
    (gdb) save breakpoints bps
    (gdb) shell cat bps
    break -label done <-- not correct!
    We need "--function main" added, just like in the linespec case.
    I've made it so that the behavior is EXACTLY the same
    as linespec case, made LS const, and saved everything in the
    canonical location.
    - make EXPLICIT const in convert_explicit_location_to_sals
    - canonicalize_linespec: only save string representation for
      linespec.  Needed so that we display linespecs as linespecs.
      Other option is to not do this and save is_linespec somewhere
    - new_explicit_location: remove error check
    - removed duplicate function comments
  

Patch

gdb/ChangeLog:

	* breakpoint.c (create_overlay_breakpoint): Convert linespec
	into explicit location.
	(create_longjmp_master_breakpoint): Likewise.
	(create_std_terminate_master_breakpoint): Likewise.
	(create_exception_master_breakpoint): Likewise.
	(create_breakpoint): For pending explicit locations, append extra_string
	to the canonical representation.
	(update_static_tracepoint): Convert linespec into explicit location.
	(location_to_sals): Save the string representation of the location
	for pending locations which were resolved.
	* linespec.c (enum offset_relative_sign): Move to location.h.
	(struct line_offset): Likewise.
	(struct linespec) <expression, expr_pc, source_filename>
	<function_name, label_name, line_offset>: Replace with ...
	<explicit>: ... this.
	<is_linespec>: New member.
	(PARSER_EXPLICIT): New accessor macro.
	(undefined_label_error): New function.
	(source_file_not_found_error): New function.
	(linespec_parse_basic): The parser result is now an explicit location.
	Use PARSER_EXPLICIT to access it.
	Use undefined_label_error.
	(canonicalize_linespec): Convert canonical linespec into explicit
	location.
	Move string representation of location to explicit_location_to_linespec
	and use it and explicit_location_to_string to save string
	representations of the canonical location.
	(create_sals_line_offset): Use PARSER_EXPLICIT to access the parser's
	explicit location result.
	(convert_linespec_to_sals): `ls' contains an explicit location.
	Update all references.
	(convert_explicit_location_to_sals): New function.
	(linespec_parse_basic): Use PARSER_EXPLICIT to access the parser
	result's explicit location.
	(linespec_state_constructor): Initialize is_linespec.
	Use PARSER_EXPLICIT.
	(linespec_parser_delete): Use PARSER_EXPLICIT to access the parser's
	result.
	(event_location_to_sals): For linespec locations, set is_linespec.
	Handle explicit locations.
	(decode_objc): 'ls' contains an explicit location now. Update all
	references.
	(symtabs_from_filename): Use source_file_not_found_error.
	* location.c (struct event_location.u) <explicit>: New member.
	(initialize_explicit_location): New function.
	(initialize_event_location): Initialize explicit locations.
	(copy_event_location): Handle explicit locations.
	(explicit_to_string_internal): New function; most of contents moved
	from canonicalize_linespec.
	(explicit_location_to_string): New function.
	(explicit_location_to_linespec): New function.
	(delete_event_location): Handle explicit locations.
	(event_location_to_string_const): Likewise.
	(event_location_empty_p): Likewise.
	* location.h (enum offset_relative_sign): Move here from linespec.h.
	(struct line_offset): Likewise.
	(enum event_location_type): Add EXPLICIT_LOCATION.
	(struct explicit_location): New structure.
	(explicit_location_to_string): Declare.
	(explicit_location_to_linespec): Declare.
	(initialize_explicit_location): Declare.
---
 gdb/breakpoint.c |   48 +++++++--
 gdb/linespec.c   |  303 ++++++++++++++++++++++++++++++------------------------
 gdb/location.c   |  167 ++++++++++++++++++++++++++++++
 gdb/location.h   |   82 +++++++++++++++
 4 files changed, 453 insertions(+), 147 deletions(-)

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 0fceeac..c25b02f 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -3281,6 +3281,7 @@  create_overlay_event_breakpoint (void)
       struct breakpoint *b;
       struct breakpoint_objfile_data *bp_objfile_data;
       CORE_ADDR addr;
+      struct explicit_location explicit;
 
       bp_objfile_data = get_breakpoint_objfile_data (objfile);
 
@@ -3305,7 +3306,9 @@  create_overlay_event_breakpoint (void)
       b = create_internal_breakpoint (get_objfile_arch (objfile), addr,
                                       bp_overlay_event,
 				      &internal_breakpoint_ops);
-      b->location = new_linespec_location (func_name);
+      initialize_explicit_location (&explicit);
+      explicit.function_name = ASTRDUP (func_name);
+      b->location = new_explicit_location (&explicit);
 
       if (overlay_debugging == ovly_auto)
         {
@@ -3402,6 +3405,7 @@  create_longjmp_master_breakpoint (void)
 	  struct breakpoint *b;
 	  const char *func_name;
 	  CORE_ADDR addr;
+	  struct explicit_location explicit;
 
 	  if (msym_not_found_p (bp_objfile_data->longjmp_msym[i].minsym))
 	    continue;
@@ -3424,7 +3428,9 @@  create_longjmp_master_breakpoint (void)
 	  addr = BMSYMBOL_VALUE_ADDRESS (bp_objfile_data->longjmp_msym[i]);
 	  b = create_internal_breakpoint (gdbarch, addr, bp_longjmp_master,
 					  &internal_breakpoint_ops);
-	  b->location = new_linespec_location (func_name);
+	  initialize_explicit_location (&explicit);
+	  explicit.function_name = ASTRDUP (func_name);
+	  b->location = new_explicit_location (&explicit);
 	  b->enable_state = bp_disabled;
 	}
     }
@@ -3455,6 +3461,7 @@  create_std_terminate_master_breakpoint (void)
     {
       struct breakpoint *b;
       struct breakpoint_objfile_data *bp_objfile_data;
+      struct explicit_location explicit;
 
       bp_objfile_data = get_breakpoint_objfile_data (objfile);
 
@@ -3480,7 +3487,9 @@  create_std_terminate_master_breakpoint (void)
       b = create_internal_breakpoint (get_objfile_arch (objfile), addr,
                                       bp_std_terminate_master,
 				      &internal_breakpoint_ops);
-      b->location = new_linespec_location (func_name);
+      initialize_explicit_location (&explicit);
+      explicit.function_name = ASTRDUP (func_name);
+      b->location = new_explicit_location (&explicit);
       b->enable_state = bp_disabled;
     }
   }
@@ -3504,6 +3513,7 @@  create_exception_master_breakpoint (void)
       struct gdbarch *gdbarch;
       struct breakpoint_objfile_data *bp_objfile_data;
       CORE_ADDR addr;
+      struct explicit_location explicit;
 
       bp_objfile_data = get_breakpoint_objfile_data (objfile);
 
@@ -3584,7 +3594,9 @@  create_exception_master_breakpoint (void)
 						 &current_target);
       b = create_internal_breakpoint (gdbarch, addr, bp_exception_master,
 				      &internal_breakpoint_ops);
-      b->location = new_linespec_location (func_name);
+      initialize_explicit_location (&explicit);
+      explicit.function_name = ASTRDUP (func_name);
+      b->location = new_explicit_location (&explicit);
       b->enable_state = bp_disabled;
     }
 
@@ -9890,8 +9902,21 @@  create_breakpoint (struct gdbarch *gdbarch,
 	     is defaulted on behalf of the user.  */
 	  {
 	    struct linespec_sals lsal;
+	    char *estr = event_location_to_string_const (location);
 
-	    lsal.canonical = event_location_to_string_const (location);
+	    /* Append extra_string, if set, to the canonical representation
+	       of the lsal.  This preserves any conditions that the user may
+	       have specified.  */
+	    if (extra_string != NULL)
+	      {
+		char *new;
+
+		new = xstrprintf ("%s %s", estr, extra_string);
+		xfree (estr);
+		estr = new;
+	      }
+
+	    lsal.canonical = estr;
 	    lsal.sals.nelts = 1;
 	    lsal.sals.sals = XNEW (struct symtab_and_line);
 	    init_sal (&lsal.sals.sals[0]);
@@ -14223,11 +14248,11 @@  update_static_tracepoint (struct breakpoint *b, struct symtab_and_line sal)
 
       if (!VEC_empty(static_tracepoint_marker_p, markers))
 	{
-	  char *tmp;
 	  struct symtab_and_line sal2;
 	  struct symbol *sym;
 	  struct static_tracepoint_marker *tpmarker;
 	  struct ui_out *uiout = current_uiout;
+	  struct explicit_location explicit;
 
 	  tpmarker = VEC_index (static_tracepoint_marker_p, markers, 0);
 
@@ -14269,11 +14294,12 @@  update_static_tracepoint (struct breakpoint *b, struct symtab_and_line sal)
 	  b->loc->symtab = sym != NULL ? sal2.symtab : NULL;
 
 	  delete_event_location (b->location);
-	  tmp = xstrprintf ("%s:%d",
-			    symtab_to_filename_for_display (sal2.symtab),
-			    b->loc->line_number);
-	  b->location = new_linespec_location (tmp);
-	  xfree (tmp);
+	  initialize_explicit_location (&explicit);
+	  explicit.source_filename
+	    = ASTRDUP (symtab_to_filename_for_display (sal2.symtab));
+	  explicit.line_offset.offset = b->loc->line_number;
+	  explicit.line_offset.sign = LINE_OFFSET_NONE;
+	  b->location = new_explicit_location (&explicit);
 
 	  /* Might be nice to check if function changed, and warn if
 	     so.  */
diff --git a/gdb/linespec.c b/gdb/linespec.c
index bc4484f..a529457 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -66,73 +66,26 @@  typedef struct bound_minimal_symbol bound_minimal_symbol_d;
 
 DEF_VEC_O (bound_minimal_symbol_d);
 
-/* An enumeration of possible signs for a line offset.  */
-enum offset_relative_sign
-{
-  /* No sign  */
-  LINE_OFFSET_NONE,
-
-  /* A plus sign ("+")  */
-  LINE_OFFSET_PLUS,
-
-  /* A minus sign ("-")  */
-  LINE_OFFSET_MINUS,
-
-  /* A special "sign" for unspecified offset.  */
-  LINE_OFFSET_UNKNOWN
-};
-
-/* A line offset in a linespec.  */
-
-struct line_offset
-{
-  /* Line offset and any specified sign.  */
-  int offset;
-  enum offset_relative_sign sign;
-};
-
 /* A linespec.  Elements of this structure are filled in by a parser
    (either parse_linespec or some other function).  The structure is
    then converted into SALs by convert_linespec_to_sals.  */
 
 struct linespec
 {
-  /* An expression and the resulting PC.  Specifying an expression
-     currently precludes the use of other members.  */
-
-  /* The expression entered by the user.  */
-  const char *expression;
-
-  /* The resulting PC expression derived from evaluating EXPRESSION.  */
-  CORE_ADDR expr_pc;
-
-  /* Any specified file symtabs.  */
-
-  /* The user-supplied source filename or NULL if none was specified.  */
-  const char *source_filename;
+  /* An explicit location describing the SaLs.  */
+  struct explicit_location explicit;
 
   /* The list of symtabs to search to which to limit the search.  May not
-     be NULL.  If SOURCE_FILENAME is NULL (no user-specified filename),
-     FILE_SYMTABS should contain one single NULL member.  This will
-     cause the code to use the default symtab.  */
+     be NULL.  If explicit.SOURCE_FILENAME is NULL (no user-specified
+     filename), FILE_SYMTABS should contain one single NULL member.  This
+     will cause the code to use the default symtab.  */
   VEC (symtab_ptr) *file_symtabs;
 
-  /* The name of a function or method and any matching symbols.  */
-
-  /* The user-specified function name.  If no function name was
-     supplied, this may be NULL.  */
-  const char *function_name;
-
   /* A list of matching function symbols and minimal symbols.  Both lists
      may be NULL if no matching symbols were found.  */
   VEC (symbolp) *function_symbols;
   VEC (bound_minimal_symbol_d) *minimal_symbols;
 
-  /* The name of a label and matching symbols.  */
-
-  /* The user-specified label name.  */
-  const char *label_name;
-
   /* A structure of matching label symbols and the corresponding
      function symbol in which the label was found.  Both may be NULL
      or both must be non-NULL.  */
@@ -141,10 +94,6 @@  struct linespec
     VEC (symbolp) *label_symbols;
     VEC (symbolp) *function_symbols;
   } labels;
-
-  /* Line offset.  It may be LINE_OFFSET_UNKNOWN, meaning that no
-   offset was specified.  */
-  struct line_offset line_offset;
 };
 typedef struct linespec *linespec_p;
 
@@ -197,6 +146,9 @@  struct linespec_state
   /* This is a set of address_entry objects which is used to prevent
      duplicate symbols from being entered into the result.  */
   htab_t addr_set;
+
+  /* Are we building a linespec?  */
+  int is_linespec;
 };
 
 /* This is a helper object that is used when collecting symbols into a
@@ -307,6 +259,10 @@  struct ls_parser
 };
 typedef struct ls_parser linespec_parser;
 
+/* A convenience macro for accessing the explicit location result of
+   the parser.  */
+#define PARSER_EXPLICIT(PPTR) (&PARSER_RESULT ((PPTR))->explicit)
+
 /* Prototypes for local functions.  */
 
 static void iterate_over_file_blocks (struct symtab *symtab,
@@ -1550,6 +1506,29 @@  unexpected_linespec_error (linespec_parser *parser)
 		 token_type_strings[token.type]);
 }
 
+/* Throw an undefined label error.  */
+
+static void ATTRIBUTE_NORETURN
+undefined_label_error (const char *function, const char *label)
+{
+  if (function != NULL)
+    throw_error (NOT_FOUND_ERROR,
+                _("No label \"%s\" defined in function \"%s\"."),
+                label, function);
+  else
+    throw_error (NOT_FOUND_ERROR,
+                _("No label \"%s\" defined in current function."),
+                label);
+}
+
+/* Throw a source file not found error.  */
+
+static void ATTRIBUTE_NORETURN
+source_file_not_found_error (const char *name)
+{
+  throw_error (NOT_FOUND_ERROR, _("No source file named %s."), name);
+}
+
 /* Parse and return a line offset in STRING.  */
 
 static struct line_offset
@@ -1596,7 +1575,7 @@  linespec_parse_basic (linespec_parser *parser)
       /* Record the line offset and get the next token.  */
       name = copy_token_string (token);
       cleanup = make_cleanup (xfree, name);
-      PARSER_RESULT (parser)->line_offset = linespec_parse_line_offset (name);
+      PARSER_EXPLICIT (parser)->line_offset = linespec_parse_line_offset (name);
       do_cleanups (cleanup);
 
       /* Get the next token.  */
@@ -1633,7 +1612,7 @@  linespec_parse_basic (linespec_parser *parser)
     {
       PARSER_RESULT (parser)->function_symbols = symbols;
       PARSER_RESULT (parser)->minimal_symbols = minimal_symbols;
-      PARSER_RESULT (parser)->function_name = name;
+      PARSER_EXPLICIT (parser)->function_name = name;
       symbols = NULL;
       discard_cleanups (cleanup);
     }
@@ -1647,7 +1626,7 @@  linespec_parse_basic (linespec_parser *parser)
 	{
 	  PARSER_RESULT (parser)->labels.label_symbols = labels;
 	  PARSER_RESULT (parser)->labels.function_symbols = symbols;
-	  PARSER_RESULT (parser)->label_name = name;
+	  PARSER_EXPLICIT (parser)->label_name = name;
 	  symbols = NULL;
 	  discard_cleanups (cleanup);
 	}
@@ -1655,14 +1634,14 @@  linespec_parse_basic (linespec_parser *parser)
 	       && *LS_TOKEN_STOKEN (token).ptr == '$')
 	{
 	  /* User specified a convenience variable or history value.  */
-	  PARSER_RESULT (parser)->line_offset
+	  PARSER_EXPLICIT (parser)->line_offset
 	    = linespec_parse_variable (PARSER_STATE (parser), name);
 
-	  if (PARSER_RESULT (parser)->line_offset.sign == LINE_OFFSET_UNKNOWN)
+	  if (PARSER_EXPLICIT (parser)->line_offset.sign == LINE_OFFSET_UNKNOWN)
 	    {
 	      /* The user-specified variable was not valid.  Do not
 		 throw an error here.  parse_linespec will do it for us.  */
-	      PARSER_RESULT (parser)->function_name = name;
+	      PARSER_EXPLICIT (parser)->function_name = name;
 	      discard_cleanups (cleanup);
 	      return;
 	    }
@@ -1677,7 +1656,7 @@  linespec_parse_basic (linespec_parser *parser)
 	     an error here.  parse_linespec will do it for us.  */
 
 	  /* Save a copy of the name we were trying to lookup.  */
-	  PARSER_RESULT (parser)->function_name = name;
+	  PARSER_EXPLICIT (parser)->function_name = name;
 	  discard_cleanups (cleanup);
 	  return;
 	}
@@ -1697,7 +1676,7 @@  linespec_parse_basic (linespec_parser *parser)
 	     get the next token.  */
 	  name = copy_token_string (token);
 	  cleanup = make_cleanup (xfree, name);
-	  PARSER_RESULT (parser)->line_offset
+	  PARSER_EXPLICIT (parser)->line_offset
 	    = linespec_parse_line_offset (name);
 	  do_cleanups (cleanup);
 
@@ -1717,16 +1696,15 @@  linespec_parse_basic (linespec_parser *parser)
 	    {
 	      PARSER_RESULT (parser)->labels.label_symbols = labels;
 	      PARSER_RESULT (parser)->labels.function_symbols = symbols;
-	      PARSER_RESULT (parser)->label_name = name;
+	      PARSER_EXPLICIT (parser)->label_name = name;
 	      symbols = NULL;
 	      discard_cleanups (cleanup);
 	    }
 	  else
 	    {
 	      /* We don't know what it was, but it isn't a label.  */
-	      throw_error (NOT_FOUND_ERROR,
-			   _("No label \"%s\" defined in function \"%s\"."),
-			   name, PARSER_RESULT (parser)->function_name);
+	      undefined_label_error (PARSER_EXPLICIT (parser)->function_name,
+				     name);
 	    }
 
 	  /* Check for a line offset.  */
@@ -1744,7 +1722,7 @@  linespec_parse_basic (linespec_parser *parser)
 	      name = copy_token_string (token);
 	      cleanup = make_cleanup (xfree, name);
 
-	      PARSER_RESULT (parser)->line_offset
+	      PARSER_EXPLICIT (parser)->line_offset
 		= linespec_parse_line_offset (name);
 	      do_cleanups (cleanup);
 
@@ -1761,41 +1739,28 @@  linespec_parse_basic (linespec_parser *parser)
 }
 
 /* Canonicalize the linespec contained in LS.  The result is saved into
-   STATE->canonical.  */
+   STATE->canonical.  This function handles both linespec and explicit
+   locations.  */
 
 static void
-canonicalize_linespec (struct linespec_state *state, linespec_p ls)
+canonicalize_linespec (struct linespec_state *state, const linespec_p ls)
 {
-  char *tmp;
-  struct ui_file *buf;
-  int need_colon = 0;
+  struct event_location *canon;
+  struct explicit_location *explicit;
 
   /* If canonicalization was not requested, no need to do anything.  */
   if (!state->canonical)
     return;
 
-  buf = mem_fileopen ();
-
-  if (ls->source_filename)
-    {
-      fputs_unfiltered (ls->source_filename, buf);
-      need_colon = 1;
-    }
-
-  if (ls->function_name)
-    {
-      if (need_colon)
-	fputc_unfiltered (':', buf);
-      fputs_unfiltered (ls->function_name, buf);
-      need_colon = 1;
-    }
+  /* Save everything as an explicit location.  */
+  canon = state->canonical->location = new_explicit_location (&ls->explicit);
+  explicit = get_explicit_location (canon);
 
-  if (ls->label_name)
+  if (explicit->label_name != NULL)
     {
-      if (need_colon)
-	fputc_unfiltered (':', buf);
+      state->canonical->special_display = 1;
 
-      if (ls->function_name == NULL)
+      if (explicit->function_name == NULL)
 	{
 	  struct symbol *s;
 
@@ -1804,30 +1769,17 @@  canonicalize_linespec (struct linespec_state *state, linespec_p ls)
 		      && (VEC_length (symbolp, ls->labels.function_symbols)
 			  == 1));
 	  s = VEC_index (symbolp, ls->labels.function_symbols, 0);
-	  fputs_unfiltered (SYMBOL_NATURAL_NAME (s), buf);
-	  fputc_unfiltered (':', buf);
+	  explicit->function_name = xstrdup (SYMBOL_NATURAL_NAME (s));
 	}
-
-      fputs_unfiltered (ls->label_name, buf);
-      need_colon = 1;
-      state->canonical->special_display = 1;
     }
 
-  if (ls->line_offset.sign != LINE_OFFSET_UNKNOWN)
+  /* If this location originally came from a linespec, save a string
+     representation of it for display and saving to file.  */
+  if (state->is_linespec)
     {
-      if (need_colon)
-	fputc_unfiltered (':', buf);
-      fprintf_filtered (buf, "%s%d",
-			(ls->line_offset.sign == LINE_OFFSET_NONE ? ""
-			 : (ls->line_offset.sign
-			    == LINE_OFFSET_PLUS ? "+" : "-")),
-			ls->line_offset.offset);
+      set_event_location_string (canon,
+				 explicit_location_to_linespec (explicit));
     }
-
-  tmp = ui_file_xstrdup (buf, NULL);
-  state->canonical->location = new_linespec_location (tmp);
-  xfree (tmp);
-  ui_file_delete (buf);
 }
 
 /* Given a line offset in LS, construct the relevant SALs.  */
@@ -1867,18 +1819,18 @@  create_sals_line_offset (struct linespec_state *self,
       use_default = 1;
     }
 
-  val.line = ls->line_offset.offset;
-  switch (ls->line_offset.sign)
+  val.line = ls->explicit.line_offset.offset;
+  switch (ls->explicit.line_offset.sign)
     {
     case LINE_OFFSET_PLUS:
-      if (ls->line_offset.offset == 0)
+      if (ls->explicit.line_offset.offset == 0)
 	val.line = 5;
       if (use_default)
 	val.line = self->default_line + val.line;
       break;
 
     case LINE_OFFSET_MINUS:
-      if (ls->line_offset.offset == 0)
+      if (ls->explicit.line_offset.offset == 0)
 	val.line = 15;
       if (use_default)
 	val.line = self->default_line - val.line;
@@ -1970,9 +1922,9 @@  create_sals_line_offset (struct linespec_state *self,
 
   if (values.nelts == 0)
     {
-      if (ls->source_filename)
+      if (ls->explicit.source_filename)
 	throw_error (NOT_FOUND_ERROR, _("No line %d in file \"%s\"."),
-		     val.line, ls->source_filename);
+		     val.line, ls->explicit.source_filename);
       else
 	throw_error (NOT_FOUND_ERROR, _("No line %d in the current file."),
 		     val.line);
@@ -2072,13 +2024,13 @@  convert_linespec_to_sals (struct linespec_state *state, linespec_p ls)
 	    }
 	}
     }
-  else if (ls->line_offset.sign != LINE_OFFSET_UNKNOWN)
+  else if (ls->explicit.line_offset.sign != LINE_OFFSET_UNKNOWN)
     {
       /* Only an offset was specified.  */
 	sals = create_sals_line_offset (state, ls);
 
 	/* Make sure we have a filename for canonicalization.  */
-	if (ls->source_filename == NULL)
+	if (ls->explicit.source_filename == NULL)
 	  {
 	    const char *fullname = symtab_to_fullname (state->default_symtab);
 
@@ -2086,7 +2038,7 @@  convert_linespec_to_sals (struct linespec_state *state, linespec_p ls)
 	       form so that displaying SOURCE_FILENAME can follow the current
 	       FILENAME_DISPLAY_STRING setting.  But as it is used only rarely
 	       it has been kept for code simplicity only in absolute form.  */
-	    ls->source_filename = xstrdup (fullname);
+	    ls->explicit.source_filename = xstrdup (fullname);
 	  }
     }
   else
@@ -2103,6 +2055,73 @@  convert_linespec_to_sals (struct linespec_state *state, linespec_p ls)
   return sals;
 }
 
+/* Convert the explicit location EXPLICIT into SaLs.  */
+
+static struct symtabs_and_lines
+convert_explicit_location_to_sals (struct linespec_state *self,
+				   linespec_p result,
+				   const struct explicit_location *explicit)
+{
+  VEC (symbolp) *symbols, *labels;
+  VEC (bound_minimal_symbol_d) *minimal_symbols;
+
+  if (explicit->source_filename != NULL)
+    {
+      volatile struct gdb_exception except;
+
+      TRY_CATCH (except, RETURN_MASK_ERROR)
+	{
+	  result->file_symtabs
+	    = symtabs_from_filename (explicit->source_filename);
+	}
+
+      if (except.reason < 0 || result->file_symtabs == NULL)
+	source_file_not_found_error (explicit->source_filename);
+
+      result->explicit.source_filename = xstrdup (explicit->source_filename);
+    }
+  else
+    {
+      /* A NULL entry means to use the default symtab.  */
+      VEC_safe_push (symtab_ptr, result->file_symtabs, NULL);
+    }
+
+  if (explicit->function_name != NULL)
+    {
+      find_linespec_symbols (self, result->file_symtabs,
+			     explicit->function_name, &symbols,
+			     &minimal_symbols);
+
+      if (symbols == NULL && minimal_symbols == NULL)
+	symbol_not_found_error (explicit->function_name,
+				result->explicit.source_filename);
+
+      result->explicit.function_name = xstrdup (explicit->function_name);
+      result->function_symbols = symbols;
+      result->minimal_symbols = minimal_symbols;
+    }
+
+  if (explicit->label_name != NULL)
+    {
+      symbols = NULL;
+      labels = find_label_symbols (self, result->function_symbols,
+				   &symbols, explicit->label_name);
+
+      if (labels == NULL)
+	undefined_label_error (result->explicit.function_name,
+			       explicit->label_name);
+
+      result->explicit.label_name = xstrdup (explicit->label_name);
+      result->labels.label_symbols = labels;
+      result->labels.function_symbols = symbols;
+    }
+
+  if (explicit->line_offset.sign != LINE_OFFSET_UNKNOWN)
+    result->explicit.line_offset = explicit->line_offset;
+
+   return convert_linespec_to_sals (self, result);
+}
+
 /* Parse a string that specifies a linespec.
    Pass the address of a char * variable; that variable will be
    advanced over the characters actually parsed.
@@ -2215,13 +2234,13 @@  parse_linespec (linespec_parser *parser, const char **argptr)
       /* User specified a convenience variable or history value.  */
       var = copy_token_string (token);
       cleanup = make_cleanup (xfree, var);
-      PARSER_RESULT (parser)->line_offset
+      PARSER_EXPLICIT (parser)->line_offset
 	= linespec_parse_variable (PARSER_STATE (parser), var);
       do_cleanups (cleanup);
 
       /* If a line_offset wasn't found (VAR is the name of a user
 	 variable/function), then skip to normal symbol processing.  */
-      if (PARSER_RESULT (parser)->line_offset.sign != LINE_OFFSET_UNKNOWN)
+      if (PARSER_EXPLICIT (parser)->line_offset.sign != LINE_OFFSET_UNKNOWN)
 	{
 	  /* Consume this token.  */
 	  linespec_lexer_consume_token (parser);
@@ -2257,7 +2276,7 @@  parse_linespec (linespec_parser *parser, const char **argptr)
       if (file_exception.reason >= 0)
 	{
 	  /* Symtabs were found for the file.  Record the filename.  */
-	  PARSER_RESULT (parser)->source_filename = user_filename;
+	  PARSER_EXPLICIT (parser)->source_filename = user_filename;
 
 	  /* Get the next token.  */
 	  token = linespec_lexer_consume_token (parser);
@@ -2294,7 +2313,7 @@  parse_linespec (linespec_parser *parser, const char **argptr)
 
   if (PARSER_RESULT (parser)->function_symbols == NULL
       && PARSER_RESULT (parser)->labels.label_symbols == NULL
-      && PARSER_RESULT (parser)->line_offset.sign == LINE_OFFSET_UNKNOWN
+      && PARSER_EXPLICIT (parser)->line_offset.sign == LINE_OFFSET_UNKNOWN
       && PARSER_RESULT (parser)->minimal_symbols == NULL)
     {
       /* The linespec didn't parse.  Re-throw the file exception if
@@ -2303,8 +2322,8 @@  parse_linespec (linespec_parser *parser, const char **argptr)
 	throw_exception (file_exception);
 
       /* Otherwise, the symbol is not found.  */
-      symbol_not_found_error (PARSER_RESULT (parser)->function_name,
-			      PARSER_RESULT (parser)->source_filename);
+      symbol_not_found_error (PARSER_EXPLICIT (parser)->function_name,
+			      PARSER_EXPLICIT (parser)->source_filename);
     }
 
  convert_to_sals:
@@ -2342,6 +2361,7 @@  linespec_state_constructor (struct linespec_state *self,
   self->program_space = current_program_space;
   self->addr_set = htab_create_alloc (10, hash_address_entry, eq_address_entry,
 				      xfree, xcalloc, xfree);
+  self->is_linespec = 0;
 }
 
 /* Initialize a new linespec parser.  */
@@ -2356,7 +2376,7 @@  linespec_parser_new (linespec_parser *parser,
   memset (parser, 0, sizeof (linespec_parser));
   parser->lexer.current.type = LSTOKEN_CONSUMED;
   memset (PARSER_RESULT (parser), 0, sizeof (struct linespec));
-  PARSER_RESULT (parser)->line_offset.sign = LINE_OFFSET_UNKNOWN;
+  PARSER_EXPLICIT (parser)->line_offset.sign = LINE_OFFSET_UNKNOWN;
   linespec_state_constructor (PARSER_STATE (parser), flags, language,
 			      default_symtab, default_line, canonical);
 }
@@ -2376,10 +2396,9 @@  linespec_parser_delete (void *arg)
 {
   linespec_parser *parser = (linespec_parser *) arg;
 
-  xfree ((char *) PARSER_RESULT (parser)->expression);
-  xfree ((char *) PARSER_RESULT (parser)->source_filename);
-  xfree ((char *) PARSER_RESULT (parser)->label_name);
-  xfree ((char *) PARSER_RESULT (parser)->function_name);
+  xfree (PARSER_EXPLICIT (parser)->source_filename);
+  xfree (PARSER_EXPLICIT (parser)->label_name);
+  xfree (PARSER_EXPLICIT (parser)->function_name);
 
   if (PARSER_RESULT (parser)->file_symtabs != NULL)
     VEC_free (symtab_ptr, PARSER_RESULT (parser)->file_symtabs);
@@ -2415,6 +2434,7 @@  event_location_to_sals (linespec_parser *parser,
 	const char *copy, *orig;
 	volatile struct gdb_exception except;
 
+	PARSER_STATE (parser)->is_linespec = 1;
 	TRY_CATCH (except, RETURN_MASK_ERROR)
 	  {
 	    orig = copy = get_linespec_location (location);
@@ -2433,6 +2453,17 @@  event_location_to_sals (linespec_parser *parser,
 					    get_address_location (location));
       break;
 
+    case EXPLICIT_LOCATION:
+      {
+	struct explicit_location *explicit;
+
+	explicit = get_explicit_location (location);
+	result = convert_explicit_location_to_sals (PARSER_STATE (parser),
+						    PARSER_RESULT (parser),
+						    explicit);
+      }
+      break;
+
     case PROBE_LOCATION:
       /* Probes are handled by their own decoders.  */
       gdb_assert_not_reached ("attempt to decode probe location");
@@ -2691,7 +2722,7 @@  decode_objc (struct linespec_state *self, linespec_p ls, const char **argptr)
       memcpy (saved_arg, *argptr, new_argptr - *argptr);
       saved_arg[new_argptr - *argptr] = '\0';
 
-      ls->function_name = xstrdup (saved_arg);
+      ls->explicit.function_name = xstrdup (saved_arg);
       ls->function_symbols = info.result.symbols;
       ls->minimal_symbols = info.result.minimal_symbols;
       values = convert_linespec_to_sals (self, ls);
@@ -2702,10 +2733,10 @@  decode_objc (struct linespec_state *self, linespec_p ls, const char **argptr)
 
 	  self->canonical->pre_expanded = 1;
 
-	  if (ls->source_filename)
+	  if (ls->explicit.source_filename)
 	    {
 	      str = xstrprintf ("%s:%s",
-				ls->source_filename, saved_arg);
+				ls->explicit.source_filename, saved_arg);
 	    }
 	  else
 	    str = xstrdup (saved_arg);
@@ -3087,7 +3118,7 @@  symtabs_from_filename (const char *filename)
 	throw_error (NOT_FOUND_ERROR,
 		     _("No symbol table is loaded.  "
 		       "Use the \"file\" command."));
-      throw_error (NOT_FOUND_ERROR, _("No source file named %s."), filename);
+      source_file_not_found_error (filename);
     }
 
   return result;
diff --git a/gdb/location.c b/gdb/location.c
index 8d8fa89..9faad40 100644
--- a/gdb/location.c
+++ b/gdb/location.c
@@ -50,6 +50,10 @@  struct event_location
     /* An address in the inferior.  */
     CORE_ADDR address;
 #define EL_ADDRESS(PTR) (PTR)->u.address
+
+    /* An explicit location.  */
+    struct explicit_location explicit;
+#define EL_EXPLICIT(PTR) (&((PTR)->u.explicit))
   } u;
 
   /* Cached string representation of this location.  This is used, e.g., to
@@ -68,6 +72,15 @@  event_location_type (const struct event_location *location)
 
 /* See description in location.h.  */
 
+void
+initialize_explicit_location (struct explicit_location *explicit)
+{
+  memset (explicit, 0, sizeof (struct explicit_location));
+  explicit->line_offset.sign = LINE_OFFSET_UNKNOWN;
+}
+
+/* See description in location.h.  */
+
 struct event_location *
 new_linespec_location (const char *linespec)
 {
@@ -137,6 +150,126 @@  get_probe_location (struct event_location *location)
 /* See description in location.h.  */
 
 struct event_location *
+new_explicit_location (const struct explicit_location *explicit)
+{
+  struct event_location tmp;
+
+  memset (&tmp, 0, sizeof (struct event_location));
+  EL_TYPE (&tmp) = EXPLICIT_LOCATION;
+  initialize_explicit_location (EL_EXPLICIT (&tmp));
+  if (explicit != NULL)
+    {
+      if (explicit->source_filename != NULL)
+	{
+	  EL_EXPLICIT (&tmp)->source_filename
+	    = explicit->source_filename;
+	}
+
+      if (explicit->function_name != NULL)
+	EL_EXPLICIT (&tmp)->function_name
+	  = explicit->function_name;
+
+      if (explicit->label_name != NULL)
+	EL_EXPLICIT (&tmp)->label_name = explicit->label_name;
+
+      if (explicit->line_offset.sign != LINE_OFFSET_UNKNOWN)
+	EL_EXPLICIT (&tmp)->line_offset = explicit->line_offset;
+    }
+
+  return copy_event_location (&tmp);
+}
+
+/* See description in location.h.  */
+
+struct explicit_location *
+get_explicit_location (struct event_location *location)
+{
+  gdb_assert (EL_TYPE (location) == EXPLICIT_LOCATION);
+  return EL_EXPLICIT (location);
+}
+
+/* This convenience function returns a malloc'd string which
+   represents the location in EXPLICIT.
+
+   AS_LINESPEC is non-zero if this string should be a linespec.
+   Otherwise it will be output in explicit form.  */
+
+static char *
+explicit_to_string_internal (int as_linespec,
+			     const struct explicit_location *explicit)
+{
+  struct ui_file *buf;
+  char space, *result;
+  int need_space = 0;
+
+  space = as_linespec ? ':' : ' ';
+  buf = mem_fileopen ();
+
+  if (explicit->source_filename != NULL)
+    {
+      if (!as_linespec)
+	fputs_unfiltered ("-source ", buf);
+      fputs_unfiltered (explicit->source_filename, buf);
+      need_space = 1;
+    }
+
+  if (explicit->function_name != NULL)
+    {
+      if (need_space)
+	fputc_unfiltered (space, buf);
+      if (!as_linespec)
+	fputs_unfiltered ("-function ", buf);
+      fputs_unfiltered (explicit->function_name, buf);
+      need_space = 1;
+    }
+
+  if (explicit->label_name != NULL)
+    {
+      if (need_space)
+	fputc_unfiltered (space, buf);
+      if (!as_linespec)
+	fputs_unfiltered ("-label ", buf);
+      fputs_unfiltered (explicit->label_name, buf);
+      need_space = 1;
+    }
+
+  if (explicit->line_offset.sign != LINE_OFFSET_UNKNOWN)
+    {
+      if (need_space)
+	fputc_unfiltered (space, buf);
+      if (!as_linespec)
+	fputs_unfiltered ("-line ", buf);
+      fprintf_filtered (buf, "%s%d",
+			(explicit->line_offset.sign == LINE_OFFSET_NONE ? ""
+			 : (explicit->line_offset.sign
+			    == LINE_OFFSET_PLUS ? "+" : "-")),
+			explicit->line_offset.offset);
+    }
+
+  result = ui_file_xstrdup (buf, NULL);
+  ui_file_delete (buf);
+  return result;
+}
+
+/* See description in location.h.  */
+
+char *
+explicit_location_to_string (const struct explicit_location *explicit)
+{
+  return explicit_to_string_internal (0, explicit);
+}
+
+/* See description in location.h.  */
+
+char *
+explicit_location_to_linespec (const struct explicit_location *explicit)
+{
+  return explicit_to_string_internal (1, explicit);
+}
+
+/* See description in location.h.  */
+
+struct event_location *
 copy_event_location (const struct event_location *src)
 {
   struct event_location *dst;
@@ -157,6 +290,22 @@  copy_event_location (const struct event_location *src)
       EL_ADDRESS (dst) = EL_ADDRESS (src);
       break;
 
+    case EXPLICIT_LOCATION:
+      if (EL_EXPLICIT (src)->source_filename != NULL)
+	EL_EXPLICIT (dst)->source_filename
+	  = xstrdup (EL_EXPLICIT (src)->source_filename);
+
+      if (EL_EXPLICIT (src)->function_name != NULL)
+	EL_EXPLICIT (dst)->function_name
+	  = xstrdup (EL_EXPLICIT (src)->function_name);
+
+      if (EL_EXPLICIT (src)->label_name != NULL)
+	EL_EXPLICIT (dst)->label_name = xstrdup (EL_EXPLICIT (src)->label_name);
+
+      EL_EXPLICIT (dst)->line_offset = EL_EXPLICIT (src)->line_offset;
+      break;
+
+
     case PROBE_LOCATION:
       if (EL_PROBE (src) != NULL)
 	EL_PROBE (dst) = xstrdup (EL_PROBE (src));
@@ -206,6 +355,12 @@  delete_event_location (struct event_location *location)
 	  /* Nothing to do.  */
 	  break;
 
+	case EXPLICIT_LOCATION:
+	  xfree (EL_EXPLICIT (location)->source_filename);
+	  xfree (EL_EXPLICIT (location)->function_name);
+	  xfree (EL_EXPLICIT (location)->label_name);
+	  break;
+
 	case PROBE_LOCATION:
 	  xfree (EL_PROBE (location));
 	  break;
@@ -252,6 +407,10 @@  event_location_to_string_const (const struct event_location *location)
 		      core_addr_to_string (EL_ADDRESS (location)));
       break;
 
+    case EXPLICIT_LOCATION:
+      result = explicit_location_to_string (EL_EXPLICIT (location));
+      break;
+
     case PROBE_LOCATION:
       result = xstrdup (EL_PROBE (location));
       break;
@@ -331,6 +490,14 @@  event_location_empty_p (const struct event_location *location)
     case ADDRESS_LOCATION:
       return 0;
 
+    case EXPLICIT_LOCATION:
+      return (EL_EXPLICIT (location) == NULL
+	      || (EL_EXPLICIT (location)->source_filename == NULL
+		  && EL_EXPLICIT (location)->function_name == NULL
+		  && EL_EXPLICIT (location)->label_name == NULL
+		  && (EL_EXPLICIT (location)->line_offset.sign
+		      == LINE_OFFSET_UNKNOWN)));
+
     case PROBE_LOCATION:
       return EL_PROBE (location) == NULL;
 
diff --git a/gdb/location.h b/gdb/location.h
index fa4d0f5..0adb0c5 100644
--- a/gdb/location.h
+++ b/gdb/location.h
@@ -22,6 +22,32 @@ 
 struct language_defn;
 struct event_location;
 
+/* An enumeration of possible signs for a line offset.  */
+
+enum offset_relative_sign
+{
+  /* No sign  */
+  LINE_OFFSET_NONE,
+
+  /* A plus sign ("+")  */
+  LINE_OFFSET_PLUS,
+
+  /* A minus sign ("-")  */
+  LINE_OFFSET_MINUS,
+
+  /* A special "sign" for unspecified offset.  */
+  LINE_OFFSET_UNKNOWN
+};
+
+/* A line offset in a location.  */
+
+struct line_offset
+{
+  /* Line offset and any specified sign.  */
+  int offset;
+  enum offset_relative_sign sign;
+};
+
 /* An enumeration of the various ways to specify a stop event
    location (used with create_breakpoint).  */
 
@@ -33,15 +59,52 @@  enum event_location_type
   /* An address in the inferior.  */
   ADDRESS_LOCATION,
 
+  /* An explicit location.  */
+  EXPLICIT_LOCATION,
+
   /* A probe location.  */
   PROBE_LOCATION
 };
 
+/* An explicit location.  This structure is used to bypass the
+   parsing done on linespecs.  It still has the same requirements
+   as linespecs, though.  For example, source_filename requires
+   at least one other field.  */
+
+struct explicit_location
+{
+  /* The source filename. Malloc'd.  */
+  char *source_filename;
+
+  /* The function name.  Malloc'd.  */
+  char *function_name;
+
+  /* The name of a label.  Malloc'd.  */
+  char *label_name;
+
+  /* A line offset relative to the start of the symbol
+     identified by the above fields or the current symtab
+     if the other fields are NULL.  */
+  struct line_offset line_offset;
+};
+
 /* Return the type of the given event location.  */
 
 extern enum event_location_type
   event_location_type (const struct event_location *);
 
+/* Return a malloc'd explicit string representation of the given
+   explicit location.  The location must already be canonicalized/valid.  */
+
+extern char *
+  explicit_location_to_string (const struct explicit_location *explicit);
+
+/* Return a malloc'd linespec string representation of the given
+   explicit location.  The location must already be canonicalized/valid.  */
+
+extern char *
+  explicit_location_to_linespec (const struct explicit_location *explicit);
+
 /* Return a string representation of the LOCATION.
    This function may return NULL for unspecified linespecs,
    e.g, LOCATION_LINESPEC and addr_string is NULL.
@@ -94,6 +157,21 @@  extern struct event_location *
 extern char *
   get_probe_location (struct event_location *location);
 
+/* Create a new explicit location.  If not NULL, EXPLICIT is checked for
+   validity.  If invalid, an exception is thrown.
+
+   The return result is malloc'd and should be freed with
+   delete_event_location.  */
+
+extern struct event_location *
+  new_explicit_location (const struct explicit_location *explicit);
+
+/* Return the explicit location of the given event_location
+   (which must be of type EXPLICIT_LOCATION).  */
+
+extern struct explicit_location *
+  get_explicit_location (struct event_location *location);
+
 /* Free an event location and any associated data.  */
 
 extern void delete_event_location (struct event_location *location);
@@ -115,6 +193,10 @@  extern struct event_location *
 extern struct event_location *
   copy_event_location_tmp (const struct event_location *src);
 
+/* Initialize the given explicit location.  */
+
+extern void initialize_explicit_location (struct explicit_location *explicit);
+
 /* Attempt to convert the input string in *ARGP into an event location.
    ARGP is advanced past any processed input.  Returns a event_location
    (malloc'd) if an event location was successfully found in *ARGP,