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 + + * 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 * 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_"; } @@ -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 -> "); 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,10 +4981,370 @@ 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 +reverse_all_dies (die) + register dw_die_ref die; +{ + register dw_die_ref c; + + reverse_die_lists (die); + + 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) @@ -4936,9 +5352,8 @@ add_sibling_attributes (die) { register dw_die_ref c; - reverse_die_lists (die); - - if (die != comp_unit_die && die->die_sib && die->die_child != NULL) + 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 @@ -2560,6 +2581,10 @@ rest_of_type_compilation (type, toplev) #ifdef SDB_DEBUGGING_INFO 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 + + * md5.h: New file. + 2000-08-24 Greg McGary * 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 + +#if defined HAVE_LIMITS_H || _LIBC +# include +#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 +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 ) 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 + + * Makefile.in (REQUIRED_OFILES): Add md5.o. + (CFILES): Add md5.c. + * md5.c: New file. + 2000-08-27 Alex Samuel * 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 , 1995. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#if STDC_HEADERS || defined _LIBC +# include +# include +#else +# ifndef HAVE_MEMCPY +# define memcpy(d, s, n) bcopy ((s), (d), (n)) +# endif +#endif + +#include "md5.h" + +#ifdef _LIBC +# include +# 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; +}