We could reduce our peak memory consumption during partial symbol table construction by freeing stuff from this obstack more often --- say, after processing each compilation unit, or each die --- but it turns out that this saves almost nothing.  For an executable with 11Mb of Dwarf 2 data, I found about 64k allocated on dwarf2_tmp_obstack.  Some investigation showed:
create_all_comp_units (objfile);
dwarf2_build_psymtabs_hard (struct objfile *objfile, int mainline){  /* Instead of reading this into a big buffer, we should probably use     mmap()  on architectures that support it. (FIXME) */  bfd *abfd = objfile->obfd;  char *info_ptr, *abbrev_ptr;  char *beg_of_comp_unit;  struct partial_die_info comp_unit_die;  struct partial_symtab *pst;  struct cleanup *back_to;  CORE_ADDR lowpc, highpc, baseaddr;  info_ptr = dwarf_info_buffer;  abbrev_ptr = dwarf_abbrev_buffer;  /* We use dwarf2_tmp_obstack for objects that don't need to survive     the partial symbol scan, like attribute values.     We could reduce our peak memory consumption during partial symbol     table construction by freeing stuff from this obstack more often     --- say, after processing each compilation unit, or each die ---     but it turns out that this saves almost nothing.  For an     executable with 11Mb of Dwarf 2 data, I found about 64k allocated     on dwarf2_tmp_obstack.  Some investigation showed:     1) 69% of the attributes used forms DW_FORM_addr, DW_FORM_data*,        DW_FORM_flag, DW_FORM_[su]data, and DW_FORM_ref*.  These are        all fixed-length values not requiring dynamic allocation.     2) 30% of the attributes used the form DW_FORM_string.  For        DW_FORM_string, read_attribute simply hands back a pointer to        the null-terminated string in dwarf_info_buffer, so no dynamic        allocation is needed there either.     3) The remaining 1% of the attributes all used DW_FORM_block1.        75% of those were DW_AT_frame_base location lists for        functions; the rest were DW_AT_location attributes, probably        for the global variables.     Anyway, what this all means is that the memory the dwarf2     reader uses as temporary space reading partial symbols is about     0.5% as much as we use for dwarf_*_buffer.  That's noise.  */  obstack_init (&dwarf2_tmp_obstack);  back_to = make_cleanup (dwarf2_free_tmp_obstack, NULL);  /* Since the objects we're extracting from dwarf_info_buffer vary in     length, only the individual functions to extract them (like     read_comp_unit_head and read_partial_die) can really know whether     the buffer is large enough to hold another complete object.     At the moment, they don't actually check that.  If     dwarf_info_buffer holds just one extra byte after the last     compilation unit's dies, then read_comp_unit_head will happily     read off the end of the buffer.  read_partial_die is similarly     casual.  Those functions should be fixed.     For this loop condition, simply checking whether there's any data     left at all should be sufficient.  */  while (info_ptr < dwarf_info_buffer + dwarf_info_size)    {      struct dwarf2_cu cu;      beg_of_comp_unit = info_ptr;      cu.objfile = objfile;      info_ptr = read_comp_unit_head (&cu.header, info_ptr, abfd);      if (cu.header.version != 2)	{	  error ("Dwarf Error: wrong version in compilation unit header (is %d, should be %d) [in module %s]", cu.header.version, 2, bfd_get_filename (abfd));	  return;	}      if (cu.header.abbrev_offset >= dwarf_abbrev_size)	{	  error ("Dwarf Error: bad offset (0x%lx) in compilation unit header (offset 0x%lx + 6) [in module %s]",		 (long) cu.header.abbrev_offset,		 (long) (beg_of_comp_unit - dwarf_info_buffer),		 bfd_get_filename (abfd));	  return;	}      if (beg_of_comp_unit + cu.header.length + cu.header.initial_length_size	  > dwarf_info_buffer + dwarf_info_size)	{	  error ("Dwarf Error: bad length (0x%lx) in compilation unit header (offset 0x%lx + 0) [in module %s]",		 (long) cu.header.length,		 (long) (beg_of_comp_unit - dwarf_info_buffer),		 bfd_get_filename (abfd));	  return;	}      /* Complete the cu_header */      cu.header.offset = beg_of_comp_unit - dwarf_info_buffer;      cu.header.first_die_ptr = info_ptr;      cu.header.cu_head_ptr = beg_of_comp_unit;      cu.list_in_scope = &file_symbols;      /* Read the abbrevs for this compilation unit into a table */      dwarf2_read_abbrevs (abfd, &cu);      make_cleanup (dwarf2_empty_abbrev_table, cu.header.dwarf2_abbrevs);      /* Read the compilation unit die */      info_ptr = read_partial_die (&comp_unit_die, abfd, info_ptr,				   &cu);      /* Set the language we're debugging */      set_cu_language (comp_unit_die.language, &cu);      /* Allocate a new partial symbol table structure */      pst = start_psymtab_common (objfile, objfile->section_offsets,				  comp_unit_die.name ? comp_unit_die.name : "",				  comp_unit_die.lowpc,				  objfile->global_psymbols.next,				  objfile->static_psymbols.next);      pst->read_symtab_private = (char *)	obstack_alloc (&objfile->objfile_obstack, sizeof (struct dwarf2_pinfo));      DWARF_INFO_BUFFER (pst) = dwarf_info_buffer;      DWARF_INFO_OFFSET (pst) = beg_of_comp_unit - dwarf_info_buffer;      DWARF_ABBREV_BUFFER (pst) = dwarf_abbrev_buffer;      DWARF_ABBREV_SIZE (pst) = dwarf_abbrev_size;      DWARF_LINE_BUFFER (pst) = dwarf_line_buffer;      DWARF_LINE_SIZE (pst) = dwarf_line_size;      DWARF_STR_BUFFER (pst) = dwarf_str_buffer;      DWARF_STR_SIZE (pst) = dwarf_str_size;      DWARF_MACINFO_BUFFER (pst) = dwarf_macinfo_buffer;      DWARF_MACINFO_SIZE (pst) = dwarf_macinfo_size;      DWARF_RANGES_BUFFER (pst) = dwarf_ranges_buffer;      DWARF_RANGES_SIZE (pst) = dwarf_ranges_size;      DWARF_LOC_BUFFER (pst) = dwarf_loc_buffer;      DWARF_LOC_SIZE (pst) = dwarf_loc_size;      baseaddr = ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile));      /* Store the function that reads in the rest of the symbol table */      pst->read_symtab = dwarf2_psymtab_to_symtab;      /* Check if comp unit has_children.         If so, read the rest of the partial symbols from this comp unit.         If not, there's no more debug_info for this comp unit. */      if (comp_unit_die.has_children)	{	  lowpc = ((CORE_ADDR) -1);	  highpc = ((CORE_ADDR) 0);	  info_ptr = scan_partial_symbols (info_ptr, &lowpc, &highpc,					   &cu, NULL);	  /* If we didn't find a lowpc, set it to highpc to avoid	     complaints from `maint check'.  */	  if (lowpc == ((CORE_ADDR) -1))	    lowpc = highpc;	  	  /* If the compilation unit didn't have an explicit address range,	     then use the information extracted from its child dies.  */	  if (! comp_unit_die.has_pc_info)	    {	      comp_unit_die.lowpc = lowpc;	      comp_unit_die.highpc = highpc;	    }	}      pst->textlow = comp_unit_die.lowpc + baseaddr;      pst->texthigh = comp_unit_die.highpc + baseaddr;      pst->n_global_syms = objfile->global_psymbols.next -	(objfile->global_psymbols.list + pst->globals_offset);      pst->n_static_syms = objfile->static_psymbols.next -	(objfile->static_psymbols.list + pst->statics_offset);      sort_pst_symbols (pst);      /* If there is already a psymtab or symtab for a file of this         name, remove it. (If there is a symtab, more drastic things         also happen.) This happens in VxWorks.  */      free_named_symtabs (pst->filename);      info_ptr = beg_of_comp_unit + cu.header.length                                   + cu.header.initial_length_size;    }  do_cleanups (back_to);}