[PATCH] gdbstub/user-target: Map errno values from the host OS to GDB

Yodel Eldar via posted 1 patch 1 month ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20251015162520.15736-1-yodel.eldar@yodel.dev
Maintainers: "Alex Bennée" <alex.bennee@linaro.org>, "Philippe Mathieu-Daudé" <philmd@linaro.org>
gdbstub/user-target.c | 93 +++++++++++++++++++++++++++++++++++++++++--
1 file changed, 89 insertions(+), 4 deletions(-)
[PATCH] gdbstub/user-target: Map errno values from the host OS to GDB
Posted by Yodel Eldar via 1 month ago
This patch introduces the function "gdb_host_errno_to_gdb" that maps
host-dependent errno values to their GDB protocol-specific
representations as listed in the GDB manual [1].

The stub now uses the correct GDB errno values in F reply packets.

[1] https://sourceware.org/gdb/current/onlinedocs/gdb.html/Errno-Values.html

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2751
Reported-by: Dominik 'Disconnect3d' Czarnota <dominik.b.czarnota@gmail.com>
Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
---
 gdbstub/user-target.c | 93 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 89 insertions(+), 4 deletions(-)

diff --git a/gdbstub/user-target.c b/gdbstub/user-target.c
index 43231e695e..29feb0509c 100644
--- a/gdbstub/user-target.c
+++ b/gdbstub/user-target.c
@@ -302,6 +302,87 @@ static void hostio_reply_with_data(const void *buf, size_t n)
                           gdbserver_state.str_buf->len, true);
 }
 
