toolkit/crashreporter/breakpad-patches/03-unique-string.patch
author Bill McCloskey <wmccloskey@mozilla.com>
Sat, 13 Sep 2014 11:20:16 -0700
changeset 205207 161056025760f1b74506953da9c8df17b5c18676
parent 135489 24e9d0e5998d0a6b846473ec22e022b338857543
child 281887 211a4c710fb6af2cad10102c4cabc7cb525998b8
permissions -rw-r--r--
Bug 1047076 - Disable e10s on Nightly if Accessibility is enabled (r=felipe)

# HG changeset patch
# User Ted Mielczarek <ted.mielczarek@gmail.com>
# Date 1360255134 18000
#      Thu Feb 07 11:38:54 2013 -0500
# Node ID 79cecfef3c2a10d719fe1af9fae2e5257109b028
# Parent 08f184a7e6d6d15ecc20abd56bc4e36669c0c68a
Rework PostfixEvaluator to use a UniqueString type
Patch by Julian Seward <jseward@acm.org>, R=ted

diff --git a/Makefile.am b/Makefile.am
--- a/Makefile.am
+++ b/Makefile.am
@@ -134,16 +134,17 @@
 	src/google_breakpad/processor/source_line_resolver_interface.h \
 	src/google_breakpad/processor/stack_frame.h \
 	src/google_breakpad/processor/stack_frame_cpu.h \
 	src/google_breakpad/processor/stack_frame_symbolizer.h \
 	src/google_breakpad/processor/stackwalker.h \
 	src/google_breakpad/processor/symbol_supplier.h \
 	src/google_breakpad/processor/system_info.h \
 	src/common/module.cc \
+	src/common/unique_string.cc \
 	src/processor/address_map-inl.h \
 	src/processor/address_map.h \
 	src/processor/basic_code_module.h \
 	src/processor/basic_code_modules.cc \
 	src/processor/basic_code_modules.h \
 	src/processor/basic_source_line_resolver_types.h \
 	src/processor/basic_source_line_resolver.cc \
 	src/processor/binarystream.h \
@@ -430,16 +431,17 @@
 src_tools_linux_dump_syms_dump_syms_SOURCES = \
 	src/common/dwarf_cfi_to_module.cc \
 	src/common/dwarf_cu_to_module.cc \
 	src/common/dwarf_line_to_module.cc \
 	src/common/language.cc \
 	src/common/module.cc \
 	src/common/stabs_reader.cc \
 	src/common/stabs_to_module.cc \
+	src/common/unique_string.cc \
 	src/common/dwarf/bytereader.cc \
 	src/common/dwarf/dwarf2diehandler.cc \
 	src/common/dwarf/dwarf2reader.cc \
 	src/common/linux/dump_symbols.cc \
 	src/common/linux/elf_symbols_to_module.cc \
 	src/common/linux/elfutils.cc \
 	src/common/linux/file_id.cc \
 	src/common/linux/linux_libc_support.cc \
@@ -473,16 +475,17 @@
 	src/common/memory_range_unittest.cc \
 	src/common/module.cc \
 	src/common/module_unittest.cc \
 	src/common/stabs_reader.cc \
 	src/common/stabs_reader_unittest.cc \
 	src/common/stabs_to_module.cc \
 	src/common/stabs_to_module_unittest.cc \
 	src/common/test_assembler.cc \
+	src/common/unique_string.cc \
 	src/common/dwarf/bytereader.cc \
 	src/common/dwarf/bytereader_unittest.cc \
 	src/common/dwarf/cfi_assembler.cc \
 	src/common/dwarf/dwarf2diehandler.cc \
 	src/common/dwarf/dwarf2diehandler_unittest.cc \
 	src/common/dwarf/dwarf2reader.cc \
 	src/common/dwarf/dwarf2reader_cfi_unittest.cc \
 	src/common/dwarf/dwarf2reader_die_unittest.cc \
@@ -561,31 +564,33 @@
 src_processor_basic_source_line_resolver_unittest_CPPFLAGS = \
 	-I$(top_srcdir)/src \
 	-I$(top_srcdir)/src/testing/include \
 	-I$(top_srcdir)/src/testing/gtest/include \
 	-I$(top_srcdir)/src/testing/gtest \
 	-I$(top_srcdir)/src/testing
 src_processor_basic_source_line_resolver_unittest_LDADD = \
 	src/common/module.o \
+	src/common/unique_string.o \
 	src/processor/basic_source_line_resolver.o \
 	src/processor/cfi_frame_info.o \
 	src/processor/pathname_stripper.o \
 	src/processor/logging.o \
 	src/processor/source_line_resolver_base.o \
 	src/processor/tokenize.o \
 	$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
 
 src_processor_cfi_frame_info_unittest_SOURCES = \
 	src/processor/cfi_frame_info_unittest.cc \
 	src/testing/gtest/src/gtest-all.cc \
 	src/testing/gtest/src/gtest_main.cc \
 	src/testing/src/gmock-all.cc
 src_processor_cfi_frame_info_unittest_LDADD = \
 	src/common/module.o \
+	src/common/unique_string.o \
 	src/processor/cfi_frame_info.o \
 	src/processor/logging.o \
 	src/processor/pathname_stripper.o \
 	$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
 src_processor_cfi_frame_info_unittest_CPPFLAGS = \
 	-I$(top_srcdir)/src \
 	-I$(top_srcdir)/src/testing/include \
 	-I$(top_srcdir)/src/testing/gtest/include \
@@ -606,16 +611,17 @@
 src_processor_exploitability_unittest_CPPFLAGS = \
 	-I$(top_srcdir)/src \
 	-I$(top_srcdir)/src/testing/include \
 	-I$(top_srcdir)/src/testing/gtest/include \
 	-I$(top_srcdir)/src/testing/gtest \
 	-I$(top_srcdir)/src/testing
 src_processor_exploitability_unittest_LDADD = \
 	src/common/module.o \
+	src/common/unique_string.o \
 	src/processor/minidump_processor.o \
 	src/processor/process_state.o \
 	src/processor/disassembler_x86.o \
 	src/processor/exploitability.o \
 	src/processor/exploitability_win.o \
 	src/processor/basic_code_modules.o \
 	src/processor/basic_source_line_resolver.o \
 	src/processor/call_stack.o \
@@ -659,16 +665,17 @@
 src_processor_fast_source_line_resolver_unittest_CPPFLAGS = \
 	-I$(top_srcdir)/src \
 	-I$(top_srcdir)/src/testing/include \
 	-I$(top_srcdir)/src/testing/gtest/include \
 	-I$(top_srcdir)/src/testing/gtest \
 	-I$(top_srcdir)/src/testing
 src_processor_fast_source_line_resolver_unittest_LDADD = \
 	src/common/module.o \
+	src/common/unique_string.o \
 	src/processor/fast_source_line_resolver.o \
 	src/processor/basic_source_line_resolver.o \
 	src/processor/cfi_frame_info.o \
 	src/processor/module_comparer.o \
 	src/processor/module_serializer.o \
 	src/processor/pathname_stripper.o \
 	src/processor/logging.o \
 	src/processor/source_line_resolver_base.o \
@@ -697,16 +704,17 @@
 src_processor_minidump_processor_unittest_CPPFLAGS = \
 	-I$(top_srcdir)/src \
 	-I$(top_srcdir)/src/testing/include \
 	-I$(top_srcdir)/src/testing/gtest/include \
 	-I$(top_srcdir)/src/testing/gtest \
 	-I$(top_srcdir)/src/testing
 src_processor_minidump_processor_unittest_LDADD = \
 	src/common/module.o \
+	src/common/unique_string.o \
 	src/processor/basic_code_modules.o \
 	src/processor/basic_source_line_resolver.o \
 	src/processor/call_stack.o \
 	src/processor/cfi_frame_info.o \
 	src/processor/disassembler_x86.o \
 	src/processor/exploitability.o \
 	src/processor/exploitability_win.o \
 	src/processor/logging.o \
@@ -812,16 +820,17 @@
 src_processor_pathname_stripper_unittest_LDADD = \
 	src/processor/pathname_stripper.o \
 	$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
 
 src_processor_postfix_evaluator_unittest_SOURCES = \
 	src/processor/postfix_evaluator_unittest.cc
 src_processor_postfix_evaluator_unittest_LDADD = \
 	src/common/module.o \
+	src/common/unique_string.o \
 	src/processor/logging.o \
 	src/processor/pathname_stripper.o \
 	$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
 
 src_processor_range_map_unittest_SOURCES = \
 	src/processor/range_map_unittest.cc
 src_processor_range_map_unittest_LDADD = \
 	src/processor/logging.o \
@@ -943,16 +952,17 @@
 	src/processor/logging.o \
 	src/processor/minidump.o \
 	src/processor/pathname_stripper.o
 
 src_processor_minidump_stackwalk_SOURCES = \
 	src/processor/minidump_stackwalk.cc
 src_processor_minidump_stackwalk_LDADD = \
 	src/common/module.o \
+	src/common/unique_string.o \
 	src/processor/basic_code_modules.o \
 	src/processor/basic_source_line_resolver.o \
 	src/processor/binarystream.o \
 	src/processor/call_stack.o \
 	src/processor/cfi_frame_info.o \
 	src/processor/disassembler_x86.o \
 	src/processor/exploitability.o \
 	src/processor/exploitability_win.o \
diff --git a/Makefile.in b/Makefile.in
--- a/Makefile.in
+++ b/Makefile.in
@@ -267,18 +267,19 @@
 	src/google_breakpad/processor/source_line_resolver_base.h \
 	src/google_breakpad/processor/source_line_resolver_interface.h \
 	src/google_breakpad/processor/stack_frame.h \
 	src/google_breakpad/processor/stack_frame_cpu.h \
 	src/google_breakpad/processor/stack_frame_symbolizer.h \
 	src/google_breakpad/processor/stackwalker.h \
 	src/google_breakpad/processor/symbol_supplier.h \
 	src/google_breakpad/processor/system_info.h \
-	src/common/module.cc src/processor/address_map-inl.h \
-	src/processor/address_map.h src/processor/basic_code_module.h \
+	src/common/module.cc src/common/unique_string.cc \
+	src/processor/address_map-inl.h src/processor/address_map.h \
+	src/processor/basic_code_module.h \
 	src/processor/basic_code_modules.cc \
 	src/processor/basic_code_modules.h \
 	src/processor/basic_source_line_resolver_types.h \
 	src/processor/basic_source_line_resolver.cc \
 	src/processor/binarystream.h src/processor/binarystream.cc \
 	src/processor/call_stack.cc src/processor/cfi_frame_info.cc \
 	src/processor/cfi_frame_info.h \
 	src/processor/contained_range_map-inl.h \
@@ -332,16 +333,17 @@
 	src/processor/static_map_iterator-inl.h \
 	src/processor/static_map_iterator.h \
 	src/processor/static_map-inl.h src/processor/static_map.h \
 	src/processor/static_range_map-inl.h \
 	src/processor/static_range_map.h src/processor/tokenize.cc \
 	src/processor/tokenize.h
 @DISABLE_PROCESSOR_FALSE@am_src_libbreakpad_a_OBJECTS =  \
 @DISABLE_PROCESSOR_FALSE@	src/common/module.$(OBJEXT) \
+@DISABLE_PROCESSOR_FALSE@	src/common/unique_string.$(OBJEXT) \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_code_modules.$(OBJEXT) \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_source_line_resolver.$(OBJEXT) \
 @DISABLE_PROCESSOR_FALSE@	src/processor/binarystream.$(OBJEXT) \
 @DISABLE_PROCESSOR_FALSE@	src/processor/call_stack.$(OBJEXT) \
 @DISABLE_PROCESSOR_FALSE@	src/processor/cfi_frame_info.$(OBJEXT) \
 @DISABLE_PROCESSOR_FALSE@	src/processor/disassembler_x86.$(OBJEXT) \
 @DISABLE_PROCESSOR_FALSE@	src/processor/exploitability.$(OBJEXT) \
 @DISABLE_PROCESSOR_FALSE@	src/processor/exploitability_win.$(OBJEXT) \
@@ -525,17 +527,18 @@
 	src/common/dwarf_cu_to_module_unittest.cc \
 	src/common/dwarf_line_to_module.cc \
 	src/common/dwarf_line_to_module_unittest.cc \
 	src/common/language.cc src/common/memory_range_unittest.cc \
 	src/common/module.cc src/common/module_unittest.cc \
 	src/common/stabs_reader.cc src/common/stabs_reader_unittest.cc \
 	src/common/stabs_to_module.cc \
 	src/common/stabs_to_module_unittest.cc \
-	src/common/test_assembler.cc src/common/dwarf/bytereader.cc \
+	src/common/test_assembler.cc src/common/unique_string.cc \
+	src/common/dwarf/bytereader.cc \
 	src/common/dwarf/bytereader_unittest.cc \
 	src/common/dwarf/cfi_assembler.cc \
 	src/common/dwarf/dwarf2diehandler.cc \
 	src/common/dwarf/dwarf2diehandler_unittest.cc \
 	src/common/dwarf/dwarf2reader.cc \
 	src/common/dwarf/dwarf2reader_cfi_unittest.cc \
 	src/common/dwarf/dwarf2reader_die_unittest.cc \
 	src/common/linux/dump_symbols.cc \
@@ -569,16 +572,17 @@
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/src_common_dumper_unittest-memory_range_unittest.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/src_common_dumper_unittest-module.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/src_common_dumper_unittest-module_unittest.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/src_common_dumper_unittest-stabs_reader.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/src_common_dumper_unittest-stabs_reader_unittest.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/src_common_dumper_unittest-stabs_to_module.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/src_common_dumper_unittest-stabs_to_module_unittest.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/src_common_dumper_unittest-test_assembler.$(OBJEXT) \
+@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/src_common_dumper_unittest-unique_string.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/src_common_dumper_unittest-bytereader.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/src_common_dumper_unittest-bytereader_unittest.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/src_common_dumper_unittest-cfi_assembler.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/src_common_dumper_unittest-dwarf2diehandler.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/src_common_dumper_unittest-dwarf2diehandler_unittest.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/src_common_dumper_unittest-dwarf2reader.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/src_common_dumper_unittest-dwarf2reader_cfi_unittest.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/src_common_dumper_unittest-dwarf2reader_die_unittest.$(OBJEXT) \
@@ -637,16 +641,17 @@
 	src/testing/gtest/src/gtest-all.cc \
 	src/testing/src/gmock-all.cc
 @DISABLE_PROCESSOR_FALSE@am_src_processor_basic_source_line_resolver_unittest_OBJECTS = src/processor/src_processor_basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.$(OBJEXT) \
 @DISABLE_PROCESSOR_FALSE@	src/testing/gtest/src/src_processor_basic_source_line_resolver_unittest-gtest-all.$(OBJEXT) \
 @DISABLE_PROCESSOR_FALSE@	src/testing/src/src_processor_basic_source_line_resolver_unittest-gmock-all.$(OBJEXT)
 src_processor_basic_source_line_resolver_unittest_OBJECTS = $(am_src_processor_basic_source_line_resolver_unittest_OBJECTS)
 @DISABLE_PROCESSOR_FALSE@src_processor_basic_source_line_resolver_unittest_DEPENDENCIES =  \
 @DISABLE_PROCESSOR_FALSE@	src/common/module.o \
+@DISABLE_PROCESSOR_FALSE@	src/common/unique_string.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_source_line_resolver.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/cfi_frame_info.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/pathname_stripper.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/logging.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/source_line_resolver_base.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/tokenize.o \
 @DISABLE_PROCESSOR_FALSE@	$(am__DEPENDENCIES_1) \
 @DISABLE_PROCESSOR_FALSE@	$(am__DEPENDENCIES_1)
@@ -671,16 +676,17 @@
 @DISABLE_PROCESSOR_FALSE@am_src_processor_cfi_frame_info_unittest_OBJECTS = src/processor/src_processor_cfi_frame_info_unittest-cfi_frame_info_unittest.$(OBJEXT) \
 @DISABLE_PROCESSOR_FALSE@	src/testing/gtest/src/src_processor_cfi_frame_info_unittest-gtest-all.$(OBJEXT) \
 @DISABLE_PROCESSOR_FALSE@	src/testing/gtest/src/src_processor_cfi_frame_info_unittest-gtest_main.$(OBJEXT) \
 @DISABLE_PROCESSOR_FALSE@	src/testing/src/src_processor_cfi_frame_info_unittest-gmock-all.$(OBJEXT)
 src_processor_cfi_frame_info_unittest_OBJECTS =  \
 	$(am_src_processor_cfi_frame_info_unittest_OBJECTS)
 @DISABLE_PROCESSOR_FALSE@src_processor_cfi_frame_info_unittest_DEPENDENCIES =  \
 @DISABLE_PROCESSOR_FALSE@	src/common/module.o \
+@DISABLE_PROCESSOR_FALSE@	src/common/unique_string.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/cfi_frame_info.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/logging.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/pathname_stripper.o \
 @DISABLE_PROCESSOR_FALSE@	$(am__DEPENDENCIES_1) \
 @DISABLE_PROCESSOR_FALSE@	$(am__DEPENDENCIES_1)
 am__src_processor_contained_range_map_unittest_SOURCES_DIST =  \
 	src/processor/contained_range_map_unittest.cc
 @DISABLE_PROCESSOR_FALSE@am_src_processor_contained_range_map_unittest_OBJECTS = src/processor/contained_range_map_unittest.$(OBJEXT)
@@ -713,16 +719,17 @@
 @DISABLE_PROCESSOR_FALSE@am_src_processor_exploitability_unittest_OBJECTS = src/processor/src_processor_exploitability_unittest-exploitability_unittest.$(OBJEXT) \
 @DISABLE_PROCESSOR_FALSE@	src/testing/gtest/src/src_processor_exploitability_unittest-gtest-all.$(OBJEXT) \
 @DISABLE_PROCESSOR_FALSE@	src/testing/gtest/src/src_processor_exploitability_unittest-gtest_main.$(OBJEXT) \
 @DISABLE_PROCESSOR_FALSE@	src/testing/src/src_processor_exploitability_unittest-gmock-all.$(OBJEXT)
 src_processor_exploitability_unittest_OBJECTS =  \
 	$(am_src_processor_exploitability_unittest_OBJECTS)
 @DISABLE_PROCESSOR_FALSE@src_processor_exploitability_unittest_DEPENDENCIES =  \
 @DISABLE_PROCESSOR_FALSE@	src/common/module.o \
+@DISABLE_PROCESSOR_FALSE@	src/common/unique_string.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/minidump_processor.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/process_state.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/disassembler_x86.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/exploitability.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/exploitability_win.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_code_modules.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_source_line_resolver.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/call_stack.o \
@@ -748,16 +755,17 @@
 	src/testing/gtest/src/gtest-all.cc \
 	src/testing/src/gmock-all.cc
 @DISABLE_PROCESSOR_FALSE@am_src_processor_fast_source_line_resolver_unittest_OBJECTS = src/processor/src_processor_fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.$(OBJEXT) \
 @DISABLE_PROCESSOR_FALSE@	src/testing/gtest/src/src_processor_fast_source_line_resolver_unittest-gtest-all.$(OBJEXT) \
 @DISABLE_PROCESSOR_FALSE@	src/testing/src/src_processor_fast_source_line_resolver_unittest-gmock-all.$(OBJEXT)
 src_processor_fast_source_line_resolver_unittest_OBJECTS = $(am_src_processor_fast_source_line_resolver_unittest_OBJECTS)
 @DISABLE_PROCESSOR_FALSE@src_processor_fast_source_line_resolver_unittest_DEPENDENCIES =  \
 @DISABLE_PROCESSOR_FALSE@	src/common/module.o \
+@DISABLE_PROCESSOR_FALSE@	src/common/unique_string.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/fast_source_line_resolver.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_source_line_resolver.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/cfi_frame_info.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/module_comparer.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/module_serializer.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/pathname_stripper.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/logging.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/source_line_resolver_base.o \
@@ -794,16 +802,17 @@
 	src/testing/src/gmock-all.cc
 @DISABLE_PROCESSOR_FALSE@am_src_processor_minidump_processor_unittest_OBJECTS = src/processor/src_processor_minidump_processor_unittest-minidump_processor_unittest.$(OBJEXT) \
 @DISABLE_PROCESSOR_FALSE@	src/testing/gtest/src/src_processor_minidump_processor_unittest-gtest-all.$(OBJEXT) \
 @DISABLE_PROCESSOR_FALSE@	src/testing/src/src_processor_minidump_processor_unittest-gmock-all.$(OBJEXT)
 src_processor_minidump_processor_unittest_OBJECTS =  \
 	$(am_src_processor_minidump_processor_unittest_OBJECTS)
 @DISABLE_PROCESSOR_FALSE@src_processor_minidump_processor_unittest_DEPENDENCIES =  \
 @DISABLE_PROCESSOR_FALSE@	src/common/module.o \
+@DISABLE_PROCESSOR_FALSE@	src/common/unique_string.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_code_modules.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_source_line_resolver.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/call_stack.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/cfi_frame_info.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/disassembler_x86.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/exploitability.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/exploitability_win.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/logging.o \
@@ -826,16 +835,17 @@
 @DISABLE_PROCESSOR_FALSE@	$(am__DEPENDENCIES_1)
 am__src_processor_minidump_stackwalk_SOURCES_DIST =  \
 	src/processor/minidump_stackwalk.cc
 @DISABLE_PROCESSOR_FALSE@am_src_processor_minidump_stackwalk_OBJECTS = src/processor/minidump_stackwalk.$(OBJEXT)
 src_processor_minidump_stackwalk_OBJECTS =  \
 	$(am_src_processor_minidump_stackwalk_OBJECTS)
 @DISABLE_PROCESSOR_FALSE@src_processor_minidump_stackwalk_DEPENDENCIES =  \
 @DISABLE_PROCESSOR_FALSE@	src/common/module.o \
+@DISABLE_PROCESSOR_FALSE@	src/common/unique_string.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_code_modules.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_source_line_resolver.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/binarystream.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/call_stack.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/cfi_frame_info.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/disassembler_x86.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/exploitability.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/exploitability_win.o \
@@ -889,16 +899,17 @@
 @DISABLE_PROCESSOR_FALSE@	$(am__DEPENDENCIES_1)
 am__src_processor_postfix_evaluator_unittest_SOURCES_DIST =  \
 	src/processor/postfix_evaluator_unittest.cc
 @DISABLE_PROCESSOR_FALSE@am_src_processor_postfix_evaluator_unittest_OBJECTS = src/processor/postfix_evaluator_unittest.$(OBJEXT)
 src_processor_postfix_evaluator_unittest_OBJECTS =  \
 	$(am_src_processor_postfix_evaluator_unittest_OBJECTS)
 @DISABLE_PROCESSOR_FALSE@src_processor_postfix_evaluator_unittest_DEPENDENCIES =  \
 @DISABLE_PROCESSOR_FALSE@	src/common/module.o \
+@DISABLE_PROCESSOR_FALSE@	src/common/unique_string.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/logging.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/pathname_stripper.o \
 @DISABLE_PROCESSOR_FALSE@	$(am__DEPENDENCIES_1) \
 @DISABLE_PROCESSOR_FALSE@	$(am__DEPENDENCIES_1)
 am__src_processor_range_map_unittest_SOURCES_DIST =  \
 	src/processor/range_map_unittest.cc
 @DISABLE_PROCESSOR_FALSE@am_src_processor_range_map_unittest_OBJECTS = src/processor/range_map_unittest.$(OBJEXT)
 src_processor_range_map_unittest_OBJECTS =  \
@@ -1069,33 +1080,35 @@
 src_tools_linux_core2md_core2md_OBJECTS =  \
 	$(am_src_tools_linux_core2md_core2md_OBJECTS)
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_tools_linux_core2md_core2md_DEPENDENCIES = src/client/linux/libbreakpad_client.a
 am__src_tools_linux_dump_syms_dump_syms_SOURCES_DIST =  \
 	src/common/dwarf_cfi_to_module.cc \
 	src/common/dwarf_cu_to_module.cc \
 	src/common/dwarf_line_to_module.cc src/common/language.cc \
 	src/common/module.cc src/common/stabs_reader.cc \
-	src/common/stabs_to_module.cc src/common/dwarf/bytereader.cc \
+	src/common/stabs_to_module.cc src/common/unique_string.cc \
+	src/common/dwarf/bytereader.cc \
 	src/common/dwarf/dwarf2diehandler.cc \
 	src/common/dwarf/dwarf2reader.cc \
 	src/common/linux/dump_symbols.cc \
 	src/common/linux/elf_symbols_to_module.cc \
 	src/common/linux/elfutils.cc src/common/linux/file_id.cc \
 	src/common/linux/linux_libc_support.cc \
 	src/common/linux/memory_mapped_file.cc \
 	src/common/linux/safe_readlink.cc \
 	src/tools/linux/dump_syms/dump_syms.cc
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@am_src_tools_linux_dump_syms_dump_syms_OBJECTS = src/common/dwarf_cfi_to_module.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf_cu_to_module.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf_line_to_module.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/language.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/module.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/stabs_reader.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/stabs_to_module.$(OBJEXT) \
+@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/unique_string.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/bytereader.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/dwarf2diehandler.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/dwarf2reader.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/linux/dump_symbols.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/linux/elf_symbols_to_module.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/linux/elfutils.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/linux/file_id.$(OBJEXT) \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/linux/linux_libc_support.$(OBJEXT) \
@@ -1416,16 +1429,17 @@
 @DISABLE_PROCESSOR_FALSE@	src/google_breakpad/processor/source_line_resolver_interface.h \
 @DISABLE_PROCESSOR_FALSE@	src/google_breakpad/processor/stack_frame.h \
 @DISABLE_PROCESSOR_FALSE@	src/google_breakpad/processor/stack_frame_cpu.h \
 @DISABLE_PROCESSOR_FALSE@	src/google_breakpad/processor/stack_frame_symbolizer.h \
 @DISABLE_PROCESSOR_FALSE@	src/google_breakpad/processor/stackwalker.h \
 @DISABLE_PROCESSOR_FALSE@	src/google_breakpad/processor/symbol_supplier.h \
 @DISABLE_PROCESSOR_FALSE@	src/google_breakpad/processor/system_info.h \
 @DISABLE_PROCESSOR_FALSE@	src/common/module.cc \
