From: "Kris Warkentin" <kewarken@qnx.com>
To: "Mark Kettenis" <kettenis@chello.nl>
Cc: <gdb-patches@sources.redhat.com>
Subject: Re: [RFC] QNX Neutrino/i386 support
Date: Sun, 09 Mar 2003 17:51:00 -0000 [thread overview]
Message-ID: <002501c2e665$150a09e0$2a00a8c0@dash> (raw)
In-Reply-To: <200303090013.h290DnBU000820@elgar.kettenis.dyndns.org>
[-- Attachment #1: Type: text/plain, Size: 407 bytes --]
> Here are my comments about the i386-nto-tdep.c file. Some general
> stuff first (which probably applies to the other files as well):
Hi Mark,
I've done the cleaning that you proposed and attached the revised files.
I'm not really fond of the branch and merging idea so if you really don't
want to put all of this stuff in, we'll rip everything out, start with the
minimum and work up from there.
Kris
[-- Attachment #2: nto-tdep.c --]
[-- Type: application/octet-stream, Size: 8792 bytes --]
/* nto-tdep.c - general QNX Neutrino target functionality */
/* Copyright 2003 Free Software Foundation */
/* Contributed by QNX Software Systems Ltd. */
#include <sys/stat.h>
#include "nto-tdep.h"
#include "top.h"
#include "cli/cli-decode.h"
#include "cli/cli-cmds.h"
#include "inferior.h"
#include "gdbarch.h"
#include "bfd.h"
#include "elf-bfd.h"
#include "solib-svr4.h"
#include "gdbcore.h"
#ifdef __CYGWIN__
#include <sys/cygwin.h>
#endif
#ifdef __CYGWIN__
static char default_nto_target[] = "C:\\QNXsdk\\target\\qnx6";
#elif defined(__sun__) || defined(linux)
static char default_nto_target[] = "/opt/QNXsdk/target/qnx6";
#else
static char default_nto_target[] = "";
#endif
/* Maintenance debugging flag */
int nto_internal_debugging;
/* Filled in cpu info structure and flag to indicate its validity.
* This is initialized in procfs_attach or nto_start_remote depending on
* our host/target. It would only be invalid if we were talking to an
* older pdebug which didn't support the cpuinfo message. */
unsigned nto_cpuinfo_flags;
int nto_cpuinfo_valid;
static char *
nto_target (void)
{
char *p = getenv ("QNX_TARGET");
#ifdef __CYGWIN__
static char buf[PATH_MAX];
if (p)
cygwin_conv_to_posix_path (p, buf);
else
cygwin_conv_to_posix_path (default_nto_target, buf);
return buf;
#else
return p ? p : default_nto_target;
#endif
}
/* Take a string such as i386, rs6000, etc. and map it onto CPUTYPE_X86,
CPUTYPE_PPC, etc. as defined in nto-share/dsmsgs.h. */
int
nto_map_arch_to_cputype(const char *arch)
{
if(!strcmp(arch, "i386") || !strcmp(arch, "x86"))
return CPUTYPE_X86;
if(!strcmp(arch, "rs6000") || !strcmp(arch, "ppc"))
return CPUTYPE_PPC;
if(!strcmp(arch, "mips"))
return CPUTYPE_MIPS;
if(!strcmp(arch, "arm"))
return CPUTYPE_ARM;
if(!strcmp(arch, "sh"))
return CPUTYPE_SH;
return CPUTYPE_UNKNOWN;
}
int
nto_find_and_open_solib (char *solib, unsigned o_flags, char **temp_pathname)
{
char *buf, arch_path[PATH_MAX], *nto_root, *endian;
const char *arch;
char *path_fmt = "%s/lib:%s/usr/lib:%s/usr/photon/lib\
:%s/usr/photon/dll:%s/lib/dll";
nto_root = nto_target ();
if (strcmp (TARGET_ARCHITECTURE->arch_name, "i386") == 0)
{
arch = "x86";
endian = "";
}
else if (strcmp (TARGET_ARCHITECTURE->arch_name, "rs6000") == 0)
{
arch = "ppc";
endian = "be";
}
else
{
arch = TARGET_ARCHITECTURE->arch_name;
endian = TARGET_BYTE_ORDER == BFD_ENDIAN_BIG ? "be" : "le";
}
sprintf (arch_path, "%s/%s%s", nto_root, arch, endian);
buf = alloca (strlen(path_fmt) + strlen(arch_path) * 5 + 1);
sprintf (buf, path_fmt, arch_path, arch_path, arch_path, arch_path, arch_path);
return openp(buf, 1, solib, o_flags, 0, temp_pathname);
}
void
nto_init_solib_absolute_prefix (void)
{
char buf[PATH_MAX * 2], arch_path[PATH_MAX];
char *nto_root, *endian;
const char *arch;
nto_root = nto_target ();
if (strcmp (TARGET_ARCHITECTURE->arch_name, "i386") == 0)
{
arch = "x86";
endian = "";
}
else if (strcmp (TARGET_ARCHITECTURE->arch_name, "rs6000") == 0)
{
arch = "ppc";
endian = "be";
}
else
{
arch = TARGET_ARCHITECTURE->arch_name;
endian = TARGET_BYTE_ORDER == BFD_ENDIAN_BIG ? "be" : "le";
}
sprintf (arch_path, "%s/%s%s", nto_root, arch, endian);
sprintf (buf, "set solib-absolute-prefix %s", arch_path);
execute_command (buf, 0);
}
char **
nto_parse_redirection (char *pargv[], char **pin, char **pout, char **perr)
{
char **argv;
char *in, *out, *err, *p;
int argc, i, n;
for (n = 0; pargv[n]; n++);
if (n == 0)
return NULL;
in = "";
out = "";
err = "";
argv = xcalloc (n + 1, sizeof argv[0]);
argc = n;
for (i = 0, n = 0; n < argc; n++)
{
p = pargv[n];
if (*p == '>')
{
p++;
if (*p)
out = p;
else
out = pargv[++n];
}
else if (*p == '<')
{
p++;
if (*p)
in = p;
else
in = pargv[++n];
}
else if (*p++ == '2' && *p++ == '>')
{
if (*p == '&' && *(p + 1) == '1')
err = out;
else if (*p)
err = p;
else
err = pargv[++n];
}
else
argv[i++] = pargv[n];
}
*pin = in;
*pout = out;
*perr = err;
return argv;
}
void
nto_source_extra_gdbinit (char *file)
{
char *homedir, *homeinit;
struct stat statbuf;
homedir = getenv ("HOME");
if (homedir && !inhibit_gdbinit)
{
homeinit = (char *) alloca (strlen (homedir) + strlen (file) + 10);
sprintf (homeinit, "%s/%s", homedir, file);
if (stat (homeinit, &statbuf) == 0)
catch_command_errors (source_command, homeinit, 0, RETURN_MASK_ALL);
}
}
/* struct lm_info, LM_ADDR, and nto_truncate_ptr are copied from
solib-svr4.c to support nto_relocate_section_addresses
which is different from the svr4 version. */
struct lm_info
{
/* Pointer to copy of link map from inferior. The type is char *
rather than void *, so that we may use byte offsets to find the
various fields without the need for a cast. */
char *lm;
};
static CORE_ADDR
LM_ADDR (struct so_list *so)
{
struct link_map_offsets *lmo = nto_fetch_link_map_offsets ();
return (CORE_ADDR) extract_signed_integer (so->lm_info->lm +
lmo->l_addr_offset,
lmo->l_addr_size);
}
static CORE_ADDR
nto_truncate_ptr (CORE_ADDR addr)
{
if (TARGET_PTR_BIT == sizeof (CORE_ADDR) * 8)
/* We don't need to truncate anything, and the bit twiddling below
will fail due to overflow problems. */
return addr;
else
return addr & (((CORE_ADDR) 1 << TARGET_PTR_BIT) - 1);
}
Elf_Internal_Phdr *
find_load_phdr (bfd *abfd)
{
Elf_Internal_Phdr *phdr;
unsigned int i;
if (!elf_tdata (abfd))
return NULL;
phdr = elf_tdata (abfd)->phdr;
for (i = 0; i < elf_elfheader (abfd)->e_phnum; i++, phdr++)
{
if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_X))
return phdr;
}
return NULL;
}
void
nto_relocate_section_addresses (struct so_list *so, struct section_table *sec)
{
/* Neutrino treats the l_addr base address field in link.h as different than
the base address in the System V ABI and so the offset needs to be
calculated and applied to relocations. */
Elf_Internal_Phdr *phdr = find_load_phdr (sec->bfd);
unsigned vaddr = phdr ? phdr->p_vaddr : 0;
sec->addr = nto_truncate_ptr (sec->addr + LM_ADDR (so) - vaddr);
sec->endaddr = nto_truncate_ptr (sec->endaddr + LM_ADDR (so) - vaddr);
}
static void
fetch_core_registers (char *core_reg_sect, unsigned core_reg_size,
int which, CORE_ADDR reg_addr)
{
nto_gregset_t gregset;
nto_fpregset_t fpregset;
nto_init_solib_absolute_prefix ();
if (which == 0)
{
memcpy ((char *) &gregset, core_reg_sect,
min (core_reg_size, sizeof (gregset)));
nto_supply_gregset (&gregset);
}
else if (which == 2)
{
memcpy ((char *) &fpregset, core_reg_sect,
min (core_reg_size, sizeof (fpregset)));
nto_supply_fpregset (&fpregset);
}
}
/* Register that we are able to handle ELF file formats using standard
procfs "regset" structures. */
static struct core_fns regset_core_fns = {
bfd_target_elf_flavour, /* core_flavour */
default_check_format, /* check_format */
default_core_sniffer, /* core_sniffer */
fetch_core_registers, /* core_read_registers */
NULL /* next */
};
void
_initialize_nto_tdep ()
{
add_show_from_set (add_set_cmd ("nto-debug", class_maintenance, var_zinteger, (char *) &nto_internal_debugging, "Set QNX NTO internal debugging.\n\
When non-zero, nto specific debug info is\n\
displayed. Different information is displayed\n\
for different positive values.", &setdebuglist),
&showdebuglist);
/* We use SIG45 for pulses, or something, so nostop, noprint
and pass them. */
signal_stop_update (target_signal_from_name ("SIG45"), 0);
signal_print_update (target_signal_from_name ("SIG45"), 0);
signal_pass_update (target_signal_from_name ("SIG45"), 1);
/* By default we don't want to stop on these two, but we do want to pass. */
#if defined(SIGSELECT)
signal_stop_update (SIGSELECT, 0);
signal_print_update (SIGSELECT, 0);
signal_pass_update (SIGSELECT, 1);
#endif
#if defined(SIGPHOTON)
signal_stop_update (SIGPHOTON, 0);
signal_print_update (SIGPHOTON, 0);
signal_pass_update (SIGPHOTON, 1);
#endif
/* Register core file support. */
add_core_fns (®set_core_fns);
}
[-- Attachment #3: nto-tdep.h --]
[-- Type: application/octet-stream, Size: 1894 bytes --]
/* nto-tdep.h - QNX Neutrino target header */
/* Copyright 2003 Free Software Foundation */
/* Contributed by QNX Software Systems Ltd. */
#ifndef _NTO_TDEP_H
#define _NTO_TDEP_H
#include "defs.h"
#include "nto-share/debug.h"
#include "solist.h"
/* Generic functions in nto-tdep.c. */
extern void nto_init_solib_absolute_prefix PARAMS ((void));
char **nto_parse_redirection
PARAMS ((char *start_argv[], char **in, char **out, char **err));
int proc_iterate_over_mappings PARAMS ((int (*func) (int, CORE_ADDR)));
void nto_source_extra_gdbinit PARAMS ((char *));
void nto_relocate_section_addresses
PARAMS ((struct so_list *, struct section_table *));
int nto_map_arch_to_cputype PARAMS((const char *));
int nto_find_and_open_solib PARAMS((char *, unsigned, char **));
/* Functions exported from all Neutrino targets (<target>-nto-tdep.c). */
unsigned nto_get_regset_id PARAMS ((int regno));
void nto_cpu_regset_fetch PARAMS ((int endian, int regset, void *data));
unsigned nto_get_regset_area PARAMS ((unsigned regset, char *subcmd));
unsigned nto_cpu_register_area
PARAMS ((unsigned first_regno, unsigned last_regno, unsigned char *subcmd,
unsigned *off, unsigned *len));
int nto_cpu_register_store
PARAMS ((int endian, unsigned first_regno, unsigned last_regno, void *data));
void nto_supply_gregset PARAMS ((union _debug_gregs *));
void nto_supply_fpregset PARAMS ((union _debug_fpregs *));
struct link_map_offsets *nto_fetch_link_map_offsets PARAMS ((void));
/* Globals. */
/* For 'maintenance debug nto-debug' command. */
extern int nto_internal_debugging;
/* The CPUINFO flags from the remote. Currently used by
i386 for fxsave but future proofing other hosts */
extern unsigned nto_cpuinfo_flags;
/* True if successfully retrieved cpuinfo from remote */
extern int nto_cpuinfo_valid;
#endif
[-- Attachment #4: i386-nto-tdep.c --]
[-- Type: application/octet-stream, Size: 9401 bytes --]
/* i386-nto-tdep.c - i386 specific functionality for QNX Neutrino */
/* Copyright 2003 Free Software Foundation */
/* Contributed by QNX Software Systems Ltd. */
#include "defs.h"
#include "frame.h"
#include "target.h"
#include "regcache.h"
#include "solib-svr4.h"
#include "i386-tdep.h"
#include "nto-tdep.h"
#include "osabi.h"
#include "nto-share/debug.h"
#include "i387-tdep.h"
#define NUM_GPREGS 13
static int gdb_to_nto[NUM_GPREGS] =
{ 7, 6, 5, 4, 11, 2, 1, 0, 8, 10, 9, 12, -1 };
#define GDB_TO_OS(x) ((x >= 0 && x < NUM_GPREGS) ? gdb_to_nto[x] : -1)
#ifndef X86_CPU_FXSR
#define X86_CPU_FXSR (1L << 12)
#endif
enum QNX_REGS
{
QNX_REGS_GP = 0,
QNX_REGS_FP = 1,
QNX_REGS_END = 2
};
/* Given a register, return an id that represents the Neutrino
regset it came from. If reg == -1 update all regsets. */
unsigned
nto_get_regset_id (int regno)
{
if (regno == -1)
return QNX_REGS_END;
else if (regno < FP0_REGNUM)
return QNX_REGS_GP;
else if (regno < FPC_REGNUM)
return QNX_REGS_FP;
return -1; /* error */
}
/* Tell GDB about the regset contained in the 'data' buffer. */
static void
gp_regset_fetch (char *data)
{
unsigned first_regno;
for (first_regno = 0; first_regno < FP0_REGNUM; first_regno++)
{
int regno = GDB_TO_OS (first_regno);
int const blank = 0;
if (data == NULL || regno == -1)
supply_register (first_regno, (char *) &blank);
else
supply_register (first_regno, &data[regno * 4]);
}
}
/* Get 8087 data. */
static void
fp_regset_fetch (char *data)
{
if (nto_cpuinfo_valid && nto_cpuinfo_flags | X86_CPU_FXSR)
i387_supply_fxsave (data);
else
i387_supply_fsave (data);
}
void
nto_cpu_regset_fetch (int endian, int regset, void *data)
{
endian = endian; /* For i386, we don't care about endian. */
switch (regset)
{
case QNX_REGS_GP: /* QNX has different ordering of GP regs than GDB. */
gp_regset_fetch ((char *) data);
break;
case QNX_REGS_FP:
fp_regset_fetch ((char *) data);
break;
}
}
/* Get regset characteristics. */
unsigned
nto_get_regset_area (unsigned regset, char *subcmd)
{
unsigned length = 0;
switch (regset)
{
case QNX_REGS_GP:
*subcmd = NTO_REG_GENERAL;
length = NUM_GPREGS * sizeof (unsigned);
break;
case QNX_REGS_FP:
*subcmd = NTO_REG_FLOAT;
/* FIXME: should we calculate based on fxsave/fsave? */
length = 512;
break;
default:
length = 0;
}
return length;
}
/* Given the first and last register number, figure out the size/len
of the Neutrino register save area to ask for/tell about. Also set
the register set that's being dealt with in *subcmd. Watch out for
the range crossing a register set boundry. */
unsigned
nto_cpu_register_area (unsigned first_regno, unsigned last_regno,
unsigned char *subcmd, unsigned *off, unsigned *len)
{
int regno = -1;
if (first_regno < FP0_REGNUM)
{
if (last_regno >= FP0_REGNUM)
last_regno = FP0_REGNUM - 1;
*subcmd = NTO_REG_GENERAL;
regno = GDB_TO_OS (first_regno);
*off = regno * sizeof (unsigned);
if (regno == -1)
*len = 0;
else
*len = (last_regno - first_regno + 1) * sizeof (unsigned);
}
else if (first_regno == FP_REGNUM)
{
/* Frame Pointer Psuedo-register */
*off = SP_REGNUM * sizeof (unsigned);
*len = sizeof (unsigned);
return FP_REGNUM;
}
else if (first_regno >= FP0_REGNUM && first_regno < FPC_REGNUM)
{
unsigned off_adjust, regsize;
if (nto_cpuinfo_valid && nto_cpuinfo_flags | X86_CPU_FXSR)
{
off_adjust = 32;
regsize = 16;
}
else
{
off_adjust = 28;
regsize = 10;
}
if (last_regno >= FPC_REGNUM)
last_regno = FPC_REGNUM - 1;
*subcmd = NTO_REG_FLOAT;
*off = (first_regno - FP0_REGNUM) * regsize + off_adjust;
*len = (last_regno - first_regno + 1) * 10;
/* Why 10? GDB only stores 10 bytes per FP register so if we're
sending a register back to the target, we only want pdebug to write
10 bytes so as not to clobber the reserved 6 bytes in the fxsave
structure. The astute reader will note that this will fail if we
try to send a range of fpregs rather than 1 at a time but, as far
as I know, there is no way to send more than one fpreg at a time
anyway. If this turns out to be wrong, we may need to put more code
in pdebug to deal with this - perhaps by masking off part of the
register when it writes it in.*/
}
else
{
*len = 0;
return last_regno;
}
return last_regno;
}
/* Build the Neutrino register set info into the 'data' buffer. */
int
nto_cpu_register_store (int endian, unsigned first_regno,
unsigned last_regno, void *data)
{
/* Mostly (always?) you're only storing one at a time. */
if (first_regno == last_regno)
{
regcache_collect (first_regno, data);
return 1;
}
/* Floating point is the same for gdb and target */
if (first_regno >= FP0_REGNUM)
{
for (; first_regno <= last_regno; first_regno++)
{
regcache_collect (first_regno, data);
(char *) data += REGISTER_RAW_SIZE (first_regno);
}
return 1;
}
/* GP registers are mapped differently for NTO than GDB */
for (; first_regno <= last_regno; first_regno++)
{
int regnum = GDB_TO_OS (first_regno);
if (regnum == -1)
continue;
regcache_collect (first_regno,
(char *) data + sizeof (unsigned) * regnum);
}
return 1;
}
void
nto_supply_gregset (nto_gregset_t *gregsetp)
{
int regi, i = 0;
unsigned *regp = (unsigned *) gregsetp;
for (regi = 0; regi < (NUM_REGS - FP0_REGNUM); regi++)
{
i = GDB_TO_OS (regi);
supply_register (regi, (char *) ®p[i]);
}
}
void
nto_supply_fpregset (nto_fpregset_t *fpregsetp)
{
if (nto_cpuinfo_valid && nto_cpuinfo_flags | X86_CPU_FXSR)
i387_supply_fxsave ((char *) fpregsetp);
else
i387_supply_fsave ((char *) fpregsetp);
}
/* Fetch (and possibly build) an appropriate link_map_offsets
structure for native x86 targets using the struct offsets
defined in link.h (but without actual reference to that file).
This makes it possible to access x86 shared libraries from a GDB
that was not built on an x86 host (for cross debugging). */
static struct link_map_offsets *
i386_nto_svr4_fetch_link_map_offsets (void)
{
static struct link_map_offsets lmo;
static struct link_map_offsets *lmp = NULL;
if (lmp == NULL)
{
lmp = &lmo;
lmo.r_debug_size = 8; /* The actual size is 20 bytes, but
this is all we need. */
lmo.r_map_offset = 4;
lmo.r_map_size = 4;
lmo.link_map_size = 20; /* The actual size is 552 bytes, but
this is all we need. */
lmo.l_addr_offset = 0;
lmo.l_addr_size = 4;
lmo.l_name_offset = 4;
lmo.l_name_size = 4;
lmo.l_next_offset = 12;
lmo.l_next_size = 4;
lmo.l_prev_offset = 16;
lmo.l_prev_size = 4;
}
return lmp;
}
struct link_map_offsets *
nto_fetch_link_map_offsets (void)
{
return i386_nto_svr4_fetch_link_map_offsets ();
}
static int
i386_nto_pc_in_sigtramp (CORE_ADDR pc, char *name)
{
return name && strcmp ("__signalstub", name) == 0;
}
static CORE_ADDR
i386_nto_sigcontext_addr (struct frame_info *frame)
{
int sigcontext_offset = 136;
if (get_next_frame (frame))
return get_frame_base (get_next_frame (frame)) + sigcontext_offset;
return read_register (SP_REGNUM) + sigcontext_offset;
}
static void
i386_nto_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
/* NTO uses ELF. */
i386_elf_init_abi (info, gdbarch);
/* neutrino rewinds to look more normal */
set_gdbarch_decr_pc_after_break (gdbarch, 0);
/* NTO has shared libraries. */
set_gdbarch_in_solib_call_trampoline (gdbarch, in_plt_section);
set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
set_gdbarch_pc_in_sigtramp (gdbarch, i386_nto_pc_in_sigtramp);
tdep->sigcontext_addr = i386_nto_sigcontext_addr;
tdep->sc_pc_offset = 56;
tdep->sc_sp_offset = 68;
/* setjmp()'s return PC saved in EDX (5) */
tdep->jb_pc_offset = 20; /* 5x32 bit ints in. */
set_solib_svr4_fetch_link_map_offsets (gdbarch,
i386_nto_svr4_fetch_link_map_offsets);
/* Our loader handles solib relocations slightly differently than svr4 */
TARGET_SO_RELOCATE_SECTION_ADDRESSES = nto_relocate_section_addresses;
/* Supply a nice function to find our solibs */
TARGET_SO_FIND_AND_OPEN_SOLIB = nto_find_and_open_solib;
/* After a watchpoint trap, the PC points to the instruction after
the one that caused the trap. Therefore we don't need to step over it.
But we do need to reset the status register to avoid another trap. */
HAVE_CONTINUABLE_WATCHPOINT = 1;
}
void
_initialize_i386_nto_tdep (void)
{
gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_QNXNTO,
i386_nto_init_abi);
}
next prev parent reply other threads:[~2003-03-09 17:51 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2003-03-08 13:45 Mark Kettenis
2003-03-08 15:02 ` Kris Warkentin
2003-03-08 23:49 ` Mark Kettenis
2003-03-09 0:14 ` Mark Kettenis
2003-03-09 4:34 ` Kris Warkentin
2003-03-09 17:51 ` Kris Warkentin [this message]
2003-03-19 18:19 ` Kris Warkentin
2003-03-19 18:27 ` Mark Kettenis
2003-03-08 15:19 ` Kris Warkentin
2003-03-08 15:22 ` Kris Warkentin
2003-03-09 14:54 Kris Warkentin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to='002501c2e665$150a09e0$2a00a8c0@dash' \
--to=kewarken@qnx.com \
--cc=gdb-patches@sources.redhat.com \
--cc=kettenis@chello.nl \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox