From: Tom Hughes Date: Sun, 13 Nov 2005 16:52:56 +0000 (+0000) Subject: Make the address space manager use fstat64 when it is available. There X-Git-Tag: svn/VALGRIND_3_1_0~116 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0618e53e2841a6451c9a09c32a30a306fc61fd66;p=thirdparty%2Fvalgrind.git Make the address space manager use fstat64 when it is available. There are two reasons for this: - It can cope with manjor and minor device numbers outside the traditional 0-255 range. - It returns correct results for x86 binaries on amd64 systems where fstat returns uninitialised rubbish in the top 16 bits of the device number. We also make the /proc/self/maps reading code encode device numbers in the new style to cope with manjor and minor device numbers outside the traditional 0-255 range. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@5116 --- diff --git a/coregrind/m_aspacemgr/aspacemgr.c b/coregrind/m_aspacemgr/aspacemgr.c index 96cc84f4da..1a419d7dc9 100644 --- a/coregrind/m_aspacemgr/aspacemgr.c +++ b/coregrind/m_aspacemgr/aspacemgr.c @@ -508,6 +508,14 @@ static Int aspacem_fstat( Int fd, struct vki_stat* buf ) return res.isError ? (-1) : 0; } +#ifdef __NR_fstat64 +static Int aspacem_fstat64( Int fd, struct vki_stat64* buf ) +{ + SysRes res = VG_(do_syscall2)(__NR_fstat64, fd, (UWord)buf); + return res.isError ? (-1) : 0; +} +#endif + static void aspacem_exit( Int status ) { (void)VG_(do_syscall1)(__NR_exit_group, status ); @@ -545,7 +553,21 @@ Bool get_inode_for_fd ( Int fd, /*OUT*/UWord* dev, /*OUT*/UWord* ino, /*OUT*/UInt* mode ) { struct vki_stat buf; - Int r = aspacem_fstat(fd, &buf); + Int r; +#ifdef __NR_fstat64 + struct vki_stat64 buf64; + /* Try fstat64 first as it can cope with minor and major device + numbers outside the 0-255 range and it works properly for x86 + binaries on amd64 systems where fstat seems to be broken. */ + r = aspacem_fstat64(fd, &buf64); + if (r == 0) { + *dev = buf64.st_dev; + *ino = buf64.st_ino; + *mode = buf64.st_mode; + return True; + } +#endif + r = aspacem_fstat(fd, &buf); if (r == 0) { *dev = buf.st_dev; *ino = buf.st_ino; @@ -3200,7 +3222,7 @@ static void parse_procselfmaps ( UChar* filename; UChar rr, ww, xx, pp, ch, tmp; UInt ino, prot; - UWord maj, min; + UWord maj, min, dev; ULong foffset; read_procselfmaps_into_buf(); @@ -3311,11 +3333,32 @@ static void parse_procselfmaps ( if (ww == 'w') prot |= VKI_PROT_WRITE; if (xx == 'x') prot |= VKI_PROT_EXEC; + /* Linux has two ways to encode a device number when it + is exposed to user space (via fstat etc). The old way + is the traditional unix scheme that produces a 16 bit + device number with the top 8 being the major number and + the bottom 8 the minor number. + + The new scheme allows for a 12 bit major number and + a 20 bit minor number by using a 32 bit device number + and putting the top 12 bits of the minor number into + the top 12 bits of the device number thus leaving an + extra 4 bits for the major number. + + If the minor and major number are both single byte + values then both schemes give the same result so we + use the new scheme here in case either number is + outside the 0-255 range and then use fstat64 when + available (or fstat on 64 bit systems) so that we + should always have a new style device number and + everything should match. */ + dev = (min & 0xff) | (maj << 8) | ((min & ~0xff) << 12); + if (record_gap && gapStart < start) (*record_gap) ( gapStart, start-gapStart ); (*record_mapping) ( start, endPlusOne-start, - prot, maj * 256 + min, ino, + prot, dev, ino, foffset, filename ); if ('\0' != tmp) {