+@DISABLE_PROCESSOR_FALSE@	src/common/unique_string.cc \
 @DISABLE_PROCESSOR_FALSE@	src/processor/address_map-inl.h \
 @DISABLE_PROCESSOR_FALSE@	src/processor/address_map.h \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_code_module.h \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_code_modules.cc \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_code_modules.h \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_source_line_resolver_types.h \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_source_line_resolver.cc \
 @DISABLE_PROCESSOR_FALSE@	src/processor/binarystream.h \
@@ -1618,16 +1632,17 @@
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_tools_linux_dump_syms_dump_syms_SOURCES = \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf_cfi_to_module.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf_cu_to_module.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf_line_to_module.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/language.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/module.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/stabs_reader.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/stabs_to_module.cc \
+@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/unique_string.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/bytereader.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/dwarf2diehandler.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/dwarf2reader.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/linux/dump_symbols.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/linux/elf_symbols_to_module.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/linux/elfutils.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/linux/file_id.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/linux/linux_libc_support.cc \
@@ -1661,16 +1676,17 @@
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/memory_range_unittest.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/module.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/module_unittest.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/stabs_reader.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/stabs_reader_unittest.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/stabs_to_module.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/stabs_to_module_unittest.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/test_assembler.cc \
+@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/unique_string.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/bytereader.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/bytereader_unittest.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/cfi_assembler.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/dwarf2diehandler.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/dwarf2diehandler_unittest.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/dwarf2reader.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/dwarf2reader_cfi_unittest.cc \
 @DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@	src/common/dwarf/dwarf2reader_die_unittest.cc \
@@ -1753,32 +1769,34 @@
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src \
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src/testing/include \
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src/testing/gtest/include \
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src/testing/gtest \
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src/testing
 
 @DISABLE_PROCESSOR_FALSE@src_processor_basic_source_line_resolver_unittest_LDADD = \
 @DISABLE_PROCESSOR_FALSE@	src/common/module.o \
+@DISABLE_PROCESSOR_FALSE@	src/common/unique_string.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_source_line_resolver.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/cfi_frame_info.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/pathname_stripper.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/logging.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/source_line_resolver_base.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/tokenize.o \
 @DISABLE_PROCESSOR_FALSE@	$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
 
 @DISABLE_PROCESSOR_FALSE@src_processor_cfi_frame_info_unittest_SOURCES = \
 @DISABLE_PROCESSOR_FALSE@	src/processor/cfi_frame_info_unittest.cc \
 @DISABLE_PROCESSOR_FALSE@	src/testing/gtest/src/gtest-all.cc \
 @DISABLE_PROCESSOR_FALSE@	src/testing/gtest/src/gtest_main.cc \
 @DISABLE_PROCESSOR_FALSE@	src/testing/src/gmock-all.cc
 
 @DISABLE_PROCESSOR_FALSE@src_processor_cfi_frame_info_unittest_LDADD = \
 @DISABLE_PROCESSOR_FALSE@	src/common/module.o \
+@DISABLE_PROCESSOR_FALSE@	src/common/unique_string.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/cfi_frame_info.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/logging.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/pathname_stripper.o \
 @DISABLE_PROCESSOR_FALSE@	$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
 
 @DISABLE_PROCESSOR_FALSE@src_processor_cfi_frame_info_unittest_CPPFLAGS = \
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src \
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src/testing/include \
@@ -1803,16 +1821,17 @@
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src \
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src/testing/include \
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src/testing/gtest/include \
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src/testing/gtest \
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src/testing
 
 @DISABLE_PROCESSOR_FALSE@src_processor_exploitability_unittest_LDADD = \
 @DISABLE_PROCESSOR_FALSE@	src/common/module.o \
+@DISABLE_PROCESSOR_FALSE@	src/common/unique_string.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/minidump_processor.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/process_state.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/disassembler_x86.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/exploitability.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/exploitability_win.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_code_modules.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_source_line_resolver.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/call_stack.o \
@@ -1860,16 +1879,17 @@
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src \
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src/testing/include \
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src/testing/gtest/include \
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src/testing/gtest \
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src/testing
 
 @DISABLE_PROCESSOR_FALSE@src_processor_fast_source_line_resolver_unittest_LDADD = \
 @DISABLE_PROCESSOR_FALSE@	src/common/module.o \
+@DISABLE_PROCESSOR_FALSE@	src/common/unique_string.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/fast_source_line_resolver.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_source_line_resolver.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/cfi_frame_info.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/module_comparer.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/module_serializer.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/pathname_stripper.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/logging.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/source_line_resolver_base.o \
@@ -1902,16 +1922,17 @@
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src \
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src/testing/include \
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src/testing/gtest/include \
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src/testing/gtest \
 @DISABLE_PROCESSOR_FALSE@	-I$(top_srcdir)/src/testing
 
 @DISABLE_PROCESSOR_FALSE@src_processor_minidump_processor_unittest_LDADD = \
 @DISABLE_PROCESSOR_FALSE@	src/common/module.o \
+@DISABLE_PROCESSOR_FALSE@	src/common/unique_string.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_code_modules.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_source_line_resolver.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/call_stack.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/cfi_frame_info.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/disassembler_x86.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/exploitability.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/exploitability_win.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/logging.o \
@@ -2029,16 +2050,17 @@
 @DISABLE_PROCESSOR_FALSE@	src/processor/pathname_stripper.o \
 @DISABLE_PROCESSOR_FALSE@	$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
 
 @DISABLE_PROCESSOR_FALSE@src_processor_postfix_evaluator_unittest_SOURCES = \
 @DISABLE_PROCESSOR_FALSE@	src/processor/postfix_evaluator_unittest.cc
 
 @DISABLE_PROCESSOR_FALSE@src_processor_postfix_evaluator_unittest_LDADD = \
 @DISABLE_PROCESSOR_FALSE@	src/common/module.o \
+@DISABLE_PROCESSOR_FALSE@	src/common/unique_string.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/logging.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/pathname_stripper.o \
 @DISABLE_PROCESSOR_FALSE@	$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
 
 @DISABLE_PROCESSOR_FALSE@src_processor_range_map_unittest_SOURCES = \
 @DISABLE_PROCESSOR_FALSE@	src/processor/range_map_unittest.cc
 
 @DISABLE_PROCESSOR_FALSE@src_processor_range_map_unittest_LDADD = \
@@ -2169,16 +2191,17 @@
 @DISABLE_PROCESSOR_FALSE@	src/processor/minidump.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/pathname_stripper.o
 
 @DISABLE_PROCESSOR_FALSE@src_processor_minidump_stackwalk_SOURCES = \
 @DISABLE_PROCESSOR_FALSE@	src/processor/minidump_stackwalk.cc
 
 @DISABLE_PROCESSOR_FALSE@src_processor_minidump_stackwalk_LDADD = \
 @DISABLE_PROCESSOR_FALSE@	src/common/module.o \
+@DISABLE_PROCESSOR_FALSE@	src/common/unique_string.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_code_modules.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/basic_source_line_resolver.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/binarystream.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/call_stack.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/cfi_frame_info.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/disassembler_x86.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/exploitability.o \
 @DISABLE_PROCESSOR_FALSE@	src/processor/exploitability_win.o \
@@ -2527,16 +2550,18 @@
 	@$(MKDIR_P) src/client/linux
 	@: > src/client/linux/$(am__dirstamp)
 src/client/linux/libbreakpad_client.a: $(src_client_linux_libbreakpad_client_a_OBJECTS) $(src_client_linux_libbreakpad_client_a_DEPENDENCIES) $(EXTRA_src_client_linux_libbreakpad_client_a_DEPENDENCIES) src/client/linux/$(am__dirstamp)
 	-rm -f src/client/linux/libbreakpad_client.a
 	$(src_client_linux_libbreakpad_client_a_AR) src/client/linux/libbreakpad_client.a $(src_client_linux_libbreakpad_client_a_OBJECTS) $(src_client_linux_libbreakpad_client_a_LIBADD)
 	$(RANLIB) src/client/linux/libbreakpad_client.a
 src/common/module.$(OBJEXT): src/common/$(am__dirstamp) \
 	src/common/$(DEPDIR)/$(am__dirstamp)
+src/common/unique_string.$(OBJEXT): src/common/$(am__dirstamp) \
+	src/common/$(DEPDIR)/$(am__dirstamp)
 src/processor/$(am__dirstamp):
 	@$(MKDIR_P) src/processor
 	@: > src/processor/$(am__dirstamp)
 src/processor/$(DEPDIR)/$(am__dirstamp):
 	@$(MKDIR_P) src/processor/$(DEPDIR)
 	@: > src/processor/$(DEPDIR)/$(am__dirstamp)
 src/processor/basic_code_modules.$(OBJEXT):  \
 	src/processor/$(am__dirstamp) \
@@ -2863,16 +2888,19 @@
 	src/common/$(am__dirstamp) \
 	src/common/$(DEPDIR)/$(am__dirstamp)
 src/common/src_common_dumper_unittest-stabs_to_module_unittest.$(OBJEXT):  \
 	src/common/$(am__dirstamp) \
 	src/common/$(DEPDIR)/$(am__dirstamp)
 src/common/src_common_dumper_unittest-test_assembler.$(OBJEXT):  \
 	src/common/$(am__dirstamp) \
 	src/common/$(DEPDIR)/$(am__dirstamp)
+src/common/src_common_dumper_unittest-unique_string.$(OBJEXT):  \
+	src/common/$(am__dirstamp) \
+	src/common/$(DEPDIR)/$(am__dirstamp)
 src/common/dwarf/$(am__dirstamp):
 	@$(MKDIR_P) src/common/dwarf
 	@: > src/common/dwarf/$(am__dirstamp)
 src/common/dwarf/$(DEPDIR)/$(am__dirstamp):
 	@$(MKDIR_P) src/common/dwarf/$(DEPDIR)
 	@: > src/common/dwarf/$(DEPDIR)/$(am__dirstamp)
 src/common/dwarf/src_common_dumper_unittest-bytereader.$(OBJEXT):  \
 	src/common/dwarf/$(am__dirstamp) \
@@ -3470,28 +3498,30 @@
 	-rm -f src/common/src_common_dumper_unittest-memory_range_unittest.$(OBJEXT)
 	-rm -f src/common/src_common_dumper_unittest-module.$(OBJEXT)
 	-rm -f src/common/src_common_dumper_unittest-module_unittest.$(OBJEXT)
 	-rm -f src/common/src_common_dumper_unittest-stabs_reader.$(OBJEXT)
 	-rm -f src/common/src_common_dumper_unittest-stabs_reader_unittest.$(OBJEXT)
 	-rm -f src/common/src_common_dumper_unittest-stabs_to_module.$(OBJEXT)
 	-rm -f src/common/src_common_dumper_unittest-stabs_to_module_unittest.$(OBJEXT)
 	-rm -f src/common/src_common_dumper_unittest-test_assembler.$(OBJEXT)
+	-rm -f src/common/src_common_dumper_unittest-unique_string.$(OBJEXT)
 	-rm -f src/common/src_common_test_assembler_unittest-test_assembler.$(OBJEXT)
 	-rm -f src/common/src_common_test_assembler_unittest-test_assembler_unittest.$(OBJEXT)
 	-rm -f src/common/src_processor_minidump_unittest-test_assembler.$(OBJEXT)
 	-rm -f src/common/src_processor_stackwalker_amd64_unittest-test_assembler.$(OBJEXT)
 	-rm -f src/common/src_processor_stackwalker_arm_unittest-test_assembler.$(OBJEXT)
 	-rm -f src/common/src_processor_stackwalker_x86_unittest-test_assembler.$(OBJEXT)
 	-rm -f src/common/src_processor_synth_minidump_unittest-test_assembler.$(OBJEXT)
 	-rm -f src/common/stabs_reader.$(OBJEXT)
 	-rm -f src/common/stabs_to_module.$(OBJEXT)
 	-rm -f src/common/string_conversion.$(OBJEXT)
 	-rm -f src/common/tests/src_client_linux_linux_client_unittest_shlib-file_utils.$(OBJEXT)
 	-rm -f src/common/tests/src_common_dumper_unittest-file_utils.$(OBJEXT)
+	-rm -f src/common/unique_string.$(OBJEXT)
 	-rm -f src/processor/address_map_unittest.$(OBJEXT)
 	-rm -f src/processor/basic_code_modules.$(OBJEXT)
 	-rm -f src/processor/basic_source_line_resolver.$(OBJEXT)
 	-rm -f src/processor/binarystream.$(OBJEXT)
 	-rm -f src/processor/call_stack.$(OBJEXT)
 	-rm -f src/processor/cfi_frame_info.$(OBJEXT)
 	-rm -f src/processor/contained_range_map_unittest.$(OBJEXT)
 	-rm -f src/processor/disassembler_x86.$(OBJEXT)
@@ -3663,26 +3693,28 @@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/src_common_dumper_unittest-memory_range_unittest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/src_common_dumper_unittest-module.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/src_common_dumper_unittest-module_unittest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/src_common_dumper_unittest-stabs_reader.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/src_common_dumper_unittest-stabs_reader_unittest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/src_common_dumper_unittest-stabs_to_module.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/src_common_dumper_unittest-stabs_to_module_unittest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/src_common_dumper_unittest-test_assembler.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/src_common_dumper_unittest-unique_string.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/src_common_test_assembler_unittest-test_assembler.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/src_common_test_assembler_unittest-test_assembler_unittest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/src_processor_minidump_unittest-test_assembler.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/src_processor_stackwalker_amd64_unittest-test_assembler.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/src_processor_stackwalker_arm_unittest-test_assembler.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/src_processor_stackwalker_x86_unittest-test_assembler.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/src_processor_synth_minidump_unittest-test_assembler.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/stabs_reader.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/stabs_to_module.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/string_conversion.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/unique_string.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/android/$(DEPDIR)/breakpad_getcontext.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/android/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-breakpad_getcontext.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/android/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/bytereader.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/dwarf2diehandler.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/dwarf2reader.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/src_common_dumper_unittest-bytereader.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/src_common_dumper_unittest-bytereader_unittest.Po@am__quote@
@@ -4474,16 +4506,30 @@
 
 src/common/src_common_dumper_unittest-test_assembler.obj: src/common/test_assembler.cc
 @am__fastdepCXX_TRUE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/src_common_dumper_unittest-test_assembler.obj -MD -MP -MF src/common/$(DEPDIR)/src_common_dumper_unittest-test_assembler.Tpo -c -o src/common/src_common_dumper_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi`
 @am__fastdepCXX_TRUE@	$(am__mv) src/common/$(DEPDIR)/src_common_dumper_unittest-test_assembler.Tpo src/common/$(DEPDIR)/src_common_dumper_unittest-test_assembler.Po
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='src/common/test_assembler.cc' object='src/common/src_common_dumper_unittest-test_assembler.obj' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/src_common_dumper_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi`
 
+src/common/src_common_dumper_unittest-unique_string.o: src/common/unique_string.cc
+@am__fastdepCXX_TRUE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/src_common_dumper_unittest-unique_string.o -MD -MP -MF src/common/$(DEPDIR)/src_common_dumper_unittest-unique_string.Tpo -c -o src/common/src_common_dumper_unittest-unique_string.o `test -f 'src/common/unique_string.cc' || echo '$(srcdir)/'`src/common/unique_string.cc
+@am__fastdepCXX_TRUE@	$(am__mv) src/common/$(DEPDIR)/src_common_dumper_unittest-unique_string.Tpo src/common/$(DEPDIR)/src_common_dumper_unittest-unique_string.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='src/common/unique_string.cc' object='src/common/src_common_dumper_unittest-unique_string.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/src_common_dumper_unittest-unique_string.o `test -f 'src/common/unique_string.cc' || echo '$(srcdir)/'`src/common/unique_string.cc
+
+src/common/src_common_dumper_unittest-unique_string.obj: src/common/unique_string.cc
+@am__fastdepCXX_TRUE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/src_common_dumper_unittest-unique_string.obj -MD -MP -MF src/common/$(DEPDIR)/src_common_dumper_unittest-unique_string.Tpo -c -o src/common/src_common_dumper_unittest-unique_string.obj `if test -f 'src/common/unique_string.cc'; then $(CYGPATH_W) 'src/common/unique_string.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/unique_string.cc'; fi`
+@am__fastdepCXX_TRUE@	$(am__mv) src/common/$(DEPDIR)/src_common_dumper_unittest-unique_string.Tpo src/common/$(DEPDIR)/src_common_dumper_unittest-unique_string.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='src/common/unique_string.cc' object='src/common/src_common_dumper_unittest-unique_string.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/src_common_dumper_unittest-unique_string.obj `if test -f 'src/common/unique_string.cc'; then $(CYGPATH_W) 'src/common/unique_string.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/unique_string.cc'; fi`
+
 src/common/dwarf/src_common_dumper_unittest-bytereader.o: src/common/dwarf/bytereader.cc
 @am__fastdepCXX_TRUE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/src_common_dumper_unittest-bytereader.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/src_common_dumper_unittest-bytereader.Tpo -c -o src/common/dwarf/src_common_dumper_unittest-bytereader.o `test -f 'src/common/dwarf/bytereader.cc' || echo '$(srcdir)/'`src/common/dwarf/bytereader.cc
 @am__fastdepCXX_TRUE@	$(am__mv) src/common/dwarf/$(DEPDIR)/src_common_dumper_unittest-bytereader.Tpo src/common/dwarf/$(DEPDIR)/src_common_dumper_unittest-bytereader.Po
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	source='src/common/dwarf/bytereader.cc' object='src/common/dwarf/src_common_dumper_unittest-bytereader.o' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@	DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@	$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/src_common_dumper_unittest-bytereader.o `test -f 'src/common/dwarf/bytereader.cc' || echo '$(srcdir)/'`src/common/dwarf/bytereader.cc
 
 src/common/dwarf/src_common_dumper_unittest-bytereader.obj: src/common/dwarf/bytereader.cc
