1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/debug/proc_maps_linux.h"
9 #if defined(OS_LINUX) || defined(OS_ANDROID)
13 #include "base/files/file_util.h"
14 #include "base/files/scoped_file.h"
15 #include "base/strings/string_split.h"
17 #if defined(OS_ANDROID) && !defined(__LP64__)
18 // In 32-bit mode, Bionic's inttypes.h defines PRI/SCNxPTR as an
19 // unsigned long int, which is incompatible with Bionic's stdint.h
20 // defining uintptr_t as an unsigned int:
21 // https://code.google.com/p/android/issues/detail?id=57218
29 // Scans |proc_maps| starting from |pos| returning true if the gate VMA was
30 // found, otherwise returns false.
31 static bool ContainsGateVMA(std::string
* proc_maps
, size_t pos
) {
32 #if defined(ARCH_CPU_ARM_FAMILY)
33 // The gate VMA on ARM kernels is the interrupt vectors page.
34 return proc_maps
->find(" [vectors]\n", pos
) != std::string::npos
;
35 #elif defined(ARCH_CPU_X86_64)
36 // The gate VMA on x86 64-bit kernels is the virtual system call page.
37 return proc_maps
->find(" [vsyscall]\n", pos
) != std::string::npos
;
39 // Otherwise assume there is no gate VMA in which case we shouldn't
40 // get duplicate entires.
45 bool ReadProcMaps(std::string
* proc_maps
) {
46 // seq_file only writes out a page-sized amount on each call. Refer to header
48 const long kReadSize
= sysconf(_SC_PAGESIZE
);
50 base::ScopedFD
fd(HANDLE_EINTR(open("/proc/self/maps", O_RDONLY
)));
52 DPLOG(ERROR
) << "Couldn't open /proc/self/maps";
58 // To avoid a copy, resize |proc_maps| so read() can write directly into it.
59 // Compute |buffer| afterwards since resize() may reallocate.
60 size_t pos
= proc_maps
->size();
61 proc_maps
->resize(pos
+ kReadSize
);
62 void* buffer
= &(*proc_maps
)[pos
];
64 ssize_t bytes_read
= HANDLE_EINTR(read(fd
.get(), buffer
, kReadSize
));
66 DPLOG(ERROR
) << "Couldn't read /proc/self/maps";
71 // ... and don't forget to trim off excess bytes.
72 proc_maps
->resize(pos
+ bytes_read
);
77 // The gate VMA is handled as a special case after seq_file has finished
78 // iterating through all entries in the virtual memory table.
80 // Unfortunately, if additional entries are added at this point in time
81 // seq_file gets confused and the next call to read() will return duplicate
82 // entries including the gate VMA again.
84 // Avoid this by searching for the gate VMA and breaking early.
85 if (ContainsGateVMA(proc_maps
, pos
))
92 bool ParseProcMaps(const std::string
& input
,
93 std::vector
<MappedMemoryRegion
>* regions_out
) {
95 std::vector
<MappedMemoryRegion
> regions
;
97 // This isn't async safe nor terribly efficient, but it doesn't need to be at
98 // this point in time.
99 std::vector
<std::string
> lines
;
100 SplitString(input
, '\n', &lines
);
102 for (size_t i
= 0; i
< lines
.size(); ++i
) {
103 // Due to splitting on '\n' the last line should be empty.
104 if (i
== lines
.size() - 1) {
105 if (!lines
[i
].empty()) {
106 DLOG(WARNING
) << "Last line not empty";
112 MappedMemoryRegion region
;
113 const char* line
= lines
[i
].c_str();
114 char permissions
[5] = {'\0'}; // Ensure NUL-terminated string.
120 // Sample format from man 5 proc:
122 // address perms offset dev inode pathname
123 // 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm
125 // The final %n term captures the offset in the input string, which is used
126 // to determine the path name. It *does not* increment the return value.
127 // Refer to man 3 sscanf for details.
128 if (sscanf(line
, "%" SCNxPTR
"-%" SCNxPTR
" %4c %llx %hhx:%hhx %ld %n",
129 ®ion
.start
, ®ion
.end
, permissions
, ®ion
.offset
,
130 &dev_major
, &dev_minor
, &inode
, &path_index
) < 7) {
131 DPLOG(WARNING
) << "sscanf failed for line: " << line
;
135 region
.permissions
= 0;
137 if (permissions
[0] == 'r')
138 region
.permissions
|= MappedMemoryRegion::READ
;
139 else if (permissions
[0] != '-')
142 if (permissions
[1] == 'w')
143 region
.permissions
|= MappedMemoryRegion::WRITE
;
144 else if (permissions
[1] != '-')
147 if (permissions
[2] == 'x')
148 region
.permissions
|= MappedMemoryRegion::EXECUTE
;
149 else if (permissions
[2] != '-')
152 if (permissions
[3] == 'p')
153 region
.permissions
|= MappedMemoryRegion::PRIVATE
;
154 else if (permissions
[3] != 's' && permissions
[3] != 'S') // Shared memory.
157 // Pushing then assigning saves us a string copy.
158 regions
.push_back(region
);
159 regions
.back().path
.assign(line
+ path_index
);
162 regions_out
->swap(regions
);