1 /* Tests that Valgrind coredump support works correctly even when 2 number of segments exceeds 0xffff. 3 For this to work, large number of pages is mmap()'ed into the 4 process (virtual) address space. These pages must not be adjacent 5 to each other otherwise the memory manager will coalesce them 6 into a single one. So they are one page apart. 7 8 NOTE: Valgrind's internal limit VG_N_SEGMENTS must be at least 9 140000 otherwise you get a fatal error similar to this one: 10 "FATAL: VG_N_SEGMENTS is too low." 11 12 Test case passes successfully if the number of segments is 13 correctly displayed in elfdump output: 14 15 $ elfdump -e vgcore.* 16 ELF Header 17 ... 18 e_phoff: 0x34 e_phentsize: 32 e_phnum: PN_XNUM (see shdr[0].sh_info) 19 ^^^^^^^^^^^^^^^^ 20 Section Header[0]: (ELF Ehdr extensions) 21 ... 22 sh_link: 0 (e_shstrndx) sh_info: 65554 (e_phnum) 23 ^^^^^^^^^^^^^^^^^^^^^^^^ 24 */ 25 26 #include <assert.h> 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <inttypes.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <unistd.h> 34 #include <sys/ipc.h> 35 #include <sys/mman.h> 36 #include <sys/procfs.h> 37 #include <sys/stat.h> 38 39 #define SEGMENTS (0xffff + 2) 40 41 #if 0 42 #define DEBUG(format, ...) printf(format, ## __VA_ARGS__) 43 #else 44 #define DEBUG(format, ...) 45 #endif 46 47 #define PRINT(format, ...) printf(format, ## __VA_ARGS__) 48 49 /* Represents a free range of a virtual address space. */ 50 typedef struct range { 51 uintptr_t start; 52 uintptr_t end; 53 size_t size; 54 struct range *next; 55 } range_t; 56 57 /* Processes a single prmap_t entry and builds the free ranges. */ 58 static int process_map(const prmap_t *map, range_t **ranges_head, 59 range_t **ranges_tail, size_t page_size) 60 { 61 assert(map != NULL); 62 assert(ranges_head != NULL); 63 assert(ranges_tail != NULL); 64 65 range_t *head = *ranges_head; 66 range_t *tail = *ranges_tail; 67 68 DEBUG("processing map with addr=%p and size=%zu\n", 69 map->pr_vaddr, map->pr_size); 70 71 if (head == NULL) { 72 head = calloc(1, sizeof(range_t)); 73 if (head == NULL) { 74 fprintf(stderr, "calloc failed\n"); 75 return -1; 76 } 77 head->start = (uintptr_t) page_size; // do not start at address '0' 78 79 tail = head; 80 *ranges_head = head; 81 *ranges_tail = tail; 82 } 83 84 if ((map->pr_vaddr < tail->start) || 85 (map->pr_vaddr - tail->start < 3 * page_size)) { 86 DEBUG("last range at %p is too small, skipping it\n", 87 tail->start); 88 tail->start = map->pr_vaddr + map->pr_size + page_size; 89 return 0; 90 } 91 92 tail->end = map->pr_vaddr - page_size; 93 tail->size = tail->end - tail->start; 94 95 range_t *new_one = calloc(1, sizeof(range_t)); 96 if (new_one == NULL) { 97 fprintf(stderr, "calloc failed\n"); 98 return -1; 99 } 100 101 new_one->start = map->pr_vaddr + map->pr_size + page_size; 102 tail->next = new_one; 103 *ranges_tail = new_one; 104 return 0; 105 } 106 107 /* Reads /proc/self/map and builds free ranges. */ 108 static range_t *read_proc_map(size_t page_size) 109 { 110 int fd = open("/proc/self/map", O_RDONLY); 111 if (fd == -1) { 112 int error = errno; 113 fprintf(stderr, "open failed: %s (%d)\n", strerror(error), error); 114 return NULL; 115 } 116 117 prmap_t map; 118 range_t *ranges_head = NULL; 119 range_t *ranges_tail = NULL; 120 121 ssize_t bytes = read(fd, &map, sizeof(map)); 122 while (bytes == sizeof(map)) { 123 if (map.pr_size != 0) { 124 if (process_map(&map, &ranges_head, &ranges_tail, 125 page_size) != 0) { 126 return NULL; 127 } 128 } 129 bytes = read(fd, &map, sizeof(map)); 130 } 131 132 if (ranges_tail != NULL) { 133 ranges_tail->end = (uintptr_t) ~0; 134 ranges_tail->size = ranges_tail->end - ranges_tail->start; 135 } 136 137 close(fd); 138 return ranges_head; 139 } 140 141 static void print_ranges(const range_t *head) 142 { 143 while (head != NULL) { 144 DEBUG("free range [%8p - %8p] of size %7zuK\n", 145 head->start, head->end, head->size / 1024); 146 head = head->next; 147 } 148 } 149 150 static size_t sum_ranges(const range_t *head) 151 { 152 size_t sum = 0; 153 154 while (head != NULL) { 155 sum += head->size; 156 head = head->next; 157 } 158 159 return sum; 160 } 161 162 static void *map_segment(void *fixed_addr) 163 { 164 int flags = MAP_NORESERVE | MAP_ANON | MAP_PRIVATE | MAP_FIXED; 165 void *addr = mmap(fixed_addr, 1, PROT_READ | PROT_WRITE, 166 flags, -1, 0); 167 if (addr == MAP_FAILED) { 168 int error = errno; 169 fprintf(stderr, "mmap failed: %s (%d)\n", strerror(error), error); 170 return NULL; 171 } 172 assert(addr == fixed_addr); 173 174 *((char *) addr) = 1; // make the mmap'ed page dirty 175 // DEBUG("mmap(%8p) = %8p\n", fixed_addr, addr); 176 return addr; 177 } 178 179 int main(int argc, const char *argv[]) 180 { 181 long page_size = sysconf(_SC_PAGESIZE); 182 if (page_size == -1) { 183 perror("sysconf"); 184 return 1; 185 } 186 187 PRINT("Page size determined as %ld bytes.\n", page_size); 188 189 range_t *ranges = read_proc_map(page_size); 190 print_ranges(ranges); 191 192 size_t sum = sum_ranges(ranges); 193 if (sum < SEGMENTS * page_size) { 194 fprintf(stderr, "Free (virtual) address space cannot accomodate " 195 "%u pages.\n", SEGMENTS); 196 return 1; 197 } 198 199 PRINT("mmap()'ing %u segments:", SEGMENTS); 200 fflush(stdout); 201 202 unsigned int segment = 0; 203 while ((ranges != NULL) && (segment < SEGMENTS)) { 204 unsigned int page; 205 for (page = 0; page < ranges->size / (2 * page_size); page++) { 206 uintptr_t start = ranges->start + 2 * page * page_size; 207 void *addr = map_segment((void *) start); 208 if (addr == NULL) { 209 fprintf(stderr, "Mapping failed for segment %u at address " 210 "%" PRIxPTR ".\n", segment, start); 211 return 1; 212 } 213 214 segment += 1; 215 if (segment >= SEGMENTS) { 216 break; 217 } 218 219 if (segment % (SEGMENTS / 10) == 0) { 220 PRINT(" %u0%%", segment / (SEGMENTS / 10)); 221 fflush(stdout); 222 } 223 } 224 ranges = ranges->next; 225 } 226 assert(segment == SEGMENTS); 227 228 PRINT(".\nDumping core...\n"); 229 char *nihil = NULL; 230 *nihil = 0; // SEGV here 231 fprintf(stderr, "Should not reach here.\n"); 232 233 return 0; 234 } 235