diff --git a/src/common/dwarf_cfi_to_module.cc b/src/common/dwarf_cfi_to_module.cc
--- a/src/common/dwarf_cfi_to_module.cc
+++ b/src/common/dwarf_cfi_to_module.cc
@@ -37,40 +37,44 @@
 #include <sstream>
 
 #include "common/dwarf_cfi_to_module.h"
 
 namespace google_breakpad {
 
 using std::ostringstream;
 
-vector<string> DwarfCFIToModule::RegisterNames::MakeVector(
-    const char * const *strings,
+vector<const UniqueString*> DwarfCFIToModule::RegisterNames::MakeVector(
+    const char* const* strings,
     size_t size) {
-  vector<string> names(strings, strings + size);
+  vector<const UniqueString*> names(size, NULL);
+  for (size_t i = 0; i < size; ++i) {
+    names[i] = ToUniqueString(strings[i]);
+  }
+
   return names;
 }
 
-vector<string> DwarfCFIToModule::RegisterNames::I386() {
+vector<const UniqueString*> DwarfCFIToModule::RegisterNames::I386() {
   static const char *const names[] = {
     "$eax", "$ecx", "$edx", "$ebx", "$esp", "$ebp", "$esi", "$edi",
     "$eip", "$eflags", "$unused1",
     "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7",
     "$unused2", "$unused3",
     "$xmm0", "$xmm1", "$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7",
     "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7",
     "$fcw", "$fsw", "$mxcsr",
     "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused4", "$unused5",
     "$tr", "$ldtr"
   };
 
   return MakeVector(names, sizeof(names) / sizeof(names[0]));
 }
 
-vector<string> DwarfCFIToModule::RegisterNames::X86_64() {
+vector<const UniqueString*> DwarfCFIToModule::RegisterNames::X86_64() {
   static const char *const names[] = {
     "$rax", "$rdx", "$rcx", "$rbx", "$rsi", "$rdi", "$rbp", "$rsp",
     "$r8",  "$r9",  "$r10", "$r11", "$r12", "$r13", "$r14", "$r15",
     "$rip",
     "$xmm0","$xmm1","$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7",
     "$xmm8","$xmm9","$xmm10","$xmm11","$xmm12","$xmm13","$xmm14","$xmm15",
     "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7",
     "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7",
@@ -80,17 +84,17 @@
     "$tr", "$ldtr",
     "$mxcsr", "$fcw", "$fsw"
   };
 
   return MakeVector(names, sizeof(names) / sizeof(names[0]));
 }
 
 // Per ARM IHI 0040A, section 3.1
-vector<string> DwarfCFIToModule::RegisterNames::ARM() {
+vector<const UniqueString*> DwarfCFIToModule::RegisterNames::ARM() {
   static const char *const names[] = {
     "r0",  "r1",  "r2",  "r3",  "r4",  "r5",  "r6",  "r7",
     "r8",  "r9",  "r10", "r11", "r12", "sp",  "lr",  "pc",
     "f0",  "f1",  "f2",  "f3",  "f4",  "f5",  "f6",  "f7",
     "fps", "cpsr", "",   "",    "",    "",    "",    "",
     "",    "",    "",    "",    "",    "",    "",    "",
     "",    "",    "",    "",    "",    "",    "",    "",
     "",    "",    "",    "",    "",    "",    "",    "",
@@ -122,40 +126,40 @@
   return_address_ = return_address;
 
   // Breakpad STACK CFI records must provide a .ra rule, but DWARF CFI
   // may not establish any rule for .ra if the return address column
   // is an ordinary register, and that register holds the return
   // address on entry to the function. So establish an initial .ra
   // rule citing the return address register.
   if (return_address_ < register_names_.size())
-    entry_->initial_rules[ra_name_]
+    entry_->initial_rules[ustr__ZDra()]
       = Module::Expr(register_names_[return_address_], 0, false);
 
   return true;
 }
 
-string DwarfCFIToModule::RegisterName(int i) {
+const UniqueString* DwarfCFIToModule::RegisterName(int i) {
   assert(entry_);
   if (i < 0) {
     assert(i == kCFARegister);
-    return cfa_name_;
+    return ustr__ZDcfa();
   }
   unsigned reg = i;
   if (reg == return_address_)
-    return ra_name_;
+    return ustr__ZDra();
 
   // Ensure that a non-empty name exists for this register value.
-  if (reg < register_names_.size() && !register_names_[reg].empty())
+  if (reg < register_names_.size() && register_names_[reg] != ustr__empty())
     return register_names_[reg];
 
   reporter_->UnnamedRegister(entry_offset_, reg);
   char buf[30];
   sprintf(buf, "unnamed_register%u", reg);
-  return buf;
+  return ToUniqueString(buf);
 }
 
 void DwarfCFIToModule::Record(Module::Address address, int reg,
                               const Module::Expr &rule) {
   assert(entry_);
 
   // Is this one of this entry's initial rules?
   if (address == entry_->address)
@@ -228,28 +232,30 @@
 
 void DwarfCFIToModule::Reporter::UnnamedRegister(size_t offset, int reg) {
   fprintf(stderr, "%s, section '%s': "
           "the call frame entry at offset 0x%zx refers to register %d,"
           " whose name we don't know\n",
           file_.c_str(), section_.c_str(), offset, reg);
 }
 
-void DwarfCFIToModule::Reporter::UndefinedNotSupported(size_t offset,
-                                                       const string &reg) {
+void DwarfCFIToModule::Reporter::UndefinedNotSupported(
+    size_t offset,
+    const UniqueString* reg) {
   fprintf(stderr, "%s, section '%s': "
           "the call frame entry at offset 0x%zx sets the rule for "
           "register '%s' to 'undefined', but the Breakpad symbol file format"
           " cannot express this\n",
-          file_.c_str(), section_.c_str(), offset, reg.c_str());
+          file_.c_str(), section_.c_str(), offset, FromUniqueString(reg));
 }
 
-void DwarfCFIToModule::Reporter::ExpressionsNotSupported(size_t offset,
-                                                         const string &reg) {
+void DwarfCFIToModule::Reporter::ExpressionsNotSupported(
+    size_t offset,
+    const UniqueString* reg) {
   fprintf(stderr, "%s, section '%s': "
           "the call frame entry at offset 0x%zx uses a DWARF expression to"
           " describe how to recover register '%s', "
           " but this translator cannot yet translate DWARF expressions to"
           " Breakpad postfix expressions\n",
-          file_.c_str(), section_.c_str(), offset, reg.c_str());
+          file_.c_str(), section_.c_str(), offset, FromUniqueString(reg));
 }
 
 } // namespace google_breakpad
diff --git a/src/common/dwarf_cfi_to_module.h b/src/common/dwarf_cfi_to_module.h
--- a/src/common/dwarf_cfi_to_module.h
+++ b/src/common/dwarf_cfi_to_module.h
@@ -44,16 +44,17 @@
 
 #include <set>
 #include <string>
 #include <vector>
 
 #include "common/module.h"
 #include "common/dwarf/dwarf2reader.h"
 #include "common/using_std_string.h"
+#include "common/unique_string.h"
 
 namespace google_breakpad {
 
 using dwarf2reader::CallFrameInfo;
 using google_breakpad::Module;
 using std::set;
 using std::vector;
 
@@ -78,61 +79,65 @@
     // The DWARF CFI entry at OFFSET cites register REG, but REG is not
     // covered by the vector of register names passed to the
     // DwarfCFIToModule constructor, nor does it match the return
     // address column number for this entry.
     virtual void UnnamedRegister(size_t offset, int reg);
 
     // The DWARF CFI entry at OFFSET says that REG is undefined, but the
     // Breakpad symbol file format cannot express this.
-    virtual void UndefinedNotSupported(size_t offset, const string &reg);
+    virtual void UndefinedNotSupported(size_t offset,
+                                       const UniqueString* reg);
 
     // The DWARF CFI entry at OFFSET says that REG uses a DWARF
     // expression to find its value, but DwarfCFIToModule is not
     // capable of translating DWARF expressions to Breakpad postfix
     // expressions.
-    virtual void ExpressionsNotSupported(size_t offset, const string &reg);
+    virtual void ExpressionsNotSupported(size_t offset,
+                                         const UniqueString* reg);
 
   protected:
     string file_, section_;
   };
 
   // Register name tables. If TABLE is a vector returned by one of these
   // functions, then TABLE[R] is the name of the register numbered R in
   // DWARF call frame information.
   class RegisterNames {
    public:
     // Intel's "x86" or IA-32.
-    static vector<string> I386();
+    static vector<const UniqueString*> I386();
 
     // AMD x86_64, AMD64, Intel EM64T, or Intel 64
-    static vector<string> X86_64();
+    static vector<const UniqueString*> X86_64();
 
     // ARM.
-    static vector<string> ARM();
+    static vector<const UniqueString*> ARM();
 
    private:
     // Given STRINGS, an array of C strings with SIZE elements, return an
     // equivalent vector<string>.
-    static vector<string> MakeVector(const char * const *strings, size_t size);
+    static vector<const UniqueString*> MakeVector(const char * const *strings,
+                                                  size_t size);
   };
 
   // Create a handler for the dwarf2reader::CallFrameInfo parser that
   // records the stack unwinding information it receives in MODULE.
   //
   // Use REGISTER_NAMES[I] as the name of register number I; *this
   // keeps a reference to the vector, so the vector should remain
   // alive for as long as the DwarfCFIToModule does.
   //
   // Use REPORTER for reporting problems encountered in the conversion
   // process.
-  DwarfCFIToModule(Module *module, const vector<string> &register_names,
+  DwarfCFIToModule(Module *module,
+                   const vector<const UniqueString*> &register_names,
                    Reporter *reporter)
       : module_(module), register_names_(register_names), reporter_(reporter),
-        entry_(NULL), return_address_(-1), cfa_name_(".cfa"), ra_name_(".ra") {
+        entry_(NULL), return_address_(-1) {
   }
   virtual ~DwarfCFIToModule() { delete entry_; }
 
   virtual bool Entry(size_t offset, uint64 address, uint64 length,
                      uint8 version, const string &augmentation,
                      unsigned return_address);
   virtual bool UndefinedRule(uint64 address, int reg);
   virtual bool SameValueRule(uint64 address, int reg);
@@ -144,53 +149,36 @@
   virtual bool ExpressionRule(uint64 address, int reg,
                               const string &expression);
   virtual bool ValExpressionRule(uint64 address, int reg,
                                  const string &expression);
   virtual bool End();
 
  private:
   // Return the name to use for register REG.
-  string RegisterName(int i);
+  const UniqueString* RegisterName(int i);
 
   // Record RULE for register REG at ADDRESS.
   void Record(Module::Address address, int reg, const Module::Expr &rule);
 
   // The module to which we should add entries.
   Module *module_;
 
   // Map from register numbers to register names.
-  const vector<string> &register_names_;
+  const vector<const UniqueString*> &register_names_;
 
   // The reporter to use to report problems.
   Reporter *reporter_;
 
   // The current entry we're constructing.
   Module::StackFrameEntry *entry_;
 
   // The section offset of the current frame description entry, for
   // use in error messages.
   size_t entry_offset_;
 
   // The return address column for that entry.
   unsigned return_address_;
-
-  // The names of the return address and canonical frame address. Putting
-  // these here instead of using string literals allows us to share their
-  // texts in reference-counted std::string implementations (all the
-  // popular ones). Many, many rules cite these strings.
-  string cfa_name_, ra_name_;
-
-  // A set of strings used by this CFI. Before storing a string in one of
-  // our data structures, insert it into this set, and then use the string
-  // from the set.
-  // 
-  // Because std::string uses reference counting internally, simply using
-  // strings from this set, even if passed by value, assigned, or held
-  // directly in structures and containers (map<string, ...>, for example),
-  // causes those strings to share a single instance of each distinct piece
-  // of text.
-  set<string> common_strings_;
 };
 
 } // namespace google_breakpad
 
 #endif // COMMON_LINUX_DWARF_CFI_TO_MODULE_H
diff --git a/src/common/dwarf_cfi_to_module_unittest.cc b/src/common/dwarf_cfi_to_module_unittest.cc
--- a/src/common/dwarf_cfi_to_module_unittest.cc
+++ b/src/common/dwarf_cfi_to_module_unittest.cc
@@ -37,52 +37,59 @@
 #include "breakpad_googletest_includes.h"
 #include "common/dwarf_cfi_to_module.h"
 #include "common/using_std_string.h"
 
 using std::vector;
 
 using google_breakpad::Module;
 using google_breakpad::DwarfCFIToModule;
+using google_breakpad::ToUniqueString;
+using google_breakpad::UniqueString;
+using google_breakpad::ustr__ZDcfa;
+using google_breakpad::ustr__ZDra;
+using google_breakpad::ustr__empty;
 using testing::ContainerEq;
 using testing::Test;
 using testing::_;
 
 struct MockCFIReporter: public DwarfCFIToModule::Reporter {
   MockCFIReporter(const string &file, const string &section)
       : Reporter(file, section) { }
   MOCK_METHOD2(UnnamedRegister, void(size_t offset, int reg));
-  MOCK_METHOD2(UndefinedNotSupported, void(size_t offset, const string &reg));
-  MOCK_METHOD2(ExpressionsNotSupported, void(size_t offset, const string &reg));
+  MOCK_METHOD2(UndefinedNotSupported, void(size_t offset,
+                                           const UniqueString* reg));
+  MOCK_METHOD2(ExpressionsNotSupported, void(size_t offset,
+                                             const UniqueString* reg));
 };
 
 struct DwarfCFIToModuleFixture {
   DwarfCFIToModuleFixture()
       : module("module name", "module os", "module arch", "module id"),
         reporter("reporter file", "reporter section"),
         handler(&module, register_names, &reporter) {
-    register_names.push_back("reg0");
-    register_names.push_back("reg1");
-    register_names.push_back("reg2");
-    register_names.push_back("reg3");
-    register_names.push_back("reg4");
-    register_names.push_back("reg5");
-    register_names.push_back("reg6");
-    register_names.push_back("reg7");
-    register_names.push_back("sp");
-    register_names.push_back("pc");
-    register_names.push_back("");
+    register_names.push_back(ToUniqueString("reg0"));
+    register_names.push_back(ToUniqueString("reg1"));
+    register_names.push_back(ToUniqueString("reg2"));
+    register_names.push_back(ToUniqueString("reg3"));
+    register_names.push_back(ToUniqueString("reg4"));
+    register_names.push_back(ToUniqueString("reg5"));
+    register_names.push_back(ToUniqueString("reg6"));
+    register_names.push_back(ToUniqueString("reg7"));
+    register_names.push_back(ToUniqueString("sp"));
+    register_names.push_back(ToUniqueString("pc"));
+    register_names.push_back(ustr__empty());
 
     EXPECT_CALL(reporter, UnnamedRegister(_, _)).Times(0);
     EXPECT_CALL(reporter, UndefinedNotSupported(_, _)).Times(0);
     EXPECT_CALL(reporter, ExpressionsNotSupported(_, _)).Times(0);
   }
 
   Module module;
-  vector<string> register_names;
+  vector<const UniqueString*> register_names;
   MockCFIReporter reporter;
   DwarfCFIToModule handler;
   vector<Module::StackFrameEntry *> entries;
 };
 
 class Entry: public DwarfCFIToModuleFixture, public Test { };
 
 TEST_F(Entry, Accept) {
@@ -127,183 +134,190 @@
   }
   uint64 entry_address, entry_size;
   unsigned return_reg;
 };
 
 class Rule: public RuleFixture, public Test { };
 
 TEST_F(Rule, UndefinedRule) {
-  EXPECT_CALL(reporter, UndefinedNotSupported(_, "reg7"));
+  EXPECT_CALL(reporter, UndefinedNotSupported(_, ToUniqueString("reg7")));
   StartEntry();
   ASSERT_TRUE(handler.UndefinedRule(entry_address, 7));
   ASSERT_TRUE(handler.End());
   CheckEntry();
   EXPECT_EQ(0U, entries[0]->initial_rules.size());
   EXPECT_EQ(0U, entries[0]->rule_changes.size());
 }
 
 TEST_F(Rule, RegisterWithEmptyName) {
   EXPECT_CALL(reporter, UnnamedRegister(_, 10));
-  EXPECT_CALL(reporter, UndefinedNotSupported(_, "unnamed_register10"));
+  EXPECT_CALL(reporter,
+              UndefinedNotSupported(_, ToUniqueString("unnamed_register10")));
   StartEntry();
   ASSERT_TRUE(handler.UndefinedRule(entry_address, 10));
   ASSERT_TRUE(handler.End());
   CheckEntry();
   EXPECT_EQ(0U, entries[0]->initial_rules.size());
   EXPECT_EQ(0U, entries[0]->rule_changes.size());
 }
 
 TEST_F(Rule, SameValueRule) {
   StartEntry();
   ASSERT_TRUE(handler.SameValueRule(entry_address, 6));
   ASSERT_TRUE(handler.End());
   CheckEntry();
   Module::RuleMap expected_initial;
-  expected_initial["reg6"] = Module::Expr("reg6", 0, false);
+  const UniqueString* reg6 = ToUniqueString("reg6");
+  expected_initial[reg6] = Module::Expr(reg6, 0, false);
   EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
   EXPECT_EQ(0U, entries[0]->rule_changes.size());
 }
 
 TEST_F(Rule, OffsetRule) {
   StartEntry();
   ASSERT_TRUE(handler.OffsetRule(entry_address + 1, return_reg,
                                  DwarfCFIToModule::kCFARegister,
                                  16927065));
   ASSERT_TRUE(handler.End());
   CheckEntry();
   EXPECT_EQ(0U, entries[0]->initial_rules.size());
   Module::RuleChangeMap expected_changes;
-  expected_changes[entry_address + 1][".ra"] =
-      Module::Expr(".cfa", 16927065, true);
+  expected_changes[entry_address + 1][ustr__ZDra()] =
+      Module::Expr(ustr__ZDcfa(), 16927065, true);
   EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
 }
 
 TEST_F(Rule, OffsetRuleNegative) {
   StartEntry();
   ASSERT_TRUE(handler.OffsetRule(entry_address + 1,
                                  DwarfCFIToModule::kCFARegister, 4, -34530721));
   ASSERT_TRUE(handler.End());
   CheckEntry();
   EXPECT_EQ(0U, entries[0]->initial_rules.size());
   Module::RuleChangeMap expected_changes;
-  expected_changes[entry_address + 1][".cfa"] =
-      Module::Expr("reg4", -34530721, true);
+  expected_changes[entry_address + 1][ustr__ZDcfa()] =
+      Module::Expr(ToUniqueString("reg4"), -34530721, true);
   EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
 }
 
 TEST_F(Rule, ValOffsetRule) {
   // Use an unnamed register number, to exercise that branch of RegisterName.
   EXPECT_CALL(reporter, UnnamedRegister(_, 11));
   StartEntry();
   ASSERT_TRUE(handler.ValOffsetRule(entry_address + 0x5ab7,
                                     DwarfCFIToModule::kCFARegister,
                                     11, 61812979));
   ASSERT_TRUE(handler.End());
   CheckEntry();
   EXPECT_EQ(0U, entries[0]->initial_rules.size());
   Module::RuleChangeMap expected_changes;
-  expected_changes[entry_address + 0x5ab7][".cfa"] =
-      Module::Expr("unnamed_register11", 61812979, false);
+  expected_changes[entry_address + 0x5ab7][ustr__ZDcfa()] =
+      Module::Expr(ToUniqueString("unnamed_register11"), 61812979, false);
   EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
 }
 
 TEST_F(Rule, RegisterRule) {
   StartEntry();
   ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 3));
   ASSERT_TRUE(handler.End());
   CheckEntry();
   Module::RuleMap expected_initial;
-  expected_initial[".ra"] = Module::Expr("reg3", 0, false);
+  expected_initial[ustr__ZDra()] =
+      Module::Expr(ToUniqueString("reg3"), 0, false);
   EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
   EXPECT_EQ(0U, entries[0]->rule_changes.size());
 }
 
 TEST_F(Rule, ExpressionRule) {
-  EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg2"));
+  EXPECT_CALL(reporter, ExpressionsNotSupported(_, ToUniqueString("reg2")));
   StartEntry();
   ASSERT_TRUE(handler.ExpressionRule(entry_address + 0xf326, 2,
                                      "it takes two to tango"));
   ASSERT_TRUE(handler.End());
   CheckEntry();
   EXPECT_EQ(0U, entries[0]->initial_rules.size());
   EXPECT_EQ(0U, entries[0]->rule_changes.size());
 }
 
 TEST_F(Rule, ValExpressionRule) {
-  EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg0"));
+  EXPECT_CALL(reporter, ExpressionsNotSupported(_, ToUniqueString("reg0")));
   StartEntry();
   ASSERT_TRUE(handler.ValExpressionRule(entry_address + 0x6367, 0,
                                         "bit off more than he could chew"));
   ASSERT_TRUE(handler.End());
   CheckEntry();
   EXPECT_EQ(0U, entries[0]->initial_rules.size());
   EXPECT_EQ(0U, entries[0]->rule_changes.size());
 }
 
 TEST_F(Rule, DefaultReturnAddressRule) {
   return_reg = 2;
   StartEntry();
   ASSERT_TRUE(handler.RegisterRule(entry_address, 0, 1));
   ASSERT_TRUE(handler.End());
   CheckEntry();
   Module::RuleMap expected_initial;
-  expected_initial[".ra"] = Module::Expr("reg2", 0, false);
-  expected_initial["reg0"] = Module::Expr("reg1", 0, false);
+  expected_initial[ustr__ZDra()] =
+      Module::Expr(ToUniqueString("reg2"), 0, false);
+  expected_initial[ToUniqueString("reg0")] =
+      Module::Expr(ToUniqueString("reg1"), 0, false);
   EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
   EXPECT_EQ(0U, entries[0]->rule_changes.size());
 }
 
 TEST_F(Rule, DefaultReturnAddressRuleOverride) {
   return_reg = 2;
   StartEntry();
   ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 1));
   ASSERT_TRUE(handler.End());
   CheckEntry();
   Module::RuleMap expected_initial;
-  expected_initial[".ra"] = Module::Expr("reg1", 0, false);
+  expected_initial[ustr__ZDra()] =
+      Module::Expr(ToUniqueString("reg1"), 0, false);
   EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
   EXPECT_EQ(0U, entries[0]->rule_changes.size());
 }
 
 TEST_F(Rule, DefaultReturnAddressRuleLater) {
   return_reg = 2;
   StartEntry();
   ASSERT_TRUE(handler.RegisterRule(entry_address + 1, return_reg, 1));
   ASSERT_TRUE(handler.End());
   CheckEntry();
   Module::RuleMap expected_initial;
-  expected_initial[".ra"] = Module::Expr("reg2", 0, false);
+  expected_initial[ustr__ZDra()] =
+      Module::Expr(ToUniqueString("reg2"), 0, false);
   EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
   Module::RuleChangeMap expected_changes;
-  expected_changes[entry_address + 1][".ra"] =
-      Module::Expr("reg1", 0, false);
+  expected_changes[entry_address + 1][ustr__ZDra()] =
+      Module::Expr(ToUniqueString("reg1"), 0, false);
   EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
 }
 
 TEST(RegisterNames, I386) {
-  vector<string> names = DwarfCFIToModule::RegisterNames::I386();
+  vector<const UniqueString*> names = DwarfCFIToModule::RegisterNames::I386();
 
-  EXPECT_EQ("$eax", names[0]);
-  EXPECT_EQ("$ecx", names[1]);
-  EXPECT_EQ("$esp", names[4]);
-  EXPECT_EQ("$eip", names[8]);
+  EXPECT_EQ(ToUniqueString("$eax"), names[0]);
+  EXPECT_EQ(ToUniqueString("$ecx"), names[1]);
+  EXPECT_EQ(ToUniqueString("$esp"), names[4]);
+  EXPECT_EQ(ToUniqueString("$eip"), names[8]);
 }
 
 TEST(RegisterNames, ARM) {
-  vector<string> names = DwarfCFIToModule::RegisterNames::ARM();
+  vector<const UniqueString*> names = DwarfCFIToModule::RegisterNames::ARM();
 
-  EXPECT_EQ("r0", names[0]);
-  EXPECT_EQ("r10", names[10]);
-  EXPECT_EQ("sp", names[13]);
-  EXPECT_EQ("lr", names[14]);
-  EXPECT_EQ("pc", names[15]);
+  EXPECT_EQ(ToUniqueString("r0"), names[0]);
+  EXPECT_EQ(ToUniqueString("r10"), names[10]);
+  EXPECT_EQ(ToUniqueString("sp"), names[13]);
+  EXPECT_EQ(ToUniqueString("lr"), names[14]);
+  EXPECT_EQ(ToUniqueString("pc"), names[15]);
 }
 
 TEST(RegisterNames, X86_64) {
-  vector<string> names = DwarfCFIToModule::RegisterNames::X86_64();
+  vector<const UniqueString*> names = DwarfCFIToModule::RegisterNames::X86_64();
 
-  EXPECT_EQ("$rax", names[0]);
-  EXPECT_EQ("$rdx", names[1]);
-  EXPECT_EQ("$rbp", names[6]);
-  EXPECT_EQ("$rsp", names[7]);
-  EXPECT_EQ("$rip", names[16]);
+  EXPECT_EQ(ToUniqueString("$rax"), names[0]);
+  EXPECT_EQ(ToUniqueString("$rdx"), names[1]);
+  EXPECT_EQ(ToUniqueString("$rbp"), names[6]);
+  EXPECT_EQ(ToUniqueString("$rsp"), names[7]);
+  EXPECT_EQ(ToUniqueString("$rip"), names[16]);
 }
diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc
--- a/src/common/linux/dump_symbols.cc
+++ b/src/common/linux/dump_symbols.cc
@@ -81,16 +81,17 @@
 using google_breakpad::ElfClass64;
 using google_breakpad::FindElfSectionByName;
 using google_breakpad::GetOffset;
 using google_breakpad::IsValidElf;
 using google_breakpad::Module;
 #ifndef NO_STABS_SUPPORT
 using google_breakpad::StabsToModule;
 #endif
+using google_breakpad::UniqueString;
 using google_breakpad::scoped_ptr;
 
 //
 // FDWrapper
 //
 // Wrapper class to make sure opened file is closed.
 //
 class FDWrapper {
@@ -278,17 +279,17 @@
 
 // Fill REGISTER_NAMES with the register names appropriate to the
 // machine architecture given in HEADER, indexed by the register
 // numbers used in DWARF call frame information. Return true on
 // success, or false if HEADER's machine architecture is not
 // supported.
 template<typename ElfClass>
 bool DwarfCFIRegisterNames(const typename ElfClass::Ehdr* elf_header,
-                           std::vector<string>* register_names) {
+                           std::vector<const UniqueString*>* register_names) {
   switch (elf_header->e_machine) {
     case EM_386:
       *register_names = DwarfCFIToModule::RegisterNames::I386();
       return true;
     case EM_ARM:
       *register_names = DwarfCFIToModule::RegisterNames::ARM();
       return true;
     case EM_X86_64:
@@ -306,17 +307,17 @@
                   const typename ElfClass::Shdr* section,
                   const bool eh_frame,
                   const typename ElfClass::Shdr* got_section,
                   const typename ElfClass::Shdr* text_section,
                   const bool big_endian,
                   Module* module) {
   // Find the appropriate set of register names for this file's
   // architecture.
-  std::vector<string> register_names;
+  std::vector<const UniqueString*> register_names;
   if (!DwarfCFIRegisterNames<ElfClass>(elf_header, &register_names)) {
     fprintf(stderr, "%s: unrecognized ELF machine architecture '%d';"
             " cannot convert DWARF call frame information\n",
             dwarf_filename.c_str(), elf_header->e_machine);
     return false;
   }
 
   const dwarf2reader::Endianness endianness = big_endian ?
diff --git a/src/common/mac/dump_syms.mm b/src/common/mac/dump_syms.mm
--- a/src/common/mac/dump_syms.mm
+++ b/src/common/mac/dump_syms.mm
@@ -313,17 +313,17 @@
 }
 
 bool DumpSymbols::ReadCFI(google_breakpad::Module *module,
                           const mach_o::Reader &macho_reader,
                           const mach_o::Section &section,
                           bool eh_frame) const {
   // Find the appropriate set of register names for this file's
   // architecture.
-  vector<string> register_names;
+  vector<const UniqueString*> register_names;
   switch (macho_reader.cpu_type()) {
     case CPU_TYPE_X86:
       register_names = DwarfCFIToModule::RegisterNames::I386();
       break;
     case CPU_TYPE_X86_64:
       register_names = DwarfCFIToModule::RegisterNames::X86_64();
       break;
     case CPU_TYPE_ARM:
diff --git a/src/common/module.cc b/src/common/module.cc
--- a/src/common/module.cc
+++ b/src/common/module.cc
@@ -33,16 +33,17 @@
 
 #include "common/module.h"
 
 #include <assert.h>
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
 
+#include <algorithm>
 #include <iostream>
 #include <utility>
 
 namespace google_breakpad {
 
 using std::dec;
 using std::endl;
 using std::hex;
@@ -255,36 +256,50 @@
           strerror(errno));
   return false;
 }
 
 std::ostream& operator<<(std::ostream& stream, const Module::Expr& expr) {
   assert(!expr.invalid());
   switch (expr.how_) {
     case Module::kExprSimple:
-      stream << expr.ident_ << " " << expr.offset_ << " +";
+      stream << FromUniqueString(expr.ident_) << " " << expr.offset_ << " +";
       break;
     case Module::kExprSimpleMem:
-      stream << expr.ident_ << " " << expr.offset_ << " + ^";
+      stream << FromUniqueString(expr.ident_) << " " << expr.offset_ << " + ^";
       break;
     case Module::kExprPostfix:
       stream << expr.postfix_; break;
     case Module::kExprInvalid:
     default:
       break;
   }
   return stream;
 }
 
 bool Module::WriteRuleMap(const RuleMap &rule_map, std::ostream &stream) {
+  // Visit the register rules in alphabetical order.  Because
+  // rule_map has the elements in some arbitrary order,
+  // get the names out into a vector, sort them, and visit in
+  // sorted order.
+  std::vector<const UniqueString*> rr_names;
   for (RuleMap::const_iterator it = rule_map.begin();
        it != rule_map.end(); ++it) {
-    if (it != rule_map.begin())
-      stream << ' ';
-    stream << it->first << ": " << it->second;
+    rr_names.push_back(it->first);
+  }
+
+  std::sort(rr_names.begin(), rr_names.end(), LessThan_UniqueString);
+
+  // Now visit the register rules in alphabetical order.
+  for (std::vector<const UniqueString*>::const_iterator name = rr_names.begin();
+       name != rr_names.end();
+       ++name) {
+    if (name != rr_names.begin())
+      stream << " ";
+    stream << FromUniqueString(*name) << ": " << rule_map.find(*name)->second;
   }
   return stream.good();
 }
 
 bool Module::Write(std::ostream &stream, SymbolData symbol_data) {
   stream << "MODULE " << os_ << " " << architecture_ << " "
          << id_ << " " << name_ << endl;
   if (!stream.good())
diff --git a/src/common/module.h b/src/common/module.h
--- a/src/common/module.h
+++ b/src/common/module.h
@@ -41,16 +41,17 @@
 #include <iostream>
 #include <map>
 #include <set>
 #include <string>
 #include <vector>
 
 #include "common/symbol_data.h"
 #include "common/using_std_string.h"
+#include "common/unique_string.h"
 #include "google_breakpad/common/breakpad_types.h"
 
 namespace google_breakpad {
 
 using std::set;
 using std::vector;
 using std::map;
 
@@ -131,69 +132,69 @@
   enum ExprHow {
     kExprInvalid = 1,
     kExprPostfix,
     kExprSimple,
     kExprSimpleMem
   };
   struct Expr {
     // Construct a simple-form expression
-    Expr(string ident, long offset, bool deref) {
-      if (ident.empty()) {
+    Expr(const UniqueString* ident, long offset, bool deref) {
+      if (ident == ustr__empty()) {
         Expr();
       } else {
         postfix_ = "";
         ident_ = ident;
         offset_ = offset;
         how_ = deref ? kExprSimpleMem : kExprSimple;
       }
     }
     // Construct an expression from a postfix string
     Expr(string postfix) {
       if (postfix.empty()) {
         Expr();
       } else {
         postfix_ = postfix;
-        ident_ = "";
+        ident_ = NULL;
         offset_ = 0;
         how_ = kExprPostfix;
       }
     }
     // Construct an invalid expression
     Expr() {
       postfix_ = "";
-      ident_ = "";
+      ident_ = NULL;
       offset_ = 0;
       how_ = kExprInvalid;
     }
     bool invalid() const { return how_ == kExprInvalid; }
     bool operator==(const Expr& other) const {
       return how_ == other.how_ &&
           ident_ == other.ident_ &&
           offset_ == other.offset_ &&
           postfix_ == other.postfix_;
     }
 
     // The identifier that gives the starting value for simple expressions.
-    string  ident_;
+    const UniqueString* ident_;
     // The offset to add for simple expressions.
     long    offset_;
     // The Postfix expression string to evaluate for non-simple expressions.
     string  postfix_;
     // The operation expressed by this expression.
     ExprHow how_;
 
     friend std::ostream& operator<<(std::ostream& stream, const Expr& expr);
   };
 
   // A map from register names to expressions that recover
   // their values. This can represent a complete set of rules to
   // follow at some address, or a set of changes to be applied to an
   // extant set of rules.
-  typedef map<string, Expr> RuleMap;
+  typedef map<const UniqueString*, Expr> RuleMap;
 
   // A map from addresses to RuleMaps, representing changes that take
   // effect at given addresses.
   typedef map<Address, RuleMap> RuleChangeMap;
 
   // A range of 'STACK CFI' stack walking information. An instance of
   // this structure corresponds to a 'STACK CFI INIT' record and the
   // subsequent 'STACK CFI' records that fall within its range.
diff --git a/src/common/module_unittest.cc b/src/common/module_unittest.cc
--- a/src/common/module_unittest.cc
+++ b/src/common/module_unittest.cc
@@ -40,16 +40,18 @@
 #include <sstream>
 #include <string>
 
 #include "breakpad_googletest_includes.h"
 #include "common/module.h"
 #include "common/using_std_string.h"
 
 using google_breakpad::Module;
+using google_breakpad::ToUniqueString;
+using google_breakpad::ustr__ZDcfa;
 using std::stringstream;
 using std::vector;
 using testing::ContainerEq;
 
 static Module::Function *generate_duplicate_function(const string &name) {
   const Module::Address DUP_ADDRESS = 0xd35402aac7a7ad5cLL;
   const Module::Address DUP_SIZE = 0x200b26e605f99071LL;
   const Module::Address DUP_PARAMETER_SIZE = 0xf14ac4fed48c4a99LL;
@@ -125,21 +127,27 @@
   function->lines.push_back(line1);
 
   m.AddFunction(function);
 
   // Some stack information.
   Module::StackFrameEntry *entry = new Module::StackFrameEntry();
   entry->address = 0x30f9e5c83323973dULL;
   entry->size = 0x49fc9ca7c7c13dc2ULL;
-  entry->initial_rules[".cfa"] = Module::Expr("he was a handsome man");
-  entry->initial_rules["and"] = Module::Expr("what i want to know is");
-  entry->rule_changes[0x30f9e5c83323973eULL]["how"] =
-    Module::Expr("do you like your blueeyed boy");
-  entry->rule_changes[0x30f9e5c83323973eULL]["Mister"] = Module::Expr("Death");
+  entry->initial_rules[ustr__ZDcfa()] = Module::Expr("he was a handsome man");
+  entry->initial_rules[ToUniqueString("and")] =
+      Module::Expr("what i want to know is");
+  entry->initial_rules[ToUniqueString("stallion")] =
+      Module::Expr(ToUniqueString("and break"), 8, false);
+  entry->initial_rules[ToUniqueString("onetwothreefourfive")] =
+      Module::Expr(ToUniqueString("pigeonsjustlikethat"), 42, true);
+  entry->rule_changes[0x30f9e5c83323973eULL][ToUniqueString("how")] =
+      Module::Expr("do you like your blueeyed boy");
+  entry->rule_changes[0x30f9e5c83323973eULL][ToUniqueString("Mister")] =
+      Module::Expr("Death");
   m.AddStackFrameEntry(entry);
 
   // Set the load address.  Doing this after adding all the data to
   // the module must work fine.
   m.SetLoadAddress(0x2ab698b0b6407073LL);
 
   m.Write(s, ALL_SYMBOL_DATA);
   string contents = s.str();
@@ -147,17 +155,19 @@
                "FILE 0 filename-a.cc\n"
                "FILE 1 filename-b.cc\n"
                "FUNC 9410dc39a798c580 2922088f98d3f6fc e5e9aa008bd5f0d0"
                " A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)\n"
                "b03cc3106d47eb91 cf621b8d324d0eb 67519080 0\n"
                "9410dc39a798c580 1c2be6d6c5af2611 41676901 1\n"
                "STACK CFI INIT 6434d177ce326ca 49fc9ca7c7c13dc2"
                " .cfa: he was a handsome man"
-               " and: what i want to know is\n"
+               " and: what i want to know is"
+               " onetwothreefourfive: pigeonsjustlikethat 42 + ^"
+               " stallion: and break 8 +\n"
                "STACK CFI 6434d177ce326cb"
                " Mister: Death"
                " how: do you like your blueeyed boy\n",
                contents.c_str());
 }
 
 TEST(Write, OmitUnusedFiles) {
   Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
@@ -229,21 +239,23 @@
   function->lines.push_back(line1);
 
   m.AddFunction(function);
 
   // Some stack information.
   Module::StackFrameEntry *entry = new Module::StackFrameEntry();
   entry->address = 0x30f9e5c83323973dULL;
   entry->size = 0x49fc9ca7c7c13dc2ULL;
-  entry->initial_rules[".cfa"] = Module::Expr("he was a handsome man");
-  entry->initial_rules["and"] = Module::Expr("what i want to know is");
-  entry->rule_changes[0x30f9e5c83323973eULL]["how"] =
-    Module::Expr("do you like your blueeyed boy");
-  entry->rule_changes[0x30f9e5c83323973eULL]["Mister"] = Module::Expr("Death");
+  entry->initial_rules[ustr__ZDcfa()] = Module::Expr("he was a handsome man");
+  entry->initial_rules[ToUniqueString("and")] =
+      Module::Expr("what i want to know is");
+  entry->rule_changes[0x30f9e5c83323973eULL][ToUniqueString("how")] =
+      Module::Expr("do you like your blueeyed boy");
+  entry->rule_changes[0x30f9e5c83323973eULL][ToUniqueString("Mister")] =
+      Module::Expr("Death");
   m.AddStackFrameEntry(entry);
 
   // Set the load address.  Doing this after adding all the data to
   // the module must work fine.
   m.SetLoadAddress(0x2ab698b0b6407073LL);
 
   m.Write(s, NO_CFI);
   string contents = s.str();
@@ -305,33 +317,36 @@
   entry1->address = 0xddb5f41285aa7757ULL;
   entry1->size = 0x1486493370dc5073ULL;
   m.AddStackFrameEntry(entry1);
 
   // Second STACK CFI entry, with initial rules but no deltas.
   Module::StackFrameEntry *entry2 = new Module::StackFrameEntry();
   entry2->address = 0x8064f3af5e067e38ULL;
   entry2->size = 0x0de2a5ee55509407ULL;
-  entry2->initial_rules[".cfa"] = Module::Expr("I think that I shall never see");
-  entry2->initial_rules["stromboli"] = Module::Expr("a poem lovely as a tree");
-  entry2->initial_rules["cannoli"] = Module::Expr("a tree whose hungry mouth is prest");
+  entry2->initial_rules[ustr__ZDcfa()] =
+      Module::Expr("I think that I shall never see");
+  entry2->initial_rules[ToUniqueString("stromboli")] =
+      Module::Expr("a poem lovely as a tree");
+  entry2->initial_rules[ToUniqueString("cannoli")] =
+      Module::Expr("a tree whose hungry mouth is prest");
   m.AddStackFrameEntry(entry2);
 
   // Third STACK CFI entry, with initial rules and deltas.
   Module::StackFrameEntry *entry3 = new Module::StackFrameEntry();
   entry3->address = 0x5e8d0db0a7075c6cULL;
   entry3->size = 0x1c7edb12a7aea229ULL;
-  entry3->initial_rules[".cfa"] = Module::Expr("Whose woods are these");
-  entry3->rule_changes[0x47ceb0f63c269d7fULL]["calzone"] =
+  entry3->initial_rules[ustr__ZDcfa()] = Module::Expr("Whose woods are these");
+  entry3->rule_changes[0x47ceb0f63c269d7fULL][ToUniqueString("calzone")] =
     Module::Expr("the village though");
-  entry3->rule_changes[0x47ceb0f63c269d7fULL]["cannoli"] =
+  entry3->rule_changes[0x47ceb0f63c269d7fULL][ToUniqueString("cannoli")] =
     Module::Expr("he will not see me stopping here");
-  entry3->rule_changes[0x36682fad3763ffffULL]["stromboli"] =
+  entry3->rule_changes[0x36682fad3763ffffULL][ToUniqueString("stromboli")] =
     Module::Expr("his house is in");
-  entry3->rule_changes[0x36682fad3763ffffULL][".cfa"] =
+  entry3->rule_changes[0x36682fad3763ffffULL][ustr__ZDcfa()] =
     Module::Expr("I think I know");
   m.AddStackFrameEntry(entry3);
 
   // Check that Write writes STACK CFI records properly.
   m.Write(s, ALL_SYMBOL_DATA);
   string contents = s.str();
   EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
                "STACK CFI INIT 5e8d0db0a7075c6c 1c7edb12a7aea229"
@@ -352,33 +367,39 @@
   // Check that GetStackFrameEntries works.
   vector<Module::StackFrameEntry *> entries;
   m.GetStackFrameEntries(&entries);
   ASSERT_EQ(3U, entries.size());
   // Check first entry.
   EXPECT_EQ(0x5e8d0db0a7075c6cULL, entries[0]->address);
   EXPECT_EQ(0x1c7edb12a7aea229ULL, entries[0]->size);
   Module::RuleMap entry1_initial;
-  entry1_initial[".cfa"] = Module::Expr("Whose woods are these");
+  entry1_initial[ustr__ZDcfa()] = Module::Expr("Whose woods are these");
   EXPECT_THAT(entries[0]->initial_rules, ContainerEq(entry1_initial));
   Module::RuleChangeMap entry1_changes;
-  entry1_changes[0x36682fad3763ffffULL][".cfa"] = Module::Expr("I think I know");
-  entry1_changes[0x36682fad3763ffffULL]["stromboli"] = Module::Expr("his house is in");
-  entry1_changes[0x47ceb0f63c269d7fULL]["calzone"] = Module::Expr("the village though");
-  entry1_changes[0x47ceb0f63c269d7fULL]["cannoli"] =
-    Module::Expr("he will not see me stopping here");
+  entry1_changes[0x36682fad3763ffffULL][ustr__ZDcfa()] =
+      Module::Expr("I think I know");
+  entry1_changes[0x36682fad3763ffffULL][ToUniqueString("stromboli")] =
+      Module::Expr("his house is in");
+  entry1_changes[0x47ceb0f63c269d7fULL][ToUniqueString("calzone")] =
+      Module::Expr("the village though");
+  entry1_changes[0x47ceb0f63c269d7fULL][ToUniqueString("cannoli")] =
+      Module::Expr("he will not see me stopping here");
   EXPECT_THAT(entries[0]->rule_changes, ContainerEq(entry1_changes));
   // Check second entry.
   EXPECT_EQ(0x8064f3af5e067e38ULL, entries[1]->address);
   EXPECT_EQ(0x0de2a5ee55509407ULL, entries[1]->size);
   ASSERT_EQ(3U, entries[1]->initial_rules.size());
   Module::RuleMap entry2_initial;
-  entry2_initial[".cfa"] = Module::Expr("I think that I shall never see");
-  entry2_initial["stromboli"] = Module::Expr("a poem lovely as a tree");
-  entry2_initial["cannoli"] = Module::Expr("a tree whose hungry mouth is prest");
+  entry2_initial[ustr__ZDcfa()] =
+      Module::Expr("I think that I shall never see");
+  entry2_initial[ToUniqueString("stromboli")] =
+      Module::Expr("a poem lovely as a tree");
+  entry2_initial[ToUniqueString("cannoli")] =
+      Module::Expr("a tree whose hungry mouth is prest");
   EXPECT_THAT(entries[1]->initial_rules, ContainerEq(entry2_initial));
   ASSERT_EQ(0U, entries[1]->rule_changes.size());
   // Check third entry.
   EXPECT_EQ(0xddb5f41285aa7757ULL, entries[2]->address);
   EXPECT_EQ(0x1486493370dc5073ULL, entries[2]->size);
   ASSERT_EQ(0U, entries[2]->initial_rules.size());
   ASSERT_EQ(0U, entries[2]->rule_changes.size());
 }
@@ -585,33 +606,36 @@
   entry1->address = 0x2000;
   entry1->size = 0x900;
   m.AddStackFrameEntry(entry1);
 
   // Second STACK CFI entry, with initial rules but no deltas.
   Module::StackFrameEntry *entry2 = new Module::StackFrameEntry();
   entry2->address = 0x3000;
   entry2->size = 0x900;
-  entry2->initial_rules[".cfa"] = Module::Expr("I think that I shall never see");
-  entry2->initial_rules["stromboli"] = Module::Expr("a poem lovely as a tree");
-  entry2->initial_rules["cannoli"] = Module::Expr("a tree whose hungry mouth is prest");
+  entry2->initial_rules[ustr__ZDcfa()] =
+      Module::Expr("I think that I shall never see");
+  entry2->initial_rules[ToUniqueString("stromboli")] =
+      Module::Expr("a poem lovely as a tree");
+  entry2->initial_rules[ToUniqueString("cannoli")] =
+      Module::Expr("a tree whose hungry mouth is prest");
   m.AddStackFrameEntry(entry2);
 
   // Third STACK CFI entry, with initial rules and deltas.
   Module::StackFrameEntry *entry3 = new Module::StackFrameEntry();
   entry3->address = 0x1000;
   entry3->size = 0x900;
-  entry3->initial_rules[".cfa"] = Module::Expr("Whose woods are these");
-  entry3->rule_changes[0x47ceb0f63c269d7fULL]["calzone"] =
+  entry3->initial_rules[ustr__ZDcfa()] = Module::Expr("Whose woods are these");
+  entry3->rule_changes[0x47ceb0f63c269d7fULL][ToUniqueString("calzone")] =
     Module::Expr("the village though");
-  entry3->rule_changes[0x47ceb0f63c269d7fULL]["cannoli"] =
+  entry3->rule_changes[0x47ceb0f63c269d7fULL][ToUniqueString("cannoli")] =
     Module::Expr("he will not see me stopping here");
-  entry3->rule_changes[0x36682fad3763ffffULL]["stromboli"] =
+  entry3->rule_changes[0x36682fad3763ffffULL][ToUniqueString("stromboli")] =
     Module::Expr("his house is in");
-  entry3->rule_changes[0x36682fad3763ffffULL][".cfa"] =
+  entry3->rule_changes[0x36682fad3763ffffULL][ustr__ZDcfa()] =
     Module::Expr("I think I know");
   m.AddStackFrameEntry(entry3);
 
   Module::StackFrameEntry* s = m.FindStackFrameEntryByAddress(0x1000);
   EXPECT_EQ(entry3, s);
   s = m.FindStackFrameEntryByAddress(0x18FF);
   EXPECT_EQ(entry3, s);
 
diff --git a/src/common/unique_string.cc b/src/common/unique_string.cc
new file mode 100644
--- /dev/null
+++ b/src/common/unique_string.cc
@@ -0,0 +1,110 @@
+// Copyright (c) 2013 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <string>
+#include <map>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "common/unique_string.h"
+
+namespace google_breakpad {
+
+///////////////////////////////////////////////////////////////////
+// UniqueString
+//
+class UniqueString {
+ public:
+  UniqueString(string str) { str_ = strdup(str.c_str()); }
+  ~UniqueString() { free(reinterpret_cast<void*>(const_cast<char*>(str_))); }
+  const char* str_;
+};
+
+class UniqueStringUniverse {
+ public:
+  UniqueStringUniverse() {};
+  const UniqueString* FindOrCopy(string str) {
+    std::map<string, UniqueString*>::iterator it = map_.find(str);
+    if (it == map_.end()) {
+      UniqueString* ustr = new UniqueString(str);
+      map_[str] = ustr;
+      return ustr;
+    } else {
+      return it->second;
+    }
+  }
+ private:
+  std::map<string, UniqueString*> map_;
+};
+
+//
+///////////////////////////////////////////////////////////////////
+
+
+static UniqueStringUniverse* sUSU = NULL;
+
+
+// This isn't threadsafe.
+const UniqueString* ToUniqueString(string str) {
+  if (!sUSU) {
+    sUSU = new UniqueStringUniverse();
+  }
+  return sUSU->FindOrCopy(str);
+}
+
+// This isn't threadsafe.
+const UniqueString* ToUniqueString_n(const char* str, size_t n) {
+  if (!sUSU) {
+    sUSU = new UniqueStringUniverse();
+  }
+  string key(str, n);
+  return sUSU->FindOrCopy(key);
+}
+
+const char Index(const UniqueString* us, int ix)
+{
+  return us->str_[ix];
+}
+
+const char* const FromUniqueString(const UniqueString* ustr)
+{
+  return ustr->str_;
+}
+
+int StrcmpUniqueString(const UniqueString* us1, const UniqueString* us2) {
+  return strcmp(us1->str_, us2->str_);
+}
+
+bool LessThan_UniqueString(const UniqueString* us1, const UniqueString* us2) {
+  int r = StrcmpUniqueString(us1, us2);
+  return r < 0;
+}
+
+}  // namespace google_breakpad
diff --git a/src/common/unique_string.h b/src/common/unique_string.h
new file mode 100644
--- /dev/null
+++ b/src/common/unique_string.h
@@ -0,0 +1,239 @@
+// Copyright (c) 2013 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef COMMON_UNIQUE_STRING_H_
+#define COMMON_UNIQUE_STRING_H_
+
+#include <string>
+#include "common/using_std_string.h"
+
+namespace google_breakpad {
+
+// Abstract type
+class UniqueString;
+
+// Unique-ify a string.  |ToUniqueString| can never return NULL.
+const UniqueString* ToUniqueString(string);
+
+// ditto, starting instead from the first n characters of a C string
+const UniqueString* ToUniqueString_n(const char* str, size_t n);
+
+// Pull chars out of the string.  No range checking.
+const char Index(const UniqueString*, int);
+
+// Get the contained C string (debugging only)
+const char* const FromUniqueString(const UniqueString*);
+
+// Do a strcmp-style comparison on the contained C string
+int StrcmpUniqueString(const UniqueString*, const UniqueString*);
+
+// Less-than comparison of two UniqueStrings, usable for std::sort.
+bool LessThan_UniqueString(const UniqueString*, const UniqueString*);
+
+// Some handy pre-uniqified strings.  Z is an escape character:
+//   ZS        '$'
+//   ZD        '.'
+//   Zeq       '='
+//   Zplus     '+'
+//   Zstar     '*'
+//   Zslash    '/'
+//   Zpercent  '%'
+//   Zat       '@'
+//   Zcaret    '^'
+
+// Note that ustr__empty and (UniqueString*)NULL are considered
+// to be different.
+//
+// Unfortunately these have to be written as functions so as to
+// make them safe to use in static initialisers.
+
+// ""
+inline static const UniqueString* ustr__empty() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString("");
+  return us;
+}
+
+// "$eip"
+inline static const UniqueString* ustr__ZSeip() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString("$eip");
+  return us;
+}
+
+// "$ebp"
+inline static const UniqueString* ustr__ZSebp() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString("$ebp");
+  return us;
+}
+
+// "$esp"
+inline static const UniqueString* ustr__ZSesp() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString("$esp");
+  return us;
+}
+
+// "$ebx"
+inline static const UniqueString* ustr__ZSebx() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString("$ebx");
+  return us;
+}
+
+// "$esi"
+inline static const UniqueString* ustr__ZSesi() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString("$esi");
+  return us;
+}
+
+// "$edi"
+inline static const UniqueString* ustr__ZSedi() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString("$edi");
+  return us;
+}
+
+// ".cbCalleeParams"
+inline static const UniqueString* ustr__ZDcbCalleeParams() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString(".cbCalleeParams");
+  return us;
+}
+
+// ".cbSavedRegs"
+inline static const UniqueString* ustr__ZDcbSavedRegs() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString(".cbSavedRegs");
+  return us;
+}
+
+// ".cbLocals"
+inline static const UniqueString* ustr__ZDcbLocals() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString(".cbLocals");
+  return us;
+}
+
+// ".raSearchStart"
+inline static const UniqueString* ustr__ZDraSearchStart() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString(".raSearchStart");
+  return us;
+}
+
+// ".raSearch"
+inline static const UniqueString* ustr__ZDraSearch() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString(".raSearch");
+  return us;
+}
+
+// ".cbParams"
+inline static const UniqueString* ustr__ZDcbParams() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString(".cbParams");
+  return us;
+}
+
+// "+"
+inline static const UniqueString* ustr__Zplus() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString("+");
+  return us;
+}
+
+// "-"
+inline static const UniqueString* ustr__Zminus() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString("-");
+  return us;
+}
+
+// "*"
+inline static const UniqueString* ustr__Zstar() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString("*");
+  return us;
+}
+
+// "/"
+inline static const UniqueString* ustr__Zslash() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString("/");
+  return us;
+}
+
+// "%"
+inline static const UniqueString* ustr__Zpercent() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString("%");
+  return us;
+}
+
+// "@"
+inline static const UniqueString* ustr__Zat() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString("@");
+  return us;
+}
+
+// "^"
+inline static const UniqueString* ustr__Zcaret() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString("^");
+  return us;
+}
+
+// "="
+inline static const UniqueString* ustr__Zeq() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString("=");
+  return us;
+}
+
+// ".cfa"
+inline static const UniqueString* ustr__ZDcfa() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString(".cfa");
+  return us;
+}
+
+// ".ra"
+inline static const UniqueString* ustr__ZDra() {
+  static const UniqueString* us = NULL;
+  if (!us) us = ToUniqueString(".ra");
+  return us;
+}
+
+}  // namespace google_breakpad
+
+#endif  // COMMON_UNIQUE_STRING_H_
diff --git a/src/processor/basic_source_line_resolver_unittest.cc b/src/processor/basic_source_line_resolver_unittest.cc
--- a/src/processor/basic_source_line_resolver_unittest.cc
+++ b/src/processor/basic_source_line_resolver_unittest.cc
@@ -43,21 +43,30 @@
 #include "processor/windows_frame_info.h"
 #include "processor/cfi_frame_info.h"
 
 namespace {
 
 using google_breakpad::BasicSourceLineResolver;
 using google_breakpad::CFIFrameInfo;
 using google_breakpad::CodeModule;
+using google_breakpad::FromUniqueString;
 using google_breakpad::MemoryRegion;
 using google_breakpad::StackFrame;
+using google_breakpad::ToUniqueString;
 using google_breakpad::WindowsFrameInfo;
 using google_breakpad::linked_ptr;
 using google_breakpad::scoped_ptr;
+using google_breakpad::ustr__ZDcfa;
+using google_breakpad::ustr__ZDra;
+using google_breakpad::ustr__ZSebx;
+using google_breakpad::ustr__ZSebp;
+using google_breakpad::ustr__ZSedi;
+using google_breakpad::ustr__ZSesi;
+using google_breakpad::ustr__ZSesp;
 
 class TestCodeModule : public CodeModule {
  public:
   TestCodeModule(string code_file) : code_file_(code_file) {}
   virtual ~TestCodeModule() {}
 
   virtual uint64_t base_address() const { return 0; }
   virtual uint64_t size() const { return 0xb000; }
@@ -107,34 +116,34 @@
 // association. (That is, ACTUAL's associations should be a subset of
 // EXPECTED's.) Also verify that ACTUAL has associations for ".ra" and
 // ".cfa".
 static bool VerifyRegisters(
     const char *file, int line,
     const CFIFrameInfo::RegisterValueMap<uint32_t> &expected,
     const CFIFrameInfo::RegisterValueMap<uint32_t> &actual) {
   CFIFrameInfo::RegisterValueMap<uint32_t>::const_iterator a;
-  a = actual.find(".cfa");
+  a = actual.find(ustr__ZDcfa());
   if (a == actual.end())
     return false;
-  a = actual.find(".ra");
+  a = actual.find(ustr__ZDra());
   if (a == actual.end())
     return false;
   for (a = actual.begin(); a != actual.end(); a++) {
     CFIFrameInfo::RegisterValueMap<uint32_t>::const_iterator e =
       expected.find(a->first);
     if (e == expected.end()) {
       fprintf(stderr, "%s:%d: unexpected register '%s' recovered, value 0x%x\n",
-              file, line, a->first.c_str(), a->second);
+              file, line, FromUniqueString(a->first), a->second);
       return false;
     }
     if (e->second != a->second) {
       fprintf(stderr,
               "%s:%d: register '%s' recovered value was 0x%x, expected 0x%x\n",
-              file, line, a->first.c_str(), a->second, e->second);
+              file, line, FromUniqueString(a->first), a->second, e->second);
       return false;
     }
     // Don't complain if this doesn't recover all registers. Although
     // the DWARF spec says that unmentioned registers are undefined,
     // GCC uses omission to mean that they are unchanged.
   }
   return true;
 }
@@ -254,81 +263,81 @@
 
   CFIFrameInfo::RegisterValueMap<uint32_t> current_registers;
   CFIFrameInfo::RegisterValueMap<uint32_t> caller_registers;
   CFIFrameInfo::RegisterValueMap<uint32_t> expected_caller_registers;
   MockMemoryRegion memory;
 
   // Regardless of which instruction evaluation takes place at, it
   // should produce the same values for the caller's registers.
-  expected_caller_registers[".cfa"] = 0x1001c;
-  expected_caller_registers[".ra"]  = 0xf6438648;
-  expected_caller_registers["$ebp"] = 0x10038;
-  expected_caller_registers["$ebx"] = 0x98ecadc3;
-  expected_caller_registers["$esi"] = 0x878f7524;
-  expected_caller_registers["$edi"] = 0x6312f9a5;
+  expected_caller_registers[ustr__ZDcfa()] = 0x1001c;
+  expected_caller_registers[ustr__ZDra()]  = 0xf6438648;
+  expected_caller_registers[ustr__ZSebp()] = 0x10038;
+  expected_caller_registers[ustr__ZSebx()] = 0x98ecadc3;
+  expected_caller_registers[ustr__ZSesi()] = 0x878f7524;
+  expected_caller_registers[ustr__ZSedi()] = 0x6312f9a5;
 
   frame.instruction = 0x3d40;
   frame.module = &module1;
   current_registers.clear();
-  current_registers["$esp"] = 0x10018;
-  current_registers["$ebp"] = 0x10038;
-  current_registers["$ebx"] = 0x98ecadc3;
-  current_registers["$esi"] = 0x878f7524;
-  current_registers["$edi"] = 0x6312f9a5;
+  current_registers[ustr__ZSesp()] = 0x10018;
+  current_registers[ustr__ZSebp()] = 0x10038;
+  current_registers[ustr__ZSebx()] = 0x98ecadc3;
+  current_registers[ustr__ZSesi()] = 0x878f7524;
+  current_registers[ustr__ZSedi()] = 0x6312f9a5;
   cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
   ASSERT_TRUE(cfi_frame_info.get());
   ASSERT_TRUE(cfi_frame_info.get()
               ->FindCallerRegs<uint32_t>(current_registers, memory,
                                           &caller_registers));
   ASSERT_TRUE(VerifyRegisters(__FILE__, __LINE__,
                               expected_caller_registers, caller_registers));
 
   frame.instruction = 0x3d41;
