diff options
author | 2015-05-22 17:04:47 +0100 | |
---|---|---|
committer | 2015-05-27 01:44:02 +0100 | |
commit | f8980875ef8fb0ce86be4ed2c0af7070f5ae9cfd (patch) | |
tree | 9750cf170ec12bbd0638c5e373a35fe832999173 | |
parent | eabafcefca8ead8309dccc1c5510e6e0845e471d (diff) |
Split .oat_patches to multiple sections.
.oat_patches section currently contains encoded patch locations for
several other sections. Split it to several sections - one for each
of the destination sections. For example, .text.oat_patches section
contains patch locations for the .text section.
This ensures that if we strip some the sections using standard
tools, we strip the corresponding .oat_patches section as well.
It also makes the overall design simpler.
I should have done it this way in the first place.
Since ApplyOatPatches has been simplified and uses unaligned memory
access, this also fixes bug 21403573.
Bug:20556771
Bug:21403573
Change-Id: Iae7c423911507b524eec500e8d61744046fcd3ba
-rw-r--r-- | compiler/elf_builder.h | 14 | ||||
-rw-r--r-- | compiler/elf_writer_quick.cc | 78 | ||||
-rw-r--r-- | compiler/elf_writer_quick.h | 2 | ||||
-rw-r--r-- | compiler/elf_writer_test.cc | 98 | ||||
-rw-r--r-- | compiler/oat_writer.cc | 8 | ||||
-rw-r--r-- | compiler/oat_writer.h | 19 | ||||
-rw-r--r-- | runtime/elf_file.cc | 83 | ||||
-rw-r--r-- | runtime/elf_file_impl.h | 9 |
8 files changed, 110 insertions, 201 deletions
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h index 8a63a4882f..730d780f6b 100644 --- a/compiler/elf_builder.h +++ b/compiler/elf_builder.h @@ -168,6 +168,10 @@ class ElfBuilder FINAL { patched_(false), patch_(patch), patch_base_section_(patch_base_section) { } + RawSection(const std::string& name, Elf_Word type) + : RawSection(name, type, 0, nullptr, 0, 1, 0, nullptr, nullptr) { + } + Elf_Word GetSize() const OVERRIDE { return buffer_.size(); } @@ -779,10 +783,12 @@ class ElfBuilder FINAL { template<typename T> static bool WriteArray(File* elf_file, const T* data, size_t count) { - DCHECK(data != nullptr); - if (!elf_file->WriteFully(data, count * sizeof(T))) { - PLOG(ERROR) << "Failed to write to file " << elf_file->GetPath(); - return false; + if (count != 0) { + DCHECK(data != nullptr); + if (!elf_file->WriteFully(data, count * sizeof(T))) { + PLOG(ERROR) << "Failed to write to file " << elf_file->GetPath(); + return false; + } } return true; } diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc index 05b5c6862a..4a90b4ea73 100644 --- a/compiler/elf_writer_quick.cc +++ b/compiler/elf_writer_quick.cc @@ -19,6 +19,7 @@ #include <unordered_map> #include <unordered_set> +#include "base/casts.h" #include "base/logging.h" #include "base/unix_file/fd_file.h" #include "compiled_method.h" @@ -69,36 +70,17 @@ bool ElfWriterQuick<ElfTypes>::Create(File* elf_file, template <typename ElfTypes> static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, OatWriter* oat_writer); -// Encode patch locations in .oat_patches format. +// Encode patch locations as LEB128 list of deltas between consecutive addresses. template <typename ElfTypes> -void ElfWriterQuick<ElfTypes>::EncodeOatPatches( - const OatWriter::PatchLocationsMap& sections, - std::vector<uint8_t>* buffer) { - for (const auto& section : sections) { - const std::string& name = section.first; - std::vector<uintptr_t>* locations = section.second.get(); - DCHECK(!name.empty()); - std::sort(locations->begin(), locations->end()); - // Reserve buffer space - guess 2 bytes per ULEB128. - buffer->reserve(buffer->size() + name.size() + locations->size() * 2); - // Write null-terminated section name. - const uint8_t* name_data = reinterpret_cast<const uint8_t*>(name.c_str()); - buffer->insert(buffer->end(), name_data, name_data + name.size() + 1); - // Write placeholder for data length. - size_t length_pos = buffer->size(); - EncodeUnsignedLeb128(buffer, UINT32_MAX); - // Write LEB128 encoded list of advances (deltas between consequtive addresses). - size_t data_pos = buffer->size(); - uintptr_t address = 0; // relative to start of section. - for (uintptr_t location : *locations) { - DCHECK_LT(location - address, UINT32_MAX) << "Large gap between patch locations"; - EncodeUnsignedLeb128(buffer, location - address); - address = location; - } - // Update length. - UpdateUnsignedLeb128(buffer->data() + length_pos, buffer->size() - data_pos); +void ElfWriterQuick<ElfTypes>::EncodeOatPatches(const std::vector<uintptr_t>& locations, + std::vector<uint8_t>* buffer) { + buffer->reserve(buffer->size() + locations.size() * 2); // guess 2 bytes per ULEB128. + uintptr_t address = 0; // relative to start of section. + for (uintptr_t location : locations) { + DCHECK_GE(location, address) << "Patch locations are not in sorted order"; + EncodeUnsignedLeb128(buffer, dchecked_integral_cast<uint32_t>(location - address)); + address = location; } - buffer->push_back(0); // End of sections. } class RodataWriter FINAL : public CodeOutput { @@ -175,7 +157,7 @@ bool ElfWriterQuick<ElfTypes>::Write( // Add debug sections. // They are stack allocated here (in the same scope as the builder), - // but they are registred with the builder only if they are used. + // but they are registered with the builder only if they are used. using RawSection = typename ElfBuilder<ElfTypes>::RawSection; const auto* text = builder->GetText(); const bool is64bit = Is64BitInstructionSet(isa); @@ -190,12 +172,15 @@ bool ElfWriterQuick<ElfTypes>::Write( is64bit ? Patch<Elf_Addr, uint64_t, kAbsoluteAddress> : Patch<Elf_Addr, uint32_t, kAbsoluteAddress>, text); + RawSection debug_frame_oat_patches(".debug_frame.oat_patches", SHT_OAT_PATCH); RawSection debug_info(".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0, Patch<Elf_Addr, uint32_t, kAbsoluteAddress>, text); - RawSection debug_abbrev(".debug_abbrev", SHT_PROGBITS, 0, nullptr, 0, 1, 0); - RawSection debug_str(".debug_str", SHT_PROGBITS, 0, nullptr, 0, 1, 0); + RawSection debug_info_oat_patches(".debug_info.oat_patches", SHT_OAT_PATCH); + RawSection debug_abbrev(".debug_abbrev", SHT_PROGBITS); + RawSection debug_str(".debug_str", SHT_PROGBITS); RawSection debug_line(".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0, Patch<Elf_Addr, uint32_t, kAbsoluteAddress>, text); + RawSection debug_line_oat_patches(".debug_line.oat_patches", SHT_OAT_PATCH); if (!oat_writer->GetMethodDebugInfo().empty()) { if (compiler_driver_->GetCompilerOptions().GetIncludeCFI()) { if (kCFIFormat == dwarf::DW_EH_FRAME_FORMAT) { @@ -214,8 +199,9 @@ bool ElfWriterQuick<ElfTypes>::Write( debug_frame.GetBuffer(), debug_frame.GetPatchLocations(), nullptr, nullptr); builder->RegisterSection(&debug_frame); - *oat_writer->GetAbsolutePatchLocationsFor(".debug_frame") = - *debug_frame.GetPatchLocations(); + EncodeOatPatches(*debug_frame.GetPatchLocations(), + debug_frame_oat_patches.GetBuffer()); + builder->RegisterSection(&debug_frame_oat_patches); } } if (compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) { @@ -229,24 +215,26 @@ bool ElfWriterQuick<ElfTypes>::Write( debug_str.GetBuffer(), debug_line.GetBuffer(), debug_line.GetPatchLocations()); builder->RegisterSection(&debug_info); + EncodeOatPatches(*debug_info.GetPatchLocations(), + debug_info_oat_patches.GetBuffer()); + builder->RegisterSection(&debug_info_oat_patches); builder->RegisterSection(&debug_abbrev); builder->RegisterSection(&debug_str); builder->RegisterSection(&debug_line); - *oat_writer->GetAbsolutePatchLocationsFor(".debug_info") = - *debug_info.GetPatchLocations(); - *oat_writer->GetAbsolutePatchLocationsFor(".debug_line") = - *debug_line.GetPatchLocations(); + EncodeOatPatches(*debug_line.GetPatchLocations(), + debug_line_oat_patches.GetBuffer()); + builder->RegisterSection(&debug_line_oat_patches); } } - // Add relocation section. - RawSection oat_patches(".oat_patches", SHT_OAT_PATCH, 0, nullptr, 0, 1, 0); - if (compiler_driver_->GetCompilerOptions().GetIncludePatchInformation() || - // ElfWriter::Fixup will be called regardless and it needs to be able - // to patch debug sections so we have to include patches for them. - compiler_driver_->GetCompilerOptions().GetIncludeDebugSymbols()) { - EncodeOatPatches(oat_writer->GetAbsolutePatchLocations(), oat_patches.GetBuffer()); - builder->RegisterSection(&oat_patches); + // Add relocation section for .text. + RawSection text_oat_patches(".text.oat_patches", SHT_OAT_PATCH); + if (compiler_driver_->GetCompilerOptions().GetIncludePatchInformation()) { + // Note that ElfWriter::Fixup will be called regardless and therefore + // we need to include oat_patches for debug sections unconditionally. + EncodeOatPatches(oat_writer->GetAbsolutePatchLocations(), + text_oat_patches.GetBuffer()); + builder->RegisterSection(&text_oat_patches); } return builder->Write(elf_file_); diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h index 955b5684e7..fd202eeb5f 100644 --- a/compiler/elf_writer_quick.h +++ b/compiler/elf_writer_quick.h @@ -35,7 +35,7 @@ class ElfWriterQuick FINAL : public ElfWriter { const CompilerDriver& driver) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static void EncodeOatPatches(const OatWriter::PatchLocationsMap& sections, + static void EncodeOatPatches(const std::vector<uintptr_t>& locations, std::vector<uint8_t>* buffer); protected: diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc index 08523d8587..ccf34b816b 100644 --- a/compiler/elf_writer_test.cc +++ b/compiler/elf_writer_test.cc @@ -88,73 +88,41 @@ TEST_F(ElfWriterTest, dlsym) { } } -// Run only on host since we do unaligned memory accesses. -#ifndef HAVE_ANDROID_OS - -static void PatchSection(const std::vector<uintptr_t>& patch_locations, - std::vector<uint8_t>* section, int32_t delta) { - for (uintptr_t location : patch_locations) { - *reinterpret_cast<int32_t*>(section->data() + location) += delta; - } -} - TEST_F(ElfWriterTest, EncodeDecodeOatPatches) { - std::vector<uint8_t> oat_patches; // Encoded patches. - - // Encode patch locations for a few sections. - OatWriter::PatchLocationsMap sections; - std::vector<uintptr_t> patches0 { 0, 4, 8, 15, 128, 200 }; // NOLINT - sections.emplace(".section0", std::unique_ptr<std::vector<uintptr_t>>( - new std::vector<uintptr_t> { patches0 })); - std::vector<uintptr_t> patches1 { 8, 127 }; // NOLINT - sections.emplace(".section1", std::unique_ptr<std::vector<uintptr_t>>( - new std::vector<uintptr_t> { patches1 })); - std::vector<uintptr_t> patches2 { }; // NOLINT - sections.emplace(".section2", std::unique_ptr<std::vector<uintptr_t>>( - new std::vector<uintptr_t> { patches2 })); - ElfWriterQuick32::EncodeOatPatches(sections, &oat_patches); - - // Create buffers to be patched. - std::vector<uint8_t> initial_data(256); - for (size_t i = 0; i < initial_data.size(); i++) { - initial_data[i] = i; + const std::vector<std::vector<uintptr_t>> test_data { + { 0, 4, 8, 15, 128, 200 }, + { 8, 8 + 127 }, + { 8, 8 + 128 }, + { }, + }; + for (const auto& patch_locations : test_data) { + constexpr int32_t delta = 0x11235813; + + // Encode patch locations. + std::vector<uint8_t> oat_patches; + ElfWriterQuick32::EncodeOatPatches(patch_locations, &oat_patches); + + // Create buffer to be patched. + std::vector<uint8_t> initial_data(256); + for (size_t i = 0; i < initial_data.size(); i++) { + initial_data[i] = i; + } + + // Patch manually. + std::vector<uint8_t> expected = initial_data; + for (uintptr_t location : patch_locations) { + typedef __attribute__((__aligned__(1))) uint32_t UnalignedAddress; + *reinterpret_cast<UnalignedAddress*>(expected.data() + location) += delta; + } + + // Decode and apply patch locations. + std::vector<uint8_t> actual = initial_data; + ElfFileImpl32::ApplyOatPatches( + oat_patches.data(), oat_patches.data() + oat_patches.size(), delta, + actual.data(), actual.data() + actual.size()); + + EXPECT_EQ(expected, actual); } - std::vector<uint8_t> section0_expected = initial_data; - std::vector<uint8_t> section1_expected = initial_data; - std::vector<uint8_t> section2_expected = initial_data; - std::vector<uint8_t> section0_actual = initial_data; - std::vector<uint8_t> section1_actual = initial_data; - std::vector<uint8_t> section2_actual = initial_data; - - // Patch manually. - constexpr int32_t delta = 0x11235813; - PatchSection(patches0, §ion0_expected, delta); - PatchSection(patches1, §ion1_expected, delta); - PatchSection(patches2, §ion2_expected, delta); - - // Decode and apply patch locations. - bool section0_successful = ElfFileImpl32::ApplyOatPatches( - oat_patches.data(), oat_patches.data() + oat_patches.size(), - ".section0", delta, - section0_actual.data(), section0_actual.data() + section0_actual.size()); - EXPECT_TRUE(section0_successful); - EXPECT_EQ(section0_expected, section0_actual); - - bool section1_successful = ElfFileImpl32::ApplyOatPatches( - oat_patches.data(), oat_patches.data() + oat_patches.size(), - ".section1", delta, - section1_actual.data(), section1_actual.data() + section1_actual.size()); - EXPECT_TRUE(section1_successful); - EXPECT_EQ(section1_expected, section1_actual); - - bool section2_successful = ElfFileImpl32::ApplyOatPatches( - oat_patches.data(), oat_patches.data() + oat_patches.size(), - ".section2", delta, - section2_actual.data(), section2_actual.data() + section2_actual.size()); - EXPECT_TRUE(section2_successful); - EXPECT_EQ(section2_expected, section2_actual); } -#endif - } // namespace art diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 15b4017816..745cdcfaab 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -351,9 +351,8 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { public: InitCodeMethodVisitor(OatWriter* writer, size_t offset) : OatDexMethodVisitor(writer, offset), - text_absolute_patch_locations_(writer->GetAbsolutePatchLocationsFor(".text")), debuggable_(writer->GetCompilerDriver()->GetCompilerOptions().GetDebuggable()) { - text_absolute_patch_locations_->reserve( + writer_->absolute_patch_locations_.reserve( writer_->compiler_driver_->GetNonRelativeLinkerPatchCount()); } @@ -444,7 +443,7 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { uintptr_t base_loc = offset_ - code_size - writer_->oat_header_->GetExecutableOffset(); for (const LinkerPatch& patch : compiled_method->GetPatches()) { if (!patch.IsPcRelative()) { - text_absolute_patch_locations_->push_back(base_loc + patch.LiteralOffset()); + writer_->absolute_patch_locations_.push_back(base_loc + patch.LiteralOffset()); } } } @@ -547,9 +546,6 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { // so we can simply compare the pointers to find out if things are duplicated. SafeMap<const CompiledMethod*, uint32_t, CodeOffsetsKeyComparator> dedupe_map_; - // Patch locations for the .text section. - std::vector<uintptr_t>* const text_absolute_patch_locations_; - // Cache of compiler's --debuggable option. const bool debuggable_; }; diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index 6f1b4ec15a..82b9377c07 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -19,7 +19,6 @@ #include <stdint.h> #include <cstddef> -#include <map> #include <memory> #include "linker/relative_patcher.h" // For linker::RelativePatcherTargetProvider. @@ -82,8 +81,6 @@ class TimingLogger; // class OatWriter { public: - typedef std::map<std::string, std::unique_ptr<std::vector<uintptr_t>>> PatchLocationsMap; - OatWriter(const std::vector<const DexFile*>& dex_files, uint32_t image_file_location_oat_checksum, uintptr_t image_file_location_oat_begin, @@ -105,19 +102,10 @@ class OatWriter { return bss_size_; } - const PatchLocationsMap& GetAbsolutePatchLocations() const { + const std::vector<uintptr_t>& GetAbsolutePatchLocations() const { return absolute_patch_locations_; } - std::vector<uintptr_t>* GetAbsolutePatchLocationsFor(const char* section_name) { - auto it = absolute_patch_locations_.emplace( - std::string(section_name), std::unique_ptr<std::vector<uintptr_t>>()); - if (it.second) { // Inserted new item. - it.first->second.reset(new std::vector<uintptr_t>()); - } - return it.first->second.get(); - } - bool WriteRodata(OutputStream* out); bool WriteCode(OutputStream* out); @@ -339,9 +327,8 @@ class OatWriter { std::unique_ptr<linker::RelativePatcher> relative_patcher_; - // The locations of absolute patches relative to the start of section. - // The map's key is the ELF's section name (including the dot). - PatchLocationsMap absolute_patch_locations_; + // The locations of absolute patches relative to the start of the executable section. + std::vector<uintptr_t> absolute_patch_locations_; // Map method reference to assigned offset. // Wrap the map in a class implementing linker::RelativePatcherTargetProvider. diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc index b1d933df62..9fd8c87435 100644 --- a/runtime/elf_file.cc +++ b/runtime/elf_file.cc @@ -1401,86 +1401,53 @@ typename ElfTypes::Shdr* ElfFileImpl<ElfTypes>::FindSectionByName( } template <typename ElfTypes> -bool ElfFileImpl<ElfTypes>::FixupDebugSections(typename std::make_signed<Elf_Off>::type base_address_delta) { +bool ElfFileImpl<ElfTypes>::FixupDebugSections(Elf_Addr base_address_delta) { if (base_address_delta == 0) { return true; } - if (FindSectionByName(".debug_frame") != nullptr) { - if (!ApplyOatPatchesTo(".debug_frame", base_address_delta)) { - return false; - } - } - if (FindSectionByName(".debug_info") != nullptr) { - if (!ApplyOatPatchesTo(".debug_info", base_address_delta)) { - return false; - } - } - if (FindSectionByName(".debug_line") != nullptr) { - if (!ApplyOatPatchesTo(".debug_line", base_address_delta)) { - return false; - } - } - return true; + return ApplyOatPatchesTo(".debug_frame", base_address_delta) && + ApplyOatPatchesTo(".debug_info", base_address_delta) && + ApplyOatPatchesTo(".debug_line", base_address_delta); } template <typename ElfTypes> bool ElfFileImpl<ElfTypes>::ApplyOatPatchesTo( - const char* target_section_name, - typename std::make_signed<Elf_Off>::type delta) { - auto patches_section = FindSectionByName(".oat_patches"); + const char* target_section_name, Elf_Addr delta) { + auto target_section = FindSectionByName(target_section_name); + if (target_section == nullptr) { + return true; + } + std::string patches_name = target_section_name + std::string(".oat_patches"); + auto patches_section = FindSectionByName(patches_name.c_str()); if (patches_section == nullptr) { - LOG(ERROR) << ".oat_patches section not found."; + LOG(ERROR) << patches_name << " section not found."; return false; } if (patches_section->sh_type != SHT_OAT_PATCH) { - LOG(ERROR) << "Unexpected type of .oat_patches."; - return false; - } - auto target_section = FindSectionByName(target_section_name); - if (target_section == nullptr) { - LOG(ERROR) << target_section_name << " section not found."; + LOG(ERROR) << "Unexpected type of " << patches_name; return false; } - if (!ApplyOatPatches( + ApplyOatPatches( Begin() + patches_section->sh_offset, Begin() + patches_section->sh_offset + patches_section->sh_size, - target_section_name, delta, + delta, Begin() + target_section->sh_offset, - Begin() + target_section->sh_offset + target_section->sh_size)) { - LOG(ERROR) << target_section_name << " section not found in .oat_patches."; - } + Begin() + target_section->sh_offset + target_section->sh_size); return true; } -// Apply .oat_patches to given section. +// Apply LEB128 encoded patches to given section. template <typename ElfTypes> -bool ElfFileImpl<ElfTypes>::ApplyOatPatches( - const uint8_t* patches, const uint8_t* patches_end, - const char* target_section_name, - typename std::make_signed<Elf_Off>::type delta, +void ElfFileImpl<ElfTypes>::ApplyOatPatches( + const uint8_t* patches, const uint8_t* patches_end, Elf_Addr delta, uint8_t* to_patch, const uint8_t* to_patch_end) { - // Read null-terminated section name. - const char* section_name; - while ((section_name = reinterpret_cast<const char*>(patches))[0] != '\0') { - patches += strlen(section_name) + 1; - uint32_t length = DecodeUnsignedLeb128(&patches); - const uint8_t* next_section = patches + length; - // Is it the section we want to patch? - if (strcmp(section_name, target_section_name) == 0) { - // Read LEB128 encoded list of advances. - while (patches < next_section) { - DCHECK_LT(patches, patches_end) << "Unexpected end of .oat_patches."; - to_patch += DecodeUnsignedLeb128(&patches); - DCHECK_LT(to_patch, to_patch_end) << "Patch past the end of " << section_name; - // TODO: 32-bit vs 64-bit. What is the right type to use here? - auto* patch_loc = reinterpret_cast<typename std::make_signed<Elf_Off>::type*>(to_patch); - *patch_loc += delta; - } - return true; - } - patches = next_section; + typedef __attribute__((__aligned__(1))) Elf_Addr UnalignedAddress; + while (patches < patches_end) { + to_patch += DecodeUnsignedLeb128(&patches); + DCHECK_LE(patches, patches_end) << "Unexpected end of patch list."; + DCHECK_LT(to_patch, to_patch_end) << "Patch past the end of section."; + *reinterpret_cast<UnalignedAddress*>(to_patch) += delta; } - return false; } template <typename ElfTypes> diff --git a/runtime/elf_file_impl.h b/runtime/elf_file_impl.h index 3ad096f983..0f466bd7b8 100644 --- a/runtime/elf_file_impl.h +++ b/runtime/elf_file_impl.h @@ -119,12 +119,9 @@ class ElfFileImpl { bool FixupProgramHeaders(Elf_Addr base_address); bool FixupSymbols(Elf_Addr base_address, bool dynamic); bool FixupRelocations(Elf_Addr base_address); - bool FixupDebugSections(typename std::make_signed<Elf_Off>::type base_address_delta); - bool ApplyOatPatchesTo(const char* target_section_name, - typename std::make_signed<Elf_Off>::type base_address_delta); - static bool ApplyOatPatches(const uint8_t* patches, const uint8_t* patches_end, - const char* target_section_name, - typename std::make_signed<Elf_Off>::type delta, + bool FixupDebugSections(Elf_Addr base_address_delta); + bool ApplyOatPatchesTo(const char* target_section_name, Elf_Addr base_address_delta); + static void ApplyOatPatches(const uint8_t* patches, const uint8_t* patches_end, Elf_Addr delta, uint8_t* to_patch, const uint8_t* to_patch_end); bool Strip(std::string* error_msg); |