Implement Luma3DS gdb hio (#433)

This commit is contained in:
TuxSH 2019-06-23 19:06:47 +02:00 committed by Dave Murphy
parent 754c334c29
commit ed7674626e
5 changed files with 721 additions and 0 deletions

View File

@ -91,6 +91,8 @@ extern "C" {
#include <3ds/font.h>
#include <3ds/mii.h>
#include <3ds/gdbhio_dev.h>
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,29 @@
/**
* @file gdbhio.h
* @brief Luma3DS GDB HIO (called File I/O in GDB documentation) functions.
*/
#pragma once
#include <fcntl.h>
#include <sys/time.h>
#include <sys/stat.h>
#define GDBHIO_STDIN_FILENO 0
#define GDBHIO_STDOUT_FILENO 1
#define GDBHIO_STDERR_FILENO 2
int gdbHioOpen(const char *pathname, int flags, mode_t mode);
int gdbHioClose(int fd);
int gdbHioRead(int fd, void *buf, unsigned int count);
int gdbHioWrite(int fd, const void *buf, unsigned int count);
off_t gdbHioLseek(int fd, off_t offset, int flag);
int gdbHioRename(const char *oldpath, const char *newpath);
int gdbHioUnlink(const char *pathname);
int gdbHioStat(const char *pathname, struct stat *st);
int gdbHioFstat(int fd, struct stat *st);
int gdbHioGettimeofday(struct timeval *tv, void *tz);
int gdbHioIsatty(int fd);
///< Host I/O 'system' function, requires 'set remote system-call-allowed 1'.
int gdbHioSystem(const char *command);

View File

@ -0,0 +1,37 @@
/**
* @file gdbhio_dev.h
* @brief Luma3DS GDB HIO (called File I/O in GDB documentation) devoptab wrapper.
*/
#pragma once
#include <stdbool.h>
struct timeval;
///< Initializes the GDB HIO devoptab wrapper, returns 0 on success, -1 on failure.
int gdbHioDevInit(void);
///< Deinitializes the GDB HIO devoptab wrapper.
void gdbHioDevExit(void);
///< Returns a file descriptor mapping to the GDB client console's standard input stream.
int gdbHioDevGetStdin(void);
///< Returns a file descriptor mapping to the GDB client console's standard output stream.
int gdbHioDevGetStdout(void);
///< Returns a file descriptor mapping to the GDB client console's standard error stream.
int gdbHioDevGetStderr(void);
///< Redirects 0 to 3 of the application's standard streams to GDB client console's. Returns -1, -2, or -3, resp., on failure; 0 on success.
int gdbHioDevRedirectStdStreams(bool in, bool out, bool err);
///< GDB HIO POSIX function gettimeofday.
int gdbHioDevGettimeofday(struct timeval *tv, void *tz);
///< GDB HIO POSIX function isatty.
int gdbHioDevIsatty(int fd);
///< GDB HIO POSIX function system. Requires 'set remote system-call-allowed 1'.
int gdbHioDevSystem(const char *command);

403
libctru/source/gdbhio.c Normal file
View File

@ -0,0 +1,403 @@
#include <3ds/gdbhio.h>
#include <3ds/svc.h>
#include <3ds/result.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#define GDBHIO_O_RDONLY 0x0
#define GDBHIO_O_WRONLY 0x1
#define GDBHIO_O_RDWR 0x2
#define GDBHIO_O_ACCMODE 0x3
#define GDBHIO_O_APPEND 0x8
#define GDBHIO_O_CREAT 0x200
#define GDBHIO_O_TRUNC 0x400
#define GDBHIO_O_EXCL 0x800
#define GDBHIO_O_SUPPORTED (GDBHIO_O_RDONLY | GDBHIO_O_WRONLY| \
GDBHIO_O_RDWR | GDBHIO_O_APPEND| \
GDBHIO_O_CREAT | GDBHIO_O_TRUNC| \
GDBHIO_O_EXCL)
#define GDBHIO_S_IFREG 0100000
#define GDBHIO_S_IFDIR 040000
#define GDBHIO_S_IFCHR 020000
#define GDBHIO_S_IRUSR 0400
#define GDBHIO_S_IWUSR 0200
#define GDBHIO_S_IXUSR 0100
#define GDBHIO_S_IRWXU (GDBHIO_S_IRUSR | GDBHIO_S_IWUSR | GDBHIO_S_IXUSR)
#define GDBHIO_S_IRGRP 040
#define GDBHIO_S_IWGRP 020
#define GDBHIO_S_IXGRP 010
#define GDBHIO_S_IRWXG (GDBHIO_S_IRGRP | GDBHIO_S_IWGRP | GDBHIO_S_IXGRP)
#define GDBHIO_S_IROTH 04
#define GDBHIO_S_IWOTH 02
#define GDBHIO_S_IXOTH 01
#define GDBHIO_S_IRWXO (GDBHIO_S_IROTH | GDBHIO_S_IWOTH | GDBHIO_S_IXOTH)
#define GDBHIO_S_SUPPORTED (GDBHIO_S_IFREG|GDBHIO_S_IFDIR| \
GDBHIO_S_IRWXU|GDBHIO_S_IRWXG| \
GDBHIO_S_IRWXO)
#define GDBHIO_SEEK_SET 0
#define GDBHIO_SEEK_CUR 1
#define GDBHIO_SEEK_END 2
#define GDBHIO_EPERM 1
#define GDBHIO_ENOENT 2
#define GDBHIO_EINTR 4
#define GDBHIO_EIO 5
#define GDBHIO_EBADF 9
#define GDBHIO_EACCES 13
#define GDBHIO_EFAULT 14
#define GDBHIO_EBUSY 16
#define GDBHIO_EEXIST 17
#define GDBHIO_ENODEV 19
#define GDBHIO_ENOTDIR 20
#define GDBHIO_EISDIR 21
#define GDBHIO_EINVAL 22
#define GDBHIO_ENFILE 23
#define GDBHIO_EMFILE 24
#define GDBHIO_EFBIG 27
#define GDBHIO_ENOSPC 28
#define GDBHIO_ESPIPE 29
#define GDBHIO_EROFS 30
#define GDBHIO_ENOSYS 88
#define GDBHIO_ENAMETOOLONG 91
#define GDBHIO_EUNKNOWN 9999
typedef struct PackedGdbHioRequest
{
char magic[4]; // "GDB\x00"
u32 version;
// Request
char functionName[16+1];
char paramFormat[8+1];
u64 parameters[8];
size_t stringLengths[8];
// Return
s64 retval;
int gdbErrno;
bool ctrlC;
} PackedGdbHioRequest;
static __thread bool g_gdbHioWasInterruptedByCtrlC = false;
bool gdbHioWasInterruptedByCtrlC(void)
{
return g_gdbHioWasInterruptedByCtrlC;
}
static int _gdbHioImportErrno(int errnum)
{
switch (errnum) {
case GDBHIO_EPERM: return EPERM;
case GDBHIO_ENOENT: return ENOENT;
case GDBHIO_EINTR: return EINTR;
case GDBHIO_EIO: return EIO;
case GDBHIO_EBADF: return EBADF;
case GDBHIO_EACCES: return EACCES;
case GDBHIO_EFAULT: return EFAULT;
case GDBHIO_EBUSY: return EBUSY;
case GDBHIO_EEXIST: return EEXIST;
case GDBHIO_ENODEV: return ENODEV;
case GDBHIO_ENOTDIR: return ENOTDIR;
case GDBHIO_EISDIR: return EISDIR;
case GDBHIO_EINVAL: return EINVAL;
case GDBHIO_ENFILE: return ENFILE;
case GDBHIO_EMFILE: return EMFILE;
case GDBHIO_EFBIG: return EFBIG;
case GDBHIO_ENOSPC: return ENOSPC;
case GDBHIO_ESPIPE: return ESPIPE;
case GDBHIO_EROFS: return EROFS;
case GDBHIO_ENOSYS: return ENOSYS;
case GDBHIO_ENAMETOOLONG: return ENAMETOOLONG;
default: return EPIPE;
}
}
static int _gdbHioExportOpenFlags(int flags)
{
int outflags = 0;
if (flags & O_CREAT) outflags |= GDBHIO_O_CREAT;
if (flags & O_EXCL) outflags |= GDBHIO_O_EXCL;
if (flags & O_TRUNC) outflags |= GDBHIO_O_TRUNC;
if (flags & O_APPEND) outflags |= GDBHIO_O_APPEND;
switch (flags & O_ACCMODE) {
case O_RDONLY:
outflags |= GDBHIO_O_RDONLY;
break;
case O_WRONLY:
outflags |= GDBHIO_O_WRONLY;
break;
case O_RDWR:
outflags |= GDBHIO_O_RDWR;
break;
default:
break;
}
// Note: O_BINARY is implicit if the host supports it
return outflags;
}
typedef s32 gdbhio_mode_t;
static mode_t _gdbHioImportFileMode(gdbhio_mode_t gdbMode)
{
mode_t mode = 0;
if (mode & ~GDBHIO_S_SUPPORTED) return -1;
if (gdbMode & GDBHIO_S_IFREG) mode |= S_IFREG;
if (gdbMode & GDBHIO_S_IFDIR) mode |= S_IFDIR;
if (gdbMode & GDBHIO_S_IFCHR) mode |= S_IFCHR;
if (gdbMode & GDBHIO_S_IRUSR) mode |= S_IRUSR;
if (gdbMode & GDBHIO_S_IWUSR) mode |= S_IWUSR;
if (gdbMode & GDBHIO_S_IXUSR) mode |= S_IXUSR;
if (gdbMode & GDBHIO_S_IRGRP) mode |= S_IRGRP;
if (gdbMode & GDBHIO_S_IWGRP) mode |= S_IWGRP;
if (gdbMode & GDBHIO_S_IXGRP) mode |= S_IXGRP;
if (gdbMode & GDBHIO_S_IROTH) mode |= S_IROTH;
if (gdbMode & GDBHIO_S_IWOTH) mode |= S_IWOTH;
if (gdbMode & GDBHIO_S_IXOTH) mode |= S_IXOTH;
return mode;
}
static int _gdbHioExportFileMode(mode_t mode)
{
gdbhio_mode_t gdbMode = 0;
if (mode & ~GDBHIO_S_SUPPORTED) return -1;
if (mode & S_IFREG) gdbMode |= GDBHIO_S_IFREG;
if (mode & S_IFDIR) gdbMode |= GDBHIO_S_IFDIR;
if (mode & S_IFCHR) gdbMode |= GDBHIO_S_IFCHR;
if (mode & S_IRUSR) gdbMode |= GDBHIO_S_IRUSR;
if (mode & S_IWUSR) gdbMode |= GDBHIO_S_IWUSR;
if (mode & S_IXUSR) gdbMode |= GDBHIO_S_IXUSR;
if (mode & S_IRGRP) gdbMode |= GDBHIO_S_IRGRP;
if (mode & S_IWGRP) gdbMode |= GDBHIO_S_IWGRP;
if (mode & S_IXGRP) gdbMode |= GDBHIO_S_IXGRP;
if (mode & S_IROTH) gdbMode |= GDBHIO_S_IROTH;
if (mode & S_IWOTH) gdbMode |= GDBHIO_S_IWOTH;
if (mode & S_IXOTH) gdbMode |= GDBHIO_S_IXOTH;
return mode;
}
static int _gdbExportSeekFlag(int flag)
{
switch (flag) {
case SEEK_SET: return GDBHIO_SEEK_SET;
case SEEK_CUR: return GDBHIO_SEEK_CUR;
case SEEK_END: return GDBHIO_SEEK_END;
default: return GDBHIO_SEEK_SET;
}
}
// https://sourceware.org/gdb/onlinedocs/gdb/struct-stat.html#struct-stat
typedef u32 gdbhio_time_t;
struct PACKED ALIGN(4) gdbhio_stat {
u32 st_dev; /* device */
u32 st_ino; /* inode */
gdbhio_mode_t st_mode; /* protection */
u32 st_nlink; /* number of hard links */
u32 st_uid; /* user ID of owner */
u32 st_gid; /* group ID of owner */
u32 st_rdev; /* device type (if inode device) */
u64 st_size; /* total size, in bytes */
u64 st_blksize; /* blocksize for filesystem I/O */
u64 st_blocks; /* number of blocks allocated */
gdbhio_time_t st_atime; /* time of last access */
gdbhio_time_t st_mtime; /* time of last modification */
gdbhio_time_t st_ctime; /* time of last change */
};
static inline u32 _gdbHioImportScalar32(u32 v)
{
return __builtin_bswap32(v);
}
static inline u64 _gdbHioImportScalar64(u64 v)
{
return __builtin_bswap64(v);
}
static void _gdbHioImportStructStat(struct stat *out, const struct gdbhio_stat *in)
{
memset(out, 0, sizeof(struct stat));
out->st_dev = _gdbHioImportScalar32(in->st_dev);
out->st_ino = _gdbHioImportScalar32(in->st_ino);
out->st_mode = _gdbHioImportFileMode(_gdbHioImportScalar32(in->st_mode));
out->st_nlink = _gdbHioImportScalar32(in->st_nlink);
out->st_uid = _gdbHioImportScalar32(in->st_uid);
out->st_gid = _gdbHioImportScalar32(in->st_gid);
out->st_rdev = _gdbHioImportScalar32(in->st_rdev);
out->st_size = (off_t)_gdbHioImportScalar64(in->st_size);
out->st_blksize = (blksize_t)_gdbHioImportScalar64(in->st_blksize);
out->st_blocks = (blkcnt_t)_gdbHioImportScalar64(in->st_blocks);
out->st_atime = _gdbHioImportScalar32(in->st_atime);
out->st_mtime = _gdbHioImportScalar32(in->st_mtime);
out->st_ctime = _gdbHioImportScalar32(in->st_ctime);
}
struct gdbhio_timeval {
gdbhio_time_t tv_sec; /* second */
u64 tv_usec; /* microsecond */
};
static void _gdbHioImportStructTimeval(struct timeval *out, const struct gdbhio_timeval *in)
{
out->tv_sec = _gdbHioImportScalar32(in->tv_sec);
out->tv_usec = _gdbHioImportScalar64(in->tv_usec);
}
static void _gdbHioSetErrno(int gdbErrno, bool ctrlC)
{
if (gdbErrno != 0) {
errno = _gdbHioImportErrno(gdbErrno);
}
g_gdbHioWasInterruptedByCtrlC = ctrlC;
}
static s64 _gdbHioSendSyncRequest64V(const char *name, const char *paramFormat, va_list args)
{
PackedGdbHioRequest req = {{0}};
memcpy(req.magic, "GDB", 4);
strncpy(req.functionName, name, 16);
strncpy(req.paramFormat, paramFormat, 8);
u32 numStrs = 0;
const char *str;
for (u32 i = 0; i < 8 && paramFormat[i] != 0; i++) {
switch (paramFormat[i]) {
case 'i':
case 'I':
req.parameters[i] = va_arg(args, u32);
break;
case 'l':
case 'L':
req.parameters[i] = va_arg(args, u64);
break;
case 'p':
req.parameters[i] = va_arg(args, uintptr_t);
break;
case 's':
str = va_arg(args, const char *);
req.parameters[i] = (uintptr_t)str;
req.stringLengths[numStrs++] = strlen(str)+1;
break;
default:
break;
}
}
if (R_FAILED(svcOutputDebugString((const char *)&req, 0)) || req.paramFormat[0] != 0) {
errno = EPIPE;
g_gdbHioWasInterruptedByCtrlC = false;
return -1;
}
_gdbHioSetErrno(req.gdbErrno, req.ctrlC);
return req.retval;
}
static s64 _gdbHioSendSyncRequest64(const char *name, const char *paramFormat, ...)
{
s64 ret = 0;
va_list args;
va_start(args, paramFormat);
ret = _gdbHioSendSyncRequest64V(name, paramFormat, args);
va_end(args);
return ret;
}
static int _gdbHioSendSyncRequest(const char *name, const char *paramFormat, ...)
{
s64 ret = 0;
va_list args;
va_start(args, paramFormat);
ret = _gdbHioSendSyncRequest64V(name, paramFormat, args);
va_end(args);
return (int)ret;
}
int gdbHioOpen(const char *pathname, int flags, mode_t mode)
{
return _gdbHioSendSyncRequest("open", "siI", pathname, _gdbHioExportOpenFlags(flags), _gdbHioExportFileMode(mode));
}
int gdbHioClose(int fd)
{
return _gdbHioSendSyncRequest("close", "i", fd);
}
int gdbHioRead(int fd, void *buf, unsigned int count)
{
return _gdbHioSendSyncRequest("read", "ipI", fd, buf, count);
}
int gdbHioWrite(int fd, const void *buf, unsigned int count)
{
return _gdbHioSendSyncRequest("write", "ipI", fd, buf, count);
}
off_t gdbHioLseek(int fd, off_t offset, int flag)
{
return _gdbHioSendSyncRequest64("lseek", "ili", fd, offset, _gdbExportSeekFlag(flag));
}
int gdbHioRename(const char *oldpath, const char *newpath)
{
return _gdbHioSendSyncRequest("rename", "ss", oldpath, newpath);
}
int gdbHioUnlink(const char *pathname)
{
return _gdbHioSendSyncRequest("unlink", "s", pathname);
}
int gdbHioStat(const char *pathname, struct stat *st)
{
struct gdbhio_stat gst;
int ret = _gdbHioSendSyncRequest("stat", "sp", pathname, &gst);
_gdbHioImportStructStat(st, &gst);
return ret;
}
int gdbHioFstat(int fd, struct stat *st)
{
struct gdbhio_stat gst;
int ret = _gdbHioSendSyncRequest("fstat", "ip", fd, &gst);
_gdbHioImportStructStat(st, &gst);
return ret;
}
int gdbHioGettimeofday(struct timeval *tv, void *tz)
{
// GDB ignores tz and passes NULL
struct gdbhio_timeval gtv;
int ret = _gdbHioSendSyncRequest("gettimeofday", "pp", &gtv, tz);
_gdbHioImportStructTimeval(tv, &gtv);
return ret;
}
int gdbHioIsatty(int fd)
{
return _gdbHioSendSyncRequest("isatty", "i", fd);
}
// Requires set remote system-call-allowed 1
int gdbHioSystem(const char *command)
{
return _gdbHioSendSyncRequest("system", "s", command);
}

250
libctru/source/gdbhio_dev.c Normal file
View File

@ -0,0 +1,250 @@
#include <3ds/gdbhio_dev.h>
#include <3ds/gdbhio.h>
#include <sys/iosupport.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
static int g_gdbHioStdinFd = -1, g_gdbHioStdoutFd = -1, g_gdbHioStderrFd = -1;
static int _gdbHioGetFd(int fd)
{
__handle *handle = __get_handle(fd);
if (handle == NULL) {
errno = EBADF;
return -1;
}
if(strcmp(devoptab_list[handle->device]->name, "gdbhio") != 0) {
errno = EBADF;
return -1;
}
return *(int *)handle->fileStruct;
}
static inline int _gdbHioGetFdFromPtr(void *fdptr)
{
return *(int *)fdptr;
}
static inline const char *_gdbHioSkipMountpoint(const char *pathname)
{
return strncmp(pathname, "gdbhio:", 7) == 0 ? pathname + 7 : pathname;
}
static int _gdbHioDevOpen(struct _reent *r, void *fdptr, const char *pathname, int flags, int mode)
{
(void)r;
pathname = _gdbHioSkipMountpoint(pathname);
int ret = gdbHioOpen(pathname, flags, mode);
if (ret < 0) {
return ret;
} else {
*(int *)fdptr = ret;
return 0;
}
}
static int _gdbHioDevClose(struct _reent *r, void *fdptr)
{
(void)r;
int fd = _gdbHioGetFdFromPtr(fdptr);
if (fd == g_gdbHioStdinFd)
g_gdbHioStdinFd = -1;
else if (fd == g_gdbHioStdoutFd)
g_gdbHioStdoutFd = -1;
else if (fd == g_gdbHioStderrFd)
g_gdbHioStderrFd = -1;
else
return gdbHioClose(fd);
return 0;
}
static ssize_t _gdbHioDevRead(struct _reent *r, void *fdptr, char *buf, size_t count)
{
(void)r;
return gdbHioRead(_gdbHioGetFdFromPtr(fdptr), buf, (unsigned int)count);
}
static ssize_t _gdbHioDevWrite(struct _reent *r, void *fdptr, const char *buf, size_t count)
{
(void)r;
return gdbHioWrite(_gdbHioGetFdFromPtr(fdptr), buf, (unsigned int)count);
}
static off_t _gdbHioDevLseek(struct _reent *r, void *fdptr, off_t offset, int flag)
{
(void)r;
return gdbHioLseek(_gdbHioGetFdFromPtr(fdptr), offset, flag);
}
static int _gdbHioDevRename(struct _reent *r, const char *oldpath, const char *newpath)
{
(void)r;
return gdbHioRename(_gdbHioSkipMountpoint(oldpath), _gdbHioSkipMountpoint(newpath));
}
static int _gdbHioDevUnlink(struct _reent *r, const char *pathname)
{
(void)r;
return gdbHioUnlink(_gdbHioSkipMountpoint(pathname));
}
static int _gdbHioDevStat(struct _reent *r, const char *pathname, struct stat *st)
{
(void)r;
return gdbHioStat(_gdbHioSkipMountpoint(pathname), st);
}
static int _gdbHioDevFstat(struct _reent *r, void *fdptr, struct stat *st)
{
(void)r;
return gdbHioFstat(_gdbHioGetFdFromPtr(fdptr), st);
}
static int _gdbHioDevImportFd(int gdbFd)
{
int fd, dev;
dev = FindDevice("gdbhio:");
if(dev == -1)
return -1;
fd = __alloc_handle(dev);
if(fd == -1)
return -1;
*(int *)__get_handle(fd)->fileStruct = gdbFd;
return fd;
}
static const devoptab_t g_gdbHioDevoptab = {
.name = "gdbhio",
.structSize = sizeof(int),
.open_r = _gdbHioDevOpen,
.close_r = _gdbHioDevClose,
.write_r = _gdbHioDevWrite,
.read_r = _gdbHioDevRead,
.seek_r = _gdbHioDevLseek,
.fstat_r = _gdbHioDevFstat,
.stat_r = _gdbHioDevStat,
.link_r = NULL,
.unlink_r = _gdbHioDevUnlink,
.chdir_r = NULL,
.rename_r = _gdbHioDevRename,
.mkdir_r = NULL,
.dirStateSize = 0,
.diropen_r = NULL,
.dirreset_r = NULL,
.dirnext_r = NULL,
.dirclose_r = NULL,
.statvfs_r = NULL,
.ftruncate_r = NULL,
.fsync_r = NULL,
.deviceData = 0,
.chmod_r = NULL,
.fchmod_r = NULL,
.rmdir_r = NULL,
};
int gdbHioDevGettimeofday(struct timeval *tv, void *tz)
{
return gdbHioGettimeofday(tv, tz);
}
int gdbHioDevIsatty(int fd)
{
return gdbHioIsatty(_gdbHioGetFd(fd));
}
int gdbHioDevSystem(const char *command)
{
return gdbHioSystem(command);
}
int gdbHioDevInit(void)
{
int dev = FindDevice("gdbhio:");
if (dev >= 0) {
return -1;
}
dev = AddDevice(&g_gdbHioDevoptab);
if (dev < 0) return -1;
return 0;
}
void gdbHioDevExit(void)
{
close(g_gdbHioStdinFd);
close(g_gdbHioStdoutFd);
close(g_gdbHioStderrFd);
RemoveDevice("gdbhio:");
}
int gdbHioDevGetStdin(void)
{
if (g_gdbHioStdinFd < 0)
g_gdbHioStdinFd = _gdbHioDevImportFd(GDBHIO_STDIN_FILENO);
return g_gdbHioStdinFd;
}
int gdbHioDevGetStdout(void)
{
if (g_gdbHioStdoutFd < 0)
g_gdbHioStdoutFd = _gdbHioDevImportFd(GDBHIO_STDOUT_FILENO);
return g_gdbHioStdoutFd;
}
int gdbHioDevGetStderr(void)
{
if (g_gdbHioStderrFd < 0)
g_gdbHioStderrFd = _gdbHioDevImportFd(GDBHIO_STDERR_FILENO);
return g_gdbHioStderrFd;
}
int gdbHioDevRedirectStdStreams(bool in, bool out, bool err)
{
int ret = 0;
if (in) {
if (gdbHioDevGetStdin() < 0) return -1;
ret = dup2(g_gdbHioStdinFd, STDIN_FILENO);
if (ret < 0) return -1;
if (ret != g_gdbHioStdinFd) {
close(g_gdbHioStdinFd);
g_gdbHioStdinFd = STDIN_FILENO;
}
}
if (out) {
if (gdbHioDevGetStdout() < 0) return -2;
ret = dup2(g_gdbHioStdoutFd, STDOUT_FILENO);
if (ret < 0) return -2;
if (ret != g_gdbHioStdoutFd) {
close(g_gdbHioStdoutFd);
g_gdbHioStdoutFd = STDOUT_FILENO;
}
}
if (err) {
if (gdbHioDevGetStderr() < 0) return -3;
ret = dup2(g_gdbHioStderrFd, STDERR_FILENO);
if (ret < 0) return -3;
if (ret != g_gdbHioStderrFd) {
close(g_gdbHioStderrFd);
g_gdbHioStderrFd = STDERR_FILENO;
}
}
return 0;
}