-  current_registers["$esp"] = 0x10014;
+  current_registers[ustr__ZSesp()] = 0x10014;
   cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
   ASSERT_TRUE(cfi_frame_info.get());
   ASSERT_TRUE(cfi_frame_info.get()
               ->FindCallerRegs<uint32_t>(current_registers, memory,
                                           &caller_registers));
   ASSERT_TRUE(VerifyRegisters(__FILE__, __LINE__,
                               expected_caller_registers, caller_registers));
 
   frame.instruction = 0x3d43;
-  current_registers["$ebp"] = 0x10014;
+  current_registers[ustr__ZSebp()] = 0x10014;
   cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
   ASSERT_TRUE(cfi_frame_info.get());
   ASSERT_TRUE(cfi_frame_info.get()
               ->FindCallerRegs<uint32_t>(current_registers, memory,
                                           &caller_registers));
   VerifyRegisters(__FILE__, __LINE__,
                   expected_caller_registers, caller_registers);
 
   frame.instruction = 0x3d54;
-  current_registers["$ebx"] = 0x6864f054U;
+  current_registers[ustr__ZSebx()] = 0x6864f054U;
   cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
   ASSERT_TRUE(cfi_frame_info.get());
   ASSERT_TRUE(cfi_frame_info.get()
               ->FindCallerRegs<uint32_t>(current_registers, memory,
                                           &caller_registers));
   VerifyRegisters(__FILE__, __LINE__,
                   expected_caller_registers, caller_registers);
 
   frame.instruction = 0x3d5a;
-  current_registers["$esi"] = 0x6285f79aU;
+  current_registers[ustr__ZSesi()] = 0x6285f79aU;
   cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
   ASSERT_TRUE(cfi_frame_info.get());
   ASSERT_TRUE(cfi_frame_info.get()
               ->FindCallerRegs<uint32_t>(current_registers, memory,
                                           &caller_registers));
   VerifyRegisters(__FILE__, __LINE__,
                   expected_caller_registers, caller_registers);
 
   frame.instruction = 0x3d84;
-  current_registers["$edi"] = 0x64061449U;
+  current_registers[ustr__ZSedi()] = 0x64061449U;
   cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
   ASSERT_TRUE(cfi_frame_info.get());
   ASSERT_TRUE(cfi_frame_info.get()
               ->FindCallerRegs<uint32_t>(current_registers, memory,
                                           &caller_registers));
   VerifyRegisters(__FILE__, __LINE__,
                   expected_caller_registers, caller_registers);
 
diff --git a/src/processor/cfi_frame_info.cc b/src/processor/cfi_frame_info.cc
--- a/src/processor/cfi_frame_info.cc
+++ b/src/processor/cfi_frame_info.cc
@@ -31,16 +31,17 @@
 
 // cfi_frame_info.cc: Implementation of CFIFrameInfo class.
 // See cfi_frame_info.h for details.
 
 #include "processor/cfi_frame_info.h"
 
 #include <string.h>
 
+#include <algorithm>
 #include <sstream>
 
 #include "common/scoped_ptr.h"
 #include "processor/postfix_evaluator-inl.h"
 
 namespace google_breakpad {
 
 #ifdef _WIN32
@@ -65,33 +66,33 @@
   V cfa;
   working = registers;
   if (!evaluator.EvaluateForValue(cfa_rule_, &cfa))
     return false;
 
   // Then, compute the return address.
   V ra;
   working = registers;
-  working[".cfa"] = cfa;
+  working[ustr__ZDcfa()] = cfa;
   if (!evaluator.EvaluateForValue(ra_rule_, &ra))
     return false;
 
   // Now, compute values for all the registers register_rules_ mentions.
   for (RuleMap::const_iterator it = register_rules_.begin();
        it != register_rules_.end(); it++) {
     V value;
     working = registers;
-    working[".cfa"] = cfa;
+    working[ustr__ZDcfa()] = cfa;
     if (!evaluator.EvaluateForValue(it->second, &value))
       return false;
     (*caller_registers)[it->first] = value;
   }
 
-  (*caller_registers)[".ra"] = ra;
-  (*caller_registers)[".cfa"] = cfa;
+  (*caller_registers)[ustr__ZDra()] = ra;
+  (*caller_registers)[ustr__ZDcfa()] = cfa;
 
   return true;
 }
 
 // Explicit instantiations for 32-bit and 64-bit architectures.
 template bool CFIFrameInfo::FindCallerRegs<uint32_t>(
     const RegisterValueMap<uint32_t> &registers,
     const MemoryRegion &memory,
@@ -107,81 +108,98 @@
   if (!cfa_rule_.invalid()) {
     stream << ".cfa: " << cfa_rule_;
   }
   if (!ra_rule_.invalid()) {
     if (static_cast<std::streamoff>(stream.tellp()) != 0)
       stream << " ";
     stream << ".ra: " << ra_rule_;
   }
+
+  // Visit the register rules in alphabetical order.  Because
+  // register_rules_ has the elements in some arbitrary order,
+  // get the names out into a vector, sort them, and visit in
+  // sorted order.
+  std::vector<const UniqueString*> rr_names;
   for (RuleMap::const_iterator iter = register_rules_.begin();
        iter != register_rules_.end();
        ++iter) {
+    rr_names.push_back(iter->first);
+  }
+
+  std::sort(rr_names.begin(), rr_names.end(), LessThan_UniqueString);
+
+  // Now visit the register rules in alphabetical order.
+  for (std::vector<const UniqueString*>::const_iterator name = rr_names.begin();
+       name != rr_names.end();
+       ++name) {
+    const UniqueString* nm = *name;
+    Module::Expr rule = register_rules_.find(nm)->second;
     if (static_cast<std::streamoff>(stream.tellp()) != 0)
       stream << " ";
-    stream << iter->first << ": " << iter->second;
+    stream << FromUniqueString(nm) << ": " << rule;
   }
 
   return stream.str();
 }
 
 bool CFIRuleParser::Parse(const string &rule_set) {
   size_t rule_set_len = rule_set.size();
   scoped_array<char> working_copy(new char[rule_set_len + 1]);
   memcpy(working_copy.get(), rule_set.data(), rule_set_len);
   working_copy[rule_set_len] = '\0';
 
-  name_.clear();
+  name_ = ustr__empty();
   expression_.clear();
 
   char *cursor;
   static const char token_breaks[] = " \t\r\n";
   char *token = strtok_r(working_copy.get(), token_breaks, &cursor);
 
   for (;;) {
     // End of rule set?
     if (!token) return Report();
 
     // Register/pseudoregister name?
     size_t token_len = strlen(token);
     if (token_len >= 1 && token[token_len - 1] == ':') {
       // Names can't be empty.
       if (token_len < 2) return false;
       // If there is any pending content, report it.
-      if (!name_.empty() || !expression_.empty()) {
+      if (name_ != ustr__empty() || !expression_.empty()) {
         if (!Report()) return false;
       }
-      name_.assign(token, token_len - 1);
+      name_ = ToUniqueString_n(token, token_len - 1);
       expression_.clear();
     } else {
       // Another expression component.
       assert(token_len > 0); // strtok_r guarantees this, I think.
       if (!expression_.empty())
         expression_ += ' ';
       expression_ += token;
     }
     token = strtok_r(NULL, token_breaks, &cursor);
   }
 }
 
 bool CFIRuleParser::Report() {
-  if (name_.empty() || expression_.empty()) return false;
-  if (name_ == ".cfa") handler_->CFARule(expression_);
-  else if (name_ == ".ra") handler_->RARule(expression_);
+  if (name_ == ustr__empty() || expression_.empty()) return false;
+  if (name_ == ustr__ZDcfa()) handler_->CFARule(expression_);
+  else if (name_ == ustr__ZDra()) handler_->RARule(expression_);
   else handler_->RegisterRule(name_, expression_);
   return true;
 }
 
 void CFIFrameInfoParseHandler::CFARule(const string &expression) {
   // 'expression' is a postfix expression string.
   frame_info_->SetCFARule(Module::Expr(expression));
 }
 
 void CFIFrameInfoParseHandler::RARule(const string &expression) {
   frame_info_->SetRARule(Module::Expr(expression));
 }
 
-void CFIFrameInfoParseHandler::RegisterRule(const string &name,
+void CFIFrameInfoParseHandler::RegisterRule(const UniqueString* name,
                                             const string &expression) {
   frame_info_->SetRegisterRule(name, Module::Expr(expression));
 }
 
 } // namespace google_breakpad
diff --git a/src/processor/cfi_frame_info.h b/src/processor/cfi_frame_info.h
--- a/src/processor/cfi_frame_info.h
+++ b/src/processor/cfi_frame_info.h
@@ -37,16 +37,17 @@
 
 #ifndef PROCESSOR_CFI_FRAME_INFO_H_
 #define PROCESSOR_CFI_FRAME_INFO_H_
 
 #include <map>
 #include <string>
 
 #include "common/using_std_string.h"
+#include "common/unique_string.h"
 #include "google_breakpad/common/breakpad_types.h"
 #include "common/module.h"
 
 namespace google_breakpad {
 
 using std::map;
 
 class MemoryRegion;
@@ -63,24 +64,24 @@
 // changes given by the 'STACK CFI' records up to our instruction's
 // address. Then, use the FindCallerRegs member function to apply the
 // rules to the callee frame's register values, yielding the caller
 // frame's register values.
 class CFIFrameInfo {
  public:
   // A map from register names onto values.
   template<typename ValueType> class RegisterValueMap:
-    public map<string, ValueType> { };
+    public map<const UniqueString*, ValueType> { };
 
   // Set the expression for computing a call frame address, return
   // address, or register's value. At least the CFA rule and the RA
   // rule must be set before calling FindCallerRegs.
   void SetCFARule(const Module::Expr& rule) { cfa_rule_ = rule; }
   void SetRARule(const Module::Expr& rule)  { ra_rule_ = rule; }
-  void SetRegisterRule(const string& register_name,
+  void SetRegisterRule(const UniqueString* register_name,
                        const Module::Expr& rule) {
     register_rules_[register_name] = rule;
   }
 
   // Compute the values of the calling frame's registers, according to
   // this rule set. Use ValueType in expression evaluation; this
   // should be uint32_t on machines with 32-bit addresses, or
   // uint64_t on machines with 64-bit addresses.
@@ -104,17 +105,17 @@
 
   // Serialize the rules in this object into a string in the format
   // of STACK CFI records.
   string Serialize() const;
 
  private:
 
   // A map from register names onto evaluation rules.
-  typedef map<string, Module::Expr> RuleMap;
+  typedef map<const UniqueString*, Module::Expr> RuleMap;
 
   // An expression for computing the current frame's CFA (call
   // frame address). The CFA is a reference address for the frame that
   // remains unchanged throughout the frame's lifetime. You should
   // evaluate this expression with a dictionary initially populated
   // with the values of the current frame's known registers.
   Module::Expr cfa_rule_;
 
@@ -145,17 +146,18 @@
     Handler() { }
     virtual ~Handler() { }
 
     // The input specifies EXPRESSION as the CFA/RA computation rule.
     virtual void CFARule(const string &expression) = 0;
     virtual void RARule(const string &expression) = 0;
 
     // The input specifies EXPRESSION as the recovery rule for register NAME.
-    virtual void RegisterRule(const string &name, const string &expression) = 0;
+    virtual void RegisterRule(const UniqueString* name,
+                              const string &expression) = 0;
   };
     
   // Construct a parser which feeds its results to HANDLER.
   CFIRuleParser(Handler *handler) : handler_(handler) { }
 
   // Parse RULE_SET as a set of CFA computation and RA/register
   // recovery rules, as appearing in STACK CFI records. Report the
   // results of parsing by making the appropriate calls to handler_.
@@ -165,30 +167,31 @@
  private:
   // Report any accumulated rule to handler_
   bool Report();
 
   // The handler to which the parser reports its findings.
   Handler *handler_;
 
   // Working data.
-  string name_, expression_;
+  const UniqueString* name_;
+  string expression_;
 };
 
 // A handler for rule set parsing that populates a CFIFrameInfo with
 // the results.
 class CFIFrameInfoParseHandler: public CFIRuleParser::Handler {
  public:
   // Populate FRAME_INFO with the results of parsing.
   CFIFrameInfoParseHandler(CFIFrameInfo *frame_info)
       : frame_info_(frame_info) { }
 
   void CFARule(const string &expression);
   void RARule(const string &expression);
-  void RegisterRule(const string &name, const string &expression);
+  void RegisterRule(const UniqueString* name, const string &expression);
 
  private:
   CFIFrameInfo *frame_info_;
 };
 
 // A utility class template for simple 'STACK CFI'-driven stack walkers.
 // Given a CFIFrameInfo instance, a table describing the architecture's
 // register set, and a context holding the last frame's registers, an
@@ -205,24 +208,24 @@
 // uint32_t or uint64_t. RawContextType should be the raw context
 // structure type for this architecture.
 template <typename RegisterType, class RawContextType>
 class SimpleCFIWalker {
  public:
   // A structure describing one architecture register.
   struct RegisterSet {
     // The register name, as it appears in STACK CFI rules.
-    const char *name;
+    const UniqueString* name;
 
     // An alternate name that the register's value might be found
     // under in a register value dictionary, or NULL. When generating
     // names, prefer NAME to this value. It's common to list ".cfa" as
     // an alternative name for the stack pointer, and ".ra" as an
     // alternative name for the instruction pointer.
-    const char *alternate_name;
+    const UniqueString* alternate_name;
 
     // True if the callee is expected to preserve the value of this
     // register. If this flag is true for some register R, and the STACK
     // CFI records provide no rule to recover R, then SimpleCFIWalker
     // assumes that the callee has not changed R's value, and the caller's
     // value for R is that currently in the callee's context.
     bool callee_saves;
 
diff --git a/src/processor/cfi_frame_info_unittest.cc b/src/processor/cfi_frame_info_unittest.cc
--- a/src/processor/cfi_frame_info_unittest.cc
+++ b/src/processor/cfi_frame_info_unittest.cc
@@ -38,19 +38,24 @@
 #include "common/module.h"
 #include "common/using_std_string.h"
 #include "processor/cfi_frame_info.h"
 #include "google_breakpad/processor/memory_region.h"
 
 using google_breakpad::CFIFrameInfo;
 using google_breakpad::CFIFrameInfoParseHandler;
 using google_breakpad::CFIRuleParser;
+using google_breakpad::FromUniqueString;
 using google_breakpad::MemoryRegion;
 using google_breakpad::Module;
 using google_breakpad::SimpleCFIWalker;
+using google_breakpad::ToUniqueString;
+using google_breakpad::UniqueString;
+using google_breakpad::ustr__ZDcfa;
+using google_breakpad::ustr__ZDra;
 using testing::_;
 using testing::A;
 using testing::AtMost;
 using testing::DoAll;
 using testing::Return;
 using testing::SetArgumentPointee;
 using testing::Test;
 
@@ -107,41 +112,47 @@
 TEST_F(Simple, SetCFAAndRARule) {
   ExpectNoMemoryReferences();
 
   cfi.SetCFARule(Module::Expr("330903416631436410"));
   cfi.SetRARule(Module::Expr("5870666104170902211"));
   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
                                             &caller_registers));
   ASSERT_EQ(2U, caller_registers.size());
-  ASSERT_EQ(330903416631436410ULL, caller_registers[".cfa"]);
-  ASSERT_EQ(5870666104170902211ULL, caller_registers[".ra"]);
+  ASSERT_EQ(330903416631436410ULL, caller_registers[ustr__ZDcfa()]);
+  ASSERT_EQ(5870666104170902211ULL, caller_registers[ustr__ZDra()]);
 
   ASSERT_EQ(".cfa: 330903416631436410 .ra: 5870666104170902211",
             cfi.Serialize());
 }
 
 TEST_F(Simple, SetManyRules) {
   ExpectNoMemoryReferences();
 
   cfi.SetCFARule(Module::Expr("$temp1 68737028 = $temp2 61072337 = $temp1 $temp2 -"));
   cfi.SetRARule(Module::Expr(".cfa 99804755 +"));
-  cfi.SetRegisterRule("register1", Module::Expr(".cfa 54370437 *"));
-  cfi.SetRegisterRule("vodkathumbscrewingly", Module::Expr("24076308 .cfa +"));
-  cfi.SetRegisterRule("pubvexingfjordschmaltzy", Module::Expr(".cfa 29801007 -"));
-  cfi.SetRegisterRule("uncopyrightables", Module::Expr("92642917 .cfa /"));
+
+  const UniqueString* reg1 = ToUniqueString("register1");
+  const UniqueString* reg2 = ToUniqueString("vodkathumbscrewingly");
+  const UniqueString* reg3 = ToUniqueString("pubvexingfjordschmaltzy");
+  const UniqueString* reg4 = ToUniqueString("uncopyrightables");
+
+  cfi.SetRegisterRule(reg1, Module::Expr(".cfa 54370437 *"));
+  cfi.SetRegisterRule(reg2, Module::Expr("24076308 .cfa +"));
+  cfi.SetRegisterRule(reg3, Module::Expr(".cfa 29801007 -"));
+  cfi.SetRegisterRule(reg4, Module::Expr("92642917 .cfa /"));
   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
                                             &caller_registers));
   ASSERT_EQ(6U, caller_registers.size());
-  ASSERT_EQ(7664691U,           caller_registers[".cfa"]);
-  ASSERT_EQ(107469446U,         caller_registers[".ra"]);
-  ASSERT_EQ(416732599139967ULL, caller_registers["register1"]);
-  ASSERT_EQ(31740999U,          caller_registers["vodkathumbscrewingly"]);
-  ASSERT_EQ(-22136316ULL,       caller_registers["pubvexingfjordschmaltzy"]);
-  ASSERT_EQ(12U,                caller_registers["uncopyrightables"]);
+  ASSERT_EQ(7664691U,           caller_registers[ustr__ZDcfa()]);
+  ASSERT_EQ(107469446U,         caller_registers[ustr__ZDra()]);
+  ASSERT_EQ(416732599139967ULL, caller_registers[reg1]);
+  ASSERT_EQ(31740999U,          caller_registers[reg2]);
+  ASSERT_EQ(-22136316ULL,       caller_registers[reg3]);
+  ASSERT_EQ(12U,                caller_registers[reg4]);
   ASSERT_EQ(".cfa: $temp1 68737028 = $temp2 61072337 = $temp1 $temp2 - "
             ".ra: .cfa 99804755 + "
             "pubvexingfjordschmaltzy: .cfa 29801007 - "
             "register1: .cfa 54370437 * "
             "uncopyrightables: 92642917 .cfa / "
             "vodkathumbscrewingly: 24076308 .cfa +",
             cfi.Serialize());
 }
@@ -150,18 +161,18 @@
   ExpectNoMemoryReferences();
 
   cfi.SetCFARule(Module::Expr("330903416631436410"));
   cfi.SetRARule(Module::Expr("5870666104170902211"));
   cfi.SetCFARule(Module::Expr("2828089117179001"));
   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
                                             &caller_registers));
   ASSERT_EQ(2U, caller_registers.size());
-  ASSERT_EQ(2828089117179001ULL, caller_registers[".cfa"]);
-  ASSERT_EQ(5870666104170902211ULL, caller_registers[".ra"]);
+  ASSERT_EQ(2828089117179001ULL, caller_registers[ustr__ZDcfa()]);
+  ASSERT_EQ(5870666104170902211ULL, caller_registers[ustr__ZDra()]);
   ASSERT_EQ(".cfa: 2828089117179001 .ra: 5870666104170902211",
             cfi.Serialize());
 }
 
 class Scope: public CFIFixture, public Test { };
 
 // There should be no value for .cfa in scope when evaluating the CFA rule.
 TEST_F(Scope, CFALacksCFA) {
@@ -183,37 +194,39 @@
                                              &caller_registers));
 }
 
 // The current frame's registers should be in scope when evaluating
 // the CFA rule.
 TEST_F(Scope, CFASeesCurrentRegs) {
   ExpectNoMemoryReferences();
 
-  registers[".baraminology"] = 0x06a7bc63e4f13893ULL;
-  registers[".ornithorhynchus"] = 0x5e0bf850bafce9d2ULL;
+  const UniqueString* reg1 = ToUniqueString(".baraminology");
+  const UniqueString* reg2 = ToUniqueString(".ornithorhynchus");
+  registers[reg1] = 0x06a7bc63e4f13893ULL;
+  registers[reg2] = 0x5e0bf850bafce9d2ULL;
   cfi.SetCFARule(Module::Expr(".baraminology .ornithorhynchus +"));
   cfi.SetRARule(Module::Expr("0"));
   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
                                             &caller_registers));
   ASSERT_EQ(2U, caller_registers.size());
   ASSERT_EQ(0x06a7bc63e4f13893ULL + 0x5e0bf850bafce9d2ULL,
-            caller_registers[".cfa"]);
+            caller_registers[ustr__ZDcfa()]);
 }
 
 // .cfa should be in scope in the return address expression.
 TEST_F(Scope, RASeesCFA) {
   ExpectNoMemoryReferences();
 
   cfi.SetCFARule(Module::Expr("48364076"));
   cfi.SetRARule(Module::Expr(".cfa"));
   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
                                             &caller_registers));
   ASSERT_EQ(2U, caller_registers.size());
-  ASSERT_EQ(48364076U, caller_registers[".ra"]);
+  ASSERT_EQ(48364076U, caller_registers[ustr__ZDra()]);
 }
 
 // There should be no value for .ra in scope when evaluating the CFA rule.
 TEST_F(Scope, RALacksRA) {
   ExpectNoMemoryReferences();
 
   cfi.SetCFARule(Module::Expr("0"));
   cfi.SetRARule(Module::Expr(".ra"));
@@ -221,64 +234,69 @@
                                              &caller_registers));
 }
 
 // The current frame's registers should be in scope in the return
 // address expression.
 TEST_F(Scope, RASeesCurrentRegs) {
   ExpectNoMemoryReferences();
 
-  registers["noachian"] = 0x54dc4a5d8e5eb503ULL;
   cfi.SetCFARule(Module::Expr("10359370"));
-  cfi.SetRARule(Module::Expr("noachian"));
+  const UniqueString* reg1 = ToUniqueString("noachian");
+  registers[reg1] = 0x54dc4a5d8e5eb503ULL;
+  cfi.SetRARule(Module::Expr(reg1, 0, false));
   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
                                             &caller_registers));
   ASSERT_EQ(2U, caller_registers.size());
-  ASSERT_EQ(0x54dc4a5d8e5eb503ULL, caller_registers[".ra"]);
+  ASSERT_EQ(0x54dc4a5d8e5eb503ULL, caller_registers[ustr__ZDra()]);
 }
 
 // .cfa should be in scope for register rules.
 TEST_F(Scope, RegistersSeeCFA) {
   ExpectNoMemoryReferences();
 
   cfi.SetCFARule(Module::Expr("6515179"));
   cfi.SetRARule(Module::Expr(".cfa"));
-  cfi.SetRegisterRule("rogerian", Module::Expr(".cfa"));
+  const UniqueString* reg1 = ToUniqueString("rogerian");
+  cfi.SetRegisterRule(reg1, Module::Expr(".cfa"));
   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
                                             &caller_registers));
   ASSERT_EQ(3U, caller_registers.size());
-  ASSERT_EQ(6515179U, caller_registers["rogerian"]);
+  ASSERT_EQ(6515179U, caller_registers[reg1]);
 }
 
 // The return address should not be in scope for register rules.
 TEST_F(Scope, RegsLackRA) {
   ExpectNoMemoryReferences();
 
   cfi.SetCFARule(Module::Expr("42740329"));
   cfi.SetRARule(Module::Expr("27045204"));
-  cfi.SetRegisterRule("$r1", Module::Expr(".ra"));
+  const UniqueString* reg1 = ToUniqueString("$r1");
+  cfi.SetRegisterRule(reg1, Module::Expr(".ra"));
   ASSERT_FALSE(cfi.FindCallerRegs<uint64_t>(registers, memory,
                                              &caller_registers));
 }
 
 // Register rules can see the current frame's register values.
 TEST_F(Scope, RegsSeeRegs) {
   ExpectNoMemoryReferences();
 
-  registers["$r1"] = 0x6ed3582c4bedb9adULL;
-  registers["$r2"] = 0xd27d9e742b8df6d0ULL;
+  const UniqueString* reg1 = ToUniqueString("$r1");
+  const UniqueString* reg2 = ToUniqueString("$r2");
+  registers[reg1] = 0x6ed3582c4bedb9adULL;
+  registers[reg2] = 0xd27d9e742b8df6d0ULL;
   cfi.SetCFARule(Module::Expr("88239303"));
   cfi.SetRARule(Module::Expr("30503835"));
-  cfi.SetRegisterRule("$r1", Module::Expr("$r1 42175211 = $r2"));
-  cfi.SetRegisterRule("$r2", Module::Expr("$r2 21357221 = $r1"));
+  cfi.SetRegisterRule(reg1, Module::Expr("$r1 42175211 = $r2"));
+  cfi.SetRegisterRule(reg2, Module::Expr("$r2 21357221 = $r1"));
   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
                                             &caller_registers));
   ASSERT_EQ(4U, caller_registers.size());