+/*
+ * Map host error numbers to their GDB protocol counterparts.
+ * For the list of GDB File-I/O supported error numbers, please consult:
+ * https://sourceware.org/gdb/current/onlinedocs/gdb.html/Errno-Values.html
+ */
+
+static int gdb_host_errno_to_gdb(int errnum)
+{
+    enum {
+        GDB_EPERM        =    1,
+        GDB_ENOENT       =    2,
+        GDB_EINTR        =    4,
+        GDB_EIO          =    5,
+        GDB_EBADF        =    9,
+        GDB_EACCES       =   13,
+        GDB_EFAULT       =   14,
+        GDB_EBUSY        =   16,
+        GDB_EEXIST       =   17,
+        GDB_ENODEV       =   19,
+        GDB_ENOTDIR      =   20,
+        GDB_EISDIR       =   21,
+        GDB_EINVAL       =   22,
+        GDB_ENFILE       =   23,
+        GDB_EMFILE       =   24,
+        GDB_EFBIG        =   27,
+        GDB_ENOSPC       =   28,
+        GDB_ESPIPE       =   29,
+        GDB_EROFS        =   30,
+        GDB_ENOSYS       =   88,
+        GDB_ENAMETOOLONG =   91,
+        GDB_EUNKNOWN     = 9999,
+    };
+
+    switch (errnum) {
+    case EPERM:
+        return GDB_EPERM;
+    case ENOENT:
+        return GDB_ENOENT;
+    case EINTR:
+        return GDB_EINTR;
+    case EIO:
+        return GDB_EIO;
+    case EBADF:
+        return GDB_EBADF;
+    case EACCES:
+        return GDB_EACCES;
+    case EFAULT:
+        return GDB_EFAULT;
+    case EBUSY:
+        return GDB_EBUSY;
+    case EEXIST:
+        return GDB_EEXIST;
+    case ENODEV:
+        return GDB_ENODEV;
+    case ENOTDIR:
+        return GDB_ENOTDIR;
+    case EISDIR:
+        return GDB_EISDIR;
+    case EINVAL:
+        return GDB_EINVAL;
+    case ENFILE:
+        return GDB_ENFILE;
+    case EMFILE:
+        return GDB_EMFILE;
+    case EFBIG:
+        return GDB_EFBIG;
+    case ENOSPC:
+        return GDB_ENOSPC;
+    case ESPIPE:
+        return GDB_ESPIPE;
+    case EROFS:
+        return GDB_EROFS;
+    case ENOSYS:
+        return GDB_ENOSYS;
+    case ENAMETOOLONG:
+        return GDB_ENAMETOOLONG;
+    default:
+        return GDB_EUNKNOWN;
+    }
+}
+
 void gdb_handle_v_file_open(GArray *params, void *user_ctx)
 {
     const char *filename = get_filename_param(params, 0);
@@ -315,7 +396,8 @@ void gdb_handle_v_file_open(GArray *params, void *user_ctx)
     int fd = open(filename, flags, mode);
 #endif
     if (fd < 0) {
-        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
+        int gdb_errno = gdb_host_errno_to_gdb(errno);
+        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
     } else {
         g_string_printf(gdbserver_state.str_buf, "F%x", fd);
     }
@@ -327,7 +409,8 @@ void gdb_handle_v_file_close(GArray *params, void *user_ctx)
     int fd = gdb_get_cmd_param(params, 0)->val_ul;
 
     if (close(fd) == -1) {
-        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
+        int gdb_errno = gdb_host_errno_to_gdb(errno);
+        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
         gdb_put_strbuf();
         return;
     }
@@ -350,7 +433,8 @@ void gdb_handle_v_file_pread(GArray *params, void *user_ctx)
 
     ssize_t n = pread(fd, buf, bufsiz, offset);
     if (n < 0) {
-        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
+        int gdb_errno = gdb_host_errno_to_gdb(errno);
+        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
         gdb_put_strbuf();
         return;
     }
@@ -373,7 +457,8 @@ void gdb_handle_v_file_readlink(GArray *params, void *user_ctx)
     ssize_t n = readlink(filename, buf, BUFSIZ);
 #endif
     if (n < 0) {
-        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
+        int gdb_errno = gdb_host_errno_to_gdb(errno);
+        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
         gdb_put_strbuf();
         return;
     }
-- 
2.51.0
Re: [PATCH] gdbstub/user-target: Map errno values from the host OS to GDB
Posted by Alex Bennée 4 weeks, 1 day ago
Yodel Eldar <yodel.eldar@yodel.dev> writes:

> This patch introduces the function "gdb_host_errno_to_gdb" that maps
> host-dependent errno values to their GDB protocol-specific
> representations as listed in the GDB manual [1].
>
> The stub now uses the correct GDB errno values in F reply packets.
>
> [1] https://sourceware.org/gdb/current/onlinedocs/gdb.html/Errno-Values.html
>
> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2751
> Reported-by: Dominik 'Disconnect3d' Czarnota <dominik.b.czarnota@gmail.com>
> Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
> ---
>  gdbstub/user-target.c | 93 +++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 89 insertions(+), 4 deletions(-)
>
> diff --git a/gdbstub/user-target.c b/gdbstub/user-target.c
> index 43231e695e..29feb0509c 100644
> --- a/gdbstub/user-target.c
> +++ b/gdbstub/user-target.c
> @@ -302,6 +302,87 @@ static void hostio_reply_with_data(const void *buf, size_t n)
>                            gdbserver_state.str_buf->len, true);
>  }
>  
> +/*
> + * Map host error numbers to their GDB protocol counterparts.
> + * For the list of GDB File-I/O supported error numbers, please consult:
> + * https://sourceware.org/gdb/current/onlinedocs/gdb.html/Errno-Values.html
> + */
> +
> +static int gdb_host_errno_to_gdb(int errnum)
> +{
> +    enum {
> +        GDB_EPERM        =    1,
> +        GDB_ENOENT       =    2,
> +        GDB_EINTR        =    4,
> +        GDB_EIO          =    5,
> +        GDB_EBADF        =    9,
> +        GDB_EACCES       =   13,
> +        GDB_EFAULT       =   14,
> +        GDB_EBUSY        =   16,
> +        GDB_EEXIST       =   17,
> +        GDB_ENODEV       =   19,
> +        GDB_ENOTDIR      =   20,
> +        GDB_EISDIR       =   21,
> +        GDB_EINVAL       =   22,
> +        GDB_ENFILE       =   23,
> +        GDB_EMFILE       =   24,
> +        GDB_EFBIG        =   27,
> +        GDB_ENOSPC       =   28,
> +        GDB_ESPIPE       =   29,
> +        GDB_EROFS        =   30,
> +        GDB_ENOSYS       =   88,
> +        GDB_ENAMETOOLONG =   91,
> +        GDB_EUNKNOWN     = 9999,
> +    };

We have this enum in include/gdbstub/syscalls.h already.

> +
> +    switch (errnum) {
> +    case EPERM:
> +        return GDB_EPERM;
> +    case ENOENT:
> +        return GDB_ENOENT;
> +    case EINTR:
> +        return GDB_EINTR;
> +    case EIO:
> +        return GDB_EIO;
> +    case EBADF:
> +        return GDB_EBADF;
> +    case EACCES:
> +        return GDB_EACCES;
> +    case EFAULT:
> +        return GDB_EFAULT;
> +    case EBUSY:
> +        return GDB_EBUSY;
> +    case EEXIST:
> +        return GDB_EEXIST;
> +    case ENODEV:
> +        return GDB_ENODEV;
> +    case ENOTDIR:
> +        return GDB_ENOTDIR;
> +    case EISDIR:
> +        return GDB_EISDIR;
> +    case EINVAL:
> +        return GDB_EINVAL;
> +    case ENFILE:
> +        return GDB_ENFILE;
> +    case EMFILE:
> +        return GDB_EMFILE;
> +    case EFBIG:
> +        return GDB_EFBIG;
> +    case ENOSPC:
> +        return GDB_ENOSPC;
> +    case ESPIPE:
> +        return GDB_ESPIPE;
> +    case EROFS:
> +        return GDB_EROFS;
> +    case ENOSYS:
> +        return GDB_ENOSYS;
> +    case ENAMETOOLONG:
> +        return GDB_ENAMETOOLONG;
> +    default:
> +        return GDB_EUNKNOWN;
> +    }
> +}
> +
>  void gdb_handle_v_file_open(GArray *params, void *user_ctx)
>  {
>      const char *filename = get_filename_param(params, 0);
> @@ -315,7 +396,8 @@ void gdb_handle_v_file_open(GArray *params, void *user_ctx)
>      int fd = open(filename, flags, mode);
>  #endif
>      if (fd < 0) {
> -        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
> +        int gdb_errno = gdb_host_errno_to_gdb(errno);
> +        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
>      } else {
>          g_string_printf(gdbserver_state.str_buf, "F%x", fd);
>      }
> @@ -327,7 +409,8 @@ void gdb_handle_v_file_close(GArray *params, void *user_ctx)
>      int fd = gdb_get_cmd_param(params, 0)->val_ul;
>  
>      if (close(fd) == -1) {
> -        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
> +        int gdb_errno = gdb_host_errno_to_gdb(errno);
> +        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
>          gdb_put_strbuf();
>          return;
>      }
> @@ -350,7 +433,8 @@ void gdb_handle_v_file_pread(GArray *params, void *user_ctx)
>  
>      ssize_t n = pread(fd, buf, bufsiz, offset);
>      if (n < 0) {
> -        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
> +        int gdb_errno = gdb_host_errno_to_gdb(errno);
> +        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
>          gdb_put_strbuf();
>          return;
>      }
> @@ -373,7 +457,8 @@ void gdb_handle_v_file_readlink(GArray *params, void *user_ctx)
>      ssize_t n = readlink(filename, buf, BUFSIZ);
>  #endif
>      if (n < 0) {
> -        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
> +        int gdb_errno = gdb_host_errno_to_gdb(errno);
> +        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
>          gdb_put_strbuf();
>          return;
>      }

with that fixed:

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro
Re: [PATCH] gdbstub/user-target: Map errno values from the host OS to GDB
Posted by Yodel Eldar via 4 weeks ago
Hi, Alex!

On 10/16/25 9:56 AM, Alex Bennée wrote:
> Yodel Eldar <yodel.eldar@yodel.dev> writes:
> 
>> This patch introduces the function "gdb_host_errno_to_gdb" that maps
>> host-dependent errno values to their GDB protocol-specific
>> representations as listed in the GDB manual [1].
>>
>> The stub now uses the correct GDB errno values in F reply packets.
>>
>> [1] https://sourceware.org/gdb/current/onlinedocs/gdb.html/Errno-Values.html
>>
>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2751
>> Reported-by: Dominik 'Disconnect3d' Czarnota <dominik.b.czarnota@gmail.com>
>> Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
>> ---
>>   gdbstub/user-target.c | 93 +++++++++++++++++++++++++++++++++++++++++--
>>   1 file changed, 89 insertions(+), 4 deletions(-)
>>
>> diff --git a/gdbstub/user-target.c b/gdbstub/user-target.c
>> index 43231e695e..29feb0509c 100644
>> --- a/gdbstub/user-target.c
>> +++ b/gdbstub/user-target.c
>> @@ -302,6 +302,87 @@ static void hostio_reply_with_data(const void *buf, size_t n)
>>                             gdbserver_state.str_buf->len, true);
>>   }
>>   
>> +/*
>> + * Map host error numbers to their GDB protocol counterparts.
>> + * For the list of GDB File-I/O supported error numbers, please consult:
>> + * https://sourceware.org/gdb/current/onlinedocs/gdb.html/Errno-Values.html
>> + */
>> +
>> +static int gdb_host_errno_to_gdb(int errnum)
>> +{
>> +    enum {
>> +        GDB_EPERM        =    1,
>> +        GDB_ENOENT       =    2,
>> +        GDB_EINTR        =    4,
>> +        GDB_EIO          =    5,
>> +        GDB_EBADF        =    9,
>> +        GDB_EACCES       =   13,
>> +        GDB_EFAULT       =   14,
>> +        GDB_EBUSY        =   16,
>> +        GDB_EEXIST       =   17,
>> +        GDB_ENODEV       =   19,
>> +        GDB_ENOTDIR      =   20,
>> +        GDB_EISDIR       =   21,
>> +        GDB_EINVAL       =   22,
>> +        GDB_ENFILE       =   23,
>> +        GDB_EMFILE       =   24,
>> +        GDB_EFBIG        =   27,
>> +        GDB_ENOSPC       =   28,
>> +        GDB_ESPIPE       =   29,
>> +        GDB_EROFS        =   30,
>> +        GDB_ENOSYS       =   88,
>> +        GDB_ENAMETOOLONG =   91,
>> +        GDB_EUNKNOWN     = 9999,
>> +    };
> 
> We have this enum in include/gdbstub/syscalls.h already.
> 

Thanks for pointing that out!

>> +
>> +    switch (errnum) {
>> +    case EPERM:
>> +        return GDB_EPERM;
>> +    case ENOENT:
>> +        return GDB_ENOENT;
>> +    case EINTR:
>> +        return GDB_EINTR;
>> +    case EIO:
>> +        return GDB_EIO;
>> +    case EBADF:
>> +        return GDB_EBADF;
>> +    case EACCES:
>> +        return GDB_EACCES;
>> +    case EFAULT:
>> +        return GDB_EFAULT;
>> +    case EBUSY:
>> +        return GDB_EBUSY;
>> +    case EEXIST:
>> +        return GDB_EEXIST;
>> +    case ENODEV:
>> +        return GDB_ENODEV;
>> +    case ENOTDIR:
>> +        return GDB_ENOTDIR;
>> +    case EISDIR:
>> +        return GDB_EISDIR;
>> +    case EINVAL:
>> +        return GDB_EINVAL;
>> +    case ENFILE:
>> +        return GDB_ENFILE;
>> +    case EMFILE:
>> +        return GDB_EMFILE;
>> +    case EFBIG:
>> +        return GDB_EFBIG;
>> +    case ENOSPC:
>> +        return GDB_ENOSPC;
>> +    case ESPIPE:
>> +        return GDB_ESPIPE;
>> +    case EROFS:
>> +        return GDB_EROFS;
>> +    case ENOSYS:
>> +        return GDB_ENOSYS;
>> +    case ENAMETOOLONG:
>> +        return GDB_ENAMETOOLONG;
>> +    default:
>> +        return GDB_EUNKNOWN;
>> +    }
>> +}
>> +
>>   void gdb_handle_v_file_open(GArray *params, void *user_ctx)
>>   {
>>       const char *filename = get_filename_param(params, 0);
>> @@ -315,7 +396,8 @@ void gdb_handle_v_file_open(GArray *params, void *user_ctx)
>>       int fd = open(filename, flags, mode);
>>   #endif
>>       if (fd < 0) {
>> -        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
>> +        int gdb_errno = gdb_host_errno_to_gdb(errno);
>> +        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
>>       } else {
>>           g_string_printf(gdbserver_state.str_buf, "F%x", fd);
>>       }
>> @@ -327,7 +409,8 @@ void gdb_handle_v_file_close(GArray *params, void *user_ctx)
>>       int fd = gdb_get_cmd_param(params, 0)->val_ul;
>>   
>>       if (close(fd) == -1) {
>> -        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
>> +        int gdb_errno = gdb_host_errno_to_gdb(errno);
>> +        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
>>           gdb_put_strbuf();
>>           return;
>>       }
>> @@ -350,7 +433,8 @@ void gdb_handle_v_file_pread(GArray *params, void *user_ctx)
>>   
>>       ssize_t n = pread(fd, buf, bufsiz, offset);
>>       if (n < 0) {
>> -        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
>> +        int gdb_errno = gdb_host_errno_to_gdb(errno);
>> +        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
>>           gdb_put_strbuf();
>>           return;
>>       }
>> @@ -373,7 +457,8 @@ void gdb_handle_v_file_readlink(GArray *params, void *user_ctx)
>>       ssize_t n = readlink(filename, buf, BUFSIZ);
>>   #endif
>>       if (n < 0) {
>> -        g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno);
>> +        int gdb_errno = gdb_host_errno_to_gdb(errno);
>> +        g_string_printf(gdbserver_state.str_buf, "F-1,%x", gdb_errno);
>>           gdb_put_strbuf();
>>           return;
>>       }
> 
> with that fixed:
> 
> Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
> 

