Hi. I'm seeing arguments to preadv and pwritev parsed incorrectly on an
ARM EABI kernel. I have this test program (built with gcc -std=gnu99
-D_FILE_OFFSET_BITS=64)

 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <sys/uio.h>

 int main(void)
 {
     const off_t offset = 1234567890123456789LL;
     char buf[4];

     int fd_zero = open("/dev/zero", O_RDONLY);
     pread (fd_zero, buf, sizeof(buf), offset);
     preadv(fd_zero,
            &(struct iovec){ .iov_base = buf,
                    .iov_len = sizeof(buf)},
            1, offset );

     int fd_null = open("/dev/null", O_WRONLY);
     pwrite(fd_null, buf, sizeof(buf), offset);
     pwritev(fd_null,
             &(struct iovec){.iov_base = buf, .iov_len = sizeof(buf)},
             1, offset );

     return 0;
 }

The relevant parts of strace output are

 open("/dev/zero", O_RDONLY|O_LARGEFILE) = 3
 pread(3, "\0\0\0\0", 4, 1234567890123456789) = 4
 preadv(3, [{"\0\0\0\0", 4}], 1, 4582412532) = 4
 open("/dev/null", O_WRONLY|O_LARGEFILE) = 4
 pwrite(4, "\0\0\0\0", 4, 1234567890123456789) = 4
 pwritev(4, [{"\0\0\0\0", 4}], 1, 4582412532) = 4

Note that the 'offset' parameter in preadv/pwritev is reported as
4582412532. As you can see in the source, the offset is actually the
same for all the calls: 1234567890123456789.

I can fix it with the following patch (not proposing this patch for
merging; it's just to demonstrate the issue).


--- a/io.c
+++ b/io.c
@@ -231,7 +231,7 @@ sys_preadv(struct tcb *tcp)
                }
                tprint_iov(tcp, tcp->u_arg[2], tcp->u_arg[1], 1);
                tprintf(", %lu, ", tcp->u_arg[2]);
-               printllval(tcp, "%llu", PREAD_OFFSET_ARG);
+               printllval(tcp, "%llu", PREAD_OFFSET_ARG + 1000);
        }
        return 0;
 }
@@ -244,7 +244,7 @@ sys_pwritev(struct tcb *tcp)
                tprints(", ");
                tprint_iov(tcp, tcp->u_arg[2], tcp->u_arg[1], 1);
                tprintf(", %lu, ", tcp->u_arg[2]);
-               printllval(tcp, "%llu", PREAD_OFFSET_ARG);
+               printllval(tcp, "%llu", PREAD_OFFSET_ARG + 1000);
        }
        return 0;
 }
diff --git a/util.c b/util.c
index 85bb94c..49a6c2e 100644
--- a/util.c
+++ b/util.c
@@ -262,7 +262,7 @@ printllval(struct tcb *tcp, const char *format, int arg_no)
      defined POWERPC || \
      defined XTENSA
        /* Align arg_no to the next even number. */
-       arg_no = (arg_no + 1) & 0xe;
+       arg_no = arg_no > 1000 ? (arg_no - 1000) : ((arg_no + 1) & 0xe);
 # endif
        tprintf(format, LONG_LONG(tcp->u_arg[arg_no], tcp->u_arg[arg_no + 1]));
        arg_no += 2;


This relates to how the kernel passes 64-bit arguments in 32-bit
architectures. The ARM EABI is supposed to pass these in a consecutive
pair of 32-bit registers, starting with an even one. This is what strace
does. For some reason that I'm not yet clear on, this appears to be
wrong for preadv and pwritev. The patch above short-circuits this
logic for preadv/pwritev, to take this argument in r3/r4 instead of
r4/r5.

Does anybody know what's going on? I can imagine anything from the
kernel being inconsistent with itself to glibc producing an incorrect
kernel interface.

dima

------------------------------------------------------------------------------
Learn Graph Databases - Download FREE O'Reilly Book
"Graph Databases" is the definitive new guide to graph databases and their
applications. Written by three acclaimed leaders in the field,
this first edition is now available. Download your free book today!
http://p.sf.net/sfu/NeoTech
_______________________________________________
Strace-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/strace-devel

Reply via email to