-  ASSERT_EQ(0xd27d9e742b8df6d0ULL, caller_registers["$r1"]);
-  ASSERT_EQ(0x6ed3582c4bedb9adULL, caller_registers["$r2"]);
+  ASSERT_EQ(0xd27d9e742b8df6d0ULL, caller_registers[reg1]);
+  ASSERT_EQ(0x6ed3582c4bedb9adULL, caller_registers[reg2]);
 }
 
 // Each rule's temporaries are separate.
 TEST_F(Scope, SeparateTempsRA) {
   ExpectNoMemoryReferences();
 
   cfi.SetCFARule(Module::Expr("$temp1 76569129 = $temp1"));
   cfi.SetRARule(Module::Expr("0"));
@@ -290,17 +308,17 @@
   ASSERT_FALSE(cfi.FindCallerRegs<uint64_t>(registers, memory,
                                              &caller_registers));
 }
 
 class MockCFIRuleParserHandler: public CFIRuleParser::Handler {
  public:
   MOCK_METHOD1(CFARule, void(const string &));
   MOCK_METHOD1(RARule,  void(const string &));
-  MOCK_METHOD2(RegisterRule, void(const string &, const string &));
+  MOCK_METHOD2(RegisterRule, void(const UniqueString*, const string &));
 };
 
 // A fixture class for testing CFIRuleParser.
 class CFIParserFixture {
  public:
   CFIParserFixture() : parser(&mock_handler) {
     // Expect no parsing results to be reported to mock_handler. Individual
     // tests can override this.
@@ -361,100 +379,100 @@
 }
 
 TEST_F(Parser, RA) {
   EXPECT_CALL(mock_handler, RARule("notoriety")).WillOnce(Return());
   EXPECT_TRUE(parser.Parse(".ra: notoriety"));
 }
 
 TEST_F(Parser, Reg) {
-  EXPECT_CALL(mock_handler, RegisterRule("nemo", "mellifluous"))
+  EXPECT_CALL(mock_handler, RegisterRule(ToUniqueString("nemo"), "mellifluous"))
       .WillOnce(Return());
   EXPECT_TRUE(parser.Parse("nemo: mellifluous"));
 }
 
 TEST_F(Parser, CFARARegs) {
   EXPECT_CALL(mock_handler, CFARule("cfa expression")).WillOnce(Return());
   EXPECT_CALL(mock_handler, RARule("ra expression")).WillOnce(Return());
-  EXPECT_CALL(mock_handler, RegisterRule("galba", "praetorian"))
+  EXPECT_CALL(mock_handler, RegisterRule(ToUniqueString("galba"), "praetorian"))
       .WillOnce(Return());
-  EXPECT_CALL(mock_handler, RegisterRule("otho", "vitellius"))
+  EXPECT_CALL(mock_handler, RegisterRule(ToUniqueString("otho"), "vitellius"))
       .WillOnce(Return());
   EXPECT_TRUE(parser.Parse(".cfa: cfa expression .ra: ra expression "
                     "galba: praetorian otho: vitellius"));
 }
 
 TEST_F(Parser, Whitespace) {
-  EXPECT_CALL(mock_handler, RegisterRule("r1", "r1 expression"))
+  EXPECT_CALL(mock_handler, RegisterRule(ToUniqueString("r1"), "r1 expression"))
       .WillOnce(Return());
-  EXPECT_CALL(mock_handler, RegisterRule("r2", "r2 expression"))
+  EXPECT_CALL(mock_handler, RegisterRule(ToUniqueString("r2"), "r2 expression"))
       .WillOnce(Return());
   EXPECT_TRUE(parser.Parse(" r1:\tr1\nexpression \tr2:\t\rr2\r\n "
                            "expression  \n"));
 }
 
 TEST_F(Parser, WhitespaceLoneColon) {
   EXPECT_FALSE(parser.Parse("  \n:\t  "));
 }
 
 TEST_F(Parser, EmptyName) {
-  EXPECT_CALL(mock_handler, RegisterRule("reg", _))
+  EXPECT_CALL(mock_handler, RegisterRule(ToUniqueString("reg"), _))
       .Times(AtMost(1))
       .WillRepeatedly(Return());
   EXPECT_FALSE(parser.Parse("reg: expr1 : expr2"));
 }
 
 TEST_F(Parser, RuleLoneColon) {
-  EXPECT_CALL(mock_handler, RegisterRule("r1", "expr"))
+  EXPECT_CALL(mock_handler, RegisterRule(ToUniqueString("r1"), "expr"))
       .Times(AtMost(1))
       .WillRepeatedly(Return());
   EXPECT_FALSE(parser.Parse(" r1:   expr   :"));
 }
 
 TEST_F(Parser, RegNoExprRule) {
-  EXPECT_CALL(mock_handler, RegisterRule("r1", "expr"))
+  EXPECT_CALL(mock_handler, RegisterRule(ToUniqueString("r1"), "expr"))
       .Times(AtMost(1))
       .WillRepeatedly(Return());
   EXPECT_FALSE(parser.Parse("r0: r1:   expr"));
 }
 
 class ParseHandlerFixture: public CFIFixture {
  public:
   ParseHandlerFixture() : CFIFixture(), handler(&cfi) { }
   CFIFrameInfoParseHandler handler;
 };
 
 class ParseHandler: public ParseHandlerFixture, public Test { };
 
 TEST_F(ParseHandler, CFARARule) {
   handler.CFARule("reg-for-cfa");
   handler.RARule("reg-for-ra");
-  registers["reg-for-cfa"] = 0x268a9a4a3821a797ULL;
-  registers["reg-for-ra"] = 0x6301b475b8b91c02ULL;
+  registers[ToUniqueString("reg-for-cfa")] = 0x268a9a4a3821a797ULL;
+  registers[ToUniqueString("reg-for-ra")] = 0x6301b475b8b91c02ULL;
   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
                                             &caller_registers));
-  ASSERT_EQ(0x268a9a4a3821a797ULL, caller_registers[".cfa"]);
-  ASSERT_EQ(0x6301b475b8b91c02ULL, caller_registers[".ra"]);
+  ASSERT_EQ(0x268a9a4a3821a797ULL, caller_registers[ustr__ZDcfa()]);
+  ASSERT_EQ(0x6301b475b8b91c02ULL, caller_registers[ustr__ZDra()]);
 }
 
 TEST_F(ParseHandler, RegisterRules) {
   handler.CFARule("reg-for-cfa");
   handler.RARule("reg-for-ra");
-  handler.RegisterRule("reg1", "reg-for-reg1");
-  handler.RegisterRule("reg2", "reg-for-reg2");
-  registers["reg-for-cfa"] = 0x268a9a4a3821a797ULL;
-  registers["reg-for-ra"] = 0x6301b475b8b91c02ULL;
-  registers["reg-for-reg1"] = 0x06cde8e2ff062481ULL;
-  registers["reg-for-reg2"] = 0xff0c4f76403173e2ULL;
+  handler.RegisterRule(ToUniqueString("reg1"), "reg-for-reg1");
+  handler.RegisterRule(ToUniqueString("reg2"), "reg-for-reg2");
+  registers[ToUniqueString("reg-for-cfa")] = 0x268a9a4a3821a797ULL;
+  registers[ToUniqueString("reg-for-ra")] = 0x6301b475b8b91c02ULL;
+  registers[ToUniqueString("reg-for-reg1")] = 0x06cde8e2ff062481ULL;
+  registers[ToUniqueString("reg-for-reg2")] = 0xff0c4f76403173e2ULL;
   ASSERT_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
                                             &caller_registers));
-  ASSERT_EQ(0x268a9a4a3821a797ULL, caller_registers[".cfa"]);
-  ASSERT_EQ(0x6301b475b8b91c02ULL, caller_registers[".ra"]);
-  ASSERT_EQ(0x06cde8e2ff062481ULL, caller_registers["reg1"]);
-  ASSERT_EQ(0xff0c4f76403173e2ULL, caller_registers["reg2"]);
+  ASSERT_EQ(0x268a9a4a3821a797ULL, caller_registers[ustr__ZDcfa()]);
+  ASSERT_EQ(0x6301b475b8b91c02ULL, caller_registers[ustr__ZDra()]);
+  ASSERT_EQ(0x06cde8e2ff062481ULL, caller_registers[ToUniqueString("reg1")]);
+  ASSERT_EQ(0xff0c4f76403173e2ULL, caller_registers[ToUniqueString("reg2")]);
 }
 
 struct SimpleCFIWalkerFixture {
   struct RawContext {
     uint64_t r0, r1, r2, r3, r4, sp, pc;
   };
   enum Validity {
     R0_VALID = 0x01,
@@ -475,23 +493,23 @@
   CFIFrameInfo call_frame_info;
   CFIWalker walker;
   MockMemoryRegion memory;
   RawContext callee_context, caller_context;
 };
 
 SimpleCFIWalkerFixture::CFIWalker::RegisterSet
 SimpleCFIWalkerFixture::register_map[7] = {
-  { "r0", NULL,   true,  R0_VALID, &RawContext::r0 },
-  { "r1", NULL,   true,  R1_VALID, &RawContext::r1 },
-  { "r2", NULL,   false, R2_VALID, &RawContext::r2 },
-  { "r3", NULL,   false, R3_VALID, &RawContext::r3 },
-  { "r4", NULL,   true,  R4_VALID, &RawContext::r4 },
-  { "sp", ".cfa", true,  SP_VALID, &RawContext::sp },
-  { "pc", ".ra",  true,  PC_VALID, &RawContext::pc },
+  { ToUniqueString("r0"), NULL,   true,  R0_VALID, &RawContext::r0 },
+  { ToUniqueString("r1"), NULL,   true,  R1_VALID, &RawContext::r1 },
+  { ToUniqueString("r2"), NULL,   false, R2_VALID, &RawContext::r2 },
+  { ToUniqueString("r3"), NULL,   false, R3_VALID, &RawContext::r3 },
+  { ToUniqueString("r4"), NULL,   true,  R4_VALID, &RawContext::r4 },
+  { ToUniqueString("sp"), ustr__ZDcfa(), true,  SP_VALID, &RawContext::sp },
+  { ToUniqueString("pc"), ustr__ZDra(),  true,  PC_VALID, &RawContext::pc },
 };
 
 class SimpleWalker: public SimpleCFIWalkerFixture, public Test { };
 
 TEST_F(SimpleWalker, Walk) {
   // Stack_top is the current stack pointer, pointing to the lowest
   // address of a frame that looks like this (all 64-bit words):
   //
@@ -516,18 +534,20 @@
   // Saved return address.
   EXPECT_CALL(memory,
               GetMemoryAtAddress(stack_top + 16, A<uint64_t *>()))
       .WillRepeatedly(DoAll(SetArgumentPointee<1>(0xba5ad6d9acce28deULL),
                             Return(true)));
 
   call_frame_info.SetCFARule(Module::Expr("sp 24 +"));
   call_frame_info.SetRARule(Module::Expr(".cfa 8 - ^"));
-  call_frame_info.SetRegisterRule("r0", Module::Expr(".cfa 24 - ^"));
-  call_frame_info.SetRegisterRule("r1", Module::Expr("r2"));
+  call_frame_info.SetRegisterRule(ToUniqueString("r0"),
+                                  Module::Expr(".cfa 24 - ^"));
+  call_frame_info.SetRegisterRule(ToUniqueString("r1"),
+                                  Module::Expr("r2"));
 
   callee_context.r0 = 0x94e030ca79edd119ULL;
   callee_context.r1 = 0x937b4d7e95ce52d9ULL;
   callee_context.r2 = 0x5fe0027416b8b62aULL; // caller's r1
   // callee_context.r3 is not valid in callee.
   // callee_context.r4 is not valid in callee.
   callee_context.sp = stack_top;
   callee_context.pc = 0x25b21b224311d280ULL;
diff --git a/src/processor/fast_source_line_resolver_unittest.cc b/src/processor/fast_source_line_resolver_unittest.cc
--- a/src/processor/fast_source_line_resolver_unittest.cc
+++ b/src/processor/fast_source_line_resolver_unittest.cc
@@ -51,25 +51,34 @@
 #include "processor/module_serializer.h"
 #include "processor/module_comparer.h"
 
 namespace {
 
 using google_breakpad::SourceLineResolverBase;
 using google_breakpad::BasicSourceLineResolver;
 using google_breakpad::FastSourceLineResolver;
+using google_breakpad::FromUniqueString;
 using google_breakpad::ModuleSerializer;
 using google_breakpad::ModuleComparer;
 using google_breakpad::CFIFrameInfo;
 using google_breakpad::CodeModule;
 using google_breakpad::MemoryRegion;
 using google_breakpad::StackFrame;
+using google_breakpad::ToUniqueString;
 using google_breakpad::WindowsFrameInfo;
 using google_breakpad::linked_ptr;
 using google_breakpad::scoped_ptr;
+using google_breakpad::ustr__ZDcfa;
+using google_breakpad::ustr__ZDra;
+using google_breakpad::ustr__ZSebx;
+using google_breakpad::ustr__ZSebp;
+using google_breakpad::ustr__ZSedi;
+using google_breakpad::ustr__ZSesi;
+using google_breakpad::ustr__ZSesp;
 
 class TestCodeModule : public CodeModule {
  public:
   explicit TestCodeModule(string code_file) : code_file_(code_file) {}
   virtual ~TestCodeModule() {}
 
   virtual uint64_t base_address() const { return 0; }
   virtual uint64_t size() const { return 0xb000; }
@@ -119,34 +128,34 @@
 // association. (That is, ACTUAL's associations should be a subset of
 // EXPECTED's.) Also verify that ACTUAL has associations for ".ra" and
 // ".cfa".
 static bool VerifyRegisters(
     const char *file, int line,
     const CFIFrameInfo::RegisterValueMap<uint32_t> &expected,
     const CFIFrameInfo::RegisterValueMap<uint32_t> &actual) {
   CFIFrameInfo::RegisterValueMap<uint32_t>::const_iterator a;
-  a = actual.find(".cfa");
+  a = actual.find(ustr__ZDcfa());
   if (a == actual.end())
     return false;
-  a = actual.find(".ra");
+  a = actual.find(ustr__ZDra());
   if (a == actual.end())
     return false;
   for (a = actual.begin(); a != actual.end(); a++) {
     CFIFrameInfo::RegisterValueMap<uint32_t>::const_iterator e =
       expected.find(a->first);
     if (e == expected.end()) {
       fprintf(stderr, "%s:%d: unexpected register '%s' recovered, value 0x%x\n",
-              file, line, a->first.c_str(), a->second);
+              file, line, FromUniqueString(a->first), a->second);
       return false;
     }
     if (e->second != a->second) {
       fprintf(stderr,
               "%s:%d: register '%s' recovered value was 0x%x, expected 0x%x\n",
-              file, line, a->first.c_str(), a->second, e->second);
+              file, line, FromUniqueString(a->first), a->second, e->second);
       return false;
     }
     // Don't complain if this doesn't recover all registers. Although
     // the DWARF spec says that unmentioned registers are undefined,
     // GCC uses omission to mean that they are unchanged.
   }
   return true;
 }
@@ -282,81 +291,81 @@
 
   CFIFrameInfo::RegisterValueMap<uint32_t> current_registers;
   CFIFrameInfo::RegisterValueMap<uint32_t> caller_registers;
   CFIFrameInfo::RegisterValueMap<uint32_t> expected_caller_registers;
   MockMemoryRegion memory;
 
   // Regardless of which instruction evaluation takes place at, it
   // should produce the same values for the caller's registers.
-  expected_caller_registers[".cfa"] = 0x1001c;
-  expected_caller_registers[".ra"]  = 0xf6438648;
-  expected_caller_registers["$ebp"] = 0x10038;
-  expected_caller_registers["$ebx"] = 0x98ecadc3;
-  expected_caller_registers["$esi"] = 0x878f7524;
-  expected_caller_registers["$edi"] = 0x6312f9a5;
+  expected_caller_registers[ustr__ZDcfa()] = 0x1001c;
+  expected_caller_registers[ustr__ZDra()]  = 0xf6438648;
+  expected_caller_registers[ustr__ZSebp()] = 0x10038;
+  expected_caller_registers[ustr__ZSebx()] = 0x98ecadc3;
+  expected_caller_registers[ustr__ZSesi()] = 0x878f7524;
+  expected_caller_registers[ustr__ZSedi()] = 0x6312f9a5;
 
   frame.instruction = 0x3d40;
   frame.module = &module1;
   current_registers.clear();
-  current_registers["$esp"] = 0x10018;
-  current_registers["$ebp"] = 0x10038;
-  current_registers["$ebx"] = 0x98ecadc3;
-  current_registers["$esi"] = 0x878f7524;
-  current_registers["$edi"] = 0x6312f9a5;
+  current_registers[ustr__ZSesp()] = 0x10018;
+  current_registers[ustr__ZSebp()] = 0x10038;
+  current_registers[ustr__ZSebx()] = 0x98ecadc3;
+  current_registers[ustr__ZSesi()] = 0x878f7524;
+  current_registers[ustr__ZSedi()] = 0x6312f9a5;
   cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
   ASSERT_TRUE(cfi_frame_info.get());
   ASSERT_TRUE(cfi_frame_info.get()
               ->FindCallerRegs<uint32_t>(current_registers, memory,
                                           &caller_registers));
   ASSERT_TRUE(VerifyRegisters(__FILE__, __LINE__,
                               expected_caller_registers, caller_registers));
 
   frame.instruction = 0x3d41;
-  current_registers["$esp"] = 0x10014;
+  current_registers[ustr__ZSesp()] = 0x10014;
   cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
   ASSERT_TRUE(cfi_frame_info.get());
   ASSERT_TRUE(cfi_frame_info.get()
               ->FindCallerRegs<uint32_t>(current_registers, memory,
                                           &caller_registers));
   ASSERT_TRUE(VerifyRegisters(__FILE__, __LINE__,
                               expected_caller_registers, caller_registers));
 
   frame.instruction = 0x3d43;
-  current_registers["$ebp"] = 0x10014;
+  current_registers[ustr__ZSebp()] = 0x10014;
   cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
   ASSERT_TRUE(cfi_frame_info.get());
   ASSERT_TRUE(cfi_frame_info.get()
               ->FindCallerRegs<uint32_t>(current_registers, memory,
                                           &caller_registers));
   VerifyRegisters(__FILE__, __LINE__,
                   expected_caller_registers, caller_registers);
 
   frame.instruction = 0x3d54;
-  current_registers["$ebx"] = 0x6864f054U;
+  current_registers[ustr__ZSebx()] = 0x6864f054U;
   cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
   ASSERT_TRUE(cfi_frame_info.get());
   ASSERT_TRUE(cfi_frame_info.get()
               ->FindCallerRegs<uint32_t>(current_registers, memory,
                                           &caller_registers));
   VerifyRegisters(__FILE__, __LINE__,
                   expected_caller_registers, caller_registers);
 
   frame.instruction = 0x3d5a;
-  current_registers["$esi"] = 0x6285f79aU;
+  current_registers[ustr__ZSesi()] = 0x6285f79aU;
   cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
   ASSERT_TRUE(cfi_frame_info.get());
   ASSERT_TRUE(cfi_frame_info.get()
               ->FindCallerRegs<uint32_t>(current_registers, memory,
                                           &caller_registers));
   VerifyRegisters(__FILE__, __LINE__,
                   expected_caller_registers, caller_registers);
 
   frame.instruction = 0x3d84;
-  current_registers["$edi"] = 0x64061449U;
+  current_registers[ustr__ZSedi()] = 0x64061449U;
   cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
   ASSERT_TRUE(cfi_frame_info.get());
   ASSERT_TRUE(cfi_frame_info.get()
               ->FindCallerRegs<uint32_t>(current_registers, memory,
                                           &caller_registers));
   VerifyRegisters(__FILE__, __LINE__,
                   expected_caller_registers, caller_registers);
 
diff --git a/src/processor/postfix_evaluator-inl.h b/src/processor/postfix_evaluator-inl.h
--- a/src/processor/postfix_evaluator-inl.h
+++ b/src/processor/postfix_evaluator-inl.h
@@ -51,23 +51,25 @@
 namespace google_breakpad {
 
 using std::istringstream;
 using std::ostringstream;
 
 
 // A small class used in Evaluate to make sure to clean up the stack
 // before returning failure.
+template<typename ValueType>
 class AutoStackClearer {
  public:
-  explicit AutoStackClearer(vector<string> *stack) : stack_(stack) {}
+  explicit AutoStackClearer(vector<StackElem<ValueType> > *stack)
+    : stack_(stack) {}
   ~AutoStackClearer() { stack_->clear(); }
 
  private:
-  vector<string> *stack_;
+  vector<StackElem<ValueType> > *stack_;
 };
 
 
 template<typename ValueType>
 bool PostfixEvaluator<ValueType>::EvaluateToken(
     const string &token,
     const string &expression,
     DictionaryValidityType *assigned) {
@@ -170,38 +172,57 @@
       BPLOG(INFO) << "Could not PopValue to get value to assign: " <<
                      expression;
       return false;
     }
 
     // Assignment is only meaningful when assigning into an identifier.
     // The identifier must name a variable, not a constant.  Variables
     // begin with '$'.
-    string identifier;
+    const UniqueString* identifier;
     if (PopValueOrIdentifier(NULL, &identifier) != POP_RESULT_IDENTIFIER) {
       BPLOG(ERROR) << "PopValueOrIdentifier returned a value, but an "
                       "identifier is needed to assign " <<
                       HexString(value) << ": " << expression;
       return false;
     }
-    if (identifier.empty() || identifier[0] != '$') {
+    if (identifier == ustr__empty() || Index(identifier,0) != '$') {
       BPLOG(ERROR) << "Can't assign " << HexString(value) << " to " <<
                       identifier << ": " << expression;
       return false;
     }
 
     (*dictionary_)[identifier] = value;
     if (assigned)
       (*assigned)[identifier] = true;
   } else {
-    // The token is not an operator, it's a literal value or an identifier.
-    // Push it onto the stack as-is.  Use push_back instead of PushValue
-    // because PushValue pushes ValueType as a string, but token is already
-    // a string.
-    stack_.push_back(token);
+    // Push it onto the stack as-is, but first convert it either to a
+    // ValueType (if a literal) or to a UniqueString* (if an identifier).
+    //
+    // First, try to treat the value as a literal. Literals may have leading
+    // '-' sign, and the entire remaining string must be parseable as
+    // ValueType. If this isn't possible, it can't be a literal, so treat it
+    // as an identifier instead.
+    //
+    // Some versions of the libstdc++, the GNU standard C++ library, have
+    // stream extractors for unsigned integer values that permit a leading
+    // '-' sign (6.0.13); others do not (6.0.9). Since we require it, we
+    // handle it explicitly here.
+    istringstream token_stream(token);
+    ValueType literal = ValueType();
+    bool negative = false;
+    if (token_stream.peek() == '-') {
+      negative = true;
+      token_stream.get();
+    }
+    if (token_stream >> literal && token_stream.peek() == EOF) {
+      PushValue(negative ? (-literal) : literal);
+    } else {
+      PushIdentifier(ToUniqueString(token));
+    }
   }
   return true;
 }
 
 template<typename ValueType>
 bool PostfixEvaluator<ValueType>::EvaluateInternal(
     const string &expression,
     DictionaryValidityType *assigned) {
@@ -236,17 +257,17 @@
   // The expression is being exevaluated only for its side effects.  Skip
   // expressions that denote values only.
   if (expr.how_ != Module::kExprPostfix) {
     BPLOG(ERROR) << "Can't evaluate for side-effects: " << expr;
     return false;
   }
 
   // Ensure that the stack is cleared before returning.
-  AutoStackClearer clearer(&stack_);
+  AutoStackClearer<ValueType> clearer(&stack_);
 
   if (!EvaluateInternal(expr.postfix_, assigned))
     return false;
 
   // If there's anything left on the stack, it indicates incomplete execution.
   // This is a failure case.  If the stack is empty, evalution was complete
   // and successful.
   if (stack_.empty())
@@ -260,17 +281,17 @@
 bool PostfixEvaluator<ValueType>::EvaluateForValue(const Module::Expr& expr,
                                                    ValueType* result) {
   switch (expr.how_) {
 
     // Postfix expression.  Give to the evaluator and return the
     // one-and-only stack element that should be left over.
     case Module::kExprPostfix: {
       // Ensure that the stack is cleared before returning.
-      AutoStackClearer clearer(&stack_);
+      AutoStackClearer<ValueType> clearer(&stack_);
 
       if (!EvaluateInternal(expr.postfix_, NULL))
         return false;
 
       // A successful execution should leave exactly one value on the stack.
       if (stack_.size() != 1) {
         BPLOG(ERROR) << "Expression yielded bad number of results: "
                      << "'" << expr << "'";
@@ -314,77 +335,56 @@
       return false;
   }
 }
 
 
 template<typename ValueType>
 typename PostfixEvaluator<ValueType>::PopResult
 PostfixEvaluator<ValueType>::PopValueOrIdentifier(
-    ValueType *value, string *identifier) {
+    ValueType *value, const UniqueString** identifier) {
   // There needs to be at least one element on the stack to pop.
   if (!stack_.size())
     return POP_RESULT_FAIL;
 
-  string token = stack_.back();
+  StackElem<ValueType> el = stack_.back();
   stack_.pop_back();
 
-  // First, try to treat the value as a literal. Literals may have leading
-  // '-' sign, and the entire remaining string must be parseable as
-  // ValueType. If this isn't possible, it can't be a literal, so treat it
-  // as an identifier instead.
-  //
-  // Some versions of the libstdc++, the GNU standard C++ library, have
-  // stream extractors for unsigned integer values that permit a leading
-  // '-' sign (6.0.13); others do not (6.0.9). Since we require it, we
-  // handle it explicitly here.
-  istringstream token_stream(token);
-  ValueType literal = ValueType();
-  bool negative;
-  if (token_stream.peek() == '-') {
-    negative = true;
-    token_stream.get();
-  } else {
-    negative = false;
-  }
-  if (token_stream >> literal && token_stream.peek() == EOF) {
-    if (value) {
-      *value = literal;
-    }
-    if (negative)
-      *value = -*value;
+  if (el.isValue) {
+    if (value)
+      *value = el.u.val;
     return POP_RESULT_VALUE;
   } else {
-    if (identifier) {
-      *identifier = token;
-    }
+    if (identifier)
+      *identifier = el.u.ustr;
     return POP_RESULT_IDENTIFIER;
   }
 }
 
 
 template<typename ValueType>
 bool PostfixEvaluator<ValueType>::PopValue(ValueType *value) {
   ValueType literal = ValueType();
-  string token;
+  const UniqueString* token;
   PopResult result;
   if ((result = PopValueOrIdentifier(&literal, &token)) == POP_RESULT_FAIL) {
     return false;
   } else if (result == POP_RESULT_VALUE) {
     // This is the easy case.
     *value = literal;
   } else {  // result == POP_RESULT_IDENTIFIER
     // There was an identifier at the top of the stack.  Resolve it to a
     // value by looking it up in the dictionary.
     typename DictionaryType::const_iterator iterator =
         dictionary_->find(token);
     if (iterator == dictionary_->end()) {
       // The identifier wasn't found in the dictionary.  Don't imply any
       // default value, just fail.
-      BPLOG(INFO) << "Identifier " << token << " not in dictionary";
+      BPLOG(INFO) << "Identifier " << FromUniqueString(token)
+                  << " not in dictionary";
       return false;
     }
 
     *value = iterator->second;
   }
 
   return true;
 }
@@ -394,18 +394,23 @@
 bool PostfixEvaluator<ValueType>::PopValues(ValueType *value1,
                                             ValueType *value2) {
   return PopValue(value2) && PopValue(value1);
 }
 
 
 template<typename ValueType>
 void PostfixEvaluator<ValueType>::PushValue(const ValueType &value) {
-  ostringstream token_stream;
-  token_stream << value;
-  stack_.push_back(token_stream.str());
+  StackElem<ValueType> el(value);
+  stack_.push_back(el);
+}
+
+template<typename ValueType>
+void PostfixEvaluator<ValueType>::PushIdentifier(const UniqueString* str) {
+  StackElem<ValueType> el(str);
+  stack_.push_back(el);
 }
 
 
 }  // namespace google_breakpad
 
 
 #endif  // PROCESSOR_POSTFIX_EVALUATOR_INL_H__
diff --git a/src/processor/postfix_evaluator.h b/src/processor/postfix_evaluator.h
--- a/src/processor/postfix_evaluator.h
+++ b/src/processor/postfix_evaluator.h
@@ -70,30 +70,41 @@
 #define PROCESSOR_POSTFIX_EVALUATOR_H__
 
 
 #include <map>
 #include <string>
 #include <vector>
 
 #include "common/using_std_string.h"
+#include "common/unique_string.h"
 #include "common/module.h"
 
 namespace google_breakpad {
 
 using std::map;
 using std::vector;
 
 class MemoryRegion;
 
+// A union type for elements in the postfix evaluator's stack.
+template<typename ValueType>
+class StackElem {
+ public:
+  StackElem(ValueType val) { isValue = true; u.val = val; }
+  StackElem(const UniqueString* ustr) { isValue = false; u.ustr = ustr; }
+  bool isValue;
+  union { ValueType val; const UniqueString* ustr; } u;
+};
+
 template<typename ValueType>
 class PostfixEvaluator {
  public:
-  typedef map<string, ValueType> DictionaryType;
-  typedef map<string, bool> DictionaryValidityType;
+  typedef map<const UniqueString*, ValueType> DictionaryType;
+  typedef map<const UniqueString*, bool> DictionaryValidityType;
 
   // Create a PostfixEvaluator object that may be used (with Evaluate) on
   // one or more expressions.  PostfixEvaluator does not take ownership of
   // either argument.  |memory| may be NULL, in which case dereferencing
   // (^) will not be supported.  |dictionary| may be NULL, but evaluation
   // will fail in that case unless set_dictionary is used before calling
   // Evaluate.
   PostfixEvaluator(DictionaryType *dictionary, const MemoryRegion *memory)
@@ -128,24 +139,28 @@
   };
 
   // Retrieves the topmost literal value, constant, or variable from the
   // stack.  Returns POP_RESULT_VALUE if the topmost entry is a literal
   // value, and sets |value| accordingly.  Returns POP_RESULT_IDENTIFIER
   // if the topmost entry is a constant or variable identifier, and sets
   // |identifier| accordingly.  Returns POP_RESULT_FAIL on failure, such
   // as when the stack is empty.
-  PopResult PopValueOrIdentifier(ValueType *value, string *identifier);
+  PopResult PopValueOrIdentifier(ValueType *value,
+                                 const UniqueString** identifier);
 
   // Retrieves the topmost value on the stack.  If the topmost entry is
   // an identifier, the dictionary is queried for the identifier's value.
   // Returns false on failure, such as when the stack is empty or when
   // a nonexistent identifier is named.
   bool PopValue(ValueType *value);
 
+  // Pushes a UniqueString* on the stack.
+  void PushIdentifier(const UniqueString* ustr);
+
   // Retrieves the top two values on the stack, in the style of PopValue.
   // value2 is popped before value1, so that value1 corresponds to the
   // entry that was pushed prior to value2.  Returns false on failure.
   bool PopValues(ValueType *value1, ValueType *value2);
 
   // Pushes a new value onto the stack.
   void PushValue(const ValueType &value);
 
@@ -166,15 +181,15 @@
 
   // If non-NULL, the MemoryRegion used for dereference (^) operations.
   // If NULL, dereferencing is unsupported and will fail.  Weak pointer.
   const MemoryRegion *memory_;
 
   // The stack contains state information as execution progresses.  Values
   // are pushed on to it as the expression string is read and as operations
   // yield values; values are popped when used as operands to operators.
-  vector<string> stack_;
+  vector<StackElem<ValueType> > stack_;
 };
 
 }  // namespace google_breakpad
 
 
 #endif  // PROCESSOR_POSTFIX_EVALUATOR_H__
diff --git a/src/processor/postfix_evaluator_unittest.cc b/src/processor/postfix_evaluator_unittest.cc
--- a/src/processor/postfix_evaluator_unittest.cc
+++ b/src/processor/postfix_evaluator_unittest.cc
@@ -43,18 +43,32 @@
 #include "google_breakpad/processor/memory_region.h"
 #include "processor/logging.h"
 
 
 namespace {
 
 
 using std::map;
+using google_breakpad::FromUniqueString;
 using google_breakpad::MemoryRegion;
 using google_breakpad::PostfixEvaluator;
+using google_breakpad::ToUniqueString;
+using google_breakpad::UniqueString;
+using google_breakpad::ustr__ZDcbParams;
+using google_breakpad::ustr__ZDcbSavedRegs;
+using google_breakpad::ustr__ZDcfa;
+using google_breakpad::ustr__ZDra;
+using google_breakpad::ustr__ZDraSearchStart;
+using google_breakpad::ustr__ZSebx;
+using google_breakpad::ustr__ZSebp;
+using google_breakpad::ustr__ZSedi;
+using google_breakpad::ustr__ZSeip;
+using google_breakpad::ustr__ZSesi;
+using google_breakpad::ustr__ZSesp;
 
 
 // FakeMemoryRegion is used to test PostfixEvaluator's dereference (^)
 // operator.  The result of dereferencing a value is one greater than
 // the value.
 class FakeMemoryRegion : public MemoryRegion {
  public:
   virtual uint64_t GetBase() const { return 0; }
@@ -95,17 +109,17 @@
   // The list of tests.
   const EvaluateTest *evaluate_tests;
 
   // The number of tests.
   unsigned int evaluate_test_count;
 
   // Identifiers and their expected values upon completion of the Evaluate
   // tests in the set.
-  map<string, unsigned int> *validate_data;
+  map<const UniqueString*, unsigned int> *validate_data;
 };
 
 
 struct EvaluateForValueTest {
   // Expression passed to PostfixEvaluator::Evaluate.
   const string expression;
   
   // True if the expression is expected to be evaluable, false if evaluation
@@ -147,39 +161,39 @@
     { "$rMul 9 6 * =",     true },   // $rMul = 9 * 6 = 54
     { "$rSub 9 6 - =",     true },   // $rSub = 9 - 6 = 3
     { "$rDivQ 9 6 / =",    true },   // $rDivQ = 9 / 6 = 1
     { "$rDivM 9 6 % =",    true },   // $rDivM = 9 % 6 = 3
     { "$rDeref 9 ^ =",     true },   // $rDeref = ^9 = 10 (FakeMemoryRegion)
     { "$rAlign 36 8 @ =",  true },   // $rAlign = 36 @ 8
     { "$rAdd3 2 2 + =$rMul2 9 6 * =", true } // smashed-equals tokenization
   };
-  map<string, unsigned int> validate_data_0;
-  validate_data_0["$rAdd"]   = 8;
-  validate_data_0["$rAdd2"]  = 4;
-  validate_data_0["$rSub"]   = 3;
-  validate_data_0["$rMul"]   = 54;
-  validate_data_0["$rDivQ"]  = 1;
-  validate_data_0["$rDivM"]  = 3;
-  validate_data_0["$rDeref"] = 10;
-  validate_data_0["$rAlign"] = 32;
-  validate_data_0["$rAdd3"]  = 4;
-  validate_data_0["$rMul2"]  = 54;
+  map<const UniqueString*, unsigned int> validate_data_0;
+  validate_data_0[ToUniqueString("$rAdd")]   = 8;
+  validate_data_0[ToUniqueString("$rAdd2")]  = 4;
+  validate_data_0[ToUniqueString("$rSub")]   = 3;
+  validate_data_0[ToUniqueString("$rMul")]   = 54;
+  validate_data_0[ToUniqueString("$rDivQ")]  = 1;
+  validate_data_0[ToUniqueString("$rDivM")]  = 3;
+  validate_data_0[ToUniqueString("$rDeref")] = 10;
+  validate_data_0[ToUniqueString("$rAlign")] = 32;
+  validate_data_0[ToUniqueString("$rAdd3")]  = 4;
+  validate_data_0[ToUniqueString("$rMul2")]  = 54;
 
   // The second test set simulates a couple of MSVC program strings.
   // The data is fudged a little bit because the tests use FakeMemoryRegion
   // instead of a real stack snapshot, but the program strings are real and
   // the implementation doesn't know or care that the data is not real.
   PostfixEvaluator<unsigned int>::DictionaryType dictionary_1;
-  dictionary_1["$ebp"] = 0xbfff0010;
-  dictionary_1["$eip"] = 0x10000000;
-  dictionary_1["$esp"] = 0xbfff0000;
-  dictionary_1[".cbSavedRegs"] = 4;
-  dictionary_1[".cbParams"] = 4;
-  dictionary_1[".raSearchStart"] = 0xbfff0020;
+  dictionary_1[ustr__ZSebp()] = 0xbfff0010;
+  dictionary_1[ustr__ZSeip()] = 0x10000000;
+  dictionary_1[ustr__ZSesp()] = 0xbfff0000;
+  dictionary_1[ustr__ZDcbSavedRegs()] = 4;
+  dictionary_1[ustr__ZDcbParams()] = 4;
+  dictionary_1[ustr__ZDraSearchStart()] = 0xbfff0020;
   const EvaluateTest evaluate_tests_1[] = {
     { "$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = "
       "$L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =", true },
     // Intermediate state: $T0  = 0xbfff0010, $eip = 0xbfff0015,
     //                     $ebp = 0xbfff0011, $esp = 0xbfff0018,
     //                     $L   = 0xbfff000c, $P   = 0xbfff001c
     { "$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = "
       "$L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + = $ebx $T0 28 - ^ =",
@@ -188,28 +202,28 @@
     //                     $ebp = 0xbfff0012, $esp = 0xbfff0019,
     //                     $L   = 0xbfff000d, $P   = 0xbfff001d,
     //                     $ebx = 0xbffefff6
     { "$T0 $ebp = $T2 $esp = $T1 .raSearchStart = $eip $T1 ^ = $ebp $T0 = "
       "$esp $T1 4 + = $L $T0 .cbSavedRegs - = $P $T1 4 + .cbParams + = "
       "$ebx $T0 28 - ^ =",
       true }
   };
-  map<string, unsigned int> validate_data_1;
-  validate_data_1["$T0"]  = 0xbfff0012;
-  validate_data_1["$T1"]  = 0xbfff0020;
-  validate_data_1["$T2"]  = 0xbfff0019;
-  validate_data_1["$eip"] = 0xbfff0021;
-  validate_data_1["$ebp"] = 0xbfff0012;
-  validate_data_1["$esp"] = 0xbfff0024;
-  validate_data_1["$L"]   = 0xbfff000e;
-  validate_data_1["$P"]   = 0xbfff0028;
-  validate_data_1["$ebx"] = 0xbffefff7;
-  validate_data_1[".cbSavedRegs"] = 4;
-  validate_data_1[".cbParams"] = 4;
+  map<const UniqueString*, unsigned int> validate_data_1;
+  validate_data_1[ToUniqueString("$T0")]  = 0xbfff0012;
+  validate_data_1[ToUniqueString("$T1")]  = 0xbfff0020;
+  validate_data_1[ToUniqueString("$T2")]  = 0xbfff0019;
+  validate_data_1[ustr__ZSeip()] = 0xbfff0021;
+  validate_data_1[ustr__ZSebp()] = 0xbfff0012;
+  validate_data_1[ustr__ZSesp()] = 0xbfff0024;
+  validate_data_1[ToUniqueString("$L")]   = 0xbfff000e;
+  validate_data_1[ToUniqueString("$P")]   = 0xbfff0028;
+  validate_data_1[ustr__ZSebx()] = 0xbffefff7;
+  validate_data_1[ustr__ZDcbSavedRegs()] = 4;
+  validate_data_1[ustr__ZDcbParams()] = 4;
 
   EvaluateTestSet evaluate_test_sets[] = {
     { &dictionary_0, evaluate_tests_0,
           sizeof(evaluate_tests_0) / sizeof(EvaluateTest), &validate_data_0 },
     { &dictionary_1, evaluate_tests_1,
           sizeof(evaluate_tests_1) / sizeof(EvaluateTest), &validate_data_1 },
   };
 
@@ -251,97 +265,100 @@
                 evaluate_test->expression.c_str(),
                 evaluate_test->evaluable ? "evaluable" : "not evaluable",
                 result ? "evaluted" : "not evaluated");
         return false;
       }
     }
 
     // Validate the results.
-    for (map<string, unsigned int>::const_iterator validate_iterator =
+    for (map<const UniqueString*, unsigned int>::const_iterator
+            validate_iterator =
             evaluate_test_set->validate_data->begin();
         validate_iterator != evaluate_test_set->validate_data->end();
         ++validate_iterator) {
-      const string identifier = validate_iterator->first;
+      const UniqueString* identifier = validate_iterator->first;
       unsigned int expected_value = validate_iterator->second;
 
-      map<string, unsigned int>::const_iterator dictionary_iterator =
+      map<const UniqueString*, unsigned int>::const_iterator
+          dictionary_iterator =
           evaluate_test_set->dictionary->find(identifier);
 
       // The identifier must exist in the dictionary.
       if (dictionary_iterator == evaluate_test_set->dictionary->end()) {
         fprintf(stderr, "FAIL: evaluate test set %d/%d, "
                         "validate identifier \"%s\", "
                         "expected %d, observed not found\n",
                 evaluate_test_set_index, evaluate_test_set_count,
-                identifier.c_str(), expected_value);
+                FromUniqueString(identifier), expected_value);
         return false;
       }
 
       // The value in the dictionary must be the same as the expected value.
       unsigned int observed_value = dictionary_iterator->second;
       if (expected_value != observed_value) {
         fprintf(stderr, "FAIL: evaluate test set %d/%d, "
                         "validate identifier \"%s\", "
                         "expected %d, observed %d\n",
                 evaluate_test_set_index, evaluate_test_set_count,
-                identifier.c_str(), expected_value, observed_value);
+                FromUniqueString(identifier), expected_value, observed_value);
         return false;
       }
 
       // The value must be set in the "assigned" dictionary if it was a
       // variable.  It must not have been assigned if it was a constant.
-      bool expected_assigned = identifier[0] == '$';
+      bool expected_assigned = FromUniqueString(identifier)[0] == '$';
       bool observed_assigned = false;
       PostfixEvaluator<unsigned int>::DictionaryValidityType::const_iterator
           iterator_assigned = assigned.find(identifier);
       if (iterator_assigned != assigned.end()) {
         observed_assigned = iterator_assigned->second;
       }
       if (expected_assigned != observed_assigned) {
         fprintf(stderr, "FAIL: evaluate test set %d/%d, "
                         "validate assignment of \"%s\", "
                         "expected %d, observed %d\n",
                 evaluate_test_set_index, evaluate_test_set_count,
-                identifier.c_str(), expected_assigned, observed_assigned);
+                FromUniqueString(identifier), expected_assigned,
+                observed_assigned);
         return false;
       }
     }
   }
 
   // EvaluateForValue tests.
   PostfixEvaluator<unsigned int>::DictionaryType dictionary_2;
