diff options
author | Denys Vlasenko <dvlasenk@redhat.com> | 2013-11-09 20:40:31 +0100 |
---|---|---|
committer | Denys Vlasenko <dvlasenk@redhat.com> | 2013-11-09 20:40:31 +0100 |
commit | b338f2d65ea9d911a0fead3229ba336bc30e6214 (patch) | |
tree | 8a37a1bd344653fdd4d684418556db3b3d578b6a | |
parent | 1297a513dad3ce3bc6f8472ed592a865aea18fac (diff) | |
download | strace-b338f2d65ea9d911a0fead3229ba336bc30e6214.tar.gz strace-b338f2d65ea9d911a0fead3229ba336bc30e6214.tar.bz2 strace-b338f2d65ea9d911a0fead3229ba336bc30e6214.tar.xz |
Fix select decoding on e.g. 32-bit ppc process by 64-bit strace.
Added next_set_bit() function which finds the next set bit,
properly taking into account word size of the traced process.
Use it in decode_select() instead of fd_isset().
Also, properly round fdsize up to word size of traced process,
not to strace's word size.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
-rw-r--r-- | defs.h | 1 | ||||
-rw-r--r-- | desc.c | 68 | ||||
-rw-r--r-- | util.c | 47 |
3 files changed, 75 insertions, 41 deletions
@@ -648,6 +648,7 @@ extern const char *xlookup(const struct xlat *, int); extern int string_to_uint(const char *str); extern int string_quote(const char *, char *, long, int); +extern int next_set_bit(const void *bit_array, unsigned cur_bit, unsigned size_bits); /* a refers to the lower numbered u_arg, * b refers to the higher numbered u_arg @@ -477,22 +477,6 @@ sys_getdtablesize(struct tcb *tcp) } #endif -/* FD_ISSET from libc would abort for large fd if built with - * debug flags/library hacks which enforce array bound checks - * (fd_set contains a fixed-size array of longs). - * We need to use a homegrown replacement. - */ -static inline int -fd_isset(unsigned fd, fd_set *fds) -{ - /* Using unsigned types to avoid signed divisions and shifts, - * which are slow(er) on many CPUs. - */ - const unsigned bpl = 8 * sizeof(long); - unsigned long *s = (unsigned long *) fds; - return s[fd / bpl] & (1UL << (fd % bpl)); -} - static int decode_select(struct tcb *tcp, long *args, enum bitness_t bitness) { @@ -518,7 +502,7 @@ decode_select(struct tcb *tcp, long *args, enum bitness_t bitness) * We had bugs a-la "while (j < args[0])" and "umoven(args[0])" below. * Instead of args[0], use nfds for fd count, fdsize for array lengths. */ - fdsize = (((nfds + 7) / 8) + sizeof(long)-1) & -sizeof(long); + fdsize = (((nfds + 7) / 8) + current_wordsize-1) & -current_wordsize; if (entering(tcp)) { tprintf("%d", (int) args[0]); @@ -543,12 +527,13 @@ decode_select(struct tcb *tcp, long *args, enum bitness_t bitness) continue; } tprints(", ["); - for (j = 0, sep = ""; j < nfds; j++) { - if (fd_isset(j, fds)) { - tprints(sep); - printfd(tcp, j); - sep = " "; - } + for (j = 0, sep = "";; j++) { + j = next_set_bit(fds, j, nfds); + if (j < 0) + break; + tprints(sep); + printfd(tcp, j); + sep = " "; } tprints("]"); } @@ -583,26 +568,27 @@ decode_select(struct tcb *tcp, long *args, enum bitness_t bitness) arg = args[i+1]; if (!arg || umoven(tcp, arg, fdsize, (char *) fds) < 0) continue; - for (j = 0; j < nfds; j++) { - if (fd_isset(j, fds)) { - /* +2 chars needed at the end: ']',NUL */ - if (outptr < end_outstr - (sizeof(", except [") + sizeof(int)*3 + 2)) { - if (first) { - outptr += sprintf(outptr, "%s%s [%u", - sep, - i == 0 ? "in" : i == 1 ? "out" : "except", - j - ); - first = 0; - sep = ", "; - } - else { - outptr += sprintf(outptr, " %u", j); - } + for (j = 0;; j++) { + j = next_set_bit(fds, j, nfds); + if (j < 0) + break; + /* +2 chars needed at the end: ']',NUL */ + if (outptr < end_outstr - (sizeof(", except [") + sizeof(int)*3 + 2)) { + if (first) { + outptr += sprintf(outptr, "%s%s [%u", + sep, + i == 0 ? "in" : i == 1 ? "out" : "except", + j + ); + first = 0; + sep = ", "; + } + else { + outptr += sprintf(outptr, " %u", j); } - if (--ready_fds == 0) - break; } + if (--ready_fds == 0) + break; } if (outptr != outstr) *outptr++ = ']'; @@ -160,6 +160,53 @@ stpcpy(char *dst, const char *src) } #endif +/* Find a next bit which is set. + * Starts testing at cur_bit. + * Returns -1 if no more bits are set. + * + * We never touch bytes we don't need to. + * On big-endian, array is assumed to consist of + * current_wordsize wide words: for example, is current_wordsize is 4, + * the bytes are walked in 3,2,1,0, 7,6,5,4, 11,10,9,8 ... sequence. + * On little-endian machines, word size is immaterial. + */ +int +next_set_bit(const void *bit_array, unsigned cur_bit, unsigned size_bits) +{ + const unsigned endian = 1; + int little_endian = *(char*)&endian; + + const uint8_t *array = bit_array; + unsigned pos = cur_bit / 8; + unsigned pos_xor_mask = little_endian ? 0 : current_wordsize-1; + + for (;;) { + uint8_t bitmask; + uint8_t cur_byte; + + if (cur_bit >= size_bits) + return -1; + cur_byte = array[pos ^ pos_xor_mask]; + if (cur_byte == 0) { + cur_bit = (cur_bit + 8) & (-8); + pos++; + continue; + } + bitmask = 1 << (cur_bit & 7); + for (;;) { + if (cur_byte & bitmask) + return cur_bit; + cur_bit++; + if (cur_bit >= size_bits) + return -1; + bitmask <<= 1; + /* This check *can't be* optimized out: */ + if (bitmask == 0) + break; + } + pos++; + } +} /* * Print entry in struct xlat table, if there. */ |