GNU Binutils with patches for OS216
修订版 | 25d36f4a5b8edebbef03fb3c45d48259df480c6d (tree) |
---|---|
时间 | 2020-06-16 21:58:32 |
作者 | Luis Machado <luis.machado@lina...> |
Commiter | Luis Machado |
Refactor parsing of /proc/<pid>/smaps
The Linux kernel exposes the information about MTE-protected pages via the
proc filesystem, more specifically through the smaps file.
What we're looking for is a mapping with the 'mt' flag, which tells us that
mapping was created with a PROT_MTE flag and, thus, is capable of using memory
tagging.
We already parse that file for other purposes (core file
generation/filtering), so this patch refactors the code to make the parsing
of the smaps file reusable for memory tagging.
The function linux_address_in_memtag_page uses the refactored code to allow
querying for memory tag support in a particular address, and it gets used in the
next patch.
gdb/ChangeLog:
YYYY-MM-DD Luis Machado <luis.machado@linaro.org>
* linux-tdep.c (struct smaps_vmflags) <memory_tagging>: New flag
bit.
(struct smaps_data): New struct.
(decode_vmflags): Handle the 'mt' flag.
(parse_smaps_data): New function, refactored from
linux_find_memory_regions_full.
(linux_address_in_memtag_page): New function.
(linux_find_memory_regions_full): Refactor into parse_smaps_data.
* linux-tdep.h (linux_address_in_memtag_page): New prototype.
@@ -86,8 +86,33 @@ struct smaps_vmflags | ||
86 | 86 | /* Is this a MAP_SHARED mapping (VM_SHARED, "sh"). */ |
87 | 87 | |
88 | 88 | unsigned int shared_mapping : 1; |
89 | + | |
90 | + /* Memory map has memory tagging enabled. */ | |
91 | + | |
92 | + unsigned int memory_tagging : 1; | |
89 | 93 | }; |
90 | 94 | |
95 | +/* Data structure that holds the information contained in the | |
96 | + /proc/<pid>/smaps file. */ | |
97 | + | |
98 | +struct smaps_data | |
99 | +{ | |
100 | + ULONGEST start_address; | |
101 | + ULONGEST end_address; | |
102 | + std::string filename; | |
103 | + struct smaps_vmflags vmflags; | |
104 | + bool read; | |
105 | + bool write; | |
106 | + bool exec; | |
107 | + bool priv; | |
108 | + bool has_anonymous; | |
109 | + bool mapping_anon_p; | |
110 | + bool mapping_file_p; | |
111 | + | |
112 | + ULONGEST inode; | |
113 | + ULONGEST offset; | |
114 | +}; | |
115 | + | |
91 | 116 | /* Whether to take the /proc/PID/coredump_filter into account when |
92 | 117 | generating a corefile. */ |
93 | 118 |
@@ -472,6 +497,8 @@ decode_vmflags (char *p, struct smaps_vmflags *v) | ||
472 | 497 | v->exclude_coredump = 1; |
473 | 498 | else if (strcmp (s, "sh") == 0) |
474 | 499 | v->shared_mapping = 1; |
500 | + else if (strcmp (s, "mt") == 0) | |
501 | + v->memory_tagging = 1; | |
475 | 502 | } |
476 | 503 | } |
477 | 504 |
@@ -1172,6 +1199,185 @@ typedef int linux_find_memory_region_ftype (ULONGEST vaddr, ULONGEST size, | ||
1172 | 1199 | const char *filename, |
1173 | 1200 | void *data); |
1174 | 1201 | |
1202 | +/* Helper function to parse the contents of /proc/<pid>/smaps into a data | |
1203 | + structure, for easy access. | |
1204 | + | |
1205 | + DATA is the contents of the smaps file. The parsed contents are stored | |
1206 | + into the SMAPS vector. */ | |
1207 | + | |
1208 | +static int | |
1209 | +parse_smaps_data (const char *data, | |
1210 | + std::vector<struct smaps_data> &smaps, | |
1211 | + const char *mapsfilename) | |
1212 | +{ | |
1213 | + char *line, *t; | |
1214 | + | |
1215 | + gdb_assert (data != nullptr); | |
1216 | + | |
1217 | + smaps.clear (); | |
1218 | + | |
1219 | + line = strtok_r ((char *) data, "\n", &t); | |
1220 | + | |
1221 | + while (line != NULL) | |
1222 | + { | |
1223 | + ULONGEST addr, endaddr, offset, inode; | |
1224 | + const char *permissions, *device, *filename; | |
1225 | + struct smaps_vmflags v; | |
1226 | + size_t permissions_len, device_len; | |
1227 | + int read, write, exec, priv; | |
1228 | + int has_anonymous = 0; | |
1229 | + int mapping_anon_p; | |
1230 | + int mapping_file_p; | |
1231 | + | |
1232 | + memset (&v, 0, sizeof (v)); | |
1233 | + read_mapping (line, &addr, &endaddr, &permissions, &permissions_len, | |
1234 | + &offset, &device, &device_len, &inode, &filename); | |
1235 | + mapping_anon_p = mapping_is_anonymous_p (filename); | |
1236 | + /* If the mapping is not anonymous, then we can consider it | |
1237 | + to be file-backed. These two states (anonymous or | |
1238 | + file-backed) seem to be exclusive, but they can actually | |
1239 | + coexist. For example, if a file-backed mapping has | |
1240 | + "Anonymous:" pages (see more below), then the Linux | |
1241 | + kernel will dump this mapping when the user specified | |
1242 | + that she only wants anonymous mappings in the corefile | |
1243 | + (*even* when she explicitly disabled the dumping of | |
1244 | + file-backed mappings). */ | |
1245 | + mapping_file_p = !mapping_anon_p; | |
1246 | + | |
1247 | + /* Decode permissions. */ | |
1248 | + read = (memchr (permissions, 'r', permissions_len) != 0); | |
1249 | + write = (memchr (permissions, 'w', permissions_len) != 0); | |
1250 | + exec = (memchr (permissions, 'x', permissions_len) != 0); | |
1251 | + /* 'private' here actually means VM_MAYSHARE, and not | |
1252 | + VM_SHARED. In order to know if a mapping is really | |
1253 | + private or not, we must check the flag "sh" in the | |
1254 | + VmFlags field. This is done by decode_vmflags. However, | |
1255 | + if we are using a Linux kernel released before the commit | |
1256 | + 834f82e2aa9a8ede94b17b656329f850c1471514 (3.10), we will | |
1257 | + not have the VmFlags there. In this case, there is | |
1258 | + really no way to know if we are dealing with VM_SHARED, | |
1259 | + so we just assume that VM_MAYSHARE is enough. */ | |
1260 | + priv = memchr (permissions, 'p', permissions_len) != 0; | |
1261 | + | |
1262 | + /* Try to detect if region should be dumped by parsing smaps | |
1263 | + counters. */ | |
1264 | + for (line = strtok_r (NULL, "\n", &t); | |
1265 | + line != NULL && line[0] >= 'A' && line[0] <= 'Z'; | |
1266 | + line = strtok_r (NULL, "\n", &t)) | |
1267 | + { | |
1268 | + char keyword[64 + 1]; | |
1269 | + | |
1270 | + if (sscanf (line, "%64s", keyword) != 1) | |
1271 | + { | |
1272 | + warning (_("Error parsing {s,}maps file '%s'"), mapsfilename); | |
1273 | + break; | |
1274 | + } | |
1275 | + | |
1276 | + if (strcmp (keyword, "Anonymous:") == 0) | |
1277 | + { | |
1278 | + /* Older Linux kernels did not support the | |
1279 | + "Anonymous:" counter. Check it here. */ | |
1280 | + has_anonymous = 1; | |
1281 | + } | |
1282 | + else if (strcmp (keyword, "VmFlags:") == 0) | |
1283 | + decode_vmflags (line, &v); | |
1284 | + | |
1285 | + if (strcmp (keyword, "AnonHugePages:") == 0 | |
1286 | + || strcmp (keyword, "Anonymous:") == 0) | |
1287 | + { | |
1288 | + unsigned long number; | |
1289 | + | |
1290 | + if (sscanf (line, "%*s%lu", &number) != 1) | |
1291 | + { | |
1292 | + warning (_("Error parsing {s,}maps file '%s' number"), | |
1293 | + mapsfilename); | |
1294 | + break; | |
1295 | + } | |
1296 | + if (number > 0) | |
1297 | + { | |
1298 | + /* Even if we are dealing with a file-backed | |
1299 | + mapping, if it contains anonymous pages we | |
1300 | + consider it to be *also* an anonymous | |
1301 | + mapping, because this is what the Linux | |
1302 | + kernel does: | |
1303 | + | |
1304 | + // Dump segments that have been written to. | |
1305 | + if (vma->anon_vma && FILTER(ANON_PRIVATE)) | |
1306 | + goto whole; | |
1307 | + | |
1308 | + Note that if the mapping is already marked as | |
1309 | + file-backed (i.e., mapping_file_p is | |
1310 | + non-zero), then this is a special case, and | |
1311 | + this mapping will be dumped either when the | |
1312 | + user wants to dump file-backed *or* anonymous | |
1313 | + mappings. */ | |
1314 | + mapping_anon_p = 1; | |
1315 | + } | |
1316 | + } | |
1317 | + } | |
1318 | + /* Save the smaps entry to the vector. */ | |
1319 | + struct smaps_data map; | |
1320 | + std::string fname (filename); | |
1321 | + | |
1322 | + map.start_address = addr; | |
1323 | + map.end_address = endaddr; | |
1324 | + map.filename = fname; | |
1325 | + map.vmflags = v; | |
1326 | + map.read = read? true : false; | |
1327 | + map.write = write? true : false; | |
1328 | + map.exec = exec? true : false; | |
1329 | + map.priv = priv? true : false; | |
1330 | + map.has_anonymous = has_anonymous; | |
1331 | + map.mapping_anon_p = mapping_anon_p? true : false; | |
1332 | + map.mapping_file_p = mapping_file_p? true : false; | |
1333 | + map.offset = offset; | |
1334 | + map.inode = inode; | |
1335 | + | |
1336 | + smaps.emplace_back (map); | |
1337 | + } | |
1338 | + | |
1339 | + return 0; | |
1340 | +} | |
1341 | + | |
1342 | +/* See linux-tdep.h. */ | |
1343 | + | |
1344 | +bool | |
1345 | +linux_address_in_memtag_page (CORE_ADDR address) | |
1346 | +{ | |
1347 | + if (current_inferior ()->fake_pid_p) | |
1348 | + return false; | |
1349 | + | |
1350 | + pid_t pid = current_inferior ()->pid; | |
1351 | + | |
1352 | + std::string smaps_file = string_printf ("/proc/%d/smaps", pid); | |
1353 | + | |
1354 | + gdb::unique_xmalloc_ptr<char> data | |
1355 | + = target_fileio_read_stralloc (NULL, smaps_file.c_str ()); | |
1356 | + | |
1357 | + if (data == nullptr) | |
1358 | + return false; | |
1359 | + | |
1360 | + std::vector<struct smaps_data> smaps; | |
1361 | + | |
1362 | + /* Parse the contents of smaps into a vector. */ | |
1363 | + parse_smaps_data (data.get (), smaps, smaps_file.c_str ()); | |
1364 | + | |
1365 | + if (!smaps.empty ()) | |
1366 | + { | |
1367 | + for (struct smaps_data map : smaps) | |
1368 | + { | |
1369 | + /* Is the address within [start_address, end_address) in a page | |
1370 | + mapped with memory tagging? */ | |
1371 | + if (address >= map.start_address | |
1372 | + && address < map.end_address | |
1373 | + && map.vmflags.memory_tagging) | |
1374 | + return true; | |
1375 | + } | |
1376 | + } | |
1377 | + | |
1378 | + return false; | |
1379 | +} | |
1380 | + | |
1175 | 1381 | /* List memory regions in the inferior for a corefile. */ |
1176 | 1382 | |
1177 | 1383 | static int |
@@ -1179,8 +1385,7 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch, | ||
1179 | 1385 | linux_find_memory_region_ftype *func, |
1180 | 1386 | void *obfd) |
1181 | 1387 | { |
1182 | - char mapsfilename[100]; | |
1183 | - char coredumpfilter_name[100]; | |
1388 | + std::string coredumpfilter_name; | |
1184 | 1389 | pid_t pid; |
1185 | 1390 | /* Default dump behavior of coredump_filter (0x33), according to |
1186 | 1391 | Documentation/filesystems/proc.txt from the Linux kernel |
@@ -1198,10 +1403,9 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch, | ||
1198 | 1403 | |
1199 | 1404 | if (use_coredump_filter) |
1200 | 1405 | { |
1201 | - xsnprintf (coredumpfilter_name, sizeof (coredumpfilter_name), | |
1202 | - "/proc/%d/coredump_filter", pid); | |
1406 | + coredumpfilter_name = string_printf ("/proc/%d/coredump_filter", pid); | |
1203 | 1407 | gdb::unique_xmalloc_ptr<char> coredumpfilterdata |
1204 | - = target_fileio_read_stralloc (NULL, coredumpfilter_name); | |
1408 | + = target_fileio_read_stralloc (NULL, coredumpfilter_name.c_str ()); | |
1205 | 1409 | if (coredumpfilterdata != NULL) |
1206 | 1410 | { |
1207 | 1411 | unsigned int flags; |
@@ -1211,124 +1415,37 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch, | ||
1211 | 1415 | } |
1212 | 1416 | } |
1213 | 1417 | |
1214 | - xsnprintf (mapsfilename, sizeof mapsfilename, "/proc/%d/smaps", pid); | |
1418 | + std::string mapsfilename = string_printf ("/proc/%d/smaps", pid); | |
1215 | 1419 | gdb::unique_xmalloc_ptr<char> data |
1216 | - = target_fileio_read_stralloc (NULL, mapsfilename); | |
1420 | + = target_fileio_read_stralloc (NULL, mapsfilename.c_str ()); | |
1217 | 1421 | if (data == NULL) |
1218 | 1422 | { |
1219 | 1423 | /* Older Linux kernels did not support /proc/PID/smaps. */ |
1220 | - xsnprintf (mapsfilename, sizeof mapsfilename, "/proc/%d/maps", pid); | |
1221 | - data = target_fileio_read_stralloc (NULL, mapsfilename); | |
1424 | + mapsfilename = string_printf ("/proc/%d/maps", pid); | |
1425 | + data = target_fileio_read_stralloc (NULL, mapsfilename.c_str ()); | |
1222 | 1426 | } |
1223 | 1427 | |
1224 | - if (data != NULL) | |
1225 | - { | |
1226 | - char *line, *t; | |
1227 | - | |
1228 | - line = strtok_r (data.get (), "\n", &t); | |
1229 | - while (line != NULL) | |
1230 | - { | |
1231 | - ULONGEST addr, endaddr, offset, inode; | |
1232 | - const char *permissions, *device, *filename; | |
1233 | - struct smaps_vmflags v; | |
1234 | - size_t permissions_len, device_len; | |
1235 | - int read, write, exec, priv; | |
1236 | - int has_anonymous = 0; | |
1237 | - int should_dump_p = 0; | |
1238 | - int mapping_anon_p; | |
1239 | - int mapping_file_p; | |
1240 | - | |
1241 | - memset (&v, 0, sizeof (v)); | |
1242 | - read_mapping (line, &addr, &endaddr, &permissions, &permissions_len, | |
1243 | - &offset, &device, &device_len, &inode, &filename); | |
1244 | - mapping_anon_p = mapping_is_anonymous_p (filename); | |
1245 | - /* If the mapping is not anonymous, then we can consider it | |
1246 | - to be file-backed. These two states (anonymous or | |
1247 | - file-backed) seem to be exclusive, but they can actually | |
1248 | - coexist. For example, if a file-backed mapping has | |
1249 | - "Anonymous:" pages (see more below), then the Linux | |
1250 | - kernel will dump this mapping when the user specified | |
1251 | - that she only wants anonymous mappings in the corefile | |
1252 | - (*even* when she explicitly disabled the dumping of | |
1253 | - file-backed mappings). */ | |
1254 | - mapping_file_p = !mapping_anon_p; | |
1255 | - | |
1256 | - /* Decode permissions. */ | |
1257 | - read = (memchr (permissions, 'r', permissions_len) != 0); | |
1258 | - write = (memchr (permissions, 'w', permissions_len) != 0); | |
1259 | - exec = (memchr (permissions, 'x', permissions_len) != 0); | |
1260 | - /* 'private' here actually means VM_MAYSHARE, and not | |
1261 | - VM_SHARED. In order to know if a mapping is really | |
1262 | - private or not, we must check the flag "sh" in the | |
1263 | - VmFlags field. This is done by decode_vmflags. However, | |
1264 | - if we are using a Linux kernel released before the commit | |
1265 | - 834f82e2aa9a8ede94b17b656329f850c1471514 (3.10), we will | |
1266 | - not have the VmFlags there. In this case, there is | |
1267 | - really no way to know if we are dealing with VM_SHARED, | |
1268 | - so we just assume that VM_MAYSHARE is enough. */ | |
1269 | - priv = memchr (permissions, 'p', permissions_len) != 0; | |
1270 | - | |
1271 | - /* Try to detect if region should be dumped by parsing smaps | |
1272 | - counters. */ | |
1273 | - for (line = strtok_r (NULL, "\n", &t); | |
1274 | - line != NULL && line[0] >= 'A' && line[0] <= 'Z'; | |
1275 | - line = strtok_r (NULL, "\n", &t)) | |
1276 | - { | |
1277 | - char keyword[64 + 1]; | |
1428 | + if (data == nullptr) | |
1429 | + return 1; | |
1278 | 1430 | |
1279 | - if (sscanf (line, "%64s", keyword) != 1) | |
1280 | - { | |
1281 | - warning (_("Error parsing {s,}maps file '%s'"), mapsfilename); | |
1282 | - break; | |
1283 | - } | |
1431 | + std::vector<struct smaps_data> smaps; | |
1284 | 1432 | |
1285 | - if (strcmp (keyword, "Anonymous:") == 0) | |
1286 | - { | |
1287 | - /* Older Linux kernels did not support the | |
1288 | - "Anonymous:" counter. Check it here. */ | |
1289 | - has_anonymous = 1; | |
1290 | - } | |
1291 | - else if (strcmp (keyword, "VmFlags:") == 0) | |
1292 | - decode_vmflags (line, &v); | |
1433 | + /* Parse the contents of smaps into a vector. */ | |
1434 | + parse_smaps_data (data.get (), smaps, mapsfilename.c_str ()); | |
1293 | 1435 | |
1294 | - if (strcmp (keyword, "AnonHugePages:") == 0 | |
1295 | - || strcmp (keyword, "Anonymous:") == 0) | |
1296 | - { | |
1297 | - unsigned long number; | |
1298 | - | |
1299 | - if (sscanf (line, "%*s%lu", &number) != 1) | |
1300 | - { | |
1301 | - warning (_("Error parsing {s,}maps file '%s' number"), | |
1302 | - mapsfilename); | |
1303 | - break; | |
1304 | - } | |
1305 | - if (number > 0) | |
1306 | - { | |
1307 | - /* Even if we are dealing with a file-backed | |
1308 | - mapping, if it contains anonymous pages we | |
1309 | - consider it to be *also* an anonymous | |
1310 | - mapping, because this is what the Linux | |
1311 | - kernel does: | |
1312 | - | |
1313 | - // Dump segments that have been written to. | |
1314 | - if (vma->anon_vma && FILTER(ANON_PRIVATE)) | |
1315 | - goto whole; | |
1316 | - | |
1317 | - Note that if the mapping is already marked as | |
1318 | - file-backed (i.e., mapping_file_p is | |
1319 | - non-zero), then this is a special case, and | |
1320 | - this mapping will be dumped either when the | |
1321 | - user wants to dump file-backed *or* anonymous | |
1322 | - mappings. */ | |
1323 | - mapping_anon_p = 1; | |
1324 | - } | |
1325 | - } | |
1326 | - } | |
1436 | + if (!smaps.empty ()) | |
1437 | + { | |
1438 | + for (struct smaps_data map : smaps) | |
1439 | + { | |
1440 | + int should_dump_p = 0; | |
1327 | 1441 | |
1328 | - if (has_anonymous) | |
1329 | - should_dump_p = dump_mapping_p (filterflags, &v, priv, | |
1330 | - mapping_anon_p, mapping_file_p, | |
1331 | - filename, addr, offset); | |
1442 | + if (map.has_anonymous) | |
1443 | + should_dump_p = dump_mapping_p (filterflags, &map.vmflags, map.priv, | |
1444 | + map.mapping_anon_p, | |
1445 | + map.mapping_file_p, | |
1446 | + map.filename.c_str (), | |
1447 | + map.start_address, | |
1448 | + map.offset); | |
1332 | 1449 | else |
1333 | 1450 | { |
1334 | 1451 | /* Older Linux kernels did not support the "Anonymous:" counter. |
@@ -1338,16 +1455,15 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch, | ||
1338 | 1455 | |
1339 | 1456 | /* Invoke the callback function to create the corefile segment. */ |
1340 | 1457 | if (should_dump_p) |
1341 | - func (addr, endaddr - addr, offset, inode, | |
1342 | - read, write, exec, 1, /* MODIFIED is true because we | |
1343 | - want to dump the mapping. */ | |
1344 | - filename, obfd); | |
1458 | + func (map.start_address, map.end_address - map.start_address, | |
1459 | + map.offset, map.inode, map.read, map.write, map.exec, | |
1460 | + 1, /* MODIFIED is true because we want to dump | |
1461 | + the mapping. */ | |
1462 | + map.filename.c_str (), obfd); | |
1345 | 1463 | } |
1346 | - | |
1347 | - return 0; | |
1348 | 1464 | } |
1349 | 1465 | |
1350 | - return 1; | |
1466 | + return 0; | |
1351 | 1467 | } |
1352 | 1468 | |
1353 | 1469 | /* A structure for passing information through |
@@ -41,6 +41,10 @@ DEF_ENUM_FLAGS_TYPE (enum linux_siginfo_extra_field_values, | ||
41 | 41 | struct type *linux_get_siginfo_type_with_fields (struct gdbarch *gdbarch, |
42 | 42 | linux_siginfo_extra_fields); |
43 | 43 | |
44 | + /* Return true if ADDRESS is within the boundaries of a page mapped with | |
45 | + memory tagging protection. */ | |
46 | +bool linux_address_in_memtag_page (CORE_ADDR address); | |
47 | + | |
44 | 48 | typedef char *(*linux_collect_thread_registers_ftype) (const struct regcache *, |
45 | 49 | ptid_t, |
46 | 50 | bfd *, char *, int *, |