-  dictionary_2["$ebp"] = 0xbfff0010;
-  dictionary_2["$eip"] = 0x10000000;
-  dictionary_2["$esp"] = 0xbfff0000;
-  dictionary_2[".cbSavedRegs"] = 4;
-  dictionary_2[".cbParams"] = 4;
-  dictionary_2[".raSearchStart"] = 0xbfff0020;
+  dictionary_2[ustr__ZSebp()] = 0xbfff0010;
+  dictionary_2[ustr__ZSeip()] = 0x10000000;
+  dictionary_2[ustr__ZSesp()] = 0xbfff0000;
+  dictionary_2[ustr__ZDcbSavedRegs()] = 4;
+  dictionary_2[ustr__ZDcbParams()] = 4;
+  dictionary_2[ustr__ZDraSearchStart()] = 0xbfff0020;
   const EvaluateForValueTest evaluate_for_value_tests_2[] = {
     { "28907223",               true,  28907223 },      // simple constant
     { "89854293 40010015 +",    true,  89854293 + 40010015 }, // arithmetic
     { "-870245 8769343 +",      true,  7899098 },       // negative constants
     { "$ebp $esp - $eip +",     true,  0x10000010 },    // variable references
     { "18929794 34015074",      false, 0 },             // too many values
     { "$ebp $ebp 4 - =",        false, 0 },             // too few values
     { "$new $eip = $new",       true,  0x10000000 },    // make new variable
     { "$new 4 +",               true,  0x10000004 },    // see prior assignments
     { ".cfa 42 = 10",           false, 0 }              // can't set constants
   };
   const int evaluate_for_value_tests_2_size
       = (sizeof (evaluate_for_value_tests_2)
          / sizeof (evaluate_for_value_tests_2[0]));
-  map<string, unsigned int> validate_data_2;
-  validate_data_2["$eip"] = 0x10000000;
-  validate_data_2["$ebp"] = 0xbfff000c;
-  validate_data_2["$esp"] = 0xbfff0000;
-  validate_data_2["$new"] = 0x10000000;
-  validate_data_2[".cbSavedRegs"] = 4;
-  validate_data_2[".cbParams"] = 4;
-  validate_data_2[".raSearchStart"] = 0xbfff0020;
+  map<const UniqueString*, unsigned int> validate_data_2;
+  validate_data_2[ustr__ZSeip()] = 0x10000000;
+  validate_data_2[ustr__ZSebp()] = 0xbfff000c;
+  validate_data_2[ustr__ZSesp()] = 0xbfff0000;
+  validate_data_2[ToUniqueString("$new")] = 0x10000000;
+  validate_data_2[ustr__ZDcbSavedRegs()] = 4;
+  validate_data_2[ustr__ZDcbParams()] = 4;
+  validate_data_2[ustr__ZDraSearchStart()] = 0xbfff0020;
 
   postfix_evaluator.set_dictionary(&dictionary_2);
   for (int i = 0; i < evaluate_for_value_tests_2_size; i++) {
     const EvaluateForValueTest *test = &evaluate_for_value_tests_2[i];
     unsigned int result;
     if (postfix_evaluator.EvaluateForValue(test->expression, &result)
         != test->evaluable) {
       fprintf(stderr, "FAIL: evaluate for value test %d, "
@@ -353,40 +370,43 @@
     if (test->evaluable && result != test->value) {
       fprintf(stderr, "FAIL: evaluate for value test %d, "
               "expected value to be 0x%x, but it was 0x%x\n",
               i, test->value, result);
       return false;
     }
   }
 
-  for (map<string, unsigned int>::iterator v = validate_data_2.begin();
+  for (map<const UniqueString*, unsigned int>::iterator v =
+         validate_data_2.begin();
        v != validate_data_2.end(); v++) {
-    map<string, unsigned int>::iterator a = dictionary_2.find(v->first);
+    map<const UniqueString*, unsigned int>::iterator a =
+        dictionary_2.find(v->first);
     if (a == dictionary_2.end()) {
       fprintf(stderr, "FAIL: evaluate for value dictionary check: "
               "expected dict[\"%s\"] to be 0x%x, but it was unset\n",
-              v->first.c_str(), v->second);
+              FromUniqueString(v->first), v->second);
       return false;
     } else if (a->second != v->second) {
       fprintf(stderr, "FAIL: evaluate for value dictionary check: "
               "expected dict[\"%s\"] to be 0x%x, but it was 0x%x\n",
-              v->first.c_str(), v->second, a->second);
+              FromUniqueString(v->first), v->second, a->second);
       return false;
     } 
     dictionary_2.erase(a);
   }
   
-  map<string, unsigned int>::iterator remaining = dictionary_2.begin();
+  map<const UniqueString*, unsigned int>::iterator remaining =
+      dictionary_2.begin();
   if (remaining != dictionary_2.end()) {
     fprintf(stderr, "FAIL: evaluation of test expressions put unexpected "
             "values in dictionary:\n");
     for (; remaining != dictionary_2.end(); remaining++)
       fprintf(stderr, "    dict[\"%s\"] == 0x%x\n",
-              remaining->first.c_str(), remaining->second);
+              FromUniqueString(remaining->first), remaining->second);
     return false;
   }
 
   return true;
 }
 
 
 }  // namespace
