summaryrefslogtreecommitdiffstats
path: root/lldb/source/Plugins/Process
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/source/Plugins/Process')
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/scripts/cc-swig47
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/scripts/config.pl71
-rwxr-xr-xlldb/source/Plugins/Process/MacOSX-User/scripts/test-ProcessDebug.pl409
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.cpp575
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.h148
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.cpp674
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.h138
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext.h48
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.cpp1884
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.h63
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.cpp245
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.h57
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.cpp255
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.h72
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.cpp195
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.h36
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.cpp183
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.h63
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/ProcessControl-mig.defs16
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.cpp2228
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.h490
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.cpp124
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.h62
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.cpp1819
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.h206
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.cpp1448
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.h302
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.cpp1202
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.h256
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.cpp1328
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.h261
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.cpp769
-rw-r--r--lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.h159
-rw-r--r--lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.cpp327
-rw-r--r--lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.h83
-rw-r--r--lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.cpp306
-rw-r--r--lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.h22
-rw-r--r--lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp255
-rw-r--r--lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h83
-rw-r--r--lldb/source/Plugins/Process/Utility/UnwindLibUnwind.cpp73
-rw-r--r--lldb/source/Plugins/Process/Utility/UnwindLibUnwind.h66
-rw-r--r--lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp243
-rw-r--r--lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h77
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/include/libunwind.h509
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/include/mach-o/compact_unwind_encoding.h212
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/include/unwind.h213
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/AddressSpace.hpp456
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/ArchDefaultUnwinder.hpp115
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyInstructions.hpp147
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyParser.hpp409
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/CompactUnwinder.hpp1019
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/DwarfInstructions.hpp1686
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/DwarfParser.hpp869
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/FileAbstraction.hpp135
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/InternalMacros.h89
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/Registers.hpp985
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/Registers.s261
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/RemoteDebuggerDummyUnwinder.hpp88
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/RemoteProcInfo.hpp977
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/RemoteRegisterMap.hpp405
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/RemoteUnwindProfile.h85
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/Unwind-sjlj.c466
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/UnwindCursor.hpp1307
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1-gcc-ext.c282
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1.c443
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/dwarf2.h245
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/libunwind_priv.h35
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/libuwind.cxx421
-rw-r--r--lldb/source/Plugins/Process/Utility/libunwind/src/unw_getcontext.s229
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp813
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h270
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp508
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h250
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/GDBServer.cpp1148
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/GDBServerLog.cpp80
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/GDBServerLog.h55
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp2272
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h404
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp121
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h53
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp296
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h156
82 files changed, 35882 insertions, 0 deletions
diff --git a/lldb/source/Plugins/Process/MacOSX-User/scripts/cc-swig b/lldb/source/Plugins/Process/MacOSX-User/scripts/cc-swig
new file mode 100644
index 00000000000..0bb089a653e
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/scripts/cc-swig
@@ -0,0 +1,47 @@
+#!/usr/bin/perl
+
+use File::Basename;
+
+sub execute_command
+{
+ print join(' ', @_), "\n";
+ if (scalar(@_) > 0) {
+ system(@_);
+ } else {
+ system($_[0]);
+ }
+}
+
+my $infile = $ENV{SCRIPT_INPUT_FILE_1};
+my($in_basename, $in_dirname, $in_extension) = fileparse($infile, qr/\.[^.]*/);
+my $outdir = "$ENV{DERIVED_FILE_DIR}";
+my $perl_wrap_c = "$outdir/${in_basename}_perl_wrap.c";
+mkdir "$ENV{OBJECT_FILE_DIR}";
+my $perl_wrap_o = "$ENV{OBJECT_FILE_DIR}/${in_basename}_perl_wrap.o";
+my $perl_module = "$outdir/${in_basename}.pm";
+my $header_paths = "-I'../../../../../debugcore/source' -I'../../../../../DebugBase'";
+my $framework_opts = "-F'$ENV{CONFIGURATION_BUILD_DIR}' ";
+execute_command("/usr/bin/swig -shadow -perl5 -DHAS_BOOL $header_paths -outdir '$outdir' -o '$perl_wrap_c' '$infile'");
+
+# Get any needed perl options for the next compile
+my $ccopts = `perl -MExtUtils::Embed -e ccopts`;
+my $libperl_dir = undef;
+if ($ccopts =~ /-I(\/System.*CORE)/)
+{
+ $libperl_dir = $1;
+ print "libperl directory: '$libperl_dir'\n";
+}
+
+execute_command("cd '$ENV{OBJECT_FILE_DIR}' && ln -s '$libperl_dir/libperl.dylib'");
+
+
+# Strip out the default architectures it gave us, we will add them back with
+# the $arch_opts below
+$ccopts =~ s/-arch [a-z_0-9]+//g;
+
+# Get a list of our build architectures
+my $arch_opts = "-arch " . join(' -arch ', split('\s+', $ENV{ARCHS}));
+
+execute_command("gcc -c -Dbool=char $arch_opts $ccopts $header_paths $framework_opts -I'$ENV{PROJECT_DIR}/source' '$perl_wrap_c' -o '$perl_wrap_o'");
+
+execute_command("cp '$perl_module' '$ENV{CONFIGURATION_BUILD_DIR}/$ENV{SHARED_SUPPORT_FOLDER_PATH}'"); \ No newline at end of file
diff --git a/lldb/source/Plugins/Process/MacOSX-User/scripts/config.pl b/lldb/source/Plugins/Process/MacOSX-User/scripts/config.pl
new file mode 100644
index 00000000000..a6cf6ce2396
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/scripts/config.pl
@@ -0,0 +1,71 @@
+#!/usr/bin/perl
+
+use strict;
+my $config_file = "$ENV{SCRIPT_OUTPUT_FILE_0}";
+
+# Define the tests we need to run during this configuration
+my @config_tests = (
+ {
+ NAME => "HAVE_64_BIT_MACH_EXCEPTIONS",
+ TEST => "-e '$ENV{SDKROOT}/usr/include/mach/mach_exc.defs'",
+ COMMENT => "// Defined if we can use 64 bit mach exceptions",
+ FAIL => "#undef HAVE_64_BIT_MACH_EXCEPTIONS\
+#define mach_exception_data_t exception_data_t\
+#define mach_exception_data_type_t exception_data_type_t\
+#define mach_exc_server exc_server\
+#define MACH_EXCEPTION_CODES 0\n",
+ SUCCESS => "#define HAVE_64_BIT_MACH_EXCEPTIONS 1\n",
+ }
+);
+
+#----------------------------------------------------------------------
+# Open the config file
+#----------------------------------------------------------------------
+open(CONFIG, "> $config_file") || die "Couldn't open '$config_file' for writing: $!\n";
+print CONFIG "/*" . "-" x 72 . "\n";
+print CONFIG "// This file is auto generated by a config.pl, do not edit by hand!\n";
+print CONFIG "//" . "-" x 72 . "\n";
+print CONFIG "// COMMAND LINE\n";
+print CONFIG "// " . join(' ', @ARGV) . "\n";
+print CONFIG "//" . "-" x 72 . "\n";
+print CONFIG "// ENVIRONMENT\n";
+my $key;
+my $val;
+while (($key, $val) = each %ENV)
+{
+ printf CONFIG "// %s = %s\n", $key, $val;
+}
+print CONFIG "//" . "-" x 72 . "\n";
+print CONFIG "// SETTINGS\n";
+print CONFIG "// config_file: '$config_file'\n";
+print CONFIG "//" . "-" x 72 . "\n";
+print CONFIG "*/\n\n";
+print CONFIG "#ifndef liblldb_PDConfig_h_\n";
+print CONFIG "#define liblldb_PDConfig_h_\n";
+
+
+#----------------------------------------------------------------------
+# Run the tests
+#----------------------------------------------------------------------
+foreach my $test_href (@config_tests)
+{
+ if (exists $test_href->{COMMENT}) {
+ print CONFIG "\n$test_href->{COMMENT}\n";
+ } else {
+ print CONFIG "\n// $test_href->{NAME}\n";
+ }
+
+ my $test_result = eval "$test_href->{TEST}";
+ if ($test_result != 0)
+ {
+ print CONFIG "$test_href->{SUCCESS}\n";
+ }
+ else
+ {
+ print CONFIG "$test_href->{FAIL}\n";
+ }
+}
+
+print CONFIG "#endif // #ifndef liblldb_PDConfig_h_\n";
+close(CONFIG);
+
diff --git a/lldb/source/Plugins/Process/MacOSX-User/scripts/test-ProcessDebug.pl b/lldb/source/Plugins/Process/MacOSX-User/scripts/test-ProcessDebug.pl
new file mode 100755
index 00000000000..96b3115c912
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/scripts/test-ProcessDebug.pl
@@ -0,0 +1,409 @@
+#!/usr/bin/perl
+
+use strict;
+use Cwd 'abs_path';
+our $home = $ENV{HOME} || die "ERROR: Couldn't deduce your home directory...\n";
+
+our @inc_paths = (
+ './include',
+);
+
+my $inc_paths_added = 0;
+foreach my $inc_path (@inc_paths)
+{
+ if (-e $inc_path)
+ {
+ push (@INC, abs_path($inc_path));
+ $inc_paths_added++;
+ }
+}
+
+if ($inc_paths_added == 0)
+{
+ die "Please compile the Release version of lldb\n";
+}
+
+require lldb;
+
+# my $state = lldb::eStateAttaching;
+
+use constant UINT32_MAX => 4294967295;
+
+#----------------------------------------------------------------------
+# Interactive Commands
+#----------------------------------------------------------------------
+our %commands = (
+ break => {
+ name => 'break', # in case an alias is used to get to this command
+ description => "Sets a breakpoint.",
+ usage => ["break ADDR"],
+ function => \&command_set_breakpoint,
+ runs_target => 0,
+ },
+ delete => {
+ name => 'delete', # in case an alias is used to get to this command
+ description => "Deletes one or more breakpoints by ID.\
+If no breakpoint IDs are given all breakpoints will be deleted.\
+If one or more IDs are given, only those breakpoints will be deleted.",
+ usage => ["delete [ID1 ID2 ...]"],
+ function => \&command_clear_breakpoint,
+ runs_target => 0,
+ },
+ continue => {
+ name => 'continue', # in case an alias is used to get to this command
+ description => "Continues target execution.",
+ usage => ["continue [ADDR]"],
+ function => \&command_continue,
+ runs_target => 1
+ },
+ step => {
+ name => 'step', # in case an alias is used to get to this command
+ description => "Single steps one instruction.",
+ usage => ["step"],
+ function => \&command_step,
+ runs_target => 1
+ },
+ info => {
+ name => 'info', # in case an alias is used to get to this command
+ description => "Gets info on a variety of things.",
+ usage => ["info reg", "info thread", "info threads"],
+ function => \&command_info,
+ runs_target => 0
+ },
+ help => {
+ name => 'help', # in case an alias is used to get to this command
+ description => "Displays a list of all commands, or help for a specific command.",
+ usage => ["help", "help CMD"],
+ function => \&command_help,
+ runs_target => 0
+ }
+);
+
+#----------------------------------------------------------------------
+# Command aliases
+#----------------------------------------------------------------------
+our %aliases = (
+ b => $commands{break},
+ c => $commands{continue},
+ s => $commands{step},
+ d => $commands{delete},
+ h => $commands{help}
+);
+
+our $opt_g = 0; # Enable verbose debug logging
+our $opt_v = 0; # Verbose mode
+my $prev_command_href = undef;
+my $stdio = '/dev/stdin';
+my $launch = 0;
+my @env = ();
+my @break_ids;
+
+#----------------------------------------------------------------------
+# Given a command string, return the command hash reference for it, or
+# undef if it doesn't exist.
+#----------------------------------------------------------------------
+sub get_command_hash_ref
+{
+ my $cmd = shift;
+ my $cmd_href = undef;
+ if (length($cmd) == 0) { $cmd_href = $prev_command_href; }
+ elsif (exists $aliases{$cmd}) { $cmd_href = $aliases{$cmd}; }
+ elsif (exists $commands{$cmd}) { $cmd_href = $commands{$cmd}; }
+ defined $cmd_href and $prev_command_href = $cmd_href;
+ return $cmd_href;
+}
+
+#----------------------------------------------------------------------
+# Set a breakpoint
+#----------------------------------------------------------------------
+sub command_set_breakpoint
+{
+ my $pid = shift;
+ my $tid = shift;
+ $opt_g and print "command_set_breakpoint (pid = $pid, locations = @_)\n";
+ foreach my $location (@_)
+ {
+ my $success = 0;
+ my $address = hex($location);
+ if ($address != 0)
+ {
+ my $break_id = lldb::PDBreakpointSet ($pid, $address, 1, 0);
+ if ($break_id != $lldb::PD_INVALID_BREAK_ID)
+ {
+ printf("Breakpoint %i is set.\n", $break_id);
+ push(@break_ids, $break_id);
+ $success = 1;
+ }
+ }
+ $success or print("error: failed to set breakpoint at $location.\n");
+ }
+ return 1;
+}
+
+#----------------------------------------------------------------------
+# Clear a breakpoint
+#----------------------------------------------------------------------
+sub command_clear_breakpoint
+{
+ my $pid = shift;
+ my $tid = shift;
+ if (@_)
+ {
+ my $break_id;
+ my @cleared_break_ids;
+ my @new_break_ids;
+ $opt_g and print "command_clear_breakpoint (pid = $pid, break_ids = @_)\n";
+ foreach $break_id (@_)
+ {
+ if (lldb::PDBreakpointClear ($pid, $break_id))
+ {
+ printf("Breakpoint %i has been cleared.\n", $break_id);
+ push (@cleared_break_ids, $break_id);
+ }
+ else
+ {
+ printf("error: failed to clear breakpoint %i.\n", $break_id);
+ }
+ }
+
+ foreach my $old_break_id (@break_ids)
+ {
+ my $found_break_id = 0;
+ foreach $break_id (@cleared_break_ids)
+ {
+ if ($old_break_id == $break_id)
+ {
+ $found_break_id = 1;
+ }
+ }
+ $found_break_id or push (@new_break_ids, $old_break_id);
+ }
+ @break_ids = @new_break_ids;
+ }
+ else
+ {
+ # Nothing specified, clear all breakpoints
+ return command_clear_breakpoint($pid, $tid, @break_ids);
+ }
+ return 1;
+}
+#----------------------------------------------------------------------
+# Continue program execution
+#----------------------------------------------------------------------
+sub command_continue
+{
+ my $pid = shift;
+ my $tid = shift;
+ $opt_g and print "command_continue (pid = $pid)\n";
+ if ($pid != $lldb::PD_INVALID_PROCESS_ID)
+ {
+ $opt_v and printf("Resuming pid %d...\n", $pid);
+ return lldb::PDProcessResume ($pid);
+ }
+ return 0;
+}
+
+sub command_step
+{
+ my $pid = shift;
+ my $tid = shift;
+ $opt_g and print "command_step (pid = $pid, tid = $tid)\n";
+ if ($pid != $lldb::PD_INVALID_PROCESS_ID)
+ {
+ $opt_v and printf("Single stepping pid %d tid = %4.4x...\n", $pid, $tid);
+ return lldb::PDThreadResume ($pid, $tid, 1);
+ }
+ return 0;
+}
+
+sub command_info
+{
+ my $pid = shift;
+ my $tid = shift;
+ $opt_g and print "command_step (pid = $pid, tid = $tid)\n";
+ if ($pid != $lldb::PD_INVALID_PROCESS_ID)
+ {
+ if (@_)
+ {
+ my $info_cmd = shift;
+ if ($info_cmd eq 'reg')
+ {
+
+ }
+ elsif ($info_cmd eq 'thread')
+ {
+ # info on the current thread
+ printf("thread 0x%4.4x %s\n", $tid, lldb::PDThreadGetInfo($pid, $tid));
+ }
+ elsif ($info_cmd eq 'threads')
+ {
+ my $num_threads = lldb::PDProcessGetNumThreads( $pid );
+ for my $thread_num (1..$num_threads)
+ {
+ my $curr_tid = lldb::PDProcessGetThreadAtIndex ( $pid, $thread_num - 1 );
+ printf("%c%u - thread 0x%4.4x %s\n", $curr_tid == $tid ? '*' : ' ', $thread_num, $curr_tid, lldb::PDThreadGetInfo($pid, $curr_tid));
+ }
+ }
+ }
+ }
+ return 1;
+}
+#----------------------------------------------------------------------
+# Get help on all commands, or a specific list of commands
+#----------------------------------------------------------------------
+sub command_help
+{
+ my $pid = shift;
+ my $tid = shift;
+ if (@_)
+ {
+ $opt_g and print "command_continue (pid = $pid, commands = @_)\n";
+ foreach my $cmd (@_)
+ {
+ my $cmd_href = get_command_hash_ref($cmd);
+ if ($cmd_href)
+ {
+ print '#', '-' x 72, "\n# $cmd_href->{name}\n", '#', '-' x 72, "\n";
+ my $usage_aref = $cmd_href->{usage};
+ if (@{$usage_aref})
+ {
+ print " USAGE\n";
+ foreach my $usage (@{$usage_aref}) {
+ print " $usage\n";
+ }
+ print "\n";
+ }
+ print " DESCRIPTION\n $cmd_href->{description}\n\n";
+ }
+ else
+ {
+ print " invalid command: '$cmd'\n\n";
+ }
+ }
+ }
+ else
+ {
+ return command_help($pid, sort keys %commands);
+ }
+ return 1;
+}
+
+
+#lldb::PDLogSetLogMask ($lldb::PD_LOG_ALL);
+#lldb::PDLogSetLogFile ('/dev/stdout');
+
+print "running: ", join(' ', @ARGV), "\n";
+
+my $pid = lldb::PDProcessLaunch ($ARGV[0], \@ARGV, \@env, "i386", '/dev/stdin', '/dev/stdout', '/dev/stderr', $launch, '', 0);
+my $pid_state;
+while ($pid)
+{
+ $opt_g and printf("PDProcessWaitForEvents (%d, 0x%4.4x, SET, 1)\n", $pid, $lldb::PD_ALL_EVENTS);
+ my $events = lldb::PDProcessWaitForEvents ($pid, $lldb::PD_ALL_EVENTS, 1, 1);
+ if ($events)
+ {
+ $opt_g and printf ("Got event: 0x%8.8x\n", $events);
+
+ if ($events & $lldb::PD_EVENT_IMAGES_CHANGED)
+ {
+ $opt_g and printf("pid %d images changed...\n", $pid);
+ }
+
+ if ($events & $lldb::PD_EVENT_STDIO)
+ {
+ $opt_g and printf("pid %d has stdio...\n", $pid);
+ }
+
+ if ($events & $lldb::PD_EVENT_ASYNC_INTERRUPT)
+ {
+ $opt_g and printf("pid %d got async interrupt...\n", $pid);
+ }
+
+ if ($events & $lldb::PD_EVENT_RUNNING)
+ {
+ $pid_state = lldb::PDProcessGetState ($pid);
+ $opt_v and printf( "pid %d state: %s.\n", $pid, lldb::PDStateAsString ($pid_state) );
+ }
+
+ if ($events & $lldb::PD_EVENT_STOPPED)
+ {
+ $pid_state = lldb::PDProcessGetState ($pid);
+ $opt_v and printf( "pid %d state: %s.\n", $pid, lldb::PDStateAsString ($pid_state) );
+
+ if ($pid_state == $lldb::eStateUnloaded ||
+ $pid_state == $lldb::eStateAttaching ||
+ $pid_state == $lldb::eStateLaunching )
+ {
+
+ }
+ elsif ( $pid_state == $lldb::eStateStopped )
+ {
+ my $tid = lldb::PDProcessGetCurrentThread ( $pid );
+ my $pc = lldb::PDThreadGetRegisterHexValueByName($pid, $tid, $lldb::PD_REGISTER_SET_ALL, "eip", 0);
+ $pc != 0 and printf("pc = 0x%8.8x ", $pc);
+ # my $sp = lldb::PDThreadGetRegisterHexValueByName($pid, $tid, $lldb::PD_REGISTER_SET_ALL, "esp", 0);
+ # $sp != 0 and printf("sp = 0x%8.8x ", $sp);
+ # my $fp = lldb::PDThreadGetRegisterHexValueByName($pid, $tid, $lldb::PD_REGISTER_SET_ALL, "ebp", 0);
+ # $sp != 0 and printf("fp = 0x%8.8x ", $fp);
+ # print "\n";
+ my $done = 0;
+ my $input;
+ while (!$done)
+ {
+ print '(pdbg) ';
+
+ chomp($input = <STDIN>);
+ my @argv = split(/\s+/, $input);
+ my $cmd = @argv ? shift @argv : undef;
+ my $cmd_href = get_command_hash_ref ($cmd);
+ if ($cmd_href)
+ {
+ # Print the expanded alias if one was used
+ if ($opt_v and $cmd_href->{name} ne $cmd)
+ {
+ print "$cmd_href->{name} @argv\n";
+ }
+
+ # Call the command's callback function to make things happen
+ if ($cmd_href->{function}($pid, $tid, @argv))
+ {
+ $done = $cmd_href->{runs_target};
+ }
+ }
+ else
+ {
+ print "invalid command: '$cmd'\nType 'help' for a list of all commands.\nType 'help CMD' for help on a specific commmand.\n";
+ }
+ }
+ }
+ elsif ( $pid_state == $lldb::eStateRunning ||
+ $pid_state == $lldb::eStateStepping )
+ {
+
+ }
+ elsif ( $pid_state == $lldb::eStateCrashed ||
+ $pid_state == $lldb::eStateDetached ||
+ $pid_state == $lldb::eStateExited )
+ {
+ $pid = 0;
+ }
+ elsif ( $pid_state == $lldb::eStateSuspended )
+ {
+ }
+ else
+ {
+ }
+ }
+
+ if ($pid)
+ {
+ $opt_g and printf("PDProcessResetEvents(%d, 0x%8.8x)\n", $pid, $events);
+ lldb::PDProcessResetEvents($pid, $events);
+ }
+ }
+}
+
+if ($pid != $lldb::PD_INVALID_PROCESS_ID)
+{
+ lldb::PDProcessDetach ($pid);
+}
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.cpp
new file mode 100644
index 00000000000..7dc8d2ce652
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.cpp
@@ -0,0 +1,575 @@
+//===-- MachException.cpp ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/ptrace.h>
+
+#include "lldb/Core/StreamString.h"
+#include "lldb/Host/Host.h"
+
+#include "MachException.h"
+#include "ProcessMacOSXLog.h"
+
+using namespace lldb_private;
+
+// Routine mach_exception_raise
+extern "C"
+kern_return_t catch_mach_exception_raise
+(
+ mach_port_t exception_port,
+ mach_port_t thread,
+ mach_port_t task,
+ exception_type_t exception,
+ mach_exception_data_t code,
+ mach_msg_type_number_t codeCnt
+);
+
+extern "C"
+kern_return_t catch_mach_exception_raise_state
+(
+ mach_port_t exception_port,
+ exception_type_t exception,
+ const mach_exception_data_t code,
+ mach_msg_type_number_t codeCnt,
+ int *flavor,
+ const thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt,
+ thread_state_t new_state,
+ mach_msg_type_number_t *new_stateCnt
+);
+
+// Routine mach_exception_raise_state_identity
+extern "C"
+kern_return_t catch_mach_exception_raise_state_identity
+(
+ mach_port_t exception_port,
+ mach_port_t thread,
+ mach_port_t task,
+ exception_type_t exception,
+ mach_exception_data_t code,
+ mach_msg_type_number_t codeCnt,
+ int *flavor,
+ thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt,
+ thread_state_t new_state,
+ mach_msg_type_number_t *new_stateCnt
+);
+
+extern "C" boolean_t mach_exc_server(
+ mach_msg_header_t *InHeadP,
+ mach_msg_header_t *OutHeadP);
+
+// Any access to the g_message variable should be done by locking the
+// g_message_mutex first, using the g_message variable, then unlocking
+// the g_message_mutex. See MachException::Message::CatchExceptionRaise()
+// for sample code.
+
+static MachException::Data *g_message = NULL;
+//static pthread_mutex_t g_message_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+
+extern "C"
+kern_return_t
+catch_mach_exception_raise_state
+(
+ mach_port_t exc_port,
+ exception_type_t exc_type,
+ const mach_exception_data_t exc_data,
+ mach_msg_type_number_t exc_data_count,
+ int * flavor,
+ const thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt,
+ thread_state_t new_state,
+ mach_msg_type_number_t * new_stateCnt
+)
+{
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS);
+ if (log)
+ {
+ log->Printf("::%s ( exc_port = 0x%4.4x, exc_type = %d ( %s ), exc_data = " MACH_EXCEPTION_DATA_FMT_HEX ", exc_data_count = %d)",
+ __FUNCTION__,
+ exc_port,
+ exc_type, MachException::Name(exc_type),
+ exc_data,
+ exc_data_count);
+ }
+ return KERN_FAILURE;
+}
+
+extern "C"
+kern_return_t
+catch_mach_exception_raise_state_identity
+(
+ mach_port_t exc_port,
+ mach_port_t thread_port,
+ mach_port_t task_port,
+ exception_type_t exc_type,
+ mach_exception_data_t exc_data,
+ mach_msg_type_number_t exc_data_count,
+ int * flavor,
+ thread_state_t old_state,
+ mach_msg_type_number_t old_stateCnt,
+ thread_state_t new_state,
+ mach_msg_type_number_t *new_stateCnt
+)
+{
+ kern_return_t kret;
+ Log * log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS);
+ if (log)
+ {
+ log->Printf("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = 0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { " MACH_EXCEPTION_DATA_FMT_HEX ", " MACH_EXCEPTION_DATA_FMT_HEX " })",
+ __FUNCTION__,
+ exc_port,
+ thread_port,
+ task_port,
+ exc_type, MachException::Name(exc_type),
+ exc_data_count,
+ exc_data_count > 0 ? exc_data[0] : 0xBADDBADD,
+ exc_data_count > 1 ? exc_data[1] : 0xBADDBADD);
+ }
+ kret = mach_port_deallocate (mach_task_self (), task_port);
+ kret = mach_port_deallocate (mach_task_self (), thread_port);
+
+ return KERN_FAILURE;
+}
+
+extern "C"
+kern_return_t
+catch_mach_exception_raise
+(
+ mach_port_t exc_port,
+ mach_port_t thread_port,
+ mach_port_t task_port,
+ exception_type_t exc_type,
+ mach_exception_data_t exc_data,
+ mach_msg_type_number_t exc_data_count)
+{
+ Log * log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS);
+ if (log)
+ {
+ log->Printf("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = 0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { " MACH_EXCEPTION_DATA_FMT_HEX ", " MACH_EXCEPTION_DATA_FMT_HEX " })",
+ __FUNCTION__,
+ exc_port,
+ thread_port,
+ task_port,
+ exc_type, MachException::Name(exc_type),
+ exc_data_count,
+ exc_data_count > 0 ? exc_data[0] : 0xBADDBADD,
+ exc_data_count > 1 ? exc_data[1] : 0xBADDBADD);
+ }
+
+ g_message->task_port = task_port;
+ g_message->thread_port = thread_port;
+ g_message->exc_type = exc_type;
+ g_message->exc_data.resize(exc_data_count);
+ ::memcpy (&g_message->exc_data[0], exc_data, g_message->exc_data.size() * sizeof (mach_exception_data_type_t));
+ return KERN_SUCCESS;
+}
+
+
+void
+MachException::Message::PutToLog(Log *log) const
+{
+ if (log)
+ {
+ log->Printf(" exc_msg { bits = 0x%8.8lx size = 0x%8.8lx remote-port = 0x%8.8lx local-port = 0x%8.8lx reserved = 0x%8.8lx id = 0x%8.8lx } ",
+ exc_msg.hdr.msgh_bits,
+ exc_msg.hdr.msgh_size,
+ exc_msg.hdr.msgh_remote_port,
+ exc_msg.hdr.msgh_local_port,
+ exc_msg.hdr.msgh_reserved,
+ exc_msg.hdr.msgh_id);
+
+ log->Printf( "reply_msg { bits = 0x%8.8lx size = 0x%8.8lx remote-port = 0x%8.8lx local-port = 0x%8.8lx reserved = 0x%8.8lx id = 0x%8.8lx }",
+ reply_msg.hdr.msgh_bits,
+ reply_msg.hdr.msgh_size,
+ reply_msg.hdr.msgh_remote_port,
+ reply_msg.hdr.msgh_local_port,
+ reply_msg.hdr.msgh_reserved,
+ reply_msg.hdr.msgh_id);
+ state.PutToLog(log);
+ }
+}
+
+bool
+MachException::Data::GetStopInfo(Thread::StopInfo *stop_info) const
+{
+ // Zero out the structure.
+ stop_info->Clear();
+
+ // Make sure we have a valid exception before we return anything valid
+ if (exc_type == 0)
+ return true;
+ // We always stop with a mach exceptions
+ const size_t exc_data_count = exc_data.size();
+ stop_info->SetStopReasonWithException(exc_type, exc_data_count);
+
+ // Fill in a text description
+ const char * exc_name = MachException::Name(exc_type);
+ StreamString sstr;
+ if (exc_name)
+ sstr.PutCString(exc_name);
+ else
+ sstr.Printf ("%i", exc_type);
+
+ int signal = SoftSignal();
+ if (signal > 0)
+ {
+ const char *sig_str = Host::GetSignalAsCString(signal);
+ if (sig_str)
+ sstr.Printf (" EXC_SOFT_SIGNAL(%s)", sig_str);
+ else
+ sstr.Printf (" EXC_SOFT_SIGNAL(%i)", signal);
+ }
+ else
+ {
+ // No special disassembly for exception data, just
+ sstr.Printf (" data[%zu] = {", exc_data_count);
+
+ for (size_t idx = 0; idx < exc_data_count; ++idx)
+ sstr.Printf (MACH_EXCEPTION_DATA_FMT_MINHEX "%s", exc_data[idx], ((idx + 1 == exc_data_count) ? "" : ","));
+
+ sstr.PutChar('}');
+ }
+
+ stop_info->SetStopDescription (sstr.GetData());
+
+ // Copy the exception data
+ size_t i;
+ for (i=0; i<exc_data_count; i++)
+ stop_info->SetExceptionDataAtIndex(i, exc_data[i]);
+
+ return true;
+}
+
+
+void
+MachException::Data::DumpStopReason() const
+{
+ Log * log = ProcessMacOSXLog::GetLogIfAllCategoriesSet();
+ if (log)
+ {
+ int signal = SoftSignal();
+ if (signal > 0)
+ {
+ const char *signal_str = Host::GetSignalAsCString(signal);
+ if (signal_str)
+ log->Printf ("signal(%s)", signal_str);
+ else
+ log->Printf ("signal(%i)", signal);
+ return;
+ }
+ log->Printf ("%s", Name(exc_type));
+ }
+}
+
+kern_return_t
+MachException::Message::Receive(mach_port_t port, mach_msg_option_t options, mach_msg_timeout_t timeout, mach_port_t notify_port)
+{
+ Error err;
+ Log * log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS);
+ mach_msg_timeout_t mach_msg_timeout = options & MACH_RCV_TIMEOUT ? timeout : 0;
+ if (log && ((options & MACH_RCV_TIMEOUT) == 0))
+ {
+ // Dump this log message if we have no timeout in case it never returns
+ log->Printf ("::mach_msg ( msg->{bits = %#x, size = %u remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x)",
+ exc_msg.hdr.msgh_bits,
+ exc_msg.hdr.msgh_size,
+ exc_msg.hdr.msgh_remote_port,
+ exc_msg.hdr.msgh_local_port,
+ exc_msg.hdr.msgh_reserved,
+ exc_msg.hdr.msgh_id,
+ options,
+ 0,
+ sizeof (exc_msg.data),
+ port,
+ mach_msg_timeout,
+ notify_port);
+ }
+
+ err = ::mach_msg (&exc_msg.hdr,
+ options, // options
+ 0, // Send size
+ sizeof (exc_msg.data), // Receive size
+ port, // exception port to watch for exception on
+ mach_msg_timeout, // timeout in msec (obeyed only if MACH_RCV_TIMEOUT is ORed into the options parameter)
+ notify_port);
+
+ // Dump any errors we get
+ if (log && err.GetError() != MACH_RCV_TIMED_OUT)
+ {
+ log->Error("::mach_msg ( msg->{bits = %#x, size = %u remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x)",
+ exc_msg.hdr.msgh_bits,
+ exc_msg.hdr.msgh_size,
+ exc_msg.hdr.msgh_remote_port,
+ exc_msg.hdr.msgh_local_port,
+ exc_msg.hdr.msgh_reserved,
+ exc_msg.hdr.msgh_id,
+ options,
+ 0,
+ sizeof (exc_msg.data),
+ port,
+ mach_msg_timeout,
+ notify_port);
+ }
+ return err.GetError();
+}
+
+bool
+MachException::Message::CatchExceptionRaise()
+{
+ bool success = false;
+ // locker will keep a mutex locked until it goes out of scope
+// Mutex::Locker locker(&g_message_mutex);
+ // log->Printf ("calling mach_exc_server");
+ g_message = &state;
+ // The exc_server function is the MIG generated server handling function
+ // to handle messages from the kernel relating to the occurrence of an
+ // exception in a thread. Such messages are delivered to the exception port
+ // set via thread_set_exception_ports or task_set_exception_ports. When an
+ // exception occurs in a thread, the thread sends an exception message to
+ // its exception port, blocking in the kernel waiting for the receipt of a
+ // reply. The exc_server function performs all necessary argument handling
+ // for this kernel message and calls catch_exception_raise,
+ // catch_exception_raise_state or catch_exception_raise_state_identity,
+ // which should handle the exception. If the called routine returns
+ // KERN_SUCCESS, a reply message will be sent, allowing the thread to
+ // continue from the point of the exception; otherwise, no reply message
+ // is sent and the called routine must have dealt with the exception
+ // thread directly.
+ if (mach_exc_server (&exc_msg.hdr, &reply_msg.hdr))
+ {
+ success = true;
+ }
+ else
+ {
+ Log * log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS);
+ if (log)
+ log->Printf ("mach_exc_server returned zero...");
+ }
+ g_message = NULL;
+ return success;
+}
+
+
+
+kern_return_t
+MachException::Message::Reply(task_t task, pid_t pid, int signal)
+{
+ // Reply to the exception...
+ Error err;
+
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet();
+ if (log)
+ log->Printf("MachException::Message::Reply (task = 0x%4.4x, pid = %i, signal = %i)", task, pid, signal);
+
+ // If we had a soft signal, we need to update the thread first so it can
+ // continue without signaling
+ int soft_signal = state.SoftSignal();
+ int state_pid = LLDB_INVALID_PROCESS_ID;
+ if (task == state.task_port)
+ {
+ // This is our task, so we can update the signal to send to it
+ state_pid = pid;
+ }
+ else
+ {
+ err = ::pid_for_task(state.task_port, &state_pid);
+ }
+
+ if (signal == LLDB_INVALID_SIGNAL_NUMBER)
+ signal = 0;
+
+ if (log)
+ log->Printf("MachException::Message::Reply () updating thread signal to %i (original soft_signal = %i)", signal, soft_signal);
+
+ if (state_pid != LLDB_INVALID_PROCESS_ID)
+ {
+ errno = 0;
+ if (::ptrace (PT_THUPDATE, state_pid, (caddr_t)state.thread_port, signal) != 0)
+ {
+ if (soft_signal != LLDB_INVALID_SIGNAL_NUMBER)
+ // We know we currently can't forward signals for threads that didn't stop in EXC_SOFT_SIGNAL...
+ // So only report it as an error if we should have been able to do it.
+ err.SetErrorToErrno();
+ else
+ err.Clear();
+ }
+ else
+ err.Clear();
+
+ if (log && log->GetMask().IsSet(PD_LOG_EXCEPTIONS) || err.Fail())
+ err.PutToLog(log, "::ptrace (request = PT_THUPDATE, pid = %i, tid = 0x%4.4x, signal = %i)", state_pid, state.thread_port, signal);
+ }
+
+ err = ::mach_msg ( &reply_msg.hdr,
+ MACH_SEND_MSG | MACH_SEND_INTERRUPT,
+ reply_msg.hdr.msgh_size,
+ 0,
+ MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+
+ if (log)
+ log->LogIf (PD_LOG_EXCEPTIONS, "::mach_msg ( msg->{bits = %#x, size = %u, remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x) = 0x%8.8x",
+ reply_msg.hdr.msgh_bits,
+ reply_msg.hdr.msgh_size,
+ reply_msg.hdr.msgh_remote_port,
+ reply_msg.hdr.msgh_local_port,
+ reply_msg.hdr.msgh_reserved,
+ reply_msg.hdr.msgh_id,
+ MACH_SEND_MSG | MACH_SEND_INTERRUPT,
+ reply_msg.hdr.msgh_size,
+ 0,
+ MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL,
+ err.GetError());
+
+
+ if (err.Fail())
+ {
+ if (err.GetError() == MACH_SEND_INTERRUPTED)
+ {
+ err.PutToLog(log, "::mach_msg() - send interrupted");
+ }
+ else
+ {
+ if (state.task_port == task)
+ {
+ err.PutToLog(log, "::mach_msg() - failed (task)");
+ abort ();
+ }
+ else
+ {
+ err.PutToLog(log, "::mach_msg() - failed (child of task)");
+ }
+ }
+ }
+
+ return err.GetError();
+}
+
+
+void
+MachException::Data::PutToLog(Log *log) const
+{
+ if (log == NULL)
+ return;
+
+ const char *exc_type_name = MachException::Name(exc_type);
+
+ log->Printf (" state { task_port = 0x%4.4x, thread_port = 0x%4.4x, exc_type = %i (%s) ...", task_port, thread_port, exc_type, exc_type_name ? exc_type_name : "???");
+
+ const size_t exc_data_count = exc_data.size();
+ // Dump any special exception data contents
+ int soft_signal = SoftSignal();
+ if (soft_signal > 0)
+ {
+ const char *sig_str = Host::GetSignalAsCString(soft_signal);
+ log->Printf (" exc_data: EXC_SOFT_SIGNAL (%i (%s))", soft_signal, sig_str ? sig_str : "unknown signal");
+ }
+ else
+ {
+ // No special disassembly for this data, just dump the data
+ size_t idx;
+ for (idx = 0; idx < exc_data_count; ++idx)
+ {
+ log->Printf(" exc_data[%u]: " MACH_EXCEPTION_DATA_FMT_HEX, idx, exc_data[idx]);
+ }
+ }
+}
+
+
+MachException::PortInfo::PortInfo() :
+ count(0)
+{
+ ::bzero (masks, sizeof(masks));
+ ::bzero (ports, sizeof(ports));
+ ::bzero (behaviors, sizeof(behaviors));
+ ::bzero (flavors, sizeof(flavors));
+}
+
+
+kern_return_t
+MachException::PortInfo::Save (task_t task)
+{
+ count = EXC_TYPES_COUNT;
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_EXCEPTIONS);
+ if (log)
+ log->Printf ("MachException::PortInfo::Save (task = 0x%4.4x)", task);
+ Error err;
+ if (log)
+ log->Printf("::task_get_exception_ports (task=0x%4.4x, mask=0x%x, maskCnt<=>%u, ports, behaviors, flavors)...", task, EXC_MASK_ALL, count);
+ err = ::task_get_exception_ports (task, EXC_MASK_ALL, masks, &count, ports, behaviors, flavors);
+ if (log || err.Fail())
+ err.PutToLog(log, "::task_get_exception_ports (task=0x%4.4x, mask=0x%x, maskCnt<=>%u, ports, behaviors, flavors)", task, EXC_MASK_ALL, count);
+ if (log)
+ {
+ mach_msg_type_number_t i;
+ log->Printf("Index Mask Port Behavior Flavor", masks[i], ports[i], behaviors[i], flavors[i]);
+ log->Printf("===== -------- -------- -------- --------");
+ for (i=0; i<count; ++i)
+ log->Printf("[%3u] %8.8x %8.8x %8.8x %8.8x", i, masks[i], ports[i], behaviors[i], flavors[i]);
+ }
+ if (err.Fail())
+ count = 0;
+ return err.GetError();
+}
+
+kern_return_t
+MachException::PortInfo::Restore (task_t task)
+{
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_EXCEPTIONS);
+ if (log && log->GetMask().IsSet(PD_LOG_VERBOSE))
+ log->Printf("MachException::PortInfo::Restore (task = 0x%4.4x)", task);
+ uint32_t i = 0;
+ Error err;
+ if (count > 0)
+ {
+ for (i = 0; i < count; i++)
+ {
+ err = ::task_set_exception_ports (task, masks[i], ports[i], behaviors[i], flavors[i]);
+ if (log || err.Fail())
+ err.PutToLog(log, "::task_set_exception_ports ( task = 0x%4.4x, exception_mask = 0x%8.8x, new_port = 0x%4.4x, behavior = 0x%8.8x, new_flavor = 0x%8.8x )", task, masks[i], ports[i], behaviors[i], flavors[i]);
+
+ if (err.Fail())
+ break;
+ }
+ }
+ count = 0;
+ return err.GetError();
+}
+
+const char *
+MachException::Name(exception_type_t exc_type)
+{
+ switch (exc_type)
+ {
+ case EXC_BAD_ACCESS: return "EXC_BAD_ACCESS";
+ case EXC_BAD_INSTRUCTION: return "EXC_BAD_INSTRUCTION";
+ case EXC_ARITHMETIC: return "EXC_ARITHMETIC";
+ case EXC_EMULATION: return "EXC_EMULATION";
+ case EXC_SOFTWARE: return "EXC_SOFTWARE";
+ case EXC_BREAKPOINT: return "EXC_BREAKPOINT";
+ case EXC_SYSCALL: return "EXC_SYSCALL";
+ case EXC_MACH_SYSCALL: return "EXC_MACH_SYSCALL";
+ case EXC_RPC_ALERT: return "EXC_RPC_ALERT";
+#ifdef EXC_CRASH
+ case EXC_CRASH: return "EXC_CRASH";
+#endif
+ default:
+ break;
+ }
+ return NULL;
+}
+
+
+
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.h
new file mode 100644
index 00000000000..1f3aeb07b0a
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachException.h
@@ -0,0 +1,148 @@
+//===-- MachException.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#ifndef liblldb_MachException_h_
+#define liblldb_MachException_h_
+
+#include <mach/mach.h>
+#include <vector>
+#include "lldb/lldb-private.h"
+#include "lldb/Target/Thread.h"
+// TODO: Get the config script to run to this plug-in
+//#include "PDConfig.h"
+#define HAVE_64_BIT_MACH_EXCEPTIONS // REMOVE THIS WHEN PDConfig.h is included above
+#ifdef HAVE_64_BIT_MACH_EXCEPTIONS
+
+#define MACH_EXCEPTION_DATA_FMT_DEC "%lld"
+#define MACH_EXCEPTION_DATA_FMT_HEX "0x%16.16llx"
+#define MACH_EXCEPTION_DATA_FMT_MINHEX "0x%llx"
+
+#else
+
+#define MACH_EXCEPTION_DATA_FMT_DEC "%d"
+#define MACH_EXCEPTION_DATA_FMT_HEX "0x%8.8x"
+#define MACH_EXCEPTION_DATA_FMT_MINHEX "0x%x"
+
+#endif
+
+class MachProcess;
+
+typedef union MachMessageTag
+{
+ mach_msg_header_t hdr;
+ char data[1024];
+} MachMessage;
+
+
+class MachException
+{
+public:
+
+ struct PortInfo
+ {
+ exception_mask_t masks[EXC_TYPES_COUNT];
+ mach_port_t ports[EXC_TYPES_COUNT];
+ exception_behavior_t behaviors[EXC_TYPES_COUNT];
+ thread_state_flavor_t flavors[EXC_TYPES_COUNT];
+ mach_msg_type_number_t count;
+
+ PortInfo();
+ kern_return_t Save(task_t task);
+ kern_return_t Restore(task_t task);
+ };
+
+ struct Data
+ {
+ task_t task_port;
+ lldb::tid_t thread_port;
+ exception_type_t exc_type;
+ std::vector<mach_exception_data_type_t> exc_data;
+ Data() :
+ task_port(TASK_NULL),
+ thread_port(THREAD_NULL),
+ exc_type(0),
+ exc_data()
+ {
+ }
+
+ void Clear()
+ {
+ task_port = TASK_NULL;
+ thread_port = THREAD_NULL;
+ exc_type = 0;
+ exc_data.clear();
+ }
+ bool IsValid() const
+ {
+ return task_port != TASK_NULL &&
+ thread_port != THREAD_NULL &&
+ exc_type != 0;
+ }
+ // Return the SoftSignal for this MachException data, or zero if there is none
+ int SoftSignal() const
+ {
+ if (exc_type == EXC_SOFTWARE && exc_data.size() == 2 && exc_data[0] == EXC_SOFT_SIGNAL)
+ return exc_data[1];
+ return LLDB_INVALID_SIGNAL_NUMBER;
+ }
+ bool IsBreakpoint() const
+ {
+ return (exc_type == EXC_BREAKPOINT) || ((exc_type == EXC_SOFTWARE) && exc_data[0] == 1);
+ }
+ void PutToLog(lldb_private::Log *log) const;
+ void DumpStopReason() const;
+ bool GetStopInfo(lldb_private::Thread::StopInfo *stop_info) const;
+ };
+
+ struct Message
+ {
+ MachMessage exc_msg;
+ MachMessage reply_msg;
+ Data state;
+
+ Message() :
+ exc_msg(),
+ reply_msg(),
+ state()
+ {
+ memset(&exc_msg, 0, sizeof(exc_msg));
+ memset(&reply_msg, 0, sizeof(reply_msg));
+ }
+ bool CatchExceptionRaise();
+ void PutToLog(lldb_private::Log *log) const;
+ kern_return_t Reply (task_t task, pid_t pid, int signal);
+ kern_return_t Receive( mach_port_t receive_port,
+ mach_msg_option_t options,
+ mach_msg_timeout_t timeout,
+ mach_port_t notify_port = MACH_PORT_NULL);
+
+ typedef std::vector<Message> collection;
+ typedef collection::iterator iterator;
+ typedef collection::const_iterator const_iterator;
+ };
+
+ enum
+ {
+ e_actionForward, // Forward signal to inferior process
+ e_actionStop, // Stop when this signal is received
+ };
+ struct Action
+ {
+ task_t task_port; // Set to TASK_NULL for any TASK
+ lldb::tid_t thread_port; // Set to THREAD_NULL for any thread
+ exception_type_t exc_mask; // Mach exception mask to watch for
+ std::vector<mach_exception_data_type_t> exc_data_mask; // Mask to apply to exception data, or empty to ignore exc_data value for exception
+ std::vector<mach_exception_data_type_t> exc_data_value; // Value to compare to exception data after masking, or empty to ignore exc_data value for exception
+ uint8_t flags; // Action flags describing what to do with the exception
+ };
+ static const char *Name(exception_type_t exc_type);
+};
+
+#endif
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.cpp
new file mode 100644
index 00000000000..1a0c3c48997
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.cpp
@@ -0,0 +1,674 @@
+//===-- MachTask.cpp --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachTask.h"
+
+// C Includes
+// C++ Includes
+
+// Other libraries and framework includes
+#if defined (__arm__)
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <SpringBoardServices/SpringBoardServer.h>
+#include <SpringBoardServices/SBSWatchdogAssertion.h>
+
+#endif
+
+#include "lldb/Host/Host.h"
+#include "lldb/Core/DataExtractor.h"
+
+// Project includes
+#include "ProcessMacOSX.h"
+#include "ProcessMacOSXLog.h"
+
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// MachTask constructor
+//----------------------------------------------------------------------
+MachTask::MachTask(ProcessMacOSX *process) :
+ m_process (process),
+ m_task (TASK_NULL),
+ m_vm_memory (),
+ m_exception_thread (0),
+ m_exception_port (MACH_PORT_NULL),
+ m_exc_port_info()
+{
+ memset(&m_exc_port_info, 0, sizeof(m_exc_port_info));
+
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+MachTask::~MachTask()
+{
+ Clear();
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::Suspend
+//----------------------------------------------------------------------
+kern_return_t
+MachTask::Suspend()
+{
+ Error err;
+ task_t task = GetTaskPort();
+ err = ::task_suspend (task);
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_TASK);
+ if (log || err.Fail())
+ err.PutToLog(log, "::task_suspend ( target_task = 0x%4.4x )", task);
+ return err.GetError();
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::Resume
+//----------------------------------------------------------------------
+kern_return_t
+MachTask::Resume()
+{
+ Error err;
+ task_t task = GetTaskPort();
+ err = ::task_resume (task);
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_TASK);
+ if (log || err.Fail())
+ err.PutToLog(log, "::task_resume ( target_task = 0x%4.4x )", task);
+ return err.GetError();
+}
+
+int32_t
+MachTask::GetSuspendCount () const
+{
+ struct task_basic_info task_info;
+ if (BasicInfo(&task_info) == KERN_SUCCESS)
+ return task_info.suspend_count;
+ return -1;
+}
+
+//----------------------------------------------------------------------
+// MachTask::ExceptionPort
+//----------------------------------------------------------------------
+mach_port_t
+MachTask::ExceptionPort() const
+{
+ return m_exception_port;
+}
+
+//----------------------------------------------------------------------
+// MachTask::ExceptionPortIsValid
+//----------------------------------------------------------------------
+bool
+MachTask::ExceptionPortIsValid() const
+{
+ return MACH_PORT_VALID(m_exception_port);
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::Clear
+//----------------------------------------------------------------------
+void
+MachTask::Clear()
+{
+ // Do any cleanup needed for this task
+ m_task = TASK_NULL;
+ m_exception_thread = 0;
+ m_exception_port = MACH_PORT_NULL;
+
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::SaveExceptionPortInfo
+//----------------------------------------------------------------------
+kern_return_t
+MachTask::SaveExceptionPortInfo()
+{
+ return m_exc_port_info.Save(GetTaskPort());
+}
+
+//----------------------------------------------------------------------
+// MachTask::RestoreExceptionPortInfo
+//----------------------------------------------------------------------
+kern_return_t
+MachTask::RestoreExceptionPortInfo()
+{
+ return m_exc_port_info.Restore(GetTaskPort());
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::ReadMemory
+//----------------------------------------------------------------------
+size_t
+MachTask::ReadMemory (lldb::addr_t addr, void *buf, size_t size, Error& error)
+{
+ size_t n = 0;
+ task_t task = GetTaskPort();
+ if (task != TASK_NULL)
+ {
+ n = m_vm_memory.Read(task, addr, buf, size, error);
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY);
+ if (log)
+ {
+ log->Printf ("MachTask::ReadMemory ( addr = 0x%16.16llx, size = %zu, buf = %8.8p) => %u bytes read", (uint64_t)addr, size, buf, n);
+ if (log->GetMask().IsSet(PD_LOG_MEMORY_DATA_LONG) || (log->GetMask().IsSet(PD_LOG_MEMORY_DATA_SHORT) && size <= 8))
+ {
+ DataExtractor data((uint8_t*)buf, n, eByteOrderHost, 4);
+ data.PutToLog(log, 0, n, addr, 16, DataExtractor::TypeUInt8);
+ }
+ }
+ }
+ return n;
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::WriteMemory
+//----------------------------------------------------------------------
+size_t
+MachTask::WriteMemory (lldb::addr_t addr, const void *buf, size_t size, Error& error)
+{
+ size_t n = 0;
+ task_t task = GetTaskPort();
+ if (task != TASK_NULL)
+ {
+ n = m_vm_memory.Write(task, addr, buf, size, error);
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY);
+ if (log)
+ {
+ log->Printf ("MachTask::WriteMemory ( addr = 0x%16.16llx, size = %zu, buf = %8.8p) => %u bytes written", (uint64_t)addr, size, buf, n);
+ if (log->GetMask().IsSet(PD_LOG_MEMORY_DATA_LONG) || (log->GetMask().IsSet(PD_LOG_MEMORY_DATA_SHORT) && size <= 8))
+ {
+ DataExtractor data((uint8_t*)buf, n, eByteOrderHost, 4);
+ data.PutToLog(log, 0, n, addr, 16, DataExtractor::TypeUInt8);
+ }
+ }
+ }
+ return n;
+}
+
+//----------------------------------------------------------------------
+// MachTask::AllocateMemory
+//----------------------------------------------------------------------
+lldb::addr_t
+MachTask::AllocateMemory (size_t size, uint32_t permissions, Error& error)
+{
+ // FIXME: vm_allocate allocates a page at a time, so we should use
+ // host_page_size to get the host page size and then parcel out the
+ // page we get back until it is filled.
+ // FIXME: Add log messages.
+
+ kern_return_t kret;
+ mach_vm_address_t addr;
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY);
+
+ kret = ::mach_vm_allocate (GetTaskPort(), &addr, size, TRUE);
+ if (kret == KERN_SUCCESS)
+ {
+ // Set the protections:
+ vm_prot_t mach_prot = 0;
+ if (permissions & lldb::ePermissionsReadable)
+ mach_prot |= VM_PROT_READ;
+ if (permissions & lldb::ePermissionsWritable)
+ mach_prot |= VM_PROT_WRITE;
+ if (permissions & lldb::ePermissionsExecutable)
+ mach_prot |= VM_PROT_EXECUTE;
+
+ kret = ::mach_vm_protect (GetTaskPort(), addr, size, 0, mach_prot);
+ if (kret == KERN_SUCCESS)
+ {
+ if (log)
+ log->Printf("Allocated memory at addr = 0x%16.16llx, size = %zu, prot = 0x%x)", (uint64_t) addr, size, mach_prot);
+ m_allocations.insert (std::make_pair(addr, size));
+ return (lldb::addr_t) addr;
+ }
+ else
+ {
+ if (log)
+ log->Printf("Failed to set protections on memory at addr = 0x%16.16llx, size = %zu), prot = 0x%x", (uint64_t) addr, size, mach_prot);
+ kret = ::mach_vm_deallocate (GetTaskPort(), addr, size);
+ return LLDB_INVALID_ADDRESS;
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf("Failed to set allocate memory: size = %zu)", size);
+ return LLDB_INVALID_ADDRESS;
+ }
+}
+
+//----------------------------------------------------------------------
+// MachTask::DeallocateMemory
+//----------------------------------------------------------------------
+Error
+MachTask::DeallocateMemory (lldb::addr_t ptr)
+{
+ Error error;
+ // We have to stash away sizes for the allocations...
+ allocation_collection::iterator pos, end = m_allocations.end();
+ for (pos = m_allocations.begin(); pos != end; pos++)
+ {
+ if ((*pos).first == ptr)
+ {
+ m_allocations.erase (pos);
+ error = ::mach_vm_deallocate (GetTaskPort(), (vm_address_t) ptr, (*pos).second);
+ return error;
+ }
+ }
+ error.SetErrorStringWithFormat("no memory allocated at 0x%llx", (uint64_t)ptr);
+ return error;
+}
+
+//----------------------------------------------------------------------
+// MachTask::TaskPortForProcessID
+//----------------------------------------------------------------------
+task_t
+MachTask::GetTaskPortForProcessID (Error &err)
+{
+ err.Clear();
+ if (m_task == TASK_NULL && m_process != NULL)
+ m_task = MachTask::GetTaskPortForProcessID(m_process->GetID(), err);
+ return m_task;
+}
+
+//----------------------------------------------------------------------
+// MachTask::TaskPortForProcessID
+//----------------------------------------------------------------------
+task_t
+MachTask::GetTaskPortForProcessID (lldb::pid_t pid, Error &err)
+{
+ task_t task = TASK_NULL;
+ if (pid != LLDB_INVALID_PROCESS_ID)
+ {
+ mach_port_t task_self = mach_task_self ();
+ err = ::task_for_pid ( task_self, pid, &task);
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_TASK);
+ if (log || err.Fail())
+ {
+ err.PutToLog(log, "::task_for_pid ( target_tport = 0x%4.4x, pid = %d, task => 0x%4.4x ) %u/%u %u/%u", task_self, pid, task, getuid(), geteuid(), getgid(), getegid());
+ }
+ }
+ return task;
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::BasicInfo
+//----------------------------------------------------------------------
+kern_return_t
+MachTask::BasicInfo(struct task_basic_info *info) const
+{
+ return BasicInfo (GetTaskPort(), info);
+}
+
+//----------------------------------------------------------------------
+// MachTask::BasicInfo
+//----------------------------------------------------------------------
+kern_return_t
+MachTask::BasicInfo(task_t task, struct task_basic_info *info)
+{
+ if (info == NULL)
+ return KERN_INVALID_ARGUMENT;
+
+ Error err;
+ mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
+ err = ::task_info (task, TASK_BASIC_INFO, (task_info_t)info, &count);
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_TASK);
+ if (log || err.Fail())
+ err.PutToLog(log, "::task_info ( target_task = 0x%4.4x, flavor = TASK_BASIC_INFO, task_info_out => %p, task_info_outCnt => %u )", task, info, count);
+ if (log && log->GetMask().IsSet(PD_LOG_VERBOSE) && err.Success())
+ {
+ float user = (float)info->user_time.seconds + (float)info->user_time.microseconds / 1000000.0f;
+ float system = (float)info->user_time.seconds + (float)info->user_time.microseconds / 1000000.0f;
+ log->Printf ("task_basic_info = { suspend_count = %i, virtual_size = 0x%8.8x, resident_size = 0x%8.8x, user_time = %f, system_time = %f }",
+ info->suspend_count, info->virtual_size, info->resident_size, user, system);
+ }
+ return err.GetError();
+}
+
+
+//----------------------------------------------------------------------
+// MachTask::IsValid
+//
+// Returns true if a task is a valid task port for a current process.
+//----------------------------------------------------------------------
+bool
+MachTask::IsValid () const
+{
+ return MachTask::IsValid(GetTaskPort());
+}
+
+//----------------------------------------------------------------------
+// MachTask::IsValid
+//
+// Returns true if a task is a valid task port for a current process.
+//----------------------------------------------------------------------
+bool
+MachTask::IsValid (task_t task)
+{
+ if (task != TASK_NULL)
+ {
+ struct task_basic_info task_info;
+ return BasicInfo(task, &task_info) == KERN_SUCCESS;
+ }
+ return false;
+}
+
+
+bool
+MachTask::StartExceptionThread(Error &err)
+{
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_EXCEPTIONS);
+
+ if (log)
+ log->Printf ("MachTask::%s ( )", __FUNCTION__);
+ task_t task = GetTaskPortForProcessID(err);
+ if (MachTask::IsValid(task))
+ {
+ // Got the mach port for the current process
+ mach_port_t task_self = mach_task_self ();
+
+ // Allocate an exception port that we will use to track our child process
+ err = ::mach_port_allocate (task_self, MACH_PORT_RIGHT_RECEIVE, &m_exception_port);
+ if (log || err.Fail())
+ err.PutToLog(log, "::mach_port_allocate (task_self=0x%4.4x, MACH_PORT_RIGHT_RECEIVE, &m_exception_port => 0x%4.4x)",
+ task_self, m_exception_port);
+ if (err.Fail())
+ return false;
+
+ // Add the ability to send messages on the new exception port
+ err = ::mach_port_insert_right (task_self, m_exception_port, m_exception_port, MACH_MSG_TYPE_MAKE_SEND);
+ if (log || err.Fail())
+ err.PutToLog(log, "::mach_port_insert_right (task_self=0x%4.4x, m_exception_port=0x%4.4x, m_exception_port=0x%4.4x, MACH_MSG_TYPE_MAKE_SEND)",
+ task_self, m_exception_port, m_exception_port);
+ if (err.Fail())
+ return false;
+
+ // Save the original state of the exception ports for our child process
+ err = SaveExceptionPortInfo();
+
+ // Set the ability to get all exceptions on this port
+ err = ::task_set_exception_ports (task, EXC_MASK_ALL, m_exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE);
+ if (log || err.Fail())
+ err.PutToLog(log, "::task_set_exception_ports (task, EXC_MASK_ALL, m_exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE)");
+ if (err.Fail())
+ return false;
+
+ // Create the exception thread
+ char thread_name[256];
+ ::snprintf (thread_name, sizeof(thread_name), "<lldb.process.process-macosx.mach-exception-%d>", m_process->GetID());
+ m_exception_thread = Host::ThreadCreate (thread_name, MachTask::ExceptionThread, this, &err);
+
+ return err.Success();
+ }
+ return false;
+}
+
+kern_return_t
+MachTask::ShutDownExceptionThread()
+{
+ Error err;
+
+ if (m_exception_thread == NULL)
+ return KERN_SUCCESS;
+
+ err = RestoreExceptionPortInfo();
+
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_EXCEPTIONS);
+
+ // NULL our our exception port and let our exception thread exit
+ mach_port_t exception_port = m_exception_port;
+ m_exception_port = NULL;
+
+ Host::ThreadCancel (m_exception_thread, &err);
+ if (log || err.Fail())
+ err.PutToLog(log, "Host::ThreadCancel ( thread = %p )", m_exception_thread);
+
+ Host::ThreadJoin (m_exception_thread, NULL, &err);
+ if (log || err.Fail())
+ err.PutToLog(log, "Host::ThreadJoin ( thread = %p, result_ptr = NULL)", m_exception_thread);
+
+ // Deallocate our exception port that we used to track our child process
+ mach_port_t task_self = mach_task_self ();
+ err = ::mach_port_deallocate (task_self, exception_port);
+ if (log || err.Fail())
+ err.PutToLog(log, "::mach_port_deallocate ( task = 0x%4.4x, name = 0x%4.4x )", task_self, exception_port);
+ exception_port = NULL;
+
+ Clear();
+ return err.GetError();
+}
+
+
+void *
+MachTask::ExceptionThread (void *arg)
+{
+ if (arg == NULL)
+ return NULL;
+
+ MachTask *mach_task = (MachTask*) arg;
+ ProcessMacOSX *mach_proc = mach_task->Process();
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_EXCEPTIONS);
+ if (log)
+ log->Printf ("MachTask::%s (arg = %p) thread starting...", __FUNCTION__, arg);
+
+ // We keep a count of the number of consecutive exceptions received so
+ // we know to grab all exceptions without a timeout. We do this to get a
+ // bunch of related exceptions on our exception port so we can process
+ // then together. When we have multiple threads, we can get an exception
+ // per thread and they will come in consecutively. The main loop in this
+ // thread can stop periodically if needed to service things related to this
+ // process.
+ // flag set in the options, so we will wait forever for an exception on
+ // our exception port. After we get one exception, we then will use the
+ // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current
+ // exceptions for our process. After we have received the last pending
+ // exception, we will get a timeout which enables us to then notify
+ // our main thread that we have an exception bundle avaiable. We then wait
+ // for the main thread to tell this exception thread to start trying to get
+ // exceptions messages again and we start again with a mach_msg read with
+ // infinite timeout.
+ uint32_t num_exceptions_received = 0;
+ Error err;
+ task_t task = mach_task->GetTaskPort();
+ mach_msg_timeout_t periodic_timeout = 1000;
+
+#if defined (__arm__)
+ mach_msg_timeout_t watchdog_elapsed = 0;
+ mach_msg_timeout_t watchdog_timeout = 60 * 1000;
+ lldb::pid_t pid = mach_proc->GetID();
+ CFReleaser<SBSWatchdogAssertionRef> watchdog;
+
+ if (mach_proc->ProcessUsingSpringBoard())
+ {
+ // Request a renewal for every 60 seconds if we attached using SpringBoard
+ watchdog.reset(::SBSWatchdogAssertionCreateForPID(NULL, pid, 60));
+ if (log)
+ log->Printf ("::SBSWatchdogAssertionCreateForPID (NULL, %4.4x, 60 ) => %p", pid, watchdog.get());
+
+ if (watchdog.get())
+ {
+ ::SBSWatchdogAssertionRenew (watchdog.get());
+
+ CFTimeInterval watchdogRenewalInterval = ::SBSWatchdogAssertionGetRenewalInterval (watchdog.get());
+ if (log)
+ log->Printf ("::SBSWatchdogAssertionGetRenewalInterval ( %p ) => %g seconds", watchdog.get(), watchdogRenewalInterval);
+ if (watchdogRenewalInterval > 0.0)
+ {
+ watchdog_timeout = (mach_msg_timeout_t)watchdogRenewalInterval * 1000;
+ if (watchdog_timeout > 3000)
+ watchdog_timeout -= 1000; // Give us a second to renew our timeout
+ else if (watchdog_timeout > 1000)
+ watchdog_timeout -= 250; // Give us a quarter of a second to renew our timeout
+ }
+ }
+ if (periodic_timeout == 0 || periodic_timeout > watchdog_timeout)
+ periodic_timeout = watchdog_timeout;
+ }
+#endif // #if defined (__arm__)
+
+ while (mach_task->ExceptionPortIsValid())
+ {
+ //::pthread_testcancel ();
+
+ MachException::Message exception_message;
+
+
+ if (num_exceptions_received > 0)
+ {
+ // No timeout, just receive as many exceptions as we can since we already have one and we want
+ // to get all currently available exceptions for this task
+ err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, 0);
+ }
+ else if (periodic_timeout > 0)
+ {
+ // We need to stop periodically in this loop, so try and get a mach message with a valid timeout (ms)
+ err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, periodic_timeout);
+ }
+ else
+ {
+ // We don't need to parse all current exceptions or stop periodically,
+ // just wait for an exception forever.
+ err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0);
+ }
+
+ if (err.GetError() == MACH_RCV_INTERRUPTED)
+ {
+ // If we have no task port we should exit this thread
+ if (!mach_task->ExceptionPortIsValid())
+ {
+ if (log)
+ log->Printf ("thread cancelled...");
+ break;
+ }
+
+ // Make sure our task is still valid
+ if (MachTask::IsValid(task))
+ {
+ // Task is still ok
+ if (log)
+ log->Printf ("interrupted, but task still valid, continuing...");
+ continue;
+ }
+ else
+ {
+ if (log)
+ log->Printf ("task has exited...");
+ mach_proc->SetPrivateState (eStateExited);
+ // Our task has died, exit the thread.
+ break;
+ }
+ }
+ else if (err.GetError() == MACH_RCV_TIMED_OUT)
+ {
+ if (num_exceptions_received > 0)
+ {
+ // We were receiving all current exceptions with a timeout of zero
+ // it is time to go back to our normal looping mode
+ num_exceptions_received = 0;
+
+ // Notify our main thread we have a complete exception message
+ // bundle available.
+ mach_proc->ExceptionMessageBundleComplete();
+
+ // in case we use a timeout value when getting exceptions...
+ // Make sure our task is still valid
+ if (MachTask::IsValid(task))
+ {
+ // Task is still ok
+ if (log)
+ log->Printf ("got a timeout, continuing...");
+ continue;
+ }
+ else
+ {
+ if (log)
+ log->Printf ("task has exited...");
+ mach_proc->SetPrivateState (eStateExited);
+ // Our task has died, exit the thread.
+ break;
+ }
+ continue;
+ }
+
+#if defined (__arm__)
+ if (watchdog.get())
+ {
+ watchdog_elapsed += periodic_timeout;
+ if (watchdog_elapsed >= watchdog_timeout)
+ {
+ LogIf(PD_LOG_TASK, "SBSWatchdogAssertionRenew ( %p )", watchdog.get());
+ ::SBSWatchdogAssertionRenew (watchdog.get());
+ watchdog_elapsed = 0;
+ }
+ }
+#endif
+ }
+ else if (err.GetError() != KERN_SUCCESS)
+ {
+ if (log)
+ log->Printf ("got some other error, do something about it??? nah, continuing for now...");
+ // TODO: notify of error?
+ }
+ else
+ {
+ if (exception_message.CatchExceptionRaise())
+ {
+ ++num_exceptions_received;
+ mach_proc->ExceptionMessageReceived(exception_message);
+ }
+ }
+ }
+
+#if defined (__arm__)
+ if (watchdog.get())
+ {
+ // TODO: change SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel when we
+ // all are up and running on systems that support it. The SBS framework has a #define
+ // that will forward SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel for now
+ // so it should still build either way.
+ LogIf(PD_LOG_TASK, "::SBSWatchdogAssertionRelease(%p)", watchdog.get());
+ ::SBSWatchdogAssertionRelease (watchdog.get());
+ }
+#endif // #if defined (__arm__)
+
+ if (log)
+ log->Printf ("MachTask::%s (arg = %p) thread exiting...", __FUNCTION__, arg);
+ return NULL;
+}
+
+lldb::addr_t
+MachTask::GetDYLDAllImageInfosAddress ()
+{
+#ifdef TASK_DYLD_INFO
+ task_dyld_info_data_t dyld_info;
+ mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+ Error err;
+ // The actual task shouldn't matter for the DYLD info, so lets just use ours
+ kern_return_t kret = ::task_info (GetTaskPortForProcessID (err), TASK_DYLD_INFO, (task_info_t)&dyld_info, &count);
+ if (kret == KERN_SUCCESS)
+ {
+ // We now have the address of the all image infos structure
+ return dyld_info.all_image_info_addr;
+ }
+#endif
+ return LLDB_INVALID_ADDRESS;
+}
+
+
+
+
+
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.h
new file mode 100644
index 00000000000..228cb7c516b
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachTask.h
@@ -0,0 +1,138 @@
+//===-- MachTask.h ----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __MachTask_h__
+#define __MachTask_h__
+
+// C Includes
+// C++ Includes
+#include <map>
+// Other libraries and framework includes
+#include <mach/mach.h>
+#include <mach/mach_vm.h>
+#include <sys/socket.h>
+
+// Project includes
+#include "MachException.h"
+#include "MachVMMemory.h"
+
+class ProcessMacOSX;
+
+class MachTask
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ MachTask (ProcessMacOSX *process);
+
+ virtual
+ ~MachTask ();
+
+ void
+ Clear ();
+
+ kern_return_t
+ Suspend ();
+
+ kern_return_t
+ Resume ();
+
+ int32_t
+ GetSuspendCount () const;
+
+ size_t
+ ReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error& error);
+
+ size_t
+ WriteMemory (lldb::addr_t addr, const void *buf, size_t size, lldb_private::Error& error);
+
+ lldb::addr_t
+ AllocateMemory (size_t size, uint32_t permissions, lldb_private::Error& error);
+
+ lldb_private::Error
+ DeallocateMemory (lldb::addr_t addr);
+
+ mach_port_t
+ ExceptionPort () const;
+
+ bool
+ ExceptionPortIsValid () const;
+
+ kern_return_t
+ SaveExceptionPortInfo ();
+
+ kern_return_t
+ RestoreExceptionPortInfo ();
+
+ kern_return_t
+ ShutDownExceptionThread ();
+
+ bool
+ StartExceptionThread (lldb_private::Error &err);
+
+ lldb::addr_t
+ GetDYLDAllImageInfosAddress ();
+
+ kern_return_t
+ BasicInfo (struct task_basic_info *info) const;
+
+ static kern_return_t
+ BasicInfo (task_t task, struct task_basic_info *info);
+
+ bool
+ IsValid () const;
+
+ static bool
+ IsValid (task_t task);
+
+ static void *
+ ExceptionThread (void *arg);
+
+ task_t
+ GetTaskPort () const
+ {
+ return m_task;
+ }
+
+ task_t
+ GetTaskPortForProcessID (lldb_private::Error &err);
+
+ static task_t
+ GetTaskPortForProcessID (lldb::pid_t pid, lldb_private::Error &err);
+
+ ProcessMacOSX *
+ Process ()
+ {
+ return m_process;
+ }
+
+ const ProcessMacOSX *
+ Process () const
+ {
+ return m_process;
+ }
+
+protected:
+ ProcessMacOSX * m_process; // The mach process that owns this MachTask
+ task_t m_task;
+ MachVMMemory m_vm_memory; // Special mach memory reading class that will take care of watching for page and region boundaries
+ MachException::PortInfo m_exc_port_info; // Saved settings for all exception ports
+ lldb::thread_t m_exception_thread; // Thread ID for the exception thread in case we need it
+ mach_port_t m_exception_port; // Exception port on which we will receive child exceptions
+
+ // Maybe sort this by address and use find?
+ typedef std::map<vm_address_t,size_t> allocation_collection;
+ allocation_collection m_allocations;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN (MachTask);
+};
+
+#endif // __MachTask_h__
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext.h
new file mode 100644
index 00000000000..3166c2802f3
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext.h
@@ -0,0 +1,48 @@
+//===-- MachThreadContext.h -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_MachThreadContext_h_
+#define liblldb_MachThreadContext_h_
+
+#include <vector>
+
+#include "MachException.h"
+
+class ThreadMacOSX;
+
+class MachThreadContext
+{
+public:
+ MachThreadContext (ThreadMacOSX &thread) :
+ m_thread (thread)
+ {
+ }
+
+ virtual ~MachThreadContext()
+ {
+ }
+
+ virtual lldb_private::RegisterContext *
+ CreateRegisterContext (lldb_private::StackFrame *frame) const = 0;
+
+ virtual void InitializeInstance() = 0;
+ virtual void ThreadWillResume () = 0;
+ virtual bool ShouldStop () = 0;
+ virtual void RefreshStateAfterStop() = 0;
+ virtual bool NotifyException (MachException::Data& exc) { return false; }
+ virtual bool StepNotComplete () { return false; }
+ virtual size_t GetStackFrameData(lldb_private::StackFrame *frame, std::vector<std::pair<lldb::addr_t, lldb::addr_t> >& fp_pc_pairs) { return 0; }
+// virtual const uint8_t * SoftwareBreakpointOpcode (size_t byte_size) = 0;
+
+protected:
+ ThreadMacOSX &m_thread;
+
+};
+
+#endif // #ifndef liblldb_MachThreadContext_h_
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.cpp
new file mode 100644
index 00000000000..4f6b17c81a2
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.cpp
@@ -0,0 +1,1884 @@
+//===-- MachThreadContext_arm.cpp -------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachThreadContext_arm.h"
+
+#include <sys/sysctl.h>
+
+#include "ProcessMacOSX.h"
+#include "ProcessMacOSXLog.h"
+#include "ThreadMacOSX.h"
+
+using namespace lldb_private;
+
+//#define DNB_ARCH_MACH_ARM_DEBUG_SW_STEP 1
+
+static const uint8_t g_arm_breakpoint_opcode[] = { 0xFE, 0xDE, 0xFF, 0xE7 };
+static const uint8_t g_thumb_breakpooint_opcode[] = { 0xFE, 0xDE };
+
+// ARM constants used during decoding
+#define REG_RD 0
+#define LDM_REGLIST 1
+#define PC_REG 15
+#define PC_REGLIST_BIT 0x8000
+
+// ARM conditions
+#define COND_EQ 0x0
+#define COND_NE 0x1
+#define COND_CS 0x2
+#define COND_HS 0x2
+#define COND_CC 0x3
+#define COND_LO 0x3
+#define COND_MI 0x4
+#define COND_PL 0x5
+#define COND_VS 0x6
+#define COND_VC 0x7
+#define COND_HI 0x8
+#define COND_LS 0x9
+#define COND_GE 0xA
+#define COND_LT 0xB
+#define COND_GT 0xC
+#define COND_LE 0xD
+#define COND_AL 0xE
+#define COND_UNCOND 0xF
+
+#define MASK_CPSR_T (1u << 5)
+#define MASK_CPSR_J (1u << 24)
+
+#define MNEMONIC_STRING_SIZE 32
+#define OPERAND_STRING_SIZE 128
+
+using namespace lldb;
+using namespace lldb_private;
+
+MachThreadContext_arm::MachThreadContext_arm(ThreadMacOSX &thread) :
+ MachThreadContext(thread),
+ m_hw_single_chained_step_addr(LLDB_INVALID_ADDRESS),
+ m_bvr0_reg (LLDB_INVALID_REGNUM),
+ m_bcr0_reg (LLDB_INVALID_REGNUM),
+ m_bvr0_save (0),
+ m_bcr0_save (0)
+{
+}
+
+MachThreadContext_arm::~MachThreadContext_arm()
+{
+}
+
+RegisterContext *
+MachThreadContext_arm::CreateRegisterContext (StackFrame *frame) const
+{
+ return new RegisterContextMach_arm(m_thread, frame);
+}
+
+// Instance init function
+void
+MachThreadContext_arm::InitializeInstance()
+{
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext();
+ assert (reg_ctx != NULL);
+ const RegisterInfo * reg_info;
+ reg_info = reg_ctx->GetRegisterInfoByName ("bvr0");
+ if (reg_info)
+ m_bvr0_reg = reg_info->reg;
+
+ reg_info = reg_ctx->GetRegisterInfoByName ("bcr0");
+ if (reg_info)
+ m_bcr0_reg = reg_info->reg;
+}
+
+
+
+uint32_t
+MachThreadContext_arm::GetCPUType()
+{
+ return CPU_TYPE_ARM;
+}
+
+void
+MachThreadContext_arm::ThreadWillResume()
+{
+ // Do we need to step this thread? If so, let the mach thread tell us so.
+ if (m_thread.GetState() == eStateStepping)
+ {
+ bool step_handled = false;
+ // This is the primary thread, let the arch do anything it needs
+ if (m_thread.GetRegisterContext()->NumSupportedHardwareBreakpoints() > 0)
+ {
+#if defined (DNB_ARCH_MACH_ARM_DEBUG_SW_STEP)
+ bool half_step = m_hw_single_chained_step_addr != LLDB_INVALID_ADDRESS;
+#endif
+ step_handled = EnableHardwareSingleStep(true) == KERN_SUCCESS;
+#if defined (DNB_ARCH_MACH_ARM_DEBUG_SW_STEP)
+ if (!half_step)
+ step_handled = false;
+#endif
+ }
+
+#if defined (ENABLE_ARM_SINGLE_STEP)
+ if (!step_handled)
+ {
+ SetSingleStepSoftwareBreakpoints();
+ }
+#endif
+ }
+}
+
+bool
+MachThreadContext_arm::ShouldStop ()
+{
+ return true;
+}
+
+void
+MachThreadContext_arm::RefreshStateAfterStop ()
+{
+ EnableHardwareSingleStep (false) == KERN_SUCCESS;
+}
+
+#if defined (ENABLE_ARM_SINGLE_STEP)
+
+bool
+MachThreadContext_arm::ShouldStop ()
+{
+ return true;
+}
+
+bool
+MachThreadContext_arm::RefreshStateAfterStop ()
+{
+ success = EnableHardwareSingleStep(false) == KERN_SUCCESS;
+
+ bool success = true;
+
+ m_state.InvalidateRegisterSet (GPRRegSet);
+ m_state.InvalidateRegisterSet (VFPRegSet);
+ m_state.InvalidateRegisterSet (EXCRegSet);
+
+ // Are we stepping a single instruction?
+ if (ReadGPRRegisters(true) == KERN_SUCCESS)
+ {
+ // We are single stepping, was this the primary thread?
+ if (m_thread.GetState() == eStateStepping)
+ {
+#if defined (DNB_ARCH_MACH_ARM_DEBUG_SW_STEP)
+ success = EnableHardwareSingleStep(false) == KERN_SUCCESS;
+ // Hardware single step must work if we are going to test software
+ // single step functionality
+ assert(success);
+ if (m_hw_single_chained_step_addr == LLDB_INVALID_ADDRESS && m_sw_single_step_next_pc != LLDB_INVALID_ADDRESS)
+ {
+ uint32_t sw_step_next_pc = m_sw_single_step_next_pc & 0xFFFFFFFEu;
+ bool sw_step_next_pc_is_thumb = (m_sw_single_step_next_pc & 1) != 0;
+ bool actual_next_pc_is_thumb = (m_state.gpr.__cpsr & 0x20) != 0;
+ if (m_state.gpr.r[15] != sw_step_next_pc)
+ {
+ LogError("curr pc = 0x%8.8x - calculated single step target PC was incorrect: 0x%8.8x != 0x%8.8x", m_state.gpr.r[15], sw_step_next_pc, m_state.gpr.r[15]);
+ exit(1);
+ }
+ if (actual_next_pc_is_thumb != sw_step_next_pc_is_thumb)
+ {
+ LogError("curr pc = 0x%8.8x - calculated single step calculated mode mismatch: sw single mode = %s != %s",
+ m_state.gpr.r[15],
+ actual_next_pc_is_thumb ? "Thumb" : "ARM",
+ sw_step_next_pc_is_thumb ? "Thumb" : "ARM");
+ exit(1);
+ }
+ m_sw_single_step_next_pc = LLDB_INVALID_ADDRESS;
+ }
+#else
+#if defined (ENABLE_ARM_SINGLE_STEP)
+ // Are we software single stepping?
+ if (LLDB_BREAK_ID_IS_VALID(m_sw_single_step_break_id) || m_sw_single_step_itblock_break_count)
+ {
+ // Remove any software single stepping breakpoints that we have set
+
+ // Do we have a normal software single step breakpoint?
+ if (LLDB_BREAK_ID_IS_VALID(m_sw_single_step_break_id))
+ {
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: removing software single step breakpoint (breakID=%d)", __FUNCTION__, m_sw_single_step_break_id);
+ success = m_thread.Process()->DisableBreakpoint(m_sw_single_step_break_id, true);
+ m_sw_single_step_break_id = LLDB_INVALID_BREAK_ID;
+ }
+
+ // Do we have any Thumb IT breakpoints?
+ if (m_sw_single_step_itblock_break_count > 0)
+ {
+ // See if we hit one of our Thumb IT breakpoints?
+ DNBBreakpoint *step_bp = m_thread.Process()->Breakpoints().FindByAddress(m_state.gpr.r[15]);
+
+ if (step_bp)
+ {
+ // We did hit our breakpoint, tell the breakpoint it was
+ // hit so that it can run its callback routine and fixup
+ // the PC.
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: IT software single step breakpoint hit (breakID=%u)", __FUNCTION__, step_bp->GetID());
+ step_bp->BreakpointHit(m_thread.Process()->ProcessID(), m_thread.GetID());
+ }
+
+ // Remove all Thumb IT breakpoints
+ for (int i = 0; i < m_sw_single_step_itblock_break_count; i++)
+ {
+ if (LLDB_BREAK_ID_IS_VALID(m_sw_single_step_itblock_break_id[i]))
+ {
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: removing IT software single step breakpoint (breakID=%d)", __FUNCTION__, m_sw_single_step_itblock_break_id[i]);
+ success = m_thread.Process()->DisableBreakpoint(m_sw_single_step_itblock_break_id[i], true);
+ m_sw_single_step_itblock_break_id[i] = LLDB_INVALID_BREAK_ID;
+ }
+ }
+ m_sw_single_step_itblock_break_count = 0;
+
+ // Decode instructions up to the current PC to ensure the internal decoder state is valid for the IT block
+ // The decoder has to decode each instruction in the IT block even if it is not executed so that
+ // the fields are correctly updated
+ DecodeITBlockInstructions(m_state.gpr.r[15]);
+ }
+
+ }
+ else
+#endif
+ success = EnableHardwareSingleStep(false) == KERN_SUCCESS;
+#endif
+ }
+ else
+ {
+ // The MachThread will automatically restore the suspend count
+ // in ShouldStop (), so we don't need to do anything here if
+ // we weren't the primary thread the last time
+ }
+ }
+ return;
+}
+
+
+
+bool
+MachThreadContext_arm::StepNotComplete ()
+{
+ if (m_hw_single_chained_step_addr != LLDB_INVALID_ADDRESS)
+ {
+ kern_return_t kret = KERN_INVALID_ARGUMENT;
+ kret = ReadGPRRegisters(false);
+ if (kret == KERN_SUCCESS)
+ {
+ if (m_state.gpr.r[15] == m_hw_single_chained_step_addr)
+ {
+ //ProcessMacOSXLog::LogIf(PD_LOG_STEP, "Need to step some more at 0x%8.8x", m_hw_single_chained_step_addr);
+ return true;
+ }
+ }
+ }
+
+ m_hw_single_chained_step_addr = LLDB_INVALID_ADDRESS;
+ return false;
+}
+
+
+void
+MachThreadContext_arm::DecodeITBlockInstructions(lldb::addr_t curr_pc)
+
+{
+ uint16_t opcode16;
+ uint32_t opcode32;
+ lldb::addr_t next_pc_in_itblock;
+ lldb::addr_t pc_in_itblock = m_last_decode_pc;
+
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: last_decode_pc=0x%8.8x", __FUNCTION__, m_last_decode_pc);
+
+ // Decode IT block instruction from the instruction following the m_last_decoded_instruction at
+ // PC m_last_decode_pc upto and including the instruction at curr_pc
+ if (m_thread.Process()->Task().ReadMemory(pc_in_itblock, 2, &opcode16) == 2)
+ {
+ opcode32 = opcode16;
+ pc_in_itblock += 2;
+ // Check for 32 bit thumb opcode and read the upper 16 bits if needed
+ if (((opcode32 & 0xE000) == 0xE000) && opcode32 & 0x1800)
+ {
+ // Adjust 'next_pc_in_itblock' to point to the default next Thumb instruction for
+ // a 32 bit Thumb opcode
+ // Read bits 31:16 of a 32 bit Thumb opcode
+ if (m_thread.Process()->Task().ReadMemory(pc_in_itblock, 2, &opcode16) == 2)
+ {
+ pc_in_itblock += 2;
+ // 32 bit thumb opcode
+ opcode32 = (opcode32 << 16) | opcode16;
+ }
+ else
+ {
+ LogError("%s: Unable to read opcode bits 31:16 for a 32 bit thumb opcode at pc=0x%8.8lx", __FUNCTION__, pc_in_itblock);
+ }
+ }
+ }
+ else
+ {
+ LogError("%s: Error reading 16-bit Thumb instruction at pc=0x%8.8x", __FUNCTION__, pc_in_itblock);
+ }
+
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: pc_in_itblock=0x%8.8x, curr_pc=0x%8.8x", __FUNCTION__, pc_in_itblock, curr_pc);
+
+ next_pc_in_itblock = pc_in_itblock;
+ while (next_pc_in_itblock <= curr_pc)
+ {
+ arm_error_t decodeError;
+
+ m_last_decode_pc = pc_in_itblock;
+ decodeError = DecodeInstructionUsingDisassembler(pc_in_itblock, m_state.gpr.__cpsr, &m_last_decode_arm, &m_last_decode_thumb, &next_pc_in_itblock);
+
+ pc_in_itblock = next_pc_in_itblock;
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: next_pc_in_itblock=0x%8.8x", __FUNCTION__, next_pc_in_itblock);
+ }
+}
+
+#endif
+
+// Set the single step bit in the processor status register.
+kern_return_t
+MachThreadContext_arm::EnableHardwareSingleStep (bool enable)
+{
+ Error err;
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_STEP);
+
+ if (log) log->Printf("%s( enable = %d )", __FUNCTION__, enable);
+
+ if (m_bvr0_reg == LLDB_INVALID_REGNUM || m_bcr0_reg == LLDB_INVALID_REGNUM)
+ return KERN_INVALID_ARGUMENT;
+
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext();
+ uint32_t bvr = 0;
+ uint32_t bcr = 0;
+
+ const uint32_t i = 0;
+ if (enable)
+ {
+ m_hw_single_chained_step_addr = LLDB_INVALID_ADDRESS;
+
+ // Save our previous state
+ m_bvr0_save = reg_ctx->ReadRegisterAsUnsigned(m_bvr0_reg, 0);
+ m_bcr0_save = reg_ctx->ReadRegisterAsUnsigned(m_bcr0_reg, 0);
+ lldb::addr_t pc = reg_ctx->GetPC(LLDB_INVALID_ADDRESS);
+ lldb::addr_t cpsr = reg_ctx->GetFlags(0);
+ if (pc == LLDB_INVALID_ADDRESS)
+ return KERN_INVALID_ARGUMENT;
+
+ // Set a breakpoint that will stop when the PC doesn't match the current one!
+ bvr = pc & 0xFFFFFFFCu; // Set the current PC as the breakpoint address
+ bcr = BCR_M_IMVA_MISMATCH | // Stop on address mismatch
+ S_USER | // Stop only in user mode
+ BCR_ENABLE; // Enable this breakpoint
+ if (cpsr & 0x20)
+ {
+ // Thumb breakpoint
+ if (pc & 2)
+ bcr |= BAS_IMVA_2_3;
+ else
+ bcr |= BAS_IMVA_0_1;
+
+ uint16_t opcode;
+ Error error;
+ if (sizeof(opcode) == m_thread.GetProcess().ReadMemory(pc, &opcode, sizeof(opcode), error))
+ {
+ if (((opcode & 0xE000) == 0xE000) && opcode & 0x1800)
+ {
+ // 32 bit thumb opcode...
+ if (pc & 2)
+ {
+ // We can't take care of a 32 bit thumb instruction single step
+ // with just IVA mismatching. We will need to chain an extra
+ // hardware single step in order to complete this single step...
+ m_hw_single_chained_step_addr = pc + 2;
+ }
+ else
+ {
+ // Extend the number of bits to ignore for the mismatch
+ bcr |= BAS_IMVA_ALL;
+ }
+ }
+ }
+ }
+ else
+ {
+ // ARM breakpoint
+ bcr |= BAS_IMVA_ALL; // Stop when any address bits change
+ }
+
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: BVR%u=0x%8.8x BCR%u=0x%8.8x", __FUNCTION__, i, bvr, i, bcr);
+
+ m_bvr0_save = reg_ctx->ReadRegisterAsUnsigned(m_bvr0_reg, 0);
+ m_bcr0_save = reg_ctx->ReadRegisterAsUnsigned(m_bcr0_reg, 0);
+
+// for (uint32_t j=i+1; j<16; ++j)
+// {
+// // Disable all others
+// m_state.dbg.bvr[j] = 0;
+// m_state.dbg.bcr[j] = 0;
+// }
+ }
+ else
+ {
+ // Just restore the state we had before we did single stepping
+ bvr = m_bvr0_save;
+ bcr = m_bcr0_save;
+ }
+
+ if (reg_ctx->WriteRegisterFromUnsigned(m_bvr0_reg, bvr) &&
+ reg_ctx->WriteRegisterFromUnsigned(m_bcr0_reg, bcr))
+ return KERN_SUCCESS;
+
+ return KERN_INVALID_ARGUMENT;
+}
+
+#if defined (ENABLE_ARM_SINGLE_STEP)
+
+// return 1 if bit "BIT" is set in "value"
+static inline uint32_t bit(uint32_t value, uint32_t bit)
+{
+ return (value >> bit) & 1u;
+}
+
+// return the bitfield "value[msbit:lsbit]".
+static inline uint32_t bits(uint32_t value, uint32_t msbit, uint32_t lsbit)
+{
+ assert(msbit >= lsbit);
+ uint32_t shift_left = sizeof(value) * 8 - 1 - msbit;
+ value <<= shift_left; // shift anything above the msbit off of the unsigned edge
+ value >>= shift_left + lsbit; // shift it back again down to the lsbit (including undoing any shift from above)
+ return value; // return our result
+}
+
+bool
+MachThreadContext_arm::ConditionPassed(uint8_t condition, uint32_t cpsr)
+{
+ uint32_t cpsr_n = bit(cpsr, 31); // Negative condition code flag
+ uint32_t cpsr_z = bit(cpsr, 30); // Zero condition code flag
+ uint32_t cpsr_c = bit(cpsr, 29); // Carry condition code flag
+ uint32_t cpsr_v = bit(cpsr, 28); // Overflow condition code flag
+
+ switch (condition) {
+ case COND_EQ: // (0x0)
+ if (cpsr_z == 1) return true;
+ break;
+ case COND_NE: // (0x1)
+ if (cpsr_z == 0) return true;
+ break;
+ case COND_CS: // (0x2)
+ if (cpsr_c == 1) return true;
+ break;
+ case COND_CC: // (0x3)
+ if (cpsr_c == 0) return true;
+ break;
+ case COND_MI: // (0x4)
+ if (cpsr_n == 1) return true;
+ break;
+ case COND_PL: // (0x5)
+ if (cpsr_n == 0) return true;
+ break;
+ case COND_VS: // (0x6)
+ if (cpsr_v == 1) return true;
+ break;
+ case COND_VC: // (0x7)
+ if (cpsr_v == 0) return true;
+ break;
+ case COND_HI: // (0x8)
+ if ((cpsr_c == 1) && (cpsr_z == 0)) return true;
+ break;
+ case COND_LS: // (0x9)
+ if ((cpsr_c == 0) || (cpsr_z == 1)) return true;
+ break;
+ case COND_GE: // (0xA)
+ if (cpsr_n == cpsr_v) return true;
+ break;
+ case COND_LT: // (0xB)
+ if (cpsr_n != cpsr_v) return true;
+ break;
+ case COND_GT: // (0xC)
+ if ((cpsr_z == 0) && (cpsr_n == cpsr_v)) return true;
+ break;
+ case COND_LE: // (0xD)
+ if ((cpsr_z == 1) || (cpsr_n != cpsr_v)) return true;
+ break;
+ default:
+ return true;
+ break;
+ }
+
+ return false;
+}
+
+bool
+MachThreadContext_arm::ComputeNextPC(lldb::addr_t currentPC, arm_decoded_instruction_t decodedInstruction, bool currentPCIsThumb, lldb::addr_t *targetPC)
+{
+ lldb::addr_t myTargetPC, addressWherePCLives;
+ lldb::pid_t mypid;
+
+ uint32_t cpsr_c = bit(m_state.gpr.cpsr, 29); // Carry condition code flag
+
+ uint32_t firstOperand=0, secondOperand=0, shiftAmount=0, secondOperandAfterShift=0, immediateValue=0;
+ uint32_t halfwords=0, baseAddress=0, immediateOffset=0, addressOffsetFromRegister=0, addressOffsetFromRegisterAfterShift;
+ uint32_t baseAddressIndex=LLDB_INVALID_INDEX32;
+ uint32_t firstOperandIndex=LLDB_INVALID_INDEX32;
+ uint32_t secondOperandIndex=LLDB_INVALID_INDEX32;
+ uint32_t addressOffsetFromRegisterIndex=LLDB_INVALID_INDEX32;
+ uint32_t shiftRegisterIndex=LLDB_INVALID_INDEX32;
+ uint16_t registerList16, registerList16NoPC;
+ uint8_t registerList8;
+ uint32_t numRegistersToLoad=0;
+
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: instruction->code=%d", __FUNCTION__, decodedInstruction.instruction->code);
+
+ // Get the following in this switch statement:
+ // - firstOperand, secondOperand, immediateValue, shiftAmount: For arithmetic, logical and move instructions
+ // - baseAddress, immediateOffset, shiftAmount: For LDR
+ // - numRegistersToLoad: For LDM and POP instructions
+ switch (decodedInstruction.instruction->code)
+ {
+ // Arithmetic operations that can change the PC
+ case ARM_INST_ADC:
+ case ARM_INST_ADCS:
+ case ARM_INST_ADD:
+ case ARM_INST_ADDS:
+ case ARM_INST_AND:
+ case ARM_INST_ANDS:
+ case ARM_INST_ASR:
+ case ARM_INST_ASRS:
+ case ARM_INST_BIC:
+ case ARM_INST_BICS:
+ case ARM_INST_EOR:
+ case ARM_INST_EORS:
+ case ARM_INST_ORR:
+ case ARM_INST_ORRS:
+ case ARM_INST_RSB:
+ case ARM_INST_RSBS:
+ case ARM_INST_RSC:
+ case ARM_INST_RSCS:
+ case ARM_INST_SBC:
+ case ARM_INST_SBCS:
+ case ARM_INST_SUB:
+ case ARM_INST_SUBS:
+ switch (decodedInstruction.addressMode)
+ {
+ case ARM_ADDR_DATA_IMM:
+ if (decodedInstruction.numOperands != 3)
+ {
+ LogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ if (decodedInstruction.op[0].value != PC_REG)
+ {
+ LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+
+ // Get firstOperand register value (at index=1)
+ firstOperandIndex = decodedInstruction.op[1].value; // first operand register index
+ firstOperand = m_state.gpr.r[firstOperandIndex];
+
+ // Get immediateValue (at index=2)
+ immediateValue = decodedInstruction.op[2].value;
+
+ break;
+
+ case ARM_ADDR_DATA_REG:
+ if (decodedInstruction.numOperands != 3)
+ {
+ LogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ if (decodedInstruction.op[0].value != PC_REG)
+ {
+ LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+
+ // Get firstOperand register value (at index=1)
+ firstOperandIndex = decodedInstruction.op[1].value; // first operand register index
+ firstOperand = m_state.gpr.r[firstOperandIndex];
+
+ // Get secondOperand register value (at index=2)
+ secondOperandIndex = decodedInstruction.op[2].value; // second operand register index
+ secondOperand = m_state.gpr.r[secondOperandIndex];
+
+ break;
+
+ case ARM_ADDR_DATA_SCALED_IMM:
+ if (decodedInstruction.numOperands != 4)
+ {
+ LogError("Expected 4 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ if (decodedInstruction.op[0].value != PC_REG)
+ {
+ LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+
+ // Get firstOperand register value (at index=1)
+ firstOperandIndex = decodedInstruction.op[1].value; // first operand register index
+ firstOperand = m_state.gpr.r[firstOperandIndex];
+
+ // Get secondOperand register value (at index=2)
+ secondOperandIndex = decodedInstruction.op[2].value; // second operand register index
+ secondOperand = m_state.gpr.r[secondOperandIndex];
+
+ // Get shiftAmount as immediate value (at index=3)
+ shiftAmount = decodedInstruction.op[3].value;
+
+ break;
+
+
+ case ARM_ADDR_DATA_SCALED_REG:
+ if (decodedInstruction.numOperands != 4)
+ {
+ LogError("Expected 4 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ if (decodedInstruction.op[0].value != PC_REG)
+ {
+ LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+
+ // Get firstOperand register value (at index=1)
+ firstOperandIndex = decodedInstruction.op[1].value; // first operand register index
+ firstOperand = m_state.gpr.r[firstOperandIndex];
+
+ // Get secondOperand register value (at index=2)
+ secondOperandIndex = decodedInstruction.op[2].value; // second operand register index
+ secondOperand = m_state.gpr.r[secondOperandIndex];
+
+ // Get shiftAmount from register (at index=3)
+ shiftRegisterIndex = decodedInstruction.op[3].value; // second operand register index
+ shiftAmount = m_state.gpr.r[shiftRegisterIndex];
+
+ break;
+
+ case THUMB_ADDR_HR_HR:
+ if (decodedInstruction.numOperands != 2)
+ {
+ LogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ if (decodedInstruction.op[0].value != PC_REG)
+ {
+ LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+
+ // Get firstOperand register value (at index=0)
+ firstOperandIndex = decodedInstruction.op[0].value; // first operand register index
+ firstOperand = m_state.gpr.r[firstOperandIndex];
+
+ // Get secondOperand register value (at index=1)
+ secondOperandIndex = decodedInstruction.op[1].value; // second operand register index
+ secondOperand = m_state.gpr.r[secondOperandIndex];
+
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ // Logical shifts and move operations that can change the PC
+ case ARM_INST_LSL:
+ case ARM_INST_LSLS:
+ case ARM_INST_LSR:
+ case ARM_INST_LSRS:
+ case ARM_INST_MOV:
+ case ARM_INST_MOVS:
+ case ARM_INST_MVN:
+ case ARM_INST_MVNS:
+ case ARM_INST_ROR:
+ case ARM_INST_RORS:
+ case ARM_INST_RRX:
+ case ARM_INST_RRXS:
+ // In these cases, the firstOperand is always 0, as if it does not exist
+ switch (decodedInstruction.addressMode)
+ {
+ case ARM_ADDR_DATA_IMM:
+ if (decodedInstruction.numOperands != 2)
+ {
+ LogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ if (decodedInstruction.op[0].value != PC_REG)
+ {
+ LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+
+ // Get immediateValue (at index=1)
+ immediateValue = decodedInstruction.op[1].value;
+
+ break;
+
+ case ARM_ADDR_DATA_REG:
+ if (decodedInstruction.numOperands != 2)
+ {
+ LogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ if (decodedInstruction.op[0].value != PC_REG)
+ {
+ LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+
+ // Get secondOperand register value (at index=1)
+ secondOperandIndex = decodedInstruction.op[1].value; // second operand register index
+ secondOperand = m_state.gpr.r[secondOperandIndex];
+
+ break;
+
+ case ARM_ADDR_DATA_SCALED_IMM:
+ if (decodedInstruction.numOperands != 3)
+ {
+ LogError("Expected 4 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ if (decodedInstruction.op[0].value != PC_REG)
+ {
+ LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+
+ // Get secondOperand register value (at index=1)
+ secondOperandIndex = decodedInstruction.op[2].value; // second operand register index
+ secondOperand = m_state.gpr.r[secondOperandIndex];
+
+ // Get shiftAmount as immediate value (at index=2)
+ shiftAmount = decodedInstruction.op[2].value;
+
+ break;
+
+
+ case ARM_ADDR_DATA_SCALED_REG:
+ if (decodedInstruction.numOperands != 3)
+ {
+ LogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ if (decodedInstruction.op[0].value != PC_REG)
+ {
+ LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+
+ // Get secondOperand register value (at index=1)
+ secondOperandIndex = decodedInstruction.op[1].value; // second operand register index
+ secondOperand = m_state.gpr.r[secondOperandIndex];
+
+ // Get shiftAmount from register (at index=2)
+ shiftRegisterIndex = decodedInstruction.op[2].value; // second operand register index
+ shiftAmount = m_state.gpr.r[shiftRegisterIndex];
+
+ break;
+
+ case THUMB_ADDR_HR_HR:
+ if (decodedInstruction.numOperands != 2)
+ {
+ LogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ if (decodedInstruction.op[0].value != PC_REG)
+ {
+ LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+
+ // Get secondOperand register value (at index=1)
+ secondOperandIndex = decodedInstruction.op[1].value; // second operand register index
+ secondOperand = m_state.gpr.r[secondOperandIndex];
+
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ // Simple branches, used to hop around within a routine
+ case ARM_INST_B:
+ *targetPC = decodedInstruction.targetPC; // Known targetPC
+ return true;
+ break;
+
+ // Branch-and-link, used to call ARM subroutines
+ case ARM_INST_BL:
+ *targetPC = decodedInstruction.targetPC; // Known targetPC
+ return true;
+ break;
+
+ // Branch-and-link with exchange, used to call opposite-mode subroutines
+ case ARM_INST_BLX:
+ if ((decodedInstruction.addressMode == ARM_ADDR_BRANCH_IMM) ||
+ (decodedInstruction.addressMode == THUMB_ADDR_UNCOND))
+ {
+ *targetPC = decodedInstruction.targetPC; // Known targetPC
+ return true;
+ }
+ else // addressMode == ARM_ADDR_BRANCH_REG
+ {
+ // Unknown target unless we're branching to the PC itself,
+ // although this may not work properly with BLX
+ if (decodedInstruction.op[REG_RD].value == PC_REG)
+ {
+ // this should (almost) never happen
+ *targetPC = decodedInstruction.targetPC; // Known targetPC
+ return true;
+ }
+
+ // Get the branch address and return
+ if (decodedInstruction.numOperands != 1)
+ {
+ LogError("Expected 1 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ // Get branch address in register (at index=0)
+ *targetPC = m_state.gpr.r[decodedInstruction.op[0].value];
+ return true;
+ }
+ break;
+
+ // Branch with exchange, used to hop to opposite-mode code
+ // Branch to Jazelle code, used to execute Java; included here since it
+ // acts just like BX unless the Jazelle unit is active and JPC is
+ // already loaded into it.
+ case ARM_INST_BX:
+ case ARM_INST_BXJ:
+ // Unknown target unless we're branching to the PC itself,
+ // although this can never switch to Thumb mode and is
+ // therefore pretty much useless
+ if (decodedInstruction.op[REG_RD].value == PC_REG)
+ {
+ // this should (almost) never happen
+ *targetPC = decodedInstruction.targetPC; // Known targetPC
+ return true;
+ }
+
+ // Get the branch address and return
+ if (decodedInstruction.numOperands != 1)
+ {
+ LogError("Expected 1 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ // Get branch address in register (at index=0)
+ *targetPC = m_state.gpr.r[decodedInstruction.op[0].value];
+ return true;
+ break;
+
+ // Compare and branch on zero/non-zero (Thumb-16 only)
+ // Unusual condition check built into the instruction
+ case ARM_INST_CBZ:
+ case ARM_INST_CBNZ:
+ // Branch address is known at compile time
+ // Get the branch address and return
+ if (decodedInstruction.numOperands != 2)
+ {
+ LogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ // Get branch address as an immediate value (at index=1)
+ *targetPC = decodedInstruction.op[1].value;
+ return true;
+ break;
+
+ // Load register can be used to load PC, usually with a function pointer
+ case ARM_INST_LDR:
+ if (decodedInstruction.op[REG_RD].value != PC_REG)
+ {
+ LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+ switch (decodedInstruction.addressMode)
+ {
+ case ARM_ADDR_LSWUB_IMM:
+ case ARM_ADDR_LSWUB_IMM_PRE:
+ case ARM_ADDR_LSWUB_IMM_POST:
+ if (decodedInstruction.numOperands != 3)
+ {
+ LogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ // Get baseAddress from register (at index=1)
+ baseAddressIndex = decodedInstruction.op[1].value;
+ baseAddress = m_state.gpr.r[baseAddressIndex];
+
+ // Get immediateOffset (at index=2)
+ immediateOffset = decodedInstruction.op[2].value;
+ break;
+
+ case ARM_ADDR_LSWUB_REG:
+ case ARM_ADDR_LSWUB_REG_PRE:
+ case ARM_ADDR_LSWUB_REG_POST:
+ if (decodedInstruction.numOperands != 3)
+ {
+ LogError("Expected 3 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ // Get baseAddress from register (at index=1)
+ baseAddressIndex = decodedInstruction.op[1].value;
+ baseAddress = m_state.gpr.r[baseAddressIndex];
+
+ // Get immediateOffset from register (at index=2)
+ addressOffsetFromRegisterIndex = decodedInstruction.op[2].value;
+ addressOffsetFromRegister = m_state.gpr.r[addressOffsetFromRegisterIndex];
+
+ break;
+
+ case ARM_ADDR_LSWUB_SCALED:
+ case ARM_ADDR_LSWUB_SCALED_PRE:
+ case ARM_ADDR_LSWUB_SCALED_POST:
+ if (decodedInstruction.numOperands != 4)
+ {
+ LogError("Expected 4 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ // Get baseAddress from register (at index=1)
+ baseAddressIndex = decodedInstruction.op[1].value;
+ baseAddress = m_state.gpr.r[baseAddressIndex];
+
+ // Get immediateOffset from register (at index=2)
+ addressOffsetFromRegisterIndex = decodedInstruction.op[2].value;
+ addressOffsetFromRegister = m_state.gpr.r[addressOffsetFromRegisterIndex];
+
+ // Get shiftAmount (at index=3)
+ shiftAmount = decodedInstruction.op[3].value;
+
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ // 32b load multiple operations can load the PC along with everything else,
+ // usually to return from a function call
+ case ARM_INST_LDMDA:
+ case ARM_INST_LDMDB:
+ case ARM_INST_LDMIA:
+ case ARM_INST_LDMIB:
+ if (decodedInstruction.op[LDM_REGLIST].value & PC_REGLIST_BIT)
+ {
+ if (decodedInstruction.numOperands != 2)
+ {
+ LogError("Expected 2 operands in decoded instruction structure. numOperands is %d!", decodedInstruction.numOperands);
+ return false;
+ }
+
+ // Get baseAddress from register (at index=0)
+ baseAddressIndex = decodedInstruction.op[0].value;
+ baseAddress = m_state.gpr.r[baseAddressIndex];
+
+ // Get registerList from register (at index=1)
+ registerList16 = (uint16_t)decodedInstruction.op[1].value;
+
+ // Count number of registers to load in the multiple register list excluding the PC
+ registerList16NoPC = registerList16&0x3FFF; // exclude the PC
+ numRegistersToLoad=0;
+ for (int i = 0; i < 16; i++)
+ {
+ if (registerList16NoPC & 0x1) numRegistersToLoad++;
+ registerList16NoPC = registerList16NoPC >> 1;
+ }
+ }
+ else
+ {
+ LogError("Destination register is not a PC! %s routine should be called on on instructions that modify the PC. Destination register is R%d!", __FUNCTION__, decodedInstruction.op[0].value);
+ return false;
+ }
+ break;
+
+ // Normal 16-bit LD multiple can't touch R15, but POP can
+ case ARM_INST_POP: // Can also get the PC & updates SP
+ // Get baseAddress from SP (at index=0)
+ baseAddress = m_state.gpr.__sp;
+
+ if (decodedInstruction.thumb16b)
+ {
+ // Get registerList from register (at index=0)
+ registerList8 = (uint8_t)decodedInstruction.op[0].value;
+
+ // Count number of registers to load in the multiple register list
+ numRegistersToLoad=0;
+ for (int i = 0; i < 8; i++)
+ {
+ if (registerList8 & 0x1) numRegistersToLoad++;
+ registerList8 = registerList8 >> 1;
+ }
+ }
+ else
+ {
+ // Get registerList from register (at index=0)
+ registerList16 = (uint16_t)decodedInstruction.op[0].value;
+
+ // Count number of registers to load in the multiple register list excluding the PC
+ registerList16NoPC = registerList16&0x3FFF; // exclude the PC
+ numRegistersToLoad=0;
+ for (int i = 0; i < 16; i++)
+ {
+ if (registerList16NoPC & 0x1) numRegistersToLoad++;
+ registerList16NoPC = registerList16NoPC >> 1;
+ }
+ }
+ break;
+
+ // 16b TBB and TBH instructions load a jump address from a table
+ case ARM_INST_TBB:
+ case ARM_INST_TBH:
+ // Get baseAddress from register (at index=0)
+ baseAddressIndex = decodedInstruction.op[0].value;
+ baseAddress = m_state.gpr.r[baseAddressIndex];
+
+ // Get immediateOffset from register (at index=1)
+ addressOffsetFromRegisterIndex = decodedInstruction.op[1].value;
+ addressOffsetFromRegister = m_state.gpr.r[addressOffsetFromRegisterIndex];
+ break;
+
+ // ThumbEE branch-to-handler instructions: Jump to handlers at some offset
+ // from a special base pointer register (which is unknown at disassembly time)
+ case ARM_INST_HB:
+ case ARM_INST_HBP:
+// TODO: ARM_INST_HB, ARM_INST_HBP
+ break;
+
+ case ARM_INST_HBL:
+ case ARM_INST_HBLP:
+// TODO: ARM_INST_HBL, ARM_INST_HBLP
+ break;
+
+ // Breakpoint and software interrupt jump to interrupt handler (always ARM)
+ case ARM_INST_BKPT:
+ case ARM_INST_SMC:
+ case ARM_INST_SVC:
+
+ // Return from exception, obviously modifies PC [interrupt only!]
+ case ARM_INST_RFEDA:
+ case ARM_INST_RFEDB:
+ case ARM_INST_RFEIA:
+ case ARM_INST_RFEIB:
+
+ // Other instructions either can't change R15 or are "undefined" if you do,
+ // so no sane compiler should ever generate them & we don't care here.
+ // Also, R15 can only legally be used in a read-only manner for the
+ // various ARM addressing mode (to get PC-relative addressing of constants),
+ // but can NOT be used with any of the update modes.
+ default:
+ LogError("%s should not be called for instruction code %d!", __FUNCTION__, decodedInstruction.instruction->code);
+ return false;
+ break;
+ }
+
+ // Adjust PC if PC is one of the input operands
+ if (baseAddressIndex == PC_REG)
+ {
+ if (currentPCIsThumb)
+ baseAddress += 4;
+ else
+ baseAddress += 8;
+ }
+
+ if (firstOperandIndex == PC_REG)
+ {
+ if (currentPCIsThumb)
+ firstOperand += 4;
+ else
+ firstOperand += 8;
+ }
+
+ if (secondOperandIndex == PC_REG)
+ {
+ if (currentPCIsThumb)
+ secondOperand += 4;
+ else
+ secondOperand += 8;
+ }
+
+ if (addressOffsetFromRegisterIndex == PC_REG)
+ {
+ if (currentPCIsThumb)
+ addressOffsetFromRegister += 4;
+ else
+ addressOffsetFromRegister += 8;
+ }
+
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE,
+ "%s: firstOperand=%8.8x, secondOperand=%8.8x, immediateValue = %d, shiftAmount = %d, baseAddress = %8.8x, addressOffsetFromRegister = %8.8x, immediateOffset = %d, numRegistersToLoad = %d",
+ __FUNCTION__,
+ firstOperand,
+ secondOperand,
+ immediateValue,
+ shiftAmount,
+ baseAddress,
+ addressOffsetFromRegister,
+ immediateOffset,
+ numRegistersToLoad);
+
+
+ // Calculate following values after applying shiftAmount:
+ // - immediateOffsetAfterShift, secondOperandAfterShift
+
+ switch (decodedInstruction.scaleMode)
+ {
+ case ARM_SCALE_NONE:
+ addressOffsetFromRegisterAfterShift = addressOffsetFromRegister;
+ secondOperandAfterShift = secondOperand;
+ break;
+
+ case ARM_SCALE_LSL: // Logical shift left
+ addressOffsetFromRegisterAfterShift = addressOffsetFromRegister << shiftAmount;
+ secondOperandAfterShift = secondOperand << shiftAmount;
+ break;
+
+ case ARM_SCALE_LSR: // Logical shift right
+ addressOffsetFromRegisterAfterShift = addressOffsetFromRegister >> shiftAmount;
+ secondOperandAfterShift = secondOperand >> shiftAmount;
+ break;
+
+ case ARM_SCALE_ASR: // Arithmetic shift right
+ asm("mov %0, %1, asr %2" : "=r" (addressOffsetFromRegisterAfterShift) : "r" (addressOffsetFromRegister), "r" (shiftAmount));
+ asm("mov %0, %1, asr %2" : "=r" (secondOperandAfterShift) : "r" (secondOperand), "r" (shiftAmount));
+ break;
+
+ case ARM_SCALE_ROR: // Rotate right
+ asm("mov %0, %1, ror %2" : "=r" (addressOffsetFromRegisterAfterShift) : "r" (addressOffsetFromRegister), "r" (shiftAmount));
+ asm("mov %0, %1, ror %2" : "=r" (secondOperandAfterShift) : "r" (secondOperand), "r" (shiftAmount));
+ break;
+
+ case ARM_SCALE_RRX: // Rotate right, pulling in carry (1-bit shift only)
+ asm("mov %0, %1, rrx" : "=r" (addressOffsetFromRegisterAfterShift) : "r" (addressOffsetFromRegister));
+ asm("mov %0, %1, rrx" : "=r" (secondOperandAfterShift) : "r" (secondOperand));
+ break;
+ }
+
+ // Emulate instruction to calculate targetPC
+ // All branches are already handled in the first switch statement. A branch should not reach this switch
+ switch (decodedInstruction.instruction->code)
+ {
+ // Arithmetic operations that can change the PC
+ case ARM_INST_ADC:
+ case ARM_INST_ADCS:
+ // Add with Carry
+ *targetPC = firstOperand + (secondOperandAfterShift + immediateValue) + cpsr_c;
+ break;
+
+ case ARM_INST_ADD:
+ case ARM_INST_ADDS:
+ *targetPC = firstOperand + (secondOperandAfterShift + immediateValue);
+ break;
+
+ case ARM_INST_AND:
+ case ARM_INST_ANDS:
+ *targetPC = firstOperand & (secondOperandAfterShift + immediateValue);
+ break;
+
+ case ARM_INST_ASR:
+ case ARM_INST_ASRS:
+ asm("mov %0, %1, asr %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue));
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_BIC:
+ case ARM_INST_BICS:
+ asm("bic %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue));
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_EOR:
+ case ARM_INST_EORS:
+ asm("eor %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue));
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_ORR:
+ case ARM_INST_ORRS:
+ asm("orr %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue));
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_RSB:
+ case ARM_INST_RSBS:
+ asm("rsb %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue));
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_RSC:
+ case ARM_INST_RSCS:
+ myTargetPC = secondOperandAfterShift - (firstOperand + !cpsr_c);
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_SBC:
+ case ARM_INST_SBCS:
+ asm("sbc %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue + !cpsr_c));
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_SUB:
+ case ARM_INST_SUBS:
+ asm("sub %0, %1, %2" : "=r" (myTargetPC) : "r" (firstOperand), "r" (secondOperandAfterShift + immediateValue));
+ *targetPC = myTargetPC;
+ break;
+
+ // Logical shifts and move operations that can change the PC
+ case ARM_INST_LSL:
+ case ARM_INST_LSLS:
+ case ARM_INST_LSR:
+ case ARM_INST_LSRS:
+ case ARM_INST_MOV:
+ case ARM_INST_MOVS:
+ case ARM_INST_ROR:
+ case ARM_INST_RORS:
+ case ARM_INST_RRX:
+ case ARM_INST_RRXS:
+ myTargetPC = secondOperandAfterShift + immediateValue;
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_MVN:
+ case ARM_INST_MVNS:
+ myTargetPC = !(secondOperandAfterShift + immediateValue);
+ *targetPC = myTargetPC;
+ break;
+
+ // Load register can be used to load PC, usually with a function pointer
+ case ARM_INST_LDR:
+ switch (decodedInstruction.addressMode) {
+ case ARM_ADDR_LSWUB_IMM_POST:
+ case ARM_ADDR_LSWUB_REG_POST:
+ case ARM_ADDR_LSWUB_SCALED_POST:
+ addressWherePCLives = baseAddress;
+ break;
+
+ case ARM_ADDR_LSWUB_IMM:
+ case ARM_ADDR_LSWUB_REG:
+ case ARM_ADDR_LSWUB_SCALED:
+ case ARM_ADDR_LSWUB_IMM_PRE:
+ case ARM_ADDR_LSWUB_REG_PRE:
+ case ARM_ADDR_LSWUB_SCALED_PRE:
+ addressWherePCLives = baseAddress + (addressOffsetFromRegisterAfterShift + immediateOffset);
+ break;
+
+ default:
+ break;
+ }
+
+ mypid = m_thread.ProcessID();
+ if (PDProcessMemoryRead(mypid, addressWherePCLives, sizeof(lldb::addr_t), &myTargetPC) != sizeof(lldb::addr_t))
+ {
+ LogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives);
+ return false;
+ }
+
+ *targetPC = myTargetPC;
+ break;
+
+ // 32b load multiple operations can load the PC along with everything else,
+ // usually to return from a function call
+ case ARM_INST_LDMDA:
+ mypid = m_thread.ProcessID();
+ addressWherePCLives = baseAddress;
+ if (PDProcessMemoryRead(mypid, addressWherePCLives, sizeof(lldb::addr_t), &myTargetPC) != sizeof(lldb::addr_t))
+ {
+ LogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives);
+ return false;
+ }
+
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_LDMDB:
+ mypid = m_thread.ProcessID();
+ addressWherePCLives = baseAddress - 4;
+ if (PDProcessMemoryRead(mypid, addressWherePCLives, sizeof(lldb::addr_t), &myTargetPC) != sizeof(lldb::addr_t))
+ {
+ LogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives);
+ return false;
+ }
+
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_LDMIB:
+ mypid = m_thread.ProcessID();
+ addressWherePCLives = baseAddress + numRegistersToLoad*4 + 4;
+ if (PDProcessMemoryRead(mypid, addressWherePCLives, sizeof(lldb::addr_t), &myTargetPC) != sizeof(lldb::addr_t))
+ {
+ LogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives);
+ return false;
+ }
+
+ *targetPC = myTargetPC;
+ break;
+
+ case ARM_INST_LDMIA: // same as pop
+ // Normal 16-bit LD multiple can't touch R15, but POP can
+ case ARM_INST_POP: // Can also get the PC & updates SP
+ mypid = m_thread.ProcessID();
+ addressWherePCLives = baseAddress + numRegistersToLoad*4;
+ if (PDProcessMemoryRead(mypid, addressWherePCLives, sizeof(lldb::addr_t), &myTargetPC) != sizeof(lldb::addr_t))
+ {
+ LogError("Could not read memory at %8.8x to get targetPC when processing the pop instruction!", addressWherePCLives);
+ return false;
+ }
+
+ *targetPC = myTargetPC;
+ break;
+
+ // 16b TBB and TBH instructions load a jump address from a table
+ case ARM_INST_TBB:
+ mypid = m_thread.ProcessID();
+ addressWherePCLives = baseAddress + addressOffsetFromRegisterAfterShift;
+ if (PDProcessMemoryRead(mypid, addressWherePCLives, 1, &halfwords) != 1)
+ {
+ LogError("Could not read memory at %8.8x to get targetPC when processing the TBB instruction!", addressWherePCLives);
+ return false;
+ }
+ // add 4 to currentPC since we are in Thumb mode and then add 2*halfwords
+ *targetPC = (currentPC + 4) + 2*halfwords;
+ break;
+
+ case ARM_INST_TBH:
+ mypid = m_thread.ProcessID();
+ addressWherePCLives = ((baseAddress + (addressOffsetFromRegisterAfterShift << 1)) & ~0x1);
+ if (PDProcessMemoryRead(mypid, addressWherePCLives, 2, &halfwords) != 2)
+ {
+ LogError("Could not read memory at %8.8x to get targetPC when processing the TBH instruction!", addressWherePCLives);
+ return false;
+ }
+ // add 4 to currentPC since we are in Thumb mode and then add 2*halfwords
+ *targetPC = (currentPC + 4) + 2*halfwords;
+ break;
+
+ // ThumbEE branch-to-handler instructions: Jump to handlers at some offset
+ // from a special base pointer register (which is unknown at disassembly time)
+ case ARM_INST_HB:
+ case ARM_INST_HBP:
+ // TODO: ARM_INST_HB, ARM_INST_HBP
+ break;
+
+ case ARM_INST_HBL:
+ case ARM_INST_HBLP:
+ // TODO: ARM_INST_HBL, ARM_INST_HBLP
+ break;
+
+ // Breakpoint and software interrupt jump to interrupt handler (always ARM)
+ case ARM_INST_BKPT:
+ case ARM_INST_SMC:
+ case ARM_INST_SVC:
+ // TODO: ARM_INST_BKPT, ARM_INST_SMC, ARM_INST_SVC
+ break;
+
+ // Return from exception, obviously modifies PC [interrupt only!]
+ case ARM_INST_RFEDA:
+ case ARM_INST_RFEDB:
+ case ARM_INST_RFEIA:
+ case ARM_INST_RFEIB:
+ // TODO: ARM_INST_RFEDA, ARM_INST_RFEDB, ARM_INST_RFEIA, ARM_INST_RFEIB
+ break;
+
+ // Other instructions either can't change R15 or are "undefined" if you do,
+ // so no sane compiler should ever generate them & we don't care here.
+ // Also, R15 can only legally be used in a read-only manner for the
+ // various ARM addressing mode (to get PC-relative addressing of constants),
+ // but can NOT be used with any of the update modes.
+ default:
+ LogError("%s should not be called for instruction code %d!", __FUNCTION__, decodedInstruction.instruction->code);
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+void
+MachThreadContext_arm::EvaluateNextInstructionForSoftwareBreakpointSetup(lldb::addr_t currentPC, uint32_t cpsr, bool currentPCIsThumb, lldb::addr_t *nextPC, bool *nextPCIsThumb)
+{
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "MachThreadContext_arm::EvaluateNextInstructionForSoftwareBreakpointSetup() called");
+
+ lldb::addr_t targetPC = LLDB_INVALID_ADDRESS;
+ uint32_t registerValue;
+ arm_error_t decodeError;
+ lldb::addr_t currentPCInITBlock, nextPCInITBlock;
+ int i;
+ bool last_decoded_instruction_executes = true;
+
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: default nextPC=0x%8.8x (%s)", __FUNCTION__, *nextPC, *nextPCIsThumb ? "Thumb" : "ARM");
+
+ // Update *nextPC and *nextPCIsThumb for special cases
+ if (m_last_decode_thumb.itBlockRemaining) // we are in an IT block
+ {
+ // Set the nextPC to the PC of the instruction which will execute in the IT block
+ // If none of the instruction execute in the IT block based on the condition flags,
+ // then point to the instruction immediately following the IT block
+ const int itBlockRemaining = m_last_decode_thumb.itBlockRemaining;
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: itBlockRemaining=%8.8x", __FUNCTION__, itBlockRemaining);
+
+ // Determine the PC at which the next instruction resides
+ if (m_last_decode_arm.thumb16b)
+ currentPCInITBlock = currentPC + 2;
+ else
+ currentPCInITBlock = currentPC + 4;
+
+ for (i = 0; i < itBlockRemaining; i++)
+ {
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: currentPCInITBlock=%8.8x", __FUNCTION__, currentPCInITBlock);
+ decodeError = DecodeInstructionUsingDisassembler(currentPCInITBlock, cpsr, &m_last_decode_arm, &m_last_decode_thumb, &nextPCInITBlock);
+
+ if (decodeError != ARM_SUCCESS)
+ LogError("unable to disassemble instruction at 0x%8.8lx", currentPCInITBlock);
+
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: condition=%d", __FUNCTION__, m_last_decode_arm.condition);
+ if (ConditionPassed(m_last_decode_arm.condition, cpsr))
+ {
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: Condition codes matched for instruction %d", __FUNCTION__, i);
+ break; // break from the for loop
+ }
+ else
+ {
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: Condition codes DID NOT matched for instruction %d", __FUNCTION__, i);
+ }
+
+ // update currentPC and nextPCInITBlock
+ currentPCInITBlock = nextPCInITBlock;
+ }
+
+ if (i == itBlockRemaining) // We came out of the IT block without executing any instructions
+ last_decoded_instruction_executes = false;
+
+ *nextPC = currentPCInITBlock;
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: After IT block step-through: *nextPC=%8.8x", __FUNCTION__, *nextPC);
+ }
+
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE,
+ "%s: cpsr = %8.8x, thumb16b = %d, thumb = %d, branch = %d, conditional = %d, knownTarget = %d, links = %d, canSwitchMode = %d, doesSwitchMode = %d",
+ __FUNCTION__,
+ cpsr,
+ m_last_decode_arm.thumb16b,
+ m_last_decode_arm.thumb,
+ m_last_decode_arm.branch,
+ m_last_decode_arm.conditional,
+ m_last_decode_arm.knownTarget,
+ m_last_decode_arm.links,
+ m_last_decode_arm.canSwitchMode,
+ m_last_decode_arm.doesSwitchMode);
+
+
+ if (last_decoded_instruction_executes && // Was this a conditional instruction that did execute?
+ m_last_decode_arm.branch && // Can this instruction change the PC?
+ (m_last_decode_arm.instruction->code != ARM_INST_SVC)) // If this instruction is not an SVC instruction
+ {
+ // Set targetPC. Compute if needed.
+ if (m_last_decode_arm.knownTarget)
+ {
+ // Fixed, known PC-relative
+ targetPC = m_last_decode_arm.targetPC;
+ }
+ else
+ {
+ // if targetPC is not known at compile time (PC-relative target), compute targetPC
+ if (!ComputeNextPC(currentPC, m_last_decode_arm, currentPCIsThumb, &targetPC))
+ {
+ LogError("%s: Unable to compute targetPC for instruction at 0x%8.8lx", __FUNCTION__, currentPC);
+ targetPC = LLDB_INVALID_ADDRESS;
+ }
+ }
+
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: targetPC=0x%8.8x, cpsr=0x%8.8x, condition=0x%hhx", __FUNCTION__, targetPC, cpsr, m_last_decode_arm.condition);
+
+ // Refine nextPC computation
+ if ((m_last_decode_arm.instruction->code == ARM_INST_CBZ) ||
+ (m_last_decode_arm.instruction->code == ARM_INST_CBNZ))
+ {
+ // Compare and branch on zero/non-zero (Thumb-16 only)
+ // Unusual condition check built into the instruction
+ registerValue = m_state.gpr.r[m_last_decode_arm.op[REG_RD].value];
+
+ if (m_last_decode_arm.instruction->code == ARM_INST_CBZ)
+ {
+ if (registerValue == 0)
+ *nextPC = targetPC;
+ }
+ else
+ {
+ if (registerValue != 0)
+ *nextPC = targetPC;
+ }
+ }
+ else if (m_last_decode_arm.conditional) // Is the change conditional on flag results?
+ {
+ if (ConditionPassed(m_last_decode_arm.condition, cpsr)) // conditions match
+ {
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: Condition matched!", __FUNCTION__);
+ *nextPC = targetPC;
+ }
+ else
+ {
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: Condition did not match!", __FUNCTION__);
+ }
+ }
+ else
+ {
+ *nextPC = targetPC;
+ }
+
+ // Refine nextPCIsThumb computation
+ if (m_last_decode_arm.doesSwitchMode)
+ {
+ *nextPCIsThumb = !currentPCIsThumb;
+ }
+ else if (m_last_decode_arm.canSwitchMode)
+ {
+ // Legal to switch ARM <--> Thumb mode with this branch
+ // dependent on bit[0] of targetPC
+ *nextPCIsThumb = (*nextPC & 1u) != 0;
+ }
+ else
+ {
+ *nextPCIsThumb = currentPCIsThumb;
+ }
+ }
+
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: calculated nextPC=0x%8.8x (%s)", __FUNCTION__, *nextPC, *nextPCIsThumb ? "Thumb" : "ARM");
+}
+
+
+arm_error_t
+MachThreadContext_arm::DecodeInstructionUsingDisassembler(lldb::addr_t curr_pc, uint32_t curr_cpsr, arm_decoded_instruction_t *decodedInstruction, thumb_static_data_t *thumbStaticData, lldb::addr_t *next_pc)
+{
+
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: pc=0x%8.8x, cpsr=0x%8.8x", __FUNCTION__, curr_pc, curr_cpsr);
+
+ const uint32_t isetstate_mask = MASK_CPSR_T | MASK_CPSR_J;
+ const uint32_t curr_isetstate = curr_cpsr & isetstate_mask;
+ uint32_t opcode32;
+ lldb::addr_t nextPC = curr_pc;
+ arm_error_t decodeReturnCode = ARM_SUCCESS;
+
+ m_last_decode_pc = curr_pc;
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: last_decode_pc=0x%8.8x", __FUNCTION__, m_last_decode_pc);
+
+ switch (curr_isetstate) {
+ case 0x0: // ARM Instruction
+ // Read the ARM opcode
+ if (m_thread.Process()->Task().ReadMemory(curr_pc, 4, &opcode32) != 4)
+ {
+ LogError("unable to read opcode bits 31:0 for an ARM opcode at 0x%8.8lx", curr_pc);
+ decodeReturnCode = ARM_ERROR;
+ }
+ else
+ {
+ nextPC += 4;
+ decodeReturnCode = ArmDisassembler((uint64_t)curr_pc, opcode32, false, decodedInstruction, NULL, 0, NULL, 0);
+
+ if (decodeReturnCode != ARM_SUCCESS)
+ LogError("Unable to decode ARM instruction 0x%8.8x at 0x%8.8lx", opcode32, curr_pc);
+ }
+ break;
+
+ case 0x20: // Thumb Instruction
+ uint16_t opcode16;
+ // Read the a 16 bit Thumb opcode
+ if (m_thread.Process()->Task().ReadMemory(curr_pc, 2, &opcode16) != 2)
+ {
+ LogError("unable to read opcode bits 15:0 for a thumb opcode at 0x%8.8lx", curr_pc);
+ decodeReturnCode = ARM_ERROR;
+ }
+ else
+ {
+ nextPC += 2;
+ opcode32 = opcode16;
+
+ decodeReturnCode = ThumbDisassembler((uint64_t)curr_pc, opcode16, false, false, thumbStaticData, decodedInstruction, NULL, 0, NULL, 0);
+
+ switch (decodeReturnCode) {
+ case ARM_SKIP:
+ // 32 bit thumb opcode
+ nextPC += 2;
+ if (m_thread.Process()->Task().ReadMemory(curr_pc+2, 2, &opcode16) != 2)
+ {
+ LogError("unable to read opcode bits 15:0 for a thumb opcode at 0x%8.8lx", curr_pc+2);
+ }
+ else
+ {
+ opcode32 = (opcode32 << 16) | opcode16;
+
+ decodeReturnCode = ThumbDisassembler((uint64_t)(curr_pc+2), opcode16, false, false, thumbStaticData, decodedInstruction, NULL, 0, NULL, 0);
+
+ if (decodeReturnCode != ARM_SUCCESS)
+ LogError("Unable to decode 2nd half of Thumb instruction 0x%8.4hx at 0x%8.8lx", opcode16, curr_pc+2);
+ break;
+ }
+ break;
+
+ case ARM_SUCCESS:
+ // 16 bit thumb opcode; at this point we are done decoding the opcode
+ break;
+
+ default:
+ LogError("Unable to decode Thumb instruction 0x%8.4hx at 0x%8.8lx", opcode16, curr_pc);
+ decodeReturnCode = ARM_ERROR;
+ break;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (next_pc)
+ *next_pc = nextPC;
+
+ return decodeReturnCode;
+}
+
+bool
+MachThreadContext_arm::BreakpointHit(lldb::pid_t pid, lldb::tid_t tid, lldb::user_id_t breakID, void *baton)
+{
+ lldb::addr_t bkpt_pc = (lldb::addr_t)baton;
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s(pid = %i, tid = %4.4x, breakID = %u, baton = %p): Setting PC to 0x%8.8x", __FUNCTION__, pid, tid, breakID, baton, bkpt_pc);
+ return PDThreadSetRegisterValueByID(pid, tid, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, bkpt_pc);
+}
+
+// Set the single step bit in the processor status register.
+kern_return_t
+MachThreadContext_arm::SetSingleStepSoftwareBreakpoints()
+{
+ Error err;
+ err = ReadGPRRegisters(false);
+
+ if (err.Fail())
+ {
+ err.Log("%s: failed to read the GPR registers", __FUNCTION__);
+ return err.GetError();
+ }
+
+ lldb::addr_t curr_pc = m_state.gpr.r[15];
+ uint32_t curr_cpsr = m_state.gpr.__cpsr;
+ lldb::addr_t next_pc = curr_pc;
+
+ bool curr_pc_is_thumb = (m_state.gpr.__cpsr & 0x20) != 0;
+ bool next_pc_is_thumb = curr_pc_is_thumb;
+
+ uint32_t curr_itstate = ((curr_cpsr & 0x6000000) >> 25) | ((curr_cpsr & 0xFC00) >> 8);
+ bool inITBlock = (curr_itstate & 0xF) ? 1 : 0;
+ bool lastInITBlock = ((curr_itstate & 0xF) == 0x8) ? 1 : 0;
+
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: curr_pc=0x%8.8x (%s), curr_itstate=0x%x, inITBlock=%d, lastInITBlock=%d", __FUNCTION__, curr_pc, curr_pc_is_thumb ? "Thumb" : "ARM", curr_itstate, inITBlock, lastInITBlock);
+
+ // If the instruction is not in the IT block, then decode using the Disassembler and compute next_pc
+ if (!inITBlock)
+ {
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: Decoding an instruction NOT in the IT block", __FUNCTION__);
+
+ arm_error_t decodeReturnCode = DecodeInstructionUsingDisassembler(curr_pc, curr_cpsr, &m_last_decode_arm, &m_last_decode_thumb, &next_pc);
+
+ if (decodeReturnCode != ARM_SUCCESS)
+ {
+ err = KERN_INVALID_ARGUMENT;
+ LogError("MachThreadContext_arm::SetSingleStepSoftwareBreakpoints: Unable to disassemble instruction at 0x%8.8lx", curr_pc);
+ }
+ }
+ else
+ {
+ next_pc = curr_pc + ((m_last_decode_arm.thumb16b) ? 2 : 4);
+ }
+
+ // Instruction is NOT in the IT block OR
+ if (!inITBlock)
+ {
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: normal instruction", __FUNCTION__);
+ EvaluateNextInstructionForSoftwareBreakpointSetup(curr_pc, m_state.gpr.__cpsr, curr_pc_is_thumb, &next_pc, &next_pc_is_thumb);
+ }
+ else if (inITBlock && !m_last_decode_arm.setsFlags)
+ {
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: IT instruction that doesn't set flags", __FUNCTION__);
+ EvaluateNextInstructionForSoftwareBreakpointSetup(curr_pc, m_state.gpr.__cpsr, curr_pc_is_thumb, &next_pc, &next_pc_is_thumb);
+ }
+ else if (lastInITBlock && m_last_decode_arm.branch)
+ {
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: IT instruction which last in the IT block and is a branch", __FUNCTION__);
+ EvaluateNextInstructionForSoftwareBreakpointSetup(curr_pc, m_state.gpr.__cpsr, curr_pc_is_thumb, &next_pc, &next_pc_is_thumb);
+ }
+ else
+ {
+ // Instruction is in IT block and can modify the CPSR flags
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: IT instruction that sets flags", __FUNCTION__);
+
+ // NOTE: When this point of code is reached, the instruction at curr_pc has already been decoded
+ // inside the function ShouldStop (). Therefore m_last_decode_arm, m_last_decode_thumb
+ // reflect the decoded instruction at curr_pc
+
+ // If we find an instruction inside the IT block which will set/modify the condition flags (NZCV bits in CPSR),
+ // we set breakpoints at all remaining instructions inside the IT block starting from the instruction immediately
+ // following this one AND a breakpoint at the instruction immediately following the IT block. We do this because
+ // we cannot determine the next_pc until the instruction at which we are currently stopped executes. Hence we
+ // insert (m_last_decode_thumb.itBlockRemaining+1) 16-bit Thumb breakpoints at consecutive memory locations
+ // starting at addrOfNextInstructionInITBlock. We record these breakpoints in class variable m_sw_single_step_itblock_break_id[],
+ // and also record the total number of IT breakpoints set in the variable 'm_sw_single_step_itblock_break_count'.
+
+ // The instructions inside the IT block, which are replaced by the 16-bit Thumb breakpoints (opcode=0xDEFE)
+ // instructions, can be either Thumb-16 or Thumb-32. When a Thumb-32 instruction (say, inst#1) is replaced Thumb
+ // by a 16-bit breakpoint (OS only supports 16-bit breakpoints in Thumb mode and 32-bit breakpoints in ARM mode), the
+ // breakpoint for the next instruction (say instr#2) is saved in the upper half of this Thumb-32 (instr#1)
+ // instruction. Hence if the execution stops at Breakpoint2 corresponding to instr#2, the PC is offset by 16-bits.
+ // We therefore have to keep track of PC of each instruction in the IT block that is being replaced with the 16-bit
+ // Thumb breakpoint, to ensure that when the breakpoint is hit, the PC is adjusted to the correct value. We save
+ // the actual PC corresponding to each instruction in the IT block by associating a call back with each breakpoint
+ // we set and passing it as a baton. When the breakpoint hits and the callback routine is called, the routine
+ // adjusts the PC based on the baton that is passed to it.
+
+ lldb::addr_t addrOfNextInstructionInITBlock, pcInITBlock, nextPCInITBlock, bpAddressInITBlock;
+ uint16_t opcode16;
+ uint32_t opcode32;
+
+ addrOfNextInstructionInITBlock = (m_last_decode_arm.thumb16b) ? curr_pc + 2 : curr_pc + 4;
+
+ pcInITBlock = addrOfNextInstructionInITBlock;
+
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: itBlockRemaining=%d", __FUNCTION__, m_last_decode_thumb.itBlockRemaining);
+
+ m_sw_single_step_itblock_break_count = 0;
+ for (int i = 0; i <= m_last_decode_thumb.itBlockRemaining; i++)
+ {
+ if (LLDB_BREAK_ID_IS_VALID(m_sw_single_step_itblock_break_id[i]))
+ {
+ LogError("FunctionProfiler::SetSingleStepSoftwareBreakpoints(): Array m_sw_single_step_itblock_break_id should not contain any valid breakpoint IDs at this point. But found a valid breakID=%d at index=%d", m_sw_single_step_itblock_break_id[i], i);
+ }
+ else
+ {
+ nextPCInITBlock = pcInITBlock;
+ // Compute nextPCInITBlock based on opcode present at pcInITBlock
+ if (m_thread.Process()->Task().ReadMemory(pcInITBlock, 2, &opcode16) == 2)
+ {
+ opcode32 = opcode16;
+ nextPCInITBlock += 2;
+
+ // Check for 32 bit thumb opcode and read the upper 16 bits if needed
+ if (((opcode32 & 0xE000) == 0xE000) && (opcode32 & 0x1800))
+ {
+ // Adjust 'next_pc_in_itblock' to point to the default next Thumb instruction for
+ // a 32 bit Thumb opcode
+ // Read bits 31:16 of a 32 bit Thumb opcode
+ if (m_thread.Process()->Task().ReadMemory(pcInITBlock+2, 2, &opcode16) == 2)
+ {
+ // 32 bit thumb opcode
+ opcode32 = (opcode32 << 16) | opcode16;
+ nextPCInITBlock += 2;
+ }
+ else
+ {
+ LogError("FunctionProfiler::SetSingleStepSoftwareBreakpoints(): Unable to read opcode bits 31:16 for a 32 bit thumb opcode at pc=0x%8.8lx", nextPCInITBlock);
+ }
+ }
+ }
+ else
+ {
+ LogError("FunctionProfiler::SetSingleStepSoftwareBreakpoints(): Error reading 16-bit Thumb instruction at pc=0x%8.8x", nextPCInITBlock);
+ }
+
+
+ // Set breakpoint and associate a callback function with it
+ bpAddressInITBlock = addrOfNextInstructionInITBlock + 2*i;
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: Setting IT breakpoint[%d] at address: 0x%8.8x", __FUNCTION__, i, bpAddressInITBlock);
+
+ m_sw_single_step_itblock_break_id[i] = m_thread.Process()->CreateBreakpoint(bpAddressInITBlock, 2, false, m_thread.GetID());
+ if (!LLDB_BREAK_ID_IS_VALID(m_sw_single_step_itblock_break_id[i]))
+ err = KERN_INVALID_ARGUMENT;
+ else
+ {
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: Set IT breakpoint[%i]=%d set at 0x%8.8x for instruction at 0x%8.8x", __FUNCTION__, i, m_sw_single_step_itblock_break_id[i], bpAddressInITBlock, pcInITBlock);
+
+ // Set the breakpoint callback for these special IT breakpoints
+ // so that if one of these breakpoints gets hit, it knows to
+ // update the PC to the original address of the conditional
+ // IT instruction.
+ PDBreakpointSetCallback(m_thread.ProcessID(), m_sw_single_step_itblock_break_id[i], MachThreadContext_arm::BreakpointHit, (void*)pcInITBlock);
+ m_sw_single_step_itblock_break_count++;
+ }
+ }
+
+ pcInITBlock = nextPCInITBlock;
+ }
+
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP | PD_LOG_VERBOSE, "%s: Set %u IT software single breakpoints.", __FUNCTION__, m_sw_single_step_itblock_break_count);
+
+ }
+
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: next_pc=0x%8.8x (%s)", __FUNCTION__, next_pc, next_pc_is_thumb ? "Thumb" : "ARM");
+
+ if (next_pc & 0x1)
+ {
+ assert(next_pc_is_thumb);
+ }
+
+ if (next_pc_is_thumb)
+ {
+ next_pc &= ~0x1;
+ }
+ else
+ {
+ assert((next_pc & 0x3) == 0);
+ }
+
+ if (!inITBlock || (inITBlock && !m_last_decode_arm.setsFlags) || (lastInITBlock && m_last_decode_arm.branch))
+ {
+ err = KERN_SUCCESS;
+
+#if defined DNB_ARCH_MACH_ARM_DEBUG_SW_STEP
+ m_sw_single_step_next_pc = next_pc;
+ if (next_pc_is_thumb)
+ m_sw_single_step_next_pc |= 1; // Set bit zero if the next PC is expected to be Thumb
+#else
+ const DNBBreakpoint *bp = m_thread.Process()->Breakpoints().FindByAddress(next_pc);
+
+ if (bp == NULL)
+ {
+ m_sw_single_step_break_id = m_thread.Process()->CreateBreakpoint(next_pc, next_pc_is_thumb ? 2 : 4, false, m_thread.GetID());
+ if (!LLDB_BREAK_ID_IS_VALID(m_sw_single_step_break_id))
+ err = KERN_INVALID_ARGUMENT;
+ ProcessMacOSXLog::LogIf(PD_LOG_STEP, "%s: software single step breakpoint with breakID=%d set at 0x%8.8x", __FUNCTION__, m_sw_single_step_break_id, next_pc);
+ }
+#endif
+ }
+
+ return err.GetError();
+}
+
+#endif
+
+MachThreadContext*
+MachThreadContext_arm::Create (const ArchSpec &arch_spec, ThreadMacOSX &thread)
+{
+ return new MachThreadContext_arm(thread);
+}
+
+void
+MachThreadContext_arm::Initialize()
+{
+ ArchSpec arch_spec(CPU_TYPE_ARM, CPU_TYPE_ANY);
+ ProcessMacOSX::AddArchCreateCallback(arch_spec, MachThreadContext_arm::Create);
+}
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.h
new file mode 100644
index 00000000000..4ec72dfd0d4
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_arm.h
@@ -0,0 +1,63 @@
+//===-- MachThreadContext_arm.h ---------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_MachThreadContext_arm_h_
+#define liblldb_MachThreadContext_arm_h_
+
+#include "MachThreadContext.h"
+#include "RegisterContextMach_arm.h"
+
+class ThreadMacOSX;
+
+class MachThreadContext_arm : public MachThreadContext
+{
+public:
+ enum { kMaxNumThumbITBreakpoints = 4 };
+
+ static MachThreadContext*
+ Create (const lldb_private::ArchSpec &arch_spec, ThreadMacOSX &thread);
+
+ static void
+ Initialize();
+
+ MachThreadContext_arm(ThreadMacOSX &thread);
+
+ virtual
+ ~MachThreadContext_arm();
+
+ virtual lldb_private::RegisterContext *
+ CreateRegisterContext (lldb_private::StackFrame *frame) const;
+
+ virtual void
+ InitializeInstance();
+
+ virtual void
+ ThreadWillResume ();
+
+ virtual bool
+ ShouldStop ();
+
+ virtual void
+ RefreshStateAfterStop ();
+
+ static uint32_t
+ GetCPUType ();
+
+protected:
+ kern_return_t
+ EnableHardwareSingleStep (bool enable);
+
+protected:
+ lldb::addr_t m_hw_single_chained_step_addr;
+ uint32_t m_bvr0_reg;
+ uint32_t m_bcr0_reg;
+ uint32_t m_bvr0_save;
+ uint32_t m_bcr0_save;
+};
+#endif // #ifndef liblldb_MachThreadContext_arm_h_
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.cpp
new file mode 100644
index 00000000000..ea148a69f41
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.cpp
@@ -0,0 +1,245 @@
+//===-- MachThreadContext_i386.cpp ------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined (__i386__) || defined (__x86_64__)
+
+
+#include "MachThreadContext_i386.h"
+
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+
+#include "ProcessMacOSX.h"
+#include "ThreadMacOSX.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+MachThreadContext_i386::MachThreadContext_i386 (ThreadMacOSX &thread) :
+ MachThreadContext (thread),
+ m_flags_reg(LLDB_INVALID_REGNUM)
+{
+}
+
+MachThreadContext_i386::~MachThreadContext_i386()
+{
+}
+
+
+MachThreadContext*
+MachThreadContext_i386::Create (const ArchSpec &arch_spec, ThreadMacOSX &thread)
+{
+ return new MachThreadContext_i386(thread);
+}
+
+// Class init function
+void
+MachThreadContext_i386::Initialize()
+{
+ ArchSpec arch_spec("i386");
+ ProcessMacOSX::AddArchCreateCallback(arch_spec, MachThreadContext_i386::Create);
+}
+
+// Instance init function
+void
+MachThreadContext_i386::InitializeInstance()
+{
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext();
+ assert (reg_ctx != NULL);
+ m_flags_reg = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
+}
+
+
+uint32_t
+MachThreadContext_i386::GetCPUType()
+{
+ return CPU_TYPE_I386;
+}
+
+void
+MachThreadContext_i386::ThreadWillResume()
+{
+ m_thread.GetRegisterContext()->HardwareSingleStep (m_thread.GetState() == eStateStepping);
+}
+
+bool
+MachThreadContext_i386::ShouldStop()
+{
+ return true;
+}
+
+void
+MachThreadContext_i386::RefreshStateAfterStop()
+{
+ m_thread.GetRegisterContext()->HardwareSingleStep (false);
+}
+
+bool
+MachThreadContext_i386::NotifyException (MachException::Data& exc)
+{
+ switch (exc.exc_type)
+ {
+ case EXC_BAD_ACCESS:
+ break;
+ case EXC_BAD_INSTRUCTION:
+ break;
+ case EXC_ARITHMETIC:
+ break;
+ case EXC_EMULATION:
+ break;
+ case EXC_SOFTWARE:
+ break;
+ case EXC_BREAKPOINT:
+ if (exc.exc_data.size() >= 2 && exc.exc_data[0] == 2)
+ {
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext();
+ assert (reg_ctx);
+ lldb::addr_t pc = reg_ctx->GetPC(LLDB_INVALID_ADDRESS);
+ if (pc != LLDB_INVALID_ADDRESS && pc > 0)
+ {
+ pc -= 1;
+ reg_ctx->SetPC(pc);
+ }
+ return true;
+ }
+ break;
+ case EXC_SYSCALL:
+ break;
+ case EXC_MACH_SYSCALL:
+ break;
+ case EXC_RPC_ALERT:
+ break;
+ }
+ return false;
+}
+
+
+// Set the single step bit in the processor status register.
+//kern_return_t
+//MachThreadContext_i386::EnableHardwareSingleStep (bool enable)
+//{
+// RegisterContext *reg_ctx = m_thread.GetRegisterContext();
+// assert (reg_ctx);
+// Scalar rflags_scalar;
+//
+// if (reg_ctx->ReadRegisterValue (m_flags_reg, rflags_scalar))
+// {
+// Flags rflags(rflags_scalar.UInt());
+// const uint32_t trace_bit = 0x100u;
+// if (enable)
+// {
+// // If the trace bit is already cleared, there is nothing to do
+// if (rflags.IsSet (trace_bit))
+// return KERN_SUCCESS;
+// else
+// rflags.Set (trace_bit);
+// }
+// else
+// {
+// // If the trace bit is already cleared, there is nothing to do
+// if (rflags.IsClear (trace_bit))
+// return KERN_SUCCESS;
+// else
+// rflags.Clear(trace_bit);
+// }
+//
+// rflags_scalar = rflags.GetAllFlagBits();
+// // If the code makes it here we have changes to the GPRs which
+// // we need to write back out, so lets do that.
+// if (reg_ctx->WriteRegisterValue(m_flags_reg, rflags_scalar))
+// return KERN_SUCCESS;
+// }
+// // Return the error code for reading the GPR registers back
+// return KERN_INVALID_ARGUMENT;
+//}
+
+RegisterContext *
+MachThreadContext_i386::CreateRegisterContext (StackFrame *frame) const
+{
+ return new RegisterContextMach_i386(m_thread, frame);
+}
+
+
+size_t
+MachThreadContext_i386::GetStackFrameData(StackFrame *first_frame, std::vector<std::pair<lldb::addr_t, lldb::addr_t> >& fp_pc_pairs)
+{
+ fp_pc_pairs.clear();
+
+ std::pair<lldb::addr_t, lldb::addr_t> fp_pc_pair;
+
+ typedef struct Frame_i386
+ {
+ uint32_t fp;
+ uint32_t pc;
+ };
+
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext();
+ assert (reg_ctx);
+
+ Frame_i386 frame = { reg_ctx->GetFP(0), reg_ctx->GetPC(LLDB_INVALID_ADDRESS) };
+
+ fp_pc_pairs.push_back(std::make_pair(frame.fp, frame.pc));
+
+ const size_t k_frame_size = sizeof(frame);
+ Error error;
+
+ while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0))
+ {
+ // Read both the FP and PC (8 bytes)
+ if (m_thread.GetProcess().ReadMemory (frame.fp, &frame.fp, k_frame_size, error) != k_frame_size)
+ break;
+
+ if (frame.pc != 0)
+ fp_pc_pairs.push_back(std::make_pair(frame.fp, frame.pc));
+ }
+ if (!fp_pc_pairs.empty())
+ {
+ lldb::addr_t first_frame_pc = fp_pc_pairs.front().second;
+ if (first_frame_pc != LLDB_INVALID_ADDRESS)
+ {
+ const uint32_t resolve_scope = eSymbolContextModule |
+ eSymbolContextCompUnit |
+ eSymbolContextFunction |
+ eSymbolContextSymbol;
+
+ SymbolContext first_frame_sc(first_frame->GetSymbolContext(resolve_scope));
+ const AddressRange *addr_range_ptr = NULL;
+ if (first_frame_sc.function)
+ addr_range_ptr = &first_frame_sc.function->GetAddressRange();
+ else if (first_frame_sc.symbol)
+ addr_range_ptr = first_frame_sc.symbol->GetAddressRangePtr();
+
+ if (addr_range_ptr)
+ {
+ if (first_frame->GetPC() == addr_range_ptr->GetBaseAddress())
+ {
+ // We are at the first instruction, so we can recover the
+ // previous PC by dereferencing the SP
+ lldb::addr_t first_frame_sp = reg_ctx->GetSP(0);
+ // Read the real second frame return address into frame.pc
+ if (m_thread.GetProcess().ReadMemory (first_frame_sp, &frame.pc, sizeof(frame.pc), error) == sizeof(frame.pc))
+ {
+ // Construct a correct second frame (we already read the pc for it above
+ frame.fp = fp_pc_pairs.front().first;
+
+ // Insert the frame
+ fp_pc_pairs.insert(fp_pc_pairs.begin()+1, std::make_pair(frame.fp, frame.pc));
+
+ // Correct the fp in the first frame to use the SP
+ fp_pc_pairs.front().first = first_frame_sp;
+ }
+ }
+ }
+ }
+ }
+ return fp_pc_pairs.size();
+}
+
+
+#endif // #if defined (__i386__)
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.h
new file mode 100644
index 00000000000..4bee2e7be8f
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_i386.h
@@ -0,0 +1,57 @@
+//===-- MachThreadContext_i386.h --------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_MachThreadContext_i386_h_
+#define liblldb_MachThreadContext_i386_h_
+
+#if defined (__i386__) || defined (__x86_64__)
+
+#include "MachThreadContext.h"
+#include "RegisterContextMach_i386.h"
+
+class ThreadMacOSX;
+
+class MachThreadContext_i386 : public MachThreadContext
+{
+public:
+ static MachThreadContext* Create(const lldb_private::ArchSpec &arch_spec, ThreadMacOSX &thread);
+
+ // Class init function
+ static void Initialize();
+
+ MachThreadContext_i386(ThreadMacOSX &thread);
+
+ virtual
+ ~MachThreadContext_i386();
+
+ virtual lldb_private::RegisterContext *
+ CreateRegisterContext (lldb_private::StackFrame *frame) const;
+
+ virtual void InitializeInstance();
+ virtual void ThreadWillResume();
+ virtual bool ShouldStop ();
+ virtual void RefreshStateAfterStop();
+
+ virtual bool NotifyException(MachException::Data& exc);
+ virtual size_t GetStackFrameData(lldb_private::StackFrame *first_frame, std::vector<std::pair<lldb::addr_t, lldb::addr_t> >& fp_pc_pairs);
+ static uint32_t GetCPUType();
+
+protected:
+// kern_return_t EnableHardwareSingleStep (bool enable);
+ uint32_t m_flags_reg;
+private:
+ DISALLOW_COPY_AND_ASSIGN (MachThreadContext_i386);
+};
+
+//#if defined (__i386__)
+//typedef MachThreadContext_i386 DNBArch;
+//#endif
+
+#endif // defined (__i386__) || defined (__x86_64__)
+#endif // #ifndef liblldb_MachThreadContext_i386_h_
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.cpp
new file mode 100644
index 00000000000..3ee9eb419de
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.cpp
@@ -0,0 +1,255 @@
+//===-- MachThreadContext_x86_64.cpp ----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined (__i386__) || defined (__x86_64__)
+
+#include <sys/cdefs.h>
+
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+
+#include "MachThreadContext_x86_64.h"
+#include "ProcessMacOSX.h"
+#include "ThreadMacOSX.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+MachThreadContext_x86_64::MachThreadContext_x86_64(ThreadMacOSX &thread) :
+ MachThreadContext (thread),
+ m_flags_reg(LLDB_INVALID_REGNUM)
+{
+}
+
+MachThreadContext_x86_64::~MachThreadContext_x86_64()
+{
+}
+
+MachThreadContext*
+MachThreadContext_x86_64::Create(const ArchSpec &arch_spec, ThreadMacOSX &thread)
+{
+ return new MachThreadContext_x86_64(thread);
+}
+
+// Class init function
+void
+MachThreadContext_x86_64::Initialize()
+{
+ ArchSpec arch_spec("x86_64");
+ ProcessMacOSX::AddArchCreateCallback(arch_spec, MachThreadContext_x86_64::Create);
+}
+
+// Instance init function
+void
+MachThreadContext_x86_64::InitializeInstance()
+{
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext();
+ assert (reg_ctx != NULL);
+ m_flags_reg = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
+}
+
+uint32_t
+MachThreadContext_x86_64::GetCPUType()
+{
+ return CPU_TYPE_X86_64;
+}
+
+void
+MachThreadContext_x86_64::ThreadWillResume()
+{
+ m_thread.GetRegisterContext()->HardwareSingleStep (m_thread.GetState() == eStateStepping);
+}
+
+bool
+MachThreadContext_x86_64::ShouldStop()
+{
+ return true;
+}
+
+void
+MachThreadContext_x86_64::RefreshStateAfterStop()
+{
+ m_thread.GetRegisterContext()->HardwareSingleStep (false);
+}
+
+bool
+MachThreadContext_x86_64::NotifyException(MachException::Data& exc)
+{
+ switch (exc.exc_type)
+ {
+ case EXC_BAD_ACCESS:
+ break;
+ case EXC_BAD_INSTRUCTION:
+ break;
+ case EXC_ARITHMETIC:
+ break;
+ case EXC_EMULATION:
+ break;
+ case EXC_SOFTWARE:
+ break;
+ case EXC_BREAKPOINT:
+ if (exc.exc_data.size() >= 2 && exc.exc_data[0] == 2)
+ {
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext();
+ assert (reg_ctx);
+ lldb::addr_t pc = reg_ctx->GetPC(LLDB_INVALID_ADDRESS);
+ if (pc != LLDB_INVALID_ADDRESS && pc > 0)
+ {
+ pc -= 1;
+ reg_ctx->SetPC(pc);
+ }
+ return true;
+ }
+ break;
+ case EXC_SYSCALL:
+ break;
+ case EXC_MACH_SYSCALL:
+ break;
+ case EXC_RPC_ALERT:
+ break;
+ }
+ return false;
+}
+
+
+// Set the single step bit in the processor status register.
+//kern_return_t
+//MachThreadContext_x86_64::EnableHardwareSingleStep (bool enable)
+//{
+// RegisterContext *reg_ctx = m_thread.GetRegisterContext();
+// assert (reg_ctx);
+// Scalar rflags_scalar;
+//
+// if (reg_ctx->ReadRegisterValue (m_flags_reg, rflags_scalar))
+// {
+// Flags rflags(rflags_scalar.UInt());
+// const uint32_t trace_bit = 0x100u;
+// if (enable)
+// {
+// // If the trace bit is already set, there is nothing to do
+// if (rflags.IsSet (trace_bit))
+// return KERN_SUCCESS;
+// else
+// rflags.Set (trace_bit);
+// }
+// else
+// {
+// // If the trace bit is already cleared, there is nothing to do
+// if (rflags.IsClear (trace_bit))
+// return KERN_SUCCESS;
+// else
+// rflags.Clear(trace_bit);
+// }
+//
+// rflags_scalar = rflags.GetAllFlagBits();
+// // If the code makes it here we have changes to the GPRs which
+// // we need to write back out, so lets do that.
+// if (reg_ctx->WriteRegisterValue(m_flags_reg, rflags_scalar))
+// return KERN_SUCCESS;
+// }
+// // Return the error code for reading the GPR registers back
+// return KERN_INVALID_ARGUMENT;
+//}
+//
+
+//----------------------------------------------------------------------
+// Register information defintions for 32 bit PowerPC.
+//----------------------------------------------------------------------
+
+
+
+RegisterContext *
+MachThreadContext_x86_64::CreateRegisterContext (StackFrame *frame) const
+{
+ return new RegisterContextMach_x86_64(m_thread, frame);
+}
+
+
+//bool
+//MachThreadContext_x86_64::RegisterSetStateIsValid (uint32_t set) const
+//{
+// return m_state.RegisterSetIsCached(set);
+//}
+
+
+size_t
+MachThreadContext_x86_64::GetStackFrameData(StackFrame *first_frame, std::vector<std::pair<lldb::addr_t, lldb::addr_t> >& fp_pc_pairs)
+{
+ fp_pc_pairs.clear();
+
+ std::pair<lldb::addr_t, lldb::addr_t> fp_pc_pair;
+
+ typedef struct Frame_x86_64
+ {
+ uint64_t fp;
+ uint64_t pc;
+ };
+
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext();
+ assert (reg_ctx);
+
+ Frame_x86_64 frame = { reg_ctx->GetFP(0), reg_ctx->GetPC(LLDB_INVALID_ADDRESS) };
+
+ fp_pc_pairs.push_back(std::make_pair(frame.fp, frame.pc));
+ Error error;
+ const size_t k_frame_size = sizeof(frame);
+ while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0))
+ {
+ // Read both the FP and PC (16 bytes)
+ if (m_thread.GetProcess().ReadMemory (frame.fp, &frame.fp, k_frame_size, error) != k_frame_size)
+ break;
+
+ if (frame.pc >= 0x1000)
+ fp_pc_pairs.push_back(std::make_pair(frame.fp, frame.pc));
+ }
+ if (!fp_pc_pairs.empty())
+ {
+ lldb::addr_t first_frame_pc = fp_pc_pairs.front().second;
+ if (first_frame_pc != LLDB_INVALID_ADDRESS)
+ {
+ const uint32_t resolve_scope = eSymbolContextModule |
+ eSymbolContextCompUnit |
+ eSymbolContextFunction |
+ eSymbolContextSymbol;
+
+ SymbolContext first_frame_sc(first_frame->GetSymbolContext(resolve_scope));
+ const AddressRange *addr_range_ptr = NULL;
+ if (first_frame_sc.function)
+ addr_range_ptr = &first_frame_sc.function->GetAddressRange();
+ else if (first_frame_sc.symbol)
+ addr_range_ptr = first_frame_sc.symbol->GetAddressRangePtr();
+
+ if (addr_range_ptr)
+ {
+ if (first_frame->GetPC() == addr_range_ptr->GetBaseAddress())
+ {
+ // We are at the first instruction, so we can recover the
+ // previous PC by dereferencing the SP
+ lldb::addr_t first_frame_sp = reg_ctx->GetSP(0);
+ // Read the real second frame return address into frame.pc
+ if (m_thread.GetProcess().ReadMemory (first_frame_sp, &frame.pc, sizeof(frame.pc), error) == sizeof(frame.pc))
+ {
+ // Construct a correct second frame (we already read the pc for it above
+ frame.fp = fp_pc_pairs.front().first;
+
+ // Insert the frame
+ fp_pc_pairs.insert(fp_pc_pairs.begin()+1, std::make_pair(frame.fp, frame.pc));
+
+ // Correct the fp in the first frame to use the SP
+ fp_pc_pairs.front().first = first_frame_sp;
+ }
+ }
+ }
+ }
+ }
+ return fp_pc_pairs.size();
+}
+
+
+#endif // #if defined (__i386__) || defined (__x86_64__)
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.h
new file mode 100644
index 00000000000..45d5a375052
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachThreadContext_x86_64.h
@@ -0,0 +1,72 @@
+//===-- MachThreadContext_x86_64.h ------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_MachThreadContext_x86_64_h_
+#define liblldb_MachThreadContext_x86_64_h_
+
+#if defined (__i386__) || defined (__x86_64__)
+
+#include "MachThreadContext.h"
+#include "RegisterContextMach_x86_64.h"
+
+class ThreadMacOSX;
+
+class MachThreadContext_x86_64 : public MachThreadContext
+{
+public:
+ static MachThreadContext*
+ Create(const lldb_private::ArchSpec &arch_spec, ThreadMacOSX &thread);
+
+ // Class init function
+ static void
+ Initialize();
+
+ // Instance init function
+ void
+ InitializeInstance();
+
+ MachThreadContext_x86_64 (ThreadMacOSX &thread);
+
+ virtual
+ ~MachThreadContext_x86_64();
+
+ virtual lldb_private::RegisterContext *
+ CreateRegisterContext (lldb_private::StackFrame *frame) const;
+
+ virtual void
+ ThreadWillResume ();
+
+ virtual bool
+ ShouldStop ();
+
+ virtual void
+ RefreshStateAfterStop ();
+
+ virtual bool
+ NotifyException (MachException::Data& exc);
+
+ virtual size_t
+ GetStackFrameData (lldb_private::StackFrame *first_frame, std::vector<std::pair<lldb::addr_t, lldb::addr_t> >& fp_pc_pairs);
+
+ static uint32_t
+ GetCPUType();
+
+protected:
+// kern_return_t EnableHardwareSingleStep (bool enable);
+ uint32_t m_flags_reg;
+private:
+ DISALLOW_COPY_AND_ASSIGN (MachThreadContext_x86_64);
+};
+
+//#if defined (__x86_64__)
+//typedef MachThreadContext_x86_64 DNBArch;
+//#endif
+
+#endif // defined (__i386__) || defined (__x86_64__)
+#endif // #ifndef liblldb_MachThreadContext_x86_64_h_
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.cpp
new file mode 100644
index 00000000000..c91af3c3596
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.cpp
@@ -0,0 +1,195 @@
+//===-- MachVMMemory.cpp ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MachVMMemory.h"
+
+#include <mach/mach_vm.h>
+
+#include "MachVMRegion.h"
+#include "ProcessMacOSXLog.h"
+
+using namespace lldb_private;
+
+MachVMMemory::MachVMMemory() :
+ m_page_size (kInvalidPageSize)
+{
+}
+
+MachVMMemory::~MachVMMemory()
+{
+}
+
+size_t
+MachVMMemory::PageSize(lldb_private::Error &error)
+{
+ if (m_page_size == kInvalidPageSize)
+ {
+ error = ::host_page_size( ::mach_host_self(), &m_page_size);
+ if (error.Fail())
+ m_page_size = 0;
+ }
+
+ if (m_page_size != 0 && m_page_size != kInvalidPageSize)
+ {
+ if (error.Success())
+ error.SetErrorString ("unable to determine page size");
+ }
+ return m_page_size;
+}
+
+size_t
+MachVMMemory::MaxBytesLeftInPage (lldb::addr_t addr, size_t count)
+{
+ Error error;
+ const size_t page_size = PageSize(error);
+ if (page_size > 0)
+ {
+ size_t page_offset = (addr % page_size);
+ size_t bytes_left_in_page = page_size - page_offset;
+ if (count > bytes_left_in_page)
+ count = bytes_left_in_page;
+ }
+ return count;
+}
+
+size_t
+MachVMMemory::Read(task_t task, lldb::addr_t address, void *data, size_t data_count, Error &error)
+{
+ if (data == NULL || data_count == 0)
+ return 0;
+
+ size_t total_bytes_read = 0;
+ lldb::addr_t curr_addr = address;
+ uint8_t *curr_data = (uint8_t*)data;
+ while (total_bytes_read < data_count)
+ {
+ mach_vm_size_t curr_size = MaxBytesLeftInPage(curr_addr, data_count - total_bytes_read);
+ mach_msg_type_number_t curr_bytes_read = 0;
+ vm_offset_t vm_memory = NULL;
+ error = ::mach_vm_read (task, curr_addr, curr_size, &vm_memory, &curr_bytes_read);
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY|PD_LOG_VERBOSE);
+
+ if (log || error.Fail())
+ error.PutToLog (log, "::mach_vm_read (task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, data => %8.8p, dataCnt => %i)", task, (uint64_t)curr_addr, (uint64_t)curr_size, vm_memory, curr_bytes_read);
+
+ if (error.Success())
+ {
+ if (curr_bytes_read != curr_size)
+ {
+ if (log)
+ error.PutToLog (log, "::mach_vm_read (task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, data => %8.8p, dataCnt=>%i) only read %u of %llu bytes", task, (uint64_t)curr_addr, (uint64_t)curr_size, vm_memory, curr_bytes_read, curr_bytes_read, (uint64_t)curr_size);
+ }
+ ::memcpy (curr_data, (void *)vm_memory, curr_bytes_read);
+ ::vm_deallocate (mach_task_self (), vm_memory, curr_bytes_read);
+ total_bytes_read += curr_bytes_read;
+ curr_addr += curr_bytes_read;
+ curr_data += curr_bytes_read;
+ }
+ else
+ {
+ break;
+ }
+ }
+ return total_bytes_read;
+}
+
+
+size_t
+MachVMMemory::Write(task_t task, lldb::addr_t address, const void *data, size_t data_count, Error &error)
+{
+ MachVMRegion vmRegion(task);
+
+ size_t total_bytes_written = 0;
+ lldb::addr_t curr_addr = address;
+ const uint8_t *curr_data = (const uint8_t*)data;
+
+
+ while (total_bytes_written < data_count)
+ {
+ if (vmRegion.GetRegionForAddress(curr_addr))
+ {
+ mach_vm_size_t curr_data_count = data_count - total_bytes_written;
+ mach_vm_size_t region_bytes_left = vmRegion.BytesRemaining(curr_addr);
+ if (region_bytes_left == 0)
+ {
+ break;
+ }
+ if (curr_data_count > region_bytes_left)
+ curr_data_count = region_bytes_left;
+
+ if (vmRegion.SetProtections(curr_addr, curr_data_count, VM_PROT_READ | VM_PROT_WRITE))
+ {
+ size_t bytes_written = WriteRegion(task, curr_addr, curr_data, curr_data_count, error);
+ if (bytes_written <= 0)
+ {
+ // Error should have already be posted by WriteRegion...
+ break;
+ }
+ else
+ {
+ total_bytes_written += bytes_written;
+ curr_addr += bytes_written;
+ curr_data += bytes_written;
+ }
+ }
+ else
+ {
+ ProcessMacOSXLog::LogIf (PD_LOG_MEMORY_PROTECTIONS, "Failed to set read/write protections on region for address: [0x%8.8llx-0x%8.8llx)", (uint64_t)curr_addr, (uint64_t)(curr_addr + curr_data_count));
+ break;
+ }
+ }
+ else
+ {
+ ProcessMacOSXLog::LogIf (PD_LOG_MEMORY_PROTECTIONS, "Failed to get region for address: 0x%8.8llx", (uint64_t)address);
+ break;
+ }
+ }
+
+ return total_bytes_written;
+}
+
+
+size_t
+MachVMMemory::WriteRegion(task_t task, const lldb::addr_t address, const void *data, const size_t data_count, Error &error)
+{
+ if (data == NULL || data_count == 0)
+ return 0;
+
+ size_t total_bytes_written = 0;
+ lldb::addr_t curr_addr = address;
+ const uint8_t *curr_data = (const uint8_t*)data;
+ while (total_bytes_written < data_count)
+ {
+ mach_msg_type_number_t curr_data_count = MaxBytesLeftInPage(curr_addr, data_count - total_bytes_written);
+ error = ::mach_vm_write (task, curr_addr, (pointer_t) curr_data, curr_data_count);
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY);
+ if (log || error.Fail())
+ error.PutToLog (log, "::mach_vm_write ( task = 0x%4.4x, addr = 0x%8.8llx, data = %8.8p, dataCnt = %u )", task, (uint64_t)curr_addr, curr_data, curr_data_count);
+
+#if defined (__powerpc__) || defined (__ppc__)
+ vm_machine_attribute_val_t mattr_value = MATTR_VAL_CACHE_FLUSH;
+
+ error = ::vm_machine_attribute (task, curr_addr, curr_data_count, MATTR_CACHE, &mattr_value);
+ if (log || error.Fail())
+ error.Log(log, "::vm_machine_attribute ( task = 0x%4.4x, addr = 0x%8.8llx, size = %u, attr = MATTR_CACHE, mattr_value => MATTR_VAL_CACHE_FLUSH )", task, (uint64_t)curr_addr, curr_data_count);
+#endif
+
+ if (error.Success())
+ {
+ total_bytes_written += curr_data_count;
+ curr_addr += curr_data_count;
+ curr_data += curr_data_count;
+ }
+ else
+ {
+ break;
+ }
+ }
+ return total_bytes_written;
+}
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.h
new file mode 100644
index 00000000000..866fa369706
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMMemory.h
@@ -0,0 +1,36 @@
+//===-- MachVMMemory.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_MachVMMemory_h_
+#define liblldb_MachVMMemory_h_
+
+#include <mach/mach.h>
+
+#include "lldb/lldb-private.h"
+#include "lldb/Core/Error.h"
+
+class MachVMMemory
+{
+public:
+ enum { kInvalidPageSize = ~0 };
+ MachVMMemory();
+ ~MachVMMemory();
+ size_t Read(task_t task, lldb::addr_t address, void *data, size_t data_count, lldb_private::Error &error);
+ size_t Write(task_t task, lldb::addr_t address, const void *data, size_t data_count, lldb_private::Error &error);
+ size_t PageSize(lldb_private::Error &error);
+
+protected:
+ size_t MaxBytesLeftInPage(lldb::addr_t addr, size_t count);
+
+ size_t WriteRegion(task_t task, const lldb::addr_t address, const void *data, const size_t data_count, lldb_private::Error &error);
+ vm_size_t m_page_size;
+};
+
+
+#endif // #ifndef liblldb_MachVMMemory_h_
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.cpp
new file mode 100644
index 00000000000..87044fb31b2
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.cpp
@@ -0,0 +1,183 @@
+//===-- MachVMRegion.cpp ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <assert.h>
+#include <mach/mach_vm.h>
+#include "MachVMRegion.h"
+#include "ProcessMacOSXLog.h"
+
+using namespace lldb_private;
+
+MachVMRegion::MachVMRegion(task_t task) :
+ m_task(task),
+ m_addr(LLDB_INVALID_ADDRESS),
+ m_err(),
+ m_start(LLDB_INVALID_ADDRESS),
+ m_size(0),
+ m_depth(-1),
+ m_data(),
+ m_curr_protection(0),
+ m_protection_addr(LLDB_INVALID_ADDRESS),
+ m_protection_size(0)
+{
+ memset(&m_data, 0, sizeof(m_data));
+}
+
+MachVMRegion::~MachVMRegion()
+{
+ // Restore any original protections and clear our vars
+ Clear();
+}
+
+void
+MachVMRegion::Clear()
+{
+ RestoreProtections();
+ m_addr = LLDB_INVALID_ADDRESS;
+ m_err.Clear();
+ m_start = LLDB_INVALID_ADDRESS;
+ m_size = 0;
+ m_depth = -1;
+ memset(&m_data, 0, sizeof(m_data));
+ m_curr_protection = 0;
+ m_protection_addr = LLDB_INVALID_ADDRESS;
+ m_protection_size = 0;
+}
+
+bool
+MachVMRegion::SetProtections(mach_vm_address_t addr, mach_vm_size_t size, vm_prot_t prot)
+{
+ if (ContainsAddress(addr))
+ {
+ mach_vm_size_t prot_size = size;
+ mach_vm_address_t end_addr = EndAddress();
+ if (prot_size > (end_addr - addr))
+ prot_size = end_addr - addr;
+
+
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY_PROTECTIONS);
+ if (prot_size > 0)
+ {
+ if (prot == (m_curr_protection & VM_PROT_ALL))
+ {
+ if (log)
+ log->Printf ("MachVMRegion::%s: protections (%u) already sufficient for task 0x%4.4x at address 0x%8.8llx) ", __FUNCTION__, prot, m_task, (uint64_t)addr);
+ // Protections are already set as requested...
+ return true;
+ }
+ else
+ {
+ m_err = ::mach_vm_protect (m_task, addr, prot_size, 0, prot);
+ if (log || m_err.Fail())
+ m_err.PutToLog(log, "::mach_vm_protect ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, set_max = %i, prot = %u )", m_task, (uint64_t)addr, (uint64_t)prot_size, 0, prot);
+ if (m_err.Fail())
+ {
+ // Try again with the ability to create a copy on write region
+ m_err = ::mach_vm_protect (m_task, addr, prot_size, 0, prot | VM_PROT_COPY);
+ if (log || m_err.Fail())
+ m_err.PutToLog(log, "::mach_vm_protect ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, set_max = %i, prot = %u )", m_task, (uint64_t)addr, (uint64_t)prot_size, 0, prot | VM_PROT_COPY);
+ }
+ if (m_err.Success())
+ {
+ m_curr_protection = prot;
+ m_protection_addr = addr;
+ m_protection_size = prot_size;
+ return true;
+ }
+ }
+ }
+ else
+ {
+ log->Printf("MachVMRegion::%s: Zero size for task 0x%4.4x at address 0x%8.8llx) ", __FUNCTION__, m_task, (uint64_t)addr);
+ }
+ }
+ return false;
+}
+
+bool
+MachVMRegion::RestoreProtections()
+{
+ if (m_curr_protection != m_data.protection && m_protection_size > 0)
+ {
+ m_err = ::mach_vm_protect (m_task, m_protection_addr, m_protection_size, 0, m_data.protection);
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY_PROTECTIONS);
+ if (log || m_err.Fail())
+ m_err.PutToLog(log, "::mach_vm_protect ( task = 0x%4.4x, addr = 0x%8.8llx, size = %llu, set_max = %i, prot = %u )", m_task, (uint64_t)m_protection_addr, (uint64_t)m_protection_size, 0, m_data.protection);
+ if (m_err.Success())
+ {
+ m_protection_size = 0;
+ m_protection_addr = LLDB_INVALID_ADDRESS;
+ m_curr_protection = m_data.protection;
+ return true;
+ }
+ }
+ else
+ {
+ m_err.Clear();
+ return true;
+ }
+
+ return false;
+}
+
+bool
+MachVMRegion::GetRegionForAddress(lldb::addr_t addr)
+{
+ // Restore any original protections and clear our vars
+ Clear();
+ m_addr = addr;
+ m_start = addr;
+ m_depth = 1024;
+ mach_msg_type_number_t info_size = kRegionInfoSize;
+ assert(sizeof(info_size) == 4);
+ m_err = ::mach_vm_region_recurse (m_task, &m_start, &m_size, &m_depth, (vm_region_recurse_info_t)&m_data, &info_size);
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_MEMORY_PROTECTIONS);
+ if (log || m_err.Fail())
+ m_err.PutToLog(log, "::mach_vm_region_recurse ( task = 0x%4.4x, address => 0x%8.8llx, size => %llu, nesting_depth => %d, info => %p, infoCnt => %d) addr = 0x%8.8llx ", m_task, (uint64_t)m_start, (uint64_t)m_size, m_depth, &m_data, info_size, (uint64_t)addr);
+ if (m_err.Fail())
+ {
+ return false;
+ }
+ else
+ {
+ if (log && log->GetMask().IsSet(PD_LOG_VERBOSE))
+ {
+ log->Printf("info = { prot = %u, "
+ "max_prot = %u, "
+ "inheritance = 0x%8.8x, "
+ "offset = 0x%8.8llx, "
+ "user_tag = 0x%8.8x, "
+ "ref_count = %u, "
+ "shadow_depth = %u, "
+ "ext_pager = %u, "
+ "share_mode = %u, "
+ "is_submap = %d, "
+ "behavior = %d, "
+ "object_id = 0x%8.8x, "
+ "user_wired_count = 0x%4.4x }",
+ m_data.protection,
+ m_data.max_protection,
+ m_data.inheritance,
+ (uint64_t)m_data.offset,
+ m_data.user_tag,
+ m_data.ref_count,
+ m_data.shadow_depth,
+ m_data.external_pager,
+ m_data.share_mode,
+ m_data.is_submap,
+ m_data.behavior,
+ m_data.object_id,
+ m_data.user_wired_count);
+ }
+ }
+
+ m_curr_protection = m_data.protection;
+
+ return true;
+}
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.h b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.h
new file mode 100644
index 00000000000..b12353df823
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/MachVMRegion.h
@@ -0,0 +1,63 @@
+//===-- MachVMRegion.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_MachVMRegion_h_
+#define liblldb_MachVMRegion_h_
+
+#include <mach/mach.h>
+#include "lldb/lldb-private.h"
+#include "lldb/Core/Error.h"
+
+class MachVMRegion
+{
+public:
+ MachVMRegion(task_t task);
+ ~MachVMRegion();
+
+ void Clear();
+ mach_vm_address_t StartAddress() const { return m_start; }
+ mach_vm_address_t EndAddress() const { return m_start + m_size; }
+ mach_vm_address_t BytesRemaining(mach_vm_address_t addr) const
+ {
+ if (ContainsAddress(addr))
+ return m_size - (addr - m_start);
+ else
+ return 0;
+ }
+ bool ContainsAddress(mach_vm_address_t addr) const
+ {
+ return addr >= StartAddress() && addr < EndAddress();
+ }
+
+ bool SetProtections(mach_vm_address_t addr, mach_vm_size_t size, vm_prot_t prot);
+ bool RestoreProtections();
+ bool GetRegionForAddress(lldb::addr_t addr);
+protected:
+#if defined (VM_REGION_SUBMAP_SHORT_INFO_COUNT_64)
+ typedef vm_region_submap_short_info_data_64_t RegionInfo;
+ enum { kRegionInfoSize = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 };
+#else
+ typedef vm_region_submap_info_data_64_t RegionInfo;
+ enum { kRegionInfoSize = VM_REGION_SUBMAP_INFO_COUNT_64 };
+#endif
+
+ task_t m_task;
+ mach_vm_address_t m_addr;
+ lldb_private::Error m_err;
+ mach_vm_address_t m_start;
+ mach_vm_size_t m_size;
+ natural_t m_depth;
+ RegionInfo m_data;
+ vm_prot_t m_curr_protection; // The current, possibly modified protections. Original value is saved in m_data.protections.
+ mach_vm_address_t m_protection_addr; // The start address at which protections were changed
+ mach_vm_size_t m_protection_size; // The size of memory that had its protections changed
+
+};
+
+#endif // #ifndef liblldb_MachVMRegion_h_
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/ProcessControl-mig.defs b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/ProcessControl-mig.defs
new file mode 100644
index 00000000000..7f16fe13356
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/MacOSX/ProcessControl-mig.defs
@@ -0,0 +1,16 @@
+/*
+ * nub.defs
+ */
+
+/*
+ * DNBConfig.h is autogenerated by a perl script that is run as a build
+ * script in XCode. XCode is responsible for calling the script and setting
+ * the include paths correctly to locate it. The file will exist in the
+ * derived sources directory in the build folder.
+ *
+ */
+
+#include "DNBConfig.h"
+
+
+#include <mach/mach_exc.defs>
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.cpp
new file mode 100644
index 00000000000..3c37b64dc46
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.cpp
@@ -0,0 +1,2228 @@
+//===-- ProcessMacOSX.cpp ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// C Includes
+#include <errno.h>
+#include <mach/mach.h>
+#include <mach/mach_vm.h>
+#include <spawn.h>
+#include <sys/fcntl.h>
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <unistd.h>
+
+// C++ Includes
+#include <algorithm>
+#include <map>
+
+// Other libraries and framework includes
+
+#include "lldb/Breakpoint/WatchpointLocation.h"
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Core/FileSpec.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/State.h"
+#include "lldb/Core/Timer.h"
+#include "lldb/Host/TimeValue.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/TargetList.h"
+#include "PseudoTerminal.h"
+
+#if defined (__arm__)
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <SpringBoardServices/SpringBoardServer.h>
+#include <SpringBoardServices/SBSWatchdogAssertion.h>
+
+#endif // #if defined (__arm__)
+
+// Project includes
+#include "lldb/Host/Host.h"
+#include "ProcessMacOSX.h"
+#include "ProcessMacOSXLog.h"
+#include "ThreadMacOSX.h"
+
+
+#if 0
+#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__)
+#else
+#define DEBUG_LOG(fmt, ...)
+#endif
+
+#ifndef MACH_PROCESS_USE_POSIX_SPAWN
+#define MACH_PROCESS_USE_POSIX_SPAWN 1
+#endif
+
+
+#if defined (__arm__)
+
+static bool
+IsSBProcess (lldb::pid_t pid)
+{
+ bool opt_runningApps = true;
+ bool opt_debuggable = false;
+
+ CFReleaser<CFArrayRef> sbsAppIDs (::SBSCopyApplicationDisplayIdentifiers (opt_runningApps, opt_debuggable));
+ if (sbsAppIDs.get() != NULL)
+ {
+ CFIndex count = ::CFArrayGetCount (sbsAppIDs.get());
+ CFIndex i = 0;
+ for (i = 0; i < count; i++)
+ {
+ CFStringRef displayIdentifier = (CFStringRef)::CFArrayGetValueAtIndex (sbsAppIDs.get(), i);
+
+ // Get the process id for the app (if there is one)
+ lldb::pid_t sbs_pid = LLDB_INVALID_PROCESS_ID;
+ if (::SBSProcessIDForDisplayIdentifier ((CFStringRef)displayIdentifier, &sbs_pid) == TRUE)
+ {
+ if (sbs_pid == pid)
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+#endif // #if defined (__arm__)
+
+using namespace lldb;
+using namespace lldb_private;
+//
+//void *
+//ProcessMacOSX::WaitForChildProcessToExit (void *pid_ptr)
+//{
+// const lldb::pid_t pid = *((lldb::user_id_t *)pid_ptr);
+//
+// Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_THREAD);
+//
+// if (log)
+// log->Printf ("ProcessMacOSX::%s (arg = %p) thread starting...", __FUNCTION__, pid_ptr);
+//
+// int status = -1;
+//
+// while (1)
+// {
+// if (log)
+// log->Printf("::waitpid (pid = %i, stat_loc = %p, options = 0)...", pid, &status);
+//
+// lldb::pid_t return_pid = ::waitpid (pid, &status, 0);
+//
+// if (return_pid < 0)
+// {
+// if (log)
+// log->Printf("%s ::waitpid (pid = %i, stat_loc = %p, options = 0) => errno = %i, status = 0x%8.8x", pid, &status, errno, status);
+// break;
+// }
+//
+// bool set_exit_status = false;
+// if (WIFSTOPPED(status))
+// {
+// if (log)
+// log->Printf("::waitpid (pid = %i, stat_loc = %p, options = 0) => return_pid = %i, status = 0x%8.8x (STOPPED)", pid, &status, return_pid, status);
+// }
+// else if (WIFEXITED(status))
+// {
+// set_exit_status = true;
+// if (log)
+// log->Printf("::waitpid (pid = %i, stat_loc = %p, options = 0) => return_pid = %i, status = 0x%8.8x (EXITED)", pid, &status, return_pid, status);
+// }
+// else if (WIFSIGNALED(status))
+// {
+// set_exit_status = true;
+// if (log)
+// log->Printf("::waitpid (pid = %i, stat_loc = %p, options = 0) => return_pid = %i, status = 0x%8.8x (SIGNALED)", pid, &status, return_pid, status);
+// }
+// else
+// {
+// if (log)
+// log->Printf("::waitpid (pid = %i, stat_loc = %p, options = 0) => return_pid = %i, status = 0x%8.8x", pid, &status, return_pid, status);
+// }
+//
+// if (set_exit_status)
+// {
+// // Try and deliver the news to the process if it is still around
+// TargetSP target_sp(TargetList::SharedList().FindTargetWithProcessID (return_pid));
+// if (target_sp.get())
+// {
+// ProcessMacOSX *process = dynamic_cast<ProcessMacOSX*>(target_sp->GetProcess().get());
+// if (process)
+// {
+// process->SetExitStatus (status);
+// if (log)
+// log->Printf("Setting exit status of %i to 0x%8.8x", pid, status);
+// process->Task().ShutDownExceptionThread();
+// }
+// }
+// // Break out of the loop and return.
+// break;
+// }
+// }
+//
+// if (log)
+// log->Printf ("ProcessMacOSX::%s (arg = %p) thread exiting...", __FUNCTION__, pid_ptr);
+//
+// return NULL;
+//}
+//
+
+const char *
+ProcessMacOSX::GetPluginNameStatic()
+{
+ return "process.macosx";
+}
+
+const char *
+ProcessMacOSX::GetPluginDescriptionStatic()
+{
+ return "Native MacOSX user process debugging plug-in.";
+}
+
+void
+ProcessMacOSX::Terminate()
+{
+ PluginManager::UnregisterPlugin (ProcessMacOSX::CreateInstance);
+}
+
+
+Process*
+ProcessMacOSX::CreateInstance (Target &target, Listener &listener)
+{
+ ProcessMacOSX::Initialize();
+
+ return new ProcessMacOSX (target, listener);
+}
+
+bool
+ProcessMacOSX::CanDebug(Target &target)
+{
+ // For now we are just making sure the file exists for a given module
+ ModuleSP exe_module_sp(target.GetExecutableModule());
+ if (exe_module_sp.get())
+ return exe_module_sp->GetFileSpec().Exists();
+ return false;
+}
+
+//----------------------------------------------------------------------
+// ProcessMacOSX constructor
+//----------------------------------------------------------------------
+ProcessMacOSX::ProcessMacOSX(Target& target, Listener &listener) :
+ Process (target, listener),
+ m_dynamic_loader_ap (),
+// m_wait_thread (LLDB_INVALID_HOST_THREAD),
+ m_byte_order (eByteOrderHost),
+ m_stdio_ours (false),
+ m_child_stdin (-1),
+ m_child_stdout (-1),
+ m_child_stderr (-1),
+ m_task (this),
+ m_flags (eFlagsNone),
+ m_stdio_thread (LLDB_INVALID_HOST_THREAD),
+ m_stdio_mutex (Mutex::eMutexTypeRecursive),
+ m_stdout_data (),
+ m_exception_messages (),
+ m_exception_messages_mutex (Mutex::eMutexTypeRecursive),
+ m_arch_spec ()
+{
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+ProcessMacOSX::~ProcessMacOSX()
+{
+// m_mach_process.UnregisterNotificationCallbacks (this);
+ Clear();
+}
+
+//----------------------------------------------------------------------
+// PluginInterface
+//----------------------------------------------------------------------
+const char *
+ProcessMacOSX::GetPluginName()
+{
+ return "Process debugging plug-in for MacOSX";
+}
+
+const char *
+ProcessMacOSX::GetShortPluginName()
+{
+ return GetPluginNameStatic();
+}
+
+uint32_t
+ProcessMacOSX::GetPluginVersion()
+{
+ return 1;
+}
+
+void
+ProcessMacOSX::GetPluginCommandHelp (const char *command, Stream *strm)
+{
+ strm->Printf("The following arguments can be supplied to the 'log %s' command:\n", GetShortPluginName());
+ strm->PutCString("\tverbose - enable verbose logging\n");
+ strm->PutCString("\tprocess - enable process logging\n");
+ strm->PutCString("\tthread - enable thread logging\n");
+ strm->PutCString("\texceptions - enable exception logging\n");
+ strm->PutCString("\tdynamic - enable DynamicLoader logging\n");
+ strm->PutCString("\tmemory-calls - enable memory read and write call logging\n");
+ strm->PutCString("\tmemory-data-short - log short memory read and write byte data\n");
+ strm->PutCString("\tmemory-data-long - log all memory read and write byte data\n");
+ strm->PutCString("\tmemory-protections - log memory protection calls\n");
+ strm->PutCString("\tbreakpoints - log breakpoint calls\n");
+ strm->PutCString("\twatchpoints - log watchpoint calls\n");
+ strm->PutCString("\tevents - log event and event queue status\n");
+ strm->PutCString("\tstep - log step related activity\n");
+ strm->PutCString("\ttask - log task functions\n");
+}
+
+Error
+ProcessMacOSX::ExecutePluginCommand (Args &command, Stream *strm)
+{
+ Error error;
+ error.SetErrorString("No plug-in command are currently supported.");
+ return error;
+}
+
+Log *
+ProcessMacOSX::EnablePluginLogging (Stream *strm, Args &command)
+{
+ return NULL;
+}
+
+//----------------------------------------------------------------------
+// Process Control
+//----------------------------------------------------------------------
+Error
+ProcessMacOSX::DoLaunch
+(
+ Module* module,
+ char const *argv[],
+ char const *envp[],
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path
+)
+{
+// ::LogSetBitMask (PD_LOG_DEFAULT);
+// ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD);
+// ::LogSetLogFile ("/dev/stdout");
+
+ Error error;
+ ObjectFile * object_file = module->GetObjectFile();
+ if (object_file)
+ {
+ ArchSpec arch_spec(module->GetArchitecture());
+
+ // Set our user ID to our process ID.
+ SetID (LaunchForDebug(argv[0], argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, eLaunchDefault, error));
+ }
+ else
+ {
+ // Set our user ID to an invalid process ID.
+ SetID (LLDB_INVALID_PROCESS_ID);
+ error.SetErrorToGenericError ();
+ error.SetErrorStringWithFormat("Failed to get object file from '%s' for arch %s.\n", module->GetFileSpec().GetFilename().AsCString(), module->GetArchitecture().AsCString());
+ }
+
+ // Return the process ID we have
+ return error;
+}
+
+Error
+ProcessMacOSX::DoAttach (lldb::pid_t attach_pid)
+{
+ Error error;
+
+ // Clear out and clean up from any current state
+ Clear();
+ // HACK: require arch be set correctly at the target level until we can
+ // figure out a good way to determine the arch of what we are attaching to
+ m_arch_spec = m_target.GetArchitecture();
+
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS);
+ if (attach_pid != LLDB_INVALID_PROCESS_ID)
+ {
+ SetPrivateState (eStateAttaching);
+ SetID(attach_pid);
+ // Let ourselves know we are going to be using SBS if the correct flag bit is set...
+#if defined (__arm__)
+ if (IsSBProcess(pid))
+ m_flags |= eFlagsUsingSBS;
+#endif
+
+ if (Task().GetTaskPortForProcessID(error) == TASK_NULL)
+ {
+ if (log)
+ log->Printf ("error attaching to pid %i: %s", GetID(), error.AsCString());
+
+ }
+ else
+ {
+ Task().StartExceptionThread(error);
+
+ if (error.Success())
+ {
+ errno = 0;
+ if (::ptrace (PT_ATTACHEXC, GetID(), 0, 0) == 0)
+ {
+ m_flags.Set (eFlagsAttached);
+ // Sleep a bit to let the exception get received and set our process status
+ // to stopped.
+ ::usleep(250000);
+
+ if (log)
+ log->Printf ("successfully attached to pid %d", GetID());
+ return error;
+ }
+ else
+ {
+ error.SetErrorToErrno();
+ if (log)
+ log->Printf ("error: failed to attach to pid %d", GetID());
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf ("error: failed to start exception thread for pid %d: %s", GetID(), error.AsCString());
+ }
+
+ }
+ }
+ SetID (LLDB_INVALID_PROCESS_ID);
+ if (error.Success())
+ error.SetErrorStringWithFormat ("failed to attach to pid %d", attach_pid);
+ return error;
+}
+
+Error
+ProcessMacOSX::WillLaunchOrAttach ()
+{
+ Error error;
+ // TODO: this is hardcoded for macosx right now. We need this to be more dynamic
+ m_dynamic_loader_ap.reset(DynamicLoader::FindPlugin(this, "dynamic-loader.macosx-dyld"));
+
+ if (m_dynamic_loader_ap.get() == NULL)
+ error.SetErrorString("unable to find the dynamic loader named 'dynamic-loader.macosx-dyld'");
+
+ return error;
+}
+
+
+Error
+ProcessMacOSX::WillLaunch (Module* module)
+{
+ return WillLaunchOrAttach ();
+}
+
+void
+ProcessMacOSX::DidLaunchOrAttach ()
+{
+ if (GetID() == LLDB_INVALID_PROCESS_ID)
+ {
+ m_dynamic_loader_ap.reset();
+ }
+ else
+ {
+ Module * exe_module = GetTarget().GetExecutableModule ().get();
+ assert (exe_module);
+
+ m_arch_spec = exe_module->GetArchitecture();
+ assert (m_arch_spec.IsValid());
+
+ ObjectFile *exe_objfile = exe_module->GetObjectFile();
+ assert (exe_objfile);
+
+ m_byte_order = exe_objfile->GetByteOrder();
+ assert (m_byte_order != eByteOrderInvalid);
+ // Install a signal handler so we can catch when our child process
+ // dies and set the exit status correctly.
+
+ Host::StartMonitoringChildProcess (Process::SetProcessExitStatus, NULL, GetID(), false);
+
+ if (m_arch_spec == ArchSpec("arm"))
+ {
+ // On ARM we want the actual target triple of the OS to get the
+ // most capable ARM slice for the process. Since this plug-in is
+ // only used for doing native debugging this will work.
+ m_target_triple = Host::GetTargetTriple();
+ }
+ else
+ {
+ // We want the arch of the process, and the vendor and OS from the
+ // host OS.
+ StreamString triple;
+
+ triple.Printf("%s-%s-%s",
+ m_arch_spec.AsCString(),
+ Host::GetVendorString().AsCString("apple"),
+ Host::GetOSString().AsCString("darwin"));
+
+ m_target_triple.SetCString(triple.GetString().c_str());
+ }
+ }
+}
+
+void
+ProcessMacOSX::DidLaunch ()
+{
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSX::DidLaunch()");
+ DidLaunchOrAttach ();
+ if (m_dynamic_loader_ap.get())
+ m_dynamic_loader_ap->DidLaunch();
+}
+
+void
+ProcessMacOSX::DidAttach ()
+{
+ DidLaunchOrAttach ();
+ if (m_dynamic_loader_ap.get())
+ m_dynamic_loader_ap->DidAttach();
+}
+
+Error
+ProcessMacOSX::WillAttach (lldb::pid_t pid)
+{
+ return WillLaunchOrAttach ();
+}
+
+Error
+ProcessMacOSX::DoResume ()
+{
+ Error error;
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSX::Resume()");
+ const StateType state = m_private_state.GetValue();
+
+ if (CanResume(state))
+ {
+ error = PrivateResume(LLDB_INVALID_THREAD_ID);
+ }
+ else if (state == eStateRunning)
+ {
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "Resume() - task 0x%x is running, ignoring...", Task().GetTaskPort());
+ }
+ else
+ {
+ error.SetErrorStringWithFormat("task 0x%x can't continue, ignoring...", Task().GetTaskPort());
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "Resume() - task 0x%x can't continue, ignoring...", Task().GetTaskPort());
+ }
+ return error;
+}
+
+size_t
+ProcessMacOSX::GetSoftwareBreakpointTrapOpcode (BreakpointSite* bp_site)
+{
+ const uint8_t *trap_opcode = NULL;
+ uint32_t trap_opcode_size = 0;
+
+ static const uint8_t g_arm_breakpoint_opcode[] = { 0xFE, 0xDE, 0xFF, 0xE7 };
+ //static const uint8_t g_thumb_breakpooint_opcode[] = { 0xFE, 0xDE };
+ static const uint8_t g_ppc_breakpoint_opcode[] = { 0x7F, 0xC0, 0x00, 0x08 };
+ static const uint8_t g_i386_breakpoint_opcode[] = { 0xCC };
+
+ switch (m_arch_spec.GetCPUType())
+ {
+ case CPU_TYPE_ARM:
+ // TODO: fill this in for ARM. We need to dig up the symbol for
+ // the address in the breakpoint locaiton and figure out if it is
+ // an ARM or Thumb breakpoint.
+ trap_opcode = g_arm_breakpoint_opcode;
+ trap_opcode_size = sizeof(g_arm_breakpoint_opcode);
+ break;
+
+ case CPU_TYPE_POWERPC:
+ case CPU_TYPE_POWERPC64:
+ trap_opcode = g_ppc_breakpoint_opcode;
+ trap_opcode_size = sizeof(g_ppc_breakpoint_opcode);
+ break;
+
+ case CPU_TYPE_I386:
+ case CPU_TYPE_X86_64:
+ trap_opcode = g_i386_breakpoint_opcode;
+ trap_opcode_size = sizeof(g_i386_breakpoint_opcode);
+ break;
+
+ default:
+ assert(!"Unhandled architecture in ProcessMacOSX::GetSoftwareBreakpointTrapOpcode()");
+ return 0;
+ }
+
+ if (trap_opcode && trap_opcode_size)
+ {
+ if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size))
+ return trap_opcode_size;
+ }
+ return 0;
+}
+uint32_t
+ProcessMacOSX::UpdateThreadListIfNeeded ()
+{
+ // locker will keep a mutex locked until it goes out of scope
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_THREAD);
+ if (log && log->GetMask().IsSet(PD_LOG_VERBOSE))
+ log->Printf ("ProcessMacOSX::%s (pid = %4.4x)", __FUNCTION__, GetID());
+
+ const uint32_t stop_id = GetStopID();
+ if (m_thread_list.GetSize(false) == 0 || stop_id != m_thread_list.GetStopID())
+ {
+ // Update the thread list's stop id immediately so we don't recurse into this function.
+ thread_array_t thread_list = NULL;
+ mach_msg_type_number_t thread_list_count = 0;
+ task_t task = Task().GetTaskPort();
+ Error err(::task_threads (task, &thread_list, &thread_list_count), eErrorTypeMachKernel);
+
+ if (log || err.Fail())
+ err.PutToLog(log, "::task_threads ( task = 0x%4.4x, thread_list => %p, thread_list_count => %u )", task, thread_list, thread_list_count);
+
+ if (err.GetError() == KERN_SUCCESS && thread_list_count > 0)
+ {
+ ThreadList curr_thread_list (this);
+ curr_thread_list.SetStopID(stop_id);
+
+ size_t idx;
+ // Iterator through the current thread list and see which threads
+ // we already have in our list (keep them), which ones we don't
+ // (add them), and which ones are not around anymore (remove them).
+ for (idx = 0; idx < thread_list_count; ++idx)
+ {
+ const lldb::tid_t tid = thread_list[idx];
+ ThreadSP thread_sp(GetThreadList().FindThreadByID (tid, false));
+ if (thread_sp.get() == NULL)
+ thread_sp.reset (new ThreadMacOSX (*this, tid));
+ curr_thread_list.AddThread(thread_sp);
+ }
+
+ m_thread_list = curr_thread_list;
+
+ // Free the vm memory given to us by ::task_threads()
+ vm_size_t thread_list_size = (vm_size_t) (thread_list_count * sizeof (lldb::tid_t));
+ ::vm_deallocate (::mach_task_self(),
+ (vm_address_t)thread_list,
+ thread_list_size);
+ }
+ }
+ return GetThreadList().GetSize(false);
+}
+
+
+void
+ProcessMacOSX::RefreshStateAfterStop ()
+{
+ // If we are attaching, let our dynamic loader plug-in know so it can get
+ // an initial list of shared libraries.
+
+ // We must be attaching if we don't already have a valid architecture
+ if (!m_arch_spec.IsValid())
+ {
+ Module *exe_module = GetTarget().GetExecutableModule().get();
+ if (exe_module)
+ m_arch_spec = exe_module->GetArchitecture();
+ }
+ // Discover new threads:
+ UpdateThreadListIfNeeded ();
+
+ // Let all threads recover from stopping and do any clean up based
+ // on the previous thread state (if any).
+ m_thread_list.RefreshStateAfterStop();
+
+ // Let each thread know of any exceptions
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS);
+ task_t task = Task().GetTaskPort();
+ size_t i;
+ for (i=0; i<m_exception_messages.size(); ++i)
+ {
+ // Let the thread list figure use the ProcessMacOSX to forward all exceptions
+ // on down to each thread.
+ if (m_exception_messages[i].state.task_port == task)
+ {
+ ThreadSP thread_sp(m_thread_list.FindThreadByID(m_exception_messages[i].state.thread_port));
+ if (thread_sp.get())
+ {
+ ThreadMacOSX *macosx_thread = (ThreadMacOSX *)thread_sp.get();
+ macosx_thread->NotifyException (m_exception_messages[i].state);
+ }
+ }
+ if (log)
+ m_exception_messages[i].PutToLog(log);
+ }
+
+}
+
+Error
+ProcessMacOSX::DoHalt ()
+{
+ return Signal (SIGSTOP);
+}
+
+Error
+ProcessMacOSX::WillDetach ()
+{
+ Error error;
+ const StateType state = m_private_state.GetValue();
+
+ if (IsRunning(state))
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorString("Process must be stopped in order to detach.");
+ }
+ return error;
+}
+
+Error
+ProcessMacOSX::DoSIGSTOP (bool clear_all_breakpoints)
+{
+ Error error;
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_PROCESS);
+
+ if (log)
+ log->Printf ("ProcessMacOSX::DoSIGSTOP()");
+ EventSP event_sp;
+ TimeValue timeout_time;
+
+ StateType state = m_private_state.GetValue();
+
+ lldb::pid_t pid = GetID();
+
+ if (IsRunning(state))
+ {
+ // If our process is running, we need to SIGSTOP it so we can detach.
+ if (log)
+ log->Printf ("ProcessMacOSX::DoDestroy() - kill (%i, SIGSTOP)", pid);
+
+ // Send the SIGSTOP and wait a few seconds for it to stop
+
+ // Pause the Private State Thread so it doesn't intercept the events we need to wait for.
+ PausePrivateStateThread();
+
+ m_thread_list.DiscardThreadPlans();
+
+ // First jettison all the current thread plans, since we want to make sure it
+ // really just stops.
+
+ if (::kill (pid, SIGSTOP) == 0)
+ error.Clear();
+ else
+ error.SetErrorToErrno();
+
+ if (error.Fail())
+ error.PutToLog(log, "::kill (pid = %i, SIGSTOP)", pid);
+
+ timeout_time = TimeValue::Now();
+ timeout_time.OffsetWithSeconds(2);
+
+ state = WaitForStateChangedEventsPrivate (&timeout_time, event_sp);
+
+ // Resume the private state thread at this point.
+ ResumePrivateStateThread();
+
+ if (!StateIsStoppedState (state))
+ {
+ if (log)
+ log->Printf("ProcessMacOSX::DoSIGSTOP() failed to stop after sending SIGSTOP");
+ return error;
+ }
+ if (clear_all_breakpoints)
+ GetTarget().DisableAllBreakpoints();
+ }
+ else if (!HasExited(state))
+ {
+ if (clear_all_breakpoints)
+ GetTarget().DisableAllBreakpoints();
+
+// const uint32_t num_threads = GetNumThreads();
+// for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx)
+// {
+// Thread *thread = GetThreadAtIndex(thread_idx);
+// thread->SetResumeState(eStateRunning);
+// if (thread_idx == 0)
+// thread->SetResumeSignal(SIGSTOP);
+// }
+
+ // Our process was stopped, so resume it and then SIGSTOP it so we can
+ // detach.
+ // But discard all the thread plans first, so we don't keep going because we
+ // are in mid-plan.
+
+ // Pause the Private State Thread so it doesn't intercept the events we need to wait for.
+ PausePrivateStateThread();
+
+ m_thread_list.DiscardThreadPlans();
+
+ if (::kill (pid, SIGSTOP) == 0)
+ error.Clear();
+ else
+ error.SetErrorToErrno();
+
+ if (log || error.Fail())
+ error.PutToLog(log, "ProcessMacOSX::DoSIGSTOP() ::kill (pid = %i, SIGSTOP)", pid);
+
+ error = PrivateResume(LLDB_INVALID_THREAD_ID);
+
+ // Wait a few seconds for our process to resume
+ timeout_time = TimeValue::Now();
+ timeout_time.OffsetWithSeconds(2);
+ state = WaitForStateChangedEventsPrivate (&timeout_time, event_sp);
+
+ // Make sure the process resumed
+ if (StateIsStoppedState (state))
+ {
+ if (log)
+ log->Printf ("ProcessMacOSX::DoSIGSTOP() couldn't resume process, state = %s", StateAsCString(state));
+ error.SetErrorStringWithFormat("ProcessMacOSX::DoSIGSTOP() couldn't resume process, state = %s", StateAsCString(state));
+ }
+ else
+ {
+ // Send the SIGSTOP and wait a few seconds for it to stop
+ timeout_time = TimeValue::Now();
+ timeout_time.OffsetWithSeconds(2);
+ state = WaitForStateChangedEventsPrivate (&timeout_time, event_sp);
+ if (!StateIsStoppedState (state))
+ {
+ if (log)
+ log->Printf("ProcessMacOSX::DoSIGSTOP() failed to stop after sending SIGSTOP");
+ error.SetErrorString("ProcessMacOSX::DoSIGSTOP() failed to stop after sending SIGSTOP");
+ }
+ }
+ // Resume the private state thread at this point.
+ ResumePrivateStateThread();
+ }
+
+ return error;
+}
+
+Error
+ProcessMacOSX::DoDestroy ()
+{
+ Error error;
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_PROCESS);
+ if (log)
+ log->Printf ("ProcessMacOSX::DoDestroy()");
+
+ error = DoSIGSTOP (true);
+ if (error.Success())
+ {
+ StopSTDIOThread(true);
+
+ if (log)
+ log->Printf ("ProcessMacOSX::DoDestroy() DoSIGSTOP succeeded");
+ const StateType state = m_private_state.GetValue();
+ // Scope for "locker" so we can reply to all of our exceptions (the SIGSTOP
+ // exception).
+ {
+ Mutex::Locker locker(m_exception_messages_mutex);
+ ReplyToAllExceptions();
+ }
+ if (log)
+ log->Printf ("ProcessMacOSX::DoDestroy() replied to all exceptions");
+
+ // Shut down the exception thread and cleanup our exception remappings
+ Task().ShutDownExceptionThread();
+
+ if (log)
+ log->Printf ("ProcessMacOSX::DoDestroy() exception thread has been shutdown");
+
+ if (!HasExited(state))
+ {
+ lldb::pid_t pid = GetID();
+
+ // Detach from our process while we are stopped.
+ errno = 0;
+
+ // Detach from our process
+ ::ptrace (PT_KILL, pid, 0, 0);
+
+ error.SetErrorToErrno();
+
+ if (log || error.Fail())
+ error.PutToLog (log, "::ptrace (PT_KILL, %u, 0, 0)", pid);
+
+ // Resume our task and let the SIGKILL do its thing. The thread named
+ // "ProcessMacOSX::WaitForChildProcessToExit(void*)" will catch the
+ // process exiting, so we don't need to set our state to exited in this
+ // function.
+ Task().Resume();
+ }
+
+ // NULL our task out as we have already retored all exception ports
+ Task().Clear();
+
+ // Clear out any notion of the process we once were
+ Clear();
+ }
+ return error;
+}
+
+ByteOrder
+ProcessMacOSX::GetByteOrder () const
+{
+ return m_byte_order;
+}
+
+//------------------------------------------------------------------
+// Process Queries
+//------------------------------------------------------------------
+
+bool
+ProcessMacOSX::IsAlive ()
+{
+ return MachTask::IsValid (Task().GetTaskPort());
+}
+
+lldb::addr_t
+ProcessMacOSX::GetImageInfoAddress()
+{
+ return Task().GetDYLDAllImageInfosAddress();
+}
+
+DynamicLoader *
+ProcessMacOSX::GetDynamicLoader()
+{
+ return m_dynamic_loader_ap.get();
+}
+
+//------------------------------------------------------------------
+// Process Memory
+//------------------------------------------------------------------
+
+size_t
+ProcessMacOSX::DoReadMemory (lldb::addr_t addr, void *buf, size_t size, Error& error)
+{
+ return Task().ReadMemory(addr, buf, size, error);
+}
+
+size_t
+ProcessMacOSX::DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size, Error& error)
+{
+ return Task().WriteMemory(addr, buf, size, error);
+}
+
+lldb::addr_t
+ProcessMacOSX::DoAllocateMemory (size_t size, uint32_t permissions, Error& error)
+{
+ return Task().AllocateMemory (size, permissions, error);
+}
+
+Error
+ProcessMacOSX::DoDeallocateMemory (lldb::addr_t ptr)
+{
+ return Task().DeallocateMemory (ptr);
+}
+
+//------------------------------------------------------------------
+// Process STDIO
+//------------------------------------------------------------------
+
+size_t
+ProcessMacOSX::GetSTDOUT (char *buf, size_t buf_size, Error &error)
+{
+ error.Clear();
+ Mutex::Locker locker(m_stdio_mutex);
+ size_t bytes_available = m_stdout_data.size();
+ if (bytes_available > 0)
+ {
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSX::%s (&%p[%u]) ...", __FUNCTION__, buf, buf_size);
+ if (bytes_available > buf_size)
+ {
+ memcpy(buf, m_stdout_data.data(), buf_size);
+ m_stdout_data.erase(0, buf_size);
+ bytes_available = buf_size;
+ }
+ else
+ {
+ memcpy(buf, m_stdout_data.data(), bytes_available);
+ m_stdout_data.clear();
+
+ //ResetEventBits(eBroadcastBitSTDOUT);
+ }
+ }
+ return bytes_available;
+}
+
+size_t
+ProcessMacOSX::GetSTDERR (char *buf, size_t buf_size, Error &error)
+{
+ error.Clear();
+ return 0;
+}
+
+size_t
+ProcessMacOSX::PutSTDIN (const char *buf, size_t buf_size, Error &error)
+{
+ if (m_child_stdin == -1)
+ {
+ error.SetErrorString ("Invalid child stdin handle.");
+ }
+ else
+ {
+ ssize_t bytes_written = ::write (m_child_stdin, buf, buf_size);
+ if (bytes_written == -1)
+ error.SetErrorToErrno();
+ else
+ {
+ error.Clear();
+ return bytes_written;
+ }
+ }
+ return 0;
+}
+
+Error
+ProcessMacOSX::EnableBreakpoint (BreakpointSite *bp_site)
+{
+ Error error;
+ assert (bp_site != NULL);
+ const lldb::addr_t addr = bp_site->GetLoadAddress();
+ const lldb::user_id_t site_id = bp_site->GetID();
+
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_BREAKPOINTS);
+ if (log)
+ log->Printf ("ProcessMacOSX::EnableBreakpoint (site_id = %d) addr = 0x%8.8llx", site_id, (uint64_t)addr);
+
+ if (bp_site->IsEnabled())
+ {
+ if (log)
+ log->Printf ("ProcessMacOSX::EnableBreakpoint (site_id = %d) addr = 0x%8.8llx -- SUCCESS (already enabled)", site_id, (uint64_t)addr);
+ return error;
+ }
+
+ if (bp_site->HardwarePreferred())
+ {
+ ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(bp_site->GetThreadID()).get();
+ if (thread)
+ {
+ bp_site->SetHardwareIndex (thread->SetHardwareBreakpoint(bp_site));
+ if (bp_site->IsHardware())
+ {
+ bp_site->SetEnabled(true);
+ return error;
+ }
+ }
+ }
+
+ // Just let lldb::Process::EnableSoftwareBreakpoint() handle everything...
+ return EnableSoftwareBreakpoint (bp_site);
+}
+
+Error
+ProcessMacOSX::DisableBreakpoint (BreakpointSite *bp_site)
+{
+ Error error;
+ assert (bp_site != NULL);
+ const lldb::addr_t addr = bp_site->GetLoadAddress();
+ const lldb::user_id_t site_id = bp_site->GetID();
+
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_BREAKPOINTS);
+ if (log)
+ log->Printf ("ProcessMacOSX::DisableBreakpoint (site_id = %d) addr = 0x%8.8llx", site_id, (uint64_t)addr);
+
+ if (bp_site->IsHardware())
+ {
+ ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(bp_site->GetThreadID()).get();
+ if (thread)
+ {
+ if (thread->ClearHardwareBreakpoint(bp_site))
+ {
+ bp_site->SetEnabled(false);
+ if (log)
+ log->Printf ("ProcessMacOSX::DisableBreakpoint (site_id = %d) addr = 0x%8.8llx -- SUCCESS (hardware)", site_id, (uint64_t)addr);
+ return error;
+ }
+ }
+ error.SetErrorString("hardware breakpoints are no supported");
+ return error;
+ }
+
+ // Just let lldb::Process::EnableSoftwareBreakpoint() handle everything...
+ return DisableSoftwareBreakpoint (bp_site);
+}
+
+Error
+ProcessMacOSX::EnableWatchpoint (WatchpointLocation *wp)
+{
+ Error error;
+ if (wp)
+ {
+ lldb::user_id_t watchID = wp->GetID();
+ lldb::addr_t addr = wp->GetLoadAddress();
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_WATCHPOINTS);
+ if (log)
+ log->Printf ("ProcessMacOSX::EnableWatchpoint(watchID = %d)", watchID);
+ if (wp->IsEnabled())
+ {
+ if (log)
+ log->Printf("ProcessMacOSX::EnableWatchpoint(watchID = %d) addr = 0x%8.8llx: watchpoint already enabled.", watchID, (uint64_t)addr);
+ return error;
+ }
+ else
+ {
+ ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(wp->GetThreadID()).get();
+ if (thread)
+ {
+ wp->SetHardwareIndex (thread->SetHardwareWatchpoint (wp));
+ if (wp->IsHardware ())
+ {
+ wp->SetEnabled(true);
+ return error;
+ }
+ }
+ else
+ {
+ error.SetErrorString("Watchpoints currently only support thread specific watchpoints.");
+ }
+ }
+ }
+ return error;
+}
+
+Error
+ProcessMacOSX::DisableWatchpoint (WatchpointLocation *wp)
+{
+ Error error;
+ if (wp)
+ {
+ lldb::user_id_t watchID = wp->GetID();
+
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_WATCHPOINTS);
+
+ lldb::addr_t addr = wp->GetLoadAddress();
+ if (log)
+ log->Printf ("ProcessMacOSX::DisableWatchpoint (watchID = %d) addr = 0x%8.8llx", watchID, (uint64_t)addr);
+
+ if (wp->IsHardware())
+ {
+ ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(wp->GetThreadID()).get();
+ if (thread)
+ {
+ if (thread->ClearHardwareWatchpoint (wp))
+ {
+ wp->SetEnabled(false);
+ if (log)
+ log->Printf ("ProcessMacOSX::Disablewatchpoint (watchID = %d) addr = 0x%8.8llx (hardware) => success", watchID, (uint64_t)addr);
+ return error;
+ }
+ }
+ }
+ // TODO: clear software watchpoints if we implement them
+ error.SetErrorToGenericError();
+ }
+ else
+ {
+ error.SetErrorString("Watchpoint location argument was NULL.");
+ }
+ return error;
+}
+
+
+static ProcessMacOSX::CreateArchCalback
+ArchCallbackMap(const ArchSpec& arch_spec, ProcessMacOSX::CreateArchCalback callback, bool add )
+{
+ // We must wrap the "g_arch_map" file static in a function to avoid
+ // any global constructors so we don't get a build verification error
+ typedef std::multimap<ArchSpec, ProcessMacOSX::CreateArchCalback> ArchToProtocolMap;
+ static ArchToProtocolMap g_arch_map;
+
+ if (add)
+ {
+ g_arch_map.insert(std::make_pair(arch_spec, callback));
+ return callback;
+ }
+ else
+ {
+ ArchToProtocolMap::const_iterator pos = g_arch_map.find(arch_spec);
+ if (pos != g_arch_map.end())
+ {
+ return pos->second;
+ }
+ }
+ return NULL;
+}
+
+void
+ProcessMacOSX::AddArchCreateCallback(const ArchSpec& arch_spec, CreateArchCalback callback)
+{
+ ArchCallbackMap (arch_spec, callback, true);
+}
+
+ProcessMacOSX::CreateArchCalback
+ProcessMacOSX::GetArchCreateCallback()
+{
+ return ArchCallbackMap (m_arch_spec, NULL, false);
+}
+
+void
+ProcessMacOSX::Clear()
+{
+ // Clear any cached thread list while the pid and task are still valid
+
+ Task().Clear();
+ // Now clear out all member variables
+ CloseChildFileDescriptors();
+
+ m_flags = eFlagsNone;
+ m_thread_list.Clear();
+ {
+ Mutex::Locker locker(m_exception_messages_mutex);
+ m_exception_messages.clear();
+ }
+
+}
+
+bool
+ProcessMacOSX::StartSTDIOThread()
+{
+ // If we created and own the child STDIO file handles, then we track the
+ // STDIO ourselves, else we let whomever owns these file handles track
+ // the IO themselves.
+ if (m_stdio_ours)
+ {
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSX::%s ( )", __FUNCTION__);
+ // Create the thread that watches for the child STDIO
+ m_stdio_thread = Host::ThreadCreate ("<lldb.process.process-macosx.stdio>", ProcessMacOSX::STDIOThread, this, NULL);
+ return m_stdio_thread != LLDB_INVALID_HOST_THREAD;
+ }
+ return false;
+}
+
+
+void
+ProcessMacOSX::StopSTDIOThread(bool close_child_fds)
+{
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSX::%s ( )", __FUNCTION__);
+ // Stop the stdio thread
+ if (m_stdio_thread != LLDB_INVALID_HOST_THREAD)
+ {
+ Host::ThreadCancel (m_stdio_thread, NULL);
+ thread_result_t result = NULL;
+ Host::ThreadJoin (m_stdio_thread, &result, NULL);
+ if (close_child_fds)
+ CloseChildFileDescriptors();
+ else
+ {
+ // We may have given up control of these file handles, so just
+ // set them to invalid values so the STDIO thread can exit when
+ // we interrupt it with pthread_cancel...
+ m_child_stdin = -1;
+ m_child_stdout = -1;
+ m_child_stderr = -1;
+ }
+ }
+}
+
+
+void *
+ProcessMacOSX::STDIOThread(void *arg)
+{
+ ProcessMacOSX *proc = (ProcessMacOSX*) arg;
+
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS);
+ if (log)
+ log->Printf ("ProcessMacOSX::%s (arg = %p) thread starting...", __FUNCTION__, arg);
+
+ // We start use a base and more options so we can control if we
+ // are currently using a timeout on the mach_msg. We do this to get a
+ // bunch of related exceptions on our exception port so we can process
+ // then together. When we have multiple threads, we can get an exception
+ // per thread and they will come in consecutively. The main thread loop
+ // will start by calling mach_msg to without having the MACH_RCV_TIMEOUT
+ // flag set in the options, so we will wait forever for an exception on
+ // our exception port. After we get one exception, we then will use the
+ // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current
+ // exceptions for our process. After we have received the last pending
+ // exception, we will get a timeout which enables us to then notify
+ // our main thread that we have an exception bundle avaiable. We then wait
+ // for the main thread to tell this exception thread to start trying to get
+ // exceptions messages again and we start again with a mach_msg read with
+ // infinite timeout.
+ Error err;
+ int stdout_fd = proc->GetStdoutFileDescriptor();
+ int stderr_fd = proc->GetStderrFileDescriptor();
+ if (stdout_fd == stderr_fd)
+ stderr_fd = -1;
+
+ while (stdout_fd >= 0 || stderr_fd >= 0)
+ {
+ //::pthread_testcancel ();
+
+ fd_set read_fds;
+ FD_ZERO (&read_fds);
+ if (stdout_fd >= 0)
+ FD_SET (stdout_fd, &read_fds);
+ if (stderr_fd >= 0)
+ FD_SET (stderr_fd, &read_fds);
+ int nfds = std::max<int>(stdout_fd, stderr_fd) + 1;
+
+ int num_set_fds = select (nfds, &read_fds, NULL, NULL, NULL);
+ if (log)
+ log->Printf("select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds);
+
+ if (num_set_fds < 0)
+ {
+ int select_errno = errno;
+ if (log)
+ {
+ err.SetError (select_errno, eErrorTypePOSIX);
+ err.LogIfError(log, "select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds);
+ }
+
+ switch (select_errno)
+ {
+ case EAGAIN: // The kernel was (perhaps temporarily) unable to allocate the requested number of file descriptors, or we have non-blocking IO
+ break;
+ case EBADF: // One of the descriptor sets specified an invalid descriptor.
+ return NULL;
+ break;
+ case EINTR: // A signal was delivered before the time limit expired and before any of the selected events occurred.
+ case EINVAL: // The specified time limit is invalid. One of its components is negative or too large.
+ default: // Other unknown error
+ break;
+ }
+ }
+ else if (num_set_fds == 0)
+ {
+ }
+ else
+ {
+ char s[1024];
+ s[sizeof(s)-1] = '\0'; // Ensure we have NULL termination
+ int bytes_read = 0;
+ if (stdout_fd >= 0 && FD_ISSET (stdout_fd, &read_fds))
+ {
+ do
+ {
+ bytes_read = ::read (stdout_fd, s, sizeof(s)-1);
+ if (bytes_read < 0)
+ {
+ int read_errno = errno;
+ if (log)
+ log->Printf("read (stdout_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno));
+ }
+ else if (bytes_read == 0)
+ {
+ // EOF...
+ if (log)
+ log->Printf("read (stdout_fd, ) => %d (reached EOF for child STDOUT)", bytes_read);
+ stdout_fd = -1;
+ }
+ else if (bytes_read > 0)
+ {
+ proc->AppendSTDOUT(s, bytes_read);
+ }
+
+ } while (bytes_read > 0);
+ }
+
+ if (stderr_fd >= 0 && FD_ISSET (stderr_fd, &read_fds))
+ {
+ do
+ {
+ bytes_read = ::read (stderr_fd, s, sizeof(s)-1);
+ if (bytes_read < 0)
+ {
+ int read_errno = errno;
+ if (log)
+ log->Printf("read (stderr_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno));
+ }
+ else if (bytes_read == 0)
+ {
+ // EOF...
+ if (log)
+ log->Printf("read (stderr_fd, ) => %d (reached EOF for child STDERR)", bytes_read);
+ stderr_fd = -1;
+ }
+ else if (bytes_read > 0)
+ {
+ proc->AppendSTDOUT(s, bytes_read);
+ }
+
+ } while (bytes_read > 0);
+ }
+ }
+ }
+
+ if (log)
+ log->Printf("ProcessMacOSX::%s (%p): thread exiting...", __FUNCTION__, arg);
+
+ return NULL;
+}
+
+Error
+ProcessMacOSX::DoSignal (int signal)
+{
+ Error error;
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_PROCESS);
+ if (log)
+ log->Printf ("ProcessMacOSX::DoSignal (signal = %d)", signal);
+ if (::kill (GetID(), signal) != 0)
+ {
+ error.SetErrorToErrno();
+ error.LogIfError(log, "ProcessMacOSX::DoSignal (%d)", signal);
+ }
+ return error;
+}
+
+
+Error
+ProcessMacOSX::DoDetach()
+{
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_PROCESS);
+ if (log)
+ log->Printf ("ProcessMacOSX::DoDetach()");
+
+ Error error (DoSIGSTOP (true));
+ if (error.Success())
+ {
+ CloseChildFileDescriptors ();
+
+ // Scope for "locker" so we can reply to all of our exceptions (the SIGSTOP
+ // exception).
+ {
+ Mutex::Locker locker(m_exception_messages_mutex);
+ ReplyToAllExceptions();
+ }
+
+ // Shut down the exception thread and cleanup our exception remappings
+ Task().ShutDownExceptionThread();
+
+ lldb::pid_t pid = GetID();
+
+ // Detach from our process while we are stopped.
+ errno = 0;
+
+ // Detach from our process
+ ::ptrace (PT_DETACH, pid, (caddr_t)1, 0);
+
+ error.SetErrorToErrno();
+
+ if (log || error.Fail())
+ error.PutToLog(log, "::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid);
+
+ // Resume our task
+ Task().Resume();
+
+ // NULL our task out as we have already retored all exception ports
+ Task().Clear();
+
+ // Clear out any notion of the process we once were
+ Clear();
+
+ SetPrivateState (eStateDetached);
+ }
+ return error;
+}
+
+
+
+Error
+ProcessMacOSX::ReplyToAllExceptions()
+{
+ Error error;
+ Mutex::Locker locker(m_exception_messages_mutex);
+ if (m_exception_messages.empty() == false)
+ {
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS);
+
+ MachException::Message::iterator pos;
+ MachException::Message::iterator begin = m_exception_messages.begin();
+ MachException::Message::iterator end = m_exception_messages.end();
+ for (pos = begin; pos != end; ++pos)
+ {
+ int resume_signal = -1;
+ ThreadSP thread_sp = m_thread_list.FindThreadByID(pos->state.thread_port);
+ if (thread_sp.get())
+ resume_signal = thread_sp->GetResumeSignal();
+ if (log)
+ log->Printf ("Replying to exception %d for thread 0x%4.4x (resume_signal = %i).", std::distance(begin, pos), thread_sp->GetID(), resume_signal);
+ Error curr_error (pos->Reply (Task().GetTaskPort(), GetID(), resume_signal));
+
+ // Only report the first error
+ if (curr_error.Fail() && error.Success())
+ error = curr_error;
+
+ error.LogIfError(log, "Error replying to exception");
+ }
+
+ // Erase all exception message as we should have used and replied
+ // to them all already.
+ m_exception_messages.clear();
+ }
+ return error;
+}
+
+
+Error
+ProcessMacOSX::PrivateResume (lldb::tid_t tid)
+{
+
+ Mutex::Locker locker(m_exception_messages_mutex);
+ Error error (ReplyToAllExceptions());
+
+ // Let the thread prepare to resume and see if any threads want us to
+ // step over a breakpoint instruction (ProcessWillResume will modify
+ // the value of stepOverBreakInstruction).
+ //StateType process_state = m_thread_list.ProcessWillResume(this);
+
+ // Set our state accordingly
+ SetPrivateState (eStateRunning);
+
+ // Now resume our task.
+ error = Task().Resume();
+ return error;
+}
+
+// Called by the exception thread when an exception has been received from
+// our process. The exception message is completely filled and the exception
+// data has already been copied.
+void
+ProcessMacOSX::ExceptionMessageReceived (const MachException::Message& exceptionMessage)
+{
+ Mutex::Locker locker(m_exception_messages_mutex);
+
+ if (m_exception_messages.empty())
+ Task().Suspend();
+
+ ProcessMacOSXLog::LogIf (PD_LOG_EXCEPTIONS, "ProcessMacOSX::ExceptionMessageReceived ( )");
+
+ // Use a locker to automatically unlock our mutex in case of exceptions
+ // Add the exception to our internal exception stack
+ m_exception_messages.push_back(exceptionMessage);
+}
+
+
+//bool
+//ProcessMacOSX::GetProcessInfo (struct kinfo_proc* proc_info)
+//{
+// int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, GetID() };
+// size_t buf_size = sizeof(struct kinfo_proc);
+//
+// if (::sysctl (mib, (unsigned)(sizeof(mib)/sizeof(int)), &proc_info, &buf_size, NULL, 0) == 0)
+// return buf_size > 0;
+//
+// return false;
+//}
+//
+//
+void
+ProcessMacOSX::ExceptionMessageBundleComplete()
+{
+ // We have a complete bundle of exceptions for our child process.
+ Mutex::Locker locker(m_exception_messages_mutex);
+ ProcessMacOSXLog::LogIf (PD_LOG_EXCEPTIONS, "%s: %d exception messages.", __PRETTY_FUNCTION__, m_exception_messages.size());
+ if (!m_exception_messages.empty())
+ {
+ SetPrivateState (eStateStopped);
+ }
+ else
+ {
+ ProcessMacOSXLog::LogIf (PD_LOG_EXCEPTIONS, "%s empty exception messages bundle.", __PRETTY_FUNCTION__, m_exception_messages.size());
+ }
+}
+
+bool
+ProcessMacOSX::ReleaseChildFileDescriptors ( int *stdin_fileno, int *stdout_fileno, int *stderr_fileno )
+{
+ if (stdin_fileno)
+ *stdin_fileno = m_child_stdin;
+ if (stdout_fileno)
+ *stdout_fileno = m_child_stdout;
+ if (stderr_fileno)
+ *stderr_fileno = m_child_stderr;
+ // Stop the stdio thread if we have one, but don't have it close the child
+ // file descriptors since we are giving control of these descriptors to the
+ // caller
+ bool close_child_fds = false;
+ StopSTDIOThread(close_child_fds);
+ return true;
+}
+
+void
+ProcessMacOSX::AppendSTDOUT (const char* s, size_t len)
+{
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSX::%s (<%d> %s) ...", __FUNCTION__, len, s);
+ Mutex::Locker locker(m_stdio_mutex);
+ m_stdout_data.append(s, len);
+
+ // FIXME: Make a real data object for this and put it out.
+ BroadcastEventIfUnique (eBroadcastBitSTDOUT);
+}
+
+lldb::pid_t
+ProcessMacOSX::LaunchForDebug
+(
+ const char *path,
+ char const *argv[],
+ char const *envp[],
+ ArchSpec& arch_spec,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ PDLaunchType launch_type,
+ Error &launch_err)
+{
+ // Clear out and clean up from any current state
+ Clear();
+
+ m_arch_spec = arch_spec;
+
+ if (launch_type == eLaunchDefault)
+ launch_type = eLaunchPosixSpawn;
+
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS);
+ if (log)
+ log->Printf ("%s( path = '%s', argv = %p, envp = %p, launch_type = %u )", __FUNCTION__, path, argv, envp, launch_type);
+
+ // Fork a child process for debugging
+ SetPrivateState (eStateLaunching);
+ switch (launch_type)
+ {
+ case eLaunchForkExec:
+ SetID(ProcessMacOSX::ForkChildForPTraceDebugging(path, argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, this, launch_err));
+ break;
+
+ case eLaunchPosixSpawn:
+ SetID(ProcessMacOSX::PosixSpawnChildForPTraceDebugging(path, argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, this, launch_err));
+ break;
+
+#if defined (__arm__)
+
+ case eLaunchSpringBoard:
+ {
+ const char *app_ext = strstr(path, ".app");
+ if (app_ext != NULL)
+ {
+ std::string app_bundle_path(path, app_ext + strlen(".app"));
+ return SBLaunchForDebug (app_bundle_path.c_str(), argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, launch_err);
+ }
+ }
+ break;
+
+#endif
+
+ default:
+ // Invalid launch
+ launch_err.SetErrorToGenericError ();
+ return LLDB_INVALID_PROCESS_ID;
+ }
+
+ lldb::pid_t pid = GetID();
+
+ if (pid == LLDB_INVALID_PROCESS_ID)
+ {
+ // If we don't have a valid process ID and no one has set the error,
+ // then return a generic error
+ if (launch_err.Success())
+ launch_err.SetErrorToGenericError ();
+ }
+ else
+ {
+ // Make sure we can get our task port before going any further
+ Task().GetTaskPortForProcessID (launch_err);
+
+ // If that goes well then kick off our exception thread
+ if (launch_err.Success())
+ Task().StartExceptionThread(launch_err);
+
+ if (launch_err.Success())
+ {
+ //m_path = path;
+// size_t i;
+// if (argv)
+// {
+// char const *arg;
+// for (i=0; (arg = argv[i]) != NULL; i++)
+// m_args.push_back(arg);
+// }
+
+ StartSTDIOThread();
+
+ if (launch_type == eLaunchPosixSpawn)
+ {
+
+ //SetState (eStateAttaching);
+ errno = 0;
+ if (::ptrace (PT_ATTACHEXC, pid, 0, 0) == 0)
+ launch_err.Clear();
+ else
+ launch_err.SetErrorToErrno();
+
+ if (launch_err.Fail() || log)
+ launch_err.PutToLog(log, "::ptrace (PT_ATTACHEXC, pid = %i, 0, 0 )", pid);
+
+ if (launch_err.Success())
+ m_flags.Set (eFlagsAttached);
+ else
+ SetPrivateState (eStateExited);
+ }
+ else
+ {
+ launch_err.Clear();
+ }
+ }
+ else
+ {
+ // We were able to launch the process, but not get its task port
+ // so now we need to make it sleep with da fishes.
+ SetID(LLDB_INVALID_PROCESS_ID);
+ ::ptrace (PT_KILL, pid, 0, 0 );
+ ::kill (pid, SIGCONT);
+ pid = LLDB_INVALID_PROCESS_ID;
+ }
+
+ }
+ return pid;
+}
+
+lldb::pid_t
+ProcessMacOSX::PosixSpawnChildForPTraceDebugging
+(
+ const char *path,
+ char const *argv[],
+ char const *envp[],
+ ArchSpec& arch_spec,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ ProcessMacOSX* process,
+ Error &err
+)
+{
+ posix_spawnattr_t attr;
+
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS);
+
+ Error local_err; // Errors that don't affect the spawning.
+ if (log)
+ log->Printf ("%s ( path='%s', argv=%p, envp=%p, process )", __FUNCTION__, path, argv, envp);
+ err.SetError( ::posix_spawnattr_init (&attr), eErrorTypePOSIX);
+ if (err.Fail() || log)
+ err.PutToLog(log, "::posix_spawnattr_init ( &attr )");
+ if (err.Fail())
+ return LLDB_INVALID_PROCESS_ID;
+
+ err.SetError( ::posix_spawnattr_setflags (&attr, POSIX_SPAWN_START_SUSPENDED), eErrorTypePOSIX);
+ if (err.Fail() || log)
+ err.PutToLog(log, "::posix_spawnattr_setflags ( &attr, POSIX_SPAWN_START_SUSPENDED )");
+ if (err.Fail())
+ return LLDB_INVALID_PROCESS_ID;
+
+#if !defined(__arm__)
+
+ // We don't need to do this for ARM, and we really shouldn't now that we
+ // have multiple CPU subtypes and no posix_spawnattr call that allows us
+ // to set which CPU subtype to launch...
+ cpu_type_t cpu = arch_spec.GetCPUType();
+ if (cpu != 0 && cpu != CPU_TYPE_ANY && cpu != LLDB_INVALID_CPUTYPE)
+ {
+ size_t ocount = 0;
+ err.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu, &ocount), eErrorTypePOSIX);
+ if (err.Fail() || log)
+ err.PutToLog(log, "::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %zu )", cpu, ocount);
+
+ if (err.Fail() != 0 || ocount != 1)
+ return LLDB_INVALID_PROCESS_ID;
+ }
+
+#endif
+
+ lldb_utility::PseudoTerminal pty;
+
+ posix_spawn_file_actions_t file_actions;
+ err.SetError( ::posix_spawn_file_actions_init (&file_actions), eErrorTypePOSIX);
+ int file_actions_valid = err.Success();
+ if (!file_actions_valid || log)
+ err.PutToLog(log, "::posix_spawn_file_actions_init ( &file_actions )");
+ Error stdio_err;
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ if (file_actions_valid)
+ {
+ // If the user specified any STDIO files, then use those
+ if (stdin_path || stdout_path || stderr_path)
+ {
+ process->SetSTDIOIsOurs(false);
+ if (stderr_path != NULL && stderr_path[0])
+ {
+ stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDERR_FILENO, stderr_path, O_RDWR, 0), eErrorTypePOSIX);
+ if (stdio_err.Fail() || log)
+ stdio_err.PutToLog(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDERR_FILENO, path = '%s', oflag = O_RDWR, mode = 0 )", stderr_path);
+ }
+
+ if (stdin_path != NULL && stdin_path[0])
+ {
+ stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDIN_FILENO, stdin_path, O_RDONLY, 0), eErrorTypePOSIX);
+ if (stdio_err.Fail() || log)
+ stdio_err.PutToLog(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDIN_FILENO, path = '%s', oflag = O_RDONLY, mode = 0 )", stdin_path);
+ }
+
+ if (stdout_path != NULL && stdout_path[0])
+ {
+ stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDOUT_FILENO, stdout_path, O_WRONLY, 0), eErrorTypePOSIX);
+ if (stdio_err.Fail() || log)
+ stdio_err.PutToLog(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDOUT_FILENO, path = '%s', oflag = O_WRONLY, mode = 0 )", stdout_path);
+ }
+ }
+ else
+ {
+ // The user did not specify any STDIO files, use a pseudo terminal.
+ // Callers can then access the file handles using the
+ // ProcessMacOSX::ReleaseChildFileDescriptors() function, otherwise
+ // this class will spawn a thread that tracks STDIO and buffers it.
+ process->SetSTDIOIsOurs(true);
+ char error_str[1024];
+ if (pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY, error_str, sizeof(error_str)))
+ {
+ const char* slave_name = pty.GetSlaveName(error_str, sizeof(error_str));
+ if (slave_name == NULL)
+ slave_name = "/dev/null";
+ stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDERR_FILENO, slave_name, O_RDWR|O_NOCTTY, 0), eErrorTypePOSIX);
+ if (stdio_err.Fail() || log)
+ stdio_err.PutToLog(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDERR_FILENO, path = '%s', oflag = O_RDWR|O_NOCTTY, mode = 0 )", slave_name);
+
+ stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDIN_FILENO, slave_name, O_RDONLY|O_NOCTTY, 0), eErrorTypePOSIX);
+ if (stdio_err.Fail() || log)
+ stdio_err.PutToLog(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDIN_FILENO, path = '%s', oflag = O_RDONLY|O_NOCTTY, mode = 0 )", slave_name);
+
+ stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDOUT_FILENO, slave_name, O_WRONLY|O_NOCTTY, 0), eErrorTypePOSIX);
+ if (stdio_err.Fail() || log)
+ stdio_err.PutToLog(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDOUT_FILENO, path = '%s', oflag = O_WRONLY|O_NOCTTY, mode = 0 )", slave_name);
+ }
+ else
+ {
+ if (error_str[0])
+ stdio_err.SetErrorString(error_str);
+ else
+ stdio_err.SetErrorString("Unable to open master side of pty for inferior.");
+ }
+
+ }
+ err.SetError( ::posix_spawnp (&pid, path, &file_actions, &attr, (char * const*)argv, (char * const*)envp), eErrorTypePOSIX);
+ if (err.Fail() || log)
+ err.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, &file_actions, &attr, argv, envp);
+
+ if (stdio_err.Success())
+ {
+ // If we have a valid process and we created the STDIO file handles,
+ // then remember them on our process class so we can spawn a STDIO
+ // thread and close them when we are done with them.
+ if (process != NULL && process->STDIOIsOurs())
+ {
+ int master_fd = pty.ReleaseMasterFileDescriptor ();
+ process->SetChildFileDescriptors (master_fd, master_fd, master_fd);
+ }
+ }
+ }
+ else
+ {
+ err.SetError( ::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), eErrorTypePOSIX);
+ if (err.Fail() || log)
+ err.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, NULL, &attr, argv, envp);
+ }
+
+ // We have seen some cases where posix_spawnp was returning a valid
+ // looking pid even when an error was returned, so clear it out
+ if (err.Fail())
+ pid = LLDB_INVALID_PROCESS_ID;
+
+ if (file_actions_valid)
+ {
+ local_err.SetError( ::posix_spawn_file_actions_destroy (&file_actions), eErrorTypePOSIX);
+ if (local_err.Fail() || log)
+ local_err.PutToLog(log, "::posix_spawn_file_actions_destroy ( &file_actions )");
+ }
+
+ return pid;
+}
+
+lldb::pid_t
+ProcessMacOSX::ForkChildForPTraceDebugging
+(
+ const char *path,
+ char const *argv[],
+ char const *envp[],
+ ArchSpec& arch_spec,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ ProcessMacOSX* process,
+ Error &launch_err
+)
+{
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+
+ if (stdin_path || stdout_path || stderr_path)
+ {
+ assert(!"TODO: ForkChildForPTraceDebugging doesn't currently support fork/exec with user file handles...");
+ }
+ else
+ {
+
+ // Use a fork that ties the child process's stdin/out/err to a pseudo
+ // terminal so we can read it in our ProcessMacOSX::STDIOThread
+ // as unbuffered io.
+ lldb_utility::PseudoTerminal pty;
+ char error_str[1024];
+ pid = pty.Fork(error_str, sizeof(error_str));
+
+ if (pid < 0)
+ {
+ launch_err.SetErrorString (error_str);
+ //--------------------------------------------------------------
+ // Error during fork.
+ //--------------------------------------------------------------
+ return pid;
+ }
+ else if (pid == 0)
+ {
+ //--------------------------------------------------------------
+ // Child process
+ //--------------------------------------------------------------
+ ::ptrace (PT_TRACE_ME, 0, 0, 0); // Debug this process
+ ::ptrace (PT_SIGEXC, 0, 0, 0); // Get BSD signals as mach exceptions
+
+ // If our parent is setgid, lets make sure we don't inherit those
+ // extra powers due to nepotism.
+ ::setgid (getgid ());
+
+ // Let the child have its own process group. We need to execute
+ // this call in both the child and parent to avoid a race condition
+ // between the two processes.
+ ::setpgid (0, 0); // Set the child process group to match its pid
+
+ // Sleep a bit to before the exec call
+ ::sleep (1);
+
+ // Turn this process into
+ ::execv (path, (char * const *)argv);
+ // Exit with error code. Child process should have taken
+ // over in above exec call and if the exec fails it will
+ // exit the child process below.
+ ::exit (127);
+ }
+ else
+ {
+ //--------------------------------------------------------------
+ // Parent process
+ //--------------------------------------------------------------
+ // Let the child have its own process group. We need to execute
+ // this call in both the child and parent to avoid a race condition
+ // between the two processes.
+ ::setpgid (pid, pid); // Set the child process group to match its pid
+
+ if (process != NULL)
+ {
+ // Release our master pty file descriptor so the pty class doesn't
+ // close it and so we can continue to use it in our STDIO thread
+ int master_fd = pty.ReleaseMasterFileDescriptor ();
+ process->SetChildFileDescriptors (master_fd, master_fd, master_fd);
+ }
+ }
+ }
+ return pid;
+}
+
+#if defined (__arm__)
+
+lldb::pid_t
+ProcessMacOSX::SBLaunchForDebug
+(
+ const char *path,
+ char const *argv[],
+ char const *envp[],
+ ArchSpec& arch_spec,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ Error &launch_err
+)
+{
+ // Clear out and clean up from any current state
+ Clear();
+
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path);
+
+ // Fork a child process for debugging
+ SetState(eStateLaunching);
+ m_pid = ProcessMacOSX::SBLaunchForDebug(path, argv, envp, this, launch_err);
+ if (m_pid != 0)
+ {
+ m_flags |= eFlagsUsingSBS;
+ //m_path = path;
+// size_t i;
+// char const *arg;
+// for (i=0; (arg = argv[i]) != NULL; i++)
+// m_args.push_back(arg);
+ Task().StartExceptionThread();
+ StartSTDIOThread();
+ SetState (eStateAttaching);
+ int err = ptrace (PT_ATTACHEXC, m_pid, 0, 0);
+ if (err == 0)
+ {
+ m_flags |= eFlagsAttached;
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "successfully attached to pid %d", m_pid);
+ }
+ else
+ {
+ SetState (eStateExited);
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "error: failed to attach to pid %d", m_pid);
+ }
+ }
+ return m_pid;
+}
+
+#include <servers/bootstrap.h>
+#include "CFBundle.h"
+#include "CFData.h"
+#include "CFString.h"
+
+lldb::pid_t
+ProcessMacOSX::SBLaunchForDebug
+(
+ const char *app_bundle_path,
+ char const *argv[],
+ char const *envp[],
+ ArchSpec& arch_spec,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ ProcessMacOSX* process,
+ Error &launch_err
+)
+{
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, app_bundle_path, process);
+ CFAllocatorRef alloc = kCFAllocatorDefault;
+ if (argv[0] == NULL)
+ return LLDB_INVALID_PROCESS_ID;
+
+ size_t argc = 0;
+ // Count the number of arguments
+ while (argv[argc] != NULL)
+ argc++;
+
+ // Enumerate the arguments
+ size_t first_launch_arg_idx = 1;
+ CFReleaser<CFMutableArrayRef> launch_argv;
+
+ if (argv[first_launch_arg_idx])
+ {
+ size_t launch_argc = argc > 0 ? argc - 1 : 0;
+ launch_argv.reset (::CFArrayCreateMutable (alloc, launch_argc, &kCFTypeArrayCallBacks));
+ size_t i;
+ char const *arg;
+ CFString launch_arg;
+ for (i=first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); i++)
+ {
+ launch_arg.reset(::CFStringCreateWithCString (alloc, arg, kCFStringEncodingUTF8));
+ if (launch_arg.get() != NULL)
+ CFArrayAppendValue(launch_argv.get(), launch_arg.get());
+ else
+ break;
+ }
+ }
+
+ // Next fill in the arguments dictionary. Note, the envp array is of the form
+ // Variable=value but SpringBoard wants a CF dictionary. So we have to convert
+ // this here.
+
+ CFReleaser<CFMutableDictionaryRef> launch_envp;
+
+ if (envp[0])
+ {
+ launch_envp.reset(::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+ const char *value;
+ int name_len;
+ CFString name_string, value_string;
+
+ for (int i = 0; envp[i] != NULL; i++)
+ {
+ value = strstr (envp[i], "=");
+
+ // If the name field is empty or there's no =, skip it. Somebody's messing with us.
+ if (value == NULL || value == envp[i])
+ continue;
+
+ name_len = value - envp[i];
+
+ // Now move value over the "="
+ value++;
+
+ name_string.reset(::CFStringCreateWithBytes(alloc, (const UInt8 *) envp[i], name_len, kCFStringEncodingUTF8, false));
+ value_string.reset(::CFStringCreateWithCString(alloc, value, kCFStringEncodingUTF8));
+ CFDictionarySetValue (launch_envp.get(), name_string.get(), value_string.get());
+ }
+ }
+
+ CFString stdout_cf_path;
+ CFString stderr_cf_path;
+ PseudoTerminal pty;
+
+ if (stdin_path || stdout_path || stderr_path)
+ {
+ process->SetSTDIOIsOurs(false);
+ if (stdout_path)
+ stdout_cf_path.SetFileSystemRepresentation (stdout_path);
+ if (stderr_path)
+ stderr_cf_path.SetFileSystemRepresentation (stderr_path);
+ }
+ else
+ {
+ process->SetSTDIOIsOurs(true);
+ PseudoTerminal::Error pty_err = pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY);
+ if (pty_err == PseudoTerminal::success)
+ {
+ const char* slave_name = pty.SlaveName();
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() successfully opened master pty, slave is %s", __FUNCTION__, slave_name);
+ if (slave_name && slave_name[0])
+ {
+ ::chmod (slave_name, S_IRWXU | S_IRWXG | S_IRWXO);
+ stdout_cf_path.SetFileSystemRepresentation (slave_name);
+ stderr_cf_path.(stdout_cf_path);
+ }
+ }
+ }
+
+ if (stdout_cf_path.get() == NULL)
+ stdout_cf_path.SetFileSystemRepresentation ("/dev/null");
+ if (stderr_cf_path.get() == NULL)
+ stderr_cf_path.SetFileSystemRepresentation ("/dev/null");
+
+ CFBundle bundle(app_bundle_path);
+ CFStringRef bundleIDCFStr = bundle.GetIdentifier();
+ std::string bundleID;
+ if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL)
+ {
+ struct stat app_bundle_stat;
+ if (::stat (app_bundle_path, &app_bundle_stat) < 0)
+ {
+ launch_err.SetError(errno, eErrorTypePOSIX);
+ launch_err.SetErrorStringWithFormat("%s: \"%s\".\n", launch_err.AsString(), app_bundle_path);
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() error: %s", __FUNCTION__, launch_err.AsCString());
+ }
+ else
+ {
+ launch_err.SetError(-1, eErrorTypeGeneric);
+ launch_err.SetErrorStringWithFormat("Failed to extract CFBundleIdentifier from %s.\n", app_bundle_path);
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() error: failed to extract CFBundleIdentifier from '%s'", __FUNCTION__, app_bundle_path);
+ }
+ return LLDB_INVALID_PROCESS_ID;
+ }
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s", __FUNCTION__, bundleID.c_str());
+
+
+ CFData argv_data(NULL);
+
+ if (launch_argv.get())
+ {
+ if (argv_data.Serialize(launch_argv.get(), kCFPropertyListBinaryFormat_v1_0) == NULL)
+ {
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() error: failed to serialize launch arg array...", __FUNCTION__);
+ return LLDB_INVALID_PROCESS_ID;
+ }
+ }
+
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() serialized launch arg array", __FUNCTION__);
+
+ // Find SpringBoard
+ SBSApplicationLaunchError sbs_error = 0;
+ sbs_error = SBSLaunchApplication ( bundleIDCFStr,
+ (CFURLRef)NULL, // openURL
+ launch_argv.get(),
+ launch_envp.get(), // CFDictionaryRef environment
+ stdout_cf_path.get(),
+ stderr_cf_path.get(),
+ SBSApplicationLaunchWaitForDebugger | SBSApplicationLaunchUnlockDevice);
+
+
+ launch_err.SetError(sbs_error, eErrorTypeSpringBoard);
+
+ if (sbs_error == SBSApplicationLaunchErrorSuccess)
+ {
+ static const useconds_t pid_poll_interval = 200000;
+ static const useconds_t pid_poll_timeout = 30000000;
+
+ useconds_t pid_poll_total = 0;
+
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ Boolean pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid);
+ // Poll until the process is running, as long as we are getting valid responses and the timeout hasn't expired
+ // A return PID of 0 means the process is not running, which may be because it hasn't been (asynchronously) started
+ // yet, or that it died very quickly (if you weren't using waitForDebugger).
+ while (!pid_found && pid_poll_total < pid_poll_timeout)
+ {
+ usleep (pid_poll_interval);
+ pid_poll_total += pid_poll_interval;
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() polling Springboard for pid for %s...", __FUNCTION__, bundleID.c_str());
+ pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid);
+ }
+
+ if (pid_found)
+ {
+ // If we have a valid process and we created the STDIO file handles,
+ // then remember them on our process class so we can spawn a STDIO
+ // thread and close them when we are done with them.
+ if (process != NULL && process->STDIOIsOurs())
+ {
+ // Release our master pty file descriptor so the pty class doesn't
+ // close it and so we can continue to use it in our STDIO thread
+ int master_fd = pty.ReleaseMasterFD();
+ process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
+ }
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() => pid = %4.4x", __FUNCTION__, pid);
+ }
+ else
+ {
+ LogError("failed to lookup the process ID for CFBundleIdentifier %s.", bundleID.c_str());
+ }
+ return pid;
+ }
+
+ LogError("unable to launch the application with CFBundleIdentifier '%s' sbs_error = %u", bundleID.c_str(), sbs_error);
+ return LLDB_INVALID_PROCESS_ID;
+}
+
+#endif // #if defined (__arm__)
+
+
+#include "MachThreadContext_x86_64.h"
+#include "MachThreadContext_i386.h"
+#include "MachThreadContext_arm.h"
+
+void
+ProcessMacOSX::Initialize()
+{
+ static bool g_initialized = false;
+
+ if (g_initialized == false)
+ {
+ g_initialized = true;
+
+ MachThreadContext_x86_64::Initialize();
+ MachThreadContext_i386::Initialize();
+ MachThreadContext_arm::Initialize();
+ PluginManager::RegisterPlugin (GetPluginNameStatic(),
+ GetPluginDescriptionStatic(),
+ CreateInstance);
+
+ Log::Callbacks log_callbacks = {
+ ProcessMacOSXLog::DisableLog,
+ ProcessMacOSXLog::EnableLog,
+ ProcessMacOSXLog::ListLogCategories
+ };
+
+ Log::RegisterLogChannel (ProcessMacOSX::GetPluginNameStatic(), log_callbacks);
+
+
+ }
+}
+
+
+
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.h b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.h
new file mode 100644
index 00000000000..8388d4e46fe
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSX.h
@@ -0,0 +1,490 @@
+//===-- ProcessMacOSX.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_MacOSXProcess_H_
+#define liblldb_MacOSXProcess_H_
+
+// C Includes
+
+// C++ Includes
+#include <list>
+
+// Other libraries and framework includes
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Core/ThreadSafeValue.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+
+// Project includes
+#include "MachTask.h"
+#include "MachException.h"
+
+typedef enum PDLaunch
+{
+ eLaunchDefault = 0,
+ eLaunchPosixSpawn,
+ eLaunchForkExec,
+#if defined (__arm__)
+ eLaunchSpringBoard,
+#endif
+} PDLaunchType;
+
+
+
+class ThreadMacOSX;
+class MachThreadContext;
+
+class ProcessMacOSX :
+ public lldb_private::Process
+{
+public:
+ friend class ThreadMacOSX;
+ friend class MachTask;
+
+ typedef MachThreadContext* (*CreateArchCalback) (const lldb_private::ArchSpec &arch_spec, ThreadMacOSX &thread);
+
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ static Process*
+ CreateInstance (lldb_private::Target& target, lldb_private::Listener &listener);
+
+ static void
+ Initialize();
+
+ static void
+ Terminate();
+
+ static const char *
+ GetPluginNameStatic();
+
+ static const char *
+ GetPluginDescriptionStatic();
+
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ ProcessMacOSX(lldb_private::Target& target, lldb_private::Listener &listener);
+
+ virtual
+ ~ProcessMacOSX();
+
+ //------------------------------------------------------------------
+ // Check if a given Process
+ //------------------------------------------------------------------
+ virtual bool
+ CanDebug (lldb_private::Target &target);
+
+ //------------------------------------------------------------------
+ // Creating a new process, or attaching to an existing one
+ //------------------------------------------------------------------
+ virtual lldb_private::Error
+ WillLaunch (lldb_private::Module* module);
+
+ virtual lldb_private::Error
+ DoLaunch (lldb_private::Module* module,
+ char const *argv[], // Can be NULL
+ char const *envp[], // Can be NULL
+ const char *stdin_path, // Can be NULL
+ const char *stdout_path, // Can be NULL
+ const char *stderr_path); // Can be NULL
+
+ virtual void
+ DidLaunch ();
+
+ virtual lldb_private::Error
+ WillAttach (lldb::pid_t pid);
+
+ virtual lldb_private::Error
+ DoAttach (lldb::pid_t pid);
+
+ virtual void
+ DidAttach ();
+
+ //------------------------------------------------------------------
+ // PluginInterface protocol
+ //------------------------------------------------------------------
+ virtual const char *
+ GetPluginName();
+
+ virtual const char *
+ GetShortPluginName();
+
+ virtual uint32_t
+ GetPluginVersion();
+
+ virtual void
+ GetPluginCommandHelp (const char *command, lldb_private::Stream *strm);
+
+ virtual lldb_private::Error
+ ExecutePluginCommand (lldb_private::Args &command, lldb_private::Stream *strm);
+
+ virtual lldb_private::Log *
+ EnablePluginLogging (lldb_private::Stream *strm, lldb_private::Args &command);
+
+ //------------------------------------------------------------------
+ // Process Control
+ //------------------------------------------------------------------
+ virtual lldb_private::Error
+ DoResume ();
+
+ virtual lldb_private::Error
+ DoHalt ();
+
+ virtual lldb_private::Error
+ WillDetach ();
+
+ virtual lldb_private::Error
+ DoDetach ();
+
+ virtual lldb_private::Error
+ DoSignal (int signal);
+
+ virtual lldb_private::Error
+ DoDestroy ();
+
+ virtual void
+ RefreshStateAfterStop();
+
+ //------------------------------------------------------------------
+ // Process Queries
+ //------------------------------------------------------------------
+ virtual bool
+ IsAlive ();
+
+ virtual lldb::addr_t
+ GetImageInfoAddress();
+
+ //------------------------------------------------------------------
+ // Process Memory
+ //------------------------------------------------------------------
+ virtual size_t
+ DoReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error);
+
+ virtual size_t
+ DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size, lldb_private::Error &error);
+
+ virtual lldb::addr_t
+ DoAllocateMemory (size_t size, uint32_t permissions, lldb_private::Error &error);
+
+ virtual lldb_private::Error
+ DoDeallocateMemory (lldb::addr_t ptr);
+
+ //------------------------------------------------------------------
+ // Process STDIO
+ //------------------------------------------------------------------
+ virtual size_t
+ GetSTDOUT (char *buf, size_t buf_size, lldb_private::Error &error);
+
+ virtual size_t
+ GetSTDERR (char *buf, size_t buf_size, lldb_private::Error &error);
+
+ virtual size_t
+ PutSTDIN (const char *buf, size_t buf_size, lldb_private::Error &error);
+
+ //----------------------------------------------------------------------
+ // Process Breakpoints
+ //----------------------------------------------------------------------
+ virtual size_t
+ GetSoftwareBreakpointTrapOpcode (lldb_private::BreakpointSite *bp_site);
+
+ //----------------------------------------------------------------------
+ // Process Breakpoints
+ //----------------------------------------------------------------------
+ virtual lldb_private::Error
+ EnableBreakpoint (lldb_private::BreakpointSite *bp_site);
+
+ virtual lldb_private::Error
+ DisableBreakpoint (lldb_private::BreakpointSite *bp_site);
+
+ //----------------------------------------------------------------------
+ // Process Watchpoints
+ //----------------------------------------------------------------------
+ virtual lldb_private::Error
+ EnableWatchpoint (lldb_private::WatchpointLocation *wp_loc);
+
+ virtual lldb_private::Error
+ DisableWatchpoint (lldb_private::WatchpointLocation *wp_loc);
+
+ virtual lldb::ByteOrder
+ GetByteOrder () const;
+
+ virtual lldb_private::DynamicLoader *
+ GetDynamicLoader ();
+
+ static void
+ AddArchCreateCallback(const lldb_private::ArchSpec& arch_spec,
+ ProcessMacOSX::CreateArchCalback callback);
+
+protected:
+
+ bool m_stdio_ours; // True if we created and own the child STDIO file handles, false if they were supplied to us and owned by someone else
+ int m_child_stdin;
+ int m_child_stdout;
+ int m_child_stderr;
+ MachTask m_task; // The mach task for this process
+ lldb_private::Flags m_flags; // Process specific flags (see eFlags enums)
+ lldb::thread_t m_stdio_thread; // Thread ID for the thread that watches for child process stdio
+ lldb_private::Mutex m_stdio_mutex; // Multithreaded protection for stdio
+ std::string m_stdout_data;
+ MachException::Message::collection m_exception_messages; // A collection of exception messages caught when listening to the exception port
+ lldb_private::Mutex m_exception_messages_mutex; // Multithreaded protection for m_exception_messages
+ lldb_private::ArchSpec m_arch_spec;
+ std::auto_ptr<lldb_private::DynamicLoader> m_dynamic_loader_ap;
+// lldb::thread_t m_wait_thread;
+ lldb::ByteOrder m_byte_order;
+
+ //----------------------------------------------------------------------
+ // Child process control
+ //----------------------------------------------------------------------
+ lldb::pid_t
+ LaunchForDebug (const char *path,
+ char const *argv[],
+ char const *envp[],
+ lldb_private::ArchSpec& arch_spec,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ PDLaunchType launch_type,
+ lldb_private::Error &launch_err);
+
+ static lldb::pid_t
+ ForkChildForPTraceDebugging (const char *path,
+ char const *argv[],
+ char const *envp[],
+ lldb_private::ArchSpec& arch_spec,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ ProcessMacOSX* process,
+ lldb_private::Error &launch_err);
+
+ static lldb::pid_t
+ PosixSpawnChildForPTraceDebugging (const char *path,
+ char const *argv[],
+ char const *envp[],
+ lldb_private::ArchSpec& arch_spec,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ ProcessMacOSX* process,
+ lldb_private::Error &launch_err);
+
+#if defined (__arm__)
+ lldb::pid_t
+ SBLaunchForDebug (const char *path,
+ char const *argv[],
+ char const *envp[],
+ lldb_private::ArchSpec& arch_spec,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ lldb_private::Error &launch_err);
+
+ static lldb::pid_t
+ SBLaunchForDebug (const char *path,
+ char const *argv[],
+ char const *envp[],
+ lldb_private::ArchSpec& arch_spec,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ ProcessMacOSX* process,
+ lldb_private::Error &launch_err);
+#endif
+
+ //----------------------------------------------------------------------
+ // Exception thread functions
+ //----------------------------------------------------------------------
+ bool
+ StartSTDIOThread ();
+
+ void
+ StopSTDIOThread (bool close_child_fds);
+
+ static void *
+ STDIOThread (void *arg);
+
+ void
+ ExceptionMessageReceived (const MachException::Message& exceptionMessage);
+
+ void
+ ExceptionMessageBundleComplete ();
+
+ //----------------------------------------------------------------------
+ // Accessors
+ //----------------------------------------------------------------------
+ bool
+ ProcessIDIsValid ( ) const;
+
+ MachTask&
+ Task() { return m_task; }
+
+ const MachTask&
+ Task() const { return m_task; }
+
+ bool
+ IsRunning ( lldb::StateType state )
+ {
+ return state == lldb::eStateRunning || IsStepping(state);
+ }
+
+ bool
+ IsStepping ( lldb::StateType state)
+ {
+ return state == lldb::eStateStepping;
+ }
+ bool
+ CanResume ( lldb::StateType state)
+ {
+ return state == lldb::eStateStopped;
+ }
+
+ bool
+ HasExited (lldb::StateType state)
+ {
+ return state == lldb::eStateExited;
+ }
+
+ void
+ SetChildFileDescriptors (int stdin_fileno, int stdout_fileno, int stderr_fileno)
+ {
+ m_child_stdin = stdin_fileno;
+ m_child_stdout = stdout_fileno;
+ m_child_stderr = stderr_fileno;
+ }
+
+ int
+ GetStdinFileDescriptor () const
+ {
+ return m_child_stdin;
+ }
+
+ int
+ GetStdoutFileDescriptor () const
+ {
+ return m_child_stdout;
+ }
+ int
+ GetStderrFileDescriptor () const
+ {
+ return m_child_stderr;
+ }
+ bool
+ ReleaseChildFileDescriptors ( int *stdin_fileno, int *stdout_fileno, int *stderr_fileno );
+
+ void
+ AppendSTDOUT (const char* s, size_t len);
+
+ void
+ CloseChildFileDescriptors ()
+ {
+ if (m_child_stdin >= 0)
+ {
+ ::close (m_child_stdin);
+ m_child_stdin = -1;
+ }
+ if (m_child_stdout >= 0)
+ {
+ ::close (m_child_stdout);
+ m_child_stdout = -1;
+ }
+ if (m_child_stderr >= 0)
+ {
+ ::close (m_child_stderr);
+ m_child_stderr = -1;
+ }
+ }
+
+ bool
+ ProcessUsingSpringBoard() const
+ {
+ return m_flags.IsSet(eFlagsUsingSBS);
+ }
+
+ lldb_private::ArchSpec&
+ GetArchSpec()
+ {
+ return m_arch_spec;
+ }
+ const lldb_private::ArchSpec&
+ GetArchSpec() const
+ {
+ return m_arch_spec;
+ }
+
+ CreateArchCalback
+ GetArchCreateCallback();
+
+ enum
+ {
+ eFlagsNone = 0,
+ eFlagsAttached = (1 << 0),
+ eFlagsUsingSBS = (1 << 1)
+ };
+
+ void
+ Clear ( );
+
+ lldb_private::Error
+ ReplyToAllExceptions();
+
+ lldb_private::Error
+ PrivateResume ( lldb::tid_t tid);
+
+ lldb_private::Flags &
+ GetFlags ()
+ {
+ return m_flags;
+ }
+
+ const lldb_private::Flags &
+ GetFlags () const
+ {
+ return m_flags;
+ }
+
+ bool
+ STDIOIsOurs() const
+ {
+ return m_stdio_ours;
+ }
+
+ void
+ SetSTDIOIsOurs(bool b)
+ {
+ m_stdio_ours = b;
+ }
+
+ uint32_t
+ UpdateThreadListIfNeeded ();
+
+private:
+
+ void
+ DidLaunchOrAttach ();
+
+ lldb_private::Error
+ DoSIGSTOP (bool clear_all_breakpoints);
+
+ lldb_private::Error
+ WillLaunchOrAttach ();
+
+// static void *
+// WaitForChildProcessToExit (void *pid_ptr);
+//
+//
+ //------------------------------------------------------------------
+ // For ProcessMacOSX only
+ //------------------------------------------------------------------
+ DISALLOW_COPY_AND_ASSIGN (ProcessMacOSX);
+
+};
+
+#endif // liblldb_MacOSXProcess_H_
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.cpp
new file mode 100644
index 00000000000..4bfd1ff466e
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.cpp
@@ -0,0 +1,124 @@
+//===-- ProcessMacOSXLog.cpp ------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProcessMacOSXLog.h"
+
+#include "lldb/Core/Args.h"
+#include "lldb/Core/StreamFile.h"
+
+#include "ProcessMacOSX.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+
+static Log* g_log = NULL; // Leak for now as auto_ptr was being cleaned up
+ // by global constructors before other threads
+ // were done with it.
+Log *
+ProcessMacOSXLog::GetLogIfAllCategoriesSet (uint32_t mask)
+{
+ Log *log = g_log;
+ if (log && mask)
+ {
+ uint32_t log_mask = log->GetMask().GetAllFlagBits();
+ if ((log_mask & mask) != mask)
+ return NULL;
+ }
+ return log;
+}
+
+void
+ProcessMacOSXLog::DisableLog ()
+{
+ if (g_log)
+ {
+ delete g_log;
+ g_log = NULL;
+ }
+}
+
+Log *
+ProcessMacOSXLog::EnableLog (StreamSP &log_stream_sp, uint32_t log_options, Args &args, Stream *feedback_strm)
+{
+ DisableLog ();
+ g_log = new Log (log_stream_sp);
+ if (g_log)
+ {
+ uint32_t flag_bits = 0;
+ bool got_unknown_category = false;
+ const size_t argc = args.GetArgumentCount();
+ for (size_t i=0; i<argc; ++i)
+ {
+ const char *arg = args.GetArgumentAtIndex(i);
+
+ if (::strcasecmp (arg, "all") == 0 ) flag_bits |= PD_LOG_ALL;
+ else if (::strcasestr (arg, "break") == arg ) flag_bits |= PD_LOG_BREAKPOINTS;
+ else if (::strcasecmp (arg, "default") == 0 ) flag_bits |= PD_LOG_DEFAULT;
+ else if (::strcasestr (arg, "exc") == arg ) flag_bits |= PD_LOG_EXCEPTIONS;
+ else if (::strcasecmp (arg, "memory") == 0 ) flag_bits |= PD_LOG_MEMORY;
+ else if (::strcasecmp (arg, "data-short") == 0 ) flag_bits |= PD_LOG_MEMORY_DATA_SHORT;
+ else if (::strcasecmp (arg, "data-long") == 0 ) flag_bits |= PD_LOG_MEMORY_DATA_LONG;
+ else if (::strcasecmp (arg, "protections")== 0 ) flag_bits |= PD_LOG_MEMORY_PROTECTIONS;
+ else if (::strcasecmp (arg, "process") == 0 ) flag_bits |= PD_LOG_PROCESS;
+ else if (::strcasecmp (arg, "step") == 0 ) flag_bits |= PD_LOG_STEP;
+ else if (::strcasecmp (arg, "task") == 0 ) flag_bits |= PD_LOG_TASK;
+ else if (::strcasecmp (arg, "thread") == 0 ) flag_bits |= PD_LOG_THREAD;
+ else if (::strcasecmp (arg, "verbose") == 0 ) flag_bits |= PD_LOG_VERBOSE;
+ else if (::strcasestr (arg, "watch") == arg ) flag_bits |= PD_LOG_WATCHPOINTS;
+ else
+ {
+ feedback_strm->Printf("error: unrecognized log category '%s'\n", arg);
+ if (got_unknown_category == false)
+ {
+ got_unknown_category = true;
+ ListLogCategories (feedback_strm);
+ }
+ }
+ }
+ if (flag_bits == 0)
+ flag_bits = PD_LOG_DEFAULT;
+ g_log->GetMask().SetAllFlagBits(flag_bits);
+ g_log->GetOptions().SetAllFlagBits(log_options);
+ }
+ return g_log;
+}
+
+void
+ProcessMacOSXLog::ListLogCategories (Stream *strm)
+{
+ strm->Printf("Logging categories for '%s':\n"
+ "\tall - turn on all available logging categories\n"
+ "\tbreak - log breakpoints\n"
+ "\tdefault - enable the default set of logging categories for liblldb\n"
+ "\tmemory - log memory reads and writes\n"
+ "\tdata-short - log memory bytes for memory reads and writes for short transactions only\n"
+ "\tdata-long - log memory bytes for memory reads and writes for all transactions\n"
+ "\tprocess - log process events and activities\n"
+ "\tprotections - log memory protections\n"
+ "\ttask - log mach task calls\n"
+ "\tthread - log thread events and activities\n"
+ "\tstep - log step related activities\n"
+ "\tverbose - enable verbose loggging\n"
+ "\twatch - log watchpoint related activities\n", ProcessMacOSX::GetPluginNameStatic());
+}
+
+
+void
+ProcessMacOSXLog::LogIf (uint32_t mask, const char *format, ...)
+{
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (mask);
+ if (log)
+ {
+ va_list args;
+ va_start (args, format);
+ log->VAPrintf (format, args);
+ va_end (args);
+ }
+}
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.h b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.h
new file mode 100644
index 00000000000..cb2a4e8ee98
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXLog.h
@@ -0,0 +1,62 @@
+//===-- ProcessMacOSXLog.h --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcessMacOSXLog_h_
+#define liblldb_ProcessMacOSXLog_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+
+// Project includes
+#include "lldb/Core/Log.h"
+
+#define PD_LOG_VERBOSE (1u << 0)
+#define PD_LOG_PROCESS (1u << 1)
+#define PD_LOG_THREAD (1u << 2)
+#define PD_LOG_EXCEPTIONS (1u << 3)
+#define PD_LOG_MEMORY (1u << 4) // Log memory reads/writes calls
+#define PD_LOG_MEMORY_DATA_SHORT (1u << 5) // Log short memory reads/writes bytes
+#define PD_LOG_MEMORY_DATA_LONG (1u << 6) // Log all memory reads/writes bytes
+#define PD_LOG_MEMORY_PROTECTIONS (1u << 7) // Log memory protection changes
+#define PD_LOG_BREAKPOINTS (1u << 8)
+#define PD_LOG_WATCHPOINTS (1u << 9)
+#define PD_LOG_STEP (1u << 10)
+#define PD_LOG_TASK (1u << 11)
+#define PD_LOG_ALL (UINT32_MAX)
+#define PD_LOG_DEFAULT (PD_LOG_PROCESS |\
+ PD_LOG_TASK |\
+ PD_LOG_THREAD |\
+ PD_LOG_EXCEPTIONS |\
+ PD_LOG_MEMORY |\
+ PD_LOG_MEMORY_DATA_SHORT |\
+ PD_LOG_BREAKPOINTS |\
+ PD_LOG_WATCHPOINTS |\
+ PD_LOG_STEP )
+
+class ProcessMacOSXLog
+{
+public:
+ static lldb_private::Log *
+ GetLogIfAllCategoriesSet(uint32_t mask = 0);
+
+ static void
+ DisableLog ();
+
+ static lldb_private::Log *
+ EnableLog (lldb::StreamSP &log_stream_sp, uint32_t log_options, lldb_private::Args &args, lldb_private::Stream *feedback_strm);
+
+ static void
+ ListLogCategories (lldb_private::Stream *strm);
+
+ static void
+ LogIf (uint32_t mask, const char *format, ...);
+};
+
+#endif // liblldb_ProcessMacOSXLog_h_
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.cpp
new file mode 100644
index 00000000000..835d003a9b2
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.cpp
@@ -0,0 +1,1819 @@
+//===-- ProcessMacOSXRemote.cpp ---------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//----------------------------------------------------------------------
+//
+// ProcessMacOSXRemote.cpp
+// liblldb
+//
+// Created by Greg Clayton on 4/21/09.
+//
+//
+//----------------------------------------------------------------------
+
+// C Includes
+#include <errno.h>
+
+// C++ Includes
+//#include <algorithm>
+//#include <map>
+
+// Other libraries and framework includes
+
+// Project includes
+#include "ProcessMacOSXRemote.h"
+#include "ProcessMacOSXLog.h"
+#include "ThreadMacOSX.h"
+
+Process*
+ProcessMacOSXRemote::CreateInstance (Target &target)
+{
+ return new ProcessMacOSXRemote (target);
+}
+
+bool
+ProcessMacOSXRemote::CanDebug(Target &target)
+{
+ // For now we are just making sure the file exists for a given module
+ ModuleSP exe_module_sp(target.GetExecutableModule());
+ if (exe_module_sp.get())
+ return exe_module_sp->GetFileSpec().Exists();
+ return false;
+}
+
+//----------------------------------------------------------------------
+// ProcessMacOSXRemote constructor
+//----------------------------------------------------------------------
+ProcessMacOSXRemote::ProcessMacOSXRemote(Target& target) :
+ Process (target),
+ m_flags (0),
+ m_arch_spec (),
+ m_dynamic_loader_ap (),
+ m_byte_order(eByteOrderInvalid)
+{
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+ProcessMacOSXRemote::~DCProcessMacOSXRemote()
+{
+ Clear();
+}
+
+//----------------------------------------------------------------------
+// Process Control
+//----------------------------------------------------------------------
+lldb::pid_t
+ProcessMacOSXRemote::DoLaunch
+(
+ Module* module,
+ char const *argv[],
+ char const *envp[],
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path
+)
+{
+// ::LogSetBitMask (PD_LOG_DEFAULT);
+// ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD);
+// ::LogSetLogFile ("/dev/stdout");
+
+ ObjectFile * object_file = module->GetObjectFile();
+ if (object_file)
+ {
+ char exec_file_path[PATH_MAX];
+ FileSpec* file_spec_ptr = object_file->GetFileSpec();
+ if (file_spec_ptr)
+ file_spec_ptr->GetPath(exec_file_path, sizeof(exec_file_path));
+
+ ArchSpec arch_spec(module->GetArchitecture());
+
+ switch (arch_spec.GetCPUType())
+ {
+
+ }
+ // Set our user ID to our process ID.
+ SetID(LaunchForDebug(exec_file_path, argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, eLaunchDefault, GetError()));
+ }
+ else
+ {
+ // Set our user ID to an invalid process ID.
+ SetID(LLDB_INVALID_PROCESS_ID);
+ GetError().SetErrorToGenericError ();
+ GetError().SetErrorStringWithFormat ("Failed to get object file from '%s' for arch %s.\n", module->GetFileSpec().GetFilename().AsCString(), module->GetArchitecture().AsCString());
+ }
+
+ // Return the process ID we have
+ return GetID();
+}
+
+lldb::pid_t
+ProcessMacOSXRemote::DoAttach (lldb::pid_t attach_pid)
+{
+ // Set our user ID to the attached process ID (which can be invalid if
+ // the attach fails
+ lldb::pid_t pid = AttachForDebug(attach_pid);
+ SetID(pid);
+
+// if (pid != LLDB_INVALID_PROCESS_ID)
+// {
+// // Wait for a process stopped event, but don't consume it
+// if (WaitForEvents(LLDB_EVENT_STOPPED, NULL, 30))
+// {
+// }
+// }
+//
+ // Return the process ID we have
+ return pid;
+}
+
+
+void
+ProcessMacOSXRemote::DidLaunch ()
+{
+ if (GetID() == LLDB_INVALID_PROCESS_ID)
+ {
+ m_dynamic_loader_ap.reset();
+ }
+ else
+ {
+ Module * exe_module = GetTarget().GetExecutableModule ().get();
+ assert(exe_module);
+ ObjectFile *exe_objfile = exe_module->GetObjectFile();
+ assert(exe_objfile);
+ m_byte_order = exe_objfile->GetByteOrder();
+ assert(m_byte_order != eByteOrderInvalid);
+ // Install a signal handler so we can catch when our child process
+ // dies and set the exit status correctly.
+ m_wait_thread = Host::ThreadCreate (ProcessMacOSXRemote::WaitForChildProcessToExit, &m_uid, &m_error);
+ if (m_wait_thread != LLDB_INVALID_HOST_THREAD)
+ {
+ // Don't need to get the return value of this thread, so just let
+ // it clean up after itself when it dies.
+ Host::ThreadDetach (m_wait_thread, NULL);
+ }
+ m_dynamic_loader_ap.reset(DynamicLoader::FindPlugin(this, "macosx-dyld"));
+ }
+
+}
+
+void
+ProcessMacOSXRemote::DidAttach ()
+{
+ DidLaunch ();
+ m_need_to_run_did_attach = true;
+}
+
+bool
+ProcessMacOSXRemote::DoResume ()
+{
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSXRemote::Resume()");
+ State state = GetState();
+
+ if (CanResume(state))
+ {
+ PrivateResume(LLDB_INVALID_THREAD_ID);
+ }
+ else if (state == eStateRunning)
+ {
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "Resume() - task 0x%x is running, ignoring...", m_task.TaskPort());
+ GetError().Clear();
+
+ }
+ else
+ {
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "Resume() - task 0x%x can't continue, ignoring...", m_task.TaskPort());
+ GetError().SetError(UINT_MAX, Error::Generic);
+ }
+
+ return GetError().Success();
+}
+
+size_t
+ProcessMacOSXRemote::GetSoftwareBreakpointTrapOpcode (BreakpointSite *bp_site)
+{
+ ModuleSP exe_module_sp(GetTarget().GetExecutableModule());
+ if (exe_module_sp.get())
+ {
+ const ArchSpec &exe_arch = exe_module_sp->GetArchitecture();
+ const uint8_t *trap_opcode = NULL;
+ uint32_t trap_opcode_size = 0;
+
+ static const uint8_t g_arm_breakpoint_opcode[] = { 0xFE, 0xDE, 0xFF, 0xE7 };
+ //static const uint8_t g_thumb_breakpooint_opcode[] = { 0xFE, 0xDE };
+ static const uint8_t g_ppc_breakpoint_opcode[] = { 0x7F, 0xC0, 0x00, 0x08 };
+ static const uint8_t g_i386_breakpoint_opcode[] = { 0xCC };
+
+ switch (exe_arch.GetCPUType())
+ {
+ case CPU_TYPE_ARM:
+ // TODO: fill this in for ARM. We need to dig up the symbol for
+ // the address in the breakpoint locaiton and figure out if it is
+ // an ARM or Thumb breakpoint.
+ trap_opcode = g_arm_breakpoint_opcode;
+ trap_opcode_size = sizeof(g_arm_breakpoint_opcode);
+ break;
+
+ case CPU_TYPE_POWERPC:
+ case CPU_TYPE_POWERPC64:
+ trap_opcode = g_ppc_breakpoint_opcode;
+ trap_opcode_size = sizeof(g_ppc_breakpoint_opcode);
+ break;
+
+ case CPU_TYPE_I386:
+ case CPU_TYPE_X86_64:
+ trap_opcode = g_i386_breakpoint_opcode;
+ trap_opcode_size = sizeof(g_i386_breakpoint_opcode);
+ break;
+
+ default:
+ assert(!"Unhandled architecture in ProcessMacOSXRemote::GetSoftwareBreakpointTrapOpcode()");
+ return 0;
+ }
+
+ if (trap_opcode && trap_opcode_size)
+ {
+ if (bp_loc->SetTrapOpcode(trap_opcode, trap_opcode_size))
+ return trap_opcode_size;
+ }
+ }
+ // No executable yet, so we can't tell what the breakpoint opcode will be.
+ return 0;
+}
+uint32_t
+ProcessMacOSXRemote::UpdateThreadListIfNeeded ()
+{
+ // locker will keep a mutex locked until it goes out of scope
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_THREAD);
+ if (log && log->GetMask().IsSet(PD_LOG_VERBOSE))
+ log->Printf ("ProcessMacOSXRemote::%s (pid = %4.4x)", __FUNCTION__, GetID());
+
+ const uint32_t stop_id = GetStopID();
+ if (m_thread_list.GetSize() == 0 || stop_id != m_thread_list.GetID())
+ {
+ m_thread_list.SetID (stop_id);
+ thread_array_t thread_list = NULL;
+ mach_msg_type_number_t thread_list_count = 0;
+ task_t task = Task().TaskPort();
+ Error err(::task_threads (task, &thread_list, &thread_list_count), Error::MachKernel);
+
+ if (log || err.Fail())
+ err.Log(log, "::task_threads ( task = 0x%4.4x, thread_list => %p, thread_list_count => %u )", task, thread_list, thread_list_count);
+
+ if (err.GetError() == KERN_SUCCESS && thread_list_count > 0)
+ {
+ ThreadList curr_thread_list;
+
+ size_t idx;
+ // Iterator through the current thread list and see which threads
+ // we already have in our list (keep them), which ones we don't
+ // (add them), and which ones are not around anymore (remove them).
+ for (idx = 0; idx < thread_list_count; ++idx)
+ {
+ const lldb::tid_t tid = thread_list[idx];
+ ThreadSP thread_sp(m_thread_list.FindThreadByID (tid));
+ if (thread_sp.get() == NULL)
+ thread_sp.reset (new ThreadMacOSX (this, tid));
+ curr_thread_list.AddThread(thread_sp);
+ }
+
+ m_thread_list = curr_thread_list;
+
+ // Free the vm memory given to us by ::task_threads()
+ vm_size_t thread_list_size = (vm_size_t) (thread_list_count * sizeof (lldb::tid_t));
+ ::vm_deallocate (::mach_task_self(),
+ (vm_address_t)thread_list,
+ thread_list_size);
+ }
+ }
+ return m_thread_list.GetSize();
+}
+
+bool
+ProcessMacOSXRemote::ShouldStop ()
+{
+ // If we are attaching, let our dynamic loader plug-in know so it can get
+ // an initial list of shared libraries.
+ if (m_need_to_run_did_attach && m_dynamic_loader_ap.get())
+ {
+ m_need_to_run_did_attach = false;
+ m_dynamic_loader_ap->DidAttach();
+ }
+
+ // We must be attaching if we don't already have a valid architecture
+ if (!m_arch_spec.IsValid())
+ {
+ Module *exe_module = GetTarget().GetExecutableModule().get();
+ if (exe_module)
+ m_arch_spec = exe_module->GetArchitecture();
+ }
+ // Let all threads recover from stopping and do any clean up based
+ // on the previous thread state (if any).
+ UpdateThreadListIfNeeded ();
+
+ if (m_thread_list.ShouldStop())
+ {
+ // Let each thread know of any exceptions
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS);
+ task_t task = m_task.TaskPort();
+ size_t i;
+ for (i=0; i<m_exception_messages.size(); ++i)
+ {
+ // Let the thread list figure use the ProcessMacOSXRemote to forward all exceptions
+ // on down to each thread.
+ if (m_exception_messages[i].state.task_port == task)
+ {
+ ThreadSP thread_sp(m_thread_list.FindThreadByID(m_exception_messages[i].state.thread_port));
+ if (thread_sp.get())
+ {
+ ThreadMacOSX *macosx_thread = (ThreadMacOSX *)thread_sp.get();
+ macosx_thread->NotifyException (m_exception_messages[i].state);
+ }
+ }
+ if (log)
+ m_exception_messages[i].Log(log);
+ }
+ return true;
+ }
+ return false;
+}
+
+bool
+ProcessMacOSXRemote::DoHalt ()
+{
+ return Kill (SIGINT);
+}
+
+bool
+ProcessMacOSXRemote::WillDetach ()
+{
+ State state = GetState();
+
+ if (IsRunning(state))
+ {
+ m_error.SetErrorToGenericError();
+ m_error.SetErrorString("Process must be stopped in order to detach.");
+ return false;
+ }
+ return true;
+}
+
+bool
+ProcessMacOSXRemote::DoDetach ()
+{
+ m_use_public_queue = false;
+ bool success = Detach();
+ m_use_public_queue = true;
+ if (success)
+ SetState (eStateDetached);
+ return success;
+}
+
+bool
+ProcessMacOSXRemote::DoKill (int signal)
+{
+ return Kill (signal);
+}
+
+
+//------------------------------------------------------------------
+// Thread Queries
+//------------------------------------------------------------------
+
+Thread *
+ProcessMacOSXRemote::GetCurrentThread ()
+{
+ return m_thread_list.GetCurrentThread().get();
+}
+
+ByteOrder
+ProcessMacOSXRemote::GetByteOrder () const
+{
+ return m_byte_order;
+}
+
+
+
+//------------------------------------------------------------------
+// Process Queries
+//------------------------------------------------------------------
+
+bool
+ProcessMacOSXRemote::IsAlive ()
+{
+ return MachTask::IsValid (Task().TaskPort());
+}
+
+bool
+ProcessMacOSXRemote::IsRunning ()
+{
+ return LLDB_STATE_IS_RUNNING(GetState());
+}
+
+lldb::addr_t
+ProcessMacOSXRemote::GetImageInfoAddress()
+{
+ return Task().GetDYLDAllImageInfosAddress();
+}
+
+DynamicLoader *
+ProcessMacOSXRemote::GetDynamicLoader()
+{
+ return m_dynamic_loader_ap.get();
+}
+
+//------------------------------------------------------------------
+// Process Memory
+//------------------------------------------------------------------
+
+size_t
+ProcessMacOSXRemote::DoReadMemory (lldb::addr_t addr, void *buf, size_t size)
+{
+ return Task().ReadMemory(addr, buf, size);
+}
+
+size_t
+ProcessMacOSXRemote::DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size)
+{
+ return Task().WriteMemory(addr, buf, size);
+}
+
+//------------------------------------------------------------------
+// Process STDIO
+//------------------------------------------------------------------
+
+size_t
+ProcessMacOSXRemote::GetSTDOUT (char *buf, size_t buf_size)
+{
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSXRemote::%s (&%p[%u]) ...", __FUNCTION__, buf, buf_size);
+ Mutex::Locker locker(m_stdio_mutex);
+ size_t bytes_available = m_stdout_data.size();
+ if (bytes_available > 0)
+ {
+ if (bytes_available > buf_size)
+ {
+ memcpy(buf, m_stdout_data.data(), buf_size);
+ m_stdout_data.erase(0, buf_size);
+ bytes_available = buf_size;
+ }
+ else
+ {
+ memcpy(buf, m_stdout_data.data(), bytes_available);
+ m_stdout_data.clear();
+ }
+ }
+ return bytes_available;
+}
+
+size_t
+ProcessMacOSXRemote::GetSTDERR (char *buf, size_t buf_size)
+{
+ return 0;
+}
+
+bool
+ProcessMacOSXRemote::EnableBreakpoint (BreakpointLocation *bp)
+{
+ assert (bp != NULL);
+
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_BREAKPOINTS);
+ lldb::user_id_t breakID = bp->GetID();
+ lldb::addr_t addr = bp->GetAddress();
+ if (bp->IsEnabled())
+ {
+ if (log)
+ log->Printf("ProcessMacOSXRemote::EnableBreakpoint ( breakID = %d ) breakpoint already enabled.", breakID);
+ return true;
+ }
+ else
+ {
+ if (bp->HardwarePreferred())
+ {
+ ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(bp->GetThreadID()).get();
+ if (thread)
+ {
+ bp->SetHardwareIndex (thread->EnableHardwareBreakpoint(bp));
+ if (bp->IsHardware())
+ {
+ bp->SetEnabled(true);
+ return true;
+ }
+ }
+ }
+
+ const size_t break_op_size = GetSoftwareBreakpointTrapOpcode (bp);
+ assert (break_op_size > 0);
+ const uint8_t * const break_op = bp->GetTrapOpcodeBytes();
+
+ if (break_op_size > 0)
+ {
+ // Save the original opcode by reading it
+ if (m_task.ReadMemory(addr, bp->GetSavedOpcodeBytes(), break_op_size) == break_op_size)
+ {
+ // Write a software breakpoint in place of the original opcode
+ if (m_task.WriteMemory(addr, break_op, break_op_size) == break_op_size)
+ {
+ uint8_t verify_break_op[4];
+ if (m_task.ReadMemory(addr, verify_break_op, break_op_size) == break_op_size)
+ {
+ if (memcmp(break_op, verify_break_op, break_op_size) == 0)
+ {
+ bp->SetEnabled(true);
+ if (log)
+ log->Printf("ProcessMacOSXRemote::EnableBreakpoint ( breakID = %d ) SUCCESS.", breakID, (uint64_t)addr);
+ return true;
+ }
+ else
+ {
+ GetError().SetErrorString("Failed to verify the breakpoint trap in memory.");
+ }
+ }
+ else
+ {
+ GetError().SetErrorString("Unable to read memory to verify breakpoint trap.");
+ }
+ }
+ else
+ {
+ GetError().SetErrorString("Unable to write breakpoint trap to memory.");
+ }
+ }
+ else
+ {
+ GetError().SetErrorString("Unable to read memory at breakpoint address.");
+ }
+ }
+ }
+
+ if (log)
+ {
+ const char *err_string = GetError().AsCString();
+ log->Printf ("ProcessMacOSXRemote::EnableBreakpoint ( breakID = %d ) error: %s",
+ breakID, err_string ? err_string : "NULL");
+ }
+ GetError().SetErrorToGenericError();
+ return false;
+}
+
+bool
+ProcessMacOSXRemote::DisableBreakpoint (BreakpointLocation *bp)
+{
+ assert (bp != NULL);
+ lldb::addr_t addr = bp->GetAddress();
+ lldb::user_id_t breakID = bp->GetID();
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_BREAKPOINTS);
+ if (log)
+ log->Printf ("ProcessMacOSXRemote::DisableBreakpoint (breakID = %d) addr = 0x%8.8llx", breakID, (uint64_t)addr);
+
+ if (bp->IsHardware())
+ {
+ ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(bp->GetThreadID()).get();
+ if (thread)
+ {
+ if (thread->DisableHardwareBreakpoint(bp))
+ {
+ bp->SetEnabled(false);
+ if (log)
+ log->Printf ("ProcessMacOSXRemote::DisableBreakpoint (breakID = %d) (hardware) => success", breakID);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ const size_t break_op_size = bp->GetByteSize();
+ assert (break_op_size > 0);
+ const uint8_t * const break_op = bp->GetTrapOpcodeBytes();
+ if (break_op_size > 0)
+ {
+ // Clear a software breakoint instruction
+ uint8_t curr_break_op[break_op_size];
+ bool break_op_found = false;
+
+ // Read the breakpoint opcode
+ if (m_task.ReadMemory(addr, curr_break_op, break_op_size) == break_op_size)
+ {
+ bool verify = false;
+ if (bp->IsEnabled())
+ {
+ // Make sure we have the a breakpoint opcode exists at this address
+ if (memcmp(curr_break_op, break_op, break_op_size) == 0)
+ {
+ break_op_found = true;
+ // We found a valid breakpoint opcode at this address, now restore
+ // the saved opcode.
+ if (m_task.WriteMemory(addr, bp->GetSavedOpcodeBytes(), break_op_size) == break_op_size)
+ {
+ verify = true;
+ }
+ else
+ {
+ GetError().SetErrorString("Memory write failed when restoring original opcode.");
+ }
+ }
+ else
+ {
+ GetError().SetErrorString("Original breakpoint trap is no longer in memory.");
+ // Set verify to true and so we can check if the original opcode has already been restored
+ verify = true;
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf ("ProcessMacOSXRemote::DisableBreakpoint (breakID = %d) is already disabled", breakID);
+ // Set verify to true and so we can check if the original opcode is there
+ verify = true;
+ }
+
+ if (verify)
+ {
+ uint8_t verify_opcode[break_op_size];
+ // Verify that our original opcode made it back to the inferior
+ if (m_task.ReadMemory(addr, verify_opcode, break_op_size) == break_op_size)
+ {
+ // compare the memory we just read with the original opcode
+ if (memcmp(bp->GetSavedOpcodeBytes(), verify_opcode, break_op_size) == 0)
+ {
+ // SUCCESS
+ bp->SetEnabled(false);
+ if (log)
+ log->Printf ("ProcessMacOSXRemote::DisableBreakpoint (breakID = %d) SUCCESS", breakID);
+ return true;
+ }
+ else
+ {
+ if (break_op_found)
+ GetError().SetErrorString("Failed to restore original opcode.");
+ }
+ }
+ else
+ {
+ GetError().SetErrorString("Failed to read memory to verify that breakpoint trap was restored.");
+ }
+ }
+ }
+ else
+ {
+ GetError().SetErrorString("Unable to read memory that should contain the breakpoint trap.");
+ }
+ }
+
+ GetError().SetErrorToGenericError();
+ return false;
+}
+
+bool
+ProcessMacOSXRemote::EnableWatchpoint (WatchpointLocation *wp)
+{
+ if (wp)
+ {
+ lldb::user_id_t watchID = wp->GetID();
+ lldb::addr_t addr = wp->GetAddress();
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_WATCHPOINTS);
+ if (log)
+ log->Printf ("ProcessMacOSXRemote::EnableWatchpoint(watchID = %d)", watchID);
+ if (wp->IsEnabled())
+ {
+ if (log)
+ log->Printf("ProcessMacOSXRemote::EnableWatchpoint(watchID = %d) addr = 0x%8.8llx: watchpoint already enabled.", watchID, (uint64_t)addr);
+ return true;
+ }
+ else
+ {
+ ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(wp->GetThreadID()).get();
+ if (thread)
+ {
+ wp->SetHardwareIndex (thread->EnableHardwareWatchpoint (wp));
+ if (wp->IsHardware ())
+ {
+ wp->SetEnabled(true);
+ return true;
+ }
+ }
+ else
+ {
+ GetError().SetErrorString("Watchpoints currently only support thread specific watchpoints.");
+ }
+ }
+ }
+ return false;
+}
+
+bool
+ProcessMacOSXRemote::DisableWatchpoint (WatchpointLocation *wp)
+{
+ if (wp)
+ {
+ lldb::user_id_t watchID = wp->GetID();
+
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_WATCHPOINTS);
+
+ lldb::addr_t addr = wp->GetAddress();
+ if (log)
+ log->Printf ("ProcessMacOSXRemote::DisableWatchpoint (watchID = %d) addr = 0x%8.8llx", watchID, (uint64_t)addr);
+
+ if (wp->IsHardware())
+ {
+ ThreadMacOSX *thread = (ThreadMacOSX *)m_thread_list.FindThreadByID(wp->GetThreadID()).get();
+ if (thread)
+ {
+ if (thread->DisableHardwareWatchpoint (wp))
+ {
+ wp->SetEnabled(false);
+ if (log)
+ log->Printf ("ProcessMacOSXRemote::Disablewatchpoint (watchID = %d) addr = 0x%8.8llx (hardware) => success", watchID, (uint64_t)addr);
+ return true;
+ }
+ }
+ }
+ // TODO: clear software watchpoints if we implement them
+ }
+ else
+ {
+ GetError().SetErrorString("Watchpoint location argument was NULL.");
+ }
+ GetError().SetErrorToGenericError();
+ return false;
+}
+
+
+static ProcessMacOSXRemote::CreateArchCalback
+ArchDCScriptInterpreter::TypeMap(const ArchSpec& arch_spec, ProcessMacOSXRemote::CreateArchCalback callback, bool add )
+{
+ // We must wrap the "g_arch_map" file static in a function to avoid
+ // any global constructors so we don't get a build verification error
+ typedef std::multimap<ArchSpec, ProcessMacOSXRemote::CreateArchCalback> ArchToProtocolMap;
+ static ArchToProtocolMap g_arch_map;
+
+ if (add)
+ {
+ g_arch_map.insert(std::make_pair(arch_spec, callback));
+ return callback;
+ }
+ else
+ {
+ ArchToProtocolMap::const_iterator pos = g_arch_map.find(arch_spec);
+ if (pos != g_arch_map.end())
+ {
+ return pos->second;
+ }
+ }
+ return NULL;
+}
+
+void
+ProcessMacOSXRemote::AddArchCreateDCScriptInterpreter::Type(const ArchSpec& arch_spec, CreateArchCalback callback)
+{
+ ArchDCScriptInterpreter::TypeMap (arch_spec, callback, true);
+}
+
+ProcessMacOSXRemote::CreateArchCalback
+ProcessMacOSXRemote::GetArchCreateDCScriptInterpreter::Type()
+{
+ return ArchDCScriptInterpreter::TypeMap (m_arch_spec, NULL, false);
+}
+
+void
+ProcessMacOSXRemote::Clear()
+{
+ // Clear any cached thread list while the pid and task are still valid
+
+ m_task.Clear();
+ // Now clear out all member variables
+ CloseChildFileDescriptors();
+
+ m_flags = eFlagsNone;
+ m_thread_list.Clear();
+ {
+ Mutex::Locker locker(m_exception_messages_mutex);
+ m_exception_messages.clear();
+ }
+
+}
+
+
+bool
+ProcessMacOSXRemote::Kill (int signal)
+{
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_PROCESS);
+ if (log)
+ log->Printf ("ProcessMacOSXRemote::Kill(signal = %d)", signal);
+ State state = GetState();
+
+ if (IsRunning(state))
+ {
+ if (::kill (GetID(), signal) == 0)
+ {
+ GetError().Clear();
+ }
+ else
+ {
+ GetError().SetErrorToErrno();
+ GetError().LogIfError(log, "ProcessMacOSXRemote::Kill(%d)", signal);
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf ("ProcessMacOSXRemote::Kill(signal = %d) pid %u (task = 0x%4.4x) was't running, ignoring...", signal, GetID(), m_task.TaskPort());
+ GetError().Clear();
+ }
+ return GetError().Success();
+
+}
+
+
+bool
+ProcessMacOSXRemote::Detach()
+{
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSXRemote::Detach()");
+
+ State state = GetState();
+
+ if (!IsRunning(state))
+ {
+ // Resume our process
+ PrivateResume(LLDB_INVALID_THREAD_ID);
+
+ // We have resumed and now we wait for that event to get posted
+ Event event;
+ if (WaitForPrivateEvents(LLDB_EVENT_RUNNING, &event, 2) == false)
+ return false;
+
+
+ // We need to be stopped in order to be able to detach, so we need
+ // to send ourselves a SIGSTOP
+ if (Kill(SIGSTOP))
+ {
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_PROCESS);
+
+ lldb::pid_t pid = GetID();
+ // Wait for our process stop event to get posted
+ if (WaitForPrivateEvents(LLDB_EVENT_STOPPED, &event, 2) == false)
+ {
+ GetError().Log(log, "::kill (pid = %u, SIGSTOP)", pid);
+ return false;
+ }
+
+ // Shut down the exception thread and cleanup our exception remappings
+ m_task.ShutDownExceptionThread();
+
+ // Detach from our process while we are stopped.
+ errno = 0;
+
+ // Detach from our process
+ ::ptrace (PT_DETACH, pid, (caddr_t)1, 0);
+
+ GetError().SetErrorToErrno();
+
+ if (log || GetError().Fail())
+ GetError().Log(log, "::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid);
+
+ // Resume our task
+ m_task.Resume();
+
+ // NULL our task out as we have already retored all exception ports
+ m_task.Clear();
+
+ // Clear out any notion of the process we once were
+ Clear();
+ }
+ }
+ else
+ {
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSXRemote::Detach() error: process must be stopped (SIGINT the process first).");
+ }
+ return false;
+}
+
+
+
+void
+ProcessMacOSXRemote::ReplyToAllExceptions()
+{
+ Mutex::Locker locker(m_exception_messages_mutex);
+ if (m_exception_messages.empty() == false)
+ {
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_EXCEPTIONS);
+
+ MachException::Message::iterator pos;
+ MachException::Message::iterator begin = m_exception_messages.begin();
+ MachException::Message::iterator end = m_exception_messages.end();
+ for (pos = begin; pos != end; ++pos)
+ {
+ if (log)
+ log->Printf ("Replying to exception %d...", std::distance(begin, pos));
+ int resume_signal = 0;
+ ThreadSP thread_sp = m_thread_list.FindThreadByID(pos->state.thread_port);
+ if (thread_sp.get())
+ resume_signal = thread_sp->GetResumeSignal();
+ GetError() = pos->Reply (Task().TaskPort(), GetID(), resume_signal);
+ GetError().LogIfError(log, "Error replying to exception");
+ }
+
+ // Erase all exception message as we should have used and replied
+ // to them all already.
+ m_exception_messages.clear();
+ }
+}
+void
+ProcessMacOSXRemote::PrivateResume (lldb::tid_t tid)
+{
+ Mutex::Locker locker(m_exception_messages_mutex);
+ ReplyToAllExceptions();
+
+ // Let the thread prepare to resume and see if any threads want us to
+ // step over a breakpoint instruction (ProcessWillResume will modify
+ // the value of stepOverBreakInstruction).
+ //StateType process_state = m_thread_list.ProcessWillResume(this);
+
+ // Set our state accordingly
+ SetState(eStateRunning);
+
+ // Now resume our task.
+ GetError() = m_task.Resume();
+
+}
+
+// Called by the exception thread when an exception has been received from
+// our process. The exception message is completely filled and the exception
+// data has already been copied.
+void
+ProcessMacOSXRemote::ExceptionMessageReceived (const MachException::Message& exceptionMessage)
+{
+ Mutex::Locker locker(m_exception_messages_mutex);
+
+ if (m_exception_messages.empty())
+ m_task.Suspend();
+
+ ProcessMacOSXLog::LogIf (PD_LOG_EXCEPTIONS, "ProcessMacOSXRemote::ExceptionMessageReceived ( )");
+
+ // Use a locker to automatically unlock our mutex in case of exceptions
+ // Add the exception to our internal exception stack
+ m_exception_messages.push_back(exceptionMessage);
+}
+
+
+//bool
+//ProcessMacOSXRemote::GetProcessInfo (struct kinfo_proc* proc_info)
+//{
+// int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, GetID() };
+// size_t buf_size = sizeof(struct kinfo_proc);
+//
+// if (::sysctl (mib, (unsigned)(sizeof(mib)/sizeof(int)), &proc_info, &buf_size, NULL, 0) == 0)
+// return buf_size > 0;
+//
+// return false;
+//}
+//
+//
+void
+ProcessMacOSXRemote::ExceptionMessageBundleComplete()
+{
+ // We have a complete bundle of exceptions for our child process.
+ Mutex::Locker locker(m_exception_messages_mutex);
+ ProcessMacOSXLog::LogIf (PD_LOG_EXCEPTIONS, "%s: %d exception messages.", __PRETTY_FUNCTION__, m_exception_messages.size());
+ if (!m_exception_messages.empty())
+ {
+ SetState (eStateStopped);
+ }
+ else
+ {
+ ProcessMacOSXLog::LogIf (PD_LOG_EXCEPTIONS, "%s empty exception messages bundle.", __PRETTY_FUNCTION__, m_exception_messages.size());
+ }
+}
+
+bool
+ProcessMacOSXRemote::ReleaseChildFileDescriptors ( int *stdin_fileno, int *stdout_fileno, int *stderr_fileno )
+{
+ if (stdin_fileno)
+ *stdin_fileno = m_child_stdin;
+ if (stdout_fileno)
+ *stdout_fileno = m_child_stdout;
+ if (stderr_fileno)
+ *stderr_fileno = m_child_stderr;
+ // Stop the stdio thread if we have one, but don't have it close the child
+ // file descriptors since we are giving control of these descriptors to the
+ // caller
+ bool close_child_fds = false;
+ StopSTDIOThread(close_child_fds);
+ return true;
+}
+
+void
+ProcessMacOSXRemote::AppendSTDOUT (char* s, size_t len)
+{
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "ProcessMacOSXRemote::%s (<%d> %s) ...", __FUNCTION__, len, s);
+ Mutex::Locker locker(m_stdio_mutex);
+ m_stdout_data.append(s, len);
+ AppendEvent (LLDB_EVENT_STDIO);
+}
+
+void *
+ProcessMacOSXRemote::STDIOThread(void *arg)
+{
+ ProcessMacOSXRemote *proc = (ProcessMacOSXRemote*) arg;
+
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS);
+ if (log)
+ log->Printf ("ProcessMacOSXRemote::%s (arg = %p) thread starting...", __FUNCTION__, arg);
+
+ // We start use a base and more options so we can control if we
+ // are currently using a timeout on the mach_msg. We do this to get a
+ // bunch of related exceptions on our exception port so we can process
+ // then together. When we have multiple threads, we can get an exception
+ // per thread and they will come in consecutively. The main thread loop
+ // will start by calling mach_msg to without having the MACH_RCV_TIMEOUT
+ // flag set in the options, so we will wait forever for an exception on
+ // our exception port. After we get one exception, we then will use the
+ // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current
+ // exceptions for our process. After we have received the last pending
+ // exception, we will get a timeout which enables us to then notify
+ // our main thread that we have an exception bundle avaiable. We then wait
+ // for the main thread to tell this exception thread to start trying to get
+ // exceptions messages again and we start again with a mach_msg read with
+ // infinite timeout.
+ Error err;
+ int stdout_fd = proc->GetStdoutFileDescriptor();
+ int stderr_fd = proc->GetStderrFileDescriptor();
+ if (stdout_fd == stderr_fd)
+ stderr_fd = -1;
+
+ while (stdout_fd >= 0 || stderr_fd >= 0)
+ {
+ ::pthread_testcancel ();
+
+ fd_set read_fds;
+ FD_ZERO (&read_fds);
+ if (stdout_fd >= 0)
+ FD_SET (stdout_fd, &read_fds);
+ if (stderr_fd >= 0)
+ FD_SET (stderr_fd, &read_fds);
+ int nfds = std::max<int>(stdout_fd, stderr_fd) + 1;
+
+ int num_set_fds = select (nfds, &read_fds, NULL, NULL, NULL);
+ if (log)
+ log->Printf("select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds);
+
+ if (num_set_fds < 0)
+ {
+ int select_errno = errno;
+ if (log)
+ {
+ err.SetError (select_errno, Error::POSIX);
+ err.LogIfError(log, "select (nfds, &read_fds, NULL, NULL, NULL) => %d", num_set_fds);
+ }
+
+ switch (select_errno)
+ {
+ case EAGAIN: // The kernel was (perhaps temporarily) unable to allocate the requested number of file descriptors, or we have non-blocking IO
+ break;
+ case EBADF: // One of the descriptor sets specified an invalid descriptor.
+ return NULL;
+ break;
+ case EINTR: // A signal was delivered before the time limit expired and before any of the selected events occurred.
+ case EINVAL: // The specified time limit is invalid. One of its components is negative or too large.
+ default: // Other unknown error
+ break;
+ }
+ }
+ else if (num_set_fds == 0)
+ {
+ }
+ else
+ {
+ char s[1024];
+ s[sizeof(s)-1] = '\0'; // Ensure we have NULL termination
+ int bytes_read = 0;
+ if (stdout_fd >= 0 && FD_ISSET (stdout_fd, &read_fds))
+ {
+ do
+ {
+ bytes_read = ::read (stdout_fd, s, sizeof(s)-1);
+ if (bytes_read < 0)
+ {
+ int read_errno = errno;
+ if (log)
+ log->Printf("read (stdout_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno));
+ }
+ else if (bytes_read == 0)
+ {
+ // EOF...
+ if (log)
+ log->Printf("read (stdout_fd, ) => %d (reached EOF for child STDOUT)", bytes_read);
+ stdout_fd = -1;
+ }
+ else if (bytes_read > 0)
+ {
+ proc->AppendSTDOUT(s, bytes_read);
+ }
+
+ } while (bytes_read > 0);
+ }
+
+ if (stderr_fd >= 0 && FD_ISSET (stderr_fd, &read_fds))
+ {
+ do
+ {
+ bytes_read = ::read (stderr_fd, s, sizeof(s)-1);
+ if (bytes_read < 0)
+ {
+ int read_errno = errno;
+ if (log)
+ log->Printf("read (stderr_fd, ) => %d errno: %d (%s)", bytes_read, read_errno, strerror(read_errno));
+ }
+ else if (bytes_read == 0)
+ {
+ // EOF...
+ if (log)
+ log->Printf("read (stderr_fd, ) => %d (reached EOF for child STDERR)", bytes_read);
+ stderr_fd = -1;
+ }
+ else if (bytes_read > 0)
+ {
+ proc->AppendSTDOUT(s, bytes_read);
+ }
+
+ } while (bytes_read > 0);
+ }
+ }
+ }
+
+ if (log)
+ log->Printf("ProcessMacOSXRemote::%s (%p): thread exiting...", __FUNCTION__, arg);
+
+ return NULL;
+}
+
+lldb::pid_t
+ProcessMacOSXRemote::AttachForDebug (lldb::pid_t pid)
+{
+ // Clear out and clean up from any current state
+ Clear();
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS);
+ if (pid != 0)
+ {
+ SetState(eStateAttaching);
+ SetID(pid);
+ // Let ourselves know we are going to be using SBS if the correct flag bit is set...
+#if defined (__arm__)
+ if (IsSBProcess(pid))
+ m_flags |= eFlagsUsingSBS;
+#endif
+ m_task.StartExceptionThread(GetError());
+
+ if (GetError().Success())
+ {
+ if (ptrace (PT_ATTACHEXC, pid, 0, 0) == 0)
+ {
+ m_flags.Set (eFlagsAttached);
+ // Sleep a bit to let the exception get received and set our process status
+ // to stopped.
+ ::usleep(250000);
+ if (log)
+ log->Printf ("successfully attached to pid %d", pid);
+ return GetID();
+ }
+ else
+ {
+ GetError().SetErrorToErrno();
+ if (log)
+ log->Printf ("error: failed to attach to pid %d", pid);
+ }
+ }
+ else
+ {
+ GetError().Log(log, "ProcessMacOSXRemote::%s (pid = %i) failed to start exception thread", __FUNCTION__, pid);
+ }
+ }
+ return LLDB_INVALID_PROCESS_ID;
+}
+
+lldb::pid_t
+ProcessMacOSXRemote::LaunchForDebug
+(
+ const char *path,
+ char const *argv[],
+ char const *envp[],
+ ArchSpec& arch_spec,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ PDLaunchType launch_type,
+ Error &launch_err)
+{
+ // Clear out and clean up from any current state
+ Clear();
+
+ m_arch_spec = arch_spec;
+
+ if (launch_type == eLaunchDefault)
+ launch_type = eLaunchPosixSpawn;
+
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS);
+ if (log)
+ log->Printf ("%s( path = '%s', argv = %p, envp = %p, launch_type = %u )", __FUNCTION__, path, argv, envp, launch_type);
+
+ // Fork a child process for debugging
+ SetState(eStateLaunching);
+ switch (launch_type)
+ {
+ case eLaunchForkExec:
+ SetID(ProcessMacOSXRemote::ForkChildForPTraceDebugging(path, argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, this, launch_err));
+ break;
+
+ case eLaunchPosixSpawn:
+ SetID(ProcessMacOSXRemote::PosixSpawnChildForPTraceDebugging(path, argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, this, launch_err));
+ break;
+
+#if defined (__arm__)
+
+ case eLaunchSpringBoard:
+ {
+ const char *app_ext = strstr(path, ".app");
+ if (app_ext != NULL)
+ {
+ std::string app_bundle_path(path, app_ext + strlen(".app"));
+ return SBLaunchForDebug (app_bundle_path.c_str(), argv, envp, arch_spec, stdin_path, stdout_path, stderr_path, launch_err);
+ }
+ }
+ break;
+
+#endif
+
+ default:
+ // Invalid launch
+ launch_err.SetErrorToGenericError ();
+ return LLDB_INVALID_PROCESS_ID;
+ }
+
+ lldb::pid_t pid = GetID();
+
+ if (pid == LLDB_INVALID_PROCESS_ID)
+ {
+ // If we don't have a valid process ID and no one has set the error,
+ // then return a generic error
+ if (launch_err.Success())
+ launch_err.SetErrorToGenericError ();
+ }
+ else
+ {
+ // Make sure we can get our task port before going any further
+ m_task.TaskPortForProcessID (launch_err);
+
+ // If that goes well then kick off our exception thread
+ if (launch_err.Success())
+ m_task.StartExceptionThread(launch_err);
+
+ if (launch_err.Success())
+ {
+ //m_path = path;
+// size_t i;
+// if (argv)
+// {
+// char const *arg;
+// for (i=0; (arg = argv[i]) != NULL; i++)
+// m_args.push_back(arg);
+// }
+
+ StartSTDIOThread();
+
+ if (launch_type == eLaunchPosixSpawn)
+ {
+
+ //SetState (eStateAttaching);
+ errno = 0;
+ if (::ptrace (PT_ATTACHEXC, pid, 0, 0) == 0)
+ launch_err.Clear();
+ else
+ launch_err.SetErrorToErrno();
+
+ if (launch_err.Fail() || log)
+ launch_err.Log(log, "::ptrace (PT_ATTACHEXC, pid = %i, 0, 0 )", pid);
+
+ if (launch_err.Success())
+ m_flags.Set (eFlagsAttached);
+ else
+ SetState (eStateExited);
+ }
+ else
+ {
+ launch_err.Clear();
+ }
+ }
+ else
+ {
+ // We were able to launch the process, but not get its task port
+ // so now we need to make it sleep with da fishes.
+ SetID(LLDB_INVALID_PROCESS_ID);
+ ::kill (pid, SIGCONT);
+ ::kill (pid, SIGKILL);
+ pid = LLDB_INVALID_PROCESS_ID;
+ }
+
+ }
+ return pid;
+}
+
+lldb::pid_t
+ProcessMacOSXRemote::PosixSpawnChildForPTraceDebugging
+(
+ const char *path,
+ char const *argv[],
+ char const *envp[],
+ ArchSpec& arch_spec,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ ProcessMacOSXRemote* process,
+ Error &err
+)
+{
+ posix_spawnattr_t attr;
+
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_PROCESS);
+
+ Error local_err; // Errors that don't affect the spawning.
+ if (log)
+ log->Printf ("%s ( path='%s', argv=%p, envp=%p, process )", __FUNCTION__, path, argv, envp);
+ err.SetError( ::posix_spawnattr_init (&attr), Error::POSIX);
+ if (err.Fail() || log)
+ err.Log(log, "::posix_spawnattr_init ( &attr )");
+ if (err.Fail())
+ return LLDB_INVALID_PROCESS_ID;
+
+ err.SetError( ::posix_spawnattr_setflags (&attr, POSIX_SPAWN_START_SUSPENDED), Error::POSIX);
+ if (err.Fail() || log)
+ err.Log(log, "::posix_spawnattr_setflags ( &attr, POSIX_SPAWN_START_SUSPENDED )");
+ if (err.Fail())
+ return LLDB_INVALID_PROCESS_ID;
+
+#if !defined(__arm__)
+
+ // We don't need to do this for ARM, and we really shouldn't now that we
+ // have multiple CPU subtypes and no posix_spawnattr call that allows us
+ // to set which CPU subtype to launch...
+ cpu_type_t cpu = arch_spec.GetCPUType();
+ if (cpu != 0 && cpu != CPU_TYPE_ANY && cpu != LLDB_INVALID_CPUTYPE)
+ {
+ size_t ocount = 0;
+ err.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu, &ocount), Error::POSIX);
+ if (err.Fail() || log)
+ err.Log(log, "::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %zu )", cpu, ocount);
+
+ if (err.Fail() != 0 || ocount != 1)
+ return LLDB_INVALID_PROCESS_ID;
+ }
+
+#endif
+
+ PseudoTerminal pty;
+
+ posix_spawn_file_actions_t file_actions;
+ err.SetError( ::posix_spawn_file_actions_init (&file_actions), Error::POSIX);
+ int file_actions_valid = err.Success();
+ if (!file_actions_valid || log)
+ err.Log(log, "::posix_spawn_file_actions_init ( &file_actions )");
+ Error stdio_err;
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ if (file_actions_valid)
+ {
+ // If the user specified any STDIO files, then use those
+ if (stdin_path || stdout_path || stderr_path)
+ {
+ process->SetSTDIOIsOurs(false);
+ if (stderr_path != NULL && stderr_path[0])
+ {
+ stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDERR_FILENO, stderr_path, O_RDWR, 0), Error::POSIX);
+ if (stdio_err.Fail() || log)
+ stdio_err.Log(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDERR_FILENO, path = '%s', oflag = O_RDWR, mode = 0 )", stderr_path);
+ }
+
+ if (stdin_path != NULL && stdin_path[0])
+ {
+ stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDIN_FILENO, stdin_path, O_RDONLY, 0), Error::POSIX);
+ if (stdio_err.Fail() || log)
+ stdio_err.Log(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDIN_FILENO, path = '%s', oflag = O_RDONLY, mode = 0 )", stdin_path);
+ }
+
+ if (stdout_path != NULL && stdout_path[0])
+ {
+ stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDOUT_FILENO, stdout_path, O_WRONLY, 0), Error::POSIX);
+ if (stdio_err.Fail() || log)
+ stdio_err.Log(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDOUT_FILENO, path = '%s', oflag = O_WRONLY, mode = 0 )", stdout_path);
+ }
+ }
+ else
+ {
+ // The user did not specify any STDIO files, use a pseudo terminal.
+ // Callers can then access the file handles using the
+ // ProcessMacOSXRemote::ReleaseChildFileDescriptors() function, otherwise
+ // this class will spawn a thread that tracks STDIO and buffers it.
+ process->SetSTDIOIsOurs(true);
+ if (pty.OpenFirstAvailableMaster(O_RDWR, &stdio_err))
+ {
+ const char* slave_name = pty.GetSlaveName(&stdio_err);
+ if (slave_name == NULL)
+ slave_name = "/dev/null";
+ stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDERR_FILENO, slave_name, O_RDWR, 0), Error::POSIX);
+ if (stdio_err.Fail() || log)
+ stdio_err.Log(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDERR_FILENO, path = '%s', oflag = O_RDWR, mode = 0 )", slave_name);
+
+ stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDIN_FILENO, slave_name, O_RDONLY, 0), Error::POSIX);
+ if (stdio_err.Fail() || log)
+ stdio_err.Log(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDIN_FILENO, path = '%s', oflag = O_RDONLY, mode = 0 )", slave_name);
+
+ stdio_err.SetError( ::posix_spawn_file_actions_addopen(&file_actions, STDOUT_FILENO, slave_name, O_WRONLY, 0), Error::POSIX);
+ if (stdio_err.Fail() || log)
+ stdio_err.Log(log, "::posix_spawn_file_actions_addopen ( &file_actions, filedes = STDOUT_FILENO, path = '%s', oflag = O_WRONLY, mode = 0 )", slave_name);
+ }
+ }
+ err.SetError( ::posix_spawnp (&pid, path, &file_actions, &attr, (char * const*)argv, (char * const*)envp), Error::POSIX);
+ if (err.Fail() || log)
+ err.Log(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, &file_actions, &attr, argv, envp);
+
+ if (stdio_err.Success())
+ {
+ // If we have a valid process and we created the STDIO file handles,
+ // then remember them on our process class so we can spawn a STDIO
+ // thread and close them when we are done with them.
+ if (process != NULL && process->STDIOIsOurs())
+ {
+ int master_fd = pty.ReleaseMasterFileDescriptor ();
+ process->SetChildFileDescriptors (master_fd, master_fd, master_fd);
+ }
+ }
+ }
+ else
+ {
+ err.SetError( ::posix_spawnp (&pid, path, NULL, &attr, (char * const*)argv, (char * const*)envp), Error::POSIX);
+ if (err.Fail() || log)
+ err.Log(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", pid, path, NULL, &attr, argv, envp);
+ }
+
+ // We have seen some cases where posix_spawnp was returning a valid
+ // looking pid even when an error was returned, so clear it out
+ if (err.Fail())
+ pid = LLDB_INVALID_PROCESS_ID;
+
+ if (file_actions_valid)
+ {
+ local_err.SetError( ::posix_spawn_file_actions_destroy (&file_actions), Error::POSIX);
+ if (local_err.Fail() || log)
+ local_err.Log(log, "::posix_spawn_file_actions_destroy ( &file_actions )");
+ }
+
+ return pid;
+}
+
+lldb::pid_t
+ProcessMacOSXRemote::ForkChildForPTraceDebugging
+(
+ const char *path,
+ char const *argv[],
+ char const *envp[],
+ ArchSpec& arch_spec,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ ProcessMacOSXRemote* process,
+ Error &launch_err
+)
+{
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+
+ if (stdin_path || stdout_path || stderr_path)
+ {
+ assert(!"TODO: ForkChildForPTraceDebugging doesn't currently support fork/exec with user file handles...");
+ }
+ else
+ {
+
+ // Use a fork that ties the child process's stdin/out/err to a pseudo
+ // terminal so we can read it in our ProcessMacOSXRemote::STDIOThread
+ // as unbuffered io.
+ PseudoTerminal pty;
+ pid = pty.Fork(&launch_err);
+
+ if (pid < 0)
+ {
+ //--------------------------------------------------------------
+ // Error during fork.
+ //--------------------------------------------------------------
+ return pid;
+ }
+ else if (pid == 0)
+ {
+ //--------------------------------------------------------------
+ // Child process
+ //--------------------------------------------------------------
+ ::ptrace (PT_TRACE_ME, 0, 0, 0); // Debug this process
+ ::ptrace (PT_SIGEXC, 0, 0, 0); // Get BSD signals as mach exceptions
+
+ // If our parent is setgid, lets make sure we don't inherit those
+ // extra powers due to nepotism.
+ ::setgid (getgid ());
+
+ // Let the child have its own process group. We need to execute
+ // this call in both the child and parent to avoid a race condition
+ // between the two processes.
+ ::setpgid (0, 0); // Set the child process group to match its pid
+
+ // Sleep a bit to before the exec call
+ ::sleep (1);
+
+ // Turn this process into
+ ::execv (path, (char * const *)argv);
+ // Exit with error code. Child process should have taken
+ // over in above exec call and if the exec fails it will
+ // exit the child process below.
+ ::exit (127);
+ }
+ else
+ {
+ //--------------------------------------------------------------
+ // Parent process
+ //--------------------------------------------------------------
+ // Let the child have its own process group. We need to execute
+ // this call in both the child and parent to avoid a race condition
+ // between the two processes.
+ ::setpgid (pid, pid); // Set the child process group to match its pid
+
+ if (process != NULL)
+ {
+ // Release our master pty file descriptor so the pty class doesn't
+ // close it and so we can continue to use it in our STDIO thread
+ int master_fd = pty.ReleaseMasterFileDescriptor ();
+ process->SetChildFileDescriptors (master_fd, master_fd, master_fd);
+ }
+ }
+ }
+ return pid;
+}
+
+#if defined (__arm__)
+
+lldb::pid_t
+ProcessMacOSXRemote::SBLaunchForDebug
+(
+ const char *path,
+ char const *argv[],
+ char const *envp[],
+ ArchSpec& arch_spec,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ Error &launch_err
+)
+{
+ // Clear out and clean up from any current state
+ Clear();
+
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s( '%s', argv)", __FUNCTION__, path);
+
+ // Fork a child process for debugging
+ SetState(eStateLaunching);
+ m_pid = ProcessMacOSXRemote::SBLaunchForDebug(path, argv, envp, this, launch_err);
+ if (m_pid != 0)
+ {
+ m_flags |= eFlagsUsingSBS;
+ //m_path = path;
+// size_t i;
+// char const *arg;
+// for (i=0; (arg = argv[i]) != NULL; i++)
+// m_args.push_back(arg);
+ m_task.StartExceptionThread();
+ StartSTDIOThread();
+ SetState (eStateAttaching);
+ int err = ptrace (PT_ATTACHEXC, m_pid, 0, 0);
+ if (err == 0)
+ {
+ m_flags |= eFlagsAttached;
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "successfully attached to pid %d", m_pid);
+ }
+ else
+ {
+ SetState (eStateExited);
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "error: failed to attach to pid %d", m_pid);
+ }
+ }
+ return m_pid;
+}
+
+#include <servers/bootstrap.h>
+#include "CFBundle.h"
+#include "CFData.h"
+#include "CFString.h"
+
+lldb::pid_t
+ProcessMacOSXRemote::SBLaunchForDebug
+(
+ const char *app_bundle_path,
+ char const *argv[],
+ char const *envp[],
+ ArchSpec& arch_spec,
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path,
+ ProcessMacOSXRemote* process,
+ Error &launch_err
+)
+{
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s( '%s', argv, %p)", __FUNCTION__, app_bundle_path, process);
+ CFAllocatorRef alloc = kCFAllocatorDefault;
+ if (argv[0] == NULL)
+ return LLDB_INVALID_PROCESS_ID;
+
+ size_t argc = 0;
+ // Count the number of arguments
+ while (argv[argc] != NULL)
+ argc++;
+
+ // Enumerate the arguments
+ size_t first_launch_arg_idx = 1;
+ CFReleaser<CFMutableArrayRef> launch_argv;
+
+ if (argv[first_launch_arg_idx])
+ {
+ size_t launch_argc = argc > 0 ? argc - 1 : 0;
+ launch_argv.reset (::CFArrayCreateMutable (alloc, launch_argc, &kCFTypeArrayCallBacks));
+ size_t i;
+ char const *arg;
+ CFString launch_arg;
+ for (i=first_launch_arg_idx; (i < argc) && ((arg = argv[i]) != NULL); i++)
+ {
+ launch_arg.reset(::CFStringCreateWithCString (alloc, arg, kCFStringEncodingUTF8));
+ if (launch_arg.get() != NULL)
+ CFArrayAppendValue(launch_argv.get(), launch_arg.get());
+ else
+ break;
+ }
+ }
+
+ // Next fill in the arguments dictionary. Note, the envp array is of the form
+ // Variable=value but SpringBoard wants a CF dictionary. So we have to convert
+ // this here.
+
+ CFReleaser<CFMutableDictionaryRef> launch_envp;
+
+ if (envp[0])
+ {
+ launch_envp.reset(::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+ const char *value;
+ int name_len;
+ CFString name_string, value_string;
+
+ for (int i = 0; envp[i] != NULL; i++)
+ {
+ value = strstr (envp[i], "=");
+
+ // If the name field is empty or there's no =, skip it. Somebody's messing with us.
+ if (value == NULL || value == envp[i])
+ continue;
+
+ name_len = value - envp[i];
+
+ // Now move value over the "="
+ value++;
+
+ name_string.reset(::CFStringCreateWithBytes(alloc, (const UInt8 *) envp[i], name_len, kCFStringEncodingUTF8, false));
+ value_string.reset(::CFStringCreateWithCString(alloc, value, kCFStringEncodingUTF8));
+ CFDictionarySetValue (launch_envp.get(), name_string.get(), value_string.get());
+ }
+ }
+
+ CFString stdout_cf_path;
+ CFString stderr_cf_path;
+ PseudoTerminal pty;
+
+ if (stdin_path || stdout_path || stderr_path)
+ {
+ process->SetSTDIOIsOurs(false);
+ if (stdout_path)
+ stdout_cf_path.SetFileSystemRepresentation (stdout_path);
+ if (stderr_path)
+ stderr_cf_path.SetFileSystemRepresentation (stderr_path);
+ }
+ else
+ {
+ process->SetSTDIOIsOurs(true);
+ PseudoTerminal::Error pty_err = pty.OpenFirstAvailableMaster(O_RDWR);
+ if (pty_err == PseudoTerminal::success)
+ {
+ const char* slave_name = pty.SlaveName();
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() successfully opened master pty, slave is %s", __FUNCTION__, slave_name);
+ if (slave_name && slave_name[0])
+ {
+ ::chmod (slave_name, S_IRWXU | S_IRWXG | S_IRWXO);
+ stdout_cf_path.SetFileSystemRepresentation (slave_name);
+ stderr_cf_path.(stdout_cf_path);
+ }
+ }
+ }
+
+ if (stdout_cf_path.get() == NULL)
+ stdout_cf_path.SetFileSystemRepresentation ("/dev/null");
+ if (stderr_cf_path.get() == NULL)
+ stderr_cf_path.SetFileSystemRepresentation ("/dev/null");
+
+ CFBundle bundle(app_bundle_path);
+ CFStringRef bundleIDCFStr = bundle.GetIdentifier();
+ std::string bundleID;
+ if (CFString::UTF8(bundleIDCFStr, bundleID) == NULL)
+ {
+ struct stat app_bundle_stat;
+ if (::stat (app_bundle_path, &app_bundle_stat) < 0)
+ {
+ launch_err.SetError(errno, Error::POSIX);
+ launch_err.SetErrorStringWithFormat ("%s: \"%s\".\n", launch_err.AsString(), app_bundle_path);
+ }
+ else
+ {
+ launch_err.SetError(-1, Error::Generic);
+ launch_err.SetErrorStringWithFormat ("Failed to extract CFBundleIdentifier from %s.\n", app_bundle_path);
+ }
+ return LLDB_INVALID_PROCESS_ID;
+ }
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() extracted CFBundleIdentifier: %s", __FUNCTION__, bundleID.c_str());
+
+
+ CFData argv_data(NULL);
+
+ if (launch_argv.get())
+ {
+ if (argv_data.Serialize(launch_argv.get(), kCFPropertyListBinaryFormat_v1_0) == NULL)
+ {
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() error: failed to serialize launch arg array...", __FUNCTION__);
+ return LLDB_INVALID_PROCESS_ID;
+ }
+ }
+
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() serialized launch arg array", __FUNCTION__);
+
+ // Find SpringBoard
+ SBSApplicationLaunchError sbs_error = 0;
+ sbs_error = SBSLaunchApplication ( bundleIDCFStr,
+ (CFURLRef)NULL, // openURL
+ launch_argv.get(),
+ launch_envp.get(), // CFDictionaryRef environment
+ stdout_cf_path.get(),
+ stderr_cf_path.get(),
+ SBSApplicationLaunchWaitForDebugger | SBSApplicationLaunchUnlockDevice);
+
+
+ launch_err.SetError(sbs_error, Error::SpringBoard);
+
+ if (sbs_error == SBSApplicationLaunchErrorSuccess)
+ {
+ static const useconds_t pid_poll_interval = 200000;
+ static const useconds_t pid_poll_timeout = 30000000;
+
+ useconds_t pid_poll_total = 0;
+
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ Boolean pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid);
+ // Poll until the process is running, as long as we are getting valid responses and the timeout hasn't expired
+ // A return PID of 0 means the process is not running, which may be because it hasn't been (asynchronously) started
+ // yet, or that it died very quickly (if you weren't using waitForDebugger).
+ while (!pid_found && pid_poll_total < pid_poll_timeout)
+ {
+ usleep (pid_poll_interval);
+ pid_poll_total += pid_poll_interval;
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() polling Springboard for pid for %s...", __FUNCTION__, bundleID.c_str());
+ pid_found = SBSProcessIDForDisplayIdentifier(bundleIDCFStr, &pid);
+ }
+
+ if (pid_found)
+ {
+ // If we have a valid process and we created the STDIO file handles,
+ // then remember them on our process class so we can spawn a STDIO
+ // thread and close them when we are done with them.
+ if (process != NULL && process->STDIOIsOurs())
+ {
+ // Release our master pty file descriptor so the pty class doesn't
+ // close it and so we can continue to use it in our STDIO thread
+ int master_fd = pty.ReleaseMasterFD();
+ process->SetChildFileDescriptors(master_fd, master_fd, master_fd);
+ }
+ ProcessMacOSXLog::LogIf (PD_LOG_PROCESS, "%s() => pid = %4.4x", __FUNCTION__, pid);
+ }
+ else
+ {
+ LogError("failed to lookup the process ID for CFBundleIdentifier %s.", bundleID.c_str());
+ }
+ return pid;
+ }
+
+ LogError("unable to launch the application with CFBundleIdentifier '%s' sbs_error = %u", bundleID.c_str(), sbs_error);
+ return LLDB_INVALID_PROCESS_ID;
+}
+
+#endif // #if defined (__arm__)
+
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.h b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.h
new file mode 100644
index 00000000000..01905c6192a
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/ProcessMacOSXRemote.h
@@ -0,0 +1,206 @@
+//===-- ProcessMacOSXRemote.h -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//----------------------------------------------------------------------
+//
+// ProcessMacOSXRemote.h
+// liblldb
+//
+// Created by Greg Clayton on 4/21/09.
+//
+//
+//----------------------------------------------------------------------
+
+#ifndef liblldb_ProcessMacOSXRemote_H_
+#define liblldb_ProcessMacOSXRemote_H_
+
+// C Includes
+
+// C++ Includes
+#include <list>
+
+// Other libraries and framework includes
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+
+class ThreadMacOSXRemote;
+
+class ProcessMacOSXRemote :
+ public Process
+{
+public:
+ friend class ThreadMacOSX;
+
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ ProcessMacOSXRemote(Target& target);
+ virtual ~DCProcessMacOSXRemote();
+
+ static Process* CreateInstance (Target& target);
+
+ //------------------------------------------------------------------
+ // Check if a given Process
+ //------------------------------------------------------------------
+ virtual bool CanDebug(Target &target);
+
+ //------------------------------------------------------------------
+ // Creating a new process, or attaching to an existing one
+ //------------------------------------------------------------------
+ virtual lldb::pid_t DoLaunch (Module* module,
+ char const *argv[], // Can be NULL
+ char const *envp[], // Can be NULL
+ const char *stdin_path, // Can be NULL
+ const char *stdout_path, // Can be NULL
+ const char *stderr_path); // Can be NULL
+ virtual void DidLaunch ();
+ virtual lldb::pid_t DoAttach (lldb::pid_t pid);
+ virtual void DidAttach ();
+
+ //------------------------------------------------------------------
+ // Process Control
+ //------------------------------------------------------------------
+// virtual bool WillResume ();
+ virtual bool DoResume ();
+// virtual void DidResume ();
+
+ virtual bool DoHalt ();
+ virtual bool WillDetach ();
+ virtual bool DoDetach ();
+ virtual bool DoKill (int signal);
+
+ virtual bool ShouldStop ();
+
+ //------------------------------------------------------------------
+ // Process Queries
+ //------------------------------------------------------------------
+ virtual bool IsAlive ();
+ virtual bool IsRunning ();
+ virtual lldb::addr_t GetImageInfoAddress();
+
+ //------------------------------------------------------------------
+ // Process Memory
+ //------------------------------------------------------------------
+ virtual size_t DoReadMemory (lldb::addr_t addr, void *buf, size_t size);
+ virtual size_t DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size);
+
+ //------------------------------------------------------------------
+ // Process STDIO
+ //------------------------------------------------------------------
+ virtual size_t GetSTDOUT (char *buf, size_t buf_size);
+ virtual size_t GetSTDERR (char *buf, size_t buf_size);
+
+ //----------------------------------------------------------------------
+ // Process Breakpoints
+ //----------------------------------------------------------------------
+ virtual size_t
+ GetSoftwareBreakpointTrapOpcode (lldb::BreakpointSite *bp_site);
+
+ //----------------------------------------------------------------------
+ // Process Breakpoints
+ //----------------------------------------------------------------------
+ virtual bool
+ EnableBreakpoint (lldb::BreakpointSite *bp_site);
+
+ virtual bool
+ DisableBreakpoint (lldb::BreakpointSite *bp_site);
+
+ //----------------------------------------------------------------------
+ // Process Watchpoints
+ //----------------------------------------------------------------------
+ virtual bool EnableWatchpoint (WatchpointLocation *wp_loc);
+ virtual bool DisableWatchpoint (WatchpointLocation *wp_loc);
+
+ //------------------------------------------------------------------
+ // Thread Queries
+ //------------------------------------------------------------------
+ virtual Thread * GetCurrentThread ();
+ virtual bool SetCurrentThread (lldb::tid_t tid);
+ virtual Thread * GetThreadAtIndex (uint32_t idx);
+ virtual Thread * GetThreadByID (lldb::tid_t tid);
+ virtual size_t GetNumThreads ();
+
+ virtual ByteOrder GetByteOrder () const;
+
+ virtual DynamicLoader *
+ GetDynamicLoader ();
+
+protected:
+ Flags m_flags; // Process specific flags (see eFlags enums)
+ ArchSpec m_arch_spec;
+ std::auto_ptr<DynamicLoader> m_dynamic_loader_ap;
+ ByteOrder m_byte_order;
+
+ //----------------------------------------------------------------------
+ // Accessors
+ //----------------------------------------------------------------------
+ bool
+ ProcessIDIsValid ( ) const;
+
+ bool
+ IsRunning ( State state )
+ {
+ return state == eStateRunning || IsStepping(state);
+ }
+
+ bool
+ IsStepping ( State state)
+ {
+ return state == eStateStepping;
+ }
+ bool
+ CanResume ( State state)
+ {
+ return state == eStateStopped;
+ }
+
+ ArchSpec&
+ GetArchSpec()
+ {
+ return m_arch_spec;
+ }
+ const ArchSpec&
+ GetArchSpec() const
+ {
+ return m_arch_spec;
+ }
+
+ enum
+ {
+ eFlagsNone = 0,
+ eFlagsAttached = (1 << 0),
+ eFlagsUsingSBS = (1 << 1)
+ };
+
+ void
+ Clear ( );
+
+ Flags &
+ GetFlags ()
+ {
+ return m_flags;
+ }
+
+ const Flags &
+ GetFlags () const
+ {
+ return m_flags;
+ }
+
+ uint32_t
+ UpdateThreadListIfNeeded ();
+
+private:
+ //------------------------------------------------------------------
+ // For ProcessMacOSXRemote only
+ //------------------------------------------------------------------
+ DISALLOW_COPY_AND_ASSIGN (ProcessMacOSXRemote);
+
+};
+
+#endif // liblldb_ProcessMacOSXRemote_H_
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.cpp
new file mode 100644
index 00000000000..37472547f55
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.cpp
@@ -0,0 +1,1448 @@
+//===-- RegisterContextMach_arm.cpp -----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextMach_arm.h"
+
+// C Includes
+#include <mach/thread_act.h>
+
+// C++ Includes
+// Other libraries and framework includes
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/Scalar.h"
+
+// Project includes
+#include "ARM_GCC_Registers.h"
+#include "ARM_DWARF_Registers.h"
+#include "ProcessMacOSXLog.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum
+{
+ gpr_r0 = 0,
+ gpr_r1,
+ gpr_r2,
+ gpr_r3,
+ gpr_r4,
+ gpr_r5,
+ gpr_r6,
+ gpr_r7,
+ gpr_r8,
+ gpr_r9,
+ gpr_r10,
+ gpr_r11,
+ gpr_r12,
+ gpr_r13, gpr_sp = gpr_r13,
+ gpr_r14, gpr_lr = gpr_r14,
+ gpr_r15, gpr_pc = gpr_r15,
+ gpr_cpsr,
+
+ fpu_s0,
+ fpu_s1,
+ fpu_s2,
+ fpu_s3,
+ fpu_s4,
+ fpu_s5,
+ fpu_s6,
+ fpu_s7,
+ fpu_s8,
+ fpu_s9,
+ fpu_s10,
+ fpu_s11,
+ fpu_s12,
+ fpu_s13,
+ fpu_s14,
+ fpu_s15,
+ fpu_s16,
+ fpu_s17,
+ fpu_s18,
+ fpu_s19,
+ fpu_s20,
+ fpu_s21,
+ fpu_s22,
+ fpu_s23,
+ fpu_s24,
+ fpu_s25,
+ fpu_s26,
+ fpu_s27,
+ fpu_s28,
+ fpu_s29,
+ fpu_s30,
+ fpu_s31,
+ fpu_fpscr,
+
+ exc_exception,
+ exc_fsr,
+ exc_far,
+
+ dbg_bvr0,
+ dbg_bvr1,
+ dbg_bvr2,
+ dbg_bvr3,
+ dbg_bvr4,
+ dbg_bvr5,
+ dbg_bvr6,
+ dbg_bvr7,
+ dbg_bvr8,
+ dbg_bvr9,
+ dbg_bvr10,
+ dbg_bvr11,
+ dbg_bvr12,
+ dbg_bvr13,
+ dbg_bvr14,
+ dbg_bvr15,
+
+ dbg_bcr0,
+ dbg_bcr1,
+ dbg_bcr2,
+ dbg_bcr3,
+ dbg_bcr4,
+ dbg_bcr5,
+ dbg_bcr6,
+ dbg_bcr7,
+ dbg_bcr8,
+ dbg_bcr9,
+ dbg_bcr10,
+ dbg_bcr11,
+ dbg_bcr12,
+ dbg_bcr13,
+ dbg_bcr14,
+ dbg_bcr15,
+
+ dbg_wvr0,
+ dbg_wvr1,
+ dbg_wvr2,
+ dbg_wvr3,
+ dbg_wvr4,
+ dbg_wvr5,
+ dbg_wvr6,
+ dbg_wvr7,
+ dbg_wvr8,
+ dbg_wvr9,
+ dbg_wvr10,
+ dbg_wvr11,
+ dbg_wvr12,
+ dbg_wvr13,
+ dbg_wvr14,
+ dbg_wvr15,
+
+ dbg_wcr0,
+ dbg_wcr1,
+ dbg_wcr2,
+ dbg_wcr3,
+ dbg_wcr4,
+ dbg_wcr5,
+ dbg_wcr6,
+ dbg_wcr7,
+ dbg_wcr8,
+ dbg_wcr9,
+ dbg_wcr10,
+ dbg_wcr11,
+ dbg_wcr12,
+ dbg_wcr13,
+ dbg_wcr14,
+ dbg_wcr15,
+
+ k_num_registers
+};
+
+
+RegisterContextMach_arm::RegisterContextMach_arm(Thread &thread, StackFrame *frame) :
+ RegisterContext(thread, frame),
+ gpr(),
+ fpu(),
+ exc()
+{
+ uint32_t i;
+ for (i=0; i<kNumErrors; i++)
+ {
+ gpr_errs[i] = -1;
+ fpu_errs[i] = -1;
+ exc_errs[i] = -1;
+ }
+}
+
+RegisterContextMach_arm::~RegisterContextMach_arm()
+{
+}
+
+
+#define GPR_OFFSET(idx) ((idx) * 4)
+#define FPU_OFFSET(idx) ((idx) * 4 + sizeof (RegisterContextMach_arm::GPR))
+#define EXC_OFFSET(idx) ((idx) * 4 + sizeof (RegisterContextMach_arm::GPR) + sizeof (RegisterContextMach_arm::FPU))
+#define DBG_OFFSET(reg) (offsetof (RegisterContextMach_arm::DBG, reg) + sizeof (RegisterContextMach_arm::GPR) + sizeof (RegisterContextMach_arm::FPU) + sizeof (RegisterContextMach_arm::EXC))
+
+#define DEFINE_DBG(reg, i) #reg, NULL, sizeof(((RegisterContextMach_arm::DBG *)NULL)->reg[i]), DBG_OFFSET(reg[i]), eEncodingUint, eFormatHex, dbg_##reg##i, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }
+#define REG_CONTEXT_SIZE (sizeof (RegisterContextMach_arm::GPR) + sizeof (RegisterContextMach_arm::FPU) + sizeof (RegisterContextMach_arm::EXC))
+// General purpose registers
+static lldb::RegisterInfo
+g_register_infos[] =
+{
+// NAME ALT SZ OFFSET ENCODING FORMAT NATIVE COMPILER DWARF GENERIC
+// ====== ======= == ============= ============= ============ ========== =============== =============== =========
+{ "r0", NULL, 4, GPR_OFFSET(0), eEncodingUint, eFormatHex, gpr_r0, { gcc_r0, dwarf_r0, LLDB_INVALID_REGNUM }},
+{ "r1", NULL, 4, GPR_OFFSET(1), eEncodingUint, eFormatHex, gpr_r1, { gcc_r1, dwarf_r1, LLDB_INVALID_REGNUM }},
+{ "r2", NULL, 4, GPR_OFFSET(2), eEncodingUint, eFormatHex, gpr_r2, { gcc_r2, dwarf_r2, LLDB_INVALID_REGNUM }},
+{ "r3", NULL, 4, GPR_OFFSET(3), eEncodingUint, eFormatHex, gpr_r3, { gcc_r3, dwarf_r3, LLDB_INVALID_REGNUM }},
+{ "r4", NULL, 4, GPR_OFFSET(4), eEncodingUint, eFormatHex, gpr_r4, { gcc_r4, dwarf_r4, LLDB_INVALID_REGNUM }},
+{ "r5", NULL, 4, GPR_OFFSET(5), eEncodingUint, eFormatHex, gpr_r5, { gcc_r5, dwarf_r5, LLDB_INVALID_REGNUM }},
+{ "r6", NULL, 4, GPR_OFFSET(6), eEncodingUint, eFormatHex, gpr_r6, { gcc_r6, dwarf_r6, LLDB_INVALID_REGNUM }},
+{ "r7", NULL, 4, GPR_OFFSET(7), eEncodingUint, eFormatHex, gpr_r7, { gcc_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP }},
+{ "r8", NULL, 4, GPR_OFFSET(8), eEncodingUint, eFormatHex, gpr_r8, { gcc_r8, dwarf_r8, LLDB_INVALID_REGNUM }},
+{ "r9", NULL, 4, GPR_OFFSET(9), eEncodingUint, eFormatHex, gpr_r9, { gcc_r9, dwarf_r9, LLDB_INVALID_REGNUM }},
+{ "r10", NULL, 4, GPR_OFFSET(10), eEncodingUint, eFormatHex, gpr_r10, { gcc_r10, dwarf_r10, LLDB_INVALID_REGNUM }},
+{ "r11", NULL, 4, GPR_OFFSET(11), eEncodingUint, eFormatHex, gpr_r11, { gcc_r11, dwarf_r11, LLDB_INVALID_REGNUM }},
+{ "r12", NULL, 4, GPR_OFFSET(12), eEncodingUint, eFormatHex, gpr_r12, { gcc_r12, dwarf_r12, LLDB_INVALID_REGNUM }},
+{ "sp", "r13", 4, GPR_OFFSET(13), eEncodingUint, eFormatHex, gpr_sp, { gcc_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP }},
+{ "lr", "r14", 4, GPR_OFFSET(14), eEncodingUint, eFormatHex, gpr_lr, { gcc_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA }},
+{ "pc", "r15", 4, GPR_OFFSET(15), eEncodingUint, eFormatHex, gpr_pc, { gcc_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC }},
+{ "cpsr", "psr", 4, GPR_OFFSET(16), eEncodingUint, eFormatHex, gpr_cpsr, { gcc_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS }},
+
+{ "s0", NULL, 4, FPU_OFFSET(0), eEncodingIEEE754,eFormatFloat, fpu_s0, { LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM }},
+{ "s1", NULL, 4, FPU_OFFSET(1), eEncodingIEEE754,eFormatFloat, fpu_s1, { LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM }},
+{ "s2", NULL, 4, FPU_OFFSET(2), eEncodingIEEE754,eFormatFloat, fpu_s2, { LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM }},
+{ "s3", NULL, 4, FPU_OFFSET(3), eEncodingIEEE754,eFormatFloat, fpu_s3, { LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM }},
+{ "s4", NULL, 4, FPU_OFFSET(4), eEncodingIEEE754,eFormatFloat, fpu_s4, { LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM }},
+{ "s5", NULL, 4, FPU_OFFSET(5), eEncodingIEEE754,eFormatFloat, fpu_s5, { LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM }},
+{ "s6", NULL, 4, FPU_OFFSET(6), eEncodingIEEE754,eFormatFloat, fpu_s6, { LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM }},
+{ "s7", NULL, 4, FPU_OFFSET(7), eEncodingIEEE754,eFormatFloat, fpu_s7, { LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM }},
+{ "s8", NULL, 4, FPU_OFFSET(8), eEncodingIEEE754,eFormatFloat, fpu_s8, { LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM }},
+{ "s9", NULL, 4, FPU_OFFSET(9), eEncodingIEEE754,eFormatFloat, fpu_s9, { LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM }},
+{ "s10", NULL, 4, FPU_OFFSET(10), eEncodingIEEE754,eFormatFloat, fpu_s10, { LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM }},
+{ "s11", NULL, 4, FPU_OFFSET(11), eEncodingIEEE754,eFormatFloat, fpu_s11, { LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM }},
+{ "s12", NULL, 4, FPU_OFFSET(12), eEncodingIEEE754,eFormatFloat, fpu_s12, { LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM }},
+{ "s13", NULL, 4, FPU_OFFSET(13), eEncodingIEEE754,eFormatFloat, fpu_s13, { LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM }},
+{ "s14", NULL, 4, FPU_OFFSET(14), eEncodingIEEE754,eFormatFloat, fpu_s14, { LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM }},
+{ "s15", NULL, 4, FPU_OFFSET(15), eEncodingIEEE754,eFormatFloat, fpu_s15, { LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM }},
+{ "s16", NULL, 4, FPU_OFFSET(16), eEncodingIEEE754,eFormatFloat, fpu_s16, { LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM }},
+{ "s17", NULL, 4, FPU_OFFSET(17), eEncodingIEEE754,eFormatFloat, fpu_s17, { LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM }},
+{ "s18", NULL, 4, FPU_OFFSET(18), eEncodingIEEE754,eFormatFloat, fpu_s18, { LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM }},
+{ "s19", NULL, 4, FPU_OFFSET(19), eEncodingIEEE754,eFormatFloat, fpu_s19, { LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM }},
+{ "s20", NULL, 4, FPU_OFFSET(20), eEncodingIEEE754,eFormatFloat, fpu_s20, { LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM }},
+{ "s21", NULL, 4, FPU_OFFSET(21), eEncodingIEEE754,eFormatFloat, fpu_s21, { LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM }},
+{ "s22", NULL, 4, FPU_OFFSET(22), eEncodingIEEE754,eFormatFloat, fpu_s22, { LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM }},
+{ "s23", NULL, 4, FPU_OFFSET(23), eEncodingIEEE754,eFormatFloat, fpu_s23, { LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM }},
+{ "s24", NULL, 4, FPU_OFFSET(24), eEncodingIEEE754,eFormatFloat, fpu_s24, { LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM }},
+{ "s25", NULL, 4, FPU_OFFSET(25), eEncodingIEEE754,eFormatFloat, fpu_s25, { LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM }},
+{ "s26", NULL, 4, FPU_OFFSET(26), eEncodingIEEE754,eFormatFloat, fpu_s26, { LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM }},
+{ "s27", NULL, 4, FPU_OFFSET(27), eEncodingIEEE754,eFormatFloat, fpu_s27, { LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM }},
+{ "s28", NULL, 4, FPU_OFFSET(28), eEncodingIEEE754,eFormatFloat, fpu_s28, { LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM }},
+{ "s29", NULL, 4, FPU_OFFSET(29), eEncodingIEEE754,eFormatFloat, fpu_s29, { LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM }},
+{ "s30", NULL, 4, FPU_OFFSET(30), eEncodingIEEE754,eFormatFloat, fpu_s30, { LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM }},
+{ "s31", NULL, 4, FPU_OFFSET(31), eEncodingIEEE754,eFormatFloat, fpu_s31, { LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM }},
+{ "fpscr", NULL, 4, FPU_OFFSET(32), eEncodingUint, eFormatHex, fpu_fpscr, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }},
+
+{ "exception",NULL, 4, EXC_OFFSET(0), eEncodingUint, eFormatHex, exc_exception,{ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }},
+{ "fsr", NULL, 4, EXC_OFFSET(1), eEncodingUint, eFormatHex, exc_fsr, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }},
+{ "far", NULL, 4, EXC_OFFSET(2), eEncodingUint, eFormatHex, exc_far, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }},
+
+{ DEFINE_DBG (bvr, 0) },
+{ DEFINE_DBG (bvr, 0) },
+{ DEFINE_DBG (bvr, 1) },
+{ DEFINE_DBG (bvr, 2) },
+{ DEFINE_DBG (bvr, 3) },
+{ DEFINE_DBG (bvr, 4) },
+{ DEFINE_DBG (bvr, 5) },
+{ DEFINE_DBG (bvr, 6) },
+{ DEFINE_DBG (bvr, 7) },
+{ DEFINE_DBG (bvr, 8) },
+{ DEFINE_DBG (bvr, 9) },
+{ DEFINE_DBG (bvr, 10) },
+{ DEFINE_DBG (bvr, 11) },
+{ DEFINE_DBG (bvr, 12) },
+{ DEFINE_DBG (bvr, 13) },
+{ DEFINE_DBG (bvr, 14) },
+{ DEFINE_DBG (bvr, 15) },
+
+{ DEFINE_DBG (bcr, 0) },
+{ DEFINE_DBG (bcr, 0) },
+{ DEFINE_DBG (bcr, 1) },
+{ DEFINE_DBG (bcr, 2) },
+{ DEFINE_DBG (bcr, 3) },
+{ DEFINE_DBG (bcr, 4) },
+{ DEFINE_DBG (bcr, 5) },
+{ DEFINE_DBG (bcr, 6) },
+{ DEFINE_DBG (bcr, 7) },
+{ DEFINE_DBG (bcr, 8) },
+{ DEFINE_DBG (bcr, 9) },
+{ DEFINE_DBG (bcr, 10) },
+{ DEFINE_DBG (bcr, 11) },
+{ DEFINE_DBG (bcr, 12) },
+{ DEFINE_DBG (bcr, 13) },
+{ DEFINE_DBG (bcr, 14) },
+{ DEFINE_DBG (bcr, 15) },
+
+{ DEFINE_DBG (wvr, 0) },
+{ DEFINE_DBG (wvr, 0) },
+{ DEFINE_DBG (wvr, 1) },
+{ DEFINE_DBG (wvr, 2) },
+{ DEFINE_DBG (wvr, 3) },
+{ DEFINE_DBG (wvr, 4) },
+{ DEFINE_DBG (wvr, 5) },
+{ DEFINE_DBG (wvr, 6) },
+{ DEFINE_DBG (wvr, 7) },
+{ DEFINE_DBG (wvr, 8) },
+{ DEFINE_DBG (wvr, 9) },
+{ DEFINE_DBG (wvr, 10) },
+{ DEFINE_DBG (wvr, 11) },
+{ DEFINE_DBG (wvr, 12) },
+{ DEFINE_DBG (wvr, 13) },
+{ DEFINE_DBG (wvr, 14) },
+{ DEFINE_DBG (wvr, 15) },
+
+{ DEFINE_DBG (wcr, 0) },
+{ DEFINE_DBG (wcr, 0) },
+{ DEFINE_DBG (wcr, 1) },
+{ DEFINE_DBG (wcr, 2) },
+{ DEFINE_DBG (wcr, 3) },
+{ DEFINE_DBG (wcr, 4) },
+{ DEFINE_DBG (wcr, 5) },
+{ DEFINE_DBG (wcr, 6) },
+{ DEFINE_DBG (wcr, 7) },
+{ DEFINE_DBG (wcr, 8) },
+{ DEFINE_DBG (wcr, 9) },
+{ DEFINE_DBG (wcr, 10) },
+{ DEFINE_DBG (wcr, 11) },
+{ DEFINE_DBG (wcr, 12) },
+{ DEFINE_DBG (wcr, 13) },
+{ DEFINE_DBG (wcr, 14) },
+{ DEFINE_DBG (wcr, 15) }
+};
+
+// General purpose registers
+static uint32_t
+g_gpr_regnums[] =
+{
+ gpr_r0,
+ gpr_r1,
+ gpr_r2,
+ gpr_r3,
+ gpr_r4,
+ gpr_r5,
+ gpr_r6,
+ gpr_r7,
+ gpr_r8,
+ gpr_r9,
+ gpr_r10,
+ gpr_r11,
+ gpr_r12,
+ gpr_sp,
+ gpr_lr,
+ gpr_pc,
+ gpr_cpsr
+};
+
+// Floating point registers
+static uint32_t
+g_fpu_regnums[] =
+{
+ fpu_s0,
+ fpu_s1,
+ fpu_s2,
+ fpu_s3,
+ fpu_s4,
+ fpu_s5,
+ fpu_s6,
+ fpu_s7,
+ fpu_s8,
+ fpu_s9,
+ fpu_s10,
+ fpu_s11,
+ fpu_s12,
+ fpu_s13,
+ fpu_s14,
+ fpu_s15,
+ fpu_s16,
+ fpu_s17,
+ fpu_s18,
+ fpu_s19,
+ fpu_s20,
+ fpu_s21,
+ fpu_s22,
+ fpu_s23,
+ fpu_s24,
+ fpu_s25,
+ fpu_s26,
+ fpu_s27,
+ fpu_s28,
+ fpu_s29,
+ fpu_s30,
+ fpu_s31,
+ fpu_fpscr,
+};
+
+// Exception registers
+
+static uint32_t
+g_exc_regnums[] =
+{
+ exc_exception,
+ exc_fsr,
+ exc_far,
+};
+
+static size_t k_num_register_infos = (sizeof(g_register_infos)/sizeof(RegisterInfo));
+
+void
+RegisterContextMach_arm::Invalidate ()
+{
+ InvalidateAllRegisterStates();
+}
+
+
+size_t
+RegisterContextMach_arm::GetRegisterCount ()
+{
+ assert(k_num_register_infos == k_num_registers);
+ return k_num_registers;
+}
+
+const RegisterInfo *
+RegisterContextMach_arm::GetRegisterInfoAtIndex (uint32_t reg)
+{
+ assert(k_num_register_infos == k_num_registers);
+ if (reg < k_num_registers)
+ return &g_register_infos[reg];
+ return NULL;
+}
+
+size_t
+RegisterContextMach_arm::GetRegisterInfosCount ()
+{
+ return k_num_register_infos;
+}
+
+const RegisterInfo *
+RegisterContextMach_arm::GetRegisterInfos ()
+{
+ return g_register_infos;
+}
+
+
+// Number of registers in each register set
+const size_t k_num_gpr_registers = sizeof(g_gpr_regnums) / sizeof(uint32_t);
+const size_t k_num_fpu_registers = sizeof(g_fpu_regnums) / sizeof(uint32_t);
+const size_t k_num_exc_registers = sizeof(g_exc_regnums) / sizeof(uint32_t);
+
+//----------------------------------------------------------------------
+// Register set definitions. The first definitions at register set index
+// of zero is for all registers, followed by other registers sets. The
+// register information for the all register set need not be filled in.
+//----------------------------------------------------------------------
+static const RegisterSet g_reg_sets[] =
+{
+ { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums, },
+ { "Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums },
+ { "Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums }
+};
+
+const size_t k_num_regsets = sizeof(g_reg_sets) / sizeof(RegisterSet);
+
+
+size_t
+RegisterContextMach_arm::GetRegisterSetCount ()
+{
+ return k_num_regsets;
+}
+
+const RegisterSet *
+RegisterContextMach_arm::GetRegisterSet (uint32_t reg_set)
+{
+ if (reg_set < k_num_regsets)
+ return &g_reg_sets[reg_set];
+ return NULL;
+}
+
+
+//----------------------------------------------------------------------
+// Register information defintions for 32 bit i386.
+//----------------------------------------------------------------------
+int
+RegisterContextMach_arm::GetSetForNativeRegNum (int reg)
+{
+ if (reg < fpu_s0)
+ return GPRRegSet;
+ else if (reg < exc_exception)
+ return FPURegSet;
+ else if (reg < k_num_registers)
+ return EXCRegSet;
+ return -1;
+}
+
+kern_return_t
+RegisterContextMach_arm::ReadGPR (bool force)
+{
+ int set = GPRRegSet;
+ if (force || !RegisterSetIsCached(set))
+ {
+ mach_msg_type_number_t count = GPRWordCount;
+ SetError(GPRRegSet, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&gpr, &count));
+ }
+ return GetError(GPRRegSet, Read);
+}
+
+kern_return_t
+RegisterContextMach_arm::ReadFPU (bool force)
+{
+ int set = FPURegSet;
+ if (force || !RegisterSetIsCached(set))
+ {
+ mach_msg_type_number_t count = FPUWordCount;
+ SetError(FPURegSet, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&fpu, &count));
+ }
+ return GetError(FPURegSet, Read);
+}
+
+kern_return_t
+RegisterContextMach_arm::ReadEXC (bool force)
+{
+ int set = EXCRegSet;
+ if (force || !RegisterSetIsCached(set))
+ {
+ mach_msg_type_number_t count = EXCWordCount;
+ SetError(EXCRegSet, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&exc, &count));
+ }
+ return GetError(EXCRegSet, Read);
+}
+
+kern_return_t
+RegisterContextMach_arm::ReadDBG (bool force)
+{
+ int set = DBGRegSet;
+ if (force || !RegisterSetIsCached(set))
+ {
+ mach_msg_type_number_t count = DBGWordCount;
+ SetError(DBGRegSet, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&dbg, &count));
+ }
+ return GetError(DBGRegSet, Read);
+}
+
+kern_return_t
+RegisterContextMach_arm::WriteGPR ()
+{
+ int set = GPRRegSet;
+ if (!RegisterSetIsCached(set))
+ {
+ SetError (set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError(GPRRegSet, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&gpr, GPRWordCount));
+ return GetError(GPRRegSet, Write);
+}
+
+kern_return_t
+RegisterContextMach_arm::WriteFPU ()
+{
+ int set = FPURegSet;
+ if (!RegisterSetIsCached(set))
+ {
+ SetError (set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError(FPURegSet, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&fpu, FPUWordCount));
+ return GetError(FPURegSet, Write);
+}
+
+kern_return_t
+RegisterContextMach_arm::WriteEXC ()
+{
+ int set = EXCRegSet;
+ if (!RegisterSetIsCached(set))
+ {
+ SetError (set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError(EXCRegSet, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&exc, EXCWordCount));
+ return GetError(EXCRegSet, Write);
+}
+
+kern_return_t
+RegisterContextMach_arm::WriteDBG ()
+{
+ int set = DBGRegSet;
+ if (!RegisterSetIsCached(set))
+ {
+ SetError (set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError(DBGRegSet, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&dbg, DBGWordCount));
+ return GetError(DBGRegSet, Write);
+}
+
+
+kern_return_t
+RegisterContextMach_arm::ReadRegisterSet (uint32_t set, bool force)
+{
+ switch (set)
+ {
+ case GPRRegSet: return ReadGPR(force);
+ case FPURegSet: return ReadFPU(force);
+ case EXCRegSet: return ReadEXC(force);
+ case DBGRegSet: return ReadDBG(force);
+ default: break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+kern_return_t
+RegisterContextMach_arm::WriteRegisterSet (uint32_t set)
+{
+ // Make sure we have a valid context to set.
+ if (RegisterSetIsCached(set))
+ {
+ switch (set)
+ {
+ case GPRRegSet: return WriteGPR();
+ case FPURegSet: return WriteFPU();
+ case EXCRegSet: return WriteEXC();
+ case DBGRegSet: return WriteDBG();
+ default: break;
+ }
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+void
+RegisterContextMach_arm::LogDBGRegisters (Log *log, const DBG& dbg)
+{
+ if (log)
+ {
+ for (uint32_t i=0; i<16; i++)
+ log->Printf("BVR%-2u/BCR%-2u = { 0x%8.8x, 0x%8.8x } WVR%-2u/WCR%-2u = { 0x%8.8x, 0x%8.8x }",
+ i, i, dbg.bvr[i], dbg.bcr[i],
+ i, i, dbg.wvr[i], dbg.wcr[i]);
+ }
+}
+
+
+bool
+RegisterContextMach_arm::ReadRegisterValue (uint32_t reg, Scalar &value)
+{
+ int set = RegisterContextMach_arm::GetSetForNativeRegNum (reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+ switch (reg)
+ {
+ case gpr_r0:
+ case gpr_r1:
+ case gpr_r2:
+ case gpr_r3:
+ case gpr_r4:
+ case gpr_r5:
+ case gpr_r6:
+ case gpr_r7:
+ case gpr_r8:
+ case gpr_r9:
+ case gpr_r10:
+ case gpr_r11:
+ case gpr_r12:
+ case gpr_sp:
+ case gpr_lr:
+ case gpr_pc:
+ case gpr_cpsr:
+ value = gpr.r[reg - gpr_r0];
+ break;
+
+ case fpu_s0:
+ case fpu_s1:
+ case fpu_s2:
+ case fpu_s3:
+ case fpu_s4:
+ case fpu_s5:
+ case fpu_s6:
+ case fpu_s7:
+ case fpu_s8:
+ case fpu_s9:
+ case fpu_s10:
+ case fpu_s11:
+ case fpu_s12:
+ case fpu_s13:
+ case fpu_s14:
+ case fpu_s15:
+ case fpu_s16:
+ case fpu_s17:
+ case fpu_s18:
+ case fpu_s19:
+ case fpu_s20:
+ case fpu_s21:
+ case fpu_s22:
+ case fpu_s23:
+ case fpu_s24:
+ case fpu_s25:
+ case fpu_s26:
+ case fpu_s27:
+ case fpu_s28:
+ case fpu_s29:
+ case fpu_s30:
+ case fpu_s31:
+ value = fpu.floats.s[reg];
+ break;
+
+ case fpu_fpscr:
+ value = fpu.fpscr;
+ break;
+
+ case exc_exception:
+ value = exc.exception;
+ break;
+ case exc_fsr:
+ value = exc.fsr;
+ break;
+ case exc_far:
+ value = exc.far;
+ break;
+
+ default:
+ return false;
+
+ }
+ return true;
+}
+
+
+bool
+RegisterContextMach_arm::WriteRegisterValue (uint32_t reg, const Scalar &value)
+{
+ int set = GetSetForNativeRegNum (reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+ switch (reg)
+ {
+ case gpr_r0:
+ case gpr_r1:
+ case gpr_r2:
+ case gpr_r3:
+ case gpr_r4:
+ case gpr_r5:
+ case gpr_r6:
+ case gpr_r7:
+ case gpr_r8:
+ case gpr_r9:
+ case gpr_r10:
+ case gpr_r11:
+ case gpr_r12:
+ case gpr_sp:
+ case gpr_lr:
+ case gpr_pc:
+ case gpr_cpsr:
+ gpr.r[reg - gpr_r0] = value.UInt(0);
+ break;
+
+ case fpu_s0:
+ case fpu_s1:
+ case fpu_s2:
+ case fpu_s3:
+ case fpu_s4:
+ case fpu_s5:
+ case fpu_s6:
+ case fpu_s7:
+ case fpu_s8:
+ case fpu_s9:
+ case fpu_s10:
+ case fpu_s11:
+ case fpu_s12:
+ case fpu_s13:
+ case fpu_s14:
+ case fpu_s15:
+ case fpu_s16:
+ case fpu_s17:
+ case fpu_s18:
+ case fpu_s19:
+ case fpu_s20:
+ case fpu_s21:
+ case fpu_s22:
+ case fpu_s23:
+ case fpu_s24:
+ case fpu_s25:
+ case fpu_s26:
+ case fpu_s27:
+ case fpu_s28:
+ case fpu_s29:
+ case fpu_s30:
+ case fpu_s31:
+ fpu.floats.s[reg] = value.UInt(0);
+ break;
+
+ case fpu_fpscr:
+ fpu.fpscr = value.UInt(0);
+ break;
+
+ case exc_exception:
+ exc.exception = value.UInt(0);
+ break;
+ case exc_fsr:
+ exc.fsr = value.UInt(0);
+ break;
+ case exc_far:
+ exc.far = value.UInt(0);
+ break;
+
+ default:
+ return false;
+
+ }
+ return WriteRegisterSet(set) == KERN_SUCCESS;
+}
+
+bool
+RegisterContextMach_arm::ReadRegisterBytes (uint32_t reg, DataExtractor &data)
+{
+ int set = RegisterContextMach_arm::GetSetForNativeRegNum (reg);
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+ const RegisterInfo * reg_info = GetRegisterInfoAtIndex (reg);
+ if (reg_info == NULL)
+ return false;
+
+ switch (reg)
+ {
+ case gpr_r0:
+ case gpr_r1:
+ case gpr_r2:
+ case gpr_r3:
+ case gpr_r4:
+ case gpr_r5:
+ case gpr_r6:
+ case gpr_r7:
+ case gpr_r8:
+ case gpr_r9:
+ case gpr_r10:
+ case gpr_r11:
+ case gpr_r12:
+ case gpr_sp:
+ case gpr_lr:
+ case gpr_pc:
+ case gpr_cpsr:
+ data.SetData(&gpr.r[reg - gpr_r0], reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_s0:
+ case fpu_s1:
+ case fpu_s2:
+ case fpu_s3:
+ case fpu_s4:
+ case fpu_s5:
+ case fpu_s6:
+ case fpu_s7:
+ case fpu_s8:
+ case fpu_s9:
+ case fpu_s10:
+ case fpu_s11:
+ case fpu_s12:
+ case fpu_s13:
+ case fpu_s14:
+ case fpu_s15:
+ case fpu_s16:
+ case fpu_s17:
+ case fpu_s18:
+ case fpu_s19:
+ case fpu_s20:
+ case fpu_s21:
+ case fpu_s22:
+ case fpu_s23:
+ case fpu_s24:
+ case fpu_s25:
+ case fpu_s26:
+ case fpu_s27:
+ case fpu_s28:
+ case fpu_s29:
+ case fpu_s30:
+ case fpu_s31:
+ data.SetData(&fpu.floats.s[reg - fpu_s0], reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_fpscr:
+ data.SetData(&fpu.fpscr, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case exc_exception:
+ data.SetData(&exc.exception, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case exc_fsr:
+ data.SetData(&exc.fsr, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case exc_far:
+ data.SetData(&exc.far, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool
+RegisterContextMach_arm::WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset)
+{
+ int set = GetSetForNativeRegNum (reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+
+ const RegisterInfo * reg_info = GetRegisterInfoAtIndex (reg);
+ if (reg_info == NULL && data.ValidOffsetForDataOfSize(data_offset, reg_info->byte_size))
+ return false;
+
+ uint32_t offset = data_offset;
+ switch (reg)
+ {
+ case gpr_r0:
+ case gpr_r1:
+ case gpr_r2:
+ case gpr_r3:
+ case gpr_r4:
+ case gpr_r5:
+ case gpr_r6:
+ case gpr_r7:
+ case gpr_r8:
+ case gpr_r9:
+ case gpr_r10:
+ case gpr_r11:
+ case gpr_r12:
+ case gpr_sp:
+ case gpr_lr:
+ case gpr_pc:
+ case gpr_cpsr:
+ gpr.r[reg - gpr_r0] = data.GetU32 (&offset);
+ break;
+
+ case fpu_s0:
+ case fpu_s1:
+ case fpu_s2:
+ case fpu_s3:
+ case fpu_s4:
+ case fpu_s5:
+ case fpu_s6:
+ case fpu_s7:
+ case fpu_s8:
+ case fpu_s9:
+ case fpu_s10:
+ case fpu_s11:
+ case fpu_s12:
+ case fpu_s13:
+ case fpu_s14:
+ case fpu_s15:
+ case fpu_s16:
+ case fpu_s17:
+ case fpu_s18:
+ case fpu_s19:
+ case fpu_s20:
+ case fpu_s21:
+ case fpu_s22:
+ case fpu_s23:
+ case fpu_s24:
+ case fpu_s25:
+ case fpu_s26:
+ case fpu_s27:
+ case fpu_s28:
+ case fpu_s29:
+ case fpu_s30:
+ case fpu_s31:
+ fpu.floats.s[reg - fpu_s0] = data.GetU32 (&offset);
+ break;
+
+ case fpu_fpscr:
+ fpu.fpscr = data.GetU32 (&offset);
+ break;
+
+ case exc_exception:
+ fpu.fpscr = data.GetU32 (&offset);
+ break;
+
+ case exc_fsr:
+ exc.fsr = data.GetU32 (&offset);
+ break;
+
+ case exc_far:
+ exc.far = data.GetU32 (&offset);
+ break;
+
+ default:
+ return false;
+ }
+ return WriteRegisterSet(set) == KERN_SUCCESS;
+}
+
+bool
+RegisterContextMach_arm::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
+{
+ data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0));
+ if (data_sp &&
+ ReadGPR (false) == KERN_SUCCESS &&
+ ReadFPU (false) == KERN_SUCCESS &&
+ ReadEXC (false) == KERN_SUCCESS)
+ {
+ uint8_t *dst = data_sp->GetBytes();
+ ::memcpy (dst, &gpr, sizeof(gpr));
+ dst += sizeof(gpr);
+
+ ::memcpy (dst, &fpu, sizeof(fpu));
+ dst += sizeof(gpr);
+
+ ::memcpy (dst, &exc, sizeof(exc));
+ return true;
+ }
+ return false;
+}
+
+bool
+RegisterContextMach_arm::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp)
+{
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE)
+ {
+ const uint8_t *src = data_sp->GetBytes();
+ ::memcpy (&gpr, src, sizeof(gpr));
+ src += sizeof(gpr);
+
+ ::memcpy (&fpu, src, sizeof(fpu));
+ src += sizeof(gpr);
+
+ ::memcpy (&exc, src, sizeof(exc));
+ uint32_t success_count = 0;
+ if (WriteGPR() == KERN_SUCCESS)
+ ++success_count;
+ if (WriteFPU() == KERN_SUCCESS)
+ ++success_count;
+ if (WriteEXC() == KERN_SUCCESS)
+ ++success_count;
+ return success_count == 3;
+ }
+ return false;
+}
+
+uint32_t
+RegisterContextMach_arm::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t reg)
+{
+ if (kind == eRegisterKindGeneric)
+ {
+ switch (reg)
+ {
+ case LLDB_REGNUM_GENERIC_PC: return gpr_pc;
+ case LLDB_REGNUM_GENERIC_SP: return gpr_sp;
+ case LLDB_REGNUM_GENERIC_FP: return gpr_r7;
+ case LLDB_REGNUM_GENERIC_RA: return gpr_lr;
+ case LLDB_REGNUM_GENERIC_FLAGS: return gpr_cpsr;
+ default:
+ break;
+ }
+ }
+ else if (kind == eRegisterKindDWARF)
+ {
+ switch (reg)
+ {
+ case dwarf_r0: return gpr_r0;
+ case dwarf_r1: return gpr_r1;
+ case dwarf_r2: return gpr_r2;
+ case dwarf_r3: return gpr_r3;
+ case dwarf_r4: return gpr_r4;
+ case dwarf_r5: return gpr_r5;
+ case dwarf_r6: return gpr_r6;
+ case dwarf_r7: return gpr_r7;
+ case dwarf_r8: return gpr_r8;
+ case dwarf_r9: return gpr_r9;
+ case dwarf_r10: return gpr_r10;
+ case dwarf_r11: return gpr_r11;
+ case dwarf_r12: return gpr_r12;
+ case dwarf_sp: return gpr_sp;
+ case dwarf_lr: return gpr_lr;
+ case dwarf_pc: return gpr_pc;
+ case dwarf_spsr: return gpr_cpsr;
+
+ case dwarf_s0: return fpu_s0;
+ case dwarf_s1: return fpu_s1;
+ case dwarf_s2: return fpu_s2;
+ case dwarf_s3: return fpu_s3;
+ case dwarf_s4: return fpu_s4;
+ case dwarf_s5: return fpu_s5;
+ case dwarf_s6: return fpu_s6;
+ case dwarf_s7: return fpu_s7;
+ case dwarf_s8: return fpu_s8;
+ case dwarf_s9: return fpu_s9;
+ case dwarf_s10: return fpu_s10;
+ case dwarf_s11: return fpu_s11;
+ case dwarf_s12: return fpu_s12;
+ case dwarf_s13: return fpu_s13;
+ case dwarf_s14: return fpu_s14;
+ case dwarf_s15: return fpu_s15;
+ case dwarf_s16: return fpu_s16;
+ case dwarf_s17: return fpu_s17;
+ case dwarf_s18: return fpu_s18;
+ case dwarf_s19: return fpu_s19;
+ case dwarf_s20: return fpu_s20;
+ case dwarf_s21: return fpu_s21;
+ case dwarf_s22: return fpu_s22;
+ case dwarf_s23: return fpu_s23;
+ case dwarf_s24: return fpu_s24;
+ case dwarf_s25: return fpu_s25;
+ case dwarf_s26: return fpu_s26;
+ case dwarf_s27: return fpu_s27;
+ case dwarf_s28: return fpu_s28;
+ case dwarf_s29: return fpu_s29;
+ case dwarf_s30: return fpu_s30;
+ case dwarf_s31: return fpu_s31;
+
+ default:
+ break;
+ }
+ }
+ else if (kind == eRegisterKindGCC)
+ {
+ switch (reg)
+ {
+ case gcc_r0: return gpr_r0;
+ case gcc_r1: return gpr_r1;
+ case gcc_r2: return gpr_r2;
+ case gcc_r3: return gpr_r3;
+ case gcc_r4: return gpr_r4;
+ case gcc_r5: return gpr_r5;
+ case gcc_r6: return gpr_r6;
+ case gcc_r7: return gpr_r7;
+ case gcc_r8: return gpr_r8;
+ case gcc_r9: return gpr_r9;
+ case gcc_r10: return gpr_r10;
+ case gcc_r11: return gpr_r11;
+ case gcc_r12: return gpr_r12;
+ case gcc_sp: return gpr_sp;
+ case gcc_lr: return gpr_lr;
+ case gcc_pc: return gpr_pc;
+ case gcc_cpsr: return gpr_cpsr;
+ }
+ }
+ return LLDB_INVALID_REGNUM;
+}
+
+
+uint32_t
+RegisterContextMach_arm::NumSupportedHardwareBreakpoints ()
+{
+#if defined (__arm__)
+ // Set the init value to something that will let us know that we need to
+ // autodetect how many breakpoints are supported dynamically...
+ static uint32_t g_num_supported_hw_breakpoints = UINT_MAX
+ if (g_num_supported_hw_breakpoints == UINT_MAX)
+ {
+ // Set this to zero in case we can't tell if there are any HW breakpoints
+ g_num_supported_hw_breakpoints = 0;
+
+ // Read the DBGDIDR to get the number of available hardware breakpoints
+ // However, in some of our current armv7 processors, hardware
+ // breakpoints/watchpoints were not properly connected. So detect those
+ // cases using a field in a sysctl. For now we are using "hw.cpusubtype"
+ // field to distinguish CPU architectures. This is a hack until we can
+ // get <rdar://problem/6372672> fixed, at which point we will switch to
+ // using a different sysctl string that will tell us how many BRPs
+ // are available to us directly without having to read DBGDIDR.
+ uint32_t register_DBGDIDR;
+
+ asm("mrc p14, 0, %0, c0, c0, 0" : "=r" (register_DBGDIDR));
+ uint32_t numBRPs = bits(register_DBGDIDR, 27, 24);
+ // Zero is reserved for the BRP count, so don't increment it if it is zero
+ if (numBRPs > 0)
+ numBRPs++;
+ ProcessMacOSXLog::LogIf(PD_LOG_THREAD, "DBGDIDR=0x%8.8x (number BRP pairs = %u)", register_DBGDIDR, numBRPs);
+
+ if (numBRPs > 0)
+ {
+ uint32_t cpu_subtype;
+ size_t len;
+ len = sizeof(cpusubtype);
+ // TODO: remove this hack and change to using hw.optional.xx when implmented
+ if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0)
+ {
+ ProcessMacOSXLog::LogIf(PD_LOG_THREAD, "hw.cpusubtype=0x%d", cpusubtype);
+ if (cpusubtype == CPU_SUBTYPE_ARM_V7)
+ ProcessMacOSXLog::LogIf(PD_LOG_THREAD, "Hardware breakpoints disabled for armv7 (rdar://problem/6372672)");
+ else
+ g_num_supported_hw_breakpoints = numBRPs;
+ }
+ }
+
+ }
+ return g_num_supported_hw_breakpoints;
+#else
+ // TODO: figure out remote case here!
+ return 6;
+#endif
+}
+
+uint32_t
+RegisterContextMach_arm::SetHardwareBreakpoint (lldb::addr_t addr, size_t size)
+{
+ // Make sure our address isn't bogus
+ if (addr & 1)
+ return LLDB_INVALID_INDEX32;
+
+ kern_return_t kret = ReadDBG (false);
+
+ if (kret == KERN_SUCCESS)
+ {
+ const uint32_t num_hw_breakpoints = NumSupportedHardwareBreakpoints();
+ uint32_t i;
+ for (i=0; i<num_hw_breakpoints; ++i)
+ {
+ if ((dbg.bcr[i] & BCR_ENABLE) == 0)
+ break; // We found an available hw breakpoint slot (in i)
+ }
+
+ // See if we found an available hw breakpoint slot above
+ if (i < num_hw_breakpoints)
+ {
+ // Make sure bits 1:0 are clear in our address
+ dbg.bvr[i] = addr & ~((lldb::addr_t)3);
+
+ if (size == 2 || addr & 2)
+ {
+ uint32_t byte_addr_select = (addr & 2) ? BAS_IMVA_2_3 : BAS_IMVA_0_1;
+
+ // We have a thumb breakpoint
+ // We have an ARM breakpoint
+ dbg.bcr[i] = BCR_M_IMVA_MATCH | // Stop on address mismatch
+ byte_addr_select | // Set the correct byte address select so we only trigger on the correct opcode
+ S_USER | // Which modes should this breakpoint stop in?
+ BCR_ENABLE; // Enable this hardware breakpoint
+ ProcessMacOSXLog::LogIf(PD_LOG_BREAKPOINTS, "RegisterContextMach_arm::EnableHardwareBreakpoint( addr = %8.8p, size = %u ) - BVR%u/BCR%u = 0x%8.8x / 0x%8.8x (Thumb)",
+ addr,
+ size,
+ i,
+ i,
+ dbg.bvr[i],
+ dbg.bcr[i]);
+ }
+ else if (size == 4)
+ {
+ // We have an ARM breakpoint
+ dbg.bcr[i] = BCR_M_IMVA_MATCH | // Stop on address mismatch
+ BAS_IMVA_ALL | // Stop on any of the four bytes following the IMVA
+ S_USER | // Which modes should this breakpoint stop in?
+ BCR_ENABLE; // Enable this hardware breakpoint
+ ProcessMacOSXLog::LogIf(PD_LOG_BREAKPOINTS, "RegisterContextMach_arm::EnableHardwareBreakpoint( addr = %8.8p, size = %u ) - BVR%u/BCR%u = 0x%8.8x / 0x%8.8x (ARM)",
+ addr,
+ size,
+ i,
+ i,
+ dbg.bvr[i],
+ dbg.bcr[i]);
+ }
+
+ kret = WriteDBG();
+ ProcessMacOSXLog::LogIf(PD_LOG_BREAKPOINTS, "RegisterContextMach_arm::EnableHardwareBreakpoint() WriteDBG() => 0x%8.8x.", kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ }
+ else
+ {
+ ProcessMacOSXLog::LogIf(PD_LOG_BREAKPOINTS, "RegisterContextMach_arm::EnableHardwareBreakpoint(addr = %8.8p, size = %u) => all hardware breakpoint resources are being used.", addr, size);
+ }
+ }
+
+ return LLDB_INVALID_INDEX32;
+}
+
+bool
+RegisterContextMach_arm::ClearHardwareBreakpoint (uint32_t hw_index)
+{
+ kern_return_t kret = ReadDBG (false);
+
+ const uint32_t num_hw_points = NumSupportedHardwareBreakpoints();
+ if (kret == KERN_SUCCESS)
+ {
+ if (hw_index < num_hw_points)
+ {
+ dbg.bcr[hw_index] = 0;
+ ProcessMacOSXLog::LogIf(PD_LOG_BREAKPOINTS, "RegisterContextMach_arm::SetHardwareBreakpoint( %u ) - BVR%u = 0x%8.8x BCR%u = 0x%8.8x",
+ hw_index,
+ hw_index,
+ dbg.bvr[hw_index],
+ hw_index,
+ dbg.bcr[hw_index]);
+
+ kret = WriteDBG();
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ }
+ }
+ return false;
+}
+
+uint32_t
+RegisterContextMach_arm::NumSupportedHardwareWatchpoints ()
+{
+#if defined (__arm__)
+ // Set the init value to something that will let us know that we need to
+ // autodetect how many watchpoints are supported dynamically...
+ static uint32_t g_num_supported_hw_watchpoints = UINT_MAX;
+ if (g_num_supported_hw_watchpoints == UINT_MAX)
+ {
+ // Set this to zero in case we can't tell if there are any HW breakpoints
+ g_num_supported_hw_watchpoints = 0;
+ // Read the DBGDIDR to get the number of available hardware breakpoints
+ // However, in some of our current armv7 processors, hardware
+ // breakpoints/watchpoints were not properly connected. So detect those
+ // cases using a field in a sysctl. For now we are using "hw.cpusubtype"
+ // field to distinguish CPU architectures. This is a hack until we can
+ // get <rdar://problem/6372672> fixed, at which point we will switch to
+ // using a different sysctl string that will tell us how many WRPs
+ // are available to us directly without having to read DBGDIDR.
+
+ uint32_t register_DBGDIDR;
+ asm("mrc p14, 0, %0, c0, c0, 0" : "=r" (register_DBGDIDR));
+ uint32_t numWRPs = bits(register_DBGDIDR, 31, 28) + 1;
+ ProcessMacOSXLog::LogIf(PD_LOG_THREAD, "DBGDIDR=0x%8.8x (number WRP pairs = %u)", register_DBGDIDR, numWRPs);
+
+ if (numWRPs > 0)
+ {
+ uint32_t cpusubtype;
+ size_t len;
+ len = sizeof(cpusubtype);
+ // TODO: remove this hack and change to using hw.optional.xx when implmented
+ if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0)
+ {
+ ProcessMacOSXLog::LogIf(PD_LOG_THREAD, "hw.cpusubtype=0x%d", cpusubtype);
+
+ if (cpusubtype == CPU_SUBTYPE_ARM_V7)
+ ProcessMacOSXLog::LogIf(PD_LOG_THREAD, "Hardware watchpoints disabled for armv7 (rdar://problem/6372672)");
+ else
+ g_num_supported_hw_watchpoints = numWRPs;
+ }
+ }
+
+ }
+ return g_num_supported_hw_watchpoints;
+#else
+ // TODO: figure out remote case here!
+ return 2;
+#endif
+}
+
+
+uint32_t
+RegisterContextMach_arm::SetHardwareWatchpoint (lldb::addr_t addr, size_t size, bool read, bool write)
+{
+ ProcessMacOSXLog::LogIf(PD_LOG_WATCHPOINTS, "RegisterContextMach_arm::EnableHardwareWatchpoint(addr = %8.8p, size = %u, read = %u, write = %u)", addr, size, read, write);
+
+ const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints();
+
+ // Can't watch zero bytes
+ if (size == 0)
+ return LLDB_INVALID_INDEX32;
+
+ // We must watch for either read or write
+ if (read == false && write == false)
+ return LLDB_INVALID_INDEX32;
+
+ // Can't watch more than 4 bytes per WVR/WCR pair
+ if (size > 4)
+ return LLDB_INVALID_INDEX32;
+
+ // We can only watch up to four bytes that follow a 4 byte aligned address
+ // per watchpoint register pair. Since we have at most so we can only watch
+ // until the next 4 byte boundary and we need to make sure we can properly
+ // encode this.
+ uint32_t addr_word_offset = addr % 4;
+ ProcessMacOSXLog::LogIf(PD_LOG_WATCHPOINTS, "RegisterContextMach_arm::EnableHardwareWatchpoint() - addr_word_offset = 0x%8.8x", addr_word_offset);
+
+ uint32_t byte_mask = ((1u << size) - 1u) << addr_word_offset;
+ ProcessMacOSXLog::LogIf(PD_LOG_WATCHPOINTS, "RegisterContextMach_arm::EnableHardwareWatchpoint() - byte_mask = 0x%8.8x", byte_mask);
+ if (byte_mask > 0xfu)
+ return LLDB_INVALID_INDEX32;
+
+ // Read the debug state
+ kern_return_t kret = ReadDBG (false);
+
+ if (kret == KERN_SUCCESS)
+ {
+ // Check to make sure we have the needed hardware support
+ uint32_t i = 0;
+
+ for (i=0; i<num_hw_watchpoints; ++i)
+ {
+ if ((dbg.wcr[i] & WCR_ENABLE) == 0)
+ break; // We found an available hw breakpoint slot (in i)
+ }
+
+ // See if we found an available hw breakpoint slot above
+ if (i < num_hw_watchpoints)
+ {
+ // Make the byte_mask into a valid Byte Address Select mask
+ uint32_t byte_address_select = byte_mask << 5;
+ // Make sure bits 1:0 are clear in our address
+ dbg.wvr[i] = addr & ~((lldb::addr_t)3);
+ dbg.wcr[i] = byte_address_select | // Which bytes that follow the IMVA that we will watch
+ S_USER | // Stop only in user mode
+ (read ? WCR_LOAD : 0) | // Stop on read access?
+ (write ? WCR_STORE : 0) | // Stop on write access?
+ WCR_ENABLE; // Enable this watchpoint;
+
+ kret = WriteDBG();
+ ProcessMacOSXLog::LogIf(PD_LOG_WATCHPOINTS, "RegisterContextMach_arm::EnableHardwareWatchpoint() WriteDBG() => 0x%8.8x.", kret);
+
+ if (kret == KERN_SUCCESS)
+ return i;
+ }
+ else
+ {
+ ProcessMacOSXLog::LogIf(PD_LOG_WATCHPOINTS, "RegisterContextMach_arm::EnableHardwareWatchpoint(): All hardware resources (%u) are in use.", num_hw_watchpoints);
+ }
+ }
+ return LLDB_INVALID_INDEX32;
+}
+
+bool
+RegisterContextMach_arm::ClearHardwareWatchpoint (uint32_t hw_index)
+{
+ kern_return_t kret = ReadDBG (false);
+
+ const uint32_t num_hw_points = NumSupportedHardwareWatchpoints();
+ if (kret == KERN_SUCCESS)
+ {
+ if (hw_index < num_hw_points)
+ {
+ dbg.wcr[hw_index] = 0;
+ ProcessMacOSXLog::LogIf(PD_LOG_WATCHPOINTS, "RegisterContextMach_arm::ClearHardwareWatchpoint( %u ) - WVR%u = 0x%8.8x WCR%u = 0x%8.8x",
+ hw_index,
+ hw_index,
+ dbg.wvr[hw_index],
+ hw_index,
+ dbg.wcr[hw_index]);
+
+ kret = WriteDBG();
+
+ if (kret == KERN_SUCCESS)
+ return true;
+ }
+ }
+ return false;
+}
+
+
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.h b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.h
new file mode 100644
index 00000000000..37821cdd536
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_arm.h
@@ -0,0 +1,302 @@
+//===-- RegisterContextMach_arm.h -------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextMach_arm_h_
+#define liblldb_RegisterContextMach_arm_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/lldb-private.h"
+#include "lldb/Target/RegisterContext.h"
+
+// BCR address match type
+#define BCR_M_IMVA_MATCH ((uint32_t)(0u << 21))
+#define BCR_M_CONTEXT_ID_MATCH ((uint32_t)(1u << 21))
+#define BCR_M_IMVA_MISMATCH ((uint32_t)(2u << 21))
+#define BCR_M_RESERVED ((uint32_t)(3u << 21))
+
+// Link a BVR/BCR or WVR/WCR pair to another
+#define E_ENABLE_LINKING ((uint32_t)(1u << 20))
+
+// Byte Address Select
+#define BAS_IMVA_PLUS_0 ((uint32_t)(1u << 5))
+#define BAS_IMVA_PLUS_1 ((uint32_t)(1u << 6))
+#define BAS_IMVA_PLUS_2 ((uint32_t)(1u << 7))
+#define BAS_IMVA_PLUS_3 ((uint32_t)(1u << 8))
+#define BAS_IMVA_0_1 ((uint32_t)(3u << 5))
+#define BAS_IMVA_2_3 ((uint32_t)(3u << 7))
+#define BAS_IMVA_ALL ((uint32_t)(0xfu << 5))
+
+// Break only in priveleged or user mode
+#define S_RSVD ((uint32_t)(0u << 1))
+#define S_PRIV ((uint32_t)(1u << 1))
+#define S_USER ((uint32_t)(2u << 1))
+#define S_PRIV_USER ((S_PRIV) | (S_USER))
+
+#define BCR_ENABLE ((uint32_t)(1u))
+#define WCR_ENABLE ((uint32_t)(1u))
+
+// Watchpoint load/store
+#define WCR_LOAD ((uint32_t)(1u << 3))
+#define WCR_STORE ((uint32_t)(1u << 4))
+
+class RegisterContextMach_arm : public lldb_private::RegisterContext
+{
+public:
+
+ RegisterContextMach_arm(lldb_private::Thread &thread, lldb_private::StackFrame *frame);
+
+ virtual
+ ~RegisterContextMach_arm();
+
+ virtual void
+ Invalidate ();
+
+ virtual size_t
+ GetRegisterCount ();
+
+ virtual const lldb::RegisterInfo *
+ GetRegisterInfoAtIndex (uint32_t reg);
+
+ virtual size_t
+ GetRegisterSetCount ();
+
+ virtual const lldb::RegisterSet *
+ GetRegisterSet (uint32_t set);
+
+ virtual bool
+ ReadRegisterValue (uint32_t reg, lldb_private::Scalar &value);
+
+ virtual bool
+ ReadRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data);
+
+ virtual bool
+ ReadAllRegisterValues (lldb::DataBufferSP &data_sp);
+
+ virtual bool
+ WriteRegisterValue (uint32_t reg, const lldb_private::Scalar &value);
+
+ virtual bool
+ WriteRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data, uint32_t data_offset = 0);
+
+ virtual bool
+ WriteAllRegisterValues (const lldb::DataBufferSP &data_sp);
+
+ virtual uint32_t
+ ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num);
+
+ virtual uint32_t
+ NumSupportedHardwareBreakpoints ();
+
+ virtual uint32_t
+ SetHardwareBreakpoint (lldb::addr_t addr, size_t size);
+
+ virtual bool
+ ClearHardwareBreakpoint (uint32_t hw_idx);
+
+ virtual uint32_t
+ NumSupportedHardwareWatchpoints ();
+
+ virtual uint32_t
+ SetHardwareWatchpoint (lldb::addr_t addr, size_t size, bool read, bool write);
+
+ virtual bool
+ ClearHardwareWatchpoint (uint32_t hw_index);
+
+ struct GPR
+ {
+ uint32_t r[16]; // R0-R15
+ uint32_t cpsr; // CPSR
+ };
+
+
+ struct FPU
+ {
+ union {
+ uint32_t s[32];
+ uint64_t d[16];
+ } floats;
+ uint32_t fpscr;
+ };
+
+// struct NeonReg
+// {
+// uint8_t bytes[16];
+// };
+//
+// struct VFPv3
+// {
+// union {
+// uint32_t s[32];
+// uint64_t d[32];
+// NeonReg q[16];
+// } v3;
+// uint32_t fpscr;
+// };
+
+ struct EXC
+ {
+ uint32_t exception;
+ uint32_t fsr; /* Fault status */
+ uint32_t far; /* Virtual Fault Address */
+ };
+
+ struct DBG
+ {
+ uint32_t bvr[16];
+ uint32_t bcr[16];
+ uint32_t wvr[16];
+ uint32_t wcr[16];
+ };
+
+ static void
+ LogDBGRegisters (lldb_private::Log *log, const DBG& dbg);
+
+protected:
+
+ typedef enum
+ {
+ GPRRegSet = 1,
+ FPURegSet = 2,
+ EXCRegSet = 3,
+ DBGRegSet = 4,
+ };
+
+ enum
+ {
+ GPRWordCount = sizeof(GPR)/sizeof(uint32_t),
+ FPUWordCount = sizeof(FPU)/sizeof(uint32_t),
+ EXCWordCount = sizeof(EXC)/sizeof(uint32_t),
+ DBGWordCount = sizeof(DBG)/sizeof(uint32_t)
+ };
+
+ enum
+ {
+ Read = 0,
+ Write = 1,
+ kNumErrors = 2
+ };
+
+ GPR gpr;
+ FPU fpu;
+ EXC exc;
+ DBG dbg;
+ kern_return_t gpr_errs[2]; // Read/Write errors
+ kern_return_t fpu_errs[2]; // Read/Write errors
+ kern_return_t exc_errs[2]; // Read/Write errors
+ kern_return_t dbg_errs[2]; // Read/Write errors
+
+ void
+ InvalidateAllRegisterStates()
+ {
+ SetError (GPRRegSet, Read, -1);
+ SetError (FPURegSet, Read, -1);
+ SetError (EXCRegSet, Read, -1);
+ }
+
+ kern_return_t
+ GetError (int flavor, uint32_t err_idx) const
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (flavor)
+ {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case GPRRegSet: return gpr_errs[err_idx];
+ case FPURegSet: return fpu_errs[err_idx];
+ case EXCRegSet: return exc_errs[err_idx];
+ case DBGRegSet: return dbg_errs[err_idx];
+ default: break;
+ }
+ }
+ return -1;
+ }
+
+ bool
+ SetError (int flavor, uint32_t err_idx, kern_return_t err)
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (flavor)
+ {
+ case GPRRegSet:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case FPURegSet:
+ fpu_errs[err_idx] = err;
+ return true;
+
+ case EXCRegSet:
+ exc_errs[err_idx] = err;
+ return true;
+
+ case DBGRegSet:
+ exc_errs[err_idx] = err;
+ return true;
+
+ default: break;
+ }
+ }
+ return false;
+ }
+
+ bool
+ RegisterSetIsCached (int set) const
+ {
+ return GetError(set, Read) == KERN_SUCCESS;
+ }
+
+ kern_return_t
+ ReadGPR (bool force);
+
+ kern_return_t
+ ReadFPU (bool force);
+
+ kern_return_t
+ ReadEXC (bool force);
+
+ kern_return_t
+ ReadDBG (bool force);
+
+ kern_return_t
+ WriteGPR ();
+
+ kern_return_t
+ WriteFPU ();
+
+ kern_return_t
+ WriteEXC ();
+
+ kern_return_t
+ WriteDBG ();
+
+ kern_return_t
+ ReadRegisterSet (uint32_t set, bool force);
+
+ kern_return_t
+ WriteRegisterSet (uint32_t set);
+
+ static uint32_t
+ GetRegisterNumber (uint32_t reg_kind, uint32_t reg_num);
+
+ static int
+ GetSetForNativeRegNum (int reg_num);
+
+ static size_t
+ GetRegisterInfosCount ();
+
+ static const lldb::RegisterInfo *
+ GetRegisterInfos ();
+};
+
+#endif // liblldb_RegisterContextMach_arm_h_
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.cpp
new file mode 100644
index 00000000000..daa4f0d4916
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.cpp
@@ -0,0 +1,1202 @@
+//===-- RegisterContextMach_i386.cpp ----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+// C Includes
+#include <mach/thread_act.h>
+
+// C++ Includes
+// Other libraries and framework includes
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/Scalar.h"
+
+// Project includes
+#include "RegisterContextMach_i386.h"
+#include "ProcessMacOSXLog.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum
+{
+ gpr_eax = 0,
+ gpr_ebx,
+ gpr_ecx,
+ gpr_edx,
+ gpr_edi,
+ gpr_esi,
+ gpr_ebp,
+ gpr_esp,
+ gpr_ss,
+ gpr_eflags,
+ gpr_eip,
+ gpr_cs,
+ gpr_ds,
+ gpr_es,
+ gpr_fs,
+ gpr_gs,
+
+ fpu_fcw,
+ fpu_fsw,
+ fpu_ftw,
+ fpu_fop,
+ fpu_ip,
+ fpu_cs,
+ fpu_dp,
+ fpu_ds,
+ fpu_mxcsr,
+ fpu_mxcsrmask,
+ fpu_stmm0,
+ fpu_stmm1,
+ fpu_stmm2,
+ fpu_stmm3,
+ fpu_stmm4,
+ fpu_stmm5,
+ fpu_stmm6,
+ fpu_stmm7,
+ fpu_xmm0,
+ fpu_xmm1,
+ fpu_xmm2,
+ fpu_xmm3,
+ fpu_xmm4,
+ fpu_xmm5,
+ fpu_xmm6,
+ fpu_xmm7,
+
+ exc_trapno,
+ exc_err,
+ exc_faultvaddr,
+
+ k_num_registers,
+
+ // Aliases
+ fpu_fctrl = fpu_fcw,
+ fpu_fstat = fpu_fsw,
+ fpu_ftag = fpu_ftw,
+ fpu_fiseg = fpu_cs,
+ fpu_fioff = fpu_ip,
+ fpu_foseg = fpu_ds,
+ fpu_fooff = fpu_dp
+};
+
+enum
+{
+ gcc_eax = 0,
+ gcc_ecx,
+ gcc_edx,
+ gcc_ebx,
+ gcc_ebp,
+ gcc_esp,
+ gcc_esi,
+ gcc_edi,
+ gcc_eip,
+ gcc_eflags
+};
+
+enum
+{
+ dwarf_eax = 0,
+ dwarf_ecx,
+ dwarf_edx,
+ dwarf_ebx,
+ dwarf_esp,
+ dwarf_ebp,
+ dwarf_esi,
+ dwarf_edi,
+ dwarf_eip,
+ dwarf_eflags,
+ dwarf_stmm0 = 11,
+ dwarf_stmm1,
+ dwarf_stmm2,
+ dwarf_stmm3,
+ dwarf_stmm4,
+ dwarf_stmm5,
+ dwarf_stmm6,
+ dwarf_stmm7,
+ dwarf_xmm0 = 21,
+ dwarf_xmm1,
+ dwarf_xmm2,
+ dwarf_xmm3,
+ dwarf_xmm4,
+ dwarf_xmm5,
+ dwarf_xmm6,
+ dwarf_xmm7
+};
+
+enum
+{
+ gdb_eax = 0,
+ gdb_ecx = 1,
+ gdb_edx = 2,
+ gdb_ebx = 3,
+ gdb_esp = 4,
+ gdb_ebp = 5,
+ gdb_esi = 6,
+ gdb_edi = 7,
+ gdb_eip = 8,
+ gdb_eflags = 9,
+ gdb_cs = 10,
+ gdb_ss = 11,
+ gdb_ds = 12,
+ gdb_es = 13,
+ gdb_fs = 14,
+ gdb_gs = 15,
+ gdb_stmm0 = 16,
+ gdb_stmm1 = 17,
+ gdb_stmm2 = 18,
+ gdb_stmm3 = 19,
+ gdb_stmm4 = 20,
+ gdb_stmm5 = 21,
+ gdb_stmm6 = 22,
+ gdb_stmm7 = 23,
+ gdb_fctrl = 24, gdb_fcw = gdb_fctrl,
+ gdb_fstat = 25, gdb_fsw = gdb_fstat,
+ gdb_ftag = 26, gdb_ftw = gdb_ftag,
+ gdb_fiseg = 27, gdb_fpu_cs = gdb_fiseg,
+ gdb_fioff = 28, gdb_ip = gdb_fioff,
+ gdb_foseg = 29, gdb_fpu_ds = gdb_foseg,
+ gdb_fooff = 30, gdb_dp = gdb_fooff,
+ gdb_fop = 31,
+ gdb_xmm0 = 32,
+ gdb_xmm1 = 33,
+ gdb_xmm2 = 34,
+ gdb_xmm3 = 35,
+ gdb_xmm4 = 36,
+ gdb_xmm5 = 37,
+ gdb_xmm6 = 38,
+ gdb_xmm7 = 39,
+ gdb_mxcsr = 40,
+ gdb_mm0 = 41,
+ gdb_mm1 = 42,
+ gdb_mm2 = 43,
+ gdb_mm3 = 44,
+ gdb_mm4 = 45,
+ gdb_mm5 = 46,
+ gdb_mm6 = 47,
+ gdb_mm7 = 48
+};
+
+RegisterContextMach_i386::RegisterContextMach_i386 (Thread &thread, StackFrame *frame) :
+ RegisterContext(thread, frame),
+ gpr(),
+ fpu(),
+ exc()
+{
+ uint32_t i;
+ for (i=0; i<kNumErrors; i++)
+ {
+ gpr_errs[i] = -1;
+ fpu_errs[i] = -1;
+ exc_errs[i] = -1;
+ }
+}
+
+RegisterContextMach_i386::~RegisterContextMach_i386()
+{
+}
+
+
+
+#define GPR_OFFSET(reg) (offsetof (RegisterContextMach_i386::GPR, reg))
+#define FPU_OFFSET(reg) (offsetof (RegisterContextMach_i386::FPU, reg) + sizeof (RegisterContextMach_i386::GPR))
+#define EXC_OFFSET(reg) (offsetof (RegisterContextMach_i386::EXC, reg) + sizeof (RegisterContextMach_i386::GPR) + sizeof (RegisterContextMach_i386::FPU))
+
+// These macros will auto define the register name, alt name, register size,
+// register offset, encoding, format and native register. This ensures that
+// the register state structures are defined correctly and have the correct
+// sizes and offsets.
+#define DEFINE_GPR(reg, alt) #reg, alt, sizeof(((RegisterContextMach_i386::GPR *)NULL)->reg), GPR_OFFSET(reg), eEncodingUint, eFormatHex, gpr_##reg
+#define DEFINE_FPU_UINT(reg) #reg, NULL, sizeof(((RegisterContextMach_i386::FPU *)NULL)->reg), FPU_OFFSET(reg), eEncodingUint, eFormatHex, fpu_##reg
+#define DEFINE_FPU_VECT(reg, i) #reg#i, NULL, sizeof(((RegisterContextMach_i386::FPU *)NULL)->reg[i].bytes), FPU_OFFSET(reg[i]), eEncodingVector, eFormatVectorOfUInt8, fpu_##reg##i, { LLDB_INVALID_REGNUM, dwarf_##reg##i, LLDB_INVALID_REGNUM, gdb_##reg##i }
+
+#define DEFINE_EXC(reg) #reg, NULL, sizeof(((RegisterContextMach_i386::EXC *)NULL)->reg), EXC_OFFSET(reg), eEncodingUint, eFormatHex, exc_##reg
+#define REG_CONTEXT_SIZE (sizeof (RegisterContextMach_i386::GPR) + sizeof (RegisterContextMach_i386::FPU) + sizeof (RegisterContextMach_i386::EXC))
+
+static RegisterInfo g_register_infos[] =
+{
+// Macro auto defines most stuff GCC REG KIND NUM DWARF REG KIND NUM GENERIC REG KIND NUM GDB REG KIND NUM
+// =============================== ======================= =================== ========================== ==========================
+ { DEFINE_GPR(eax , NULL) , { gcc_eax , dwarf_eax , LLDB_INVALID_REGNUM , gdb_eax }},
+ { DEFINE_GPR(ebx , NULL) , { gcc_ebx , dwarf_ebx , LLDB_INVALID_REGNUM , gdb_ebx }},
+ { DEFINE_GPR(ecx , NULL) , { gcc_ecx , dwarf_ecx , LLDB_INVALID_REGNUM , gdb_ecx }},
+ { DEFINE_GPR(edx , NULL) , { gcc_edx , dwarf_edx , LLDB_INVALID_REGNUM , gdb_edx }},
+ { DEFINE_GPR(edi , NULL) , { gcc_edi , dwarf_edi , LLDB_INVALID_REGNUM , gdb_edi }},
+ { DEFINE_GPR(esi , NULL) , { gcc_esi , dwarf_esi , LLDB_INVALID_REGNUM , gdb_esi }},
+ { DEFINE_GPR(ebp , "fp") , { gcc_ebp , dwarf_ebp , LLDB_REGNUM_GENERIC_FP , gdb_ebp }},
+ { DEFINE_GPR(esp , "sp") , { gcc_esp , dwarf_esp , LLDB_REGNUM_GENERIC_SP , gdb_esp }},
+ { DEFINE_GPR(ss , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ss }},
+ { DEFINE_GPR(eflags , "flags") , { gcc_eflags , dwarf_eflags , LLDB_REGNUM_GENERIC_FLAGS , gdb_eflags }},
+ { DEFINE_GPR(eip , "pc") , { gcc_eip , dwarf_eip , LLDB_REGNUM_GENERIC_PC , gdb_eip }},
+ { DEFINE_GPR(cs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_cs }},
+ { DEFINE_GPR(ds , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ds }},
+ { DEFINE_GPR(es , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_es }},
+ { DEFINE_GPR(fs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fs }},
+ { DEFINE_GPR(gs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_gs }},
+
+ { DEFINE_FPU_UINT(fcw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fcw }},
+ { DEFINE_FPU_UINT(fsw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fsw }},
+ { DEFINE_FPU_UINT(ftw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ftw }},
+ { DEFINE_FPU_UINT(fop) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fop }},
+ { DEFINE_FPU_UINT(ip) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ip }},
+ { DEFINE_FPU_UINT(cs) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_cs }},
+ { DEFINE_FPU_UINT(dp) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_dp }},
+ { DEFINE_FPU_UINT(ds) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ds }},
+ { DEFINE_FPU_UINT(mxcsr) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_mxcsr }},
+ { DEFINE_FPU_UINT(mxcsrmask) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }},
+ { DEFINE_FPU_VECT(stmm,0) },
+ { DEFINE_FPU_VECT(stmm,1) },
+ { DEFINE_FPU_VECT(stmm,2) },
+ { DEFINE_FPU_VECT(stmm,3) },
+ { DEFINE_FPU_VECT(stmm,4) },
+ { DEFINE_FPU_VECT(stmm,5) },
+ { DEFINE_FPU_VECT(stmm,6) },
+ { DEFINE_FPU_VECT(stmm,7) },
+ { DEFINE_FPU_VECT(xmm,0) },
+ { DEFINE_FPU_VECT(xmm,1) },
+ { DEFINE_FPU_VECT(xmm,2) },
+ { DEFINE_FPU_VECT(xmm,3) },
+ { DEFINE_FPU_VECT(xmm,4) },
+ { DEFINE_FPU_VECT(xmm,5) },
+ { DEFINE_FPU_VECT(xmm,6) },
+ { DEFINE_FPU_VECT(xmm,7) },
+
+ { DEFINE_EXC(trapno) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }},
+ { DEFINE_EXC(err) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }},
+ { DEFINE_EXC(faultvaddr) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }}
+};
+
+static size_t k_num_register_infos = (sizeof(g_register_infos)/sizeof(RegisterInfo));
+
+void
+RegisterContextMach_i386::Invalidate ()
+{
+ InvalidateAllRegisterStates();
+}
+
+
+size_t
+RegisterContextMach_i386::GetRegisterCount ()
+{
+ assert(k_num_register_infos == k_num_registers);
+ return k_num_registers;
+}
+
+const RegisterInfo *
+RegisterContextMach_i386::GetRegisterInfoAtIndex (uint32_t reg)
+{
+ assert(k_num_register_infos == k_num_registers);
+ if (reg < k_num_registers)
+ return &g_register_infos[reg];
+ return NULL;
+}
+
+size_t
+RegisterContextMach_i386::GetRegisterInfosCount ()
+{
+ return k_num_register_infos;
+}
+
+const RegisterInfo *
+RegisterContextMach_i386::GetRegisterInfos ()
+{
+ return g_register_infos;
+}
+
+
+// General purpose registers
+static uint32_t
+g_gpr_regnums[] =
+{
+ gpr_eax,
+ gpr_ebx,
+ gpr_ecx,
+ gpr_edx,
+ gpr_edi,
+ gpr_esi,
+ gpr_ebp,
+ gpr_esp,
+ gpr_ss,
+ gpr_eflags,
+ gpr_eip,
+ gpr_cs,
+ gpr_ds,
+ gpr_es,
+ gpr_fs,
+ gpr_gs
+};
+
+// Floating point registers
+static uint32_t
+g_fpu_regnums[] =
+{
+ fpu_fcw,
+ fpu_fsw,
+ fpu_ftw,
+ fpu_fop,
+ fpu_ip,
+ fpu_cs,
+ fpu_dp,
+ fpu_ds,
+ fpu_mxcsr,
+ fpu_mxcsrmask,
+ fpu_stmm0,
+ fpu_stmm1,
+ fpu_stmm2,
+ fpu_stmm3,
+ fpu_stmm4,
+ fpu_stmm5,
+ fpu_stmm6,
+ fpu_stmm7,
+ fpu_xmm0,
+ fpu_xmm1,
+ fpu_xmm2,
+ fpu_xmm3,
+ fpu_xmm4,
+ fpu_xmm5,
+ fpu_xmm6,
+ fpu_xmm7
+};
+
+// Exception registers
+
+static uint32_t
+g_exc_regnums[] =
+{
+ exc_trapno,
+ exc_err,
+ exc_faultvaddr
+};
+
+// Number of registers in each register set
+const size_t k_num_gpr_registers = sizeof(g_gpr_regnums) / sizeof(uint32_t);
+const size_t k_num_fpu_registers = sizeof(g_fpu_regnums) / sizeof(uint32_t);
+const size_t k_num_exc_registers = sizeof(g_exc_regnums) / sizeof(uint32_t);
+
+//----------------------------------------------------------------------
+// Register set definitions. The first definitions at register set index
+// of zero is for all registers, followed by other registers sets. The
+// register information for the all register set need not be filled in.
+//----------------------------------------------------------------------
+static const RegisterSet g_reg_sets[] =
+{
+ { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums, },
+ { "Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums },
+ { "Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums }
+};
+
+const size_t k_num_regsets = sizeof(g_reg_sets) / sizeof(RegisterSet);
+
+
+size_t
+RegisterContextMach_i386::GetRegisterSetCount ()
+{
+ return k_num_regsets;
+}
+
+const RegisterSet *
+RegisterContextMach_i386::GetRegisterSet (uint32_t reg_set)
+{
+ if (reg_set < k_num_regsets)
+ return &g_reg_sets[reg_set];
+ return NULL;
+}
+
+
+//----------------------------------------------------------------------
+// Register information defintions for 32 bit i386.
+//----------------------------------------------------------------------
+int
+RegisterContextMach_i386::GetSetForNativeRegNum (int reg_num)
+{
+ if (reg_num < fpu_fcw)
+ return GPRRegSet;
+ else if (reg_num < exc_trapno)
+ return FPURegSet;
+ else if (reg_num < k_num_registers)
+ return EXCRegSet;
+ return -1;
+}
+
+
+void
+RegisterContextMach_i386::LogGPR(Log *log, const char *title)
+{
+ if (log)
+ {
+ if (title)
+ log->Printf ("%s", title);
+ for (uint32_t i=0; i<k_num_gpr_registers; i++)
+ {
+ uint32_t reg = gpr_eax + i;
+ log->Printf("%12s = 0x%8.8x", g_register_infos[reg].name, (&gpr.eax)[reg]);
+ }
+ }
+}
+
+
+
+kern_return_t
+RegisterContextMach_i386::ReadGPR (bool force)
+{
+ int set = GPRRegSet;
+ if (force || !RegisterSetIsCached(set))
+ {
+ mach_msg_type_number_t count = GPRWordCount;
+ SetError(set, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&gpr, &count));
+ LogGPR (ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_THREAD), "RegisterContextMach_i386::ReadGPR()");
+ }
+ return GetError(set, Read);
+}
+
+kern_return_t
+RegisterContextMach_i386::ReadFPU (bool force)
+{
+ int set = FPURegSet;
+ if (force || !RegisterSetIsCached(set))
+ {
+ mach_msg_type_number_t count = FPUWordCount;
+ SetError(set, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&fpu, &count));
+ }
+ return GetError(set, Read);
+}
+
+kern_return_t
+RegisterContextMach_i386::ReadEXC (bool force)
+{
+ int set = EXCRegSet;
+ if (force || !RegisterSetIsCached(set))
+ {
+ mach_msg_type_number_t count = EXCWordCount;
+ SetError(set, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&exc, &count));
+ }
+ return GetError(set, Read);
+}
+
+kern_return_t
+RegisterContextMach_i386::WriteGPR ()
+{
+ int set = GPRRegSet;
+ if (!RegisterSetIsCached(set))
+ {
+ SetError (set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError (set, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&gpr, GPRWordCount));
+ SetError (set, Read, -1);
+ return GetError(set, Write);
+}
+
+kern_return_t
+RegisterContextMach_i386::WriteFPU ()
+{
+ int set = FPURegSet;
+ if (!RegisterSetIsCached(set))
+ {
+ SetError (set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError (set, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&fpu, FPUWordCount));
+ SetError (set, Read, -1);
+ return GetError(set, Write);
+}
+
+kern_return_t
+RegisterContextMach_i386::WriteEXC ()
+{
+ int set = EXCRegSet;
+ if (!RegisterSetIsCached(set))
+ {
+ SetError (set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError (set, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&exc, EXCWordCount));
+ SetError (set, Read, -1);
+ return GetError(set, Write);
+}
+
+kern_return_t
+RegisterContextMach_i386::ReadRegisterSet (uint32_t set, bool force)
+{
+ switch (set)
+ {
+ case GPRRegSet: return ReadGPR(force);
+ case FPURegSet: return ReadFPU(force);
+ case EXCRegSet: return ReadEXC(force);
+ default: break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+kern_return_t
+RegisterContextMach_i386::WriteRegisterSet (uint32_t set)
+{
+ // Make sure we have a valid context to set.
+ if (RegisterSetIsCached(set))
+ {
+ switch (set)
+ {
+ case GPRRegSet: return WriteGPR();
+ case FPURegSet: return WriteFPU();
+ case EXCRegSet: return WriteEXC();
+ default: break;
+ }
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+bool
+RegisterContextMach_i386::ReadRegisterValue (uint32_t reg, Scalar &value)
+{
+ int set = RegisterContextMach_i386::GetSetForNativeRegNum (reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+ switch (reg)
+ {
+ case gpr_eax:
+ case gpr_ebx:
+ case gpr_ecx:
+ case gpr_edx:
+ case gpr_edi:
+ case gpr_esi:
+ case gpr_ebp:
+ case gpr_esp:
+ case gpr_ss:
+ case gpr_eflags:
+ case gpr_eip:
+ case gpr_cs:
+ case gpr_ds:
+ case gpr_es:
+ case gpr_fs:
+ case gpr_gs:
+ value = (&gpr.eax)[reg - gpr_eax];
+ break;
+
+ case fpu_fcw:
+ value = fpu.fcw;
+ break;
+
+ case fpu_fsw:
+ value = fpu.fsw;
+ break;
+
+ case fpu_ftw:
+ value = fpu.ftw;
+ break;
+
+ case fpu_fop:
+ value = fpu.fop;
+ break;
+
+ case fpu_ip:
+ value = fpu.ip;
+ break;
+
+ case fpu_cs:
+ value = fpu.cs;
+ break;
+
+ case fpu_dp:
+ value = fpu.dp;
+ break;
+
+ case fpu_ds:
+ value = fpu.ds;
+ break;
+
+ case fpu_mxcsr:
+ value = fpu.mxcsr;
+ break;
+
+ case fpu_mxcsrmask:
+ value = fpu.mxcsrmask;
+ break;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ // These values don't fit into scalar types,
+ // RegisterContext::ReadRegisterBytes() must be used for these
+ // registers
+ //::memcpy (reg_value.value.vector.uint8, fpu.stmm[reg - fpu_stmm0].bytes, 10);
+ return false;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes()
+ // must be used for these registers
+ //::memcpy (reg_value.value.vector.uint8, fpu.xmm[reg - fpu_xmm0].bytes, 16);
+ return false;
+
+ case exc_trapno:
+ value = exc.trapno;
+ break;
+
+ case exc_err:
+ value = exc.err;
+ break;
+
+ case exc_faultvaddr:
+ value = exc.faultvaddr;
+ break;
+
+ default:
+ return false;
+ }
+ return true;
+}
+
+
+bool
+RegisterContextMach_i386::WriteRegisterValue (uint32_t reg, const Scalar &value)
+{
+ int set = GetSetForNativeRegNum (reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+ switch (reg)
+ {
+ case gpr_eax:
+ case gpr_ebx:
+ case gpr_ecx:
+ case gpr_edx:
+ case gpr_edi:
+ case gpr_esi:
+ case gpr_ebp:
+ case gpr_esp:
+ case gpr_ss:
+ case gpr_eflags:
+ case gpr_eip:
+ case gpr_cs:
+ case gpr_ds:
+ case gpr_es:
+ case gpr_fs:
+ case gpr_gs:
+ (&gpr.eax)[reg - gpr_eax] = value.UInt(0);
+ break;
+
+ case fpu_fcw:
+ fpu.fcw = value.UInt(0);
+ break;
+
+ case fpu_fsw:
+ fpu.fsw = value.UInt(0);
+ break;
+
+ case fpu_ftw:
+ fpu.ftw = value.UInt(0);
+ break;
+
+ case fpu_fop:
+ fpu.fop = value.UInt(0);
+ break;
+
+ case fpu_ip:
+ fpu.ip = value.UInt(0);
+ break;
+
+ case fpu_cs:
+ fpu.cs = value.UInt(0);
+ break;
+
+ case fpu_dp:
+ fpu.dp = value.UInt(0);
+ break;
+
+ case fpu_ds:
+ fpu.ds = value.UInt(0);
+ break;
+
+ case fpu_mxcsr:
+ fpu.mxcsr = value.UInt(0);
+ break;
+
+ case fpu_mxcsrmask:
+ fpu.mxcsrmask = value.UInt(0);
+ break;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes()
+ // must be used for these registers
+ //::memcpy (fpu.stmm[reg - fpu_stmm0].bytes, reg_value.value.vector.uint8, 10);
+ return false;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes()
+ // must be used for these registers
+ //::memcpy (fpu.xmm[reg - fpu_xmm0].bytes, reg_value.value.vector.uint8, 16);
+ return false;
+
+ case exc_trapno:
+ exc.trapno = value.UInt(0);
+ break;
+
+ case exc_err:
+ exc.err = value.UInt(0);
+ break;
+
+ case exc_faultvaddr:
+ exc.faultvaddr = value.UInt(0);
+ break;
+
+ default:
+ return false;
+ }
+ return WriteRegisterSet(set) == KERN_SUCCESS;
+}
+
+bool
+RegisterContextMach_i386::ReadRegisterBytes (uint32_t reg, DataExtractor &data)
+{
+ int set = RegisterContextMach_i386::GetSetForNativeRegNum (reg);
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+ const RegisterInfo * reg_info = GetRegisterInfoAtIndex (reg);
+ if (reg_info == NULL)
+ return false;
+
+ switch (reg)
+ {
+ case gpr_eax:
+ case gpr_ebx:
+ case gpr_ecx:
+ case gpr_edx:
+ case gpr_edi:
+ case gpr_esi:
+ case gpr_ebp:
+ case gpr_esp:
+ case gpr_ss:
+ case gpr_eflags:
+ case gpr_eip:
+ case gpr_cs:
+ case gpr_ds:
+ case gpr_es:
+ case gpr_fs:
+ case gpr_gs:
+ data.SetData(&gpr.eax + reg - gpr_eax, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_fcw:
+ data.SetData(&fpu.fcw, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_fsw:
+ data.SetData(&fpu.fsw, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_ftw:
+ data.SetData(&fpu.ftw, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_fop:
+ data.SetData(&fpu.fop, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_ip:
+ data.SetData(&fpu.ip, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_cs:
+ data.SetData(&fpu.cs, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_dp:
+ data.SetData(&fpu.dp, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_ds:
+ data.SetData(&fpu.ds, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_mxcsr:
+ data.SetData(&fpu.mxcsr, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_mxcsrmask:
+ data.SetData(&fpu.mxcsrmask, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ data.SetData(fpu.stmm[reg - fpu_stmm0].bytes, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ data.SetData(fpu.xmm[reg - fpu_xmm0].bytes, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case exc_trapno:
+ data.SetData(&exc.trapno, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case exc_err:
+ data.SetData(&exc.err, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case exc_faultvaddr:
+ data.SetData(&exc.faultvaddr, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool
+RegisterContextMach_i386::WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset)
+{
+ int set = GetSetForNativeRegNum (reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+
+ const RegisterInfo * reg_info = GetRegisterInfoAtIndex (reg);
+ if (reg_info == NULL && data.ValidOffsetForDataOfSize(data_offset, reg_info->byte_size))
+ return false;
+
+ uint32_t offset = data_offset;
+ switch (reg)
+ {
+ case gpr_eax:
+ case gpr_ebx:
+ case gpr_ecx:
+ case gpr_edx:
+ case gpr_edi:
+ case gpr_esi:
+ case gpr_ebp:
+ case gpr_esp:
+ case gpr_ss:
+ case gpr_eflags:
+ case gpr_eip:
+ case gpr_cs:
+ case gpr_ds:
+ case gpr_es:
+ case gpr_fs:
+ case gpr_gs:
+ (&gpr.eax)[reg - gpr_eax] = data.GetU32 (&offset);
+ break;
+
+ case fpu_fcw:
+ fpu.fcw = data.GetU16(&offset);
+ break;
+
+ case fpu_fsw:
+ fpu.fsw = data.GetU16(&offset);
+ break;
+
+ case fpu_ftw:
+ fpu.ftw = data.GetU8(&offset);
+ break;
+
+ case fpu_fop:
+ fpu.fop = data.GetU16(&offset);
+ break;
+
+ case fpu_ip:
+ fpu.ip = data.GetU32(&offset);
+ break;
+
+ case fpu_cs:
+ fpu.cs = data.GetU16(&offset);
+ break;
+
+ case fpu_dp:
+ fpu.dp = data.GetU32(&offset);
+ break;
+
+ case fpu_ds:
+ fpu.ds = data.GetU16(&offset);
+ break;
+
+ case fpu_mxcsr:
+ fpu.mxcsr = data.GetU32(&offset);
+ break;
+
+ case fpu_mxcsrmask:
+ fpu.mxcsrmask = data.GetU32(&offset);
+ break;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ ::memcpy (fpu.stmm[reg - fpu_stmm0].bytes, data.PeekData(offset, reg_info->byte_size), reg_info->byte_size);
+ return false;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes()
+ // must be used for these registers
+ ::memcpy (fpu.xmm[reg - fpu_xmm0].bytes, data.PeekData(offset, reg_info->byte_size), reg_info->byte_size);
+ return false;
+
+ case exc_trapno:
+ exc.trapno = data.GetU32 (&offset);
+ break;
+
+ case exc_err:
+ exc.err = data.GetU32 (&offset);
+ break;
+
+ case exc_faultvaddr:
+ exc.faultvaddr = data.GetU32 (&offset);
+ break;
+
+ default:
+ return false;
+ }
+ return WriteRegisterSet(set) == KERN_SUCCESS;
+}
+
+bool
+RegisterContextMach_i386::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
+{
+ data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0));
+ if (data_sp &&
+ ReadGPR (false) == KERN_SUCCESS &&
+ ReadFPU (false) == KERN_SUCCESS &&
+ ReadEXC (false) == KERN_SUCCESS)
+ {
+ uint8_t *dst = data_sp->GetBytes();
+ ::memcpy (dst, &gpr, sizeof(gpr));
+ dst += sizeof(gpr);
+
+ ::memcpy (dst, &fpu, sizeof(fpu));
+ dst += sizeof(gpr);
+
+ ::memcpy (dst, &exc, sizeof(exc));
+ return true;
+ }
+ return false;
+}
+
+bool
+RegisterContextMach_i386::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp)
+{
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE)
+ {
+ const uint8_t *src = data_sp->GetBytes();
+ ::memcpy (&gpr, src, sizeof(gpr));
+ src += sizeof(gpr);
+
+ ::memcpy (&fpu, src, sizeof(fpu));
+ src += sizeof(gpr);
+
+ ::memcpy (&exc, src, sizeof(exc));
+ uint32_t success_count = 0;
+ if (WriteGPR() == KERN_SUCCESS)
+ ++success_count;
+ if (WriteFPU() == KERN_SUCCESS)
+ ++success_count;
+ if (WriteEXC() == KERN_SUCCESS)
+ ++success_count;
+ return success_count == 3;
+ }
+ return false;
+}
+
+
+uint32_t
+RegisterContextMach_i386::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t reg)
+{
+ if (kind == eRegisterKindGeneric)
+ {
+ switch (reg)
+ {
+ case LLDB_REGNUM_GENERIC_PC: return gpr_eip;
+ case LLDB_REGNUM_GENERIC_SP: return gpr_esp;
+ case LLDB_REGNUM_GENERIC_FP: return gpr_ebp;
+ case LLDB_REGNUM_GENERIC_FLAGS: return gpr_eflags;
+ case LLDB_REGNUM_GENERIC_RA:
+ default:
+ break;
+ }
+ }
+ else if (kind == eRegisterKindGCC || kind == eRegisterKindDWARF)
+ {
+ switch (reg)
+ {
+ case dwarf_eax: return gpr_eax;
+ case dwarf_ecx: return gpr_ecx;
+ case dwarf_edx: return gpr_edx;
+ case dwarf_ebx: return gpr_ebx;
+ case dwarf_esp: return gpr_esp;
+ case dwarf_ebp: return gpr_ebp;
+ case dwarf_esi: return gpr_esi;
+ case dwarf_edi: return gpr_edi;
+ case dwarf_eip: return gpr_eip;
+ case dwarf_eflags: return gpr_eflags;
+ case dwarf_stmm0: return fpu_stmm0;
+ case dwarf_stmm1: return fpu_stmm1;
+ case dwarf_stmm2: return fpu_stmm2;
+ case dwarf_stmm3: return fpu_stmm3;
+ case dwarf_stmm4: return fpu_stmm4;
+ case dwarf_stmm5: return fpu_stmm5;
+ case dwarf_stmm6: return fpu_stmm6;
+ case dwarf_stmm7: return fpu_stmm7;
+ case dwarf_xmm0: return fpu_xmm0;
+ case dwarf_xmm1: return fpu_xmm1;
+ case dwarf_xmm2: return fpu_xmm2;
+ case dwarf_xmm3: return fpu_xmm3;
+ case dwarf_xmm4: return fpu_xmm4;
+ case dwarf_xmm5: return fpu_xmm5;
+ case dwarf_xmm6: return fpu_xmm6;
+ case dwarf_xmm7: return fpu_xmm7;
+ default:
+ break;
+ }
+ }
+ else if (kind == eRegisterKindGDB)
+ {
+ switch (reg)
+ {
+ case gdb_eax : return gpr_eax;
+ case gdb_ebx : return gpr_ebx;
+ case gdb_ecx : return gpr_ecx;
+ case gdb_edx : return gpr_edx;
+ case gdb_esi : return gpr_esi;
+ case gdb_edi : return gpr_edi;
+ case gdb_ebp : return gpr_ebp;
+ case gdb_esp : return gpr_esp;
+ case gdb_eip : return gpr_eip;
+ case gdb_eflags : return gpr_eflags;
+ case gdb_cs : return gpr_cs;
+ case gdb_ss : return gpr_ss;
+ case gdb_ds : return gpr_ds;
+ case gdb_es : return gpr_es;
+ case gdb_fs : return gpr_fs;
+ case gdb_gs : return gpr_gs;
+ case gdb_stmm0 : return fpu_stmm0;
+ case gdb_stmm1 : return fpu_stmm1;
+ case gdb_stmm2 : return fpu_stmm2;
+ case gdb_stmm3 : return fpu_stmm3;
+ case gdb_stmm4 : return fpu_stmm4;
+ case gdb_stmm5 : return fpu_stmm5;
+ case gdb_stmm6 : return fpu_stmm6;
+ case gdb_stmm7 : return fpu_stmm7;
+ case gdb_fctrl : return fpu_fctrl;
+ case gdb_fstat : return fpu_fstat;
+ case gdb_ftag : return fpu_ftag;
+ case gdb_fiseg : return fpu_fiseg;
+ case gdb_fioff : return fpu_fioff;
+ case gdb_foseg : return fpu_foseg;
+ case gdb_fooff : return fpu_fooff;
+ case gdb_fop : return fpu_fop;
+ case gdb_xmm0 : return fpu_xmm0;
+ case gdb_xmm1 : return fpu_xmm1;
+ case gdb_xmm2 : return fpu_xmm2;
+ case gdb_xmm3 : return fpu_xmm3;
+ case gdb_xmm4 : return fpu_xmm4;
+ case gdb_xmm5 : return fpu_xmm5;
+ case gdb_xmm6 : return fpu_xmm6;
+ case gdb_xmm7 : return fpu_xmm7;
+ case gdb_mxcsr : return fpu_mxcsr;
+ default:
+ break;
+ }
+ }
+ return LLDB_INVALID_REGNUM;
+}
+
+
+bool
+RegisterContextMach_i386::HardwareSingleStep (bool enable)
+{
+ if (ReadGPR(false) != KERN_SUCCESS)
+ return false;
+
+ const uint32_t trace_bit = 0x100u;
+ if (enable)
+ {
+ // If the trace bit is already set, there is nothing to do
+ if (gpr.eflags & trace_bit)
+ return true;
+ else
+ gpr.eflags |= trace_bit;
+ }
+ else
+ {
+ // If the trace bit is already cleared, there is nothing to do
+ if (gpr.eflags & trace_bit)
+ gpr.eflags &= ~trace_bit;
+ else
+ return true;
+ }
+
+ return WriteGPR() == KERN_SUCCESS;
+}
+
+
+
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.h b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.h
new file mode 100644
index 00000000000..580186752a9
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_i386.h
@@ -0,0 +1,256 @@
+//===-- RegisterContextMach_i386.h ------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextMach_i386_h_
+#define liblldb_RegisterContextMach_i386_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/lldb-private.h"
+#include "lldb/Target/RegisterContext.h"
+
+class RegisterContextMach_i386 : public lldb_private::RegisterContext
+{
+public:
+
+ RegisterContextMach_i386(lldb_private::Thread &thread,
+ lldb_private::StackFrame *frame);
+
+ virtual
+ ~RegisterContextMach_i386();
+
+ virtual void
+ Invalidate ();
+
+ virtual size_t
+ GetRegisterCount ();
+
+ virtual const lldb::RegisterInfo *
+ GetRegisterInfoAtIndex (uint32_t reg);
+
+ virtual size_t
+ GetRegisterSetCount ();
+
+ virtual const lldb::RegisterSet *
+ GetRegisterSet (uint32_t set);
+
+ virtual bool
+ ReadRegisterValue (uint32_t reg, lldb_private::Scalar &value);
+
+ virtual bool
+ ReadRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data);
+
+ virtual bool
+ ReadAllRegisterValues (lldb::DataBufferSP &data_sp);
+
+ virtual bool
+ WriteRegisterValue (uint32_t reg, const lldb_private::Scalar &value);
+
+ virtual bool
+ WriteRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data, uint32_t data_offset = 0);
+
+ virtual bool
+ WriteAllRegisterValues (const lldb::DataBufferSP &data_sp);
+
+ virtual uint32_t
+ ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num);
+
+ virtual bool
+ HardwareSingleStep (bool enable);
+
+ struct GPR
+ {
+ uint32_t eax;
+ uint32_t ebx;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t edi;
+ uint32_t esi;
+ uint32_t ebp;
+ uint32_t esp;
+ uint32_t ss;
+ uint32_t eflags;
+ uint32_t eip;
+ uint32_t cs;
+ uint32_t ds;
+ uint32_t es;
+ uint32_t fs;
+ uint32_t gs;
+ };
+
+ struct MMSReg
+ {
+ uint8_t bytes[10];
+ uint8_t pad[6];
+ };
+
+ struct XMMReg
+ {
+ uint8_t bytes[16];
+ };
+
+ struct FPU
+ {
+ uint32_t pad[2];
+ uint16_t fcw;
+ uint16_t fsw;
+ uint8_t ftw;
+ uint8_t pad1;
+ uint16_t fop;
+ uint32_t ip;
+ uint16_t cs;
+ uint16_t pad2;
+ uint32_t dp;
+ uint16_t ds;
+ uint16_t pad3;
+ uint32_t mxcsr;
+ uint32_t mxcsrmask;
+ MMSReg stmm[8];
+ XMMReg xmm[8];
+ uint8_t pad4[14*16];
+ int pad5;
+ };
+
+ struct EXC
+ {
+ uint32_t trapno;
+ uint32_t err;
+ uint32_t faultvaddr;
+ };
+
+protected:
+
+ enum
+ {
+ GPRRegSet = 1,
+ FPURegSet = 2,
+ EXCRegSet = 3
+ };
+
+ enum
+ {
+ GPRWordCount = sizeof(GPR)/sizeof(uint32_t),
+ FPUWordCount = sizeof(FPU)/sizeof(uint32_t),
+ EXCWordCount = sizeof(EXC)/sizeof(uint32_t)
+ };
+
+ enum
+ {
+ Read = 0,
+ Write = 1,
+ kNumErrors = 2
+ };
+
+ GPR gpr;
+ FPU fpu;
+ EXC exc;
+ kern_return_t gpr_errs[2]; // Read/Write errors
+ kern_return_t fpu_errs[2]; // Read/Write errors
+ kern_return_t exc_errs[2]; // Read/Write errors
+
+ void
+ InvalidateAllRegisterStates()
+ {
+ SetError (GPRRegSet, Read, -1);
+ SetError (FPURegSet, Read, -1);
+ SetError (EXCRegSet, Read, -1);
+ }
+
+ kern_return_t
+ GetError (int flavor, uint32_t err_idx) const
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (flavor)
+ {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case GPRRegSet: return gpr_errs[err_idx];
+ case FPURegSet: return fpu_errs[err_idx];
+ case EXCRegSet: return exc_errs[err_idx];
+ default: break;
+ }
+ }
+ return -1;
+ }
+
+ bool
+ SetError (int flavor, uint32_t err_idx, kern_return_t err)
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (flavor)
+ {
+ case GPRRegSet:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case FPURegSet:
+ fpu_errs[err_idx] = err;
+ return true;
+
+ case EXCRegSet:
+ exc_errs[err_idx] = err;
+ return true;
+
+ default: break;
+ }
+ }
+ return false;
+ }
+
+ bool
+ RegisterSetIsCached (int set) const
+ {
+ return GetError(set, Read) == KERN_SUCCESS;
+ }
+
+ void
+ LogGPR (lldb_private::Log *log, const char *title);
+
+ kern_return_t
+ ReadGPR (bool force);
+
+ kern_return_t
+ ReadFPU (bool force);
+
+ kern_return_t
+ ReadEXC (bool force);
+
+ kern_return_t
+ WriteGPR ();
+
+ kern_return_t
+ WriteFPU ();
+
+ kern_return_t
+ WriteEXC ();
+
+ kern_return_t
+ ReadRegisterSet (uint32_t set, bool force);
+
+ kern_return_t
+ WriteRegisterSet (uint32_t set);
+
+ static uint32_t
+ GetRegisterNumber (uint32_t reg_kind, uint32_t reg_num);
+
+ static int
+ GetSetForNativeRegNum (int reg_num);
+
+ static size_t
+ GetRegisterInfosCount ();
+
+ static const lldb::RegisterInfo *
+ GetRegisterInfos ();
+};
+
+#endif // liblldb_RegisterContextMach_i386_h_
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.cpp
new file mode 100644
index 00000000000..a7ed32e649a
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.cpp
@@ -0,0 +1,1328 @@
+//===-- RegisterContextMach_x86_64.cpp --------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+// C Includes
+#include <mach/thread_act.h>
+
+// C++ Includes
+// Other libraries and framework includes
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/Scalar.h"
+
+// Project includes
+#include "RegisterContextMach_x86_64.h"
+#include "ProcessMacOSXLog.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+enum
+{
+ gpr_rax = 0,
+ gpr_rbx,
+ gpr_rcx,
+ gpr_rdx,
+ gpr_rdi,
+ gpr_rsi,
+ gpr_rbp,
+ gpr_rsp,
+ gpr_r8,
+ gpr_r9,
+ gpr_r10,
+ gpr_r11,
+ gpr_r12,
+ gpr_r13,
+ gpr_r14,
+ gpr_r15,
+ gpr_rip,
+ gpr_rflags,
+ gpr_cs,
+ gpr_fs,
+ gpr_gs,
+
+ fpu_fcw,
+ fpu_fsw,
+ fpu_ftw,
+ fpu_fop,
+ fpu_ip,
+ fpu_cs,
+ fpu_dp,
+ fpu_ds,
+ fpu_mxcsr,
+ fpu_mxcsrmask,
+ fpu_stmm0,
+ fpu_stmm1,
+ fpu_stmm2,
+ fpu_stmm3,
+ fpu_stmm4,
+ fpu_stmm5,
+ fpu_stmm6,
+ fpu_stmm7,
+ fpu_xmm0,
+ fpu_xmm1,
+ fpu_xmm2,
+ fpu_xmm3,
+ fpu_xmm4,
+ fpu_xmm5,
+ fpu_xmm6,
+ fpu_xmm7,
+ fpu_xmm8,
+ fpu_xmm9,
+ fpu_xmm10,
+ fpu_xmm11,
+ fpu_xmm12,
+ fpu_xmm13,
+ fpu_xmm14,
+ fpu_xmm15,
+
+ exc_trapno,
+ exc_err,
+ exc_faultvaddr,
+
+ k_num_registers,
+
+ // Aliases
+ fpu_fctrl = fpu_fcw,
+ fpu_fstat = fpu_fsw,
+ fpu_ftag = fpu_ftw,
+ fpu_fiseg = fpu_cs,
+ fpu_fioff = fpu_ip,
+ fpu_foseg = fpu_ds,
+ fpu_fooff = fpu_dp,
+};
+
+enum gcc_dwarf_regnums
+{
+ gcc_dwarf_gpr_rax = 0,
+ gcc_dwarf_gpr_rdx,
+ gcc_dwarf_gpr_rcx,
+ gcc_dwarf_gpr_rbx,
+ gcc_dwarf_gpr_rsi,
+ gcc_dwarf_gpr_rdi,
+ gcc_dwarf_gpr_rbp,
+ gcc_dwarf_gpr_rsp,
+ gcc_dwarf_gpr_r8,
+ gcc_dwarf_gpr_r9,
+ gcc_dwarf_gpr_r10,
+ gcc_dwarf_gpr_r11,
+ gcc_dwarf_gpr_r12,
+ gcc_dwarf_gpr_r13,
+ gcc_dwarf_gpr_r14,
+ gcc_dwarf_gpr_r15,
+ gcc_dwarf_gpr_rip,
+ gcc_dwarf_fpu_xmm0,
+ gcc_dwarf_fpu_xmm1,
+ gcc_dwarf_fpu_xmm2,
+ gcc_dwarf_fpu_xmm3,
+ gcc_dwarf_fpu_xmm4,
+ gcc_dwarf_fpu_xmm5,
+ gcc_dwarf_fpu_xmm6,
+ gcc_dwarf_fpu_xmm7,
+ gcc_dwarf_fpu_xmm8,
+ gcc_dwarf_fpu_xmm9,
+ gcc_dwarf_fpu_xmm10,
+ gcc_dwarf_fpu_xmm11,
+ gcc_dwarf_fpu_xmm12,
+ gcc_dwarf_fpu_xmm13,
+ gcc_dwarf_fpu_xmm14,
+ gcc_dwarf_fpu_xmm15,
+ gcc_dwarf_fpu_stmm0,
+ gcc_dwarf_fpu_stmm1,
+ gcc_dwarf_fpu_stmm2,
+ gcc_dwarf_fpu_stmm3,
+ gcc_dwarf_fpu_stmm4,
+ gcc_dwarf_fpu_stmm5,
+ gcc_dwarf_fpu_stmm6,
+ gcc_dwarf_fpu_stmm7,
+
+};
+
+enum gdb_regnums
+{
+ gdb_gpr_rax = 0,
+ gdb_gpr_rbx = 1,
+ gdb_gpr_rcx = 2,
+ gdb_gpr_rdx = 3,
+ gdb_gpr_rsi = 4,
+ gdb_gpr_rdi = 5,
+ gdb_gpr_rbp = 6,
+ gdb_gpr_rsp = 7,
+ gdb_gpr_r8 = 8,
+ gdb_gpr_r9 = 9,
+ gdb_gpr_r10 = 10,
+ gdb_gpr_r11 = 11,
+ gdb_gpr_r12 = 12,
+ gdb_gpr_r13 = 13,
+ gdb_gpr_r14 = 14,
+ gdb_gpr_r15 = 15,
+ gdb_gpr_rip = 16,
+ gdb_gpr_rflags = 17,
+ gdb_gpr_cs = 18,
+ gdb_gpr_ss = 19,
+ gdb_gpr_ds = 20,
+ gdb_gpr_es = 21,
+ gdb_gpr_fs = 22,
+ gdb_gpr_gs = 23,
+ gdb_fpu_stmm0 = 24,
+ gdb_fpu_stmm1 = 25,
+ gdb_fpu_stmm2 = 26,
+ gdb_fpu_stmm3 = 27,
+ gdb_fpu_stmm4 = 28,
+ gdb_fpu_stmm5 = 29,
+ gdb_fpu_stmm6 = 30,
+ gdb_fpu_stmm7 = 31,
+ gdb_fpu_fctrl = 32, gdb_fpu_fcw = gdb_fpu_fctrl,
+ gdb_fpu_fstat = 33, gdb_fpu_fsw = gdb_fpu_fstat,
+ gdb_fpu_ftag = 34, gdb_fpu_ftw = gdb_fpu_ftag,
+ gdb_fpu_fiseg = 35, gdb_fpu_cs = gdb_fpu_fiseg,
+ gdb_fpu_fioff = 36, gdb_fpu_ip = gdb_fpu_fioff,
+ gdb_fpu_foseg = 37, gdb_fpu_ds = gdb_fpu_foseg,
+ gdb_fpu_fooff = 38, gdb_fpu_dp = gdb_fpu_fooff,
+ gdb_fpu_fop = 39,
+ gdb_fpu_xmm0 = 40,
+ gdb_fpu_xmm1 = 41,
+ gdb_fpu_xmm2 = 42,
+ gdb_fpu_xmm3 = 43,
+ gdb_fpu_xmm4 = 44,
+ gdb_fpu_xmm5 = 45,
+ gdb_fpu_xmm6 = 46,
+ gdb_fpu_xmm7 = 47,
+ gdb_fpu_xmm8 = 48,
+ gdb_fpu_xmm9 = 49,
+ gdb_fpu_xmm10 = 50,
+ gdb_fpu_xmm11 = 51,
+ gdb_fpu_xmm12 = 52,
+ gdb_fpu_xmm13 = 53,
+ gdb_fpu_xmm14 = 54,
+ gdb_fpu_xmm15 = 55,
+ gdb_fpu_mxcsr = 56,
+};
+
+RegisterContextMach_x86_64::RegisterContextMach_x86_64 (Thread &thread, StackFrame *frame) :
+ RegisterContext (thread, frame),
+ gpr(),
+ fpu(),
+ exc()
+{
+ uint32_t i;
+ for (i=0; i<kNumErrors; i++)
+ {
+ gpr_errs[i] = -1;
+ fpu_errs[i] = -1;
+ exc_errs[i] = -1;
+ }
+}
+
+RegisterContextMach_x86_64::~RegisterContextMach_x86_64()
+{
+}
+
+#define GPR_OFFSET(reg) (offsetof (RegisterContextMach_x86_64::GPR, reg))
+#define FPU_OFFSET(reg) (offsetof (RegisterContextMach_x86_64::FPU, reg) + sizeof (RegisterContextMach_x86_64::GPR))
+#define EXC_OFFSET(reg) (offsetof (RegisterContextMach_x86_64::EXC, reg) + sizeof (RegisterContextMach_x86_64::GPR) + sizeof (RegisterContextMach_x86_64::FPU))
+
+// These macros will auto define the register name, alt name, register size,
+// register offset, encoding, format and native register. This ensures that
+// the register state structures are defined correctly and have the correct
+// sizes and offsets.
+#define DEFINE_GPR(reg, alt) #reg, alt, sizeof(((RegisterContextMach_x86_64::GPR *)NULL)->reg), GPR_OFFSET(reg), eEncodingUint, eFormatHex, gpr_##reg
+#define DEFINE_FPU_UINT(reg) #reg, NULL, sizeof(((RegisterContextMach_x86_64::FPU *)NULL)->reg), FPU_OFFSET(reg), eEncodingUint, eFormatHex, fpu_##reg
+#define DEFINE_FPU_VECT(reg, i) #reg#i, NULL, sizeof(((RegisterContextMach_x86_64::FPU *)NULL)->reg[i].bytes), FPU_OFFSET(reg[i]), eEncodingVector, eFormatVectorOfUInt8, fpu_##reg##i, { gcc_dwarf_fpu_##reg##i, gcc_dwarf_fpu_##reg##i, LLDB_INVALID_REGNUM, gdb_fpu_##reg##i }
+#define DEFINE_EXC(reg) #reg, NULL, sizeof(((RegisterContextMach_x86_64::EXC *)NULL)->reg), EXC_OFFSET(reg), eEncodingUint, eFormatHex, exc_##reg
+
+#define REG_CONTEXT_SIZE (sizeof (RegisterContextMach_x86_64::GPR) + sizeof (RegisterContextMach_x86_64::FPU) + sizeof (RegisterContextMach_x86_64::EXC))
+
+// General purpose registers for 64 bit
+static RegisterInfo g_register_infos[] =
+{
+// Macro auto defines most stuff GCC REG KIND NUM DWARF REG KIND NUM GENERIC REG KIND NUM GDB REG KIND NUM
+// =============================== ======================= =================== ========================== ==========================
+ { DEFINE_GPR (rax , NULL) , { gcc_dwarf_gpr_rax , gcc_dwarf_gpr_rax , LLDB_INVALID_REGNUM , gdb_gpr_rax }},
+ { DEFINE_GPR (rbx , NULL) , { gcc_dwarf_gpr_rbx , gcc_dwarf_gpr_rbx , LLDB_INVALID_REGNUM , gdb_gpr_rbx }},
+ { DEFINE_GPR (rcx , NULL) , { gcc_dwarf_gpr_rcx , gcc_dwarf_gpr_rcx , LLDB_INVALID_REGNUM , gdb_gpr_rcx }},
+ { DEFINE_GPR (rdx , NULL) , { gcc_dwarf_gpr_rdx , gcc_dwarf_gpr_rdx , LLDB_INVALID_REGNUM , gdb_gpr_rdx }},
+ { DEFINE_GPR (rdi , NULL) , { gcc_dwarf_gpr_rdi , gcc_dwarf_gpr_rdi , LLDB_INVALID_REGNUM , gdb_gpr_rdi }},
+ { DEFINE_GPR (rsi , NULL) , { gcc_dwarf_gpr_rsi , gcc_dwarf_gpr_rsi , LLDB_INVALID_REGNUM , gdb_gpr_rsi }},
+ { DEFINE_GPR (rbp , "fp") , { gcc_dwarf_gpr_rbp , gcc_dwarf_gpr_rbp , LLDB_REGNUM_GENERIC_FP , gdb_gpr_rbp }},
+ { DEFINE_GPR (rsp , "sp") , { gcc_dwarf_gpr_rsp , gcc_dwarf_gpr_rsp , LLDB_REGNUM_GENERIC_SP , gdb_gpr_rsp }},
+ { DEFINE_GPR (r8 , NULL) , { gcc_dwarf_gpr_r8 , gcc_dwarf_gpr_r8 , LLDB_INVALID_REGNUM , gdb_gpr_r8 }},
+ { DEFINE_GPR (r9 , NULL) , { gcc_dwarf_gpr_r9 , gcc_dwarf_gpr_r9 , LLDB_INVALID_REGNUM , gdb_gpr_r9 }},
+ { DEFINE_GPR (r10 , NULL) , { gcc_dwarf_gpr_r10 , gcc_dwarf_gpr_r10 , LLDB_INVALID_REGNUM , gdb_gpr_r10 }},
+ { DEFINE_GPR (r11 , NULL) , { gcc_dwarf_gpr_r11 , gcc_dwarf_gpr_r11 , LLDB_INVALID_REGNUM , gdb_gpr_r11 }},
+ { DEFINE_GPR (r12 , NULL) , { gcc_dwarf_gpr_r12 , gcc_dwarf_gpr_r12 , LLDB_INVALID_REGNUM , gdb_gpr_r12 }},
+ { DEFINE_GPR (r13 , NULL) , { gcc_dwarf_gpr_r13 , gcc_dwarf_gpr_r13 , LLDB_INVALID_REGNUM , gdb_gpr_r13 }},
+ { DEFINE_GPR (r14 , NULL) , { gcc_dwarf_gpr_r14 , gcc_dwarf_gpr_r14 , LLDB_INVALID_REGNUM , gdb_gpr_r14 }},
+ { DEFINE_GPR (r15 , NULL) , { gcc_dwarf_gpr_r15 , gcc_dwarf_gpr_r15 , LLDB_INVALID_REGNUM , gdb_gpr_r15 }},
+ { DEFINE_GPR (rip , "pc") , { gcc_dwarf_gpr_rip , gcc_dwarf_gpr_rip , LLDB_REGNUM_GENERIC_PC , gdb_gpr_rip }},
+ { DEFINE_GPR (rflags, "flags") , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_REGNUM_GENERIC_FLAGS , gdb_gpr_rflags}},
+ { DEFINE_GPR (cs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_gpr_cs }},
+ { DEFINE_GPR (fs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_gpr_fs }},
+ { DEFINE_GPR (gs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_gpr_gs }},
+
+ { DEFINE_FPU_UINT(fcw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_fcw }},
+ { DEFINE_FPU_UINT(fsw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_fsw }},
+ { DEFINE_FPU_UINT(ftw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_ftw }},
+ { DEFINE_FPU_UINT(fop) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_fop }},
+ { DEFINE_FPU_UINT(ip) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_ip }},
+ { DEFINE_FPU_UINT(cs) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_cs }},
+ { DEFINE_FPU_UINT(dp) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_dp }},
+ { DEFINE_FPU_UINT(ds) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_ds }},
+ { DEFINE_FPU_UINT(mxcsr) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fpu_mxcsr }},
+ { DEFINE_FPU_UINT(mxcsrmask) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }},
+ { DEFINE_FPU_VECT(stmm,0) },
+ { DEFINE_FPU_VECT(stmm,1) },
+ { DEFINE_FPU_VECT(stmm,2) },
+ { DEFINE_FPU_VECT(stmm,3) },
+ { DEFINE_FPU_VECT(stmm,4) },
+ { DEFINE_FPU_VECT(stmm,5) },
+ { DEFINE_FPU_VECT(stmm,6) },
+ { DEFINE_FPU_VECT(stmm,7) },
+ { DEFINE_FPU_VECT(xmm,0) },
+ { DEFINE_FPU_VECT(xmm,1) },
+ { DEFINE_FPU_VECT(xmm,2) },
+ { DEFINE_FPU_VECT(xmm,3) },
+ { DEFINE_FPU_VECT(xmm,4) },
+ { DEFINE_FPU_VECT(xmm,5) },
+ { DEFINE_FPU_VECT(xmm,6) },
+ { DEFINE_FPU_VECT(xmm,7) },
+ { DEFINE_FPU_VECT(xmm,8) },
+ { DEFINE_FPU_VECT(xmm,9) },
+ { DEFINE_FPU_VECT(xmm,10) },
+ { DEFINE_FPU_VECT(xmm,11) },
+ { DEFINE_FPU_VECT(xmm,12) },
+ { DEFINE_FPU_VECT(xmm,13) },
+ { DEFINE_FPU_VECT(xmm,14) },
+ { DEFINE_FPU_VECT(xmm,15) },
+
+ { DEFINE_EXC(trapno) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }},
+ { DEFINE_EXC(err) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }},
+ { DEFINE_EXC(faultvaddr) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM }}
+};
+
+static size_t k_num_register_infos = (sizeof(g_register_infos)/sizeof(RegisterInfo));
+
+
+void
+RegisterContextMach_x86_64::Invalidate ()
+{
+ InvalidateAllRegisterStates();
+}
+
+
+size_t
+RegisterContextMach_x86_64::GetRegisterCount ()
+{
+ assert(k_num_register_infos == k_num_registers);
+ return k_num_registers;
+}
+
+
+const RegisterInfo *
+RegisterContextMach_x86_64::GetRegisterInfoAtIndex (uint32_t reg)
+{
+ assert(k_num_register_infos == k_num_registers);
+ if (reg < k_num_registers)
+ return &g_register_infos[reg];
+ return NULL;
+}
+
+
+size_t
+RegisterContextMach_x86_64::GetRegisterInfosCount ()
+{
+ return k_num_register_infos;
+}
+
+const RegisterInfo *
+RegisterContextMach_x86_64::GetRegisterInfos ()
+{
+ return g_register_infos;
+}
+
+
+
+static uint32_t g_gpr_regnums[] =
+{
+ gpr_rax,
+ gpr_rbx,
+ gpr_rcx,
+ gpr_rdx,
+ gpr_rdi,
+ gpr_rsi,
+ gpr_rbp,
+ gpr_rsp,
+ gpr_r8,
+ gpr_r9,
+ gpr_r10,
+ gpr_r11,
+ gpr_r12,
+ gpr_r13,
+ gpr_r14,
+ gpr_r15,
+ gpr_rip,
+ gpr_rflags,
+ gpr_cs,
+ gpr_fs,
+ gpr_gs
+};
+
+static uint32_t g_fpu_regnums[] =
+{
+ fpu_fcw,
+ fpu_fsw,
+ fpu_ftw,
+ fpu_fop,
+ fpu_ip,
+ fpu_cs,
+ fpu_dp,
+ fpu_ds,
+ fpu_mxcsr,
+ fpu_mxcsrmask,
+ fpu_stmm0,
+ fpu_stmm1,
+ fpu_stmm2,
+ fpu_stmm3,
+ fpu_stmm4,
+ fpu_stmm5,
+ fpu_stmm6,
+ fpu_stmm7,
+ fpu_xmm0,
+ fpu_xmm1,
+ fpu_xmm2,
+ fpu_xmm3,
+ fpu_xmm4,
+ fpu_xmm5,
+ fpu_xmm6,
+ fpu_xmm7,
+ fpu_xmm8,
+ fpu_xmm9,
+ fpu_xmm10,
+ fpu_xmm11,
+ fpu_xmm12,
+ fpu_xmm13,
+ fpu_xmm14,
+ fpu_xmm15
+};
+
+static uint32_t
+g_exc_regnums[] =
+{
+ exc_trapno,
+ exc_err,
+ exc_faultvaddr
+};
+
+// Number of registers in each register set
+const size_t k_num_gpr_registers = sizeof(g_gpr_regnums) / sizeof(uint32_t);
+const size_t k_num_fpu_registers = sizeof(g_fpu_regnums) / sizeof(uint32_t);
+const size_t k_num_exc_registers = sizeof(g_exc_regnums) / sizeof(uint32_t);
+
+//----------------------------------------------------------------------
+// Register set definitions. The first definitions at register set index
+// of zero is for all registers, followed by other registers sets. The
+// register information for the all register set need not be filled in.
+//----------------------------------------------------------------------
+static const RegisterSet g_reg_sets[] =
+{
+ { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums, },
+ { "Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums },
+ { "Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums }
+};
+
+const size_t k_num_regsets = sizeof(g_reg_sets) / sizeof(RegisterSet);
+
+
+size_t
+RegisterContextMach_x86_64::GetRegisterSetCount ()
+{
+ return k_num_regsets;
+}
+
+const RegisterSet *
+RegisterContextMach_x86_64::GetRegisterSet (uint32_t reg_set)
+{
+ if (reg_set < k_num_regsets)
+ return &g_reg_sets[reg_set];
+ return NULL;
+}
+
+int
+RegisterContextMach_x86_64::GetSetForNativeRegNum (int reg_num)
+{
+ if (reg_num < fpu_fcw)
+ return GPRRegSet;
+ else if (reg_num < exc_trapno)
+ return FPURegSet;
+ else if (reg_num < k_num_registers)
+ return EXCRegSet;
+ return -1;
+}
+
+void
+RegisterContextMach_x86_64::LogGPR(Log *log, const char *format, ...)
+{
+ if (log)
+ {
+ if (format)
+ {
+ va_list args;
+ va_start (args, format);
+ log->VAPrintf (format, args);
+ va_end (args);
+ }
+ for (uint32_t i=0; i<k_num_gpr_registers; i++)
+ {
+ uint32_t reg = gpr_rax + i;
+ log->Printf("%12s = 0x%16.16llx", g_register_infos[reg].name, (&gpr.rax)[reg]);
+ }
+ }
+}
+
+kern_return_t
+RegisterContextMach_x86_64::ReadGPR (bool force)
+{
+ int set = GPRRegSet;
+ if (force || !RegisterSetIsCached(set))
+ {
+ mach_msg_type_number_t count = GPRWordCount;
+ SetError(GPRRegSet, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&gpr, &count));
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_THREAD);
+ if (log)
+ LogGPR (log, "RegisterContextMach_x86_64::ReadGPR(thread = 0x%4.4x)", GetThreadID());
+ }
+ return GetError(GPRRegSet, Read);
+}
+
+kern_return_t
+RegisterContextMach_x86_64::ReadFPU (bool force)
+{
+ int set = FPURegSet;
+ if (force || !RegisterSetIsCached(set))
+ {
+ mach_msg_type_number_t count = FPUWordCount;
+ SetError(FPURegSet, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&fpu, &count));
+ }
+ return GetError(FPURegSet, Read);
+}
+
+kern_return_t
+RegisterContextMach_x86_64::ReadEXC (bool force)
+{
+ int set = EXCRegSet;
+ if (force || !RegisterSetIsCached(set))
+ {
+ mach_msg_type_number_t count = EXCWordCount;
+ SetError(EXCRegSet, Read, ::thread_get_state(GetThreadID(), set, (thread_state_t)&exc, &count));
+ }
+ return GetError(EXCRegSet, Read);
+}
+
+kern_return_t
+RegisterContextMach_x86_64::WriteGPR ()
+{
+ int set = GPRRegSet;
+ if (!RegisterSetIsCached(set))
+ {
+ SetError (set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet (PD_LOG_THREAD);
+ if (log)
+ LogGPR (log, "RegisterContextMach_x86_64::WriteGPR (thread = 0x%4.4x)", GetThreadID());
+ SetError (set, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&gpr, GPRWordCount));
+ SetError (set, Read, -1);
+ return GetError (set, Write);
+}
+
+kern_return_t
+RegisterContextMach_x86_64::WriteFPU ()
+{
+ int set = FPURegSet;
+ if (!RegisterSetIsCached(set))
+ {
+ SetError (set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError (set, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&fpu, FPUWordCount));
+ SetError (set, Read, -1);
+ return GetError (set, Write);
+}
+
+kern_return_t
+RegisterContextMach_x86_64::WriteEXC ()
+{
+ int set = EXCRegSet;
+ if (!RegisterSetIsCached(set))
+ {
+ SetError (set, Write, -1);
+ return KERN_INVALID_ARGUMENT;
+ }
+ SetError (set, Write, ::thread_set_state(GetThreadID(), set, (thread_state_t)&exc, EXCWordCount));
+ SetError (set, Read, -1);
+ return GetError (set, Write);
+}
+
+kern_return_t
+RegisterContextMach_x86_64::ReadRegisterSet(uint32_t set, bool force)
+{
+ switch (set)
+ {
+ case GPRRegSet: return ReadGPR (force);
+ case FPURegSet: return ReadFPU (force);
+ case EXCRegSet: return ReadEXC (force);
+ default: break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+kern_return_t
+RegisterContextMach_x86_64::WriteRegisterSet(uint32_t set)
+{
+ // Make sure we have a valid context to set.
+ switch (set)
+ {
+ case GPRRegSet: return WriteGPR ();
+ case FPURegSet: return WriteFPU ();
+ case EXCRegSet: return WriteEXC ();
+ default: break;
+ }
+ return KERN_INVALID_ARGUMENT;
+}
+
+
+bool
+RegisterContextMach_x86_64::ReadRegisterValue (uint32_t reg, Scalar &value)
+{
+ int set = RegisterContextMach_x86_64::GetSetForNativeRegNum (reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+ switch (reg)
+ {
+ case gpr_rax:
+ case gpr_rbx:
+ case gpr_rcx:
+ case gpr_rdx:
+ case gpr_rdi:
+ case gpr_rsi:
+ case gpr_rbp:
+ case gpr_rsp:
+ case gpr_r8:
+ case gpr_r9:
+ case gpr_r10:
+ case gpr_r11:
+ case gpr_r12:
+ case gpr_r13:
+ case gpr_r14:
+ case gpr_r15:
+ case gpr_rip:
+ case gpr_rflags:
+ case gpr_cs:
+ case gpr_fs:
+ case gpr_gs:
+ value = (&gpr.rax)[reg - gpr_rax];
+ break;
+
+ case fpu_fcw:
+ value = fpu.fcw;
+ break;
+
+ case fpu_fsw:
+ value = fpu.fsw;
+ break;
+
+ case fpu_ftw:
+ value = fpu.ftw;
+ break;
+
+ case fpu_fop:
+ value = fpu.fop;
+ break;
+
+ case fpu_ip:
+ value = fpu.ip;
+ break;
+
+ case fpu_cs:
+ value = fpu.cs;
+ break;
+
+ case fpu_dp:
+ value = fpu.dp;
+ break;
+
+ case fpu_ds:
+ value = fpu.ds;
+ break;
+
+ case fpu_mxcsr:
+ value = fpu.mxcsr;
+ break;
+
+ case fpu_mxcsrmask:
+ value = fpu.mxcsrmask;
+ break;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ // These values don't fit into scalar types,
+ // RegisterContext::ReadRegisterBytes() must be used for these
+ // registers
+ //::memcpy (reg_value.value.vector.uint8, fpu.stmm[reg - fpu_stmm0].bytes, 10);
+ return false;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ case fpu_xmm8:
+ case fpu_xmm9:
+ case fpu_xmm10:
+ case fpu_xmm11:
+ case fpu_xmm12:
+ case fpu_xmm13:
+ case fpu_xmm14:
+ case fpu_xmm15:
+ // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes()
+ // must be used for these registers
+ //::memcpy (reg_value.value.vector.uint8, fpu.xmm[reg - fpu_xmm0].bytes, 16);
+ return false;
+
+ case exc_trapno:
+ value = exc.trapno;
+ break;
+
+ case exc_err:
+ value = exc.err;
+ break;
+
+ case exc_faultvaddr:
+ value = exc.faultvaddr;
+ break;
+
+ default:
+ return false;
+ }
+ return true;
+}
+
+
+bool
+RegisterContextMach_x86_64::WriteRegisterValue (uint32_t reg, const Scalar &value)
+{
+ int set = RegisterContextMach_x86_64::GetSetForNativeRegNum (reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+ switch (reg)
+ {
+ case gpr_rax:
+ case gpr_rbx:
+ case gpr_rcx:
+ case gpr_rdx:
+ case gpr_rdi:
+ case gpr_rsi:
+ case gpr_rbp:
+ case gpr_rsp:
+ case gpr_r8:
+ case gpr_r9:
+ case gpr_r10:
+ case gpr_r11:
+ case gpr_r12:
+ case gpr_r13:
+ case gpr_r14:
+ case gpr_r15:
+ case gpr_rip:
+ case gpr_rflags:
+ case gpr_cs:
+ case gpr_fs:
+ case gpr_gs:
+ (&gpr.rax)[reg - gpr_rax] = value.ULongLong(0);
+ break;
+
+ case fpu_fcw:
+ fpu.fcw = value.UInt(0);
+ break;
+
+ case fpu_fsw:
+ fpu.fsw = value.UInt(0);
+ break;
+
+ case fpu_ftw:
+ fpu.ftw = value.UInt(0);
+ break;
+
+ case fpu_fop:
+ fpu.fop = value.UInt(0);
+ break;
+
+ case fpu_ip:
+ fpu.ip = value.UInt(0);
+ break;
+
+ case fpu_cs:
+ fpu.cs = value.UInt(0);
+ break;
+
+ case fpu_dp:
+ fpu.dp = value.UInt(0);
+ break;
+
+ case fpu_ds:
+ fpu.ds = value.UInt(0);
+ break;
+
+ case fpu_mxcsr:
+ fpu.mxcsr = value.UInt(0);
+ break;
+
+ case fpu_mxcsrmask:
+ fpu.mxcsrmask = value.UInt(0);
+ break;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes()
+ // must be used for these registers
+ //::memcpy (fpu.stmm[reg - fpu_stmm0].bytes, reg_value.value.vector.uint8, 10);
+ return false;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ case fpu_xmm8:
+ case fpu_xmm9:
+ case fpu_xmm10:
+ case fpu_xmm11:
+ case fpu_xmm12:
+ case fpu_xmm13:
+ case fpu_xmm14:
+ case fpu_xmm15:
+ // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes()
+ // must be used for these registers
+ //::memcpy (fpu.xmm[reg - fpu_xmm0].bytes, reg_value.value.vector.uint8, 16);
+ return false;
+
+ case exc_trapno:
+ exc.trapno = value.UInt(0);
+ break;
+
+ case exc_err:
+ exc.err = value.UInt(0);
+ break;
+
+ case exc_faultvaddr:
+ exc.faultvaddr = value.UInt(0);
+ break;
+
+ default:
+ return false;
+ }
+ return WriteRegisterSet(set) == KERN_SUCCESS;
+}
+
+bool
+RegisterContextMach_x86_64::ReadRegisterBytes (uint32_t reg, DataExtractor &data)
+{
+ int set = RegisterContextMach_x86_64::GetSetForNativeRegNum (reg);
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+ const RegisterInfo * reg_info = GetRegisterInfoAtIndex (reg);
+ if (reg_info == NULL)
+ return false;
+
+ switch (reg)
+ {
+ case gpr_rax:
+ case gpr_rbx:
+ case gpr_rcx:
+ case gpr_rdx:
+ case gpr_rdi:
+ case gpr_rsi:
+ case gpr_rbp:
+ case gpr_rsp:
+ case gpr_r8:
+ case gpr_r9:
+ case gpr_r10:
+ case gpr_r11:
+ case gpr_r12:
+ case gpr_r13:
+ case gpr_r14:
+ case gpr_r15:
+ case gpr_rip:
+ case gpr_rflags:
+ case gpr_cs:
+ case gpr_fs:
+ case gpr_gs:
+ data.SetData(&gpr.rax + reg - gpr_rax, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_fcw:
+ data.SetData(&fpu.fcw, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_fsw:
+ data.SetData(&fpu.fsw, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_ftw:
+ data.SetData(&fpu.ftw, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_fop:
+ data.SetData(&fpu.fop, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_ip:
+ data.SetData(&fpu.ip, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_cs:
+ data.SetData(&fpu.cs, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_dp:
+ data.SetData(&fpu.dp, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_ds:
+ data.SetData(&fpu.ds, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_mxcsr:
+ data.SetData(&fpu.mxcsr, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_mxcsrmask:
+ data.SetData(&fpu.mxcsrmask, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ data.SetData(fpu.stmm[reg - fpu_stmm0].bytes, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ case fpu_xmm8:
+ case fpu_xmm9:
+ case fpu_xmm10:
+ case fpu_xmm11:
+ case fpu_xmm12:
+ case fpu_xmm13:
+ case fpu_xmm14:
+ case fpu_xmm15:
+ data.SetData(fpu.xmm[reg - fpu_xmm0].bytes, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case exc_trapno:
+ data.SetData(&exc.trapno, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case exc_err:
+ data.SetData(&exc.err, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ case exc_faultvaddr:
+ data.SetData(&exc.faultvaddr, reg_info->byte_size, eByteOrderHost);
+ break;
+
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool
+RegisterContextMach_x86_64::WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset)
+{
+ int set = RegisterContextMach_x86_64::GetSetForNativeRegNum (reg);
+
+ if (set == -1)
+ return false;
+
+ if (ReadRegisterSet(set, false) != KERN_SUCCESS)
+ return false;
+
+
+ const RegisterInfo * reg_info = GetRegisterInfoAtIndex (reg);
+ if (reg_info == NULL && data.ValidOffsetForDataOfSize(data_offset, reg_info->byte_size))
+ return false;
+
+ uint32_t offset = data_offset;
+ switch (reg)
+ {
+ case gpr_rax:
+ case gpr_rbx:
+ case gpr_rcx:
+ case gpr_rdx:
+ case gpr_rdi:
+ case gpr_rsi:
+ case gpr_rbp:
+ case gpr_rsp:
+ case gpr_r8:
+ case gpr_r9:
+ case gpr_r10:
+ case gpr_r11:
+ case gpr_r12:
+ case gpr_r13:
+ case gpr_r14:
+ case gpr_r15:
+ case gpr_rip:
+ case gpr_rflags:
+ case gpr_cs:
+ case gpr_fs:
+ case gpr_gs:
+ (&gpr.rax)[reg - gpr_rax] = data.GetU32 (&offset);
+ break;
+
+ case fpu_fcw:
+ fpu.fcw = data.GetU16(&offset);
+ break;
+
+ case fpu_fsw:
+ fpu.fsw = data.GetU16(&offset);
+ break;
+
+ case fpu_ftw:
+ fpu.ftw = data.GetU8(&offset);
+ break;
+
+ case fpu_fop:
+ fpu.fop = data.GetU16(&offset);
+ break;
+
+ case fpu_ip:
+ fpu.ip = data.GetU32(&offset);
+ break;
+
+ case fpu_cs:
+ fpu.cs = data.GetU16(&offset);
+ break;
+
+ case fpu_dp:
+ fpu.dp = data.GetU32(&offset);
+ break;
+
+ case fpu_ds:
+ fpu.ds = data.GetU16(&offset);
+ break;
+
+ case fpu_mxcsr:
+ fpu.mxcsr = data.GetU32(&offset);
+ break;
+
+ case fpu_mxcsrmask:
+ fpu.mxcsrmask = data.GetU32(&offset);
+ break;
+
+ case fpu_stmm0:
+ case fpu_stmm1:
+ case fpu_stmm2:
+ case fpu_stmm3:
+ case fpu_stmm4:
+ case fpu_stmm5:
+ case fpu_stmm6:
+ case fpu_stmm7:
+ ::memcpy (fpu.stmm[reg - fpu_stmm0].bytes, data.PeekData(offset, reg_info->byte_size), reg_info->byte_size);
+ return false;
+
+ case fpu_xmm0:
+ case fpu_xmm1:
+ case fpu_xmm2:
+ case fpu_xmm3:
+ case fpu_xmm4:
+ case fpu_xmm5:
+ case fpu_xmm6:
+ case fpu_xmm7:
+ case fpu_xmm8:
+ case fpu_xmm9:
+ case fpu_xmm10:
+ case fpu_xmm11:
+ case fpu_xmm12:
+ case fpu_xmm13:
+ case fpu_xmm14:
+ case fpu_xmm15:
+ // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes()
+ // must be used for these registers
+ ::memcpy (fpu.xmm[reg - fpu_xmm0].bytes, data.PeekData(offset, reg_info->byte_size), reg_info->byte_size);
+ return false;
+
+ case exc_trapno:
+ exc.trapno = data.GetU32 (&offset);
+ break;
+
+ case exc_err:
+ exc.err = data.GetU32 (&offset);
+ break;
+
+ case exc_faultvaddr:
+ exc.faultvaddr = data.GetU32 (&offset);
+ break;
+
+ default:
+ return false;
+ }
+ return WriteRegisterSet(set) == KERN_SUCCESS;
+}
+
+bool
+RegisterContextMach_x86_64::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
+{
+ data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0));
+ if (data_sp &&
+ ReadGPR (false) == KERN_SUCCESS &&
+ ReadFPU (false) == KERN_SUCCESS &&
+ ReadEXC (false) == KERN_SUCCESS)
+ {
+ uint8_t *dst = data_sp->GetBytes();
+ ::memcpy (dst, &gpr, sizeof(gpr));
+ dst += sizeof(gpr);
+
+ ::memcpy (dst, &fpu, sizeof(fpu));
+ dst += sizeof(gpr);
+
+ ::memcpy (dst, &exc, sizeof(exc));
+ return true;
+ }
+ return false;
+}
+
+bool
+RegisterContextMach_x86_64::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp)
+{
+ if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE)
+ {
+ const uint8_t *src = data_sp->GetBytes();
+ ::memcpy (&gpr, src, sizeof(gpr));
+ src += sizeof(gpr);
+
+ ::memcpy (&fpu, src, sizeof(fpu));
+ src += sizeof(gpr);
+
+ ::memcpy (&exc, src, sizeof(exc));
+ uint32_t success_count = 0;
+ if (WriteGPR() == KERN_SUCCESS)
+ ++success_count;
+ if (WriteFPU() == KERN_SUCCESS)
+ ++success_count;
+ if (WriteEXC() == KERN_SUCCESS)
+ ++success_count;
+ return success_count == 3;
+ }
+ return false;
+}
+
+
+uint32_t
+RegisterContextMach_x86_64::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t reg)
+{
+ if (kind == eRegisterKindGeneric)
+ {
+ switch (reg)
+ {
+ case LLDB_REGNUM_GENERIC_PC: return gpr_rip;
+ case LLDB_REGNUM_GENERIC_SP: return gpr_rsp;
+ case LLDB_REGNUM_GENERIC_FP: return gpr_rbp;
+ case LLDB_REGNUM_GENERIC_FLAGS: return gpr_rflags;
+ case LLDB_REGNUM_GENERIC_RA:
+ default:
+ break;
+ }
+ }
+ else if (kind == eRegisterKindGCC || kind == eRegisterKindDWARF)
+ {
+ switch (reg)
+ {
+ case gcc_dwarf_gpr_rax: return gpr_rax;
+ case gcc_dwarf_gpr_rdx: return gpr_rdx;
+ case gcc_dwarf_gpr_rcx: return gpr_rcx;
+ case gcc_dwarf_gpr_rbx: return gpr_rbx;
+ case gcc_dwarf_gpr_rsi: return gpr_rsi;
+ case gcc_dwarf_gpr_rdi: return gpr_rdi;
+ case gcc_dwarf_gpr_rbp: return gpr_rbp;
+ case gcc_dwarf_gpr_rsp: return gpr_rsp;
+ case gcc_dwarf_gpr_r8: return gpr_r8;
+ case gcc_dwarf_gpr_r9: return gpr_r9;
+ case gcc_dwarf_gpr_r10: return gpr_r10;
+ case gcc_dwarf_gpr_r11: return gpr_r11;
+ case gcc_dwarf_gpr_r12: return gpr_r12;
+ case gcc_dwarf_gpr_r13: return gpr_r13;
+ case gcc_dwarf_gpr_r14: return gpr_r14;
+ case gcc_dwarf_gpr_r15: return gpr_r15;
+ case gcc_dwarf_gpr_rip: return gpr_rip;
+ case gcc_dwarf_fpu_xmm0: return fpu_xmm0;
+ case gcc_dwarf_fpu_xmm1: return fpu_xmm1;
+ case gcc_dwarf_fpu_xmm2: return fpu_xmm2;
+ case gcc_dwarf_fpu_xmm3: return fpu_xmm3;
+ case gcc_dwarf_fpu_xmm4: return fpu_xmm4;
+ case gcc_dwarf_fpu_xmm5: return fpu_xmm5;
+ case gcc_dwarf_fpu_xmm6: return fpu_xmm6;
+ case gcc_dwarf_fpu_xmm7: return fpu_xmm7;
+ case gcc_dwarf_fpu_xmm8: return fpu_xmm8;
+ case gcc_dwarf_fpu_xmm9: return fpu_xmm9;
+ case gcc_dwarf_fpu_xmm10: return fpu_xmm10;
+ case gcc_dwarf_fpu_xmm11: return fpu_xmm11;
+ case gcc_dwarf_fpu_xmm12: return fpu_xmm12;
+ case gcc_dwarf_fpu_xmm13: return fpu_xmm13;
+ case gcc_dwarf_fpu_xmm14: return fpu_xmm14;
+ case gcc_dwarf_fpu_xmm15: return fpu_xmm15;
+ case gcc_dwarf_fpu_stmm0: return fpu_stmm0;
+ case gcc_dwarf_fpu_stmm1: return fpu_stmm1;
+ case gcc_dwarf_fpu_stmm2: return fpu_stmm2;
+ case gcc_dwarf_fpu_stmm3: return fpu_stmm3;
+ case gcc_dwarf_fpu_stmm4: return fpu_stmm4;
+ case gcc_dwarf_fpu_stmm5: return fpu_stmm5;
+ case gcc_dwarf_fpu_stmm6: return fpu_stmm6;
+ case gcc_dwarf_fpu_stmm7: return fpu_stmm7;
+ default:
+ break;
+ }
+ }
+ else if (kind == eRegisterKindGDB)
+ {
+ switch (reg)
+ {
+ case gdb_gpr_rax : return gpr_rax;
+ case gdb_gpr_rbx : return gpr_rbx;
+ case gdb_gpr_rcx : return gpr_rcx;
+ case gdb_gpr_rdx : return gpr_rdx;
+ case gdb_gpr_rsi : return gpr_rsi;
+ case gdb_gpr_rdi : return gpr_rdi;
+ case gdb_gpr_rbp : return gpr_rbp;
+ case gdb_gpr_rsp : return gpr_rsp;
+ case gdb_gpr_r8 : return gpr_r8;
+ case gdb_gpr_r9 : return gpr_r9;
+ case gdb_gpr_r10 : return gpr_r10;
+ case gdb_gpr_r11 : return gpr_r11;
+ case gdb_gpr_r12 : return gpr_r12;
+ case gdb_gpr_r13 : return gpr_r13;
+ case gdb_gpr_r14 : return gpr_r14;
+ case gdb_gpr_r15 : return gpr_r15;
+ case gdb_gpr_rip : return gpr_rip;
+ case gdb_gpr_rflags : return gpr_rflags;
+ case gdb_gpr_cs : return gpr_cs;
+ case gdb_gpr_ss : return gpr_gs; // HACK: For now for "ss", just copy what is in "gs"
+ case gdb_gpr_ds : return gpr_gs; // HACK: For now for "ds", just copy what is in "gs"
+ case gdb_gpr_es : return gpr_gs; // HACK: For now for "es", just copy what is in "gs"
+ case gdb_gpr_fs : return gpr_fs;
+ case gdb_gpr_gs : return gpr_gs;
+ case gdb_fpu_stmm0 : return fpu_stmm0;
+ case gdb_fpu_stmm1 : return fpu_stmm1;
+ case gdb_fpu_stmm2 : return fpu_stmm2;
+ case gdb_fpu_stmm3 : return fpu_stmm3;
+ case gdb_fpu_stmm4 : return fpu_stmm4;
+ case gdb_fpu_stmm5 : return fpu_stmm5;
+ case gdb_fpu_stmm6 : return fpu_stmm6;
+ case gdb_fpu_stmm7 : return fpu_stmm7;
+ case gdb_fpu_fctrl : return fpu_fctrl;
+ case gdb_fpu_fstat : return fpu_fstat;
+ case gdb_fpu_ftag : return fpu_ftag;
+ case gdb_fpu_fiseg : return fpu_fiseg;
+ case gdb_fpu_fioff : return fpu_fioff;
+ case gdb_fpu_foseg : return fpu_foseg;
+ case gdb_fpu_fooff : return fpu_fooff;
+ case gdb_fpu_fop : return fpu_fop;
+ case gdb_fpu_xmm0 : return fpu_xmm0;
+ case gdb_fpu_xmm1 : return fpu_xmm1;
+ case gdb_fpu_xmm2 : return fpu_xmm2;
+ case gdb_fpu_xmm3 : return fpu_xmm3;
+ case gdb_fpu_xmm4 : return fpu_xmm4;
+ case gdb_fpu_xmm5 : return fpu_xmm5;
+ case gdb_fpu_xmm6 : return fpu_xmm6;
+ case gdb_fpu_xmm7 : return fpu_xmm7;
+ case gdb_fpu_xmm8 : return fpu_xmm8;
+ case gdb_fpu_xmm9 : return fpu_xmm9;
+ case gdb_fpu_xmm10 : return fpu_xmm10;
+ case gdb_fpu_xmm11 : return fpu_xmm11;
+ case gdb_fpu_xmm12 : return fpu_xmm12;
+ case gdb_fpu_xmm13 : return fpu_xmm13;
+ case gdb_fpu_xmm14 : return fpu_xmm14;
+ case gdb_fpu_xmm15 : return fpu_xmm15;
+ case gdb_fpu_mxcsr : return fpu_mxcsr;
+ default:
+ break;
+ }
+ }
+ return LLDB_INVALID_REGNUM;
+}
+
+bool
+RegisterContextMach_x86_64::HardwareSingleStep (bool enable)
+{
+ if (ReadGPR(true) != KERN_SUCCESS)
+ return false;
+
+ const uint64_t trace_bit = 0x100ull;
+ if (enable)
+ {
+
+ if (gpr.rflags & trace_bit)
+ return true; // trace bit is already set, there is nothing to do
+ else
+ gpr.rflags |= trace_bit;
+ }
+ else
+ {
+ if (gpr.rflags & trace_bit)
+ gpr.rflags &= ~trace_bit;
+ else
+ return true; // trace bit is clear, there is nothing to do
+ }
+
+ return WriteGPR() == KERN_SUCCESS;
+}
+
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.h b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.h
new file mode 100644
index 00000000000..4f33bbdc2db
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/RegisterContextMach_x86_64.h
@@ -0,0 +1,261 @@
+//===-- RegisterContextMach_x86_64.h ----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_RegisterContextMach_x86_64_h_
+#define liblldb_RegisterContextMach_x86_64_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/lldb-private.h"
+#include "lldb/Target/RegisterContext.h"
+
+class RegisterContextMach_x86_64 : public lldb_private::RegisterContext
+{
+public:
+ RegisterContextMach_x86_64 (lldb_private::Thread &thread,
+ lldb_private::StackFrame *frame);
+
+ virtual
+ ~RegisterContextMach_x86_64();
+
+ virtual void
+ Invalidate ();
+
+ virtual size_t
+ GetRegisterCount ();
+
+ virtual const lldb::RegisterInfo *
+ GetRegisterInfoAtIndex (uint32_t reg);
+
+ virtual size_t
+ GetRegisterSetCount ();
+
+ virtual const lldb::RegisterSet *
+ GetRegisterSet (uint32_t set);
+
+ virtual bool
+ ReadRegisterValue (uint32_t reg, lldb_private::Scalar &value);
+
+ virtual bool
+ ReadRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data);
+
+ virtual bool
+ ReadAllRegisterValues (lldb::DataBufferSP &data_sp);
+
+ virtual bool
+ WriteRegisterValue (uint32_t reg, const lldb_private::Scalar &value);
+
+ virtual bool
+ WriteRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data, uint32_t data_offset = 0);
+
+ virtual bool
+ WriteAllRegisterValues (const lldb::DataBufferSP &data_sp);
+
+ virtual uint32_t
+ ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num);
+
+ virtual bool
+ HardwareSingleStep (bool enable);
+
+ struct GPR
+ {
+ uint64_t rax;
+ uint64_t rbx;
+ uint64_t rcx;
+ uint64_t rdx;
+ uint64_t rdi;
+ uint64_t rsi;
+ uint64_t rbp;
+ uint64_t rsp;
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t rip;
+ uint64_t rflags;
+ uint64_t cs;
+ uint64_t fs;
+ uint64_t gs;
+ };
+
+ struct MMSReg
+ {
+ uint8_t bytes[10];
+ uint8_t pad[6];
+ };
+
+ struct XMMReg
+ {
+ uint8_t bytes[16];
+ };
+
+ struct FPU
+ {
+ uint32_t pad[2];
+ uint16_t fcw; // "fctrl"
+ uint16_t fsw; // "fstat"
+ uint8_t ftw; // "ftag"
+ uint8_t pad1;
+ uint16_t fop; // "fop"
+ uint32_t ip; // "fioff"
+ uint16_t cs; // "fiseg"
+ uint16_t pad2;
+ uint32_t dp; // "fooff"
+ uint16_t ds; // "foseg"
+ uint16_t pad3;
+ uint32_t mxcsr;
+ uint32_t mxcsrmask;
+ MMSReg stmm[8];
+ XMMReg xmm[16];
+ uint8_t pad4[6*16];
+ int pad5;
+ };
+
+ struct EXC
+ {
+ uint32_t trapno;
+ uint32_t err;
+ uint64_t faultvaddr;
+ };
+
+protected:
+
+ typedef enum
+ {
+ GPRRegSet = 4,
+ FPURegSet = 5,
+ EXCRegSet = 6
+ };
+
+ enum
+ {
+ GPRWordCount = sizeof(GPR)/sizeof(uint32_t),
+ FPUWordCount = sizeof(FPU)/sizeof(uint32_t),
+ EXCWordCount = sizeof(EXC)/sizeof(uint32_t)
+ };
+
+ enum
+ {
+ Read = 0,
+ Write = 1,
+ kNumErrors = 2
+ };
+
+ GPR gpr;
+ FPU fpu;
+ EXC exc;
+ kern_return_t gpr_errs[2]; // Read/Write errors
+ kern_return_t fpu_errs[2]; // Read/Write errors
+ kern_return_t exc_errs[2]; // Read/Write errors
+
+ void
+ InvalidateAllRegisterStates()
+ {
+ SetError (GPRRegSet, Read, -1);
+ SetError (FPURegSet, Read, -1);
+ SetError (EXCRegSet, Read, -1);
+ }
+
+ kern_return_t
+ GetError (int flavor, uint32_t err_idx) const
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (flavor)
+ {
+ // When getting all errors, just OR all values together to see if
+ // we got any kind of error.
+ case GPRRegSet: return gpr_errs[err_idx];
+ case FPURegSet: return fpu_errs[err_idx];
+ case EXCRegSet: return exc_errs[err_idx];
+ default: break;
+ }
+ }
+ return -1;
+ }
+
+ bool
+ SetError (int flavor, uint32_t err_idx, kern_return_t err)
+ {
+ if (err_idx < kNumErrors)
+ {
+ switch (flavor)
+ {
+ case GPRRegSet:
+ gpr_errs[err_idx] = err;
+ return true;
+
+ case FPURegSet:
+ fpu_errs[err_idx] = err;
+ return true;
+
+ case EXCRegSet:
+ exc_errs[err_idx] = err;
+ return true;
+
+ default: break;
+ }
+ }
+ return false;
+ }
+
+ bool
+ RegisterSetIsCached (int set) const
+ {
+ return GetError(set, Read) == KERN_SUCCESS;
+ }
+
+ void
+ LogGPR (lldb_private::Log *log, const char *format, ...);
+
+ kern_return_t
+ ReadGPR (bool force);
+
+ kern_return_t
+ ReadFPU (bool force);
+
+ kern_return_t
+ ReadEXC (bool force);
+
+ kern_return_t
+ WriteGPR ();
+
+ kern_return_t
+ WriteFPU ();
+
+ kern_return_t
+ WriteEXC ();
+
+ kern_return_t
+ ReadRegisterSet (uint32_t set, bool force);
+
+ kern_return_t
+ WriteRegisterSet (uint32_t set);
+
+ static uint32_t
+ GetRegisterNumber (uint32_t reg_kind, uint32_t reg_num);
+
+ static int
+ GetSetForNativeRegNum (int reg_num);
+
+ static size_t
+ GetRegisterInfosCount ();
+
+ static const lldb::RegisterInfo *
+ GetRegisterInfos ();
+
+};
+
+#endif // liblldb_RegisterContextMach_x86_64_h_
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.cpp b/lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.cpp
new file mode 100644
index 00000000000..46d84a853d0
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.cpp
@@ -0,0 +1,769 @@
+//===-- ThreadMacOSX.cpp ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#include "ThreadMacOSX.h"
+
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+
+#include "ProcessMacOSX.h"
+#include "ProcessMacOSXLog.h"
+#include "MachThreadContext.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Breakpoint/WatchpointLocation.h"
+#include "lldb/Core/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// Thread Registers
+//----------------------------------------------------------------------
+
+ThreadMacOSX::ThreadMacOSX (ProcessMacOSX &process, lldb::tid_t tid) :
+ Thread(process, tid),
+ m_fp_pc_pairs(),
+ m_basic_info(),
+ m_suspend_count(0),
+ m_stop_exception(),
+ m_context()
+{
+ ProcessMacOSX::CreateArchCalback create_arch_callback = process.GetArchCreateCallback();
+ assert(create_arch_callback != NULL);
+ m_context.reset(create_arch_callback(process.GetArchSpec(), *this));
+ assert(m_context.get() != NULL);
+ m_context->InitializeInstance();
+ ::bzero (&m_basic_info, sizeof (m_basic_info));
+ ::bzero (&m_ident_info, sizeof (m_ident_info));
+ ::bzero (&m_proc_threadinfo, sizeof (m_proc_threadinfo));
+ ProcessMacOSXLog::LogIf(PD_LOG_THREAD | PD_LOG_VERBOSE, "ThreadMacOSX::ThreadMacOSX ( pid = %i, tid = 0x%4.4x, )", m_process.GetID(), GetID());
+}
+
+ThreadMacOSX::~ThreadMacOSX ()
+{
+}
+
+#if defined (__i386__) || defined (__x86_64__)
+ #define MACH_SOFTWARE_BREAKPOINT_DATA_0 EXC_I386_BPT
+ #define MACH_TRAP_DATA_0 EXC_I386_SGL
+#elif defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__)
+ #define MACH_SOFTWARE_BREAKPOINT_DATA_0 EXC_PPC_BREAKPOINT
+
+#elif defined (__arm__)
+ #define MACH_SOFTWARE_BREAKPOINT_DATA_0 EXC_ARM_BREAKPOINT
+#endif
+
+
+bool
+ThreadMacOSX::GetRawStopReason (Thread::StopInfo *stop_info )
+{
+ stop_info->SetThread(this);
+
+ bool success = GetStopException().GetStopInfo(stop_info);
+
+
+#if defined (MACH_SOFTWARE_BREAKPOINT_DATA_0) || defined (MACH_TRAP_DATA_0)
+ if (stop_info->GetStopReason() == eStopReasonException)
+ {
+ if (stop_info->GetExceptionType() == EXC_BREAKPOINT && stop_info->GetExceptionDataCount() == 2)
+ {
+ const lldb::addr_t data_0 = stop_info->GetExceptionDataAtIndex(0);
+#if defined (MACH_SOFTWARE_BREAKPOINT_DATA_0)
+ if (data_0 == MACH_SOFTWARE_BREAKPOINT_DATA_0)
+ {
+ lldb::addr_t pc = GetRegisterContext()->GetPC();
+ lldb::user_id_t break_id = m_process.GetBreakpointSiteList().FindIDByAddress(pc);
+ if (break_id != LLDB_INVALID_BREAK_ID)
+ {
+ stop_info->Clear ();
+ stop_info->SetStopReasonWithBreakpointSiteID (break_id);
+ return success;
+ }
+ }
+#endif
+#if defined (MACH_TRAP_DATA_0)
+ if (data_0 == MACH_TRAP_DATA_0)
+ {
+ stop_info->Clear ();
+ stop_info->SetStopReasonToTrace ();
+ return success;
+ }
+#endif
+ }
+ }
+#endif
+
+ if (stop_info->GetStopReason() == eStopReasonException)
+ {
+ if (stop_info->GetExceptionType() == EXC_SOFTWARE &&
+ stop_info->GetExceptionDataCount() == 2 &&
+ stop_info->GetExceptionDataAtIndex(0) == EXC_SOFT_SIGNAL)
+ {
+ int signo = stop_info->GetExceptionDataAtIndex(1);
+ stop_info->Clear ();
+ stop_info->SetStopReasonWithSignal (signo);
+ }
+ }
+ else
+ {
+ stop_info->SetStopReasonToNone();
+ }
+
+ return success;
+}
+
+const char *
+ThreadMacOSX::GetInfo ()
+{
+ return GetBasicInfoAsString();
+}
+
+bool
+ThreadMacOSX::GetIdentifierInfo ()
+{
+#ifdef THREAD_IDENTIFIER_INFO_COUNT
+ if (m_ident_info.thread_id == 0)
+ {
+ mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
+ return ::thread_info (GetID(), THREAD_IDENTIFIER_INFO, (thread_info_t) &m_ident_info, &count) == KERN_SUCCESS;
+ }
+#else
+ //m_error.SetErrorString("Thread_info doesn't support THREAD_IDENTIFIER_INFO.");
+#endif
+
+ return false;
+}
+
+const char *
+ThreadMacOSX::GetDispatchQueueName()
+{
+ if (GetIdentifierInfo ())
+ {
+ if (m_ident_info.dispatch_qaddr == 0)
+ return NULL;
+
+ uint8_t memory_buffer[8];
+ DataExtractor data(memory_buffer, sizeof(memory_buffer), m_process.GetByteOrder(), m_process.GetAddressByteSize());
+ ModuleSP module_sp(m_process.GetTarget().GetImages().FindFirstModuleForFileSpec (FileSpec("libSystem.B.dylib")));
+ if (module_sp.get() == NULL)
+ return NULL;
+
+ lldb::addr_t dispatch_queue_offsets_addr = LLDB_INVALID_ADDRESS;
+ const Symbol *dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType (ConstString("dispatch_queue_offsets"), eSymbolTypeData);
+ if (dispatch_queue_offsets_symbol)
+ dispatch_queue_offsets_addr = dispatch_queue_offsets_symbol->GetValue().GetLoadAddress(&GetProcess());
+
+ if (dispatch_queue_offsets_addr == LLDB_INVALID_ADDRESS)
+ return NULL;
+
+ // Excerpt from src/queue_private.h
+ struct dispatch_queue_offsets_s
+ {
+ uint16_t dqo_version;
+ uint16_t dqo_label;
+ uint16_t dqo_label_size;
+ } dispatch_queue_offsets;
+
+ Error error;
+ if (m_process.ReadMemory (dispatch_queue_offsets_addr, memory_buffer, sizeof(dispatch_queue_offsets), error) == sizeof(dispatch_queue_offsets))
+ {
+ uint32_t data_offset = 0;
+ if (data.GetU16(&data_offset, &dispatch_queue_offsets.dqo_version, sizeof(dispatch_queue_offsets)/sizeof(uint16_t)))
+ {
+ if (m_process.ReadMemory (m_ident_info.dispatch_qaddr, &memory_buffer, data.GetAddressByteSize(), error) == data.GetAddressByteSize())
+ {
+ data_offset = 0;
+ lldb::addr_t queue_addr = data.GetAddress(&data_offset);
+ lldb::addr_t label_addr = queue_addr + dispatch_queue_offsets.dqo_label;
+ const size_t chunk_size = 32;
+ uint32_t label_pos = 0;
+ m_dispatch_queue_name.resize(chunk_size, '\0');
+ while (1)
+ {
+ size_t bytes_read = m_process.ReadMemory (label_addr + label_pos, &m_dispatch_queue_name[label_pos], chunk_size, error);
+
+ if (bytes_read <= 0)
+ break;
+
+ if (m_dispatch_queue_name.find('\0', label_pos) != std::string::npos)
+ break;
+ label_pos += bytes_read;
+ }
+ m_dispatch_queue_name.erase(m_dispatch_queue_name.find('\0'));
+ }
+ }
+ }
+ }
+
+ if (m_dispatch_queue_name.empty())
+ return NULL;
+ return m_dispatch_queue_name.c_str();
+}
+
+const char *
+ThreadMacOSX::GetName ()
+{
+ if (GetIdentifierInfo ())
+ ::proc_pidinfo (m_process.GetID(), PROC_PIDTHREADINFO, m_ident_info.thread_handle, &m_proc_threadinfo, sizeof (m_proc_threadinfo));
+
+ // No thread name, lets return the queue name instead
+ if (m_proc_threadinfo.pth_name[0] == '\0')
+ return GetDispatchQueueName();
+
+ // Return the thread name if there was one
+ if (m_proc_threadinfo.pth_name[0])
+ return m_proc_threadinfo.pth_name;
+ return NULL;
+}
+
+bool
+ThreadMacOSX::WillResume (StateType resume_state)
+{
+ ThreadWillResume(resume_state);
+ Thread::WillResume(resume_state);
+ return true;
+}
+
+void
+ThreadMacOSX::RefreshStateAfterStop()
+{
+ // Invalidate all registers in our register context
+ GetRegisterContext()->Invalidate();
+
+ m_context->RefreshStateAfterStop();
+
+ // We may have suspended this thread so the primary thread could step
+ // without worrying about race conditions, so lets restore our suspend
+ // count.
+ RestoreSuspendCount();
+
+ // Update the basic information for a thread for suspend count reasons.
+ ThreadMacOSX::GetBasicInfo(GetID(), &m_basic_info);
+ m_suspend_count = m_basic_info.suspend_count;
+ m_basic_info_string.clear();
+}
+
+uint32_t
+ThreadMacOSX::GetStackFrameCount()
+{
+ if (m_fp_pc_pairs.empty())
+ GetStackFrameData(m_fp_pc_pairs);
+ return m_fp_pc_pairs.size();
+}
+
+// Make sure that GetStackFrameAtIndex() does NOT call GetStackFrameCount() when
+// getting the stack frame at index zero! This way GetStackFrameCount() (via
+// GetStackFRameData()) can call this function to get the first frame in order
+// to provide the first frame to a lower call for efficiency sake (avoid
+// redundant lookups in the frame symbol context).
+lldb::StackFrameSP
+ThreadMacOSX::GetStackFrameAtIndex (uint32_t idx)
+{
+ StackFrameSP frame_sp(m_frames.GetFrameAtIndex(idx));
+
+ if (frame_sp)
+ return frame_sp;
+
+ // Don't try and fetch a frame while process is running
+ // Calling IsRunning isn't right here, because IsRunning reads the Public
+ // state but we need to be able to read the stack frames in the ShouldStop
+ // methods, which happen before the Public state has been updated.
+// if (m_process.IsRunning())
+// return frame_sp;
+
+ // Special case the first frame (idx == 0) so that we don't need to
+ // know how many stack frames there are to get it. If we need any other
+ // frames, then we do need to know if "idx" is a valid index.
+ if (idx == 0)
+ {
+ // If this is the first frame, we want to share the thread register
+ // context with the stack frame at index zero.
+ GetRegisterContext();
+ assert (m_reg_context_sp.get());
+ frame_sp.reset (new StackFrame (idx, *this, m_reg_context_sp, m_reg_context_sp->GetFP(), m_reg_context_sp->GetPC()));
+ }
+ else if (idx < GetStackFrameCount())
+ {
+ assert (idx < m_fp_pc_pairs.size());
+ frame_sp.reset (new StackFrame (idx, *this, m_fp_pc_pairs[idx].first, m_fp_pc_pairs[idx].second));
+ }
+ m_frames.SetFrameAtIndex(idx, frame_sp);
+ return frame_sp;
+}
+
+void
+ThreadMacOSX::ClearStackFrames ()
+{
+ m_fp_pc_pairs.clear();
+ Thread::ClearStackFrames();
+}
+
+
+
+int32_t
+ThreadMacOSX::Suspend()
+{
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_THREAD);
+ if (log && log->GetMask().IsSet(PD_LOG_VERBOSE))
+ log->Printf ("ThreadMacOSX::%s ( )", __FUNCTION__);
+ lldb::tid_t tid = GetID ();
+ if (ThreadIDIsValid(tid))
+ {
+ Error err(::thread_suspend (tid), eErrorTypeMachKernel);
+ if (err.Success())
+ m_suspend_count++;
+ if (log || err.Fail())
+ err.PutToLog(log, "::thread_suspend (%4.4x)", tid);
+ }
+ return GetSuspendCount();
+}
+
+int32_t
+ThreadMacOSX::Resume()
+{
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_THREAD);
+ if (log && log->GetMask().IsSet(PD_LOG_VERBOSE))
+ log->Printf ("ThreadMacOSX::%s ()", __FUNCTION__);
+ lldb::tid_t tid = GetID ();
+ if (ThreadIDIsValid(tid))
+ {
+ while (m_suspend_count > 0)
+ {
+ Error err(::thread_resume (tid), eErrorTypeMachKernel);
+ if (err.Success())
+ m_suspend_count--;
+ if (log || err.Fail())
+ err.PutToLog(log, "::thread_resume (%4.4x)", tid);
+ }
+ }
+ return GetSuspendCount();
+}
+
+bool
+ThreadMacOSX::RestoreSuspendCount()
+{
+ Log *log = ProcessMacOSXLog::GetLogIfAllCategoriesSet(PD_LOG_THREAD);
+ if (log && log->GetMask().IsSet(PD_LOG_VERBOSE))
+ log->Printf ("ThreadMacOSX::%s ( )", __FUNCTION__);
+ Error err;
+ lldb::tid_t tid = GetID ();
+ if (ThreadIDIsValid(tid) == false)
+ return false;
+ else if (m_suspend_count > m_basic_info.suspend_count)
+ {
+ while (m_suspend_count > m_basic_info.suspend_count)
+ {
+ err = ::thread_resume (tid);
+ if (err.Success())
+ --m_suspend_count;
+ if (log || err.Fail())
+ err.PutToLog(log, "::thread_resume (%4.4x)", tid);
+ }
+ }
+ else if (m_suspend_count < m_basic_info.suspend_count)
+ {
+ while (m_suspend_count < m_basic_info.suspend_count)
+ {
+ err = ::thread_suspend (tid);
+ if (err.Success())
+ --m_suspend_count;
+ if (log || err.Fail())
+ err.PutToLog(log, "::thread_suspend (%4.4x)", tid);
+ }
+ }
+ return m_suspend_count == m_basic_info.suspend_count;
+}
+
+
+const char *
+ThreadMacOSX::GetBasicInfoAsString ()
+{
+ if (m_basic_info_string.empty())
+ {
+ StreamString sstr;
+ struct thread_basic_info basicInfo;
+
+ lldb::tid_t tid = GetID ();
+ if (GetBasicInfo(tid, &basicInfo))
+ {
+// char run_state_str[32];
+// size_t run_state_str_size = sizeof(run_state_str);
+// switch (basicInfo.run_state)
+// {
+// case TH_STATE_RUNNING: strncpy(run_state_str, "running", run_state_str_size); break;
+// case TH_STATE_STOPPED: strncpy(run_state_str, "stopped", run_state_str_size); break;
+// case TH_STATE_WAITING: strncpy(run_state_str, "waiting", run_state_str_size); break;
+// case TH_STATE_UNINTERRUPTIBLE: strncpy(run_state_str, "uninterruptible", run_state_str_size); break;
+// case TH_STATE_HALTED: strncpy(run_state_str, "halted", run_state_str_size); break;
+// default: snprintf(run_state_str, run_state_str_size, "%d", basicInfo.run_state); break; // ???
+// }
+ float user = (float)basicInfo.user_time.seconds + (float)basicInfo.user_time.microseconds / 1000000.0f;
+ float system = (float)basicInfo.user_time.seconds + (float)basicInfo.user_time.microseconds / 1000000.0f;
+ sstr.Printf("Thread 0x%4.4x: user=%f system=%f cpu=%d sleep_time=%d",
+ InferiorThreadID(),
+ user,
+ system,
+ basicInfo.cpu_usage,
+ basicInfo.sleep_time);
+ m_basic_info_string.assign (sstr.GetData(), sstr.GetSize());
+ }
+ }
+ if (m_basic_info_string.empty())
+ return NULL;
+ return m_basic_info_string.c_str();
+}
+
+
+//const uint8_t *
+//ThreadMacOSX::SoftwareBreakpointOpcode (size_t break_op_size) const
+//{
+// return m_context->SoftwareBreakpointOpcode(break_op_size);
+//}
+
+
+lldb::tid_t
+ThreadMacOSX::InferiorThreadID() const
+{
+ mach_msg_type_number_t i;
+ mach_port_name_array_t names;
+ mach_port_type_array_t types;
+ mach_msg_type_number_t ncount, tcount;
+ lldb::tid_t inferior_tid = LLDB_INVALID_THREAD_ID;
+ task_t my_task = ::mach_task_self();
+ task_t task = GetMacOSXProcess().Task().GetTaskPort();
+
+ kern_return_t kret = ::mach_port_names (task, &names, &ncount, &types, &tcount);
+ if (kret == KERN_SUCCESS)
+ {
+ lldb::tid_t tid = GetID ();
+
+ for (i = 0; i < ncount; i++)
+ {
+ mach_port_t my_name;
+ mach_msg_type_name_t my_type;
+
+ kret = ::mach_port_extract_right (task, names[i], MACH_MSG_TYPE_COPY_SEND, &my_name, &my_type);
+ if (kret == KERN_SUCCESS)
+ {
+ ::mach_port_deallocate (my_task, my_name);
+ if (my_name == tid)
+ {
+ inferior_tid = names[i];
+ break;
+ }
+ }
+ }
+ // Free up the names and types
+ ::vm_deallocate (my_task, (vm_address_t) names, ncount * sizeof (mach_port_name_t));
+ ::vm_deallocate (my_task, (vm_address_t) types, tcount * sizeof (mach_port_type_t));
+ }
+ return inferior_tid;
+}
+
+bool
+ThreadMacOSX::GetBasicInfo(lldb::tid_t thread, struct thread_basic_info *basicInfoPtr)
+{
+ if (ThreadIDIsValid(thread))
+ {
+ unsigned int info_count = THREAD_BASIC_INFO_COUNT;
+ kern_return_t err = ::thread_info (thread, THREAD_BASIC_INFO, (thread_info_t) basicInfoPtr, &info_count);
+ if (err == KERN_SUCCESS)
+ return true;
+ }
+ ::memset (basicInfoPtr, 0, sizeof (struct thread_basic_info));
+ return false;
+}
+
+
+bool
+ThreadMacOSX::ThreadIDIsValid (lldb::tid_t thread)
+{
+ return thread != 0;
+}
+
+void
+ThreadMacOSX::Dump(Log *log, uint32_t index)
+{
+ const char * thread_run_state = NULL;
+
+ switch (m_basic_info.run_state)
+ {
+ case TH_STATE_RUNNING: thread_run_state = "running"; break; // 1 thread is running normally
+ case TH_STATE_STOPPED: thread_run_state = "stopped"; break; // 2 thread is stopped
+ case TH_STATE_WAITING: thread_run_state = "waiting"; break; // 3 thread is waiting normally
+ case TH_STATE_UNINTERRUPTIBLE: thread_run_state = "uninter"; break; // 4 thread is in an uninterruptible wait
+ case TH_STATE_HALTED: thread_run_state = "halted "; break; // 5 thread is halted at a
+ default: thread_run_state = "???"; break;
+ }
+
+ RegisterContext *reg_context = GetRegisterContext();
+ log->Printf ("thread[%u] %4.4x (%u): pc: 0x%8.8llx sp: 0x%8.8llx breakID: %d user: %d.%06.6d system: %d.%06.6d cpu: %d policy: %d run_state: %d (%s) flags: %d suspend_count: %d (current %d) sleep_time: %d",
+ index,
+ GetID (),
+ reg_context->GetPC (LLDB_INVALID_ADDRESS),
+ reg_context->GetSP (LLDB_INVALID_ADDRESS),
+ m_basic_info.user_time.seconds, m_basic_info.user_time.microseconds,
+ m_basic_info.system_time.seconds, m_basic_info.system_time.microseconds,
+ m_basic_info.cpu_usage,
+ m_basic_info.policy,
+ m_basic_info.run_state,
+ thread_run_state,
+ m_basic_info.flags,
+ m_basic_info.suspend_count, m_suspend_count,
+ m_basic_info.sleep_time);
+ //DumpRegisterState(0);
+}
+
+void
+ThreadMacOSX::ThreadWillResume (StateType resume_state)
+{
+ // Update the thread state to be the state we wanted when the task resumes
+ SetState (resume_state);
+ switch (resume_state)
+ {
+ case eStateSuspended:
+ Suspend();
+ break;
+
+ case eStateRunning:
+ case eStateStepping:
+ Resume();
+ break;
+ }
+ m_context->ThreadWillResume();
+}
+
+void
+ThreadMacOSX::DidResume ()
+{
+ // TODO: cache current stack frames for next time in case we can match things up??
+ ClearStackFrames();
+ m_stop_exception.Clear();
+ Thread::DidResume();
+}
+
+bool
+ThreadMacOSX::ShouldStop(bool &step_more)
+{
+// TODO: REmove this after all is working, Process should be managing this
+// for us.
+//
+// // See if this thread is at a breakpoint?
+// lldb::user_id_t breakID = CurrentBreakpoint();
+//
+// if (LLDB_BREAK_ID_IS_VALID(breakID))
+// {
+// // This thread is sitting at a breakpoint, ask the breakpoint
+// // if we should be stopping here.
+// if (Process()->Breakpoints().ShouldStop(ProcessID(), ThreadID(), breakID))
+// return true;
+// else
+// {
+// // The breakpoint said we shouldn't stop, but we may have gotten
+// // a signal or the user may have requested to stop in some other
+// // way. Stop if we have a valid exception (this thread won't if
+// // another thread was the reason this process stopped) and that
+// // exception, is NOT a breakpoint exception (a common case would
+// // be a SIGINT signal).
+// if (GetStopException().IsValid() && !GetStopException().IsBreakpoint())
+// return true;
+// }
+// }
+// else
+// {
+ if (m_context->StepNotComplete())
+ {
+ step_more = true;
+ return false;
+ }
+// // The thread state is used to let us know what the thread was
+// // trying to do. ThreadMacOSX::ThreadWillResume() will set the
+// // thread state to various values depending if the thread was
+// // the current thread and if it was to be single stepped, or
+// // resumed.
+// if (GetState() == eStateRunning)
+// {
+// // If our state is running, then we should continue as we are in
+// // the process of stepping over a breakpoint.
+// return false;
+// }
+// else
+// {
+// // Stop if we have any kind of valid exception for this
+// // thread.
+// if (GetStopException().IsValid())
+// return true;
+// }
+// }
+// return false;
+ return true;
+}
+
+bool
+ThreadMacOSX::NotifyException(MachException::Data& exc)
+{
+ if (m_stop_exception.IsValid())
+ {
+ // We may have more than one exception for a thread, but we need to
+ // only remember the one that we will say is the reason we stopped.
+ // We may have been single stepping and also gotten a signal exception,
+ // so just remember the most pertinent one.
+ if (m_stop_exception.IsBreakpoint())
+ m_stop_exception = exc;
+ }
+ else
+ {
+ m_stop_exception = exc;
+ }
+// bool handled =
+ m_context->NotifyException(exc);
+// if (!handled)
+// {
+// handled = true;
+// lldb::addr_t pc = GetPC();
+// lldb::user_id_t breakID = m_process.Breakpoints().FindIDCyAddress(pc);
+// SetCurrentBreakpoint(breakID);
+// switch (exc.exc_type)
+// {
+// case EXC_BAD_ACCESS:
+// break;
+// case EXC_BAD_INSTRUCTION:
+// break;
+// case EXC_ARITHMETIC:
+// break;
+// case EXC_EMULATION:
+// break;
+// case EXC_SOFTWARE:
+// break;
+// case EXC_BREAKPOINT:
+// break;
+// case EXC_SYSCALL:
+// break;
+// case EXC_MACH_SYSCALL:
+// break;
+// case EXC_RPC_ALERT:
+// break;
+// }
+// }
+// return handled;
+ return true;
+}
+
+RegisterContext *
+ThreadMacOSX::GetRegisterContext ()
+{
+ if (m_reg_context_sp.get() == NULL)
+ m_reg_context_sp.reset (CreateRegisterContextForFrame (NULL));
+ return m_reg_context_sp.get();
+}
+
+RegisterContext *
+ThreadMacOSX::CreateRegisterContextForFrame (StackFrame *frame)
+{
+ return m_context->CreateRegisterContext (frame);
+}
+
+uint32_t
+ThreadMacOSX::SetHardwareBreakpoint (const BreakpointSite *bp)
+{
+ if (bp != NULL)
+ return GetRegisterContext()->SetHardwareBreakpoint(bp->GetLoadAddress(), bp->GetByteSize());
+ return LLDB_INVALID_INDEX32;
+}
+
+uint32_t
+ThreadMacOSX::SetHardwareWatchpoint (const WatchpointLocation *wp)
+{
+ if (wp != NULL)
+ return GetRegisterContext()->SetHardwareWatchpoint(wp->GetLoadAddress(), wp->GetByteSize(), wp->WatchpointRead(), wp->WatchpointWrite());
+ return LLDB_INVALID_INDEX32;
+}
+
+
+bool
+ThreadMacOSX::SaveFrameZeroState (RegisterCheckpoint &checkpoint)
+{
+ lldb::StackFrameSP frame_sp(GetStackFrameAtIndex (0));
+ if (frame_sp)
+ {
+ checkpoint.SetStackID(frame_sp->GetStackID());
+ return frame_sp->GetRegisterContext()->ReadAllRegisterValues (checkpoint.GetData());
+ }
+ return false;
+}
+
+bool
+ThreadMacOSX::RestoreSaveFrameZero (const RegisterCheckpoint &checkpoint)
+{
+ lldb::StackFrameSP frame_sp(GetStackFrameAtIndex (0));
+ if (frame_sp)
+ {
+ bool ret = frame_sp->GetRegisterContext()->WriteAllRegisterValues (checkpoint.GetData());
+
+ // Clear out all stack frames as our world just changed.
+ ClearStackFrames();
+ frame_sp->GetRegisterContext()->Invalidate();
+
+ return ret;
+ }
+ return false;
+}
+
+bool
+ThreadMacOSX::ClearHardwareBreakpoint (const BreakpointSite *bp)
+{
+ if (bp != NULL && bp->IsHardware())
+ return GetRegisterContext()->ClearHardwareBreakpoint(bp->GetHardwareIndex());
+ return false;
+}
+
+bool
+ThreadMacOSX::ClearHardwareWatchpoint (const WatchpointLocation *wp)
+{
+ if (wp != NULL && wp->IsHardware())
+ return GetRegisterContext()->ClearHardwareWatchpoint(wp->GetHardwareIndex());
+ return false;
+}
+
+size_t
+ThreadMacOSX::GetStackFrameData(std::vector<std::pair<lldb::addr_t, lldb::addr_t> >& fp_pc_pairs)
+{
+ lldb::StackFrameSP frame_sp(GetStackFrameAtIndex (0));
+ return m_context->GetStackFrameData(frame_sp.get(), fp_pc_pairs);
+}
+
+
+//void
+//ThreadMacOSX::NotifyBreakpointChanged (const BreakpointSite *bp)
+//{
+// if (bp)
+// {
+// lldb::user_id_t breakID = bp->GetID();
+// if (bp->IsEnabled())
+// {
+// if (bp->Address() == GetPC())
+// {
+// SetCurrentBreakpoint(breakID);
+// }
+// }
+// else
+// {
+// if (CurrentBreakpoint() == breakID)
+// {
+// SetCurrentBreakpoint(LLDB_INVALID_BREAK_ID);
+// }
+// }
+// }
+//}
+//
+
+
diff --git a/lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.h b/lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.h
new file mode 100644
index 00000000000..0039f3639f1
--- /dev/null
+++ b/lldb/source/Plugins/Process/MacOSX-User/source/ThreadMacOSX.h
@@ -0,0 +1,159 @@
+//===-- ThreadMacOSX.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ThreadMacOSX_h_
+#define liblldb_ThreadMacOSX_h_
+
+#include <libproc.h>
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+#include "MachException.h"
+
+class ProcessMacOSX;
+class MachThreadContext;
+
+class ThreadMacOSX : public lldb_private::Thread
+{
+public:
+ ThreadMacOSX (ProcessMacOSX &process, lldb::tid_t tid);
+
+ virtual
+ ~ThreadMacOSX ();
+
+ virtual bool
+ WillResume (lldb::StateType resume_state);
+
+ virtual void
+ RefreshStateAfterStop();
+
+ virtual const char *
+ GetInfo ();
+
+ virtual const char *
+ GetName ();
+
+ virtual lldb_private::RegisterContext *
+ GetRegisterContext ();
+
+ virtual lldb_private::RegisterContext *
+ CreateRegisterContextForFrame (lldb_private::StackFrame *frame);
+
+ virtual bool
+ SaveFrameZeroState (RegisterCheckpoint &checkpoint);
+
+ virtual bool
+ RestoreSaveFrameZero (const RegisterCheckpoint &checkpoint);
+
+ virtual uint32_t
+ GetStackFrameCount();
+
+ virtual lldb::StackFrameSP
+ GetStackFrameAtIndex (uint32_t idx);
+
+ virtual void
+ ClearStackFrames ();
+
+ ProcessMacOSX &
+ GetMacOSXProcess ()
+ {
+ return (ProcessMacOSX &)m_process;
+ }
+
+ const ProcessMacOSX &
+ GetMacOSXProcess () const
+ {
+ return (ProcessMacOSX &)m_process;
+ }
+
+ void
+ Dump (lldb_private::Log *log, uint32_t index);
+
+ lldb::tid_t
+ InferiorThreadID () const;
+
+ static bool
+ ThreadIDIsValid (lldb::tid_t thread);
+
+ int32_t
+ Resume ();
+
+ int32_t
+ Suspend ();
+
+ int32_t
+ GetSuspendCount () const { return m_suspend_count; }
+
+ bool
+ RestoreSuspendCount ();
+
+ uint32_t
+ SetHardwareBreakpoint (const lldb_private::BreakpointSite *bp);
+
+ uint32_t
+ SetHardwareWatchpoint (const lldb_private::WatchpointLocation *wp);
+
+ bool
+ ClearHardwareBreakpoint (const lldb_private::BreakpointSite *bp);
+
+ bool
+ ClearHardwareWatchpoint (const lldb_private::WatchpointLocation *wp);
+
+ void
+ ThreadWillResume (lldb::StateType resume_state);
+
+ virtual void
+ DidResume ();
+
+ bool
+ ShouldStop (bool &step_more);
+
+ bool
+ NotifyException (MachException::Data& exc);
+
+ const MachException::Data&
+ GetStopException () { return m_stop_exception; }
+
+ const char *
+ GetBasicInfoAsString ();
+
+ size_t
+ GetStackFrameData (std::vector<std::pair<lldb::addr_t, lldb::addr_t> >& fp_pc_pairs);
+
+ virtual bool
+ GetRawStopReason (lldb_private::Thread::StopInfo *stop_info);
+
+protected:
+ bool
+ GetIdentifierInfo ();
+
+ const char *
+ GetDispatchQueueName();
+
+ static bool
+ GetBasicInfo (lldb::tid_t threadID, struct thread_basic_info *basic_info);
+
+ //------------------------------------------------------------------
+ // Member variables.
+ //------------------------------------------------------------------
+ std::vector<std::pair<lldb::addr_t, lldb::addr_t> > m_fp_pc_pairs;
+ struct thread_basic_info m_basic_info; // Basic information for a thread used to see if a thread is valid
+ std::string m_basic_info_string;// Basic thread info as a C string.
+#ifdef THREAD_IDENTIFIER_INFO_COUNT
+ thread_identifier_info_data_t m_ident_info;
+ struct proc_threadinfo m_proc_threadinfo;
+ std::string m_dispatch_queue_name;
+#endif
+ int32_t m_suspend_count; // The current suspend count
+ MachException::Data m_stop_exception; // The best exception that describes why this thread is stopped
+ std::auto_ptr<MachThreadContext> m_context; // The arch specific thread context for this thread (register state and more)
+
+};
+
+#endif // liblldb_ThreadMacOSX_h_
diff --git a/lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.cpp b/lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.cpp
new file mode 100644
index 00000000000..bf6b6c2eac4
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.cpp
@@ -0,0 +1,327 @@
+//===-- LibUnwindRegisterContext.cpp ----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "LibUnwindRegisterContext.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Target/Thread.h"
+// Project includes
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// LibUnwindRegisterContext constructor
+//----------------------------------------------------------------------
+LibUnwindRegisterContext::LibUnwindRegisterContext
+(
+ Thread &thread,
+ StackFrame *frame,
+ const lldb_private::unw_cursor_t& unwind_cursor
+) :
+ RegisterContext (thread, frame),
+ m_unwind_cursor (unwind_cursor),
+ m_unwind_cursor_is_valid (true)
+{
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+LibUnwindRegisterContext::~LibUnwindRegisterContext()
+{
+}
+
+void
+LibUnwindRegisterContext::Invalidate ()
+{
+ m_unwind_cursor_is_valid = false;
+}
+
+size_t
+LibUnwindRegisterContext::GetRegisterCount ()
+{
+ return m_thread.GetRegisterContext()->GetRegisterCount();
+}
+
+const lldb::RegisterInfo *
+LibUnwindRegisterContext::GetRegisterInfoAtIndex (uint32_t reg)
+{
+ return m_thread.GetRegisterContext()->GetRegisterInfoAtIndex(reg);
+}
+
+size_t
+LibUnwindRegisterContext::GetRegisterSetCount ()
+{
+ return m_thread.GetRegisterContext()->GetRegisterSetCount();
+}
+
+
+
+const lldb::RegisterSet *
+LibUnwindRegisterContext::GetRegisterSet (uint32_t reg_set)
+{
+ return m_thread.GetRegisterContext()->GetRegisterSet (reg_set);
+}
+
+
+
+bool
+LibUnwindRegisterContext::ReadRegisterValue (uint32_t reg, Scalar &value)
+{
+ if (m_unwind_cursor_is_valid == false)
+ return false;
+
+ // Read the register
+ unw_word_t reg_value;
+ if (unw_get_reg (&m_unwind_cursor, reg, &reg_value) != UNW_ESUCCESS)
+ return false;
+
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg);
+ switch (reg_info->encoding)
+ {
+ case eEncodingUint:
+ switch (reg_info->byte_size)
+ {
+ case 1:
+ case 2:
+ case 4:
+ value = (uint32_t)reg_value;
+ return true;
+
+ case 8:
+ value = (uint64_t)reg_value;
+ return true;
+ }
+ break;
+
+ case eEncodingSint:
+ switch (reg_info->byte_size)
+ {
+ case 1:
+ case 2:
+ case 4:
+ value = (int32_t)reg_value;
+ return true;
+
+ case 8:
+ value = (int64_t)reg_value;
+ return true;
+ }
+ break;
+
+ case eEncodingIEEE754:
+ if (reg_info->byte_size > sizeof(unw_word_t))
+ return false;
+
+ switch (reg_info->byte_size)
+ {
+ case sizeof (float):
+ if (sizeof (float) == sizeof(uint32_t))
+ {
+ value = (uint32_t)reg_value;
+ return true;
+ }
+ else if (sizeof (float) == sizeof(uint64_t))
+ {
+ value = (uint64_t)reg_value;
+ return true;
+ }
+ break;
+
+ case sizeof (double):
+ if (sizeof (double) == sizeof(uint32_t))
+ {
+ value = (uint32_t)reg_value;
+ return true;
+ }
+ else if (sizeof (double) == sizeof(uint64_t))
+ {
+ value = (uint64_t)reg_value;
+ return true;
+ }
+ break;
+
+ case sizeof (long double):
+ if (sizeof (long double) == sizeof(uint32_t))
+ {
+ value = (uint32_t)reg_value;
+ return true;
+ }
+ else if (sizeof (long double) == sizeof(uint64_t))
+ {
+ value = (uint64_t)reg_value;
+ return true;
+ }
+ break;
+ }
+ break;
+ }
+ return false;
+}
+
+
+bool
+LibUnwindRegisterContext::ReadRegisterBytes (uint32_t reg, DataExtractor &data)
+{
+ Scalar reg_value;
+
+ if (ReadRegisterValue (reg, reg_value))
+ {
+ if (reg_value.GetData(data))
+ {
+ // "reg_value" is local and now "data" points to the data within
+ // "reg_value", so we must make a copy that will live within "data"
+ DataBufferSP data_sp (new DataBufferHeap (data.GetDataStart(), data.GetByteSize()));
+ data.SetData (data_sp, 0, data.GetByteSize());
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool
+LibUnwindRegisterContext::WriteRegisterValue (uint32_t reg, const Scalar &value)
+{
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg);
+ if (reg_info == NULL)
+ return false;
+ unw_word_t reg_value;
+ switch (value.GetType())
+ {
+ case Scalar::e_sint: reg_value = value.SInt(); break;
+ case Scalar::e_uint: reg_value = value.UInt(); break;
+ case Scalar::e_slong: reg_value = value.SLong(); break;
+ case Scalar::e_ulong: reg_value = value.ULong(); break;
+ case Scalar::e_slonglong: reg_value = value.SLongLong(); break;
+ case Scalar::e_ulonglong: reg_value = value.ULongLong(); break;
+ case Scalar::e_float:
+ if (sizeof (float) == sizeof (unsigned int))
+ reg_value = value.UInt();
+ else if (sizeof (float) == sizeof (unsigned long))
+ reg_value = value.ULong();
+ else if (sizeof (float) == sizeof (unsigned long long))
+ reg_value = value.ULongLong();
+ else
+ return false;
+ break;
+
+ case Scalar::e_double:
+ if (sizeof (double) == sizeof (unsigned int))
+ reg_value = value.UInt();
+ else if (sizeof (double) == sizeof (unsigned long))
+ reg_value = value.ULong();
+ else if (sizeof (double) == sizeof (unsigned long long))
+ reg_value = value.ULongLong();
+ else
+ return false;
+ break;
+
+ case Scalar::e_long_double:
+ if (sizeof (long double) == sizeof (unsigned int))
+ reg_value = value.UInt();
+ else if (sizeof (long double) == sizeof (unsigned long))
+ reg_value = value.ULong();
+ else if (sizeof (long double) == sizeof (unsigned long long))
+ reg_value = value.ULongLong();
+ else
+ return false;
+ break;
+ }
+
+ return unw_set_reg (&m_unwind_cursor, reg, reg_value) == UNW_ESUCCESS;
+}
+
+
+bool
+LibUnwindRegisterContext::WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset)
+{
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg);
+
+ if (reg_info == NULL)
+ return false;
+ if (reg_info->byte_size > sizeof (unw_word_t))
+ return false;
+
+ Scalar value;
+ uint32_t offset = data_offset;
+
+ switch (reg_info->encoding)
+ {
+ case eEncodingUint:
+ if (reg_info->byte_size <= 4)
+ value = data.GetMaxU32 (&offset, reg_info->byte_size);
+ else if (reg_info->byte_size <= 8)
+ value = data.GetMaxU64 (&offset, reg_info->byte_size);
+ else
+ return false;
+ break;
+
+ case eEncodingSint:
+ if (reg_info->byte_size <= 4)
+ value = (int32_t)data.GetMaxU32 (&offset, reg_info->byte_size);
+ else if (reg_info->byte_size <= 8)
+ value = data.GetMaxS64 (&offset, reg_info->byte_size);
+ else
+ return false;
+ break;
+
+ case eEncodingIEEE754:
+ switch (reg_info->byte_size)
+ {
+ case sizeof (float):
+ value = data.GetFloat (&offset);
+ break;
+
+ case sizeof (double):
+ value = data.GetDouble (&offset);
+ break;
+
+ case sizeof (long double):
+ value = data.GetLongDouble (&offset);
+ break;
+ default:
+ return false;
+ }
+ }
+ return WriteRegisterValue (reg, value);
+}
+
+
+bool
+LibUnwindRegisterContext::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
+{
+ // libunwind frames can't handle this it doesn't always have all register
+ // values. This call should only be called on frame zero anyway so there
+ // shouldn't be any problem
+ return false;
+}
+
+bool
+LibUnwindRegisterContext::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp)
+{
+ // Since this class doesn't respond to "ReadAllRegisterValues()", it must
+ // not have been the one that saved all the register values. So we just let
+ // the thread's register context (the register context for frame zero) do
+ // the writing.
+ return m_thread.GetRegisterContext()->WriteAllRegisterValues(data_sp);
+}
+
+
+uint32_t
+LibUnwindRegisterContext::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num)
+{
+ return m_thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber (kind, num);
+}
+
diff --git a/lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.h b/lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.h
new file mode 100644
index 00000000000..4e89b27961f
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/LibUnwindRegisterContext.h
@@ -0,0 +1,83 @@
+//===-- LibUnwindRegisterContext.h ------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_LibUnwindRegisterContext_h_
+#define lldb_LibUnwindRegisterContext_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/lldb-private.h"
+#include "lldb/Target/RegisterContext.h"
+
+#include "libunwind.h"
+
+class LibUnwindRegisterContext : public lldb_private::RegisterContext
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ LibUnwindRegisterContext (lldb_private::Thread &thread,
+ lldb_private::StackFrame *frame,
+ const lldb_private::unw_cursor_t &unwind_cursor);
+
+ virtual
+ ~LibUnwindRegisterContext ();
+
+ //------------------------------------------------------------------
+ // Subclasses must override these functions
+ //------------------------------------------------------------------
+ virtual void
+ Invalidate ();
+
+ virtual size_t
+ GetRegisterCount ();
+
+ virtual const lldb::RegisterInfo *
+ GetRegisterInfoAtIndex (uint32_t reg);
+
+ virtual size_t
+ GetRegisterSetCount ();
+
+ virtual const lldb::RegisterSet *
+ GetRegisterSet (uint32_t reg_set);
+
+ virtual bool
+ ReadRegisterValue (uint32_t reg, lldb_private::Scalar &value);
+
+ virtual bool
+ ReadRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data);
+
+ virtual bool
+ ReadAllRegisterValues (lldb::DataBufferSP &data_sp);
+
+ virtual bool
+ WriteRegisterValue (uint32_t reg, const lldb_private::Scalar &value);
+
+ virtual bool
+ WriteRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data, uint32_t data_offset);
+
+ virtual bool
+ WriteAllRegisterValues (const lldb::DataBufferSP &data_sp);
+
+ virtual uint32_t
+ ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num);
+
+private:
+ lldb_private::unw_cursor_t m_unwind_cursor;
+ bool m_unwind_cursor_is_valid;
+ //------------------------------------------------------------------
+ // For LibUnwindRegisterContext only
+ //------------------------------------------------------------------
+ DISALLOW_COPY_AND_ASSIGN (LibUnwindRegisterContext);
+};
+
+#endif // lldb_LibUnwindRegisterContext_h_
diff --git a/lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.cpp b/lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.cpp
new file mode 100644
index 00000000000..58a7ac043c5
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.cpp
@@ -0,0 +1,306 @@
+//===-- MacOSXLibunwindCallbacks.cpp ----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_MacOSXLibunwindCallbacks_cpp_
+#define liblldb_MacOSXLibunwindCallbacks_cpp_
+#if defined(__cplusplus)
+
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/FileSpec.h"
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+
+#include "lldb-enumerations.h"
+#include "libunwind.h"
+#include "llvm-c/EnhancedDisassembly.h"
+
+using namespace lldb;
+
+namespace lldb_private {
+
+/* Don't implement (libunwind does not use)
+ find_proc_info
+ put_unwind_info
+ get_dyn_info_list_addr
+ access_mem
+ resume
+*/
+/*
+ Should implement (not needed yet)
+ access_fpreg
+ access_vecreg
+ proc_is_sigtramp
+ proc_is_inferior_function_call
+ access_reg_inf_func_call
+*/
+
+static int
+access_reg (lldb_private::unw_addr_space_t as, lldb_private::unw_regnum_t regnum, lldb_private::unw_word_t *valp, int write, void *arg)
+{
+ if (arg == 0)
+ return -1;
+ Thread *th = (Thread *) arg;
+ /* FIXME Only support reading for now. */
+ if (write == 1)
+ return -1;
+ if (th->GetRegisterContext()->GetRegisterInfoAtIndex(regnum) == NULL)
+ return -1;
+ DataExtractor de;
+ if (!th->GetRegisterContext()->ReadRegisterBytes (regnum, de))
+ return -1;
+ memcpy (valp, de.GetDataStart(), de.GetByteSize());
+ return UNW_ESUCCESS;
+}
+
+static int
+get_proc_name (lldb_private::unw_addr_space_t as, lldb_private::unw_word_t ip, char *bufp, size_t buf_len, lldb_private::unw_word_t *offp, void *arg)
+{
+ if (arg == 0)
+ return -1;
+ Thread *th = (Thread *) arg;
+ Address addr;
+ if (!th->GetProcess().ResolveLoadAddress(ip, addr))
+ return -1;
+
+ SymbolContext sc;
+ if (!th->GetProcess().GetTarget().GetImages().ResolveSymbolContextForAddress (addr, eSymbolContextFunction, sc))
+ return -1;
+ if (!sc.symbol)
+ return -1;
+ strlcpy (bufp, sc.symbol->GetMangled().GetMangledName().AsCString(""), buf_len);
+ if (offp)
+ *offp = addr.GetLoadAddress(&th->GetProcess()) - sc.symbol->GetValue().GetLoadAddress(&th->GetProcess());
+ return UNW_ESUCCESS;
+}
+
+static int
+find_image_info (lldb_private::unw_addr_space_t as, lldb_private::unw_word_t load_addr, lldb_private::unw_word_t *mh,
+ lldb_private::unw_word_t *text_start, lldb_private::unw_word_t *text_end,
+ lldb_private::unw_word_t *eh_frame, lldb_private::unw_word_t *eh_frame_len,
+ lldb_private::unw_word_t *compact_unwind_start, lldb_private::unw_word_t *compact_unwind_len, void *arg)
+{
+ if (arg == 0)
+ return -1;
+ Thread *th = (Thread *) arg;
+ Address addr;
+ if (!th->GetProcess().ResolveLoadAddress(load_addr, addr))
+ return -1;
+
+ SymbolContext sc;
+ if (!th->GetProcess().GetTarget().GetImages().ResolveSymbolContextForAddress (addr, eSymbolContextModule, sc))
+ return -1;
+
+ SectionList *sl = sc.module_sp->GetObjectFile()->GetSectionList();
+ static ConstString g_segment_name_TEXT("__TEXT");
+ SectionSP text_segment_sp(sl->FindSectionByName(g_segment_name_TEXT));
+ if (!text_segment_sp)
+ return -1;
+
+ *mh = text_segment_sp->GetLoadBaseAddress (&th->GetProcess());
+ *text_start = text_segment_sp->GetLoadBaseAddress (&th->GetProcess());
+ *text_end = *text_start + text_segment_sp->GetByteSize();
+
+ static ConstString g_section_name_eh_frame ("__eh_frame");
+ SectionSP eh_frame_section_sp = text_segment_sp->GetChildren().FindSectionByName(g_section_name_eh_frame);
+ if (eh_frame_section_sp.get()) {
+ *eh_frame = eh_frame_section_sp->GetLoadBaseAddress (&th->GetProcess());
+ *eh_frame_len = eh_frame_section_sp->GetByteSize();
+ } else {
+ *eh_frame = 0;
+ *eh_frame_len = 0;
+ }
+
+ static ConstString g_section_name_unwind_info ("__unwind_info");
+ SectionSP unwind_info_section_sp = text_segment_sp->GetChildren().FindSectionByName(g_section_name_unwind_info);
+ if (unwind_info_section_sp.get()) {
+ *compact_unwind_start = unwind_info_section_sp->GetLoadBaseAddress (&th->GetProcess());
+ *compact_unwind_len = unwind_info_section_sp->GetByteSize();
+ } else {
+ *compact_unwind_start = 0;
+ *compact_unwind_len = 0;
+ }
+ return UNW_ESUCCESS;
+}
+
+static int
+get_proc_bounds (lldb_private::unw_addr_space_t as, lldb_private::unw_word_t ip, lldb_private::unw_word_t *low, lldb_private::unw_word_t *high, void *arg)
+{
+ if (arg == 0)
+ return -1;
+ Thread *th = (Thread *) arg;
+ Address addr;
+ if (!th->GetProcess().ResolveLoadAddress(ip, addr))
+ return -1;
+ SymbolContext sc;
+ if (!th->GetProcess().GetTarget().GetImages().ResolveSymbolContextForAddress (addr, eSymbolContextFunction | eSymbolContextSymbol, sc))
+ return -1;
+ if (sc.function)
+ {
+ lldb::addr_t start, len;
+ start = sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress(&th->GetProcess());
+ len = sc.function->GetAddressRange().GetByteSize();
+ if (start == LLDB_INVALID_ADDRESS || len == LLDB_INVALID_ADDRESS)
+ return -1;
+ *low = start;
+ *high = start + len;
+ return UNW_ESUCCESS;
+ }
+ if (sc.symbol)
+ {
+ lldb::addr_t start, len;
+ start = sc.symbol->GetAddressRangeRef().GetBaseAddress().GetLoadAddress(&th->GetProcess());
+ len = sc.symbol->GetAddressRangeRef().GetByteSize();
+ if (start == LLDB_INVALID_ADDRESS)
+ return -1;
+ *low = start;
+ if (len != LLDB_INVALID_ADDRESS)
+ *high = start + len;
+ else
+ *high = 0;
+ return UNW_ESUCCESS;
+ }
+ return -1;
+}
+
+static int
+access_raw (lldb_private::unw_addr_space_t as, lldb_private::unw_word_t addr, lldb_private::unw_word_t extent, uint8_t *valp, int write, void *arg)
+{
+ if (arg == 0)
+ return -1;
+ Thread *th = (Thread *) arg;
+ /* FIXME Only support reading for now. */
+ if (write == 1)
+ return -1;
+
+ Error error;
+ if (th->GetProcess().ReadMemory (addr, valp, extent, error) != extent)
+ return -1;
+ return UNW_ESUCCESS;
+}
+
+
+static int
+reg_info (lldb_private::unw_addr_space_t as, lldb_private::unw_regnum_t regnum, lldb_private::unw_regtype_t *type, char *buf, size_t buflen, void *arg)
+{
+ if (arg == 0)
+ return -1;
+ Thread *th = (Thread *) arg;
+ RegisterContext *regc = th->GetRegisterContext();
+ if (regnum > regc->GetRegisterCount())
+ {
+ *type = UNW_NOT_A_REG;
+ return UNW_ESUCCESS;
+ }
+
+ const char *name = regc->GetRegisterName (regnum);
+ if (name == NULL)
+ {
+ *type = UNW_NOT_A_REG;
+ return UNW_ESUCCESS;
+ }
+ strlcpy (buf, name, buflen);
+
+ const lldb::RegisterInfo *reginfo = regc->GetRegisterInfoAtIndex (regnum);
+ if (reginfo == NULL || reginfo->encoding == eEncodingInvalid)
+ {
+ *type = UNW_NOT_A_REG;
+ return UNW_ESUCCESS;
+ }
+ if (reginfo->encoding == eEncodingUint || reginfo->encoding == eEncodingSint)
+ *type = UNW_INTEGER_REG;
+ if (reginfo->encoding == eEncodingIEEE754)
+ *type = UNW_FLOATING_POINT_REG;
+ if (reginfo->encoding == eEncodingVector)
+ *type = UNW_VECTOR_REG;
+
+ return UNW_ESUCCESS;
+}
+
+
+static int
+read_byte_for_edis (uint8_t *buf, uint64_t addr, void *arg)
+{
+ if (arg == 0)
+ return -1;
+ Thread *th = (Thread *) arg;
+ DataBufferHeap onebyte(1, 0);
+ Error error;
+ if (th->GetProcess().ReadMemory (addr, onebyte.GetBytes(), onebyte.GetByteSize(), error) != 1)
+ return -1;
+ *buf = onebyte.GetBytes()[0];
+ return UNW_ESUCCESS;
+}
+
+static int
+instruction_length (lldb_private::unw_addr_space_t as, lldb_private::unw_word_t addr, int *length, void *arg)
+{
+ EDDisassemblerRef disasm;
+ EDInstRef cur_insn;
+
+ if (arg == 0)
+ return -1;
+ Thread *th = (Thread *) arg;
+ const ArchSpec target_arch (th->GetProcess().GetTarget().GetArchitecture ());
+
+ if (target_arch.GetCPUType() == CPU_TYPE_I386)
+ {
+ if (EDGetDisassembler (&disasm, "i386-apple-darwin", kEDAssemblySyntaxX86ATT) != 0)
+ return -1;
+ }
+ else if (target_arch.GetCPUType() == CPU_TYPE_X86_64)
+ {
+ if (EDGetDisassembler (&disasm, "x86_64-apple-darwin", kEDAssemblySyntaxX86ATT) != 0)
+ return -1;
+ }
+ else
+ {
+ return -1;
+ }
+
+ if (EDCreateInsts (&cur_insn, 1, disasm, read_byte_for_edis, addr, arg) != 1)
+ return -1;
+ *length = EDInstByteSize (cur_insn);
+ EDReleaseInst (cur_insn);
+ return UNW_ESUCCESS;
+}
+
+lldb_private::unw_accessors_t
+get_macosx_libunwind_callbacks () {
+ lldb_private::unw_accessors_t ap;
+ bzero (&ap, sizeof (lldb_private::unw_accessors_t));
+ ap.find_proc_info = NULL;
+ ap.put_unwind_info = NULL;
+ ap.get_dyn_info_list_addr = NULL;
+ ap.find_image_info = find_image_info;
+ ap.access_mem = NULL;
+ ap.access_reg = access_reg;
+ ap.access_fpreg = NULL;
+ ap.access_vecreg = NULL;
+ ap.resume = NULL;
+ ap.get_proc_name = get_proc_name;
+ ap.get_proc_bounds = get_proc_bounds;
+ ap.access_raw = access_raw;
+ ap.reg_info = reg_info;
+ ap.proc_is_sigtramp = NULL;
+ ap.proc_is_inferior_function_call = NULL;
+ ap.access_reg_inf_func_call = NULL;
+ ap.instruction_length = instruction_length;
+ return ap;
+}
+
+
+} // namespace lldb_private
+
+#endif // #if defined(__cplusplus)
+#endif // #ifndef liblldb_MacOSXLibunwindCallbacks_cpp_
diff --git a/lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.h b/lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.h
new file mode 100644
index 00000000000..78bd27b2ad3
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/MacOSXLibunwindCallbacks.h
@@ -0,0 +1,22 @@
+//===-- MacOSXLibunwindCallbacks.h ------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_MacOSXLibunwindCallbacks_h_
+#define liblldb_MacOSXLibunwindCallbacks_h_
+#if defined(__cplusplus)
+
+namespace lldb_private {
+
+unw_accessors_t get_macosx_libunwind_callbacks ();
+
+} // namespace lldb_utility
+
+#endif // #if defined(__cplusplus)
+#endif // #ifndef liblldb_MacOSXLibunwindCallbacks_h_
+
diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp b/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp
new file mode 100644
index 00000000000..df2f7c07f65
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp
@@ -0,0 +1,255 @@
+//===-- RegisterContextMacOSXFrameBackchain.cpp -----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RegisterContextMacOSXFrameBackchain.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/Scalar.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Target/Thread.h"
+// Project includes
+#include "StringExtractorGDBRemote.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// RegisterContextMacOSXFrameBackchain constructor
+//----------------------------------------------------------------------
+RegisterContextMacOSXFrameBackchain::RegisterContextMacOSXFrameBackchain
+(
+ Thread &thread,
+ StackFrame *frame,
+ const UnwindMacOSXFrameBackchain::Cursor &cursor
+) :
+ RegisterContext (thread, frame),
+ m_cursor (cursor),
+ m_cursor_is_valid (true)
+{
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+RegisterContextMacOSXFrameBackchain::~RegisterContextMacOSXFrameBackchain()
+{
+}
+
+void
+RegisterContextMacOSXFrameBackchain::Invalidate ()
+{
+ m_cursor_is_valid = false;
+}
+
+size_t
+RegisterContextMacOSXFrameBackchain::GetRegisterCount ()
+{
+ return m_thread.GetRegisterContext()->GetRegisterCount();
+}
+
+const lldb::RegisterInfo *
+RegisterContextMacOSXFrameBackchain::GetRegisterInfoAtIndex (uint32_t reg)
+{
+ return m_thread.GetRegisterContext()->GetRegisterInfoAtIndex(reg);
+}
+
+size_t
+RegisterContextMacOSXFrameBackchain::GetRegisterSetCount ()
+{
+ return m_thread.GetRegisterContext()->GetRegisterSetCount();
+}
+
+
+
+const lldb::RegisterSet *
+RegisterContextMacOSXFrameBackchain::GetRegisterSet (uint32_t reg_set)
+{
+ return m_thread.GetRegisterContext()->GetRegisterSet (reg_set);
+}
+
+
+
+bool
+RegisterContextMacOSXFrameBackchain::ReadRegisterValue (uint32_t reg, Scalar &value)
+{
+ if (!m_cursor_is_valid)
+ return false;
+
+ uint64_t reg_value = LLDB_INVALID_ADDRESS;
+
+ const RegisterInfo *reg_info = m_thread.GetRegisterContext()->GetRegisterInfoAtIndex (reg);
+ if (reg_info == NULL)
+ return false;
+
+ switch (reg_info->kinds[eRegisterKindGeneric])
+ {
+ case LLDB_REGNUM_GENERIC_PC:
+ if (m_cursor.pc == LLDB_INVALID_ADDRESS)
+ return false;
+ reg_value = m_cursor.pc;
+ break;
+
+ case LLDB_REGNUM_GENERIC_FP:
+ if (m_cursor.fp == LLDB_INVALID_ADDRESS)
+ return false;
+ reg_value = m_cursor.pc;
+ break;
+
+ default:
+ return false;
+ }
+
+ switch (reg_info->encoding)
+ {
+ case eEncodingUint:
+ switch (reg_info->byte_size)
+ {
+ case 1:
+ case 2:
+ case 4:
+ value = (uint32_t)reg_value;
+ return true;
+
+ case 8:
+ value = (uint64_t)reg_value;
+ return true;
+ }
+ break;
+
+ case eEncodingSint:
+ switch (reg_info->byte_size)
+ {
+ case 1:
+ case 2:
+ case 4:
+ value = (int32_t)reg_value;
+ return true;
+
+ case 8:
+ value = (int64_t)reg_value;
+ return true;
+ }
+ break;
+
+ case eEncodingIEEE754:
+ switch (reg_info->byte_size)
+ {
+ case sizeof (float):
+ if (sizeof (float) == sizeof(uint32_t))
+ {
+ value = (uint32_t)reg_value;
+ return true;
+ }
+ else if (sizeof (float) == sizeof(uint64_t))
+ {
+ value = (uint64_t)reg_value;
+ return true;
+ }
+ break;
+
+ case sizeof (double):
+ if (sizeof (double) == sizeof(uint32_t))
+ {
+ value = (uint32_t)reg_value;
+ return true;
+ }
+ else if (sizeof (double) == sizeof(uint64_t))
+ {
+ value = (uint64_t)reg_value;
+ return true;
+ }
+ break;
+
+ case sizeof (long double):
+ if (sizeof (long double) == sizeof(uint32_t))
+ {
+ value = (uint32_t)reg_value;
+ return true;
+ }
+ else if (sizeof (long double) == sizeof(uint64_t))
+ {
+ value = (uint64_t)reg_value;
+ return true;
+ }
+ break;
+ }
+ break;
+ }
+ return false;
+}
+
+
+bool
+RegisterContextMacOSXFrameBackchain::ReadRegisterBytes (uint32_t reg, DataExtractor &data)
+{
+ Scalar reg_value;
+
+ if (ReadRegisterValue (reg, reg_value))
+ {
+ if (reg_value.GetData(data))
+ {
+ // "reg_value" is local and now "data" points to the data within
+ // "reg_value", so we must make a copy that will live within "data"
+ DataBufferSP data_sp (new DataBufferHeap (data.GetDataStart(), data.GetByteSize()));
+ data.SetData (data_sp, 0, data.GetByteSize());
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool
+RegisterContextMacOSXFrameBackchain::WriteRegisterValue (uint32_t reg, const Scalar &value)
+{
+ // Not supported yet. We could easily add support for this by remembering
+ // the address of each entry (it would need to be part of the cursor)
+ return false;
+}
+
+
+bool
+RegisterContextMacOSXFrameBackchain::WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset)
+{
+ // Not supported yet. We could easily add support for this by remembering
+ // the address of each entry (it would need to be part of the cursor)
+ return false;
+}
+
+
+bool
+RegisterContextMacOSXFrameBackchain::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
+{
+ // libunwind frames can't handle this it doesn't always have all register
+ // values. This call should only be called on frame zero anyway so there
+ // shouldn't be any problem
+ return false;
+}
+
+bool
+RegisterContextMacOSXFrameBackchain::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp)
+{
+ // Since this class doesn't respond to "ReadAllRegisterValues()", it must
+ // not have been the one that saved all the register values. So we just let
+ // the thread's register context (the register context for frame zero) do
+ // the writing.
+ return m_thread.GetRegisterContext()->WriteAllRegisterValues(data_sp);
+}
+
+
+uint32_t
+RegisterContextMacOSXFrameBackchain::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num)
+{
+ return m_thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber (kind, num);
+}
+
diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h b/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h
new file mode 100644
index 00000000000..f4118c2795d
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h
@@ -0,0 +1,83 @@
+//===-- RegisterContextMacOSXFrameBackchain.h -------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_RegisterContextMacOSXFrameBackchain_h_
+#define lldb_RegisterContextMacOSXFrameBackchain_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/lldb-private.h"
+#include "lldb/Target/RegisterContext.h"
+
+#include "UnwindMacOSXFrameBackchain.h"
+
+class RegisterContextMacOSXFrameBackchain : public lldb_private::RegisterContext
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ RegisterContextMacOSXFrameBackchain (lldb_private::Thread &thread,
+ lldb_private::StackFrame *frame,
+ const UnwindMacOSXFrameBackchain::Cursor &cursor);
+
+ virtual
+ ~RegisterContextMacOSXFrameBackchain ();
+
+ //------------------------------------------------------------------
+ // Subclasses must override these functions
+ //------------------------------------------------------------------
+ virtual void
+ Invalidate ();
+
+ virtual size_t
+ GetRegisterCount ();
+
+ virtual const lldb::RegisterInfo *
+ GetRegisterInfoAtIndex (uint32_t reg);
+
+ virtual size_t
+ GetRegisterSetCount ();
+
+ virtual const lldb::RegisterSet *
+ GetRegisterSet (uint32_t reg_set);
+
+ virtual bool
+ ReadRegisterValue (uint32_t reg, lldb_private::Scalar &value);
+
+ virtual bool
+ ReadRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data);
+
+ virtual bool
+ ReadAllRegisterValues (lldb::DataBufferSP &data_sp);
+
+ virtual bool
+ WriteRegisterValue (uint32_t reg, const lldb_private::Scalar &value);
+
+ virtual bool
+ WriteRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data, uint32_t data_offset);
+
+ virtual bool
+ WriteAllRegisterValues (const lldb::DataBufferSP &data_sp);
+
+ virtual uint32_t
+ ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num);
+
+private:
+ UnwindMacOSXFrameBackchain::Cursor m_cursor;
+ bool m_cursor_is_valid;
+ //------------------------------------------------------------------
+ // For RegisterContextMacOSXFrameBackchain only
+ //------------------------------------------------------------------
+ DISALLOW_COPY_AND_ASSIGN (RegisterContextMacOSXFrameBackchain);
+};
+
+#endif // lldb_RegisterContextMacOSXFrameBackchain_h_
diff --git a/lldb/source/Plugins/Process/Utility/UnwindLibUnwind.cpp b/lldb/source/Plugins/Process/Utility/UnwindLibUnwind.cpp
new file mode 100644
index 00000000000..1b6fa58dcd4
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/UnwindLibUnwind.cpp
@@ -0,0 +1,73 @@
+//===-- UnwindLibUnwind.cpp -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Target/Thread.h"
+#include "UnwindLibUnwind.h"
+#include "LibUnwindRegisterContext.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+UnwindLibUnwind::UnwindLibUnwind (Thread &thread, unw_addr_space_t addr_space) :
+ Unwind (thread),
+ m_addr_space (addr_space),
+ m_cursors()
+{
+ m_pc_regnum = thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ m_sp_regnum = thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+}
+
+uint32_t
+UnwindLibUnwind::GetFrameCount()
+{
+ if (m_cursors.empty())
+ {
+ unw_cursor_t cursor;
+ unw_init_remote (&cursor, m_addr_space, &m_thread);
+
+ m_cursors.push_back (cursor);
+
+ while (1)
+ {
+ int stepresult = unw_step (&cursor);
+ if (stepresult > 0)
+ m_cursors.push_back (cursor);
+ else
+ break;
+ }
+ }
+ return m_cursors.size();
+}
+
+bool
+UnwindLibUnwind::GetFrameInfoAtIndex (uint32_t idx, addr_t& cfa, addr_t& pc)
+{
+ const uint32_t frame_count = GetFrameCount();
+ if (idx < frame_count)
+ {
+ int pc_err = unw_get_reg (&m_cursors[idx], m_pc_regnum, &pc);
+ int sp_err = unw_get_reg (&m_cursors[idx], m_sp_regnum, &cfa);
+ return pc_err == UNW_ESUCCESS && sp_err == UNW_ESUCCESS;
+ }
+ return false;
+}
+
+RegisterContext *
+UnwindLibUnwind::CreateRegisterContextForFrame (StackFrame *frame)
+{
+ uint32_t idx = frame->GetID();
+ const uint32_t frame_count = GetFrameCount();
+ if (idx < frame_count)
+ return new LibUnwindRegisterContext (m_thread, frame, m_cursors[idx]);
+ return NULL;
+}
diff --git a/lldb/source/Plugins/Process/Utility/UnwindLibUnwind.h b/lldb/source/Plugins/Process/Utility/UnwindLibUnwind.h
new file mode 100644
index 00000000000..d91f164a2f9
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/UnwindLibUnwind.h
@@ -0,0 +1,66 @@
+//===-- UnwindLibUnwind.h ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_UnwindLibUnwind_h_
+#define lldb_UnwindLibUnwind_h_
+
+// C Includes
+// C++ Includes
+#include <vector>
+
+// Other libraries and framework includes
+#include "libunwind.h"
+
+// Project includes
+#include "lldb/lldb-private.h"
+#include "lldb/Target/Unwind.h"
+
+class UnwindLibUnwind : public lldb_private::Unwind
+{
+public:
+ UnwindLibUnwind (lldb_private::Thread &thread,
+ lldb_private::unw_addr_space_t addr_space);
+
+ virtual
+ ~UnwindLibUnwind()
+ {
+ }
+
+ virtual void
+ Clear()
+ {
+ m_cursors.clear();
+ }
+
+ virtual uint32_t
+ GetFrameCount();
+
+ bool
+ GetFrameInfoAtIndex (uint32_t frame_idx,
+ lldb::addr_t& cfa,
+ lldb::addr_t& pc);
+
+ lldb_private::RegisterContext *
+ CreateRegisterContextForFrame (lldb_private::StackFrame *frame);
+
+ lldb_private::Thread &
+ GetThread();
+
+private:
+ lldb_private::unw_addr_space_t m_addr_space;
+ std::vector<lldb_private::unw_cursor_t> m_cursors;
+ uint32_t m_pc_regnum;
+ uint32_t m_sp_regnum;
+ //------------------------------------------------------------------
+ // For UnwindLibUnwind only
+ //------------------------------------------------------------------
+ DISALLOW_COPY_AND_ASSIGN (UnwindLibUnwind);
+};
+
+#endif // lldb_UnwindLibUnwind_h_
diff --git a/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp b/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp
new file mode 100644
index 00000000000..586e3d78864
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp
@@ -0,0 +1,243 @@
+//===-- UnwindMacOSXFrameBackchain.cpp --------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+
+#include "RegisterContextMacOSXFrameBackchain.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+UnwindMacOSXFrameBackchain::UnwindMacOSXFrameBackchain (Thread &thread) :
+ Unwind (thread),
+ m_cursors()
+{
+}
+
+uint32_t
+UnwindMacOSXFrameBackchain::GetFrameCount()
+{
+ if (m_cursors.empty())
+ {
+ const ArchSpec target_arch (m_thread.GetProcess().GetTarget().GetArchitecture ());
+ // Frame zero should always be supplied by the thread...
+ StackFrameSP frame_sp (m_thread.GetStackFrameAtIndex (0));
+ if (target_arch == ArchSpec("x86_64"))
+ GetStackFrameData_x86_64 (frame_sp.get());
+ else if (target_arch == ArchSpec("i386"))
+ GetStackFrameData_i386 (frame_sp.get());
+
+ }
+ return m_cursors.size();
+}
+
+bool
+UnwindMacOSXFrameBackchain::GetFrameInfoAtIndex (uint32_t idx, addr_t& cfa, addr_t& pc)
+{
+ const uint32_t frame_count = GetFrameCount();
+ if (idx < frame_count)
+ {
+ if (m_cursors[idx].pc == LLDB_INVALID_ADDRESS)
+ return false;
+ if (m_cursors[idx].fp == LLDB_INVALID_ADDRESS)
+ return false;
+
+ pc = m_cursors[idx].pc;
+ cfa = m_cursors[idx].fp;
+
+ return true;
+ }
+ return false;
+}
+
+RegisterContext *
+UnwindMacOSXFrameBackchain::CreateRegisterContextForFrame (StackFrame *frame)
+{
+ uint32_t idx = frame->GetID();
+ const uint32_t frame_count = GetFrameCount();
+ if (idx < frame_count)
+ return new RegisterContextMacOSXFrameBackchain (m_thread, frame, m_cursors[idx]);
+ return NULL;
+}
+
+size_t
+UnwindMacOSXFrameBackchain::GetStackFrameData_i386 (StackFrame *first_frame)
+{
+ m_cursors.clear();
+
+ std::pair<lldb::addr_t, lldb::addr_t> fp_pc_pair;
+
+ typedef struct Frame_i386
+ {
+ uint32_t fp;
+ uint32_t pc;
+ };
+
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext();
+ assert (reg_ctx);
+
+ Cursor cursor;
+ cursor.pc = reg_ctx->GetPC (LLDB_INVALID_ADDRESS);
+ cursor.fp = reg_ctx->GetFP (0);
+
+ Frame_i386 frame = { cursor.fp, cursor.pc };
+
+ m_cursors.push_back(cursor);
+
+ const size_t k_frame_size = sizeof(frame);
+ Error error;
+ while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0))
+ {
+ // Read both the FP and PC (8 bytes)
+ if (m_thread.GetProcess().ReadMemory (frame.fp, &frame.fp, k_frame_size, error) != k_frame_size)
+ break;
+ if (frame.pc >= 0x1000)
+ {
+ cursor.pc = frame.pc;
+ cursor.fp = frame.fp;
+ m_cursors.push_back (cursor);
+ }
+ }
+ if (!m_cursors.empty())
+ {
+ lldb::addr_t first_frame_pc = m_cursors.front().pc;
+ if (first_frame_pc != LLDB_INVALID_ADDRESS)
+ {
+ const uint32_t resolve_scope = eSymbolContextModule |
+ eSymbolContextCompUnit |
+ eSymbolContextFunction |
+ eSymbolContextSymbol;
+
+ SymbolContext first_frame_sc (first_frame->GetSymbolContext(resolve_scope));
+ const AddressRange *addr_range_ptr = NULL;
+ if (first_frame_sc.function)
+ addr_range_ptr = &first_frame_sc.function->GetAddressRange();
+ else if (first_frame_sc.symbol)
+ addr_range_ptr = first_frame_sc.symbol->GetAddressRangePtr();
+
+ if (addr_range_ptr)
+ {
+ if (first_frame->GetPC() == addr_range_ptr->GetBaseAddress())
+ {
+ // We are at the first instruction, so we can recover the
+ // previous PC by dereferencing the SP
+ lldb::addr_t first_frame_sp = reg_ctx->GetSP (0);
+ // Read the real second frame return address into frame.pc
+ if (first_frame_sp && m_thread.GetProcess().ReadMemory (first_frame_sp, &frame.pc, sizeof(frame.pc), error) == sizeof(frame.pc))
+ {
+ cursor.fp = m_cursors.front().fp;
+ cursor.pc = frame.pc; // Set the new second frame PC
+
+ // Insert the second frame
+ m_cursors.insert(m_cursors.begin()+1, cursor);
+
+ m_cursors.front().fp = first_frame_sp;
+ }
+ }
+ }
+ }
+ }
+// uint32_t i=0;
+// printf(" PC FP\n");
+// printf(" ------------------ ------------------ \n");
+// for (i=0; i<m_cursors.size(); ++i)
+// {
+// printf("[%3u] 0x%16.16llx 0x%16.16llx\n", i, m_cursors[i].pc, m_cursors[i].fp);
+// }
+ return m_cursors.size();
+}
+
+
+size_t
+UnwindMacOSXFrameBackchain::GetStackFrameData_x86_64 (StackFrame *first_frame)
+{
+ m_cursors.clear();
+
+ std::pair<lldb::addr_t, lldb::addr_t> fp_pc_pair;
+
+ typedef struct Frame_x86_64
+ {
+ uint64_t fp;
+ uint64_t pc;
+ };
+
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext();
+ assert (reg_ctx);
+
+ Cursor cursor;
+ cursor.pc = reg_ctx->GetPC (LLDB_INVALID_ADDRESS);
+ cursor.fp = reg_ctx->GetFP (0);
+
+ Frame_x86_64 frame = { cursor.fp, cursor.pc };
+
+ m_cursors.push_back(cursor);
+ Error error;
+ const size_t k_frame_size = sizeof(frame);
+ while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0))
+ {
+ // Read both the FP and PC (16 bytes)
+ if (m_thread.GetProcess().ReadMemory (frame.fp, &frame.fp, k_frame_size, error) != k_frame_size)
+ break;
+
+ if (frame.pc >= 0x1000)
+ {
+ cursor.pc = frame.pc;
+ cursor.fp = frame.fp;
+ m_cursors.push_back (cursor);
+ }
+ }
+ if (!m_cursors.empty())
+ {
+ lldb::addr_t first_frame_pc = m_cursors.front().pc;
+ if (first_frame_pc != LLDB_INVALID_ADDRESS)
+ {
+ const uint32_t resolve_scope = eSymbolContextModule |
+ eSymbolContextCompUnit |
+ eSymbolContextFunction |
+ eSymbolContextSymbol;
+
+ SymbolContext first_frame_sc(first_frame->GetSymbolContext(resolve_scope));
+ const AddressRange *addr_range_ptr = NULL;
+ if (first_frame_sc.function)
+ addr_range_ptr = &first_frame_sc.function->GetAddressRange();
+ else if (first_frame_sc.symbol)
+ addr_range_ptr = first_frame_sc.symbol->GetAddressRangePtr();
+
+ if (addr_range_ptr)
+ {
+ if (first_frame->GetPC() == addr_range_ptr->GetBaseAddress())
+ {
+ // We are at the first instruction, so we can recover the
+ // previous PC by dereferencing the SP
+ lldb::addr_t first_frame_sp = reg_ctx->GetSP (0);
+ // Read the real second frame return address into frame.pc
+ if (m_thread.GetProcess().ReadMemory (first_frame_sp, &frame.pc, sizeof(frame.pc), error) == sizeof(frame.pc))
+ {
+ cursor.fp = m_cursors.front().fp;
+ cursor.pc = frame.pc; // Set the new second frame PC
+
+ // Insert the second frame
+ m_cursors.insert(m_cursors.begin()+1, cursor);
+
+ m_cursors.front().fp = first_frame_sp;
+ }
+ }
+ }
+ }
+ }
+ return m_cursors.size();
+}
+
diff --git a/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h b/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h
new file mode 100644
index 00000000000..86ba6e7ae7f
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h
@@ -0,0 +1,77 @@
+//===-- UnwindMacOSXFrameBackchain.h ----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_UnwindMacOSXFrameBackchain_h_
+#define lldb_UnwindMacOSXFrameBackchain_h_
+
+// C Includes
+// C++ Includes
+#include <vector>
+
+// Other libraries and framework includes
+
+// Project includes
+#include "lldb/lldb-private.h"
+#include "lldb/Target/Unwind.h"
+
+class UnwindMacOSXFrameBackchain : public lldb_private::Unwind
+{
+public:
+ UnwindMacOSXFrameBackchain (lldb_private::Thread &thread);
+
+ virtual
+ ~UnwindMacOSXFrameBackchain()
+ {
+ }
+
+ virtual void
+ Clear()
+ {
+ m_cursors.clear();
+ }
+
+ virtual uint32_t
+ GetFrameCount();
+
+ bool
+ GetFrameInfoAtIndex (uint32_t frame_idx,
+ lldb::addr_t& cfa,
+ lldb::addr_t& pc);
+
+ lldb_private::RegisterContext *
+ CreateRegisterContextForFrame (lldb_private::StackFrame *frame);
+
+ lldb_private::Thread &
+ GetThread();
+
+protected:
+ friend class RegisterContextMacOSXFrameBackchain;
+
+ typedef struct Cursor
+ {
+ lldb::addr_t pc; // Program counter
+ lldb::addr_t fp; // Frame pointer for us with backchain
+ };
+
+private:
+ std::vector<Cursor> m_cursors;
+
+ size_t
+ GetStackFrameData_i386 (lldb_private::StackFrame *first_frame);
+
+ size_t
+ GetStackFrameData_x86_64 (lldb_private::StackFrame *first_frame);
+
+ //------------------------------------------------------------------
+ // For UnwindMacOSXFrameBackchain only
+ //------------------------------------------------------------------
+ DISALLOW_COPY_AND_ASSIGN (UnwindMacOSXFrameBackchain);
+};
+
+#endif // lldb_UnwindMacOSXFrameBackchain_h_
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/include/libunwind.h b/lldb/source/Plugins/Process/Utility/libunwind/include/libunwind.h
new file mode 100644
index 00000000000..63cc8ba2366
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/include/libunwind.h
@@ -0,0 +1,509 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- libunwind.h ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// C interface to libuwind
+//
+// Source compatible with Level 1 Base ABI documented at:
+// http://www.codesourcery.com/public/cxx-abi/abi-eh.html
+//
+//===----------------------------------------------------------------------===//
+
+
+#ifndef __LIBUNWIND__
+#define __LIBUNWIND__
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <mach/mach_types.h>
+#include <Availability.h>
+
+namespace lldb_private {
+
+#pragma mark Error codes
+
+enum {
+ UNW_ESUCCESS = 0, /* no error */
+ UNW_EUNSPEC = -6540, /* unspecified (general) error */
+ UNW_ENOMEM = -6541, /* out of memory */
+ UNW_EBADREG = -6542, /* bad register number */
+ UNW_EREADONLYREG = -6543, /* attempt to write read-only register */
+ UNW_ESTOPUNWIND = -6544, /* stop unwinding */
+ UNW_EINVALIDIP = -6545, /* invalid IP */
+ UNW_EBADFRAME = -6546, /* bad frame */
+ UNW_EINVAL = -6547, /* unsupported operation or bad value */
+ UNW_EBADVERSION = -6548, /* unwind info has unsupported version */
+ UNW_ENOINFO = -6549, /* no unwind info found */
+ UNW_EREGUNAVAILABLE = -6550 /* contents of requested reg are not available */
+};
+
+#pragma mark General data structures
+
+struct unw_context_t { uint64_t data[128]; };
+typedef struct unw_context_t unw_context_t;
+
+struct unw_cursor_t { uint64_t data[140]; };
+typedef struct unw_cursor_t unw_cursor_t;
+
+enum unw_as_type { UNW_LOCAL, UNW_REMOTE };
+struct unw_addr_space
+{
+ enum unw_as_type type;
+ uint8_t data[];
+};
+typedef struct unw_addr_space* unw_addr_space_t;
+
+typedef int unw_regnum_t;
+typedef uint64_t unw_word_t;
+typedef double unw_fpreg_t;
+
+enum unw_vecreg_format {
+ UNW_VECREG_SIGNED,
+ UNW_VECREG_UNSIGNED,
+ UNW_VECREG_FLOAT
+};
+
+typedef struct
+{
+ union {
+ double doubles[8];
+ float floats [16];
+
+ uint64_t dwords [8];
+ uint32_t words [16];
+ uint16_t hwords [32];
+ uint8_t bytes [64];
+ } data;
+ uint16_t unit_size; // bits
+ uint16_t num_units;
+ uint8_t format;
+} unw_vecreg_t;
+
+struct unw_proc_info_t
+{
+ unw_word_t start_ip; /* start address of function */
+ unw_word_t end_ip; /* address after end of function */
+ unw_word_t lsda; /* address of language specific data area, or zero if not used */
+ unw_word_t handler; /* personality routine, or zero if not used */
+ unw_word_t gp; /* not used */
+ unw_word_t flags; /* not used */
+ uint32_t format; /* compact unwind encoding, or zero if none */
+ uint32_t unwind_info_size; /* size of dwarf unwind info, or zero if none */
+ unw_word_t unwind_info; /* address of dwarf unwind info, or zero if none */
+ unw_word_t extra; /* mach_header of mach-o image containing function */
+};
+typedef struct unw_proc_info_t unw_proc_info_t;
+
+#pragma mark Local API
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int unw_getcontext(unw_context_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+extern int unw_init_local(unw_cursor_t*, unw_context_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+extern int unw_step(unw_cursor_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+extern int unw_get_reg(unw_cursor_t*, unw_regnum_t, unw_word_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+extern int unw_get_fpreg(unw_cursor_t*, unw_regnum_t, unw_fpreg_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+extern int unw_set_reg(unw_cursor_t*, unw_regnum_t, unw_word_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+extern int unw_set_fpreg(unw_cursor_t*, unw_regnum_t, unw_fpreg_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+extern int unw_resume(unw_cursor_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+
+extern const char* unw_regname(unw_cursor_t*, unw_regnum_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+extern int unw_get_proc_info(unw_cursor_t*, unw_proc_info_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+extern int unw_is_fpreg(unw_cursor_t*, unw_regnum_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+extern int unw_is_signal_frame(unw_cursor_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+extern int unw_get_proc_name(unw_cursor_t*, char*, size_t, unw_word_t*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+//extern int unw_get_save_loc(unw_cursor_t*, int, unw_save_loc_t*);
+
+
+#pragma mark Remote data structures
+
+typedef enum {
+ UNW_NOT_A_REG = 0,
+ UNW_INTEGER_REG,
+ UNW_FLOATING_POINT_REG,
+ UNW_VECTOR_REG,
+ UNW_OTHER_REG
+} unw_regtype_t;
+
+typedef enum {
+ UNW_TARGET_UNSPECIFIED = 0,
+ UNW_TARGET_I386,
+ UNW_TARGET_X86_64,
+ UNW_TARGET_PPC,
+ UNW_TARGET_ARM
+} unw_targettype_t;
+
+typedef enum {
+ UNW_LOG_LEVEL_NONE = 0x00000000,
+ UNW_LOG_LEVEL_INFO = 0x00000001,
+ UNW_LOG_LEVEL_API = 0x00000002,
+ UNW_LOG_LEVEL_VERBOSE = 0x00000004,
+ UNW_LOG_LEVEL_TIMINGS = 0x00000008,
+ UNW_LOG_LEVEL_DEBUG = 0x00000010,
+ UNW_LOG_LEVEL_ALL = 0x0FFFFFFF
+} unw_log_level_t;
+
+typedef enum {
+ UNW_CACHE_NONE = 0,
+ UNW_CACHE_GLOBAL,
+ UNW_CACHE_PER_THREAD
+} unw_caching_policy_t;
+
+typedef struct {
+ int (*find_proc_info)(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pip, int need_unwind_info, void *arg);
+ int (*put_unwind_info)(unw_addr_space_t as, unw_proc_info_t *pip, void *arg);
+ int (*get_dyn_info_list_addr)(unw_addr_space_t as, unw_word_t *dilap, void *arg);
+
+ // Reads or writes a memory object the size of a target pointer.
+ // Byte-swaps if necessary.
+ int (*access_mem)(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, int write, void *arg);
+
+ // Register contents sent as-is (i.e. not byte-swapped).
+ // Register numbers are the driver program's numbering scheme as
+ // determined by the reg_info callbacks; libunwind will interrogate
+ // the driver program to figure out which numbers it uses to refer to
+ // which registers.
+ int (*access_reg)(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp, int write, void *arg);
+ int (*access_fpreg)(unw_addr_space_t as, unw_regnum_t regnum, unw_fpreg_t *valp, int write, void *arg);
+ int (*resume)(unw_addr_space_t as, unw_cursor_t *cp, void *arg);
+ int (*get_proc_name)(unw_addr_space_t as, unw_word_t addr, char *bufp, size_t buf_len, unw_word_t *offp, void *arg);
+
+
+ // Added to find the start of the image (executable, bundle, dylib, etc)
+ // for a given address.
+ // as - The address space to use
+ // ip - The address libunwind wants to know about
+ // mh - The Mach-O header address for this image
+ // text_start - The start of __TEXT segment (all its sections)
+ // text_end - The end address of __TEXT segment (all its sections)
+ // eh_frame - The start of __TEXT,__eh_frame
+ // eh_frame_len - The length of __TEXT,__eh_frame
+ // compact_unwind_start - The start of __TEXT,__unwind_info
+ // compact_unwind_len - The length of __TEXT,__unwind_info
+ // arg - The driver-provided generic argument
+ // All addresses are the in-memory, slid, addresses.
+ // If eh_frame or unwind_info are missing, addr and len is returned as 0.
+ int (*find_image_info)(unw_addr_space_t as, unw_word_t ip, unw_word_t *mh,
+ unw_word_t *text_start, unw_word_t *text_end,
+ unw_word_t *eh_frame, unw_word_t *eh_frame_len,
+ unw_word_t *compact_unwind_start,
+ unw_word_t *compact_unwind_len, void *arg);
+
+ // Added to get the start and end address of a function without needing
+ // all of the information (and potential allocation) that the
+ // find_proc_info() call entails.
+ // as - The address space to use
+ // ip - The address libunwind wants to know about
+ // low - The start address of the function at 'ip'
+ // high - The first address past the function at 'ip'
+ // arg - The driver-provided generic argument
+ // If HIGH is unknown, it should be set to 0. All addresses
+ // are the in-memory, slid, addresses.
+ int (*get_proc_bounds)(unw_addr_space_t as, unw_word_t ip,
+ unw_word_t *low, unw_word_t *high, void *arg);
+
+ // Added to support accessing non-word-size memory objects across
+ // platforms. No byte swapping should be done.
+ // as - The address space to use
+ // addr - The starting address to access
+ // extent - The extent of the region to access, in bytes
+ // valp - The local region to be written from / read into
+ // write - non-zero if the data is to be written into the target
+ // rather than read
+ // arg - The driver-provided generic argument (see unw_init_remote)
+ int (*access_raw)(unw_addr_space_t as, unw_word_t addr, unw_word_t extent,
+ uint8_t *valp, int write, void *arg);
+
+ // Added to support identifying registers.
+ // libunwind will interrogate the driver program via this callback to
+ // identify what register numbers it is using; the register names are
+ // used to correlate that the driver program's register numbers with
+ // libunwind's internal register numbers. The driver program should
+ // use its own register numbers when requesting registers with
+ // unw_get_reg() and libunwind will provide the driver program's
+ // register numbers to the access_reg callback function.
+ // as - The address space to use
+ // regnum - The register number
+ // type - Write the register type to this address
+ // For a non-existent register, return UNW_ESUCCESS but
+ // write UNW_NOT_A_REG to type
+ // buf - If non-NULL, the register name is written to this address
+ // buf_len - The size of the buffer provided for the register name
+ // arg - The driver-provided generic argument (see unw_init_remote)
+ int (*reg_info)(unw_addr_space_t as, unw_regnum_t regnum,
+ unw_regtype_t* type, char *bufp, size_t buf_len, void *arg);
+
+ // Added to read a vector register's value from the remote machine.
+ // as - The address space to use
+ // regnum - The register number
+ // valp - The local region to be written from / read into
+ // write - non-zero if the data is to be written into the target
+ // rather than read
+ // arg - The driver-specified generic argument
+ int (*access_vecreg)(unw_addr_space_t as, unw_regnum_t regnum,
+ unw_vecreg_t* valp, int write, void *arg);
+
+ // Added to identify if an unwind cursor is pointing to _sigtramp().
+ // After a _sigtramp we have an entire register set available and we should
+ // return any of the registers requested.
+ // as - The address space to use
+ // ip - The address of the function libunwind is examining
+ // arg - The driver-provided generic argument
+ // This function returns non-zero if ip is in _sigtramp.
+ int (*proc_is_sigtramp) (unw_addr_space_t as, unw_word_t ip, void *arg);
+
+ // Added to identify if an unwind cursor is pointing to a debugger's
+ // inferior function call dummy frame.
+ // The driver program will need to provide the full register set (via the
+ // standard access_reg callback) for the function that was executing
+ // when the inferior function call was made; it will use these register
+ // values and not try to unwind out of the inferior function call dummy
+ // frame.
+ // After a inf func call we have an entire register set available and
+ // we should return any of the registers requested.
+ // as - The address space to use
+ // ip - The address of the function libunwind is examining
+ // sp - The stack pointer value of the frame
+ // arg - The driver-provided generic argument (see unw_init_remote)
+ // This function returns non-zero if ip/sp is an inferior function call
+ // dummy frame.
+ int (*proc_is_inferior_function_call) (unw_addr_space_t as, unw_word_t ip,
+ unw_word_t sp, void *arg);
+
+ // Added to retrieve a register value from a above a debugger's inferior
+ // function call dummy frame. Similar to _sigtramp but the debugger will
+ // have the register context squirreled away in its own memory (or possibly
+ // saved on the stack somewhere).
+ // May be NULL if the program being unwound will not have a debugger
+ // calling functions mid-execution.
+ // as - The address space to use
+ // ip - The pc value for the dummy frame
+ // sp - The stack pointer for the dummy frame
+ // regnum - The register number in the driver program's register
+ // numbering scheme.
+ // valp - Pointer to a word of memory to be read/written
+ // write - Non-zero if libunwind is writing a new value to the reg,
+ // else it is reading the contents of that register.
+ // arg - The driver-provided generic argument (see unw_init_remote)
+ int (*access_reg_inf_func_call)(unw_addr_space_t as, unw_word_t ip,
+ unw_word_t sp, unw_regnum_t regnum,
+ unw_word_t *valp, int write, void *arg);
+
+ // Added to iterate over unknown assembly instructions when analyzing a
+ // function prologue. Needed for ISAs with variable length instructions
+ // (i386, x86_64) or multiple instruction sizes (arm, thumb).
+ // Returns zero if the instruction length was successfully measured.
+ // as - The address space to use
+ // addr - The address of the instruction being measured
+ // length - Set to the length of the instruction
+ // arg - The driver-provided generic argument (see unw_init_remote)
+ int (*instruction_length)(unw_addr_space_t as, unw_word_t addr,
+ int *length, void *arg);
+
+} unw_accessors_t;
+
+extern int unw_init_remote(unw_cursor_t*, unw_addr_space_t, void*) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+extern unw_accessors_t* unw_get_accessors(unw_addr_space_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+extern unw_addr_space_t unw_create_addr_space(unw_accessors_t*, unw_targettype_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+extern void unw_flush_caches(unw_addr_space_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+extern int unw_set_caching_policy(unw_addr_space_t, unw_caching_policy_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+extern void unw_destroy_addr_space(unw_addr_space_t asp) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+extern void unw_set_logging_level(unw_addr_space_t, FILE *, unw_log_level_t) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+
+// Should be called when remote unwinding if a bundle in the remote process
+// is unloaded
+extern void unw_image_was_unloaded(unw_addr_space_t, unw_word_t mh) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+
+// Try to discern where the function's prologue instructions end
+// start - start address of the function, required
+// end - first address beyond the function, or zero if unknown
+// endofprologue - set to the address after the last prologue instruction if successful
+extern int unw_end_of_prologue_setup(unw_cursor_t*, unw_word_t start, unw_word_t end, unw_word_t *endofprologue) __OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA);
+
+/*
+ * Dynamic unwinding API
+ * NOT IMPLEMENTED on Mac OS X
+ * extern void _U_dyn_register(unw_dyn_info_t*);
+ * extern void _U_dyn_cancel(unw_dyn_info_t*);
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#pragma mark Register numbers
+
+// architecture independent register numbers
+enum {
+ UNW_REG_IP = -1, // instruction pointer
+ UNW_REG_SP = -2, // stack pointer
+};
+
+
+// 32-bit x86 registers
+enum {
+ UNW_X86_EAX = 0,
+ UNW_X86_ECX = 1,
+ UNW_X86_EDX = 2,
+ UNW_X86_EBX = 3,
+ UNW_X86_EBP = 4,
+ UNW_X86_ESP = 5,
+ UNW_X86_ESI = 6,
+ UNW_X86_EDI = 7
+};
+
+
+// 64-bit x86_64 registers
+enum {
+ UNW_X86_64_RAX = 0,
+ UNW_X86_64_RDX = 1,
+ UNW_X86_64_RCX = 2,
+ UNW_X86_64_RBX = 3,
+ UNW_X86_64_RSI = 4,
+ UNW_X86_64_RDI = 5,
+ UNW_X86_64_RBP = 6,
+ UNW_X86_64_RSP = 7,
+ UNW_X86_64_R8 = 8,
+ UNW_X86_64_R9 = 9,
+ UNW_X86_64_R10 = 10,
+ UNW_X86_64_R11 = 11,
+ UNW_X86_64_R12 = 12,
+ UNW_X86_64_R13 = 13,
+ UNW_X86_64_R14 = 14,
+ UNW_X86_64_R15 = 15
+};
+
+
+// 32-bit ppc register numbers
+enum {
+ UNW_PPC_R0 = 0,
+ UNW_PPC_R1 = 1,
+ UNW_PPC_R2 = 2,
+ UNW_PPC_R3 = 3,
+ UNW_PPC_R4 = 4,
+ UNW_PPC_R5 = 5,
+ UNW_PPC_R6 = 6,
+ UNW_PPC_R7 = 7,
+ UNW_PPC_R8 = 8,
+ UNW_PPC_R9 = 9,
+ UNW_PPC_R10 = 10,
+ UNW_PPC_R11 = 11,
+ UNW_PPC_R12 = 12,
+ UNW_PPC_R13 = 13,
+ UNW_PPC_R14 = 14,
+ UNW_PPC_R15 = 15,
+ UNW_PPC_R16 = 16,
+ UNW_PPC_R17 = 17,
+ UNW_PPC_R18 = 18,
+ UNW_PPC_R19 = 19,
+ UNW_PPC_R20 = 20,
+ UNW_PPC_R21 = 21,
+ UNW_PPC_R22 = 22,
+ UNW_PPC_R23 = 23,
+ UNW_PPC_R24 = 24,
+ UNW_PPC_R25 = 25,
+ UNW_PPC_R26 = 26,
+ UNW_PPC_R27 = 27,
+ UNW_PPC_R28 = 28,
+ UNW_PPC_R29 = 29,
+ UNW_PPC_R30 = 30,
+ UNW_PPC_R31 = 31,
+ UNW_PPC_F0 = 32,
+ UNW_PPC_F1 = 33,
+ UNW_PPC_F2 = 34,
+ UNW_PPC_F3 = 35,
+ UNW_PPC_F4 = 36,
+ UNW_PPC_F5 = 37,
+ UNW_PPC_F6 = 38,
+ UNW_PPC_F7 = 39,
+ UNW_PPC_F8 = 40,
+ UNW_PPC_F9 = 41,
+ UNW_PPC_F10 = 42,
+ UNW_PPC_F11 = 43,
+ UNW_PPC_F12 = 44,
+ UNW_PPC_F13 = 45,
+ UNW_PPC_F14 = 46,
+ UNW_PPC_F15 = 47,
+ UNW_PPC_F16 = 48,
+ UNW_PPC_F17 = 49,
+ UNW_PPC_F18 = 50,
+ UNW_PPC_F19 = 51,
+ UNW_PPC_F20 = 52,
+ UNW_PPC_F21 = 53,
+ UNW_PPC_F22 = 54,
+ UNW_PPC_F23 = 55,
+ UNW_PPC_F24 = 56,
+ UNW_PPC_F25 = 57,
+ UNW_PPC_F26 = 58,
+ UNW_PPC_F27 = 59,
+ UNW_PPC_F28 = 60,
+ UNW_PPC_F29 = 61,
+ UNW_PPC_F30 = 62,
+ UNW_PPC_F31 = 63,
+ UNW_PPC_MQ = 64,
+ UNW_PPC_LR = 65,
+ UNW_PPC_CTR = 66,
+ UNW_PPC_AP = 67,
+ UNW_PPC_CR0 = 68,
+ UNW_PPC_CR1 = 69,
+ UNW_PPC_CR2 = 70,
+ UNW_PPC_CR3 = 71,
+ UNW_PPC_CR4 = 72,
+ UNW_PPC_CR5 = 73,
+ UNW_PPC_CR6 = 74,
+ UNW_PPC_CR7 = 75,
+ UNW_PPC_XER = 76,
+ UNW_PPC_V0 = 77,
+ UNW_PPC_V1 = 78,
+ UNW_PPC_V2 = 79,
+ UNW_PPC_V3 = 80,
+ UNW_PPC_V4 = 81,
+ UNW_PPC_V5 = 82,
+ UNW_PPC_V6 = 83,
+ UNW_PPC_V7 = 84,
+ UNW_PPC_V8 = 85,
+ UNW_PPC_V9 = 86,
+ UNW_PPC_V10 = 87,
+ UNW_PPC_V11 = 88,
+ UNW_PPC_V12 = 89,
+ UNW_PPC_V13 = 90,
+ UNW_PPC_V14 = 91,
+ UNW_PPC_V15 = 92,
+ UNW_PPC_V16 = 93,
+ UNW_PPC_V17 = 94,
+ UNW_PPC_V18 = 95,
+ UNW_PPC_V19 = 96,
+ UNW_PPC_V20 = 97,
+ UNW_PPC_V21 = 98,
+ UNW_PPC_V22 = 99,
+ UNW_PPC_V23 = 100,
+ UNW_PPC_V24 = 101,
+ UNW_PPC_V25 = 102,
+ UNW_PPC_V26 = 103,
+ UNW_PPC_V27 = 104,
+ UNW_PPC_V28 = 105,
+ UNW_PPC_V29 = 106,
+ UNW_PPC_V30 = 107,
+ UNW_PPC_V31 = 108,
+ UNW_PPC_VRSAVE = 109,
+ UNW_PPC_VSCR = 110,
+ UNW_PPC_SPE_ACC = 111,
+ UNW_PPC_SPEFSCR = 112
+
+};
+
+
+}; // namespace lldb_private
+
+
+#endif
+
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/include/mach-o/compact_unwind_encoding.h b/lldb/source/Plugins/Process/Utility/libunwind/include/mach-o/compact_unwind_encoding.h
new file mode 100644
index 00000000000..bee2ad578d6
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/include/mach-o/compact_unwind_encoding.h
@@ -0,0 +1,212 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- compact_unwind_encoding.h -------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#ifndef __COMPACT_UNWIND_ENCODING__
+#define __COMPACT_UNWIND_ENCODING__
+
+#include <stdint.h>
+
+namespace lldb_private {
+
+//
+// Each final linked mach-o image has an optional __TEXT, __unwind_info section.
+// This section is much smaller and faster to use than the __eh_frame section.
+//
+
+
+
+//
+// Compilers usually emit standard Dwarf FDEs. The linker recognizes standard FDEs and
+// synthesizes a matching compact_unwind_encoding_t and adds it to the __unwind_info table.
+// It is also possible for the compiler to emit __unwind_info entries for functions that
+// have different unwind requirements at different ranges in the function.
+//
+typedef uint32_t compact_unwind_encoding_t;
+
+
+
+//
+// The __unwind_info section is laid out for an efficient two level lookup.
+// The header of the section contains a coarse index that maps function address
+// to the page (4096 byte block) containing the unwind info for that function.
+//
+
+#define UNWIND_SECTION_VERSION 1
+struct unwind_info_section_header
+{
+ uint32_t version; // UNWIND_SECTION_VERSION
+ uint32_t commonEncodingsArraySectionOffset;
+ uint32_t commonEncodingsArrayCount;
+ uint32_t personalityArraySectionOffset;
+ uint32_t personalityArrayCount;
+ uint32_t indexSectionOffset;
+ uint32_t indexCount;
+ // compact_unwind_encoding_t[]
+ // uintptr_t personalities[]
+ // unwind_info_section_header_index_entry[]
+ // unwind_info_section_header_lsda_index_entry[]
+};
+
+struct unwind_info_section_header_index_entry
+{
+ uint32_t functionOffset;
+ uint32_t secondLevelPagesSectionOffset; // section offset to start of regular or compress page
+ uint32_t lsdaIndexArraySectionOffset; // section offset to start of lsda_index array for this range
+};
+
+struct unwind_info_section_header_lsda_index_entry
+{
+ uint32_t functionOffset;
+ uint32_t lsdaOffset;
+};
+
+//
+// There are two kinds of second level index pages: regular and compressed.
+// A compressed page can hold up to 1021 entries, but it cannot be used
+// if too many different encoding types are used. The regular page holds
+// 511 entries.
+//
+
+struct unwind_info_regular_second_level_entry
+{
+ uint32_t functionOffset;
+ compact_unwind_encoding_t encoding;
+};
+
+#define UNWIND_SECOND_LEVEL_REGULAR 2
+struct unwind_info_regular_second_level_page_header
+{
+ uint32_t kind; // UNWIND_SECOND_LEVEL_REGULAR
+ uint16_t entryPageOffset;
+ uint16_t entryCount;
+ // entry array
+};
+
+#define UNWIND_SECOND_LEVEL_COMPRESSED 3
+struct unwind_info_compressed_second_level_page_header
+{
+ uint32_t kind; // UNWIND_SECOND_LEVEL_COMPRESSED
+ uint16_t entryPageOffset;
+ uint16_t entryCount;
+ uint16_t encodingsPageOffset;
+ uint16_t encodingsCount;
+ // 32-bit entry array
+ // encodings array
+};
+
+#define UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry) (entry & 0x00FFFFFF)
+#define UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry) ((entry >> 24) & 0xFF)
+
+
+
+// architecture independent bits
+enum {
+ UNWIND_IS_NOT_FUNCTION_START = 0x80000000,
+ UNWIND_HAS_LSDA = 0x40000000,
+ UNWIND_PERSONALITY_MASK = 0x30000000,
+};
+
+
+// x86_64
+//
+// 1-bit: start
+// 1-bit: has lsda
+// 2-bit: personality index
+//
+// 4-bits: 0=old, 1=rbp based, 2=stack-imm, 3=stack-ind, 4=dwarf
+// rbp based:
+// 15-bits (5*3-bits per reg) register permutation
+// 8-bits for stack offset
+// frameless:
+// 8-bits stack size
+// 3-bits stack adjust
+// 3-bits register count
+// 10-bits register permutation
+//
+enum {
+ UNWIND_X86_64_MODE_MASK = 0x0F000000,
+ UNWIND_X86_64_MODE_COMPATIBILITY = 0x00000000,
+ UNWIND_X86_64_MODE_RBP_FRAME = 0x01000000,
+ UNWIND_X86_64_MODE_STACK_IMMD = 0x02000000,
+ UNWIND_X86_64_MODE_STACK_IND = 0x03000000,
+ UNWIND_X86_64_MODE_DWARF = 0x04000000,
+
+ UNWIND_X86_64_RBP_FRAME_REGISTERS = 0x00007FFF,
+ UNWIND_X86_64_RBP_FRAME_OFFSET = 0x00FF0000,
+
+ UNWIND_X86_64_FRAMELESS_STACK_SIZE = 0x00FF0000,
+ UNWIND_X86_64_FRAMELESS_STACK_ADJUST = 0x0000E000,
+ UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT = 0x00001C00,
+ UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF,
+
+ UNWIND_X86_64_DWARF_SECTION_OFFSET = 0x00FFFFFF,
+};
+
+enum {
+ UNWIND_X86_64_REG_NONE = 0,
+ UNWIND_X86_64_REG_RBX = 1,
+ UNWIND_X86_64_REG_R12 = 2,
+ UNWIND_X86_64_REG_R13 = 3,
+ UNWIND_X86_64_REG_R14 = 4,
+ UNWIND_X86_64_REG_R15 = 5,
+ UNWIND_X86_64_REG_RBP = 6,
+};
+
+
+// x86
+//
+// 1-bit: start
+// 1-bit: has lsda
+// 2-bit: personality index
+//
+// 4-bits: 0=old, 1=ebp based, 2=stack-imm, 3=stack-ind, 4=dwarf
+// ebp based:
+// 15-bits (5*3-bits per reg) register permutation
+// 8-bits for stack offset
+// frameless:
+// 8-bits stack size
+// 3-bits stack adjust
+// 3-bits register count
+// 10-bits register permutation
+//
+enum {
+ UNWIND_X86_MODE_MASK = 0x0F000000,
+ UNWIND_X86_MODE_COMPATIBILITY = 0x00000000,
+ UNWIND_X86_MODE_EBP_FRAME = 0x01000000,
+ UNWIND_X86_MODE_STACK_IMMD = 0x02000000,
+ UNWIND_X86_MODE_STACK_IND = 0x03000000,
+ UNWIND_X86_MODE_DWARF = 0x04000000,
+
+ UNWIND_X86_EBP_FRAME_REGISTERS = 0x00007FFF,
+ UNWIND_X86_EBP_FRAME_OFFSET = 0x00FF0000,
+
+ UNWIND_X86_FRAMELESS_STACK_SIZE = 0x00FF0000,
+ UNWIND_X86_FRAMELESS_STACK_ADJUST = 0x0000E000,
+ UNWIND_X86_FRAMELESS_STACK_REG_COUNT = 0x00001C00,
+ UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF,
+
+ UNWIND_X86_DWARF_SECTION_OFFSET = 0x00FFFFFF,
+};
+
+enum {
+ UNWIND_X86_REG_NONE = 0,
+ UNWIND_X86_REG_EBX = 1,
+ UNWIND_X86_REG_ECX = 2,
+ UNWIND_X86_REG_EDX = 3,
+ UNWIND_X86_REG_EDI = 4,
+ UNWIND_X86_REG_ESI = 5,
+ UNWIND_X86_REG_EBP = 6,
+};
+
+}; // namespace lldb_private
+
+#endif
+
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/include/unwind.h b/lldb/source/Plugins/Process/Utility/libunwind/include/unwind.h
new file mode 100644
index 00000000000..80b9d2881c2
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/include/unwind.h
@@ -0,0 +1,213 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- unwind.h ------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// C interface to libuwind
+//
+// Source compatible with Level 1 Base ABI documented at:
+// http://www.codesourcery.com/public/cxx-abi/abi-eh.html
+//
+//===----------------------------------------------------------------------===//
+
+
+#ifndef __UNWIND_H__
+#define __UNWIND_H__
+
+#include <stdint.h>
+#include <stddef.h>
+#include <Availability.h>
+
+namespace lldb_private {
+
+typedef enum {
+ _URC_NO_REASON = 0,
+ _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
+ _URC_FATAL_PHASE2_ERROR = 2,
+ _URC_FATAL_PHASE1_ERROR = 3,
+ _URC_NORMAL_STOP = 4,
+ _URC_END_OF_STACK = 5,
+ _URC_HANDLER_FOUND = 6,
+ _URC_INSTALL_CONTEXT = 7,
+ _URC_CONTINUE_UNWIND = 8
+} _Unwind_Reason_Code;
+
+typedef enum {
+ _UA_SEARCH_PHASE = 1,
+ _UA_CLEANUP_PHASE = 2,
+ _UA_HANDLER_FRAME = 4,
+ _UA_FORCE_UNWIND = 8,
+ _UA_END_OF_STACK = 16 // gcc extension to C++ ABI
+} _Unwind_Action;
+
+
+struct _Unwind_Context; // opaque
+struct _Unwind_Exception; // forward declaration
+
+struct _Unwind_Exception {
+ uint64_t exception_class;
+ void (*exception_cleanup)(_Unwind_Reason_Code reason, struct _Unwind_Exception* exc);
+ uintptr_t private_1; // non-zero means forced unwind
+ uintptr_t private_2; // holds sp that phase1 found for phase2 to use
+};
+
+
+typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)
+ (int version,
+ _Unwind_Action actions,
+ uint64_t exceptionClass,
+ struct _Unwind_Exception* exceptionObject,
+ struct _Unwind_Context* context,
+ void* stop_parameter );
+
+
+typedef _Unwind_Reason_Code (*__personality_routine)
+ (int version,
+ _Unwind_Action actions,
+ uint64_t exceptionClass,
+ struct _Unwind_Exception* exceptionObject,
+ struct _Unwind_Context* context);
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//
+// The following are the base functions documented by the C++ ABI
+//
+#if __arm__
+ extern _Unwind_Reason_Code _Unwind_SjLj_RaiseException(struct _Unwind_Exception* exception_object);
+ extern void _Unwind_SjLj_Resume(struct _Unwind_Exception* exception_object);
+#else
+ extern _Unwind_Reason_Code _Unwind_RaiseException(struct _Unwind_Exception* exception_object);
+ extern void _Unwind_Resume(struct _Unwind_Exception* exception_object);
+#endif
+extern void _Unwind_DeleteException(struct _Unwind_Exception* exception_object);
+extern uintptr_t _Unwind_GetGR(struct _Unwind_Context* context, int index);
+extern void _Unwind_SetGR(struct _Unwind_Context* context, int index, uintptr_t new_value);
+extern uintptr_t _Unwind_GetIP(struct _Unwind_Context* context);
+extern void _Unwind_SetIP(struct _Unwind_Context*, uintptr_t new_value);
+extern uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context* context);
+extern uintptr_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context* context);
+#if __arm__
+ extern _Unwind_Reason_Code _Unwind_SjLj_ForcedUnwind(struct _Unwind_Exception* exception_object, _Unwind_Stop_Fn stop, void* stop_parameter );
+#else
+ extern _Unwind_Reason_Code _Unwind_ForcedUnwind(struct _Unwind_Exception* exception_object, _Unwind_Stop_Fn stop, void* stop_parameter );
+#endif
+
+#if __arm__
+ typedef struct _Unwind_FunctionContext* _Unwind_FunctionContext_t;
+ extern void _Unwind_SjLj_Register(_Unwind_FunctionContext_t fc);
+ extern void _Unwind_SjLj_Unregister(_Unwind_FunctionContext_t fc);
+#endif
+
+//
+// The following are semi-suppoted extensions to the C++ ABI
+//
+
+
+//
+// called by __cxa_rethrow().
+//
+#if __arm__
+ extern _Unwind_Reason_Code _Unwind_SjLj_Resume_or_Rethrow(struct _Unwind_Exception* exception_object);
+#else
+ extern _Unwind_Reason_Code _Unwind_Resume_or_Rethrow(struct _Unwind_Exception* exception_object);
+#endif
+
+
+//
+// _Unwind_Backtrace() is a gcc extension that walks the stack and calls the
+// _Unwind_Trace_Fn once per frame until it reaches the bottom of the stack
+// or the _Unwind_Trace_Fn function returns something other than _URC_NO_REASON.
+//
+typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn)(struct _Unwind_Context*, void*);
+extern _Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn, void*);
+
+
+//
+// _Unwind_GetCFA is a gcc extension that can be called from within a personality
+// handler to get the CFA (stack pointer before call) of current frame.
+//
+extern uintptr_t _Unwind_GetCFA(struct _Unwind_Context*);
+
+
+//
+// _Unwind_GetIPInfo is a gcc extension that can be called from within a personality
+// handler. Similar to _Unwind_GetIP() but also returns in *ipBefore a non-zero
+// value if the instruction pointer is at or before the instruction causing
+// the unwind. Normally, in a function call, the IP returned is the return address
+// which is after the call instruction and may be past the end of the function
+// containing the call instruction.
+//
+extern uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context* context, int* ipBefore);
+
+
+//
+// __register_frame() is used with dynamically generated code to register the FDE
+// for a generated (JIT) code. The FDE must use pc-rel addressing to point to its
+// function and optional LSDA. __register_frame() has existed in all versions of
+// Mac OS X, but in 10.4 and 10.5 it was buggy and did not actually register the
+// FDE with the unwinder. In 10.6 and later it does register properly.
+//
+extern void __register_frame(const void* fde);
+extern void __deregister_frame(const void* fde);
+
+
+//
+// _Unwind_Find_FDE() will locate the FDE if the pc is in some function that has
+// an associated FDE. Note, Mac OS X 10.6 and later, introduces "compact unwind info"
+// which the runtime uses in preference to dwarf unwind info. This function
+// will only work if the target function has an FDE but no compact unwind info.
+//
+struct dwarf_eh_bases
+{
+ uintptr_t tbase;
+ uintptr_t dbase;
+ uintptr_t func;
+};
+extern const void* _Unwind_Find_FDE(const void* pc, struct dwarf_eh_bases*);
+
+
+//
+// This function attempts to find the start (address of first instruction) of
+// a function given an address inside the function. It only works if the function
+// has an FDE (dwarf unwind info).
+// This function is unimplemented on Mac OS X 10.6 and later. Instead, use
+// _Unwind_Find_FDE() and look at the dwarf_eh_bases.func result.
+extern void* _Unwind_FindEnclosingFunction(void* pc);
+
+
+// Mac OS X does not support text-rel and data-rel addressing so these functions are unimplemented
+extern uintptr_t _Unwind_GetDataRelBase(struct _Unwind_Context* context) __attribute__((unavailable));
+extern uintptr_t _Unwind_GetTextRelBase(struct _Unwind_Context* context) __attribute__((unavailable));
+
+
+
+// Mac OS X 10.4 and 10.5 had implementations of these functions in libgcc_s.dylib,
+// but they never worked. These functions are no longer available.
+extern void __register_frame_info_bases(const void* fde, void* ob, void* tb, void* db) __attribute__((unavailable));
+extern void __register_frame_info(const void* fde, void* ob) __attribute__((unavailable));
+extern void __register_frame_info_table_bases(const void* fde, void* ob,void* tb, void* db) __attribute__((unavailable));
+extern void __register_frame_info_table(const void* fde, void* ob) __attribute__((unavailable));
+extern void __register_frame_table(const void* fde) __attribute__((unavailable));
+extern void* __deregister_frame_info(const void* fde) __attribute__((unavailable));
+extern void* __deregister_frame_info_bases(const void* fde) __attribute__((unavailable));
+
+
+#ifdef __cplusplus
+}
+#endif
+
+}; // namespace lldb_private
+
+#endif // __UNWIND_H__
+
+
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/AddressSpace.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/AddressSpace.hpp
new file mode 100644
index 00000000000..5173dc0068e
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/AddressSpace.hpp
@@ -0,0 +1,456 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- AddressSpace.hpp ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+//
+// C++ interface to lower levels of libuwind
+//
+
+#ifndef __ADDRESSSPACE_HPP__
+#define __ADDRESSSPACE_HPP__
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <mach-o/loader.h>
+#include <mach-o/getsect.h>
+#if !defined (SUPPORT_REMOTE_UNWINDING)
+#include <mach-o/dyld_priv.h>
+#endif
+#include <mach/ppc/thread_status.h>
+#include <mach/i386/thread_status.h>
+#include <Availability.h>
+
+#include "FileAbstraction.hpp"
+#include "libunwind.h"
+#include "InternalMacros.h"
+#include "dwarf2.h"
+#include "RemoteProcInfo.hpp"
+
+#if defined (SUPPORT_REMOTE_UNWINDING)
+bool _dyld_find_unwind_sections(void* addr, void* info)
+{
+ assert("unwinding with a non-remote process not supported.");
+ return false;
+}
+#endif // SUPPORT_REMOTE_UNWINDING
+
+namespace lldb_private {
+
+///
+/// LocalAddressSpace is used as a template parameter to UnwindCursor when unwinding a thread
+/// in the same process. It compiles away and making local unwinds very fast.
+///
+class LocalAddressSpace
+{
+public:
+
+ #if __LP64__
+ typedef uint64_t pint_t;
+ typedef int64_t sint_t;
+ #else
+ typedef uint32_t pint_t;
+ typedef int32_t sint_t;
+ #endif
+ int getBytes(pint_t addr, pint_t extent, uint8_t* buf) { memcpy(buf, (void*)addr, extent); return 1; }
+ uint8_t get8(pint_t addr) { return *((uint8_t*)addr); }
+ uint16_t get16(pint_t addr) { return *((uint16_t*)addr); }
+ uint32_t get32(pint_t addr) { return *((uint32_t*)addr); }
+ uint64_t get64(pint_t addr) { return *((uint64_t*)addr); }
+ double getDouble(pint_t addr) { return *((double*)addr); }
+ v128 getVector(pint_t addr) { return *((v128*)addr); }
+
+ uint8_t get8(pint_t addr, int& err) { return *((uint8_t*)addr); err = 0; }
+ uint16_t get16(pint_t addr, int& err) { return *((uint16_t*)addr); err = 0; }
+ uint32_t get32(pint_t addr, int& err) { return *((uint32_t*)addr); err = 0; }
+ uint64_t get64(pint_t addr, int& err) { return *((uint64_t*)addr); err = 0; }
+ double getDouble(pint_t addr, int& err) { return *((double*)addr); err = 0; }
+ v128 getVector(pint_t addr, int& err) { return *((v128*)addr); err = 0; }
+
+ uintptr_t getP(pint_t addr);
+ uintptr_t getP(pint_t addr, int &err);
+ static uint64_t getULEB128(pint_t& addr, pint_t end);
+ static int64_t getSLEB128(pint_t& addr, pint_t end);
+
+ pint_t getEncodedP(pint_t& addr, pint_t end, uint8_t encoding);
+ bool findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset);
+ bool findUnwindSections(pint_t addr, pint_t& mh, pint_t& dwarfStart, pint_t& dwarfLen, pint_t& compactStart);
+
+#if defined (SUPPORT_REMOTE_UNWINDING)
+ RemoteProcInfo* getRemoteProcInfo () { return NULL; }
+ unw_accessors_t* accessors() { return NULL; }
+ unw_addr_space_t wrap() { return NULL; }
+#endif
+};
+
+LocalAddressSpace sThisAddress;
+
+inline uintptr_t LocalAddressSpace::getP(pint_t addr)
+{
+#if __LP64__
+ return get64(addr);
+#else
+ return get32(addr);
+#endif
+}
+
+inline uintptr_t LocalAddressSpace::getP(pint_t addr, int &err)
+{
+#if __LP64__
+ return get64(addr);
+#else
+ return get32(addr);
+#endif
+ err = 0;
+}
+
+/* Read a ULEB128 into a 64-bit word. */
+inline uint64_t
+LocalAddressSpace::getULEB128(pint_t& addr, pint_t end)
+{
+ const uint8_t* p = (uint8_t*)addr;
+ const uint8_t* pend = (uint8_t*)end;
+ uint64_t result = 0;
+ int bit = 0;
+ do {
+ uint64_t b;
+
+ if ( p == pend )
+ ABORT("truncated uleb128 expression");
+
+ b = *p & 0x7f;
+
+ if (bit >= 64 || b << bit >> bit != b) {
+ ABORT("malformed uleb128 expression");
+ }
+ else {
+ result |= b << bit;
+ bit += 7;
+ }
+ } while ( *p++ >= 0x80 );
+ addr = (pint_t)p;
+ return result;
+}
+
+/* Read a SLEB128 into a 64-bit word. */
+inline int64_t
+LocalAddressSpace::getSLEB128(pint_t& addr, pint_t end)
+{
+ const uint8_t* p = (uint8_t*)addr;
+ int64_t result = 0;
+ int bit = 0;
+ uint8_t byte;
+ do {
+ byte = *p++;
+ result |= ((byte & 0x7f) << bit);
+ bit += 7;
+ } while (byte & 0x80);
+ // sign extend negative numbers
+ if ( (byte & 0x40) != 0 )
+ result |= (-1LL) << bit;
+ addr = (pint_t)p;
+ return result;
+}
+
+LocalAddressSpace::pint_t
+LocalAddressSpace::getEncodedP(pint_t& addr, pint_t end, uint8_t encoding)
+{
+ pint_t startAddr = addr;
+ const uint8_t* p = (uint8_t*)addr;
+ pint_t result;
+
+ // first get value
+ switch (encoding & 0x0F) {
+ case DW_EH_PE_ptr:
+ result = getP(addr);
+ p += sizeof(pint_t);
+ addr = (pint_t)p;
+ break;
+ case DW_EH_PE_uleb128:
+ result = getULEB128(addr, end);
+ break;
+ case DW_EH_PE_udata2:
+ result = get16(addr);
+ p += 2;
+ addr = (pint_t)p;
+ break;
+ case DW_EH_PE_udata4:
+ result = get32(addr);
+ p += 4;
+ addr = (pint_t)p;
+ break;
+ case DW_EH_PE_udata8:
+ result = get64(addr);
+ p += 8;
+ addr = (pint_t)p;
+ break;
+ case DW_EH_PE_sleb128:
+ result = getSLEB128(addr, end);
+ break;
+ case DW_EH_PE_sdata2:
+ result = (int16_t)get16(addr);
+ p += 2;
+ addr = (pint_t)p;
+ break;
+ case DW_EH_PE_sdata4:
+ result = (int32_t)get32(addr);
+ p += 4;
+ addr = (pint_t)p;
+ break;
+ case DW_EH_PE_sdata8:
+ result = get64(addr);
+ p += 8;
+ addr = (pint_t)p;
+ break;
+ default:
+ ABORT("unknown pointer encoding");
+ }
+
+ // then add relative offset
+ switch ( encoding & 0x70 ) {
+ case DW_EH_PE_absptr:
+ // do nothing
+ break;
+ case DW_EH_PE_pcrel:
+ result += startAddr;
+ break;
+ case DW_EH_PE_textrel:
+ ABORT("DW_EH_PE_textrel pointer encoding not supported");
+ break;
+ case DW_EH_PE_datarel:
+ ABORT("DW_EH_PE_datarel pointer encoding not supported");
+ break;
+ case DW_EH_PE_funcrel:
+ ABORT("DW_EH_PE_funcrel pointer encoding not supported");
+ break;
+ case DW_EH_PE_aligned:
+ ABORT("DW_EH_PE_aligned pointer encoding not supported");
+ break;
+ default:
+ ABORT("unknown pointer encoding");
+ break;
+ }
+
+ if ( encoding & DW_EH_PE_indirect )
+ result = getP(result);
+
+ return result;
+}
+
+
+inline bool LocalAddressSpace::findUnwindSections(pint_t addr, pint_t& mh, pint_t& dwarfStart, pint_t& dwarfLen, pint_t& compactStart)
+{
+#if !defined (SUPPORT_REMOTE_UNWINDING)
+ dyld_unwind_sections info;
+ if ( _dyld_find_unwind_sections((void*)addr, &info) ) {
+ mh = (pint_t)info.mh;
+ dwarfStart = (pint_t)info.dwarf_section;
+ dwarfLen = (pint_t)info.dwarf_section_length;
+ compactStart = (pint_t)info.compact_unwind_section;
+ return true;
+ }
+#else
+ assert("unwinding with a non-remote process not supported.");
+#endif
+ return false;
+}
+
+
+inline bool LocalAddressSpace::findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset)
+{
+ dl_info dyldInfo;
+ if ( dladdr((void*)addr, &dyldInfo) ) {
+ if ( dyldInfo.dli_sname != NULL ) {
+ strlcpy(buf, dyldInfo.dli_sname, bufLen);
+ *offset = (addr - (pint_t)dyldInfo.dli_saddr);
+ return true;
+ }
+ }
+ return false;
+}
+
+#if defined (SUPPORT_REMOTE_UNWINDING)
+///
+/// OtherAddressSpace is used as a template parameter to UnwindCursor when unwinding a thread
+/// in the another process. The other process can be a different endianness and a different
+/// pointer size and is handled by the P template parameter.
+///
+template <typename P>
+class OtherAddressSpace
+{
+public:
+ OtherAddressSpace (unw_addr_space_t remote_addr_space, void* arg) : fAddrSpace ((unw_addr_space_remote *)remote_addr_space), fArg(arg)
+ {
+ if (fAddrSpace->type != UNW_REMOTE)
+ ABORT("OtherAddressSpace ctor called with non-remote address space.");
+ fRemoteProcInfo = fAddrSpace->ras;
+ }
+
+ typedef typename P::uint_t pint_t;
+ typedef typename P::int_t sint_t;
+
+ int getBytes(pint_t addr, pint_t extent, uint8_t* buf) { return fRemoteProcInfo->getBytes (addr, extent, buf, fArg); }
+ uint8_t get8(pint_t addr) { return fRemoteProcInfo->get8(addr, fArg); }
+ uint16_t get16(pint_t addr) { return fRemoteProcInfo->get16(addr, fArg); }
+ uint32_t get32(pint_t addr) { return fRemoteProcInfo->get32(addr, fArg); }
+ uint64_t get64(pint_t addr) { return fRemoteProcInfo->get64(addr, fArg); }
+ pint_t getP(pint_t addr) { return fRemoteProcInfo->getP(addr, fArg); }
+
+ uint8_t get8(pint_t addr, int& err) { return fRemoteProcInfo->get8(addr, err, fArg); }
+ uint16_t get16(pint_t addr, int& err) { return fRemoteProcInfo->get16(addr, err, fArg); }
+ uint32_t get32(pint_t addr, int& err) { return fRemoteProcInfo->get32(addr, err, fArg); }
+ uint64_t get64(pint_t addr, int& err) { return fRemoteProcInfo->get64(addr, err, fArg); }
+ pint_t getP(pint_t addr, int &err) { return fRemoteProcInfo->getP(addr, err, fArg); }
+
+ uint64_t getULEB128(pint_t& addr, pint_t end) { return fRemoteProcInfo->getULEB128 (addr, end, fArg); }
+ int64_t getSLEB128(pint_t& addr, pint_t end) { return fRemoteProcInfo->getSLEB128 (addr, end, fArg); }
+ pint_t getEncodedP(pint_t& addr, pint_t end, uint8_t encoding);
+ double getDouble(pint_t addr);
+ v128 getVector(pint_t addr);
+ bool findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset);
+ bool findFunctionExtent(pint_t addr, unw_word_t* begin, unw_word_t* end);
+ bool findUnwindSections(pint_t addr, pint_t& mh, pint_t& eh_frame_start, pint_t& eh_frame_len, pint_t& compactStart);
+ RemoteProcInfo* getRemoteProcInfo () { return fRemoteProcInfo; }
+ unw_accessors_t* accessors() { return fRemoteProcInfo->getAccessors(); }
+ unw_addr_space_t wrap() { return (unw_addr_space_t) fAddrSpace; }
+private:
+ void* localCopy(pint_t addr);
+ unw_addr_space_remote *fAddrSpace;
+ RemoteProcInfo* fRemoteProcInfo;
+ void* fArg;
+};
+
+template <typename P>
+typename OtherAddressSpace<P>::pint_t OtherAddressSpace<P>::getEncodedP(pint_t& addr, pint_t end, uint8_t encoding)
+{
+ pint_t startAddr = addr;
+ pint_t p = addr;
+ pint_t result;
+
+ // first get value
+ switch (encoding & 0x0F) {
+ case DW_EH_PE_ptr:
+ result = fRemoteProcInfo->getP(addr, fArg);
+ p += sizeof(pint_t);
+ addr = p;
+ break;
+ case DW_EH_PE_uleb128:
+ result = fRemoteProcInfo->getULEB128(addr, end, fArg);
+ break;
+ case DW_EH_PE_udata2:
+ result = fRemoteProcInfo->get16(addr, fArg);
+ p += 2;
+ addr = p;
+ break;
+ case DW_EH_PE_udata4:
+ result = fRemoteProcInfo->get32(addr, fArg);
+ p += 4;
+ addr = p;
+ break;
+ case DW_EH_PE_udata8:
+ result = fRemoteProcInfo->get64(addr, fArg);
+ p += 8;
+ addr = p;
+ break;
+ case DW_EH_PE_sleb128:
+ result = fRemoteProcInfo->getSLEB128(addr, end, fArg);
+ break;
+ case DW_EH_PE_sdata2:
+ result = (int16_t)fRemoteProcInfo->get16(addr, fArg);
+ p += 2;
+ addr = p;
+ break;
+ case DW_EH_PE_sdata4:
+ result = (int32_t)fRemoteProcInfo->get32(addr, fArg);
+ p += 4;
+ addr = p;
+ break;
+ case DW_EH_PE_sdata8:
+ result = fRemoteProcInfo->get64(addr, fArg);
+ p += 8;
+ addr = p;
+ break;
+ default:
+ ABORT("unknown pointer encoding");
+ }
+
+ // then add relative offset
+ switch ( encoding & 0x70 ) {
+ case DW_EH_PE_absptr:
+ // do nothing
+ break;
+ case DW_EH_PE_pcrel:
+ result += startAddr;
+ break;
+ case DW_EH_PE_textrel:
+ ABORT("DW_EH_PE_textrel pointer encoding not supported");
+ break;
+ case DW_EH_PE_datarel:
+ ABORT("DW_EH_PE_datarel pointer encoding not supported");
+ break;
+ case DW_EH_PE_funcrel:
+ ABORT("DW_EH_PE_funcrel pointer encoding not supported");
+ break;
+ case DW_EH_PE_aligned:
+ ABORT("DW_EH_PE_aligned pointer encoding not supported");
+ break;
+ default:
+ ABORT("unknown pointer encoding");
+ break;
+ }
+
+ if ( encoding & DW_EH_PE_indirect )
+ result = fRemoteProcInfo->getP(result, fArg);
+
+ return result;
+}
+
+template <typename P>
+double OtherAddressSpace<P>::getDouble(pint_t addr)
+{
+ return fRemoteProcInfo->getDouble(addr, fArg);
+}
+
+template <typename P>
+v128 OtherAddressSpace<P>::getVector(pint_t addr)
+{
+ return fRemoteProcInfo->getVector(addr, fArg);
+}
+
+template <typename P>
+bool OtherAddressSpace<P>::findUnwindSections(pint_t addr, pint_t& mh, pint_t& eh_frame_start, pint_t& eh_frame_len, pint_t& compactStart)
+{
+ compactStart = 0;
+ uint64_t t_mh, t_text_start, t_text_end, t_eh_frame_start, t_eh_frame_len, t_compact_start;
+ if (fRemoteProcInfo->getImageAddresses (addr, t_mh, t_text_start, t_text_end, t_eh_frame_start, t_eh_frame_len, t_compact_start, fArg))
+ {
+ mh = t_mh;
+ eh_frame_start = t_eh_frame_start;
+ eh_frame_len = t_eh_frame_len;
+ compactStart = t_compact_start;
+ return true;
+ }
+ return false;
+}
+
+template <typename P>
+bool OtherAddressSpace<P>::findFunctionName(pint_t addr, char* buf, size_t bufLen, unw_word_t* offset)
+{
+ return fRemoteProcInfo->findFunctionName (addr, buf, bufLen, offset, fArg);
+}
+
+#endif // SUPPORT_REMOTE_UNWINDING
+
+
+} // namespace lldb_private
+
+
+
+#endif // __ADDRESSSPACE_HPP__
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/ArchDefaultUnwinder.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/ArchDefaultUnwinder.hpp
new file mode 100644
index 00000000000..d19d7aea50f
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/ArchDefaultUnwinder.hpp
@@ -0,0 +1,115 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- ArchDefaultUnwinder.hpp ---------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// Unwind a stack frame using nothing but the default conventions on
+// this architecture.
+
+#ifndef __ARCH_DEFAULT_UNWINDER_HPP
+#define __ARCH_DEFAULT_UNWINDER_HPP
+
+#if defined (SUPPORT_REMOTE_UNWINDING)
+
+#include "AddressSpace.hpp"
+#include "Registers.hpp"
+#include "RemoteRegisterMap.hpp"
+#include "RemoteProcInfo.hpp"
+
+namespace lldb_private
+{
+
+// As a last ditch attempt to unwind a stack frame, unwind by the
+// architecture's typical conventions. We try compact unwind, eh frame CFI,
+// and then assembly profiling if we have function bounds -- but if we're
+// looking at an address with no function bounds or unwind info, make a best
+// guess at how to get out.
+
+// In practice, this is usually hit when we try to step out of start() in a
+// stripped application binary, we've jumped to 0x0, or we're in jitted code
+// in the heap.
+
+template <typename A, typename R>
+int stepByArchitectureDefault_x86 (A& addressSpace, R& registers,
+ uint64_t pc, int wordsize) {
+ R newRegisters(registers);
+ RemoteRegisterMap *rmap = addressSpace.getRemoteProcInfo()->getRegisterMap();
+ int frame_reg = rmap->unwind_regno_for_frame_pointer();
+ int stack_reg = rmap->unwind_regno_for_stack_pointer();
+ int err;
+
+ /* If the pc is 0x0 either we call'ed 0 (went thorugh a null function
+ pointer) or this is a thread in the middle of being created that has
+ no stack at all.
+ For the call-0x0 case, we know how to unwind that - the pc is at
+ the stack pointer.
+
+ Otherwise follow the usual convention of trusting that RBP/EBP has the
+ start of the stack frame and we can find the caller's pc based on
+ that. */
+
+ uint64_t newpc, newframeptr;
+ newpc = 0;
+ newframeptr = -1;
+ if (pc == 0) {
+ uint64_t oldsp = registers.getRegister(stack_reg);
+ err = 0;
+ if (oldsp != 0) {
+ newpc = addressSpace.getP(registers.getRegister(stack_reg), err);
+ if (err != 0)
+ return UNW_EUNSPEC;
+ newRegisters.setIP (newpc);
+ newRegisters.setRegister (stack_reg, registers.getRegister(stack_reg) +
+ wordsize);
+ }
+ }
+ else {
+ newpc = addressSpace.getP(registers.getRegister(frame_reg) +
+ wordsize, err);
+ if (err != 0)
+ return UNW_EUNSPEC;
+
+ newRegisters.setIP (newpc);
+ newframeptr = addressSpace.getP(registers.getRegister(frame_reg),
+ err);
+ if (err != 0)
+ return UNW_EUNSPEC;
+
+ newRegisters.setRegister (frame_reg, newframeptr);
+ newRegisters.setRegister (stack_reg, registers.getRegister(frame_reg) +
+ (wordsize * 2));
+ }
+ registers = newRegisters;
+ if (newpc == 0 || newframeptr == 0)
+ return UNW_STEP_END;
+ return UNW_STEP_SUCCESS;
+}
+
+template <typename A>
+int stepByArchitectureDefault (A& addressSpace, Registers_x86_64 &registers,
+ uint64_t pc) {
+ return stepByArchitectureDefault_x86 (addressSpace, registers, pc, 8);
+}
+
+template <typename A>
+int stepByArchitectureDefault (A& addressSpace, Registers_x86& registers,
+ uint64_t pc) {
+ return stepByArchitectureDefault_x86 (addressSpace, registers, pc, 4);
+}
+
+template <typename A>
+int stepByArchitectureDefault (A& addressSpace, Registers_ppc& registers,
+ uint64_t pc) {
+ ABORT("Remote unwinding not supported for ppc.");
+ return UNW_EUNSPEC;
+}
+
+}; // namespace lldb_private
+
+#endif // SUPPORT_REMOTE_UNWINDING
+#endif // __ARCH_DEFAULT_UNWINDER_HPP
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyInstructions.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyInstructions.hpp
new file mode 100644
index 00000000000..1e695d5e4f0
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyInstructions.hpp
@@ -0,0 +1,147 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- AssemblyInstructions.hpp --------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __ASSEMBLY_INSTRUCTIONS_HPP
+#define __ASSEMBLY_INSTRUCTIONS_HPP
+
+#if defined (SUPPORT_REMOTE_UNWINDING)
+
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS
+#endif
+#ifndef __STDC_CONSTANT_MACROS
+#define __STDC_CONSTANT_MACROS
+#endif
+
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <map>
+
+#include "libunwind.h"
+#include "AssemblyParser.hpp"
+#include "AddressSpace.hpp"
+#include "Registers.hpp"
+#include "RemoteUnwindProfile.h"
+
+namespace lldb_private
+{
+
+// A debug function to dump the contents of an RemoteUnwindProfile to
+// stdout in a human readable form.
+
+template <typename A, typename R>
+void printProfile (A& addressSpace, uint64_t pc, RemoteUnwindProfile* profile, R& registers) {
+ RemoteProcInfo *procinfo = addressSpace.getRemoteProcInfo();
+ RemoteRegisterMap *regmap = procinfo->getRegisterMap();
+
+ procinfo->logDebug ("Print profile: given pc of 0x%llx, profile has range 0x%llx - 0x%llx", pc, profile->fStart, profile->fEnd);
+ procinfo->logDebug ("CFA locations:");
+ std::map<uint64_t, RemoteUnwindProfile::CFALocation>::iterator i;
+ for (i = profile->cfa.begin(); i != profile->cfa.end(); ++i) {
+ procinfo->logDebug (" as of 0x%llx cfa is based off of reg %d (%s) offset %d", i->first, i->second.regno, regmap->unwind_regno_to_name(i->second.regno), i->second.offset);
+ }
+ procinfo->logDebug ("Caller's saved IP is at %d bytes offset from the cfa", (int)profile->returnAddress.value);
+ procinfo->logDebug ("Register saves:");
+ std::map<uint64_t, std::vector<RemoteUnwindProfile::SavedReg> >::iterator j;
+ for (j = profile->saved_registers.begin(); j != profile->saved_registers.end(); ++j) {
+ char *tbuf1, *tbuf2, *tbuf3;
+ asprintf (&tbuf1, " at pc 0x%llx there are %d registers saved ", j->first, (int) j->second.size());
+ std::vector<RemoteUnwindProfile::SavedReg>::iterator k;
+ for (k = j->second.begin(); k != j->second.end(); ++k) {
+ if (k->location == RemoteUnwindProfile::kRegisterOffsetFromCFA) {
+ asprintf (&tbuf2, "[reg %d (%s) is %d bytes from cfa] ", k->regno, regmap->unwind_regno_to_name(k->regno), (int) k->value);
+ int newlen = strlen (tbuf1) + strlen (tbuf2) + 1;
+ tbuf3 = (char *) malloc (newlen);
+ strcpy (tbuf3, tbuf1);
+ strcat (tbuf3, tbuf2);
+ free (tbuf1);
+ free (tbuf2);
+ tbuf1 = tbuf3;
+ }
+ if (k->location == RemoteUnwindProfile::kRegisterIsCFA) {
+ asprintf (&tbuf2, "[reg %d (%s) is the same as the cfa] ", k->regno, regmap->unwind_regno_to_name(k->regno));
+ int newlen = strlen (tbuf1) + strlen (tbuf2) + 1;
+ tbuf3 = (char *) malloc (newlen);
+ strcpy (tbuf3, tbuf1);
+ strcat (tbuf3, tbuf2);
+ free (tbuf1);
+ free (tbuf2);
+ tbuf1 = tbuf3;
+ }
+ }
+ procinfo->logDebug ("%s", tbuf1);
+ free (tbuf1);
+ }
+}
+
+template <typename A, typename R>
+int stepWithAssembly (A& addressSpace, uint64_t pc, RemoteUnwindProfile* profile, R& registers) {
+ R newRegisters(registers);
+ RemoteProcInfo *procinfo = addressSpace.getRemoteProcInfo();
+ if (pc > profile->fEnd)
+ ABORT("stepWithAssembly called with pc not in RemoteUnwindProfile's bounds");
+
+ if (procinfo && (procinfo->getDebugLoggingLevel() & UNW_LOG_LEVEL_DEBUG))
+ printProfile (addressSpace, pc, profile, registers);
+
+ std::map<uint64_t, RemoteUnwindProfile::CFALocation>::iterator i = profile->cfa.lower_bound (pc);
+ if (i == profile->cfa.begin() && i == profile->cfa.end())
+ return UNW_EINVAL;
+ if (i == profile->cfa.end()) {
+ --i;
+ } else {
+ if (i != profile->cfa.begin() && i->first != pc)
+ --i;
+ }
+
+ uint64_t cfa = registers.getRegister (i->second.regno) + i->second.offset;
+
+ std::map<uint64_t, std::vector<RemoteUnwindProfile::SavedReg> >::iterator j;
+
+ for (j = profile->saved_registers.begin(); j != profile->saved_registers.end() && j->first <= pc; ++j) {
+ std::vector<RemoteUnwindProfile::SavedReg>::iterator k = j->second.begin();
+ for (; k != j->second.end(); ++k) {
+ RemoteUnwindProfile::SavedReg sr = *k;
+ if (sr.type == RemoteUnwindProfile::kGeneralPurposeRegister) {
+ uint64_t result;
+ int err = 0;
+ switch (sr.location) {
+ case RemoteUnwindProfile::kRegisterOffsetFromCFA:
+ result = addressSpace.getP(cfa + sr.value, err);
+ break;
+ case RemoteUnwindProfile::kRegisterIsCFA:
+ result = cfa;
+ break;
+ default:
+ ABORT("Unknown saved register location in stepWithAssembly.");
+ }
+ // If we failed to read remote memory, stop unwinding.
+ if (err)
+ return UNW_STEP_END;
+ newRegisters.setRegister (sr.regno, result);
+ }
+ }
+ }
+ newRegisters.setSP(cfa);
+ uint64_t ip = addressSpace.getP(cfa + profile->returnAddress.value);
+ if (ip == 0)
+ return UNW_STEP_END;
+ newRegisters.setIP(ip);
+ registers = newRegisters;
+ return UNW_STEP_SUCCESS;
+}
+
+}; // namespace lldb_private
+
+#endif // SUPPORT_REMOTE_UNWINDING
+#endif //ASSEMBLY_INSTRUCTIONS_HPP
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyParser.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyParser.hpp
new file mode 100644
index 00000000000..b34f93f50c6
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/AssemblyParser.hpp
@@ -0,0 +1,409 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- AssemblyParser.hpp --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// Disassemble the prologue instructions in functions, create a profile
+// of stack movements and register saves performed therein.
+
+#ifndef __ASSEMBLY_PARSER_HPP
+#define __ASSEMBLY_PARSER_HPP
+
+#if defined (SUPPORT_REMOTE_UNWINDING)
+
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS
+#endif
+#ifndef __STDC_CONSTANT_MACROS
+#define __STDC_CONSTANT_MACROS
+#endif
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <map>
+#include <vector>
+
+#include "libunwind.h"
+#include "RemoteProcInfo.hpp"
+#include "Registers.hpp"
+#include "FileAbstraction.hpp"
+#include "AddressSpace.hpp"
+#include "RemoteUnwindProfile.h"
+
+namespace lldb_private
+{
+
+// Analyze the instructions in an x86_64/i386 function prologue, fill out an RemoteUnwindProfile.
+
+class AssemblyParse_x86 {
+public:
+ AssemblyParse_x86 (RemoteProcInfo& procinfo, unw_accessors_t *acc, unw_addr_space_t as, void *arg) : fArg(arg), fAccessors(acc), fAs(as), fRemoteProcInfo(procinfo) {
+ fRegisterMap = fRemoteProcInfo.getRegisterMap();
+ if (fRemoteProcInfo.getTargetArch() == UNW_TARGET_X86_64) {
+ fStackPointerRegnum = UNW_X86_64_RSP;
+ fFramePointerRegnum = UNW_X86_64_RBP;
+ fWordSize = 8;
+ } else {
+ fStackPointerRegnum = UNW_X86_ESP;
+ fFramePointerRegnum = UNW_X86_EBP;
+ fWordSize = 4;
+ }
+ }
+
+ uint32_t extract_4_LE (uint8_t *b) {
+ uint32_t v = 0;
+ for (int i = 3; i >= 0; i--)
+ v = (v << 8) | b[i];
+ return v;
+ }
+
+ bool push_rbp_pattern_p ();
+ bool push_0_pattern_p ();
+ bool mov_rsp_rbp_pattern_p ();
+ bool sub_rsp_pattern_p (int *amount);
+ bool push_reg_p (int *regno);
+ bool mov_reg_to_local_stack_frame_p (int *regno, int *rbp_offset);
+ bool ret_pattern_p ();
+ bool profileFunction (uint64_t start, uint64_t end, RemoteUnwindProfile& profile);
+
+private:
+
+ void *fArg;
+ uint8_t* fCurInsnByteBuf;
+ int fCurInsnSize;
+ RemoteProcInfo& fRemoteProcInfo;
+ RemoteRegisterMap *fRegisterMap;
+ unw_accessors_t *fAccessors;
+ unw_addr_space_t fAs;
+ int fWordSize;
+ int fStackPointerRegnum;
+ int fFramePointerRegnum;
+};
+
+// Macro to detect if this is a REX mode prefix byte.
+#define REX_W_PREFIX_P(opcode) (((opcode) & (~0x5)) == 0x48)
+
+// The high bit which should be added to the source register number (the "R" bit)
+#define REX_W_SRCREG(opcode) (((opcode) & 0x4) >> 2)
+
+// The high bit which should be added to the destination register number (the "B" bit)
+#define REX_W_DSTREG(opcode) ((opcode) & 0x1)
+
+// pushq %rbp [0x55]
+bool AssemblyParse_x86::push_rbp_pattern_p () {
+ uint8_t *p = fCurInsnByteBuf;
+ if (*p == 0x55)
+ return true;
+ return false;
+}
+
+// pushq $0 ; the first instruction in start() [0x6a 0x00]
+bool AssemblyParse_x86::push_0_pattern_p ()
+{
+ uint8_t *p = fCurInsnByteBuf;
+ if (*p == 0x6a && *(p + 1) == 0x0)
+ return true;
+ return false;
+}
+
+// movq %rsp, %rbp [0x48 0x8b 0xec] or [0x48 0x89 0xe5]
+// movl %esp, %ebp [0x8b 0xec] or [0x89 0xe5]
+bool AssemblyParse_x86::mov_rsp_rbp_pattern_p () {
+ uint8_t *p = fCurInsnByteBuf;
+ if (fWordSize == 8 && *p == 0x48)
+ p++;
+ if (*(p) == 0x8b && *(p + 1) == 0xec)
+ return true;
+ if (*(p) == 0x89 && *(p + 1) == 0xe5)
+ return true;
+ return false;
+}
+
+// subq $0x20, %rsp
+bool AssemblyParse_x86::sub_rsp_pattern_p (int *amount) {
+ uint8_t *p = fCurInsnByteBuf;
+ if (fWordSize == 8 && *p == 0x48)
+ p++;
+ // 8-bit immediate operand
+ if (*p == 0x83 && *(p + 1) == 0xec) {
+ *amount = (int8_t) *(p + 2);
+ return true;
+ }
+ // 32-bit immediate operand
+ if (*p == 0x81 && *(p + 1) == 0xec) {
+ *amount = (int32_t) extract_4_LE (p + 2);
+ return true;
+ }
+ // Not handled: [0x83 0xc4] for imm8 with neg values
+ // [0x81 0xc4] for imm32 with neg values
+ return false;
+}
+
+// pushq %rbx
+// pushl $ebx
+bool AssemblyParse_x86::push_reg_p (int *regno) {
+ uint8_t *p = fCurInsnByteBuf;
+ int regno_prefix_bit = 0;
+ // If we have a rex prefix byte, check to see if a B bit is set
+ if (fWordSize == 8 && *p == 0x41) {
+ regno_prefix_bit = 1 << 3;
+ p++;
+ }
+ if (*p >= 0x50 && *p <= 0x57) {
+ int r = (*p - 0x50) | regno_prefix_bit;
+ if (fRegisterMap->machine_regno_to_unwind_regno (r, *regno) == true) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Look for an instruction sequence storing a nonvolatile register
+// on to the stack frame.
+
+// movq %rax, -0x10(%rbp) [0x48 0x89 0x45 0xf0]
+// movl %eax, -0xc(%ebp) [0x89 0x45 0xf4]
+bool AssemblyParse_x86::mov_reg_to_local_stack_frame_p (int *regno, int *rbp_offset) {
+ uint8_t *p = fCurInsnByteBuf;
+ int src_reg_prefix_bit = 0;
+ int target_reg_prefix_bit = 0;
+
+ if (fWordSize == 8 && REX_W_PREFIX_P (*p)) {
+ src_reg_prefix_bit = REX_W_SRCREG (*p) << 3;
+ target_reg_prefix_bit = REX_W_DSTREG (*p) << 3;
+ if (target_reg_prefix_bit == 1) {
+ // rbp/ebp don't need a prefix bit - we know this isn't the
+ // reg we care about.
+ return false;
+ }
+ p++;
+ }
+
+ if (*p == 0x89) {
+ /* Mask off the 3-5 bits which indicate the destination register
+ if this is a ModR/M byte. */
+ int opcode_destreg_masked_out = *(p + 1) & (~0x38);
+
+ /* Is this a ModR/M byte with Mod bits 01 and R/M bits 101
+ and three bits between them, e.g. 01nnn101
+ We're looking for a destination of ebp-disp8 or ebp-disp32. */
+ int immsize;
+ if (opcode_destreg_masked_out == 0x45)
+ immsize = 2;
+ else if (opcode_destreg_masked_out == 0x85)
+ immsize = 4;
+ else
+ return false;
+
+ int offset = 0;
+ if (immsize == 2)
+ offset = (int8_t) *(p + 2);
+ if (immsize == 4)
+ offset = (uint32_t) extract_4_LE (p + 2);
+ if (offset > 0)
+ return false;
+
+ int savedreg = ((*(p + 1) >> 3) & 0x7) | src_reg_prefix_bit;
+ if (fRegisterMap->machine_regno_to_unwind_regno (savedreg, *regno) == true) {
+ *rbp_offset = offset > 0 ? offset : -offset;
+ return true;
+ }
+ }
+ return false;
+}
+
+// ret [0xc9] or [0xc2 imm8] or [0xca imm8]
+bool AssemblyParse_x86::ret_pattern_p () {
+ uint8_t *p = fCurInsnByteBuf;
+ if (*p == 0xc9 || *p == 0xc2 || *p == 0xca || *p == 0xc3)
+ return true;
+ return false;
+}
+
+bool AssemblyParse_x86::profileFunction (uint64_t start, uint64_t end, RemoteUnwindProfile& profile) {
+ if (start == -1 || end == 0)
+ return false;
+
+ profile.fStart = start;
+ profile.fEnd = end;
+ profile.fRegSizes[RemoteUnwindProfile::kGeneralPurposeRegister] = fWordSize;
+ profile.fRegSizes[RemoteUnwindProfile::kFloatingPointRegister] = 8;
+ profile.fRegSizes[RemoteUnwindProfile::kVectorRegister] = 16;
+
+ // On function entry, the CFA is rsp+fWordSize
+
+ RemoteUnwindProfile::CFALocation initial_cfaloc;
+ initial_cfaloc.regno = fStackPointerRegnum;
+ initial_cfaloc.offset = fWordSize;
+ profile.cfa[start] = initial_cfaloc;
+
+ // The return address is at CFA - fWordSize
+ // CFA doesn't change value during the lifetime of the function (hence "C")
+ // so the returnAddress is the same for the duration of the function.
+
+ profile.returnAddress.regno = 0;
+ profile.returnAddress.location = RemoteUnwindProfile::kRegisterOffsetFromCFA;
+ profile.returnAddress.value = -fWordSize;
+ profile.returnAddress.adj = 0;
+ profile.returnAddress.type = RemoteUnwindProfile::kGeneralPurposeRegister;
+
+ // The caller's rsp has the same value as the CFA at all points during
+ // this function's lifetime.
+
+ RemoteUnwindProfile::SavedReg rsp_loc;
+ rsp_loc.regno = fStackPointerRegnum;
+ rsp_loc.location = RemoteUnwindProfile::kRegisterIsCFA;
+ rsp_loc.value = 0;
+ rsp_loc.adj = 0;
+ rsp_loc.type = RemoteUnwindProfile::kGeneralPurposeRegister;
+ profile.saved_registers[start].push_back(rsp_loc);
+ profile.fRegistersSaved[fStackPointerRegnum] = 1;
+
+ int non_prologue_insn_count = 0;
+ int insn_count = 0;
+ uint64_t cur_addr = start;
+ uint64_t first_insn_past_prologue = start;
+ int push_rbp_seen = 0;
+ int current_cfa_register = fStackPointerRegnum;
+ int sp_adjustments = 0;
+
+ while (cur_addr < end && non_prologue_insn_count < 10)
+ {
+ int offset, regno;
+ uint64_t next_addr;
+ insn_count++;
+ int is_prologue_insn = 0;
+
+ if (fAccessors->instruction_length (fAs, cur_addr, &fCurInsnSize, fArg) != 0) {
+ /* An error parsing the instruction; stop scanning. */
+ break;
+ }
+ fCurInsnByteBuf = (uint8_t *) malloc (fCurInsnSize);
+ if (fRemoteProcInfo.getBytes (cur_addr, fCurInsnSize, fCurInsnByteBuf, fArg) == 0)
+ return false;
+ next_addr = cur_addr + fCurInsnSize;
+
+ // start () opens with a 'push $0x0' which is in the saved ip slot on the stack -
+ // so we know to stop backtracing here. We need to ignore this instruction.
+ if (push_0_pattern_p () && push_rbp_seen == 0 && insn_count == 1)
+ {
+ cur_addr = next_addr;
+ first_insn_past_prologue = next_addr;
+ continue;
+ }
+
+ if (push_rbp_pattern_p () && push_rbp_seen == 0)
+ {
+ if (current_cfa_register == fStackPointerRegnum) {
+ sp_adjustments -= fWordSize;
+ RemoteUnwindProfile::CFALocation cfaloc;
+ cfaloc.regno = fStackPointerRegnum;
+ cfaloc.offset = abs (sp_adjustments - fWordSize);
+ profile.cfa[next_addr] = cfaloc;
+ }
+
+ RemoteUnwindProfile::SavedReg sreg;
+ sreg.regno = fFramePointerRegnum;
+ sreg.location = RemoteUnwindProfile::kRegisterOffsetFromCFA;
+ sreg.value = sp_adjustments - fWordSize;
+ sreg.adj = 0;
+ sreg.type = RemoteUnwindProfile::kGeneralPurposeRegister;
+ profile.saved_registers[next_addr].push_back(sreg);
+
+ push_rbp_seen = 1;
+ profile.fRegistersSaved[fFramePointerRegnum] = 1;
+ is_prologue_insn = 1;
+ goto next_iteration;
+ }
+ if (mov_rsp_rbp_pattern_p ()) {
+ RemoteUnwindProfile::CFALocation cfaloc;
+ cfaloc.regno = fFramePointerRegnum;
+ cfaloc.offset = abs (sp_adjustments - fWordSize);
+ profile.cfa[next_addr] = cfaloc;
+ current_cfa_register = fFramePointerRegnum;
+ is_prologue_insn = 1;
+ goto next_iteration;
+ }
+ if (ret_pattern_p ()) {
+ break;
+ }
+ if (sub_rsp_pattern_p (&offset)) {
+ sp_adjustments -= offset;
+ if (current_cfa_register == fStackPointerRegnum) {
+ RemoteUnwindProfile::CFALocation cfaloc;
+ cfaloc.regno = fStackPointerRegnum;
+ cfaloc.offset = abs (sp_adjustments - fWordSize);
+ profile.cfa[next_addr] = cfaloc;
+ }
+ is_prologue_insn = 1;
+ }
+ if (push_reg_p (&regno)) {
+ sp_adjustments -= fWordSize;
+ if (current_cfa_register == fStackPointerRegnum) {
+ RemoteUnwindProfile::CFALocation cfaloc;
+ cfaloc.regno = fStackPointerRegnum;
+ cfaloc.offset = abs (sp_adjustments - fWordSize);
+ profile.cfa[next_addr] = cfaloc;
+ is_prologue_insn = 1;
+ }
+ if (fRegisterMap->nonvolatile_reg_p (regno) && profile.fRegistersSaved[regno] == 0) {
+ RemoteUnwindProfile::SavedReg sreg;
+ sreg.regno = regno;
+ sreg.location = RemoteUnwindProfile::kRegisterOffsetFromCFA;
+ sreg.value = sp_adjustments - fWordSize;
+ sreg.adj = 0;
+ sreg.type = RemoteUnwindProfile::kGeneralPurposeRegister;
+ profile.saved_registers[next_addr].push_back(sreg);
+ profile.fRegistersSaved[regno] = 1;
+ is_prologue_insn = 1;
+ }
+ }
+ if (mov_reg_to_local_stack_frame_p (&regno, &offset)
+ && fRegisterMap->nonvolatile_reg_p (regno)
+ && profile.fRegistersSaved[regno] == 0) {
+ RemoteUnwindProfile::SavedReg sreg;
+ sreg.regno = regno;
+ sreg.location = RemoteUnwindProfile::kRegisterOffsetFromCFA;
+ sreg.value = offset - fWordSize;
+ sreg.adj = 0;
+ sreg.type = RemoteUnwindProfile::kGeneralPurposeRegister;
+ profile.saved_registers[next_addr].push_back(sreg);
+ profile.fRegistersSaved[regno] = 1;
+ is_prologue_insn = 1;
+ }
+next_iteration:
+ if (is_prologue_insn) {
+ first_insn_past_prologue = next_addr;
+ non_prologue_insn_count = 0;
+ }
+ cur_addr = next_addr;
+ non_prologue_insn_count++;
+ }
+ profile.fFirstInsnPastPrologue = first_insn_past_prologue;
+ return true;
+}
+
+
+
+
+bool AssemblyParse (RemoteProcInfo *procinfo, unw_accessors_t *acc, unw_addr_space_t as, uint64_t start, uint64_t end, RemoteUnwindProfile &profile, void *arg) {
+ if (procinfo->getTargetArch() == UNW_TARGET_X86_64 || procinfo->getTargetArch() == UNW_TARGET_I386) {
+ AssemblyParse_x86 parser(*procinfo, acc, as, arg);
+ return parser.profileFunction (start, end, profile);
+ } else {
+ ABORT("Only x86_64 and i386 assembly parsing supported at this time");
+ return false;
+ }
+}
+
+}; // namespace lldb_private
+
+#endif // SUPPORT_REMOTE_UNWINDING
+#endif //ASSEMBLY_PARSER_HPP
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/CompactUnwinder.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/CompactUnwinder.hpp
new file mode 100644
index 00000000000..dda2308ada6
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/CompactUnwinder.hpp
@@ -0,0 +1,1019 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- CompactUnwinder.hpp -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+//
+// C++ interface to lower levels of libuwind
+//
+
+#ifndef __COMPACT_UNWINDER_HPP__
+#define __COMPACT_UNWINDER_HPP__
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <libunwind.h>
+#include <mach-o/compact_unwind_encoding.h>
+
+#include "AddressSpace.hpp"
+#include "Registers.hpp"
+
+
+
+#define EXTRACT_BITS(value, mask) \
+ ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) )
+
+#define SUPPORT_OLD_BINARIES 0
+
+namespace lldb_private {
+
+
+
+///
+/// CompactUnwinder_x86 uses a compact unwind info to virtually "step" (aka unwind) by
+/// modifying a Registers_x86 register set
+///
+template <typename A>
+class CompactUnwinder_x86
+{
+public:
+
+ static int stepWithCompactEncoding(compact_unwind_encoding_t info, uint32_t functionStart, A& addressSpace, Registers_x86& registers);
+
+private:
+ typename A::pint_t pint_t;
+
+ static void frameUnwind(A& addressSpace, Registers_x86& registers);
+ static void framelessUnwind(A& addressSpace, typename A::pint_t returnAddressLocation, Registers_x86& registers);
+ static int stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, A& addressSpace, Registers_x86& registers);
+ static int stepWithCompactEncodingFrameless(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, A& addressSpace, Registers_x86& registers, bool indirectStackSize);
+#if SUPPORT_OLD_BINARIES
+ static int stepWithCompactEncodingCompat(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, A& addressSpace, Registers_x86& registers);
+#endif
+};
+
+
+
+template <typename A>
+int CompactUnwinder_x86<A>::stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, A& addressSpace, Registers_x86& registers)
+{
+ //fprintf(stderr, "stepWithCompactEncoding(0x%08X)\n", compactEncoding);
+ switch ( compactEncoding & UNWIND_X86_MODE_MASK ) {
+#if SUPPORT_OLD_BINARIES
+ case UNWIND_X86_MODE_COMPATIBILITY:
+ return stepWithCompactEncodingCompat(compactEncoding, functionStart, addressSpace, registers);
+#endif
+ case UNWIND_X86_MODE_EBP_FRAME:
+ return stepWithCompactEncodingEBPFrame(compactEncoding, functionStart, addressSpace, registers);
+ case UNWIND_X86_MODE_STACK_IMMD:
+ return stepWithCompactEncodingFrameless(compactEncoding, functionStart, addressSpace, registers, false);
+ case UNWIND_X86_MODE_STACK_IND:
+ return stepWithCompactEncodingFrameless(compactEncoding, functionStart, addressSpace, registers, true);
+ }
+ ABORT("invalid compact unwind encoding");
+}
+
+
+template <typename A>
+int CompactUnwinder_x86<A>::stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
+ A& addressSpace, Registers_x86& registers)
+{
+ uint32_t savedRegistersOffset = EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_OFFSET);
+ uint32_t savedRegistersLocations = EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_REGISTERS);
+
+ uint64_t savedRegisters = registers.getEBP() - 4*savedRegistersOffset;
+ for (int i=0; i < 5; ++i) {
+ switch (savedRegistersLocations & 0x7) {
+ case UNWIND_X86_REG_NONE:
+ // no register saved in this slot
+ break;
+ case UNWIND_X86_REG_EBX:
+ registers.setEBX(addressSpace.get32(savedRegisters));
+ break;
+ case UNWIND_X86_REG_ECX:
+ registers.setECX(addressSpace.get32(savedRegisters));
+ break;
+ case UNWIND_X86_REG_EDX:
+ registers.setEDX(addressSpace.get32(savedRegisters));
+ break;
+ case UNWIND_X86_REG_EDI:
+ registers.setEDI(addressSpace.get32(savedRegisters));
+ break;
+ case UNWIND_X86_REG_ESI:
+ registers.setESI(addressSpace.get32(savedRegisters));
+ break;
+ default:
+ DEBUG_MESSAGE("bad register for EBP frame, encoding=%08X for function starting at 0x%X\n", compactEncoding, functionStart);
+ ABORT("invalid compact unwind encoding");
+ }
+ savedRegisters += 4;
+ savedRegistersLocations = (savedRegistersLocations >> 3);
+ }
+ frameUnwind(addressSpace, registers);
+ return UNW_STEP_SUCCESS;
+}
+
+
+template <typename A>
+int CompactUnwinder_x86<A>::stepWithCompactEncodingFrameless(compact_unwind_encoding_t encoding, uint32_t functionStart,
+ A& addressSpace, Registers_x86& registers, bool indirectStackSize)
+{
+ uint32_t stackSizeEncoded = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);
+ uint32_t stackAdjust = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST);
+ uint32_t regCount = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT);
+ uint32_t permutation = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION);
+ uint32_t stackSize = stackSizeEncoded*4;
+ if ( indirectStackSize ) {
+ // stack size is encoded in subl $xxx,%esp instruction
+ uint32_t subl = addressSpace.get32(functionStart+stackSizeEncoded);
+ stackSize = subl + 4*stackAdjust;
+ }
+ // decompress permutation
+ int permunreg[6];
+ switch ( regCount ) {
+ case 6:
+ permunreg[0] = permutation/120;
+ permutation -= (permunreg[0]*120);
+ permunreg[1] = permutation/24;
+ permutation -= (permunreg[1]*24);
+ permunreg[2] = permutation/6;
+ permutation -= (permunreg[2]*6);
+ permunreg[3] = permutation/2;
+ permutation -= (permunreg[3]*2);
+ permunreg[4] = permutation;
+ permunreg[5] = 0;
+ break;
+ case 5:
+ permunreg[0] = permutation/120;
+ permutation -= (permunreg[0]*120);
+ permunreg[1] = permutation/24;
+ permutation -= (permunreg[1]*24);
+ permunreg[2] = permutation/6;
+ permutation -= (permunreg[2]*6);
+ permunreg[3] = permutation/2;
+ permutation -= (permunreg[3]*2);
+ permunreg[4] = permutation;
+ break;
+ case 4:
+ permunreg[0] = permutation/60;
+ permutation -= (permunreg[0]*60);
+ permunreg[1] = permutation/12;
+ permutation -= (permunreg[1]*12);
+ permunreg[2] = permutation/3;
+ permutation -= (permunreg[2]*3);
+ permunreg[3] = permutation;
+ break;
+ case 3:
+ permunreg[0] = permutation/20;
+ permutation -= (permunreg[0]*20);
+ permunreg[1] = permutation/4;
+ permutation -= (permunreg[1]*4);
+ permunreg[2] = permutation;
+ break;
+ case 2:
+ permunreg[0] = permutation/5;
+ permutation -= (permunreg[0]*5);
+ permunreg[1] = permutation;
+ break;
+ case 1:
+ permunreg[0] = permutation;
+ break;
+ }
+ // re-number registers back to standard numbers
+ int registersSaved[6];
+ bool used[7] = { false, false, false, false, false, false, false };
+ for (uint32_t i=0; i < regCount; ++i) {
+ int renum = 0;
+ for (int u=1; u < 7; ++u) {
+ if ( !used[u] ) {
+ if ( renum == permunreg[i] ) {
+ registersSaved[i] = u;
+ used[u] = true;
+ break;
+ }
+ ++renum;
+ }
+ }
+ }
+ uint64_t savedRegisters = registers.getSP() + stackSize - 4 - 4*regCount;
+ for (uint32_t i=0; i < regCount; ++i) {
+ switch ( registersSaved[i] ) {
+ case UNWIND_X86_REG_EBX:
+ registers.setEBX(addressSpace.get32(savedRegisters));
+ break;
+ case UNWIND_X86_REG_ECX:
+ registers.setECX(addressSpace.get32(savedRegisters));
+ break;
+ case UNWIND_X86_REG_EDX:
+ registers.setEDX(addressSpace.get32(savedRegisters));
+ break;
+ case UNWIND_X86_REG_EDI:
+ registers.setEDI(addressSpace.get32(savedRegisters));
+ break;
+ case UNWIND_X86_REG_ESI:
+ registers.setESI(addressSpace.get32(savedRegisters));
+ break;
+ case UNWIND_X86_REG_EBP:
+ registers.setEBP(addressSpace.get32(savedRegisters));
+ break;
+ default:
+ DEBUG_MESSAGE("bad register for frameless, encoding=%08X for function starting at 0x%X\n", encoding, functionStart);
+ ABORT("invalid compact unwind encoding");
+ }
+ savedRegisters += 4;
+ }
+ framelessUnwind(addressSpace, savedRegisters, registers);
+ return UNW_STEP_SUCCESS;
+}
+
+
+#if SUPPORT_OLD_BINARIES
+template <typename A>
+int CompactUnwinder_x86<A>::stepWithCompactEncodingCompat(compact_unwind_encoding_t compactEncoding, uint32_t functionStart, A& addressSpace, Registers_x86& registers)
+{
+ //fprintf(stderr, "stepWithCompactEncoding(0x%08X)\n", compactEncoding);
+ typename A::pint_t savedRegisters;
+ uint32_t stackValue = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_SIZE);
+ uint32_t stackSize;
+ uint32_t stackAdjust;
+ switch (compactEncoding & UNWIND_X86_CASE_MASK ) {
+ case UNWIND_X86_UNWIND_INFO_UNSPECIFIED:
+ return UNW_ENOINFO;
+
+ case UNWIND_X86_EBP_FRAME_NO_REGS:
+ frameUnwind(addressSpace, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_EBP_FRAME_EBX:
+ savedRegisters = registers.getEBP() - 4;
+ registers.setEBX(addressSpace.get32(savedRegisters));
+ frameUnwind(addressSpace, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_EBP_FRAME_ESI:
+ savedRegisters = registers.getEBP() - 4;
+ registers.setESI(addressSpace.get32(savedRegisters));
+ frameUnwind(addressSpace, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_EBP_FRAME_EDI:
+ savedRegisters = registers.getEBP() - 4;
+ registers.setEDI(addressSpace.get32(savedRegisters));
+ frameUnwind(addressSpace, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_EBP_FRAME_EBX_ESI:
+ savedRegisters = registers.getEBP() - 8;
+ registers.setEBX(addressSpace.get32(savedRegisters));
+ registers.setESI(addressSpace.get32(savedRegisters+4));
+ frameUnwind(addressSpace, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_EBP_FRAME_ESI_EDI:
+ savedRegisters = registers.getEBP() - 8;
+ registers.setESI(addressSpace.get32(savedRegisters));
+ registers.setEDI(addressSpace.get32(savedRegisters+4));
+ frameUnwind(addressSpace, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_EBP_FRAME_EBX_ESI_EDI:
+ savedRegisters = registers.getEBP() - 12;
+ registers.setEBX(addressSpace.get32(savedRegisters));
+ registers.setESI(addressSpace.get32(savedRegisters+4));
+ registers.setEDI(addressSpace.get32(savedRegisters+8));
+ frameUnwind(addressSpace, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_EBP_FRAME_EBX_EDI:
+ savedRegisters = registers.getEBP() - 8;
+ registers.setEBX(addressSpace.get32(savedRegisters));
+ registers.setEDI(addressSpace.get32(savedRegisters+4));
+ frameUnwind(addressSpace, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_IMM_STK_NO_REGS:
+ stackSize = stackValue * 4;
+ savedRegisters = registers.getSP() + stackSize - 4 - 4*0;
+ framelessUnwind(addressSpace, savedRegisters+4*0, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_IMM_STK_EBX:
+ stackSize = stackValue * 4;
+ savedRegisters = registers.getSP() + stackSize - 4 - 4*1;
+ registers.setEBX(addressSpace.get32(savedRegisters));
+ framelessUnwind(addressSpace, savedRegisters+4*1, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_IMM_STK_ESI:
+ stackSize = stackValue * 4;
+ savedRegisters = registers.getSP() + stackSize - 4 - 4*1;
+ registers.setESI(addressSpace.get32(savedRegisters));
+ framelessUnwind(addressSpace, savedRegisters+4*1, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_IMM_STK_EDI:
+ stackSize = stackValue * 4;
+ savedRegisters = registers.getSP() + stackSize - 4 - 4*1;
+ registers.setEDI(addressSpace.get32(savedRegisters));
+ framelessUnwind(addressSpace, savedRegisters+4*1, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_IMM_STK_EBX_ESI:
+ stackSize = stackValue * 4;
+ savedRegisters = registers.getSP() + stackSize - 4 - 4*2;
+ registers.setEBX(addressSpace.get32(savedRegisters));
+ registers.setESI(addressSpace.get32(savedRegisters+4));
+ framelessUnwind(addressSpace, savedRegisters+4*2, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_IMM_STK_ESI_EDI:
+ stackSize = stackValue * 4;
+ savedRegisters = registers.getSP() + stackSize - 4 - 4*2;
+ registers.setESI(addressSpace.get32(savedRegisters));
+ registers.setEDI(addressSpace.get32(savedRegisters+4));
+ framelessUnwind(addressSpace, savedRegisters+4*2, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_IMM_STK_ESI_EDI_EBP:
+ stackSize = stackValue * 4;
+ savedRegisters = registers.getSP() + stackSize - 4 - 4*3;
+ registers.setESI(addressSpace.get32(savedRegisters));
+ registers.setEDI(addressSpace.get32(savedRegisters+4));
+ registers.setEBP(addressSpace.get32(savedRegisters+8));
+ framelessUnwind(addressSpace, savedRegisters+4*3, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_IMM_STK_EBX_ESI_EDI:
+ stackSize = stackValue * 4;
+ savedRegisters = registers.getSP() + stackSize - 4 - 4*3;
+ registers.setEBX(addressSpace.get32(savedRegisters));
+ registers.setESI(addressSpace.get32(savedRegisters+4));
+ registers.setEDI(addressSpace.get32(savedRegisters+8));
+ framelessUnwind(addressSpace, savedRegisters+4*3, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_IMM_STK_EBX_ESI_EDI_EBP:
+ stackSize = stackValue * 4;
+ savedRegisters = registers.getSP() + stackSize - 4 - 4*4;
+ registers.setEBX(addressSpace.get32(savedRegisters));
+ registers.setESI(addressSpace.get32(savedRegisters+4));
+ registers.setEDI(addressSpace.get32(savedRegisters+8));
+ registers.setEBP(addressSpace.get32(savedRegisters+12));
+ framelessUnwind(addressSpace, savedRegisters+4*4, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_IND_STK_NO_REGS:
+ stackSize = addressSpace.get32(functionStart+stackValue);
+ stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST);
+ stackSize += stackAdjust*4;
+ savedRegisters = registers.getSP() + stackSize - 4 - 4*0;
+ framelessUnwind(addressSpace, savedRegisters+4*0, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_IND_STK_EBX:
+ stackSize = addressSpace.get32(functionStart+stackValue);
+ stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST);
+ stackSize += stackAdjust*4;
+ savedRegisters = registers.getSP() + stackSize - 4 - 4*1;
+ registers.setEBX(addressSpace.get32(savedRegisters));
+ framelessUnwind(addressSpace, savedRegisters+4*1, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_IND_STK_ESI:
+ stackSize = addressSpace.get32(functionStart+stackValue);
+ stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST);
+ stackSize += stackAdjust*4;
+ savedRegisters = registers.getSP() + stackSize - 4 - 4*1;
+ registers.setESI(addressSpace.get32(savedRegisters));
+ framelessUnwind(addressSpace, savedRegisters+4*1, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_IND_STK_EDI:
+ stackSize = addressSpace.get32(functionStart+stackValue);
+ stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST);
+ stackSize += stackAdjust*4;
+ savedRegisters = registers.getSP() + stackSize - 4 - 4*1;
+ registers.setEDI(addressSpace.get32(savedRegisters));
+ return UNW_STEP_SUCCESS;
+ framelessUnwind(addressSpace, savedRegisters+4*1, registers);
+
+ case UNWIND_X86_IND_STK_EBX_ESI:
+ stackSize = addressSpace.get32(functionStart+stackValue);
+ stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST);
+ stackSize += stackAdjust*4;
+ savedRegisters = registers.getSP() + stackSize - 4 - 4*2;
+ registers.setEBX(addressSpace.get32(savedRegisters));
+ registers.setESI(addressSpace.get32(savedRegisters+4));
+ framelessUnwind(addressSpace, savedRegisters+4*2, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_IND_STK_ESI_EDI:
+ stackSize = addressSpace.get32(functionStart+stackValue);
+ stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST);
+ stackSize += stackAdjust*4;
+ savedRegisters = registers.getSP() + stackSize - 4 - 4*2;
+ registers.setESI(addressSpace.get32(savedRegisters));
+ registers.setEDI(addressSpace.get32(savedRegisters+4));
+ framelessUnwind(addressSpace, savedRegisters+4*2, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_IND_STK_ESI_EDI_EBP:
+ stackSize = addressSpace.get32(functionStart+stackValue);
+ stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST);
+ stackSize += stackAdjust*4;
+ savedRegisters = registers.getSP() + stackSize - 4 - 4*3;
+ registers.setESI(addressSpace.get32(savedRegisters));
+ registers.setEDI(addressSpace.get32(savedRegisters+4));
+ registers.setEBP(addressSpace.get32(savedRegisters+8));
+ framelessUnwind(addressSpace, savedRegisters+4*3, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_IND_STK_EBX_ESI_EDI:
+ stackSize = addressSpace.get32(functionStart+stackValue);
+ stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST);
+ stackSize += stackAdjust*4;
+ savedRegisters = registers.getSP() + stackSize - 4 - 4*3;
+ registers.setEBX(addressSpace.get32(savedRegisters));
+ registers.setESI(addressSpace.get32(savedRegisters+4));
+ registers.setEDI(addressSpace.get32(savedRegisters+8));
+ framelessUnwind(addressSpace, savedRegisters+4*3, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_IND_STK_EBX_ESI_EDI_EBP:
+ stackSize = addressSpace.get32(functionStart+stackValue);
+ stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_STACK_ADJUST);
+ stackSize += stackAdjust*4;
+ savedRegisters = registers.getSP() + stackSize - 4 - 4*4;
+ registers.setEBX(addressSpace.get32(savedRegisters));
+ registers.setESI(addressSpace.get32(savedRegisters+4));
+ registers.setEDI(addressSpace.get32(savedRegisters+8));
+ registers.setEBP(addressSpace.get32(savedRegisters+12));
+ framelessUnwind(addressSpace, savedRegisters+4*4, registers);
+ return UNW_STEP_SUCCESS;
+
+ default:
+ DEBUG_MESSAGE("unknown compact unwind encoding %08X for function starting at 0x%X\n",
+ compactEncoding & UNWIND_X86_CASE_MASK, functionStart);
+ ABORT("unknown compact unwind encoding");
+ }
+ return UNW_EINVAL;
+}
+#endif // SUPPORT_OLD_BINARIES
+
+
+
+template <typename A>
+void CompactUnwinder_x86<A>::frameUnwind(A& addressSpace, Registers_x86& registers)
+{
+ typename A::pint_t bp = registers.getEBP();
+ // ebp points to old ebp
+ registers.setEBP(addressSpace.get32(bp));
+ // old esp is ebp less saved ebp and return address
+ registers.setSP(bp+8);
+ // pop return address into eip
+ registers.setIP(addressSpace.get32(bp+4));
+}
+
+template <typename A>
+void CompactUnwinder_x86<A>::framelessUnwind(A& addressSpace, typename A::pint_t returnAddressLocation, Registers_x86& registers)
+{
+ // return address is on stack after last saved register
+ registers.setIP(addressSpace.get32(returnAddressLocation));
+ // old esp is before return address
+ registers.setSP(returnAddressLocation+4);
+}
+
+
+
+
+
+///
+/// CompactUnwinder_x86_64 uses a compact unwind info to virtually "step" (aka unwind) by
+/// modifying a Registers_x86_64 register set
+///
+template <typename A>
+class CompactUnwinder_x86_64
+{
+public:
+
+ static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers);
+
+private:
+ typename A::pint_t pint_t;
+
+ static void frameUnwind(A& addressSpace, Registers_x86_64& registers);
+ static void framelessUnwind(A& addressSpace, uint64_t returnAddressLocation, Registers_x86_64& registers);
+ static int stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers);
+ static int stepWithCompactEncodingFrameless(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers, bool indirectStackSize);
+#if SUPPORT_OLD_BINARIES
+ static int stepWithCompactEncodingCompat(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers);
+#endif
+};
+
+
+template <typename A>
+int CompactUnwinder_x86_64<A>::stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers)
+{
+ //fprintf(stderr, "stepWithCompactEncoding(0x%08X)\n", compactEncoding);
+ switch ( compactEncoding & UNWIND_X86_64_MODE_MASK ) {
+#if SUPPORT_OLD_BINARIES
+ case UNWIND_X86_64_MODE_COMPATIBILITY:
+ return stepWithCompactEncodingCompat(compactEncoding, functionStart, addressSpace, registers);
+#endif
+ case UNWIND_X86_64_MODE_RBP_FRAME:
+ return stepWithCompactEncodingRBPFrame(compactEncoding, functionStart, addressSpace, registers);
+ case UNWIND_X86_64_MODE_STACK_IMMD:
+ return stepWithCompactEncodingFrameless(compactEncoding, functionStart, addressSpace, registers, false);
+ case UNWIND_X86_64_MODE_STACK_IND:
+ return stepWithCompactEncodingFrameless(compactEncoding, functionStart, addressSpace, registers, true);
+ }
+ ABORT("invalid compact unwind encoding");
+}
+
+
+template <typename A>
+int CompactUnwinder_x86_64<A>::stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
+ A& addressSpace, Registers_x86_64& registers)
+{
+ uint32_t savedRegistersOffset = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_OFFSET);
+ uint32_t savedRegistersLocations = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);
+
+ uint64_t savedRegisters = registers.getRBP() - 8*savedRegistersOffset;
+ for (int i=0; i < 5; ++i) {
+ int readerr = 0;
+ switch (savedRegistersLocations & 0x7) {
+ case UNWIND_X86_64_REG_NONE:
+ // no register saved in this slot
+ break;
+ case UNWIND_X86_64_REG_RBX:
+ registers.setRBX(addressSpace.get64(savedRegisters, readerr));
+ break;
+ case UNWIND_X86_64_REG_R12:
+ registers.setR12(addressSpace.get64(savedRegisters, readerr));
+ break;
+ case UNWIND_X86_64_REG_R13:
+ registers.setR13(addressSpace.get64(savedRegisters, readerr));
+ break;
+ case UNWIND_X86_64_REG_R14:
+ registers.setR14(addressSpace.get64(savedRegisters, readerr));
+ break;
+ case UNWIND_X86_64_REG_R15:
+ registers.setR15(addressSpace.get64(savedRegisters, readerr));
+ break;
+ default:
+ DEBUG_MESSAGE("bad register for RBP frame, encoding=%08X for function starting at 0x%llX\n", compactEncoding, functionStart);
+ ABORT("invalid compact unwind encoding");
+ }
+ // Error reading memory while doing a remote unwind?
+ if (readerr)
+ return UNW_STEP_END;
+
+ savedRegisters += 8;
+ savedRegistersLocations = (savedRegistersLocations >> 3);
+ }
+ frameUnwind(addressSpace, registers);
+ return UNW_STEP_SUCCESS;
+}
+
+
+template <typename A>
+int CompactUnwinder_x86_64<A>::stepWithCompactEncodingFrameless(compact_unwind_encoding_t encoding, uint64_t functionStart,
+ A& addressSpace, Registers_x86_64& registers, bool indirectStackSize)
+{
+ uint32_t stackSizeEncoded = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
+ uint32_t stackAdjust = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST);
+ uint32_t regCount = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT);
+ uint32_t permutation = EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION);
+ uint32_t stackSize = stackSizeEncoded*8;
+ if ( indirectStackSize ) {
+ // stack size is encoded in subl $xxx,%esp instruction
+ uint32_t subl = addressSpace.get32(functionStart+stackSizeEncoded);
+ stackSize = subl + 8*stackAdjust;
+ }
+ // decompress permutation
+ int permunreg[6];
+ switch ( regCount ) {
+ case 6:
+ permunreg[0] = permutation/120;
+ permutation -= (permunreg[0]*120);
+ permunreg[1] = permutation/24;
+ permutation -= (permunreg[1]*24);
+ permunreg[2] = permutation/6;
+ permutation -= (permunreg[2]*6);
+ permunreg[3] = permutation/2;
+ permutation -= (permunreg[3]*2);
+ permunreg[4] = permutation;
+ permunreg[5] = 0;
+ break;
+ case 5:
+ permunreg[0] = permutation/120;
+ permutation -= (permunreg[0]*120);
+ permunreg[1] = permutation/24;
+ permutation -= (permunreg[1]*24);
+ permunreg[2] = permutation/6;
+ permutation -= (permunreg[2]*6);
+ permunreg[3] = permutation/2;
+ permutation -= (permunreg[3]*2);
+ permunreg[4] = permutation;
+ break;
+ case 4:
+ permunreg[0] = permutation/60;
+ permutation -= (permunreg[0]*60);
+ permunreg[1] = permutation/12;
+ permutation -= (permunreg[1]*12);
+ permunreg[2] = permutation/3;
+ permutation -= (permunreg[2]*3);
+ permunreg[3] = permutation;
+ break;
+ case 3:
+ permunreg[0] = permutation/20;
+ permutation -= (permunreg[0]*20);
+ permunreg[1] = permutation/4;
+ permutation -= (permunreg[1]*4);
+ permunreg[2] = permutation;
+ break;
+ case 2:
+ permunreg[0] = permutation/5;
+ permutation -= (permunreg[0]*5);
+ permunreg[1] = permutation;
+ break;
+ case 1:
+ permunreg[0] = permutation;
+ break;
+ }
+ // re-number registers back to standard numbers
+ int registersSaved[6];
+ bool used[7] = { false, false, false, false, false, false, false };
+ for (uint32_t i=0; i < regCount; ++i) {
+ int renum = 0;
+ for (int u=1; u < 7; ++u) {
+ if ( !used[u] ) {
+ if ( renum == permunreg[i] ) {
+ registersSaved[i] = u;
+ used[u] = true;
+ break;
+ }
+ ++renum;
+ }
+ }
+ }
+ uint64_t savedRegisters = registers.getSP() + stackSize - 8 - 8*regCount;
+ for (uint32_t i=0; i < regCount; ++i) {
+ switch ( registersSaved[i] ) {
+ case UNWIND_X86_64_REG_RBX:
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ break;
+ case UNWIND_X86_64_REG_R12:
+ registers.setR12(addressSpace.get64(savedRegisters));
+ break;
+ case UNWIND_X86_64_REG_R13:
+ registers.setR13(addressSpace.get64(savedRegisters));
+ break;
+ case UNWIND_X86_64_REG_R14:
+ registers.setR14(addressSpace.get64(savedRegisters));
+ break;
+ case UNWIND_X86_64_REG_R15:
+ registers.setR15(addressSpace.get64(savedRegisters));
+ break;
+ case UNWIND_X86_64_REG_RBP:
+ registers.setRBP(addressSpace.get64(savedRegisters));
+ break;
+ default:
+ DEBUG_MESSAGE("bad register for frameless, encoding=%08X for function starting at 0x%llX\n", encoding, functionStart);
+ ABORT("invalid compact unwind encoding");
+ }
+ savedRegisters += 8;
+ }
+ framelessUnwind(addressSpace, savedRegisters, registers);
+ return UNW_STEP_SUCCESS;
+}
+
+#if SUPPORT_OLD_BINARIES
+template <typename A>
+int CompactUnwinder_x86_64<A>::stepWithCompactEncodingCompat(compact_unwind_encoding_t compactEncoding, uint64_t functionStart, A& addressSpace, Registers_x86_64& registers)
+{
+ uint64_t savedRegisters;
+ uint32_t stackValue = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_SIZE);
+ uint64_t stackSize;
+ uint32_t stackAdjust;
+
+ switch (compactEncoding & UNWIND_X86_64_CASE_MASK ) {
+ case UNWIND_X86_64_UNWIND_INFO_UNSPECIFIED:
+ return UNW_ENOINFO;
+
+ case UNWIND_X86_64_RBP_FRAME_NO_REGS:
+ frameUnwind(addressSpace, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_RBP_FRAME_RBX:
+ savedRegisters = registers.getRBP() - 8*1;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ frameUnwind(addressSpace, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_RBP_FRAME_RBX_R12:
+ savedRegisters = registers.getRBP() - 8*2;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setR12(addressSpace.get64(savedRegisters+8));
+ frameUnwind(addressSpace, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_RBP_FRAME_RBX_R12_R13:
+ savedRegisters = registers.getRBP() - 8*3;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setR12(addressSpace.get64(savedRegisters+8));
+ registers.setR13(addressSpace.get64(savedRegisters+16));
+ frameUnwind(addressSpace, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_RBP_FRAME_RBX_R12_R13_R14:
+ savedRegisters = registers.getRBP() - 8*4;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setR12(addressSpace.get64(savedRegisters+8));
+ registers.setR13(addressSpace.get64(savedRegisters+16));
+ registers.setR14(addressSpace.get64(savedRegisters+24));
+ frameUnwind(addressSpace, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_RBP_FRAME_RBX_R12_R13_R14_R15:
+ savedRegisters = registers.getRBP() - 8*5;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setR12(addressSpace.get64(savedRegisters+8));
+ registers.setR13(addressSpace.get64(savedRegisters+16));
+ registers.setR14(addressSpace.get64(savedRegisters+24));
+ registers.setR15(addressSpace.get64(savedRegisters+32));
+ frameUnwind(addressSpace, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IMM_STK_NO_REGS:
+ stackSize = stackValue * 8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*0;
+ framelessUnwind(addressSpace, savedRegisters+8*0, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IMM_STK_RBX:
+ stackSize = stackValue * 8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*1;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ framelessUnwind(addressSpace, savedRegisters+8*1, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IMM_STK_RBX_R12:
+ stackSize = stackValue * 8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*2;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setR12(addressSpace.get64(savedRegisters+8));
+ framelessUnwind(addressSpace, savedRegisters+8*2, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IMM_STK_RBX_RBP:
+ stackSize = stackValue * 8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*2;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setRBP(addressSpace.get64(savedRegisters+8));
+ framelessUnwind(addressSpace, savedRegisters+8*2, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IMM_STK_RBX_R12_R13:
+ stackSize = stackValue * 8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*3;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setR12(addressSpace.get64(savedRegisters+8));
+ registers.setR13(addressSpace.get64(savedRegisters+16));
+ framelessUnwind(addressSpace, savedRegisters+8*3, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IMM_STK_RBX_R12_R13_R14:
+ stackSize = stackValue * 8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*4;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setR12(addressSpace.get64(savedRegisters+8));
+ registers.setR13(addressSpace.get64(savedRegisters+16));
+ registers.setR14(addressSpace.get64(savedRegisters+24));
+ framelessUnwind(addressSpace, savedRegisters+8*4, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IMM_STK_RBX_R12_R13_R14_R15:
+ stackSize = stackValue * 8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*5;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setR12(addressSpace.get64(savedRegisters+8));
+ registers.setR13(addressSpace.get64(savedRegisters+16));
+ registers.setR14(addressSpace.get64(savedRegisters+24));
+ registers.setR15(addressSpace.get64(savedRegisters+32));
+ framelessUnwind(addressSpace, savedRegisters+8*5, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IMM_STK_RBX_RBP_R12_R13_R14_R15:
+ stackSize = stackValue * 8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*6;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setRBP(addressSpace.get64(savedRegisters+8));
+ registers.setR12(addressSpace.get64(savedRegisters+16));
+ registers.setR13(addressSpace.get64(savedRegisters+24));
+ registers.setR14(addressSpace.get64(savedRegisters+32));
+ registers.setR15(addressSpace.get64(savedRegisters+40));
+ framelessUnwind(addressSpace, savedRegisters+8*6, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IMM_STK_RBX_RBP_R12:
+ stackSize = stackValue * 8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*3;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setRBP(addressSpace.get64(savedRegisters+8));
+ registers.setR12(addressSpace.get64(savedRegisters+16));
+ framelessUnwind(addressSpace, savedRegisters+8*3, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IMM_STK_RBX_RBP_R12_R13:
+ stackSize = stackValue * 8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*4;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setRBP(addressSpace.get64(savedRegisters+8));
+ registers.setR12(addressSpace.get64(savedRegisters+16));
+ registers.setR13(addressSpace.get64(savedRegisters+24));
+ framelessUnwind(addressSpace, savedRegisters+8*4, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IMM_STK_RBX_RBP_R12_R13_R14:
+ stackSize = stackValue * 8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*5;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setRBP(addressSpace.get64(savedRegisters+8));
+ registers.setR12(addressSpace.get64(savedRegisters+16));
+ registers.setR13(addressSpace.get64(savedRegisters+24));
+ registers.setR14(addressSpace.get64(savedRegisters+32));
+ framelessUnwind(addressSpace, savedRegisters+8*5, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IND_STK_NO_REGS:
+ stackSize = addressSpace.get32(functionStart+stackValue);
+ stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST);
+ stackSize += stackAdjust*8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*0;
+ framelessUnwind(addressSpace, savedRegisters+8*0, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IND_STK_RBX:
+ stackSize = addressSpace.get32(functionStart+stackValue);
+ stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST);
+ stackSize += stackAdjust*8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*1;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ framelessUnwind(addressSpace, savedRegisters+8*1, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IND_STK_RBX_R12:
+ stackSize = addressSpace.get32(functionStart+stackValue);
+ stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST);
+ stackSize += stackAdjust*8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*2;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setR12(addressSpace.get64(savedRegisters+8));
+ framelessUnwind(addressSpace, savedRegisters+8*2, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IND_STK_RBX_RBP:
+ stackSize = addressSpace.get32(functionStart+stackValue);
+ stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST);
+ stackSize += stackAdjust*8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*2;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setRBP(addressSpace.get64(savedRegisters+8));
+ framelessUnwind(addressSpace, savedRegisters+8*2, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IND_STK_RBX_R12_R13:
+ stackSize = addressSpace.get32(functionStart+stackValue);
+ stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST);
+ stackSize += stackAdjust*8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*3;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setR12(addressSpace.get64(savedRegisters+8));
+ registers.setR13(addressSpace.get64(savedRegisters+16));
+ framelessUnwind(addressSpace, savedRegisters+8*3, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IND_STK_RBX_R12_R13_R14:
+ stackSize = addressSpace.get32(functionStart+stackValue);
+ stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST);
+ stackSize += stackAdjust*8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*4;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setR12(addressSpace.get64(savedRegisters+8));
+ registers.setR13(addressSpace.get64(savedRegisters+16));
+ registers.setR14(addressSpace.get64(savedRegisters+24));
+ framelessUnwind(addressSpace, savedRegisters+8*4, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IND_STK_RBX_R12_R13_R14_R15:
+ stackSize = addressSpace.get32(functionStart+stackValue);
+ stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST);
+ stackSize += stackAdjust*8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*5;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setR12(addressSpace.get64(savedRegisters+8));
+ registers.setR13(addressSpace.get64(savedRegisters+16));
+ registers.setR14(addressSpace.get64(savedRegisters+24));
+ registers.setR15(addressSpace.get64(savedRegisters+32));
+ framelessUnwind(addressSpace, savedRegisters+8*5, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IND_STK_RBX_RBP_R12_R13_R14_R15:
+ stackSize = addressSpace.get32(functionStart+stackValue);
+ stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST);
+ stackSize += stackAdjust*8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*6;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setRBP(addressSpace.get64(savedRegisters+8));
+ registers.setR12(addressSpace.get64(savedRegisters+16));
+ registers.setR13(addressSpace.get64(savedRegisters+24));
+ registers.setR14(addressSpace.get64(savedRegisters+32));
+ registers.setR15(addressSpace.get64(savedRegisters+40));
+ framelessUnwind(addressSpace, savedRegisters+8*6, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IND_STK_RBX_RBP_R12:
+ stackSize = addressSpace.get32(functionStart+stackValue);
+ stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST);
+ stackSize += stackAdjust*8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*3;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setRBP(addressSpace.get64(savedRegisters+8));
+ registers.setR12(addressSpace.get64(savedRegisters+16));
+ framelessUnwind(addressSpace, savedRegisters+8*3, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IND_STK_RBX_RBP_R12_R13:
+ stackSize = addressSpace.get32(functionStart+stackValue);
+ stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST);
+ stackSize += stackAdjust*8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*4;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setRBP(addressSpace.get64(savedRegisters+8));
+ registers.setR12(addressSpace.get64(savedRegisters+16));
+ registers.setR13(addressSpace.get64(savedRegisters+24));
+ framelessUnwind(addressSpace, savedRegisters+8*4, registers);
+ return UNW_STEP_SUCCESS;
+
+ case UNWIND_X86_64_IND_STK_RBX_RBP_R12_R13_R14:
+ stackSize = addressSpace.get32(functionStart+stackValue);
+ stackAdjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_STACK_ADJUST);
+ stackSize += stackAdjust*8;
+ savedRegisters = registers.getSP() + stackSize - 8 - 8*5;
+ registers.setRBX(addressSpace.get64(savedRegisters));
+ registers.setRBP(addressSpace.get64(savedRegisters+8));
+ registers.setR12(addressSpace.get64(savedRegisters+16));
+ registers.setR13(addressSpace.get64(savedRegisters+24));
+ registers.setR14(addressSpace.get64(savedRegisters+32));
+ framelessUnwind(addressSpace, savedRegisters+8*5, registers);
+ return UNW_STEP_SUCCESS;
+
+ default:
+ DEBUG_MESSAGE("unknown compact unwind encoding %08X for function starting at 0x%llX\n",
+ compactEncoding & UNWIND_X86_64_CASE_MASK, functionStart);
+ ABORT("unknown compact unwind encoding");
+ }
+ return UNW_EINVAL;
+}
+#endif // SUPPORT_OLD_BINARIES
+
+
+template <typename A>
+void CompactUnwinder_x86_64<A>::frameUnwind(A& addressSpace, Registers_x86_64& registers)
+{
+ uint64_t rbp = registers.getRBP();
+ // ebp points to old ebp
+ registers.setRBP(addressSpace.get64(rbp));
+ // old esp is ebp less saved ebp and return address
+ registers.setSP(rbp+16);
+ // pop return address into eip
+ registers.setIP(addressSpace.get64(rbp+8));
+}
+
+template <typename A>
+void CompactUnwinder_x86_64<A>::framelessUnwind(A& addressSpace, uint64_t returnAddressLocation, Registers_x86_64& registers)
+{
+ // return address is on stack after last saved register
+ registers.setIP(addressSpace.get64(returnAddressLocation));
+ // old esp is before return address
+ registers.setSP(returnAddressLocation+8);
+}
+
+
+}; // namespace lldb_private
+
+
+
+#endif // __COMPACT_UNWINDER_HPP__
+
+
+
+
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/DwarfInstructions.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/DwarfInstructions.hpp
new file mode 100644
index 00000000000..589c30b50b9
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/DwarfInstructions.hpp
@@ -0,0 +1,1686 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- DwarfInstructions.hpp -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+//
+// processor specific parsing of dwarf unwind instructions
+//
+
+#ifndef __DWARF_INSTRUCTIONS_HPP__
+#define __DWARF_INSTRUCTIONS_HPP__
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <algorithm>
+#include <vector>
+
+#include <libunwind.h>
+#include <mach-o/compact_unwind_encoding.h>
+
+#include "dwarf2.h"
+#include "AddressSpace.hpp"
+#include "Registers.hpp"
+#include "DwarfParser.hpp"
+#include "InternalMacros.h"
+//#include "CompactUnwinder.hpp"
+
+#define EXTRACT_BITS(value, mask) \
+ ( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) )
+
+#define CFI_INVALID_ADDRESS ((pint_t)(-1))
+
+namespace lldb_private {
+
+///
+/// Used by linker when parsing __eh_frame section
+///
+template <typename A>
+struct CFI_Reference {
+ typedef typename A::pint_t pint_t;
+ uint8_t encodingOfTargetAddress;
+ uint32_t offsetInCFI;
+ pint_t targetAddress;
+};
+template <typename A>
+struct CFI_Atom_Info {
+ typedef typename A::pint_t pint_t;
+ pint_t address;
+ uint32_t size;
+ bool isCIE;
+ union {
+ struct {
+ CFI_Reference<A> function;
+ CFI_Reference<A> cie;
+ CFI_Reference<A> lsda;
+ uint32_t compactUnwindInfo;
+ } fdeInfo;
+ struct {
+ CFI_Reference<A> personality;
+ } cieInfo;
+ } u;
+};
+
+typedef void (*WarnFunc)(void* ref, uint64_t funcAddr, const char* msg);
+
+///
+/// DwarfInstructions maps abtract dwarf unwind instructions to a particular architecture
+///
+template <typename A, typename R>
+class DwarfInstructions
+{
+public:
+ typedef typename A::pint_t pint_t;
+ typedef typename A::sint_t sint_t;
+
+ static const char* parseCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength,
+ CFI_Atom_Info<A>* infos, uint32_t infosCount, void* ref, WarnFunc warn);
+
+
+ static compact_unwind_encoding_t createCompactEncodingFromFDE(A& addressSpace, pint_t fdeStart,
+ pint_t* lsda, pint_t* personality,
+ char warningBuffer[1024]);
+
+ static int stepWithDwarf(A& addressSpace, pint_t pc, pint_t fdeStart, R& registers);
+
+private:
+
+ enum {
+ DW_X86_64_RET_ADDR = 16
+ };
+
+ enum {
+ DW_X86_RET_ADDR = 8
+ };
+
+ static pint_t evaluateExpression(pint_t expression, A& addressSpace, const R& registers, pint_t initialStackValue);
+ static pint_t getSavedRegister(A& addressSpace, const R& registers, pint_t cfa,
+ const typename CFI_Parser<A>::RegisterLocation& savedReg);
+ static double getSavedFloatRegister(A& addressSpace, const R& registers, pint_t cfa,
+ const typename CFI_Parser<A>::RegisterLocation& savedReg);
+ static v128 getSavedVectorRegister(A& addressSpace, const R& registers, pint_t cfa,
+ const typename CFI_Parser<A>::RegisterLocation& savedReg);
+
+ // x86 specific variants
+ static int lastRestoreReg(const Registers_x86&);
+ static bool isReturnAddressRegister(int regNum, const Registers_x86&);
+ static pint_t getCFA(A& addressSpace, const typename CFI_Parser<A>::PrologInfo& prolog, const Registers_x86&);
+
+ static uint32_t getEBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure);
+ static compact_unwind_encoding_t encodeToUseDwarf(const Registers_x86&);
+ static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr,
+ const Registers_x86&, const typename CFI_Parser<A>::PrologInfo& prolog,
+ char warningBuffer[1024]);
+
+ // x86_64 specific variants
+ static int lastRestoreReg(const Registers_x86_64&);
+ static bool isReturnAddressRegister(int regNum, const Registers_x86_64&);
+ static pint_t getCFA(A& addressSpace, const typename CFI_Parser<A>::PrologInfo& prolog, const Registers_x86_64&);
+
+ static uint32_t getRBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure);
+ static compact_unwind_encoding_t encodeToUseDwarf(const Registers_x86_64&);
+ static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr,
+ const Registers_x86_64&, const typename CFI_Parser<A>::PrologInfo& prolog,
+ char warningBuffer[1024]);
+
+ // ppc specific variants
+ static int lastRestoreReg(const Registers_ppc&);
+ static bool isReturnAddressRegister(int regNum, const Registers_ppc&);
+ static pint_t getCFA(A& addressSpace, const typename CFI_Parser<A>::PrologInfo& prolog, const Registers_ppc&);
+ static compact_unwind_encoding_t encodeToUseDwarf(const Registers_ppc&);
+ static compact_unwind_encoding_t createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr,
+ const Registers_ppc&, const typename CFI_Parser<A>::PrologInfo& prolog,
+ char warningBuffer[1024]);
+};
+
+
+
+
+template <typename A, typename R>
+const char* DwarfInstructions<A,R>::parseCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength,
+ CFI_Atom_Info<A>* infos, uint32_t infosCount, void* ref, WarnFunc warn)
+{
+ typename CFI_Parser<A>::CIE_Info cieInfo;
+ CFI_Atom_Info<A>* entry = infos;
+ CFI_Atom_Info<A>* end = &infos[infosCount];
+ const pint_t ehSectionEnd = ehSectionStart + sectionLength;
+ for (pint_t p=ehSectionStart; p < ehSectionEnd; ) {
+ pint_t currentCFI = p;
+ uint64_t cfiLength = addressSpace.get32(p);
+ p += 4;
+ if ( cfiLength == 0xffffffff ) {
+ // 0xffffffff means length is really next 8 bytes
+ cfiLength = addressSpace.get64(p);
+ p += 8;
+ }
+ if ( cfiLength == 0 )
+ return NULL; // end marker
+ if ( entry >= end )
+ return "too little space allocated for parseCFIs";
+ pint_t nextCFI = p + cfiLength;
+ uint32_t id = addressSpace.get32(p);
+ if ( id == 0 ) {
+ // is CIE
+ const char* err = CFI_Parser<A>::parseCIE(addressSpace, currentCFI, &cieInfo);
+ if ( err != NULL )
+ return err;
+ entry->address = currentCFI;
+ entry->size = nextCFI - currentCFI;
+ entry->isCIE = true;
+ entry->u.cieInfo.personality.targetAddress = cieInfo.personality;
+ entry->u.cieInfo.personality.offsetInCFI = cieInfo.personalityOffsetInCIE;
+ entry->u.cieInfo.personality.encodingOfTargetAddress = cieInfo.personalityEncoding;
+ ++entry;
+ }
+ else {
+ // is FDE
+ entry->address = currentCFI;
+ entry->size = nextCFI - currentCFI;
+ entry->isCIE = false;
+ entry->u.fdeInfo.function.targetAddress = CFI_INVALID_ADDRESS;
+ entry->u.fdeInfo.cie.targetAddress = CFI_INVALID_ADDRESS;
+ entry->u.fdeInfo.lsda.targetAddress = CFI_INVALID_ADDRESS;
+ uint32_t ciePointer = addressSpace.get32(p);
+ pint_t cieStart = p-ciePointer;
+ // validate pointer to CIE is within section
+ if ( (cieStart < ehSectionStart) || (cieStart > ehSectionEnd) )
+ return "FDE points to CIE outside __eh_frame section";
+ // optimize usual case where cie is same for all FDEs
+ if ( cieStart != cieInfo.cieStart ) {
+ const char* err = CFI_Parser<A>::parseCIE(addressSpace, cieStart, &cieInfo);
+ if ( err != NULL )
+ return err;
+ }
+ entry->u.fdeInfo.cie.targetAddress = cieStart;
+ entry->u.fdeInfo.cie.offsetInCFI = p-currentCFI;
+ entry->u.fdeInfo.cie.encodingOfTargetAddress = DW_EH_PE_sdata4 | DW_EH_PE_pcrel;
+ p += 4;
+ // parse pc begin and range
+ pint_t offsetOfFunctionAddress = p-currentCFI;
+ pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding);
+ pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding & 0x0F);
+ //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange));
+ // test if pc is within the function this FDE covers
+ entry->u.fdeInfo.function.targetAddress = pcStart;
+ entry->u.fdeInfo.function.offsetInCFI = offsetOfFunctionAddress;
+ entry->u.fdeInfo.function.encodingOfTargetAddress = cieInfo.pointerEncoding;
+ // check for augmentation length
+ if ( cieInfo.fdesHaveAugmentationData ) {
+ uintptr_t augLen = addressSpace.getULEB128(p, nextCFI);
+ pint_t endOfAug = p + augLen;
+ if ( cieInfo.lsdaEncoding != 0 ) {
+ // peek at value (without indirection). Zero means no lsda
+ pint_t lsdaStart = p;
+ if ( addressSpace.getEncodedP(p, nextCFI, cieInfo.lsdaEncoding & 0x0F) != 0 ) {
+ // reset pointer and re-parse lsda address
+ p = lsdaStart;
+ pint_t offsetOfLSDAAddress = p-currentCFI;
+ entry->u.fdeInfo.lsda.targetAddress = addressSpace.getEncodedP(p, nextCFI, cieInfo.lsdaEncoding);
+ entry->u.fdeInfo.lsda.offsetInCFI = offsetOfLSDAAddress;
+ entry->u.fdeInfo.lsda.encodingOfTargetAddress = cieInfo.lsdaEncoding;
+ }
+ }
+ p = endOfAug;
+ }
+ // compute compact unwind encoding
+ typename CFI_Parser<A>::FDE_Info fdeInfo;
+ fdeInfo.fdeStart = currentCFI;
+ fdeInfo.fdeLength = nextCFI - currentCFI;
+ fdeInfo.fdeInstructions = p;
+ fdeInfo.pcStart = pcStart;
+ fdeInfo.pcEnd = pcStart + pcRange;
+ fdeInfo.lsda = entry->u.fdeInfo.lsda.targetAddress;
+ typename CFI_Parser<A>::PrologInfo prolog;
+ R dummy; // for proper selection of architecture specific functions
+ if ( CFI_Parser<A>::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, CFI_INVALID_ADDRESS, &prolog) ) {
+ char warningBuffer[1024];
+ entry->u.fdeInfo.compactUnwindInfo = createCompactEncodingFromProlog(addressSpace, fdeInfo.pcStart, dummy, prolog, warningBuffer);
+ if ( fdeInfo.lsda != CFI_INVALID_ADDRESS )
+ entry->u.fdeInfo.compactUnwindInfo |= UNWIND_HAS_LSDA;
+ if ( warningBuffer[0] != '\0' )
+ warn(ref, fdeInfo.pcStart, warningBuffer);
+ }
+ else {
+ warn(ref, CFI_INVALID_ADDRESS, "dwarf unwind instructions could not be parsed");
+ entry->u.fdeInfo.compactUnwindInfo = encodeToUseDwarf(dummy);
+ }
+ ++entry;
+ }
+ p = nextCFI;
+ }
+ if ( entry != end )
+ return "wrong entry count for parseCFIs";
+ return NULL; // success
+}
+
+
+
+
+template <typename A, typename R>
+compact_unwind_encoding_t DwarfInstructions<A,R>::createCompactEncodingFromFDE(A& addressSpace, pint_t fdeStart,
+ pint_t* lsda, pint_t* personality,
+ char warningBuffer[1024])
+{
+ typename CFI_Parser<A>::FDE_Info fdeInfo;
+ typename CFI_Parser<A>::CIE_Info cieInfo;
+ R dummy; // for proper selection of architecture specific functions
+ if ( CFI_Parser<A>::decodeFDE(addressSpace, fdeStart, &fdeInfo, &cieInfo) == NULL ) {
+ typename CFI_Parser<A>::PrologInfo prolog;
+ if ( CFI_Parser<A>::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, CFI_INVALID_ADDRESS, &prolog) ) {
+ *lsda = fdeInfo.lsda;
+ *personality = cieInfo.personality;
+ compact_unwind_encoding_t encoding;
+ encoding = createCompactEncodingFromProlog(addressSpace, fdeInfo.pcStart, dummy, prolog, warningBuffer);
+ if ( fdeInfo.lsda != 0 )
+ encoding |= UNWIND_HAS_LSDA;
+ return encoding;
+ }
+ else {
+ strcpy(warningBuffer, "dwarf unwind instructions could not be parsed");
+ return encodeToUseDwarf(dummy);
+ }
+ }
+ else {
+ strcpy(warningBuffer, "dwarf FDE could not be parsed");
+ return encodeToUseDwarf(dummy);
+ }
+}
+
+
+template <typename A, typename R>
+typename A::pint_t DwarfInstructions<A,R>::getSavedRegister(A& addressSpace, const R& registers, pint_t cfa,
+ const typename CFI_Parser<A>::RegisterLocation& savedReg)
+{
+ switch ( savedReg.location ) {
+ case CFI_Parser<A>::kRegisterInCFA:
+ return addressSpace.getP(cfa + savedReg.value);
+
+ case CFI_Parser<A>::kRegisterAtExpression:
+ return addressSpace.getP(evaluateExpression(savedReg.value, addressSpace, registers, cfa));
+
+ case CFI_Parser<A>::kRegisterIsExpression:
+ return evaluateExpression(savedReg.value, addressSpace, registers, cfa);
+
+ case CFI_Parser<A>::kRegisterInRegister:
+ return registers.getRegister(savedReg.value);
+
+ case CFI_Parser<A>::kRegisterUnused:
+ case CFI_Parser<A>::kRegisterOffsetFromCFA:
+ // FIX ME
+ break;
+ }
+ ABORT("unsupported restore location for register");
+}
+
+template <typename A, typename R>
+double DwarfInstructions<A,R>::getSavedFloatRegister(A& addressSpace, const R& registers, pint_t cfa,
+ const typename CFI_Parser<A>::RegisterLocation& savedReg)
+{
+ switch ( savedReg.location ) {
+ case CFI_Parser<A>::kRegisterInCFA:
+ return addressSpace.getDouble(cfa + savedReg.value);
+
+ case CFI_Parser<A>::kRegisterAtExpression:
+ return addressSpace.getDouble(evaluateExpression(savedReg.value, addressSpace, registers, cfa));
+
+ case CFI_Parser<A>::kRegisterIsExpression:
+ case CFI_Parser<A>::kRegisterUnused:
+ case CFI_Parser<A>::kRegisterOffsetFromCFA:
+ case CFI_Parser<A>::kRegisterInRegister:
+ // FIX ME
+ break;
+ }
+ ABORT("unsupported restore location for float register");
+}
+
+template <typename A, typename R>
+v128 DwarfInstructions<A,R>::getSavedVectorRegister(A& addressSpace, const R& registers, pint_t cfa,
+ const typename CFI_Parser<A>::RegisterLocation& savedReg)
+{
+ switch ( savedReg.location ) {
+ case CFI_Parser<A>::kRegisterInCFA:
+ return addressSpace.getVector(cfa + savedReg.value);
+
+ case CFI_Parser<A>::kRegisterAtExpression:
+ return addressSpace.getVector(evaluateExpression(savedReg.value, addressSpace, registers, cfa));
+
+ case CFI_Parser<A>::kRegisterIsExpression:
+ case CFI_Parser<A>::kRegisterUnused:
+ case CFI_Parser<A>::kRegisterOffsetFromCFA:
+ case CFI_Parser<A>::kRegisterInRegister:
+ // FIX ME
+ break;
+ }
+ ABORT("unsupported restore location for vector register");
+}
+
+
+template <typename A, typename R>
+int DwarfInstructions<A,R>::stepWithDwarf(A& addressSpace, pint_t pc, pint_t fdeStart, R& registers)
+{
+ //fprintf(stderr, "stepWithDwarf(pc=0x%0llX, fdeStart=0x%0llX)\n", (uint64_t)pc, (uint64_t)fdeStart);
+ typename CFI_Parser<A>::FDE_Info fdeInfo;
+ typename CFI_Parser<A>::CIE_Info cieInfo;
+ if ( CFI_Parser<A>::decodeFDE(addressSpace, fdeStart, &fdeInfo, &cieInfo) == NULL ) {
+ typename CFI_Parser<A>::PrologInfo prolog;
+ if ( CFI_Parser<A>::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, pc, &prolog) ) {
+ R newRegisters = registers;
+
+ // get pointer to cfa (architecture specific)
+ pint_t cfa = getCFA(addressSpace, prolog, registers);
+
+ // restore registers that dwarf says were saved
+ pint_t returnAddress = 0;
+ for (int i=0; i <= lastRestoreReg(newRegisters); ++i) {
+ if ( prolog.savedRegisters[i].location != CFI_Parser<A>::kRegisterUnused ) {
+ if ( registers.validFloatRegister(i) )
+ newRegisters.setFloatRegister(i, getSavedFloatRegister(addressSpace, registers, cfa, prolog.savedRegisters[i]));
+ else if ( registers.validVectorRegister(i) )
+ newRegisters.setVectorRegister(i, getSavedVectorRegister(addressSpace, registers, cfa, prolog.savedRegisters[i]));
+ else if ( isReturnAddressRegister(i, registers) )
+ returnAddress = getSavedRegister(addressSpace, registers, cfa, prolog.savedRegisters[i]);
+ else if ( registers.validRegister(i) )
+ newRegisters.setRegister(i, getSavedRegister(addressSpace, registers, cfa, prolog.savedRegisters[i]));
+ else
+ return UNW_EBADREG;
+ }
+ }
+
+ // by definition the CFA is the stack pointer at the call site, so restoring SP means setting it to CFA
+ newRegisters.setSP(cfa);
+
+ // return address is address after call site instruction, so setting IP to that does a return
+ newRegisters.setIP(returnAddress);
+
+ // do the actual step by replacing the register set with the new ones
+ registers = newRegisters;
+
+ return UNW_STEP_SUCCESS;
+ }
+ }
+ return UNW_EBADFRAME;
+}
+
+
+
+template <typename A, typename R>
+typename A::pint_t DwarfInstructions<A,R>::evaluateExpression(pint_t expression, A& addressSpace,
+ const R& registers, pint_t initialStackValue)
+{
+ const bool log = false;
+ pint_t p = expression;
+ pint_t expressionEnd = expression+20; // just need something until length is read
+ uint64_t length = addressSpace.getULEB128(p, expressionEnd);
+ expressionEnd = p + length;
+ if (log) fprintf(stderr, "evaluateExpression(): length=%llu\n", length);
+ pint_t stack[100];
+ pint_t* sp = stack;
+ *(++sp) = initialStackValue;
+
+ while ( p < expressionEnd ) {
+ if (log) {
+ for(pint_t* t = sp; t > stack; --t) {
+ fprintf(stderr, "sp[] = 0x%llX\n", (uint64_t)(*t));
+ }
+ }
+ uint8_t opcode = addressSpace.get8(p++);
+ sint_t svalue;
+ pint_t value;
+ uint32_t reg;
+ switch (opcode) {
+ case DW_OP_addr:
+ // push immediate address sized value
+ value = addressSpace.getP(p);
+ p += sizeof(pint_t);
+ *(++sp) = value;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_deref:
+ // pop stack, dereference, push result
+ value = *sp--;
+ *(++sp) = addressSpace.getP(value);
+ if (log) fprintf(stderr, "dereference 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_const1u:
+ // push immediate 1 byte value
+ value = addressSpace.get8(p);
+ p += 1;
+ *(++sp) = value;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_const1s:
+ // push immediate 1 byte signed value
+ svalue = (int8_t)addressSpace.get8(p);
+ p += 1;
+ *(++sp) = svalue;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue);
+ break;
+
+ case DW_OP_const2u:
+ // push immediate 2 byte value
+ value = addressSpace.get16(p);
+ p += 2;
+ *(++sp) = value;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_const2s:
+ // push immediate 2 byte signed value
+ svalue = (int16_t)addressSpace.get16(p);
+ p += 2;
+ *(++sp) = svalue;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue);
+ break;
+
+ case DW_OP_const4u:
+ // push immediate 4 byte value
+ value = addressSpace.get32(p);
+ p += 4;
+ *(++sp) = value;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_const4s:
+ // push immediate 4 byte signed value
+ svalue = (int32_t)addressSpace.get32(p);
+ p += 4;
+ *(++sp) = svalue;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue);
+ break;
+
+ case DW_OP_const8u:
+ // push immediate 8 byte value
+ value = addressSpace.get64(p);
+ p += 8;
+ *(++sp) = value;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_const8s:
+ // push immediate 8 byte signed value
+ value = (int32_t)addressSpace.get64(p);
+ p += 8;
+ *(++sp) = value;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_constu:
+ // push immediate ULEB128 value
+ value = addressSpace.getULEB128(p, expressionEnd);
+ *(++sp) = value;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_consts:
+ // push immediate SLEB128 value
+ svalue = addressSpace.getSLEB128(p, expressionEnd);
+ *(++sp) = svalue;
+ if (log) fprintf(stderr, "push 0x%llX\n", (uint64_t)svalue);
+ break;
+
+ case DW_OP_dup:
+ // push top of stack
+ value = *sp;
+ *(++sp) = value;
+ if (log) fprintf(stderr, "duplicate top of stack\n");
+ break;
+
+ case DW_OP_drop:
+ // pop
+ --sp;
+ if (log) fprintf(stderr, "pop top of stack\n");
+ break;
+
+ case DW_OP_over:
+ // dup second
+ value = sp[-1];
+ *(++sp) = value;
+ if (log) fprintf(stderr, "duplicate second in stack\n");
+ break;
+
+ case DW_OP_pick:
+ // pick from
+ reg = addressSpace.get8(p);
+ p += 1;
+ value = sp[-reg];
+ *(++sp) = value;
+ if (log) fprintf(stderr, "duplicate %d in stack\n", reg);
+ break;
+
+ case DW_OP_swap:
+ // swap top two
+ value = sp[0];
+ sp[0] = sp[-1];
+ sp[-1] = value;
+ if (log) fprintf(stderr, "swap top of stack\n");
+ break;
+
+ case DW_OP_rot:
+ // rotate top three
+ value = sp[0];
+ sp[0] = sp[-1];
+ sp[-1] = sp[-2];
+ sp[-2] = value;
+ if (log) fprintf(stderr, "rotate top three of stack\n");
+ break;
+
+ case DW_OP_xderef:
+ // pop stack, dereference, push result
+ value = *sp--;
+ *sp = *((uint64_t*)value);
+ if (log) fprintf(stderr, "x-dereference 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_abs:
+ svalue = *sp;
+ if ( svalue < 0 )
+ *sp = -svalue;
+ if (log) fprintf(stderr, "abs\n");
+ break;
+
+ case DW_OP_and:
+ value = *sp--;
+ *sp &= value;
+ if (log) fprintf(stderr, "and\n");
+ break;
+
+ case DW_OP_div:
+ svalue = *sp--;
+ *sp = *sp / svalue;
+ if (log) fprintf(stderr, "div\n");
+ break;
+
+ case DW_OP_minus:
+ svalue = *sp--;
+ *sp = *sp - svalue;
+ if (log) fprintf(stderr, "minus\n");
+ break;
+
+ case DW_OP_mod:
+ svalue = *sp--;
+ *sp = *sp % svalue;
+ if (log) fprintf(stderr, "module\n");
+ break;
+
+ case DW_OP_mul:
+ svalue = *sp--;
+ *sp = *sp * svalue;
+ if (log) fprintf(stderr, "mul\n");
+ break;
+
+ case DW_OP_neg:
+ *sp = 0 - *sp;
+ if (log) fprintf(stderr, "neg\n");
+ break;
+
+ case DW_OP_not:
+ svalue = *sp;
+ *sp = ~svalue;
+ if (log) fprintf(stderr, "not\n");
+ break;
+
+ case DW_OP_or:
+ value = *sp--;
+ *sp |= value;
+ if (log) fprintf(stderr, "or\n");
+ break;
+
+ case DW_OP_plus:
+ value = *sp--;
+ *sp += value;
+ if (log) fprintf(stderr, "plus\n");
+ break;
+
+ case DW_OP_plus_uconst:
+ // pop stack, add uelb128 constant, push result
+ *sp += addressSpace.getULEB128(p, expressionEnd);
+ if (log) fprintf(stderr, "add constant\n");
+ break;
+
+ case DW_OP_shl:
+ value = *sp--;
+ *sp = *sp << value;
+ if (log) fprintf(stderr, "shift left\n");
+ break;
+
+ case DW_OP_shr:
+ value = *sp--;
+ *sp = *sp >> value;
+ if (log) fprintf(stderr, "shift left\n");
+ break;
+
+ case DW_OP_shra:
+ value = *sp--;
+ svalue = *sp;
+ *sp = svalue >> value;
+ if (log) fprintf(stderr, "shift left arithmetric\n");
+ break;
+
+ case DW_OP_xor:
+ value = *sp--;
+ *sp ^= value;
+ if (log) fprintf(stderr, "xor\n");
+ break;
+
+ case DW_OP_skip:
+ svalue = (int16_t)addressSpace.get16(p);
+ p += 2;
+ p += svalue;
+ if (log) fprintf(stderr, "skip %lld\n", (uint64_t)svalue);
+ break;
+
+ case DW_OP_bra:
+ svalue = (int16_t)addressSpace.get16(p);
+ p += 2;
+ if ( *sp-- )
+ p += svalue;
+ if (log) fprintf(stderr, "bra %lld\n", (uint64_t)svalue);
+ break;
+
+ case DW_OP_eq:
+ value = *sp--;
+ *sp = (*sp == value);
+ if (log) fprintf(stderr, "eq\n");
+ break;
+
+ case DW_OP_ge:
+ value = *sp--;
+ *sp = (*sp >= value);
+ if (log) fprintf(stderr, "ge\n");
+ break;
+
+ case DW_OP_gt:
+ value = *sp--;
+ *sp = (*sp > value);
+ if (log) fprintf(stderr, "gt\n");
+ break;
+
+ case DW_OP_le:
+ value = *sp--;
+ *sp = (*sp <= value);
+ if (log) fprintf(stderr, "le\n");
+ break;
+
+ case DW_OP_lt:
+ value = *sp--;
+ *sp = (*sp < value);
+ if (log) fprintf(stderr, "lt\n");
+ break;
+
+ case DW_OP_ne:
+ value = *sp--;
+ *sp = (*sp != value);
+ if (log) fprintf(stderr, "ne\n");
+ break;
+
+ case DW_OP_lit0:
+ case DW_OP_lit1:
+ case DW_OP_lit2:
+ case DW_OP_lit3:
+ case DW_OP_lit4:
+ case DW_OP_lit5:
+ case DW_OP_lit6:
+ case DW_OP_lit7:
+ case DW_OP_lit8:
+ case DW_OP_lit9:
+ case DW_OP_lit10:
+ case DW_OP_lit11:
+ case DW_OP_lit12:
+ case DW_OP_lit13:
+ case DW_OP_lit14:
+ case DW_OP_lit15:
+ case DW_OP_lit16:
+ case DW_OP_lit17:
+ case DW_OP_lit18:
+ case DW_OP_lit19:
+ case DW_OP_lit20:
+ case DW_OP_lit21:
+ case DW_OP_lit22:
+ case DW_OP_lit23:
+ case DW_OP_lit24:
+ case DW_OP_lit25:
+ case DW_OP_lit26:
+ case DW_OP_lit27:
+ case DW_OP_lit28:
+ case DW_OP_lit29:
+ case DW_OP_lit30:
+ case DW_OP_lit31:
+ value = opcode - DW_OP_lit0;
+ *(++sp) = value;
+ if (log) fprintf(stderr, "push literal 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_reg0:
+ case DW_OP_reg1:
+ case DW_OP_reg2:
+ case DW_OP_reg3:
+ case DW_OP_reg4:
+ case DW_OP_reg5:
+ case DW_OP_reg6:
+ case DW_OP_reg7:
+ case DW_OP_reg8:
+ case DW_OP_reg9:
+ case DW_OP_reg10:
+ case DW_OP_reg11:
+ case DW_OP_reg12:
+ case DW_OP_reg13:
+ case DW_OP_reg14:
+ case DW_OP_reg15:
+ case DW_OP_reg16:
+ case DW_OP_reg17:
+ case DW_OP_reg18:
+ case DW_OP_reg19:
+ case DW_OP_reg20:
+ case DW_OP_reg21:
+ case DW_OP_reg22:
+ case DW_OP_reg23:
+ case DW_OP_reg24:
+ case DW_OP_reg25:
+ case DW_OP_reg26:
+ case DW_OP_reg27:
+ case DW_OP_reg28:
+ case DW_OP_reg29:
+ case DW_OP_reg30:
+ case DW_OP_reg31:
+ reg = opcode - DW_OP_reg0;
+ *(++sp) = registers.getRegister(reg);
+ if (log) fprintf(stderr, "push reg %d\n", reg);
+ break;
+
+ case DW_OP_regx:
+ reg = addressSpace.getULEB128(p, expressionEnd);
+ *(++sp) = registers.getRegister(reg);
+ if (log) fprintf(stderr, "push reg %d + 0x%llX\n", reg, (uint64_t)svalue);
+ break;
+
+ case DW_OP_breg0:
+ case DW_OP_breg1:
+ case DW_OP_breg2:
+ case DW_OP_breg3:
+ case DW_OP_breg4:
+ case DW_OP_breg5:
+ case DW_OP_breg6:
+ case DW_OP_breg7:
+ case DW_OP_breg8:
+ case DW_OP_breg9:
+ case DW_OP_breg10:
+ case DW_OP_breg11:
+ case DW_OP_breg12:
+ case DW_OP_breg13:
+ case DW_OP_breg14:
+ case DW_OP_breg15:
+ case DW_OP_breg16:
+ case DW_OP_breg17:
+ case DW_OP_breg18:
+ case DW_OP_breg19:
+ case DW_OP_breg20:
+ case DW_OP_breg21:
+ case DW_OP_breg22:
+ case DW_OP_breg23:
+ case DW_OP_breg24:
+ case DW_OP_breg25:
+ case DW_OP_breg26:
+ case DW_OP_breg27:
+ case DW_OP_breg28:
+ case DW_OP_breg29:
+ case DW_OP_breg30:
+ case DW_OP_breg31:
+ reg = opcode - DW_OP_breg0;
+ svalue = addressSpace.getSLEB128(p, expressionEnd);
+ *(++sp) = registers.getRegister(reg) + svalue;
+ if (log) fprintf(stderr, "push reg %d + 0x%llX\n", reg, (uint64_t)svalue);
+ break;
+
+ case DW_OP_bregx:
+ reg = addressSpace.getULEB128(p, expressionEnd);
+ svalue = addressSpace.getSLEB128(p, expressionEnd);
+ *(++sp) = registers.getRegister(reg) + svalue;
+ if (log) fprintf(stderr, "push reg %d + 0x%llX\n", reg, (uint64_t)svalue);
+ break;
+
+ case DW_OP_fbreg:
+ ABORT("DW_OP_fbreg not implemented");
+ break;
+
+ case DW_OP_piece:
+ ABORT("DW_OP_piece not implemented");
+ break;
+
+ case DW_OP_deref_size:
+ // pop stack, dereference, push result
+ value = *sp--;
+ switch ( addressSpace.get8(p++) ) {
+ case 1:
+ value = addressSpace.get8(value);
+ break;
+ case 2:
+ value = addressSpace.get16(value);
+ break;
+ case 4:
+ value = addressSpace.get32(value);
+ break;
+ case 8:
+ value = addressSpace.get64(value);
+ break;
+ default:
+ ABORT("DW_OP_deref_size with bad size");
+ }
+ *(++sp) = value;
+ if (log) fprintf(stderr, "sized dereference 0x%llX\n", (uint64_t)value);
+ break;
+
+ case DW_OP_xderef_size:
+ case DW_OP_nop:
+ case DW_OP_push_object_addres:
+ case DW_OP_call2:
+ case DW_OP_call4:
+ case DW_OP_call_ref:
+ default:
+ ABORT("dwarf opcode not implemented");
+ }
+
+ }
+ if (log) fprintf(stderr, "expression evaluates to 0x%llX\n", (uint64_t)*sp);
+ return *sp;
+}
+
+
+
+//
+// x86_64 specific functions
+//
+
+template <typename A, typename R>
+int DwarfInstructions<A,R>::lastRestoreReg(const Registers_x86_64&)
+{
+ COMPILE_TIME_ASSERT( (int)CFI_Parser<A>::kMaxRegisterNumber > (int)DW_X86_64_RET_ADDR );
+ return DW_X86_64_RET_ADDR;
+}
+
+template <typename A, typename R>
+bool DwarfInstructions<A,R>::isReturnAddressRegister(int regNum, const Registers_x86_64&)
+{
+ return (regNum == DW_X86_64_RET_ADDR);
+}
+
+template <typename A, typename R>
+typename A::pint_t DwarfInstructions<A,R>::getCFA(A& addressSpace, const typename CFI_Parser<A>::PrologInfo& prolog,
+ const Registers_x86_64& registers)
+{
+ if ( prolog.cfaRegister != 0 )
+ return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset;
+ else if ( prolog.cfaExpression != 0 )
+ return evaluateExpression(prolog.cfaExpression, addressSpace, registers, 0);
+ else
+ ABORT("getCFA(): unknown location for x86_64 cfa");
+}
+
+
+
+template <typename A, typename R>
+compact_unwind_encoding_t DwarfInstructions<A,R>::encodeToUseDwarf(const Registers_x86_64&)
+{
+ return UNWIND_X86_64_MODE_DWARF;
+}
+
+template <typename A, typename R>
+compact_unwind_encoding_t DwarfInstructions<A,R>::encodeToUseDwarf(const Registers_x86&)
+{
+ return UNWIND_X86_MODE_DWARF;
+}
+
+
+
+template <typename A, typename R>
+uint32_t DwarfInstructions<A,R>::getRBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure)
+{
+ if ( (regOffsetFromBaseOffset < 0) || (regOffsetFromBaseOffset > 32) ) {
+ failure = true;
+ return 0;
+ }
+ unsigned int slotIndex = regOffsetFromBaseOffset/8;
+
+ switch ( reg ) {
+ case UNW_X86_64_RBX:
+ return UNWIND_X86_64_REG_RBX << (slotIndex*3);
+ case UNW_X86_64_R12:
+ return UNWIND_X86_64_REG_R12 << (slotIndex*3);
+ case UNW_X86_64_R13:
+ return UNWIND_X86_64_REG_R13 << (slotIndex*3);
+ case UNW_X86_64_R14:
+ return UNWIND_X86_64_REG_R14 << (slotIndex*3);
+ case UNW_X86_64_R15:
+ return UNWIND_X86_64_REG_R15 << (slotIndex*3);
+ }
+
+ // invalid register
+ failure = true;
+ return 0;
+}
+
+
+
+template <typename A, typename R>
+compact_unwind_encoding_t DwarfInstructions<A,R>::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr,
+ const Registers_x86_64& r, const typename CFI_Parser<A>::PrologInfo& prolog,
+ char warningBuffer[1024])
+{
+ warningBuffer[0] = '\0';
+
+ // don't create compact unwind info for unsupported dwarf kinds
+ if ( prolog.registerSavedMoreThanOnce ) {
+ strcpy(warningBuffer, "register saved more than once (might be shrink wrap)");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ if ( prolog.cfaOffsetWasNegative ) {
+ strcpy(warningBuffer, "cfa had negative offset (dwarf might contain epilog)");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ if ( prolog.spExtraArgSize != 0 ) {
+ strcpy(warningBuffer, "dwarf uses DW_CFA_GNU_args_size");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+
+ // figure out which kind of frame this function uses
+ bool standardRBPframe = (
+ (prolog.cfaRegister == UNW_X86_64_RBP)
+ && (prolog.cfaRegisterOffset == 16)
+ && (prolog.savedRegisters[UNW_X86_64_RBP].location == CFI_Parser<A>::kRegisterInCFA)
+ && (prolog.savedRegisters[UNW_X86_64_RBP].value == -16) );
+ bool standardRSPframe = (prolog.cfaRegister == UNW_X86_64_RSP);
+ if ( !standardRBPframe && !standardRSPframe ) {
+ // no compact encoding for this
+ strcpy(warningBuffer, "does not use RBP or RSP based frame");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+
+ // scan which registers are saved
+ int saveRegisterCount = 0;
+ bool rbxSaved = false;
+ bool r12Saved = false;
+ bool r13Saved = false;
+ bool r14Saved = false;
+ bool r15Saved = false;
+ bool rbpSaved = false;
+ for (int i=0; i < 64; ++i) {
+ if ( prolog.savedRegisters[i].location != CFI_Parser<A>::kRegisterUnused ) {
+ if ( prolog.savedRegisters[i].location != CFI_Parser<A>::kRegisterInCFA ) {
+ sprintf(warningBuffer, "register %d saved somewhere other that in frame", i);
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ switch (i) {
+ case UNW_X86_64_RBX:
+ rbxSaved = true;
+ ++saveRegisterCount;
+ break;
+ case UNW_X86_64_R12:
+ r12Saved = true;
+ ++saveRegisterCount;
+ break;
+ case UNW_X86_64_R13:
+ r13Saved = true;
+ ++saveRegisterCount;
+ break;
+ case UNW_X86_64_R14:
+ r14Saved = true;
+ ++saveRegisterCount;
+ break;
+ case UNW_X86_64_R15:
+ r15Saved = true;
+ ++saveRegisterCount;
+ break;
+ case UNW_X86_64_RBP:
+ rbpSaved = true;
+ ++saveRegisterCount;
+ break;
+ case DW_X86_64_RET_ADDR:
+ break;
+ default:
+ sprintf(warningBuffer, "non-standard register %d being saved in prolog", i);
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ }
+ }
+ const int64_t cfaOffsetRBX = prolog.savedRegisters[UNW_X86_64_RBX].value;
+ const int64_t cfaOffsetR12 = prolog.savedRegisters[UNW_X86_64_R12].value;
+ const int64_t cfaOffsetR13 = prolog.savedRegisters[UNW_X86_64_R13].value;
+ const int64_t cfaOffsetR14 = prolog.savedRegisters[UNW_X86_64_R14].value;
+ const int64_t cfaOffsetR15 = prolog.savedRegisters[UNW_X86_64_R15].value;
+ const int64_t cfaOffsetRBP = prolog.savedRegisters[UNW_X86_64_RBP].value;
+
+ // encode standard RBP frames
+ compact_unwind_encoding_t encoding = 0;
+ if ( standardRBPframe ) {
+ // | |
+ // +--------------+ <- CFA
+ // | ret addr |
+ // +--------------+
+ // | rbp |
+ // +--------------+ <- rbp
+ // ~ ~
+ // +--------------+
+ // | saved reg3 |
+ // +--------------+ <- CFA - offset+16
+ // | saved reg2 |
+ // +--------------+ <- CFA - offset+8
+ // | saved reg1 |
+ // +--------------+ <- CFA - offset
+ // | |
+ // +--------------+
+ // | |
+ // <- rsp
+ //
+ encoding = UNWIND_X86_64_MODE_RBP_FRAME;
+
+ // find save location of farthest register from rbp
+ int furthestCfaOffset = 0;
+ if ( rbxSaved & (cfaOffsetRBX < furthestCfaOffset) )
+ furthestCfaOffset = cfaOffsetRBX;
+ if ( r12Saved & (cfaOffsetR12 < furthestCfaOffset) )
+ furthestCfaOffset = cfaOffsetR12;
+ if ( r13Saved & (cfaOffsetR13 < furthestCfaOffset) )
+ furthestCfaOffset = cfaOffsetR13;
+ if ( r14Saved & (cfaOffsetR14 < furthestCfaOffset) )
+ furthestCfaOffset = cfaOffsetR14;
+ if ( r15Saved & (cfaOffsetR15 < furthestCfaOffset) )
+ furthestCfaOffset = cfaOffsetR15;
+
+ if ( furthestCfaOffset == 0 ) {
+ // no registers saved, nothing more to encode
+ return encoding;
+ }
+
+ // add stack offset to encoding
+ int rbpOffset = furthestCfaOffset + 16;
+ int encodedOffset = rbpOffset/(-8);
+ if ( encodedOffset > 255 ) {
+ strcpy(warningBuffer, "offset of saved registers too far to encode");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ encoding |= (encodedOffset << __builtin_ctz(UNWIND_X86_64_RBP_FRAME_OFFSET));
+
+ // add register saved from each stack location
+ bool encodingFailure = false;
+ if ( rbxSaved )
+ encoding |= getRBPEncodedRegister(UNW_X86_64_RBX, cfaOffsetRBX - furthestCfaOffset, encodingFailure);
+ if ( r12Saved )
+ encoding |= getRBPEncodedRegister(UNW_X86_64_R12, cfaOffsetR12 - furthestCfaOffset, encodingFailure);
+ if ( r13Saved )
+ encoding |= getRBPEncodedRegister(UNW_X86_64_R13, cfaOffsetR13 - furthestCfaOffset, encodingFailure);
+ if ( r14Saved )
+ encoding |= getRBPEncodedRegister(UNW_X86_64_R14, cfaOffsetR14 - furthestCfaOffset, encodingFailure);
+ if ( r15Saved )
+ encoding |= getRBPEncodedRegister(UNW_X86_64_R15, cfaOffsetR15 - furthestCfaOffset, encodingFailure);
+
+ if ( encodingFailure ){
+ strcpy(warningBuffer, "saved registers not contiguous");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+
+ return encoding;
+ }
+ else {
+ // | |
+ // +--------------+ <- CFA
+ // | ret addr |
+ // +--------------+
+ // | saved reg1 |
+ // +--------------+ <- CFA - 16
+ // | saved reg2 |
+ // +--------------+ <- CFA - 24
+ // | saved reg3 |
+ // +--------------+ <- CFA - 32
+ // | saved reg4 |
+ // +--------------+ <- CFA - 40
+ // | saved reg5 |
+ // +--------------+ <- CFA - 48
+ // | saved reg6 |
+ // +--------------+ <- CFA - 56
+ // | |
+ // <- esp
+ //
+
+ // for RSP based frames we need to encode stack size in unwind info
+ encoding = UNWIND_X86_64_MODE_STACK_IMMD;
+ uint64_t stackValue = prolog.cfaRegisterOffset / 8;
+ uint32_t stackAdjust = 0;
+ bool immedStackSize = true;
+ const uint32_t stackMaxImmedValue = EXTRACT_BITS(0xFFFFFFFF,UNWIND_X86_64_FRAMELESS_STACK_SIZE);
+ if ( stackValue > stackMaxImmedValue ) {
+ // stack size is too big to fit as an immediate value, so encode offset of subq instruction in function
+ pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4;
+ uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns);
+ stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/8;
+ stackValue = functionContentAdjustStackIns - funcAddr;
+ immedStackSize = false;
+ if ( stackAdjust > 7 ) {
+ strcpy(warningBuffer, "stack subq instruction is too different from dwarf stack size");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ encoding = UNWIND_X86_64_MODE_STACK_IND;
+ }
+
+
+ // validate that saved registers are all within 6 slots abutting return address
+ int registers[6];
+ for (int i=0; i < 6;++i)
+ registers[i] = 0;
+ if ( r15Saved ) {
+ if ( cfaOffsetR15 < -56 ) {
+ strcpy(warningBuffer, "r15 is saved too far from return address");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ registers[(cfaOffsetR15+56)/8] = UNWIND_X86_64_REG_R15;
+ }
+ if ( r14Saved ) {
+ if ( cfaOffsetR14 < -56 ) {
+ strcpy(warningBuffer, "r14 is saved too far from return address");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ registers[(cfaOffsetR14+56)/8] = UNWIND_X86_64_REG_R14;
+ }
+ if ( r13Saved ) {
+ if ( cfaOffsetR13 < -56 ) {
+ strcpy(warningBuffer, "r13 is saved too far from return address");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ registers[(cfaOffsetR13+56)/8] = UNWIND_X86_64_REG_R13;
+ }
+ if ( r12Saved ) {
+ if ( cfaOffsetR12 < -56 ) {
+ strcpy(warningBuffer, "r12 is saved too far from return address");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ registers[(cfaOffsetR12+56)/8] = UNWIND_X86_64_REG_R12;
+ }
+ if ( rbxSaved ) {
+ if ( cfaOffsetRBX < -56 ) {
+ strcpy(warningBuffer, "rbx is saved too far from return address");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ registers[(cfaOffsetRBX+56)/8] = UNWIND_X86_64_REG_RBX;
+ }
+ if ( rbpSaved ) {
+ if ( cfaOffsetRBP < -56 ) {
+ strcpy(warningBuffer, "rbp is saved too far from return address");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ registers[(cfaOffsetRBP+56)/8] = UNWIND_X86_64_REG_RBP;
+ }
+
+ // validate that saved registers are contiguous and abut return address on stack
+ for (int i=0; i < saveRegisterCount; ++i) {
+ if ( registers[5-i] == 0 ) {
+ strcpy(warningBuffer, "registers not save contiguously in stack");
+ return UNWIND_X86_64_MODE_DWARF;
+ }
+ }
+
+ // encode register permutation
+ // the 10-bits are encoded differently depending on the number of registers saved
+ int renumregs[6];
+ for (int i=6-saveRegisterCount; i < 6; ++i) {
+ int countless = 0;
+ for (int j=6-saveRegisterCount; j < i; ++j) {
+ if ( registers[j] < registers[i] )
+ ++countless;
+ }
+ renumregs[i] = registers[i] - countless -1;
+ }
+ uint32_t permutationEncoding = 0;
+ switch ( saveRegisterCount ) {
+ case 6:
+ permutationEncoding |= (120*renumregs[0] + 24*renumregs[1] + 6*renumregs[2] + 2*renumregs[3] + renumregs[4]);
+ break;
+ case 5:
+ permutationEncoding |= (120*renumregs[1] + 24*renumregs[2] + 6*renumregs[3] + 2*renumregs[4] + renumregs[5]);
+ break;
+ case 4:
+ permutationEncoding |= (60*renumregs[2] + 12*renumregs[3] + 3*renumregs[4] + renumregs[5]);
+ break;
+ case 3:
+ permutationEncoding |= (20*renumregs[3] + 4*renumregs[4] + renumregs[5]);
+ break;
+ case 2:
+ permutationEncoding |= (5*renumregs[4] + renumregs[5]);
+ break;
+ case 1:
+ permutationEncoding |= (renumregs[5]);
+ break;
+ }
+
+ encoding |= (stackValue << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_SIZE));
+ encoding |= (stackAdjust << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_ADJUST));
+ encoding |= (saveRegisterCount << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT));
+ encoding |= (permutationEncoding << __builtin_ctz(UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION));
+ return encoding;
+ }
+}
+
+
+
+
+//
+// x86 specific functions
+//
+template <typename A, typename R>
+int DwarfInstructions<A,R>::lastRestoreReg(const Registers_x86&)
+{
+ COMPILE_TIME_ASSERT( (int)CFI_Parser<A>::kMaxRegisterNumber > (int)DW_X86_RET_ADDR );
+ return DW_X86_RET_ADDR;
+}
+
+template <typename A, typename R>
+bool DwarfInstructions<A,R>::isReturnAddressRegister(int regNum, const Registers_x86&)
+{
+ return (regNum == DW_X86_RET_ADDR);
+}
+
+template <typename A, typename R>
+typename A::pint_t DwarfInstructions<A,R>::getCFA(A& addressSpace, const typename CFI_Parser<A>::PrologInfo& prolog,
+ const Registers_x86& registers)
+{
+ if ( prolog.cfaRegister != 0 )
+ return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset;
+ else if ( prolog.cfaExpression != 0 )
+ return evaluateExpression(prolog.cfaExpression, addressSpace, registers, 0);
+ else
+ ABORT("getCFA(): unknown location for x86 cfa");
+}
+
+
+
+
+
+template <typename A, typename R>
+uint32_t DwarfInstructions<A,R>::getEBPEncodedRegister(uint32_t reg, int32_t regOffsetFromBaseOffset, bool& failure)
+{
+ if ( (regOffsetFromBaseOffset < 0) || (regOffsetFromBaseOffset > 16) ) {
+ failure = true;
+ return 0;
+ }
+ unsigned int slotIndex = regOffsetFromBaseOffset/4;
+
+ switch ( reg ) {
+ case UNW_X86_EBX:
+ return UNWIND_X86_REG_EBX << (slotIndex*3);
+ case UNW_X86_ECX:
+ return UNWIND_X86_REG_ECX << (slotIndex*3);
+ case UNW_X86_EDX:
+ return UNWIND_X86_REG_EDX << (slotIndex*3);
+ case UNW_X86_EDI:
+ return UNWIND_X86_REG_EDI << (slotIndex*3);
+ case UNW_X86_ESI:
+ return UNWIND_X86_REG_ESI << (slotIndex*3);
+ }
+
+ // invalid register
+ failure = true;
+ return 0;
+}
+
+template <typename A, typename R>
+compact_unwind_encoding_t DwarfInstructions<A,R>::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr,
+ const Registers_x86& r, const typename CFI_Parser<A>::PrologInfo& prolog,
+ char warningBuffer[1024])
+{
+ warningBuffer[0] = '\0';
+
+ // don't create compact unwind info for unsupported dwarf kinds
+ if ( prolog.registerSavedMoreThanOnce ) {
+ strcpy(warningBuffer, "register saved more than once (might be shrink wrap)");
+ return UNWIND_X86_MODE_DWARF;
+ }
+ if ( prolog.spExtraArgSize != 0 ) {
+ strcpy(warningBuffer, "dwarf uses DW_CFA_GNU_args_size");
+ return UNWIND_X86_MODE_DWARF;
+ }
+
+ // figure out which kind of frame this function uses
+ bool standardEBPframe = (
+ (prolog.cfaRegister == UNW_X86_EBP)
+ && (prolog.cfaRegisterOffset == 8)
+ && (prolog.savedRegisters[UNW_X86_EBP].location == CFI_Parser<A>::kRegisterInCFA)
+ && (prolog.savedRegisters[UNW_X86_EBP].value == -8) );
+ bool standardESPframe = (prolog.cfaRegister == UNW_X86_ESP);
+ if ( !standardEBPframe && !standardESPframe ) {
+ // no compact encoding for this
+ strcpy(warningBuffer, "does not use EBP or ESP based frame");
+ return UNWIND_X86_MODE_DWARF;
+ }
+
+ // scan which registers are saved
+ int saveRegisterCount = 0;
+ bool ebxSaved = false;
+ bool ecxSaved = false;
+ bool edxSaved = false;
+ bool esiSaved = false;
+ bool ediSaved = false;
+ bool ebpSaved = false;
+ for (int i=0; i < 64; ++i) {
+ if ( prolog.savedRegisters[i].location != CFI_Parser<A>::kRegisterUnused ) {
+ if ( prolog.savedRegisters[i].location != CFI_Parser<A>::kRegisterInCFA ) {
+ sprintf(warningBuffer, "register %d saved somewhere other that in frame", i);
+ return UNWIND_X86_MODE_DWARF;
+ }
+ switch (i) {
+ case UNW_X86_EBX:
+ ebxSaved = true;
+ ++saveRegisterCount;
+ break;
+ case UNW_X86_ECX:
+ ecxSaved = true;
+ ++saveRegisterCount;
+ break;
+ case UNW_X86_EDX:
+ edxSaved = true;
+ ++saveRegisterCount;
+ break;
+ case UNW_X86_ESI:
+ esiSaved = true;
+ ++saveRegisterCount;
+ break;
+ case UNW_X86_EDI:
+ ediSaved = true;
+ ++saveRegisterCount;
+ break;
+ case UNW_X86_EBP:
+ ebpSaved = true;
+ ++saveRegisterCount;
+ break;
+ case DW_X86_RET_ADDR:
+ break;
+ default:
+ sprintf(warningBuffer, "non-standard register %d being saved in prolog", i);
+ return UNWIND_X86_MODE_DWARF;
+ }
+ }
+ }
+ const int32_t cfaOffsetEBX = prolog.savedRegisters[UNW_X86_EBX].value;
+ const int32_t cfaOffsetECX = prolog.savedRegisters[UNW_X86_ECX].value;
+ const int32_t cfaOffsetEDX = prolog.savedRegisters[UNW_X86_EDX].value;
+ const int32_t cfaOffsetEDI = prolog.savedRegisters[UNW_X86_EDI].value;
+ const int32_t cfaOffsetESI = prolog.savedRegisters[UNW_X86_ESI].value;
+ const int32_t cfaOffsetEBP = prolog.savedRegisters[UNW_X86_EBP].value;
+
+ // encode standard RBP frames
+ compact_unwind_encoding_t encoding = 0;
+ if ( standardEBPframe ) {
+ // | |
+ // +--------------+ <- CFA
+ // | ret addr |
+ // +--------------+
+ // | ebp |
+ // +--------------+ <- ebp
+ // ~ ~
+ // +--------------+
+ // | saved reg3 |
+ // +--------------+ <- CFA - offset+8
+ // | saved reg2 |
+ // +--------------+ <- CFA - offset+e
+ // | saved reg1 |
+ // +--------------+ <- CFA - offset
+ // | |
+ // +--------------+
+ // | |
+ // <- esp
+ //
+ encoding = UNWIND_X86_MODE_EBP_FRAME;
+
+ // find save location of farthest register from ebp
+ int furthestCfaOffset = 0;
+ if ( ebxSaved & (cfaOffsetEBX < furthestCfaOffset) )
+ furthestCfaOffset = cfaOffsetEBX;
+ if ( ecxSaved & (cfaOffsetECX < furthestCfaOffset) )
+ furthestCfaOffset = cfaOffsetECX;
+ if ( edxSaved & (cfaOffsetEDX < furthestCfaOffset) )
+ furthestCfaOffset = cfaOffsetEDX;
+ if ( ediSaved & (cfaOffsetEDI < furthestCfaOffset) )
+ furthestCfaOffset = cfaOffsetEDI;
+ if ( esiSaved & (cfaOffsetESI < furthestCfaOffset) )
+ furthestCfaOffset = cfaOffsetESI;
+
+ if ( furthestCfaOffset == 0 ) {
+ // no registers saved, nothing more to encode
+ return encoding;
+ }
+
+ // add stack offset to encoding
+ int ebpOffset = furthestCfaOffset + 8;
+ int encodedOffset = ebpOffset/(-4);
+ if ( encodedOffset > 255 ) {
+ strcpy(warningBuffer, "offset of saved registers too far to encode");
+ return UNWIND_X86_MODE_DWARF;
+ }
+ encoding |= (encodedOffset << __builtin_ctz(UNWIND_X86_EBP_FRAME_OFFSET));
+
+ // add register saved from each stack location
+ bool encodingFailure = false;
+ if ( ebxSaved )
+ encoding |= getEBPEncodedRegister(UNW_X86_EBX, cfaOffsetEBX - furthestCfaOffset, encodingFailure);
+ if ( ecxSaved )
+ encoding |= getEBPEncodedRegister(UNW_X86_ECX, cfaOffsetECX - furthestCfaOffset, encodingFailure);
+ if ( edxSaved )
+ encoding |= getEBPEncodedRegister(UNW_X86_EDX, cfaOffsetEDX - furthestCfaOffset, encodingFailure);
+ if ( ediSaved )
+ encoding |= getEBPEncodedRegister(UNW_X86_EDI, cfaOffsetEDI - furthestCfaOffset, encodingFailure);
+ if ( esiSaved )
+ encoding |= getEBPEncodedRegister(UNW_X86_ESI, cfaOffsetESI - furthestCfaOffset, encodingFailure);
+
+ if ( encodingFailure ){
+ strcpy(warningBuffer, "saved registers not contiguous");
+ return UNWIND_X86_MODE_DWARF;
+ }
+
+ return encoding;
+ }
+ else {
+ // | |
+ // +--------------+ <- CFA
+ // | ret addr |
+ // +--------------+
+ // | saved reg1 |
+ // +--------------+ <- CFA - 8
+ // | saved reg2 |
+ // +--------------+ <- CFA - 12
+ // | saved reg3 |
+ // +--------------+ <- CFA - 16
+ // | saved reg4 |
+ // +--------------+ <- CFA - 20
+ // | saved reg5 |
+ // +--------------+ <- CFA - 24
+ // | saved reg6 |
+ // +--------------+ <- CFA - 28
+ // | |
+ // <- esp
+ //
+
+ // for ESP based frames we need to encode stack size in unwind info
+ encoding = UNWIND_X86_MODE_STACK_IMMD;
+ uint64_t stackValue = prolog.cfaRegisterOffset / 4;
+ uint32_t stackAdjust = 0;
+ bool immedStackSize = true;
+ const uint32_t stackMaxImmedValue = EXTRACT_BITS(0xFFFFFFFF,UNWIND_X86_FRAMELESS_STACK_SIZE);
+ if ( stackValue > stackMaxImmedValue ) {
+ // stack size is too big to fit as an immediate value, so encode offset of subq instruction in function
+ pint_t functionContentAdjustStackIns = funcAddr + prolog.codeOffsetAtStackDecrement - 4;
+ uint32_t stackDecrementInCode = addressSpace.get32(functionContentAdjustStackIns);
+ stackAdjust = (prolog.cfaRegisterOffset - stackDecrementInCode)/4;
+ stackValue = functionContentAdjustStackIns - funcAddr;
+ immedStackSize = false;
+ if ( stackAdjust > 7 ) {
+ strcpy(warningBuffer, "stack subq instruction is too different from dwarf stack size");
+ return UNWIND_X86_MODE_DWARF;
+ }
+ encoding = UNWIND_X86_MODE_STACK_IND;
+ }
+
+
+ // validate that saved registers are all within 6 slots abutting return address
+ int registers[6];
+ for (int i=0; i < 6;++i)
+ registers[i] = 0;
+ if ( ebxSaved ) {
+ if ( cfaOffsetEBX < -28 ) {
+ strcpy(warningBuffer, "ebx is saved too far from return address");
+ return UNWIND_X86_MODE_DWARF;
+ }
+ registers[(cfaOffsetEBX+28)/4] = UNWIND_X86_REG_EBX;
+ }
+ if ( ecxSaved ) {
+ if ( cfaOffsetECX < -28 ) {
+ strcpy(warningBuffer, "ecx is saved too far from return address");
+ return UNWIND_X86_MODE_DWARF;
+ }
+ registers[(cfaOffsetECX+28)/4] = UNWIND_X86_REG_ECX;
+ }
+ if ( edxSaved ) {
+ if ( cfaOffsetEDX < -28 ) {
+ strcpy(warningBuffer, "edx is saved too far from return address");
+ return UNWIND_X86_MODE_DWARF;
+ }
+ registers[(cfaOffsetEDX+28)/4] = UNWIND_X86_REG_EDX;
+ }
+ if ( ediSaved ) {
+ if ( cfaOffsetEDI < -28 ) {
+ strcpy(warningBuffer, "edi is saved too far from return address");
+ return UNWIND_X86_MODE_DWARF;
+ }
+ registers[(cfaOffsetEDI+28)/4] = UNWIND_X86_REG_EDI;
+ }
+ if ( esiSaved ) {
+ if ( cfaOffsetESI < -28 ) {
+ strcpy(warningBuffer, "esi is saved too far from return address");
+ return UNWIND_X86_MODE_DWARF;
+ }
+ registers[(cfaOffsetESI+28)/4] = UNWIND_X86_REG_ESI;
+ }
+ if ( ebpSaved ) {
+ if ( cfaOffsetEBP < -28 ) {
+ strcpy(warningBuffer, "ebp is saved too far from return address");
+ return UNWIND_X86_MODE_DWARF;
+ }
+ registers[(cfaOffsetEBP+28)/4] = UNWIND_X86_REG_EBP;
+ }
+
+ // validate that saved registers are contiguous and abut return address on stack
+ for (int i=0; i < saveRegisterCount; ++i) {
+ if ( registers[5-i] == 0 ) {
+ strcpy(warningBuffer, "registers not save contiguously in stack");
+ return UNWIND_X86_MODE_DWARF;
+ }
+ }
+
+ // encode register permutation
+ // the 10-bits are encoded differently depending on the number of registers saved
+ int renumregs[6];
+ for (int i=6-saveRegisterCount; i < 6; ++i) {
+ int countless = 0;
+ for (int j=6-saveRegisterCount; j < i; ++j) {
+ if ( registers[j] < registers[i] )
+ ++countless;
+ }
+ renumregs[i] = registers[i] - countless -1;
+ }
+ uint32_t permutationEncoding = 0;
+ switch ( saveRegisterCount ) {
+ case 6:
+ permutationEncoding |= (120*renumregs[0] + 24*renumregs[1] + 6*renumregs[2] + 2*renumregs[3] + renumregs[4]);
+ break;
+ case 5:
+ permutationEncoding |= (120*renumregs[1] + 24*renumregs[2] + 6*renumregs[3] + 2*renumregs[4] + renumregs[5]);
+ break;
+ case 4:
+ permutationEncoding |= (60*renumregs[2] + 12*renumregs[3] + 3*renumregs[4] + renumregs[5]);
+ break;
+ case 3:
+ permutationEncoding |= (20*renumregs[3] + 4*renumregs[4] + renumregs[5]);
+ break;
+ case 2:
+ permutationEncoding |= (5*renumregs[4] + renumregs[5]);
+ break;
+ case 1:
+ permutationEncoding |= (renumregs[5]);
+ break;
+ }
+
+ encoding |= (stackValue << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_SIZE));
+ encoding |= (stackAdjust << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_ADJUST));
+ encoding |= (saveRegisterCount << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_REG_COUNT));
+ encoding |= (permutationEncoding << __builtin_ctz(UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION));
+ return encoding;
+ }
+}
+
+
+
+
+
+
+
+//
+// ppc specific functions
+//
+template <typename A, typename R>
+int DwarfInstructions<A,R>::lastRestoreReg(const Registers_ppc&)
+{
+ COMPILE_TIME_ASSERT( (int)CFI_Parser<A>::kMaxRegisterNumber > (int)UNW_PPC_SPEFSCR );
+ return UNW_PPC_SPEFSCR;
+}
+
+template <typename A, typename R>
+bool DwarfInstructions<A,R>::isReturnAddressRegister(int regNum, const Registers_ppc&)
+{
+ return (regNum == UNW_PPC_LR);
+}
+
+template <typename A, typename R>
+typename A::pint_t DwarfInstructions<A,R>::getCFA(A& addressSpace, const typename CFI_Parser<A>::PrologInfo& prolog,
+ const Registers_ppc& registers)
+{
+ if ( prolog.cfaRegister != 0 )
+ return registers.getRegister(prolog.cfaRegister) + prolog.cfaRegisterOffset;
+ else if ( prolog.cfaExpression != 0 )
+ return evaluateExpression(prolog.cfaExpression, addressSpace, registers, 0);
+ else
+ ABORT("getCFA(): unknown location for ppc cfa");
+}
+
+
+template <typename A, typename R>
+compact_unwind_encoding_t DwarfInstructions<A,R>::encodeToUseDwarf(const Registers_ppc&)
+{
+ return UNWIND_X86_MODE_DWARF;
+}
+
+
+template <typename A, typename R>
+compact_unwind_encoding_t DwarfInstructions<A,R>::createCompactEncodingFromProlog(A& addressSpace, pint_t funcAddr,
+ const Registers_ppc& r, const typename CFI_Parser<A>::PrologInfo& prolog,
+ char warningBuffer[1024])
+{
+ warningBuffer[0] = '\0';
+ return UNWIND_X86_MODE_DWARF;
+}
+
+
+
+
+} // namespace lldb_private
+
+
+#endif // __DWARF_INSTRUCTIONS_HPP__
+
+
+
+
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/DwarfParser.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/DwarfParser.hpp
new file mode 100644
index 00000000000..b11cb8ce441
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/DwarfParser.hpp
@@ -0,0 +1,869 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- DwarfParser.hpp -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+//
+// processor specific parsing of dwarf unwind instructions
+//
+
+#ifndef __DWARF_PARSER_HPP__
+#define __DWARF_PARSER_HPP__
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <vector>
+
+#include "libunwind.h"
+#include "dwarf2.h"
+
+#include "AddressSpace.hpp"
+#include "RemoteUnwindProfile.h"
+
+namespace lldb_private {
+
+
+///
+/// CFI_Parser does basic parsing of a CFI (Call Frame Information) records.
+/// See Dwarf Spec for details:
+/// http://www.linux-foundation.org/spec/booksets/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
+///
+template <typename A>
+class CFI_Parser
+{
+public:
+ typedef typename A::pint_t pint_t;
+
+ ///
+ /// Information encoded in a CIE (Common Information Entry)
+ ///
+ struct CIE_Info {
+ pint_t cieStart;
+ pint_t cieLength;
+ pint_t cieInstructions;
+ uint8_t pointerEncoding;
+ uint8_t lsdaEncoding;
+ uint8_t personalityEncoding;
+ uint8_t personalityOffsetInCIE;
+ pint_t personality;
+ int codeAlignFactor;
+ int dataAlignFactor;
+ bool isSignalFrame;
+ bool fdesHaveAugmentationData;
+ };
+
+ ///
+ /// Information about an FDE (Frame Description Entry)
+ ///
+ struct FDE_Info {
+ pint_t fdeStart;
+ pint_t fdeLength;
+ pint_t fdeInstructions;
+ pint_t pcStart;
+ pint_t pcEnd;
+ pint_t lsda;
+ };
+
+ ///
+ /// Used by linker when parsing __eh_frame section
+ ///
+ struct FDE_Reference {
+ pint_t address;
+ uint32_t offsetInFDE;
+ uint8_t encodingOfAddress;
+ };
+ struct FDE_Atom_Info {
+ pint_t fdeAddress;
+ FDE_Reference function;
+ FDE_Reference cie;
+ FDE_Reference lsda;
+ };
+ struct CIE_Atom_Info {
+ pint_t cieAddress;
+ FDE_Reference personality;
+ };
+
+
+ ///
+ /// Information about a frame layout and registers saved determined
+ /// by "running" the dwarf FDE "instructions"
+ ///
+ enum { kMaxRegisterNumber = 120 };
+ enum RegisterSavedWhere { kRegisterUnused, kRegisterInCFA, kRegisterOffsetFromCFA,
+ kRegisterInRegister, kRegisterAtExpression, kRegisterIsExpression } ;
+ struct RegisterLocation {
+ RegisterSavedWhere location;
+ int64_t value;
+ };
+ struct PrologInfo {
+ uint32_t cfaRegister;
+ int32_t cfaRegisterOffset; // CFA = (cfaRegister)+cfaRegisterOffset
+ int64_t cfaExpression; // CFA = expression
+ bool registersInOtherRegisters;
+ bool registerSavedMoreThanOnce;
+ bool cfaOffsetWasNegative;
+ uint32_t spExtraArgSize;
+ uint32_t codeOffsetAtStackDecrement;
+
+ RegisterLocation savedRegisters[kMaxRegisterNumber]; // from where to restore registers
+ };
+
+ struct PrologInfoStackEntry {
+ PrologInfoStackEntry(PrologInfoStackEntry* n, const PrologInfo& i)
+ : next(n), info(i) {}
+ PrologInfoStackEntry* next;
+ PrologInfo info;
+ };
+
+ static bool findFDE(A& addressSpace, pint_t pc, pint_t ehSectionStart, uint32_t sectionLength, pint_t fdeHint, FDE_Info* fdeInfo, CIE_Info* cieInfo);
+
+#if defined (SUPPORT_REMOTE_UNWINDING)
+ static bool functionFuncBoundsViaFDE(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength, std::vector<FuncBounds> &funcbounds);
+#endif
+
+ static const char* decodeFDE(A& addressSpace, pint_t fdeStart, FDE_Info* fdeInfo, CIE_Info* cieInfo);
+ static bool parseFDEInstructions(A& addressSpace, const FDE_Info& fdeInfo, const CIE_Info& cieInfo, pint_t upToPC, PrologInfo* results);
+ static const char* getCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength,
+ std::vector<FDE_Atom_Info>& fdes, std::vector<CIE_Atom_Info>& cies);
+ static uint32_t getCFICount(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength);
+
+ static const char* parseCIE(A& addressSpace, pint_t cie, CIE_Info* cieInfo);
+
+private:
+ static bool parseInstructions(A& addressSpace, pint_t instructions, pint_t instructionsEnd, const CIE_Info& cieInfo,
+ pint_t pcoffset, PrologInfoStackEntry*& rememberStack, PrologInfo* results);
+
+};
+
+
+///
+/// Parse a FDE into a CIE_Info and an FDE_Info
+///
+template <typename A>
+const char* CFI_Parser<A>::decodeFDE(A& addressSpace, pint_t fdeStart, FDE_Info* fdeInfo, CIE_Info* cieInfo)
+{
+ pint_t p = fdeStart;
+ uint64_t cfiLength = addressSpace.get32(p);
+ p += 4;
+ if ( cfiLength == 0xffffffff ) {
+ // 0xffffffff means length is really next 8 bytes
+ cfiLength = addressSpace.get64(p);
+ p += 8;
+ }
+ if ( cfiLength == 0 )
+ return "FDE has zero length"; // end marker
+ uint32_t ciePointer = addressSpace.get32(p);
+ if ( ciePointer == 0 )
+ return "FDE is really a CIE"; // this is a CIE not an FDE
+ pint_t nextCFI = p + cfiLength;
+ pint_t cieStart = p-ciePointer;
+ const char* err = parseCIE(addressSpace, cieStart, cieInfo);
+ if (err != NULL)
+ return err;
+ p += 4;
+ // parse pc begin and range
+ pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding);
+ pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F);
+ // parse rest of info
+ fdeInfo->lsda = 0;
+ // check for augmentation length
+ if ( cieInfo->fdesHaveAugmentationData ) {
+ uintptr_t augLen = addressSpace.getULEB128(p, nextCFI);
+ pint_t endOfAug = p + augLen;
+ if ( cieInfo->lsdaEncoding != 0 ) {
+ // peek at value (without indirection). Zero means no lsda
+ pint_t lsdaStart = p;
+ if ( addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0 ) {
+ // reset pointer and re-parse lsda address
+ p = lsdaStart;
+ fdeInfo->lsda = addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding);
+ }
+ }
+ p = endOfAug;
+ }
+ fdeInfo->fdeStart = fdeStart;
+ fdeInfo->fdeLength = nextCFI - fdeStart;
+ fdeInfo->fdeInstructions = p;
+ fdeInfo->pcStart = pcStart;
+ fdeInfo->pcEnd = pcStart+pcRange;
+ return NULL; // success
+}
+
+
+///
+/// Scan an eh_frame section to find an FDE for a pc
+///
+template <typename A>
+bool CFI_Parser<A>::findFDE(A& addressSpace, pint_t pc, pint_t ehSectionStart, uint32_t sectionLength, pint_t fdeHint, FDE_Info* fdeInfo, CIE_Info* cieInfo)
+{
+ //fprintf(stderr, "findFDE(0x%llX)\n", (long long)pc);
+ pint_t p = (fdeHint != 0) ? fdeHint : ehSectionStart;
+ const pint_t ehSectionEnd = p + sectionLength;
+ while ( p < ehSectionEnd ) {
+ pint_t currentCFI = p;
+ //fprintf(stderr, "findFDE() CFI at 0x%llX\n", (long long)p);
+ uint64_t cfiLength = addressSpace.get32(p);
+ p += 4;
+ if ( cfiLength == 0xffffffff ) {
+ // 0xffffffff means length is really next 8 bytes
+ cfiLength = addressSpace.get64(p);
+ p += 8;
+ }
+ if ( cfiLength == 0 )
+ return false; // end marker
+ uint32_t id = addressSpace.get32(p);
+ if ( id == 0 ) {
+ // skip over CIEs
+ p += cfiLength;
+ }
+ else {
+ // process FDE to see if it covers pc
+ pint_t nextCFI = p + cfiLength;
+ uint32_t ciePointer = addressSpace.get32(p);
+ pint_t cieStart = p-ciePointer;
+ // validate pointer to CIE is within section
+ if ( (ehSectionStart <= cieStart) && (cieStart < ehSectionEnd) ) {
+ if ( parseCIE(addressSpace, cieStart, cieInfo) == NULL ) {
+ p += 4;
+ // parse pc begin and range
+ pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding);
+ pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F);
+ //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange));
+ // test if pc is within the function this FDE covers
+ if ( (pcStart < pc) && (pc <= pcStart+pcRange) ) {
+ // parse rest of info
+ fdeInfo->lsda = 0;
+ // check for augmentation length
+ if ( cieInfo->fdesHaveAugmentationData ) {
+ uintptr_t augLen = addressSpace.getULEB128(p, nextCFI);
+ pint_t endOfAug = p + augLen;
+ if ( cieInfo->lsdaEncoding != 0 ) {
+ // peek at value (without indirection). Zero means no lsda
+ pint_t lsdaStart = p;
+ if ( addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0 ) {
+ // reset pointer and re-parse lsda address
+ p = lsdaStart;
+ fdeInfo->lsda = addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding);
+ }
+ }
+ p = endOfAug;
+ }
+ fdeInfo->fdeStart = currentCFI;
+ fdeInfo->fdeLength = nextCFI - currentCFI;
+ fdeInfo->fdeInstructions = p;
+ fdeInfo->pcStart = pcStart;
+ fdeInfo->pcEnd = pcStart+pcRange;
+ //fprintf(stderr, "findFDE(pc=0x%llX) found with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pc, (uint64_t)pcStart, (uint64_t)(pcStart+pcRange));
+ return true;
+ }
+ else {
+ //fprintf(stderr, "findFDE(pc=0x%llX) not found with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pc, (uint64_t)pcStart, (uint64_t)(pcStart+pcRange));
+ // pc is not in begin/range, skip this FDE
+ }
+ }
+ else {
+ // malformed CIE, now augmentation describing pc range encoding
+ //fprintf(stderr, "malformed CIE\n");
+ }
+ }
+ else {
+ // malformed FDE. CIE is bad
+ //fprintf(stderr, "malformed FDE, cieStart=0x%llX, ehSectionStart=0x%llX, ehSectionEnd=0x%llX\n",
+ // (uint64_t)cieStart, (uint64_t)ehSectionStart, (uint64_t)ehSectionEnd);
+ }
+ p = nextCFI;
+ }
+ }
+ //fprintf(stderr, "findFDE(pc=0x%llX) not found\n",(uint64_t)pc);
+ return false;
+}
+
+#if defined (SUPPORT_REMOTE_UNWINDING)
+/// Scan an eh_frame section to find all the function start addresses
+/// This is only made for working with libunwind-remote. It copies
+/// the eh_frame section into local memory and steps through it quickly
+/// to find the start addresses of the CFIs.
+///
+template <typename A>
+bool CFI_Parser<A>::functionFuncBoundsViaFDE(A& addressSpace, pint_t ehSectionStart,
+ uint32_t sectionLength, std::vector<FuncBounds> &funcbounds)
+{
+ //fprintf(stderr, "functionFuncBoundsViaFDE(0x%llX)\n", (long long)pc);
+ pint_t p = ehSectionStart;
+ const pint_t ehSectionEnd = p + sectionLength;
+ pint_t lastCieSeen = (pint_t) -1;
+ CIE_Info cieInfo;
+ while ( p < ehSectionEnd ) {
+ //fprintf(stderr, "functionFuncBoundsViaFDE() CFI at 0x%llX\n", (long long)p);
+ uint64_t cfiLength = addressSpace.get32(p);
+ p += 4;
+ if ( cfiLength == 0xffffffff ) {
+ // 0xffffffff means length is really next 8 bytes
+ cfiLength = addressSpace.get64(p);
+ p += 8;
+ }
+ if ( cfiLength == 0 )
+ return false; // end marker
+ uint32_t id = addressSpace.get32(p);
+ if ( id == 0 ) {
+ // skip over CIEs
+ p += cfiLength;
+ }
+ else {
+ // process FDE to see if it covers pc
+ pint_t nextCFI = p + cfiLength;
+ uint32_t ciePointer = addressSpace.get32(p);
+ pint_t cieStart = p-ciePointer;
+ // validate pointer to CIE is within section
+ if ( (ehSectionStart <= cieStart) && (cieStart < ehSectionEnd) ) {
+ const char *errmsg;
+ // don't re-parse the cie if this fde is pointing to one we already parsed
+ if (cieStart == lastCieSeen) {
+ errmsg = NULL;
+ }
+ else {
+ errmsg = parseCIE(addressSpace, cieStart, &cieInfo);
+ if (errmsg == NULL)
+ lastCieSeen = cieStart;
+ }
+ if ( errmsg == NULL ) {
+ p += 4;
+ // parse pc begin and range
+ pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding);
+ pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding & 0x0F);
+ //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange));
+ funcbounds.push_back(FuncBounds(pcStart, pcStart + pcRange));
+ }
+ else {
+ // malformed CIE, now augmentation describing pc range encoding
+ //fprintf(stderr, "malformed CIE\n");
+ return false;
+ }
+ }
+ else {
+ // malformed FDE. CIE is bad
+ //fprintf(stderr, "malformed FDE, cieStart=0x%llX, ehSectionStart=0x%llX, ehSectionEnd=0x%llX\n",
+ // (uint64_t)cieStart, (uint64_t)ehSectionStart, (uint64_t)ehSectionEnd);
+ return false;
+ }
+ p = nextCFI;
+ }
+ }
+ return true;
+}
+#endif // SUPPORT_REMOTE_UNWINDING
+
+
+
+///
+/// Extract info from a CIE
+///
+template <typename A>
+const char* CFI_Parser<A>::parseCIE(A& addressSpace, pint_t cie, CIE_Info* cieInfo)
+{
+ //fprintf(stderr, "parseCIE(0x%llX)\n", (long long)cie);
+ cieInfo->pointerEncoding = 0;
+ cieInfo->lsdaEncoding = 0;
+ cieInfo->personalityEncoding = 0;
+ cieInfo->personalityOffsetInCIE = 0;
+ cieInfo->personality = 0;
+ cieInfo->codeAlignFactor = 0;
+ cieInfo->dataAlignFactor = 0;
+ cieInfo->isSignalFrame = false;
+ cieInfo->fdesHaveAugmentationData = false;
+ cieInfo->cieStart = cie;
+ pint_t p = cie;
+ uint64_t cieLength = addressSpace.get32(p);
+ p += 4;
+ pint_t cieContentEnd = p + cieLength;
+ if ( cieLength == 0xffffffff ) {
+ // 0xffffffff means length is really next 8 bytes
+ cieLength = addressSpace.get64(p);
+ p += 8;
+ cieContentEnd = p + cieLength;
+ }
+ if ( cieLength == 0 )
+ return false;
+ // CIE ID is always 0
+ if ( addressSpace.get32(p) != 0 )
+ return "CIE ID is not zero";
+ p += 4;
+ // Version is always 1 or 3
+ uint8_t version = addressSpace.get8(p);
+ if ( (version != 1) && (version != 3) )
+ return "CIE version is not 1 or 3";
+ ++p;
+ // save start of augmentation string and find end
+ pint_t strStart = p;
+ while ( addressSpace.get8(p) != 0 )
+ ++p;
+ ++p;
+ // parse code aligment factor
+ cieInfo->codeAlignFactor = addressSpace.getULEB128(p, cieContentEnd);
+ // parse data alignment factor
+ cieInfo->dataAlignFactor = addressSpace.getSLEB128(p, cieContentEnd);
+ // parse return address register
+ addressSpace.getULEB128(p, cieContentEnd);
+ // parse augmentation data based on augmentation string
+ const char* result = NULL;
+ if ( addressSpace.get8(strStart) == 'z' ) {
+ // parse augmentation data length
+ addressSpace.getULEB128(p, cieContentEnd);
+ for (pint_t s=strStart; addressSpace.get8(s) != '\0'; ++s) {
+ switch ( addressSpace.get8(s) ) {
+ case 'z':
+ cieInfo->fdesHaveAugmentationData = true;
+ break;
+ case 'P':
+ cieInfo->personalityEncoding = addressSpace.get8(p);
+ ++p;
+ cieInfo->personalityOffsetInCIE = p-cie;
+ cieInfo->personality = addressSpace.getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding);
+ break;
+ case 'L':
+ cieInfo->lsdaEncoding = addressSpace.get8(p);
+ ++p;
+ break;
+ case 'R':
+ cieInfo->pointerEncoding = addressSpace.get8(p);
+ ++p;
+ break;
+ case 'S':
+ cieInfo->isSignalFrame = true;
+ break;
+ default:
+ // ignore unknown letters
+ break;
+ }
+ }
+ }
+ cieInfo->cieLength = cieContentEnd - cieInfo->cieStart;
+ cieInfo->cieInstructions = p;
+ return result;
+}
+
+
+template <typename A>
+uint32_t CFI_Parser<A>::getCFICount(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength)
+{
+ uint32_t count = 0;
+ const pint_t ehSectionEnd = ehSectionStart + sectionLength;
+ for (pint_t p=ehSectionStart; p < ehSectionEnd; ) {
+ uint64_t cfiLength = addressSpace.get32(p);
+ p += 4;
+ if ( cfiLength == 0xffffffff ) {
+ // 0xffffffff means length is really next 8 bytes
+ cfiLength = addressSpace.get64(p);
+ p += 8;
+ }
+ if ( cfiLength == 0 )
+ return count; // end marker
+ ++count;
+ p += cfiLength;
+ }
+ return count;
+}
+
+
+
+template <typename A>
+const char* CFI_Parser<A>::getCFIs(A& addressSpace, pint_t ehSectionStart, uint32_t sectionLength,
+ std::vector<FDE_Atom_Info>& fdes, std::vector<CIE_Atom_Info>& cies)
+{
+ const pint_t ehSectionEnd = ehSectionStart + sectionLength;
+ for (pint_t p=ehSectionStart; p < ehSectionEnd; ) {
+ pint_t currentCFI = p;
+ uint64_t cfiLength = addressSpace.get32(p);
+ p += 4;
+ if ( cfiLength == 0xffffffff ) {
+ // 0xffffffff means length is really next 8 bytes
+ cfiLength = addressSpace.get64(p);
+ p += 8;
+ }
+ if ( cfiLength == 0 )
+ return NULL; // end marker
+ uint32_t id = addressSpace.get32(p);
+ if ( id == 0 ) {
+ // is CIE
+ CIE_Info cieInfo;
+ const char* err = parseCIE(addressSpace, currentCFI, &cieInfo);
+ if ( err != NULL )
+ return err;
+ CIE_Atom_Info entry;
+ entry.cieAddress = currentCFI;
+ entry.personality.address = cieInfo.personality;
+ entry.personality.offsetInFDE = cieInfo.personalityOffsetInCIE;
+ entry.personality.encodingOfAddress = cieInfo.personalityEncoding;
+ cies.push_back(entry);
+ p += cfiLength;
+ }
+ else {
+ // is FDE
+ FDE_Atom_Info entry;
+ entry.fdeAddress = currentCFI;
+ entry.function.address = 0;
+ entry.cie.address = 0;
+ entry.lsda.address = 0;
+ pint_t nextCFI = p + cfiLength;
+ uint32_t ciePointer = addressSpace.get32(p);
+ pint_t cieStart = p-ciePointer;
+ // validate pointer to CIE is within section
+ if ( (cieStart < ehSectionStart) || (cieStart > ehSectionEnd) )
+ return "FDE points to CIE outside __eh_frame section";
+ CIE_Info cieInfo;
+ const char* err = parseCIE(addressSpace, cieStart, &cieInfo);
+ if ( err != NULL )
+ return err;
+ entry.cie.address = cieStart;
+ entry.cie.offsetInFDE = p-currentCFI;
+ entry.cie.encodingOfAddress = DW_EH_PE_sdata4 | DW_EH_PE_pcrel;
+ p += 4;
+ // parse pc begin and range
+ pint_t offsetOfFunctionAddress = p-currentCFI;
+ pint_t pcStart = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding);
+ pint_t pcRange = addressSpace.getEncodedP(p, nextCFI, cieInfo.pointerEncoding & 0x0F);
+ //fprintf(stderr, "FDE with pcRange [0x%08llX, 0x%08llX)\n",(uint64_t)pcStart, (uint64_t)(pcStart+pcRange));
+ // test if pc is within the function this FDE covers
+ entry.function.address = pcStart;
+ entry.function.offsetInFDE = offsetOfFunctionAddress;
+ entry.function.encodingOfAddress = cieInfo.pointerEncoding;
+ // skip over augmentation length
+ if ( cieInfo.fdesHaveAugmentationData ) {
+ uintptr_t augLen = addressSpace.getULEB128(p, nextCFI);
+ pint_t endOfAug = p + augLen;
+ if ( (cieInfo.lsdaEncoding != 0) && (addressSpace.getP(p) != 0) ) {
+ pint_t offsetOfLSDAAddress = p-currentCFI;
+ entry.lsda.address = addressSpace.getEncodedP(p, nextCFI, cieInfo.lsdaEncoding);
+ entry.lsda.offsetInFDE = offsetOfLSDAAddress;
+ entry.lsda.encodingOfAddress = cieInfo.lsdaEncoding;
+ }
+ p = endOfAug;
+ }
+ fdes.push_back(entry);
+ p = nextCFI;
+ }
+ }
+ return NULL; // success
+}
+
+
+
+///
+/// "run" the dwarf instructions and create the abstact PrologInfo for an FDE
+///
+template <typename A>
+bool CFI_Parser<A>::parseFDEInstructions(A& addressSpace, const FDE_Info& fdeInfo, const CIE_Info& cieInfo, pint_t upToPC, PrologInfo* results)
+{
+ // clear results
+ bzero(results, sizeof(PrologInfo));
+ PrologInfoStackEntry* rememberStack = NULL;
+
+ // parse CIE then FDE instructions
+ return parseInstructions(addressSpace, cieInfo.cieInstructions, cieInfo.cieStart+cieInfo.cieLength,
+ cieInfo, (pint_t)(-1), rememberStack, results)
+ && parseInstructions(addressSpace, fdeInfo.fdeInstructions, fdeInfo.fdeStart+fdeInfo.fdeLength,
+ cieInfo, upToPC-fdeInfo.pcStart, rememberStack, results);
+}
+
+
+///
+/// "run" the dwarf instructions
+///
+template <typename A>
+bool CFI_Parser<A>::parseInstructions(A& addressSpace, pint_t instructions, pint_t instructionsEnd, const CIE_Info& cieInfo,
+ pint_t pcoffset, PrologInfoStackEntry*& rememberStack, PrologInfo* results)
+{
+ const bool logDwarf = false;
+ pint_t p = instructions;
+ uint32_t codeOffset = 0;
+ PrologInfo initialState = *results;
+
+ // see Dwarf Spec, section 6.4.2 for details on unwind opcodes
+ while ( (p < instructionsEnd) && (codeOffset < pcoffset) ) {
+ uint64_t reg;
+ uint64_t reg2;
+ int64_t offset;
+ uint64_t length;
+ uint8_t opcode = addressSpace.get8(p);
+ uint8_t operand;
+ PrologInfoStackEntry* entry;
+ ++p;
+ switch (opcode) {
+ case DW_CFA_nop:
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_nop\n");
+ break;
+ case DW_CFA_set_loc:
+ codeOffset = addressSpace.getEncodedP(p, instructionsEnd, cieInfo.pointerEncoding);
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_set_loc\n");
+ break;
+ case DW_CFA_advance_loc1:
+ codeOffset += (addressSpace.get8(p) * cieInfo.codeAlignFactor);
+ p += 1;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc1: new offset=%u\n", codeOffset);
+ break;
+ case DW_CFA_advance_loc2:
+ codeOffset += (addressSpace.get16(p) * cieInfo.codeAlignFactor);
+ p += 2;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc2: new offset=%u\n", codeOffset);
+ break;
+ case DW_CFA_advance_loc4:
+ codeOffset += (addressSpace.get32(p) * cieInfo.codeAlignFactor);
+ p += 4;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc4: new offset=%u\n", codeOffset);
+ break;
+ case DW_CFA_offset_extended:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_offset_extended dwarf unwind, reg too big\n");
+ return false;
+ }
+ if ( results->savedRegisters[reg].location != kRegisterUnused )
+ results->registerSavedMoreThanOnce = true;
+ results->savedRegisters[reg].location = kRegisterInCFA;
+ results->savedRegisters[reg].value = offset;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_offset_extended(reg=%lld, offset=%lld)\n", reg, offset);
+ break;
+ case DW_CFA_restore_extended:
+ reg = addressSpace.getULEB128(p, instructionsEnd);;
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_restore_extended dwarf unwind, reg too big\n");
+ return false;
+ }
+ results->savedRegisters[reg] = initialState.savedRegisters[reg];
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_restore_extended(reg=%lld)\n", reg);
+ break;
+ case DW_CFA_undefined:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_undefined dwarf unwind, reg too big\n");
+ return false;
+ }
+ results->savedRegisters[reg].location = kRegisterUnused;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_undefined(reg=%lld)\n", reg);
+ break;
+ case DW_CFA_same_value:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_same_value dwarf unwind, reg too big\n");
+ return false;
+ }
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_same_value(reg=%lld)\n", reg);
+ break;
+ case DW_CFA_register:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ reg2 = addressSpace.getULEB128(p, instructionsEnd);
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_register dwarf unwind, reg too big\n");
+ return false;
+ }
+ if ( reg2 > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_register dwarf unwind, reg2 too big\n");
+ return false;
+ }
+ results->savedRegisters[reg].location = kRegisterInRegister;
+ results->savedRegisters[reg].value = reg2;
+ results->registersInOtherRegisters = true;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_register(reg=%lld, reg2=%lld)\n", reg, reg2);
+ break;
+ case DW_CFA_remember_state:
+ // avoid operator new, because that would be an upward dependency
+ entry = (PrologInfoStackEntry*)malloc(sizeof(PrologInfoStackEntry));
+ if ( entry != NULL ) {
+ entry->next = rememberStack;
+ entry->info = *results;
+ rememberStack = entry;
+ }
+ else {
+ return false;
+ }
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_remember_state\n");
+ break;
+ case DW_CFA_restore_state:
+ if ( rememberStack != NULL ) {
+ PrologInfoStackEntry* top = rememberStack;
+ *results = top->info;
+ rememberStack = top->next;
+ free((char*)top);
+ }
+ else {
+ return false;
+ }
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_restore_state\n");
+ break;
+ case DW_CFA_def_cfa:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ offset = addressSpace.getULEB128(p, instructionsEnd);
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_def_cfa dwarf unwind, reg too big\n");
+ return false;
+ }
+ results->cfaRegister = reg;
+ results->cfaRegisterOffset = offset;
+ if ( offset > 0x80000000 )
+ results->cfaOffsetWasNegative = true;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa(reg=%lld, offset=%lld)\n", reg, offset);
+ break;
+ case DW_CFA_def_cfa_register:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_def_cfa_register dwarf unwind, reg too big\n");
+ return false;
+ }
+ results->cfaRegister = reg;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_register(%lld)\n", reg);
+ break;
+ case DW_CFA_def_cfa_offset:
+ results->cfaRegisterOffset = addressSpace.getULEB128(p, instructionsEnd);
+ results->codeOffsetAtStackDecrement = codeOffset;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_offset(%d)\n", results->cfaRegisterOffset);
+ break;
+ case DW_CFA_def_cfa_expression:
+ results->cfaRegister = 0;
+ results->cfaExpression = p;
+ length = addressSpace.getULEB128(p, instructionsEnd);
+ p += length;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_expression(expression=0x%llX, length=%llu)\n",
+ results->cfaExpression, length);
+ break;
+ case DW_CFA_expression:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_expression dwarf unwind, reg too big\n");
+ return false;
+ }
+ results->savedRegisters[reg].location = kRegisterAtExpression;
+ results->savedRegisters[reg].value = p;
+ length = addressSpace.getULEB128(p, instructionsEnd);
+ p += length;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_expression(reg=%lld, expression=0x%llX, length=%llu)\n",
+ reg, results->savedRegisters[reg].value, length);
+ break;
+ case DW_CFA_offset_extended_sf:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_offset_extended_sf dwarf unwind, reg too big\n");
+ return false;
+ }
+ offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
+ if ( results->savedRegisters[reg].location != kRegisterUnused )
+ results->registerSavedMoreThanOnce = true;
+ results->savedRegisters[reg].location = kRegisterInCFA;
+ results->savedRegisters[reg].value = offset;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_offset_extended_sf(reg=%lld, offset=%lld)\n", reg, offset);
+ break;
+ case DW_CFA_def_cfa_sf:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_def_cfa_sf dwarf unwind, reg too big\n");
+ return false;
+ }
+ results->cfaRegister = reg;
+ results->cfaRegisterOffset = offset;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_sf(reg=%lld, offset=%lld)\n", reg, offset);
+ break;
+ case DW_CFA_def_cfa_offset_sf:
+ results->cfaRegisterOffset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
+ results->codeOffsetAtStackDecrement = codeOffset;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_def_cfa_offset_sf(%d)\n", results->cfaRegisterOffset);
+ break;
+ case DW_CFA_val_offset:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
+ results->savedRegisters[reg].location = kRegisterOffsetFromCFA;
+ results->savedRegisters[reg].value = offset;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_val_offset(reg=%lld, offset=%lld\n", reg, offset);
+ break;
+ case DW_CFA_val_offset_sf:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_val_offset_sf dwarf unwind, reg too big\n");
+ return false;
+ }
+ offset = addressSpace.getSLEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
+ results->savedRegisters[reg].location = kRegisterOffsetFromCFA;
+ results->savedRegisters[reg].value = offset;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_val_offset_sf(reg=%lld, offset=%lld\n", reg, offset);
+ break;
+ case DW_CFA_val_expression:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_val_expression dwarf unwind, reg too big\n");
+ return false;
+ }
+ results->savedRegisters[reg].location = kRegisterIsExpression;
+ results->savedRegisters[reg].value = p;
+ length = addressSpace.getULEB128(p, instructionsEnd);
+ p += length;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_val_expression(reg=%lld, expression=0x%llX, length=%lld)\n",
+ reg, results->savedRegisters[reg].value, length);
+ break;
+ case DW_CFA_GNU_args_size:
+ offset = addressSpace.getULEB128(p, instructionsEnd);
+ results->spExtraArgSize = offset;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_GNU_args_size(%lld)\n", offset);
+ break;
+ case DW_CFA_GNU_negative_offset_extended:
+ reg = addressSpace.getULEB128(p, instructionsEnd);
+ if ( reg > kMaxRegisterNumber ) {
+ fprintf(stderr, "malformed DW_CFA_GNU_negative_offset_extended dwarf unwind, reg too big\n");
+ return false;
+ }
+ offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
+ if ( results->savedRegisters[reg].location != kRegisterUnused )
+ results->registerSavedMoreThanOnce = true;
+ results->savedRegisters[reg].location = kRegisterInCFA;
+ results->savedRegisters[reg].value = -offset;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_GNU_negative_offset_extended(%lld)\n", offset);
+ break;
+ default:
+ operand = opcode & 0x3F;
+ switch ( opcode & 0xC0 ) {
+ case DW_CFA_offset:
+ reg = operand;
+ offset = addressSpace.getULEB128(p, instructionsEnd) * cieInfo.dataAlignFactor;
+ if ( results->savedRegisters[reg].location != kRegisterUnused )
+ results->registerSavedMoreThanOnce = true;
+ results->savedRegisters[reg].location = kRegisterInCFA;
+ results->savedRegisters[reg].value = offset;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_offset(reg=%d, offset=%lld)\n", operand, offset);
+ break;
+ case DW_CFA_advance_loc:
+ codeOffset += operand * cieInfo.codeAlignFactor;
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_advance_loc: new offset=%u\n", codeOffset);
+ break;
+ case DW_CFA_restore:
+ // <rdar://problem/7503075> Python crashes when handling an exception thrown by an obj-c object
+ // libffi uses DW_CFA_restore in the middle of some custom dward, so it is not a good epilog flag
+ //return true; // gcc-4.5 starts the epilog with this
+ reg = operand;
+ results->savedRegisters[reg] = initialState.savedRegisters[reg];
+ if ( logDwarf ) fprintf(stderr, "DW_CFA_restore(reg=%lld)\n", reg);
+ break;
+ default:
+ if ( logDwarf ) fprintf(stderr, "unknown CFA opcode 0x%02X\n", opcode);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+} // namespace lldb_private
+
+
+#endif // __DWARF_PARSER_HPP__
+
+
+
+
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/FileAbstraction.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/FileAbstraction.hpp
new file mode 100644
index 00000000000..a4af02051a5
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/FileAbstraction.hpp
@@ -0,0 +1,135 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- FileAbstraction.hpp -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __FILE_ABSTRACTION__
+#define __FILE_ABSTRACTION__
+
+
+#include <stdint.h>
+#include <string.h>
+#include <libkern/OSByteOrder.h>
+
+#ifdef __OPTIMIZE__
+#define INLINE __attribute__((always_inline))
+#else
+#define INLINE
+#endif
+
+//
+// This abstraction layer is for use with file formats that have 64-bit/32-bit and Big-Endian/Little-Endian variants
+//
+// For example: to make a utility that handles 32-bit little enidan files use: Pointer32<LittleEndian>
+//
+//
+// get16() read a 16-bit number from an E endian struct
+// set16() write a 16-bit number to an E endian struct
+// get32() read a 32-bit number from an E endian struct
+// set32() write a 32-bit number to an E endian struct
+// get64() read a 64-bit number from an E endian struct
+// set64() write a 64-bit number to an E endian struct
+//
+// getBits() read a bit field from an E endian struct (bitCount=number of bits in field, firstBit=bit index of field)
+// setBits() write a bit field to an E endian struct (bitCount=number of bits in field, firstBit=bit index of field)
+//
+// getBitsRaw() read a bit field from a struct with native endianness
+// setBitsRaw() write a bit field from a struct with native endianness
+//
+
+class BigEndian
+{
+public:
+ static uint16_t get16(const uint16_t& from) INLINE { return OSReadBigInt16(&from, 0); }
+ static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteBigInt16(&into, 0, value); }
+
+ static uint32_t get32(const uint32_t& from) INLINE { return OSReadBigInt32(&from, 0); }
+ static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteBigInt32(&into, 0, value); }
+
+ static uint64_t get64(const uint64_t& from) INLINE { return OSReadBigInt64(&from, 0); }
+ static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteBigInt64(&into, 0, value); }
+
+ static uint32_t getBits(const uint32_t& from,
+ uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); }
+ static void setBits(uint32_t& into, uint32_t value,
+ uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); }
+
+ static uint32_t getBitsRaw(const uint32_t& from,
+ uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> (32-firstBit-bitCount)) & ((1<<bitCount)-1)); }
+ static void setBitsRaw(uint32_t& into, uint32_t value,
+ uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = into;
+ const uint32_t mask = ((1<<bitCount)-1);
+ temp &= ~(mask << (32-firstBit-bitCount));
+ temp |= ((value & mask) << (32-firstBit-bitCount));
+ into = temp; }
+ enum { little_endian = 0 };
+};
+
+
+class LittleEndian
+{
+public:
+ static uint16_t get16(const uint16_t& from) INLINE { return OSReadLittleInt16(&from, 0); }
+ static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteLittleInt16(&into, 0, value); }
+
+ static uint32_t get32(const uint32_t& from) INLINE { return OSReadLittleInt32(&from, 0); }
+ static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteLittleInt32(&into, 0, value); }
+
+ static uint64_t get64(const uint64_t& from) INLINE { return OSReadLittleInt64(&from, 0); }
+ static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteLittleInt64(&into, 0, value); }
+
+ static uint32_t getBits(const uint32_t& from,
+ uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); }
+ static void setBits(uint32_t& into, uint32_t value,
+ uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); }
+
+ static uint32_t getBitsRaw(const uint32_t& from,
+ uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> firstBit) & ((1<<bitCount)-1)); }
+ static void setBitsRaw(uint32_t& into, uint32_t value,
+ uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = into;
+ const uint32_t mask = ((1<<bitCount)-1);
+ temp &= ~(mask << firstBit);
+ temp |= ((value & mask) << firstBit);
+ into = temp; }
+ enum { little_endian = 1 };
+};
+
+
+template <typename _E>
+class Pointer32
+{
+public:
+ typedef uint32_t uint_t;
+ typedef int32_t int_t;
+ typedef _E E;
+
+ static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); }
+ static void setP(uint_t& into, uint64_t value) INLINE { _E::set32(into, value); }
+};
+
+
+template <typename _E>
+class Pointer64
+{
+public:
+ typedef uint64_t uint_t;
+ typedef int64_t int_t;
+ typedef _E E;
+
+ static uint64_t getP(const uint_t& from) INLINE { return _E::get64(from); }
+ static void setP(uint_t& into, uint64_t value) INLINE { _E::set64(into, value); }
+};
+
+
+
+
+
+
+#endif // __FILE_ABSTRACTION__
+
+
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/InternalMacros.h b/lldb/source/Plugins/Process/Utility/libunwind/src/InternalMacros.h
new file mode 100644
index 00000000000..40483900d38
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/InternalMacros.h
@@ -0,0 +1,89 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- InternalMacros.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef INTERNAL_MACROS_H
+#define INTERNAL_MACROS_H
+
+#include <assert.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ extern void __assert_rtn(const char *, const char *, int, const char *) __attribute__((noreturn));
+#ifdef __cplusplus
+}
+#endif
+
+#define UNW_STEP_SUCCESS 1
+#define UNW_STEP_END 0
+
+
+struct v128 { unsigned int vec[4]; };
+
+
+#define EXPORT __attribute__((visibility("default")))
+
+#define COMPILE_TIME_ASSERT( expr ) \
+ extern int compile_time_assert_failed[ ( expr ) ? 1 : -1 ] __attribute__( ( unused ) );
+
+#define ABORT(msg) __assert_rtn(__func__, __FILE__, __LINE__, msg)
+
+#if NDEBUG
+ #define DEBUG_MESSAGE(msg, ...)
+ #define DEBUG_PRINT_API(msg, ...)
+ #define DEBUG_PRINT_UNWINDING_TEST 0
+ #define DEBUG_PRINT_UNWINDING(msg, ...)
+ #define DEBUG_LOG_NON_ZERO(x) x;
+ #define INITIALIZE_DEBUG_PRINT_API
+ #define INITIALIZE_DEBUG_PRINT_UNWINDING
+#else
+ #define DEBUG_MESSAGE(msg, ...) fprintf(stderr, "libuwind: " msg, __VA_ARGS__)
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
+ extern bool logAPIs();
+ extern bool logUnwinding();
+ #ifdef __cplusplus
+ }
+ #endif
+ #define DEBUG_LOG_NON_ZERO(x) { int _err = x; if ( _err != 0 ) fprintf(stderr, "libuwind: " #x "=%d in %s", _err, __FUNCTION__); }
+ #define DEBUG_PRINT_API(msg, ...) do { if ( logAPIs() ) fprintf(stderr, msg, __VA_ARGS__); } while(0)
+ #define DEBUG_PRINT_UNWINDING(msg, ...) do { if ( logUnwinding() ) fprintf(stderr, msg, __VA_ARGS__); } while(0)
+ #define DEBUG_PRINT_UNWINDING_TEST logUnwinding()
+ #define INITIALIZE_DEBUG_PRINT_API bool logAPIs() { static bool log = (getenv("LIBUNWIND_PRINT_APIS") != NULL); return log; }
+ #define INITIALIZE_DEBUG_PRINT_UNWINDING bool logUnwinding() { static bool log = (getenv("LIBUNWIND_PRINT_UNWINDING") != NULL); return log; }
+#endif
+
+
+// note hack for <rdar://problem/6175741>
+// Once libgcc_s.dylib vectors to libSystem, then we can remove the $ld$hide$os10.6$ lines
+#if __ppc__
+ #define NOT_HERE_BEFORE_10_6(sym) \
+ extern const char sym##_tmp3 __asm("$ld$hide$os10.3$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp3 = 0; \
+ extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \
+ extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0;
+ #define NEVER_HERE(sym) \
+ extern const char sym##_tmp3 __asm("$ld$hide$os10.3$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp3 = 0; \
+ extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \
+ extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0; \
+ extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp6 = 0;
+#else
+ #define NOT_HERE_BEFORE_10_6(sym) \
+ extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \
+ extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0;
+ #define NEVER_HERE(sym) \
+ extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \
+ extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp5 = 0; \
+ extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); __attribute__((visibility("default"))) const char sym##_tmp6 = 0;
+#endif
+
+
+
+#endif // INTERNAL_MACROS_H
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/Registers.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/Registers.hpp
new file mode 100644
index 00000000000..291c72425d4
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/Registers.hpp
@@ -0,0 +1,985 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- Registers.hpp -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+//
+// C++ interface to lower levels of libuwind
+//
+
+#ifndef __REGISTERS_HPP__
+#define __REGISTERS_HPP__
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <mach-o/loader.h>
+#include <mach-o/getsect.h>
+#include <mach/ppc/thread_status.h>
+#include <mach/i386/thread_status.h>
+
+#include "libunwind.h"
+#include "InternalMacros.h"
+
+namespace lldb_private {
+
+
+///
+/// Registers_x86 holds the register state of a thread in a 32-bit intel process.
+///
+class Registers_x86
+{
+public:
+ Registers_x86();
+ Registers_x86(const void* registers);
+
+ bool validRegister(int num) const;
+ uint32_t getRegister(int num) const;
+ void setRegister(int num, uint32_t value);
+ bool validFloatRegister(int num) const { return false; }
+ double getFloatRegister(int num) const;
+ void setFloatRegister(int num, double value);
+ bool validVectorRegister(int num) const { return false; }
+ v128 getVectorRegister(int num) const;
+ void setVectorRegister(int num, v128 value);
+ const char* getRegisterName(int num);
+ void jumpto() {}
+
+ uint32_t getSP() const { return fRegisters.__esp; }
+ void setSP(uint32_t value) { fRegisters.__esp = value; }
+ uint32_t getIP() const { return fRegisters.__eip; }
+ void setIP(uint32_t value) { fRegisters.__eip = value; }
+ uint32_t getEBP() const { return fRegisters.__ebp; }
+ void setEBP(uint32_t value) { fRegisters.__ebp = value; }
+ uint32_t getEBX() const { return fRegisters.__ebx; }
+ void setEBX(uint32_t value) { fRegisters.__ebx = value; }
+ uint32_t getECX() const { return fRegisters.__ecx; }
+ void setECX(uint32_t value) { fRegisters.__ecx = value; }
+ uint32_t getEDX() const { return fRegisters.__edx; }
+ void setEDX(uint32_t value) { fRegisters.__edx = value; }
+ uint32_t getESI() const { return fRegisters.__esi; }
+ void setESI(uint32_t value) { fRegisters.__esi = value; }
+ uint32_t getEDI() const { return fRegisters.__edi; }
+ void setEDI(uint32_t value) { fRegisters.__edi = value; }
+
+private:
+ i386_thread_state_t fRegisters;
+};
+
+inline Registers_x86::Registers_x86(const void* registers)
+{
+ COMPILE_TIME_ASSERT( sizeof(Registers_x86) < sizeof(unw_context_t) );
+ fRegisters = *((i386_thread_state_t*)registers);
+}
+
+inline Registers_x86::Registers_x86()
+{
+ bzero(&fRegisters, sizeof(fRegisters));
+}
+
+
+inline bool Registers_x86::validRegister(int regNum) const
+{
+ if ( regNum == UNW_REG_IP )
+ return true;
+ if ( regNum == UNW_REG_SP )
+ return true;
+ if ( regNum < 0 )
+ return false;
+ if ( regNum > 7 )
+ return false;
+ return true;
+}
+
+inline uint32_t Registers_x86::getRegister(int regNum) const
+{
+ switch ( regNum ) {
+ case UNW_REG_IP:
+ return fRegisters.__eip;
+ case UNW_REG_SP:
+ return fRegisters.__esp;
+ case UNW_X86_EAX:
+ return fRegisters.__eax;
+ case UNW_X86_ECX:
+ return fRegisters.__ecx;
+ case UNW_X86_EDX:
+ return fRegisters.__edx;
+ case UNW_X86_EBX:
+ return fRegisters.__ebx;
+ case UNW_X86_EBP:
+ return fRegisters.__ebp;
+ case UNW_X86_ESP:
+ return fRegisters.__esp;
+ case UNW_X86_ESI:
+ return fRegisters.__esi;
+ case UNW_X86_EDI:
+ return fRegisters.__edi;
+ }
+ ABORT("unsupported x86 register");
+}
+
+inline void Registers_x86::setRegister(int regNum, uint32_t value)
+{
+ switch ( regNum ) {
+ case UNW_REG_IP:
+ fRegisters.__eip = value;
+ return;
+ case UNW_REG_SP:
+ fRegisters.__esp = value;
+ return;
+ case UNW_X86_EAX:
+ fRegisters.__eax = value;
+ return;
+ case UNW_X86_ECX:
+ fRegisters.__ecx = value;
+ return;
+ case UNW_X86_EDX:
+ fRegisters.__edx = value;
+ return;
+ case UNW_X86_EBX:
+ fRegisters.__ebx = value;
+ return;
+ case UNW_X86_EBP:
+ fRegisters.__ebp = value;
+ return;
+ case UNW_X86_ESP:
+ fRegisters.__esp = value;
+ return;
+ case UNW_X86_ESI:
+ fRegisters.__esi = value;
+ return;
+ case UNW_X86_EDI:
+ fRegisters.__edi = value;
+ return;
+ }
+ ABORT("unsupported x86 register");
+}
+
+inline const char* Registers_x86::getRegisterName(int regNum)
+{
+ switch ( regNum ) {
+ case UNW_REG_IP:
+ return "ip";
+ case UNW_REG_SP:
+ return "esp";
+ case UNW_X86_EAX:
+ return "eax";
+ case UNW_X86_ECX:
+ return "ecx";
+ case UNW_X86_EDX:
+ return "edx";
+ case UNW_X86_EBX:
+ return "ebx";
+ case UNW_X86_EBP:
+ return "ebp";
+ case UNW_X86_ESP:
+ return "esp";
+ case UNW_X86_ESI:
+ return "esi";
+ case UNW_X86_EDI:
+ return "edi";
+ default:
+ return "unknown register";
+ }
+}
+
+inline double Registers_x86::getFloatRegister(int num) const
+{
+ ABORT("no x86 float registers");
+}
+
+inline void Registers_x86::setFloatRegister(int num, double value)
+{
+ ABORT("no x86 float registers");
+}
+
+inline v128 Registers_x86::getVectorRegister(int num) const
+{
+ ABORT("no x86 vector registers");
+}
+
+inline void Registers_x86::setVectorRegister(int num, v128 value)
+{
+ ABORT("no x86 vector registers");
+}
+
+
+
+
+///
+/// Registers_x86_64 holds the register state of a thread in a 64-bit intel process.
+///
+class Registers_x86_64
+{
+public:
+ Registers_x86_64();
+ Registers_x86_64(const void* registers);
+
+ bool validRegister(int num) const;
+ uint64_t getRegister(int num) const;
+ void setRegister(int num, uint64_t value);
+ bool validFloatRegister(int num) const{ return false; }
+ double getFloatRegister(int num) const;
+ void setFloatRegister(int num, double value);
+ bool validVectorRegister(int num) const { return false; }
+ v128 getVectorRegister(int num) const;
+ void setVectorRegister(int num, v128 value);
+ const char* getRegisterName(int num);
+ void jumpto() {}
+ uint64_t getSP() const { return fRegisters.__rsp; }
+ void setSP(uint64_t value) { fRegisters.__rsp = value; }
+ uint64_t getIP() const { return fRegisters.__rip; }
+ void setIP(uint64_t value) { fRegisters.__rip = value; }
+ uint64_t getRBP() const { return fRegisters.__rbp; }
+ void setRBP(uint64_t value) { fRegisters.__rbp = value; }
+ uint64_t getRBX() const { return fRegisters.__rbx; }
+ void setRBX(uint64_t value) { fRegisters.__rbx = value; }
+ uint64_t getR12() const { return fRegisters.__r12; }
+ void setR12(uint64_t value) { fRegisters.__r12 = value; }
+ uint64_t getR13() const { return fRegisters.__r13; }
+ void setR13(uint64_t value) { fRegisters.__r13 = value; }
+ uint64_t getR14() const { return fRegisters.__r14; }
+ void setR14(uint64_t value) { fRegisters.__r14 = value; }
+ uint64_t getR15() const { return fRegisters.__r15; }
+ void setR15(uint64_t value) { fRegisters.__r15 = value; }
+private:
+ x86_thread_state64_t fRegisters;
+};
+
+inline Registers_x86_64::Registers_x86_64(const void* registers)
+{
+ COMPILE_TIME_ASSERT( sizeof(Registers_x86_64) < sizeof(unw_context_t) );
+ fRegisters = *((x86_thread_state64_t*)registers);
+}
+
+inline Registers_x86_64::Registers_x86_64()
+{
+ bzero(&fRegisters, sizeof(fRegisters));
+}
+
+
+inline bool Registers_x86_64::validRegister(int regNum) const
+{
+ if ( regNum == UNW_REG_IP )
+ return true;
+ if ( regNum == UNW_REG_SP )
+ return true;
+ if ( regNum < 0 )
+ return false;
+ if ( regNum > 15 )
+ return false;
+ return true;
+}
+
+inline uint64_t Registers_x86_64::getRegister(int regNum) const
+{
+ switch ( regNum ) {
+ case UNW_REG_IP:
+ return fRegisters.__rip;
+ case UNW_REG_SP:
+ return fRegisters.__rsp;
+ case UNW_X86_64_RAX:
+ return fRegisters.__rax;
+ case UNW_X86_64_RDX:
+ return fRegisters.__rdx;
+ case UNW_X86_64_RCX:
+ return fRegisters.__rcx;
+ case UNW_X86_64_RBX:
+ return fRegisters.__rbx;
+ case UNW_X86_64_RSI:
+ return fRegisters.__rsi;
+ case UNW_X86_64_RDI:
+ return fRegisters.__rdi;
+ case UNW_X86_64_RBP:
+ return fRegisters.__rbp;
+ case UNW_X86_64_RSP:
+ return fRegisters.__rsp;
+ case UNW_X86_64_R8:
+ return fRegisters.__r8;
+ case UNW_X86_64_R9:
+ return fRegisters.__r9;
+ case UNW_X86_64_R10:
+ return fRegisters.__r10;
+ case UNW_X86_64_R11:
+ return fRegisters.__r11;
+ case UNW_X86_64_R12:
+ return fRegisters.__r12;
+ case UNW_X86_64_R13:
+ return fRegisters.__r13;
+ case UNW_X86_64_R14:
+ return fRegisters.__r14;
+ case UNW_X86_64_R15:
+ return fRegisters.__r15;
+ }
+ ABORT("unsupported x86_64 register");
+}
+
+inline void Registers_x86_64::setRegister(int regNum, uint64_t value)
+{
+ switch ( regNum ) {
+ case UNW_REG_IP:
+ fRegisters.__rip = value;
+ return;
+ case UNW_REG_SP:
+ fRegisters.__rsp = value;
+ return;
+ case UNW_X86_64_RAX:
+ fRegisters.__rax = value;
+ return;
+ case UNW_X86_64_RDX:
+ fRegisters.__rdx = value;
+ return;
+ case UNW_X86_64_RCX:
+ fRegisters.__rcx = value;
+ return;
+ case UNW_X86_64_RBX:
+ fRegisters.__rbx = value;
+ return;
+ case UNW_X86_64_RSI:
+ fRegisters.__rsi = value;
+ return;
+ case UNW_X86_64_RDI:
+ fRegisters.__rdi = value;
+ return;
+ case UNW_X86_64_RBP:
+ fRegisters.__rbp = value;
+ return;
+ case UNW_X86_64_RSP:
+ fRegisters.__rsp = value;
+ return;
+ case UNW_X86_64_R8:
+ fRegisters.__r8 = value;
+ return;
+ case UNW_X86_64_R9:
+ fRegisters.__r9 = value;
+ return;
+ case UNW_X86_64_R10:
+ fRegisters.__r10 = value;
+ return;
+ case UNW_X86_64_R11:
+ fRegisters.__r11 = value;
+ return;
+ case UNW_X86_64_R12:
+ fRegisters.__r12 = value;
+ return;
+ case UNW_X86_64_R13:
+ fRegisters.__r13 = value;
+ return;
+ case UNW_X86_64_R14:
+ fRegisters.__r14 = value;
+ return;
+ case UNW_X86_64_R15:
+ fRegisters.__r15 = value;
+ return;
+ }
+ ABORT("unsupported x86_64 register");
+}
+
+inline const char* Registers_x86_64::getRegisterName(int regNum)
+{
+ switch ( regNum ) {
+ case UNW_REG_IP:
+ return "rip";
+ case UNW_REG_SP:
+ return "rsp";
+ case UNW_X86_64_RAX:
+ return "rax";
+ case UNW_X86_64_RDX:
+ return "rdx";
+ case UNW_X86_64_RCX:
+ return "rcx";
+ case UNW_X86_64_RBX:
+ return "rbx";
+ case UNW_X86_64_RSI:
+ return "rsi";
+ case UNW_X86_64_RDI:
+ return "rdi";
+ case UNW_X86_64_RBP:
+ return "rbp";
+ case UNW_X86_64_RSP:
+ return "rsp";
+ case UNW_X86_64_R8:
+ return "r8";
+ case UNW_X86_64_R9:
+ return "r9";
+ case UNW_X86_64_R10:
+ return "r10";
+ case UNW_X86_64_R11:
+ return "r11";
+ case UNW_X86_64_R12:
+ return "r12";
+ case UNW_X86_64_R13:
+ return "r13";
+ case UNW_X86_64_R14:
+ return "r14";
+ case UNW_X86_64_R15:
+ return "r15";
+ default:
+ return "unknown register";
+ }
+}
+
+double Registers_x86_64::getFloatRegister(int num) const
+{
+ ABORT("no x86_64 float registers");
+}
+
+void Registers_x86_64::setFloatRegister(int num, double value)
+{
+ ABORT("no x86_64 float registers");
+}
+
+inline v128 Registers_x86_64::getVectorRegister(int num) const
+{
+ ABORT("no x86_64 vector registers");
+}
+
+inline void Registers_x86_64::setVectorRegister(int num, v128 value)
+{
+ ABORT("no x86_64 vector registers");
+}
+
+
+///
+/// Registers_ppc holds the register state of a thread in a 32-bit PowerPC process.
+///
+class Registers_ppc
+{
+public:
+ Registers_ppc();
+ Registers_ppc(const void* registers);
+
+ bool validRegister(int num) const;
+ uint32_t getRegister(int num) const;
+ void setRegister(int num, uint32_t value);
+ bool validFloatRegister(int num) const;
+ double getFloatRegister(int num) const;
+ void setFloatRegister(int num, double value);
+ bool validVectorRegister(int num) const;
+ v128 getVectorRegister(int num) const;
+ void setVectorRegister(int num, v128 value);
+ void jumpto() {}
+ const char* getRegisterName(int num);
+ uint64_t getSP() const { return fRegisters.__r1; }
+ void setSP(uint64_t value) { fRegisters.__r1 = value; }
+ uint64_t getIP() const { return fRegisters.__srr0; }
+ void setIP(uint64_t value) { fRegisters.__srr0 = value; }
+private:
+ ppc_thread_state_t fRegisters;
+ ppc_float_state_t fFloatRegisters;
+ v128 fVectorRegisters[32]; // offset 424
+};
+
+
+
+inline Registers_ppc::Registers_ppc(const void* registers)
+{
+ COMPILE_TIME_ASSERT( sizeof(Registers_ppc) < sizeof(unw_context_t) );
+ fRegisters = *((ppc_thread_state_t*)registers);
+ fFloatRegisters = *((ppc_float_state_t*)((char*)registers+160));
+ memcpy(fVectorRegisters, ((char*)registers+424), sizeof(fVectorRegisters));
+}
+
+inline Registers_ppc::Registers_ppc()
+{
+ bzero(&fRegisters, sizeof(fRegisters));
+ bzero(&fFloatRegisters, sizeof(fFloatRegisters));
+ bzero(&fVectorRegisters, sizeof(fVectorRegisters));
+}
+
+
+inline bool Registers_ppc::validRegister(int regNum) const
+{
+ if ( regNum == UNW_REG_IP )
+ return true;
+ if ( regNum == UNW_REG_SP )
+ return true;
+ if ( regNum == UNW_PPC_VRSAVE )
+ return true;
+ if ( regNum < 0 )
+ return false;
+ if ( regNum <= UNW_PPC_R31 )
+ return true;
+ if ( regNum == UNW_PPC_MQ )
+ return true;
+ if ( regNum == UNW_PPC_LR )
+ return true;
+ if ( regNum == UNW_PPC_CTR )
+ return true;
+ if ( (UNW_PPC_CR0 <= regNum) && (regNum <= UNW_PPC_CR7) )
+ return true;
+ return false;
+}
+
+
+inline uint32_t Registers_ppc::getRegister(int regNum) const
+{
+ switch ( regNum ) {
+ case UNW_REG_IP:
+ return fRegisters.__srr0;
+ case UNW_REG_SP:
+ return fRegisters.__r1;
+ case UNW_PPC_R0:
+ return fRegisters.__r0;
+ case UNW_PPC_R1:
+ return fRegisters.__r1;
+ case UNW_PPC_R2:
+ return fRegisters.__r2;
+ case UNW_PPC_R3:
+ return fRegisters.__r3;
+ case UNW_PPC_R4:
+ return fRegisters.__r4;
+ case UNW_PPC_R5:
+ return fRegisters.__r5;
+ case UNW_PPC_R6:
+ return fRegisters.__r6;
+ case UNW_PPC_R7:
+ return fRegisters.__r7;
+ case UNW_PPC_R8:
+ return fRegisters.__r8;
+ case UNW_PPC_R9:
+ return fRegisters.__r9;
+ case UNW_PPC_R10:
+ return fRegisters.__r10;
+ case UNW_PPC_R11:
+ return fRegisters.__r11;
+ case UNW_PPC_R12:
+ return fRegisters.__r12;
+ case UNW_PPC_R13:
+ return fRegisters.__r13;
+ case UNW_PPC_R14:
+ return fRegisters.__r14;
+ case UNW_PPC_R15:
+ return fRegisters.__r15;
+ case UNW_PPC_R16:
+ return fRegisters.__r16;
+ case UNW_PPC_R17:
+ return fRegisters.__r17;
+ case UNW_PPC_R18:
+ return fRegisters.__r18;
+ case UNW_PPC_R19:
+ return fRegisters.__r19;
+ case UNW_PPC_R20:
+ return fRegisters.__r20;
+ case UNW_PPC_R21:
+ return fRegisters.__r21;
+ case UNW_PPC_R22:
+ return fRegisters.__r22;
+ case UNW_PPC_R23:
+ return fRegisters.__r23;
+ case UNW_PPC_R24:
+ return fRegisters.__r24;
+ case UNW_PPC_R25:
+ return fRegisters.__r25;
+ case UNW_PPC_R26:
+ return fRegisters.__r26;
+ case UNW_PPC_R27:
+ return fRegisters.__r27;
+ case UNW_PPC_R28:
+ return fRegisters.__r28;
+ case UNW_PPC_R29:
+ return fRegisters.__r29;
+ case UNW_PPC_R30:
+ return fRegisters.__r30;
+ case UNW_PPC_R31:
+ return fRegisters.__r31;
+ case UNW_PPC_LR:
+ return fRegisters.__lr;
+ case UNW_PPC_CR0:
+ return (fRegisters.__cr & 0xF0000000);
+ case UNW_PPC_CR1:
+ return (fRegisters.__cr & 0x0F000000);
+ case UNW_PPC_CR2:
+ return (fRegisters.__cr & 0x00F00000);
+ case UNW_PPC_CR3:
+ return (fRegisters.__cr & 0x000F0000);
+ case UNW_PPC_CR4:
+ return (fRegisters.__cr & 0x0000F000);
+ case UNW_PPC_CR5:
+ return (fRegisters.__cr & 0x00000F00);
+ case UNW_PPC_CR6:
+ return (fRegisters.__cr & 0x000000F0);
+ case UNW_PPC_CR7:
+ return (fRegisters.__cr & 0x0000000F);
+ case UNW_PPC_VRSAVE:
+ return fRegisters.__vrsave;
+ }
+ ABORT("unsupported ppc register");
+}
+
+
+inline void Registers_ppc::setRegister(int regNum, uint32_t value)
+{
+ //fprintf(stderr, "Registers_ppc::setRegister(%d, 0x%08X)\n", regNum, value);
+ switch ( regNum ) {
+ case UNW_REG_IP:
+ fRegisters.__srr0 = value;
+ return;
+ case UNW_REG_SP:
+ fRegisters.__r1 = value;
+ return;
+ case UNW_PPC_R0:
+ fRegisters.__r0 = value;
+ return;
+ case UNW_PPC_R1:
+ fRegisters.__r1 = value;
+ return;
+ case UNW_PPC_R2:
+ fRegisters.__r2 = value;
+ return;
+ case UNW_PPC_R3:
+ fRegisters.__r3 = value;
+ return;
+ case UNW_PPC_R4:
+ fRegisters.__r4 = value;
+ return;
+ case UNW_PPC_R5:
+ fRegisters.__r5 = value;
+ return;
+ case UNW_PPC_R6:
+ fRegisters.__r6 = value;
+ return;
+ case UNW_PPC_R7:
+ fRegisters.__r7 = value;
+ return;
+ case UNW_PPC_R8:
+ fRegisters.__r8 = value;
+ return;
+ case UNW_PPC_R9:
+ fRegisters.__r9 = value;
+ return;
+ case UNW_PPC_R10:
+ fRegisters.__r10 = value;
+ return;
+ case UNW_PPC_R11:
+ fRegisters.__r11 = value;
+ return;
+ case UNW_PPC_R12:
+ fRegisters.__r12 = value;
+ return;
+ case UNW_PPC_R13:
+ fRegisters.__r13 = value;
+ return;
+ case UNW_PPC_R14:
+ fRegisters.__r14 = value;
+ return;
+ case UNW_PPC_R15:
+ fRegisters.__r15 = value;
+ return;
+ case UNW_PPC_R16:
+ fRegisters.__r16 = value;
+ return;
+ case UNW_PPC_R17:
+ fRegisters.__r17 = value;
+ return;
+ case UNW_PPC_R18:
+ fRegisters.__r18 = value;
+ return;
+ case UNW_PPC_R19:
+ fRegisters.__r19 = value;
+ return;
+ case UNW_PPC_R20:
+ fRegisters.__r20 = value;
+ return;
+ case UNW_PPC_R21:
+ fRegisters.__r21 = value;
+ return;
+ case UNW_PPC_R22:
+ fRegisters.__r22 = value;
+ return;
+ case UNW_PPC_R23:
+ fRegisters.__r23 = value;
+ return;
+ case UNW_PPC_R24:
+ fRegisters.__r24 = value;
+ return;
+ case UNW_PPC_R25:
+ fRegisters.__r25 = value;
+ return;
+ case UNW_PPC_R26:
+ fRegisters.__r26 = value;
+ return;
+ case UNW_PPC_R27:
+ fRegisters.__r27 = value;
+ return;
+ case UNW_PPC_R28:
+ fRegisters.__r28 = value;
+ return;
+ case UNW_PPC_R29:
+ fRegisters.__r29 = value;
+ return;
+ case UNW_PPC_R30:
+ fRegisters.__r30 = value;
+ return;
+ case UNW_PPC_R31:
+ fRegisters.__r31 = value;
+ return;
+ case UNW_PPC_MQ:
+ fRegisters.__mq = value;
+ return;
+ case UNW_PPC_LR:
+ fRegisters.__lr = value;
+ return;
+ case UNW_PPC_CTR:
+ fRegisters.__ctr = value;
+ return;
+ case UNW_PPC_CR0:
+ fRegisters.__cr &= 0x0FFFFFFF;
+ fRegisters.__cr |= (value & 0xF0000000);
+ return;
+ case UNW_PPC_CR1:
+ fRegisters.__cr &= 0xF0FFFFFF;
+ fRegisters.__cr |= (value & 0x0F000000);
+ return;
+ case UNW_PPC_CR2:
+ fRegisters.__cr &= 0xFF0FFFFF;
+ fRegisters.__cr |= (value & 0x00F00000);
+ return;
+ case UNW_PPC_CR3:
+ fRegisters.__cr &= 0xFFF0FFFF;
+ fRegisters.__cr |= (value & 0x000F0000);
+ return;
+ case UNW_PPC_CR4:
+ fRegisters.__cr &= 0xFFFF0FFF;
+ fRegisters.__cr |= (value & 0x0000F000);
+ return;
+ case UNW_PPC_CR5:
+ fRegisters.__cr &= 0xFFFFF0FF;
+ fRegisters.__cr |= (value & 0x00000F00);
+ return;
+ case UNW_PPC_CR6:
+ fRegisters.__cr &= 0xFFFFFF0F;
+ fRegisters.__cr |= (value & 0x000000F0);
+ return;
+ case UNW_PPC_CR7:
+ fRegisters.__cr &= 0xFFFFFFF0;
+ fRegisters.__cr |= (value & 0x0000000F);
+ return;
+ case UNW_PPC_VRSAVE:
+ fRegisters.__vrsave = value;
+ return;
+ // not saved
+ return;
+ case UNW_PPC_XER:
+ fRegisters.__xer = value;
+ return;
+ case UNW_PPC_AP:
+ case UNW_PPC_VSCR:
+ case UNW_PPC_SPEFSCR:
+ // not saved
+ return;
+ }
+ ABORT("unsupported ppc register");
+}
+
+inline bool Registers_ppc::validFloatRegister(int regNum) const
+{
+ if ( regNum < UNW_PPC_F0 )
+ return false;
+ if ( regNum > UNW_PPC_F31 )
+ return false;
+ return true;
+}
+
+inline double Registers_ppc::getFloatRegister(int regNum) const
+{
+ assert(validFloatRegister(regNum));
+ return fFloatRegisters.__fpregs[regNum-UNW_PPC_F0];
+}
+
+inline void Registers_ppc::setFloatRegister(int regNum, double value)
+{
+ //fprintf(stderr, "Registers_ppc::setFloatRegister(%d, %g))\n", regNum, value);
+ assert(validFloatRegister(regNum));
+ fFloatRegisters.__fpregs[regNum-UNW_PPC_F0] = value;
+}
+
+
+inline bool Registers_ppc::validVectorRegister(int regNum) const
+{
+ if ( regNum < UNW_PPC_V0 )
+ return false;
+ if ( regNum > UNW_PPC_V31 )
+ return false;
+ return true;
+}
+
+v128 Registers_ppc::getVectorRegister(int regNum) const
+{
+ assert(validVectorRegister(regNum));
+ v128 result = fVectorRegisters[regNum-UNW_PPC_V0];
+ //fprintf(stderr, "Registers_ppc::getVectorRegister(this=%p, %d) => <0x%08X, 0x%08X, 0x%08X, 0x%08X> \n",
+ // this, regNum, result.vec[0], result.vec[1], result.vec[2], result.vec[3]);
+ return result;
+}
+
+void Registers_ppc::setVectorRegister(int regNum, v128 value)
+{
+ assert(validVectorRegister(regNum));
+ //fprintf(stderr, "Registers_ppc::setVectorRegister(this=%p, %d) <0x%08X, 0x%08X, 0x%08X, 0x%08X> => <0x%08X, 0x%08X, 0x%08X, 0x%08X> \n",
+ // this, regNum, fVectorRegisters[regNum-UNW_PPC_V0].vec[0], fVectorRegisters[regNum-UNW_PPC_V0].vec[1], fVectorRegisters[regNum-UNW_PPC_V0].vec[2],
+ // fVectorRegisters[regNum-UNW_PPC_V0].vec[3], value.vec[0], value.vec[1], value.vec[2], value.vec[3]);
+ fVectorRegisters[regNum-UNW_PPC_V0] = value;
+}
+
+
+inline const char* Registers_ppc::getRegisterName(int regNum)
+{
+ switch ( regNum ) {
+ case UNW_REG_IP:
+ return "ip";
+ case UNW_REG_SP:
+ return "sp";
+ case UNW_PPC_R0:
+ return "r0";
+ case UNW_PPC_R1:
+ return "r1";
+ case UNW_PPC_R2:
+ return "r2";
+ case UNW_PPC_R3:
+ return "r3";
+ case UNW_PPC_R4:
+ return "r4";
+ case UNW_PPC_R5:
+ return "r5";
+ case UNW_PPC_R6:
+ return "r6";
+ case UNW_PPC_R7:
+ return "r7";
+ case UNW_PPC_R8:
+ return "r8";
+ case UNW_PPC_R9:
+ return "r9";
+ case UNW_PPC_R10:
+ return "r10";
+ case UNW_PPC_R11:
+ return "r11";
+ case UNW_PPC_R12:
+ return "r12";
+ case UNW_PPC_R13:
+ return "r13";
+ case UNW_PPC_R14:
+ return "r14";
+ case UNW_PPC_R15:
+ return "r15";
+ case UNW_PPC_R16:
+ return "r16";
+ case UNW_PPC_R17:
+ return "r17";
+ case UNW_PPC_R18:
+ return "r18";
+ case UNW_PPC_R19:
+ return "r19";
+ case UNW_PPC_R20:
+ return "r20";
+ case UNW_PPC_R21:
+ return "r21";
+ case UNW_PPC_R22:
+ return "r22";
+ case UNW_PPC_R23:
+ return "r23";
+ case UNW_PPC_R24:
+ return "r24";
+ case UNW_PPC_R25:
+ return "r25";
+ case UNW_PPC_R26:
+ return "r26";
+ case UNW_PPC_R27:
+ return "r27";
+ case UNW_PPC_R28:
+ return "r28";
+ case UNW_PPC_R29:
+ return "r29";
+ case UNW_PPC_R30:
+ return "r30";
+ case UNW_PPC_R31:
+ return "r31";
+ case UNW_PPC_F0:
+ return "fp0";
+ case UNW_PPC_F1:
+ return "fp1";
+ case UNW_PPC_F2:
+ return "fp2";
+ case UNW_PPC_F3:
+ return "fp3";
+ case UNW_PPC_F4:
+ return "fp4";
+ case UNW_PPC_F5:
+ return "fp5";
+ case UNW_PPC_F6:
+ return "fp6";
+ case UNW_PPC_F7:
+ return "fp7";
+ case UNW_PPC_F8:
+ return "fp8";
+ case UNW_PPC_F9:
+ return "fp9";
+ case UNW_PPC_F10:
+ return "fp10";
+ case UNW_PPC_F11:
+ return "fp11";
+ case UNW_PPC_F12:
+ return "fp12";
+ case UNW_PPC_F13:
+ return "fp13";
+ case UNW_PPC_F14:
+ return "fp14";
+ case UNW_PPC_F15:
+ return "fp15";
+ case UNW_PPC_F16:
+ return "fp16";
+ case UNW_PPC_F17:
+ return "fp17";
+ case UNW_PPC_F18:
+ return "fp18";
+ case UNW_PPC_F19:
+ return "fp19";
+ case UNW_PPC_F20:
+ return "fp20";
+ case UNW_PPC_F21:
+ return "fp21";
+ case UNW_PPC_F22:
+ return "fp22";
+ case UNW_PPC_F23:
+ return "fp23";
+ case UNW_PPC_F24:
+ return "fp24";
+ case UNW_PPC_F25:
+ return "fp25";
+ case UNW_PPC_F26:
+ return "fp26";
+ case UNW_PPC_F27:
+ return "fp27";
+ case UNW_PPC_F28:
+ return "fp28";
+ case UNW_PPC_F29:
+ return "fp29";
+ case UNW_PPC_F30:
+ return "fp30";
+ case UNW_PPC_F31:
+ return "fp31";
+ case UNW_PPC_LR:
+ return "lr";
+ default:
+ return "unknown register";
+ }
+
+
+}
+
+
+} // namespace lldb_private
+
+
+
+#endif // __REGISTERS_HPP__
+
+
+
+
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/Registers.s b/lldb/source/Plugins/Process/Utility/libunwind/src/Registers.s
new file mode 100644
index 00000000000..45dae3bcbfc
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/Registers.s
@@ -0,0 +1,261 @@
+
+
+#if __i386__
+ .text
+ .globl __ZN12lldb_private13Registers_x866jumptoEv
+ .private_extern __ZN12lldb_private13Registers_x866jumptoEv
+__ZN12lldb_private13Registers_x866jumptoEv:
+#
+# void lldb_private::Registers_x86::jumpto()
+#
+# On entry:
+# + +
+# +-----------------------+
+# + thread_state pointer +
+# +-----------------------+
+# + return address +
+# +-----------------------+ <-- SP
+# + +
+ movl 4(%esp), %eax
+ # set up eax and ret on new stack location
+ movl 28(%eax), %edx # edx holds new stack pointer
+ subl $8,%edx
+ movl %edx, 28(%eax)
+ movl 0(%eax), %ebx
+ movl %ebx, 0(%edx)
+ movl 40(%eax), %ebx
+ movl %ebx, 4(%edx)
+ # we now have ret and eax pushed onto where new stack will be
+ # restore all registers
+ movl 4(%eax), %ebx
+ movl 8(%eax), %ecx
+ movl 12(%eax), %edx
+ movl 16(%eax), %edi
+ movl 20(%eax), %esi
+ movl 24(%eax), %ebp
+ movl 28(%eax), %esp
+ # skip ss
+ # skip eflags
+ pop %eax # eax was already pushed on new stack
+ ret # eip was already pushed on new stack
+ # skip cs
+ # skip ds
+ # skip es
+ # skip fs
+ # skip gs
+
+#elif __x86_64__
+
+ .text
+ .globl __ZN12lldb_private16Registers_x86_646jumptoEv
+ .private_extern __ZN12lldb_private16Registers_x86_646jumptoEv
+__ZN12lldb_private16Registers_x86_646jumptoEv:
+#
+# void lldb_private::Registers_x86_64::jumpto()
+#
+# On entry, thread_state pointer is in rdi
+
+ movq 56(%rdi), %rax # rax holds new stack pointer
+ subq $16, %rax
+ movq %rax, 56(%rdi)
+ movq 32(%rdi), %rbx # store new rdi on new stack
+ movq %rbx, 0(%rax)
+ movq 128(%rdi), %rbx # store new rip on new stack
+ movq %rbx, 8(%rax)
+ # restore all registers
+ movq 0(%rdi), %rax
+ movq 8(%rdi), %rbx
+ movq 16(%rdi), %rcx
+ movq 24(%rdi), %rdx
+ # restore rdi later
+ movq 40(%rdi), %rsi
+ movq 48(%rdi), %rbp
+ # restore rsp later
+ movq 64(%rdi), %r8
+ movq 72(%rdi), %r9
+ movq 80(%rdi), %r10
+ movq 88(%rdi), %r11
+ movq 96(%rdi), %r12
+ movq 104(%rdi), %r13
+ movq 112(%rdi), %r14
+ movq 120(%rdi), %r15
+ # skip rflags
+ # skip cs
+ # skip fs
+ # skip gs
+ movq 56(%rdi), %rsp # cut back rsp to new location
+ pop %rdi # rdi was saved here earlier
+ ret # rip was saved here
+
+
+#elif __ppc__
+
+ .text
+ .globl __ZN12lldb_private13Registers_ppc6jumptoEv
+ .private_extern __ZN12lldb_private13Registers_ppc6jumptoEv
+__ZN12lldb_private13Registers_ppc6jumptoEv:
+;
+; void lldb_private::Registers_ppc::jumpto()
+;
+; On entry:
+; thread_state pointer is in r3
+;
+
+ ; restore integral registerrs
+ ; skip r0 for now
+ ; skip r1 for now
+ lwz r2, 16(r3)
+ ; skip r3 for now
+ ; skip r4 for now
+ ; skip r5 for now
+ lwz r6, 32(r3)
+ lwz r7, 36(r3)
+ lwz r8, 40(r3)
+ lwz r9, 44(r3)
+ lwz r10, 48(r3)
+ lwz r11, 52(r3)
+ lwz r12, 56(r3)
+ lwz r13, 60(r3)
+ lwz r14, 64(r3)
+ lwz r15, 68(r3)
+ lwz r16, 72(r3)
+ lwz r17, 76(r3)
+ lwz r18, 80(r3)
+ lwz r19, 84(r3)
+ lwz r20, 88(r3)
+ lwz r21, 92(r3)
+ lwz r22, 96(r3)
+ lwz r23,100(r3)
+ lwz r24,104(r3)
+ lwz r25,108(r3)
+ lwz r26,112(r3)
+ lwz r27,116(r3)
+ lwz r28,120(r3)
+ lwz r29,124(r3)
+ lwz r30,128(r3)
+ lwz r31,132(r3)
+
+ ; restore float registers
+ lfd f0, 160(r3)
+ lfd f1, 168(r3)
+ lfd f2, 176(r3)
+ lfd f3, 184(r3)
+ lfd f4, 192(r3)
+ lfd f5, 200(r3)
+ lfd f6, 208(r3)
+ lfd f7, 216(r3)
+ lfd f8, 224(r3)
+ lfd f9, 232(r3)
+ lfd f10,240(r3)
+ lfd f11,248(r3)
+ lfd f12,256(r3)
+ lfd f13,264(r3)
+ lfd f14,272(r3)
+ lfd f15,280(r3)
+ lfd f16,288(r3)
+ lfd f17,296(r3)
+ lfd f18,304(r3)
+ lfd f19,312(r3)
+ lfd f20,320(r3)
+ lfd f21,328(r3)
+ lfd f22,336(r3)
+ lfd f23,344(r3)
+ lfd f24,352(r3)
+ lfd f25,360(r3)
+ lfd f26,368(r3)
+ lfd f27,376(r3)
+ lfd f28,384(r3)
+ lfd f29,392(r3)
+ lfd f30,400(r3)
+ lfd f31,408(r3)
+
+ ; restore vector registers if any are in use
+ lwz r5,156(r3) ; test VRsave
+ cmpwi r5,0
+ beq Lnovec
+
+ subi r4,r1,16
+ rlwinm r4,r4,0,0,27 ; mask low 4-bits
+ ; r4 is now a 16-byte aligned pointer into the red zone
+ ; the fVectorRegisters may not be 16-byte aligned so copy via red zone temp buffer
+
+
+#define LOAD_VECTOR_UNALIGNEDl(_index) \
+ andis. r0,r5,(1<<(15-_index)) @\
+ beq Ldone ## _index @\
+ lwz r0, 424+_index*16(r3) @\
+ stw r0, 0(r4) @\
+ lwz r0, 424+_index*16+4(r3) @\
+ stw r0, 4(r4) @\
+ lwz r0, 424+_index*16+8(r3) @\
+ stw r0, 8(r4) @\
+ lwz r0, 424+_index*16+12(r3)@\
+ stw r0, 12(r4) @\
+ lvx v ## _index,0,r4 @\
+Ldone ## _index:
+
+#define LOAD_VECTOR_UNALIGNEDh(_index) \
+ andi. r0,r5,(1<<(31-_index)) @\
+ beq Ldone ## _index @\
+ lwz r0, 424+_index*16(r3) @\
+ stw r0, 0(r4) @\
+ lwz r0, 424+_index*16+4(r3) @\
+ stw r0, 4(r4) @\
+ lwz r0, 424+_index*16+8(r3) @\
+ stw r0, 8(r4) @\
+ lwz r0, 424+_index*16+12(r3)@\
+ stw r0, 12(r4) @\
+ lvx v ## _index,0,r4 @\
+ Ldone ## _index:
+
+
+ LOAD_VECTOR_UNALIGNEDl(0)
+ LOAD_VECTOR_UNALIGNEDl(1)
+ LOAD_VECTOR_UNALIGNEDl(2)
+ LOAD_VECTOR_UNALIGNEDl(3)
+ LOAD_VECTOR_UNALIGNEDl(4)
+ LOAD_VECTOR_UNALIGNEDl(5)
+ LOAD_VECTOR_UNALIGNEDl(6)
+ LOAD_VECTOR_UNALIGNEDl(7)
+ LOAD_VECTOR_UNALIGNEDl(8)
+ LOAD_VECTOR_UNALIGNEDl(9)
+ LOAD_VECTOR_UNALIGNEDl(10)
+ LOAD_VECTOR_UNALIGNEDl(11)
+ LOAD_VECTOR_UNALIGNEDl(12)
+ LOAD_VECTOR_UNALIGNEDl(13)
+ LOAD_VECTOR_UNALIGNEDl(14)
+ LOAD_VECTOR_UNALIGNEDl(15)
+ LOAD_VECTOR_UNALIGNEDh(16)
+ LOAD_VECTOR_UNALIGNEDh(17)
+ LOAD_VECTOR_UNALIGNEDh(18)
+ LOAD_VECTOR_UNALIGNEDh(19)
+ LOAD_VECTOR_UNALIGNEDh(20)
+ LOAD_VECTOR_UNALIGNEDh(21)
+ LOAD_VECTOR_UNALIGNEDh(22)
+ LOAD_VECTOR_UNALIGNEDh(23)
+ LOAD_VECTOR_UNALIGNEDh(24)
+ LOAD_VECTOR_UNALIGNEDh(25)
+ LOAD_VECTOR_UNALIGNEDh(26)
+ LOAD_VECTOR_UNALIGNEDh(27)
+ LOAD_VECTOR_UNALIGNEDh(28)
+ LOAD_VECTOR_UNALIGNEDh(29)
+ LOAD_VECTOR_UNALIGNEDh(30)
+ LOAD_VECTOR_UNALIGNEDh(31)
+
+Lnovec:
+ lwz r0, 136(r3) ; __cr
+ mtocrf 255,r0
+ lwz r0, 148(r3) ; __ctr
+ mtctr r0
+ lwz r0, 0(r3) ; __ssr0
+ mtctr r0
+ lwz r0, 8(r3) ; do r0 now
+ lwz r5,28(r3) ; do r5 now
+ lwz r4,24(r3) ; do r4 now
+ lwz r1,12(r3) ; do sp now
+ lwz r3,20(r3) ; do r3 last
+ bctr
+
+
+#endif
+
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteDebuggerDummyUnwinder.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteDebuggerDummyUnwinder.hpp
new file mode 100644
index 00000000000..1db3faffd10
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteDebuggerDummyUnwinder.hpp
@@ -0,0 +1,88 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- RemoteDebuggerDummyUnwinder.hpp -------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// Code to unwind past a debugger's dummy frame inserted when it does an
+// inferior function call.
+// In this case we'll need to get the saved register context from the debugger -
+// it may be in the debugger's local memory or it may be saved in a nonstandard
+// location in the inferior process' memory.
+
+#ifndef __REMOTE_DEBUGGER_DUMMY_UNWINDER_HPP__
+#define __REMOTE_DEBUGGER_DUMMY_UNWINDER_HPP__
+
+#if defined (SUPPORT_REMOTE_UNWINDING)
+
+#include "libunwind.h"
+#include "Registers.hpp"
+#include "AddressSpace.hpp"
+#include "RemoteRegisterMap.hpp"
+#include "RemoteProcInfo.hpp"
+
+namespace lldb_private
+{
+
+template <typename A>
+int stepOutOfDebuggerDummyFrame (A& addressSpace, Registers_x86_64& registers,
+ RemoteProcInfo *procinfo, uint64_t ip,
+ uint64_t sp, void* arg)
+{
+ Registers_x86_64 newRegisters(registers);
+ RemoteRegisterMap *rmap = addressSpace.getRemoteProcInfo()->getRegisterMap();
+ unw_word_t regv;
+ for (int i = UNW_X86_64_RAX; i <= UNW_X86_64_R15; i++) {
+ int driver_regnum;
+ if (!rmap->unwind_regno_to_caller_regno (i, driver_regnum))
+ continue;
+ if (addressSpace.accessors()->access_reg_inf_func_call (procinfo->wrap(), ip, sp, driver_regnum, &regv, 0, arg))
+ newRegisters.setRegister(i, regv);
+ }
+ if (!addressSpace.accessors()->access_reg_inf_func_call (procinfo->wrap(), ip, sp, rmap->caller_regno_for_ip(), &regv, 0, arg))
+ return UNW_EUNSPEC;
+ newRegisters.setIP (regv);
+ registers = newRegisters;
+ return UNW_STEP_SUCCESS;
+}
+
+template <typename A>
+int stepOutOfDebuggerDummyFrame (A& addressSpace, Registers_x86& registers,
+ RemoteProcInfo *procinfo, uint64_t ip,
+ uint64_t sp, void* arg)
+{
+ Registers_x86 newRegisters(registers);
+ RemoteRegisterMap *rmap = addressSpace.getRemoteProcInfo()->getRegisterMap();
+ unw_word_t regv;
+ for (int i = UNW_X86_EAX; i <= UNW_X86_EDI; i++) {
+ int driver_regnum;
+ if (!rmap->unwind_regno_to_caller_regno (i, driver_regnum))
+ continue;
+ if (addressSpace.accessors()->access_reg_inf_func_call (procinfo->wrap(), ip, sp, driver_regnum, &regv, 0, arg))
+ newRegisters.setRegister(i, regv);
+ }
+ if (!addressSpace.accessors()->access_reg_inf_func_call (procinfo->wrap(), ip, sp, rmap->caller_regno_for_ip(), &regv, 0, arg))
+ return UNW_EUNSPEC;
+ newRegisters.setIP (regv);
+ registers = newRegisters;
+ return UNW_STEP_SUCCESS;
+}
+
+template <typename A>
+int stepOutOfDebuggerDummyFrame (A& addressSpace, Registers_ppc& registers,
+ uint64_t ip, uint64_t sp)
+{
+ ABORT ("stepping out of a debugger dummy frame not supported on ppc");
+ return UNW_EUNSPEC;
+}
+
+}; // namespace lldb_private
+
+#endif // SUPPORT_REMOTE_UNWINDING
+
+#endif // __REMOTE_DEBUGGER_DUMMY_UNWINDER_HPP__
+
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteProcInfo.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteProcInfo.hpp
new file mode 100644
index 00000000000..3640dc6195c
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteProcInfo.hpp
@@ -0,0 +1,977 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- RemoteProcInfo.hpp --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// This file defines the primary object created when unw_create_addr_space()
+// is called. This object tracks the list of known images in memory
+// (dylibs, bundles, etc), it maintains a link to a RemoteRegisterMap for this
+// architecture, it caches the remote process memory in a local store and all
+// read/writes are filtered through its accessors which will use the memory
+// caches. It maintains a logging level set by the driver program and puts
+// timing/debug messages out on a FILE* provided to it.
+
+// RemoteProcInfo is not specific to any particular unwind so it does not
+// maintain an "arg" argument (an opaque pointer that the driver program uses
+// to track the process/thread being unwound).
+
+#ifndef __REMOTE_PROC_INFO_HPP__
+#define __REMOTE_PROC_INFO_HPP__
+
+#if defined (SUPPORT_REMOTE_UNWINDING)
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <stdarg.h>
+#include <sys/time.h>
+#include <mach-o/loader.h>
+#include <mach-o/getsect.h>
+#include <mach/ppc/thread_status.h>
+#include <mach/i386/thread_status.h>
+#include <Availability.h>
+
+#include <map>
+#include <vector>
+#include <algorithm>
+
+#include "FileAbstraction.hpp"
+#include "libunwind.h"
+#include "InternalMacros.h"
+#include "dwarf2.h"
+#include "RemoteUnwindProfile.h"
+#include "Registers.hpp"
+#include "RemoteRegisterMap.hpp"
+
+namespace lldb_private
+{
+class RemoteProcInfo;
+
+///
+/// unw_addr_space_remote is the concrete instance that a unw_addr_space_t points to when examining
+/// a remote process.
+///
+struct unw_addr_space_remote
+{
+ enum unw_as_type type; // should always be UNW_REMOTE
+ RemoteProcInfo* ras;
+};
+
+class RemoteMemoryBlob
+{
+public:
+ typedef void (*free_callback_with_arg)(void *, void*);
+ typedef void (*free_callback)(void *);
+
+ /* This object is constructed with a callback to free the memory;
+ that callback takes a pointer to the memory region and optionally
+ takes an additional argument -- the "void* arg" passed around for
+ remote unwinds, in case the driver program allocated this e.g. with
+ mach_vm_read, and needs the token to vm_deallocate it. */
+
+ RemoteMemoryBlob (uint8_t *buf, free_callback_with_arg to_free,
+ uint64_t startaddr, uint64_t len, uint64_t mh, void *arg) :
+ fBuf(buf), fToFreeWithArg(to_free), fToFree(NULL),
+ fStartAddr(startaddr), fLen(len), fMachHeader(mh),
+ fArg(arg) { }
+ RemoteMemoryBlob (uint8_t *buf, free_callback to_free, uint64_t startaddr,
+ uint64_t len, uint64_t mh, void *arg) :
+ fBuf(buf), fToFree(to_free), fToFreeWithArg(NULL),
+ fStartAddr(startaddr), fLen(len), fMachHeader(mh),
+ fArg(NULL) { }
+
+ // the following is to create a dummy RMB object for lower_bound's use in
+ // searching.
+ RemoteMemoryBlob (uint64_t startaddr) : fStartAddr(startaddr), fToFree(NULL),
+ fBuf(NULL), fToFreeWithArg(NULL), fArg(NULL), fMachHeader(-1),
+ fLen(0) { }
+ ~RemoteMemoryBlob () {
+ if (fToFreeWithArg)
+ fToFreeWithArg(fBuf, fArg);
+ else if (fToFree)
+ fToFree(fBuf);
+ }
+ bool contains_addr (uint64_t addr) {
+ if (fStartAddr <= addr && addr < fStartAddr + fLen)
+ return true;
+ else
+ return false;
+ }
+ uint8_t *get_blob_range (uint64_t remote_process_addr, int len) {
+ if (this->contains_addr (remote_process_addr) == false)
+ return NULL;
+ if (this->contains_addr (remote_process_addr + len) == false)
+ return NULL;
+ return fBuf + (remote_process_addr - fStartAddr);
+ }
+ uint64_t getMh () const { return fMachHeader; }
+ uint64_t getStartAddr() const { return fStartAddr; }
+ uint64_t getLength() const { return fLen; }
+private:
+ uint8_t *fBuf;
+ free_callback fToFree;
+ free_callback_with_arg fToFreeWithArg;
+ uint64_t fStartAddr;
+ uint64_t fLen;
+ uint64_t fMachHeader;
+ void *fArg;
+};
+
+inline bool operator<(const RemoteMemoryBlob &b1, const RemoteMemoryBlob &b2) {
+ if (b1.getStartAddr() < b2.getStartAddr())
+ return true;
+ else
+ return false;
+}
+
+// One of these for each image in memory (executable, dylib, bundle, etc)
+
+struct RemoteImageEntry
+{
+ RemoteImageEntry () : mach_header(0), text_start(0), text_end(0), eh_frame_start(0), eh_frame_len(0), compact_unwind_info_start(0), compact_unwind_info_len(0) { }
+ ~RemoteImageEntry () {
+ std::map<uint64_t, RemoteUnwindProfile *>::iterator i;
+ for (i = profiles.begin(); i != profiles.end(); ++i)
+ delete i->second;
+ }
+ uint64_t mach_header;
+ uint64_t text_start;
+ uint64_t text_end;
+ uint64_t eh_frame_start;
+ uint64_t eh_frame_len;
+ uint64_t compact_unwind_info_start;
+ uint64_t compact_unwind_info_len;
+
+ // unwind profiles created for thsi binary image so far,
+ // key is the start address of the profile.
+ std::map<uint64_t, RemoteUnwindProfile *> profiles;
+
+ // a list of function address bounds for this binary image -
+ // end addresses should be accurate and not inferred from potentially
+ // incomplete start-address data (e.g. nlist records).
+ std::vector<FuncBounds> func_bounds;
+};
+
+class RemoteImages
+{
+public:
+ RemoteImages (unw_targettype_t targarch) : fTargetArch(targarch) { }
+ ~RemoteImages ();
+ void removeAllImageProfiles();
+ void removeOneImageProfiles(uint64_t mh);
+ RemoteImageEntry *remoteEntryForTextAddr (uint64_t pc);
+ bool addFuncBounds (uint64_t mh, std::vector<FuncBounds> &startAddrs);
+ bool haveFuncBounds (uint64_t mh);
+ bool findFuncBounds (uint32_t pc, uint32_t &startAddr, uint32_t &endAddr);
+ bool findFuncBounds (uint64_t pc, uint64_t &startAddr, uint64_t &endAddr);
+ void addImage (uint64_t mh, uint64_t text_start, uint64_t text_end, uint64_t eh_frame, uint64_t eh_frame_len, uint64_t compact_unwind_start, uint64_t compact_unwind_len);
+ bool addProfile (RemoteProcInfo* procinfo, unw_accessors_t *acc, unw_addr_space_t as, uint64_t start, uint64_t end, void *arg);
+ RemoteUnwindProfile* findProfileByTextAddr (uint64_t pc);
+ void addMemBlob (RemoteMemoryBlob *blob);
+ uint8_t *getMemBlobMemory (uint64_t addr, int len);
+private:
+ RemoteImages();
+ std::map<uint64_t, RemoteImageEntry> fImages;
+ std::vector<RemoteMemoryBlob *> fMemBlobs;
+ unw_targettype_t fTargetArch;
+};
+
+RemoteImages::~RemoteImages () {
+ std::map<uint64_t, std::vector<RemoteMemoryBlob *> >::iterator i;
+ std::vector<RemoteMemoryBlob *>::iterator j;
+ for (j = fMemBlobs.begin(); j != fMemBlobs.end(); ++j) {
+ delete *j;
+ }
+ fMemBlobs.erase(fMemBlobs.begin(), fMemBlobs.end());
+}
+
+void RemoteImages::removeAllImageProfiles() {
+ fImages.erase(fImages.begin(), fImages.end());
+ std::vector<RemoteMemoryBlob *>::iterator j;
+ for (j = fMemBlobs.begin(); j != fMemBlobs.end(); ++j)
+ delete *j;
+ fMemBlobs.erase(fMemBlobs.begin(), fMemBlobs.end());
+}
+
+void RemoteImages::removeOneImageProfiles(uint64_t mh) {
+ std::map<uint64_t, RemoteImageEntry>::iterator i;
+ i = fImages.find(mh);
+ if (i != fImages.end())
+ fImages.erase(i);
+
+ std::vector<RemoteMemoryBlob *>::iterator j;
+ for (j = fMemBlobs.begin(); j != fMemBlobs.end(); ++j) {
+ if ((*j)->getMh() == mh) {
+ delete *j;
+ break;
+ }
+ }
+ if (j != fMemBlobs.end())
+ fMemBlobs.erase(j);
+}
+
+RemoteImageEntry *RemoteImages::remoteEntryForTextAddr (uint64_t pc) {
+ std::map<uint64_t, RemoteImageEntry>::iterator i = fImages.lower_bound (pc);
+ if (i == fImages.begin() && i == fImages.end())
+ return NULL;
+ if (i == fImages.end()) {
+ --i;
+ } else {
+ if (i != fImages.begin() && i->first != pc)
+ --i;
+ }
+ if (i->second.text_start <= pc && i->second.text_end > pc)
+ {
+ return &(i->second);
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+bool RemoteImages::addFuncBounds (uint64_t mh, std::vector<FuncBounds> &startAddrs) {
+ RemoteImageEntry *img = NULL;
+ std::map<uint64_t, RemoteImageEntry>::iterator i = fImages.find (mh);
+ if (i == fImages.end())
+ return false;
+ img = &i->second;
+ img->func_bounds = startAddrs;
+ std::sort(img->func_bounds.begin(), img->func_bounds.end());
+ return true;
+}
+
+bool RemoteImages::haveFuncBounds (uint64_t mh) {
+ RemoteImageEntry *img = NULL;
+ std::map<uint64_t, RemoteImageEntry>::iterator i = fImages.find (mh);
+ if (i == fImages.end())
+ return false;
+ img = &i->second;
+ if (img->func_bounds.size() > 0)
+ return true;
+ return false;
+}
+
+bool RemoteImages::findFuncBounds (uint64_t pc, uint64_t &startAddr, uint64_t &endAddr) {
+ RemoteImageEntry *img = NULL;
+ startAddr = endAddr = 0;
+ std::map<uint64_t, RemoteImageEntry>::iterator i = fImages.lower_bound (pc);
+ if (i == fImages.begin() && i == fImages.end())
+ return false;
+ if (i == fImages.end()) {
+ --i;
+ } else {
+ if (i != fImages.begin() && i->first != pc)
+ --i;
+ }
+ if (i->second.text_start <= pc && i->second.text_end > pc)
+ {
+ img = &i->second;
+ }
+ else
+ return false;
+ std::vector<FuncBounds>::iterator j;
+ j = std::lower_bound(img->func_bounds.begin(), img->func_bounds.end(), FuncBounds (pc, pc));
+ if (j == img->func_bounds.begin() && j == img->func_bounds.end())
+ return false;
+ if (j == img->func_bounds.end()) {
+ --j;
+ } else {
+ if (j != img->func_bounds.begin() && j->fStart != pc)
+ --j;
+ }
+ if (j->fStart <= pc && j->fEnd > pc) {
+ startAddr = j->fStart;
+ endAddr = j->fEnd;
+ return true;
+ }
+ return false;
+}
+
+// Add 32-bit version of findFuncBounds so we can avoid templatizing all of these functions
+// just to handle 64 and 32 bit unwinds.
+
+bool RemoteImages::findFuncBounds (uint32_t pc, uint32_t &startAddr, uint32_t &endAddr) {
+ uint64_t big_startAddr = startAddr;
+ uint64_t big_endAddr = endAddr;
+ bool ret;
+ ret = findFuncBounds (pc, big_startAddr, big_endAddr);
+ startAddr = (uint32_t) big_startAddr & 0xffffffff;
+ endAddr = (uint32_t) big_endAddr & 0xffffffff;
+ return ret;
+}
+
+// Make sure we don't cache the same memory range more than once
+// I'm not checking the length of the blobs to check for overlap -
+// as this is used today, the only duplication will be with the same
+// start address.
+
+void RemoteImages::addMemBlob (RemoteMemoryBlob *blob) {
+ std::vector<RemoteMemoryBlob *>::iterator i;
+ for (i = fMemBlobs.begin(); i != fMemBlobs.end(); ++i) {
+ if (blob->getStartAddr() == (*i)->getStartAddr())
+ return;
+ }
+ fMemBlobs.push_back(blob);
+ std::sort(fMemBlobs.begin(), fMemBlobs.end());
+}
+
+uint8_t *RemoteImages::getMemBlobMemory (uint64_t addr, int len) {
+ uint8_t *res = NULL;
+ std::vector<RemoteMemoryBlob *>::iterator j;
+ RemoteMemoryBlob *searchobj = new RemoteMemoryBlob(addr);
+ j = std::lower_bound (fMemBlobs.begin(), fMemBlobs.end(), searchobj);
+ delete searchobj;
+ if (j == fMemBlobs.end() && j == fMemBlobs.begin())
+ return NULL;
+ if (j == fMemBlobs.end()) {
+ --j;
+ } else {
+ if (j != fMemBlobs.begin() && (*j)->getStartAddr() != addr)
+ --j;
+ }
+ res = (*j)->get_blob_range (addr, len);
+ if (res != NULL)
+ return res;
+ for (j = fMemBlobs.begin(); j != fMemBlobs.end(); ++j) {
+ res = (*j)->get_blob_range (addr, len);
+ if (res != NULL)
+ break;
+ }
+ return res;
+}
+
+void RemoteImages::addImage (uint64_t mh, uint64_t text_start,
+ uint64_t text_end, uint64_t eh_frame,
+ uint64_t eh_frame_len,
+ uint64_t compact_unwind_start,
+ uint64_t compact_unwind_len) {
+ struct RemoteImageEntry img;
+ img.mach_header = mh;
+ img.text_start = text_start;
+ img.text_end = text_end;
+ img.eh_frame_start = eh_frame;
+ img.eh_frame_len = eh_frame_len;
+ img.compact_unwind_info_start = compact_unwind_start;
+ img.compact_unwind_info_len = compact_unwind_len;
+ fImages[mh] = img;
+}
+
+// The binary image for this start/end address must already be present
+bool RemoteImages::addProfile (RemoteProcInfo* procinfo, unw_accessors_t *acc, unw_addr_space_t as, uint64_t start, uint64_t end, void *arg) {
+ RemoteImageEntry *img = NULL;
+ std::map<uint64_t, RemoteImageEntry>::iterator i = fImages.lower_bound (start);
+ if (i == fImages.begin() && i == fImages.end())
+ return false;
+ if (i == fImages.end()) {
+ --i;
+ } else {
+ if (i != fImages.begin() && i->first != start) {
+ --i;
+ }
+ }
+ if (i->second.text_start <= start && i->second.text_end > start)
+ {
+ img = &i->second;
+ }
+ else
+ return false;
+ RemoteUnwindProfile* profile = new RemoteUnwindProfile;
+ if (AssemblyParse (procinfo, acc, as, start, end, *profile, arg)) {
+ img->profiles[start] = profile;
+ return true;
+ }
+ return false;
+}
+
+RemoteUnwindProfile* RemoteImages::findProfileByTextAddr (uint64_t pc) {
+ RemoteImageEntry *img = NULL;
+ std::map<uint64_t, RemoteImageEntry>::iterator i = fImages.lower_bound (pc);
+ if (i == fImages.begin() && i == fImages.end())
+ return NULL;
+ if (i == fImages.end()) {
+ --i;
+ } else {
+ if (i != fImages.begin() && i->first != pc)
+ --i;
+ }
+ if (i->second.text_start <= pc && i->second.text_end > pc)
+ {
+ img = &i->second;
+ }
+ else
+ return false;
+ std::map<uint64_t, RemoteUnwindProfile *>::iterator j;
+ j = img->profiles.lower_bound (pc);
+ if (j == img->profiles.begin() && j == img->profiles.end())
+ return NULL;
+ if (j == img->profiles.end()) {
+ --j;
+ } else {
+ if (j != img->profiles.begin() && j->first != pc)
+ --j;
+ }
+ if (j->second->fStart <= pc && j->second->fEnd > pc)
+ {
+ return j->second;
+ }
+ return NULL;
+}
+
+///
+/// RemoteProcInfo is used as a template parameter to UnwindCursor when
+/// unwinding a thread that has a custom set of accessors. It calls the
+/// custom accessors for all data.
+///
+class RemoteProcInfo
+{
+public:
+
+// libunwind documentation specifies that unw_create_addr_space defaults to
+// UNW_CACHE_NONE but that's going to work very poorly for us so we're
+// defaulting to UNW_CACHE_GLOBAL.
+
+ RemoteProcInfo(unw_accessors_t* accessors, unw_targettype_t targarch) :
+ fAccessors(*accessors), fCachingPolicy(UNW_CACHE_GLOBAL),
+ fTargetArch(targarch), fImages(targarch), fLogging(NULL),
+ fLogLevel(UNW_LOG_LEVEL_NONE)
+ {
+ fWrapper.type = UNW_REMOTE;
+ fWrapper.ras = this;
+ fRemoteRegisterMap = new RemoteRegisterMap(accessors, targarch);
+ if (fTargetArch == UNW_TARGET_X86_64 || fTargetArch == UNW_TARGET_I386
+ || fTargetArch == UNW_TARGET_ARM)
+ fLittleEndian = true;
+ else
+ fLittleEndian = false;
+ }
+
+ ~RemoteProcInfo () {
+ delete fRemoteRegisterMap;
+ }
+
+ bool haveProfile (uint64_t pc) {
+ if (fImages.findProfileByTextAddr (pc))
+ return true;
+ else
+ return false;
+ }
+
+ // returns NULL if profile does not yet exist.
+ RemoteUnwindProfile* findProfile (uint64_t pc) {
+ return fImages.findProfileByTextAddr (pc);
+ }
+
+ // returns NULL if the binary image is not yet added.
+ bool addProfile (unw_accessors_t *acc, unw_addr_space_t as, uint64_t start, uint64_t end, void *arg) {
+ if (fImages.addProfile (this, acc, as, start, end, arg))
+ return true;
+ else
+ return false;
+ }
+
+ bool haveImageEntry (uint64_t pc, void *arg);
+
+ bool getImageAddresses (uint64_t pc, uint64_t &mh, uint64_t &text_start, uint64_t &text_end,
+ uint64_t &eh_frame_start, uint64_t &eh_frame_len, uint64_t &compact_unwind_start,
+ void *arg);
+ bool getImageAddresses (uint64_t pc, uint32_t &mh, uint32_t &text_start, uint32_t &text_end,
+ uint32_t &eh_frame_start, uint32_t &eh_frame_len, uint32_t &compact_unwind_start,
+ void *arg);
+
+ bool addFuncBounds (uint64_t mh, std::vector<FuncBounds> &startAddrs) { return fImages.addFuncBounds (mh, startAddrs); }
+ bool haveFuncBounds (uint64_t mh) { return fImages.haveFuncBounds (mh); }
+ bool findStartAddr (uint64_t pc, uint32_t &startAddr, uint32_t &endAddr) { return fImages.findFuncBounds (pc, startAddr, endAddr); }
+ bool findStartAddr (uint64_t pc, uint64_t &startAddr, uint64_t &endAddr) { return fImages.findFuncBounds (pc, startAddr, endAddr); }
+ uint8_t *getMemBlobMemory (uint64_t addr, int len) { return fImages.getMemBlobMemory (addr, len); }
+
+
+ // Functions to pull memory from the target into the debugger.
+
+ int getBytes(uint64_t addr, uint64_t extent, uint8_t* buf, void* arg)
+ {
+ int err = readRaw(addr, extent, buf, arg);
+
+ if(err)
+ return 0;
+
+ return 1;
+ }
+
+#define DECLARE_INT_ACCESSOR(bits) \
+ uint##bits##_t get##bits(uint64_t addr, void* arg) \
+ { \
+ uint##bits##_t ret; \
+ int err = readRaw(addr, (unw_word_t)(bits / 8), (uint8_t*)&ret, arg); \
+ \
+ if(err) \
+ ABORT("Invalid memory access in the target"); \
+ \
+ return ret; \
+ }
+ DECLARE_INT_ACCESSOR(8)
+ DECLARE_INT_ACCESSOR(16)
+ DECLARE_INT_ACCESSOR(32)
+ DECLARE_INT_ACCESSOR(64)
+#undef DECLARE_INT_ACCESSOR
+
+// 'err' is set to 0 if there were no errors reading this
+// memory. Non-zero values indicate that the memory was not
+// read successfully. This method should be preferred over the
+// method above which asserts on failure.
+
+#define DECLARE_INT_ACCESSOR_ERR(bits) \
+ uint##bits##_t get##bits(uint64_t addr, int &err, void* arg) \
+ { \
+ uint##bits##_t ret; \
+ err = readRaw(addr, (unw_word_t)(bits / 8), (uint8_t*)&ret, arg); \
+ \
+ return ret; \
+ }
+ DECLARE_INT_ACCESSOR_ERR(8)
+ DECLARE_INT_ACCESSOR_ERR(16)
+ DECLARE_INT_ACCESSOR_ERR(32)
+ DECLARE_INT_ACCESSOR_ERR(64)
+#undef DECLARE_INT_ACCESSOR_ERR
+
+ double getDouble(uint64_t addr, void* arg)
+ {
+ double ret;
+ int err = readRaw(addr, (unw_word_t)(sizeof(ret) / 8), (uint8_t*)&ret, arg);
+ if(err)
+ ABORT("Invalid memory access in the target");
+ return ret;
+ }
+
+ v128 getVector(uint64_t addr, void* arg)
+ {
+ v128 ret;
+ int err = readRaw(addr, (unw_word_t)(sizeof(ret) / 8), (uint8_t*)&ret, arg);
+ if(err)
+ ABORT("Invalid memory access in the target");
+ return ret;
+ }
+
+ // Pull an unsigned LEB128 from the target into the debugger as a uint64_t.
+ uint64_t getULEB128(uint64_t& addr, uint64_t end, void* arg)
+ {
+ uint64_t lAddr = addr;
+ uint64_t ret = 0;
+ uint8_t shift = 0;
+ uint64_t byte;
+ do {
+ if(lAddr == end)
+ ABORT("Truncated LEB128 number in the target");
+
+ byte = (uint64_t)get8(lAddr, arg);
+ lAddr++;
+
+ if(((shift == 63) && (byte > 0x01)) || (shift > 63))
+ ABORT("LEB128 number is larger than is locally representible");
+
+ ret |= ((byte & 0x7f) << shift);
+ shift += 7;
+ } while((byte & 0x80) == 0x80);
+ addr = lAddr;
+ return ret;
+ }
+
+ // Pull an unsigned LEB128 from the target into the debugger as a uint64_t.
+ uint64_t getULEB128(uint32_t& addr, uint32_t end, void* arg)
+ {
+ uint32_t lAddr = addr;
+ uint64_t ret = 0;
+ uint8_t shift = 0;
+ uint64_t byte;
+ do {
+ if(lAddr == end)
+ ABORT("Truncated LEB128 number in the target");
+
+ byte = (uint64_t)get8(lAddr, arg);
+ lAddr++;
+
+ if(((shift == 63) && (byte > 0x01)) || (shift > 63))
+ ABORT("LEB128 number is larger than is locally representible");
+
+ ret |= ((byte & 0x7f) << shift);
+ shift += 7;
+ } while((byte & 0x80) == 0x80);
+ addr = lAddr;
+ return ret;
+ }
+
+
+ // Pull a signed LEB128 from the target into the debugger as a uint64_t.
+ int64_t getSLEB128(uint64_t& addr, uint64_t end, void* arg)
+ {
+ uint64_t lAddr = addr;
+ uint64_t ret = 0;
+ uint8_t shift = 0;
+ uint64_t byte;
+ do {
+ if(lAddr == end)
+ ABORT("Truncated LEB128 number in the target");
+ byte = (uint64_t)get8(lAddr, arg);
+ lAddr++;
+ if(((shift == 63) && (byte > 0x01)) || (shift > 63))
+ ABORT("LEB128 number is larger than is locally representible");
+ ret |= ((byte & 0x7f) << shift);
+ shift += 7;
+ } while((byte & 0x80) == 0x80);
+ // Sign-extend
+ if((shift < (sizeof(int64_t) * 8)) && (byte & 0x40))
+ ret |= -(1 << shift);
+ addr = lAddr;
+ return ret;
+ }
+
+ // Pull a signed LEB128 from the target into the debugger as a uint64_t.
+ int64_t getSLEB128(uint32_t& addr, uint32_t end, void* arg)
+ {
+ uint32_t lAddr = addr;
+ uint64_t ret = 0;
+ uint8_t shift = 0;
+ uint64_t byte;
+ do {
+ if(lAddr == end)
+ ABORT("Truncated LEB128 number in the target");
+ byte = (uint64_t)get8(lAddr, arg);
+ lAddr++;
+ if(((shift == 63) && (byte > 0x01)) || (shift > 63))
+ ABORT("LEB128 number is larger than is locally representible");
+ ret |= ((byte & 0x7f) << shift);
+ shift += 7;
+ } while((byte & 0x80) == 0x80);
+ // Sign-extend
+ if((shift < (sizeof(int64_t) * 8)) && (byte & 0x40))
+ ret |= -(1 << shift);
+ addr = lAddr;
+ return ret;
+ }
+
+
+ uint64_t getP (uint64_t addr, void *arg) {
+ switch (fTargetArch) {
+ case UNW_TARGET_X86_64:
+ return get64(addr, arg);
+ break;
+ case UNW_TARGET_I386:
+ return get32(addr, arg);
+ break;
+ }
+ ABORT("Unknown target architecture.");
+ return 0;
+ }
+
+ uint64_t getP (uint64_t addr, int& err, void *arg) {
+ switch (fTargetArch) {
+ case UNW_TARGET_X86_64:
+ return get64(addr, err, arg);
+ break;
+ case UNW_TARGET_I386:
+ return get32(addr, err, arg);
+ break;
+ }
+ ABORT("Unknown target architecture.");
+ return 0;
+ }
+
+ bool findFunctionName(uint64_t addr, char *buf, size_t bufLen, unw_word_t *offset, void* arg);
+ bool findFunctionBounds(uint64_t addr, uint64_t& low, uint64_t& high, void* arg);
+ int setCachingPolicy(unw_caching_policy_t policy);
+
+ void setLoggingLevel(FILE *f, unw_log_level_t level);
+ void logInfo(const char *fmt, ...);
+ void logAPI(const char *fmt, ...);
+ void logVerbose(const char *fmt, ...);
+ void logDebug(const char *fmt, ...);
+ struct timeval *timestamp_start ();
+ void timestamp_stop (struct timeval *tstart, const char *fmt, ...);
+
+ void flushAllCaches() { fImages.removeAllImageProfiles(); }
+ void flushCacheByMachHeader(uint64_t mh) { fImages.removeOneImageProfiles(mh); }
+ unw_targettype_t getTargetArch() { return fTargetArch; }
+ unw_accessors_t* getAccessors () { return &fAccessors; }
+ RemoteRegisterMap* getRegisterMap() { return fRemoteRegisterMap; }
+ unw_addr_space_t wrap () { return (unw_addr_space_t) &fWrapper; }
+ bool remoteIsLittleEndian () { return fLittleEndian; }
+ unw_log_level_t getDebugLoggingLevel() { return fLogLevel; }
+ void addMemBlob (RemoteMemoryBlob *blob) { fImages.addMemBlob(blob); }
+ unw_caching_policy_t getCachingPolicy() { return fCachingPolicy; }
+
+private:
+ int readRaw(uint64_t addr, uint64_t extent, uint8_t *valp, void* arg)
+ {
+ uint8_t *t = this->getMemBlobMemory (addr, extent);
+ if (t) {
+ memcpy (valp, t, extent);
+ return 0;
+ }
+ return fAccessors.access_raw((unw_addr_space_t)this, addr, extent, valp, 0, arg);
+ }
+
+ struct unw_addr_space_remote fWrapper;
+ unw_accessors_t fAccessors;
+ unw_caching_policy_t fCachingPolicy;
+ unw_targettype_t fTargetArch;
+ unw_addr_space_t fAddrSpace;
+ RemoteImages fImages;
+ RemoteRegisterMap *fRemoteRegisterMap;
+ FILE *fLogging;
+ unw_log_level_t fLogLevel;
+ bool fLittleEndian;
+};
+
+// Find an image containing the given pc, returns false if absent and
+// we can't add it via the accessors.
+bool RemoteProcInfo::haveImageEntry (uint64_t pc, void *arg) {
+ if (fImages.remoteEntryForTextAddr (pc) == NULL) {
+ unw_word_t mh, text_start, text_end, eh_frame, eh_frame_len, compact_unwind, compact_unwind_len;
+ if (fAccessors.find_image_info (wrap(), pc, &mh, &text_start,
+ &text_end, &eh_frame, &eh_frame_len, &compact_unwind, &compact_unwind_len, arg) == UNW_ESUCCESS) {
+ fImages.addImage (mh, text_start, text_end, eh_frame, eh_frame_len, compact_unwind, compact_unwind_len);
+ if (fCachingPolicy != UNW_CACHE_NONE) {
+ if (compact_unwind_len != 0) {
+ logVerbose ("Creating RemoteMemoryBlob of compact unwind info image at mh 0x%llx, %lld bytes", mh, (uint64_t) compact_unwind_len);
+ uint8_t *buf = (uint8_t*) malloc (compact_unwind_len);
+ if (this->getBytes (compact_unwind, compact_unwind_len, buf, arg)) {
+ RemoteMemoryBlob *b = new RemoteMemoryBlob(buf, free, compact_unwind, compact_unwind_len, mh, NULL);
+ fImages.addMemBlob (b);
+ }
+ } else if (eh_frame_len != 0) {
+ logVerbose ("Creating RemoteMemoryBlob of eh_frame for image at mh 0x%llx, %lld bytes", mh, (uint64_t) compact_unwind_len);
+ uint8_t *buf = (uint8_t*) malloc (eh_frame_len);
+ if (this->getBytes (eh_frame, eh_frame_len, buf, arg)) {
+ RemoteMemoryBlob *b = new RemoteMemoryBlob(buf, free, eh_frame, eh_frame_len, mh, NULL);
+ fImages.addMemBlob (b);
+ }
+ }
+ }
+ } else {
+ return false; /// find_image_info failed
+ }
+ } else {
+ return true;
+ }
+ return true;
+}
+
+bool RemoteProcInfo::getImageAddresses (uint64_t pc, uint64_t &mh, uint64_t &text_start, uint64_t &text_end,
+ uint64_t &eh_frame_start, uint64_t &eh_frame_len, uint64_t &compact_unwind_start,
+ void *arg) {
+ // Make sure we have this RemoteImageEntry already - fetch it now if needed.
+ if (haveImageEntry (pc, arg) == false) {
+ return false;
+ }
+ RemoteImageEntry *r = fImages.remoteEntryForTextAddr (pc);
+ if (r) {
+ mh = r->mach_header;
+ text_start = r->text_start;
+ text_end = r->text_end;
+ eh_frame_start = r->eh_frame_start;
+ eh_frame_len = r->eh_frame_len;
+ compact_unwind_start = r->compact_unwind_info_start;
+ return true;
+ }
+ return false;
+}
+
+
+bool RemoteProcInfo::findFunctionName(uint64_t addr, char *buf, size_t bufLen, unw_word_t *offset, void* arg)
+{
+ if(fAccessors.get_proc_name(wrap(), addr, buf, bufLen, offset, arg) == UNW_ESUCCESS)
+ return true;
+ else
+ return false;
+}
+
+bool RemoteProcInfo::findFunctionBounds(uint64_t addr, uint64_t& low, uint64_t& high, void* arg)
+{
+ if (fAccessors.get_proc_bounds(wrap(), addr, &low, &high, arg) == UNW_ESUCCESS
+ && high != 0)
+ return true;
+ else
+ return false;
+}
+
+int RemoteProcInfo::setCachingPolicy(unw_caching_policy_t policy)
+{
+ if(policy == UNW_CACHE_NONE && fCachingPolicy != UNW_CACHE_NONE)
+ {
+ flushAllCaches();
+ }
+
+ if(!(policy == UNW_CACHE_NONE || policy == UNW_CACHE_GLOBAL || policy == UNW_CACHE_PER_THREAD))
+ return UNW_EINVAL;
+
+ fCachingPolicy = policy;
+
+ return UNW_ESUCCESS;
+}
+
+void RemoteProcInfo::setLoggingLevel(FILE *f, unw_log_level_t level)
+{
+ fLogLevel = level;
+ fLogging = f;
+}
+
+void RemoteProcInfo::logInfo(const char *fmt, ...)
+{
+ if (fLogging == NULL || fLogLevel == UNW_LOG_LEVEL_NONE)
+ return;
+ if (fLogLevel & UNW_LOG_LEVEL_INFO) {
+ va_list ap;
+ va_start (ap, fmt);
+ vfprintf (fLogging, fmt, ap);
+ fputs ("\n", fLogging);
+ va_end (ap);
+ }
+}
+
+void RemoteProcInfo::logAPI(const char *fmt, ...)
+{
+ if (fLogging == NULL || fLogLevel == UNW_LOG_LEVEL_NONE)
+ return;
+ if (fLogLevel & UNW_LOG_LEVEL_API) {
+ va_list ap;
+ va_start (ap, fmt);
+ vfprintf (fLogging, fmt, ap);
+ fputs ("\n", fLogging);
+ va_end (ap);
+ }
+}
+
+void RemoteProcInfo::logVerbose(const char *fmt, ...)
+{
+ if (fLogging == NULL || fLogLevel == UNW_LOG_LEVEL_NONE)
+ return;
+ if (fLogLevel & UNW_LOG_LEVEL_VERBOSE) {
+ va_list ap;
+ va_start (ap, fmt);
+ vfprintf (fLogging, fmt, ap);
+ fputs ("\n", fLogging);
+ va_end (ap);
+ }
+}
+
+void RemoteProcInfo::logDebug(const char *fmt, ...)
+{
+ if (fLogging == NULL || fLogLevel == UNW_LOG_LEVEL_NONE)
+ return;
+ if (fLogLevel & UNW_LOG_LEVEL_DEBUG) {
+ va_list ap;
+ va_start (ap, fmt);
+ vfprintf (fLogging, fmt, ap);
+ fputs ("\n", fLogging);
+ va_end (ap);
+ }
+}
+
+struct timeval *RemoteProcInfo::timestamp_start ()
+{
+ if (fLogging == NULL || fLogLevel == UNW_LOG_LEVEL_NONE)
+ return NULL;
+ if (fLogLevel & UNW_LOG_LEVEL_TIMINGS) {
+ struct timeval *t = (struct timeval *) malloc (sizeof (struct timeval));
+ if (gettimeofday (t, NULL) != 0) {
+ free (t);
+ return NULL;
+ }
+ return t;
+ }
+ return NULL;
+}
+
+void RemoteProcInfo::timestamp_stop (struct timeval *tstart, const char *fmt, ...)
+{
+ if (fLogging == NULL || fLogLevel == UNW_LOG_LEVEL_NONE || tstart == NULL)
+ return;
+ if (fLogLevel & UNW_LOG_LEVEL_TIMINGS) {
+ struct timeval tend;
+ if (gettimeofday (&tend, NULL) != 0) {
+ free (tstart);
+ return;
+ }
+ struct timeval result;
+ timersub (&tend, tstart, &result);
+ va_list ap;
+ va_start (ap, fmt);
+ vprintf (fmt, ap);
+ printf (" duration %0.5fs\n", (double) ((result.tv_sec * 1000000) + result.tv_usec) / 1000000.0);
+ va_end (ap);
+ free (tstart);
+ }
+}
+
+
+// Initialize the register context at the start of a remote unwind.
+
+void getRemoteContext (RemoteProcInfo* procinfo, Registers_x86_64& r, void *arg) {
+ unw_accessors_t* accessors = procinfo->getAccessors();
+ unw_addr_space_t addrSpace = procinfo->wrap();
+ RemoteRegisterMap* regmap = procinfo->getRegisterMap();
+ uint64_t rv;
+
+ // now that we have a selected process/thread, ask about the valid registers.
+ regmap->scan_caller_regs (addrSpace, arg);
+
+#define FILLREG(reg) {int caller_reg; regmap->unwind_regno_to_caller_regno ((reg), caller_reg); accessors->access_reg (addrSpace, caller_reg, &rv, 0, arg); r.setRegister ((reg), rv);}
+ FILLREG (UNW_X86_64_RAX);
+ FILLREG (UNW_X86_64_RDX);
+ FILLREG (UNW_X86_64_RCX);
+ FILLREG (UNW_X86_64_RBX);
+ FILLREG (UNW_X86_64_RSI);
+ FILLREG (UNW_X86_64_RDI);
+ FILLREG (UNW_X86_64_RBP);
+ FILLREG (UNW_X86_64_RSP);
+ FILLREG (UNW_X86_64_R8);
+ FILLREG (UNW_X86_64_R9);
+ FILLREG (UNW_X86_64_R10);
+ FILLREG (UNW_X86_64_R11);
+ FILLREG (UNW_X86_64_R12);
+ FILLREG (UNW_X86_64_R13);
+ FILLREG (UNW_X86_64_R14);
+ FILLREG (UNW_X86_64_R15);
+ FILLREG (UNW_REG_IP);
+#undef FILLREG
+}
+
+void getRemoteContext (RemoteProcInfo* procinfo, Registers_x86& r, void *arg) {
+ unw_accessors_t* accessors = procinfo->getAccessors();
+ unw_addr_space_t addrSpace = procinfo->wrap();
+ RemoteRegisterMap* regmap = procinfo->getRegisterMap();
+ uint64_t rv;
+
+ // now that we have a selected process/thread, ask about the valid registers.
+ regmap->scan_caller_regs (addrSpace, arg);
+
+#define FILLREG(reg) {int caller_reg; regmap->unwind_regno_to_caller_regno ((reg), caller_reg); accessors->access_reg (addrSpace, caller_reg, &rv, 0, arg); r.setRegister ((reg), rv);}
+ FILLREG (UNW_X86_EAX);
+ FILLREG (UNW_X86_ECX);
+ FILLREG (UNW_X86_EDX);
+ FILLREG (UNW_X86_EBX);
+ FILLREG (UNW_X86_EBP);
+ FILLREG (UNW_X86_ESP);
+ FILLREG (UNW_X86_ESI);
+ FILLREG (UNW_X86_EDI);
+ FILLREG (UNW_REG_IP);
+#undef FILLREG
+}
+
+
+void getRemoteContext (RemoteProcInfo* procinfo, Registers_ppc& r, void *arg) {
+ ABORT("ppc get remote context not implemented.");
+}
+
+}; // namespace lldb_private
+
+
+
+#endif // SUPPORT_REMOTE_UNWINDING
+#endif // __REMOTE_PROC_INFO_HPP__
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteRegisterMap.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteRegisterMap.hpp
new file mode 100644
index 00000000000..19caae9a3f4
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteRegisterMap.hpp
@@ -0,0 +1,405 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- RemoteRegisterMap.hpp -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// Provide conversions between reigster names, the libunwind internal enums,
+// and the register numbers the program calling libunwind are using.
+
+#ifndef __REMOTE_REGISTER_MAP_HPP__
+#define __REMOTE_REGISTER_MAP_HPP__
+
+#if defined (SUPPORT_REMOTE_UNWINDING)
+
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS
+#endif
+#ifndef __STDC_CONSTANT_MACROS
+#define __STDC_CONSTANT_MACROS
+#endif
+
+#include "libunwind.h"
+#include <vector>
+
+namespace lldb_private
+{
+class RemoteRegisterMap {
+public:
+ RemoteRegisterMap (unw_accessors_t *accessors, unw_targettype_t target);
+ ~RemoteRegisterMap ();
+ void initialize_x86_64 ();
+ void initialize_i386 ();
+ bool name_to_caller_regno (const char *name, int& callerr);
+ bool name_to_unwind_regno (const char *name, int& unwindr);
+ bool unwind_regno_to_caller_regno (int unwindr, int& callerr);
+ bool nonvolatile_reg_p (int unwind_regno);
+ bool argument_regnum_p (int unwind_regno);
+ const char *ip_register_name();
+ const char *sp_register_name();
+ int caller_regno_for_ip ();
+ int caller_regno_for_sp ();
+ int unwind_regno_for_frame_pointer ();
+ int unwind_regno_for_stack_pointer ();
+ int wordsize () { return fWordSize; }
+ void scan_caller_regs (unw_addr_space_t as, void *arg);
+
+ bool unwind_regno_to_machine_regno (int unwindr, int& machiner);
+ bool machine_regno_to_unwind_regno (int machr, int& unwindr);
+ bool caller_regno_to_unwind_regno (int callerr, int& unwindr);
+ const char* unwind_regno_to_name (int unwindr);
+ int byte_size_for_regtype (unw_regtype_t type);
+
+private:
+
+ // A structure that collects everything we need to know about a
+ // given register in one place.
+ struct reg {
+ int unwind_regno; // What libunwind-remote uses internally
+ int caller_regno; // What the libunwind-remote driver program uses
+ int eh_frame_regno; // What the eh_frame section uses
+ int machine_regno; // What the actual bits/bytes are in instructions
+ char *name;
+ unw_regtype_t type;
+ reg () : unwind_regno(-1), machine_regno(-1), caller_regno(-1),
+ eh_frame_regno(-1), name(NULL), type(UNW_NOT_A_REG) { }
+ };
+
+ unw_accessors_t fAccessors;
+ unw_targettype_t fTarget;
+ std::vector<RemoteRegisterMap::reg> fRegMap;
+ int fWordSize;
+};
+
+void RemoteRegisterMap::initialize_x86_64 () {
+#define DEFREG(ureg, ehno, machno, regn) {RemoteRegisterMap::reg r; r.unwind_regno = ureg; r.name = regn; r.eh_frame_regno = ehno; r.machine_regno = machno; r.type = UNW_INTEGER_REG; fRegMap.push_back(r); }
+ DEFREG (UNW_X86_64_RAX, 0, 0, strdup ("rax"));
+ DEFREG (UNW_X86_64_RDX, 1, 2, strdup ("rdx"));
+ DEFREG (UNW_X86_64_RCX, 2, 1, strdup ("rcx"));
+ DEFREG (UNW_X86_64_RBX, 3, 3, strdup ("rbx"));
+ DEFREG (UNW_X86_64_RSI, 4, 6, strdup ("rsi"));
+ DEFREG (UNW_X86_64_RDI, 5, 7, strdup ("rdi"));
+ DEFREG (UNW_X86_64_RBP, 6, 5, strdup ("rbp"));
+ DEFREG (UNW_X86_64_RSP, 7, 4, strdup ("rsp"));
+ DEFREG (UNW_X86_64_R8, 8, 8, strdup ("r8"));
+ DEFREG (UNW_X86_64_R9, 9, 9, strdup ("r9"));
+ DEFREG (UNW_X86_64_R10, 10, 10, strdup ("r10"));
+ DEFREG (UNW_X86_64_R11, 11, 11, strdup ("r11"));
+ DEFREG (UNW_X86_64_R12, 12, 12, strdup ("r12"));
+ DEFREG (UNW_X86_64_R13, 13, 13, strdup ("r13"));
+ DEFREG (UNW_X86_64_R14, 14, 14, strdup ("r14"));
+ DEFREG (UNW_X86_64_R15, 15, 15, strdup ("r15"));
+#undef DEFREG
+ RemoteRegisterMap::reg r;
+ r.name = strdup ("rip");
+ r.type = UNW_INTEGER_REG;
+ r.eh_frame_regno = 16;
+ fRegMap.push_back(r);
+}
+
+void RemoteRegisterMap::initialize_i386 () {
+#define DEFREG(ureg, ehno, machno, regn) {RemoteRegisterMap::reg r; r.unwind_regno = ureg; r.name = regn; r.eh_frame_regno = ehno; r.machine_regno = machno; r.type = UNW_INTEGER_REG; fRegMap.push_back(r); }
+ DEFREG (UNW_X86_EAX, 0, 0, strdup ("eax"));
+ DEFREG (UNW_X86_ECX, 1, 1, strdup ("ecx"));
+ DEFREG (UNW_X86_EDX, 2, 2, strdup ("edx"));
+ DEFREG (UNW_X86_EBX, 3, 3, strdup ("ebx"));
+ // i386 EH frame info has the next two swapped,
+ // v. gcc/config/i386/darwin.h:DWARF2_FRAME_REG_OUT.
+ DEFREG (UNW_X86_EBP, 4, 5, strdup ("ebp"));
+ DEFREG (UNW_X86_ESP, 5, 4, strdup ("esp"));
+ DEFREG (UNW_X86_ESI, 6, 6, strdup ("esi"));
+ DEFREG (UNW_X86_EDI, 7, 7, strdup ("edi"));
+#undef DEFREG
+ RemoteRegisterMap::reg r;
+ r.name = strdup ("eip");
+ r.type = UNW_INTEGER_REG;
+ r.eh_frame_regno = 8;
+ fRegMap.push_back(r);
+}
+
+
+RemoteRegisterMap::RemoteRegisterMap (unw_accessors_t *accessors, unw_targettype_t target) {
+ fAccessors = *accessors;
+ fTarget = target;
+ switch (target) {
+ case UNW_TARGET_X86_64:
+ this->initialize_x86_64();
+ fWordSize = 8;
+ break;
+ case UNW_TARGET_I386:
+ this->initialize_i386();
+ fWordSize = 4;
+ break;
+ default:
+ ABORT("RemoteRegisterMap called with unknown target");
+ }
+}
+
+RemoteRegisterMap::~RemoteRegisterMap () {
+ std::vector<RemoteRegisterMap::reg>::iterator j;
+ for (j = fRegMap.begin(); j != fRegMap.end(); ++j)
+ free (j->name);
+}
+
+bool RemoteRegisterMap::name_to_caller_regno (const char *name, int& callerr) {
+ if (name == NULL)
+ return false;
+ for (std::vector<RemoteRegisterMap::reg>::iterator j = fRegMap.begin(); j != fRegMap.end(); ++j)
+ if (strcasecmp (j->name, name) == 0) {
+ callerr = j->caller_regno;
+ return true;
+ }
+ return false;
+}
+
+bool RemoteRegisterMap::unwind_regno_to_caller_regno (int unwindr, int& callerr) {
+ if (unwindr == UNW_REG_IP) {
+ callerr = this->caller_regno_for_ip ();
+ return true;
+ }
+ if (unwindr == UNW_REG_SP) {
+ callerr = this->caller_regno_for_sp ();
+ return true;
+ }
+ for (std::vector<RemoteRegisterMap::reg>::iterator j = fRegMap.begin(); j != fRegMap.end(); ++j)
+ if (j->unwind_regno == unwindr && j->caller_regno != -1) {
+ callerr = j->caller_regno;
+ return true;
+ }
+ return false;
+}
+
+bool RemoteRegisterMap::nonvolatile_reg_p (int unwind_regno) {
+ if (fTarget == UNW_TARGET_X86_64) {
+ switch (unwind_regno) {
+ case UNW_X86_64_RBX:
+ case UNW_X86_64_RSP:
+ case UNW_X86_64_RBP: // not actually a nonvolatile but often treated as such by convention
+ case UNW_X86_64_R12:
+ case UNW_X86_64_R13:
+ case UNW_X86_64_R14:
+ case UNW_X86_64_R15:
+ case UNW_REG_IP:
+ case UNW_REG_SP:
+ return true;
+ break;
+ default:
+ return false;
+ }
+ }
+ if (fTarget == UNW_TARGET_I386) {
+ switch (unwind_regno) {
+ case UNW_X86_EBX:
+ case UNW_X86_EBP: // not actually a nonvolatile but often treated as such by convention
+ case UNW_X86_ESI:
+ case UNW_X86_EDI:
+ case UNW_X86_ESP:
+ case UNW_REG_IP:
+ case UNW_REG_SP:
+ return true;
+ break;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+
+bool RemoteRegisterMap::argument_regnum_p (int unwind_regno) {
+ if (fTarget == UNW_TARGET_X86_64) {
+ switch (unwind_regno) {
+ case UNW_X86_64_RDI: /* arg 1 */
+ case UNW_X86_64_RSI: /* arg 2 */
+ case UNW_X86_64_RDX: /* arg 3 */
+ case UNW_X86_64_RCX: /* arg 4 */
+ case UNW_X86_64_R8: /* arg 5 */
+ case UNW_X86_64_R9: /* arg 6 */
+ return true;
+ break;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+const char *RemoteRegisterMap::ip_register_name () {
+ switch (fTarget) {
+ case UNW_TARGET_X86_64:
+ return "rip";
+ case UNW_TARGET_I386:
+ return "eip";
+ default:
+ ABORT("unsupported architecture");
+ }
+ return NULL;
+}
+
+const char *RemoteRegisterMap::sp_register_name () {
+ switch (fTarget) {
+ case UNW_TARGET_X86_64:
+ return "rsp";
+ case UNW_TARGET_I386:
+ return "esp";
+ default:
+ ABORT("unsupported architecture");
+ }
+ return NULL;
+}
+
+int RemoteRegisterMap::caller_regno_for_ip () {
+ int callerr;
+ if (this->name_to_caller_regno (this->ip_register_name(), callerr))
+ return callerr;
+ return -1;
+}
+
+int RemoteRegisterMap::caller_regno_for_sp () {
+ int callerr;
+ if (this->name_to_caller_regno (this->sp_register_name(), callerr))
+ return callerr;
+ return -1;
+}
+
+int RemoteRegisterMap::unwind_regno_for_frame_pointer () {
+ switch (fTarget) {
+ case UNW_TARGET_X86_64:
+ return UNW_X86_64_RBP;
+ case UNW_TARGET_I386:
+ return UNW_X86_EBP;
+ default:
+ ABORT("cannot be reached");
+ }
+ return -1;
+}
+
+int RemoteRegisterMap::unwind_regno_for_stack_pointer () {
+ switch (fTarget) {
+ case UNW_TARGET_X86_64:
+ return UNW_X86_64_RSP;
+ case UNW_TARGET_I386:
+ return UNW_X86_ESP;
+ default:
+ ABORT("cannot be reached");
+ }
+ return -1;
+}
+
+// This call requires a "arg" which specifies a given process/thread to
+// complete unlike the rest of the RegisterMap functions. Ideally this
+// would be in the ctor but the register map is created when an
+// AddressSpace is created and we don't have a process/thread yet.
+
+void RemoteRegisterMap::scan_caller_regs (unw_addr_space_t as, void *arg) {
+ for (int i = 0; i < 256; i++) {
+ unw_regtype_t type;
+ char namebuf[16];
+ if (fAccessors.reg_info (as, i, &type, namebuf, sizeof (namebuf), arg) == UNW_ESUCCESS
+ && type != UNW_NOT_A_REG) {
+ std::vector<RemoteRegisterMap::reg>::iterator j;
+ for (j = fRegMap.begin(); j != fRegMap.end(); ++j) {
+ if (strcasecmp (j->name, namebuf) == 0) {
+ j->caller_regno = i;
+ // if we haven't picked up a reg type yet it will be UNW_NOT_A_REG via the ctor
+ if (j->type == UNW_NOT_A_REG)
+ j->type = type;
+ if (j->type != type) {
+ ABORT("Caller and libunwind disagree about type of register");
+ break;
+ }
+ }
+ }
+ // caller knows about a register we don't have a libunwind entry for
+ if (j == fRegMap.end()) {
+ RemoteRegisterMap::reg r;
+ r.name = strdup (namebuf);
+ r.caller_regno = i;
+ r.type = type;
+ fRegMap.push_back(r);
+ }
+ }
+ }
+}
+
+
+bool RemoteRegisterMap::name_to_unwind_regno (const char *name, int& unwindr) {
+ if (name == NULL)
+ return false;
+ for (std::vector<RemoteRegisterMap::reg>::iterator j = fRegMap.begin(); j != fRegMap.end(); ++j)
+ if (strcasecmp (j->name, name) == 0) {
+ unwindr = j->unwind_regno;
+ return true;
+ }
+ return false;
+}
+
+bool RemoteRegisterMap::unwind_regno_to_machine_regno (int unwindr, int& machiner) {
+ if (unwindr == UNW_REG_IP)
+ unwindr = this->caller_regno_for_ip ();
+ if (unwindr == UNW_REG_SP)
+ unwindr = this->caller_regno_for_sp ();
+ for (std::vector<RemoteRegisterMap::reg>::iterator j = fRegMap.begin(); j != fRegMap.end(); ++j)
+ if (j->unwind_regno == unwindr && j->machine_regno != -1) {
+ machiner = j->machine_regno;
+ return true;
+ }
+ return false;
+}
+bool RemoteRegisterMap::machine_regno_to_unwind_regno (int machr, int& unwindr) {
+ for (std::vector<RemoteRegisterMap::reg>::iterator j = fRegMap.begin(); j != fRegMap.end(); ++j)
+ if (j->machine_regno == machr && j->unwind_regno != -1) {
+ unwindr = j->unwind_regno;
+ return true;
+ }
+ return false;
+}
+bool RemoteRegisterMap::caller_regno_to_unwind_regno (int callerr, int& unwindr) {
+ if (this->caller_regno_for_ip() == callerr) {
+ unwindr = UNW_REG_IP;
+ return true;
+ }
+ if (this->caller_regno_for_sp() == callerr) {
+ unwindr = UNW_REG_SP;
+ return true;
+ }
+ for (std::vector<RemoteRegisterMap::reg>::iterator j = fRegMap.begin(); j != fRegMap.end(); ++j)
+ if (j->caller_regno == callerr && j->unwind_regno != -1) {
+ unwindr = j->unwind_regno;
+ return true;
+ }
+ return false;
+}
+
+const char* RemoteRegisterMap::unwind_regno_to_name (int unwindr) {
+ for (std::vector<RemoteRegisterMap::reg>::iterator j = fRegMap.begin(); j != fRegMap.end(); ++j)
+ if (j->unwind_regno == unwindr && j->name != NULL) {
+ return j->name;
+ }
+ return NULL;
+}
+
+int RemoteRegisterMap::byte_size_for_regtype (unw_regtype_t type) {
+ switch (type) {
+ case UNW_TARGET_X86_64:
+ case UNW_TARGET_I386:
+ if (type == UNW_INTEGER_REG) return fWordSize;
+ if (type == UNW_FLOATING_POINT_REG) return 8;
+ if (type == UNW_VECTOR_REG) return 16;
+ default:
+ ABORT("cannot be reached");
+ }
+ return -1;
+}
+
+
+}; // namespace lldb_private
+
+#endif // SUPPORT_REMOTE_UNWINDING
+
+#endif // __REMOTE_REGISTER_MAP_HPP__
+
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteUnwindProfile.h b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteUnwindProfile.h
new file mode 100644
index 00000000000..b03551cd21c
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/RemoteUnwindProfile.h
@@ -0,0 +1,85 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- RemoteUnwindProfile.h -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __UNWIND_PROFILE_H__
+#define __UNWIND_PROFILE_H__
+#if defined (SUPPORT_REMOTE_UNWINDING)
+
+#include <vector>
+
+// The architecture-independent profile of a function's prologue
+
+namespace lldb_private
+{
+
+class RemoteUnwindProfile {
+public:
+ RemoteUnwindProfile () : fRegistersSaved(32, 0), fRegSizes(10, 0) { }
+ struct CFALocation {
+ int regno;
+ int offset;
+ };
+ enum RegisterSavedWhere { kRegisterOffsetFromCFA, kRegisterIsCFA };
+ enum RegisterType { kGeneralPurposeRegister = 0, kFloatingPointRegister, kVectorRegister };
+ struct SavedReg {
+ int regno;
+ RegisterSavedWhere location;
+ int64_t value;
+ int adj; // Used in kRegisterInRegister e.g. when we recover the caller's rsp by
+ // taking the contents of rbp and subtracting 16.
+ RegisterType type;
+ };
+ // In the following maps the key is the address after which this change has effect.
+ //
+ // 0 push %rbp
+ // 1 mov %rsp, %rbp
+ // 2 sub $16, %rsp
+ //
+ // At saved_registers<2> we'll find the record stating that rsp is now stored in rbp.
+
+ std::map<uint64_t, CFALocation> cfa;
+ std::map<uint64_t, std::vector<SavedReg> > saved_registers;
+
+ struct CFALocation initial_cfa; // At entry to the function
+
+ std::vector<uint8_t> fRegistersSaved;
+ std::vector<uint8_t> fRegSizes;
+ SavedReg returnAddress;
+ uint64_t fStart, fEnd; // low and high pc values for this function.
+ // END is the addr of the first insn outside the function.
+ uint64_t fFirstInsnPastPrologue;
+};
+
+class RemoteProcInfo;
+
+bool AssemblyParse (RemoteProcInfo *procinfo, unw_accessors_t *as, unw_addr_space_t as, uint64_t start, uint64_t end, RemoteUnwindProfile &profile, void *arg);
+
+
+class FuncBounds {
+ public:
+ FuncBounds (uint64_t low, uint64_t high) : fStart(low), fEnd(high) { }
+ uint64_t fStart;
+ uint64_t fEnd;
+};
+
+inline bool operator<(const FuncBounds &ap1, const FuncBounds &ap2) {
+ if (ap1.fStart < ap2.fStart)
+ return true;
+ if (ap1.fStart == ap2.fStart && ap1.fEnd < ap2.fEnd)
+ return true;
+ return false;
+}
+
+
+};
+#endif
+
+
+#endif // __UNWIND_PROFILE_H__
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/Unwind-sjlj.c b/lldb/source/Plugins/Process/Utility/libunwind/src/Unwind-sjlj.c
new file mode 100644
index 00000000000..584528353a4
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/Unwind-sjlj.c
@@ -0,0 +1,466 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- Unwind-sjlj.c -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+/*
+ *
+ * Implements setjump-longjump based C++ exceptions
+ *
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <pthread.h>
+#include <setjmp.h>
+
+#include "unwind.h"
+#include "InternalMacros.h"
+
+//
+// ARM uses setjump/longjump based C++ exceptions.
+// Other architectures use "zero cost" exceptions.
+//
+// With SJLJ based exceptions any function that has a catch clause or needs to do any clean up when
+// an exception propagates through it, needs to call _Unwind_SjLj_Register() at the start of the
+// function and _Unwind_SjLj_Unregister() at the end. The register function is called with the
+// address of a block of memory in the function's stack frame. The runtime keeps a linked list
+// (stack) of these blocks - one per thread. The calling function also sets the personality
+// and lsda fields of the block.
+//
+//
+#if __arm__
+
+struct _Unwind_FunctionContext
+{
+ // next function in stack of handlers
+ struct _Unwind_FunctionContext* prev;
+
+ // set by calling function before registering to be the landing pad
+ uintptr_t resumeLocation;
+
+ // set by personality handler to be parameters passed to landing pad function
+ uintptr_t resumeParameters[4];
+
+ // set by calling function before registering
+ __personality_routine personality; // arm offset=24
+ uintptr_t lsda; // arm offset=28
+
+ // variable length array, contains registers to restore
+ // 0 = r7, 1 = pc, 2 = sp
+ void* jbuf[];
+};
+
+
+#if FOR_DYLD
+ // implemented in dyld
+ extern struct _Unwind_FunctionContext* __Unwind_SjLj_GetTopOfFunctionStack();
+ extern void __Unwind_SjLj_SetTopOfFunctionStack(struct _Unwind_FunctionContext* fc);
+#else
+ static pthread_key_t sPerThreadTopOfFunctionStack = 0;
+ static pthread_once_t sOnceFlag = PTHREAD_ONCE_INIT;
+
+ static void __Unwind_SjLj_MakeTopOfFunctionStackKey()
+ {
+ pthread_key_create(&sPerThreadTopOfFunctionStack, NULL);
+ }
+
+ static struct _Unwind_FunctionContext* __Unwind_SjLj_GetTopOfFunctionStack()
+ {
+ pthread_once(&sOnceFlag, __Unwind_SjLj_MakeTopOfFunctionStackKey);
+ return (struct _Unwind_FunctionContext*)pthread_getspecific(sPerThreadTopOfFunctionStack);
+ }
+
+ static void __Unwind_SjLj_SetTopOfFunctionStack(struct _Unwind_FunctionContext* fc)
+ {
+ pthread_once(&sOnceFlag, __Unwind_SjLj_MakeTopOfFunctionStackKey);
+ pthread_setspecific(sPerThreadTopOfFunctionStack, fc);
+ }
+#endif
+
+
+//
+// Called at start of each function that catches exceptions
+//
+EXPORT void _Unwind_SjLj_Register(struct _Unwind_FunctionContext* fc)
+{
+ fc->prev = __Unwind_SjLj_GetTopOfFunctionStack();
+ __Unwind_SjLj_SetTopOfFunctionStack(fc);
+}
+
+
+//
+// Called at end of each function that catches exceptions
+//
+EXPORT void _Unwind_SjLj_Unregister(struct _Unwind_FunctionContext* fc)
+{
+ __Unwind_SjLj_SetTopOfFunctionStack(fc->prev);
+}
+
+
+static _Unwind_Reason_Code unwind_phase1(struct _Unwind_Exception* exception_object)
+{
+ _Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack();
+ DEBUG_PRINT_UNWINDING("unwind_phase1: initial function-context=%p\n", c);
+
+ // walk each frame looking for a place to stop
+ for (bool handlerNotFound = true; handlerNotFound; c = c->prev) {
+
+ // check for no more frames
+ if ( c == NULL ) {
+ DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): reached bottom => _URC_END_OF_STACK\n", exception_object);
+ return _URC_END_OF_STACK;
+ }
+
+ DEBUG_PRINT_UNWINDING("unwind_phase1: function-context=%p\n", c);
+ // if there is a personality routine, ask it if it will want to stop at this frame
+ if ( c->personality != NULL ) {
+ DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): calling personality function %p\n", exception_object, c->personality);
+ _Unwind_Reason_Code personalityResult = (*c->personality)(1, _UA_SEARCH_PHASE,
+ exception_object->exception_class, exception_object,
+ (struct _Unwind_Context*)c);
+ switch ( personalityResult ) {
+ case _URC_HANDLER_FOUND:
+ // found a catch clause or locals that need destructing in this frame
+ // stop search and remember function context
+ handlerNotFound = false;
+ exception_object->private_2 = (uintptr_t)c;
+ DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): _URC_HANDLER_FOUND\n", exception_object);
+ return _URC_NO_REASON;
+
+ case _URC_CONTINUE_UNWIND:
+ DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", exception_object);
+ // continue unwinding
+ break;
+
+ default:
+ // something went wrong
+ DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR\n", exception_object);
+ return _URC_FATAL_PHASE1_ERROR;
+ }
+ }
+ }
+ return _URC_NO_REASON;
+}
+
+
+static _Unwind_Reason_Code unwind_phase2(struct _Unwind_Exception* exception_object)
+{
+ DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p)\n", exception_object);
+
+ // walk each frame until we reach where search phase said to stop
+ _Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack();
+ while ( true ) {
+ DEBUG_PRINT_UNWINDING("unwind_phase2s(ex_ojb=%p): function-context=%p\n", exception_object, c);
+
+ // check for no more frames
+ if ( c == NULL ) {
+ DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step() reached bottom => _URC_END_OF_STACK\n", exception_object);
+ return _URC_END_OF_STACK;
+ }
+
+ // if there is a personality routine, tell it we are unwinding
+ if ( c->personality != NULL ) {
+ _Unwind_Action action = _UA_CLEANUP_PHASE;
+ if ( (uintptr_t)c == exception_object->private_2 )
+ action = (_Unwind_Action)(_UA_CLEANUP_PHASE|_UA_HANDLER_FRAME); // tell personality this was the frame it marked in phase 1
+ _Unwind_Reason_Code personalityResult = (*c->personality)(1, action,
+ exception_object->exception_class, exception_object,
+ (struct _Unwind_Context*)c);
+ switch ( personalityResult ) {
+ case _URC_CONTINUE_UNWIND:
+ // continue unwinding
+ DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", exception_object);
+ if ( (uintptr_t)c == exception_object->private_2 ) {
+ // phase 1 said we would stop at this frame, but we did not...
+ ABORT("during phase1 personality function said it would stop here, but now if phase2 it did not stop here");
+ }
+ break;
+ case _URC_INSTALL_CONTEXT:
+ DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): _URC_INSTALL_CONTEXT, will resume at landing pad %p\n", exception_object, c->jbuf[1]);
+ // personality routine says to transfer control to landing pad
+ // we may get control back if landing pad calls _Unwind_Resume()
+ __Unwind_SjLj_SetTopOfFunctionStack(c);
+ __builtin_longjmp(c->jbuf, 1);
+ // unw_resume() only returns if there was an error
+ return _URC_FATAL_PHASE2_ERROR;
+ default:
+ // something went wrong
+ DEBUG_MESSAGE("personality function returned unknown result %d", personalityResult);
+ return _URC_FATAL_PHASE2_ERROR;
+ }
+ }
+ c = c->prev;
+ }
+
+ // clean up phase did not resume at the frame that the search phase said it would
+ return _URC_FATAL_PHASE2_ERROR;
+}
+
+
+static _Unwind_Reason_Code unwind_phase2_forced(struct _Unwind_Exception* exception_object,
+ _Unwind_Stop_Fn stop, void* stop_parameter)
+{
+ // walk each frame until we reach where search phase said to stop
+ _Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack();
+ while ( true ) {
+
+ // get next frame (skip over first which is _Unwind_RaiseException)
+ if ( c == NULL ) {
+ DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step() reached bottom => _URC_END_OF_STACK\n", exception_object);
+ return _URC_END_OF_STACK;
+ }
+
+ // call stop function at each frame
+ _Unwind_Action action = (_Unwind_Action)(_UA_FORCE_UNWIND|_UA_CLEANUP_PHASE);
+ _Unwind_Reason_Code stopResult = (*stop)(1, action,
+ exception_object->exception_class, exception_object,
+ (struct _Unwind_Context*)c, stop_parameter);
+ DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): stop function returned %d\n", exception_object, stopResult);
+ if ( stopResult != _URC_NO_REASON ) {
+ DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): stopped by stop function\n", exception_object);
+ return _URC_FATAL_PHASE2_ERROR;
+ }
+
+ // if there is a personality routine, tell it we are unwinding
+ if ( c->personality != NULL ) {
+ __personality_routine p = (__personality_routine)c->personality;
+ DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling personality function %p\n", exception_object, p);
+ _Unwind_Reason_Code personalityResult = (*p)(1, action,
+ exception_object->exception_class, exception_object,
+ (struct _Unwind_Context*)c);
+ switch ( personalityResult ) {
+ case _URC_CONTINUE_UNWIND:
+ DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): personality returned _URC_CONTINUE_UNWIND\n", exception_object);
+ // destructors called, continue unwinding
+ break;
+ case _URC_INSTALL_CONTEXT:
+ DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): personality returned _URC_INSTALL_CONTEXT\n", exception_object);
+ // we may get control back if landing pad calls _Unwind_Resume()
+ __Unwind_SjLj_SetTopOfFunctionStack(c);
+ __builtin_longjmp(c->jbuf, 1);
+ break;
+ default:
+ // something went wrong
+ DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): personality returned %d, _URC_FATAL_PHASE2_ERROR\n",
+ exception_object, personalityResult);
+ return _URC_FATAL_PHASE2_ERROR;
+ }
+ }
+ c = c->prev;
+ }
+
+ // call stop function one last time and tell it we've reached the end of the stack
+ DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop function with _UA_END_OF_STACK\n", exception_object);
+ _Unwind_Action lastAction = (_Unwind_Action)(_UA_FORCE_UNWIND|_UA_CLEANUP_PHASE|_UA_END_OF_STACK);
+ (*stop)(1, lastAction, exception_object->exception_class, exception_object, (struct _Unwind_Context*)c, stop_parameter);
+
+ // clean up phase did not resume at the frame that the search phase said it would
+ return _URC_FATAL_PHASE2_ERROR;
+}
+
+
+//
+// Called by __cxa_throw. Only returns if there is a fatal error
+//
+EXPORT _Unwind_Reason_Code _Unwind_SjLj_RaiseException(struct _Unwind_Exception* exception_object)
+{
+ DEBUG_PRINT_API("_Unwind_SjLj_RaiseException(ex_obj=%p)\n", exception_object);
+
+ // mark that this is a non-forced unwind, so _Unwind_Resume() can do the right thing
+ exception_object->private_1 = 0;
+ exception_object->private_2 = 0;
+
+ // phase 1: the search phase
+ _Unwind_Reason_Code phase1 = unwind_phase1(exception_object);
+ if ( phase1 != _URC_NO_REASON )
+ return phase1;
+
+ // phase 2: the clean up phase
+ return unwind_phase2(exception_object);
+}
+
+
+//
+// When _Unwind_RaiseException() is in phase2, it hands control
+// to the personality function at each frame. The personality
+// may force a jump to a landing pad in that function, the landing
+// pad code may then call _Unwind_Resume() to continue with the
+// unwinding. Note: the call to _Unwind_Resume() is from compiler
+// geneated user code. All other _Unwind_* routines are called
+// by the C++ runtime __cxa_* routines.
+//
+// Re-throwing an exception is implemented by having the code call
+// __cxa_rethrow() which in turn calls _Unwind_Resume_or_Rethrow()
+//
+EXPORT void _Unwind_SjLj_Resume(struct _Unwind_Exception* exception_object)
+{
+ DEBUG_PRINT_API("_Unwind_SjLj_Resume(ex_obj=%p)\n", exception_object);
+
+ if ( exception_object->private_1 != 0 )
+ unwind_phase2_forced(exception_object, (_Unwind_Stop_Fn)exception_object->private_1, (void*)exception_object->private_2);
+ else
+ unwind_phase2(exception_object);
+
+ // clients assume _Unwind_Resume() does not return, so all we can do is abort.
+ ABORT("_Unwind_SjLj_Resume() can't return");
+}
+
+
+//
+// Called by __cxa_rethrow()
+//
+EXPORT _Unwind_Reason_Code _Unwind_SjLj_Resume_or_Rethrow(struct _Unwind_Exception* exception_object)
+{
+ DEBUG_PRINT_API("__Unwind_SjLj_Resume_or_Rethrow(ex_obj=%p), private_1=%ld\n", exception_object, exception_object->private_1);
+ // if this is non-forced and a stopping place was found, then this is a re-throw
+ // call _Unwind_RaiseException() as if this was a new exception
+ if ( exception_object->private_1 == 0 )
+ _Unwind_SjLj_RaiseException(exception_object);
+
+ // call through to _Unwind_Resume() which distiguishes between forced and regular exceptions
+ _Unwind_SjLj_Resume(exception_object);
+ ABORT("__Unwind_SjLj_Resume_or_Rethrow() called _Unwind_SjLj_Resume() which unexpectedly returned");
+}
+
+
+//
+// Called by personality handler during phase 2 to get LSDA for current frame
+//
+EXPORT uintptr_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context* context)
+{
+ _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t)context;
+ DEBUG_PRINT_API("_Unwind_GetLanguageSpecificData(context=%p) => 0x%0lX\n", context, ufc->lsda);
+ return ufc->lsda;
+}
+
+
+//
+// Called by personality handler during phase 2 to get register values
+//
+EXPORT uintptr_t _Unwind_GetGR(struct _Unwind_Context* context, int index)
+{
+ DEBUG_PRINT_API("_Unwind_GetGR(context=%p, reg=%d)\n", context, index);
+ _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t)context;
+ return ufc->resumeParameters[index];
+}
+
+
+
+//
+// Called by personality handler during phase 2 to alter register values
+//
+EXPORT void _Unwind_SetGR(struct _Unwind_Context* context, int index, uintptr_t new_value)
+{
+ DEBUG_PRINT_API("_Unwind_SetGR(context=%p, reg=%d, value=0x%0lX)\n", context, index, new_value);
+ _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t)context;
+ ufc->resumeParameters[index] = new_value;
+}
+
+
+//
+// Called by personality handler during phase 2 to get instruction pointer
+//
+EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context* context)
+{
+ _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t)context;
+ DEBUG_PRINT_API("_Unwind_GetIP(context=%p) => 0x%lX\n", context, ufc->resumeLocation+1);
+ return ufc->resumeLocation+1;
+}
+
+//
+// Called by personality handler during phase 2 to get instruction pointer
+// ipBefore is a boolean that says if IP is already adjusted to be the call
+// site address. Normally IP is the return address.
+//
+EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context* context, int* ipBefore)
+{
+ _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t)context;
+ *ipBefore = 0;
+ DEBUG_PRINT_API("_Unwind_GetIPInfo(context=%p, %p) => 0x%lX\n", context, ipBefore, ufc->resumeLocation+1);
+ return ufc->resumeLocation+1;
+}
+
+
+//
+// Called by personality handler during phase 2 to alter instruction pointer
+//
+EXPORT void _Unwind_SetIP(struct _Unwind_Context* context, uintptr_t new_value)
+{
+ DEBUG_PRINT_API("_Unwind_SetIP(context=%p, value=0x%0lX)\n", context, new_value);
+ _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t)context;
+ ufc->resumeLocation = new_value-1;
+}
+
+//
+// Called by personality handler during phase 2 to find the start of the function
+//
+EXPORT uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context* context)
+{
+ // Not supported or needed for sjlj based unwinding
+ DEBUG_PRINT_API("_Unwind_GetRegionStart(context=%p)\n", context);
+ return 0;
+}
+
+//
+// Called by personality handler during phase 2 if a foreign exception is caught
+//
+EXPORT void _Unwind_DeleteException(struct _Unwind_Exception* exception_object)
+{
+ DEBUG_PRINT_API("_Unwind_DeleteException(ex_obj=%p)\n", exception_object);
+ if ( exception_object->exception_cleanup != NULL )
+ (*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, exception_object);
+}
+
+
+//
+// Called by personality handler during phase 2 to get base address for data relative encodings
+//
+EXPORT uintptr_t _Unwind_GetDataRelBase(struct _Unwind_Context* context)
+{
+ // Not supported or needed for sjlj based unwinding
+ DEBUG_PRINT_API("_Unwind_GetDataRelBase(context=%p)\n", context);
+ ABORT("_Unwind_GetDataRelBase() not implemented");
+}
+
+
+//
+// Called by personality handler during phase 2 to get base address for text relative encodings
+//
+EXPORT uintptr_t _Unwind_GetTextRelBase(struct _Unwind_Context* context)
+{
+ // Not supported or needed for sjlj based unwinding
+ DEBUG_PRINT_API("_Unwind_GetTextRelBase(context=%p)\n", context);
+ ABORT("_Unwind_GetTextRelBase() not implemented");
+}
+
+
+
+//
+// Called by personality handler to get Call Frame Area for current frame
+//
+EXPORT uintptr_t _Unwind_GetCFA(struct _Unwind_Context* context)
+{
+ DEBUG_PRINT_API("_Unwind_GetCFA(context=%p)\n", context);
+ if ( context != NULL ) {
+ _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t)context;
+ // setjmp/longjmp based exceptions don't have a true CFA
+ // the SP in the jmpbuf is the closest approximation
+ return (uintptr_t)ufc->jbuf[2];
+ }
+ return 0;
+}
+
+
+
+
+
+#endif // __arm__
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindCursor.hpp b/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindCursor.hpp
new file mode 100644
index 00000000000..e940b8b8bf1
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindCursor.hpp
@@ -0,0 +1,1307 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- UnwindCursor.hpp ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+//
+// C++ interface to lower levels of libuwind
+//
+
+#ifndef __UNWINDCURSOR_HPP__
+#define __UNWINDCURSOR_HPP__
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <stdarg.h>
+
+#include "libunwind.h"
+
+#include "AddressSpace.hpp"
+#include "Registers.hpp"
+#include "DwarfInstructions.hpp"
+
+#include "AssemblyParser.hpp"
+#include "AssemblyInstructions.hpp"
+#include "RemoteProcInfo.hpp"
+#include "ArchDefaultUnwinder.hpp"
+#include "RemoteDebuggerDummyUnwinder.hpp"
+
+#include "CompactUnwinder.hpp"
+#include "InternalMacros.h"
+
+// private keymgr stuff
+#define KEYMGR_GCC3_DW2_OBJ_LIST 302
+extern "C" {
+ extern void _keymgr_set_and_unlock_processwide_ptr(int key, void* ptr);
+ extern void* _keymgr_get_and_lock_processwide_ptr(int key);
+};
+
+// undocumented libgcc "struct object"
+struct libgcc_object
+{
+ void* start;
+ void* unused1;
+ void* unused2;
+ void* fde;
+ unsigned long encoding;
+ void* fde_end;
+ libgcc_object* next;
+};
+
+// undocumented libgcc "struct km_object_info" referenced by KEYMGR_GCC3_DW2_OBJ_LIST
+struct libgcc_object_info {
+ struct libgcc_object* seen_objects;
+ struct libgcc_object* unseen_objects;
+ unsigned spare[2];
+};
+
+
+
+
+namespace lldb_private {
+
+#if !FOR_DYLD
+template <typename A>
+class DwarfFDECache
+{
+public:
+ typedef typename A::pint_t pint_t;
+ static pint_t findFDE(pint_t mh, pint_t pc);
+ static void add(pint_t mh, pint_t ip_start, pint_t ip_end, pint_t fde);
+ static void removeAllIn(pint_t mh);
+ static void iterateCacheEntries(void (*func)(unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh));
+private:
+ static void dyldUnloadHook(const struct mach_header* mh, intptr_t vmaddr_slide);
+
+ struct entry { pint_t mh; pint_t ip_start; pint_t ip_end; pint_t fde; };
+
+ // these fields are all static to avoid needing an initializer
+ // there is only one instance of this class per process
+ static pthread_rwlock_t fgLock;
+ static bool fgRegisteredForDyldUnloads;
+ // can't use std::vector<> here because this code must live in libSystem.dylib (which is below libstdc++.dylib)
+ static entry* fgBuffer;
+ static entry* fgBufferUsed;
+ static entry* fgBufferEnd;
+ static entry fgInitialBuffer[64];
+};
+
+template <typename A> typename DwarfFDECache<A>::entry* DwarfFDECache<A>::fgBuffer = fgInitialBuffer;
+template <typename A> typename DwarfFDECache<A>::entry* DwarfFDECache<A>::fgBufferUsed = fgInitialBuffer;
+template <typename A> typename DwarfFDECache<A>::entry* DwarfFDECache<A>::fgBufferEnd = &fgInitialBuffer[64];
+template <typename A> typename DwarfFDECache<A>::entry DwarfFDECache<A>::fgInitialBuffer[64];
+
+template <typename A>
+pthread_rwlock_t DwarfFDECache<A>::fgLock = PTHREAD_RWLOCK_INITIALIZER;
+
+template <typename A>
+bool DwarfFDECache<A>::fgRegisteredForDyldUnloads = false;
+
+
+template <typename A>
+typename A::pint_t DwarfFDECache<A>::findFDE(pint_t mh, pint_t pc)
+{
+ pint_t result = NULL;
+ DEBUG_LOG_NON_ZERO(::pthread_rwlock_rdlock(&fgLock));
+ for(entry* p=fgBuffer; p < fgBufferUsed; ++p) {
+ if ( (mh == p->mh) || (mh == 0) ) {
+ if ( (p->ip_start <= pc) && (pc < p->ip_end) ) {
+ result = p->fde;
+ break;
+ }
+ }
+ }
+ DEBUG_LOG_NON_ZERO(::pthread_rwlock_unlock(&fgLock));
+ //fprintf(stderr, "DwarfFDECache::findFDE(mh=0x%llX, pc=0x%llX) => 0x%llX\n", (uint64_t)mh, (uint64_t)pc, (uint64_t)result);
+ return result;
+}
+
+template <typename A>
+void DwarfFDECache<A>::add(pint_t mh, pint_t ip_start, pint_t ip_end, pint_t fde)
+{
+ //fprintf(stderr, "DwarfFDECache::add(mh=0x%llX, ip_start=0x%llX, ip_end=0x%llX, fde=0x%llX) pthread=%p\n",
+ // (uint64_t)mh, (uint64_t)ip_start, (uint64_t)ip_end, (uint64_t)fde, pthread_self());
+ DEBUG_LOG_NON_ZERO(::pthread_rwlock_wrlock(&fgLock));
+ if ( fgBufferUsed >= fgBufferEnd ) {
+ int oldSize = fgBufferEnd - fgBuffer;
+ int newSize = oldSize*4;
+ entry* newBuffer = (entry*)malloc(newSize*sizeof(entry)); // can't use operator new in libSystem.dylib
+ memcpy(newBuffer, fgBuffer, oldSize*sizeof(entry));
+ //fprintf(stderr, "DwarfFDECache::add() growing buffer to %d\n", newSize);
+ if ( fgBuffer != fgInitialBuffer )
+ free(fgBuffer);
+ fgBuffer = newBuffer;
+ fgBufferUsed = &newBuffer[oldSize];
+ fgBufferEnd = &newBuffer[newSize];
+ }
+ fgBufferUsed->mh = mh;
+ fgBufferUsed->ip_start = ip_start;
+ fgBufferUsed->ip_end = ip_end;
+ fgBufferUsed->fde = fde;
+ ++fgBufferUsed;
+#if !defined (SUPPORT_REMOTE_UNWINDING)
+ if ( !fgRegisteredForDyldUnloads ) {
+ _dyld_register_func_for_remove_image(&dyldUnloadHook);
+ fgRegisteredForDyldUnloads = true;
+ }
+#endif
+ DEBUG_LOG_NON_ZERO(::pthread_rwlock_unlock(&fgLock));
+}
+
+
+
+template <typename A>
+void DwarfFDECache<A>::removeAllIn(pint_t mh)
+{
+ DEBUG_LOG_NON_ZERO(::pthread_rwlock_wrlock(&fgLock));
+ entry* d=fgBuffer;
+ for(const entry* s=fgBuffer; s < fgBufferUsed; ++s) {
+ if ( s->mh != mh ) {
+ if ( d != s )
+ *d = *s;
+ ++d;
+ }
+ }
+ fgBufferUsed = d;
+ DEBUG_LOG_NON_ZERO(::pthread_rwlock_unlock(&fgLock));
+}
+
+
+template <typename A>
+void DwarfFDECache<A>::dyldUnloadHook(const struct mach_header* mh, intptr_t vmaddr_slide)
+{
+#if !defined (SUPPORT_REMOTE_UNWINDING)
+ removeAllIn((pint_t)mh);
+#endif
+}
+
+template <typename A>
+void DwarfFDECache<A>::iterateCacheEntries(void (*func)(unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh))
+{
+ DEBUG_LOG_NON_ZERO(::pthread_rwlock_wrlock(&fgLock));
+ for(entry* p=fgBuffer; p < fgBufferUsed; ++p) {
+ (*func)(p->ip_start, p->ip_end, p->fde, p->mh);
+ }
+ DEBUG_LOG_NON_ZERO(::pthread_rwlock_unlock(&fgLock));
+}
+#endif // !FOR_DYLD
+
+
+
+
+#define arrayoffsetof(type, index, field) ((size_t)(&((type *)0)[index].field))
+
+template <typename A>
+class UnwindSectionHeader {
+public:
+ UnwindSectionHeader(A& addressSpace, typename A::pint_t addr) : fAddressSpace(addressSpace), fAddr(addr) {}
+
+ uint32_t version() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_section_header, version)); }
+ uint32_t commonEncodingsArraySectionOffset() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_section_header, commonEncodingsArraySectionOffset)); }
+ uint32_t commonEncodingsArrayCount() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_section_header, commonEncodingsArrayCount)); }
+ uint32_t personalityArraySectionOffset() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_section_header, personalityArraySectionOffset)); }
+ uint32_t personalityArrayCount() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_section_header, personalityArrayCount)); }
+ uint32_t indexSectionOffset() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_section_header, indexSectionOffset)); }
+ uint32_t indexCount() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_section_header, indexCount)); }
+private:
+ A& fAddressSpace;
+ typename A::pint_t fAddr;
+};
+
+template <typename A>
+class UnwindSectionIndexArray {
+public:
+ UnwindSectionIndexArray(A& addressSpace, typename A::pint_t addr) : fAddressSpace(addressSpace), fAddr(addr) {}
+
+ uint32_t functionOffset(int index) const INLINE { return fAddressSpace.get32(fAddr + arrayoffsetof(unwind_info_section_header_index_entry, index, functionOffset)); }
+ uint32_t secondLevelPagesSectionOffset(int index) const INLINE { return fAddressSpace.get32(fAddr + arrayoffsetof(unwind_info_section_header_index_entry, index, secondLevelPagesSectionOffset)); }
+ uint32_t lsdaIndexArraySectionOffset(int index) const INLINE { return fAddressSpace.get32(fAddr + arrayoffsetof(unwind_info_section_header_index_entry, index, lsdaIndexArraySectionOffset)); }
+private:
+ A& fAddressSpace;
+ typename A::pint_t fAddr;
+};
+
+
+template <typename A>
+class UnwindSectionRegularPageHeader {
+public:
+ UnwindSectionRegularPageHeader(A& addressSpace, typename A::pint_t addr) : fAddressSpace(addressSpace), fAddr(addr) {}
+
+ uint32_t kind() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_regular_second_level_page_header, kind)); }
+ uint16_t entryPageOffset() const INLINE { return fAddressSpace.get16(fAddr + offsetof(unwind_info_regular_second_level_page_header, entryPageOffset)); }
+ uint16_t entryCount() const INLINE { return fAddressSpace.get16(fAddr + offsetof(unwind_info_regular_second_level_page_header, entryCount)); }
+private:
+ A& fAddressSpace;
+ typename A::pint_t fAddr;
+};
+
+
+template <typename A>
+class UnwindSectionRegularArray {
+public:
+ UnwindSectionRegularArray(A& addressSpace, typename A::pint_t addr) : fAddressSpace(addressSpace), fAddr(addr) {}
+
+ uint32_t functionOffset(int index) const INLINE { return fAddressSpace.get32(fAddr + arrayoffsetof(unwind_info_regular_second_level_entry, index, functionOffset)); }
+ uint32_t encoding(int index) const INLINE { return fAddressSpace.get32(fAddr + arrayoffsetof(unwind_info_regular_second_level_entry, index, encoding)); }
+private:
+ A& fAddressSpace;
+ typename A::pint_t fAddr;
+};
+
+
+template <typename A>
+class UnwindSectionCompressedPageHeader {
+public:
+ UnwindSectionCompressedPageHeader(A& addressSpace, typename A::pint_t addr) : fAddressSpace(addressSpace), fAddr(addr) {}
+
+ uint32_t kind() const INLINE { return fAddressSpace.get32(fAddr + offsetof(unwind_info_compressed_second_level_page_header, kind)); }
+ uint16_t entryPageOffset() const INLINE { return fAddressSpace.get16(fAddr + offsetof(unwind_info_compressed_second_level_page_header, entryPageOffset)); }
+ uint16_t entryCount() const INLINE { return fAddressSpace.get16(fAddr + offsetof(unwind_info_compressed_second_level_page_header, entryCount)); }
+ uint16_t encodingsPageOffset() const INLINE { return fAddressSpace.get16(fAddr + offsetof(unwind_info_compressed_second_level_page_header, encodingsPageOffset)); }
+ uint16_t encodingsCount() const INLINE { return fAddressSpace.get16(fAddr + offsetof(unwind_info_compressed_second_level_page_header, encodingsCount)); }
+private:
+ A& fAddressSpace;
+ typename A::pint_t fAddr;
+};
+
+
+template <typename A>
+class UnwindSectionCompressedArray {
+public:
+ UnwindSectionCompressedArray(A& addressSpace, typename A::pint_t addr) : fAddressSpace(addressSpace), fAddr(addr) {}
+
+ uint32_t functionOffset(int index) const INLINE { return UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET( fAddressSpace.get32(fAddr + index*sizeof(uint32_t)) ); }
+ uint16_t encodingIndex(int index) const INLINE { return UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX( fAddressSpace.get32(fAddr + index*sizeof(uint32_t)) ); }
+private:
+ A& fAddressSpace;
+ typename A::pint_t fAddr;
+};
+
+
+template <typename A>
+class UnwindSectionLsdaArray {
+public:
+ UnwindSectionLsdaArray(A& addressSpace, typename A::pint_t addr) : fAddressSpace(addressSpace), fAddr(addr) {}
+
+ uint32_t functionOffset(int index) const INLINE { return fAddressSpace.get32(fAddr + arrayoffsetof(unwind_info_section_header_lsda_index_entry, index, functionOffset)); }
+ int32_t lsdaOffset(int index) const INLINE { return fAddressSpace.get32(fAddr + arrayoffsetof(unwind_info_section_header_lsda_index_entry, index, lsdaOffset)); }
+private:
+ A& fAddressSpace;
+ typename A::pint_t fAddr;
+};
+
+
+template <typename A, typename R>
+class UnwindCursor
+{
+public:
+ UnwindCursor(unw_context_t* context, A& as);
+ virtual ~UnwindCursor() {}
+ virtual bool validReg(int);
+ virtual uint64_t getReg(int);
+ virtual int getReg(int, uint64_t*);
+ virtual int setReg(int, uint64_t);
+ virtual bool validFloatReg(int);
+ virtual double getFloatReg(int);
+ virtual int getFloatReg(int, double*);
+ virtual int setFloatReg(int, double);
+ virtual int step();
+ virtual void getInfo(unw_proc_info_t*);
+ virtual void jumpto();
+ virtual const char* getRegisterName(int num);
+ virtual bool isSignalFrame();
+ virtual bool getFunctionName(char* buf, size_t bufLen, unw_word_t* offset);
+ virtual void setInfoBasedOnIPRegister(bool isReturnAddress=false);
+
+ void operator delete(void* p, size_t size) {}
+
+protected:
+ typedef typename A::pint_t pint_t;
+ typedef uint32_t EncodedUnwindInfo;
+
+ virtual bool getInfoFromCompactEncodingSection(pint_t pc, pint_t mh, pint_t unwindSectionStart);
+ virtual bool getInfoFromDwarfSection(pint_t pc, pint_t mh, pint_t ehSectionStart, uint32_t sectionLength, uint32_t sectionOffsetOfFDE);
+
+ virtual int stepWithDwarfFDE()
+ { return DwarfInstructions<A,R>::stepWithDwarf(fAddressSpace, this->getReg(UNW_REG_IP), fInfo.unwind_info, fRegisters); }
+
+ virtual int stepWithCompactEncoding() { R dummy; return stepWithCompactEncoding(dummy); }
+ int stepWithCompactEncoding(Registers_x86_64&)
+ { return CompactUnwinder_x86_64<A>::stepWithCompactEncoding(fInfo.format, fInfo.start_ip, fAddressSpace, fRegisters); }
+ int stepWithCompactEncoding(Registers_x86&)
+ { return CompactUnwinder_x86<A>::stepWithCompactEncoding(fInfo.format, fInfo.start_ip, fAddressSpace, fRegisters); }
+ int stepWithCompactEncoding(Registers_ppc&)
+ { return UNW_EINVAL; }
+
+#if FOR_DYLD
+ #if __ppc__
+ virtual bool mustUseDwarf() const { return true; }
+ #else
+ virtual bool mustUseDwarf() const { return false; }
+ #endif
+#else
+ virtual bool mustUseDwarf() const { R dummy; uint32_t offset; return dwarfWithOffset(dummy, offset); }
+#endif
+
+ virtual bool dwarfWithOffset(uint32_t& offset) const { R dummy; return dwarfWithOffset(dummy, offset); }
+ virtual bool dwarfWithOffset(Registers_x86_64&, uint32_t& offset) const {
+ if ( (fInfo.format & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF ) {
+ offset = (fInfo.format & UNWIND_X86_64_DWARF_SECTION_OFFSET);
+ return true;
+ }
+#if SUPPORT_OLD_BINARIES
+ if ( (fInfo.format & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_COMPATIBILITY ) {
+ if ( (fInfo.format & UNWIND_X86_64_CASE_MASK) == UNWIND_X86_64_UNWIND_REQUIRES_DWARF ) {
+ offset = 0;
+ return true;
+ }
+ }
+#endif
+ return false;
+ }
+ virtual bool dwarfWithOffset(Registers_x86&, uint32_t& offset) const {
+ if ( (fInfo.format & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF ) {
+ offset = (fInfo.format & UNWIND_X86_DWARF_SECTION_OFFSET);
+ return true;
+ }
+#if SUPPORT_OLD_BINARIES
+ if ( (fInfo.format & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_COMPATIBILITY ) {
+ if ( (fInfo.format & UNWIND_X86_CASE_MASK) == UNWIND_X86_UNWIND_REQUIRES_DWARF ) {
+ offset = 0;
+ return true;
+ }
+ }
+#endif
+ return false;
+ }
+ virtual bool dwarfWithOffset(Registers_ppc&, uint32_t& offset) const { return true; }
+
+
+ virtual compact_unwind_encoding_t dwarfEncoding() const { R dummy; return dwarfEncoding(dummy); }
+ virtual compact_unwind_encoding_t dwarfEncoding(Registers_x86_64&) const { return UNWIND_X86_64_MODE_DWARF; }
+ virtual compact_unwind_encoding_t dwarfEncoding(Registers_x86&) const { return UNWIND_X86_MODE_DWARF; }
+ virtual compact_unwind_encoding_t dwarfEncoding(Registers_ppc&) const { return 0; }
+
+ unw_proc_info_t fInfo;
+ R fRegisters;
+ A& fAddressSpace;
+ bool fUnwindInfoMissing;
+ bool fIsSignalFrame;
+};
+
+typedef UnwindCursor<LocalAddressSpace,Registers_x86> AbstractUnwindCursor;
+
+template <typename A, typename R>
+UnwindCursor<A,R>::UnwindCursor(unw_context_t* context, A& as)
+ : fRegisters(context), fAddressSpace(as), fUnwindInfoMissing(false), fIsSignalFrame(false)
+{
+ COMPILE_TIME_ASSERT( sizeof(UnwindCursor<A,R>) < sizeof(unw_cursor_t) );
+
+ bzero(&fInfo, sizeof(fInfo));
+}
+
+template <typename A, typename R>
+bool UnwindCursor<A,R>::validReg(int regNum)
+{
+ return fRegisters.validRegister(regNum);
+}
+
+template <typename A, typename R>
+uint64_t UnwindCursor<A,R>::getReg(int regNum)
+{
+ return fRegisters.getRegister(regNum);
+}
+
+template <typename A, typename R>
+int UnwindCursor<A,R>::getReg(int regNum, uint64_t *valp)
+{
+ *valp = fRegisters.getRegister(regNum);
+ return UNW_ESUCCESS;
+}
+
+template <typename A, typename R>
+int UnwindCursor<A,R>::setReg(int regNum, uint64_t value)
+{
+ fRegisters.setRegister(regNum, value);
+ return UNW_ESUCCESS;
+}
+
+template <typename A, typename R>
+bool UnwindCursor<A,R>::validFloatReg(int regNum)
+{
+ return fRegisters.validFloatRegister(regNum);
+}
+
+template <typename A, typename R>
+double UnwindCursor<A,R>::getFloatReg(int regNum)
+{
+ return fRegisters.getFloatRegister(regNum);
+}
+
+template <typename A, typename R>
+int UnwindCursor<A,R>::getFloatReg(int regNum, double *valp)
+{
+ *valp = fRegisters.getFloatRegister(regNum);
+ return UNW_ESUCCESS;
+}
+
+template <typename A, typename R>
+int UnwindCursor<A,R>::setFloatReg(int regNum, double value)
+{
+ fRegisters.setFloatRegister(regNum, value);
+ return UNW_ESUCCESS;
+}
+
+template <typename A, typename R>
+void UnwindCursor<A,R>::jumpto()
+{
+#if !defined (SUPPORT_REMOTE_UNWINDING)
+ fRegisters.jumpto();
+#endif
+}
+
+template <typename A, typename R>
+const char* UnwindCursor<A,R>::getRegisterName(int regNum)
+{
+ return fRegisters.getRegisterName(regNum);
+}
+
+template <typename A, typename R>
+bool UnwindCursor<A,R>::isSignalFrame()
+{
+ return fIsSignalFrame;
+}
+
+
+template <typename A, typename R>
+bool UnwindCursor<A,R>::getInfoFromDwarfSection(pint_t pc, pint_t mh, pint_t ehSectionStart, uint32_t sectionLength, uint32_t sectionOffsetOfFDE)
+{
+ typename CFI_Parser<A>::FDE_Info fdeInfo;
+ typename CFI_Parser<A>::CIE_Info cieInfo;
+ bool foundFDE = false;
+ bool foundInCache = false;
+ // if compact encoding table gave offset into dwarf section, go directly there
+ if ( sectionOffsetOfFDE != 0 ) {
+ foundFDE = CFI_Parser<A>::findFDE(fAddressSpace, pc, ehSectionStart, sectionLength, ehSectionStart+sectionOffsetOfFDE, &fdeInfo, &cieInfo);
+ }
+#if !FOR_DYLD
+ if ( !foundFDE ) {
+ // otherwise, search cache of previously found FDEs
+ pint_t cachedFDE = DwarfFDECache<A>::findFDE(mh, pc);
+ //fprintf(stderr, "getInfoFromDwarfSection(pc=0x%llX) cachedFDE=0x%llX\n", (uint64_t)pc, (uint64_t)cachedFDE);
+ if ( cachedFDE != 0 ) {
+ foundFDE = CFI_Parser<A>::findFDE(fAddressSpace, pc, ehSectionStart, sectionLength, cachedFDE, &fdeInfo, &cieInfo);
+ foundInCache = foundFDE;
+ //fprintf(stderr, "cachedFDE=0x%llX, foundInCache=%d\n", (uint64_t)cachedFDE, foundInCache);
+ }
+ }
+#endif
+ if ( !foundFDE ) {
+ // still not found, do full scan of __eh_frame section
+ foundFDE = CFI_Parser<A>::findFDE(fAddressSpace, pc, ehSectionStart, sectionLength, 0, &fdeInfo, &cieInfo);
+ }
+ if ( foundFDE ) {
+ typename CFI_Parser<A>::PrologInfo prolog;
+ if ( CFI_Parser<A>::parseFDEInstructions(fAddressSpace, fdeInfo, cieInfo, pc, &prolog) ) {
+ // save off parsed FDE info
+ fInfo.start_ip = fdeInfo.pcStart;
+ fInfo.end_ip = fdeInfo.pcEnd;
+ fInfo.lsda = fdeInfo.lsda;
+ fInfo.handler = cieInfo.personality;
+ fInfo.gp = prolog.spExtraArgSize; // some frameless functions need SP altered when resuming in function
+ fInfo.flags = 0;
+ fInfo.format = dwarfEncoding();
+ fInfo.unwind_info = fdeInfo.fdeStart;
+ fInfo.unwind_info_size = fdeInfo.fdeLength;
+ fInfo.extra = (unw_word_t)mh;
+ if ( !foundInCache && (sectionOffsetOfFDE == 0) ) {
+ // don't add to cache entries the compact encoding table can find quickly
+ //fprintf(stderr, "getInfoFromDwarfSection(pc=0x%0llX), mh=0x%llX, start_ip=0x%0llX, fde=0x%0llX, personality=0x%0llX\n",
+ // (uint64_t)pc, (uint64_t)mh, fInfo.start_ip, fInfo.unwind_info, fInfo.handler);
+#if !FOR_DYLD
+ DwarfFDECache<A>::add(mh, fdeInfo.pcStart, fdeInfo.pcEnd, fdeInfo.fdeStart);
+#endif
+ }
+ return true;
+ }
+ }
+ //DEBUG_MESSAGE("can't find/use FDE for pc=0x%llX\n", (uint64_t)pc);
+ return false;
+}
+
+template <typename A, typename R>
+bool UnwindCursor<A,R>::getInfoFromCompactEncodingSection(pint_t pc, pint_t mh, pint_t unwindSectionStart)
+{
+ const bool log = false;
+ if ( log ) fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX, mh=0x%llX)\n", (uint64_t)pc, (uint64_t)mh);
+
+ const UnwindSectionHeader<A> sectionHeader(fAddressSpace, unwindSectionStart);
+ if ( sectionHeader.version() != UNWIND_SECTION_VERSION )
+ return false;
+
+ // do a binary search of top level index to find page with unwind info
+ uint32_t targetFunctionOffset = pc - mh;
+ const UnwindSectionIndexArray<A> topIndex(fAddressSpace, unwindSectionStart + sectionHeader.indexSectionOffset());
+ uint32_t low = 0;
+ uint32_t high = sectionHeader.indexCount();
+ const uint32_t last = high - 1;
+ while ( low < high ) {
+ uint32_t mid = (low + high)/2;
+ //if ( log ) fprintf(stderr, "\tmid=%d, low=%d, high=%d, *mid=0x%08X\n", mid, low, high, topIndex.functionOffset(mid));
+ if ( topIndex.functionOffset(mid) <= targetFunctionOffset ) {
+ if ( (mid == last) || (topIndex.functionOffset(mid+1) > targetFunctionOffset) ) {
+ low = mid;
+ break;
+ }
+ else {
+ low = mid+1;
+ }
+ }
+ else {
+ high = mid;
+ }
+ }
+ const uint32_t firstLevelFunctionOffset = topIndex.functionOffset(low);
+ const uint32_t firstLevelNextPageFunctionOffset = topIndex.functionOffset(low+1);
+ const pint_t secondLevelAddr = unwindSectionStart+topIndex.secondLevelPagesSectionOffset(low);
+ const pint_t lsdaArrayStartAddr = unwindSectionStart+topIndex.lsdaIndexArraySectionOffset(low);
+ const pint_t lsdaArrayEndAddr = unwindSectionStart+topIndex.lsdaIndexArraySectionOffset(low+1);
+ if ( log ) fprintf(stderr, "\tfirst level search for result index=%d to secondLevelAddr=0x%llX\n",
+ low, (uint64_t)secondLevelAddr);
+ // do a binary search of second level page index
+ uint32_t encoding = 0;
+ pint_t funcStart = 0;
+ pint_t funcEnd = 0;
+ pint_t lsda = 0;
+ pint_t personality = 0;
+ uint32_t pageKind = fAddressSpace.get32(secondLevelAddr);
+ if ( pageKind == UNWIND_SECOND_LEVEL_REGULAR ) {
+ // regular page
+ UnwindSectionRegularPageHeader<A> pageHeader(fAddressSpace, secondLevelAddr);
+ UnwindSectionRegularArray<A> pageIndex(fAddressSpace, secondLevelAddr + pageHeader.entryPageOffset());
+ // binary search looks for entry with e where index[e].offset <= pc < index[e+1].offset
+ if ( log ) fprintf(stderr, "\tbinary search for targetFunctionOffset=0x%08llX in regular page starting at secondLevelAddr=0x%llX\n",
+ (uint64_t)targetFunctionOffset, (uint64_t)secondLevelAddr);
+ uint32_t low = 0;
+ uint32_t high = pageHeader.entryCount();
+ while ( low < high ) {
+ uint32_t mid = (low + high)/2;
+ if ( pageIndex.functionOffset(mid) <= targetFunctionOffset ) {
+ if ( mid == (uint32_t)(pageHeader.entryCount()-1) ) {
+ // at end of table
+ low = mid;
+ funcEnd = firstLevelNextPageFunctionOffset + mh;
+ break;
+ }
+ else if ( pageIndex.functionOffset(mid+1) > targetFunctionOffset ) {
+ // next is too big, so we found it
+ low = mid;
+ funcEnd = pageIndex.functionOffset(low+1) + mh;
+ break;
+ }
+ else {
+ low = mid+1;
+ }
+ }
+ else {
+ high = mid;
+ }
+ }
+ encoding = pageIndex.encoding(low);
+ funcStart = pageIndex.functionOffset(low) + mh;
+ if ( pc < funcStart ) {
+ if ( log ) fprintf(stderr, "\tpc not in table, pc=0x%llX, funcStart=0x%llX, funcEnd=0x%llX\n", (uint64_t)pc, (uint64_t)funcStart, (uint64_t)funcEnd);
+ return false;
+ }
+ if ( pc > funcEnd ) {
+ if ( log ) fprintf(stderr, "\tpc not in table, pc=0x%llX, funcStart=0x%llX, funcEnd=0x%llX\n", (uint64_t)pc, (uint64_t)funcStart, (uint64_t)funcEnd);
+ return false;
+ }
+ }
+ else if ( pageKind == UNWIND_SECOND_LEVEL_COMPRESSED ) {
+ // compressed page
+ UnwindSectionCompressedPageHeader<A> pageHeader(fAddressSpace, secondLevelAddr);
+ UnwindSectionCompressedArray<A> pageIndex(fAddressSpace, secondLevelAddr + pageHeader.entryPageOffset());
+ const uint32_t targetFunctionPageOffset = targetFunctionOffset - firstLevelFunctionOffset;
+ // binary search looks for entry with e where index[e].offset <= pc < index[e+1].offset
+ if ( log ) fprintf(stderr, "\tbinary search of compressed page starting at secondLevelAddr=0x%llX\n", (uint64_t)secondLevelAddr);
+ uint32_t low = 0;
+ const uint32_t last = pageHeader.entryCount() - 1;
+ uint32_t high = pageHeader.entryCount();
+ while ( low < high ) {
+ uint32_t mid = (low + high)/2;
+ if ( pageIndex.functionOffset(mid) <= targetFunctionPageOffset ) {
+ if ( (mid == last) || (pageIndex.functionOffset(mid+1) > targetFunctionPageOffset) ) {
+ low = mid;
+ break;
+ }
+ else {
+ low = mid+1;
+ }
+ }
+ else {
+ high = mid;
+ }
+ }
+ funcStart = pageIndex.functionOffset(low) + firstLevelFunctionOffset + mh;
+ if ( low < last )
+ funcEnd = pageIndex.functionOffset(low+1) + firstLevelFunctionOffset + mh;
+ else
+ funcEnd = firstLevelNextPageFunctionOffset + mh;
+ if ( pc < funcStart ) {
+ DEBUG_MESSAGE("malformed __unwind_info, pc=0x%llX not in second level compressed unwind table. funcStart=0x%llX\n", (uint64_t)pc, (uint64_t)funcStart);
+ return false;
+ }
+ if ( pc > funcEnd ) {
+ DEBUG_MESSAGE("malformed __unwind_info, pc=0x%llX not in second level compressed unwind table. funcEnd=0x%llX\n", (uint64_t)pc, (uint64_t)funcEnd);
+ return false;
+ }
+ uint16_t encodingIndex = pageIndex.encodingIndex(low);
+ if ( encodingIndex < sectionHeader.commonEncodingsArrayCount() ) {
+ // encoding is in common table in section header
+ encoding = fAddressSpace.get32(unwindSectionStart+sectionHeader.commonEncodingsArraySectionOffset()+encodingIndex*sizeof(uint32_t));
+ }
+ else {
+ // encoding is in page specific table
+ uint16_t pageEncodingIndex = encodingIndex-sectionHeader.commonEncodingsArrayCount();
+ encoding = fAddressSpace.get32(secondLevelAddr+pageHeader.encodingsPageOffset()+pageEncodingIndex*sizeof(uint32_t));
+ }
+ }
+ else {
+ DEBUG_MESSAGE("malformed __unwind_info at 0x%0llX bad second level page\n", (uint64_t)unwindSectionStart);
+ return false;
+ }
+
+ // look up LSDA, if encoding says function has one
+ if ( encoding & UNWIND_HAS_LSDA ) {
+ UnwindSectionLsdaArray<A> lsdaIndex(fAddressSpace, lsdaArrayStartAddr);
+ uint32_t funcStartOffset = funcStart - mh;
+ uint32_t low = 0;
+ uint32_t high = (lsdaArrayEndAddr-lsdaArrayStartAddr)/sizeof(unwind_info_section_header_lsda_index_entry);
+ // binary search looks for entry with exact match for functionOffset
+ if ( log ) fprintf(stderr, "\tbinary search of lsda table for targetFunctionOffset=0x%08X\n", funcStartOffset);
+ while ( low < high ) {
+ uint32_t mid = (low + high)/2;
+ if ( lsdaIndex.functionOffset(mid) == funcStartOffset ) {
+ lsda = lsdaIndex.lsdaOffset(mid) + mh;
+ break;
+ }
+ else if ( lsdaIndex.functionOffset(mid) < funcStartOffset ) {
+ low = mid+1;
+ }
+ else {
+ high = mid;
+ }
+ }
+ if ( lsda == 0 ) {
+ DEBUG_MESSAGE("found encoding 0x%08X with HAS_LSDA bit set for pc=0x%0llX, but lsda table has no entry\n", encoding, (uint64_t)pc);
+ return false;
+ }
+ }
+
+ // extact personality routine, if encoding says function has one
+ uint32_t personalityIndex = (encoding & UNWIND_PERSONALITY_MASK) >> (__builtin_ctz(UNWIND_PERSONALITY_MASK));
+ if ( personalityIndex != 0 ) {
+ --personalityIndex; // change 1-based to zero-based index
+ if ( personalityIndex > sectionHeader.personalityArrayCount() ) {
+ DEBUG_MESSAGE("found encoding 0x%08X with personality index %d, but personality table has only %d entires\n",
+ encoding, personalityIndex, sectionHeader.personalityArrayCount());
+ return false;
+ }
+ int32_t personalityDelta = fAddressSpace.get32(unwindSectionStart+sectionHeader.personalityArraySectionOffset()+personalityIndex*sizeof(uint32_t));
+ pint_t personalityPointer = personalityDelta + mh;
+ personality = fAddressSpace.getP(personalityPointer);
+ if (log ) fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX), personalityDelta=0x%08X, personality=0x%08llX\n",
+ (uint64_t)pc, personalityDelta, (uint64_t)personality);
+ }
+
+ if (log ) fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX), encoding=0x%08X, lsda=0x%08llX for funcStart=0x%llX\n",
+ (uint64_t)pc, encoding, (uint64_t)lsda, (uint64_t)funcStart);
+ fInfo.start_ip = funcStart;
+ fInfo.end_ip = funcEnd;
+ fInfo.lsda = lsda;
+ fInfo.handler = personality;
+ fInfo.gp = 0;
+ fInfo.flags = 0;
+ fInfo.format = encoding;
+ fInfo.unwind_info = 0;
+ fInfo.unwind_info_size = 0;
+ fInfo.extra = mh;
+ return true;
+}
+
+template <typename A, typename R>
+void UnwindCursor<A,R>::setInfoBasedOnIPRegister(bool isReturnAddress)
+{
+ pint_t pc = this->getReg(UNW_REG_IP);
+
+ // if the last line of a function is a "throw" the compile sometimes
+ // emits no instructions after the call to __cxa_throw. This means
+ // the return address is actually the start of the next function.
+ // To disambiguate this, back up the pc when we know it is a return
+ // address.
+ if ( isReturnAddress )
+ --pc;
+
+ // ask address space object to find unwind sections for this pc
+ pint_t mh;
+ pint_t dwarfStart;
+ pint_t dwarfLength;
+ pint_t compactStart;
+ if ( fAddressSpace.findUnwindSections(pc, mh, dwarfStart, dwarfLength, compactStart) ) {
+ // if there is a compact unwind encoding table, look there first
+ if ( compactStart != 0 ) {
+ if ( this->getInfoFromCompactEncodingSection(pc, mh, compactStart) ) {
+#if !FOR_DYLD
+ // found info in table, done unless encoding says to use dwarf
+ uint32_t offsetInDwarfSection;
+ if ( (dwarfStart != 0) && dwarfWithOffset(offsetInDwarfSection) ) {
+ if ( this->getInfoFromDwarfSection(pc, mh, dwarfStart, dwarfLength, offsetInDwarfSection) ) {
+ // found info in dwarf, done
+ return;
+ }
+ }
+#endif
+ // if unwind table has entry, but entry says there is no unwind info, note that
+ if ( fInfo.format == 0 )
+ fUnwindInfoMissing = true;
+
+ // old compact encoding
+ if ( !mustUseDwarf() ) {
+ return;
+ }
+ }
+ }
+#if !FOR_DYLD || __ppc__
+ // if there is dwarf unwind info, look there next
+ if ( dwarfStart != 0 ) {
+ if ( this->getInfoFromDwarfSection(pc, mh, dwarfStart, dwarfLength, 0) ) {
+ // found info in dwarf, done
+ return;
+ }
+ }
+#endif
+ }
+
+#if !FOR_DYLD
+ // the PC is not in code loaded by dyld, look through __register_frame() registered FDEs
+ pint_t cachedFDE = DwarfFDECache<A>::findFDE(0, pc);
+ if ( cachedFDE != 0 ) {
+ typename CFI_Parser<A>::FDE_Info fdeInfo;
+ typename CFI_Parser<A>::CIE_Info cieInfo;
+ const char* msg = CFI_Parser<A>::decodeFDE(fAddressSpace, cachedFDE, &fdeInfo, &cieInfo);
+ if ( msg == NULL ) {
+ typename CFI_Parser<A>::PrologInfo prolog;
+ if ( CFI_Parser<A>::parseFDEInstructions(fAddressSpace, fdeInfo, cieInfo, pc, &prolog) ) {
+ // save off parsed FDE info
+ fInfo.start_ip = fdeInfo.pcStart;
+ fInfo.end_ip = fdeInfo.pcEnd;
+ fInfo.lsda = fdeInfo.lsda;
+ fInfo.handler = cieInfo.personality;
+ fInfo.gp = prolog.spExtraArgSize; // some frameless functions need SP altered when resuming in function
+ fInfo.flags = 0;
+ fInfo.format = dwarfEncoding();
+ fInfo.unwind_info = fdeInfo.fdeStart;
+ fInfo.unwind_info_size = fdeInfo.fdeLength;
+ fInfo.extra = 0;
+ return;
+ }
+ }
+ }
+
+#if !defined (SUPPORT_REMOTE_UNWINDING)
+ // lastly check for old style keymgr registration of dynamically generated FDEs
+
+ // acquire exclusive access to libgcc_object_info
+ libgcc_object_info* head = (libgcc_object_info*)_keymgr_get_and_lock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST);
+ if ( head != NULL ) {
+ // look at each FDE in keymgr
+ for (libgcc_object* ob = head->unseen_objects; ob != NULL; ob = ob->next) {
+ typename CFI_Parser<A>::FDE_Info fdeInfo;
+ typename CFI_Parser<A>::CIE_Info cieInfo;
+ const char* msg = CFI_Parser<A>::decodeFDE(fAddressSpace, (pint_t)ob->fde, &fdeInfo, &cieInfo);
+ if ( msg == NULL ) {
+ // see if this FDE is for a function that includes the pc we are looking for
+ if ( (fdeInfo.pcStart <= pc) && (pc < fdeInfo.pcEnd) ) {
+ typename CFI_Parser<A>::PrologInfo prolog;
+ if ( CFI_Parser<A>::parseFDEInstructions(fAddressSpace, fdeInfo, cieInfo, pc, &prolog) ) {
+ // save off parsed FDE info
+ fInfo.start_ip = fdeInfo.pcStart;
+ fInfo.end_ip = fdeInfo.pcEnd;
+ fInfo.lsda = fdeInfo.lsda;
+ fInfo.handler = cieInfo.personality;
+ fInfo.gp = prolog.spExtraArgSize; // some frameless functions need SP altered when resuming in function
+ fInfo.flags = 0;
+ fInfo.format = dwarfEncoding();
+ fInfo.unwind_info = fdeInfo.fdeStart;
+ fInfo.unwind_info_size = fdeInfo.fdeLength;
+ fInfo.extra = 0;
+ _keymgr_set_and_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST, head);
+ return;
+ }
+ }
+ }
+ }
+ }
+ // release libgcc_object_info
+ _keymgr_set_and_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST, head);
+#endif // !SUPPORT_REMOTE_UNWINDING
+
+#endif // !FOR_DYLD
+
+ // no unwind info, flag that we can't reliable unwind
+ fUnwindInfoMissing = true;
+}
+
+
+template <typename A, typename R>
+int UnwindCursor<A,R>::step()
+{
+ // bottom of stack is defined as when no more unwind info
+ if ( fUnwindInfoMissing )
+ return UNW_STEP_END;
+
+ // apply unwinding to register set
+ int result;
+ if ( this->mustUseDwarf() )
+ result = this->stepWithDwarfFDE();
+ else
+ result = this->stepWithCompactEncoding();
+
+ // update info based on new PC
+ if ( result == UNW_STEP_SUCCESS ) {
+ this->setInfoBasedOnIPRegister(true);
+ if ( fUnwindInfoMissing )
+ return UNW_STEP_END;
+ }
+
+ return result;
+}
+
+
+template <typename A, typename R>
+void UnwindCursor<A,R>::getInfo(unw_proc_info_t* info)
+{
+ *info = fInfo;
+}
+
+
+template <typename A, typename R>
+bool UnwindCursor<A,R>::getFunctionName(char* buf, size_t bufLen, unw_word_t* offset)
+{
+ return fAddressSpace.findFunctionName(this->getReg(UNW_REG_IP), buf, bufLen, offset);
+}
+
+#if defined (SUPPORT_REMOTE_UNWINDING)
+template <typename A, typename R>
+class RemoteUnwindCursor : UnwindCursor<A,R>
+{
+public:
+ typedef typename A::pint_t pint_t;
+ RemoteUnwindCursor(A& as, unw_context_t* regs, void* arg);
+ virtual bool validReg(int);
+ virtual int getReg(int r, uint64_t*);
+ virtual int setReg(int, uint64_t);
+ virtual bool validFloatReg(int);
+ virtual int getFloatReg(int, double*);
+ virtual int setFloatReg(int, double);
+ virtual const char* getRegisterName(int);
+ virtual int step();
+ virtual void setRemoteContext(void*);
+ virtual bool remoteUnwindCursor () const {return this->fAddressSpace.getRemoteProcInfo() != NULL; }
+ virtual int endOfPrologueInsns(unw_word_t, unw_word_t, unw_word_t*);
+ void operator delete(void* p, size_t size) {}
+private:
+ virtual bool caller_regno_to_unwind_regno (int, int&);
+
+ bool fIsLeafFrame;
+ bool fIsFirstFrame;
+ void* fArg;
+};
+
+typedef RemoteUnwindCursor<LocalAddressSpace,Registers_x86_64> AbstractRemoteUnwindCursor;
+
+template <typename A, typename R>
+RemoteUnwindCursor<A,R>::RemoteUnwindCursor(A& as, unw_context_t* regs, void* arg)
+ : UnwindCursor<A,R>::UnwindCursor(regs, as), fIsFirstFrame (false), fIsLeafFrame(false), fArg(arg)
+{
+ COMPILE_TIME_ASSERT( sizeof(RemoteUnwindCursor<A,R>) < sizeof(unw_cursor_t) );
+}
+
+template <typename A, typename R>
+bool RemoteUnwindCursor<A,R>::validReg(int r)
+{
+ int unwind_regno;
+ if (!caller_regno_to_unwind_regno(r, unwind_regno))
+ return false;
+ return UnwindCursor<A,R>::fRegisters.validRegister(unwind_regno);
+}
+
+template <typename A, typename R>
+int RemoteUnwindCursor<A,R>::getReg(int regNum, uint64_t *valp)
+{
+ RemoteProcInfo *procinfo = UnwindCursor<A,R>::fAddressSpace.getRemoteProcInfo ();
+ if (procinfo == NULL) {
+ ABORT("getRemoteReg called with a local unwind, use getReg instead.");
+ }
+
+ RemoteRegisterMap *regmap = procinfo->getRegisterMap ();
+ int unwind_regno;
+ if (regmap->caller_regno_to_unwind_regno (regNum, unwind_regno) == false)
+ return UNW_EBADREG;
+ regNum = unwind_regno;
+
+ // we always return nonvolatile registers. If we have the entire register state available
+ // for this frame then we can return any register requested.
+ if (regmap->nonvolatile_reg_p (regNum) == true || fIsLeafFrame == true) {
+ return this->UnwindCursor<A,R>::getReg (unwind_regno, valp);
+ }
+ return UNW_EREGUNAVAILABLE;
+}
+
+template <typename A, typename R>
+int RemoteUnwindCursor<A,R>::setReg(int regNum, uint64_t val)
+{
+ RemoteProcInfo *procinfo = UnwindCursor<A,R>::fAddressSpace.getRemoteProcInfo ();
+ if (procinfo == NULL) {
+ ABORT("setRemoteReg called with a local unwind, use setReg instead.");
+ }
+
+ RemoteRegisterMap *regmap = procinfo->getRegisterMap ();
+ int unwind_regno;
+ if (regmap->caller_regno_to_unwind_regno (regNum, unwind_regno) == false)
+ return UNW_EBADREG;
+ regNum = unwind_regno;
+
+ // Only allow the registers to be set if the unwind cursor is pointing to the
+ // first frame. We need to track where registers were retrieved from in memory
+ // in every other frame. Until then, we prohibit register setting in all but
+ // the first frame.
+ if (fIsFirstFrame) {
+ return this->setReg(unwind_regno, val);
+ }
+ return UNW_EREGUNAVAILABLE;
+}
+
+template <typename A, typename R>
+bool RemoteUnwindCursor<A,R>::validFloatReg(int r)
+{
+ int unwind_regno;
+ if (!caller_regno_to_unwind_regno(r, unwind_regno))
+ return false;
+ return UnwindCursor<A,R>::fRegisters.validFloatRegister(unwind_regno);
+}
+
+template <typename A, typename R>
+int RemoteUnwindCursor<A,R>::getFloatReg(int regNum, double *valp)
+{
+ RemoteProcInfo *procinfo = UnwindCursor<A,R>::fAddressSpace.getRemoteProcInfo ();
+ if (procinfo == NULL) {
+ ABORT("getRemoteReg called with a local unwind, use getReg instead.");
+ }
+
+ RemoteRegisterMap *regmap = procinfo->getRegisterMap ();
+ int unwind_regno;
+ if (regmap->caller_regno_to_unwind_regno (regNum, unwind_regno) == false)
+ return UNW_EBADREG;
+ regNum = unwind_regno;
+
+ // we always return nonvolatile registers. If we have the entire register state available
+ // for this frame then we can return any register requested.
+ if (regmap->nonvolatile_reg_p (regNum) == true || fIsLeafFrame == true) {
+ return this->UnwindCursor<A,R>::getFloatReg (unwind_regno, valp);
+ }
+ return UNW_EREGUNAVAILABLE;
+}
+
+template <typename A, typename R>
+int RemoteUnwindCursor<A,R>::setFloatReg(int regNum, double val)
+{
+ RemoteProcInfo *procinfo = UnwindCursor<A,R>::fAddressSpace.getRemoteProcInfo ();
+ if (procinfo == NULL) {
+ ABORT("setRemoteReg called with a local unwind, use setReg instead.");
+ }
+
+ RemoteRegisterMap *regmap = procinfo->getRegisterMap ();
+ int unwind_regno;
+ if (regmap->caller_regno_to_unwind_regno (regNum, unwind_regno) == false)
+ return UNW_EBADREG;
+ regNum = unwind_regno;
+
+ // Only allow the registers to be set if the unwind cursor is pointing to the
+ // first frame. We need to track where registers were retrieved from in memory
+ // in every other frame. Until then, we prohibit register setting in all but
+ // the first frame.
+ if (fIsFirstFrame) {
+ return this->setFloatReg(unwind_regno, val);
+ }
+ return UNW_EREGUNAVAILABLE;
+}
+
+
+template <typename A, typename R>
+const char* RemoteUnwindCursor<A,R>::getRegisterName(int r)
+{
+ int t;
+ if (!this->caller_regno_to_unwind_regno(r, t))
+ return NULL;
+ r = t;
+ return this->UnwindCursor<A,R>::getRegisterName(r);
+}
+
+template <typename A, typename R>
+int RemoteUnwindCursor<A,R>::step()
+{
+ pint_t pc = this->UnwindCursor<A,R>::getReg(UNW_REG_IP);
+ pint_t sp = this->UnwindCursor<A,R>::getReg(UNW_REG_SP);
+ RemoteProcInfo *procinfo = UnwindCursor<A,R>::fAddressSpace.getRemoteProcInfo();
+ bool frame_is_sigtramp = false;
+ bool frame_is_inferior_function_call_dummy = false;
+
+ if (procinfo == NULL) {
+ ABORT("stepRemote called with local unwind, use step() instead.");
+ return UNW_EUNSPEC;
+ }
+ struct timeval *step_remote = procinfo->timestamp_start();
+ procinfo->logVerbose ("stepRemote stepping out of frame with pc value 0x%llx", pc);
+
+ // We'll be off of the first frame once we finish this step.
+ fIsFirstFrame = false;
+
+ if (UnwindCursor<A,R>::fAddressSpace.accessors()
+ && UnwindCursor<A,R>::fAddressSpace.accessors()->proc_is_sigtramp != NULL
+ && UnwindCursor<A,R>::fAddressSpace.accessors()->proc_is_sigtramp (procinfo->wrap(), pc, fArg)) {
+ frame_is_sigtramp = true;
+ }
+ if (UnwindCursor<A,R>::fAddressSpace.accessors()
+ && UnwindCursor<A,R>::fAddressSpace.accessors()->proc_is_inferior_function_call != NULL
+ && UnwindCursor<A,R>::fAddressSpace.accessors()->proc_is_inferior_function_call (procinfo->wrap(), pc, sp, fArg)) {
+ frame_is_inferior_function_call_dummy = true;
+ }
+
+ // If the function we're unwinding can't be a leaf function,
+ // use the eh_frame or compact unwind info if possible.
+ // The caller should pass couldBeLeafFunc == 0 on the first step of a new context
+ // but we can't trust them in that.
+
+ if ((fIsLeafFrame == false && frame_is_inferior_function_call_dummy == false)
+ || frame_is_sigtramp) {
+ R saved_registers(UnwindCursor<A,R>::fRegisters);
+ this->setInfoBasedOnIPRegister(true);
+ // bottom of stack is defined as when no more unwind info
+ if ( !UnwindCursor<A,R>::fUnwindInfoMissing ) {
+ int result;
+ const char *method;
+ if ( this->mustUseDwarf() ) {
+ result = this->stepWithDwarfFDE();
+ method = "dwarf";
+ }
+ else {
+ result = this->stepWithCompactEncoding();
+ method = "compact unwind";
+ }
+ if ( result == UNW_STEP_SUCCESS ) {
+ procinfo->logInfo ("Stepped via %s", method);
+ procinfo->timestamp_stop (step_remote, "stepRemote");
+ if (frame_is_sigtramp)
+ fIsLeafFrame = true;
+ return result;
+ }
+ }
+ UnwindCursor<A,R>::fRegisters = saved_registers;
+ }
+
+ if (frame_is_sigtramp || frame_is_inferior_function_call_dummy)
+ fIsLeafFrame = true; // this will be true once we complete this stepRemote()
+ else
+ fIsLeafFrame = false;
+
+ if (frame_is_inferior_function_call_dummy) {
+ if (stepOutOfDebuggerDummyFrame (UnwindCursor<A,R>::fAddressSpace, UnwindCursor<A,R>::fRegisters, procinfo, pc, sp, fArg) == UNW_STEP_SUCCESS) {
+ procinfo->logInfo ("Stepped via stepOutOfDebuggerDummyFrame");
+ procinfo->timestamp_stop (step_remote, "stepRemote");
+ return UNW_STEP_SUCCESS;
+ }
+ }
+
+ // If we haven't already seen this function we'll need to get the function bounds via
+ // eh frame info (if available) - it's the most accurate function bounds in a
+ // stripped binary. After that we'll ask the driver program (via the get_proc_bounds accessor).
+
+ if (procinfo->haveProfile (pc) == false) {
+
+ uint64_t text_start, text_end, eh_frame_start, eh_frame_len, compact_unwind_start, mh;
+ uint64_t start_addr, end_addr;
+ if (pc == 0) {
+ int ret = stepByArchitectureDefault (UnwindCursor<A,R>::fAddressSpace, UnwindCursor<A,R>::fRegisters, pc);
+ procinfo->logInfo ("Stepped via stepByArchitectureDefault");
+ procinfo->timestamp_stop (step_remote, "stepRemote");
+ return ret;
+ }
+
+ // If the address is not contained in any image's address range either we've walked off
+ // the stack into random memory or we're backtracing through jit'ed code on the heap.
+ // Let's assume the latter and follow the architecture's default stack walking scheme.
+
+ if (!procinfo->getImageAddresses (pc, mh, text_start, text_end, eh_frame_start, eh_frame_len, compact_unwind_start, fArg)) {
+ int ret = stepByArchitectureDefault (UnwindCursor<A,R>::fAddressSpace, UnwindCursor<A,R>::fRegisters, pc);
+ procinfo->logInfo ("Stepped via stepByArchitectureDefault");
+ procinfo->timestamp_stop (step_remote, "stepRemote");
+ return ret;
+ }
+ if (procinfo->haveFuncBounds (mh) == false) {
+ struct timeval *get_func_bounds = procinfo->timestamp_start();
+ std::vector<FuncBounds> func_bounds;
+ // CFI entries are usually around 38 bytes but under-estimate a bit
+ // because we're not distinguishing between CIEs and FDEs.
+ if (eh_frame_len > 0)
+ func_bounds.reserve (eh_frame_len / 16);
+ if (procinfo->getCachingPolicy() != UNW_CACHE_NONE) {
+ // cache the entire eh frame section - we'll need to read the whole
+ // thing anyway so we might as well save it.
+ uint8_t *eh_buf = (uint8_t *)malloc (eh_frame_len);
+ if (UnwindCursor<A,R>::fAddressSpace.getBytes (eh_frame_start, eh_frame_len, eh_buf) == 0)
+ return UNW_EUNSPEC;
+ RemoteMemoryBlob *ehmem = new RemoteMemoryBlob(eh_buf, free, eh_frame_start, eh_frame_len, mh, NULL);
+ procinfo->addMemBlob (ehmem);
+ }
+
+ if (CFI_Parser<A>::functionFuncBoundsViaFDE(UnwindCursor<A,R>::fAddressSpace, eh_frame_start, eh_frame_len, func_bounds)) {
+ procinfo->addFuncBounds(mh, func_bounds);
+ procinfo->logVerbose ("Added %d function bounds", (int) func_bounds.size());
+ procinfo->timestamp_stop (get_func_bounds, "getting function bounds from EH frame FDEs");
+ }
+ }
+ if (procinfo->findStartAddr (pc, start_addr, end_addr)) {
+ // If end_addr is 0, we might be looking at the final function in this binary image
+ if (start_addr != 0 && end_addr == 0)
+ end_addr = text_end;
+ procinfo->logVerbose ("Got function bounds from func bounds vector, 0x%llx-0x%llx", start_addr, end_addr);
+ } else {
+ if (UnwindCursor<A,R>::fAddressSpace.accessors()->get_proc_bounds (procinfo->wrap(), pc, &start_addr, &end_addr, fArg) != UNW_ESUCCESS) {
+ int ret = stepByArchitectureDefault (UnwindCursor<A,R>::fAddressSpace, UnwindCursor<A,R>::fRegisters, pc);
+ procinfo->logInfo ("Stepped via stepByArchitectureDefault");
+ procinfo->timestamp_stop (step_remote, "stepRemote");
+ return ret;
+ }
+ else {
+ procinfo->logVerbose ("Got function bounds from get_proc_bounds callback, 0x%llx-0x%llx", start_addr, end_addr);
+ }
+ }
+ if (start_addr != 0) {
+ procinfo->addProfile (UnwindCursor<A,R>::fAddressSpace.accessors(), UnwindCursor<A,R>::fAddressSpace.wrap(), start_addr, end_addr, fArg);
+ }
+ }
+
+ RemoteUnwindProfile *profile = procinfo->findProfile (pc);
+ if (profile == NULL)
+ return UNW_ENOINFO;
+
+ int retval = stepWithAssembly (UnwindCursor<A,R>::fAddressSpace, pc, profile, UnwindCursor<A,R>::fRegisters);
+ if (retval >= 0) {
+ procinfo->logInfo ("Stepped via stepWithAssembly");
+ procinfo->timestamp_stop (step_remote, "stepRemote");
+ return retval;
+ }
+
+ retval = stepByArchitectureDefault (UnwindCursor<A,R>::fAddressSpace, UnwindCursor<A,R>::fRegisters, pc);
+ procinfo->logInfo ("Stepped via stepByArchitectureDefault");
+ procinfo->timestamp_stop (step_remote, "stepRemote");
+ return retval;
+}
+
+template <typename A, typename R>
+void RemoteUnwindCursor<A,R>::setRemoteContext(void *arg)
+{
+ // fill in the register state for the currently executing frame.
+ getRemoteContext (UnwindCursor<A,R>::fAddressSpace.getRemoteProcInfo(), UnwindCursor<A,R>::fRegisters, arg);
+
+ // Flag that this unwind cursor is pointing at the zeroth frame. We don't
+ // want to use compact unwind info / eh frame info to unwind out of this
+ // frame.
+
+ fIsLeafFrame = true;
+ fIsFirstFrame = true;
+}
+
+// This needs to be done in many of the functions and in libuwind.cxx in one or two
+// places so I'm defining a convenience method.
+template <typename A, typename R>
+bool RemoteUnwindCursor<A,R>::caller_regno_to_unwind_regno (int caller_regno, int& unwind_regno)
+{
+ RemoteProcInfo *procinfo = UnwindCursor<A,R>::fAddressSpace.getRemoteProcInfo ();
+ if (procinfo == NULL) {
+ unwind_regno = caller_regno;
+ return true;
+ }
+ if (procinfo->getRegisterMap()->caller_regno_to_unwind_regno (caller_regno, unwind_regno))
+ return true;
+ return false;
+}
+
+template <typename A, typename R>
+int RemoteUnwindCursor<A,R>::endOfPrologueInsns (unw_word_t start, unw_word_t end, unw_word_t *endofprologue)
+{
+ RemoteProcInfo *procinfo = UnwindCursor<A,R>::fAddressSpace.getRemoteProcInfo();
+ *endofprologue = start;
+ if (procinfo == NULL) {
+ ABORT("findEndOfPrologueSetup called with local unwind.");
+ return UNW_EUNSPEC;
+ }
+ if (procinfo->haveProfile (start) == false) {
+ uint64_t text_start, text_end, eh_frame_start, eh_frame_len, compact_unwind_start, mh;
+ if (!procinfo->getImageAddresses (start, mh, text_start, text_end, eh_frame_start, eh_frame_len, compact_unwind_start, fArg))
+ return UNW_EUNSPEC;
+ if (end == 0) {
+ if (procinfo->haveFuncBounds (mh) == false) {
+ std::vector<FuncBounds> func_bounds;
+ // CFI entries are usually around 38 bytes but under-estimate a bit
+ // because we're not distinguishing between CIEs and FDEs.
+ if (eh_frame_len > 0)
+ func_bounds.reserve (eh_frame_len / 16);
+ if (procinfo->getCachingPolicy() != UNW_CACHE_NONE) {
+ // cache the entire eh frame section - we'll need to read the whole
+ // thing anyway so we might as well save it.
+ uint8_t *eh_buf = (uint8_t *)malloc (eh_frame_len);
+ if (UnwindCursor<A,R>::fAddressSpace.getBytes (eh_frame_start, eh_frame_len, eh_buf) == 0)
+ return UNW_EUNSPEC;
+ RemoteMemoryBlob *ehmem = new RemoteMemoryBlob(eh_buf, free, eh_frame_start, eh_frame_len, mh, NULL);
+ procinfo->addMemBlob (ehmem);
+ }
+ if (CFI_Parser<A>::functionFuncBoundsViaFDE(UnwindCursor<A,R>::fAddressSpace, eh_frame_start, eh_frame_len, func_bounds)) {
+ procinfo->addFuncBounds(mh, func_bounds);
+ }
+ }
+ uint64_t bounded_start, bounded_end;
+ if (procinfo->findStartAddr (start, bounded_start, bounded_end)) {
+ end = bounded_end;
+ } else {
+ if (UnwindCursor<A,R>::fAddressSpace.accessors()->get_proc_bounds (procinfo->wrap(), start, &bounded_start, &bounded_end, fArg) != UNW_ESUCCESS)
+ if (bounded_end != 0)
+ end = bounded_end;
+ }
+ }
+ if (procinfo->addProfile (UnwindCursor<A,R>::fAddressSpace.accessors(), UnwindCursor<A,R>::fAddressSpace.wrap(), start, end, fArg) == false)
+ return UNW_EUNSPEC;
+ }
+ RemoteUnwindProfile *profile = procinfo->findProfile (start);
+ if (profile == NULL)
+ return UNW_ENOINFO;
+ *endofprologue = profile->fFirstInsnPastPrologue;
+ return UNW_ESUCCESS;
+}
+
+#endif // SUPPORT_REMOTE_UNWINDING
+
+
+}; // namespace lldb_private
+
+
+#endif // __UNWINDCURSOR_HPP__
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1-gcc-ext.c b/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1-gcc-ext.c
new file mode 100644
index 00000000000..7103c719ba2
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1-gcc-ext.c
@@ -0,0 +1,282 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- UnwindLevel1-gcc-ext.c ----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+/*
+ * Implements gcc extensions to the C++ ABI Exception Handling Level 1 as documented at:
+ * <http://www.codesourcery.com/cxx-abi/abi-eh.html>
+ * using libunwind
+ *
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libunwind.h"
+#include "unwind.h"
+#include "libunwind_priv.h"
+#include "InternalMacros.h"
+
+
+#if __ppc__ || __i386__ || __x86_64__
+
+//
+// Called by __cxa_rethrow()
+//
+EXPORT _Unwind_Reason_Code _Unwind_Resume_or_Rethrow(struct _Unwind_Exception* exception_object)
+{
+ DEBUG_PRINT_API("_Unwind_Resume_or_Rethrow(ex_obj=%p), private_1=%ld\n", exception_object, exception_object->private_1);
+ // if this is non-forced and a stopping place was found, then this is a re-throw
+ // call _Unwind_RaiseException() as if this was a new exception
+ if ( exception_object->private_1 == 0 )
+ _Unwind_RaiseException(exception_object);
+
+ // call through to _Unwind_Resume() which distiguishes between forced and regular exceptions
+ _Unwind_Resume(exception_object);
+ ABORT("_Unwind_Resume_or_Rethrow() called _Unwind_RaiseException() which unexpectedly returned");
+}
+
+
+
+//
+// Called by personality handler during phase 2 to get base address for data relative encodings
+//
+EXPORT uintptr_t _Unwind_GetDataRelBase(struct _Unwind_Context* context)
+{
+ DEBUG_PRINT_API("_Unwind_GetDataRelBase(context=%p)\n", context);
+ ABORT("_Unwind_GetDataRelBase() not implemented");
+}
+
+//
+// Called by personality handler during phase 2 to get base address for text relative encodings
+//
+EXPORT uintptr_t _Unwind_GetTextRelBase(struct _Unwind_Context* context)
+{
+ DEBUG_PRINT_API("_Unwind_GetTextRelBase(context=%p)\n", context);
+ ABORT("_Unwind_GetTextRelBase() not implemented");
+}
+
+
+
+//
+// Scans unwind information to find the function that contains the
+// specified code address "pc".
+//
+EXPORT void* _Unwind_FindEnclosingFunction(void* pc)
+{
+ DEBUG_PRINT_API("_Unwind_FindEnclosingFunction(pc=%p)\n", pc);
+ ABORT("_Unwind_FindEnclosingFunction() not implemented");
+}
+
+
+//
+// Walk every frame and call trace function at each one. If trace function
+// returns anything other than _URC_NO_REASON, then walk is terminated.
+//
+EXPORT _Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn callback, void* ref)
+{
+ unw_cursor_t cursor;
+ unw_context_t uc;
+ unw_getcontext(&uc);
+ unw_init_local(&cursor, &uc);
+
+ DEBUG_PRINT_API("_Unwind_Backtrace(callback=%p)\n", callback);
+
+ // walk each frame
+ while ( true ) {
+
+ // ask libuwind to get next frame (skip over first frame which is _Unwind_Backtrace())
+ if ( unw_step(&cursor) <= 0 ) {
+ DEBUG_PRINT_UNWINDING(" _backtrace: ended because cursor reached bottom of stack, returning %d\n", _URC_END_OF_STACK);
+ return _URC_END_OF_STACK;
+ }
+
+ // debugging
+ if ( DEBUG_PRINT_UNWINDING_TEST ) {
+ char functionName[512];
+ unw_proc_info_t frameInfo;
+ unw_word_t offset;
+ unw_get_proc_name(&cursor, functionName, 512, &offset);
+ unw_get_proc_info(&cursor, &frameInfo);
+ DEBUG_PRINT_UNWINDING(" _backtrace: start_ip=0x%llX, func=%s, lsda=0x%llX, context=%p\n",
+ frameInfo.start_ip, functionName, frameInfo.lsda, &cursor);
+ }
+
+ // call trace function with this frame
+ _Unwind_Reason_Code result = (*callback)((struct _Unwind_Context*)(&cursor), ref);
+ if ( result != _URC_NO_REASON ) {
+ DEBUG_PRINT_UNWINDING(" _backtrace: ended because callback returned %d\n", result);
+ return result;
+ }
+ }
+}
+
+
+//
+// Find dwarf unwind info for an address 'pc' in some function.
+//
+EXPORT const void* _Unwind_Find_FDE(const void* pc, struct dwarf_eh_bases* bases)
+{
+ // This is slow, but works.
+ // We create an unwind cursor then alter the IP to be pc
+ unw_cursor_t cursor;
+ unw_context_t uc;
+ unw_proc_info_t info;
+ unw_getcontext(&uc);
+ unw_init_local(&cursor, &uc);
+ unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)(long)pc);
+ unw_get_proc_info(&cursor, &info);
+ bases->tbase = info.extra;
+ bases->dbase = 0; // dbase not used on Mac OS X
+ bases->func = info.start_ip;
+ DEBUG_PRINT_API("_Unwind_Find_FDE(pc=%p) => %p\n", pc, (void*)(long)info.unwind_info);
+ return (void*)(long)info.unwind_info;
+}
+
+
+
+EXPORT uintptr_t _Unwind_GetCFA(struct _Unwind_Context* context)
+{
+ unw_cursor_t* cursor = (unw_cursor_t*)context;
+ unw_word_t result;
+ unw_get_reg(cursor, UNW_REG_SP, &result);
+ DEBUG_PRINT_API("_Unwind_GetCFA(context=%p) => 0x%llX\n", context, (uint64_t)result);
+ return result;
+}
+
+
+//
+// Called by personality handler during phase 2 to get instruction pointer.
+// ipBefore is a boolean that says if IP is already adjusted to be the call
+// site address. Normally IP is the return address.
+//
+EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context* context, int* ipBefore)
+{
+ DEBUG_PRINT_API("_Unwind_GetIPInfo(context=%p)\n", context);
+ *ipBefore = 0;
+ return _Unwind_GetIP(context);
+}
+
+
+//
+// Called by programs with dynamic code generators that want
+// to register a dynamically generated FDE.
+// This function has existed on Mac OS X since 10.4, but
+// never worked before.
+//
+EXPORT void __register_frame(const void* fde)
+{
+ DEBUG_PRINT_API("__register_frame(%p)\n", fde);
+ _unw_add_dynamic_fde((unw_word_t)(uintptr_t)fde);
+}
+
+
+//
+// Called by programs with dynamic code generators that want
+// to unregister a dynamically generated FDE.
+// This function has existed on Mac OS X since 10.4, but
+// never worked before.
+//
+EXPORT void __deregister_frame(const void* fde)
+{
+ DEBUG_PRINT_API("__deregister_frame(%p)\n", fde);
+ _unw_remove_dynamic_fde((unw_word_t)(uintptr_t)fde);
+}
+
+
+
+//
+// The following register/deregister functions are gcc extensions.
+// They have existed on Mac OS X, but have never worked because Mac OS X
+// before 10.6 used keymgr to track known FDEs, but these functions
+// never got updated to use keymgr.
+// For now, we implement these as do-nothing functions to keep any existing
+// applications working. We also add the not in 10.6 symbol so that nwe
+// application won't be able to use them.
+//
+
+EXPORT void __register_frame_info_bases(const void* fde, void* ob, void* tb, void* db)
+{
+ DEBUG_PRINT_API("__register_frame_info_bases(%p,%p, %p, %p)\n", fde, ob, tb, db);
+ // do nothing, this function never worked in Mac OS X
+}
+
+EXPORT void __register_frame_info(const void* fde, void* ob)
+{
+ DEBUG_PRINT_API("__register_frame_info(%p, %p)\n", fde, ob);
+ // do nothing, this function never worked in Mac OS X
+}
+
+
+EXPORT void __register_frame_info_table_bases(const void* fde, void* ob, void* tb, void* db)
+{
+ DEBUG_PRINT_API("__register_frame_info_table_bases(%p,%p, %p, %p)\n", fde, ob, tb, db);
+ // do nothing, this function never worked in Mac OS X
+}
+
+EXPORT void __register_frame_info_table(const void* fde, void* ob)
+{
+ DEBUG_PRINT_API("__register_frame_info_table(%p, %p)\n", fde, ob);
+ // do nothing, this function never worked in Mac OS X
+}
+
+EXPORT void __register_frame_table(const void* fde)
+{
+ DEBUG_PRINT_API("__register_frame_table(%p)\n", fde);
+ // do nothing, this function never worked in Mac OS X
+}
+
+EXPORT void* __deregister_frame_info(const void* fde)
+{
+ DEBUG_PRINT_API("__deregister_frame_info(%p)\n", fde);
+ // do nothing, this function never worked in Mac OS X
+ return NULL;
+}
+
+EXPORT void* __deregister_frame_info_bases(const void* fde)
+{
+ DEBUG_PRINT_API("__deregister_frame_info_bases(%p)\n", fde);
+ // do nothing, this function never worked in Mac OS X
+ return NULL;
+}
+
+
+
+
+//
+// symbols in libSystem.dylib in 10.6 and later, but are in libgcc_s.dylib in earlier versions
+//
+NOT_HERE_BEFORE_10_6(_Unwind_Backtrace)
+NOT_HERE_BEFORE_10_6(_Unwind_FindEnclosingFunction)
+NOT_HERE_BEFORE_10_6(_Unwind_GetCFA)
+NOT_HERE_BEFORE_10_6(_Unwind_GetDataRelBase)
+NOT_HERE_BEFORE_10_6(_Unwind_GetTextRelBase)
+NOT_HERE_BEFORE_10_6(_Unwind_Resume_or_Rethrow)
+NOT_HERE_BEFORE_10_6(_Unwind_GetIPInfo)
+
+NOT_HERE_BEFORE_10_6(__register_frame)
+NOT_HERE_BEFORE_10_6(__deregister_frame)
+
+
+//
+// symbols in libSystem.dylib for compatibility, but we don't want any new code using them
+//
+NEVER_HERE(__register_frame_info_bases)
+NEVER_HERE(__register_frame_info)
+NEVER_HERE(__register_frame_info_table_bases)
+NEVER_HERE(__register_frame_info_table)
+NEVER_HERE(__register_frame_table)
+NEVER_HERE(__deregister_frame_info)
+NEVER_HERE(__deregister_frame_info_bases)
+
+
+#endif // __ppc__ || __i386__ || __x86_64__
+
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1.c b/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1.c
new file mode 100644
index 00000000000..3aa2b6f552c
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/UnwindLevel1.c
@@ -0,0 +1,443 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- UnwindLevel1.c ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+/*
+ *
+ * Implements C++ ABI Exception Handling Level 1 as documented at:
+ * <http://www.codesourcery.com/cxx-abi/abi-eh.html>
+ * using libunwind
+ *
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "libunwind.h"
+#include "unwind.h"
+#include "InternalMacros.h"
+
+#if __ppc__ || __i386__ || __x86_64__
+
+static _Unwind_Reason_Code unwind_phase1(unw_context_t* uc, struct _Unwind_Exception* exception_object)
+{
+ unw_cursor_t cursor1;
+ unw_init_local(&cursor1, uc);
+
+ // walk each frame looking for a place to stop
+ for (bool handlerNotFound = true; handlerNotFound; ) {
+
+ // ask libuwind to get next frame (skip over first which is _Unwind_RaiseException)
+ int stepResult = unw_step(&cursor1);
+ if ( stepResult == 0 ) {
+ DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): unw_step() reached bottom => _URC_END_OF_STACK\n", exception_object);
+ return _URC_END_OF_STACK;
+ }
+ else if ( stepResult < 0 ) {
+ DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): unw_step failed => _URC_FATAL_PHASE1_ERROR\n", exception_object);
+ return _URC_FATAL_PHASE1_ERROR;
+ }
+
+ // see if frame has code to run (has personality routine)
+ unw_proc_info_t frameInfo;
+ unw_word_t sp;
+ if ( unw_get_proc_info(&cursor1, &frameInfo) != UNW_ESUCCESS ) {
+ DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): unw_get_proc_info failed => _URC_FATAL_PHASE1_ERROR\n", exception_object);
+ return _URC_FATAL_PHASE1_ERROR;
+ }
+
+ // debugging
+ if ( DEBUG_PRINT_UNWINDING_TEST ) {
+ char functionName[512];
+ unw_word_t offset;
+ if ( (unw_get_proc_name(&cursor1, functionName, 512, &offset) != UNW_ESUCCESS) || (frameInfo.start_ip+offset > frameInfo.end_ip) )
+ strcpy(functionName, ".anonymous.");
+ unw_word_t pc;
+ unw_get_reg(&cursor1, UNW_REG_IP, &pc);
+ DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): pc=0x%llX, start_ip=0x%llX, func=%s, lsda=0x%llX, personality=0x%llX\n",
+ exception_object, pc, frameInfo.start_ip, functionName, frameInfo.lsda, frameInfo.handler);
+ }
+
+ // if there is a personality routine, ask it if it will want to stop at this frame
+ if ( frameInfo.handler != 0 ) {
+ __personality_routine p = (__personality_routine)(long)(frameInfo.handler);
+ DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): calling personality function %p\n", exception_object, p);
+ _Unwind_Reason_Code personalityResult = (*p)(1, _UA_SEARCH_PHASE,
+ exception_object->exception_class, exception_object,
+ (struct _Unwind_Context*)(&cursor1));
+ switch ( personalityResult ) {
+ case _URC_HANDLER_FOUND:
+ // found a catch clause or locals that need destructing in this frame
+ // stop search and remember stack pointer at the frame
+ handlerNotFound = false;
+ unw_get_reg(&cursor1, UNW_REG_SP, &sp);
+ exception_object->private_2 = sp;
+ DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): _URC_HANDLER_FOUND\n", exception_object);
+ return _URC_NO_REASON;
+
+ case _URC_CONTINUE_UNWIND:
+ DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", exception_object);
+ // continue unwinding
+ break;
+
+ default:
+ // something went wrong
+ DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR\n", exception_object);
+ return _URC_FATAL_PHASE1_ERROR;
+ }
+ }
+ }
+ return _URC_NO_REASON;
+}
+
+
+static _Unwind_Reason_Code unwind_phase2(unw_context_t* uc, struct _Unwind_Exception* exception_object)
+{
+ unw_cursor_t cursor2;
+ unw_init_local(&cursor2, uc);
+
+ DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p)\n", exception_object);
+
+ // walk each frame until we reach where search phase said to stop
+ while ( true ) {
+
+ // ask libuwind to get next frame (skip over first which is _Unwind_RaiseException)
+ int stepResult = unw_step(&cursor2);
+ if ( stepResult == 0 ) {
+ DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step() reached bottom => _URC_END_OF_STACK\n", exception_object);
+ return _URC_END_OF_STACK;
+ }
+ else if ( stepResult < 0 ) {
+ DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step failed => _URC_FATAL_PHASE1_ERROR\n", exception_object);
+ return _URC_FATAL_PHASE2_ERROR;
+ }
+
+ // get info about this frame
+ unw_word_t sp;
+ unw_proc_info_t frameInfo;
+ unw_get_reg(&cursor2, UNW_REG_SP, &sp);
+ if ( unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS ) {
+ DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): unw_get_proc_info failed => _URC_FATAL_PHASE1_ERROR\n", exception_object);
+ return _URC_FATAL_PHASE2_ERROR;
+ }
+
+ // debugging
+ if ( DEBUG_PRINT_UNWINDING_TEST ) {
+ char functionName[512];
+ unw_word_t offset;
+ if ( (unw_get_proc_name(&cursor2, functionName, 512, &offset) != UNW_ESUCCESS) || (frameInfo.start_ip+offset > frameInfo.end_ip) )
+ strcpy(functionName, ".anonymous.");
+ DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): start_ip=0x%llX, func=%s, sp=0x%llX, lsda=0x%llX, personality=0x%llX\n",
+ exception_object, frameInfo.start_ip, functionName, sp, frameInfo.lsda, frameInfo.handler);
+ }
+
+ // if there is a personality routine, tell it we are unwinding
+ if ( frameInfo.handler != 0 ) {
+ __personality_routine p = (__personality_routine)(long)(frameInfo.handler);
+ _Unwind_Action action = _UA_CLEANUP_PHASE;
+ if ( sp == exception_object->private_2 )
+ action = (_Unwind_Action)(_UA_CLEANUP_PHASE|_UA_HANDLER_FRAME); // tell personality this was the frame it marked in phase 1
+ _Unwind_Reason_Code personalityResult = (*p)(1, action,
+ exception_object->exception_class, exception_object,
+ (struct _Unwind_Context*)(&cursor2));
+ switch ( personalityResult ) {
+ case _URC_CONTINUE_UNWIND:
+ // continue unwinding
+ DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", exception_object);
+ if ( sp == exception_object->private_2 ) {
+ // phase 1 said we would stop at this frame, but we did not...
+ ABORT("during phase1 personality function said it would stop here, but now if phase2 it did not stop here");
+ }
+ break;
+ case _URC_INSTALL_CONTEXT:
+ DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): _URC_INSTALL_CONTEXT\n", exception_object);
+ // personality routine says to transfer control to landing pad
+ // we may get control back if landing pad calls _Unwind_Resume()
+ if ( DEBUG_PRINT_UNWINDING_TEST ) {
+ unw_word_t pc;
+ unw_word_t sp;
+ unw_get_reg(&cursor2, UNW_REG_IP, &pc);
+ unw_get_reg(&cursor2, UNW_REG_SP, &sp);
+ DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): re-entering user code with ip=0x%llX, sp=0x%llX\n", exception_object, pc, sp);
+ }
+ unw_resume(&cursor2);
+ // unw_resume() only returns if there was an error
+ return _URC_FATAL_PHASE2_ERROR;
+ default:
+ // something went wrong
+ DEBUG_MESSAGE("personality function returned unknown result %d", personalityResult);
+ return _URC_FATAL_PHASE2_ERROR;
+ }
+ }
+ }
+
+ // clean up phase did not resume at the frame that the search phase said it would
+ return _URC_FATAL_PHASE2_ERROR;
+}
+
+
+static _Unwind_Reason_Code unwind_phase2_forced(unw_context_t* uc, struct _Unwind_Exception* exception_object,
+ _Unwind_Stop_Fn stop, void* stop_parameter)
+{
+ unw_cursor_t cursor2;
+ unw_init_local(&cursor2, uc);
+
+ // walk each frame until we reach where search phase said to stop
+ while ( unw_step(&cursor2) > 0 ) {
+
+ // get info about this frame
+ unw_proc_info_t frameInfo;
+ if ( unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS ) {
+ DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): unw_step failed => _URC_END_OF_STACK\n", exception_object);
+ return _URC_FATAL_PHASE1_ERROR;
+ }
+
+ // debugging
+ if ( DEBUG_PRINT_UNWINDING_TEST ) {
+ char functionName[512];
+ unw_word_t offset;
+ if ( (unw_get_proc_name(&cursor2, functionName, 512, &offset) != UNW_ESUCCESS) || (frameInfo.start_ip+offset > frameInfo.end_ip) )
+ strcpy(functionName, ".anonymous.");
+ DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): start_ip=0x%llX, func=%s, lsda=0x%llX, personality=0x%llX\n",
+ exception_object, frameInfo.start_ip, functionName, frameInfo.lsda, frameInfo.handler);
+ }
+
+ // call stop function at each frame
+ _Unwind_Action action = (_Unwind_Action)(_UA_FORCE_UNWIND|_UA_CLEANUP_PHASE);
+ _Unwind_Reason_Code stopResult = (*stop)(1, action,
+ exception_object->exception_class, exception_object,
+ (struct _Unwind_Context*)(&cursor2), stop_parameter);
+ DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): stop function returned %d\n", exception_object, stopResult);
+ if ( stopResult != _URC_NO_REASON ) {
+ DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): stopped by stop function\n", exception_object);
+ return _URC_FATAL_PHASE2_ERROR;
+ }
+
+ // if there is a personality routine, tell it we are unwinding
+ if ( frameInfo.handler != 0 ) {
+ __personality_routine p = (__personality_routine)(long)(frameInfo.handler);
+ DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling personality function %p\n", exception_object, p);
+ _Unwind_Reason_Code personalityResult = (*p)(1, action,
+ exception_object->exception_class, exception_object,
+ (struct _Unwind_Context*)(&cursor2));
+ switch ( personalityResult ) {
+ case _URC_CONTINUE_UNWIND:
+ DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): personality returned _URC_CONTINUE_UNWIND\n", exception_object);
+ // destructors called, continue unwinding
+ break;
+ case _URC_INSTALL_CONTEXT:
+ DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): personality returned _URC_INSTALL_CONTEXT\n", exception_object);
+ // we may get control back if landing pad calls _Unwind_Resume()
+ unw_resume(&cursor2);
+ break;
+ default:
+ // something went wrong
+ DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): personality returned %d, _URC_FATAL_PHASE2_ERROR\n",
+ exception_object, personalityResult);
+ return _URC_FATAL_PHASE2_ERROR;
+ }
+ }
+ }
+
+ // call stop function one last time and tell it we've reached the end of the stack
+ DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop function with _UA_END_OF_STACK\n", exception_object);
+ _Unwind_Action lastAction = (_Unwind_Action)(_UA_FORCE_UNWIND|_UA_CLEANUP_PHASE|_UA_END_OF_STACK);
+ (*stop)(1, lastAction, exception_object->exception_class, exception_object, (struct _Unwind_Context*)(&cursor2), stop_parameter);
+
+ // clean up phase did not resume at the frame that the search phase said it would
+ return _URC_FATAL_PHASE2_ERROR;
+}
+
+
+//
+// Called by __cxa_throw. Only returns if there is a fatal error
+//
+EXPORT _Unwind_Reason_Code _Unwind_RaiseException(struct _Unwind_Exception* exception_object)
+{
+ DEBUG_PRINT_API("_Unwind_RaiseException(ex_obj=%p)\n", exception_object);
+ unw_context_t uc;
+ unw_getcontext(&uc);
+
+ // mark that this is a non-forced unwind, so _Unwind_Resume() can do the right thing
+ exception_object->private_1 = 0;
+ exception_object->private_2 = 0;
+
+ // phase 1: the search phase
+ _Unwind_Reason_Code phase1 = unwind_phase1(&uc, exception_object);
+ if ( phase1 != _URC_NO_REASON )
+ return phase1;
+
+ // phase 2: the clean up phase
+ return unwind_phase2(&uc, exception_object);
+}
+
+
+//
+// When _Unwind_RaiseException() is in phase2, it hands control
+// to the personality function at each frame. The personality
+// may force a jump to a landing pad in that function, the landing
+// pad code may then call _Unwind_Resume() to continue with the
+// unwinding. Note: the call to _Unwind_Resume() is from compiler
+// geneated user code. All other _Unwind_* routines are called
+// by the C++ runtime __cxa_* routines.
+//
+// Re-throwing an exception is implemented by having the code call
+// __cxa_rethrow() which in turn calls _Unwind_Resume_or_Rethrow()
+//
+EXPORT void _Unwind_Resume(struct _Unwind_Exception* exception_object)
+{
+ DEBUG_PRINT_API("_Unwind_Resume(ex_obj=%p)\n", exception_object);
+ unw_context_t uc;
+ unw_getcontext(&uc);
+
+ if ( exception_object->private_1 != 0 )
+ unwind_phase2_forced(&uc, exception_object, (_Unwind_Stop_Fn)exception_object->private_1, (void*)exception_object->private_2);
+ else
+ unwind_phase2(&uc, exception_object);
+
+ // clients assume _Unwind_Resume() does not return, so all we can do is abort.
+ ABORT("_Unwind_Resume() can't return");
+}
+
+
+
+//
+// Not used by C++.
+// Unwinds stack, calling "stop" function at each frame
+// Could be used to implement longjmp().
+//
+EXPORT _Unwind_Reason_Code _Unwind_ForcedUnwind(struct _Unwind_Exception* exception_object, _Unwind_Stop_Fn stop, void* stop_parameter)
+{
+ DEBUG_PRINT_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)\n", exception_object, stop);
+ unw_context_t uc;
+ unw_getcontext(&uc);
+
+ // mark that this is a forced unwind, so _Unwind_Resume() can do the right thing
+ exception_object->private_1 = (uintptr_t)stop;
+ exception_object->private_2 = (uintptr_t)stop_parameter;
+
+ // doit
+ return unwind_phase2_forced(&uc, exception_object, stop, stop_parameter);
+}
+
+
+//
+// Called by personality handler during phase 2 to get LSDA for current frame
+//
+EXPORT uintptr_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context* context)
+{
+ unw_cursor_t* cursor = (unw_cursor_t*)context;
+ unw_proc_info_t frameInfo;
+ uintptr_t result = 0;
+ if ( unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS )
+ result = frameInfo.lsda;
+ DEBUG_PRINT_API("_Unwind_GetLanguageSpecificData(context=%p) => 0x%lX\n", context, result);
+ if ( result != 0 ) {
+ if ( *((uint8_t*)result) != 0xFF )
+ DEBUG_MESSAGE("lsda at 0x%lX does not start with 0xFF\n", result);
+ }
+ return result;
+}
+
+
+//
+// Called by personality handler during phase 2 to get register values
+//
+EXPORT uintptr_t _Unwind_GetGR(struct _Unwind_Context* context, int index)
+{
+ unw_cursor_t* cursor = (unw_cursor_t*)context;
+ unw_word_t result;
+ unw_get_reg(cursor, index, &result);
+ DEBUG_PRINT_API("_Unwind_GetGR(context=%p, reg=%d) => 0x%llX\n", context, index, (uint64_t)result);
+ return result;
+}
+
+
+//
+// Called by personality handler during phase 2 to alter register values
+//
+EXPORT void _Unwind_SetGR(struct _Unwind_Context* context, int index, uintptr_t new_value)
+{
+ DEBUG_PRINT_API("_Unwind_SetGR(context=%p, reg=%d, value=0x%0llX)\n", context, index, (uint64_t)new_value);
+ unw_cursor_t* cursor = (unw_cursor_t*)context;
+ unw_set_reg(cursor, index, new_value);
+}
+
+
+//
+// Called by personality handler during phase 2 to get instruction pointer
+//
+EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context* context)
+{
+ unw_cursor_t* cursor = (unw_cursor_t*)context;
+ unw_word_t result;
+ unw_get_reg(cursor, UNW_REG_IP, &result);
+ DEBUG_PRINT_API("_Unwind_GetIP(context=%p) => 0x%llX\n", context, (uint64_t)result);
+ return result;
+}
+
+
+//
+// Called by personality handler during phase 2 to alter instruction pointer
+//
+EXPORT void _Unwind_SetIP(struct _Unwind_Context* context, uintptr_t new_value)
+{
+ DEBUG_PRINT_API("_Unwind_SetIP(context=%p, value=0x%0llX)\n", context, (uint64_t)new_value);
+ unw_cursor_t* cursor = (unw_cursor_t*)context;
+ unw_set_reg(cursor, UNW_REG_IP, new_value);
+}
+
+
+//
+// Called by personality handler during phase 2 to find the start of the function
+//
+EXPORT uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context* context)
+{
+ unw_cursor_t* cursor = (unw_cursor_t*)context;
+ unw_proc_info_t frameInfo;
+ uintptr_t result = 0;
+ if ( unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS )
+ result = frameInfo.start_ip;
+ DEBUG_PRINT_API("_Unwind_GetRegionStart(context=%p) => 0x%lX\n", context, result);
+ return result;
+}
+
+
+//
+// Called by personality handler during phase 2 if a foreign exception is caught
+//
+EXPORT void _Unwind_DeleteException(struct _Unwind_Exception* exception_object)
+{
+ DEBUG_PRINT_API("_Unwind_DeleteException(ex_obj=%p)\n", exception_object);
+ if ( exception_object->exception_cleanup != NULL )
+ (*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, exception_object);
+}
+
+
+
+
+//
+// symbols in libSystem.dylib in 10.6 and later, but are in libgcc_s.dylib in earlier versions
+//
+NOT_HERE_BEFORE_10_6(_Unwind_DeleteException)
+NOT_HERE_BEFORE_10_6(_Unwind_Find_FDE)
+NOT_HERE_BEFORE_10_6(_Unwind_ForcedUnwind)
+NOT_HERE_BEFORE_10_6(_Unwind_GetGR)
+NOT_HERE_BEFORE_10_6(_Unwind_GetIP)
+NOT_HERE_BEFORE_10_6(_Unwind_GetLanguageSpecificData)
+NOT_HERE_BEFORE_10_6(_Unwind_GetRegionStart)
+NOT_HERE_BEFORE_10_6(_Unwind_RaiseException)
+NOT_HERE_BEFORE_10_6(_Unwind_Resume)
+NOT_HERE_BEFORE_10_6(_Unwind_SetGR)
+NOT_HERE_BEFORE_10_6(_Unwind_SetIP)
+
+#endif // __ppc__ || __i386__ || __x86_64__
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/dwarf2.h b/lldb/source/Plugins/Process/Utility/libunwind/src/dwarf2.h
new file mode 100644
index 00000000000..83414332c5e
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/dwarf2.h
@@ -0,0 +1,245 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- dwarf2.h ------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+/* These constants were taken from version 3 of the DWARF standard,
+ which is Copyright (c) 2005 Free Standards Group, and
+ Copyright (c) 1992, 1993 UNIX International, Inc.
+*/
+
+
+#ifndef __DWARF2__
+#define __DWARF2__
+
+namespace lldb_private {
+
+// dwarf unwind instructions
+enum {
+ DW_CFA_nop = 0x0,
+ DW_CFA_set_loc = 0x1,
+ DW_CFA_advance_loc1 = 0x2,
+ DW_CFA_advance_loc2 = 0x3,
+ DW_CFA_advance_loc4 = 0x4,
+ DW_CFA_offset_extended = 0x5,
+ DW_CFA_restore_extended = 0x6,
+ DW_CFA_undefined = 0x7,
+ DW_CFA_same_value = 0x8,
+ DW_CFA_register = 0x9,
+ DW_CFA_remember_state = 0xA,
+ DW_CFA_restore_state = 0xB,
+ DW_CFA_def_cfa = 0xC,
+ DW_CFA_def_cfa_register = 0xD,
+ DW_CFA_def_cfa_offset = 0xE,
+ DW_CFA_def_cfa_expression = 0xF,
+ DW_CFA_expression = 0x10,
+ DW_CFA_offset_extended_sf = 0x11,
+ DW_CFA_def_cfa_sf = 0x12,
+ DW_CFA_def_cfa_offset_sf = 0x13,
+ DW_CFA_val_offset = 0x14,
+ DW_CFA_val_offset_sf = 0x15,
+ DW_CFA_val_expression = 0x16,
+ DW_CFA_advance_loc = 0x40, // high 2 bits are 0x1, lower 6 bits are delta
+ DW_CFA_offset = 0x80, // high 2 bits are 0x2, lower 6 bits are register
+ DW_CFA_restore = 0xC0, // high 2 bits are 0x3, lower 6 bits are register
+
+ // GNU extensions
+ DW_CFA_GNU_window_save = 0x2D,
+ DW_CFA_GNU_args_size = 0x2E,
+ DW_CFA_GNU_negative_offset_extended = 0x2F
+};
+
+
+// FSF exception handling Pointer-Encoding constants
+// Used in CFI augmentation by gcc compiler
+enum {
+ DW_EH_PE_ptr = 0x00,
+ DW_EH_PE_uleb128 = 0x01,
+ DW_EH_PE_udata2 = 0x02,
+ DW_EH_PE_udata4 = 0x03,
+ DW_EH_PE_udata8 = 0x04,
+ DW_EH_PE_signed = 0x08,
+ DW_EH_PE_sleb128 = 0x09,
+ DW_EH_PE_sdata2 = 0x0A,
+ DW_EH_PE_sdata4 = 0x0B,
+ DW_EH_PE_sdata8 = 0x0C,
+ DW_EH_PE_absptr = 0x00,
+ DW_EH_PE_pcrel = 0x10,
+ DW_EH_PE_textrel = 0x20,
+ DW_EH_PE_datarel = 0x30,
+ DW_EH_PE_funcrel = 0x40,
+ DW_EH_PE_aligned = 0x50,
+ DW_EH_PE_indirect = 0x80,
+ DW_EH_PE_omit = 0xFF
+};
+
+
+// DWARF expressions
+enum {
+ DW_OP_addr = 0x03, // constant address (size target specific)
+ DW_OP_deref = 0x06,
+ DW_OP_const1u = 0x08, // 1-byte constant
+ DW_OP_const1s = 0x09, // 1-byte constant
+ DW_OP_const2u = 0x0A, // 2-byte constant
+ DW_OP_const2s = 0x0B, // 2-byte constant
+ DW_OP_const4u = 0x0C, // 4-byte constant
+ DW_OP_const4s = 0x0D, // 4-byte constant
+ DW_OP_const8u = 0x0E, // 8-byte constant
+ DW_OP_const8s = 0x0F, // 8-byte constant
+ DW_OP_constu = 0x10, // ULEB128 constant
+ DW_OP_consts = 0x11, // SLEB128 constant
+ DW_OP_dup = 0x12,
+ DW_OP_drop = 0x13,
+ DW_OP_over = 0x14,
+ DW_OP_pick = 0x15, // 1-byte stack index
+ DW_OP_swap = 0x16,
+ DW_OP_rot = 0x17,
+ DW_OP_xderef = 0x18,
+ DW_OP_abs = 0x19,
+ DW_OP_and = 0x1A,
+ DW_OP_div = 0x1B,
+ DW_OP_minus = 0x1C,
+ DW_OP_mod = 0x1D,
+ DW_OP_mul = 0x1E,
+ DW_OP_neg = 0x1F,
+ DW_OP_not = 0x20,
+ DW_OP_or = 0x21,
+ DW_OP_plus = 0x22,
+ DW_OP_plus_uconst = 0x23, // ULEB128 addend
+ DW_OP_shl = 0x24,
+ DW_OP_shr = 0x25,
+ DW_OP_shra = 0x26,
+ DW_OP_xor = 0x27,
+ DW_OP_skip = 0x2F, // signed 2-byte constant
+ DW_OP_bra = 0x28, // signed 2-byte constant
+ DW_OP_eq = 0x29,
+ DW_OP_ge = 0x2A,
+ DW_OP_gt = 0x2B,
+ DW_OP_le = 0x2C,
+ DW_OP_lt = 0x2D,
+ DW_OP_ne = 0x2E,
+ DW_OP_lit0 = 0x30, // Literal 0
+ DW_OP_lit1 = 0x31, // Literal 1
+ DW_OP_lit2 = 0x32, // Literal 2
+ DW_OP_lit3 = 0x33, // Literal 3
+ DW_OP_lit4 = 0x34, // Literal 4
+ DW_OP_lit5 = 0x35, // Literal 5
+ DW_OP_lit6 = 0x36, // Literal 6
+ DW_OP_lit7 = 0x37, // Literal 7
+ DW_OP_lit8 = 0x38, // Literal 8
+ DW_OP_lit9 = 0x39, // Literal 9
+ DW_OP_lit10 = 0x3A, // Literal 10
+ DW_OP_lit11 = 0x3B, // Literal 11
+ DW_OP_lit12 = 0x3C, // Literal 12
+ DW_OP_lit13 = 0x3D, // Literal 13
+ DW_OP_lit14 = 0x3E, // Literal 14
+ DW_OP_lit15 = 0x3F, // Literal 15
+ DW_OP_lit16 = 0x40, // Literal 16
+ DW_OP_lit17 = 0x41, // Literal 17
+ DW_OP_lit18 = 0x42, // Literal 18
+ DW_OP_lit19 = 0x43, // Literal 19
+ DW_OP_lit20 = 0x44, // Literal 20
+ DW_OP_lit21 = 0x45, // Literal 21
+ DW_OP_lit22 = 0x46, // Literal 22
+ DW_OP_lit23 = 0x47, // Literal 23
+ DW_OP_lit24 = 0x48, // Literal 24
+ DW_OP_lit25 = 0x49, // Literal 25
+ DW_OP_lit26 = 0x4A, // Literal 26
+ DW_OP_lit27 = 0x4B, // Literal 27
+ DW_OP_lit28 = 0x4C, // Literal 28
+ DW_OP_lit29 = 0x4D, // Literal 29
+ DW_OP_lit30 = 0x4E, // Literal 30
+ DW_OP_lit31 = 0x4F, // Literal 31
+ DW_OP_reg0 = 0x50, // Contents of reg0
+ DW_OP_reg1 = 0x51, // Contents of reg1
+ DW_OP_reg2 = 0x52, // Contents of reg2
+ DW_OP_reg3 = 0x53, // Contents of reg3
+ DW_OP_reg4 = 0x54, // Contents of reg4
+ DW_OP_reg5 = 0x55, // Contents of reg5
+ DW_OP_reg6 = 0x56, // Contents of reg6
+ DW_OP_reg7 = 0x57, // Contents of reg7
+ DW_OP_reg8 = 0x58, // Contents of reg8
+ DW_OP_reg9 = 0x59, // Contents of reg9
+ DW_OP_reg10 = 0x5A, // Contents of reg10
+ DW_OP_reg11 = 0x5B, // Contents of reg11
+ DW_OP_reg12 = 0x5C, // Contents of reg12
+ DW_OP_reg13 = 0x5D, // Contents of reg13
+ DW_OP_reg14 = 0x5E, // Contents of reg14
+ DW_OP_reg15 = 0x5F, // Contents of reg15
+ DW_OP_reg16 = 0x60, // Contents of reg16
+ DW_OP_reg17 = 0x61, // Contents of reg17
+ DW_OP_reg18 = 0x62, // Contents of reg18
+ DW_OP_reg19 = 0x63, // Contents of reg19
+ DW_OP_reg20 = 0x64, // Contents of reg20
+ DW_OP_reg21 = 0x65, // Contents of reg21
+ DW_OP_reg22 = 0x66, // Contents of reg22
+ DW_OP_reg23 = 0x67, // Contents of reg23
+ DW_OP_reg24 = 0x68, // Contents of reg24
+ DW_OP_reg25 = 0x69, // Contents of reg25
+ DW_OP_reg26 = 0x6A, // Contents of reg26
+ DW_OP_reg27 = 0x6B, // Contents of reg27
+ DW_OP_reg28 = 0x6C, // Contents of reg28
+ DW_OP_reg29 = 0x6D, // Contents of reg29
+ DW_OP_reg30 = 0x6E, // Contents of reg30
+ DW_OP_reg31 = 0x6F, // Contents of reg31
+ DW_OP_breg0 = 0x70, // base register 0 + SLEB128 offset
+ DW_OP_breg1 = 0x71, // base register 1 + SLEB128 offset
+ DW_OP_breg2 = 0x72, // base register 2 + SLEB128 offset
+ DW_OP_breg3 = 0x73, // base register 3 + SLEB128 offset
+ DW_OP_breg4 = 0x74, // base register 4 + SLEB128 offset
+ DW_OP_breg5 = 0x75, // base register 5 + SLEB128 offset
+ DW_OP_breg6 = 0x76, // base register 6 + SLEB128 offset
+ DW_OP_breg7 = 0x77, // base register 7 + SLEB128 offset
+ DW_OP_breg8 = 0x78, // base register 8 + SLEB128 offset
+ DW_OP_breg9 = 0x79, // base register 9 + SLEB128 offset
+ DW_OP_breg10 = 0x7A, // base register 10 + SLEB128 offset
+ DW_OP_breg11 = 0x7B, // base register 11 + SLEB128 offset
+ DW_OP_breg12 = 0x7C, // base register 12 + SLEB128 offset
+ DW_OP_breg13 = 0x7D, // base register 13 + SLEB128 offset
+ DW_OP_breg14 = 0x7E, // base register 14 + SLEB128 offset
+ DW_OP_breg15 = 0x7F, // base register 15 + SLEB128 offset
+ DW_OP_breg16 = 0x80, // base register 16 + SLEB128 offset
+ DW_OP_breg17 = 0x81, // base register 17 + SLEB128 offset
+ DW_OP_breg18 = 0x82, // base register 18 + SLEB128 offset
+ DW_OP_breg19 = 0x83, // base register 19 + SLEB128 offset
+ DW_OP_breg20 = 0x84, // base register 20 + SLEB128 offset
+ DW_OP_breg21 = 0x85, // base register 21 + SLEB128 offset
+ DW_OP_breg22 = 0x86, // base register 22 + SLEB128 offset
+ DW_OP_breg23 = 0x87, // base register 23 + SLEB128 offset
+ DW_OP_breg24 = 0x88, // base register 24 + SLEB128 offset
+ DW_OP_breg25 = 0x89, // base register 25 + SLEB128 offset
+ DW_OP_breg26 = 0x8A, // base register 26 + SLEB128 offset
+ DW_OP_breg27 = 0x8B, // base register 27 + SLEB128 offset
+ DW_OP_breg28 = 0x8C, // base register 28 + SLEB128 offset
+ DW_OP_breg29 = 0x8D, // base register 29 + SLEB128 offset
+ DW_OP_breg30 = 0x8E, // base register 30 + SLEB128 offset
+ DW_OP_breg31 = 0x8F, // base register 31 + SLEB128 offset
+ DW_OP_regx = 0x90, // ULEB128 register
+ DW_OP_fbreg = 0x91, // SLEB128 offset
+ DW_OP_bregx = 0x92, // ULEB128 register followed by SLEB128 offset
+ DW_OP_piece = 0x93, // ULEB128 size of piece addressed
+ DW_OP_deref_size = 0x94, // 1-byte size of data retrieved
+ DW_OP_xderef_size = 0x95, // 1-byte size of data retrieved
+ DW_OP_nop = 0x96,
+ DW_OP_push_object_addres = 0x97,
+ DW_OP_call2 = 0x98, // 2-byte offset of DIE
+ DW_OP_call4 = 0x99, // 4-byte offset of DIE
+ DW_OP_call_ref = 0x9A, // 4- or 8-byte offset of DIE
+ DW_OP_lo_user = 0xE0,
+ DW_OP_APPLE_uninit = 0xF0,
+ DW_OP_hi_user = 0xFF
+};
+
+
+}; // namespace lldb_private
+
+
+#endif
+
+
+
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/libunwind_priv.h b/lldb/source/Plugins/Process/Utility/libunwind/src/libunwind_priv.h
new file mode 100644
index 00000000000..fe25780c538
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/libunwind_priv.h
@@ -0,0 +1,35 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- libunwind_priv.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __LIBUNWIND_PRIV__
+#define __LIBUNWIND_PRIV__
+
+namespace lldb_private {
+#include "libunwind.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ // SPI
+ extern void unw_iterate_dwarf_unwind_cache(void (*func)(unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh));
+
+ // IPI
+ extern void _unw_add_dynamic_fde(unw_word_t fde);
+ extern void _unw_remove_dynamic_fde(unw_word_t fde);
+
+#ifdef __cplusplus
+}
+#endif
+
+}; // namespace lldb_private
+
+
+#endif
+
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/libuwind.cxx b/lldb/source/Plugins/Process/Utility/libunwind/src/libuwind.cxx
new file mode 100644
index 00000000000..e7e66a452f0
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/libuwind.cxx
@@ -0,0 +1,421 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 vi:set tabstop=4 expandtab: -*/
+//===-- libuwind.cxx --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#if __ppc__ || __i386__ || __x86_64__
+
+#include <mach/mach_types.h>
+#include <mach/machine.h>
+#include <new>
+
+#include "libunwind.h"
+#include "libunwind_priv.h"
+
+#include "UnwindCursor.hpp"
+#include "AddressSpace.hpp"
+
+#include "RemoteProcInfo.hpp"
+
+namespace lldb_private {
+
+// setup debug logging hooks
+INITIALIZE_DEBUG_PRINT_API
+INITIALIZE_DEBUG_PRINT_UNWINDING
+
+// internal object to represent this processes address space
+static LocalAddressSpace sThisAddressSpace;
+
+#pragma mark Local API
+
+///
+/// record the registers and stack position of the caller
+///
+extern int unw_getcontext(unw_context_t*);
+// note: unw_getcontext() implemented in assembly
+
+///
+/// create a cursor of a thread in this process given 'context' recorded by unw_getcontext()
+///
+EXPORT int unw_init_local(unw_cursor_t* cursor, unw_context_t* context)
+{
+ DEBUG_PRINT_API("unw_init_local(cursor=%p, context=%p)\n", cursor, context);
+ // use "placement new" to allocate UnwindCursor in the cursor buffer
+#if __i386__
+ new ((void*)cursor) UnwindCursor<LocalAddressSpace,Registers_x86>(context, sThisAddressSpace);
+#elif __x86_64__
+ new ((void*)cursor) UnwindCursor<LocalAddressSpace,Registers_x86_64>(context, sThisAddressSpace);
+#elif __ppc__
+ new ((void*)cursor) UnwindCursor<LocalAddressSpace,Registers_ppc>(context, sThisAddressSpace);
+#endif
+ AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
+ co->setInfoBasedOnIPRegister(NULL);
+
+ return UNW_ESUCCESS;
+}
+
+///
+/// move cursor to next frame
+///
+EXPORT int unw_step(unw_cursor_t* cursor)
+{
+ DEBUG_PRINT_API("unw_step(cursor=%p)\n", cursor);
+ AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
+ return co->step();
+}
+
+///
+/// get value of specified register at cursor position in stack frame
+///
+EXPORT int unw_get_reg(unw_cursor_t* cursor, unw_regnum_t regNum, unw_word_t* value)
+{
+ DEBUG_PRINT_API("unw_get_reg(cursor=%p, regNum=%d, &value=%p)\n", cursor, regNum, value);
+ AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
+
+ if (co->validReg(regNum) == 0)
+ return UNW_EBADREG;
+ return co->getReg(regNum, value);
+}
+
+///
+/// get value of specified float register at cursor position in stack frame
+///
+EXPORT int unw_get_fpreg(unw_cursor_t* cursor, unw_regnum_t regNum, unw_fpreg_t* value)
+{
+ DEBUG_PRINT_API("unw_get_fpreg(cursor=%p, regNum=%d, &value=%p)\n", cursor, regNum, value);
+ AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
+
+ if ( co->validFloatReg(regNum) ) {
+ return co->getFloatReg(regNum, value);
+ }
+ return UNW_EBADREG;
+}
+
+///
+/// set value of specified register at cursor position in stack frame
+///
+EXPORT int unw_set_reg(unw_cursor_t* cursor, unw_regnum_t regNum, unw_word_t value)
+{
+ DEBUG_PRINT_API("unw_set_reg(cursor=%p, regNum=%d, value=0x%llX)\n", cursor, regNum, value);
+ AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
+
+ if ( co->validReg(regNum) ) {
+ co->setReg(regNum, value);
+ // specical case altering IP to re-find info (being called by personality function)
+ if ( regNum == UNW_REG_IP ) {
+ unw_proc_info_t info;
+ co->getInfo(&info);
+ uint64_t orgArgSize = info.gp;
+ uint64_t orgFuncStart = info.start_ip;
+ co->setInfoBasedOnIPRegister(false);
+ // and adjust REG_SP if there was a DW_CFA_GNU_args_size
+ if ( (orgFuncStart == info.start_ip) && (orgArgSize != 0) )
+ co->setReg(UNW_REG_SP, co->getReg(UNW_REG_SP) + orgArgSize);
+ }
+ return UNW_ESUCCESS;
+ }
+ return UNW_EBADREG;
+}
+
+///
+/// set value of specified float register at cursor position in stack frame
+///
+EXPORT int unw_set_fpreg(unw_cursor_t* cursor, unw_regnum_t regNum, unw_fpreg_t value)
+{
+ DEBUG_PRINT_API("unw_set_fpreg(cursor=%p, regNum=%d, value=%g)\n", cursor, regNum, value);
+ AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
+
+ if ( co->validFloatReg(regNum) ) {
+ return co->setFloatReg(regNum, value);
+ }
+ return UNW_EBADREG;
+}
+
+///
+/// resume execution at cursor position (aka longjump)
+///
+EXPORT int unw_resume(unw_cursor_t* cursor)
+{
+ DEBUG_PRINT_API("unw_resume(cursor=%p)\n", cursor);
+ AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
+
+ co->jumpto();
+ return UNW_EUNSPEC;
+}
+
+///
+/// returns the name of a register
+///
+EXPORT const char* unw_regname(unw_cursor_t* cursor, unw_regnum_t regNum)
+{
+ DEBUG_PRINT_API("unw_regname(cursor=%p, regNum=%d)\n", cursor, regNum);
+ AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
+ return co->getRegisterName(regNum);
+}
+
+///
+/// get unwind info at cursor position in stack frame
+///
+EXPORT int unw_get_proc_info(unw_cursor_t* cursor, unw_proc_info_t* info)
+{
+ DEBUG_PRINT_API("unw_get_proc_info(cursor=%p, &info=%p)\n", cursor, info);
+ AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
+ co->getInfo(info);
+ if ( info->end_ip == 0 )
+ return UNW_ENOINFO;
+ else
+ return UNW_ESUCCESS;
+}
+
+///
+/// checks if a register is a floating-point register
+///
+EXPORT int unw_is_fpreg(unw_cursor_t* cursor, unw_regnum_t regNum)
+{
+ DEBUG_PRINT_API("unw_is_fpreg(cursor=%p, regNum=%d)\n", cursor, regNum);
+ AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
+ return co->validFloatReg(regNum);
+}
+
+///
+/// checks if current frame is signal trampoline
+///
+EXPORT int unw_is_signal_frame(unw_cursor_t* cursor)
+{
+ DEBUG_PRINT_API("unw_is_signal_frame(cursor=%p)\n", cursor);
+ AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
+ return co->isSignalFrame();
+}
+
+///
+/// get name of function at cursor position in stack frame
+///
+EXPORT int unw_get_proc_name(unw_cursor_t* cursor, char* buf, size_t bufLen, unw_word_t* offset)
+{
+ DEBUG_PRINT_API("unw_get_proc_name(cursor=%p, &buf=%p, bufLen=%ld)\n", cursor, buf, bufLen);
+ AbstractUnwindCursor* co = (AbstractUnwindCursor*)cursor;
+ if ( co->getFunctionName(buf, bufLen, offset) )
+ return UNW_ESUCCESS;
+ else
+ return UNW_EUNSPEC;
+}
+
+#pragma mark Remote API
+
+#if defined (SUPPORT_REMOTE_UNWINDING)
+EXPORT int unw_init_remote(unw_cursor_t *cursor, unw_addr_space_t as, void *arg)
+{
+ DEBUG_PRINT_API("init_remote(c=%p, as=%p, arg=%p)\n", cursor, as, arg);
+
+ // API docs at http://www.nongnu.org/libunwind/docs.html say we should
+ // handle a local address space but we're not doing the "remote" unwinding
+ // with local process accessors so punt on that.
+
+ if(as->type != UNW_REMOTE)
+ {
+ ABORT("unw_init_remote was passed a non-remote address space");
+ return UNW_EINVAL;
+ }
+
+ unw_accessors_t* acc = unw_get_accessors(as);
+ if(!acc) {
+ ABORT("unw_get_accessors returned NULL");
+ return UNW_EINVAL;
+ }
+
+ unw_addr_space_remote* remote = (unw_addr_space_remote*)as;
+
+ // use "placement new" to allocate UnwindCursor in the cursor buffer
+ // It isn't really necessary to use placement new in the remote API but we'll stay consistent
+ // with the rest of the code here.
+ switch ( remote->ras->getTargetArch() ) {
+ case UNW_TARGET_I386:
+ {
+ Registers_x86 *r = new Registers_x86;
+ OtherAddressSpace<Pointer32<LittleEndian> > *addrSpace = new OtherAddressSpace<Pointer32<LittleEndian> >(as, arg);
+ getRemoteContext (remote->ras, *r, arg);
+ unw_context_t *context = (unw_context_t*) r;
+ new ((void*)cursor) RemoteUnwindCursor<OtherAddressSpace<Pointer32<LittleEndian> >, Registers_x86>(*addrSpace, context, arg);
+ break;
+ }
+ break;
+ case UNW_TARGET_X86_64:
+ {
+ Registers_x86_64 *r = new Registers_x86_64;
+ OtherAddressSpace<Pointer64<LittleEndian> > *addrSpace = new OtherAddressSpace<Pointer64<LittleEndian> >(as, arg);
+ getRemoteContext (remote->ras, *r, arg);
+ unw_context_t *context = (unw_context_t*) r;
+ new ((void*)cursor) RemoteUnwindCursor<OtherAddressSpace<Pointer64<LittleEndian> >, Registers_x86_64>(*addrSpace, context, arg);
+ break;
+ }
+
+ case UNW_TARGET_PPC:
+ ABORT("ppc not supported for remote unwinds");
+ break;
+
+ case UNW_TARGET_ARM:
+ ABORT("arm not supported for remote unwinds");
+ break;
+
+ default:
+ return UNW_EUNSPEC;
+ }
+
+ AbstractRemoteUnwindCursor* co = (AbstractRemoteUnwindCursor*)cursor;
+ co->setRemoteContext(arg);
+
+ return UNW_ESUCCESS;
+}
+
+// The documentation disagrees about whether or not this returns a pointer. Now it does.
+EXPORT unw_accessors_t* unw_get_accessors(unw_addr_space_t as)
+{
+ if(as->type != UNW_REMOTE)
+ {
+ ABORT("unw_get_accessors was passed a non-remote address space");
+ return NULL;
+ }
+ unw_addr_space_remote* remote = (unw_addr_space_remote*)as;
+
+ if(remote->type != UNW_REMOTE)
+ return NULL;
+
+ return remote->ras->getAccessors();
+}
+
+EXPORT unw_addr_space_t unw_create_addr_space(unw_accessors_t *ap, unw_targettype_t targarch)
+{
+ unw_addr_space_remote* remote = (unw_addr_space_remote*)malloc(sizeof(unw_addr_space_remote));
+ remote->type = UNW_REMOTE;
+ remote->ras = new RemoteProcInfo(ap, targarch);
+ return (unw_addr_space_t)remote;
+}
+
+EXPORT void unw_flush_caches(unw_addr_space_t as)
+{
+ if(as->type != UNW_REMOTE)
+ {
+ ABORT("unw_flush_caches was passed a non-remote address space");
+ return;
+ }
+ unw_addr_space_remote* remote = (unw_addr_space_remote*)as;
+ remote->ras->flushAllCaches();
+
+ return;
+}
+
+EXPORT void unw_image_was_unloaded (unw_addr_space_t as, unw_word_t mh)
+{
+ if(as->type != UNW_REMOTE)
+ {
+ ABORT("unw_image_was_unloaded was passed a non-remote address space");
+ return;
+ }
+ unw_addr_space_remote* remote = (unw_addr_space_remote*)as;
+ remote->ras->flushCacheByMachHeader(mh);
+
+ return;
+}
+
+
+EXPORT int unw_set_caching_policy(unw_addr_space_t as, unw_caching_policy_t policy)
+{
+ if(as->type != UNW_REMOTE)
+ {
+ ABORT("unw_set_caching_policy was passed a non-remote address space");
+ return UNW_EINVAL;
+ }
+ unw_addr_space_remote* remote = (unw_addr_space_remote*)as;
+ return remote->ras->setCachingPolicy(policy);
+}
+
+EXPORT unw_addr_space_t unw_local_addr_space = (unw_addr_space_t)&sThisAddressSpace;
+
+///
+/// delete an address_space object
+///
+EXPORT void unw_destroy_addr_space(unw_addr_space_t asp)
+{
+ if(asp->type != UNW_REMOTE) {
+ ABORT("unw_destroy_addr_space was passed a non-remote address space");
+ return;
+ }
+
+ unw_addr_space_remote* remote = (unw_addr_space_remote*)asp;
+ delete remote->ras;
+}
+
+EXPORT void unw_set_logging_level(unw_addr_space_t as, FILE *f, unw_log_level_t level)
+{
+ if (as->type != UNW_REMOTE) {
+ ABORT("unw_set_logging_level was passed a non-remote address space");
+ return;
+ }
+
+ unw_addr_space_remote* remote = (unw_addr_space_remote*)as;
+ return remote->ras->setLoggingLevel(f, level);
+}
+
+
+EXPORT int unw_end_of_prologue_setup(unw_cursor_t* cursor, unw_word_t start, unw_word_t end, unw_word_t *endofprologue)
+{
+ AbstractRemoteUnwindCursor* co = (AbstractRemoteUnwindCursor*)cursor;
+ if (!co->remoteUnwindCursor())
+ ABORT("unw_end_of_prologue_setup called with a non-remote unwind cursor.");
+
+ return co->endOfPrologueInsns (start, end, endofprologue);
+}
+
+
+#endif // SUPPORT_REMOTE_UNWINDING
+
+#pragma mark Dynamic unwinding API
+
+#if !FOR_DYLD
+///
+/// SPI: walks cached dwarf entries
+///
+EXPORT void unw_iterate_dwarf_unwind_cache(void (*func)(unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh))
+{
+ DEBUG_PRINT_API("unw_iterate_dwarf_unwind_cache(func=%p)\n", func);
+ DwarfFDECache<LocalAddressSpace>::iterateCacheEntries(func);
+}
+#endif // !FOR_DYLD
+
+#if !FOR_DYLD
+//
+// IPI: for __register_frame()
+//
+void _unw_add_dynamic_fde(unw_word_t fde)
+{
+ CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo;
+ CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo;
+ const char* message = CFI_Parser<LocalAddressSpace>::decodeFDE(sThisAddressSpace, (LocalAddressSpace::pint_t)fde, & fdeInfo, &cieInfo);
+ if ( message == NULL ) {
+ // dynamically registered FDEs don't have a mach_header group they are in. Use fde as mh_group
+ unw_word_t mh_group = fdeInfo.fdeStart;
+ DwarfFDECache<LocalAddressSpace>::add(mh_group, fdeInfo.pcStart, fdeInfo.pcEnd, fdeInfo.fdeStart);
+ }
+ else {
+ DEBUG_MESSAGE("_unw_add_dynamic_fde: bad fde: %s", message);
+ }
+}
+
+//
+// IPI: for __deregister_frame()
+//
+void _unw_remove_dynamic_fde(unw_word_t fde)
+{
+ // fde is own mh_group
+ DwarfFDECache<LocalAddressSpace>::removeAllIn(fde);
+}
+#endif
+
+}; // namespace lldb_private
+
+#endif // __ppc__ || __i386__ || __x86_64__
diff --git a/lldb/source/Plugins/Process/Utility/libunwind/src/unw_getcontext.s b/lldb/source/Plugins/Process/Utility/libunwind/src/unw_getcontext.s
new file mode 100644
index 00000000000..8d3a451fd92
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/libunwind/src/unw_getcontext.s
@@ -0,0 +1,229 @@
+
+#if __i386__ || __x86_64__ || __ppc__
+
+ .text
+ .globl _unw_getcontext
+_unw_getcontext:
+
+#endif // __i386__ || __x86_64__ || __ppc__
+
+
+#if __i386__
+
+#
+# extern int unw_getcontext(unw_context_t* thread_state)
+#
+# On entry:
+# + +
+# +-----------------------+
+# + thread_state pointer +
+# +-----------------------+
+# + return address +
+# +-----------------------+ <-- SP
+# + +
+#
+ push %eax
+ movl 8(%esp), %eax
+ movl %ebx, 4(%eax)
+ movl %ecx, 8(%eax)
+ movl %edx, 12(%eax)
+ movl %edi, 16(%eax)
+ movl %esi, 20(%eax)
+ movl %ebp, 24(%eax)
+ movl %esp, %edx
+ addl $8, %edx
+ movl %edx, 28(%eax) # store what sp was at call site as esp
+ # skip ss
+ # skip eflags
+ movl 4(%esp), %edx
+ movl %edx, 40(%eax) # store return address as eip
+ # skip cs
+ # skip ds
+ # skip es
+ # skip fs
+ # skip gs
+ movl (%esp), %edx
+ movl %edx, (%eax) # store original eax
+ popl %eax
+ xorl %eax, %eax # return UNW_ESUCCESS
+ ret
+
+#elif __x86_64__
+
+#
+# extern int unw_getcontext(unw_context_t* thread_state)
+#
+# On entry:
+# thread_state pointer is in rdi
+#
+ movq %rax, (%rdi)
+ movq %rbx, 8(%rdi)
+ movq %rcx, 16(%rdi)
+ movq %rdx, 24(%rdi)
+ movq %rdi, 32(%rdi)
+ movq %rsi, 40(%rdi)
+ movq %rbp, 48(%rdi)
+ movq %rsp, 56(%rdi)
+ addq $8, 56(%rdi)
+ movq %r8, 64(%rdi)
+ movq %r9, 72(%rdi)
+ movq %r10, 80(%rdi)
+ movq %r11, 88(%rdi)
+ movq %r12, 96(%rdi)
+ movq %r13,104(%rdi)
+ movq %r14,112(%rdi)
+ movq %r15,120(%rdi)
+ movq (%rsp),%rsi
+ movq %rsi,128(%rdi) # store return address as rip
+ # skip rflags
+ # skip cs
+ # skip fs
+ # skip gs
+ xorl %eax, %eax # return UNW_ESUCCESS
+ ret
+
+#elif __ppc__
+
+;
+; extern int unw_getcontext(unw_context_t* thread_state)
+;
+; On entry:
+; thread_state pointer is in r3
+;
+ stw r0, 8(r3)
+ mflr r0
+ stw r0, 0(r3) ; store lr as ssr0
+ stw r1, 12(r3)
+ stw r2, 16(r3)
+ stw r3, 20(r3)
+ stw r4, 24(r3)
+ stw r5, 28(r3)
+ stw r6, 32(r3)
+ stw r7, 36(r3)
+ stw r8, 40(r3)
+ stw r9, 44(r3)
+ stw r10, 48(r3)
+ stw r11, 52(r3)
+ stw r12, 56(r3)
+ stw r13, 60(r3)
+ stw r14, 64(r3)
+ stw r15, 68(r3)
+ stw r16, 72(r3)
+ stw r17, 76(r3)
+ stw r18, 80(r3)
+ stw r19, 84(r3)
+ stw r20, 88(r3)
+ stw r21, 92(r3)
+ stw r22, 96(r3)
+ stw r23,100(r3)
+ stw r24,104(r3)
+ stw r25,108(r3)
+ stw r26,112(r3)
+ stw r27,116(r3)
+ stw r28,120(r3)
+ stw r29,124(r3)
+ stw r30,128(r3)
+ stw r31,132(r3)
+
+ ; save VRSave register
+ mfspr r0,256
+ stw r0,156(r3)
+ ; save CR registers
+ mfcr r0
+ stw r0,136(r3)
+ ; save CTR register
+ mfctr r0
+ stw r0,148(r3)
+
+ ; save float registers
+ stfd f0, 160(r3)
+ stfd f1, 168(r3)
+ stfd f2, 176(r3)
+ stfd f3, 184(r3)
+ stfd f4, 192(r3)
+ stfd f5, 200(r3)
+ stfd f6, 208(r3)
+ stfd f7, 216(r3)
+ stfd f8, 224(r3)
+ stfd f9, 232(r3)
+ stfd f10,240(r3)
+ stfd f11,248(r3)
+ stfd f12,256(r3)
+ stfd f13,264(r3)
+ stfd f14,272(r3)
+ stfd f15,280(r3)
+ stfd f16,288(r3)
+ stfd f17,296(r3)
+ stfd f18,304(r3)
+ stfd f19,312(r3)
+ stfd f20,320(r3)
+ stfd f21,328(r3)
+ stfd f22,336(r3)
+ stfd f23,344(r3)
+ stfd f24,352(r3)
+ stfd f25,360(r3)
+ stfd f26,368(r3)
+ stfd f27,376(r3)
+ stfd f28,384(r3)
+ stfd f29,392(r3)
+ stfd f30,400(r3)
+ stfd f31,408(r3)
+
+
+ ; save vector registers
+
+ subi r4,r1,16
+ rlwinm r4,r4,0,0,27 ; mask low 4-bits
+ ; r4 is now a 16-byte aligned pointer into the red zone
+
+#define SAVE_VECTOR_UNALIGNED(_vec, _offset) \
+ stvx _vec,0,r4 @\
+ lwz r5, 0(r4) @\
+ stw r5, _offset(r3) @\
+ lwz r5, 4(r4) @\
+ stw r5, _offset+4(r3) @\
+ lwz r5, 8(r4) @\
+ stw r5, _offset+8(r3) @\
+ lwz r5, 12(r4) @\
+ stw r5, _offset+12(r3)
+
+ SAVE_VECTOR_UNALIGNED( v0, 424+0x000)
+ SAVE_VECTOR_UNALIGNED( v1, 424+0x010)
+ SAVE_VECTOR_UNALIGNED( v2, 424+0x020)
+ SAVE_VECTOR_UNALIGNED( v3, 424+0x030)
+ SAVE_VECTOR_UNALIGNED( v4, 424+0x040)
+ SAVE_VECTOR_UNALIGNED( v5, 424+0x050)
+ SAVE_VECTOR_UNALIGNED( v6, 424+0x060)
+ SAVE_VECTOR_UNALIGNED( v7, 424+0x070)
+ SAVE_VECTOR_UNALIGNED( v8, 424+0x080)
+ SAVE_VECTOR_UNALIGNED( v9, 424+0x090)
+ SAVE_VECTOR_UNALIGNED(v10, 424+0x0A0)
+ SAVE_VECTOR_UNALIGNED(v11, 424+0x0B0)
+ SAVE_VECTOR_UNALIGNED(v12, 424+0x0C0)
+ SAVE_VECTOR_UNALIGNED(v13, 424+0x0D0)
+ SAVE_VECTOR_UNALIGNED(v14, 424+0x0E0)
+ SAVE_VECTOR_UNALIGNED(v15, 424+0x0F0)
+ SAVE_VECTOR_UNALIGNED(v16, 424+0x100)
+ SAVE_VECTOR_UNALIGNED(v17, 424+0x110)
+ SAVE_VECTOR_UNALIGNED(v18, 424+0x120)
+ SAVE_VECTOR_UNALIGNED(v19, 424+0x130)
+ SAVE_VECTOR_UNALIGNED(v20, 424+0x140)
+ SAVE_VECTOR_UNALIGNED(v21, 424+0x150)
+ SAVE_VECTOR_UNALIGNED(v22, 424+0x160)
+ SAVE_VECTOR_UNALIGNED(v23, 424+0x170)
+ SAVE_VECTOR_UNALIGNED(v24, 424+0x180)
+ SAVE_VECTOR_UNALIGNED(v25, 424+0x190)
+ SAVE_VECTOR_UNALIGNED(v26, 424+0x1A0)
+ SAVE_VECTOR_UNALIGNED(v27, 424+0x1B0)
+ SAVE_VECTOR_UNALIGNED(v28, 424+0x1C0)
+ SAVE_VECTOR_UNALIGNED(v29, 424+0x1D0)
+ SAVE_VECTOR_UNALIGNED(v30, 424+0x1E0)
+ SAVE_VECTOR_UNALIGNED(v31, 424+0x1F0)
+
+ li r3, 0 ; return UNW_ESUCCESS
+ blr
+
+
+
+#endif
+
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
new file mode 100644
index 00000000000..cac2101e4c0
--- /dev/null
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
@@ -0,0 +1,813 @@
+//===-- GDBRemoteCommunication.cpp ------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#include "GDBRemoteCommunication.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+#include "lldb/Core/Args.h"
+#include "lldb/Core/ConnectionFileDescriptor.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/State.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Host/TimeValue.h"
+
+// Project includes
+#include "StringExtractorGDBRemote.h"
+#include "ProcessGDBRemote.h"
+#include "ProcessGDBRemoteLog.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// GDBRemoteCommunication constructor
+//----------------------------------------------------------------------
+GDBRemoteCommunication::GDBRemoteCommunication() :
+ Communication("gdb-remote.packets"),
+ m_send_acks (true),
+ m_rx_packet_listener ("gdbremote.rx_packet"),
+ m_sequence_mutex (Mutex::eMutexTypeRecursive),
+ m_is_running (false),
+ m_async_mutex (Mutex::eMutexTypeRecursive),
+ m_async_packet_predicate (false),
+ m_async_packet (),
+ m_async_response (),
+ m_async_timeout (UINT32_MAX),
+ m_async_signal (-1),
+ m_arch(),
+ m_os(),
+ m_vendor(),
+ m_byte_order(eByteOrderHost),
+ m_pointer_byte_size(0)
+{
+ m_rx_packet_listener.StartListeningForEvents(this,
+ Communication::eBroadcastBitPacketAvailable |
+ Communication::eBroadcastBitReadThreadDidExit);
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+GDBRemoteCommunication::~GDBRemoteCommunication()
+{
+ m_rx_packet_listener.StopListeningForEvents(this,
+ Communication::eBroadcastBitPacketAvailable |
+ Communication::eBroadcastBitReadThreadDidExit);
+ if (IsConnected())
+ {
+ StopReadThread();
+ Disconnect();
+ }
+}
+
+
+char
+GDBRemoteCommunication::CalculcateChecksum (const char *payload, size_t payload_length)
+{
+ int checksum = 0;
+
+ // We only need to compute the checksum if we are sending acks
+ if (m_send_acks)
+ {
+ for (int i = 0; i < payload_length; ++i)
+ checksum += payload[i];
+ }
+ return checksum & 255;
+}
+
+size_t
+GDBRemoteCommunication::SendAck (char ack_char)
+{
+ Mutex::Locker locker(m_sequence_mutex);
+ ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS, "send packet: %c", ack_char);
+ ConnectionStatus status = eConnectionStatusSuccess;
+ return Write (&ack_char, 1, status, NULL) == 1;
+}
+
+size_t
+GDBRemoteCommunication::SendPacketAndWaitForResponse
+(
+ const char *payload,
+ StringExtractorGDBRemote &response,
+ uint32_t timeout_seconds,
+ bool send_async
+)
+{
+ return SendPacketAndWaitForResponse (payload,
+ ::strlen (payload),
+ response,
+ timeout_seconds,
+ send_async);
+}
+
+size_t
+GDBRemoteCommunication::SendPacketAndWaitForResponse
+(
+ const char *payload,
+ size_t payload_length,
+ StringExtractorGDBRemote &response,
+ uint32_t timeout_seconds,
+ bool send_async
+)
+{
+ Mutex::Locker locker;
+ TimeValue timeout_time;
+ timeout_time = TimeValue::Now();
+ timeout_time.OffsetWithSeconds (timeout_seconds);
+
+ if (locker.TryLock (m_sequence_mutex.GetMutex()))
+ {
+ if (SendPacketNoLock (payload, strlen(payload)))
+ return WaitForPacketNoLock (response, &timeout_time);
+ }
+ else
+ {
+ if (send_async)
+ {
+ Mutex::Locker async_locker (m_async_mutex);
+ m_async_packet.assign(payload, payload_length);
+ m_async_timeout = timeout_seconds;
+ m_async_packet_predicate.SetValue (true, eBroadcastNever);
+
+ bool timed_out = false;
+ if (SendInterrupt(1, &timed_out))
+ {
+ if (m_async_packet_predicate.WaitForValueEqualTo (false, &timeout_time, &timed_out))
+ {
+ response = m_async_response;
+ return response.GetStringRef().size();
+ }
+ }
+// if (timed_out)
+// m_error.SetErrorString("Timeout.");
+// else
+// m_error.SetErrorString("Unknown error.");
+ }
+ else
+ {
+// m_error.SetErrorString("Sequence mutex is locked.");
+ }
+ }
+ return 0;
+}
+
+//template<typename _Tp>
+//class ScopedValueChanger
+//{
+//public:
+// // Take a value reference and the value to assing it to when this class
+// // instance goes out of scope.
+// ScopedValueChanger (_Tp &value_ref, _Tp value) :
+// m_value_ref (value_ref),
+// m_value (value)
+// {
+// }
+//
+// // This object is going out of scope, change the value pointed to by
+// // m_value_ref to the value we got during construction which was stored in
+// // m_value;
+// ~ScopedValueChanger ()
+// {
+// m_value_ref = m_value;
+// }
+//protected:
+// _Tp &m_value_ref; // A reference to the value we wil change when this object destructs
+// _Tp m_value; // The value to assign to m_value_ref when this goes out of scope.
+//};
+
+StateType
+GDBRemoteCommunication::SendContinuePacketAndWaitForResponse
+(
+ ProcessGDBRemote *process,
+ const char *payload,
+ size_t packet_length,
+ StringExtractorGDBRemote &response
+)
+{
+ Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS);
+ if (log)
+ log->Printf ("GDBRemoteCommunication::%s ()", __FUNCTION__);
+
+ Mutex::Locker locker(m_sequence_mutex);
+ m_is_running.SetValue (true, eBroadcastNever);
+
+// ScopedValueChanger<bool> restore_running_to_false (m_is_running, false);
+ StateType state = eStateRunning;
+
+ if (SendPacket(payload, packet_length) == 0)
+ state = eStateInvalid;
+
+ while (state == eStateRunning)
+ {
+ if (log)
+ log->Printf ("GDBRemoteCommunication::%s () WaitForPacket(...)", __FUNCTION__);
+
+ if (WaitForPacket (response, (TimeValue*)NULL))
+ {
+ if (response.Empty())
+ state = eStateInvalid;
+ else
+ {
+ const char stop_type = response.GetChar();
+ if (log)
+ log->Printf ("GDBRemoteCommunication::%s () got '%c' packet", __FUNCTION__, stop_type);
+ switch (stop_type)
+ {
+ case 'T':
+ case 'S':
+ if (m_async_signal != -1)
+ {
+ // Save off the async signal we are supposed to send
+ const int async_signal = m_async_signal;
+ // Clear the async signal member so we don't end up
+ // sending the signal multiple times...
+ m_async_signal = -1;
+ // Check which signal we stopped with
+ uint8_t signo = response.GetHexU8(255);
+ if (signo == async_signal)
+ {
+ // We already stopped with a signal that we wanted
+ // to stop with, so we are done
+ response.SetFilePos (0);
+ }
+ else
+ {
+ // We stopped with a different signal that the one
+ // we wanted to stop with, so now we must resume
+ // with the signal we want
+ char signal_packet[32];
+ int signal_packet_len = 0;
+ signal_packet_len = ::snprintf (signal_packet,
+ sizeof (signal_packet),
+ "C%2.2x",
+ async_signal);
+
+ if (SendPacket(signal_packet, signal_packet_len) == 0)
+ {
+ state = eStateInvalid;
+ break;
+ }
+ else
+ continue;
+ }
+ }
+ else if (m_async_packet_predicate.GetValue())
+ {
+ // We are supposed to send an asynchronous packet while
+ // we are running.
+ m_async_response.Clear();
+ if (!m_async_packet.empty())
+ {
+ SendPacketAndWaitForResponse (m_async_packet.data(),
+ m_async_packet.size(),
+ m_async_response,
+ m_async_timeout,
+ false);
+ }
+ // Let the other thread that was trying to send the async
+ // packet know that the packet has been sent.
+ m_async_packet_predicate.SetValue(false, eBroadcastAlways);
+
+ // Continue again
+ if (SendPacket("c", 1) == 0)
+ {
+ state = eStateInvalid;
+ break;
+ }
+ else
+ continue;
+ }
+ // Stop with signal and thread info
+ state = eStateStopped;
+ break;
+
+ case 'W':
+ // process exited
+ state = eStateExited;
+ break;
+
+ case 'O':
+ // STDOUT
+ {
+ std::string inferior_stdout;
+ inferior_stdout.reserve(response.GetBytesLeft () / 2);
+ char ch;
+ while ((ch = response.GetHexU8()) != '\0')
+ inferior_stdout.append(1, ch);
+ process->AppendSTDOUT (inferior_stdout.c_str(), inferior_stdout.size());
+ }
+ break;
+
+ case 'E':
+ // ERROR
+ state = eStateInvalid;
+ break;
+
+ default:
+ if (log)
+ log->Printf ("GDBRemoteCommunication::%s () got unrecognized async packet: '%s'", __FUNCTION__, stop_type);
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf ("GDBRemoteCommunication::%s () WaitForPacket(...) => false", __FUNCTION__);
+ state = eStateInvalid;
+ }
+ }
+ if (log)
+ log->Printf ("GDBRemoteCommunication::%s () => %s", __FUNCTION__, StateAsCString(state));
+ response.SetFilePos(0);
+ m_is_running.SetValue (false, eBroadcastOnChange);
+ return state;
+}
+
+size_t
+GDBRemoteCommunication::SendPacket (const char *payload)
+{
+ Mutex::Locker locker(m_sequence_mutex);
+ return SendPacketNoLock (payload, ::strlen (payload));
+}
+
+size_t
+GDBRemoteCommunication::SendPacket (const char *payload, size_t payload_length)
+{
+ Mutex::Locker locker(m_sequence_mutex);
+ return SendPacketNoLock (payload, payload_length);
+}
+
+size_t
+GDBRemoteCommunication::SendPacketNoLock (const char *payload, size_t payload_length)
+{
+ if (IsConnected())
+ {
+ StreamString packet(0, 4, eByteOrderBig);
+
+ packet.PutChar('$');
+ packet.Write (payload, payload_length);
+ packet.PutChar('#');
+ packet.PutHex8(CalculcateChecksum (payload, payload_length));
+
+ ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS, "send packet: %s", packet.GetData());
+ ConnectionStatus status = eConnectionStatusSuccess;
+ size_t bytes_written = Write (packet.GetData(), packet.GetSize(), status, NULL);
+ if (bytes_written == packet.GetSize())
+ {
+ if (m_send_acks)
+ GetAck (1) == '+';
+ }
+ return bytes_written;
+ }
+ //m_error.SetErrorString("Not connected.");
+ return 0;
+}
+
+char
+GDBRemoteCommunication::GetAck (uint32_t timeout_seconds)
+{
+ StringExtractorGDBRemote response;
+ if (WaitForPacket (response, timeout_seconds) == 1)
+ return response.GetChar();
+ return 0;
+}
+
+bool
+GDBRemoteCommunication::GetSequenceMutex (Mutex::Locker& locker)
+{
+ return locker.TryLock (m_sequence_mutex.GetMutex());
+}
+
+bool
+GDBRemoteCommunication::SendAsyncSignal (int signo)
+{
+ m_async_signal = signo;
+ bool timed_out = false;
+ if (SendInterrupt(1, &timed_out))
+ return true;
+ m_async_signal = -1;
+ return false;
+}
+
+bool
+GDBRemoteCommunication::SendInterrupt (uint32_t seconds_to_wait_for_stop, bool *timed_out)
+{
+ if (timed_out)
+ *timed_out = false;
+
+ if (IsConnected() && IsRunning())
+ {
+ // Only send an interrupt if our debugserver is running...
+ if (m_sequence_mutex.TryLock() != 0)
+ {
+ // Someone has the mutex locked waiting for a response or for the
+ // inferior to stop, so send the interrupt on the down low...
+ char ctrl_c = '\x03';
+ ConnectionStatus status = eConnectionStatusSuccess;
+ TimeValue timeout;
+ if (seconds_to_wait_for_stop)
+ {
+ timeout = TimeValue::Now();
+ timeout.OffsetWithSeconds (seconds_to_wait_for_stop);
+ }
+ ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS, "send packet: \\x03");
+ if (Write (&ctrl_c, 1, status, NULL) > 0)
+ {
+ if (seconds_to_wait_for_stop)
+ m_is_running.WaitForValueEqualTo (false, &timeout, timed_out);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+size_t
+GDBRemoteCommunication::WaitForPacket (StringExtractorGDBRemote &response, uint32_t timeout_seconds)
+{
+ TimeValue timeout_time;
+ timeout_time = TimeValue::Now();
+ timeout_time.OffsetWithSeconds (timeout_seconds);
+ return WaitForPacketNoLock (response, &timeout_time);
+}
+
+size_t
+GDBRemoteCommunication::WaitForPacket (StringExtractorGDBRemote &response, TimeValue* timeout_time_ptr)
+{
+ Mutex::Locker locker(m_sequence_mutex);
+ return WaitForPacketNoLock (response, timeout_time_ptr);
+}
+
+size_t
+GDBRemoteCommunication::WaitForPacketNoLock (StringExtractorGDBRemote &response, TimeValue* timeout_time_ptr)
+{
+ bool checksum_error = false;
+ response.Clear ();
+
+ EventSP event_sp;
+
+ if (m_rx_packet_listener.WaitForEvent (timeout_time_ptr, event_sp))
+ {
+ const uint32_t event_type = event_sp->GetType();
+ if (event_type | Communication::eBroadcastBitPacketAvailable)
+ {
+ const EventDataBytes *event_bytes = EventDataBytes::GetEventDataFromEvent(event_sp.get());
+ if (event_bytes)
+ {
+ const char * packet_data = (const char *)event_bytes->GetBytes();
+ ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS, "read packet: %s", packet_data);
+ const size_t packet_size = event_bytes->GetByteSize();
+ if (packet_data && packet_size > 0)
+ {
+ std::string &response_str = response.GetStringRef();
+ if (packet_data[0] == '$')
+ {
+ assert (packet_size >= 4); // Must have at least '$#CC' where CC is checksum
+ assert (packet_data[packet_size-3] == '#');
+ assert (::isxdigit (packet_data[packet_size-2])); // Must be checksum hex byte
+ assert (::isxdigit (packet_data[packet_size-1])); // Must be checksum hex byte
+ response_str.assign (packet_data + 1, packet_size - 4);
+ if (m_send_acks)
+ {
+ char packet_checksum = strtol (&packet_data[packet_size-2], NULL, 16);
+ char actual_checksum = CalculcateChecksum (response_str.data(), response_str.size());
+ checksum_error = packet_checksum != actual_checksum;
+ // Send the ack or nack if needed
+ if (checksum_error)
+ SendAck('-');
+ else
+ SendAck('+');
+ }
+ }
+ else
+ {
+ response_str.assign (packet_data, packet_size);
+ }
+ return response_str.size();
+ }
+ }
+ }
+ else if (Communication::eBroadcastBitReadThreadDidExit)
+ {
+ // Our read thread exited on us so just fall through and return zero...
+ }
+ }
+ return 0;
+}
+
+void
+GDBRemoteCommunication::AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast)
+{
+ // Put the packet data into the buffer in a thread safe fashion
+ Mutex::Locker locker(m_bytes_mutex);
+ m_bytes.append ((const char *)src, src_len);
+
+ // Parse up the packets into gdb remote packets
+ while (!m_bytes.empty())
+ {
+ // end_idx must be one past the last valid packet byte. Start
+ // it off with an invalid value that is the same as the current
+ // index.
+ size_t end_idx = 0;
+
+ switch (m_bytes[0])
+ {
+ case '+': // Look for ack
+ case '-': // Look for cancel
+ case '\x03': // ^C to halt target
+ end_idx = 1; // The command is one byte long...
+ break;
+
+ case '$':
+ // Look for a standard gdb packet?
+ end_idx = m_bytes.find('#');
+ if (end_idx != std::string::npos)
+ {
+ if (end_idx + 2 < m_bytes.size())
+ {
+ end_idx += 3;
+ }
+ else
+ {
+ // Checksum bytes aren't all here yet
+ end_idx = std::string::npos;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (end_idx == std::string::npos)
+ {
+ //ProcessGDBRemoteLog::LogIf (GDBR_LOG_PACKETS | GDBR_LOG_VERBOSE, "GDBRemoteCommunication::%s packet not yet complete: '%s'",__FUNCTION__, m_bytes.c_str());
+ return;
+ }
+ else if (end_idx > 0)
+ {
+ // We have a valid packet...
+ assert (end_idx <= m_bytes.size());
+ std::auto_ptr<EventDataBytes> event_bytes_ap (new EventDataBytes (&m_bytes[0], end_idx));
+ ProcessGDBRemoteLog::LogIf (GDBR_LOG_COMM, "got full packet: %s", event_bytes_ap->GetBytes());
+ BroadcastEvent (eBroadcastBitPacketAvailable, event_bytes_ap.release());
+ m_bytes.erase(0, end_idx);
+ }
+ else
+ {
+ assert (1 <= m_bytes.size());
+ ProcessGDBRemoteLog::LogIf (GDBR_LOG_COMM, "GDBRemoteCommunication::%s tossing junk byte at %c",__FUNCTION__, m_bytes[0]);
+ m_bytes.erase(0, 1);
+ }
+ }
+}
+
+lldb::pid_t
+GDBRemoteCommunication::GetCurrentProcessID (uint32_t timeout_seconds)
+{
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qC", strlen("qC"), response, timeout_seconds, false))
+ {
+ if (response.GetChar() == 'Q')
+ if (response.GetChar() == 'C')
+ return response.GetHexMaxU32 (false, LLDB_INVALID_PROCESS_ID);
+ }
+ return LLDB_INVALID_PROCESS_ID;
+}
+
+bool
+GDBRemoteCommunication::GetLaunchSuccess (uint32_t timeout_seconds, std::string &error_str)
+{
+ error_str.clear();
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse("qLaunchSuccess", strlen("qLaunchSuccess"), response, timeout_seconds, false))
+ {
+ if (response.IsOKPacket())
+ return true;
+ if (response.GetChar() == 'E')
+ {
+ // A string the describes what failed when launching...
+ error_str = response.GetStringRef().substr(1);
+ }
+ else
+ {
+ error_str.assign ("unknown error occurred launching process");
+ }
+ }
+ else
+ {
+ error_str.assign ("failed to send the qLaunchSuccess packet");
+ }
+ return false;
+}
+
+int
+GDBRemoteCommunication::SendArgumentsPacket (char const *argv[], uint32_t timeout_seconds)
+{
+ if (argv && argv[0])
+ {
+ StreamString packet;
+ packet.PutChar('A');
+ const char *arg;
+ for (uint32_t i = 0; (arg = argv[i]) != NULL; ++i)
+ {
+ const int arg_len = strlen(arg);
+ if (i > 0)
+ packet.PutChar(',');
+ packet.Printf("%i,%i,", arg_len * 2, i);
+ packet.PutBytesAsRawHex8(arg, arg_len, eByteOrderHost, eByteOrderHost);
+ }
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, timeout_seconds, false))
+ {
+ if (response.IsOKPacket())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ }
+ return -1;
+}
+
+int
+GDBRemoteCommunication::SendEnvironmentPacket (char const *name_equal_value, uint32_t timeout_seconds)
+{
+ if (name_equal_value && name_equal_value[0])
+ {
+ StreamString packet;
+ packet.Printf("QEnvironment:%s", name_equal_value);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, timeout_seconds, false))
+ {
+ if (response.IsOKPacket())
+ return 0;
+ uint8_t error = response.GetError();
+ if (error)
+ return error;
+ }
+ }
+ return -1;
+}
+
+bool
+GDBRemoteCommunication::GetHostInfo (uint32_t timeout_seconds)
+{
+ m_arch.Clear();
+ m_os.Clear();
+ m_vendor.Clear();
+ m_byte_order = eByteOrderHost;
+ m_pointer_byte_size = 0;
+
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse ("qHostInfo", response, timeout_seconds, false))
+ {
+ if (response.IsUnsupportedPacket())
+ return false;
+
+
+ std::string name;
+ std::string value;
+ while (response.GetNameColonValue(name, value))
+ {
+ if (name.compare("cputype") == 0)
+ {
+ // exception type in big endian hex
+ m_arch.SetCPUType(Args::StringToUInt32 (value.c_str(), LLDB_INVALID_CPUTYPE, 0));
+ }
+ else if (name.compare("cpusubtype") == 0)
+ {
+ // exception count in big endian hex
+ m_arch.SetCPUSubtype(Args::StringToUInt32 (value.c_str(), 0, 0));
+ }
+ else if (name.compare("ostype") == 0)
+ {
+ // exception data in big endian hex
+ m_os.SetCString(value.c_str());
+ }
+ else if (name.compare("vendor") == 0)
+ {
+ m_vendor.SetCString(value.c_str());
+ }
+ else if (name.compare("endian") == 0)
+ {
+ if (value.compare("little") == 0)
+ m_byte_order = eByteOrderLittle;
+ else if (value.compare("big") == 0)
+ m_byte_order = eByteOrderBig;
+ else if (value.compare("pdp") == 0)
+ m_byte_order = eByteOrderPDP;
+ }
+ else if (name.compare("ptrsize") == 0)
+ {
+ m_pointer_byte_size = Args::StringToUInt32 (value.c_str(), 0, 0);
+ }
+ }
+ }
+ return HostInfoIsValid();
+}
+
+int
+GDBRemoteCommunication::SendAttach
+(
+ lldb::pid_t pid,
+ uint32_t timeout_seconds,
+ StringExtractorGDBRemote& response
+)
+{
+ if (pid != LLDB_INVALID_PROCESS_ID)
+ {
+ StreamString packet;
+ packet.Printf("vAttach;%x", pid);
+
+ if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, timeout_seconds, false))
+ {
+ if (response.IsErrorPacket())
+ return response.GetError();
+ return 0;
+ }
+ }
+ return -1;
+}
+
+const lldb_private::ArchSpec &
+GDBRemoteCommunication::GetHostArchitecture ()
+{
+ if (!HostInfoIsValid ())
+ GetHostInfo (1);
+ return m_arch;
+}
+
+const lldb_private::ConstString &
+GDBRemoteCommunication::GetOSString ()
+{
+ if (!HostInfoIsValid ())
+ GetHostInfo (1);
+ return m_os;
+}
+
+const lldb_private::ConstString &
+GDBRemoteCommunication::GetVendorString()
+{
+ if (!HostInfoIsValid ())
+ GetHostInfo (1);
+ return m_vendor;
+}
+
+lldb::ByteOrder
+GDBRemoteCommunication::GetByteOrder ()
+{
+ if (!HostInfoIsValid ())
+ GetHostInfo (1);
+ return m_byte_order;
+}
+
+uint32_t
+GDBRemoteCommunication::GetAddressByteSize ()
+{
+ if (!HostInfoIsValid ())
+ GetHostInfo (1);
+ return m_pointer_byte_size;
+}
+
+addr_t
+GDBRemoteCommunication::AllocateMemory (size_t size, uint32_t permissions, uint32_t timeout_seconds)
+{
+ char packet[64];
+ ::snprintf (packet, sizeof(packet), "_M%zx,%s%s%s", size,
+ permissions & lldb::ePermissionsReadable ? "r" : "",
+ permissions & lldb::ePermissionsWritable ? "w" : "",
+ permissions & lldb::ePermissionsExecutable ? "x" : "");
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet, response, timeout_seconds, false))
+ {
+ if (!response.IsErrorPacket())
+ return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS);
+ }
+ return LLDB_INVALID_ADDRESS;
+}
+
+bool
+GDBRemoteCommunication::DeallocateMemory (addr_t addr, uint32_t timeout_seconds)
+{
+ char packet[64];
+ snprintf(packet, sizeof(packet), "_m%llx", (uint64_t)addr);
+ StringExtractorGDBRemote response;
+ if (SendPacketAndWaitForResponse (packet, response, timeout_seconds, false))
+ {
+ if (!response.IsOKPacket())
+ return true;
+ }
+ return false;
+}
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
new file mode 100644
index 00000000000..051fa445ff1
--- /dev/null
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
@@ -0,0 +1,270 @@
+//===-- GDBRemoteCommunication.h --------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_GDBRemoteCommunication_h_
+#define liblldb_GDBRemoteCommunication_h_
+
+// C Includes
+// C++ Includes
+#include <list>
+#include <string>
+
+// Other libraries and framework includes
+// Project includes
+#include "lldb/lldb-private.h"
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Core/Communication.h"
+#include "lldb/Core/ConstString.h"
+#include "lldb/Core/Error.h"
+#include "lldb/Core/Listener.h"
+#include "lldb/Host/Mutex.h"
+#include "lldb/Host/Predicate.h"
+
+#include "StringExtractorGDBRemote.h"
+
+class ProcessGDBRemote;
+
+class GDBRemoteCommunication :
+ public lldb_private::Communication
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ GDBRemoteCommunication();
+
+ virtual
+ ~GDBRemoteCommunication();
+
+ size_t
+ SendPacket (const char *payload);
+
+ size_t
+ SendPacket (const char *payload,
+ size_t payload_length);
+
+ size_t
+ SendPacketAndWaitForResponse (const char *send_payload,
+ StringExtractorGDBRemote &response,
+ uint32_t timeout_seconds,
+ bool send_async);
+
+ size_t
+ SendPacketAndWaitForResponse (const char *send_payload,
+ size_t send_length,
+ StringExtractorGDBRemote &response,
+ uint32_t timeout_seconds,
+ bool send_async);
+
+ lldb::StateType
+ SendContinuePacketAndWaitForResponse (ProcessGDBRemote *process,
+ const char *packet_payload,
+ size_t packet_length,
+ StringExtractorGDBRemote &response);
+
+ // Wait for a packet within 'nsec' seconds
+ size_t
+ WaitForPacket (StringExtractorGDBRemote &response,
+ uint32_t nsec);
+
+ // Wait for a packet with an absolute timeout time. If 'timeout' is NULL
+ // wait indefinitely.
+ size_t
+ WaitForPacket (StringExtractorGDBRemote &response,
+ lldb_private::TimeValue* timeout);
+
+ char
+ GetAck (uint32_t timeout_seconds);
+
+ size_t
+ SendAck (char ack_char);
+
+ char
+ CalculcateChecksum (const char *payload,
+ size_t payload_length);
+
+ void
+ SetAckMode (bool enabled)
+ {
+ m_send_acks = enabled;
+ }
+
+ bool
+ SendAsyncSignal (int signo);
+
+ bool
+ SendInterrupt (uint32_t seconds_to_wait_for_stop, bool *timed_out = NULL);
+
+ bool
+ GetSequenceMutex(lldb_private::Mutex::Locker& locker);
+
+ //------------------------------------------------------------------
+ // Communication overrides
+ //------------------------------------------------------------------
+ virtual void
+ AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast);
+
+
+ lldb::pid_t
+ GetCurrentProcessID (uint32_t timeout_seconds);
+
+ bool
+ GetLaunchSuccess (uint32_t timeout_seconds, std::string &error_str);
+
+ //------------------------------------------------------------------
+ /// Sends a GDB remote protocol 'A' packet that delivers program
+ /// arguments to the remote server.
+ ///
+ /// @param[in] argv
+ /// A NULL terminated array of const C strings to use as the
+ /// arguments.
+ ///
+ /// @param[in] timeout_seconds
+ /// The number of seconds to wait for a response from the remote
+ /// server.
+ ///
+ /// @return
+ /// Zero if the response was "OK", a positive value if the
+ /// the response was "Exx" where xx are two hex digits, or
+ /// -1 if the call is unsupported or any other unexpected
+ /// response was received.
+ //------------------------------------------------------------------
+ int
+ SendArgumentsPacket (char const *argv[], uint32_t timeout_seconds);
+
+ //------------------------------------------------------------------
+ /// Sends a "QEnvironment:NAME=VALUE" packet that will build up the
+ /// environment that will get used when launching an application
+ /// in conjunction with the 'A' packet. This function can be called
+ /// multiple times in a row in order to pass on the desired
+ /// environment that the inferior should be launched with.
+ ///
+ /// @param[in] name_equal_value
+ /// A NULL terminated C string that contains a single enironment
+ /// in the format "NAME=VALUE".
+ ///
+ /// @param[in] timeout_seconds
+ /// The number of seconds to wait for a response from the remote
+ /// server.
+ ///
+ /// @return
+ /// Zero if the response was "OK", a positive value if the
+ /// the response was "Exx" where xx are two hex digits, or
+ /// -1 if the call is unsupported or any other unexpected
+ /// response was received.
+ //------------------------------------------------------------------
+ int
+ SendEnvironmentPacket (char const *name_equal_value,
+ uint32_t timeout_seconds);
+
+ //------------------------------------------------------------------
+ /// Sends a "vAttach:PID" where PID is in hex.
+ ///
+ /// @param[in] pid
+ /// A process ID for the remote gdb server to attach to.
+ ///
+ /// @param[in] timeout_seconds
+ /// The number of seconds to wait for a response from the remote
+ /// server.
+ ///
+ /// @param[out] response
+ /// The response received from the gdb server. If the return
+ /// value is zero, \a response will contain a stop reply
+ /// packet.
+ ///
+ /// @return
+ /// Zero if the attach was successful, or an error indicating
+ /// an error code.
+ //------------------------------------------------------------------
+ int
+ SendAttach (lldb::pid_t pid,
+ uint32_t timeout_seconds,
+ StringExtractorGDBRemote& response);
+
+
+ lldb::addr_t
+ AllocateMemory (size_t size, uint32_t permissions, uint32_t timeout_seconds);
+
+ bool
+ DeallocateMemory (lldb::addr_t addr, uint32_t timeout_seconds);
+
+ bool
+ IsRunning() const
+ {
+ return m_is_running.GetValue();
+ }
+
+ bool
+ GetHostInfo (uint32_t timeout_seconds);
+
+ bool
+ HostInfoIsValid () const
+ {
+ return m_pointer_byte_size != 0;
+ }
+
+ const lldb_private::ArchSpec &
+ GetHostArchitecture ();
+
+ const lldb_private::ConstString &
+ GetOSString ();
+
+ const lldb_private::ConstString &
+ GetVendorString();
+
+ lldb::ByteOrder
+ GetByteOrder ();
+
+ uint32_t
+ GetAddressByteSize ();
+
+protected:
+ typedef std::list<std::string> packet_collection;
+
+ size_t
+ SendPacketNoLock (const char *payload,
+ size_t payload_length);
+
+ size_t
+ WaitForPacketNoLock (StringExtractorGDBRemote &response,
+ lldb_private::TimeValue* timeout_time_ptr);
+
+ //------------------------------------------------------------------
+ // Classes that inherit from GDBRemoteCommunication can see and modify these
+ //------------------------------------------------------------------
+ bool m_send_acks;
+ lldb_private::Listener m_rx_packet_listener;
+ lldb_private::Mutex m_sequence_mutex; // Restrict access to sending/receiving packets to a single thread at a time
+ lldb_private::Predicate<bool> m_is_running;
+
+ // If we need to send a packet while the target is running, the m_async_XXX
+ // member variables take care of making this happen.
+ lldb_private::Mutex m_async_mutex;
+ lldb_private::Predicate<bool> m_async_packet_predicate;
+ std::string m_async_packet;
+ StringExtractorGDBRemote m_async_response;
+ uint32_t m_async_timeout;
+ int m_async_signal; // We were asked to deliver a signal to the inferior process.
+
+ lldb_private::ArchSpec m_arch; // Results from the qHostInfo call
+ uint32_t m_cpusubtype; // Results from the qHostInfo call
+ lldb_private::ConstString m_os; // Results from the qHostInfo call
+ lldb_private::ConstString m_vendor; // Results from the qHostInfo call
+ lldb::ByteOrder m_byte_order; // Results from the qHostInfo call
+ uint32_t m_pointer_byte_size; // Results from the qHostInfo call
+
+
+private:
+ //------------------------------------------------------------------
+ // For GDBRemoteCommunication only
+ //------------------------------------------------------------------
+ DISALLOW_COPY_AND_ASSIGN (GDBRemoteCommunication);
+};
+
+#endif // liblldb_GDBRemoteCommunication_h_
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp
new file mode 100644
index 00000000000..a64e74dc963
--- /dev/null
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp
@@ -0,0 +1,508 @@
+//===-- GDBRemoteRegisterContext.cpp ----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "GDBRemoteRegisterContext.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/Scalar.h"
+#include "lldb/Core/StreamString.h"
+// Project includes
+#include "StringExtractorGDBRemote.h"
+#include "ProcessGDBRemote.h"
+#include "ThreadGDBRemote.h"
+#include "ARM_GCC_Registers.h"
+#include "ARM_DWARF_Registers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// GDBRemoteRegisterContext constructor
+//----------------------------------------------------------------------
+GDBRemoteRegisterContext::GDBRemoteRegisterContext
+(
+ ThreadGDBRemote &thread,
+ StackFrame *frame,
+ GDBRemoteDynamicRegisterInfo &reg_info,
+ bool read_all_at_once
+) :
+ RegisterContext (thread, frame),
+ m_reg_info (reg_info),
+ m_reg_valid (),
+ m_reg_data (),
+ m_read_all_at_once (read_all_at_once)
+{
+ // Resize our vector of bools to contain one bool for every register.
+ // We will use these boolean values to know when a register value
+ // is valid in m_reg_data.
+ m_reg_valid.resize (reg_info.GetNumRegisters());
+
+ // Make a heap based buffer that is big enough to store all registers
+ DataBufferSP reg_data_sp(new DataBufferHeap (reg_info.GetRegisterDataByteSize(), 0));
+ m_reg_data.SetData (reg_data_sp);
+
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+GDBRemoteRegisterContext::~GDBRemoteRegisterContext()
+{
+}
+
+ProcessGDBRemote &
+GDBRemoteRegisterContext::GetGDBProcess()
+{
+ return static_cast<ProcessGDBRemote &>(m_thread.GetProcess());
+}
+
+ThreadGDBRemote &
+GDBRemoteRegisterContext::GetGDBThread()
+{
+ return static_cast<ThreadGDBRemote &>(m_thread);
+}
+
+void
+GDBRemoteRegisterContext::Invalidate ()
+{
+ SetAllRegisterValid (false);
+}
+
+void
+GDBRemoteRegisterContext::SetAllRegisterValid (bool b)
+{
+ std::vector<bool>::iterator pos, end = m_reg_valid.end();
+ for (pos = m_reg_valid.begin(); pos != end; ++pos)
+ *pos = b;
+}
+
+size_t
+GDBRemoteRegisterContext::GetRegisterCount ()
+{
+ return m_reg_info.GetNumRegisters ();
+}
+
+const lldb::RegisterInfo *
+GDBRemoteRegisterContext::GetRegisterInfoAtIndex (uint32_t reg)
+{
+ return m_reg_info.GetRegisterInfoAtIndex (reg);
+}
+
+size_t
+GDBRemoteRegisterContext::GetRegisterSetCount ()
+{
+ return m_reg_info.GetNumRegisterSets ();
+}
+
+
+
+const lldb::RegisterSet *
+GDBRemoteRegisterContext::GetRegisterSet (uint32_t reg_set)
+{
+ return m_reg_info.GetRegisterSet (reg_set);
+}
+
+
+
+bool
+GDBRemoteRegisterContext::ReadRegisterValue (uint32_t reg, Scalar &value)
+{
+ // Read the register
+ if (ReadRegisterBytes (reg, m_reg_data))
+ {
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg);
+ uint32_t offset = reg_info->byte_offset;
+ switch (reg_info->encoding)
+ {
+ case eEncodingUint:
+ switch (reg_info->byte_size)
+ {
+ case 1:
+ case 2:
+ case 4:
+ value = m_reg_data.GetMaxU32 (&offset, reg_info->byte_size);
+ return true;
+
+ case 8:
+ value = m_reg_data.GetMaxU64 (&offset, reg_info->byte_size);
+ return true;
+ }
+ break;
+
+ case eEncodingSint:
+ switch (reg_info->byte_size)
+ {
+ case 1:
+ case 2:
+ case 4:
+ value = (int32_t)m_reg_data.GetMaxU32 (&offset, reg_info->byte_size);
+ return true;
+
+ case 8:
+ value = m_reg_data.GetMaxS64 (&offset, reg_info->byte_size);
+ return true;
+ }
+ break;
+
+ case eEncodingIEEE754:
+ switch (reg_info->byte_size)
+ {
+ case sizeof (float):
+ value = m_reg_data.GetFloat (&offset);
+ return true;
+
+ case sizeof (double):
+ value = m_reg_data.GetDouble (&offset);
+ return true;
+
+ case sizeof (long double):
+ value = m_reg_data.GetLongDouble (&offset);
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+
+bool
+GDBRemoteRegisterContext::ReadRegisterBytes (uint32_t reg, DataExtractor &data)
+{
+ GDBRemoteCommunication &gdb_comm = GetGDBProcess().GetGDBRemote();
+// FIXME: This check isn't right because IsRunning checks the Public state, but this
+// is work you need to do - for instance in ShouldStop & friends - before the public
+// state has been changed.
+// if (gdb_comm.IsRunning())
+// return false;
+
+ if (m_reg_valid_stop_id != m_thread.GetProcess().GetStopID())
+ {
+ Invalidate();
+ m_reg_valid_stop_id = m_thread.GetProcess().GetStopID();
+ }
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg);
+ assert (reg_info);
+ if (m_reg_valid[reg] == false)
+ {
+ Mutex::Locker locker;
+ if (gdb_comm.GetSequenceMutex (locker))
+ {
+ if (GetGDBProcess().SetCurrentGDBRemoteThread(m_thread.GetID()))
+ {
+ char packet[32];
+ StringExtractorGDBRemote response;
+ int packet_len;
+ if (m_read_all_at_once)
+ {
+ // Get all registers in one packet
+ packet_len = ::snprintf (packet, sizeof(packet), "g");
+ assert (packet_len < (sizeof(packet) - 1));
+ if (gdb_comm.SendPacketAndWaitForResponse(packet, response, 1, false))
+ {
+ if (response.IsNormalPacket())
+ if (response.GetHexBytes ((void *)m_reg_data.GetDataStart(), m_reg_data.GetByteSize(), '\xcc') == m_reg_data.GetByteSize())
+ SetAllRegisterValid (true);
+ }
+ }
+ else
+ {
+ // Get each register individually
+ packet_len = ::snprintf (packet, sizeof(packet), "p%x", reg, false);
+ assert (packet_len < (sizeof(packet) - 1));
+ if (gdb_comm.SendPacketAndWaitForResponse(packet, response, 1, false))
+ if (response.GetHexBytes ((uint8_t*)m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size), reg_info->byte_size, '\xcc') == reg_info->byte_size)
+ m_reg_valid[reg] = true;
+ }
+ }
+ }
+ }
+
+ bool reg_is_valid = m_reg_valid[reg];
+ if (reg_is_valid)
+ {
+ if (&data != &m_reg_data)
+ {
+ // If we aren't extracting into our own buffer (which
+ // only happens when this function is called from
+ // ReadRegisterValue(uint32_t, Scalar&)) then
+ // we transfer bytes from our buffer into the data
+ // buffer that was passed in
+ data.SetByteOrder (m_reg_data.GetByteOrder());
+ data.SetData (m_reg_data, reg_info->byte_offset, reg_info->byte_size);
+ }
+ }
+ return reg_is_valid;
+}
+
+
+bool
+GDBRemoteRegisterContext::WriteRegisterValue (uint32_t reg, const Scalar &value)
+{
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg);
+ if (reg_info)
+ {
+ DataExtractor data;
+ if (value.GetData (data, reg_info->byte_size))
+ return WriteRegisterBytes (reg, data, 0);
+ }
+ return false;
+}
+
+
+bool
+GDBRemoteRegisterContext::WriteRegisterBytes (uint32_t reg, DataExtractor &data, uint32_t data_offset)
+{
+ GDBRemoteCommunication &gdb_comm = GetGDBProcess().GetGDBRemote();
+// FIXME: This check isn't right because IsRunning checks the Public state, but this
+// is work you need to do - for instance in ShouldStop & friends - before the public
+// state has been changed.
+// if (gdb_comm.IsRunning())
+// return false;
+
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg);
+
+ if (reg_info)
+ {
+ // Grab a pointer to where we are going to put this register
+ uint8_t *dst = (uint8_t *)m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size);
+
+ if (dst == NULL)
+ return false;
+
+ // Grab a pointer to where we are going to grab the new value from
+ const uint8_t *src = data.PeekData(0, reg_info->byte_size);
+
+ if (src == NULL)
+ return false;
+
+ if (data.GetByteOrder() == m_reg_data.GetByteOrder())
+ {
+ // No swapping, just copy the bytes
+ ::memcpy (dst, src, reg_info->byte_size);
+ }
+ else
+ {
+ // Swap the bytes
+ for (uint32_t i=0; i<reg_info->byte_size; ++i)
+ dst[i] = src[reg_info->byte_size - 1 - i];
+ }
+
+ Mutex::Locker locker;
+ if (gdb_comm.GetSequenceMutex (locker))
+ {
+ if (GetGDBProcess().SetCurrentGDBRemoteThread(m_thread.GetID()))
+ {
+ uint32_t offset, end_offset;
+ StreamString packet;
+ StringExtractorGDBRemote response;
+ if (m_read_all_at_once)
+ {
+ // Get all registers in one packet
+ packet.PutChar ('G');
+ offset = 0;
+ end_offset = m_reg_data.GetByteSize();
+
+ packet.PutBytesAsRawHex8 (m_reg_data.GetDataStart(),
+ m_reg_data.GetByteSize(),
+ eByteOrderHost,
+ eByteOrderHost);
+
+ // Invalidate all register values
+ Invalidate ();
+
+ if (gdb_comm.SendPacketAndWaitForResponse(packet.GetString().c_str(),
+ packet.GetString().size(),
+ response,
+ 1,
+ false))
+ {
+ SetAllRegisterValid (false);
+ if (response.IsOKPacket())
+ {
+ return true;
+ }
+ }
+ }
+ else
+ {
+ // Get each register individually
+ packet.Printf ("P%x=", reg);
+ packet.PutBytesAsRawHex8 (m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size),
+ reg_info->byte_size,
+ eByteOrderHost,
+ eByteOrderHost);
+
+ // Invalidate just this register
+ m_reg_valid[reg] = false;
+ if (gdb_comm.SendPacketAndWaitForResponse(packet.GetString().c_str(),
+ packet.GetString().size(),
+ response,
+ 1,
+ false))
+ {
+ if (response.IsOKPacket())
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+
+bool
+GDBRemoteRegisterContext::ReadAllRegisterValues (lldb::DataBufferSP &data_sp)
+{
+ GDBRemoteCommunication &gdb_comm = GetGDBProcess().GetGDBRemote();
+ StringExtractorGDBRemote response;
+ if (gdb_comm.SendPacketAndWaitForResponse("g", response, 1, false))
+ {
+ if (response.IsErrorPacket())
+ return false;
+
+ response.GetStringRef().insert(0, 1, 'G');
+ data_sp.reset (new DataBufferHeap(response.GetStringRef().data(),
+ response.GetStringRef().size()));
+ return true;
+ }
+ return false;
+}
+
+bool
+GDBRemoteRegisterContext::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp)
+{
+ GDBRemoteCommunication &gdb_comm = GetGDBProcess().GetGDBRemote();
+ StringExtractorGDBRemote response;
+ if (gdb_comm.SendPacketAndWaitForResponse((const char *)data_sp->GetBytes(),
+ data_sp->GetByteSize(),
+ response,
+ 1,
+ false))
+ {
+ if (response.IsOKPacket())
+ return true;
+ }
+ return false;
+}
+
+
+uint32_t
+GDBRemoteRegisterContext::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num)
+{
+ return m_reg_info.ConvertRegisterKindToRegisterNumber (kind, num);
+}
+
+void
+GDBRemoteDynamicRegisterInfo::HardcodeARMRegisters()
+{
+ static lldb::RegisterInfo
+ g_register_infos[] =
+ {
+ // NAME ALT SZ OFF ENCODING FORMAT NUM COMPILER DWARF GENERIC
+ // ====== ======= == ==== ============= ============ === =============== =============== =========
+ { "r0", NULL, 4, 0, eEncodingUint, eFormatHex, 0, { gcc_r0, dwarf_r0, LLDB_INVALID_REGNUM }},
+ { "r1", NULL, 4, 4, eEncodingUint, eFormatHex, 1, { gcc_r1, dwarf_r1, LLDB_INVALID_REGNUM }},
+ { "r2", NULL, 4, 8, eEncodingUint, eFormatHex, 2, { gcc_r2, dwarf_r2, LLDB_INVALID_REGNUM }},
+ { "r3", NULL, 4, 12, eEncodingUint, eFormatHex, 3, { gcc_r3, dwarf_r3, LLDB_INVALID_REGNUM }},
+ { "r4", NULL, 4, 16, eEncodingUint, eFormatHex, 4, { gcc_r4, dwarf_r4, LLDB_INVALID_REGNUM }},
+ { "r5", NULL, 4, 20, eEncodingUint, eFormatHex, 5, { gcc_r5, dwarf_r5, LLDB_INVALID_REGNUM }},
+ { "r6", NULL, 4, 24, eEncodingUint, eFormatHex, 6, { gcc_r6, dwarf_r6, LLDB_INVALID_REGNUM }},
+ { "r7", NULL, 4, 28, eEncodingUint, eFormatHex, 7, { gcc_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP }},
+ { "r8", NULL, 4, 32, eEncodingUint, eFormatHex, 8, { gcc_r8, dwarf_r8, LLDB_INVALID_REGNUM }},
+ { "r9", NULL, 4, 36, eEncodingUint, eFormatHex, 9, { gcc_r9, dwarf_r9, LLDB_INVALID_REGNUM }},
+ { "r10", NULL, 4, 40, eEncodingUint, eFormatHex, 10, { gcc_r10, dwarf_r10, LLDB_INVALID_REGNUM }},
+ { "r11", NULL, 4, 44, eEncodingUint, eFormatHex, 11, { gcc_r11, dwarf_r11, LLDB_INVALID_REGNUM }},
+ { "r12", NULL, 4, 48, eEncodingUint, eFormatHex, 12, { gcc_r12, dwarf_r12, LLDB_INVALID_REGNUM }},
+ { "sp", "r13", 4, 52, eEncodingUint, eFormatHex, 13, { gcc_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP }},
+ { "lr", "r14", 4, 56, eEncodingUint, eFormatHex, 14, { gcc_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA }},
+ { "pc", "r15", 4, 60, eEncodingUint, eFormatHex, 15, { gcc_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC }},
+ { NULL, NULL, 12, 64, eEncodingIEEE754, eFormatFloat, 16, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }},
+ { NULL, NULL, 12, 76, eEncodingIEEE754, eFormatFloat, 17, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }},
+ { NULL, NULL, 12, 88, eEncodingIEEE754, eFormatFloat, 18, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }},
+ { NULL, NULL, 12, 100, eEncodingIEEE754, eFormatFloat, 19, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }},
+ { NULL, NULL, 12, 112, eEncodingIEEE754, eFormatFloat, 20, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }},
+ { NULL, NULL, 12, 124, eEncodingIEEE754, eFormatFloat, 21, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }},
+ { NULL, NULL, 12, 136, eEncodingIEEE754, eFormatFloat, 22, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }},
+ { NULL, NULL, 12, 148, eEncodingIEEE754, eFormatFloat, 23, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }},
+ { NULL, NULL, 12, 160, eEncodingIEEE754, eFormatFloat, 24, { LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS, LLDB_REGNUM_GENERIC_FLAGS }},
+ { "cpsr", "psr", 4, 172, eEncodingUint, eFormatHex, 25, { gcc_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS }},
+ { "s0", NULL, 4, 176, eEncodingIEEE754, eFormatFloat, 26, { LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM }},
+ { "s1", NULL, 4, 180, eEncodingIEEE754, eFormatFloat, 27, { LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM }},
+ { "s2", NULL, 4, 184, eEncodingIEEE754, eFormatFloat, 28, { LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM }},
+ { "s3", NULL, 4, 188, eEncodingIEEE754, eFormatFloat, 29, { LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM }},
+ { "s4", NULL, 4, 192, eEncodingIEEE754, eFormatFloat, 30, { LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM }},
+ { "s5", NULL, 4, 196, eEncodingIEEE754, eFormatFloat, 31, { LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM }},
+ { "s6", NULL, 4, 200, eEncodingIEEE754, eFormatFloat, 32, { LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM }},
+ { "s7", NULL, 4, 204, eEncodingIEEE754, eFormatFloat, 33, { LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM }},
+ { "s8", NULL, 4, 208, eEncodingIEEE754, eFormatFloat, 34, { LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM }},
+ { "s9", NULL, 4, 212, eEncodingIEEE754, eFormatFloat, 35, { LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM }},
+ { "s10", NULL, 4, 216, eEncodingIEEE754, eFormatFloat, 36, { LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM }},
+ { "s11", NULL, 4, 220, eEncodingIEEE754, eFormatFloat, 37, { LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM }},
+ { "s12", NULL, 4, 224, eEncodingIEEE754, eFormatFloat, 38, { LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM }},
+ { "s13", NULL, 4, 228, eEncodingIEEE754, eFormatFloat, 39, { LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM }},
+ { "s14", NULL, 4, 232, eEncodingIEEE754, eFormatFloat, 40, { LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM }},
+ { "s15", NULL, 4, 236, eEncodingIEEE754, eFormatFloat, 41, { LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM }},
+ { "s16", NULL, 4, 240, eEncodingIEEE754, eFormatFloat, 42, { LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM }},
+ { "s17", NULL, 4, 244, eEncodingIEEE754, eFormatFloat, 43, { LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM }},
+ { "s18", NULL, 4, 248, eEncodingIEEE754, eFormatFloat, 44, { LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM }},
+ { "s19", NULL, 4, 252, eEncodingIEEE754, eFormatFloat, 45, { LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM }},
+ { "s20", NULL, 4, 256, eEncodingIEEE754, eFormatFloat, 46, { LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM }},
+ { "s21", NULL, 4, 260, eEncodingIEEE754, eFormatFloat, 47, { LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM }},
+ { "s22", NULL, 4, 264, eEncodingIEEE754, eFormatFloat, 48, { LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM }},
+ { "s23", NULL, 4, 268, eEncodingIEEE754, eFormatFloat, 49, { LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM }},
+ { "s24", NULL, 4, 272, eEncodingIEEE754, eFormatFloat, 50, { LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM }},
+ { "s25", NULL, 4, 276, eEncodingIEEE754, eFormatFloat, 51, { LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM }},
+ { "s26", NULL, 4, 280, eEncodingIEEE754, eFormatFloat, 52, { LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM }},
+ { "s27", NULL, 4, 284, eEncodingIEEE754, eFormatFloat, 53, { LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM }},
+ { "s28", NULL, 4, 288, eEncodingIEEE754, eFormatFloat, 54, { LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM }},
+ { "s29", NULL, 4, 292, eEncodingIEEE754, eFormatFloat, 55, { LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM }},
+ { "s30", NULL, 4, 296, eEncodingIEEE754, eFormatFloat, 56, { LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM }},
+ { "s31", NULL, 4, 300, eEncodingIEEE754, eFormatFloat, 57, { LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM }},
+ { "fpscr", NULL, 4, 304, eEncodingUint, eFormatHex, 58, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,LLDB_INVALID_REGNUM }},
+ { "d16", NULL, 8, 308, eEncodingIEEE754, eFormatFloat, 59, { LLDB_INVALID_REGNUM, dwarf_d16, LLDB_INVALID_REGNUM }},
+ { "d17", NULL, 8, 316, eEncodingIEEE754, eFormatFloat, 60, { LLDB_INVALID_REGNUM, dwarf_d17, LLDB_INVALID_REGNUM }},
+ { "d18", NULL, 8, 324, eEncodingIEEE754, eFormatFloat, 61, { LLDB_INVALID_REGNUM, dwarf_d18, LLDB_INVALID_REGNUM }},
+ { "d19", NULL, 8, 332, eEncodingIEEE754, eFormatFloat, 62, { LLDB_INVALID_REGNUM, dwarf_d19, LLDB_INVALID_REGNUM }},
+ { "d20", NULL, 8, 340, eEncodingIEEE754, eFormatFloat, 63, { LLDB_INVALID_REGNUM, dwarf_d20, LLDB_INVALID_REGNUM }},
+ { "d21", NULL, 8, 348, eEncodingIEEE754, eFormatFloat, 64, { LLDB_INVALID_REGNUM, dwarf_d21, LLDB_INVALID_REGNUM }},
+ { "d22", NULL, 8, 356, eEncodingIEEE754, eFormatFloat, 65, { LLDB_INVALID_REGNUM, dwarf_d22, LLDB_INVALID_REGNUM }},
+ { "d23", NULL, 8, 364, eEncodingIEEE754, eFormatFloat, 66, { LLDB_INVALID_REGNUM, dwarf_d23, LLDB_INVALID_REGNUM }},
+ { "d24", NULL, 8, 372, eEncodingIEEE754, eFormatFloat, 67, { LLDB_INVALID_REGNUM, dwarf_d24, LLDB_INVALID_REGNUM }},
+ { "d25", NULL, 8, 380, eEncodingIEEE754, eFormatFloat, 68, { LLDB_INVALID_REGNUM, dwarf_d25, LLDB_INVALID_REGNUM }},
+ { "d26", NULL, 8, 388, eEncodingIEEE754, eFormatFloat, 69, { LLDB_INVALID_REGNUM, dwarf_d26, LLDB_INVALID_REGNUM }},
+ { "d27", NULL, 8, 396, eEncodingIEEE754, eFormatFloat, 70, { LLDB_INVALID_REGNUM, dwarf_d27, LLDB_INVALID_REGNUM }},
+ { "d28", NULL, 8, 404, eEncodingIEEE754, eFormatFloat, 71, { LLDB_INVALID_REGNUM, dwarf_d28, LLDB_INVALID_REGNUM }},
+ { "d29", NULL, 8, 412, eEncodingIEEE754, eFormatFloat, 72, { LLDB_INVALID_REGNUM, dwarf_d29, LLDB_INVALID_REGNUM }},
+ { "d30", NULL, 8, 420, eEncodingIEEE754, eFormatFloat, 73, { LLDB_INVALID_REGNUM, dwarf_d30, LLDB_INVALID_REGNUM }},
+ { "d31", NULL, 8, 428, eEncodingIEEE754, eFormatFloat, 74, { LLDB_INVALID_REGNUM, dwarf_d31, LLDB_INVALID_REGNUM }},
+ };
+ static const uint32_t num_registers = sizeof (g_register_infos)/sizeof (lldb::RegisterInfo);
+ static ConstString gpr_reg_set ("General Purpose Registers");
+ static ConstString vfp_reg_set ("Floating Point Registers");
+ for (uint32_t i=0; i<num_registers; ++i)
+ {
+ ConstString name;
+ ConstString alt_name;
+ if (g_register_infos[i].name && g_register_infos[i].name[0])
+ name.SetCString(g_register_infos[i].name);
+ if (g_register_infos[i].alt_name && g_register_infos[i].alt_name[0])
+ alt_name.SetCString(g_register_infos[i].alt_name);
+
+ AddRegister (g_register_infos[i], name, alt_name, i < 26 ? gpr_reg_set : vfp_reg_set);
+ }
+}
+
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h
new file mode 100644
index 00000000000..67acdefdce0
--- /dev/null
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h
@@ -0,0 +1,250 @@
+//===-- GDBRemoteRegisterContext.h ------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_GDBRemoteRegisterContext_h_
+#define lldb_GDBRemoteRegisterContext_h_
+
+// C Includes
+// C++ Includes
+#include <vector>
+
+// Other libraries and framework includes
+// Project includes
+#include "lldb/lldb-private.h"
+#include "lldb/Core/ConstString.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Target/RegisterContext.h"
+
+
+class ThreadGDBRemote;
+class ProcessGDBRemote;
+
+class GDBRemoteDynamicRegisterInfo
+{
+public:
+ GDBRemoteDynamicRegisterInfo () :
+ m_regs (),
+ m_sets (),
+ m_set_reg_nums (),
+ m_reg_names (),
+ m_reg_alt_names (),
+ m_set_names (),
+ m_reg_data_byte_size (0)
+ {
+ }
+
+ ~GDBRemoteDynamicRegisterInfo ()
+ {
+ }
+
+ void
+ AddRegister (lldb::RegisterInfo &reg_info, lldb_private::ConstString &reg_name, lldb_private::ConstString &reg_alt_name, lldb_private::ConstString &set_name)
+ {
+ const uint32_t reg_num = m_regs.size();
+ m_reg_names.push_back (reg_name);
+ m_reg_alt_names.push_back (reg_alt_name);
+ reg_info.name = reg_name.AsCString();
+ assert (reg_info.name);
+ reg_info.alt_name = reg_alt_name.AsCString(NULL);
+ m_regs.push_back (reg_info);
+ uint32_t set = GetRegisterSetIndexByName (set_name, true);
+ assert (set < m_sets.size());
+ assert (set < m_set_reg_nums.size());
+ assert (set < m_set_names.size());
+ m_set_reg_nums[set].push_back(reg_num);
+ size_t end_reg_offset = reg_info.byte_offset + reg_info.byte_size;
+ if (m_reg_data_byte_size < end_reg_offset)
+ m_reg_data_byte_size = end_reg_offset;
+ }
+
+ void
+ Finalize ()
+ {
+ for (uint32_t set = 0; set < m_sets.size(); ++set)
+ {
+ assert (m_sets.size() == m_set_reg_nums.size());
+ m_sets[set].num_registers = m_set_reg_nums[set].size();
+ m_sets[set].registers = &m_set_reg_nums[set][0];
+ }
+ }
+
+ size_t
+ GetNumRegisters() const
+ {
+ return m_regs.size();
+ }
+
+ size_t
+ GetNumRegisterSets() const
+ {
+ return m_sets.size();
+ }
+
+ size_t
+ GetRegisterDataByteSize() const
+ {
+ return m_reg_data_byte_size;
+ }
+
+ const lldb::RegisterInfo *
+ GetRegisterInfoAtIndex (uint32_t i) const
+ {
+ if (i < m_regs.size())
+ return &m_regs[i];
+ return NULL;
+ }
+
+ const lldb::RegisterSet *
+ GetRegisterSet (uint32_t i) const
+ {
+ if (i < m_sets.size())
+ return &m_sets[i];
+ return NULL;
+ }
+
+ uint32_t
+ GetRegisterSetIndexByName (lldb_private::ConstString &set_name, bool can_create)
+ {
+ name_collection::iterator pos, end = m_set_names.end();
+ for (pos = m_set_names.begin(); pos != end; ++pos)
+ {
+ if (*pos == set_name)
+ return std::distance (m_set_names.begin(), pos);
+ }
+
+ m_set_names.push_back(set_name);
+ m_set_reg_nums.resize(m_set_reg_nums.size()+1);
+ lldb::RegisterSet new_set = { set_name.AsCString(), NULL, 0, NULL };
+ m_sets.push_back (new_set);
+ return m_sets.size() - 1;
+ }
+
+ uint32_t
+ ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) const
+ {
+ reg_collection::const_iterator pos, end = m_regs.end();
+ for (pos = m_regs.begin(); pos != end; ++pos)
+ {
+ if (pos->kinds[kind] == num)
+ return std::distance (m_regs.begin(), pos);
+ }
+
+ return LLDB_INVALID_REGNUM;
+ }
+ void
+ Clear()
+ {
+ m_regs.clear();
+ m_sets.clear();
+ m_set_reg_nums.clear();
+ m_reg_names.clear();
+ m_reg_alt_names.clear();
+ m_set_names.clear();
+ }
+
+ void
+ HardcodeARMRegisters();
+
+protected:
+ //------------------------------------------------------------------
+ // Classes that inherit from GDBRemoteRegisterContext can see and modify these
+ //------------------------------------------------------------------
+ typedef std::vector <lldb::RegisterInfo> reg_collection;
+ typedef std::vector <lldb::RegisterSet> set_collection;
+ typedef std::vector <uint32_t> reg_num_collection;
+ typedef std::vector <reg_num_collection> set_reg_num_collection;
+ typedef std::vector <lldb_private::ConstString> name_collection;
+
+ reg_collection m_regs;
+ set_collection m_sets;
+ set_reg_num_collection m_set_reg_nums;
+ name_collection m_reg_names;
+ name_collection m_reg_alt_names;
+ name_collection m_set_names;
+ size_t m_reg_data_byte_size; // The number of bytes required to store all registers
+};
+
+class GDBRemoteRegisterContext : public lldb_private::RegisterContext
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ GDBRemoteRegisterContext (ThreadGDBRemote &thread,
+ lldb_private::StackFrame *frame,
+ GDBRemoteDynamicRegisterInfo &reg_info,
+ bool read_all_at_once);
+
+ virtual
+ ~GDBRemoteRegisterContext ();
+
+ //------------------------------------------------------------------
+ // Subclasses must override these functions
+ //------------------------------------------------------------------
+ virtual void
+ Invalidate ();
+
+ virtual size_t
+ GetRegisterCount ();
+
+ virtual const lldb::RegisterInfo *
+ GetRegisterInfoAtIndex (uint32_t reg);
+
+ virtual size_t
+ GetRegisterSetCount ();
+
+ virtual const lldb::RegisterSet *
+ GetRegisterSet (uint32_t reg_set);
+
+ virtual bool
+ ReadRegisterValue (uint32_t reg, lldb_private::Scalar &value);
+
+ virtual bool
+ ReadRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data);
+
+ virtual bool
+ ReadAllRegisterValues (lldb::DataBufferSP &data_sp);
+
+ virtual bool
+ WriteRegisterValue (uint32_t reg, const lldb_private::Scalar &value);
+
+ virtual bool
+ WriteRegisterBytes (uint32_t reg, lldb_private::DataExtractor &data, uint32_t data_offset);
+
+ virtual bool
+ WriteAllRegisterValues (const lldb::DataBufferSP &data_sp);
+
+ virtual uint32_t
+ ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num);
+
+protected:
+
+ void
+ SetAllRegisterValid (bool b);
+
+ ProcessGDBRemote &
+ GetGDBProcess();
+
+ ThreadGDBRemote &
+ GetGDBThread();
+
+ GDBRemoteDynamicRegisterInfo &m_reg_info;
+ std::vector<bool> m_reg_valid;
+ uint32_t m_reg_valid_stop_id;
+ lldb_private::DataExtractor m_reg_data;
+ bool m_read_all_at_once;
+
+private:
+ //------------------------------------------------------------------
+ // For GDBRemoteRegisterContext only
+ //------------------------------------------------------------------
+ DISALLOW_COPY_AND_ASSIGN (GDBRemoteRegisterContext);
+};
+
+#endif // lldb_GDBRemoteRegisterContext_h_
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBServer.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBServer.cpp
new file mode 100644
index 00000000000..a88ec7b09d4
--- /dev/null
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBServer.cpp
@@ -0,0 +1,1148 @@
+//===-- GDBServer.cpp -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <getopt.h>
+#include <netinet/in.h>
+#include <sys/select.h>
+#include <sys/sysctl.h>
+#include <string>
+#include <vector>
+#include <asl.h>
+
+#include "GDBServerLog.h"
+#include "GDBRemoteSession.h"
+
+using namespace lldb;
+
+//----------------------------------------------------------------------
+// Run loop modes which determine which run loop function will be called
+//----------------------------------------------------------------------
+typedef enum
+{
+ eDCGSRunLoopModeInvalid = 0,
+ eDCGSRunLoopModeGetStartModeFromRemoteProtocol,
+ eDCGSRunLoopModeInferiorAttaching,
+ eDCGSRunLoopModeInferiorLaunching,
+ eDCGSRunLoopModeInferiorExecuting,
+ eDCGSRunLoopModeInferiorKillOrDetach,
+ eDCGSRunLoopModeExit
+} GSRunLoopMode;
+
+typedef enum
+{
+ eLaunchFlavorDefault = 0,
+ eLaunchFlavorPosixSpawn,
+#if defined (__arm__)
+ eLaunchFlavorSpringBoard,
+#endif
+ eLaunchFlavorForkExec,
+} GSLaunchFlavor;
+
+typedef lldb::shared_ptr<GDBRemoteSession> GDBRemoteSP;
+
+typedef struct HandleBroadcastEventInfo
+{
+ TargetSP target_sp;
+ GDBRemoteSP remote_sp;
+ GSRunLoopMode mode;
+
+ Target *
+ GetTarget ()
+ {
+ return target_sp.get();
+ }
+
+ Process *
+ GetProcess()
+ {
+ if (target_sp.get())
+ return target_sp->GetProcess().get();
+ return NULL;
+ }
+
+ GDBRemoteSession *
+ GetRemote ()
+ {
+ return remote_sp.get();
+ }
+
+};
+
+
+//----------------------------------------------------------------------
+// Global Variables
+//----------------------------------------------------------------------
+static int g_lockdown_opt = 0;
+static int g_applist_opt = 0;
+static GSLaunchFlavor g_launch_flavor = eLaunchFlavorDefault;
+int g_isatty = 0;
+
+//----------------------------------------------------------------------
+// Run Loop function prototypes
+//----------------------------------------------------------------------
+void GSRunLoopGetStartModeFromRemote (HandleBroadcastEventInfo *info);
+void GSRunLoopInferiorExecuting (HandleBroadcastEventInfo *info);
+
+
+//----------------------------------------------------------------------
+// Get our program path and arguments from the remote connection.
+// We will need to start up the remote connection without a PID, get the
+// arguments, wait for the new process to finish launching and hit its
+// entry point, and then return the run loop mode that should come next.
+//----------------------------------------------------------------------
+void
+GSRunLoopGetStartModeFromRemote (HandleBroadcastEventInfo *info)
+{
+ std::string packet;
+
+ Target *target = info->GetTarget();
+ GDBRemoteSession *remote = info->GetRemote();
+ if (target != NULL && remote != NULL)
+ {
+ // Spin waiting to get the A packet.
+ while (1)
+ {
+ gdb_err_t err = gdb_err;
+ GDBRemoteSession::PacketEnum type;
+
+ err = remote->HandleReceivedPacket (&type);
+
+ // check if we tried to attach to a process
+ if (type == GDBRemoteSession::vattach || type == GDBRemoteSession::vattachwait)
+ {
+ if (err == gdb_success)
+ {
+ info->mode = eDCGSRunLoopModeInferiorExecuting;
+ return;
+ }
+ else
+ {
+ Log::STDERR ("error: attach failed.");
+ info->mode = eDCGSRunLoopModeExit;
+ return;
+ }
+ }
+
+ if (err == gdb_success)
+ {
+ // If we got our arguments we are ready to launch using the arguments
+ // and any environment variables we received.
+ if (type == GDBRemoteSession::set_argv)
+ {
+ info->mode = eDCGSRunLoopModeInferiorLaunching;
+ return;
+ }
+ }
+ else if (err == gdb_not_connected)
+ {
+ Log::STDERR ("error: connection lost.");
+ info->mode = eDCGSRunLoopModeExit;
+ return;
+ }
+ else
+ {
+ // a catch all for any other gdb remote packets that failed
+ GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s Error getting packet.",__FUNCTION__);
+ continue;
+ }
+
+ GDBServerLog::LogIf (GS_LOG_MINIMAL, "#### %s", __FUNCTION__);
+ }
+ }
+ info->mode = eDCGSRunLoopModeExit;
+}
+
+
+//----------------------------------------------------------------------
+// This run loop mode will wait for the process to launch and hit its
+// entry point. It will currently ignore all events except for the
+// process state changed event, where it watches for the process stopped
+// or crash process state.
+//----------------------------------------------------------------------
+GSRunLoopMode
+GSRunLoopLaunchInferior (HandleBroadcastEventInfo *info)
+{
+ // The Process stuff takes a c array, the GSContext has a vector...
+ // So make up a c array.
+ Target *target = info->GetTarget();
+ GDBRemoteSession *remote = info->GetRemote();
+ Process* process = info->GetProcess();
+
+ if (process == NULL)
+ return eDCGSRunLoopModeExit;
+
+ GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s Launching '%s'...", __FUNCTION__, target->GetExecutableModule()->GetFileSpec().GetFilename().AsCString());
+
+ // Our launch type hasn't been set to anything concrete, so we need to
+ // figure our how we are going to launch automatically.
+
+ GSLaunchFlavor launch_flavor = g_launch_flavor;
+ if (launch_flavor == eLaunchFlavorDefault)
+ {
+ // Our default launch method is posix spawn
+ launch_flavor = eLaunchFlavorPosixSpawn;
+
+#if defined (__arm__)
+ // Check if we have an app bundle, if so launch using SpringBoard.
+ if (strstr(inferior_argv[0], ".app"))
+ {
+ launch_flavor = eLaunchFlavorSpringBoard;
+ }
+#endif
+ }
+
+ //ctx.SetLaunchFlavor(launch_flavor);
+
+ const char *stdio_file = NULL;
+ lldb::pid_t pid = process->Launch (remote->GetARGV(), remote->GetENVP(), stdio_file, stdio_file, stdio_file);
+
+ if (pid == LLDB_INVALID_PROCESS_ID)
+ {
+ Log::STDERR ("error: process launch failed: %s", process->GetError().AsCString());
+ }
+ else
+ {
+ if (remote->IsConnected())
+ {
+ // It we are connected already, the next thing gdb will do is ask
+ // whether the launch succeeded, and if not, whether there is an
+ // error code. So we need to fetch one packet from gdb before we wait
+ // on the stop from the target.
+ gdb_err_t err = gdb_err;
+ GDBRemoteSession::PacketEnum type;
+
+ err = remote->HandleReceivedPacket (&type);
+
+ if (err != gdb_success)
+ {
+ GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s Error getting packet.", __FUNCTION__);
+ return eDCGSRunLoopModeExit;
+ }
+ if (type != GDBRemoteSession::query_launch_success)
+ {
+ GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s Didn't get the expected qLaunchSuccess packet.", __FUNCTION__);
+ }
+ }
+ }
+
+ Listener listener("GSRunLoopLaunchInferior");
+ listener.StartListeningForEvents (process, Process::eBroadcastBitStateChanged);
+ while (process->GetID() != LLDB_INVALID_PROCESS_ID)
+ {
+ uint32_t event_mask = 0;
+ while (listener.WaitForEvent(NULL, &event_mask))
+ {
+ if (event_mask & Process::eBroadcastBitStateChanged)
+ {
+ Event event;
+ StateType event_state;
+ while ((event_state = process->GetNextEvent (&event)))
+ if (StateIsStoppedState(event_state))
+ {
+ GDBServerLog::LogIf (GS_LOG_EVENTS, "%s process %4.4x stopped with state %s", __FUNCTION__, pid, StateAsCString(event_state));
+
+ switch (event_state)
+ {
+ default:
+ case eStateInvalid:
+ case eStateUnloaded:
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateSuspended:
+ break; // Ignore
+
+ case eStateRunning:
+ case eStateStepping:
+ // Still waiting to stop at entry point...
+ break;
+
+ case eStateStopped:
+ case eStateCrashed:
+ return eDCGSRunLoopModeInferiorExecuting;
+
+ case eStateDetached:
+ case eStateExited:
+ pid = LLDB_INVALID_PROCESS_ID;
+ return eDCGSRunLoopModeExit;
+ }
+ }
+
+ if (event_state = eStateInvalid)
+ break;
+ }
+ }
+ }
+
+ return eDCGSRunLoopModeExit;
+}
+
+
+//----------------------------------------------------------------------
+// This run loop mode will wait for the process to launch and hit its
+// entry point. It will currently ignore all events except for the
+// process state changed event, where it watches for the process stopped
+// or crash process state.
+//----------------------------------------------------------------------
+GSRunLoopMode
+GSRunLoopLaunchAttaching (HandleBroadcastEventInfo *info, lldb::pid_t& pid)
+{
+ Process* process = info->GetProcess();
+
+ GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s Attaching to pid %i...", __FUNCTION__, pid);
+ pid = process->Attach(pid);
+
+ if (pid == LLDB_INVALID_PROCESS_ID)
+ return eDCGSRunLoopModeExit;
+ return eDCGSRunLoopModeInferiorExecuting;
+}
+
+//----------------------------------------------------------------------
+// Watch for signals:
+// SIGINT: so we can halt our inferior. (disabled for now)
+// SIGPIPE: in case our child process dies
+//----------------------------------------------------------------------
+lldb::pid_t g_pid;
+int g_sigpipe_received = 0;
+void
+signal_handler(int signo)
+{
+ GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s (%s)", __FUNCTION__, Host::GetSignalAsCString(signo));
+
+ switch (signo)
+ {
+// case SIGINT:
+// DNBProcessKill (g_pid, signo);
+// break;
+
+ case SIGPIPE:
+ g_sigpipe_received = 1;
+ break;
+ }
+}
+
+// Return the new run loop mode based off of the current process state
+void
+HandleProcessStateChange (HandleBroadcastEventInfo *info, bool initialize)
+{
+ Process *process = info->GetProcess();
+ if (process == NULL)
+ {
+ info->mode = eDCGSRunLoopModeExit;
+ return;
+ }
+
+ if (process->GetID() == LLDB_INVALID_PROCESS_ID)
+ {
+ GDBServerLog::LogIf (GS_LOG_MINIMAL, "#### %s error: pid invalid, exiting...", __FUNCTION__);
+ info->mode = eDCGSRunLoopModeExit;
+ return;
+ }
+ StateType pid_state = process->GetState ();
+
+ GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s (info, initialize=%i) pid_state = %s", __FUNCTION__, (int)initialize, StateAsCString(pid_state));
+
+ switch (pid_state)
+ {
+ case eStateInvalid:
+ case eStateUnloaded:
+ // Something bad happened
+ info->mode = eDCGSRunLoopModeExit;
+ return;
+
+ case eStateAttaching:
+ case eStateLaunching:
+ info->mode = eDCGSRunLoopModeInferiorExecuting;
+ return;
+
+ case eStateSuspended:
+ case eStateCrashed:
+ case eStateStopped:
+ if (initialize == false)
+ {
+ // Compare the last stop count to our current notion of a stop count
+ // to make sure we don't notify more than once for a given stop.
+ static uint32_t g_prev_stop_id = 0;
+ uint32_t stop_id = process->GetStopID();
+ bool pid_stop_count_changed = g_prev_stop_id != stop_id;
+ if (pid_stop_count_changed)
+ {
+ info->GetRemote()->FlushSTDIO();
+
+ if (stop_id == 1)
+ {
+ GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? no, first stop...", __FUNCTION__, (int)initialize, StateAsCString (pid_state), stop_id, g_prev_stop_id);
+ }
+ else
+ {
+
+ GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? YES!!!", __FUNCTION__, (int)initialize, StateAsCString (pid_state), stop_id, g_prev_stop_id);
+ info->GetRemote()->NotifyThatProcessStopped ();
+ }
+ }
+ else
+ {
+ GDBServerLog::LogIf (GS_LOG_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? skipping...", __FUNCTION__, (int)initialize, StateAsCString (pid_state), stop_id, g_prev_stop_id);
+ }
+ }
+ info->mode = eDCGSRunLoopModeInferiorExecuting;
+ return;
+
+ case eStateStepping:
+ case eStateRunning:
+ info->mode = eDCGSRunLoopModeInferiorExecuting;
+ return;
+
+ case eStateExited:
+ info->GetRemote()->HandlePacket_last_signal (NULL);
+ info->mode = eDCGSRunLoopModeExit;
+ return;
+
+ }
+
+ // Catch all...
+ info->mode = eDCGSRunLoopModeExit;
+}
+
+bool
+CommunicationHandleBroadcastEvent (Broadcaster *broadcaster, uint32_t event_mask, void *baton)
+{
+ HandleBroadcastEventInfo *info = (HandleBroadcastEventInfo *)baton;
+ Process *process = info->GetProcess();
+
+ if (process == NULL)
+ {
+ info->mode = eDCGSRunLoopModeExit;
+ return true;
+ }
+
+ if (event_mask & Communication::eBroadcastBitPacketAvailable)
+ {
+ if (process->IsRunning())
+ {
+ if (info->GetRemote()->HandleAsyncPacket() == gdb_not_connected)
+ info->mode = eDCGSRunLoopModeExit;
+ }
+ else
+ {
+ if (info->GetRemote()->HandleReceivedPacket() == gdb_not_connected)
+ info->mode = eDCGSRunLoopModeExit;
+ }
+ }
+ if (event_mask & Communication::eBroadcastBitReadThreadDidExit)
+ {
+ info->mode = eDCGSRunLoopModeExit;
+ }
+ if (event_mask & Communication::eBroadcastBitDisconnected)
+ {
+ info->mode = eDCGSRunLoopModeExit;
+ }
+
+ return true;
+
+}
+
+bool
+ProcessHandleBroadcastEvent (Broadcaster *broadcaster, uint32_t event_mask, void *baton)
+{
+ HandleBroadcastEventInfo *info = (HandleBroadcastEventInfo *)baton;
+ Process *process = info->GetProcess();
+ if (process == NULL)
+ {
+ info->mode = eDCGSRunLoopModeExit;
+ return true;
+ }
+
+ if (event_mask & Process::eBroadcastBitStateChanged)
+ {
+ // Consume all available process events with no timeout
+ Event event;
+ StateType process_state;
+ while ((process_state = process->GetNextEvent (&event)) != eStateInvalid)
+ {
+ if (StateIsStoppedState(process_state))
+ info->GetRemote()->FlushSTDIO();
+ HandleProcessStateChange (info, false);
+
+ if (info->mode != eDCGSRunLoopModeInferiorExecuting)
+ break;
+ }
+ }
+ else
+ if (event_mask & (Process::eBroadcastBitSTDOUT | Process::eBroadcastBitSTDERR))
+ {
+ info->GetRemote()->FlushSTDIO();
+ }
+ return true;
+}
+
+// This function handles the case where our inferior program is stopped and
+// we are waiting for gdb remote protocol packets. When a packet occurs that
+// makes the inferior run, we need to leave this function with a new state
+// as the return code.
+void
+GSRunLoopInferiorExecuting (HandleBroadcastEventInfo *info)
+{
+ GDBServerLog::LogIf (GS_LOG_MINIMAL, "#### %s", __FUNCTION__);
+
+ // Init our mode and set 'is_running' based on the current process state
+ HandleProcessStateChange (info, true);
+
+ uint32_t desired_mask, acquired_mask;
+ Listener listener("GSRunLoopInferiorExecuting");
+
+ desired_mask = Communication::eBroadcastBitPacketAvailable |
+ Communication::eBroadcastBitReadThreadDidExit |
+ Communication::eBroadcastBitDisconnected;
+
+ acquired_mask = listener.StartListeningForEvents (&(info->GetRemote()->GetPacketComm()),
+ desired_mask,
+ CommunicationHandleBroadcastEvent,
+ info);
+
+ assert (acquired_mask == desired_mask);
+ desired_mask = GDBRemotePacket::eBroadcastBitPacketAvailable;
+
+ acquired_mask = listener.StartListeningForEvents (&(info->GetRemote()->GetPacketComm()),
+ desired_mask,
+ CommunicationHandleBroadcastEvent,
+ info);
+
+ assert (acquired_mask == desired_mask);
+
+ desired_mask = Process::eBroadcastBitStateChanged |
+ Process::eBroadcastBitSTDOUT |
+ Process::eBroadcastBitSTDERR ;
+ acquired_mask = listener.StartListeningForEvents (info->GetProcess (),
+ desired_mask,
+ ProcessHandleBroadcastEvent,
+ info);
+
+ assert (acquired_mask == desired_mask);
+
+ Process *process = info->GetProcess();
+
+ while (process->IsAlive())
+ {
+ if (!info->GetRemote()->IsConnected())
+ {
+ info->mode = eDCGSRunLoopModeInferiorKillOrDetach;
+ break;
+ }
+
+ // We want to make sure we consume all process state changes and have
+ // whomever is notifying us to wait for us to reset the event bit before
+ // continuing.
+ //ctx.Events().SetResetAckMask (GSContext::event_proc_state_changed);
+ uint32_t event_mask = 0;
+ Broadcaster *broadcaster = listener.WaitForEvent(NULL, &event_mask);
+ if (broadcaster)
+ {
+ listener.HandleBroadcastEvent(broadcaster, event_mask);
+ }
+ }
+}
+
+
+//----------------------------------------------------------------------
+// Convenience function to set up the remote listening port
+// Returns 1 for success 0 for failure.
+//----------------------------------------------------------------------
+
+static bool
+StartListening (HandleBroadcastEventInfo *info, int listen_port)
+{
+ if (!info->GetRemote()->IsConnected())
+ {
+ Log::STDOUT ("Listening to port %i...\n", listen_port);
+ char connect_url[256];
+ snprintf(connect_url, sizeof(connect_url), "listen://%i", listen_port);
+
+ Communication &comm = info->remote_sp->GetPacketComm();
+ comm.SetConnection (new ConnectionFileDescriptor);
+
+ if (comm.Connect (connect_url))
+ {
+ if (comm.StartReadThread())
+ return true;
+
+ Log::STDERR ("Failed to start the communication read thread.\n", connect_url);
+ comm.Disconnect();
+ }
+ else
+ {
+ Log::STDERR ("Failed to connection to %s.\n", connect_url);
+ }
+ return false;
+ }
+ return true;
+}
+
+//----------------------------------------------------------------------
+// ASL Logging callback that can be registered with DNBLogSetLogDCScriptInterpreter::Type
+//----------------------------------------------------------------------
+//void
+//ASLLogDCScriptInterpreter::Type(void *baton, uint32_t flags, const char *format, va_list args)
+//{
+// if (format == NULL)
+// return;
+// static aslmsg g_aslmsg = NULL;
+// if (g_aslmsg == NULL)
+// {
+// g_aslmsg = ::asl_new (ASL_TYPE_MSG);
+// char asl_key_sender[PATH_MAX];
+// snprintf(asl_key_sender, sizeof(asl_key_sender), "com.apple.dc-gdbserver-%g", dc_gdbserverVersionNumber);
+// ::asl_set (g_aslmsg, ASL_KEY_SENDER, asl_key_sender);
+// }
+//
+// int asl_level;
+// if (flags & DNBLOG_FLAG_FATAL) asl_level = ASL_LEVEL_CRIT;
+// else if (flags & DNBLOG_FLAG_ERROR) asl_level = ASL_LEVEL_ERR;
+// else if (flags & DNBLOG_FLAG_WARNING) asl_level = ASL_LEVEL_WARNING;
+// else if (flags & DNBLOG_FLAG_VERBOSE) asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_INFO;
+// else asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_DEBUG;
+//
+// ::asl_vlog (NULL, g_aslmsg, asl_level, format, args);
+//}
+
+//----------------------------------------------------------------------
+// FILE based Logging callback that can be registered with
+// DNBLogSetLogDCScriptInterpreter::Type
+//----------------------------------------------------------------------
+void
+FileLogDCScriptInterpreter::Type(void *baton, uint32_t flags, const char *format, va_list args)
+{
+ if (baton == NULL || format == NULL)
+ return;
+
+ ::vfprintf ((FILE *)baton, format, args);
+ ::fprintf ((FILE *)baton, "\n");
+}
+
+//----------------------------------------------------------------------
+// option descriptors for getopt_long()
+//----------------------------------------------------------------------
+static struct option g_long_options[] =
+{
+ { "arch", required_argument, NULL, 'c' },
+ { "attach", required_argument, NULL, 'a' },
+ { "debug", no_argument, NULL, 'g' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "lockdown", no_argument, &g_lockdown_opt, 1 }, // short option "-k"
+ { "applist", no_argument, &g_applist_opt, 1 }, // short option "-t"
+ { "log-file", required_argument, NULL, 'l' },
+ { "log-flags", required_argument, NULL, 'f' },
+ { "launch", required_argument, NULL, 'x' }, // Valid values are "auto", "posix-spawn", "fork-exec", "springboard" (arm only)
+ { "waitfor", required_argument, NULL, 'w' }, // Wait for a process whose namet starts with ARG
+ { "waitfor-interval", required_argument, NULL, 'i' }, // Time in usecs to wait between sampling the pid list when waiting for a process by name
+ { "waitfor-duration", required_argument, NULL, 'd' }, // The time in seconds to wait for a process to show up by name
+ { NULL, 0, NULL, 0 }
+};
+
+extern const double dc_gdbserverVersionNumber;
+int
+main (int argc, char *argv[])
+{
+ Initialize();
+ Host::ThreadCreated ("[main]");
+
+ g_isatty = ::isatty (STDIN_FILENO);
+
+// signal (SIGINT, signal_handler);
+ signal (SIGPIPE, signal_handler);
+
+ Log *log = GDBServerLog::GetLogIfAllCategoriesSet(GS_LOG_ALL);
+ const char *this_exe_name = argv[0];
+ int i;
+ int attach_pid = LLDB_INVALID_PROCESS_ID;
+ for (i=0; i<argc; i++)
+ GDBServerLog::LogIf(GS_LOG_DEBUG, "argv[%i] = %s", i, argv[i]);
+
+ FILE* log_file = NULL;
+ uint32_t log_flags = 0;
+ // Parse our options
+ int ch;
+ int long_option_index = 0;
+ int debug = 0;
+ std::string waitfor_pid_name; // Wait for a process that starts with this name
+ std::string attach_pid_name;
+ useconds_t waitfor_interval = 1000; // Time in usecs between process lists polls when waiting for a process by name, default 1 msec.
+ useconds_t waitfor_duration = 0; // Time in seconds to wait for a process by name, 0 means wait forever.
+ ArchSpec arch;
+ GSRunLoopMode start_mode = eDCGSRunLoopModeExit;
+
+ while ((ch = getopt_long(argc, argv, "a:c:d:gi:vktl:f:w:x:", g_long_options, &long_option_index)) != -1)
+ {
+// DNBLogDebug("option: ch == %c (0x%2.2x) --%s%c%s\n",
+// ch, (uint8_t)ch,
+// g_long_options[long_option_index].name,
+// g_long_options[long_option_index].has_arg ? '=' : ' ',
+// optarg ? optarg : "");
+ switch (ch)
+ {
+ case 0: // Any optional that auto set themselves will return 0
+ break;
+
+ case 'c':
+ arch.SetArch(optarg);
+ if (!arch.IsValid())
+ {
+ Log::STDERR ("error: invalid arch string '%s'\n", optarg);
+ exit (8);
+ }
+ break;
+
+ case 'a':
+ if (optarg && optarg[0])
+ {
+ if (isdigit(optarg[0]))
+ {
+ char *end = NULL;
+ attach_pid = strtoul(optarg, &end, 0);
+ if (end == NULL || *end != '\0')
+ {
+ Log::STDERR ("error: invalid pid option '%s'\n", optarg);
+ exit (4);
+ }
+ }
+ else
+ {
+ attach_pid_name = optarg;
+ }
+ start_mode = eDCGSRunLoopModeInferiorAttaching;
+ }
+ break;
+
+ // --waitfor=NAME
+ case 'w':
+ if (optarg && optarg[0])
+ {
+ waitfor_pid_name = optarg;
+ start_mode = eDCGSRunLoopModeInferiorAttaching;
+ }
+ break;
+
+ // --waitfor-interval=USEC
+ case 'i':
+ if (optarg && optarg[0])
+ {
+ char *end = NULL;
+ waitfor_interval = strtoul(optarg, &end, 0);
+ if (end == NULL || *end != '\0')
+ {
+ Log::STDERR ("error: invalid waitfor-interval option value '%s'.\n", optarg);
+ exit (6);
+ }
+ }
+ break;
+
+ // --waitfor-duration=SEC
+ case 'd':
+ if (optarg && optarg[0])
+ {
+ char *end = NULL;
+ waitfor_duration = strtoul(optarg, &end, 0);
+ if (end == NULL || *end != '\0')
+ {
+ Log::STDERR ("error: invalid waitfor-duration option value '%s'.\n", optarg);
+ exit (7);
+ }
+ }
+ break;
+
+ case 'x':
+ if (optarg && optarg[0])
+ {
+ if (strcasecmp(optarg, "auto") == 0)
+ g_launch_flavor = eLaunchFlavorDefault;
+ else if (strcasestr(optarg, "posix") == optarg)
+ g_launch_flavor = eLaunchFlavorPosixSpawn;
+ else if (strcasestr(optarg, "fork") == optarg)
+ g_launch_flavor = eLaunchFlavorForkExec;
+#if defined (__arm__)
+ else if (strcasestr(optarg, "spring") == optarg)
+ g_launch_flavor = eLaunchFlavorSpringBoard;
+#endif
+ else
+ {
+ Log::STDERR ("error: invalid TYPE for the --launch=TYPE (-x TYPE) option: '%s'\n", optarg);
+ Log::STDERR ("Valid values TYPE are:\n");
+ Log::STDERR (" auto Auto-detect the best launch method to use.\n");
+ Log::STDERR (" posix Launch the executable using posix_spawn.\n");
+ Log::STDERR (" fork Launch the executable using fork and exec.\n");
+#if defined (__arm__)
+ Log::STDERR (" spring Launch the executable through Springboard.\n");
+#endif
+ exit (5);
+ }
+ }
+ break;
+
+ case 'l': // Set Log File
+ if (optarg && optarg[0])
+ {
+ if (strcasecmp(optarg, "stdout") == 0)
+ log_file = stdout;
+ else if (strcasecmp(optarg, "stderr") == 0)
+ log_file = stderr;
+ else
+ log_file = fopen(optarg, "w+");
+
+ if (log_file == NULL)
+ {
+ const char *errno_str = strerror(errno);
+ Log::STDERR ("Failed to open log file '%s' for writing: errno = %i (%s)", optarg, errno, errno_str ? errno_str : "unknown error");
+ }
+ }
+ break;
+
+ case 'f': // Log Flags
+ if (optarg && optarg[0])
+ log_flags = strtoul(optarg, NULL, 0);
+ break;
+
+ case 'g':
+ debug = 1;
+ //DNBLogSetDebug(1);
+ break;
+
+ case 't':
+ g_applist_opt = 1;
+ break;
+
+ case 'k':
+ g_lockdown_opt = 1;
+ break;
+
+ case 'v':
+ //DNBLogSetVerbose(1);
+ break;
+ }
+ }
+
+ // Skip any options we consumed with getopt_long
+ argc -= optind;
+ argv += optind;
+
+ // It is ok for us to set NULL as the logfile (this will disable any logging)
+
+// if (log_file != NULL)
+// {
+// DNBLogSetLogDCScriptInterpreter::Type(FileLogDCScriptInterpreter::Type, log_file);
+// // If our log file was set, yet we have no log flags, log everything!
+// if (log_flags == 0)
+// log_flags = LOG_ALL | LOG_DCGS_ALL;
+//
+// DNBLogSetLogMask (log_flags);
+// }
+// else
+// {
+// // Enable DNB logging
+// DNBLogSetLogDCScriptInterpreter::Type(ASLLogDCScriptInterpreter::Type, NULL);
+// DNBLogSetLogMask (log_flags);
+//
+// }
+
+ // as long as we're dropping remotenub in as a replacement for gdbserver,
+ // explicitly note that this is not gdbserver.
+
+ Log::STDOUT ("debugserver-%g \n", dc_gdbserverVersionNumber);
+ int listen_port = -1;
+ if (g_lockdown_opt == 0 && g_applist_opt == 0)
+ {
+ // Make sure we at least have port
+ if (argc < 1)
+ {
+ Log::STDERR ("Usage: %s host:port [program-name program-arg1 program-arg2 ...]\n", this_exe_name);
+ exit (1);
+ }
+ // accept 'localhost:' prefix on port number
+
+ std::string host_str;
+ std::string port_str(argv[0]);
+
+ // We just used the host:port arg...
+ argc--;
+ argv++;
+
+ size_t port_idx = port_str.find(':');
+ if (port_idx != std::string::npos)
+ {
+ host_str.assign(port_str, 0, port_idx);
+ port_str.erase(0, port_idx + 1);
+ }
+
+ if (port_str.empty())
+ {
+ Log::STDERR ("error: no port specified\nUsage: %s host:port [program-name program-arg1 program-arg2 ...]\n", this_exe_name);
+ exit (2);
+ }
+ else if (port_str.find_first_not_of("0123456789") != std::string::npos)
+ {
+ Log::STDERR ("error: port must be an integer: %s\nUsage: %s host:port [program-name program-arg1 program-arg2 ...]\n", port_str.c_str(), this_exe_name);
+ exit (3);
+ }
+ //DNBLogDebug("host_str = '%s' port_str = '%s'", host_str.c_str(), port_str.c_str());
+ listen_port = atoi (port_str.c_str());
+ }
+
+
+ // We must set up some communications now.
+
+ FileSpec exe_spec;
+ if (argv[0])
+ exe_spec.SetFile (argv[0]);
+
+ HandleBroadcastEventInfo info;
+ info.target_sp = TargetList::SharedList().CreateTarget(&exe_spec, &arch);
+ ProcessSP process_sp (info.target_sp->CreateProcess ());
+ info.remote_sp.reset (new GDBRemoteSession (process_sp));
+
+ info.remote_sp->SetLog (log);
+ StreamString sstr;
+ sstr.Printf("ConnectionFileDescriptor(%s)", argv[0]);
+
+ if (info.remote_sp.get() == NULL)
+ {
+ Log::STDERR ("error: failed to create a GDBRemoteSession class\n");
+ return -1;
+ }
+
+
+
+ // If we know we're waiting to attach, we don't need any of this other info.
+ if (start_mode != eDCGSRunLoopModeInferiorAttaching)
+ {
+ if (argc == 0 || g_lockdown_opt)
+ {
+ if (g_lockdown_opt != 0)
+ {
+ // Work around for SIGPIPE crashes due to posix_spawn issue. We have to close
+ // STDOUT and STDERR, else the first time we try and do any, we get SIGPIPE and
+ // die as posix_spawn is doing bad things with our file descriptors at the moment.
+ int null = open("/dev/null", O_RDWR);
+ dup2(null, STDOUT_FILENO);
+ dup2(null, STDERR_FILENO);
+ }
+ else if (g_applist_opt != 0)
+ {
+// // List all applications we are able to see
+// std::string applist_plist;
+// int err = ListApplications(applist_plist, false, false);
+// if (err == 0)
+// {
+// fputs (applist_plist.c_str(), stdout);
+// }
+// else
+// {
+// Log::STDERR ("error: ListApplications returned error %i\n", err);
+// }
+// // Exit with appropriate error if we were asked to list the applications
+// // with no other args were given (and we weren't trying to do this over
+// // lockdown)
+// return err;
+ return 0;
+ }
+
+ //DNBLogDebug("Get args from remote protocol...");
+ start_mode = eDCGSRunLoopModeGetStartModeFromRemoteProtocol;
+ }
+ else
+ {
+ start_mode = eDCGSRunLoopModeInferiorLaunching;
+ // Fill in the argv array in the context from the rest of our args.
+ // Skip the name of this executable and the port number
+ info.remote_sp->SetArguments (argc, argv);
+ }
+ }
+
+ if (start_mode == eDCGSRunLoopModeExit)
+ return -1;
+
+ info.mode = start_mode;
+
+ while (info.mode != eDCGSRunLoopModeExit)
+ {
+ switch (info.mode)
+ {
+ case eDCGSRunLoopModeGetStartModeFromRemoteProtocol:
+ #if defined (__arm__)
+ if (g_lockdown_opt)
+ {
+ if (!info.remote_sp->GetCommunication()->IsConnected())
+ {
+ if (info.remote_sp->GetCommunication()->ConnectToService () != gdb_success)
+ {
+ Log::STDERR ("Failed to get connection from a remote gdb process.\n");
+ info.mode = eDCGSRunLoopModeExit;
+ }
+ else if (g_applist_opt != 0)
+ {
+ // List all applications we are able to see
+ std::string applist_plist;
+ if (ListApplications(applist_plist, false, false) == 0)
+ {
+ //DNBLogDebug("Task list: %s", applist_plist.c_str());
+
+ info.remote_sp->GetCommunication()->Write(applist_plist.c_str(), applist_plist.size());
+ // Issue a read that will never yield any data until the other side
+ // closes the socket so this process doesn't just exit and cause the
+ // socket to close prematurely on the other end and cause data loss.
+ std::string buf;
+ info.remote_sp->GetCommunication()->Read(buf);
+ }
+ info.remote_sp->GetCommunication()->Disconnect(false);
+ info.mode = eDCGSRunLoopModeExit;
+ break;
+ }
+ else
+ {
+ // Start watching for remote packets
+ info.remote_sp->StartReadRemoteDataThread();
+ }
+ }
+ }
+ else
+#endif
+ {
+ if (StartListening (&info, listen_port))
+ Log::STDOUT ("Got a connection, waiting for process information for launching or attaching.\n");
+ else
+ info.mode = eDCGSRunLoopModeExit;
+ }
+
+ if (info.mode != eDCGSRunLoopModeExit)
+ GSRunLoopGetStartModeFromRemote (&info);
+ break;
+
+ case eDCGSRunLoopModeInferiorAttaching:
+ if (!waitfor_pid_name.empty())
+ {
+ // Set our end wait time if we are using a waitfor-duration
+ // option that may have been specified
+
+ TimeValue attach_timeout_abstime;
+ if (waitfor_duration != 0)
+ {
+ attach_timeout_abstime = TimeValue::Now();
+ attach_timeout_abstime.OffsetWithSeconds (waitfor_duration);
+ }
+ GSLaunchFlavor launch_flavor = g_launch_flavor;
+ if (launch_flavor == eLaunchFlavorDefault)
+ {
+ // Our default launch method is posix spawn
+ launch_flavor = eLaunchFlavorPosixSpawn;
+
+#if defined (__arm__)
+ // Check if we have an app bundle, if so launch using SpringBoard.
+ if (waitfor_pid_name.find (".app") != std::string::npos)
+ {
+ launch_flavor = eLaunchFlavorSpringBoard;
+ }
+#endif
+ }
+
+ //ctx.SetLaunchFlavor(launch_flavor);
+
+
+ lldb::pid_t pid = info.GetProcess()->Attach (waitfor_pid_name.c_str());
+ if (pid == LLDB_INVALID_PROCESS_ID)
+ {
+ info.GetRemote()->GetLaunchError() = info.GetProcess()->GetError();
+ Log::STDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), info.GetRemote()->GetLaunchError().AsCString());
+ info.mode = eDCGSRunLoopModeExit;
+ }
+ else
+ {
+ info.mode = eDCGSRunLoopModeInferiorExecuting;
+ }
+ }
+ else if (attach_pid != LLDB_INVALID_PROCESS_ID)
+ {
+ Log::STDOUT ("Attaching to process %i...\n", attach_pid);
+ info.mode = GSRunLoopLaunchAttaching (&info, attach_pid);
+ if (info.mode != eDCGSRunLoopModeInferiorExecuting)
+ {
+ const char *error_str = info.GetRemote()->GetLaunchError().AsCString();
+ Log::STDERR ("error: failed to attach process %i: %s\n", attach_pid, error_str ? error_str : "unknown error.");
+ info.mode = eDCGSRunLoopModeExit;
+ }
+ }
+ else if (!attach_pid_name.empty ())
+ {
+ lldb::pid_t pid = info.GetProcess()->Attach (waitfor_pid_name.c_str());
+ if (pid == LLDB_INVALID_PROCESS_ID)
+ {
+ info.GetRemote()->GetLaunchError() = info.GetProcess()->GetError();
+ Log::STDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), info.GetRemote()->GetLaunchError().AsCString());
+ info.mode = eDCGSRunLoopModeExit;
+ }
+ else
+ {
+ info.mode = eDCGSRunLoopModeInferiorExecuting;
+ }
+ }
+ else
+ {
+ Log::STDERR ("error: asked to attach with empty name and invalid PID.");
+ info.mode = eDCGSRunLoopModeExit;
+ }
+
+ if (info.mode != eDCGSRunLoopModeExit)
+ {
+ if (StartListening (&info, listen_port))
+ Log::STDOUT ("Got a connection, waiting for debugger instructions for process %d.\n", attach_pid);
+ else
+ info.mode = eDCGSRunLoopModeExit;
+ }
+ break;
+
+ case eDCGSRunLoopModeInferiorLaunching:
+ info.mode = GSRunLoopLaunchInferior (&info);
+
+ if (info.mode == eDCGSRunLoopModeInferiorExecuting)
+ {
+ if (StartListening (&info, listen_port))
+ Log::STDOUT ("Got a connection, waiting for debugger instructions for task \"%s\".\n", argv[0]);
+ else
+ info.mode = eDCGSRunLoopModeExit;
+ }
+ else
+ {
+ Log::STDERR ("error: failed to launch process %s: %s\n", argv[0], info.GetRemote()->GetLaunchError().AsCString());
+ }
+ break;
+
+ case eDCGSRunLoopModeInferiorExecuting:
+ GSRunLoopInferiorExecuting (&info);
+ break;
+
+ case eDCGSRunLoopModeInferiorKillOrDetach:
+ {
+ Process *process = info.GetProcess();
+ if (process && process->IsAlive())
+ {
+ process->Kill(SIGCONT);
+ process->Kill(SIGKILL);
+ }
+ }
+ info.mode = eDCGSRunLoopModeExit;
+ break;
+
+ default:
+ info.mode = eDCGSRunLoopModeExit;
+ case eDCGSRunLoopModeExit:
+ break;
+ }
+ }
+
+ return 0;
+}
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBServerLog.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBServerLog.cpp
new file mode 100644
index 00000000000..2d4116ebe7b
--- /dev/null
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBServerLog.cpp
@@ -0,0 +1,80 @@
+//===-- GDBServerLog.cpp ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//----------------------------------------------------------------------
+//
+// GDBServerLog.cpp
+// liblldb
+//
+// Created by Greg Clayton on 6/19/09.
+//
+//
+//----------------------------------------------------------------------
+
+#include "GDBServerLog.h"
+
+using namespace lldb;
+
+static Log *
+LogAccessor (bool get, Log *log)
+{
+ static Log* g_log = NULL; // Leak for now as auto_ptr was being cleaned up
+ // by global constructors before other threads
+ // were done with it.
+ if (get)
+ {
+// // Debug code below for enabling logging by default
+// if (g_log == NULL)
+// {
+// g_log = new Log("/dev/stdout", false);
+// g_log->GetMask().SetAllFlagBits(GS_LOG_ALL);
+// g_log->GetOptions().Set(LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_THREAD_NAME);
+// }
+ }
+ else
+ {
+ if (g_log)
+ delete g_log;
+ g_log = log;
+ }
+
+ return g_log;
+}
+
+Log *
+GDBServerLog::GetLogIfAllCategoriesSet (uint32_t mask)
+{
+ Log *log = LogAccessor (true, NULL);
+ if (log && mask)
+ {
+ uint32_t log_mask = log->GetMask().GetAllFlagBits();
+ if ((log_mask & mask) != mask)
+ return NULL;
+ }
+ return log;
+}
+
+void
+GDBServerLog::SetLog (Log *log)
+{
+ LogAccessor (false, log);
+}
+
+
+void
+GDBServerLog::LogIf (uint32_t mask, const char *format, ...)
+{
+ Log *log = GDBServerLog::GetLogIfAllCategoriesSet (mask);
+ if (log)
+ {
+ va_list args;
+ va_start (args, format);
+ log->VAPrintf (format, args);
+ va_end (args);
+ }
+}
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBServerLog.h b/lldb/source/Plugins/Process/gdb-remote/GDBServerLog.h
new file mode 100644
index 00000000000..3dec8088cef
--- /dev/null
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBServerLog.h
@@ -0,0 +1,55 @@
+//===-- GDBServerLog.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//----------------------------------------------------------------------
+//
+// GDBServerLog.h
+// liblldb
+//
+// Created by Greg Clayton on 6/19/09.
+//
+//
+//----------------------------------------------------------------------
+
+#ifndef liblldb_GDBServerLog_h_
+#define liblldb_GDBServerLog_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+
+#include "lldb/Core/Log.h"
+
+// Project includes
+#define GS_LOG_VERBOSE (1u << 0)
+#define GS_LOG_DEBUG (1u << 1)
+#define GS_LOG_PACKETS (1u << 2)
+#define GS_LOG_EVENTS (1u << 3)
+#define GS_LOG_MINIMAL (1u << 4)
+#define GS_LOG_ALL (UINT32_MAX)
+#define GS_LOG_DEFAULT (GS_LOG_VERBOSE |\
+ GS_LOG_PACKETS)
+
+namespace lldb {
+
+class GDBServerLog
+{
+public:
+ static Log *
+ GetLog (uint32_t mask = 0);
+
+ static void
+ SetLog (Log *log);
+
+ static void
+ LogIf (uint32_t mask, const char *format, ...);
+};
+
+} // namespace lldb
+
+#endif // liblldb_GDBServerLog_h_
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
new file mode 100644
index 00000000000..e08679c1e1c
--- /dev/null
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -0,0 +1,2272 @@
+//===-- ProcessGDBRemote.cpp ------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// C Includes
+#include <errno.h>
+#include <mach/mach.h>
+#include <mach-o/dyld.h>
+#include <spawn.h>
+#include <sys/fcntl.h>
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <unistd.h>
+
+// C++ Includes
+#include <algorithm>
+#include <map>
+
+// Other libraries and framework includes
+
+#include "lldb/Breakpoint/WatchpointLocation.h"
+#include "lldb/Core/Args.h"
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/ConnectionFileDescriptor.h"
+#include "lldb/Core/FileSpec.h"
+#include "lldb/Core/InputReader.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/State.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Core/Timer.h"
+#include "lldb/Host/TimeValue.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/TargetList.h"
+#include "PseudoTerminal.h"
+
+// Project includes
+#include "lldb/Host/Host.h"
+#include "StringExtractorGDBRemote.h"
+#include "GDBRemoteRegisterContext.h"
+#include "ProcessGDBRemote.h"
+#include "ProcessGDBRemoteLog.h"
+#include "ThreadGDBRemote.h"
+#include "libunwind.h"
+#include "MacOSXLibunwindCallbacks.h"
+
+#if defined (__i386__) || defined (__x86_64__)
+#define MACH_EXC_DATA0_SOFTWARE_BREAKPOINT EXC_I386_BPT
+#define MACH_EXC_DATA0_TRACE EXC_I386_SGL
+#elif defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__)
+#define MACH_EXC_DATA0_SOFTWARE_BREAKPOINT EXC_PPC_BREAKPOINT
+#elif defined (__arm__)
+#define MACH_EXC_DATA0_SOFTWARE_BREAKPOINT EXC_ARM_BREAKPOINT
+#endif
+
+
+#define DEBUGSERVER_BASENAME "debugserver"
+using namespace lldb;
+using namespace lldb_private;
+
+static inline uint16_t
+get_random_port ()
+{
+ return (arc4random() % (UINT16_MAX - 1000u)) + 1000u;
+}
+
+
+const char *
+ProcessGDBRemote::GetPluginNameStatic()
+{
+ return "process.gdb-remote";
+}
+
+const char *
+ProcessGDBRemote::GetPluginDescriptionStatic()
+{
+ return "GDB Remote protocol based debugging plug-in.";
+}
+
+void
+ProcessGDBRemote::Terminate()
+{
+ PluginManager::UnregisterPlugin (ProcessGDBRemote::CreateInstance);
+}
+
+
+Process*
+ProcessGDBRemote::CreateInstance (Target &target, Listener &listener)
+{
+ return new ProcessGDBRemote (target, listener);
+}
+
+bool
+ProcessGDBRemote::CanDebug(Target &target)
+{
+ // For now we are just making sure the file exists for a given module
+ ModuleSP exe_module_sp(target.GetExecutableModule());
+ if (exe_module_sp.get())
+ return exe_module_sp->GetFileSpec().Exists();
+ return false;
+}
+
+//----------------------------------------------------------------------
+// ProcessGDBRemote constructor
+//----------------------------------------------------------------------
+ProcessGDBRemote::ProcessGDBRemote(Target& target, Listener &listener) :
+ Process (target, listener),
+ m_dynamic_loader_ap (),
+ m_byte_order (eByteOrderHost),
+ m_flags (0),
+ m_stdio_communication ("gdb-remote.stdio"),
+ m_stdio_mutex (Mutex::eMutexTypeRecursive),
+ m_stdout_data (),
+ m_arch_spec (),
+ m_gdb_comm(),
+ m_debugserver_pid (LLDB_INVALID_PROCESS_ID),
+ m_debugserver_monitor (0),
+ m_register_info (),
+ m_curr_tid (LLDB_INVALID_THREAD_ID),
+ m_curr_tid_run (LLDB_INVALID_THREAD_ID),
+ m_async_broadcaster ("lldb.process.gdb-remote.async-broadcaster"),
+ m_async_thread (LLDB_INVALID_HOST_THREAD),
+ m_z0_supported (1),
+ m_continue_packet(),
+ m_dispatch_queue_offsets_addr (LLDB_INVALID_ADDRESS),
+ m_libunwind_target_type (UNW_TARGET_UNSPECIFIED),
+ m_libunwind_addr_space (NULL),
+ m_waiting_for_attach (false),
+ m_packet_timeout (1),
+ m_max_memory_size (512)
+{
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+ProcessGDBRemote::~ProcessGDBRemote()
+{
+ // m_mach_process.UnregisterNotificationCallbacks (this);
+ Clear();
+}
+
+//----------------------------------------------------------------------
+// PluginInterface
+//----------------------------------------------------------------------
+const char *
+ProcessGDBRemote::GetPluginName()
+{
+ return "Process debugging plug-in that uses the GDB remote protocol";
+}
+
+const char *
+ProcessGDBRemote::GetShortPluginName()
+{
+ return GetPluginNameStatic();
+}
+
+uint32_t
+ProcessGDBRemote::GetPluginVersion()
+{
+ return 1;
+}
+
+void
+ProcessGDBRemote::GetPluginCommandHelp (const char *command, Stream *strm)
+{
+ strm->Printf("TODO: fill this in\n");
+}
+
+Error
+ProcessGDBRemote::ExecutePluginCommand (Args &command, Stream *strm)
+{
+ Error error;
+ error.SetErrorString("No plug-in commands are currently supported.");
+ return error;
+}
+
+Log *
+ProcessGDBRemote::EnablePluginLogging (Stream *strm, Args &command)
+{
+ return NULL;
+}
+
+void
+ProcessGDBRemote::BuildDynamicRegisterInfo ()
+{
+ char register_info_command[64];
+ m_register_info.Clear();
+ StringExtractorGDBRemote::Type packet_type = StringExtractorGDBRemote::eResponse;
+ uint32_t reg_offset = 0;
+ uint32_t reg_num = 0;
+ for (; packet_type == StringExtractorGDBRemote::eResponse; ++reg_num)
+ {
+ ::snprintf (register_info_command, sizeof(register_info_command), "qRegisterInfo%x", reg_num);
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(register_info_command, response, 2, false))
+ {
+ packet_type = response.GetType();
+ if (packet_type == StringExtractorGDBRemote::eResponse)
+ {
+ std::string name;
+ std::string value;
+ ConstString reg_name;
+ ConstString alt_name;
+ ConstString set_name;
+ RegisterInfo reg_info = { NULL, // Name
+ NULL, // Alt name
+ 0, // byte size
+ reg_offset, // offset
+ eEncodingUint, // encoding
+ eFormatHex, // formate
+ reg_num, // native register number
+ {
+ LLDB_INVALID_REGNUM, // GCC reg num
+ LLDB_INVALID_REGNUM, // DWARF reg num
+ LLDB_INVALID_REGNUM, // generic reg num
+ reg_num // GDB reg num
+ }
+ };
+
+ while (response.GetNameColonValue(name, value))
+ {
+ if (name.compare("name") == 0)
+ {
+ reg_name.SetCString(value.c_str());
+ }
+ else if (name.compare("alt-name") == 0)
+ {
+ alt_name.SetCString(value.c_str());
+ }
+ else if (name.compare("bitsize") == 0)
+ {
+ reg_info.byte_size = Args::StringToUInt32(value.c_str(), 0, 0) / CHAR_BIT;
+ }
+ else if (name.compare("offset") == 0)
+ {
+ uint32_t offset = Args::StringToUInt32(value.c_str(), UINT32_MAX, 0);
+ if (offset != offset)
+ {
+ reg_offset = offset;
+ reg_info.byte_offset = offset;
+ }
+ }
+ else if (name.compare("encoding") == 0)
+ {
+ if (value.compare("uint") == 0)
+ reg_info.encoding = eEncodingUint;
+ else if (value.compare("sint") == 0)
+ reg_info.encoding = eEncodingSint;
+ else if (value.compare("ieee754") == 0)
+ reg_info.encoding = eEncodingIEEE754;
+ else if (value.compare("vector") == 0)
+ reg_info.encoding = eEncodingVector;
+ }
+ else if (name.compare("format") == 0)
+ {
+ if (value.compare("binary") == 0)
+ reg_info.format = eFormatBinary;
+ else if (value.compare("decimal") == 0)
+ reg_info.format = eFormatDecimal;
+ else if (value.compare("hex") == 0)
+ reg_info.format = eFormatHex;
+ else if (value.compare("float") == 0)
+ reg_info.format = eFormatFloat;
+ else if (value.compare("vector-sint8") == 0)
+ reg_info.format = eFormatVectorOfSInt8;
+ else if (value.compare("vector-uint8") == 0)
+ reg_info.format = eFormatVectorOfUInt8;
+ else if (value.compare("vector-sint16") == 0)
+ reg_info.format = eFormatVectorOfSInt16;
+ else if (value.compare("vector-uint16") == 0)
+ reg_info.format = eFormatVectorOfUInt16;
+ else if (value.compare("vector-sint32") == 0)
+ reg_info.format = eFormatVectorOfSInt32;
+ else if (value.compare("vector-uint32") == 0)
+ reg_info.format = eFormatVectorOfUInt32;
+ else if (value.compare("vector-float32") == 0)
+ reg_info.format = eFormatVectorOfFloat32;
+ else if (value.compare("vector-uint128") == 0)
+ reg_info.format = eFormatVectorOfUInt128;
+ }
+ else if (name.compare("set") == 0)
+ {
+ set_name.SetCString(value.c_str());
+ }
+ else if (name.compare("gcc") == 0)
+ {
+ reg_info.kinds[eRegisterKindGCC] = Args::StringToUInt32(value.c_str(), LLDB_INVALID_REGNUM, 0);
+ }
+ else if (name.compare("dwarf") == 0)
+ {
+ reg_info.kinds[eRegisterKindDWARF] = Args::StringToUInt32(value.c_str(), LLDB_INVALID_REGNUM, 0);
+ }
+ else if (name.compare("generic") == 0)
+ {
+ if (value.compare("pc") == 0)
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC;
+ else if (value.compare("sp") == 0)
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP;
+ else if (value.compare("fp") == 0)
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP;
+ else if (value.compare("ra") == 0)
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA;
+ else if (value.compare("flags") == 0)
+ reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS;
+ }
+ }
+
+ assert (reg_info.byte_size != 0);
+ reg_offset += reg_info.byte_size;
+ m_register_info.AddRegister(reg_info, reg_name, alt_name, set_name);
+ }
+ }
+ else
+ {
+ packet_type = StringExtractorGDBRemote::eError;
+ }
+ }
+
+ if (reg_num == 0)
+ {
+ // We didn't get anything. See if we are debugging ARM and fill with
+ // a hard coded register set until we can get an updated debugserver
+ // down on the devices.
+ ArchSpec arm_arch ("arm");
+ if (GetTarget().GetArchitecture() == arm_arch)
+ m_register_info.HardcodeARMRegisters();
+ }
+ m_register_info.Finalize ();
+}
+
+Error
+ProcessGDBRemote::WillLaunch (Module* module)
+{
+ return WillLaunchOrAttach ();
+}
+
+Error
+ProcessGDBRemote::WillAttach (lldb::pid_t pid)
+{
+ return WillLaunchOrAttach ();
+}
+
+Error
+ProcessGDBRemote::WillAttach (const char *process_name, bool wait_for_launch)
+{
+ return WillLaunchOrAttach ();
+}
+
+Error
+ProcessGDBRemote::WillLaunchOrAttach ()
+{
+ Error error;
+ // TODO: this is hardcoded for macosx right now. We need this to be more dynamic
+ m_dynamic_loader_ap.reset(DynamicLoader::FindPlugin(this, "dynamic-loader.macosx-dyld"));
+
+ if (m_dynamic_loader_ap.get() == NULL)
+ error.SetErrorString("unable to find the dynamic loader named 'dynamic-loader.macosx-dyld'");
+ m_stdio_communication.Clear ();
+
+ return error;
+}
+
+//----------------------------------------------------------------------
+// Process Control
+//----------------------------------------------------------------------
+Error
+ProcessGDBRemote::DoLaunch
+(
+ Module* module,
+ char const *argv[],
+ char const *envp[],
+ const char *stdin_path,
+ const char *stdout_path,
+ const char *stderr_path
+)
+{
+ // ::LogSetBitMask (GDBR_LOG_DEFAULT);
+ // ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD);
+ // ::LogSetLogFile ("/dev/stdout");
+ Error error;
+
+ ObjectFile * object_file = module->GetObjectFile();
+ if (object_file)
+ {
+ ArchSpec inferior_arch(module->GetArchitecture());
+ char host_port[128];
+ snprintf (host_port, sizeof(host_port), "localhost:%u", get_random_port ());
+
+ bool start_debugserver_with_inferior_args = false;
+ if (start_debugserver_with_inferior_args)
+ {
+ // We want to launch debugserver with the inferior program and its
+ // arguments on the command line. We should only do this if we
+ // the GDB server we are talking to doesn't support the 'A' packet.
+ error = StartDebugserverProcess (host_port,
+ argv,
+ envp,
+ NULL, //stdin_path,
+ LLDB_INVALID_PROCESS_ID,
+ NULL, false,
+ inferior_arch);
+ if (error.Fail())
+ return error;
+
+ error = ConnectToDebugserver (host_port);
+ if (error.Success())
+ {
+ SetID (m_gdb_comm.GetCurrentProcessID (m_packet_timeout));
+ }
+ }
+ else
+ {
+ error = StartDebugserverProcess (host_port,
+ NULL,
+ NULL,
+ NULL, //stdin_path,
+ LLDB_INVALID_PROCESS_ID,
+ NULL, false,
+ inferior_arch);
+ if (error.Fail())
+ return error;
+
+ error = ConnectToDebugserver (host_port);
+ if (error.Success())
+ {
+ // Send the environment and the program + arguments after we connect
+ if (envp)
+ {
+ const char *env_entry;
+ for (int i=0; (env_entry = envp[i]); ++i)
+ {
+ if (m_gdb_comm.SendEnvironmentPacket(env_entry, m_packet_timeout) != 0)
+ break;
+ }
+ }
+
+ const uint32_t arg_timeout_seconds = 10;
+ int arg_packet_err = m_gdb_comm.SendArgumentsPacket (argv, arg_timeout_seconds);
+ if (arg_packet_err == 0)
+ {
+ std::string error_str;
+ if (m_gdb_comm.GetLaunchSuccess (m_packet_timeout, error_str))
+ {
+ SetID (m_gdb_comm.GetCurrentProcessID (m_packet_timeout));
+ }
+ else
+ {
+ error.SetErrorString (error_str.c_str());
+ }
+ }
+ else
+ {
+ error.SetErrorStringWithFormat("'A' packet returned an error: %i.\n", arg_packet_err);
+ }
+
+ SetID (m_gdb_comm.GetCurrentProcessID (m_packet_timeout));
+ }
+ }
+
+ if (GetID() == LLDB_INVALID_PROCESS_ID)
+ {
+ KillDebugserverProcess ();
+ return error;
+ }
+
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse("?", 1, response, m_packet_timeout, false))
+ SetPrivateState (SetThreadStopInfo (response));
+
+ }
+ else
+ {
+ // Set our user ID to an invalid process ID.
+ SetID(LLDB_INVALID_PROCESS_ID);
+ error.SetErrorStringWithFormat("Failed to get object file from '%s' for arch %s.\n", module->GetFileSpec().GetFilename().AsCString(), module->GetArchitecture().AsCString());
+ }
+
+ // Return the process ID we have
+ return error;
+}
+
+
+Error
+ProcessGDBRemote::ConnectToDebugserver (const char *host_port)
+{
+ Error error;
+ // Sleep and wait a bit for debugserver to start to listen...
+ std::auto_ptr<ConnectionFileDescriptor> conn_ap(new ConnectionFileDescriptor());
+ if (conn_ap.get())
+ {
+ std::string connect_url("connect://");
+ connect_url.append (host_port);
+ const uint32_t max_retry_count = 50;
+ uint32_t retry_count = 0;
+ while (!m_gdb_comm.IsConnected())
+ {
+ if (conn_ap->Connect(connect_url.c_str(), &error) == eConnectionStatusSuccess)
+ {
+ m_gdb_comm.SetConnection (conn_ap.release());
+ break;
+ }
+ retry_count++;
+
+ if (retry_count >= max_retry_count)
+ break;
+
+ usleep (100000);
+ }
+ }
+
+ if (!m_gdb_comm.IsConnected())
+ {
+ if (error.Success())
+ error.SetErrorString("not connected to remote gdb server");
+ return error;
+ }
+
+ m_gdb_comm.SetAckMode (true);
+ if (m_gdb_comm.StartReadThread(&error))
+ {
+ // Send an initial ack
+ m_gdb_comm.SendAck('+');
+
+ if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID)
+ m_debugserver_monitor = Host::StartMonitoringChildProcess (MonitorDebugserverProcess,
+ (void*)(intptr_t)GetID(), // Pass the inferior pid in the thread argument (which is a void *)
+ m_debugserver_pid,
+ false);
+
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse("QStartNoAckMode", response, 1, false))
+ {
+ if (response.IsOKPacket())
+ m_gdb_comm.SetAckMode (false);
+ }
+
+ BuildDynamicRegisterInfo ();
+ }
+ return error;
+}
+
+void
+ProcessGDBRemote::DidLaunchOrAttach ()
+{
+ ProcessGDBRemoteLog::LogIf (GDBR_LOG_PROCESS, "ProcessGDBRemote::DidLaunch()");
+ if (GetID() == LLDB_INVALID_PROCESS_ID)
+ {
+ m_dynamic_loader_ap.reset();
+ }
+ else
+ {
+ m_dispatch_queue_offsets_addr = LLDB_INVALID_ADDRESS;
+
+ Module * exe_module = GetTarget().GetExecutableModule ().get();
+ assert(exe_module);
+
+ m_arch_spec = exe_module->GetArchitecture();
+
+ ObjectFile *exe_objfile = exe_module->GetObjectFile();
+ assert(exe_objfile);
+
+ m_byte_order = exe_objfile->GetByteOrder();
+ assert (m_byte_order != eByteOrderInvalid);
+
+ StreamString strm;
+
+ ArchSpec inferior_arch;
+ // See if the GDB server supports the qHostInfo information
+ const char *vendor = m_gdb_comm.GetVendorString().AsCString();
+ const char *os_type = m_gdb_comm.GetOSString().AsCString();
+
+ if (m_arch_spec.IsValid() && m_arch_spec == ArchSpec ("arm"))
+ {
+ // For ARM we can't trust the arch of the process as it could
+ // have an armv6 object file, but be running on armv7 kernel.
+ inferior_arch = m_gdb_comm.GetHostArchitecture();
+ }
+
+ if (!inferior_arch.IsValid())
+ inferior_arch = m_arch_spec;
+
+ if (vendor == NULL)
+ vendor = Host::GetVendorString().AsCString("apple");
+
+ if (os_type == NULL)
+ os_type = Host::GetOSString().AsCString("darwin");
+
+ strm.Printf ("%s-%s-%s", inferior_arch.AsCString(), vendor, os_type);
+
+ std::transform (strm.GetString().begin(),
+ strm.GetString().end(),
+ strm.GetString().begin(),
+ ::tolower);
+
+ m_target_triple.SetCString(strm.GetString().c_str());
+ }
+}
+
+void
+ProcessGDBRemote::DidLaunch ()
+{
+ DidLaunchOrAttach ();
+ if (m_dynamic_loader_ap.get())
+ m_dynamic_loader_ap->DidLaunch();
+}
+
+Error
+ProcessGDBRemote::DoAttach (pid_t attach_pid)
+{
+ Error error;
+ // Clear out and clean up from any current state
+ Clear();
+ // HACK: require arch be set correctly at the target level until we can
+ // figure out a good way to determine the arch of what we are attaching to
+ m_arch_spec = m_target.GetArchitecture();
+
+ //Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS);
+ if (attach_pid != LLDB_INVALID_PROCESS_ID)
+ {
+ SetPrivateState (eStateAttaching);
+ char host_port[128];
+ snprintf (host_port, sizeof(host_port), "localhost:%u", get_random_port ());
+ error = StartDebugserverProcess (host_port,
+ NULL,
+ NULL,
+ NULL,
+ LLDB_INVALID_PROCESS_ID,
+ NULL, false,
+ m_arch_spec);
+
+ if (error.Fail())
+ {
+ const char *error_string = error.AsCString();
+ if (error_string == NULL)
+ error_string = "unable to launch " DEBUGSERVER_BASENAME;
+
+ SetExitStatus (-1, error_string);
+ }
+ else
+ {
+ error = ConnectToDebugserver (host_port);
+ if (error.Success())
+ {
+ char packet[64];
+ const int packet_len = ::snprintf (packet, sizeof(packet), "vAttach;%x", attach_pid);
+ StringExtractorGDBRemote response;
+ StateType stop_state = m_gdb_comm.SendContinuePacketAndWaitForResponse (this,
+ packet,
+ packet_len,
+ response);
+ switch (stop_state)
+ {
+ case eStateStopped:
+ case eStateCrashed:
+ case eStateSuspended:
+ SetID (attach_pid);
+ m_last_stop_packet = response;
+ m_last_stop_packet.SetFilePos (0);
+ SetPrivateState (stop_state);
+ break;
+
+ case eStateExited:
+ m_last_stop_packet = response;
+ m_last_stop_packet.SetFilePos (0);
+ response.SetFilePos(1);
+ SetExitStatus(response.GetHexU8(), NULL);
+ break;
+
+ default:
+ SetExitStatus(-1, "unable to attach to process");
+ break;
+ }
+
+ }
+ }
+ }
+
+ lldb::pid_t pid = GetID();
+ if (pid == LLDB_INVALID_PROCESS_ID)
+ {
+ KillDebugserverProcess();
+ }
+ return error;
+}
+
+size_t
+ProcessGDBRemote::AttachInputReaderCallback
+(
+ void *baton,
+ InputReader *reader,
+ lldb::InputReaderAction notification,
+ const char *bytes,
+ size_t bytes_len
+)
+{
+ if (notification == eInputReaderGotToken)
+ {
+ ProcessGDBRemote *gdb_process = (ProcessGDBRemote *)baton;
+ if (gdb_process->m_waiting_for_attach)
+ gdb_process->m_waiting_for_attach = false;
+ reader->SetIsDone(true);
+ return 1;
+ }
+ return 0;
+}
+
+Error
+ProcessGDBRemote::DoAttach (const char *process_name, bool wait_for_launch)
+{
+ Error error;
+ // Clear out and clean up from any current state
+ Clear();
+ // HACK: require arch be set correctly at the target level until we can
+ // figure out a good way to determine the arch of what we are attaching to
+ m_arch_spec = m_target.GetArchitecture();
+
+ //Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS);
+ if (process_name && process_name[0])
+ {
+
+ SetPrivateState (eStateAttaching);
+ char host_port[128];
+ snprintf (host_port, sizeof(host_port), "localhost:%u", get_random_port ());
+ error = StartDebugserverProcess (host_port,
+ NULL,
+ NULL,
+ NULL,
+ LLDB_INVALID_PROCESS_ID,
+ NULL, false,
+ m_arch_spec);
+ if (error.Fail())
+ {
+ const char *error_string = error.AsCString();
+ if (error_string == NULL)
+ error_string = "unable to launch " DEBUGSERVER_BASENAME;
+
+ SetExitStatus (-1, error_string);
+ }
+ else
+ {
+ error = ConnectToDebugserver (host_port);
+ if (error.Success())
+ {
+ StreamString packet;
+
+ packet.PutCString("vAttach");
+ if (wait_for_launch)
+ packet.PutCString("Wait");
+ packet.PutChar(';');
+ packet.PutBytesAsRawHex8(process_name, strlen(process_name), eByteOrderHost, eByteOrderHost);
+ StringExtractorGDBRemote response;
+ StateType stop_state = m_gdb_comm.SendContinuePacketAndWaitForResponse (this,
+ packet.GetData(),
+ packet.GetSize(),
+ response);
+ switch (stop_state)
+ {
+ case eStateStopped:
+ case eStateCrashed:
+ case eStateSuspended:
+ SetID (m_gdb_comm.GetCurrentProcessID(m_packet_timeout));
+ m_last_stop_packet = response;
+ m_last_stop_packet.SetFilePos (0);
+ SetPrivateState (stop_state);
+ break;
+
+ case eStateExited:
+ m_last_stop_packet = response;
+ m_last_stop_packet.SetFilePos (0);
+ response.SetFilePos(1);
+ SetExitStatus(response.GetHexU8(), NULL);
+ break;
+
+ default:
+ SetExitStatus(-1, "unable to attach to process");
+ break;
+ }
+ }
+ }
+ }
+
+ lldb::pid_t pid = GetID();
+ if (pid == LLDB_INVALID_PROCESS_ID)
+ {
+ KillDebugserverProcess();
+ }
+ return error;
+}
+
+//
+// if (wait_for_launch)
+// {
+// InputReaderSP reader_sp (new InputReader());
+// StreamString instructions;
+// instructions.Printf("Hit any key to cancel waiting for '%s' to launch...", process_name);
+// error = reader_sp->Initialize (AttachInputReaderCallback, // callback
+// this, // baton
+// eInputReaderGranularityByte,
+// NULL, // End token
+// false);
+//
+// StringExtractorGDBRemote response;
+// m_waiting_for_attach = true;
+// FILE *reader_out_fh = reader_sp->GetOutputFileHandle();
+// while (m_waiting_for_attach)
+// {
+// // Wait for one second for the stop reply packet
+// if (m_gdb_comm.WaitForPacket(response, 1))
+// {
+// // Got some sort of packet, see if it is the stop reply packet?
+// char ch = response.GetChar(0);
+// if (ch == 'T')
+// {
+// m_waiting_for_attach = false;
+// }
+// }
+// else
+// {
+// // Put a period character every second
+// fputc('.', reader_out_fh);
+// }
+// }
+// }
+// }
+// return GetID();
+//}
+
+void
+ProcessGDBRemote::DidAttach ()
+{
+ DidLaunchOrAttach ();
+ if (m_dynamic_loader_ap.get())
+ m_dynamic_loader_ap->DidAttach();
+}
+
+Error
+ProcessGDBRemote::WillResume ()
+{
+ m_continue_packet.Clear();
+ // Start the continue packet we will use to run the target. Each thread
+ // will append what it is supposed to be doing to this packet when the
+ // ThreadList::WillResume() is called. If a thread it supposed
+ // to stay stopped, then don't append anything to this string.
+ m_continue_packet.Printf("vCont");
+ return Error();
+}
+
+Error
+ProcessGDBRemote::DoResume ()
+{
+ ProcessGDBRemoteLog::LogIf (GDBR_LOG_PROCESS, "ProcessGDBRemote::Resume()");
+ m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (m_continue_packet.GetData(), m_continue_packet.GetSize()));
+ return Error();
+}
+
+size_t
+ProcessGDBRemote::GetSoftwareBreakpointTrapOpcode (BreakpointSite* bp_site)
+{
+ const uint8_t *trap_opcode = NULL;
+ uint32_t trap_opcode_size = 0;
+
+ static const uint8_t g_arm_breakpoint_opcode[] = { 0xFE, 0xDE, 0xFF, 0xE7 };
+ //static const uint8_t g_thumb_breakpooint_opcode[] = { 0xFE, 0xDE };
+ static const uint8_t g_ppc_breakpoint_opcode[] = { 0x7F, 0xC0, 0x00, 0x08 };
+ static const uint8_t g_i386_breakpoint_opcode[] = { 0xCC };
+
+ switch (m_arch_spec.GetCPUType())
+ {
+ case CPU_TYPE_ARM:
+ // TODO: fill this in for ARM. We need to dig up the symbol for
+ // the address in the breakpoint locaiton and figure out if it is
+ // an ARM or Thumb breakpoint.
+ trap_opcode = g_arm_breakpoint_opcode;
+ trap_opcode_size = sizeof(g_arm_breakpoint_opcode);
+ break;
+
+ case CPU_TYPE_POWERPC:
+ case CPU_TYPE_POWERPC64:
+ trap_opcode = g_ppc_breakpoint_opcode;
+ trap_opcode_size = sizeof(g_ppc_breakpoint_opcode);
+ break;
+
+ case CPU_TYPE_I386:
+ case CPU_TYPE_X86_64:
+ trap_opcode = g_i386_breakpoint_opcode;
+ trap_opcode_size = sizeof(g_i386_breakpoint_opcode);
+ break;
+
+ default:
+ assert(!"Unhandled architecture in ProcessGDBRemote::GetSoftwareBreakpointTrapOpcode()");
+ return 0;
+ }
+
+ if (trap_opcode && trap_opcode_size)
+ {
+ if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size))
+ return trap_opcode_size;
+ }
+ return 0;
+}
+
+uint32_t
+ProcessGDBRemote::UpdateThreadListIfNeeded ()
+{
+ // locker will keep a mutex locked until it goes out of scope
+ Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_THREAD);
+ if (log && log->GetMask().IsSet(GDBR_LOG_VERBOSE))
+ log->Printf ("ProcessGDBRemote::%s (pid = %i)", __FUNCTION__, GetID());
+
+ const uint32_t stop_id = GetStopID();
+ if (m_thread_list.GetSize(false) == 0 || stop_id != m_thread_list.GetStopID())
+ {
+ // Update the thread list's stop id immediately so we don't recurse into this function.
+ ThreadList curr_thread_list (this);
+ curr_thread_list.SetStopID(stop_id);
+
+ Error err;
+ StringExtractorGDBRemote response;
+ for (m_gdb_comm.SendPacketAndWaitForResponse("qfThreadInfo", response, 1, false);
+ response.IsNormalPacket();
+ m_gdb_comm.SendPacketAndWaitForResponse("qsThreadInfo", response, 1, false))
+ {
+ char ch = response.GetChar();
+ if (ch == 'l')
+ break;
+ if (ch == 'm')
+ {
+ do
+ {
+ tid_t tid = response.GetHexMaxU32(false, LLDB_INVALID_THREAD_ID);
+
+ if (tid != LLDB_INVALID_THREAD_ID)
+ {
+ ThreadSP thread_sp (GetThreadList().FindThreadByID (tid, false));
+ if (thread_sp)
+ thread_sp->GetRegisterContext()->Invalidate();
+ else
+ thread_sp.reset (new ThreadGDBRemote (*this, tid));
+ curr_thread_list.AddThread(thread_sp);
+ }
+
+ ch = response.GetChar();
+ } while (ch == ',');
+ }
+ }
+
+ m_thread_list = curr_thread_list;
+
+ SetThreadStopInfo (m_last_stop_packet);
+ }
+ return GetThreadList().GetSize(false);
+}
+
+
+StateType
+ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet)
+{
+ const char stop_type = stop_packet.GetChar();
+ switch (stop_type)
+ {
+ case 'T':
+ case 'S':
+ {
+ // Stop with signal and thread info
+ const uint8_t signo = stop_packet.GetHexU8();
+ std::string name;
+ std::string value;
+ std::string thread_name;
+ uint32_t exc_type = 0;
+ std::vector<uint64_t> exc_data;
+ uint32_t tid = LLDB_INVALID_THREAD_ID;
+ addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS;
+ uint32_t exc_data_count = 0;
+ while (stop_packet.GetNameColonValue(name, value))
+ {
+ if (name.compare("metype") == 0)
+ {
+ // exception type in big endian hex
+ exc_type = Args::StringToUInt32 (value.c_str(), 0, 16);
+ }
+ else if (name.compare("mecount") == 0)
+ {
+ // exception count in big endian hex
+ exc_data_count = Args::StringToUInt32 (value.c_str(), 0, 16);
+ }
+ else if (name.compare("medata") == 0)
+ {
+ // exception data in big endian hex
+ exc_data.push_back(Args::StringToUInt64 (value.c_str(), 0, 16));
+ }
+ else if (name.compare("thread") == 0)
+ {
+ // thread in big endian hex
+ tid = Args::StringToUInt32 (value.c_str(), 0, 16);
+ }
+ else if (name.compare("name") == 0)
+ {
+ thread_name.swap (value);
+ }
+ else if (name.compare("dispatchqaddr") == 0)
+ {
+ thread_dispatch_qaddr = Args::StringToUInt64 (value.c_str(), 0, 16);
+ }
+ }
+ ThreadSP thread_sp (m_thread_list.FindThreadByID(tid, false));
+
+ if (thread_sp)
+ {
+ ThreadGDBRemote *gdb_thread = static_cast<ThreadGDBRemote *> (thread_sp.get());
+
+ gdb_thread->SetThreadDispatchQAddr (thread_dispatch_qaddr);
+ gdb_thread->SetName (thread_name.empty() ? thread_name.c_str() : NULL);
+ Thread::StopInfo& stop_info = gdb_thread->GetStopInfoRef();
+ gdb_thread->SetStopInfoStopID (GetStopID());
+ if (exc_type != 0)
+ {
+ if (exc_type == EXC_SOFTWARE && exc_data.size() == 2 && exc_data[0] == EXC_SOFT_SIGNAL)
+ {
+ stop_info.SetStopReasonWithSignal(exc_data[1]);
+ }
+#if defined (MACH_EXC_DATA0_SOFTWARE_BREAKPOINT)
+ else if (exc_type == EXC_BREAKPOINT && exc_data[0] == MACH_EXC_DATA0_SOFTWARE_BREAKPOINT)
+ {
+ addr_t pc = gdb_thread->GetRegisterContext()->GetPC();
+ user_id_t break_id = GetBreakpointSiteList().FindIDByAddress(pc);
+ if (break_id == LLDB_INVALID_BREAK_ID)
+ {
+ //log->Printf("got EXC_BREAKPOINT at 0x%llx but didn't find a breakpoint site.\n", pc);
+ stop_info.SetStopReasonWithException(exc_type, exc_data.size());
+ for (uint32_t i=0; i<exc_data.size(); ++i)
+ stop_info.SetExceptionDataAtIndex(i, exc_data[i]);
+ }
+ else
+ {
+ stop_info.Clear ();
+ stop_info.SetStopReasonWithBreakpointSiteID (break_id);
+ }
+ }
+#endif
+#if defined (MACH_EXC_DATA0_TRACE)
+ else if (exc_type == EXC_BREAKPOINT && exc_data[0] == MACH_EXC_DATA0_TRACE)
+ {
+ stop_info.SetStopReasonToTrace ();
+ }
+#endif
+ else
+ {
+ stop_info.SetStopReasonWithException(exc_type, exc_data.size());
+ for (uint32_t i=0; i<exc_data.size(); ++i)
+ stop_info.SetExceptionDataAtIndex(i, exc_data[i]);
+ }
+ }
+ else if (signo)
+ {
+ stop_info.SetStopReasonWithSignal(signo);
+ }
+ else
+ {
+ stop_info.SetStopReasonToNone();
+ }
+ }
+ return eStateStopped;
+ }
+ break;
+
+ case 'W':
+ // process exited
+ return eStateExited;
+
+ default:
+ break;
+ }
+ return eStateInvalid;
+}
+
+void
+ProcessGDBRemote::RefreshStateAfterStop ()
+{
+ // We must be attaching if we don't already have a valid architecture
+ if (!m_arch_spec.IsValid())
+ {
+ Module *exe_module = GetTarget().GetExecutableModule().get();
+ if (exe_module)
+ m_arch_spec = exe_module->GetArchitecture();
+ }
+ // Let all threads recover from stopping and do any clean up based
+ // on the previous thread state (if any).
+ m_thread_list.RefreshStateAfterStop();
+
+ // Discover new threads:
+ UpdateThreadListIfNeeded ();
+}
+
+Error
+ProcessGDBRemote::DoHalt ()
+{
+ Error error;
+ if (m_gdb_comm.IsRunning())
+ {
+ bool timed_out = false;
+ if (!m_gdb_comm.SendInterrupt (2, &timed_out))
+ {
+ if (timed_out)
+ error.SetErrorString("timed out sending interrupt packet");
+ else
+ error.SetErrorString("unknown error sending interrupt packet");
+ }
+ }
+ return error;
+}
+
+Error
+ProcessGDBRemote::WillDetach ()
+{
+ Error error;
+ const StateType state = m_private_state.GetValue();
+
+ if (IsRunning(state))
+ error.SetErrorString("Process must be stopped in order to detach.");
+
+ return error;
+}
+
+
+Error
+ProcessGDBRemote::DoDestroy ()
+{
+ Error error;
+ Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS);
+ if (log)
+ log->Printf ("ProcessGDBRemote::DoDestroy()");
+
+ // Interrupt if our inferior is running...
+ m_gdb_comm.SendInterrupt (1);
+ DisableAllBreakpointSites ();
+ SetExitStatus(-1, "process killed");
+
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse("k", response, 2, false))
+ {
+ if (log)
+ {
+ if (response.IsOKPacket())
+ log->Printf ("ProcessGDBRemote::DoDestroy() kill was successful");
+ else
+ log->Printf ("ProcessGDBRemote::DoDestroy() kill failed: %s", response.GetStringRef().c_str());
+ }
+ }
+
+ StopAsyncThread ();
+ m_gdb_comm.StopReadThread();
+ KillDebugserverProcess ();
+ return error;
+}
+
+ByteOrder
+ProcessGDBRemote::GetByteOrder () const
+{
+ return m_byte_order;
+}
+
+//------------------------------------------------------------------
+// Process Queries
+//------------------------------------------------------------------
+
+bool
+ProcessGDBRemote::IsAlive ()
+{
+ return m_gdb_comm.IsConnected();
+}
+
+addr_t
+ProcessGDBRemote::GetImageInfoAddress()
+{
+ if (!m_gdb_comm.IsRunning())
+ {
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse("qShlibInfoAddr", ::strlen ("qShlibInfoAddr"), response, 2, false))
+ {
+ if (response.IsNormalPacket())
+ return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS);
+ }
+ }
+ return LLDB_INVALID_ADDRESS;
+}
+
+DynamicLoader *
+ProcessGDBRemote::GetDynamicLoader()
+{
+ return m_dynamic_loader_ap.get();
+}
+
+//------------------------------------------------------------------
+// Process Memory
+//------------------------------------------------------------------
+size_t
+ProcessGDBRemote::DoReadMemory (addr_t addr, void *buf, size_t size, Error &error)
+{
+ if (size > m_max_memory_size)
+ {
+ // Keep memory read sizes down to a sane limit. This function will be
+ // called multiple times in order to complete the task by
+ // lldb_private::Process so it is ok to do this.
+ size = m_max_memory_size;
+ }
+
+ char packet[64];
+ const int packet_len = ::snprintf (packet, sizeof(packet), "m%llx,%zx", (uint64_t)addr, size);
+ assert (packet_len + 1 < sizeof(packet));
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, 2, true))
+ {
+ if (response.IsNormalPacket())
+ {
+ error.Clear();
+ return response.GetHexBytes(buf, size, '\xdd');
+ }
+ else if (response.IsErrorPacket())
+ error.SetErrorStringWithFormat("gdb remote returned an error: %s", response.GetStringRef().c_str());
+ else if (response.IsUnsupportedPacket())
+ error.SetErrorStringWithFormat("'%s' packet unsupported", packet);
+ else
+ error.SetErrorStringWithFormat("unexpected response to '%s': '%s'", packet, response.GetStringRef().c_str());
+ }
+ else
+ {
+ error.SetErrorStringWithFormat("failed to sent packet: '%s'", packet);
+ }
+ return 0;
+}
+
+size_t
+ProcessGDBRemote::DoWriteMemory (addr_t addr, const void *buf, size_t size, Error &error)
+{
+ StreamString packet;
+ packet.Printf("M%llx,%zx:", addr, size);
+ packet.PutBytesAsRawHex8(buf, size, eByteOrderHost, eByteOrderHost);
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetData(), packet.GetSize(), response, 2, true))
+ {
+ if (response.IsOKPacket())
+ {
+ error.Clear();
+ return size;
+ }
+ else if (response.IsErrorPacket())
+ error.SetErrorStringWithFormat("gdb remote returned an error: %s", response.GetStringRef().c_str());
+ else if (response.IsUnsupportedPacket())
+ error.SetErrorStringWithFormat("'%s' packet unsupported", packet.GetString().c_str());
+ else
+ error.SetErrorStringWithFormat("unexpected response to '%s': '%s'", packet.GetString().c_str(), response.GetStringRef().c_str());
+ }
+ else
+ {
+ error.SetErrorStringWithFormat("failed to sent packet: '%s'", packet.GetString().c_str());
+ }
+ return 0;
+}
+
+lldb::addr_t
+ProcessGDBRemote::DoAllocateMemory (size_t size, uint32_t permissions, Error &error)
+{
+ addr_t allocated_addr = m_gdb_comm.AllocateMemory (size, permissions, m_packet_timeout);
+ if (allocated_addr == LLDB_INVALID_ADDRESS)
+ error.SetErrorStringWithFormat("unable to allocate %zu bytes of memory with permissions %u", size, permissions);
+ else
+ error.Clear();
+ return allocated_addr;
+}
+
+Error
+ProcessGDBRemote::DoDeallocateMemory (lldb::addr_t addr)
+{
+ Error error;
+ if (!m_gdb_comm.DeallocateMemory (addr, m_packet_timeout))
+ error.SetErrorStringWithFormat("unable to deallocate memory at 0x%llx", addr);
+ return error;
+}
+
+
+//------------------------------------------------------------------
+// Process STDIO
+//------------------------------------------------------------------
+
+size_t
+ProcessGDBRemote::GetSTDOUT (char *buf, size_t buf_size, Error &error)
+{
+ Mutex::Locker locker(m_stdio_mutex);
+ size_t bytes_available = m_stdout_data.size();
+ if (bytes_available > 0)
+ {
+ ProcessGDBRemoteLog::LogIf (GDBR_LOG_PROCESS, "ProcessGDBRemote::%s (&%p[%u]) ...", __FUNCTION__, buf, buf_size);
+ if (bytes_available > buf_size)
+ {
+ memcpy(buf, m_stdout_data.data(), buf_size);
+ m_stdout_data.erase(0, buf_size);
+ bytes_available = buf_size;
+ }
+ else
+ {
+ memcpy(buf, m_stdout_data.data(), bytes_available);
+ m_stdout_data.clear();
+
+ //ResetEventBits(eBroadcastBitSTDOUT);
+ }
+ }
+ return bytes_available;
+}
+
+size_t
+ProcessGDBRemote::GetSTDERR (char *buf, size_t buf_size, Error &error)
+{
+ // Can we get STDERR through the remote protocol?
+ return 0;
+}
+
+size_t
+ProcessGDBRemote::PutSTDIN (const char *src, size_t src_len, Error &error)
+{
+ if (m_stdio_communication.IsConnected())
+ {
+ ConnectionStatus status;
+ m_stdio_communication.Write(src, src_len, status, NULL);
+ }
+ return 0;
+}
+
+Error
+ProcessGDBRemote::EnableBreakpoint (BreakpointSite *bp_site)
+{
+ Error error;
+ assert (bp_site != NULL);
+
+ Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS);
+ user_id_t site_id = bp_site->GetID();
+ const addr_t addr = bp_site->GetLoadAddress();
+ if (log)
+ log->Printf ("ProcessGDBRemote::EnableBreakpoint (size_id = %d) address = 0x%llx", site_id, (uint64_t)addr);
+
+ if (bp_site->IsEnabled())
+ {
+ if (log)
+ log->Printf ("ProcessGDBRemote::EnableBreakpoint (size_id = %d) address = 0x%llx -- SUCCESS (already enabled)", site_id, (uint64_t)addr);
+ return error;
+ }
+ else
+ {
+ const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode (bp_site);
+
+ if (bp_site->HardwarePreferred())
+ {
+ // Try and set hardware breakpoint, and if that fails, fall through
+ // and set a software breakpoint?
+ }
+
+ if (m_z0_supported)
+ {
+ char packet[64];
+ const int packet_len = ::snprintf (packet, sizeof(packet), "Z0,%llx,%zx", addr, bp_op_size);
+ assert (packet_len + 1 < sizeof(packet));
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, 2, true))
+ {
+ if (response.IsUnsupportedPacket())
+ {
+ // Disable z packet support and try again
+ m_z0_supported = 0;
+ return EnableBreakpoint (bp_site);
+ }
+ else if (response.IsOKPacket())
+ {
+ bp_site->SetEnabled(true);
+ bp_site->SetType (BreakpointSite::eExternal);
+ return error;
+ }
+ else
+ {
+ uint8_t error_byte = response.GetError();
+ if (error_byte)
+ error.SetErrorStringWithFormat("%x packet failed with error: %i (0x%2.2x).\n", packet, error_byte, error_byte);
+ }
+ }
+ }
+ else
+ {
+ return EnableSoftwareBreakpoint (bp_site);
+ }
+ }
+
+ if (log)
+ {
+ const char *err_string = error.AsCString();
+ log->Printf ("ProcessGDBRemote::EnableBreakpoint() error for breakpoint at 0x%8.8llx: %s",
+ bp_site->GetLoadAddress(),
+ err_string ? err_string : "NULL");
+ }
+ // We shouldn't reach here on a successful breakpoint enable...
+ if (error.Success())
+ error.SetErrorToGenericError();
+ return error;
+}
+
+Error
+ProcessGDBRemote::DisableBreakpoint (BreakpointSite *bp_site)
+{
+ Error error;
+ assert (bp_site != NULL);
+ addr_t addr = bp_site->GetLoadAddress();
+ user_id_t site_id = bp_site->GetID();
+ Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS);
+ if (log)
+ log->Printf ("ProcessGDBRemote::DisableBreakpoint (site_id = %d) addr = 0x%8.8llx", site_id, (uint64_t)addr);
+
+ if (bp_site->IsEnabled())
+ {
+ const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode (bp_site);
+
+ if (bp_site->IsHardware())
+ {
+ // TODO: disable hardware breakpoint...
+ }
+ else
+ {
+ if (m_z0_supported)
+ {
+ char packet[64];
+ const int packet_len = ::snprintf (packet, sizeof(packet), "z0,%llx,%zx", addr, bp_op_size);
+ assert (packet_len + 1 < sizeof(packet));
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, 2, true))
+ {
+ if (response.IsUnsupportedPacket())
+ {
+ error.SetErrorString("Breakpoint site was set with Z packet, yet remote debugserver states z packets are not supported.");
+ }
+ else if (response.IsOKPacket())
+ {
+ if (log)
+ log->Printf ("ProcessGDBRemote::DisableBreakpoint (site_id = %d) addr = 0x%8.8llx -- SUCCESS", site_id, (uint64_t)addr);
+ bp_site->SetEnabled(false);
+ return error;
+ }
+ else
+ {
+ uint8_t error_byte = response.GetError();
+ if (error_byte)
+ error.SetErrorStringWithFormat("%x packet failed with error: %i (0x%2.2x).\n", packet, error_byte, error_byte);
+ }
+ }
+ }
+ else
+ {
+ return DisableSoftwareBreakpoint (bp_site);
+ }
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf ("ProcessGDBRemote::DisableBreakpoint (site_id = %d) addr = 0x%8.8llx -- SUCCESS (already disabled)", site_id, (uint64_t)addr);
+ return error;
+ }
+
+ if (error.Success())
+ error.SetErrorToGenericError();
+ return error;
+}
+
+Error
+ProcessGDBRemote::EnableWatchpoint (WatchpointLocation *wp)
+{
+ Error error;
+ if (wp)
+ {
+ user_id_t watchID = wp->GetID();
+ addr_t addr = wp->GetLoadAddress();
+ Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS);
+ if (log)
+ log->Printf ("ProcessGDBRemote::EnableWatchpoint(watchID = %d)", watchID);
+ if (wp->IsEnabled())
+ {
+ if (log)
+ log->Printf("ProcessGDBRemote::EnableWatchpoint(watchID = %d) addr = 0x%8.8llx: watchpoint already enabled.", watchID, (uint64_t)addr);
+ return error;
+ }
+ else
+ {
+ // Pass down an appropriate z/Z packet...
+ error.SetErrorString("watchpoints not supported");
+ }
+ }
+ else
+ {
+ error.SetErrorString("Watchpoint location argument was NULL.");
+ }
+ if (error.Success())
+ error.SetErrorToGenericError();
+ return error;
+}
+
+Error
+ProcessGDBRemote::DisableWatchpoint (WatchpointLocation *wp)
+{
+ Error error;
+ if (wp)
+ {
+ user_id_t watchID = wp->GetID();
+
+ Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS);
+
+ addr_t addr = wp->GetLoadAddress();
+ if (log)
+ log->Printf ("ProcessGDBRemote::DisableWatchpoint (watchID = %d) addr = 0x%8.8llx", watchID, (uint64_t)addr);
+
+ if (wp->IsHardware())
+ {
+ // Pass down an appropriate z/Z packet...
+ error.SetErrorString("watchpoints not supported");
+ }
+ // TODO: clear software watchpoints if we implement them
+ }
+ else
+ {
+ error.SetErrorString("Watchpoint location argument was NULL.");
+ }
+ if (error.Success())
+ error.SetErrorToGenericError();
+ return error;
+}
+
+void
+ProcessGDBRemote::Clear()
+{
+ m_flags = 0;
+ m_thread_list.Clear();
+ {
+ Mutex::Locker locker(m_stdio_mutex);
+ m_stdout_data.clear();
+ }
+ DestoryLibUnwindAddressSpace();
+}
+
+Error
+ProcessGDBRemote::DoSignal (int signo)
+{
+ Error error;
+ Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS);
+ if (log)
+ log->Printf ("ProcessGDBRemote::DoSignal (signal = %d)", signo);
+
+ if (!m_gdb_comm.SendAsyncSignal (signo))
+ error.SetErrorStringWithFormat("failed to send signal %i", signo);
+ return error;
+}
+
+
+Error
+ProcessGDBRemote::DoDetach()
+{
+ Error error;
+ Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS);
+ if (log)
+ log->Printf ("ProcessGDBRemote::DoDetach()");
+
+ // if (DoSIGSTOP (true))
+ // {
+ // CloseChildFileDescriptors ();
+ //
+ // // Scope for "locker" so we can reply to all of our exceptions (the SIGSTOP
+ // // exception).
+ // {
+ // Mutex::Locker locker(m_exception_messages_mutex);
+ // ReplyToAllExceptions();
+ // }
+ //
+ // // Shut down the exception thread and cleanup our exception remappings
+ // Task().ShutDownExceptionThread();
+ //
+ // pid_t pid = GetID();
+ //
+ // // Detach from our process while we are stopped.
+ // errno = 0;
+ //
+ // // Detach from our process
+ // ::ptrace (PT_DETACH, pid, (caddr_t)1, 0);
+ //
+ // error.SetErrorToErrno();
+ //
+ // if (log || error.Fail())
+ // error.PutToLog(log, "::ptrace (PT_DETACH, %u, (caddr_t)1, 0)", pid);
+ //
+ // // Resume our task
+ // Task().Resume();
+ //
+ // // NULL our task out as we have already retored all exception ports
+ // Task().Clear();
+ //
+ // // Clear out any notion of the process we once were
+ // Clear();
+ //
+ // SetPrivateState (eStateDetached);
+ // return true;
+ // }
+ return error;
+}
+
+void
+ProcessGDBRemote::STDIOReadThreadBytesReceived (void *baton, const void *src, size_t src_len)
+{
+ ProcessGDBRemote *process = (ProcessGDBRemote *)baton;
+ process->AppendSTDOUT(static_cast<const char *>(src), src_len);
+}
+
+void
+ProcessGDBRemote::AppendSTDOUT (const char* s, size_t len)
+{
+ ProcessGDBRemoteLog::LogIf (GDBR_LOG_PROCESS, "ProcessGDBRemote::%s (<%d> %s) ...", __FUNCTION__, len, s);
+ Mutex::Locker locker(m_stdio_mutex);
+ m_stdout_data.append(s, len);
+
+ // FIXME: Make a real data object for this and put it out.
+ BroadcastEventIfUnique (eBroadcastBitSTDOUT);
+}
+
+
+Error
+ProcessGDBRemote::StartDebugserverProcess
+(
+ const char *debugserver_url, // The connection string to use in the spawned debugserver ("localhost:1234" or "/dev/tty...")
+ char const *inferior_argv[], // Arguments for the inferior program including the path to the inferior itself as the first argument
+ char const *inferior_envp[], // Environment to pass along to the inferior program
+ char const *stdio_path,
+ lldb::pid_t attach_pid, // If inferior inferior_argv == NULL, and attach_pid != LLDB_INVALID_PROCESS_ID then attach to this attach_pid
+ const char *attach_name, // Wait for the next process to launch whose basename matches "attach_name"
+ bool wait_for_launch, // Wait for the process named "attach_name" to launch
+ ArchSpec& inferior_arch // The arch of the inferior that we will launch
+)
+{
+ Error error;
+ if (m_debugserver_pid == LLDB_INVALID_PROCESS_ID)
+ {
+ // If we locate debugserver, keep that located version around
+ static FileSpec g_debugserver_file_spec;
+
+ FileSpec debugserver_file_spec;
+ char debugserver_path[PATH_MAX];
+
+ // Always check to see if we have an environment override for the path
+ // to the debugserver to use and use it if we do.
+ const char *env_debugserver_path = getenv("LLDB_DEBUGSERVER_PATH");
+ if (env_debugserver_path)
+ debugserver_file_spec.SetFile (env_debugserver_path);
+ else
+ debugserver_file_spec = g_debugserver_file_spec;
+ bool debugserver_exists = debugserver_file_spec.Exists();
+ if (!debugserver_exists)
+ {
+ // The debugserver binary is in the LLDB.framework/Resources
+ // directory.
+ FileSpec framework_file_spec (Host::GetModuleFileSpecForHostAddress ((void *)lldb_private::Initialize));
+ const char *framework_dir = framework_file_spec.GetDirectory().AsCString();
+ const char *lldb_framework = ::strstr (framework_dir, "/LLDB.framework");
+
+ if (lldb_framework)
+ {
+ int len = lldb_framework - framework_dir + strlen ("/LLDB.framework");
+ ::snprintf (debugserver_path,
+ sizeof(debugserver_path),
+ "%.*s/Resources/%s",
+ len,
+ framework_dir,
+ DEBUGSERVER_BASENAME);
+ debugserver_file_spec.SetFile (debugserver_path);
+ debugserver_exists = debugserver_file_spec.Exists();
+ }
+
+ if (debugserver_exists)
+ {
+ g_debugserver_file_spec = debugserver_file_spec;
+ }
+ else
+ {
+ g_debugserver_file_spec.Clear();
+ debugserver_file_spec.Clear();
+ }
+ }
+
+ if (debugserver_exists)
+ {
+ debugserver_file_spec.GetPath (debugserver_path, sizeof(debugserver_path));
+
+ m_stdio_communication.Clear();
+ posix_spawnattr_t attr;
+
+ Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS);
+
+ Error local_err; // Errors that don't affect the spawning.
+ if (log)
+ log->Printf ("%s ( path='%s', argv=%p, envp=%p, arch=%s )", __FUNCTION__, debugserver_path, inferior_argv, inferior_envp, inferior_arch.AsCString());
+ error.SetError( ::posix_spawnattr_init (&attr), eErrorTypePOSIX);
+ if (error.Fail() || log)
+ error.PutToLog(log, "::posix_spawnattr_init ( &attr )");
+ if (error.Fail())
+ return error;;
+
+#if !defined (__arm__)
+
+ // We don't need to do this for ARM, and we really shouldn't now that we
+ // have multiple CPU subtypes and no posix_spawnattr call that allows us
+ // to set which CPU subtype to launch...
+ cpu_type_t cpu = inferior_arch.GetCPUType();
+ if (cpu != 0 && cpu != CPU_TYPE_ANY && cpu != LLDB_INVALID_CPUTYPE)
+ {
+ size_t ocount = 0;
+ error.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu, &ocount), eErrorTypePOSIX);
+ if (error.Fail() || log)
+ error.PutToLog(log, "::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %zu )", cpu, ocount);
+
+ if (error.Fail() != 0 || ocount != 1)
+ return error;
+ }
+
+#endif
+
+ Args debugserver_args;
+ char arg_cstr[PATH_MAX];
+ bool launch_process = true;
+
+ if (inferior_argv == NULL && attach_pid != LLDB_INVALID_PROCESS_ID)
+ launch_process = false;
+ else if (attach_name)
+ launch_process = false; // Wait for a process whose basename matches that in inferior_argv[0]
+
+ bool pass_stdio_path_to_debugserver = true;
+ lldb_utility::PseudoTerminal pty;
+ if (stdio_path == NULL)
+ {
+ pass_stdio_path_to_debugserver = false;
+ if (pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY, NULL, 0))
+ {
+ struct termios stdin_termios;
+ if (::tcgetattr (pty.GetMasterFileDescriptor(), &stdin_termios) == 0)
+ {
+ stdin_termios.c_lflag &= ~ECHO; // Turn off echoing
+ stdin_termios.c_lflag &= ~ICANON; // Get one char at a time
+ ::tcsetattr (pty.GetMasterFileDescriptor(), TCSANOW, &stdin_termios);
+ }
+ stdio_path = pty.GetSlaveName (NULL, 0);
+ }
+ }
+
+ // Start args with "debugserver /file/path -r --"
+ debugserver_args.AppendArgument(debugserver_path);
+ debugserver_args.AppendArgument(debugserver_url);
+ debugserver_args.AppendArgument("--native-regs"); // use native registers, not the GDB registers
+ debugserver_args.AppendArgument("--setsid"); // make debugserver run in its own session so
+ // signals generated by special terminal key
+ // sequences (^C) don't affect debugserver
+
+ // Only set the inferior
+ if (launch_process)
+ {
+ if (stdio_path && pass_stdio_path_to_debugserver)
+ {
+ debugserver_args.AppendArgument("-s"); // short for --stdio-path
+ StreamString strm;
+ strm.Printf("'%s'", stdio_path);
+ debugserver_args.AppendArgument(strm.GetData()); // path to file to have inferior open as it's STDIO
+ }
+ }
+
+ const char *env_debugserver_log_file = getenv("LLDB_DEBUGSERVER_LOG_FILE");
+ if (env_debugserver_log_file)
+ {
+ ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-file=%s", env_debugserver_log_file);
+ debugserver_args.AppendArgument(arg_cstr);
+ }
+
+ const char *env_debugserver_log_flags = getenv("LLDB_DEBUGSERVER_LOG_FLAGS");
+ if (env_debugserver_log_flags)
+ {
+ ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-flags=%s", env_debugserver_log_flags);
+ debugserver_args.AppendArgument(arg_cstr);
+ }
+// debugserver_args.AppendArgument("--log-file=/tmp/debugserver.txt");
+// debugserver_args.AppendArgument("--log-flags=0x800e0e");
+
+ // Now append the program arguments
+ if (launch_process)
+ {
+ if (inferior_argv)
+ {
+ // Terminate the debugserver args so we can now append the inferior args
+ debugserver_args.AppendArgument("--");
+
+ for (int i = 0; inferior_argv[i] != NULL; ++i)
+ debugserver_args.AppendArgument (inferior_argv[i]);
+ }
+ else
+ {
+ // Will send environment entries with the 'QEnvironment:' packet
+ // Will send arguments with the 'A' packet
+ }
+ }
+ else if (attach_pid != LLDB_INVALID_PROCESS_ID)
+ {
+ ::snprintf (arg_cstr, sizeof(arg_cstr), "--attach=%u", attach_pid);
+ debugserver_args.AppendArgument (arg_cstr);
+ }
+ else if (attach_name && attach_name[0])
+ {
+ if (wait_for_launch)
+ debugserver_args.AppendArgument ("--waitfor");
+ else
+ debugserver_args.AppendArgument ("--attach");
+ debugserver_args.AppendArgument (attach_name);
+ }
+
+ Error file_actions_err;
+ posix_spawn_file_actions_t file_actions;
+#if DONT_CLOSE_DEBUGSERVER_STDIO
+ file_actions_err.SetErrorString ("Remove this after uncommenting the code block below.");
+#else
+ file_actions_err.SetError( ::posix_spawn_file_actions_init (&file_actions), eErrorTypePOSIX);
+ if (file_actions_err.Success())
+ {
+ ::posix_spawn_file_actions_addclose (&file_actions, STDIN_FILENO);
+ ::posix_spawn_file_actions_addclose (&file_actions, STDOUT_FILENO);
+ ::posix_spawn_file_actions_addclose (&file_actions, STDERR_FILENO);
+ }
+#endif
+
+ if (log)
+ {
+ StreamString strm;
+ debugserver_args.Dump (&strm);
+ log->Printf("%s arguments:\n%s", debugserver_args.GetArgumentAtIndex(0), strm.GetData());
+ }
+
+ error.SetError(::posix_spawnp (&m_debugserver_pid,
+ debugserver_path,
+ file_actions_err.Success() ? &file_actions : NULL,
+ &attr,
+ debugserver_args.GetArgumentVector(),
+ (char * const*)inferior_envp),
+ eErrorTypePOSIX);
+
+ if (file_actions_err.Success())
+ ::posix_spawn_file_actions_destroy (&file_actions);
+
+ // We have seen some cases where posix_spawnp was returning a valid
+ // looking pid even when an error was returned, so clear it out
+ if (error.Fail())
+ m_debugserver_pid = LLDB_INVALID_PROCESS_ID;
+
+ if (error.Fail() || log)
+ error.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", m_debugserver_pid, debugserver_path, NULL, &attr, inferior_argv, inferior_envp);
+
+// if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID)
+// {
+// std::auto_ptr<ConnectionFileDescriptor> conn_ap(new ConnectionFileDescriptor (pty.ReleaseMasterFileDescriptor(), true));
+// if (conn_ap.get())
+// {
+// m_stdio_communication.SetConnection(conn_ap.release());
+// if (m_stdio_communication.IsConnected())
+// {
+// m_stdio_communication.SetReadThreadBytesReceivedCallback (STDIOReadThreadBytesReceived, this);
+// m_stdio_communication.StartReadThread();
+// }
+// }
+// }
+ }
+ else
+ {
+ error.SetErrorStringWithFormat ("Unable to locate " DEBUGSERVER_BASENAME ".\n");
+ }
+
+ if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID)
+ StartAsyncThread ();
+ }
+ return error;
+}
+
+bool
+ProcessGDBRemote::MonitorDebugserverProcess
+(
+ void *callback_baton,
+ lldb::pid_t debugserver_pid,
+ int signo, // Zero for no signal
+ int exit_status // Exit value of process if signal is zero
+)
+{
+ // We pass in the ProcessGDBRemote inferior process it and name it
+ // "gdb_remote_pid". The process ID is passed in the "callback_baton"
+ // pointer value itself, thus we need the double cast...
+
+ // "debugserver_pid" argument passed in is the process ID for
+ // debugserver that we are tracking...
+
+ lldb::pid_t gdb_remote_pid = (lldb::pid_t)(intptr_t)callback_baton;
+ TargetSP target_sp(Debugger::GetSharedInstance().GetTargetList().FindTargetWithProcessID (gdb_remote_pid));
+ if (target_sp)
+ {
+ ProcessSP process_sp (target_sp->GetProcessSP());
+ if (process_sp)
+ {
+ // Sleep for a half a second to make sure our inferior process has
+ // time to set its exit status before we set it incorrectly when
+ // both the debugserver and the inferior process shut down.
+ usleep (500000);
+ // If our process hasn't yet exited, debugserver might have died.
+ // If the process did exit, the we are reaping it.
+ if (process_sp->GetState() != eStateExited)
+ {
+ char error_str[1024];
+ if (signo)
+ {
+ const char *signal_cstr = process_sp->GetUnixSignals().GetSignalAsCString (signo);
+ if (signal_cstr)
+ ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with signal %s", signal_cstr);
+ else
+ ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with signal %i", signo);
+ }
+ else
+ {
+ ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with an exit status of 0x%8.8x", exit_status);
+ }
+
+ process_sp->SetExitStatus (-1, error_str);
+ }
+ else
+ {
+ ProcessGDBRemote *gdb_process = (ProcessGDBRemote *)process_sp.get();
+ // Debugserver has exited we need to let our ProcessGDBRemote
+ // know that it no longer has a debugserver instance
+ gdb_process->m_debugserver_pid = LLDB_INVALID_PROCESS_ID;
+ // We are returning true to this function below, so we can
+ // forget about the monitor handle.
+ gdb_process->m_debugserver_monitor = 0;
+ }
+ }
+ }
+ return true;
+}
+
+void
+ProcessGDBRemote::KillDebugserverProcess ()
+{
+ if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID)
+ {
+ ::kill (m_debugserver_pid, SIGINT);
+ m_debugserver_pid = LLDB_INVALID_PROCESS_ID;
+ }
+}
+
+void
+ProcessGDBRemote::Initialize()
+{
+ static bool g_initialized = false;
+
+ if (g_initialized == false)
+ {
+ g_initialized = true;
+ PluginManager::RegisterPlugin (GetPluginNameStatic(),
+ GetPluginDescriptionStatic(),
+ CreateInstance);
+
+ Log::Callbacks log_callbacks = {
+ ProcessGDBRemoteLog::DisableLog,
+ ProcessGDBRemoteLog::EnableLog,
+ ProcessGDBRemoteLog::ListLogCategories
+ };
+
+ Log::RegisterLogChannel (ProcessGDBRemote::GetPluginNameStatic(), log_callbacks);
+ }
+}
+
+bool
+ProcessGDBRemote::SetCurrentGDBRemoteThread (int tid)
+{
+ if (m_curr_tid == tid)
+ return true;
+
+ char packet[32];
+ const int packet_len = ::snprintf (packet, sizeof(packet), "Hg%x", tid);
+ assert (packet_len + 1 < sizeof(packet));
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, 2, false))
+ {
+ if (response.IsOKPacket())
+ {
+ m_curr_tid = tid;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+ProcessGDBRemote::SetCurrentGDBRemoteThreadForRun (int tid)
+{
+ if (m_curr_tid_run == tid)
+ return true;
+
+ char packet[32];
+ const int packet_len = ::snprintf (packet, sizeof(packet), "Hg%x", tid);
+ assert (packet_len + 1 < sizeof(packet));
+ StringExtractorGDBRemote response;
+ if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, 2, false))
+ {
+ if (response.IsOKPacket())
+ {
+ m_curr_tid_run = tid;
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+ProcessGDBRemote::ResetGDBRemoteState ()
+{
+ // Reset and GDB remote state
+ m_curr_tid = LLDB_INVALID_THREAD_ID;
+ m_curr_tid_run = LLDB_INVALID_THREAD_ID;
+ m_z0_supported = 1;
+}
+
+
+bool
+ProcessGDBRemote::StartAsyncThread ()
+{
+ ResetGDBRemoteState ();
+
+ Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS);
+
+ if (log)
+ log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__);
+
+ // Create a thread that watches our internal state and controls which
+ // events make it to clients (into the DCProcess event queue).
+ m_async_thread = Host::ThreadCreate ("<lldb.process.gdb-remote.async>", ProcessGDBRemote::AsyncThread, this, NULL);
+ return m_async_thread != LLDB_INVALID_HOST_THREAD;
+}
+
+void
+ProcessGDBRemote::StopAsyncThread ()
+{
+ Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS);
+
+ if (log)
+ log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__);
+
+ m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncThreadShouldExit);
+
+ // Stop the stdio thread
+ if (m_async_thread != LLDB_INVALID_HOST_THREAD)
+ {
+ Host::ThreadJoin (m_async_thread, NULL, NULL);
+ }
+}
+
+
+void *
+ProcessGDBRemote::AsyncThread (void *arg)
+{
+ ProcessGDBRemote *process = (ProcessGDBRemote*) arg;
+
+ Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS);
+ if (log)
+ log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) thread starting...", __FUNCTION__, arg, process->GetID());
+
+ Listener listener ("ProcessGDBRemote::AsyncThread");
+ EventSP event_sp;
+ const uint32_t desired_event_mask = eBroadcastBitAsyncContinue |
+ eBroadcastBitAsyncThreadShouldExit;
+
+ if (listener.StartListeningForEvents (&process->m_async_broadcaster, desired_event_mask) == desired_event_mask)
+ {
+ bool done = false;
+ while (!done)
+ {
+ if (log)
+ log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) listener.WaitForEvent (NULL, event_sp)...", __FUNCTION__, arg, process->GetID());
+ if (listener.WaitForEvent (NULL, event_sp))
+ {
+ const uint32_t event_type = event_sp->GetType();
+ switch (event_type)
+ {
+ case eBroadcastBitAsyncContinue:
+ {
+ const EventDataBytes *continue_packet = EventDataBytes::GetEventDataFromEvent(event_sp.get());
+
+ if (continue_packet)
+ {
+ const char *continue_cstr = (const char *)continue_packet->GetBytes ();
+ const size_t continue_cstr_len = continue_packet->GetByteSize ();
+ if (log)
+ log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) got eBroadcastBitAsyncContinue: %s", __FUNCTION__, arg, process->GetID(), continue_cstr);
+
+ process->SetPrivateState(eStateRunning);
+ StringExtractorGDBRemote response;
+ StateType stop_state = process->GetGDBRemote().SendContinuePacketAndWaitForResponse (process, continue_cstr, continue_cstr_len, response);
+
+ switch (stop_state)
+ {
+ case eStateStopped:
+ case eStateCrashed:
+ case eStateSuspended:
+ process->m_last_stop_packet = response;
+ process->m_last_stop_packet.SetFilePos (0);
+ process->SetPrivateState (stop_state);
+ break;
+
+ case eStateExited:
+ process->m_last_stop_packet = response;
+ process->m_last_stop_packet.SetFilePos (0);
+ response.SetFilePos(1);
+ process->SetExitStatus(response.GetHexU8(), NULL);
+ done = true;
+ break;
+
+ case eStateInvalid:
+ break;
+
+ default:
+ process->SetPrivateState (stop_state);
+ break;
+ }
+ }
+ }
+ break;
+
+ case eBroadcastBitAsyncThreadShouldExit:
+ if (log)
+ log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) got eBroadcastBitAsyncThreadShouldExit...", __FUNCTION__, arg, process->GetID());
+ done = true;
+ break;
+
+ default:
+ if (log)
+ log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) got unknown event 0x%8.8x", __FUNCTION__, arg, process->GetID(), event_type);
+ done = true;
+ break;
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) listener.WaitForEvent (NULL, event_sp) => false", __FUNCTION__, arg, process->GetID());
+ done = true;
+ }
+ }
+ }
+
+ if (log)
+ log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) thread exiting...", __FUNCTION__, arg, process->GetID());
+
+ process->m_async_thread = LLDB_INVALID_HOST_THREAD;
+ return NULL;
+}
+
+lldb_private::unw_addr_space_t
+ProcessGDBRemote::GetLibUnwindAddressSpace ()
+{
+ unw_targettype_t target_type = UNW_TARGET_UNSPECIFIED;
+ if (m_target.GetArchitecture().GetCPUType() == CPU_TYPE_I386)
+ target_type = UNW_TARGET_I386;
+ if (m_target.GetArchitecture().GetCPUType() == CPU_TYPE_X86_64)
+ target_type = UNW_TARGET_X86_64;
+
+ if (m_libunwind_addr_space)
+ {
+ if (m_libunwind_target_type != target_type)
+ DestoryLibUnwindAddressSpace();
+ else
+ return m_libunwind_addr_space;
+ }
+ unw_accessors_t callbacks = get_macosx_libunwind_callbacks ();
+ m_libunwind_addr_space = unw_create_addr_space (&callbacks, target_type);
+ if (m_libunwind_addr_space)
+ m_libunwind_target_type = target_type;
+ else
+ m_libunwind_target_type = UNW_TARGET_UNSPECIFIED;
+ return m_libunwind_addr_space;
+}
+
+void
+ProcessGDBRemote::DestoryLibUnwindAddressSpace ()
+{
+ if (m_libunwind_addr_space)
+ {
+ unw_destroy_addr_space (m_libunwind_addr_space);
+ m_libunwind_addr_space = NULL;
+ }
+ m_libunwind_target_type = UNW_TARGET_UNSPECIFIED;
+}
+
+
+const char *
+ProcessGDBRemote::GetDispatchQueueNameForThread
+(
+ addr_t thread_dispatch_qaddr,
+ std::string &dispatch_queue_name
+)
+{
+ dispatch_queue_name.clear();
+ if (thread_dispatch_qaddr != 0 && thread_dispatch_qaddr != LLDB_INVALID_ADDRESS)
+ {
+ // Cache the dispatch_queue_offsets_addr value so we don't always have
+ // to look it up
+ if (m_dispatch_queue_offsets_addr == LLDB_INVALID_ADDRESS)
+ {
+ ModuleSP module_sp(GetTarget().GetImages().FindFirstModuleForFileSpec (FileSpec("libSystem.B.dylib")));
+ if (module_sp.get() == NULL)
+ return NULL;
+
+ const Symbol *dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType (ConstString("dispatch_queue_offsets"), eSymbolTypeData);
+ if (dispatch_queue_offsets_symbol)
+ m_dispatch_queue_offsets_addr = dispatch_queue_offsets_symbol->GetValue().GetLoadAddress(this);
+
+ if (m_dispatch_queue_offsets_addr == LLDB_INVALID_ADDRESS)
+ return NULL;
+ }
+
+ uint8_t memory_buffer[8];
+ DataExtractor data(memory_buffer, sizeof(memory_buffer), GetByteOrder(), GetAddressByteSize());
+
+ // Excerpt from src/queue_private.h
+ struct dispatch_queue_offsets_s
+ {
+ uint16_t dqo_version;
+ uint16_t dqo_label;
+ uint16_t dqo_label_size;
+ } dispatch_queue_offsets;
+
+
+ Error error;
+ if (ReadMemory (m_dispatch_queue_offsets_addr, memory_buffer, sizeof(dispatch_queue_offsets), error) == sizeof(dispatch_queue_offsets))
+ {
+ uint32_t data_offset = 0;
+ if (data.GetU16(&data_offset, &dispatch_queue_offsets.dqo_version, sizeof(dispatch_queue_offsets)/sizeof(uint16_t)))
+ {
+ if (ReadMemory (thread_dispatch_qaddr, &memory_buffer, data.GetAddressByteSize(), error) == data.GetAddressByteSize())
+ {
+ data_offset = 0;
+ lldb::addr_t queue_addr = data.GetAddress(&data_offset);
+ lldb::addr_t label_addr = queue_addr + dispatch_queue_offsets.dqo_label;
+ dispatch_queue_name.resize(dispatch_queue_offsets.dqo_label_size, '\0');
+ size_t bytes_read = ReadMemory (label_addr, &dispatch_queue_name[0], dispatch_queue_offsets.dqo_label_size, error);
+ if (bytes_read < dispatch_queue_offsets.dqo_label_size)
+ dispatch_queue_name.erase (bytes_read);
+ }
+ }
+ }
+ }
+ if (dispatch_queue_name.empty())
+ return NULL;
+ return dispatch_queue_name.c_str();
+}
+
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
new file mode 100644
index 00000000000..cd5bab0194f
--- /dev/null
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -0,0 +1,404 @@
+//===-- ProcessGDBRemote.h --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcessGDBRemote_h_
+#define liblldb_ProcessGDBRemote_h_
+
+// C Includes
+
+// C++ Includes
+#include <list>
+
+// Other libraries and framework includes
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Core/Broadcaster.h"
+#include "lldb/Core/Error.h"
+#include "lldb/Core/InputReader.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Core/ThreadSafeValue.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+
+#include "GDBRemoteCommunication.h"
+#include "StringExtractor.h"
+#include "GDBRemoteRegisterContext.h"
+#include "libunwind.h"
+
+class ThreadGDBRemote;
+
+class ProcessGDBRemote : public lldb_private::Process
+{
+public:
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ static Process*
+ CreateInstance (lldb_private::Target& target, lldb_private::Listener &listener);
+
+ static void
+ Initialize();
+
+ static void
+ Terminate();
+
+ static const char *
+ GetPluginNameStatic();
+
+ static const char *
+ GetPluginDescriptionStatic();
+
+ //------------------------------------------------------------------
+ // Constructors and Destructors
+ //------------------------------------------------------------------
+ ProcessGDBRemote(lldb_private::Target& target, lldb_private::Listener &listener);
+
+ virtual
+ ~ProcessGDBRemote();
+
+ //------------------------------------------------------------------
+ // Check if a given Process
+ //------------------------------------------------------------------
+ virtual bool
+ CanDebug (lldb_private::Target &target);
+
+ //------------------------------------------------------------------
+ // Creating a new process, or attaching to an existing one
+ //------------------------------------------------------------------
+ virtual lldb_private::Error
+ WillLaunch (lldb_private::Module* module);
+
+ virtual lldb_private::Error
+ DoLaunch (lldb_private::Module* module,
+ char const *argv[], // Can be NULL
+ char const *envp[], // Can be NULL
+ const char *stdin_path, // Can be NULL
+ const char *stdout_path, // Can be NULL
+ const char *stderr_path); // Can be NULL
+
+ virtual void
+ DidLaunch ();
+
+ virtual lldb_private::Error
+ WillAttach (lldb::pid_t pid);
+
+ virtual lldb_private::Error
+ WillAttach (const char *process_name, bool wait_for_launch);
+
+ lldb_private::Error
+ WillLaunchOrAttach ();
+
+ virtual lldb_private::Error
+ DoAttach (lldb::pid_t pid);
+
+ virtual lldb_private::Error
+ DoAttach (const char *process_name, bool wait_for_launch);
+
+ virtual void
+ DidAttach ();
+
+ //------------------------------------------------------------------
+ // PluginInterface protocol
+ //------------------------------------------------------------------
+ virtual const char *
+ GetPluginName();
+
+ virtual const char *
+ GetShortPluginName();
+
+ virtual uint32_t
+ GetPluginVersion();
+
+ virtual void
+ GetPluginCommandHelp (const char *command, lldb_private::Stream *strm);
+
+ virtual lldb_private::Error
+ ExecutePluginCommand (lldb_private::Args &command, lldb_private::Stream *strm);
+
+ virtual lldb_private::Log *
+ EnablePluginLogging (lldb_private::Stream *strm, lldb_private::Args &command);
+
+ //------------------------------------------------------------------
+ // Process Control
+ //------------------------------------------------------------------
+ virtual lldb_private::Error
+ WillResume ();
+
+ virtual lldb_private::Error
+ DoResume ();
+
+ virtual lldb_private::Error
+ DoHalt ();
+
+ virtual lldb_private::Error
+ WillDetach ();
+
+ virtual lldb_private::Error
+ DoDetach ();
+
+ virtual lldb_private::Error
+ DoSignal (int signal);
+
+ virtual lldb_private::Error
+ DoDestroy ();
+
+ virtual void
+ RefreshStateAfterStop();
+
+ //------------------------------------------------------------------
+ // Process Queries
+ //------------------------------------------------------------------
+ virtual bool
+ IsAlive ();
+
+ virtual lldb::addr_t
+ GetImageInfoAddress();
+
+ //------------------------------------------------------------------
+ // Process Memory
+ //------------------------------------------------------------------
+ virtual size_t
+ DoReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error);
+
+ virtual size_t
+ DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size, lldb_private::Error &error);
+
+ virtual lldb::addr_t
+ DoAllocateMemory (size_t size, uint32_t permissions, lldb_private::Error &error);
+
+ virtual lldb_private::Error
+ DoDeallocateMemory (lldb::addr_t ptr);
+
+ //------------------------------------------------------------------
+ // Process STDIO
+ //------------------------------------------------------------------
+ virtual size_t
+ GetSTDOUT (char *buf, size_t buf_size, lldb_private::Error &error);
+
+ virtual size_t
+ GetSTDERR (char *buf, size_t buf_size, lldb_private::Error &error);
+
+ virtual size_t
+ PutSTDIN (const char *buf, size_t buf_size, lldb_private::Error &error);
+
+ //----------------------------------------------------------------------
+ // Process Breakpoints
+ //----------------------------------------------------------------------
+ virtual size_t
+ GetSoftwareBreakpointTrapOpcode (lldb_private::BreakpointSite *bp_site);
+
+ //----------------------------------------------------------------------
+ // Process Breakpoints
+ //----------------------------------------------------------------------
+ virtual lldb_private::Error
+ EnableBreakpoint (lldb_private::BreakpointSite *bp_site);
+
+ virtual lldb_private::Error
+ DisableBreakpoint (lldb_private::BreakpointSite *bp_site);
+
+ //----------------------------------------------------------------------
+ // Process Watchpoints
+ //----------------------------------------------------------------------
+ virtual lldb_private::Error
+ EnableWatchpoint (lldb_private::WatchpointLocation *wp_loc);
+
+ virtual lldb_private::Error
+ DisableWatchpoint (lldb_private::WatchpointLocation *wp_loc);
+
+ virtual lldb::ByteOrder
+ GetByteOrder () const;
+
+ virtual lldb_private::DynamicLoader *
+ GetDynamicLoader ();
+
+protected:
+ friend class ThreadGDBRemote;
+ friend class GDBRemoteCommunication;
+ friend class GDBRemoteRegisterContext;
+
+ bool
+ SetCurrentGDBRemoteThread (int tid);
+
+ bool
+ SetCurrentGDBRemoteThreadForRun (int tid);
+
+ //----------------------------------------------------------------------
+ // Accessors
+ //----------------------------------------------------------------------
+ bool
+ IsRunning ( lldb::StateType state )
+ {
+ return state == lldb::eStateRunning || IsStepping(state);
+ }
+
+ bool
+ IsStepping ( lldb::StateType state)
+ {
+ return state == lldb::eStateStepping;
+ }
+ bool
+ CanResume ( lldb::StateType state)
+ {
+ return state == lldb::eStateStopped;
+ }
+
+ bool
+ HasExited (lldb::StateType state)
+ {
+ return state == lldb::eStateExited;
+ }
+
+ bool
+ ProcessIDIsValid ( ) const;
+
+ static void
+ STDIOReadThreadBytesReceived (void *baton, const void *src, size_t src_len);
+
+ void
+ AppendSTDOUT (const char* s, size_t len);
+
+ lldb_private::ArchSpec&
+ GetArchSpec()
+ {
+ return m_arch_spec;
+ }
+ const lldb_private::ArchSpec&
+ GetArchSpec() const
+ {
+ return m_arch_spec;
+ }
+
+ void
+ Clear ( );
+
+ lldb_private::Flags &
+ GetFlags ()
+ {
+ return m_flags;
+ }
+
+ const lldb_private::Flags &
+ GetFlags () const
+ {
+ return m_flags;
+ }
+
+ uint32_t
+ UpdateThreadListIfNeeded ();
+
+ lldb_private::Error
+ StartDebugserverProcess (const char *debugserver_url, // The connection string to use in the spawned debugserver ("localhost:1234" or "/dev/tty...")
+ char const *inferior_argv[],
+ char const *inferior_envp[],
+ const char *stdin_path,
+ lldb::pid_t attach_pid, // If inferior inferior_argv == NULL, then attach to this pid
+ const char *attach_pid_name, // Wait for the next process to launch whose basename matches "attach_wait_name"
+ bool wait_for_launch, // Wait for the process named "attach_wait_name" to launch
+ lldb_private::ArchSpec& arch_spec);
+
+ void
+ KillDebugserverProcess ();
+
+ void
+ BuildDynamicRegisterInfo ();
+
+ GDBRemoteCommunication &
+ GetGDBRemote()
+ {
+ return m_gdb_comm;
+ }
+
+ //------------------------------------------------------------------
+ /// Broadcaster event bits definitions.
+ //------------------------------------------------------------------
+ enum
+ {
+ eBroadcastBitAsyncContinue = (1 << 0),
+ eBroadcastBitAsyncThreadShouldExit = (1 << 1)
+ };
+
+
+ std::auto_ptr<lldb_private::DynamicLoader> m_dynamic_loader_ap;
+ lldb_private::Flags m_flags; // Process specific flags (see eFlags enums)
+ lldb_private::Communication m_stdio_communication;
+ lldb_private::Mutex m_stdio_mutex; // Multithreaded protection for stdio
+ std::string m_stdout_data;
+ lldb_private::ArchSpec m_arch_spec;
+ lldb::ByteOrder m_byte_order;
+ GDBRemoteCommunication m_gdb_comm;
+ lldb::pid_t m_debugserver_pid;
+ uint32_t m_debugserver_monitor;
+ StringExtractor m_last_stop_packet;
+ GDBRemoteDynamicRegisterInfo m_register_info;
+ lldb_private::Broadcaster m_async_broadcaster;
+ lldb::thread_t m_async_thread;
+ // Current GDB remote state. Any members added here need to be reset to
+ // proper default values in ResetGDBRemoteState ().
+ lldb::tid_t m_curr_tid; // Current gdb remote protocol thread index for all other operations
+ lldb::tid_t m_curr_tid_run; // Current gdb remote protocol thread index for continue, step, etc
+ uint32_t m_z0_supported:1; // Set to non-zero if Z0 and z0 packets are supported
+ lldb_private::StreamString m_continue_packet;
+ lldb::addr_t m_dispatch_queue_offsets_addr;
+ uint32_t m_packet_timeout;
+ size_t m_max_memory_size; // The maximum number of bytes to read/write when reading and writing memory
+ lldb_private::unw_targettype_t m_libunwind_target_type;
+ lldb_private::unw_addr_space_t m_libunwind_addr_space; // libunwind address space object for this process.
+ bool m_waiting_for_attach;
+
+ void
+ ResetGDBRemoteState ();
+
+ bool
+ StartAsyncThread ();
+
+ void
+ StopAsyncThread ();
+
+ static void *
+ AsyncThread (void *arg);
+
+ static bool
+ MonitorDebugserverProcess (void *callback_baton,
+ lldb::pid_t pid,
+ int signo, // Zero for no signal
+ int exit_status); // Exit value of process if signal is zero
+
+ lldb::StateType
+ SetThreadStopInfo (StringExtractor& stop_packet);
+
+ void
+ DidLaunchOrAttach ();
+
+ lldb_private::Error
+ ConnectToDebugserver (const char *host_port);
+
+ const char *
+ GetDispatchQueueNameForThread (lldb::addr_t thread_dispatch_qaddr,
+ std::string &dispatch_queue_name);
+
+ static size_t
+ AttachInputReaderCallback (void *baton,
+ lldb_private::InputReader *reader,
+ lldb::InputReaderAction notification,
+ const char *bytes,
+ size_t bytes_len);
+
+private:
+ //------------------------------------------------------------------
+ // For ProcessGDBRemote only
+ //------------------------------------------------------------------
+ DISALLOW_COPY_AND_ASSIGN (ProcessGDBRemote);
+
+ lldb_private::unw_addr_space_t
+ GetLibUnwindAddressSpace ();
+
+ void
+ DestoryLibUnwindAddressSpace ();
+};
+
+#endif // liblldb_ProcessGDBRemote_h_
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp
new file mode 100644
index 00000000000..051ce8f20f4
--- /dev/null
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp
@@ -0,0 +1,121 @@
+//===-- ProcessGDBRemoteLog.cpp ---------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProcessGDBRemoteLog.h"
+
+#include "lldb/Core/Args.h"
+#include "lldb/Core/StreamFile.h"
+
+#include "ProcessGDBRemote.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+
+static Log* g_log = NULL; // Leak for now as auto_ptr was being cleaned up
+ // by global constructors before other threads
+ // were done with it.
+Log *
+ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (uint32_t mask)
+{
+ Log *log = g_log;
+ if (log && mask)
+ {
+ uint32_t log_mask = log->GetMask().GetAllFlagBits();
+ if ((log_mask & mask) != mask)
+ return NULL;
+ }
+ return log;
+}
+
+void
+ProcessGDBRemoteLog::DisableLog ()
+{
+ if (g_log)
+ {
+ delete g_log;
+ g_log = NULL;
+ }
+}
+
+Log *
+ProcessGDBRemoteLog::EnableLog (StreamSP &log_stream_sp, uint32_t log_options, Args &args, Stream *feedback_strm)
+{
+ DisableLog ();
+ g_log = new Log (log_stream_sp);
+ if (g_log)
+ {
+ uint32_t flag_bits = 0;
+ bool got_unknown_category = false;
+ const size_t argc = args.GetArgumentCount();
+ for (size_t i=0; i<argc; ++i)
+ {
+ const char *arg = args.GetArgumentAtIndex(i);
+
+ if (::strcasecmp (arg, "all") == 0 ) flag_bits |= GDBR_LOG_ALL;
+ else if (::strcasestr (arg, "break") == arg ) flag_bits |= GDBR_LOG_BREAKPOINTS;
+ else if (::strcasecmp (arg, "default") == 0 ) flag_bits |= GDBR_LOG_DEFAULT;
+ else if (::strcasecmp (arg, "packets") == 0 ) flag_bits |= GDBR_LOG_PACKETS;
+ else if (::strcasecmp (arg, "memory") == 0 ) flag_bits |= GDBR_LOG_MEMORY;
+ else if (::strcasecmp (arg, "data-short") == 0 ) flag_bits |= GDBR_LOG_MEMORY_DATA_SHORT;
+ else if (::strcasecmp (arg, "data-long") == 0 ) flag_bits |= GDBR_LOG_MEMORY_DATA_LONG;
+ else if (::strcasecmp (arg, "process") == 0 ) flag_bits |= GDBR_LOG_PROCESS;
+ else if (::strcasecmp (arg, "step") == 0 ) flag_bits |= GDBR_LOG_STEP;
+ else if (::strcasecmp (arg, "thread") == 0 ) flag_bits |= GDBR_LOG_THREAD;
+ else if (::strcasecmp (arg, "verbose") == 0 ) flag_bits |= GDBR_LOG_VERBOSE;
+ else if (::strcasestr (arg, "watch") == arg ) flag_bits |= GDBR_LOG_WATCHPOINTS;
+ else
+ {
+ feedback_strm->Printf("error: unrecognized log category '%s'\n", arg);
+ if (got_unknown_category == false)
+ {
+ got_unknown_category = true;
+ ListLogCategories (feedback_strm);
+ }
+ }
+ }
+ if (flag_bits == 0)
+ flag_bits = GDBR_LOG_DEFAULT;
+ g_log->GetMask().SetAllFlagBits(flag_bits);
+ g_log->GetOptions().SetAllFlagBits(log_options);
+ }
+ return g_log;
+}
+
+void
+ProcessGDBRemoteLog::ListLogCategories (Stream *strm)
+{
+ strm->Printf("Logging categories for '%s':\n"
+ "\tall - turn on all available logging categories\n"
+ "\tbreak - log breakpoints\n"
+ "\tdefault - enable the default set of logging categories for liblldb\n"
+ "\tpackets - log gdb remote packets\n"
+ "\tmemory - log memory reads and writes\n"
+ "\tdata-short - log memory bytes for memory reads and writes for short transactions only\n"
+ "\tdata-long - log memory bytes for memory reads and writes for all transactions\n"
+ "\tprocess - log process events and activities\n"
+ "\tthread - log thread events and activities\n"
+ "\tstep - log step related activities\n"
+ "\tverbose - enable verbose loggging\n"
+ "\twatch - log watchpoint related activities\n", ProcessGDBRemote::GetPluginNameStatic());
+}
+
+
+void
+ProcessGDBRemoteLog::LogIf (uint32_t mask, const char *format, ...)
+{
+ Log *log = ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (mask);
+ if (log)
+ {
+ va_list args;
+ va_start (args, format);
+ log->VAPrintf (format, args);
+ va_end (args);
+ }
+}
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h
new file mode 100644
index 00000000000..97580d38674
--- /dev/null
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h
@@ -0,0 +1,53 @@
+//===-- ProcessGDBRemoteLog.h -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ProcessGDBRemoteLog_h_
+#define liblldb_ProcessGDBRemoteLog_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+
+// Project includes
+#include "lldb/Core/Log.h"
+
+#define GDBR_LOG_VERBOSE (1u << 0)
+#define GDBR_LOG_PROCESS (1u << 1)
+#define GDBR_LOG_THREAD (1u << 2)
+#define GDBR_LOG_PACKETS (1u << 3)
+#define GDBR_LOG_MEMORY (1u << 4) // Log memory reads/writes calls
+#define GDBR_LOG_MEMORY_DATA_SHORT (1u << 5) // Log short memory reads/writes bytes
+#define GDBR_LOG_MEMORY_DATA_LONG (1u << 6) // Log all memory reads/writes bytes
+#define GDBR_LOG_BREAKPOINTS (1u << 7)
+#define GDBR_LOG_WATCHPOINTS (1u << 8)
+#define GDBR_LOG_STEP (1u << 9)
+#define GDBR_LOG_COMM (1u << 10)
+#define GDBR_LOG_ALL (UINT32_MAX)
+#define GDBR_LOG_DEFAULT GDBR_LOG_PACKETS
+
+class ProcessGDBRemoteLog
+{
+public:
+ static lldb_private::Log *
+ GetLogIfAllCategoriesSet(uint32_t mask = 0);
+
+ static void
+ DisableLog ();
+
+ static lldb_private::Log *
+ EnableLog (lldb::StreamSP &log_stream_sp, uint32_t log_options, lldb_private::Args &args, lldb_private::Stream *feedback_strm);
+
+ static void
+ ListLogCategories (lldb_private::Stream *strm);
+
+ static void
+ LogIf (uint32_t mask, const char *format, ...);
+};
+
+#endif // liblldb_ProcessGDBRemoteLog_h_
diff --git a/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp
new file mode 100644
index 00000000000..37485edf4e5
--- /dev/null
+++ b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp
@@ -0,0 +1,296 @@
+//===-- ThreadGDBRemote.cpp -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#include "ThreadGDBRemote.h"
+
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Unwind.h"
+#include "lldb/Breakpoint/WatchpointLocation.h"
+
+#include "LibUnwindRegisterContext.h"
+#include "ProcessGDBRemote.h"
+#include "ProcessGDBRemoteLog.h"
+#include "StringExtractorGDBRemote.h"
+#include "UnwindLibUnwind.h"
+#include "UnwindMacOSXFrameBackchain.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// Thread Registers
+//----------------------------------------------------------------------
+
+ThreadGDBRemote::ThreadGDBRemote (ProcessGDBRemote &process, lldb::tid_t tid) :
+ Thread(process, tid),
+ m_stop_info_stop_id (0),
+ m_stop_info (this),
+ m_thread_name (),
+ m_dispatch_queue_name (),
+ m_thread_dispatch_qaddr (LLDB_INVALID_ADDRESS),
+ m_unwinder_ap ()
+{
+// ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD | GDBR_LOG_VERBOSE, "ThreadGDBRemote::ThreadGDBRemote ( pid = %i, tid = 0x%4.4x, )", m_process.GetID(), GetID());
+ ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD, "%p: ThreadGDBRemote::ThreadGDBRemote (pid = %i, tid = 0x%4.4x)", this, m_process.GetID(), GetID());
+}
+
+ThreadGDBRemote::~ThreadGDBRemote ()
+{
+ ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD, "%p: ThreadGDBRemote::~ThreadGDBRemote (pid = %i, tid = 0x%4.4x)", this, m_process.GetID(), GetID());
+}
+
+
+const char *
+ThreadGDBRemote::GetInfo ()
+{
+ return NULL;
+}
+
+
+const char *
+ThreadGDBRemote::GetName ()
+{
+ if (m_thread_name.empty())
+ return NULL;
+ return m_thread_name.c_str();
+}
+
+
+const char *
+ThreadGDBRemote::GetQueueName ()
+{
+ // Always re-fetch the dispatch queue name since it can change
+ if (m_thread_dispatch_qaddr != 0 || m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS)
+ return GetGDBProcess().GetDispatchQueueNameForThread (m_thread_dispatch_qaddr, m_dispatch_queue_name);
+ return NULL;
+}
+
+bool
+ThreadGDBRemote::WillResume (StateType resume_state)
+{
+ // TODO: cache for next time in case we can match things up??
+ ClearStackFrames();
+ int signo = GetResumeSignal();
+ m_stop_info.Clear();
+ switch (resume_state)
+ {
+ case eStateSuspended:
+ case eStateStopped:
+ // Don't append anything for threads that should stay stopped.
+ break;
+
+ case eStateRunning:
+ if (m_process.GetUnixSignals().SignalIsValid (signo))
+ GetGDBProcess().m_continue_packet.Printf(";C%2.2x:%4.4x", signo, GetID());
+ else
+ GetGDBProcess().m_continue_packet.Printf(";c:%4.4x", GetID());
+ break;
+
+ case eStateStepping:
+ if (m_process.GetUnixSignals().SignalIsValid (signo))
+ GetGDBProcess().m_continue_packet.Printf(";S%2.2x:%4.4x", signo, GetID());
+ else
+ GetGDBProcess().m_continue_packet.Printf(";s:%4.4x", GetID());
+ break;
+ }
+ Thread::WillResume(resume_state);
+ return true;
+}
+
+void
+ThreadGDBRemote::RefreshStateAfterStop()
+{
+ // Invalidate all registers in our register context
+ GetRegisterContext()->Invalidate();
+}
+
+Unwind *
+ThreadGDBRemote::GetUnwinder ()
+{
+ if (m_unwinder_ap.get() == NULL)
+ {
+ const ArchSpec target_arch (GetProcess().GetTarget().GetArchitecture ());
+ if (target_arch == ArchSpec("x86_64") || target_arch == ArchSpec("i386"))
+ {
+ m_unwinder_ap.reset (new UnwindLibUnwind (*this, GetGDBProcess().GetLibUnwindAddressSpace()));
+ }
+ else
+ {
+ m_unwinder_ap.reset (new UnwindMacOSXFrameBackchain (*this));
+ }
+ }
+ return m_unwinder_ap.get();
+}
+
+uint32_t
+ThreadGDBRemote::GetStackFrameCount()
+{
+ Unwind *unwinder = GetUnwinder ();
+ if (unwinder)
+ return unwinder->GetFrameCount();
+ return 0;
+}
+
+// Make sure that GetStackFrameAtIndex() does NOT call GetStackFrameCount() when
+// getting the stack frame at index zero! This way GetStackFrameCount() (via
+// GetStackFRameData()) can call this function to get the first frame in order
+// to provide the first frame to a lower call for efficiency sake (avoid
+// redundant lookups in the frame symbol context).
+lldb::StackFrameSP
+ThreadGDBRemote::GetStackFrameAtIndex (uint32_t idx)
+{
+
+ StackFrameSP frame_sp (m_frames.GetFrameAtIndex(idx));
+
+ if (frame_sp.get())
+ return frame_sp;
+
+ // Don't try and fetch a frame while process is running
+// FIXME: This check isn't right because IsRunning checks the Public state, but this
+// is work you need to do - for instance in ShouldStop & friends - before the public
+// state has been changed.
+// if (m_process.IsRunning())
+// return frame_sp;
+
+ // Special case the first frame (idx == 0) so that we don't need to
+ // know how many stack frames there are to get it. If we need any other
+ // frames, then we do need to know if "idx" is a valid index.
+ if (idx == 0)
+ {
+ // If this is the first frame, we want to share the thread register
+ // context with the stack frame at index zero.
+ GetRegisterContext();
+ assert (m_reg_context_sp.get());
+ frame_sp.reset (new StackFrame (idx, *this, m_reg_context_sp, m_reg_context_sp->GetSP(), m_reg_context_sp->GetPC()));
+ }
+ else if (idx < GetStackFrameCount())
+ {
+ Unwind *unwinder = GetUnwinder ();
+ if (unwinder)
+ {
+ addr_t pc, cfa;
+ if (unwinder->GetFrameInfoAtIndex(idx, cfa, pc))
+ frame_sp.reset (new StackFrame (idx, *this, cfa, pc));
+ }
+ }
+ m_frames.SetFrameAtIndex(idx, frame_sp);
+ return frame_sp;
+}
+
+void
+ThreadGDBRemote::ClearStackFrames ()
+{
+ Unwind *unwinder = GetUnwinder ();
+ if (unwinder)
+ unwinder->Clear();
+ Thread::ClearStackFrames();
+}
+
+
+bool
+ThreadGDBRemote::ThreadIDIsValid (lldb::tid_t thread)
+{
+ return thread != 0;
+}
+
+void
+ThreadGDBRemote::Dump(Log *log, uint32_t index)
+{
+}
+
+
+bool
+ThreadGDBRemote::ShouldStop (bool &step_more)
+{
+ return true;
+}
+RegisterContext *
+ThreadGDBRemote::GetRegisterContext ()
+{
+ if (m_reg_context_sp.get() == NULL)
+ m_reg_context_sp.reset (CreateRegisterContextForFrame (NULL));
+ return m_reg_context_sp.get();
+}
+
+RegisterContext *
+ThreadGDBRemote::CreateRegisterContextForFrame (StackFrame *frame)
+{
+ const bool read_all_registers_at_once = false;
+ uint32_t frame_idx = 0;
+
+ if (frame)
+ frame_idx = frame->GetID();
+
+ if (frame_idx == 0)
+ return new GDBRemoteRegisterContext (*this, frame, GetGDBProcess().m_register_info, read_all_registers_at_once);
+ else if (m_unwinder_ap.get() && frame_idx < m_unwinder_ap->GetFrameCount())
+ return m_unwinder_ap->CreateRegisterContextForFrame (frame);
+ return NULL;
+}
+
+bool
+ThreadGDBRemote::SaveFrameZeroState (RegisterCheckpoint &checkpoint)
+{
+ lldb::StackFrameSP frame_sp(GetStackFrameAtIndex (0));
+ if (frame_sp)
+ {
+ checkpoint.SetStackID(frame_sp->GetStackID());
+ return frame_sp->GetRegisterContext()->ReadAllRegisterValues (checkpoint.GetData());
+ }
+ return false;
+}
+
+bool
+ThreadGDBRemote::RestoreSaveFrameZero (const RegisterCheckpoint &checkpoint)
+{
+ lldb::StackFrameSP frame_sp(GetStackFrameAtIndex (0));
+ if (frame_sp)
+ {
+ bool ret = frame_sp->GetRegisterContext()->WriteAllRegisterValues (checkpoint.GetData());
+ frame_sp->GetRegisterContext()->Invalidate();
+ ClearStackFrames();
+ return ret;
+ }
+ return false;
+}
+
+bool
+ThreadGDBRemote::GetRawStopReason (StopInfo *stop_info)
+{
+ if (m_stop_info_stop_id != m_process.GetStopID())
+ {
+ char packet[256];
+ const int packet_len = snprintf(packet, sizeof(packet), "qThreadStopInfo%x", GetID());
+ assert (packet_len < (sizeof(packet) - 1));
+ StringExtractorGDBRemote stop_packet;
+ if (GetGDBProcess().GetGDBRemote().SendPacketAndWaitForResponse(packet, stop_packet, 1, false))
+ {
+ std::string copy(stop_packet.GetStringRef());
+ GetGDBProcess().SetThreadStopInfo (stop_packet);
+ // The process should have set the stop info stop ID and also
+ // filled this thread in with valid stop info
+ if (m_stop_info_stop_id != m_process.GetStopID())
+ {
+ //ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD, "warning: qThreadStopInfo problem: '%s' => '%s'", packet, stop_packet.GetStringRef().c_str());
+ printf("warning: qThreadStopInfo problem: '%s' => '%s'\n\torig '%s'\n", packet, stop_packet.GetStringRef().c_str(), copy.c_str()); /// REMOVE THIS
+ return false;
+ }
+ }
+ }
+ *stop_info = m_stop_info;
+ return true;
+}
+
+
diff --git a/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h
new file mode 100644
index 00000000000..3fa4ae09a2a
--- /dev/null
+++ b/lldb/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h
@@ -0,0 +1,156 @@
+//===-- ThreadGDBRemote.h ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ThreadGDBRemote_h_
+#define liblldb_ThreadGDBRemote_h_
+
+#include <string>
+
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+#include "MachException.h"
+#include "libunwind.h"
+
+class StringExtractor;
+class ProcessGDBRemote;
+
+class ThreadGDBRemote : public lldb_private::Thread
+{
+public:
+ ThreadGDBRemote (ProcessGDBRemote &process, lldb::tid_t tid);
+
+ virtual
+ ~ThreadGDBRemote ();
+
+ virtual bool
+ WillResume (lldb::StateType resume_state);
+
+ virtual void
+ RefreshStateAfterStop();
+
+ virtual const char *
+ GetInfo ();
+
+ virtual const char *
+ GetName ();
+
+ virtual const char *
+ GetQueueName ();
+
+ virtual lldb_private::RegisterContext *
+ GetRegisterContext ();
+
+ virtual lldb_private::RegisterContext *
+ CreateRegisterContextForFrame (lldb_private::StackFrame *frame);
+
+ virtual bool
+ SaveFrameZeroState (RegisterCheckpoint &checkpoint);
+
+ virtual bool
+ RestoreSaveFrameZero (const RegisterCheckpoint &checkpoint);
+
+ virtual uint32_t
+ GetStackFrameCount();
+
+ virtual lldb::StackFrameSP
+ GetStackFrameAtIndex (uint32_t idx);
+
+ virtual void
+ ClearStackFrames ();
+
+ ProcessGDBRemote &
+ GetGDBProcess ()
+ {
+ return (ProcessGDBRemote &)m_process;
+ }
+
+ const ProcessGDBRemote &
+ GetGDBProcess () const
+ {
+ return (ProcessGDBRemote &)m_process;
+ }
+
+ void
+ Dump (lldb_private::Log *log, uint32_t index);
+
+ static bool
+ ThreadIDIsValid (lldb::tid_t thread);
+
+ bool
+ ShouldStop (bool &step_more);
+
+ const char *
+ GetBasicInfoAsString ();
+
+ lldb_private::Thread::StopInfo &
+ GetStopInfoRef ()
+ {
+ return m_stop_info;
+ }
+
+ uint32_t
+ GetStopInfoStopID()
+ {
+ return m_stop_info_stop_id;
+ }
+
+ void
+ SetStopInfoStopID (uint32_t stop_id)
+ {
+ m_stop_info_stop_id = stop_id;
+ }
+
+ void
+ SetName (const char *name)
+ {
+ if (name && name[0])
+ m_thread_name.assign (name);
+ else
+ m_thread_name.clear();
+ }
+
+ lldb::addr_t
+ GetThreadDispatchQAddr ()
+ {
+ return m_thread_dispatch_qaddr;
+ }
+
+ void
+ SetThreadDispatchQAddr (lldb::addr_t thread_dispatch_qaddr)
+ {
+ m_thread_dispatch_qaddr = thread_dispatch_qaddr;
+ }
+
+protected:
+ //------------------------------------------------------------------
+ // Member variables.
+ //------------------------------------------------------------------
+ uint32_t m_stop_info_stop_id;
+ lldb_private::Thread::StopInfo m_stop_info;
+ std::string m_thread_name;
+ std::string m_dispatch_queue_name;
+ lldb::addr_t m_thread_dispatch_qaddr;
+ std::auto_ptr<lldb_private::Unwind> m_unwinder_ap;
+ //------------------------------------------------------------------
+ // Member variables.
+ //------------------------------------------------------------------
+
+ lldb_private::Unwind *
+ GetUnwinder ();
+
+ void
+ SetStopInfoFromPacket (StringExtractor &stop_packet, uint32_t stop_id);
+
+ virtual bool
+ GetRawStopReason (StopInfo *stop_info);
+
+
+};
+
+#endif // liblldb_ThreadGDBRemote_h_
OpenPOWER on IntegriCloud