summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/cp/ChangeLog7
-rw-r--r--gcc/cp/class.c2
-rw-r--r--gcc/cp/cp-tree.h5
-rw-r--r--gcc/cp/lex.c15
-rw-r--r--gcc/dwarf2.h4
-rw-r--r--gcc/dwarf2out.c611
-rw-r--r--gcc/flags.h4
-rw-r--r--gcc/toplev.c31
-rw-r--r--gcc/toplev.h1
-rw-r--r--gcc/tree.c34
-rw-r--r--gcc/tree.h1
-rw-r--r--include/ChangeLog4
-rw-r--r--include/md5.h146
-rw-r--r--libiberty/ChangeLog6
-rw-r--r--libiberty/Makefile.in4
-rw-r--r--libiberty/md5.c419
16 files changed, 1227 insertions, 67 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 8f0617f4fcb..c9224cfdd76 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,10 @@
+2000-08-28 Jason Merrill <jason@redhat.com>
+
+ * lex.c (file_name_nondirectory): Move to toplev.c.
+
+ * cp-tree.h (LOCAL_CLASS_P): New macro.
+ * class.c (finish_struct_1): Use it.
+
2000-08-27 Alex Samuel <samuel@codesourcery.com>
* mangle.c (CLASSTYPE_TEMPLATE_ID_P): Remove unexplained voodoo.
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 2834154f3d8..dfbdc94a78b 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -5200,7 +5200,7 @@ finish_struct_1 (t)
maybe_suppress_debug_info (t);
/* Finish debugging output for this type. */
- rest_of_type_compilation (t, toplevel_bindings_p ());
+ rest_of_type_compilation (t, ! LOCAL_CLASS_P (t));
}
/* When T was built up, the member declarations were added in reverse
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index aa77ab10a9a..cc5046921f9 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -2224,6 +2224,11 @@ struct lang_decl
(DECL_CONTEXT (NODE) \
&& TREE_CODE (DECL_CONTEXT (NODE)) == FUNCTION_DECL)
+/* 1 iff NODE is function-local, but for types. */
+#define LOCAL_CLASS_P(NODE) \
+ (TYPE_CONTEXT (NODE) \
+ && TREE_CODE (TYPE_CONTEXT (NODE)) == FUNCTION_DECL)
+
/* For a NAMESPACE_DECL: the list of using namespace directives
The PURPOSE is the used namespace, the value is the namespace
that is the common ancestor. */
diff --git a/gcc/cp/lex.c b/gcc/cp/lex.c
index 964bd569cf1..94679b4c718 100644
--- a/gcc/cp/lex.c
+++ b/gcc/cp/lex.c
@@ -96,21 +96,6 @@ static int is_extended_char PARAMS ((int));
static int is_extended_char_1 PARAMS ((int));
static void init_operators PARAMS ((void));
-/* Given a file name X, return the nondirectory portion.
- Keep in mind that X can be computed more than once. */
-char *
-file_name_nondirectory (x)
- const char *x;
-{
- char *tmp = (char *) rindex (x, '/');
- if (DIR_SEPARATOR != '/' && ! tmp)
- tmp = (char *) rindex (x, DIR_SEPARATOR);
- if (tmp)
- return (char *) (tmp + 1);
- else
- return (char *) x;
-}
-
/* This obstack is needed to hold text. It is not safe to use
TOKEN_BUFFER because `check_newline' calls `yylex'. */
struct obstack inline_text_obstack;
diff --git a/gcc/dwarf2.h b/gcc/dwarf2.h
index 2e2b9fe5b45..5b608284547 100644
--- a/gcc/dwarf2.h
+++ b/gcc/dwarf2.h
@@ -88,7 +88,9 @@ enum dwarf_tag
/* GNU extensions */
DW_TAG_format_label = 0x4101, /* for FORTRAN 77 and Fortran 90 */
DW_TAG_function_template = 0x4102, /* for C++ */
- DW_TAG_class_template = 0x4103 /* for C++ */
+ DW_TAG_class_template = 0x4103, /* for C++ */
+ DW_TAG_GNU_BINCL = 0x4104,
+ DW_TAG_GNU_EINCL = 0x4105
};
#define DW_TAG_lo_user 0x4080
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index cc121e12834..69e03955704 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -23,9 +23,6 @@ the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
/* TODO: Implement .debug_str handling, and share entries somehow.
- Eliminate duplicates by putting common info in a separate section
- to be collected by the linker and referring to it with
- DW_FORM_ref_addr.
Emit .debug_line header even when there are no functions, since
the file numbers are used by .debug_info. Alternately, leave
out locations for types and decls.
@@ -56,6 +53,7 @@ Boston, MA 02111-1307, USA. */
#include "toplev.h"
#include "varray.h"
#include "ggc.h"
+#include "md5.h"
#include "tm_p.h"
/* Decide whether we want to emit frame unwind information for the current
@@ -303,6 +301,7 @@ static void def_cfa_1 PARAMS ((const char *, dw_cfa_location *));
#define FDE_AFTER_SIZE_LABEL "LSFDE"
#define FDE_END_LABEL "LEFDE"
#define FDE_LENGTH_LABEL "LLFDE"
+#define DIE_LABEL_PREFIX "DW"
/* Definitions of defaults for various types of primitive assembly language
output operations. These may be overridden from within the tm.h file,
@@ -2124,7 +2123,10 @@ typedef struct dw_val_struct
long unsigned val_unsigned;
dw_long_long_const val_long_long;
dw_float_const val_float;
- dw_die_ref val_die_ref;
+ struct {
+ dw_die_ref die;
+ int external;
+ } val_die_ref;
unsigned val_fde_index;
char *val_str;
char *val_lbl_id;
@@ -2995,6 +2997,7 @@ dw_attr_node;
typedef struct die_struct
{
enum dwarf_tag die_tag;
+ char *die_symbol;
dw_attr_ref die_attr;
dw_die_ref die_parent;
dw_die_ref die_child;
@@ -3338,20 +3341,38 @@ static void equate_decl_number_to_die PARAMS ((tree, dw_die_ref));
static void print_spaces PARAMS ((FILE *));
static void print_die PARAMS ((dw_die_ref, FILE *));
static void print_dwarf_line_table PARAMS ((FILE *));
+static void reverse_die_lists PARAMS ((dw_die_ref));
+static void reverse_all_dies PARAMS ((dw_die_ref));
+static dw_die_ref push_new_compile_unit PARAMS ((dw_die_ref, dw_die_ref));
+static dw_die_ref pop_compile_unit PARAMS ((dw_die_ref));
+static void loc_checksum PARAMS ((dw_loc_descr_ref, struct md5_ctx *));
+static void attr_checksum PARAMS ((dw_attr_ref, struct md5_ctx *));
+static void die_checksum PARAMS ((dw_die_ref, struct md5_ctx *));
+static void compute_section_prefix PARAMS ((dw_die_ref));
+static int is_type_die PARAMS ((dw_die_ref));
+static int is_comdat_die PARAMS ((dw_die_ref));
+static int is_symbol_die PARAMS ((dw_die_ref));
+static char *gen_internal_sym PARAMS ((void));
+static void assign_symbol_names PARAMS ((dw_die_ref));
+static void break_out_includes PARAMS ((dw_die_ref));
static void add_sibling_attributes PARAMS ((dw_die_ref));
static void build_abbrev_table PARAMS ((dw_die_ref));
static unsigned long size_of_string PARAMS ((const char *));
static int constant_size PARAMS ((long unsigned));
static unsigned long size_of_die PARAMS ((dw_die_ref));
static void calc_die_sizes PARAMS ((dw_die_ref));
+static void clear_die_sizes PARAMS ((dw_die_ref));
static unsigned long size_of_line_prolog PARAMS ((void));
static unsigned long size_of_pubnames PARAMS ((void));
static unsigned long size_of_aranges PARAMS ((void));
static enum dwarf_form value_format PARAMS ((dw_attr_ref));
static void output_value_format PARAMS ((dw_attr_ref));
static void output_abbrev_section PARAMS ((void));
+static void output_die_symbol PARAMS ((dw_die_ref));
+static void output_symbolic_ref PARAMS ((dw_die_ref));
static void output_die PARAMS ((dw_die_ref));
static void output_compilation_unit_header PARAMS ((void));
+static void output_comp_unit PARAMS ((dw_die_ref));
static const char *dwarf2_name PARAMS ((tree, int));
static void add_pubname PARAMS ((tree, dw_die_ref));
static void output_pubnames PARAMS ((void));
@@ -3441,7 +3462,6 @@ static void gen_type_die_for_member PARAMS ((tree, tree, dw_die_ref));
static void gen_abstract_function PARAMS ((tree));
static rtx save_rtx PARAMS ((rtx));
static void splice_child_die PARAMS ((dw_die_ref, dw_die_ref));
-static void reverse_die_lists PARAMS ((dw_die_ref));
/* Section names used to hold DWARF debugging information. */
#ifndef DEBUG_INFO_SECTION
@@ -3728,6 +3748,10 @@ dwarf_tag_name (tag)
return "DW_TAG_function_template";
case DW_TAG_class_template:
return "DW_TAG_class_template";
+ case DW_TAG_GNU_BINCL:
+ return "DW_TAG_GNU_BINCL";
+ case DW_TAG_GNU_EINCL:
+ return "DW_TAG_GNU_EINCL";
default:
return "DW_TAG_<unknown>";
}
@@ -4079,7 +4103,7 @@ decl_class_context (decl)
}
/* Add an attribute/value pair to a DIE. We build the lists up in reverse
- addition order, and correct that in add_sibling_attributes. */
+ addition order, and correct that in reverse_all_dies. */
static inline void
add_dwarf_attr (die, attr)
@@ -4264,7 +4288,8 @@ add_AT_die_ref (die, attr_kind, targ_die)
attr->dw_attr_next = NULL;
attr->dw_attr = attr_kind;
attr->dw_attr_val.val_class = dw_val_class_die_ref;
- attr->dw_attr_val.v.val_die_ref = targ_die;
+ attr->dw_attr_val.v.val_die_ref.die = targ_die;
+ attr->dw_attr_val.v.val_die_ref.external = 0;
add_dwarf_attr (die, attr);
}
@@ -4274,11 +4299,34 @@ AT_ref (a)
register dw_attr_ref a;
{
if (a && AT_class (a) == dw_val_class_die_ref)
- return a->dw_attr_val.v.val_die_ref;
+ return a->dw_attr_val.v.val_die_ref.die;
abort ();
}
+static inline int AT_ref_external PARAMS ((dw_attr_ref));
+static inline int
+AT_ref_external (a)
+ register dw_attr_ref a;
+{
+ if (a && AT_class (a) == dw_val_class_die_ref)
+ return a->dw_attr_val.v.val_die_ref.external;
+
+ return 0;
+}
+
+static inline void set_AT_ref_external PARAMS ((dw_attr_ref, int));
+static inline void
+set_AT_ref_external (a, i)
+ register dw_attr_ref a;
+ int i;
+{
+ if (a && AT_class (a) == dw_val_class_die_ref)
+ a->dw_attr_val.v.val_die_ref.external = i;
+ else
+ abort ();
+}
+
/* Add an FDE reference attribute value to a DIE. */
static inline void
@@ -4611,7 +4659,7 @@ remove_children (die)
}
/* Add a child DIE below its parent. We build the lists up in reverse
- addition order, and correct that in add_sibling_attributes. */
+ addition order, and correct that in reverse_all_dies. */
static inline void
add_child_die (die, child_die)
@@ -4677,6 +4725,7 @@ new_die (tag_value, parent_die)
die->die_parent = NULL;
die->die_sib = NULL;
die->die_attr = NULL;
+ die->die_symbol = NULL;
if (parent_die != NULL)
add_child_die (parent_die, die);
@@ -4822,7 +4871,12 @@ print_die (die, outfile)
break;
case dw_val_class_die_ref:
if (AT_ref (a) != NULL)
- fprintf (outfile, "die -> %lu", AT_ref (a)->die_offset);
+ {
+ if (AT_ref (a)->die_offset == 0)
+ fprintf (outfile, "die -> label: %s", AT_ref (a)->die_symbol);
+ else
+ fprintf (outfile, "die -> %lu", AT_ref (a)->die_offset);
+ }
else
fprintf (outfile, "die -> <null>");
break;
@@ -4851,6 +4905,8 @@ print_die (die, outfile)
print_indent -= 4;
}
+ if (print_indent == 0)
+ fprintf (outfile, "\n");
}
/* Print the contents of the source code line number correspondence table.
@@ -4925,20 +4981,379 @@ reverse_die_lists (die)
die->die_child = cp;
}
-/* Traverse the DIE, reverse its lists of attributes and children, and
- add a sibling attribute if it may have the effect of speeding up
- access to siblings. To save some space, avoid generating sibling
- attributes for DIE's without children. */
+/* reverse_die_lists only reverses the single die you pass it. Since
+ we used to reverse all dies in add_sibling_attributes, which runs
+ through all the dies, it would reverse all the dies. Now, however,
+ since we don't call reverse_die_lists in add_sibling_attributes, we
+ need a routine to recursively reverse all the dies. This is that
+ routine. */
static void
-add_sibling_attributes (die)
+reverse_all_dies (die)
register dw_die_ref die;
{
register dw_die_ref c;
reverse_die_lists (die);
- if (die != comp_unit_die && die->die_sib && die->die_child != NULL)
+ for (c = die->die_child; c; c = c->die_sib)
+ reverse_all_dies (c);
+}
+
+/* Start a new compilation unit DIE for an include file. OLD_UNIT is
+ the CU for the enclosing include file, if any. BINCL_DIE is the
+ DW_TAG_GNU_BINCL DIE that marks the start of the DIEs for this
+ include file. */
+
+static dw_die_ref
+push_new_compile_unit (old_unit, bincl_die)
+ dw_die_ref old_unit, bincl_die;
+{
+ const char *filename = get_AT_string (bincl_die, DW_AT_name);
+ dw_die_ref new_unit = gen_compile_unit_die (filename);
+ new_unit->die_sib = old_unit;
+ return new_unit;
+}
+
+/* Close an include-file CU and reopen the enclosing one. */
+
+static dw_die_ref
+pop_compile_unit (old_unit)
+ dw_die_ref old_unit;
+{
+ dw_die_ref new_unit = old_unit->die_sib;
+ old_unit->die_sib = NULL;
+ return new_unit;
+}
+
+#define PROCESS(FOO) md5_process_bytes (&(FOO), sizeof (FOO), ctx)
+#define PROCESS_STRING(FOO) md5_process_bytes ((FOO), strlen (FOO), ctx)
+
+/* Calculate the checksum of a location expression. */
+
+static inline void
+loc_checksum (loc, ctx)
+ dw_loc_descr_ref loc;
+ struct md5_ctx *ctx;
+{
+ PROCESS (loc->dw_loc_opc);
+ PROCESS (loc->dw_loc_oprnd1);
+ PROCESS (loc->dw_loc_oprnd2);
+}
+
+/* Calculate the checksum of an attribute. */
+
+static void
+attr_checksum (at, ctx)
+ dw_attr_ref at;
+ struct md5_ctx *ctx;
+{
+ dw_loc_descr_ref loc;
+ rtx r;
+
+ PROCESS (at->dw_attr);
+
+ /* We don't care about differences in file numbering. */
+ if (at->dw_attr == DW_AT_decl_file)
+ return;
+
+ switch (AT_class (at))
+ {
+ case dw_val_class_const:
+ PROCESS (at->dw_attr_val.v.val_int);
+ break;
+ case dw_val_class_unsigned_const:
+ PROCESS (at->dw_attr_val.v.val_unsigned);
+ break;
+ case dw_val_class_long_long:
+ PROCESS (at->dw_attr_val.v.val_long_long);
+ break;
+ case dw_val_class_float:
+ PROCESS (at->dw_attr_val.v.val_float);
+ break;
+ case dw_val_class_flag:
+ PROCESS (at->dw_attr_val.v.val_flag);
+ break;
+
+ case dw_val_class_str:
+ PROCESS_STRING (AT_string (at));
+ break;
+ case dw_val_class_addr:
+ r = AT_addr (at);
+ switch (GET_CODE (r))
+ {
+ case SYMBOL_REF:
+ PROCESS_STRING (XSTR (r, 0));
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ case dw_val_class_loc:
+ for (loc = AT_loc (at); loc; loc = loc->dw_loc_next)
+ loc_checksum (loc, ctx);
+ break;
+
+ case dw_val_class_die_ref:
+ if (AT_ref (at)->die_offset)
+ PROCESS (AT_ref (at)->die_offset);
+ /* FIXME else use target die name or something. */
+
+ case dw_val_class_fde_ref:
+ case dw_val_class_lbl_id:
+ case dw_val_class_lbl_offset:
+
+ default:
+ break;
+ }
+}
+
+/* Calculate the checksum of a DIE. */
+
+static void
+die_checksum (die, ctx)
+ dw_die_ref die;
+ struct md5_ctx *ctx;
+{
+ dw_die_ref c;
+ dw_attr_ref a;
+
+ PROCESS (die->die_tag);
+
+ for (a = die->die_attr; a; a = a->dw_attr_next)
+ attr_checksum (a, ctx);
+
+ for (c = die->die_child; c; c = c->die_sib)
+ die_checksum (c, ctx);
+}
+
+#undef PROCESS
+#undef PROCESS_STRING
+
+/* The prefix to attach to symbols on DIEs in the current comdat debug
+ info section. */
+static char *comdat_symbol_id;
+
+/* The index of the current symbol within the current comdat CU. */
+static unsigned int comdat_symbol_number;
+
+/* Calculate the MD5 checksum of the compilation unit DIE UNIT_DIE and its
+ children, and set comdat_symbol_id accordingly. */
+
+static void
+compute_section_prefix (unit_die)
+ dw_die_ref unit_die;
+{
+ char *p, *name;
+ int i;
+ unsigned char checksum[16];
+ struct md5_ctx ctx;
+
+ md5_init_ctx (&ctx);
+ die_checksum (unit_die, &ctx);
+ md5_finish_ctx (&ctx, checksum);
+
+ p = file_name_nondirectory (get_AT_string (unit_die, DW_AT_name));
+ name = (char *) alloca (strlen (p) + 64);
+ sprintf (name, "%s.", p);
+
+ clean_symbol_name (name);
+
+ p = name + strlen (name);
+ for (i = 0; i < 4; ++i)
+ {
+ sprintf (p, "%.2x", checksum[i]);
+ p += 2;
+ }
+
+ comdat_symbol_id = unit_die->die_symbol = xstrdup (name);
+ comdat_symbol_number = 0;
+}
+
+/* Returns nonzero iff DIE represents a type, in the sense of TYPE_P. */
+
+static int
+is_type_die (die)
+ dw_die_ref die;
+{
+ switch (die->die_tag)
+ {
+ case DW_TAG_array_type:
+ case DW_TAG_class_type:
+ case DW_TAG_enumeration_type:
+ case DW_TAG_pointer_type:
+ case DW_TAG_reference_type:
+ case DW_TAG_string_type:
+ case DW_TAG_structure_type:
+ case DW_TAG_subroutine_type:
+ case DW_TAG_union_type:
+ case DW_TAG_ptr_to_member_type:
+ case DW_TAG_set_type:
+ case DW_TAG_subrange_type:
+ case DW_TAG_base_type:
+ case DW_TAG_const_type:
+ case DW_TAG_file_type:
+ case DW_TAG_packed_type:
+ case DW_TAG_volatile_type:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* Returns 1 iff C is the sort of DIE that should go into a COMDAT CU.
+ Basically, we want to choose the bits that are likely to be shared between
+ compilations (types) and leave out the bits that are specific to individual
+ compilations (functions). */
+
+static int
+is_comdat_die (c)
+ dw_die_ref c;
+{
+#if 1
+ /* I think we want to leave base types and __vtbl_ptr_type in the
+ main CU, as we do for stabs. The advantage is a greater
+ likelihood of sharing between objects that don't include headers
+ in the same order (and therefore would put the base types in a
+ different comdat). jason 8/28/00 */
+ if (c->die_tag == DW_TAG_base_type)
+ return 0;
+
+ if (c->die_tag == DW_TAG_pointer_type
+ || c->die_tag == DW_TAG_reference_type
+ || c->die_tag == DW_TAG_const_type
+ || c->die_tag == DW_TAG_volatile_type)
+ {
+ dw_die_ref t = get_AT_ref (c, DW_AT_type);
+ return t ? is_comdat_die (t) : 0;
+ }
+#endif
+
+ return is_type_die (c);
+}
+
+/* Returns 1 iff C is the sort of DIE that might be referred to from another
+ compilation unit. */
+
+static int
+is_symbol_die (c)
+ dw_die_ref c;
+{
+ if (is_type_die (c))
+ return 1;
+ if (get_AT (c, DW_AT_declaration)
+ && ! get_AT (c, DW_AT_specification))
+ return 1;
+ return 0;
+}
+
+static char *
+gen_internal_sym ()
+{
+ char buf[256];
+ static int label_num;
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LDIE", label_num++);
+ return xstrdup (buf);
+}
+
+/* Assign symbols to all worthy DIEs under DIE. */
+
+static void
+assign_symbol_names (die)
+ register dw_die_ref die;
+{
+ register dw_die_ref c;
+
+ if (is_symbol_die (die))
+ {
+ if (comdat_symbol_id)
+ {
+ char *p = alloca (strlen (comdat_symbol_id) + 64);
+ sprintf (p, "%s.%s.%x", DIE_LABEL_PREFIX,
+ comdat_symbol_id, comdat_symbol_number++);
+ die->die_symbol = xstrdup (p);
+ }
+ else
+ die->die_symbol = gen_internal_sym ();
+ }
+
+ for (c = die->die_child; c != NULL; c = c->die_sib)
+ assign_symbol_names (c);
+}
+
+/* Traverse the DIE (which is always comp_unit_die), and set up
+ additional compilation units for each of the include files we see
+ bracketed by BINCL/EINCL. */
+
+static void
+break_out_includes (die)
+ register dw_die_ref die;
+{
+ dw_die_ref *ptr;
+ register dw_die_ref unit = NULL;
+ limbo_die_node *node;
+
+ for (ptr = &(die->die_child); *ptr; )
+ {
+ register dw_die_ref c = *ptr;
+
+ if (c->die_tag == DW_TAG_GNU_BINCL
+ || c->die_tag == DW_TAG_GNU_EINCL
+ || (unit && is_comdat_die (c)))
+ {
+ /* This DIE is for a secondary CU; remove it from the main one. */
+ *ptr = c->die_sib;
+
+ if (c->die_tag == DW_TAG_GNU_BINCL)
+ {
+ unit = push_new_compile_unit (unit, c);
+ free_die (c);
+ }
+ else if (c->die_tag == DW_TAG_GNU_EINCL)
+ {
+ unit = pop_compile_unit (unit);
+ free_die (c);
+ }
+ else
+ add_child_die (unit, c);
+ }
+ else
+ {
+ /* Leave this DIE in the main CU. */
+ ptr = &(c->die_sib);
+ continue;
+ }
+ }
+
+#if 0
+ /* We can only use this in debugging, since the frontend doesn't check
+ to make sure that we leave every include file we enter. */
+ if (unit != NULL)
+ abort ();
+#endif
+
+ assign_symbol_names (die);
+ for (node = limbo_die_list; node; node = node->next)
+ {
+ compute_section_prefix (node->die);
+ assign_symbol_names (node->die);
+ }
+}
+
+/* Traverse the DIE and add a sibling attribute if it may have the
+ effect of speeding up access to siblings. To save some space,
+ avoid generating sibling attributes for DIE's without children. */
+
+static void
+add_sibling_attributes (die)
+ register dw_die_ref die;
+{
+ register dw_die_ref c;
+
+ if (die->die_tag != DW_TAG_compile_unit
+ && die->die_sib && die->die_child != NULL)
/* Add the sibling link to the front of the attribute list. */
add_AT_die_ref (die, DW_AT_sibling, die->die_sib);
@@ -4960,6 +5375,20 @@ build_abbrev_table (die)
register unsigned long n_alloc;
register dw_die_ref c;
register dw_attr_ref d_attr, a_attr;
+
+ /* Scan the DIE references, and mark as external any that refer to
+ DIEs from other CUs (i.e. those with cleared die_offset). */
+ for (d_attr = die->die_attr; d_attr; d_attr = d_attr->dw_attr_next)
+ {
+ if (AT_class (d_attr) == dw_val_class_die_ref
+ && AT_ref (d_attr)->die_offset == 0)
+ {
+ if (AT_ref (d_attr)->die_symbol == 0)
+ abort ();
+ set_AT_ref_external (d_attr, 1);
+ }
+ }
+
for (abbrev_id = 1; abbrev_id < abbrev_die_table_in_use; ++abbrev_id)
{
register dw_die_ref abbrev = abbrev_die_table[abbrev_id];
@@ -5130,6 +5559,20 @@ calc_die_sizes (die)
next_die_offset += 1;
}
+/* Clear the offsets and sizes for a die and its children. We do this so
+ that we know whether or not a reference needs to use FORM_ref_addr; only
+ DIEs in the same CU will have non-zero offsets available. */
+
+static void
+clear_die_sizes (die)
+ dw_die_ref die;
+{
+ register dw_die_ref c;
+ die->die_offset = 0;
+ for (c = die->die_child; c; c = c->die_sib)
+ clear_die_sizes (c);
+}
+
/* Return the size of the line information prolog generated for the
compilation unit. */
@@ -5250,7 +5693,10 @@ value_format (a)
case dw_val_class_flag:
return DW_FORM_flag;
case dw_val_class_die_ref:
- return DW_FORM_ref;
+ if (AT_ref_external (a))
+ return DW_FORM_ref_addr;
+ else
+ return DW_FORM_ref;
case dw_val_class_fde_ref:
return DW_FORM_data;
case dw_val_class_lbl_id:
@@ -5333,6 +5779,39 @@ output_abbrev_section ()
fprintf (asm_out_file, "\t%s\t0\n", ASM_BYTE_OP);
}
+/* Output a symbol we can use to refer to this DIE from another CU. */
+
+static inline void
+output_die_symbol (die)
+ register dw_die_ref die;
+{
+ char *sym = die->die_symbol;
+
+ if (sym == 0)
+ return;
+
+ if (strncmp (sym, DIE_LABEL_PREFIX, sizeof (DIE_LABEL_PREFIX) - 1) == 0)
+ /* We make these global, not weak; if the target doesn't support
+ .linkonce, it doesn't support combining the sections, so debugging
+ will break. */
+ ASM_GLOBALIZE_LABEL (asm_out_file, sym);
+ ASM_OUTPUT_LABEL (asm_out_file, sym);
+}
+
+/* Output a symbolic (i.e. FORM_ref_addr) reference to TARGET_DIE. */
+
+static inline void
+output_symbolic_ref (target_die)
+ dw_die_ref target_die;
+{
+ char *sym = target_die->die_symbol;
+
+ if (sym == 0)
+ abort ();
+
+ ASM_OUTPUT_DWARF_OFFSET (asm_out_file, sym);
+}
+
/* Output the DIE and its attributes. Called recursively to generate
the definitions of each child DIE. */
@@ -5344,6 +5823,11 @@ output_die (die)
register dw_die_ref c;
register unsigned long size;
+ /* If someone in another CU might refer to us, set up a symbol for
+ them to point to. */
+ if (die->die_symbol)
+ output_die_symbol (die);
+
output_uleb128 (die->die_abbrev);
if (flag_debug_asm)
fprintf (asm_out_file, " (DIE (0x%lx) %s)",
@@ -5457,7 +5941,10 @@ output_die (die)
break;
case dw_val_class_die_ref:
- ASM_OUTPUT_DWARF_DATA (asm_out_file, AT_ref (a)->die_offset);
+ if (AT_ref_external (a))
+ output_symbolic_ref (AT_ref (a));
+ else
+ ASM_OUTPUT_DWARF_DATA (asm_out_file, AT_ref (a)->die_offset);
break;
case dw_val_class_fde_ref:
@@ -5547,6 +6034,44 @@ output_compilation_unit_header ()
fputc ('\n', asm_out_file);
}
+/* Output the compilation unit DIE and its children. */
+
+static void
+output_comp_unit (die)
+ dw_die_ref die;
+{
+ char *secname;
+
+ if (die->die_child == 0)
+ return;
+
+ /* Initialize the beginning DIE offset - and calculate sizes/offsets. */
+ next_die_offset = DWARF_COMPILE_UNIT_HEADER_SIZE;
+ calc_die_sizes (die);
+
+ build_abbrev_table (die);
+
+ if (die->die_symbol)
+ {
+ secname = (char *) alloca (strlen (die->die_symbol) + 24);
+ sprintf (secname, ".gnu.linkonce.wi.%s", die->die_symbol);
+ die->die_symbol = NULL;
+ }
+ else
+ secname = (char *) DEBUG_INFO_SECTION;
+
+ /* Output debugging information. */
+ fputc ('\n', asm_out_file);
+ ASM_OUTPUT_SECTION (asm_out_file, secname);
+ output_compilation_unit_header ();
+ output_die (die);
+
+ /* Leave the sizes on the main CU, since we do it last and we use the
+ sizes in output_pubnames. */
+ if (die->die_symbol)
+ clear_die_sizes (die);
+}
+
/* The DWARF2 pubname for a nested thingy looks like "A::f". The output
of decl_printable_name for C++ looks like "A::f(int)". Let's drop the
argument list, and maybe the scope. */
@@ -5622,6 +6147,10 @@ output_pubnames ()
{
register pubname_ref pub = &pubname_table[i];
+ /* We shouldn't see pubnames for DIEs outside of the main CU. */
+ if (pub->die->die_offset == 0)
+ abort ();
+
ASM_OUTPUT_DWARF_DATA (asm_out_file, pub->die->die_offset);
if (flag_debug_asm)
fprintf (asm_out_file, "\t%s DIE offset", ASM_COMMENT_START);
@@ -5735,6 +6264,10 @@ output_aranges ()
{
dw_die_ref die = arange_table[i];
+ /* We shouldn't see aranges for DIEs outside of the main CU. */
+ if (die->die_offset == 0)
+ abort ();
+
if (die->die_tag == DW_TAG_subprogram)
ASM_OUTPUT_DWARF_ADDR (asm_out_file, get_AT_low_pc (die));
else
@@ -10134,6 +10667,12 @@ void
dwarf2out_start_source_file (filename)
register const char *filename ATTRIBUTE_UNUSED;
{
+ if (flag_eliminate_dwarf2_dups)
+ {
+ /* Record the beginning of the file for break_out_includes. */
+ dw_die_ref bincl_die = new_die (DW_TAG_GNU_BINCL, comp_unit_die);
+ add_AT_string (bincl_die, DW_AT_name, filename);
+ }
}
/* Record the end of a source file, for later output
@@ -10142,6 +10681,11 @@ dwarf2out_start_source_file (filename)
void
dwarf2out_end_source_file ()
{
+ if (flag_eliminate_dwarf2_dups)
+ {
+ /* Record the end of the file for break_out_includes. */
+ new_die (DW_TAG_GNU_EINCL, comp_unit_die);
+ }
}
/* Called from check_newline in c-parse.y. The `buffer' parameter contains
@@ -10291,9 +10835,19 @@ dwarf2out_finish ()
emit full debugging info for them. */
retry_incomplete_types ();
- /* Traverse the DIE's, reverse their lists of attributes and children,
- and add add sibling attributes to those DIE's that have children. */
+ /* We need to reverse all the dies before break_out_includes, or
+ we'll see the end of an include file before the beginning. */
+ reverse_all_dies (comp_unit_die);
+
+ /* Generate separate CUs for each of the include files we've seen.
+ They will go into limbo_die_list. */
+ break_out_includes (comp_unit_die);
+
+ /* Traverse the DIE's and add add sibling attributes to those DIE's
+ that have children. */
add_sibling_attributes (comp_unit_die);
+ for (node = limbo_die_list; node; node = node->next)
+ add_sibling_attributes (node->die);
/* Output a terminator label for the .text section. */
fputc ('\n', asm_out_file);
@@ -10339,22 +10893,17 @@ dwarf2out_finish ()
add_AT_unsigned (die, DW_AT_macro_info, 0);
#endif
+ /* Output all of the compilation units. We put the main one last so that
+ the offsets are available to output_pubnames. */
+ for (node = limbo_die_list; node; node = node->next)
+ output_comp_unit (node->die);
+ output_comp_unit (comp_unit_die);
+
/* Output the abbreviation table. */
fputc ('\n', asm_out_file);
ASM_OUTPUT_SECTION (asm_out_file, ABBREV_SECTION);
- build_abbrev_table (comp_unit_die);
output_abbrev_section ();
- /* Initialize the beginning DIE offset - and calculate sizes/offsets. */
- next_die_offset = DWARF_COMPILE_UNIT_HEADER_SIZE;
- calc_die_sizes (comp_unit_die);
-
- /* Output debugging information. */
- fputc ('\n', asm_out_file);
- ASM_OUTPUT_SECTION (asm_out_file, DEBUG_INFO_SECTION);
- output_compilation_unit_header ();
- output_die (comp_unit_die);
-
if (pubname_table_in_use)
{
/* Output public names table. */
diff --git a/gcc/flags.h b/gcc/flags.h
index de5b099d6c0..a294c37de20 100644
--- a/gcc/flags.h
+++ b/gcc/flags.h
@@ -577,3 +577,7 @@ extern enum graph_dump_types graph_dump_format;
string identifying the compiler. */
extern int flag_no_ident;
+
+/* Nonzero means we should do dwarf2 duplicate elimination. */
+
+extern int flag_eliminate_dwarf2_dups;
diff --git a/gcc/toplev.c b/gcc/toplev.c
index bbe752d730f..0d5d4f21ee7 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -422,6 +422,10 @@ tree (*lang_expand_constant) PARAMS ((tree)) = 0;
void (*incomplete_decl_finalize_hook) PARAMS ((tree)) = 0;
+/* Nonzero if doing dwarf2 duplicate elimination. */
+
+int flag_eliminate_dwarf2_dups = 0;
+
/* Nonzero if generating code to do profiling. */
int profile_flag = 0;
@@ -944,6 +948,8 @@ const char *user_label_prefix;
lang_independent_options f_options[] =
{
+ {"eliminate-dwarf2-dups", &flag_eliminate_dwarf2_dups, 1,
+ "Perform DWARF2 duplicate elimination"},
{"float-store", &flag_float_store, 1,
"Do not store floats in registers" },
{"volatile", &flag_volatile, 1,
@@ -1656,6 +1662,21 @@ strip_off_ending (name, len)
}
}
+/* Given a file name X, return the nondirectory portion. */
+
+char *
+file_name_nondirectory (x)
+ const char *x;
+{
+ char *tmp = (char *) rindex (x, '/');
+ if (DIR_SEPARATOR != '/' && ! tmp)
+ tmp = (char *) rindex (x, DIR_SEPARATOR);
+ if (tmp)
+ return (char *) (tmp + 1);
+ else
+ return (char *) x;
+}
+
/* Output a quoted string. */
void
@@ -2561,6 +2582,10 @@ rest_of_type_compilation (type, toplev)
if (write_symbols == SDB_DEBUG)
sdbout_symbol (TYPE_STUB_DECL (type), !toplev);
#endif
+#ifdef DWARF2_DEBUGGING_INFO
+ if (write_symbols == DWARF2_DEBUG && toplev)
+ dwarf2out_decl (TYPE_STUB_DECL (type));
+#endif
timevar_pop (TV_SYMOUT);
}
@@ -4973,8 +4998,7 @@ debug_start_source_file (filename)
dwarfout_start_new_source_file (filename);
#endif /* DWARF_DEBUGGING_INFO */
#ifdef DWARF2_DEBUGGING_INFO
- if (debug_info_level == DINFO_LEVEL_VERBOSE
- && write_symbols == DWARF2_DEBUG)
+ if (write_symbols == DWARF2_DEBUG)
dwarf2out_start_source_file (filename);
#endif /* DWARF2_DEBUGGING_INFO */
#ifdef SDB_DEBUGGING_INFO
@@ -5000,8 +5024,7 @@ debug_end_source_file (lineno)
dwarfout_resume_previous_source_file (lineno);
#endif /* DWARF_DEBUGGING_INFO */
#ifdef DWARF2_DEBUGGING_INFO
- if (debug_info_level == DINFO_LEVEL_VERBOSE
- && write_symbols == DWARF2_DEBUG)
+ if (write_symbols == DWARF2_DEBUG)
dwarf2out_end_source_file ();
#endif /* DWARF2_DEBUGGING_INFO */
#ifdef SDB_DEBUGGING_INFO
diff --git a/gcc/toplev.h b/gcc/toplev.h
index d61c5bfcf8d..fdfa6730b93 100644
--- a/gcc/toplev.h
+++ b/gcc/toplev.h
@@ -35,6 +35,7 @@ extern int read_integral_parameter PARAMS ((const char *, const char *,
const int));
extern int count_error PARAMS ((int));
extern void strip_off_ending PARAMS ((char *, int));
+extern char *file_name_nondirectory PARAMS ((const char *));
extern void print_time PARAMS ((const char *, long));
extern void debug_start_source_file PARAMS ((const char *));
extern void debug_end_source_file PARAMS ((unsigned));
diff --git a/gcc/tree.c b/gcc/tree.c
index 6ef5dd30ff0..23612b35605 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -5386,6 +5386,26 @@ append_random_chars (template)
template[6] = '\0';
}
+/* P is a string that will be used in a symbol. Mask out any characters
+ that are not valid in that context. */
+
+void
+clean_symbol_name (p)
+ char *p;
+{
+ for (; *p; p++)
+ if (! ( ISDIGIT(*p)
+#ifndef NO_DOLLAR_IN_LABEL /* this for `$'; unlikely, but... -- kr */
+ || *p == '$'
+#endif
+#ifndef NO_DOT_IN_LABEL /* this for `.'; unlikely, but... */
+ || *p == '.'
+#endif
+ || ISUPPER(*p)
+ || ISLOWER(*p)))
+ *p = '_';
+}
+
/* Generate a name for a function unique to this translation unit.
TYPE is some string to identify the purpose of this function to the
linker or collect2. */
@@ -5431,19 +5451,7 @@ get_file_function_name_long (type)
/* Don't need to pull weird characters out of global names. */
if (p != first_global_object_name)
- {
- for (q = buf+11; *q; q++)
- if (! ( ISDIGIT(*q)
-#ifndef NO_DOLLAR_IN_LABEL /* this for `$'; unlikely, but... -- kr */
- || *q == '$'
-#endif
-#ifndef NO_DOT_IN_LABEL /* this for `.'; unlikely, but... */
- || *q == '.'
-#endif
- || ISUPPER(*q)
- || ISLOWER(*q)))
- *q = '_';
- }
+ clean_symbol_name (buf + 11);
return get_identifier (buf);
}
diff --git a/gcc/tree.h b/gcc/tree.h
index 067af986a26..80bb787b7c9 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2435,6 +2435,7 @@ extern tree builtin_function PARAMS ((const char *, tree, int,
/* In tree.c */
extern char *perm_calloc PARAMS ((int, long));
+extern void clean_symbol_name PARAMS ((char *));
extern tree get_file_function_name PARAMS ((int));
extern tree get_file_function_name_long PARAMS ((const char *));
extern tree get_set_constructor_bits PARAMS ((tree, char *, int));
diff --git a/include/ChangeLog b/include/ChangeLog
index b31a6550393..6c68319a244 100644
--- a/include/ChangeLog
+++ b/include/ChangeLog
@@ -1,3 +1,7 @@
+2000-08-28 Jason Merrill <jason@redhat.com>
+
+ * md5.h: New file.
+
2000-08-24 Greg McGary <greg@mcgary.org>
* libiberty.h (ARRAY_SIZE): New macro.
diff --git a/include/md5.h b/include/md5.h
new file mode 100644
index 00000000000..ad97efc3213
--- /dev/null
+++ b/include/md5.h
@@ -0,0 +1,146 @@
+/* md5.h - Declaration of functions and data types used for MD5 sum
+ computing library functions.
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ NOTE: The canonical source of this file is maintained with the GNU C
+ Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+ 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, 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. */
+
+#ifndef _MD5_H
+#define _MD5_H 1
+
+#include <stdio.h>
+
+#if defined HAVE_LIMITS_H || _LIBC
+# include <limits.h>
+#endif
+
+/* The following contortions are an attempt to use the C preprocessor
+ to determine an unsigned integral type that is 32 bits wide. An
+ alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but
+ doing that would require that the configure script compile and *run*
+ the resulting executable. Locally running cross-compiled executables
+ is usually not possible. */
+
+#ifdef _LIBC
+# include <sys/types.h>
+typedef u_int32_t md5_uint32;
+#else
+# if defined __STDC__ && __STDC__
+# define UINT_MAX_32_BITS 4294967295U
+# else
+# define UINT_MAX_32_BITS 0xFFFFFFFF
+# endif
+
+/* If UINT_MAX isn't defined, assume it's a 32-bit type.
+ This should be valid for all systems GNU cares about because
+ that doesn't include 16-bit systems, and only modern systems
+ (that certainly have <limits.h>) have 64+-bit integral types. */
+
+# ifndef UINT_MAX
+# define UINT_MAX UINT_MAX_32_BITS
+# endif
+
+# if UINT_MAX == UINT_MAX_32_BITS
+ typedef unsigned int md5_uint32;
+# else
+# if USHRT_MAX == UINT_MAX_32_BITS
+ typedef unsigned short md5_uint32;
+# else
+# if ULONG_MAX == UINT_MAX_32_BITS
+ typedef unsigned long md5_uint32;
+# else
+ /* The following line is intended to evoke an error.
+ Using #error is not portable enough. */
+ "Cannot determine unsigned 32-bit data type."
+# endif
+# endif
+# endif
+#endif
+
+#undef __P
+#if defined (__STDC__) && __STDC__
+#define __P(x) x
+#else
+#define __P(x) ()
+#endif
+
+/* Structure to save state of computation between the single steps. */
+struct md5_ctx
+{
+ md5_uint32 A;
+ md5_uint32 B;
+ md5_uint32 C;
+ md5_uint32 D;
+
+ md5_uint32 total[2];
+ md5_uint32 buflen;
+ char buffer[128];
+};
+
+/*
+ * The following three functions are build up the low level used in
+ * the functions `md5_stream' and `md5_buffer'.
+ */
+
+/* Initialize structure containing state of computation.
+ (RFC 1321, 3.3: Step 3) */
+extern void md5_init_ctx __P ((struct md5_ctx *ctx));
+
+/* Starting with the result of former calls of this function (or the
+ initialization function update the context for the next LEN bytes
+ starting at BUFFER.
+ It is necessary that LEN is a multiple of 64!!! */
+extern void md5_process_block __P ((const void *buffer, size_t len,
+ struct md5_ctx *ctx));
+
+/* Starting with the result of former calls of this function (or the
+ initialization function update the context for the next LEN bytes
+ starting at BUFFER.
+ It is NOT required that LEN is a multiple of 64. */
+extern void md5_process_bytes __P ((const void *buffer, size_t len,
+ struct md5_ctx *ctx));
+
+/* Process the remaining bytes in the buffer and put result from CTX
+ in first 16 bytes following RESBUF. The result is always in little
+ endian byte order, so that a byte-wise output yields to the wanted
+ ASCII representation of the message digest.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+extern void *md5_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf));
+
+
+/* Put result from CTX in first 16 bytes following RESBUF. The result is
+ always in little endian byte order, so that a byte-wise output yields
+ to the wanted ASCII representation of the message digest.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+extern void *md5_read_ctx __P ((const struct md5_ctx *ctx, void *resbuf));
+
+
+/* Compute MD5 message digest for bytes read from STREAM. The
+ resulting message digest number will be written into the 16 bytes
+ beginning at RESBLOCK. */
+extern int md5_stream __P ((FILE *stream, void *resblock));
+
+/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The
+ result is always in little endian byte order, so that a byte-wise
+ output yields to the wanted ASCII representation of the message
+ digest. */
+extern void *md5_buffer __P ((const char *buffer, size_t len, void *resblock));
+
+#endif
diff --git a/libiberty/ChangeLog b/libiberty/ChangeLog
index 44ca8085351..6849b0019e8 100644
--- a/libiberty/ChangeLog
+++ b/libiberty/ChangeLog
@@ -1,3 +1,9 @@
+2000-08-28 Jason Merrill <jason@redhat.com>
+
+ * Makefile.in (REQUIRED_OFILES): Add md5.o.
+ (CFILES): Add md5.c.
+ * md5.c: New file.
+
2000-08-27 Alex Samuel <samuel@codesourcery.com>
* cp-demangle.c (demangle_name): Initialize template_p in local
diff --git a/libiberty/Makefile.in b/libiberty/Makefile.in
index 242fde7841b..c46d9dd1b56 100644
--- a/libiberty/Makefile.in
+++ b/libiberty/Makefile.in
@@ -128,7 +128,7 @@ CFILES = asprintf.c alloca.c argv.c atexit.c basename.c bcmp.c bcopy.c \
bzero.c calloc.c choose-temp.c clock.c concat.c cplus-dem.c \
cp-demangle.c dyn-string.c fdmatch.c fnmatch.c getcwd.c \
getpwd.c getopt.c getopt1.c getpagesize.c getruntime.c \
- floatformat.c hashtab.c hex.c index.c insque.c memchr.c memcmp.c \
+ floatformat.c hashtab.c hex.c index.c insque.c md5.c memchr.c memcmp.c\
memcpy.c memmove.c memset.c mkstemps.c objalloc.c obstack.c \
partition.c pexecute.c putenv.c random.c rename.c rindex.c setenv.c \
sigsetmask.c sort.c spaces.c splay-tree.c strcasecmp.c strncasecmp.c \
@@ -140,7 +140,7 @@ CFILES = asprintf.c alloca.c argv.c atexit.c basename.c bcmp.c bcopy.c \
# These are always included in the library.
REQUIRED_OFILES = argv.o choose-temp.o concat.o cplus-dem.o cp-demangle.o \
dyn-string.o fdmatch.o fnmatch.o getopt.o getopt1.o getpwd.o \
- getruntime.o hashtab.o hex.o floatformat.o objalloc.o obstack.o \
+ getruntime.o hashtab.o hex.o floatformat.o md5.o objalloc.o obstack.o \
partition.o pexecute.o sort.o spaces.o splay-tree.o strerror.o \
strsignal.o xatexit.o xexit.o xmalloc.o xmemdup.o xstrdup.o \
xstrerror.o
diff --git a/libiberty/md5.c b/libiberty/md5.c
new file mode 100644
index 00000000000..d742c54f665
--- /dev/null
+++ b/libiberty/md5.c
@@ -0,0 +1,419 @@
+/* md5.c - Functions to compute MD5 message digest of files or memory blocks
+ according to the definition of MD5 in RFC 1321 from April 1992.
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ NOTE: The canonical source of this file is maintained with the GNU C
+ Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+ 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, 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. */
+
+/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+
+#if STDC_HEADERS || defined _LIBC
+# include <stdlib.h>
+# include <string.h>
+#else
+# ifndef HAVE_MEMCPY
+# define memcpy(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#include "md5.h"
+
+#ifdef _LIBC
+# include <endian.h>
+# if __BYTE_ORDER == __BIG_ENDIAN
+# define WORDS_BIGENDIAN 1
+# endif
+#endif
+
+#ifdef WORDS_BIGENDIAN
+# define SWAP(n) \
+ (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+#else
+# define SWAP(n) (n)
+#endif
+
+
+/* This array contains the bytes used to pad the buffer to the next
+ 64-byte boundary. (RFC 1321, 3.1: Step 1) */
+static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ };
+
+
+/* Initialize structure containing state of computation.
+ (RFC 1321, 3.3: Step 3) */
+void
+md5_init_ctx (ctx)
+ struct md5_ctx *ctx;
+{
+ ctx->A = 0x67452301;
+ ctx->B = 0xefcdab89;
+ ctx->C = 0x98badcfe;
+ ctx->D = 0x10325476;
+
+ ctx->total[0] = ctx->total[1] = 0;
+ ctx->buflen = 0;
+}
+
+/* Put result from CTX in first 16 bytes following RESBUF. The result
+ must be in little endian byte order.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+void *
+md5_read_ctx (ctx, resbuf)
+ const struct md5_ctx *ctx;
+ void *resbuf;
+{
+ ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A);
+ ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B);
+ ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C);
+ ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D);
+
+ return resbuf;
+}
+
+/* Process the remaining bytes in the internal buffer and the usual
+ prolog according to the standard and write the result to RESBUF.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+void *
+md5_finish_ctx (ctx, resbuf)
+ struct md5_ctx *ctx;
+ void *resbuf;
+{
+ /* Take yet unprocessed bytes into account. */
+ md5_uint32 bytes = ctx->buflen;
+ size_t pad;
+
+ /* Now count remaining bytes. */
+ ctx->total[0] += bytes;
+ if (ctx->total[0] < bytes)
+ ++ctx->total[1];
+
+ pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
+ memcpy (&ctx->buffer[bytes], fillbuf, pad);
+
+ /* Put the 64-bit file length in *bits* at the end of the buffer. */
+ *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3);
+ *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) |
+ (ctx->total[0] >> 29));
+
+ /* Process last bytes. */
+ md5_process_block (ctx->buffer, bytes + pad + 8, ctx);
+
+ return md5_read_ctx (ctx, resbuf);
+}
+
+/* Compute MD5 message digest for bytes read from STREAM. The
+ resulting message digest number will be written into the 16 bytes
+ beginning at RESBLOCK. */
+int
+md5_stream (stream, resblock)
+ FILE *stream;
+ void *resblock;
+{
+ /* Important: BLOCKSIZE must be a multiple of 64. */
+#define BLOCKSIZE 4096
+ struct md5_ctx ctx;
+ char buffer[BLOCKSIZE + 72];
+ size_t sum;
+
+ /* Initialize the computation context. */
+ md5_init_ctx (&ctx);
+
+ /* Iterate over full file contents. */
+ while (1)
+ {
+ /* We read the file in blocks of BLOCKSIZE bytes. One call of the
+ computation function processes the whole buffer so that with the
+ next round of the loop another block can be read. */
+ size_t n;
+ sum = 0;
+
+ /* Read block. Take care for partial reads. */
+ do
+ {
+ n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);
+
+ sum += n;
+ }
+ while (sum < BLOCKSIZE && n != 0);
+ if (n == 0 && ferror (stream))
+ return 1;
+
+ /* If end of file is reached, end the loop. */
+ if (n == 0)
+ break;
+
+ /* Process buffer with BLOCKSIZE bytes. Note that
+ BLOCKSIZE % 64 == 0
+ */
+ md5_process_block (buffer, BLOCKSIZE, &ctx);
+ }
+
+ /* Add the last bytes if necessary. */
+ if (sum > 0)
+ md5_process_bytes (buffer, sum, &ctx);
+
+ /* Construct result in desired memory. */
+ md5_finish_ctx (&ctx, resblock);
+ return 0;
+}
+
+/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The
+ result is always in little endian byte order, so that a byte-wise
+ output yields to the wanted ASCII representation of the message
+ digest. */
+void *
+md5_buffer (buffer, len, resblock)
+ const char *buffer;
+ size_t len;
+ void *resblock;
+{
+ struct md5_ctx ctx;
+
+ /* Initialize the computation context. */
+ md5_init_ctx (&ctx);
+
+ /* Process whole buffer but last len % 64 bytes. */
+ md5_process_bytes (buffer, len, &ctx);
+
+ /* Put result in desired memory area. */
+ return md5_finish_ctx (&ctx, resblock);
+}
+
+
+void
+md5_process_bytes (buffer, len, ctx)
+ const void *buffer;
+ size_t len;
+ struct md5_ctx *ctx;
+{
+ /* When we already have some bits in our internal buffer concatenate
+ both inputs first. */
+ if (ctx->buflen != 0)
+ {
+ size_t left_over = ctx->buflen;
+ size_t add = 128 - left_over > len ? len : 128 - left_over;
+
+ memcpy (&ctx->buffer[left_over], buffer, add);
+ ctx->buflen += add;
+
+ if (left_over + add > 64)
+ {
+ md5_process_block (ctx->buffer, (left_over + add) & ~63, ctx);
+ /* The regions in the following copy operation cannot overlap. */
+ memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
+ (left_over + add) & 63);
+ ctx->buflen = (left_over + add) & 63;
+ }
+
+ buffer = (const char *) buffer + add;
+ len -= add;
+ }
+
+ /* Process available complete blocks. */
+ if (len > 64)
+ {
+ md5_process_block (buffer, len & ~63, ctx);
+ buffer = (const char *) buffer + (len & ~63);
+ len &= 63;
+ }
+
+ /* Move remaining bytes in internal buffer. */
+ if (len > 0)
+ {
+ memcpy (ctx->buffer, buffer, len);
+ ctx->buflen = len;
+ }
+}
+
+
+/* These are the four functions used in the four steps of the MD5 algorithm
+ and defined in the RFC 1321. The first function is a little bit optimized
+ (as found in Colin Plumbs public domain implementation). */
+/* #define FF(b, c, d) ((b & c) | (~b & d)) */
+#define FF(b, c, d) (d ^ (b & (c ^ d)))
+#define FG(b, c, d) FF (d, b, c)
+#define FH(b, c, d) (b ^ c ^ d)
+#define FI(b, c, d) (c ^ (b | ~d))
+
+/* Process LEN bytes of BUFFER, accumulating context into CTX.
+ It is assumed that LEN % 64 == 0. */
+
+void
+md5_process_block (buffer, len, ctx)
+ const void *buffer;
+ size_t len;
+ struct md5_ctx *ctx;
+{
+ md5_uint32 correct_words[16];
+ const md5_uint32 *words = buffer;
+ size_t nwords = len / sizeof (md5_uint32);
+ const md5_uint32 *endp = words + nwords;
+ md5_uint32 A = ctx->A;
+ md5_uint32 B = ctx->B;
+ md5_uint32 C = ctx->C;
+ md5_uint32 D = ctx->D;
+
+ /* First increment the byte count. RFC 1321 specifies the possible
+ length of the file up to 2^64 bits. Here we only compute the
+ number of bytes. Do a double word increment. */
+ ctx->total[0] += len;
+ if (ctx->total[0] < len)
+ ++ctx->total[1];
+
+ /* Process all bytes in the buffer with 64 bytes in each round of
+ the loop. */
+ while (words < endp)
+ {
+ md5_uint32 *cwp = correct_words;
+ md5_uint32 A_save = A;
+ md5_uint32 B_save = B;
+ md5_uint32 C_save = C;
+ md5_uint32 D_save = D;
+
+ /* First round: using the given function, the context and a constant
+ the next context is computed. Because the algorithms processing
+ unit is a 32-bit word and it is determined to work on words in
+ little endian byte order we perhaps have to change the byte order
+ before the computation. To reduce the work for the next steps
+ we store the swapped words in the array CORRECT_WORDS. */
+
+#define OP(a, b, c, d, s, T) \
+ do \
+ { \
+ a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \
+ ++words; \
+ CYCLIC (a, s); \
+ a += b; \
+ } \
+ while (0)
+
+ /* It is unfortunate that C does not provide an operator for
+ cyclic rotation. Hope the C compiler is smart enough. */
+#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
+
+ /* Before we start, one word to the strange constants.
+ They are defined in RFC 1321 as
+
+ T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
+ */
+
+ /* Round 1. */
+ OP (A, B, C, D, 7, 0xd76aa478);
+ OP (D, A, B, C, 12, 0xe8c7b756);
+ OP (C, D, A, B, 17, 0x242070db);
+ OP (B, C, D, A, 22, 0xc1bdceee);
+ OP (A, B, C, D, 7, 0xf57c0faf);
+ OP (D, A, B, C, 12, 0x4787c62a);
+ OP (C, D, A, B, 17, 0xa8304613);
+ OP (B, C, D, A, 22, 0xfd469501);
+ OP (A, B, C, D, 7, 0x698098d8);
+ OP (D, A, B, C, 12, 0x8b44f7af);
+ OP (C, D, A, B, 17, 0xffff5bb1);
+ OP (B, C, D, A, 22, 0x895cd7be);
+ OP (A, B, C, D, 7, 0x6b901122);
+ OP (D, A, B, C, 12, 0xfd987193);
+ OP (C, D, A, B, 17, 0xa679438e);
+ OP (B, C, D, A, 22, 0x49b40821);
+
+ /* For the second to fourth round we have the possibly swapped words
+ in CORRECT_WORDS. Redefine the macro to take an additional first
+ argument specifying the function to use. */
+#undef OP
+#define OP(f, a, b, c, d, k, s, T) \
+ do \
+ { \
+ a += f (b, c, d) + correct_words[k] + T; \
+ CYCLIC (a, s); \
+ a += b; \
+ } \
+ while (0)
+
+ /* Round 2. */
+ OP (FG, A, B, C, D, 1, 5, 0xf61e2562);
+ OP (FG, D, A, B, C, 6, 9, 0xc040b340);
+ OP (FG, C, D, A, B, 11, 14, 0x265e5a51);
+ OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
+ OP (FG, A, B, C, D, 5, 5, 0xd62f105d);
+ OP (FG, D, A, B, C, 10, 9, 0x02441453);
+ OP (FG, C, D, A, B, 15, 14, 0xd8a1e681);
+ OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
+ OP (FG, A, B, C, D, 9, 5, 0x21e1cde6);
+ OP (FG, D, A, B, C, 14, 9, 0xc33707d6);
+ OP (FG, C, D, A, B, 3, 14, 0xf4d50d87);
+ OP (FG, B, C, D, A, 8, 20, 0x455a14ed);
+ OP (FG, A, B, C, D, 13, 5, 0xa9e3e905);
+ OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8);
+ OP (FG, C, D, A, B, 7, 14, 0x676f02d9);
+ OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
+
+ /* Round 3. */
+ OP (FH, A, B, C, D, 5, 4, 0xfffa3942);
+ OP (FH, D, A, B, C, 8, 11, 0x8771f681);
+ OP (FH, C, D, A, B, 11, 16, 0x6d9d6122);
+ OP (FH, B, C, D, A, 14, 23, 0xfde5380c);
+ OP (FH, A, B, C, D, 1, 4, 0xa4beea44);
+ OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9);
+ OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60);
+ OP (FH, B, C, D, A, 10, 23, 0xbebfbc70);
+ OP (FH, A, B, C, D, 13, 4, 0x289b7ec6);
+ OP (FH, D, A, B, C, 0, 11, 0xeaa127fa);
+ OP (FH, C, D, A, B, 3, 16, 0xd4ef3085);
+ OP (FH, B, C, D, A, 6, 23, 0x04881d05);
+ OP (FH, A, B, C, D, 9, 4, 0xd9d4d039);
+ OP (FH, D, A, B, C, 12, 11, 0xe6db99e5);
+ OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8);
+ OP (FH, B, C, D, A, 2, 23, 0xc4ac5665);
+
+ /* Round 4. */
+ OP (FI, A, B, C, D, 0, 6, 0xf4292244);
+ OP (FI, D, A, B, C, 7, 10, 0x432aff97);
+ OP (FI, C, D, A, B, 14, 15, 0xab9423a7);
+ OP (FI, B, C, D, A, 5, 21, 0xfc93a039);
+ OP (FI, A, B, C, D, 12, 6, 0x655b59c3);
+ OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92);
+ OP (FI, C, D, A, B, 10, 15, 0xffeff47d);
+ OP (FI, B, C, D, A, 1, 21, 0x85845dd1);
+ OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f);
+ OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
+ OP (FI, C, D, A, B, 6, 15, 0xa3014314);
+ OP (FI, B, C, D, A, 13, 21, 0x4e0811a1);
+ OP (FI, A, B, C, D, 4, 6, 0xf7537e82);
+ OP (FI, D, A, B, C, 11, 10, 0xbd3af235);
+ OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
+ OP (FI, B, C, D, A, 9, 21, 0xeb86d391);
+
+ /* Add the starting values of the context. */
+ A += A_save;
+ B += B_save;
+ C += C_save;
+ D += D_save;
+ }
+
+ /* Put checksum in context given as argument. */
+ ctx->A = A;
+ ctx->B = B;
+ ctx->C = C;
+ ctx->D = D;
+}