summaryrefslogtreecommitdiff
path: root/src/coredump/_UCD_get_mapinfo_linux.c
diff options
context:
space:
mode:
authorStephen Webb <swebb@blackberry.com>2022-07-23 22:30:23 -0400
committerStephen M. Webb <stephen.webb@bregmasoft.ca>2022-07-26 21:44:26 -0400
commite96db56e586ad7a1c8d27593382b365f775a398f (patch)
treeab6bcc10a8504204d1f52b379d2ddc7cb1efb1ff /src/coredump/_UCD_get_mapinfo_linux.c
parent10e093db21b73e98ba8485b4b0696d330bf8dfdf (diff)
downloadlibunwind-e96db56e586ad7a1c8d27593382b365f775a398f.tar.gz
Stop assuming .text and .eh_frame segment
The coredump remote was architected on the assumption that the .text and .eh_frame sections were mapped onto the same segment, and that that segment was always the first PT_LOAD segment in an ELF file. Well, that was never a valid assumption, and moderns releases of various toolchains have started splitting the PT_LOAD segments for security reasons. This change implements an M:N mapping of PT_LOAD segments in a coredump file to backing ELF files and calculates and adjusts offsets appropriately. Because the backing files get mapped in a lot of file I/O operations have been replaced with simple memory reads. Once a backing file is memory mapped is stays mapped until the address space is destroyed. The ucd_*.[ch] files contain only functions that should not be exposed through the public API so they;re not mangled using the UB naming schedule because I just bring myself to write code with undefined behaviour. Reformatted some of the changed files using `astyle --style=gnu` for internal consistency withing the file. Fixes #363
Diffstat (limited to 'src/coredump/_UCD_get_mapinfo_linux.c')
-rw-r--r--src/coredump/_UCD_get_mapinfo_linux.c144
1 files changed, 95 insertions, 49 deletions
diff --git a/src/coredump/_UCD_get_mapinfo_linux.c b/src/coredump/_UCD_get_mapinfo_linux.c
index 0df2107f..ac2df590 100644
--- a/src/coredump/_UCD_get_mapinfo_linux.c
+++ b/src/coredump/_UCD_get_mapinfo_linux.c
@@ -3,17 +3,17 @@
*/
/*
This file is part of libunwind.
-
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
-
+
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
-
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -43,60 +43,105 @@
* containing the mapped file names. They are ordered correspondingly to each
* entry in the map structure array.
*/
-typedef struct {
+struct core_nt_file_hdr_s
+{
unsigned long count;
unsigned long pagesz;
-} linux_mapinfo_hdr_t;
+};
+typedef struct core_nt_file_hdr_s core_nt_file_hdr_t;
-typedef struct {
+struct core_nt_file_entry_s
+{
unsigned long start;
unsigned long end;
unsigned long offset;
-} linux_mapinfo_t;
+};
+typedef struct core_nt_file_entry_s core_nt_file_entry_t;
+
+
+static const char deleted[] = "(deleted)";
+static const size_t deleted_len = sizeof (deleted);
+static const size_t mapinfo_offset = sizeof (core_nt_file_hdr_t);
/**
- * Map a file note to program headers
+ * Handle the CORE/NT_FILE note type.
+ * @param[in] desc The note-specific data
+ * @param[in] arg The user-supplied callback argument
*
- * If a NT_FILE note is recognized, parse it and add the resulting backing files
- * to the program header list.
+ * The CORE/NT_FILE note type contains a list of start/end virtual addresses
+ * within the core file and an associated filename. The purpose is to mape
+ * various segments loaded into memory from ELF files with the ELF file from
+ * which those segments were loaded.
+ *
+ * This function links the file namess mapped in the CORE/NT_FILE note with
+ * the program headers in the core file through the UCD_info file table.
*
* Any file names that end in the string "(deleted)" are ignored.
*/
static int
-_handle_file_note(uint32_t n_namesz, uint32_t n_descsz, uint32_t n_type, char *name, uint8_t *desc, void *arg)
+_handle_nt_file_note (uint8_t *desc, void *arg)
{
struct UCD_info *ui = (struct UCD_info *)arg;
-#ifdef NT_FILE
- if (n_type == NT_FILE)
- {
- Debug(0, "found a PT_FILE note\n");
- static const char * deleted = "(deleted)";
- size_t deleted_len = strlen(deleted);
- static const size_t mapinfo_offset = sizeof(linux_mapinfo_hdr_t);
-
- linux_mapinfo_hdr_t *mapinfo = (linux_mapinfo_hdr_t *)desc;
- linux_mapinfo_t *maps = (linux_mapinfo_t *)(desc + mapinfo_offset);
- char *strings = (char *)(desc + mapinfo_offset + sizeof(linux_mapinfo_t)*mapinfo->count);
- for (unsigned long i = 0; i < mapinfo->count; ++i)
+ core_nt_file_hdr_t *mapinfo = (core_nt_file_hdr_t *)desc;
+ core_nt_file_entry_t *maps = (core_nt_file_entry_t *) (desc + mapinfo_offset);
+ char *strings = (char *) (desc + mapinfo_offset + sizeof (core_nt_file_entry_t) * mapinfo->count);
+
+ for (unsigned long i = 0; i < mapinfo->count; ++i)
{
- size_t len = strlen(strings);
+ size_t len = strlen (strings);
+
for (unsigned p = 0; p < ui->phdrs_count; ++p)
- {
- if (ui->phdrs[p].p_type == PT_LOAD
- && maps[i].start >= ui->phdrs[p].p_vaddr
- && maps[i].end <= ui->phdrs[p].p_vaddr + ui->phdrs[p].p_filesz)
- {
- if (len > deleted_len && memcmp(strings + len - deleted_len, deleted, deleted_len))
- {
- _UCD_add_backing_file_at_segment(ui, p, strings);
- }
- break;
- }
- }
+ {
+ if (ui->phdrs[p].p_type == PT_LOAD
+ && maps[i].start >= ui->phdrs[p].p_vaddr
+ && maps[i].end <= ui->phdrs[p].p_vaddr + ui->phdrs[p].p_memsz)
+ {
+ if (len > deleted_len && memcmp (strings + len - deleted_len, deleted, deleted_len))
+ {
+ ui->phdrs[p].p_backing_file_index = ucd_file_table_insert (&ui->ucd_file_table, strings);
+ Debug (3, "adding '%s' at index %d\n", strings, ui->phdrs[p].p_backing_file_index);
+ }
+
+ break;
+ }
+ }
+
strings += (len + 1);
}
- }
+
+ return UNW_ESUCCESS;
+}
+
+/**
+ * Callback to handle notes.
+ * @param[in] n_namesz size of name data
+ * @param[in] n_descsz size of desc data
+ * @param[in] n_type type of note
+ * @param[in] name zero-terminated string, n_namesz bytes plus alignment padding
+ * @param[in] desc note-specific data, n_descsz bytes plus alignment padding
+ * @param[in] arg user-supplied callback argument
+ *
+ * Add additional note types here for fun and frolicks. Right now the only note
+ * type handled is the CORE/NT_FILE note used on GNU/Linux. FreeBSD uses a
+ * FreeBSD/NT_PROCSTAT_VMMAP note and QNX uses a QNX/QNT_DEBUG_LINK_MAP note for
+ * similar purposes. Other target OSes probably use something else.
+ *
+ * Note interpretation requires both name and type.
+ */
+static int
+_handle_pt_note_segment (uint32_t n_namesz,
+ uint32_t n_descsz,
+ uint32_t n_type,
+ char *name,
+ uint8_t *desc,
+ void *arg)
+{
+#ifdef NT_FILE
+ if (n_type == NT_FILE && strcmp (name, "CORE") == 0)
+ {
+ return _handle_nt_file_note (desc, arg);
+ }
#endif
return UNW_ESUCCESS;
}
@@ -111,24 +156,25 @@ _handle_file_note(uint32_t n_namesz, uint32_t n_descsz, uint32_t n_type, char *n
* fail.
*/
int
-_UCD_get_mapinfo(struct UCD_info *ui, coredump_phdr_t *phdrs, unsigned phdr_size)
+_UCD_get_mapinfo (struct UCD_info *ui, coredump_phdr_t *phdrs, unsigned phdr_size)
{
int ret = UNW_ESUCCESS; /* it's OK if there are no file mappings */
for (unsigned i = 0; i < phdr_size; ++i)
- {
- if (phdrs[i].p_type == PT_NOTE)
{
- uint8_t *segment;
- size_t segment_size;
- ret = _UCD_elf_read_segment(ui, &phdrs[i], &segment, &segment_size);
- if (ret == UNW_ESUCCESS)
- {
- _UCD_elf_visit_notes(segment, segment_size, _handle_file_note, ui);
- free(segment);
- }
+ if (phdrs[i].p_type == PT_NOTE)
+ {
+ uint8_t *segment;
+ size_t segment_size;
+ ret = _UCD_elf_read_segment (ui, &phdrs[i], &segment, &segment_size);
+
+ if (ret == UNW_ESUCCESS)
+ {
+ _UCD_elf_visit_notes (segment, segment_size, _handle_pt_note_segment, ui);
+ free (segment);
+ }
+ }
}
- }
return ret;
}