blob: 97d098332c710746b47b068923751da9aecfb53d [file] [log] [blame]
/* Target-dependent code for NativeClient.
Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
#include "nacl-tdep.h"
#include "breakpoint.h"
#include "command.h"
#include "completer.h"
#include "gdbcore.h"
#include "inferior.h"
#include "solib.h"
#include "solist.h"
#include "symfile.h"
#include "symtab.h"
#include "target.h"
#include "top.h"
#include "readline/readline.h"
/*******************************************************************************
*
* GDB state
*
*******************************************************************************/
/* The basic idea of the debugger port is to switch from service runtime symbols
to nacl symbols when needed. For example, we might want to switch to nacl
symbols when execution stops in untrusted code. We call this switching gdb
states between service runtime state and nacl state.
The only way of switching states in this implementation of gdb is overriding
a bunch of global variables. This likely leads to gdb inconsistency, as we
can hardly track all the dependencies of these variables.
Probably we can do better in gdb 7.x with multiple inferiors support. */
/* From objfiles.c. */
extern struct objfile *object_files;
extern struct objfile *current_objfile;
extern struct objfile *symfile_objfile;
extern struct objfile *rt_common_objfile; /* For completeness' sake. */
/* From exec.c. */
extern bfd *exec_bfd;
/* From solib.c. */
extern struct so_list *so_list_head;
struct gdb_state
{
struct objfile *object_files;
struct objfile *current_objfile;
struct objfile *symfile_objfile;
struct objfile *rt_common_objfile;
bfd *exec_bfd;
struct so_list *so_list_head;
};
static void
switch_gdb_state (struct gdb_state *from, struct gdb_state *to)
{
from->object_files = object_files;
from->current_objfile = current_objfile;
from->symfile_objfile = symfile_objfile;
from->rt_common_objfile = rt_common_objfile;
from->exec_bfd = exec_bfd;
from->so_list_head = so_list_head;
object_files = to->object_files;
current_objfile = to->current_objfile;
symfile_objfile = to->symfile_objfile;
rt_common_objfile = to->rt_common_objfile;
exec_bfd = to->exec_bfd;
so_list_head = to->so_list_head;
}
static struct gdb_state saved_nacl_gdb_state = { 0 };
static struct gdb_state saved_sr_gdb_state = { 0 };
static int nacl_gdb_state = 0;
int
nacl_gdb_state_p (void)
{
return nacl_gdb_state;
}
int
set_nacl_gdb_state (void)
{
if (nacl_gdb_state)
return 1;
switch_gdb_state (&saved_sr_gdb_state, &saved_nacl_gdb_state);
set_prompt ("(nc-gdb) ");
nacl_gdb_state = 1;
return 0;
}
int
set_sr_gdb_state (void)
{
if (!nacl_gdb_state)
return 0;
switch_gdb_state (&saved_nacl_gdb_state, &saved_sr_gdb_state);
set_prompt ("(sr-gdb) ");
nacl_gdb_state = 0;
return 1;
}
int
set_gdb_state (int to_nacl_state)
{
if (to_nacl_state)
return set_nacl_gdb_state ();
return set_sr_gdb_state ();
}
/*******************************************************************************
*
* Native client executable
*
*******************************************************************************/
/* Intentional memory leak - finally freed at exit. */
static char *nacl_exec_file = NULL;
/*******************************************************************************
*
* Memory layout
*
*******************************************************************************/
static CORE_ADDR nacl_sandbox_base = 0;
int
nacl_address_p (CORE_ADDR address)
{
if (nacl_sandbox_base &&
address >= nacl_sandbox_base &&
address < nacl_sandbox_base + 4ULL * 1024ULL * 1024ULL * 1024ULL)
return 1;
return 0;
}
static void
nacl_init_sandbox_base ()
{
struct minimal_symbol *msymbol;
int prev_state = set_sr_gdb_state ();
msymbol = lookup_minimal_symbol ("nacl_global_xlate_base", NULL, symfile_objfile);
if (!msymbol || !SYMBOL_VALUE_ADDRESS (msymbol))
nacl_sandbox_base = 0;
else
nacl_sandbox_base = read_memory_unsigned_integer (SYMBOL_VALUE_ADDRESS (msymbol), 8);
set_gdb_state (prev_state);
}
/*******************************************************************************
*
* Native client pointer types
*
*******************************************************************************/
CORE_ADDR
nacl_pointer_to_address (struct type *type, const gdb_byte *buf)
{
CORE_ADDR addr = unsigned_pointer_to_address (type, buf);
/* HACK!
Distinguish nacl pointer types by size - for x86_64, nacl pointers
are 4 bytes while host pointers are 8 bytes. */
if (type->length == 4)
{
/* Do not change NULL pointers! */
if (addr)
addr = nacl_sandbox_base + (unsigned) addr;
}
return addr;
}
void
nacl_address_to_pointer (struct type *type, gdb_byte *buf, CORE_ADDR addr)
{
/* HACK!
This implementation may be OK, as it is used when setting pointer from
user-entered address. In NaCl mode, ask the user to enter NaCl address,
not full address... */
unsigned_address_to_pointer (type, buf, addr);
}
int
nacl_address_class_type_flags (int byte_size, int dwarf2_addr_class)
{
if (byte_size == 4)
return TYPE_FLAG_ADDRESS_CLASS_1;
else
return 0;
}
const char *
nacl_address_class_type_flags_to_name (struct gdbarch *gdbarch, int type_flags)
{
if (type_flags & TYPE_FLAG_ADDRESS_CLASS_1)
return "nacl";
else
return NULL;
}
int
nacl_address_class_name_to_type_flags (struct gdbarch *gdbarch, const char *name, int *type_flags_ptr)
{
if (strcmp (name, "nacl") == 0)
{
*type_flags_ptr = TYPE_FLAG_ADDRESS_CLASS_1;
return 1;
}
else
return 0;
}
/*******************************************************************************
*
* Load nacl executable and shared libraries
*
*******************************************************************************/
/* From solib-svr4.c. */
extern struct so_list *svr4_alloc_so (CORE_ADDR, const char *);
struct nacl_dynamic_loader_debugger_interface
{
/* _dl_debug_state - solib event break. */
CORE_ADDR _dl_debug_state;
/* _r_debug - dynamic linker interface structure. */
CORE_ADDR _r_debug;
/* _dl_argv - ld.so command line. */
CORE_ADDR _dl_argv;
};
/* ATTENTION:
We do not read target memory here - it might be not yet initialized! */
static int
nacl_discover_dynamic_loader_debugger_interface (struct nacl_dynamic_loader_debugger_interface *ni)
{
struct minimal_symbol *msymbol;
gdb_assert (nacl_gdb_state_p ());
gdb_assert (nacl_sandbox_base);
msymbol = lookup_minimal_symbol ("_dl_debug_state", NULL, symfile_objfile);
if (!msymbol || !SYMBOL_VALUE_ADDRESS (msymbol))
return 0;
ni->_dl_debug_state = SYMBOL_VALUE_ADDRESS (msymbol);
msymbol = lookup_minimal_symbol ("_r_debug", NULL, symfile_objfile);
if (!msymbol || !SYMBOL_VALUE_ADDRESS (msymbol))
return 0;
ni->_r_debug = SYMBOL_VALUE_ADDRESS (msymbol);
msymbol = lookup_minimal_symbol ("_dl_argv", NULL, symfile_objfile);
if (!msymbol || !SYMBOL_VALUE_ADDRESS (msymbol))
return 0;
ni->_dl_argv = SYMBOL_VALUE_ADDRESS (msymbol);
return 1;
}
struct so_list *
nacl_current_sos (void)
{
struct nacl_dynamic_loader_debugger_interface ni;
nacl_init_sandbox_base ();
if (!nacl_sandbox_base)
return NULL;
if (!nacl_exec_file)
return NULL;
if (nacl_discover_dynamic_loader_debugger_interface (&ni))
{
CORE_ADDR lm_addr;
struct so_list *head = 0;
struct so_list **link_ptr = &head;
/* for (lm_addr = _r_debug.r_map; lm_addr; lm_addr = lm_addr->l_next) */
for (lm_addr = read_memory_unsigned_integer (ni._r_debug + 4, 4);
lm_addr;
lm_addr = read_memory_unsigned_integer (nacl_sandbox_base + lm_addr + 16, 4))
{
CORE_ADDR l_addr;
CORE_ADDR l_name;
char* so_name;
int err;
struct so_list *new;
/* link_map::l_addr. */
l_addr = read_memory_unsigned_integer (nacl_sandbox_base + lm_addr, 4);
/* link_map::l_name. */
l_name = read_memory_unsigned_integer (nacl_sandbox_base + lm_addr + 8, 4);
target_read_string (nacl_sandbox_base + l_name, &so_name, SO_NAME_MAX_PATH_SIZE - 1, &err);
if (strcmp (so_name, "") == 0)
{
/* Native client dynamic executable. */
xfree (so_name);
l_name = read_memory_unsigned_integer (ni._dl_argv, 4);
l_name = read_memory_unsigned_integer (nacl_sandbox_base + l_name, 4);
target_read_string (nacl_sandbox_base + l_name, &so_name, SO_NAME_MAX_PATH_SIZE - 1, &err);
new = svr4_alloc_so (nacl_sandbox_base + l_addr, so_name);
}
else if (strcmp (so_name, "NaClMain") == 0)
{
/* Native client ld.so. */
new = svr4_alloc_so (nacl_sandbox_base + l_addr, nacl_exec_file);
}
else
{
/* Solib. */
new = svr4_alloc_so (nacl_sandbox_base + l_addr, so_name);
}
xfree (so_name);
new->next = 0;
*link_ptr = new;
link_ptr = &new->next;
}
if (head)
return head;
}
return svr4_alloc_so (nacl_sandbox_base, nacl_exec_file);
}
/* Arrange cooperation with nacl dynamic loader for loading nacl solibs.
Dynamic loader debugger interface:
Dynamic loader global structure "_r_debug" contains everything the debugger
needs to know. In particular we are interested in 2 fields:
- "_r_debug.r_map" points to the list of currently loaded solibs;
- "_r_debug.r_brk" points to the function that is called each time solib is
loaded or unloaded. That function is usually called "_dl_debug_state";
This function might be called when nacl dynamic loader was just loaded by
the service runtime but before it started to execute, thus "_r_debug"
structure might not be yet initialized.
Here we set a solib event breakpoint at "_dl_debug_state". */
int
nacl_solib_enable_break (void)
{
struct nacl_dynamic_loader_debugger_interface ni;
gdb_assert (nacl_gdb_state_p ());
gdb_assert (nacl_sandbox_base);
if (!nacl_discover_dynamic_loader_debugger_interface (&ni))
return 0;
create_solib_event_breakpoint (ni._dl_debug_state);
return 1;
}
/* Arrange cooperation with service runtime for loading nacl program.
Service runtime debugger interface:
When nacl program is loaded into memory but before it gets control, service
runtime calls function named "_ovly_debug_event". The name comes from the
debugger interface for i386-nacl, where nacl program loading event was
treated as an overlay event. For x86_64-nacl, we treat it as a solib event
instead.
At the moment when "_ovly_debug_event" gets called, service runtime global
variable "nacl_global_xlate_base" contains the address of the nacl sandbox
base.
Here we set a solib event breakpoint at "_ovly_debug_event". */
void
nacl_solib_create_inferior_hook (void)
{
struct minimal_symbol *msymbol;
gdb_assert (!nacl_gdb_state_p ());
/* First test the existence of nacl_global_xlate_base. It is not used here,
but this is the right place to perform a one-time test and issue a warning
if needed. */
msymbol = lookup_minimal_symbol ("nacl_global_xlate_base", NULL, symfile_objfile);
if (!msymbol || !SYMBOL_VALUE_ADDRESS (msymbol))
{
warning (_("failed to find service runtime debugger interface "
" (nacl_global_xlate_base)"));
return;
}
msymbol = lookup_minimal_symbol ("_ovly_debug_event", NULL, symfile_objfile);
if (!msymbol || !SYMBOL_VALUE_ADDRESS (msymbol))
{
warning (_("failed to find service runtime debugger interface "
" (_ovly_debug_event)"));
return;
}
/* TODO: if solib_add actually loaded some nacl stuff, this breakpoint is in
fact not needed as _ovly_debug_event was already called. */
create_solib_event_breakpoint (SYMBOL_VALUE_ADDRESS (msymbol));
}
/*******************************************************************************
*
* Commands
*
*******************************************************************************/
/* How to use the debugger:
Primary program being debugged is service runtime. Specify it as usually:
file ./sel_ldr
Set service runtime command line arguments. For sel_ldr, that should include
sel_ldr arguments together with nacl program and nacl program arguments:
set args -Q -a -c -f ./runnable-ld.so -- ./a.out
Set nacl program to be debugged. This has to be done explicitly, as we are
not always able to deduce nacl program name from the service runtime command
line, for example, chrome picks nacl program name from the html page. For
setting nacl program, we have a new command:
nacl-file ./runnable-ld.so
While debugging, we can switch views between service runtime (trusted code)
and nacl program (untrusted code0. Current view is indicated by gdb prompt:
(sr-gdb) - trusted code view
(nc-gdb) - untrusted code view
Use these new commands to switch views:
sr-gdb - switch to trusted code view
nc-gdb - switch to untrusted code view
Happy debugging! */
static void
nacl_file_command (char *args, int from_tty)
{
if (!args || !*args)
error (_("nacl-file command requires an argument"));
/* TODO: check there is exactly one argument. */
xfree (nacl_exec_file);
nacl_exec_file = tilde_expand (args);
}
static void
nacl_gdb_state_command (char *args, int from_tty)
{
set_nacl_gdb_state ();
}
static void
sr_gdb_state_command (char *args, int from_tty)
{
set_sr_gdb_state ();
}
static void
init_nacl_cmd (void)
{
struct cmd_list_element *c;
c = add_com ("nacl-file", class_files, nacl_file_command, _("\
Use FILE as untrusted program to be debugged."));
set_cmd_completer (c, filename_completer);
add_com ("nc-gdb", no_class, nacl_gdb_state_command, _("\
Debug untrusted code."));
add_com ("sr-gdb", no_class, sr_gdb_state_command, _("\
Debug trusted code."));
}
/*******************************************************************************
*
* Initialization
*
*******************************************************************************/
void
nacl_late_init (void)
{
gdb_assert (!nacl_gdb_state);
set_prompt ("(sr-gdb) ");
}
extern initialize_file_ftype _initialize_nacl_tdep; /* -Wmissing-prototypes */
void
_initialize_nacl_tdep (void)
{
init_nacl_cmd ();
}