diff --git a/src/processor/stackwalker_amd64.cc b/src/processor/stackwalker_amd64.cc
--- a/src/processor/stackwalker_amd64.cc
+++ b/src/processor/stackwalker_amd64.cc
@@ -50,49 +50,49 @@
 
 const StackwalkerAMD64::CFIWalker::RegisterSet
 StackwalkerAMD64::cfi_register_map_[] = {
   // It may seem like $rip and $rsp are callee-saves, because the callee is
   // responsible for having them restored upon return. But the callee_saves
   // flags here really means that the walker should assume they're
   // unchanged if the CFI doesn't mention them --- clearly wrong for $rip
   // and $rsp.
-  { "$rax", NULL, false,
+  { ToUniqueString("$rax"), NULL, false,
     StackFrameAMD64::CONTEXT_VALID_RAX, &MDRawContextAMD64::rax },
-  { "$rdx", NULL, false,
+  { ToUniqueString("$rdx"), NULL, false,
     StackFrameAMD64::CONTEXT_VALID_RDX, &MDRawContextAMD64::rdx },
-  { "$rcx", NULL, false,
+  { ToUniqueString("$rcx"), NULL, false,
     StackFrameAMD64::CONTEXT_VALID_RCX, &MDRawContextAMD64::rcx },
-  { "$rbx", NULL, true,
+  { ToUniqueString("$rbx"), NULL, true,
     StackFrameAMD64::CONTEXT_VALID_RBX, &MDRawContextAMD64::rbx },
-  { "$rsi", NULL, false,
+  { ToUniqueString("$rsi"), NULL, false,
     StackFrameAMD64::CONTEXT_VALID_RSI, &MDRawContextAMD64::rsi },
-  { "$rdi", NULL, false,
+  { ToUniqueString("$rdi"), NULL, false,
     StackFrameAMD64::CONTEXT_VALID_RDI, &MDRawContextAMD64::rdi },
-  { "$rbp", NULL, true,
+  { ToUniqueString("$rbp"), NULL, true,
     StackFrameAMD64::CONTEXT_VALID_RBP, &MDRawContextAMD64::rbp },
-  { "$rsp", ".cfa", false,
+  { ToUniqueString("$rsp"), ToUniqueString(".cfa"), false,
     StackFrameAMD64::CONTEXT_VALID_RSP, &MDRawContextAMD64::rsp },
-  { "$r8", NULL, false,
+  { ToUniqueString("$r8"), NULL, false,
     StackFrameAMD64::CONTEXT_VALID_R8,  &MDRawContextAMD64::r8 },
-  { "$r9", NULL, false,
+  { ToUniqueString("$r9"), NULL, false,
     StackFrameAMD64::CONTEXT_VALID_R9,  &MDRawContextAMD64::r9 },
-  { "$r10", NULL, false,
+  { ToUniqueString("$r10"), NULL, false,
     StackFrameAMD64::CONTEXT_VALID_R10, &MDRawContextAMD64::r10 },
-  { "$r11", NULL, false,
+  { ToUniqueString("$r11"), NULL, false,
     StackFrameAMD64::CONTEXT_VALID_R11, &MDRawContextAMD64::r11 },
-  { "$r12", NULL, true,
+  { ToUniqueString("$r12"), NULL, true,
     StackFrameAMD64::CONTEXT_VALID_R12, &MDRawContextAMD64::r12 },
-  { "$r13", NULL, true,
+  { ToUniqueString("$r13"), NULL, true,
     StackFrameAMD64::CONTEXT_VALID_R13, &MDRawContextAMD64::r13 },
-  { "$r14", NULL, true,
+  { ToUniqueString("$r14"), NULL, true,
     StackFrameAMD64::CONTEXT_VALID_R14, &MDRawContextAMD64::r14 },
-  { "$r15", NULL, true,
+  { ToUniqueString("$r15"), NULL, true,
     StackFrameAMD64::CONTEXT_VALID_R15, &MDRawContextAMD64::r15 },
-  { "$rip", ".ra", false,
+  { ToUniqueString("$rip"), ToUniqueString(".ra"), false,
     StackFrameAMD64::CONTEXT_VALID_RIP, &MDRawContextAMD64::rip },
 };
 
 StackwalkerAMD64::StackwalkerAMD64(const SystemInfo* system_info,
                                    const MDRawContextAMD64* context,
                                    MemoryRegion* memory,
                                    const CodeModules* modules,
                                    StackFrameSymbolizer* resolver_helper)
diff --git a/src/processor/stackwalker_arm.cc b/src/processor/stackwalker_arm.cc
--- a/src/processor/stackwalker_arm.cc
+++ b/src/processor/stackwalker_arm.cc
@@ -76,21 +76,30 @@
   return frame;
 }
 
 StackFrameARM* StackwalkerARM::GetCallerByCFIFrameInfo(
     const vector<StackFrame*> &frames,
     CFIFrameInfo* cfi_frame_info) {
   StackFrameARM* last_frame = static_cast<StackFrameARM*>(frames.back());
 
-  static const char* register_names[] = {
-    "r0",  "r1",  "r2",  "r3",  "r4",  "r5",  "r6",  "r7",
-    "r8",  "r9",  "r10", "r11", "r12", "sp",  "lr",  "pc",
-    "f0",  "f1",  "f2",  "f3",  "f4",  "f5",  "f6",  "f7",
-    "fps", "cpsr",
+  static const UniqueString *register_names[] = {
+    ToUniqueString("r0"),  ToUniqueString("r1"),
+    ToUniqueString("r2"),  ToUniqueString("r3"),
+    ToUniqueString("r4"),  ToUniqueString("r5"),
+    ToUniqueString("r6"),  ToUniqueString("r7"),
+    ToUniqueString("r8"),  ToUniqueString("r9"),
+    ToUniqueString("r10"), ToUniqueString("r11"),
+    ToUniqueString("r12"), ToUniqueString("sp"),
+    ToUniqueString("lr"),  ToUniqueString("pc"),
+    ToUniqueString("f0"),  ToUniqueString("f1"),
+    ToUniqueString("f2"),  ToUniqueString("f3"),
+    ToUniqueString("f4"),  ToUniqueString("f5"),
+    ToUniqueString("f6"),  ToUniqueString("f7"),
+    ToUniqueString("fps"), ToUniqueString("cpsr"),
     NULL
   };
 
   // Populate a dictionary with the valid register values in last_frame.
   CFIFrameInfo::RegisterValueMap<uint32_t> callee_registers;
   for (int i = 0; register_names[i]; i++)
     if (last_frame->context_validity & StackFrameARM::RegisterValidFlag(i))
       callee_registers[register_names[i]] = last_frame->context.iregs[i];
@@ -119,17 +128,17 @@
       // Call Standard for the ARM Architecture, which the Linux ABI follows.
       frame->context_validity |= StackFrameARM::RegisterValidFlag(i);
       frame->context.iregs[i] = last_frame->context.iregs[i];
     }
   }
   // If the CFI doesn't recover the PC explicitly, then use .ra.
   if (!(frame->context_validity & StackFrameARM::CONTEXT_VALID_PC)) {
     CFIFrameInfo::RegisterValueMap<uint32_t>::iterator entry =
-      caller_registers.find(".ra");
+      caller_registers.find(ustr__ZDra());
     if (entry != caller_registers.end()) {
       if (fp_register_ == -1) {
         frame->context_validity |= StackFrameARM::CONTEXT_VALID_PC;
         frame->context.iregs[MD_CONTEXT_ARM_REG_PC] = entry->second;
       } else {
         // The CFI updated the link register and not the program counter.
         // Handle getting the program counter from the link register.
         frame->context_validity |= StackFrameARM::CONTEXT_VALID_PC;
@@ -138,17 +147,17 @@
         frame->context.iregs[MD_CONTEXT_ARM_REG_PC] =
             last_frame->context.iregs[MD_CONTEXT_ARM_REG_LR];
       }
     }
   }
   // If the CFI doesn't recover the SP explicitly, then use .cfa.
   if (!(frame->context_validity & StackFrameARM::CONTEXT_VALID_SP)) {
     CFIFrameInfo::RegisterValueMap<uint32_t>::iterator entry =
-      caller_registers.find(".cfa");
+      caller_registers.find(ustr__ZDcfa());
     if (entry != caller_registers.end()) {
       frame->context_validity |= StackFrameARM::CONTEXT_VALID_SP;
       frame->context.iregs[MD_CONTEXT_ARM_REG_SP] = entry->second;
     }
   }
 
   // If we didn't recover the PC and the SP, then the frame isn't very useful.
   static const int essentials = (StackFrameARM::CONTEXT_VALID_SP
diff --git a/src/processor/stackwalker_x86.cc b/src/processor/stackwalker_x86.cc
--- a/src/processor/stackwalker_x86.cc
+++ b/src/processor/stackwalker_x86.cc
@@ -53,33 +53,33 @@
 
 const StackwalkerX86::CFIWalker::RegisterSet
 StackwalkerX86::cfi_register_map_[] = {
   // It may seem like $eip and $esp are callee-saves, because (with Unix or
   // cdecl calling conventions) the callee is responsible for having them
   // restored upon return. But the callee_saves flags here really means
   // that the walker should assume they're unchanged if the CFI doesn't
   // mention them, which is clearly wrong for $eip and $esp.
-  { "$eip", ".ra",  false,
+  { ToUniqueString("$eip"), ToUniqueString(".ra"),  false,
     StackFrameX86::CONTEXT_VALID_EIP, &MDRawContextX86::eip },
-  { "$esp", ".cfa", false,
+  { ToUniqueString("$esp"), ToUniqueString(".cfa"), false,
     StackFrameX86::CONTEXT_VALID_ESP, &MDRawContextX86::esp },
-  { "$ebp", NULL,   true,
+  { ToUniqueString("$ebp"), NULL,   true,
     StackFrameX86::CONTEXT_VALID_EBP, &MDRawContextX86::ebp },
-  { "$eax", NULL,   false,
+  { ToUniqueString("$eax"), NULL,   false,
     StackFrameX86::CONTEXT_VALID_EAX, &MDRawContextX86::eax },
-  { "$ebx", NULL,   true,
+  { ToUniqueString("$ebx"), NULL,   true,
     StackFrameX86::CONTEXT_VALID_EBX, &MDRawContextX86::ebx },
-  { "$ecx", NULL,   false,
+  { ToUniqueString("$ecx"), NULL,   false,
     StackFrameX86::CONTEXT_VALID_ECX, &MDRawContextX86::ecx },
-  { "$edx", NULL,   false,
+  { ToUniqueString("$edx"), NULL,   false,
     StackFrameX86::CONTEXT_VALID_EDX, &MDRawContextX86::edx },
-  { "$esi", NULL,   true,
+  { ToUniqueString("$esi"), NULL,   true,
     StackFrameX86::CONTEXT_VALID_ESI, &MDRawContextX86::esi },
-  { "$edi", NULL,   true,
+  { ToUniqueString("$edi"), NULL,   true,
     StackFrameX86::CONTEXT_VALID_EDI, &MDRawContextX86::edi },
 };
 
 StackwalkerX86::StackwalkerX86(const SystemInfo* system_info,
                                const MDRawContextX86* context,
                                MemoryRegion* memory,
                                const CodeModules* modules,
                                StackFrameSymbolizer* resolver_helper)
@@ -194,26 +194,26 @@
     }
   }
 
   // Set up the dictionary for the PostfixEvaluator.  %ebp and %esp are used
   // in each program string, and their previous values are known, so set them
   // here.
   PostfixEvaluator<uint32_t>::DictionaryType dictionary;
   // Provide the current register values.
-  dictionary["$ebp"] = last_frame->context.ebp;
-  dictionary["$esp"] = last_frame->context.esp;
+  dictionary[ustr__ZSebp()] = last_frame->context.ebp;
+  dictionary[ustr__ZSesp()] = last_frame->context.esp;
   // Provide constants from the debug info for last_frame and its callee.
   // .cbCalleeParams is a Breakpad extension that allows us to use the
   // PostfixEvaluator engine when certain types of debugging information
   // are present without having to write the constants into the program
   // string as literals.
-  dictionary[".cbCalleeParams"] = last_frame_callee_parameter_size;
-  dictionary[".cbSavedRegs"] = last_frame_info->saved_register_size;
-  dictionary[".cbLocals"] = last_frame_info->local_size;
+  dictionary[ustr__ZDcbCalleeParams()] = last_frame_callee_parameter_size;
+  dictionary[ustr__ZDcbSavedRegs()] = last_frame_info->saved_register_size;
+  dictionary[ustr__ZDcbLocals()] = last_frame_info->local_size;
 
   uint32_t raSearchStart = last_frame->context.esp +
                             last_frame_callee_parameter_size +
                             last_frame_info->local_size +
                             last_frame_info->saved_register_size;
 
   uint32_t raSearchStartOld = raSearchStart;
   uint32_t found = 0;  // dummy value
@@ -232,20 +232,20 @@
     // Skip one slot from the stack and do another scan in order to get the
     // actual return address.
     raSearchStart += 4;
     ScanForReturnAddress(raSearchStart, &raSearchStart, &found, 3);
   }
 
   // The difference between raSearch and raSearchStart is unknown,
   // but making them the same seems to work well in practice.
-  dictionary[".raSearchStart"] = raSearchStart;
-  dictionary[".raSearch"] = raSearchStart;
+  dictionary[ustr__ZDraSearchStart()] = raSearchStart;
+  dictionary[ustr__ZDraSearch()] = raSearchStart;
 
-  dictionary[".cbParams"] = last_frame_info->parameter_size;
+  dictionary[ustr__ZDcbParams()] = last_frame_info->parameter_size;
 
   // Decide what type of program string to use. The program string is in
   // postfix notation and will be passed to PostfixEvaluator::Evaluate.
   // Given the dictionary and the program string, it is possible to compute
   // the return address and the values of other registers in the calling
   // function. Because of bugs described below, the stack may need to be
   // scanned for these values. The results of program string evaluation
   // will be used to determine whether to scan for better values.
@@ -325,18 +325,18 @@
   }
 
   // Now crank it out, making sure that the program string set at least the
   // two required variables.
   PostfixEvaluator<uint32_t> evaluator =
       PostfixEvaluator<uint32_t>(&dictionary, memory_);
   PostfixEvaluator<uint32_t>::DictionaryValidityType dictionary_validity;
   if (!evaluator.Evaluate(program_string, &dictionary_validity) ||
-      dictionary_validity.find("$eip") == dictionary_validity.end() ||
-      dictionary_validity.find("$esp") == dictionary_validity.end()) {
+      dictionary_validity.find(ustr__ZSeip()) == dictionary_validity.end() ||
+      dictionary_validity.find(ustr__ZSesp()) == dictionary_validity.end()) {
     // Program string evaluation failed. It may be that %eip is not somewhere
     // with stack frame info, and %ebp is pointing to non-stack memory, so
     // our evaluation couldn't succeed. We'll scan the stack for a return
     // address. This can happen if the stack is in a module for which
     // we don't have symbols, and that module is compiled without a
     // frame pointer.
     uint32_t location_start = last_frame->context.esp;
     uint32_t location, eip;
@@ -344,69 +344,69 @@
       // if we can't find an instruction pointer even with stack scanning,
       // give up.
       return NULL;
     }
 
     // This seems like a reasonable return address. Since program string
     // evaluation failed, use it and set %esp to the location above the
     // one where the return address was found.
-    dictionary["$eip"] = eip;
-    dictionary["$esp"] = location + 4;
+    dictionary[ustr__ZSeip()] = eip;
+    dictionary[ustr__ZSesp()] = location + 4;
     trust = StackFrame::FRAME_TRUST_SCAN;
   }
 
   // Since this stack frame did not use %ebp in a traditional way,
   // locating the return address isn't entirely deterministic. In that
   // case, the stack can be scanned to locate the return address.
   //
   // However, if program string evaluation resulted in both %eip and
   // %ebp values of 0, trust that the end of the stack has been
   // reached and don't scan for anything else.
-  if (dictionary["$eip"] != 0 || dictionary["$ebp"] != 0) {
+  if (dictionary[ustr__ZSeip()] != 0 || dictionary[ustr__ZSebp()] != 0) {
     int offset = 0;
 
     // This scan can only be done if a CodeModules object is available, to
     // check that candidate return addresses are in fact inside a module.
     //
     // TODO(mmentovai): This ignores dynamically-generated code.  One possible
     // solution is to check the minidump's memory map to see if the candidate
     // %eip value comes from a mapped executable page, although this would
     // require dumps that contain MINIDUMP_MEMORY_INFO, which the Breakpad
     // client doesn't currently write (it would need to call MiniDumpWriteDump
     // with the MiniDumpWithFullMemoryInfo type bit set).  Even given this
     // ability, older OSes (pre-XP SP2) and CPUs (pre-P4) don't enforce
     // an independent execute privilege on memory pages.
 
-    uint32_t eip = dictionary["$eip"];
+    uint32_t eip = dictionary[ustr__ZSeip()];
     if (modules_ && !modules_->GetModuleForAddress(eip)) {
       // The instruction pointer at .raSearchStart was invalid, so start
       // looking one 32-bit word above that location.
-      uint32_t location_start = dictionary[".raSearchStart"] + 4;
+      uint32_t location_start = dictionary[ustr__ZDraSearchStart()] + 4;
       uint32_t location;
       if (ScanForReturnAddress(location_start, &location, &eip)) {
         // This is a better return address that what program string
         // evaluation found.  Use it, and set %esp to the location above the
         // one where the return address was found.
-        dictionary["$eip"] = eip;
-        dictionary["$esp"] = location + 4;
+        dictionary[ustr__ZSeip()] = eip;
+        dictionary[ustr__ZSesp()] = location + 4;
         offset = location - location_start;
         trust = StackFrame::FRAME_TRUST_CFI_SCAN;
       }
     }
 
     if (recover_ebp) {
       // When trying to recover the previous value of the frame pointer (%ebp),
       // start looking at the lowest possible address in the saved-register
       // area, and look at the entire saved register area, increased by the
       // size of |offset| to account for additional data that may be on the
       // stack.  The scan is performed from the highest possible address to
       // the lowest, because the expectation is that the function's prolog
       // would have saved %ebp early.
-      uint32_t ebp = dictionary["$ebp"];
+      uint32_t ebp = dictionary[ustr__ZSebp()];
 
       // When a scan for return address is used, it is possible to skip one or
       // more frames (when return address is not in a known module).  One
       // indication for skipped frames is when the value of %ebp is lower than
       // the location of the return address on the stack
       bool has_skipped_frames =
         (trust != StackFrame::FRAME_TRUST_CFI && ebp <= raSearchStart + offset);
 
@@ -420,49 +420,49 @@
              location >= location_end;
              location -= 4) {
           if (!memory_->GetMemoryAtAddress(location, &ebp))
             break;
 
           if (memory_->GetMemoryAtAddress(ebp, &value)) {
             // The candidate value is a pointer to the same memory region
             // (the stack).  Prefer it as a recovered %ebp result.
-            dictionary["$ebp"] = ebp;
+            dictionary[ustr__ZSebp()] = ebp;
             break;
           }
         }
       }
     }
   }
 
   // Create a new stack frame (ownership will be transferred to the caller)
   // and fill it in.
   StackFrameX86* frame = new StackFrameX86();
 
   frame->trust = trust;
   frame->context = last_frame->context;
-  frame->context.eip = dictionary["$eip"];
-  frame->context.esp = dictionary["$esp"];
-  frame->context.ebp = dictionary["$ebp"];
+  frame->context.eip = dictionary[ustr__ZSeip()];
+  frame->context.esp = dictionary[ustr__ZSesp()];
+  frame->context.ebp = dictionary[ustr__ZSebp()];
   frame->context_validity = StackFrameX86::CONTEXT_VALID_EIP |
                                 StackFrameX86::CONTEXT_VALID_ESP |
                                 StackFrameX86::CONTEXT_VALID_EBP;
 
   // These are nonvolatile (callee-save) registers, and the program string
   // may have filled them in.
-  if (dictionary_validity.find("$ebx") != dictionary_validity.end()) {
-    frame->context.ebx = dictionary["$ebx"];
+  if (dictionary_validity.find(ustr__ZSebx()) != dictionary_validity.end()) {
+    frame->context.ebx = dictionary[ustr__ZSebx()];
     frame->context_validity |= StackFrameX86::CONTEXT_VALID_EBX;
   }
-  if (dictionary_validity.find("$esi") != dictionary_validity.end()) {
-    frame->context.esi = dictionary["$esi"];
+  if (dictionary_validity.find(ustr__ZSesi()) != dictionary_validity.end()) {
+    frame->context.esi = dictionary[ustr__ZSesi()];
     frame->context_validity |= StackFrameX86::CONTEXT_VALID_ESI;
   }
-  if (dictionary_validity.find("$edi") != dictionary_validity.end()) {
-    frame->context.edi = dictionary["$edi"];
+  if (dictionary_validity.find(ustr__ZSedi()) != dictionary_validity.end()) {
+    frame->context.edi = dictionary[ustr__ZSedi()];
     frame->context_validity |= StackFrameX86::CONTEXT_VALID_EDI;
   }
 
   return frame;
 }
 
 StackFrameX86* StackwalkerX86::GetCallerByCFIFrameInfo(
     const vector<StackFrame*> &frames,
diff --git a/src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj b/src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj
--- a/src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj
+++ b/src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj
@@ -98,16 +98,22 @@
 		B8C5B51B1166534700D34F4E /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BE650430B52F6D800611104 /* macho_id.cc */; };
 		B8C5B51C1166534700D34F4E /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BE650450B52F6D800611104 /* macho_walker.cc */; };
 		B8C5B51D1166534700D34F4E /* dump_syms.mm in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* dump_syms.mm */; };
 		B8C5B51E1166534700D34F4E /* dump_syms_tool.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9BDF186E0B1BB43700F8391B /* dump_syms_tool.mm */; };
 		B8C5B523116653BA00D34F4E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; };
 		D21F97D711CBA12300239E38 /* test_assembler_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = B88FB0D9116CEC0600407530 /* test_assembler_unittest.cc */; };
 		D21F97D811CBA13D00239E38 /* libgtestmockall.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B88FB024116BDFFF00407530 /* libgtestmockall.a */; };
 		D21F97E911CBA1FF00239E38 /* test_assembler.cc in Sources */ = {isa = PBXBuildFile; fileRef = B88FAE0911665B5700407530 /* test_assembler.cc */; };
+		D24997CC16B6C16800E588C5 /* unique_string.cc in Sources */ = {isa = PBXBuildFile; fileRef = D24997CA16B6C16800E588C5 /* unique_string.cc */; };
+		D24997CD16B6C16800E588C5 /* unique_string.cc in Sources */ = {isa = PBXBuildFile; fileRef = D24997CA16B6C16800E588C5 /* unique_string.cc */; };
+		D24997CE16B6C16800E588C5 /* unique_string.cc in Sources */ = {isa = PBXBuildFile; fileRef = D24997CA16B6C16800E588C5 /* unique_string.cc */; };
+		D2499A0016B9BA6A00E588C5 /* unique_string.cc in Sources */ = {isa = PBXBuildFile; fileRef = D24997CA16B6C16800E588C5 /* unique_string.cc */; };
+		D2499A0216B9BA9600E588C5 /* unique_string.cc in Sources */ = {isa = PBXBuildFile; fileRef = D24997CA16B6C16800E588C5 /* unique_string.cc */; };
+		D2499A0316B9BA9D00E588C5 /* unique_string.cc in Sources */ = {isa = PBXBuildFile; fileRef = D24997CA16B6C16800E588C5 /* unique_string.cc */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
 		8B31051411F100CF00FCF3E4 /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
 			proxyType = 1;
 			remoteGlobalIDString = D21F97D111CBA0F200239E38;
@@ -338,16 +344,18 @@
 		B89E0E741166575200DD08C9 /* macho_dump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = macho_dump; sourceTree = BUILT_PRODUCTS_DIR; };
 		B89E0E9511665A6400DD08C9 /* macho_reader_unittest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = macho_reader_unittest; sourceTree = BUILT_PRODUCTS_DIR; };
 		B89E0E9F11665AC300DD08C9 /* gtest_main.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = gtest_main.cc; path = ../../../testing/gtest/src/gtest_main.cc; sourceTree = SOURCE_ROOT; };
 		B89E0EA011665AC300DD08C9 /* gtest-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gtest-all.cc"; path = "../../../testing/gtest/src/gtest-all.cc"; sourceTree = SOURCE_ROOT; };
 		B89E0EA311665AEA00DD08C9 /* gmock-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gmock-all.cc"; path = "../../../testing/src/gmock-all.cc"; sourceTree = SOURCE_ROOT; };
 		B8C5B5111166531A00D34F4E /* dump_syms */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dump_syms; sourceTree = BUILT_PRODUCTS_DIR; };
 		B8E8CA0C1156C854009E61B2 /* byteswap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = byteswap.h; path = ../../../common/mac/byteswap.h; sourceTree = SOURCE_ROOT; };
 		D21F97D211CBA0F200239E38 /* test_assembler_unittest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = test_assembler_unittest; sourceTree = BUILT_PRODUCTS_DIR; };
+		D24997CA16B6C16800E588C5 /* unique_string.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = unique_string.cc; path = ../../../common/unique_string.cc; sourceTree = "<group>"; };
+		D24997CB16B6C16800E588C5 /* unique_string.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = unique_string.h; path = ../../../common/unique_string.h; sourceTree = "<group>"; };
 		F95B422B0E0E22D100DBDE83 /* bytereader-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "bytereader-inl.h"; path = "../../../common/dwarf/bytereader-inl.h"; sourceTree = SOURCE_ROOT; };
 		F95B422C0E0E22D100DBDE83 /* bytereader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bytereader.cc; path = ../../../common/dwarf/bytereader.cc; sourceTree = SOURCE_ROOT; };
 		F95B422D0E0E22D100DBDE83 /* bytereader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = bytereader.h; path = ../../../common/dwarf/bytereader.h; sourceTree = SOURCE_ROOT; };
 		F95B422E0E0E22D100DBDE83 /* dwarf2enums.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dwarf2enums.h; path = ../../../common/dwarf/dwarf2enums.h; sourceTree = SOURCE_ROOT; };
 		F95B422F0E0E22D100DBDE83 /* dwarf2reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf2reader.cc; path = ../../../common/dwarf/dwarf2reader.cc; sourceTree = SOURCE_ROOT; };
 		F95B42300E0E22D100DBDE83 /* dwarf2reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dwarf2reader.h; path = ../../../common/dwarf/dwarf2reader.h; sourceTree = SOURCE_ROOT; };
 		F95B42310E0E22D100DBDE83 /* line_state_machine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = line_state_machine.h; path = ../../../common/dwarf/line_state_machine.h; sourceTree = SOURCE_ROOT; };
 /* End PBXFileReference section */
@@ -531,16 +539,18 @@
 				D21F97D211CBA0F200239E38 /* test_assembler_unittest */,
 			);
 			name = Products;
 			sourceTree = "<group>";
 		};
 		B88FAE1C11665FFD00407530 /* MODULE */ = {
 			isa = PBXGroup;
 			children = (
+				D24997CA16B6C16800E588C5 /* unique_string.cc */,
+				D24997CB16B6C16800E588C5 /* unique_string.h */,
 				B88FAE1E1166603300407530 /* dwarf_cu_to_module.cc */,
 				B88FAE1F1166603300407530 /* dwarf_cu_to_module.h */,
 				B88FB0D6116CEC0600407530 /* dwarf_cu_to_module_unittest.cc */,
 				B88FAE201166603300407530 /* dwarf_line_to_module.cc */,
 				B88FAE211166603300407530 /* dwarf_line_to_module.h */,
 				B88FB0D7116CEC0600407530 /* dwarf_line_to_module_unittest.cc */,
 				B88FAE221166603300407530 /* language.cc */,
 				B88FAE231166603300407530 /* language.h */,
@@ -940,16 +950,17 @@
 		};
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
 		B84A91F1116CF784006C210E /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				D2499A0216B9BA9600E588C5 /* unique_string.cc in Sources */,
 				B84A91FB116CF7AF006C210E /* module.cc in Sources */,
 				B84A91FC116CF7AF006C210E /* stabs_to_module.cc in Sources */,
 				B84A91FD116CF7AF006C210E /* stabs_to_module_unittest.cc in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		B88FAF2C116A591D00407530 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
@@ -983,56 +994,60 @@
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		B88FB0B6116CEABF00407530 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 				B88FB0BD116CEAE000407530 /* module_unittest.cc in Sources */,
 				B88FB0C4116CEB4100407530 /* module.cc in Sources */,
+				D24997CE16B6C16800E588C5 /* unique_string.cc in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		B88FB0DC116CEEA800407530 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 				B88FB0E5116CEED300407530 /* dwarf2diehandler.cc in Sources */,
 				B88FB0E6116CEED300407530 /* dwarf2diehandler_unittest.cc in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		B88FB0EF116CEF1900407530 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				D2499A0316B9BA9D00E588C5 /* unique_string.cc in Sources */,
 				B88FB0FA116CF00E00407530 /* dwarf_line_to_module.cc in Sources */,
 				B88FB0FE116CF02400407530 /* module.cc in Sources */,
 				B88FB0FB116CF00E00407530 /* dwarf_line_to_module_unittest.cc in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		B88FB107116CF07900407530 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				D2499A0016B9BA6A00E588C5 /* unique_string.cc in Sources */,
 				B88FB112116CF1F000407530 /* dwarf_cu_to_module.cc in Sources */,
 				B88FB113116CF1F000407530 /* dwarf_cu_to_module_unittest.cc in Sources */,
 				B88FB114116CF1F000407530 /* language.cc in Sources */,
 				B88FB115116CF1F000407530 /* module.cc in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		B88FB11C116CF27F00407530 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 				B88FB129116CF2DD00407530 /* module.cc in Sources */,
 				B88FB12A116CF2DD00407530 /* dwarf_cfi_to_module.cc in Sources */,
 				B88FB12B116CF2DD00407530 /* dwarf_cfi_to_module_unittest.cc in Sources */,
+				D24997CD16B6C16800E588C5 /* unique_string.cc in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		B88FB132116CF30F00407530 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 				B88FB13D116CF38300407530 /* cfi_assembler.cc in Sources */,
@@ -1086,16 +1101,17 @@
 				B88FAE261166603300407530 /* dwarf_cu_to_module.cc in Sources */,
 				B88FAE271166603300407530 /* dwarf_line_to_module.cc in Sources */,
 				B88FAE281166603300407530 /* language.cc in Sources */,
 				B88FAE291166603300407530 /* module.cc in Sources */,
 				B88FAE351166673E00407530 /* dwarf_cfi_to_module.cc in Sources */,
 				B88FAE3B11666C6F00407530 /* stabs_reader.cc in Sources */,
 				B88FAE3E11666C8900407530 /* stabs_to_module.cc in Sources */,
 				4D72CAF513DFBAC2006CABE3 /* md5.cc in Sources */,
+				D24997CC16B6C16800E588C5 /* unique_string.cc in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		D21F97CF11CBA0F200239E38 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 				D21F97E911CBA1FF00239E38 /* test_assembler.cc in Sources */,