1 /* $NetBSD: mem1.c,v 1.73 2023/07/30 08:58:54 rillig Exp $ */ 2 3 /* 4 * Copyright (c) 1994, 1995 Jochen Pohl 5 * All Rights Reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Jochen Pohl for 18 * The NetBSD Project. 19 * 4. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #if HAVE_NBTOOL_CONFIG_H 35 #include "nbtool_config.h" 36 #endif 37 38 #include <sys/cdefs.h> 39 #if defined(__RCSID) 40 __RCSID("$NetBSD: mem1.c,v 1.73 2023/07/30 08:58:54 rillig Exp $"); 41 #endif 42 43 #include <sys/param.h> 44 #include <stdlib.h> 45 #include <string.h> 46 47 #include "lint1.h" 48 49 /* 50 * Filenames allocated by record_filename are shared and have unlimited 51 * lifetime. 52 */ 53 struct filename { 54 const char *fn_name; 55 size_t fn_len; 56 int fn_id; 57 struct filename *fn_next; 58 }; 59 60 static struct filename *filenames; /* null-terminated array */ 61 static int next_filename_id; 62 63 /* Find the given filename, or return NULL. */ 64 static const struct filename * 65 search_filename(const char *s, size_t len) 66 { 67 const struct filename *fn; 68 69 for (fn = filenames; fn != NULL; fn = fn->fn_next) { 70 if (fn->fn_len == len && memcmp(fn->fn_name, s, len) == 0) 71 break; 72 } 73 return fn; 74 } 75 76 struct filename_replacement { 77 const char *orig; 78 size_t orig_len; 79 const char *repl; 80 const struct filename_replacement *next; 81 }; 82 83 static struct filename_replacement *filename_replacements; 84 85 void 86 add_directory_replacement(char *arg) 87 { 88 struct filename_replacement *r = xmalloc(sizeof(*r)); 89 90 char *sep = strchr(arg, '='); 91 if (sep == NULL) 92 err(1, "Bad replacement directory spec `%s'", arg); 93 *sep = '\0'; 94 95 r->orig = arg; 96 r->orig_len = (size_t)(sep - arg); 97 r->repl = sep + 1; 98 r->next = filename_replacements; 99 filename_replacements = r; 100 } 101 102 const char * 103 transform_filename(const char *name, size_t len) 104 { 105 static char buf[MAXPATHLEN]; 106 const struct filename_replacement *r; 107 108 for (r = filename_replacements; r != NULL; r = r->next) 109 if (r->orig_len < len && 110 memcmp(name, r->orig, r->orig_len) == 0) 111 break; 112 if (r == NULL) 113 return name; 114 (void)snprintf(buf, sizeof(buf), "%s%s", r->repl, name + r->orig_len); 115 return buf; 116 } 117 118 /* 119 * Return a copy of the filename s with unlimited lifetime. 120 * If the filename is new, write it to the output file. 121 */ 122 const char * 123 record_filename(const char *s, size_t slen) 124 { 125 126 const struct filename *existing_fn = search_filename(s, slen); 127 if (existing_fn != NULL) 128 return existing_fn->fn_name; 129 130 char *name = xmalloc(slen + 1); 131 (void)memcpy(name, s, slen); 132 name[slen] = '\0'; 133 134 struct filename *fn = xmalloc(sizeof(*fn)); 135 fn->fn_name = name; 136 fn->fn_len = slen; 137 fn->fn_id = next_filename_id++; 138 fn->fn_next = filenames; 139 filenames = fn; 140 141 /* Write the ID of this filename to the output file. */ 142 outclr(); 143 outint(fn->fn_id); 144 outchar('s'); 145 outstrg(transform_filename(fn->fn_name, fn->fn_len)); 146 147 return fn->fn_name; 148 } 149 150 /* Get the ID of a filename. */ 151 int 152 get_filename_id(const char *s) 153 { 154 const struct filename *fn; 155 156 if (s == NULL || (fn = search_filename(s, strlen(s))) == NULL) 157 return -1; 158 return fn->fn_id; 159 } 160 161 typedef struct memory_pools { 162 struct memory_pool *pools; 163 size_t cap; 164 } memory_pools; 165 166 /* Array of memory pools, indexed by mem_block_level. */ 167 static memory_pools mpools; 168 169 /* The pool for the current expression is independent of any block level. */ 170 static memory_pool expr_pool; 171 172 static void 173 mpool_add(memory_pool *pool, struct memory_pool_item item) 174 { 175 176 if (pool->len >= pool->cap) { 177 pool->cap = 2 * pool->len + 16; 178 pool->items = xrealloc(pool->items, 179 sizeof(*pool->items) * pool->cap); 180 } 181 pool->items[pool->len++] = item; 182 } 183 184 #ifdef DEBUG_MEM 185 static void 186 debug_memory_pool_item(const struct memory_pool_item *item) 187 { 188 void *p = item->p; 189 size_t size = item->size; 190 const char *descr = item->descr; 191 192 if (strcmp(descr, "string") == 0) { 193 const char *str = p; 194 debug_step("%s: freeing string '%s'", __func__, str); 195 } else if (strcmp(descr, "sym") == 0) { 196 const sym_t *sym = p; 197 debug_step("%s: freeing symbol '%s'", __func__, sym->s_name); 198 } else if (strcmp(descr, "type") == 0) { 199 const type_t *tp = p; 200 debug_step("%s: freeing type '%s'", __func__, type_name(tp)); 201 } else if (strcmp(descr, "tnode") == 0) { 202 const tnode_t *tn = p; 203 debug_step("%s: freeing node '%s' with type '%s'", 204 __func__, op_name(tn->tn_op), type_name(tn->tn_type)); 205 } else 206 debug_step("%s: freeing '%s' with %zu bytes", 207 __func__, descr, size); 208 } 209 #endif 210 211 static void 212 mpool_free(memory_pool *pool) 213 { 214 215 #ifdef DEBUG_MEM 216 for (size_t i = pool->len; i-- > 0; ) 217 debug_memory_pool_item(pool->items + i); 218 #endif 219 220 for (size_t i = pool->len; i-- > 0;) { 221 #ifdef DEBUG_MEM 222 static void *(*volatile set)(void *, int, size_t) = memset; 223 set(pool->items[i].p, 'Z', pool->items[i].size); 224 #endif 225 free(pool->items[i].p); 226 } 227 pool->len = 0; 228 } 229 230 static void * 231 #ifdef DEBUG_MEM 232 mpool_zero_alloc(memory_pool *pool, size_t size, const char *descr) 233 #else 234 mpool_zero_alloc(memory_pool *pool, size_t size) 235 #endif 236 { 237 238 void *mem = xmalloc(size); 239 memset(mem, 0, size); 240 #if DEBUG_MEM 241 mpool_add(pool, (struct memory_pool_item){ mem, size, descr }); 242 #else 243 mpool_add(pool, (struct memory_pool_item){ mem }); 244 #endif 245 return mem; 246 } 247 248 static memory_pool * 249 mpool_at(size_t level) 250 { 251 252 if (level >= mpools.cap) { 253 size_t prev_cap = mpools.cap; 254 mpools.cap = level + 16; 255 mpools.pools = xrealloc(mpools.pools, 256 sizeof(*mpools.pools) * mpools.cap); 257 for (size_t i = prev_cap; i < mpools.cap; i++) 258 mpools.pools[i] = (memory_pool){ NULL, 0, 0 }; 259 } 260 return mpools.pools + level; 261 } 262 263 264 /* Allocate memory associated with the level, initialized with zero. */ 265 #ifdef DEBUG_MEM 266 void * 267 level_zero_alloc(size_t level, size_t size, const char *descr) 268 { 269 270 debug_step("%s: %s at level %zu", __func__, descr, level); 271 return mpool_zero_alloc(mpool_at(level), size, descr); 272 } 273 #else 274 void * 275 (level_zero_alloc)(size_t level, size_t size) 276 { 277 278 return mpool_zero_alloc(mpool_at(level), size); 279 } 280 #endif 281 282 /* Allocate memory that is freed at the end of the current block. */ 283 #ifdef DEBUG_MEM 284 void * 285 block_zero_alloc(size_t size, const char *descr) 286 { 287 288 return level_zero_alloc(mem_block_level, size, descr); 289 } 290 #else 291 void * 292 (block_zero_alloc)(size_t size) 293 { 294 295 return (level_zero_alloc)(mem_block_level, size); 296 } 297 #endif 298 299 void 300 level_free_all(size_t level) 301 { 302 303 debug_step("+ %s %zu", __func__, level); 304 debug_indent_inc(); 305 mpool_free(mpool_at(level)); 306 debug_leave(); 307 } 308 309 /* Allocate memory that is freed at the end of the current expression. */ 310 #if DEBUG_MEM 311 void * 312 expr_zero_alloc(size_t s, const char *descr) 313 { 314 315 return mpool_zero_alloc(&expr_pool, s, descr); 316 } 317 #else 318 void * 319 (expr_zero_alloc)(size_t size) 320 { 321 322 return mpool_zero_alloc(&expr_pool, size); 323 } 324 #endif 325 326 static bool 327 str_ends_with(const char *haystack, const char *needle) 328 { 329 size_t hlen = strlen(haystack); 330 size_t nlen = strlen(needle); 331 332 return nlen <= hlen && 333 memcmp(haystack + hlen - nlen, needle, nlen) == 0; 334 } 335 336 /* 337 * Return a freshly allocated tree node that is freed at the end of the 338 * current expression. 339 * 340 * The node records whether it comes from a system file, which makes strict 341 * bool mode less restrictive. 342 */ 343 tnode_t * 344 expr_alloc_tnode(void) 345 { 346 tnode_t *tn = expr_zero_alloc(sizeof(*tn), "tnode"); 347 /* 348 * files named *.c that are different from the main translation unit 349 * typically contain generated code that cannot be influenced, such 350 * as a flex lexer or a yacc parser. 351 */ 352 tn->tn_sys = in_system_header || 353 (curr_pos.p_file != csrc_pos.p_file && 354 str_ends_with(curr_pos.p_file, ".c")); 355 return tn; 356 } 357 358 /* Free all memory which is allocated by the current expression. */ 359 void 360 expr_free_all(void) 361 { 362 363 debug_step("%s", __func__); 364 mpool_free(&expr_pool); 365 } 366 367 /* 368 * Save the memory which is used by the current expression. This memory 369 * is not freed by the next expr_free_all() call. The returned value can be 370 * used to restore the memory. 371 */ 372 memory_pool 373 expr_save_memory(void) 374 { 375 376 memory_pool saved_pool = expr_pool; 377 expr_pool = (memory_pool){ NULL, 0, 0 }; 378 return saved_pool; 379 } 380 381 /* 382 * Free all memory used for the current expression and restore the memory used 383 * by a previous expression and saved by expr_save_memory(). The next call to 384 * expr_free_all() frees the restored memory. 385 */ 386 void 387 expr_restore_memory(memory_pool saved_pool) 388 { 389 390 expr_free_all(); 391 free(expr_pool.items); 392 expr_pool = saved_pool; 393 } 394