Since your review, I've noticed that Richard Henderson already authored
a host-to-GDB errno mapping function in commit 7327e6023:
host_to_gdb_errno:target/m68k/m68k-semi.c; it's functionally identical
to the mapping proposed in this patch (albeit missing two errno values
that were undocumented in the GDB manual until recently), but written in
the context of semihosting for the m68k.

So as to avoid duplicating code, I'm considering exporting the existing
host_to_gdb_errno in a minor refactor to use that instead; do you think
that's a better route?

Thanks,
Yodel

Re: [PATCH] gdbstub/user-target: Map errno values from the host OS to GDB
Posted by Richard Henderson 4 weeks ago
On 10/16/25 13:51, Yodel Eldar via wrote:
> Since your review, I've noticed that Richard Henderson already authored
> a host-to-GDB errno mapping function in commit 7327e6023:
> host_to_gdb_errno:target/m68k/m68k-semi.c; it's functionally identical
> to the mapping proposed in this patch (albeit missing two errno values
> that were undocumented in the GDB manual until recently), but written in
> the context of semihosting for the m68k.

Heh.  I had glanced at your patch and thought "don't we do that already"?

> 
> So as to avoid duplicating code, I'm considering exporting the existing
> host_to_gdb_errno in a minor refactor to use that instead; do you think
> that's a better route?

Yes.


r~
Re: [PATCH] gdbstub/user-target: Map errno values from the host OS to GDB
Posted by Yodel Eldar via 4 weeks ago
On 10/16/25 4:51 PM, Richard Henderson wrote:
> On 10/16/25 13:51, Yodel Eldar via wrote:
>> Since your review, I've noticed that Richard Henderson already authored
>> a host-to-GDB errno mapping function in commit 7327e6023:
>> host_to_gdb_errno:target/m68k/m68k-semi.c; it's functionally identical
>> to the mapping proposed in this patch (albeit missing two errno values
>> that were undocumented in the GDB manual until recently), but written in
>> the context of semihosting for the m68k.
> 
> Heh.  I had glanced at your patch and thought "don't we do that already"?
> 
>>
>> So as to avoid duplicating code, I'm considering exporting the existing
>> host_to_gdb_errno in a minor refactor to use that instead; do you think
>> that's a better route?
> 
> Yes.
> 
> 
> r~
> 

Haha, grateful for your eyes and input on this! I'll be sure to Cc the
next time I name-check you.

Yodel