]> granicus.if.org Git - strace/blobdiff - ldt.c
nlattr: add unsigned int decoders that print in hex form
[strace] / ldt.c
diff --git a/ldt.c b/ldt.c
index 735cb137d0ea2ffc72dd2388a52804d99b5593ef..104abc315da6cb2b20db26221c8e8bcc0ec5bc8a 100644 (file)
--- a/ldt.c
+++ b/ldt.c
@@ -6,7 +6,7 @@
  * Copyright (c) 2002-2004 Roland McGrath <roland@redhat.com>
  * Copyright (c) 2010 Andreas Schwab <schwab@linux-m68k.org>
  * Copyright (c) 2014-2015 Dmitry V. Levin <ldv@altlinux.org>
- * Copyright (c) 2014-2017 The strace developers.
+ * Copyright (c) 2014-2018 The strace developers.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 # include "xstring.h"
 
 void
-print_user_desc(struct tcb *const tcp, const kernel_ulong_t addr)
+print_user_desc(struct tcb *const tcp, const kernel_ulong_t addr,
+               enum user_desc_print_filter filter)
 {
        struct user_desc desc;
+       unsigned *entry_number = get_tcb_priv_data(tcp);
 
-       if (umove_or_printaddr(tcp, addr, &desc))
-               return;
+       switch (filter) {
+       case USER_DESC_ENTERING:
+               if (umove_or_printaddr(tcp, addr, &desc.entry_number))
+                       return;
 
-       PRINT_FIELD_ID("{",  desc, entry_number);
-       PRINT_FIELD_0X(", ", desc, base_addr);
-       PRINT_FIELD_0X(", ", desc, limit);
-       PRINT_FIELD_U_CAST(", ", desc, seg_32bit, unsigned int);
-       PRINT_FIELD_U_CAST(", ", desc, contents, unsigned int);
-       PRINT_FIELD_U_CAST(", ", desc, read_exec_only, unsigned int);
-       PRINT_FIELD_U_CAST(", ", desc, limit_in_pages, unsigned int);
-       PRINT_FIELD_U_CAST(", ", desc, seg_not_present, unsigned int);
-       PRINT_FIELD_U_CAST(", ", desc, useable, unsigned int);
+               break;
+
+       case USER_DESC_EXITING:
+               if (!addr || !verbose(tcp))
+                       return;
+               if (syserror(tcp) || umove(tcp, addr, &desc)) {
+                       if (entry_number)
+                               tprints(", ...}");
+
+                       return;
+               }
+
+               break;
+
+       case USER_DESC_BOTH:
+               if (umove_or_printaddr(tcp, addr, &desc))
+                       return;
+
+               break;
+       }
+
+       if (filter & USER_DESC_ENTERING) {
+               PRINT_FIELD_ID("{", desc, entry_number);
+
+               /*
+                * If we don't print the whole structure now, let's save it for
+                * later.
+                */
+               if (filter == USER_DESC_ENTERING) {
+                       entry_number = xmalloc(sizeof(*entry_number));
+
+                       *entry_number = desc.entry_number;
+                       set_tcb_priv_data(tcp, entry_number, free);
+               }
+       }
+
+       if (filter & USER_DESC_EXITING) {
+               /*
+                * It should be the same in case of get_thread_area, but we can
+                * never be sure...
+                */
+               if (filter == USER_DESC_EXITING) {
+                       if (entry_number) {
+                               if (*entry_number != desc.entry_number) {
+                                       if ((int) desc.entry_number == -1)
+                                               tprints(" => -1");
+                                       else
+                                               tprintf(" => %u",
+                                                       desc.entry_number);
+                               }
+                       } else {
+                               /*
+                                * This is really strange. If we are here, it
+                                * means that we failed on entering but somehow
+                                * succeeded on exiting.
+                                */
+                               PRINT_FIELD_ID(" => {", desc, entry_number);
+                       }
+               }
+
+               PRINT_FIELD_0X(", ", desc, base_addr);
+               PRINT_FIELD_0X(", ", desc, limit);
+               PRINT_FIELD_U_CAST(", ", desc, seg_32bit, unsigned int);
+               PRINT_FIELD_U_CAST(", ", desc, contents, unsigned int);
+               PRINT_FIELD_U_CAST(", ", desc, read_exec_only, unsigned int);
+               PRINT_FIELD_U_CAST(", ", desc, limit_in_pages, unsigned int);
+               PRINT_FIELD_U_CAST(", ", desc, seg_not_present, unsigned int);
+               PRINT_FIELD_U_CAST(", ", desc, useable, unsigned int);
 
 # ifdef HAVE_STRUCT_USER_DESC_LM
-       /* lm is totally ignored for 32-bit processes */
-       if (current_klongsize == 8)
-               PRINT_FIELD_U_CAST(", ", desc, lm, unsigned int);
+               /* lm is totally ignored for 32-bit processes */
+               if (current_klongsize == 8)
+                       PRINT_FIELD_U_CAST(", ", desc, lm, unsigned int);
 # endif /* HAVE_STRUCT_USER_DESC_LM */
 
-       tprints("}");
+               tprints("}");
+       }
 }
 
 SYS_FUNC(modify_ldt)
 {
-       tprintf("%d, ", (int) tcp->u_arg[0]);
-       if (tcp->u_arg[2] != sizeof(struct user_desc))
-               printaddr(tcp->u_arg[1]);
-       else
-               print_user_desc(tcp, tcp->u_arg[1]);
-       tprintf(", %" PRI_klu, tcp->u_arg[2]);
+       if (entering(tcp)) {
+               tprintf("%d, ", (int) tcp->u_arg[0]);
+               if (tcp->u_arg[2] != sizeof(struct user_desc))
+                       printaddr(tcp->u_arg[1]);
+               else
+                       print_user_desc(tcp, tcp->u_arg[1], USER_DESC_BOTH);
+               tprintf(", %" PRI_klu, tcp->u_arg[2]);
+
+               return 0;
+       }
 
-       return RVAL_DECODED;
+       /*
+        * For some reason ("tht ABI for sys_modify_ldt() expects
+        * 'int'"), modify_ldt clips higher bits on x86_64.
+        */
+
+       if (syserror(tcp) || (kernel_ulong_t) tcp->u_rval < 0xfffff000)
+               return 0;
+
+       tcp->u_error = -(unsigned int) tcp->u_rval;
+
+       return 0;
 }
 
 SYS_FUNC(set_thread_area)
 {
        if (entering(tcp)) {
-               print_user_desc(tcp, tcp->u_arg[0]);
+               print_user_desc(tcp, tcp->u_arg[0], USER_DESC_BOTH);
        } else {
                struct user_desc desc;
 
@@ -103,8 +181,8 @@ SYS_FUNC(set_thread_area)
 
 SYS_FUNC(get_thread_area)
 {
-       if (exiting(tcp))
-               print_user_desc(tcp, tcp->u_arg[0]);
+       print_user_desc(tcp, tcp->u_arg[0],
+                       entering(tcp) ? USER_DESC_ENTERING : USER_DESC_EXITING);
        return 0;
 }