Adding a parser for DWARF EH LSDA records.

This currently is not async-safe due to the use of std::vector. Once a suitable async-safe data type is available, it should be switched over.

Additionally, the use of vector has forced the iOS demo app to link against libstdc++. This seems far from ideal and should be fixed.

Issue: PLCR-561
This commit is contained in:
Joe Ranieri
2015-03-17 16:03:04 -04:00
parent 7d33b169f9
commit 1f13c19918
4 changed files with 685 additions and 0 deletions

View File

@@ -804,6 +804,18 @@
C26022901642FE9B007FC29F /* PLCrashAsyncSymbolicationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C260228F1642FE9B007FC29F /* PLCrashAsyncSymbolicationTests.m */; };
C26022911642FE9B007FC29F /* PLCrashAsyncSymbolicationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C260228F1642FE9B007FC29F /* PLCrashAsyncSymbolicationTests.m */; };
C26022921642FE9B007FC29F /* PLCrashAsyncSymbolicationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C260228F1642FE9B007FC29F /* PLCrashAsyncSymbolicationTests.m */; };
D81ACF0F1AB795D4001590A9 /* PLCrashDwarfLSDATests.mm in Sources */ = {isa = PBXBuildFile; fileRef = D81ACF0E1AB795CE001590A9 /* PLCrashDwarfLSDATests.mm */; };
D81ACF101AB795D4001590A9 /* PLCrashDwarfLSDATests.mm in Sources */ = {isa = PBXBuildFile; fileRef = D81ACF0E1AB795CE001590A9 /* PLCrashDwarfLSDATests.mm */; };
D81ACF111AB795D5001590A9 /* PLCrashDwarfLSDATests.mm in Sources */ = {isa = PBXBuildFile; fileRef = D81ACF0E1AB795CE001590A9 /* PLCrashDwarfLSDATests.mm */; };
D81ACF121AB795FF001590A9 /* PLCrashDwarfLSDA.hpp in Headers */ = {isa = PBXBuildFile; fileRef = D81ACF0D1AB795CE001590A9 /* PLCrashDwarfLSDA.hpp */; };
D81ACF131AB79600001590A9 /* PLCrashDwarfLSDA.hpp in Headers */ = {isa = PBXBuildFile; fileRef = D81ACF0D1AB795CE001590A9 /* PLCrashDwarfLSDA.hpp */; };
D81ACF141AB79610001590A9 /* PLCrashDwarfLSDA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D81ACF0C1AB795CE001590A9 /* PLCrashDwarfLSDA.cpp */; };
D81ACF151AB79611001590A9 /* PLCrashDwarfLSDA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D81ACF0C1AB795CE001590A9 /* PLCrashDwarfLSDA.cpp */; };
D81ACF161AB79613001590A9 /* PLCrashDwarfLSDA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D81ACF0C1AB795CE001590A9 /* PLCrashDwarfLSDA.cpp */; };
D81ACF171AB79614001590A9 /* PLCrashDwarfLSDA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D81ACF0C1AB795CE001590A9 /* PLCrashDwarfLSDA.cpp */; };
D81ACF181AB79614001590A9 /* PLCrashDwarfLSDA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D81ACF0C1AB795CE001590A9 /* PLCrashDwarfLSDA.cpp */; };
D81ACF191AB79615001590A9 /* PLCrashDwarfLSDA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D81ACF0C1AB795CE001590A9 /* PLCrashDwarfLSDA.cpp */; };
D81ACF1A1AB79616001590A9 /* PLCrashDwarfLSDA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D81ACF0C1AB795CE001590A9 /* PLCrashDwarfLSDA.cpp */; };
FCE45210FDD184E397747BE3 /* PLCrashFrameStackUnwind.h in Headers */ = {isa = PBXBuildFile; fileRef = FCE4522F86AC61C08E9DCC17 /* PLCrashFrameStackUnwind.h */; };
FCE4550BA74D9DF923CFCD5A /* PLCrashFrameStackUnwind.c in Sources */ = {isa = PBXBuildFile; fileRef = FCE45837C8C773EFFD15C52B /* PLCrashFrameStackUnwind.c */; };
FCE4566DF9168DCC484928E1 /* PLCrashFrameStackUnwind.c in Sources */ = {isa = PBXBuildFile; fileRef = FCE45837C8C773EFFD15C52B /* PLCrashFrameStackUnwind.c */; };
@@ -1346,6 +1358,9 @@
C26022851642FCA6007FC29F /* PLCrashAsyncSymbolication.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = PLCrashAsyncSymbolication.c; sourceTree = "<group>"; };
C260228D1642FCAF007FC29F /* PLCrashAsyncSymbolication.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PLCrashAsyncSymbolication.h; sourceTree = "<group>"; };
C260228F1642FE9B007FC29F /* PLCrashAsyncSymbolicationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PLCrashAsyncSymbolicationTests.m; sourceTree = "<group>"; };
D81ACF0C1AB795CE001590A9 /* PLCrashDwarfLSDA.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PLCrashDwarfLSDA.cpp; sourceTree = "<group>"; };
D81ACF0D1AB795CE001590A9 /* PLCrashDwarfLSDA.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PLCrashDwarfLSDA.hpp; sourceTree = "<group>"; };
D81ACF0E1AB795CE001590A9 /* PLCrashDwarfLSDATests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PLCrashDwarfLSDATests.mm; sourceTree = "<group>"; };
FCE4522F86AC61C08E9DCC17 /* PLCrashFrameStackUnwind.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PLCrashFrameStackUnwind.h; sourceTree = "<group>"; };
FCE45837C8C773EFFD15C52B /* PLCrashFrameStackUnwind.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = PLCrashFrameStackUnwind.c; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -1945,6 +1960,9 @@
05E7483D175A384C009B8745 /* Decoding */ = {
isa = PBXGroup;
children = (
D81ACF0D1AB795CE001590A9 /* PLCrashDwarfLSDA.hpp */,
D81ACF0C1AB795CE001590A9 /* PLCrashDwarfLSDA.cpp */,
D81ACF0E1AB795CE001590A9 /* PLCrashDwarfLSDATests.mm */,
05E7486E1760D8AE009B8745 /* PLCrashAsyncDwarfCIE.hpp */,
05E748661760D890009B8745 /* PLCrashAsyncDwarfCIE.cpp */,
05E748711760DBBE009B8745 /* PLCrashAsyncDwarfCIETests.mm */,
@@ -2196,6 +2214,7 @@
054627AD11D998BB007891C7 /* PLCrashReportTextFormatter.h in Headers */,
054627BD11D99D06007891C7 /* PLCrashReportFormatter.h in Headers */,
05771CE313683EDD001DE4B1 /* PLCrashReportMachineInfo.h in Headers */,
D81ACF131AB79600001590A9 /* PLCrashDwarfLSDA.hpp in Headers */,
05771CE213683ED4001DE4B1 /* PLCrashReportProcessorInfo.h in Headers */,
05DEE6491636E642007E99DC /* PLCrashAsyncMObject.h in Headers */,
05D8FE5516ACAA81000ED70C /* PLCrashAsyncAllocator.h in Headers */,
@@ -2345,6 +2364,7 @@
buildActionMask = 2147483647;
files = (
05CD318B0EE93A90000FDE88 /* CrashReporter.h in Headers */,
D81ACF121AB795FF001590A9 /* PLCrashDwarfLSDA.hpp in Headers */,
05A04D8C15AB38C10011CFA4 /* PLCrashNamespace.h in Headers */,
05A5E28117A82751008A75E5 /* PLCrashMacros.h in Headers */,
05CD33A00EE948EB000FDE88 /* PLCrashSignalHandler.h in Headers */,
@@ -2904,6 +2924,7 @@
05A17DF316DBD0AD00888448 /* PLCrashAsyncThread_x86.c in Sources */,
05A17DF816DBD0C200888448 /* PLCrashAsyncThread_arm.c in Sources */,
05F3CD6216DD6A3B007911FB /* PLCrashFrameCompactUnwind.c in Sources */,
D81ACF1A1AB79616001590A9 /* PLCrashDwarfLSDA.cpp in Sources */,
05F3CD7A16DFC744007911FB /* PLCrashAsyncCompactUnwindEncoding.c in Sources */,
05E7484F175E5349009B8745 /* PLCrashAsyncDwarfPrimitives.cpp in Sources */,
05E748611760D64D009B8745 /* PLCrashAsyncDwarfFDE.cpp in Sources */,
@@ -2973,6 +2994,7 @@
05A17DF416DBD0AD00888448 /* PLCrashAsyncThread_x86.c in Sources */,
05A17DF916DBD0C200888448 /* PLCrashAsyncThread_arm.c in Sources */,
05F3CD6316DD6A3B007911FB /* PLCrashFrameCompactUnwind.c in Sources */,
D81ACF191AB79615001590A9 /* PLCrashDwarfLSDA.cpp in Sources */,
05F3CD7B16DFC744007911FB /* PLCrashAsyncCompactUnwindEncoding.c in Sources */,
057DCA18179C613200BDC648 /* PLCrashAsyncDwarfEncoding.cpp in Sources */,
05E74850175E5349009B8745 /* PLCrashAsyncDwarfPrimitives.cpp in Sources */,
@@ -3037,6 +3059,7 @@
052951EF1696A461006EDA8A /* PLCrashLogWriterEncodingTests.proto in Sources */,
05D8FE5016ACAA6E000ED70C /* PLCrashAsyncAllocator.c in Sources */,
05D8FE5816ACC8CD000ED70C /* PLCrashAsyncAllocatorTests.m in Sources */,
D81ACF0F1AB795D4001590A9 /* PLCrashDwarfLSDATests.mm in Sources */,
05A533DE16D6ACBF00C5E2B3 /* PLCrashFrameStackUnwindTests.m in Sources */,
05A17DB816D7E36400888448 /* PLCrashFrameStackUnwind.c in Sources */,
05A17DC916D7F81600888448 /* PLCrashAsyncThread.c in Sources */,
@@ -3083,6 +3106,7 @@
05507A531784DEE4009D5168 /* unwind_test_x86_frame.S in Sources */,
05C5880E1788CAA400BA118D /* unwind_test_x86_frameless.S in Sources */,
05C588121788F36800BA118D /* unwind_test_x86_frameless_big.S in Sources */,
D81ACF161AB79613001590A9 /* PLCrashDwarfLSDA.cpp in Sources */,
05C588161788F3E700BA118D /* unwind_test_x86_unusual.S in Sources */,
05C5881A1788F48500BA118D /* unwind_test_x86_disable_compact_frame.S in Sources */,
05920D36178B310A001E8975 /* unwind_test_arm.S in Sources */,
@@ -3127,6 +3151,7 @@
05EB2B1D15B6FE2A0066EB4D /* PLCrashReporterNSErrorTests.m in Sources */,
05F76DDF16305A7000A668C7 /* PLCrashAsyncMachOImage.c in Sources */,
05F76DDB162F238E00A668C7 /* PLCrashAsyncMachOImageTests.m in Sources */,
D81ACF101AB795D4001590A9 /* PLCrashDwarfLSDATests.mm in Sources */,
05DEE6441636E62B007E99DC /* PLCrashAsyncMObject.c in Sources */,
05DEE64C1636E721007E99DC /* PLCrashAsyncMObjectTests.m in Sources */,
C2198DDE1640188C006EB46A /* PLCrashAsyncObjCSection.cpp in Sources */,
@@ -3152,6 +3177,7 @@
05F3CD8216DFC78D007911FB /* PLCrashAsyncCompactUnwindEncodingTests.m in Sources */,
05659DF317456A4000D2EE21 /* PLCrashAsyncDwarfEncodingTests.mm in Sources */,
05A17DCA16D7F81600888448 /* PLCrashAsyncThread.c in Sources */,
D81ACF171AB79614001590A9 /* PLCrashDwarfLSDA.cpp in Sources */,
0518E0A7174BF82500BB47DE /* PLCrashAsyncThread_arm.c in Sources */,
0518E0A6174BF82300BB47DE /* PLCrashAsyncThread_x86.c in Sources */,
05659DFA174D2E1200D2EE21 /* PLCrashTestCase.m in Sources */,
@@ -3242,6 +3268,7 @@
052951F11696A461006EDA8A /* PLCrashLogWriterEncodingTests.proto in Sources */,
058484AE1804841100A56049 /* unwind_test_arm64_frameless.S in Sources */,
05D8FE5216ACAA6E000ED70C /* PLCrashAsyncAllocator.c in Sources */,
D81ACF111AB795D5001590A9 /* PLCrashDwarfLSDATests.mm in Sources */,
05D8FE5A16ACC8CD000ED70C /* PLCrashAsyncAllocatorTests.m in Sources */,
05A533E016D6ACBF00C5E2B3 /* PLCrashFrameStackUnwindTests.m in Sources */,
05A17DBA16D7E37100888448 /* PLCrashFrameStackUnwind.c in Sources */,
@@ -3288,6 +3315,7 @@
05507A511784DA8A009D5168 /* unwind_test_x86_64_unusual.S in Sources */,
05507A551784DEE4009D5168 /* unwind_test_x86_frame.S in Sources */,
05C588101788CAA400BA118D /* unwind_test_x86_frameless.S in Sources */,
D81ACF181AB79614001590A9 /* PLCrashDwarfLSDA.cpp in Sources */,
05C588141788F36800BA118D /* unwind_test_x86_frameless_big.S in Sources */,
05C588181788F3E700BA118D /* unwind_test_x86_unusual.S in Sources */,
05C5881C1788F48500BA118D /* unwind_test_x86_disable_compact_frame.S in Sources */,
@@ -3358,6 +3386,7 @@
05A17DC516D7F81600888448 /* PLCrashAsyncThread.c in Sources */,
05A17DF116DBD0AD00888448 /* PLCrashAsyncThread_x86.c in Sources */,
05A17DF616DBD0C200888448 /* PLCrashAsyncThread_arm.c in Sources */,
D81ACF151AB79611001590A9 /* PLCrashDwarfLSDA.cpp in Sources */,
05F3CD6016DD6A3B007911FB /* PLCrashFrameCompactUnwind.c in Sources */,
05F3CD7816DFC744007911FB /* PLCrashAsyncCompactUnwindEncoding.c in Sources */,
05E7484D175E5349009B8745 /* PLCrashAsyncDwarfPrimitives.cpp in Sources */,
@@ -3436,6 +3465,7 @@
05A17DC616D7F81600888448 /* PLCrashAsyncThread.c in Sources */,
05A17DF216DBD0AD00888448 /* PLCrashAsyncThread_x86.c in Sources */,
05A17DF716DBD0C200888448 /* PLCrashAsyncThread_arm.c in Sources */,
D81ACF141AB79610001590A9 /* PLCrashDwarfLSDA.cpp in Sources */,
05F3CD6116DD6A3B007911FB /* PLCrashFrameCompactUnwind.c in Sources */,
05F3CD7916DFC744007911FB /* PLCrashAsyncCompactUnwindEncoding.c in Sources */,
05659DEE17455DED00D2EE21 /* PLCrashAsyncDwarfEncoding.cpp in Sources */,
@@ -3669,6 +3699,7 @@
Foundation,
"-framework",
UIKit,
"-lstdc++",
);
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
@@ -3696,6 +3727,7 @@
Foundation,
"-framework",
UIKit,
"-lstdc++",
);
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;

312
Source/PLCrashDwarfLSDA.cpp Normal file
View File

@@ -0,0 +1,312 @@
/*
* Author: Joe Ranieri <joe@alacatialabs.com>
*
* Copyright (c) 2015 Plausible Labs Cooperative, Inc.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "PLCrashDwarfLSDA.hpp"
#include "PLCrashAsyncDwarfPrimitives.hpp"
#if PLCRASH_FEATURE_UNWIND_DWARF
using namespace plcrash;
using namespace plcrash::async;
/**
* Determines how many bytes are required by a given DWARF pointer encoding.
*/
template <typename machine_ptr>
static plcrash_error_t dwarf_encoding_size (uint8_t encoding, uint8_t *size)
{
if (encoding == DW_EH_PE::DW_EH_PE_omit) {
*size = 0;
return PLCRASH_ESUCCESS;
}
switch (encoding & 0x0F) {
case DW_EH_PE::DW_EH_PE_absptr:
*size = sizeof(machine_ptr);
return PLCRASH_ESUCCESS;
case DW_EH_PE::DW_EH_PE_udata2:
*size = sizeof(uint16_t);
return PLCRASH_ESUCCESS;
case DW_EH_PE::DW_EH_PE_udata4:
*size = sizeof(uint32_t);
return PLCRASH_ESUCCESS;
case DW_EH_PE::DW_EH_PE_udata8:
*size = sizeof(uint64_t);
return PLCRASH_ESUCCESS;
case DW_EH_PE::DW_EH_PE_sdata2:
*size = sizeof(int16_t);
return PLCRASH_ESUCCESS;
case DW_EH_PE::DW_EH_PE_sdata4:
*size = sizeof(int32_t);
return PLCRASH_ESUCCESS;
case DW_EH_PE::DW_EH_PE_sdata8:
*size = sizeof(int64_t);
return PLCRASH_ESUCCESS;
default:
return PLCRASH_EINVALID_DATA;
}
}
/**
* Decodes all of the action records associated with a single callsite.
*/
template <typename machine_ptr>
static plcrash_error_t read_action_table (plcrash_async_mobject_t *mobj,
pl_vm_address_t first,
std::vector<dwarf_lsda_action_record> &result,
gnu_ehptr_reader<machine_ptr> *ptr_reader,
pl_vm_address_t ttype_base,
DW_EH_PE ttype_encoding)
{
plcrash_error_t err = PLCRASH_ESUCCESS;
/* All items in the types table have the same encoding and length, allowing
* random access into it. */
uint8_t ttype_size;
if ((err = dwarf_encoding_size<machine_ptr>(ttype_encoding, &ttype_size)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Invalid ttype_encoding in LSDA: %i", ttype_encoding);
return err;
}
/* Each action record is a type filter and the relative offset to the next
* action record. An offset of zero means there are no more records. */
pl_vm_address_t address = first;
while (true) {
pl_vm_off_t offset = 0;
pl_vm_size_t size = 0;
int64_t filter;
if ((err = plcrash_async_dwarf_read_sleb128(mobj, address, offset, &filter, &size)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Failed to read action record filter");
return err;
}
offset += size;
int64_t next_offset;
if ((err = plcrash_async_dwarf_read_sleb128(mobj, address, offset, &next_offset, &size)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Failed to read action record offset");
return err;
}
/* If the filter is positive, this represents a simple catch. If the
* filter is negative, it's an exception specification which can have
* an arbitrary number of types. */
dwarf_lsda_action_record action;
if (filter > 0) {
machine_ptr type_info = 0;
if ((err = ptr_reader->read(mobj, ttype_base - (filter * ttype_size), 0, ttype_encoding, &type_info, &size)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Failed to dereference type info (filter = %lli)", filter);
return err;
}
action.kind = DWARF_LSDA_ACTION_KIND_CATCH;
action.types.push_back(type_info);
} else {
/* The exception specification type indexes list is a series of
* uleb128s that is terminated by 0. The filter we're given is a
* byte offset into the table (not an index). */
pl_vm_off_t spec_offset = -filter - 1;
action.kind = DWARF_LSDA_ACTION_KIND_EXCEPTION_SPECIFICATION;
while (true) {
uint64_t filter_index;
if ((err = plcrash_async_dwarf_read_uleb128(mobj, ttype_base, spec_offset, &filter_index, &size)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Failed to read action record filter");
return err;
}
spec_offset += size;
if (filter_index == 0)
break;
machine_ptr type_info = 0;
if ((err = ptr_reader->read(mobj, ttype_base - (filter_index * ttype_size), 0, ttype_encoding, &type_info, &size)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Failed to dereference type info (filter = %lli)", filter_index);
return err;
}
action.types.push_back(type_info);
}
}
result.push_back(action);
if (next_offset == 0)
break;
address = address + offset + next_offset;
}
return PLCRASH_ESUCCESS;
}
/**
* Decode LSDA info at target-relative @a address.
*
* Any resources held by a successfully initialized instance must be freed via
* plcrash_async_dwarf_lsda_info_free();
*
* @param info The LSDA record to be initialized.
* @param mobj The memory object containing the LSDA at the start address.
* @param byteorder The byte order of the data referenced by @a mobj.
* @param address The target-relative address containing the LSDA info to be
* decoded.
*/
template <typename machine_ptr>
plcrash_error_t plcrash::dwarf_lsda_info_init (dwarf_lsda_info_t *info,
plcrash_async_mobject_t *mobj,
const plcrash_async_byteorder_t *byteorder,
pl_vm_address_t address)
{
plcrash_error_t err;
pl_vm_size_t size;
pl_vm_off_t offset = 0;
gnu_ehptr_reader<machine_ptr> ptr_reader(byteorder);
DW_EH_PE lp_start_encoding = DW_EH_PE_omit;
if ((err = plcrash_async_mobject_read_uint8(mobj, address, offset, (uint8_t *)&lp_start_encoding)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Invalid lp_start_encoding in lsda");
return err;
}
offset += 1;
if (lp_start_encoding != DW_EH_PE_omit) {
machine_ptr ptr = 0;
if ((err = ptr_reader.read(mobj, address, offset, lp_start_encoding, &ptr, &size)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Couldn't read lp_start (encoding %i)", lp_start_encoding);
return err;
}
info->has_lp_start = true;
info->lp_start = ptr;
offset += size;
} else {
info->has_lp_start = false;
info->lp_start = 0;
}
DW_EH_PE ttype_encoding = DW_EH_PE_omit;
if ((err = plcrash_async_mobject_read_uint8(mobj, address, offset, (uint8_t *)&ttype_encoding)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Couldn't read ttype_encoding");
return err;
}
offset += 1;
pl_vm_address_t ttype_base = 0;
if (ttype_encoding != DW_EH_PE_omit) {
uint64_t ttypeOffset = 0;
if ((err = plcrash_async_dwarf_read_uleb128(mobj, address, offset, &ttypeOffset, &size)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Couldn't read ttype_base");
return err;
}
offset += size;
ttype_base = address + offset + ttypeOffset;
}
DW_EH_PE call_site_encoding = DW_EH_PE_omit;
if ((err = plcrash_async_mobject_read_uint8(mobj, address, offset, (uint8_t *)&call_site_encoding)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Couldn't read call_site_encoding");
return err;
}
offset += 1;
uint64_t call_site_table_length = 0;
if ((err = plcrash_async_dwarf_read_uleb128(mobj, address, offset, &call_site_table_length, &size)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Couldn't read call_site_table_length");
return err;
}
offset += size;
/* After all of that header information is the callsite table, which encodes
* what should happen for ranges of code in the function. We do not know
* how many items are in it, but we do know its total length. */
uint64_t call_site_start = offset;
while (offset - call_site_start < call_site_table_length) {
dwarf_lsda_callsite_info_t callsite;
machine_ptr ptr = 0;
if ((err = ptr_reader.read(mobj, address, offset, call_site_encoding, &ptr, &size)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Couldn't read call site's start");
return err;
}
/* Despite what the spec says, this pointer appears to be relative to
* the start of the function and not the previous callsite. */
callsite.start = ptr;
offset += size;
if ((err = ptr_reader.read(mobj, address, offset, call_site_encoding, &ptr, &size)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Couldn't read call site's length");
return err;
}
callsite.length = ptr;
offset += size;
if ((err = ptr_reader.read(mobj, address, offset, call_site_encoding, &ptr, &size)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Couldn't read call site's landing pad offset");
return err;
}
callsite.landing_pad_offset = ptr;
offset += size;
uint8_t action = 0;
if ((err = plcrash_async_mobject_read_uint8(mobj, address, offset, &action)) != PLCRASH_ESUCCESS) {
PLCF_DEBUG("Couldn't read call site's action");
return err;
}
offset += 1;
/* The action is a 1-based index into the actions table. If the value is
* zero and the landing pad is zero, the range of code is assumed not
* to throw. If the value is zero and there is a landing pad, this is
* a cleanup. */
if (action > 0) {
pl_vm_address_t action_table = address + call_site_start + call_site_table_length;
err = read_action_table(mobj, action_table + action - 1, callsite.actions, &ptr_reader, ttype_base, ttype_encoding);
if (err != PLCRASH_ESUCCESS)
return err;
}
info->callsites.push_back(callsite);
}
return PLCRASH_ESUCCESS;
}
void plcrash::dwarf_lsda_info_free (dwarf_lsda_info_t *info)
{
/* All resources will automatically be freed by the destructor. */
}
namespace plcrash {
template
plcrash_error_t dwarf_lsda_info_init<uint32_t> (dwarf_lsda_info_t *info,
plcrash_async_mobject_t *mobj,
const plcrash_async_byteorder_t *byteorder,
pl_vm_address_t address);
template
plcrash_error_t dwarf_lsda_info_init<uint64_t> (dwarf_lsda_info_t *info,
plcrash_async_mobject_t *mobj,
const plcrash_async_byteorder_t *byteorder,
pl_vm_address_t address);
}
#endif /* PLCRASH_FEATURE_UNWIND_DWARF */

125
Source/PLCrashDwarfLSDA.hpp Normal file
View File

@@ -0,0 +1,125 @@
/*
* Author: Joe Ranieri <joe@alacatialabs.com>
*
* Copyright (c) 2015 Plausible Labs Cooperative, Inc.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef PLCRASH_ASYNC_DWARF_LSDA_H
#define PLCRASH_ASYNC_DWARF_LSDA_H
#include "PLCrashAsync.h"
#include "PLCrashAsyncMObject.h"
#include "PLCrashAsyncDwarfPrimitives.hpp"
#include "PLCrashFeatureConfig.h"
#include <vector>
#if PLCRASH_FEATURE_UNWIND_DWARF
namespace plcrash {
/**
* @internal
* @ingroup plcrash_async_dwarf
* @{
*/
struct dwarf_lsda_callsite_info;
struct dwarf_lsda_action_record;
/**
* A DWARF Language Specific Data Area (LSDA) record. This describes how to
* handle exceptions within a function.
*/
typedef struct dwarf_lsda_info {
/** Is the lp_start field present in the LSDA? */
bool has_lp_start;
/**
* The address that all landing pads described by the LSDA are relative to.
* If this field is absent, landing pads are relative to the beginning of
* the function.
*/
uintptr_t lp_start;
std::vector<dwarf_lsda_callsite_info> callsites;
} dwarf_lsda_info_t;
/**
* A DWARF exception handling callsite record. This represents a range of code
* in a function and describes where to jump if an exception is raised in that
* range.
*/
typedef struct dwarf_lsda_callsite_info {
/** The byte offset from the start of the function for this range. */
pl_vm_address_t start;
/** The number of bytes this range covers. */
pl_vm_off_t length;
/**
* The offset to the landing pad in bytes, measured from the start of the
* function or, if specified, the lp_start field in the lsda header.
*/
pl_vm_off_t landing_pad_offset;
/**
* A collection of actions that the personality function will use to
* dispatch the exception or perform validation.
*/
std::vector<dwarf_lsda_action_record> actions;
} dwarf_lsda_callsite_info_t;
typedef enum {
DWARF_LSDA_ACTION_KIND_CATCH,
DWARF_LSDA_ACTION_KIND_EXCEPTION_SPECIFICATION
} dwarf_lsda_action_kind_t;
/**
* A DWARF Exception handling action record. This describes either a type that
* can be caught or a set of types that is allowed to be thrown.
*/
typedef struct dwarf_lsda_action_record {
dwarf_lsda_action_kind_t kind;
/**
* The type information that this action matches against. For catch actions,
* this is the type that can be caught and will have one item. For
* exception specifications, this will be all of the types that the code is
* allowed to throw.
*/
std::vector<pl_vm_address_t> types;
} dwarf_lsda_action_record_t;
template <typename machine_ptr>
plcrash_error_t dwarf_lsda_info_init (dwarf_lsda_info_t *info,
plcrash_async_mobject_t *mobj,
const plcrash_async_byteorder_t *byteorder,
pl_vm_address_t address);
void dwarf_lsda_info_free (dwarf_lsda_info_t *info);
/**
* @}
*/
}
#endif
#endif /* defined(PLCRASH_ASYNC_DWARF_LSDA_H) */

View File

@@ -0,0 +1,216 @@
/*
* Author: Joe Ranieri <joe@alacatialabs.com>
*
* Copyright (c) 2015 Plausible Labs Cooperative, Inc.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#import "PLCrashTestCase.h"
#include "PLCrashDwarfLSDA.hpp"
#include "PLCrashFeatureConfig.h"
#ifdef try
#undef try
#endif
#ifdef catch
#undef catch
#endif
#if PLCRASH_FEATURE_UNWIND_DWARF
using namespace plcrash;
using namespace plcrash::async;
@interface PLCrashAsyncDwarfLSDATests : PLCrashTestCase {
}
@end
@implementation PLCrashAsyncDwarfLSDATests
- (void) setUp {
}
- (void) tearDown {
}
- (void) testSimpleCleanup {
static const uint8_t lsda[] = {
0xff, // @LPStart Encoding = omit
0x9b, // @TType Encoding = indirect pcrel sdata4
0x29, // @TType base offset
0x03, // Call site Encoding = udata4
0x0d, // Call site table length
// >> Call Site 1 <<
0x00, 0x00, 0x00, 0x00, //
0xa2, 0x00, 0x00, 0x00, // Call between Leh_func_begin0 and Ltmp0
0x00, 0x00, 0x00, 0x00, // has no landing pad
0x00, // On action: cleanup
};
plcrash_async_mobject mobj;
plcrash_error_t err = plcrash_async_mobject_init(&mobj, mach_task_self(), (pl_vm_address_t)lsda, sizeof(lsda), true);
dwarf_lsda_info_t info;
err = dwarf_lsda_info_init<uint64_t>(&info, &mobj, &plcrash_async_byteorder_direct, (pl_vm_address_t)lsda);
STAssertEquals(err, PLCRASH_ESUCCESS, @"Failed to parse LSDA");
STAssertEquals(info.has_lp_start, false, @"Incorrect has_lp_start");
STAssertEquals(info.callsites.size(), 1, @"Incorrect number of call sites");
STAssertEquals(info.callsites[0].start, 0x00, @"callsite 1's start is wrong");
STAssertEquals(info.callsites[0].length, 0xA2, @"callsite 1's length is wrong");
STAssertEquals(info.callsites[0].landing_pad_offset, 0, @"callsite 1's landing pad is wrong");
STAssertEquals(info.callsites[0].actions.size(), 0, @"callsite 1 shouldn't have actions");
dwarf_lsda_info_free(&info);
plcrash_async_mobject_free(&mobj);
}
- (void) testExceptionSpecifications {
// A simple function with an exception specification.
static const uint8_t lsda[] = {
0xff, // LPStart Encoding = omit
0x9b, // @TType Encoding = indirect pcrel sdata4
0x99, 0x80, 0x80, 0x00, // @TType base offset
0x03, // Call site Encoding = udata4
0x0d, // Call site table length
// >> Call Site 1 <<
0x08, 0x00, 0x00, 0x00, //
0x0c, 0x00, 0x00, 0x00, // Call between Ltmp0 and Ltmp1
0x22, 0x00, 0x00, 0x00, // jumps to Ltmp2
0x01, // On action: 1
// >> Action Record 1 <<
0x7f, // Filter TypeInfo -1
0x00, // No further actions
// >> Catch TypeInfos <<
0x0B, 0x00, 0x00, 0x00, // TypeInfo 2
0x0F, 0x00, 0x00, 0x00, // TypeInfo 1
// >> Filter TypeInfos <<
0x01, // FilterInfo -1
0x02, // FilterInfo -2
0x00,
// >> Non-LSDA data <<
0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
};
plcrash_async_mobject mobj;
plcrash_error_t err = plcrash_async_mobject_init(&mobj, mach_task_self(), (pl_vm_address_t)lsda, sizeof(lsda), true);
dwarf_lsda_info_t info;
err = dwarf_lsda_info_init<uint64_t>(&info, &mobj, &plcrash_async_byteorder_direct, (pl_vm_address_t)lsda);
STAssertEquals(err, PLCRASH_ESUCCESS, @"Failed to parse LSDA");
STAssertEquals(info.has_lp_start, false, @"Incorrect has_lp_start");
STAssertEquals(info.callsites.size(), 1, @"Incorrect number of call sites");
STAssertEquals(info.callsites[0].start, 0x08, @"callsite 1's start is wrong");
STAssertEquals(info.callsites[0].length, 0x0C, @"callsite 1's length is wrong");
STAssertEquals(info.callsites[0].landing_pad_offset, 0x22, @"callsite 1's landing pad is wrong");
STAssertEquals(info.callsites[0].actions.size(), 1, @"callsite 1's should have one action");
STAssertEquals(info.callsites[0].actions[0].kind, DWARF_LSDA_ACTION_KIND_EXCEPTION_SPECIFICATION, @"wrong action kind");
STAssertEquals(info.callsites[0].actions[0].types.size(), 2, @"wrong type size");
STAssertEquals(info.callsites[0].actions[0].types[0], 0x3F3F3F3F3F3F3F3F, @"wrong type value");
STAssertEquals(info.callsites[0].actions[0].types[1], 0x2A2A2A2A2A2A2A2A, @"wrong type value");
dwarf_lsda_info_free(&info);
plcrash_async_mobject_free(&mobj);
}
- (void) testMultipleCatches {
// A function with a try block with several catches.
static const uint8_t lsda[] = {
0xff, // @LPStart Encoding = omit
0x9b, // @TType Encoding = indirect pcrel sdata4
0xae, 0x80, 0x00, // @TType base offset
0x03, // Call site Encoding = udata4
0x1a, // Call site table length
// >> Call Site 1 <<
0x00, 0x00, 0x00, 0x00, //
0x1e, 0x00, 0x00, 0x00, // Call between Ltmp0 and Ltmp1
0x00, 0x00, 0x00, 0x00, // has no landing pad
0x00, // On action: cleanup
// >> Call Site 2 <<
0x1e, 0x00, 0x00, 0x00, //
0x13, 0x00, 0x00, 0x00, // Call between Ltmp0 and Ltmp1
0x36, 0x00, 0x00, 0x00, // jumps to Ltmp2
0x05, // On action: 3
// >> Action Record 1 <<
0x01, // Catch TypeInfo 1
0x00, // No further actions
// >> Action Record 2 <<
0x02, // Catch TypeInfo 2
0x7d, // Continue to action 1
/// >> Action Record 3 <<
0x03, // Catch TypeInfo 3
0x7d, // Continue to action 2
// >> Catch TypeInfos <<
0x0C, 0x00, 0x00, 0x00, // TypeInfo 3
0x10, 0x00, 0x00, 0x00, // TypeInfo 2
0x00, 0x00, 0x00, 0x00, // TypeInfo 1
// >> Non-LSDA data <<
0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
};
plcrash_async_mobject mobj;
plcrash_error_t err = plcrash_async_mobject_init(&mobj, mach_task_self(), (pl_vm_address_t)lsda, sizeof(lsda), true);
dwarf_lsda_info_t info;
err = dwarf_lsda_info_init<uint64_t>(&info, &mobj, &plcrash_async_byteorder_direct, (pl_vm_address_t)lsda);
STAssertEquals(err, PLCRASH_ESUCCESS, @"Failed to parse LSDA");
STAssertEquals(info.has_lp_start, false, @"Incorrect has_lp_start");
STAssertEquals(info.callsites.size(), 2, @"Incorrect number of call sites");
STAssertEquals(info.callsites[0].start, 0, @"callsite 1's start is wrong");
STAssertEquals(info.callsites[0].length, 0x1e, @"callsite 1's length is wrong");
STAssertEquals(info.callsites[0].landing_pad_offset, 0, @"callsite 1's landing pad is wrong");
STAssertEquals(info.callsites[0].actions.size(), 0, @"callsite 1's shouldn't have actions");
STAssertEquals(info.callsites[1].start, 0x1e, @"callsite 2's start is wrong");
STAssertEquals(info.callsites[1].length, 0x13, @"callsite 2's length is wrong");
STAssertEquals(info.callsites[1].landing_pad_offset, 0x36, @"callsite 2's landing pad is wrong");
STAssertEquals(info.callsites[1].actions.size(), 3, @"callsite 2's action count is wrong");
STAssertEquals(info.callsites[1].actions[0].kind, DWARF_LSDA_ACTION_KIND_CATCH, @"wrong action kind");
STAssertEquals(info.callsites[1].actions[0].types.size(), 1, @"wrong type size");
STAssertEquals(info.callsites[1].actions[0].types[0], 0x2A2A2A2A2A2A2A2A, @"wrong type value");
STAssertEquals(info.callsites[1].actions[1].kind, DWARF_LSDA_ACTION_KIND_CATCH, @"wrong action kind");
STAssertEquals(info.callsites[1].actions[1].types.size(), 1, @"wrong type size");
STAssertEquals(info.callsites[1].actions[1].types[0], 0x3F3F3F3F3F3F3F3F, @"wrong type value");
STAssertEquals(info.callsites[1].actions[2].kind, DWARF_LSDA_ACTION_KIND_CATCH, @"wrong action kind");
STAssertEquals(info.callsites[1].actions[2].types.size(), 1, @"wrong type size");
STAssertEquals(info.callsites[1].actions[2].types[0], 0, @"wrong type value");
dwarf_lsda_info_free(&info);
plcrash_async_mobject_free(&mobj);
}
@end
#endif /* PLCRASH_FEATURE_UNWIND_DWARF */