📄 yamd.c
字号:
return (addr)p;}#if 0 /* Currently we never free memory. Sad, but true. */static voidfree_pages(void *p, size_t npages){ if (remap_pages(p, npages) == 0) REAL(free)(p);}#endif/* Utility functions *//* `mem_fill': Fill `dest_size' bytes of `dest' with repeating sequences of the `src_size' bytes from `src', aligned to `src_size' boundary. */static void mem_fill(uchar *dest, size_t dest_size, const uchar *src, size_t src_size){ size_t di; size_t si; si = di = 0; while (di < dest_size) { while (si < src_size && di < dest_size) dest[di++] = src[si++]; /* Start si over again. */ si = 0; }}static size_t big_magic_size = 0;static uchar *big_magic = NULL; /* Filled on startup with as much magic as we need. */static inline voidmaybe_grow_big_magic(size_t new){ if (new > big_magic_size) { NO_CATCH(); big_magic = REAL(realloc)(big_magic, new); OK_CATCH(); /* What to do if it runs out? */ if (!big_magic) { log_event(LOG_ERR, "Out of memory for internal YAMD stuff"); die(); } mem_fill(big_magic, new, magic, sizeof(magic)); }}/* Compares blocks b1 and b2. Returns offset by which they differ, or -1 if the first n bytes are the same. */static inline ssize_t memcmp_w(uchar *b1, uchar *b2, size_t n){ /* Some assembly might be useful here */ size_t i = 0; while (i < n && b1[i] == b2[i]) i++; if (i == n) return -1; else return i;}static addr magic_check_range(addr start, addr end){ ssize_t v; size_t sz = end - start; maybe_grow_big_magic(sz); v = memcmp_w((uchar *)start, big_magic, sz); if (v < 0) return 0; else return start + v;}static voidmagic_fill_range(addr start, addr end){ size_t sz = end - start; maybe_grow_big_magic(sz); memcpy((void *)start, big_magic, sz);}#ifdef COMPLETE_MAGICstatic voidmagic_fill_block(block *b){ magic_fill_range((addr )b->block_addr + PGSZ, (addr )b->user_addr); magic_fill_range((addr )b->user_addr + b->user_size, (addr )b->suffix_addr);}#endif/* Block management. */#define for_each_block(p) for (p = all_blocks; p; p = p->all_next)#define for_each_block_by_alignment(p, head) \ for (p = head; p; p = p->alignment_next) /* Should return a valid index into hash[]. *//* We use this because the low-order bits probably aren't random, nor are the highest. Here we get the middle, then shift and xor. *//* #define HASH_FUNC(n) (((n) / PGSZ) % HASH_SIZE) */#define HASH_FUNC(n) ((((n) / PGSZ) ^ (((n) / PGSZ) >> 8)) % HASH_SIZE)static voidinsert_block(block *b){ /* Insert into the hash table. */ int h; h = HASH_FUNC(b->user_addr); b->hash_next = hash[h]; hash[h] = b; /* And into the all_blocks list */ b->all_next = all_blocks; all_blocks = b; /* And into its alignment list */ if (HAS_DEFAULT_ALIGNMENT(b)) { b->alignment_next = unaligned_blocks; unaligned_blocks = b; } else { b->alignment_next = aligned_blocks; aligned_blocks = b; }}static block *find_block_by_user_addr(addr a){ block *p; for (p = hash[HASH_FUNC(a)]; p; p = p->hash_next) if (p->user_addr == a) return p; return NULL;}/* This need not be fast; it's only called in the event of an error. */static block *find_block_by_any_addr(addr a){ block *p; for_each_block(p) if ((a >= p->block_addr) && (a < (p->block_addr + p->block_size))) return p; return NULL;}#ifdef HASH_PROFILEstatic voidhash_profile(void){ double avg; double variance = 0.0; int i; avg = ((double)n_allocations) / HASH_SIZE; for (i = 0; i < HASH_SIZE; i++) { int j = 0; block *p; for (p = hash[i]; p; p = p->hash_next) j++; variance += (pow(((double)j) - avg, 2.0) / (double)HASH_SIZE); } log_printf("Average chain length = %f, std dev = %f\n", avg, sqrt(variance));}#endif static const char *get_entry_name(unsigned by_who){ static char *table[] = { [BY_NONE] "nobody", [BY_MALLOC] "malloc", [BY_FREE] "free", [BY_REALLOC] "realloc", [BY_MEMALIGN] "memalign", [BY_ALLOCA] "alloca", [BY_AUTO] "alloca auto-free", [BY_LITE] "lite-mode allocator" }; return table[by_who];}/* Logging */static voidlog_flush(void){ write(log_fd, log_buf, log_buf_pos); log_buf_pos = 0;}static voidlog_vprintf(const char *fmt, va_list va){ /* This is not at all safe; I really wish we always had vsnprintf */ if (log_buf_pos + MAX_PRINTF >= LOG_BUF_SIZE) log_flush(); log_buf_pos += vsprintf(log_buf + log_buf_pos, fmt, va); if (log_buf_pos + 1 >= LOG_BUF_SIZE) { /* Not a good thing. */ static char msg[] = "YAMD: Log buffer overrun-- increase MAX_PRINTF" \ " and recompile\n"; write(2, msg, strlen(msg)); abort(); }}static voidlog_printf(const char *fmt, ...){ va_list va; va_start(va, fmt); log_vprintf(fmt, va); va_end(va);}static voidlog_event(int level, const char *desc){ char *ls; if (level >= min_log_level) { switch (level) { case LOG_INFO: ls = "INFO"; break; case LOG_WARN: ls = "WARNING"; break; case LOG_ERR: ls = "ERROR"; break; default: ls = "uh oh, don't know this"; break; } log_detail(level, "\n%s: %s\n", ls, desc); }}static voidlog_detail(int level, const char *fmt, ...){ va_list vl; if (level >= min_log_level) { va_start(vl, fmt); log_vprintf(fmt, vl); va_end(vl); }}/* Generate a traceback and store it in `tb'. If `eip_on_stack' is 1, `start_ebp' is a frame pointer somewhere below the caller at `start_eip'. Otherwise, `start_eip' is not on the stack; the traceback will start with it and continue with the function whose frame pointer is `start_ebp'.*//* Standard GCC stack frame looks like: ... Return address Saved EBP <-- EBP points here Local vars...*/#ifdef __linux__/* Bother. There isn't really any good way to find out the limits of the stack. Guess we just have to trust the luser to have compiled without -fomit-frame-pointer and not scrogged the stack... */#define STACK_ADDR_OK(a) ((a) != 0)#endif#ifdef __DJGPP__extern int djgpp_end asm ("end");#define STACK_ADDR_OK(a) (((a) >= (addr)&djgpp_end) && \ ((a) < (addr)__djgpp_selector_limit))#endif/* This code looks a bit suspicious with respect to GCC's new aliasing rules, but I think it's okay. We only dereference pointers of type `addr *', so we aren't referring to the same object via pointers of different types. Anyone who disagrees, please let me know. */static voidgenerate_any_traceback(TRACEBACK tb, addr start_eip, addr start_ebp, int eip_on_stack){ /* Wow. Here be lots of ugly typecasts. */ addr ebp; addr last_ebp; addr eip; size_t i; if (eip_on_stack) { last_ebp = 0; ebp = start_ebp; eip = 0; /* In case we abort immediately */ /* The last test really needs to be done only once, but this is cleaner */ while (ebp > last_ebp && STACK_ADDR_OK(ebp)) { eip = *((addr *)ebp + 1); last_ebp = ebp; ebp = *(addr *)ebp; if (eip == start_eip) break; } if (eip != start_eip) { /* We broke out because the frame address went wrong, or maybe we reached the top. Assume start_eip is right, but don't go any farther than that. */ tb[0] = start_eip; tb[1] = 0; return; } } else { eip = start_eip; ebp = start_ebp; } i = 0; last_ebp = 0; tb[i++] = eip; /* Log the first one */ /* The last test really needs to be done only once, but this is cleaner */ while (i < MAX_TRACEBACK_LEVELS - 1 && ebp > last_ebp && STACK_ADDR_OK(ebp)) { tb[i++] = *((addr *)ebp + 1); last_ebp = ebp; ebp = *(addr *)ebp; } tb[i] = 0;}/* The standard case, where we want a traceback of our callers */static voidgenerate_traceback(TRACEBACK tb, addr eip){ generate_any_traceback(tb, eip, (addr)__builtin_frame_address(0), 1);}static voiddump_traceback(int level, TRACEBACK tb){ size_t i; log_detail(level, "BEGIN TRACEBACK\n"); /* to allow indentation */#ifdef HAVE_BACKTRACE_SYMBOLS if (level >= min_log_level) { for (i = 0; tb[i] != 0; i++) ; log_flush(); backtrace_symbols_fd((void **)tb, i, log_fd); }#else for (i = 0; tb[i] != 0; i++) { log_detail(level, " " POINTER_FORMAT "\n", tb[i]); }#endif log_detail(level, "END TRACEBACK\n");}static voiddo_any_traceback(int level, addr eip, addr ebp, int eip_os){ TRACEBACK buf; generate_any_traceback(buf, eip, ebp, eip_os); dump_traceback(level, buf);}static voiddo_traceback(int level, addr eip){ TRACEBACK buf; generate_traceback(buf, eip); dump_traceback(level, buf);}static voiddescribe_block(int level, block *b){ log_detail(level, "Address " POINTER_FORMAT ", size %u\n", b->user_addr, b->user_size); log_detail(level, "Allocated by %s ", get_entry_name(b->who_alloced)); if (b->who_alloced == BY_MEMALIGN) log_detail(level, "(alignment %u) ", b->alignment); log_detail(level, "at\n"); dump_traceback(level, b->where_alloced); if (b->who_freed != BY_NONE) { if (b->who_freed == BY_AUTO) log_detail(level, "Automatically freed\n"); else { log_detail(level, "Freed by %s at\n", get_entry_name(b->who_freed)); dump_traceback(level, b->where_freed); } } if (b->who_alloced == BY_REALLOC) { if (b->realloc_backlink == BAD_POINTER) { log_detail(level, "Realloced from bad pointer\n"); } else if (b->realloc_backlink == NULL) { log_detail(level, "Realloced from NULL\n"); } else { log_detail(level, "Realloced from block:\n"); describe_block(level, b->realloc_backlink); } }}static voidhandle_bad_magic(block *b, addr where){ log_event(LOG_ERR, "Corrupted block"); log_detail(LOG_ERR, "Bad magic bytes at " POINTER_FORMAT ", part of this block:\n", where); describe_block(LOG_ERR, b); log_detail(LOG_ERR, "Address in question is at offset %d\n", where - b->user_addr); if (die_on_corrupted) { log_detail(LOG_ERR, "Dumping core\n"); die(); } if (repair_corrupted) { log_detail(LOG_ERR, "Fixing\n"); /* Leave it to check_block to actually fix it, so it can catch corruption at the other end first. */ } else { log_detail(LOG_ERR, "Leaving as is\n"); }}static voidcheck_block(block *b){ addr badp; addr s, e; /* If check_front, we'd have nothing to do, so we'd better not be called in that case. */ if (b->who_freed != BY_NONE) return; /* We can't touch this area */ if (b->who_alloced == BY_LITE) return; /* we don't want to know */#ifdef COMPLETE_MAGIC
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -