File ltrace-ppc64le_git2.patch of Package ltrace

--- breakpoints.c.ori
+++ breakpoints.c
@@ -88,13 +88,18 @@ breakpoint_on_retract(struct breakpoint *bp, struct Process *proc)
 /*****************************************************************************/
 
 struct breakpoint *
-address2bpstruct(Process *proc, void *addr)
+address2bpstruct(Process *proc, arch_addr_t addr)
 {
 	assert(proc != NULL);
 	assert(proc->breakpoints != NULL);
 	assert(proc->leader == proc);
 	debug(DEBUG_FUNCTION, "address2bpstruct(pid=%d, addr=%p)", proc->pid, addr);
-	return dict_find_entry(proc->breakpoints, addr);
+
+	struct breakpoint **found
+		= DICT_FIND(proc->breakpoints, &addr, struct breakpoint *);
+	if (found == NULL)
+		return NULL;
+	return *found;
 }
 
 #ifndef ARCH_HAVE_BREAKPOINT_DATA
@@ -196,7 +201,7 @@ breakpoint_turn_off(struct breakpoint *bp, struct Process *proc)
 }
 
 struct breakpoint *
-insert_breakpoint(struct Process *proc, void *addr,
+insert_breakpoint(struct Process *proc, arch_addr_t addr,
 		  struct library_symbol *libsym)
 {
 	Process *leader = proc->leader;
@@ -218,39 +223,46 @@ insert_breakpoint(struct process *proc, void *addr,
 	 * will suffice, about the only realistic case where we need
 	 * to have more than one breakpoint per address is return from
 	 * a recursive library call.  */
-	struct breakpoint *sbp = dict_find_entry(leader->breakpoints, addr);
-	if (sbp == NULL) {
-		sbp = malloc(sizeof(*sbp));
-		if (sbp == NULL
-		    || breakpoint_init(sbp, proc, addr, libsym) < 0) {
-			free(sbp);
+	struct breakpoint **found
+		= DICT_FIND(leader->breakpoints, &addr, struct breakpoint *);
+	struct breakpoint *bp;
+	if (found == NULL) {
+		bp = malloc(sizeof(*bp));
+		if (bp == NULL
+		    || breakpoint_init(bp, proc, addr, libsym) < 0) {
+			free(bp);
 			return NULL;
 		}
-		if (proc_add_breakpoint(leader, sbp) < 0) {
+		if (proc_add_breakpoint(leader, bp) < 0) {
 		fail:
-			breakpoint_destroy(sbp);
-			free(sbp);
+			breakpoint_destroy(bp);
+			free(bp);
 			return NULL;
 		}
+	} else {
+		bp = *found;
 	}
 
-	if (breakpoint_turn_on(sbp, proc) < 0) {
-		proc_remove_breakpoint(leader, sbp);
+	if (breakpoint_turn_on(bp, proc) < 0) {
+		proc_remove_breakpoint(leader, bp);
 		goto fail;
 	}
 
-	return sbp;
+	return bp;
 }
 
 void
-delete_breakpoint(Process *proc, void *addr)
+delete_breakpoint(Process *proc, arch_addr_t addr)
 {
 	debug(DEBUG_FUNCTION, "delete_breakpoint(pid=%d, addr=%p)", proc->pid, addr);
 
 	Process * leader = proc->leader;
 	assert(leader != NULL);
 
-	struct breakpoint *sbp = dict_find_entry(leader->breakpoints, addr);
+	struct breakpoint **found
+		= DICT_FIND(leader->breakpoints, &addr, struct breakpoint *);
+	assert(found != NULL);
+	struct breakpoint *sbp = *found;
 	assert(sbp != NULL);
 	/* This should only happen on out-of-memory conditions. */
 	if (sbp == NULL)
@@ -282,12 +294,14 @@ breakpoint_library(const struct breakpoint *bp)
 	return bp->libsym != NULL ? bp->libsym->lib : NULL;
 }
 
-static void
-enable_bp_cb(void *addr, void *sbp, void *proc)
+static enum callback_status
+enable_bp_cb(arch_addr_t *addr, struct breakpoint **bpp, void *data)
 {
-	debug(DEBUG_FUNCTION, "enable_bp_cb(pid=%d)", ((Process *)proc)->pid);
-	if (((struct breakpoint *)sbp)->enabled)
-		enable_breakpoint(proc, sbp);
+	Process *proc = data;
+	debug(DEBUG_FUNCTION, "enable_bp_cb(pid=%d)", proc->pid);
+	if ((*bpp)->enabled)
+		enable_breakpoint(proc, *bpp);
+	return CBS_CONT;
 }
 
 void
@@ -296,18 +309,19 @@ enable_all_breakpoints(Process *proc)
 	debug(DEBUG_FUNCTION, "enable_all_breakpoints(pid=%d)", proc->pid);
 
 	debug(1, "Enabling breakpoints for pid %u...", proc->pid);
-	if (proc->breakpoints) {
-		dict_apply_to_all(proc->breakpoints, enable_bp_cb,
-				  proc);
-	}
+	if (proc->breakpoints != NULL)
+		DICT_EACH(proc->breakpoints, arch_addr_t, struct breakpoint *,
+			  NULL, enable_bp_cb, proc);
 }
 
-static void
-disable_bp_cb(void *addr, void *sbp, void *proc)
+static enum callback_status
+disable_bp_cb(arch_addr_t *addr, struct breakpoint **bpp, void *data)
 {
-	debug(DEBUG_FUNCTION, "disable_bp_cb(pid=%d)", ((Process *)proc)->pid);
-	if (((struct breakpoint *)sbp)->enabled)
-		disable_breakpoint(proc, sbp);
+	Process *proc = data;
+	debug(DEBUG_FUNCTION, "disable_bp_cb(pid=%d)", proc->pid);
+	if ((*bpp)->enabled)
+		disable_breakpoint(proc, *bpp);
+	return CBS_CONT;
 }
 
 void
@@ -314,7 +327,8 @@ disable_all_breakpoints(Process *proc)
 disable_all_breakpoints(Process *proc) {
 	debug(DEBUG_FUNCTION, "disable_all_breakpoints(pid=%d)", proc->pid);
 	assert(proc->leader == proc);
-	dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc);
+	DICT_EACH(proc->breakpoints, arch_addr_t, struct breakpoint *,
+		  NULL, disable_bp_cb, proc);
 }
 
 /* XXX This is not currently properly supported.  On clone, this is
--- common.h.ori
+++ common.h
@@ -74,7 +74,7 @@ struct opt_c_struct {
 #include "demangle.h"
 #endif
 
-extern Dict * dict_opt_c;
+extern struct dict *dict_opt_c;
 
 /* Events  */
 extern Event * next_event(void);
--- demangle.c.ori
+++ demangle.c
@@ -1,5 +1,6 @@
 /*
  * This file is part of ltrace.
+ * Copyright (C) 2012 Petr Machata, Red Hat Inc.
  * Copyright (C) 1998,1999,2003,2004,2008,2009 Juan Cespedes
  * Copyright (C) 2006 Ian Wienand
  *
@@ -31,34 +32,53 @@
 
 /*****************************************************************************/
 
-static Dict *d = NULL;
+static struct dict *name_cache = NULL;
 
 const char *
 my_demangle(const char *function_name) {
-	const char *tmp, *fn_copy;
 #ifdef USE_CXA_DEMANGLE
 	extern char *__cxa_demangle(const char *, char *, size_t *, int *);
 #endif
 
 	debug(DEBUG_FUNCTION, "my_demangle(name=%s)", function_name);
 
-	if (!d)
-		d = dict_init(dict_key2hash_string, dict_key_cmp_string);
+	if (name_cache == NULL) {
+		name_cache = malloc(sizeof(*name_cache));
+		if (name_cache != NULL)
+			DICT_INIT(name_cache, const char *, const char *,
+				  dict_hash_string, dict_eq_string, NULL);
+	}
+
+	const char **found = NULL;
+	if (name_cache != NULL)
+		found = DICT_FIND(name_cache, &function_name, const char *);
+
+	if (found != NULL)
+		return *found;
 
-	tmp = dict_find_entry(d, (void *)function_name);
-	if (!tmp) {
-		fn_copy = strdup(function_name);
 #ifdef HAVE_LIBIBERTY
-		tmp = cplus_demangle(function_name, DMGL_ANSI | DMGL_PARAMS);
+	const char *tmp = cplus_demangle(function_name,
+					 DMGL_ANSI | DMGL_PARAMS);
 #elif defined USE_CXA_DEMANGLE
-		int status = 0;
-		tmp = __cxa_demangle(function_name, NULL, NULL, &status);
+	int status = 0;
+	const char *tmp = __cxa_demangle(function_name, NULL, NULL, &status);
 #endif
-		if (!tmp)
-			tmp = fn_copy;
-		if (tmp)
-			dict_enter(d, (void *)fn_copy, (void *)tmp);
+	if (name_cache == NULL || tmp == NULL) {
+	fail:
+		if (tmp == NULL)
+			return function_name;
+		return tmp;
 	}
+
+	const char *fn_copy = strdup(function_name);
+	if (fn_copy == NULL)
+		goto fail;
+
+	if (DICT_INSERT(name_cache, &fn_copy, &tmp) < 0) {
+		free((char *)fn_copy);
+		goto fail;
+	}
+
 	return tmp;
 }
 
--- dict.c.ori
+++ dict.c
@@ -1,8 +1,6 @@
 /*
  * This file is part of ltrace.
- * Copyright (C) 2011,2012 Petr Machata
- * Copyright (C) 2003,2004,2008,2009 Juan Cespedes
- * Copyright (C) 2006 Ian Wienand
+ * Copyright (C) 2012 Petr Machata, Red Hat Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -20,298 +18,581 @@
  * 02110-1301 USA
  */
 
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
-#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "dict.h"
 
-#include "common.h"
+struct status_bits {
+	unsigned char taken : 1;
+	unsigned char erased : 1;
+};
 
-/*
- * Dictionary based on code by Morten Eriksen <mortene@sim.no>.
- */
+static struct status_bits *
+bitp(struct dict *dict, size_t n)
+{
+	return VECT_ELEMENT(&dict->status, struct status_bits, n);
+}
 
-struct dict_entry {
-	unsigned int hash;
-	void *key;
-	void *value;
-	struct dict_entry *next;
-};
+void
+dict_init(struct dict *dict,
+	  size_t key_size, size_t value_size,
+	  size_t (*hash1)(const void *),
+	  int (*eq)(const void *, const void *),
+	  size_t (*hash2)(size_t))
+{
+	assert(hash1 != NULL);
+	assert(eq != NULL);
 
-/* #define DICTTABLESIZE 97 */
-#define DICTTABLESIZE 997	/* Semi-randomly selected prime number. */
-/* #define DICTTABLESIZE 9973 */
-/* #define DICTTABLESIZE 99991 */
-/* #define DICTTABLESIZE 999983 */
+	vect_init(&dict->keys, key_size);
+	vect_init(&dict->values, value_size);
+	VECT_INIT(&dict->status, struct status_bits);
+	dict->size = 0;
+
+	dict->hash1 = hash1;
+	dict->hash2 = hash2;
+	dict->eq = eq;
+}
 
-struct dict {
-	struct dict_entry *buckets[DICTTABLESIZE];
-	unsigned int (*key2hash) (const void *);
-	int (*key_cmp) (const void *, const void *);
+struct clone_data {
+	struct dict *target;
+	int (*clone_key)(void *tgt, const void *src, void *data);
+	int (*clone_value)(void *tgt, const void *src, void *data);
+	void (*dtor_key)(void *tgt, void *data);
+	void (*dtor_value)(void *tgt, void *data);
+	void *data;
 };
 
-Dict *
-dict_init(unsigned int (*key2hash) (const void *),
-	  int (*key_cmp) (const void *, const void *))
+enum callback_status
+clone_cb(void *key, void *value, void *data)
 {
-	Dict *d;
-	int i;
-
-	debug(DEBUG_FUNCTION, "dict_init()");
-
-	d = malloc(sizeof(Dict));
-	if (!d) {
-		perror("malloc()");
-		exit(1);
+	struct clone_data *clone_data = data;
+
+	char nkey[clone_data->target->keys.elt_size];
+	if (clone_data->clone_key == NULL)
+		memmove(nkey, key, sizeof(nkey));
+	else if (clone_data->clone_key(&nkey, key, clone_data->data) < 0)
+		return CBS_STOP;
+
+	char nvalue[clone_data->target->values.elt_size];
+	if (clone_data->clone_value == NULL) {
+		memmove(nvalue, value, sizeof(nvalue));
+	} else if (clone_data->clone_value(&nvalue, value,
+					 clone_data->data) < 0) {
+	fail:
+		if (clone_data->clone_key != NULL)
+			clone_data->dtor_key(&nkey, clone_data->data);
+		return CBS_STOP;
 	}
-	for (i = 0; i < DICTTABLESIZE; i++) {	/* better use memset()? */
-		d->buckets[i] = NULL;
+
+	if (dict_insert(clone_data->target, nkey, nvalue) < 0) {
+		if (clone_data->clone_value != NULL)
+			clone_data->dtor_value(&nvalue, clone_data->data);
+		goto fail;
 	}
-	d->key2hash = key2hash;
-	d->key_cmp = key_cmp;
-	return d;
+
+	return CBS_CONT;
 }
 
-void
-dict_clear(Dict *d) {
-	int i;
-	struct dict_entry *entry, *nextentry;
-
-	debug(DEBUG_FUNCTION, "dict_clear()");
-	assert(d);
-	for (i = 0; i < DICTTABLESIZE; i++) {
-		for (entry = d->buckets[i]; entry != NULL; entry = nextentry) {
-			nextentry = entry->next;
-			free(entry);
-		}
-		d->buckets[i] = NULL;
+int
+dict_clone(struct dict *target, const struct dict *source,
+	   int (*clone_key)(void *tgt, const void *src, void *data),
+	   void (*dtor_key)(void *tgt, void *data),
+	   int (*clone_value)(void *tgt, const void *src, void *data),
+	   void (*dtor_value)(void *tgt, void *data),
+	   void *data)
+{
+	assert((clone_key != NULL) == (dtor_key != NULL));
+	assert((clone_value != NULL) == (dtor_value != NULL));
+
+	dict_init(target, source->keys.elt_size, source->values.elt_size,
+		  source->hash1, source->eq, source->hash2);
+	struct clone_data clone_data = {
+		target, clone_key, clone_value, dtor_key, dtor_value, data
+	};
+	if (dict_each((struct dict *)source, NULL,
+		      clone_cb, &clone_data) != NULL) {
+		dict_destroy(target, dtor_key, dtor_value, data);
+		return -1;
 	}
-	free(d);
+	return 0;
+}
+
+size_t
+dict_size(const struct dict *dict)
+{
+	return dict->size;
 }
 
 int
-dict_enter(Dict *d, void *key, void *value) {
-	struct dict_entry *entry, *newentry;
-	unsigned int hash;
-	unsigned int bucketpos;
+dict_empty(const struct dict *dict)
+{
+	return dict->size == 0;
+}
 
-	debug(DEBUG_FUNCTION, "dict_enter()");
+struct destroy_data {
+	void (*dtor_key)(void *tgt, void *data);
+	void (*dtor_value)(void *tgt, void *data);
+	void *data;
+};
 
-	hash = d->key2hash(key);
-	bucketpos = hash % DICTTABLESIZE;
+enum callback_status
+destroy_cb(void *key, void *value, void *data)
+{
+	struct destroy_data *destroy_data = data;
+	if (destroy_data->dtor_key)
+		destroy_data->dtor_key(key, destroy_data->data);
+	if (destroy_data->dtor_value)
+		destroy_data->dtor_value(value, destroy_data->data);
+	return CBS_CONT;
+}
 
-	assert(d);
-	newentry = malloc(sizeof(struct dict_entry));
-	if (!newentry) {
-		perror("malloc");
-		exit(1);
+void
+dict_destroy(struct dict *dict,
+	     void (*dtor_key)(void *tgt, void *data),
+	     void (*dtor_value)(void *tgt, void *data),
+	     void *data)
+{
+	/* Some keys and values are not initialized, so we can't call
+	 * dtors for them.  Iterate DICT instead.  */
+	if (dtor_key != NULL || dtor_value != NULL) {
+		struct destroy_data destroy_data = {
+			dtor_key, dtor_value, data
+		};
+		dict_each(dict, NULL, destroy_cb, &destroy_data);
 	}
 
-	newentry->hash = hash;
-	newentry->key = key;
-	newentry->value = value;
-	newentry->next = NULL;
-
-	entry = d->buckets[bucketpos];
-	while (entry && entry->next)
-		entry = entry->next;
+	vect_destroy(&dict->keys, NULL, NULL);
+	vect_destroy(&dict->values, NULL, NULL);
+	vect_destroy(&dict->status, NULL, NULL);
+}
 
-	if (entry)
-		entry->next = newentry;
-	else
-		d->buckets[bucketpos] = newentry;
+static size_t
+default_secondary_hash(size_t pos)
+{
+	return pos % 97 + 1;
+}
 
-	debug(3, "new dict entry at %p[%d]: (%p,%p)", d, bucketpos, key, value);
-	return 0;
+static inline size_t
+n(struct dict *dict)
+{
+	return vect_size(&dict->keys);
 }
 
-void *
-dict_remove(Dict *d, void *key)
-{
-	assert(d != NULL);
-	debug(DEBUG_FUNCTION, "dict_remove(%p)", key);
-
-	unsigned int hash = d->key2hash(key);
-	unsigned int bucketpos = hash % DICTTABLESIZE;
-
-	struct dict_entry **entryp;
-	for (entryp = &d->buckets[bucketpos]; (*entryp) != NULL;
-	     entryp = &(*entryp)->next) {
-		struct dict_entry *entry = *entryp;
-		if (hash != entry->hash)
-			continue;
-		if (d->key_cmp(key, entry->key) == 0) {
-			*entryp = entry->next;
-			void *value = entry->value;
-			free(entry);
-			return value;
-		}
-	}
-	return NULL;
+static inline size_t (*
+hash2(struct dict *dict))(size_t)
+{
+	if (dict->hash2 != NULL)
+		return dict->hash2;
+	else
+		return default_secondary_hash;
 }
 
-void *
-dict_find_entry(Dict *d, const void *key)
+static void *
+getkey(struct dict *dict, size_t pos)
 {
-	unsigned int hash;
-	unsigned int bucketpos;
-	struct dict_entry *entry;
+	return ((unsigned char *)dict->keys.data)
+		+ dict->keys.elt_size * pos;
+}
 
-	debug(DEBUG_FUNCTION, "dict_find_entry()");
+static void *
+getvalue(struct dict *dict, size_t pos)
+{
+	return ((unsigned char *)dict->values.data)
+		+ dict->values.elt_size * pos;
+}
 
-	hash = d->key2hash(key);
-	bucketpos = hash % DICTTABLESIZE;
+static size_t
+find_slot(struct dict *dict, const void *key,
+	  int *foundp, int *should_rehash, size_t *pi)
+{
+	size_t pos = dict->hash1(key) % n(dict);
+	size_t pos0 = -1;
+	size_t d = hash2(dict)(pos);
+	size_t i = 0;
+	*foundp = 0;
+
+	/* We skip over any taken or erased slots.  But we remember
+	 * the first erased that we find, and if we don't find the key
+	 * later, we return that position.  */
+	for (; bitp(dict, pos)->taken || bitp(dict, pos)->erased;
+	     pos = (pos + d) % n(dict)) {
+
+		if (pos0 == (size_t)-1 && bitp(dict, pos)->erased)
+			pos0 = pos;
+
+		if (++i > dict->size)
+			break;
 
-	assert(d);
-	for (entry = d->buckets[bucketpos]; entry; entry = entry->next) {
-		if (hash != entry->hash) {
-			continue;
-		}
-		if (!d->key_cmp(key, entry->key)) {
+		if (bitp(dict, pos)->taken
+		    && dict->eq(getkey(dict, pos), key)) {
+			*foundp = 1;
 			break;
 		}
 	}
-	return entry ? entry->value : NULL;
+
+	if (!*foundp && pos0 != (size_t)-1)
+		pos = pos0;
+
+	/* If the hash table degraded into a linked list, request a
+	 * rehash.  */
+	if (should_rehash != NULL)
+		*should_rehash = i > 10 && i > n(dict) / 10;
+
+	if (pi != NULL)
+		*pi = i;
+	return pos;
 }
 
-void
-dict_apply_to_all(Dict *d,
-		  void (*func) (void *key, void *value, void *data), void *data) {
-	int i;
+enum callback_status
+rehash_move(void *key, void *value, void *data)
+{
+	if (dict_insert(data, key, value) < 0)
+		return CBS_STOP;
+	else
+		return CBS_CONT;
+}
+
+int
+rehash(struct dict *dict, size_t nn)
+{
+	int ret = -1;
+
+	struct dict tmp;
+	dict_init(&tmp, dict->keys.elt_size, dict->values.elt_size,
+		  dict->hash1, dict->eq, dict->hash2);
+
+	/* To honor all invariants (so that we can safely call
+	 * dict_destroy), we first make a request to _reserve_ enough
+	 * room in all vectors.  This has no observable effect on
+	 * contents of vectors.  */
+	if (vect_reserve(&tmp.keys, nn) < 0
+	    || vect_reserve(&tmp.values, nn) < 0
+	    || vect_reserve(&tmp.status, nn) < 0)
+		goto done;
+
+	/* Now that we know that there is enough size in vectors, we
+	 * simply bump the size.  */
+	tmp.keys.size = nn;
+	tmp.values.size = nn;
+	size_t old_size = tmp.status.size;
+	tmp.status.size = nn;
+	memset(VECT_ELEMENT(&tmp.status, struct status_bits, old_size),
+	       0, (tmp.status.size - old_size) * tmp.status.elt_size);
+
+	/* At this point, TMP is once more an empty dictionary with NN
+	 * slots.  Now move stuff from DICT to TMP.  */
+	if (dict_each(dict, NULL, rehash_move, &tmp) != NULL)
+		goto done;
+
+	/* And now swap contents of DICT and TMP, and we are done.  */
+	{
+		struct dict tmp2 = *dict;
+		*dict = tmp;
+		tmp = tmp2;
+	}
 
-	debug(DEBUG_FUNCTION, "dict_apply_to_all()");
+	ret = 0;
 
-	if (!d) {
-		return;
+done:
+	/* We only want to release the containers, not the actual data
+	 * that they hold, so it's fine if we don't pass any dtor.  */
+	dict_destroy(&tmp, NULL, NULL, NULL);
+	return ret;
+
+}
+
+static const size_t primes[] = {
+	13, 31, 61, 127, 251, 509, 1021, 2039, 4093,
+	8191, 16381, 32749, 65521, 130981, 0
+};
+
+static size_t
+larger_size(size_t current)
+{
+	if (current == 0)
+		return primes[0];
+
+	if (current < primes[sizeof(primes)/sizeof(*primes) - 2]) {
+		size_t i;
+		for (i = 0; primes[i] != 0; ++i)
+			if (primes[i] > current)
+				return primes[i];
+		abort();
 	}
-	for (i = 0; i < DICTTABLESIZE; i++) {
-		struct dict_entry *entry = d->buckets[i];
-		while (entry) {
-			func(entry->key, entry->value, data);
-			entry = entry->next;
+
+	/* We ran out of primes, so invent a new one.  The following
+	 * gives primes until about 17M elements (and then some more
+	 * later).  */
+	return 2 * current + 6585;
+}
+
+static size_t
+smaller_size(size_t current)
+{
+	if (current <= primes[0])
+		return primes[0];
+
+	if (current <= primes[sizeof(primes)/sizeof(*primes) - 2]) {
+		size_t i;
+		size_t prev = 0;
+		for (i = 0; primes[i] != 0; ++i) {
+			if (primes[i] >= current)
+				return prev;
+			prev = primes[i];
 		}
+		abort();
 	}
+
+	return (current - 6585) / 2;
 }
 
-/*****************************************************************************/
+int
+dict_insert(struct dict *dict, void *key, void *value)
+{
+	if (n(dict) == 0 || dict->size > 0.7 * n(dict))
+	rehash:
+		if (rehash(dict, larger_size(n(dict))) < 0)
+			return -1;
+
+	int found;
+	int should_rehash;
+	size_t slot_n = find_slot(dict, key, &found, &should_rehash, NULL);
+
+	if (found)
+		return 1;
+
+	/* If rehash was requested, do that, and retry.  But just live
+	 * with it for apparently sparse tables.  No resizing can fix
+	 * a rubbish hash.  */
+	if (should_rehash && dict->size > 0.3 * n(dict))
+		goto rehash;
+
+	memmove(getkey(dict, slot_n), key, dict->keys.elt_size);
+	memmove(getvalue(dict, slot_n), value, dict->values.elt_size);
+
+	bitp(dict, slot_n)->taken = 1;
+	bitp(dict, slot_n)->erased = 0;
+	++dict->size;
 
-unsigned int
-dict_key2hash_string(const void *key)
+	return 0;
+}
+
+void *
+dict_find(struct dict *dict, const void *key)
 {
-	const char *s = (const char *)key;
-	unsigned int total = 0, shift = 0;
+	if (dict->size == 0)
+		return NULL;
 
-	assert(key);
-	while (*s) {
-		total = total ^ ((*s) << shift);
-		shift += 5;
-		if (shift > 24)
-			shift -= 24;
-		s++;
-	}
-	return total;
+	int found;
+	size_t slot_n = find_slot(dict, key, &found, NULL, NULL);
+	if (found)
+		return getvalue(dict, slot_n);
+	else
+		return NULL;
 }
 
 int
-dict_key_cmp_string(const void *key1, const void *key2)
+dict_erase(struct dict *dict, const void *key,
+	   void (*dtor_key)(void *tgt, void *data),
+	   void (*dtor_value)(void *tgt, void *data),
+	   void *data)
 {
-	assert(key1);
-	assert(key2);
-	return strcmp((const char *)key1, (const char *)key2);
+	int found;
+	size_t i;
+	size_t slot_n = find_slot(dict, key, &found, NULL, &i);
+	if (!found)
+		return -1;
+
+	if (dtor_key != NULL)
+		dtor_key(getkey(dict, slot_n), data);
+	if (dtor_value != NULL)
+		dtor_value(getvalue(dict, slot_n), data);
+
+	bitp(dict, slot_n)->taken = 0;
+	bitp(dict, slot_n)->erased = 1;
+	--dict->size;
+
+	if (dict->size < 0.3 * n(dict)) {
+		/* Don't mind if it fails when shrinking.  */
+		rehash(dict, smaller_size(n(dict)));
+	}
+
+	return 0;
 }
 
-unsigned int
-dict_key2hash_int(const void *key)
+void *
+dict_each(struct dict *dict, void *start_after,
+	  enum callback_status (*cb)(void *, void *, void *), void *data)
 {
-	return (unsigned long)key;
+	size_t i;
+	if (start_after != NULL)
+		i = ((start_after - dict->keys.data) / dict->keys.elt_size) + 1;
+	else
+		i = 0;
+
+	for (; i < dict->keys.size; ++i)
+		if (bitp(dict, i)->taken && !bitp(dict, i)->erased) {
+			void *key = getkey(dict, i);
+			if (cb(key, getvalue(dict, i), data) != CBS_CONT)
+				return key;
+		}
+
+	return NULL;
+}
+
+size_t
+dict_hash_int(const int *key)
+{
+	return (size_t)(*key * 2654435761);
 }
 
 int
-dict_key_cmp_int(const void *key1, const void *key2)
+dict_eq_int(const int *key1, const int *key2)
 {
-	return key1 - key2;
+	return *key1 == *key2;
 }
 
-Dict *
-dict_clone2(Dict * old, void * (*key_clone)(void *, void *),
-	    void * (*value_clone)(void *, void *), void * data)
+size_t
+dict_hash_string(const char **key)
 {
-	Dict *d;
-	int i;
+	size_t h = 5381;
+	const char *str = *key;
+	while (*str != 0)
+		h = h * 33 ^ *str++;
+	return h;
+}
 
-	debug(DEBUG_FUNCTION, "dict_clone()");
+int
+dict_eq_string(const char **key1, const char **key2)
+{
+	return strcmp(*key1, *key2) == 0;
+}
 
-	d = malloc(sizeof(Dict));
-	if (!d) {
-		perror("malloc()");
-		exit(1);
-	}
-	memcpy(d, old, sizeof(Dict));
-	for (i = 0; i < DICTTABLESIZE; i++) {	/* better use memset()? */
-		struct dict_entry *de_old;
-		struct dict_entry **de_new;
-
-		de_old = old->buckets[i];
-		de_new = &d->buckets[i];
-		while (de_old) {
-			void * nkey, * nval;
-			*de_new = malloc(sizeof(struct dict_entry));
-			if (!*de_new) {
-				perror("malloc()");
-				exit(1);
-			}
-			memcpy(*de_new, de_old, sizeof(struct dict_entry));
-
-			/* The error detection is rather weak :-/ */
-			nkey = key_clone(de_old->key, data);
-			if (nkey == NULL && de_old->key != NULL) {
-				perror("key_clone");
-			err:
-				/* XXX Will this actually work?  We
-				 * simply memcpy the old dictionary
-				 * over up there.  */
-				dict_clear(d);
-				free(de_new);
-				return NULL;
-			}
+#ifdef TEST
+static enum callback_status
+dump(int *key, int *value, void *data)
+{
+	char *seen = data;
+	assert(seen[*key] == 0);
+	seen[*key] = 1;
+	assert(*value == *key * 2 + 1);
+	return CBS_STOP;
+}
 
-			nval = value_clone(de_old->value, data);
-			if (nval == NULL && de_old->value != NULL) {
-				perror("value_clone");
-				goto err;
-			}
+static size_t
+dict_hash_int_silly(const int *key)
+{
+	return *key % 10;
+}
 
-			(*de_new)->key = nkey;
-			(*de_new)->value = nval;
-			de_new = &(*de_new)->next;
-			de_old = de_old->next;
-		}
-	}
-	return d;
+static void
+verify(struct dict *di, size_t len, char *seen)
+{
+	size_t ct = 0;
+	int *it;
+	for (it = NULL; (it = DICT_EACH(di, int, int, it, dump, seen)) != NULL;)
+		ct++;
+	assert(ct == len);
+	memset(seen, 0, len);
 }
 
-struct wrap_clone_cb
+static enum callback_status
+fill_keys(int *key, int *value, void *data)
 {
-	void * (*key_clone)(void *);
-	void * (*value_clone)(void *);
-};
+	int *array = data;
+	array[++array[0]] = *key;
+	return CBS_CONT;
+}
 
-static void *
-value_clone_1(void * arg, void * data)
+static void
+test1(void)
 {
-	return ((struct wrap_clone_cb *)data)->value_clone(arg);
+	struct dict di;
+	DICT_INIT(&di, int, int, dict_hash_int, dict_eq_int, NULL);
+
+	char seen[100000] = {};
+	size_t i;
+	for (i = 0; i < sizeof(seen); ++i) {
+		int key = i;
+		int value = 2 * i + 1;
+		DICT_INSERT(&di, &key, &value);
+		int *valp = DICT_FIND(&di, &key, int);
+		assert(valp != NULL);
+		assert(*valp == value);
+		assert(dict_size(&di) == i + 1);
+	}
+
+	verify(&di, sizeof(seen), seen);
+
+	struct dict d2;
+	DICT_CLONE(&d2, &di, int, int, NULL, NULL, NULL, NULL, NULL);
+	DICT_DESTROY(&di, int, int, NULL, NULL, NULL);
+	verify(&d2, sizeof(seen), seen);
+
+	/* Now we try to gradually erase all elements.  We can't erase
+	 * inside a DICT_EACH call, so copy first keys to a separate
+	 * memory area first.  */
+	int keys[d2.size + 1];
+	size_t ct = 0;
+	keys[0] = 0;
+	DICT_EACH(&d2, int, int, NULL, fill_keys, keys);
+	for (i = 0; i < (size_t)keys[0]; ++i) {
+		assert(DICT_ERASE(&d2, &keys[i + 1], int,
+				  NULL, NULL, NULL) == 0);
+		++ct;
+	}
+	assert(ct == sizeof(seen));
+	DICT_DESTROY(&d2, int, int, NULL, NULL, NULL);
 }
 
-static void *
-key_clone_1(void * arg, void * data)
+static void
+test_erase(void)
 {
-	return ((struct wrap_clone_cb *)data)->key_clone(arg);
+	int i;
+
+	/* To test erase, we need a relatively bad hash function, so
+	 * that there are some overlapping chains in the table.  */
+	struct dict d2;
+	DICT_INIT(&d2, int, int, dict_hash_int_silly, dict_eq_int, NULL);
+	const int limit = 500;
+	for (i = 0; i < limit; ++i) {
+		int key = 2 * i + 1;
+		int value = 2 * key + 1;
+		DICT_INSERT(&d2, &key, &value);
+	}
+
+	/* Now we try to delete each of the keys, and verify that none
+	 * of the chains was broken.  */
+	for (i = 0; i < limit; ++i) {
+		struct dict copy;
+		DICT_CLONE(&copy, &d2, int, int, NULL, NULL, NULL, NULL, NULL);
+		int key = 2 * i + 1;
+		DICT_ERASE(&copy, &key, int, NULL, NULL, NULL);
+		assert(dict_size(&copy) == dict_size(&d2) - 1);
+
+		int j;
+		for (j = 0; j < limit; ++j) {
+			key = 2 * j + 1;
+			int *valp = DICT_FIND(&copy, &key, int);
+			if (i != j) {
+				assert(valp != NULL);
+				assert(*valp == 2 * key + 1);
+			} else {
+				assert(valp == NULL);
+			}
+		}
+
+		DICT_DESTROY(&copy, int, int, NULL, NULL, NULL);
+	}
+	DICT_DESTROY(&d2, int, int, NULL, NULL, NULL);
 }
 
-Dict *
-dict_clone(Dict * old, void * (*key_clone)(void *),
-	   void * (*value_clone)(void *))
+int main(int argc, char *argv[])
 {
-	struct wrap_clone_cb cb = { key_clone, value_clone };
-	return dict_clone2(old, &key_clone_1, &value_clone_1, &cb);
+	test1();
+	test_erase();
+	return 0;
 }
+
+#endif
--- dict.h.ori
+++ dict.h
@@ -1,9 +1,6 @@
 /*
  * This file is part of ltrace.
- * Copyright (C) 2011,2012 Petr Machata
- * Copyright (C) 2003,2004,2008,2009 Juan Cespedes
- * Copyright (C) 2006 Ian Wienand
- * Copyright (C) ???? Morten Eriksen <mortene@sim.no>
+ * Copyright (C) 2012 Petr Machata, Red Hat Inc.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -24,32 +21,201 @@
 #ifndef _DICT_H_
 #define _DICT_H_
 
-/*
- * Dictionary based on code by Morten Eriksen <mortene@sim.no>.
- */
+#include <stddef.h>
+#include <assert.h>
+#include "vect.h"
+
+struct dict {
+	/* The invariant is that KEYS, VALUES and STATUS are of the
+	 * same size.  */
+	struct vect keys;
+	struct vect values;
+	struct vect status;
+	size_t size;
+
+	size_t (*hash1)(const void *);
+	int (*eq)(const void *, const void *);
+	size_t (*hash2)(size_t);
+};
+
+/* Initialize a dictionary DICT.  The dictionary will hold keys of the
+ * size KEY_SIZE and values of the size VALUE_SIZE.  HASH1 and HASH2
+ * are, respectively, primary and secondary hashing functions.  The
+ * latter may be NULL, in which case a default internal hash is used.
+ * EQ is a callback for comparing two keys.  */
+void dict_init(struct dict *dict,
+	       size_t key_size, size_t value_size,
+	       size_t (*hash1)(const void *),
+	       int (*eq)(const void *, const void *),
+	       size_t (*hash2)(size_t));
+
+/* Wrapper around dict_init.  Initializes a dictionary DITCP which
+ * will hold keys of type KEY_TYPE and values of type VALUE_TYPE.
+ * Other arguments as above.  */
+#define DICT_INIT(DICTP, KEY_TYPE, VALUE_TYPE, HASH1, EQ, HASH2)	\
+	({								\
+		/* Check that callbacks are typed properly.  */		\
+		size_t (*_hash1_callback)(const KEY_TYPE *) = HASH1;	\
+		int (*_eq_callback)(const KEY_TYPE *, const KEY_TYPE *) = EQ; \
+		dict_init(DICTP, sizeof(KEY_TYPE), sizeof(VALUE_TYPE),	\
+			  (size_t (*)(const void *))_hash1_callback,	\
+			  (int (*)(const void *, const void *))_eq_callback, \
+			  HASH2);					\
+	})
+
+/* Clone SOURCE to TARGET.  For cloning slots, CLONE_KEY and
+ * CLONE_VALUE are called.  These callbacks return 0 on success or a
+ * negative value on failure.  If any of the callbacks is NULL, the
+ * default action is simple memmove.  Returns 0 on success.  If the
+ * cloning fails for any reason, already-cloned keys and values are
+ * destroyed again by DTOR_KEY and DTOR_VALUE callbacks (if non-NULL),
+ * and the function returns a negative value.  DATA is passed to all
+ * callbacks verbatim.  */
+int dict_clone(struct dict *target, const struct dict *source,
+	       int (*clone_key)(void *tgt, const void *src, void *data),
+	       void (*dtor_key)(void *tgt, void *data),
+	       int (*clone_value)(void *tgt, const void *src, void *data),
+	       void (*dtor_value)(void *tgt, void *data),
+	       void *data);
+
+/* Clone SRC_DICTP, which holds KEY_TYPE-VALUE_TYPE pairs, into
+ * TGT_DICTP.  Other arguments and return codes as above.  */
+#define DICT_CLONE(TGT_DICTP, SRC_DICTP, KEY_TYPE, VALUE_TYPE,		\
+		   CLONE_KEY, DTOR_KEY, CLONE_VALUE, DTOR_VALUE, DATA)	\
+	/* xxx GCC-ism necessary to get in the safety latches.  */	\
+	({								\
+		const struct dict *_source_d = (SRC_DICTP);		\
+		assert(_source_d->keys.elt_size == sizeof(KEY_TYPE));	\
+		assert(_source_d->values.elt_size == sizeof(VALUE_TYPE)); \
+		/* Check that callbacks are typed properly.  */		\
+		void (*_key_dtor_cb)(KEY_TYPE *, void *) = DTOR_KEY;	\
+		int (*_key_clone_cb)(KEY_TYPE *, const KEY_TYPE *,	\
+				     void *) = CLONE_KEY;		\
+		void (*_value_dtor_cb)(VALUE_TYPE *, void *) = DTOR_VALUE; \
+		int (*_value_clone_cb)(VALUE_TYPE *, const VALUE_TYPE *, \
+				       void *) = CLONE_VALUE;		\
+		dict_clone((TGT_DICTP), _source_d,			\
+			   (int (*)(void *, const void *,		\
+				    void *))_key_clone_cb,		\
+			   (void (*)(void *, void *))_key_dtor_cb,	\
+			   (int (*)(void *, const void *,		\
+				    void *))_value_clone_cb,		\
+			   (void (*)(void *, void *))_value_dtor_cb,	\
+			   (DATA));					\
+	})
+
+/* Return number of key-value pairs stored in DICT.  */
+size_t dict_size(const struct dict *dict);
+
+/* Emptiness predicate.  */
+int dict_empty(const struct dict *dict);
+
+/* Insert into DICT a pair of KEY and VALUE.  Returns 0 if insertion
+ * was successful, a negative value on error, or a positive value if
+ * this key is already present in the table.  */
+int dict_insert(struct dict *dict, void *key, void *value);
+
+/* Insert into DICT a pair of KEY and VALUE.  See dict_insert for
+ * details.  In addition, make a check whether DICTP holds elements of
+ * the right size.  */
+#define DICT_INSERT(DICTP, KEYP, VALUEP)				\
+	(assert((DICTP)->keys.elt_size == sizeof(*(KEYP))),		\
+	 assert((DICTP)->values.elt_size == sizeof(*(VALUEP))),		\
+	 dict_insert((DICTP), (KEYP), (VALUEP)))
+
+/* Find in DICT a value corresponding to KEY and return a pointer to
+ * it.  Returns NULL if the key was not found.  */
+void *dict_find(struct dict *dict, const void *key);
+
+/* Find in DICTP a value of type VALUE_TYPE corresponding to KEYP and
+ * return a pointer (VALUE_TYPE *) to it.  Returns NULL if the key was
+ * not found.  */
+#define DICT_FIND(DICTP, KEYP, VALUE_TYPE)			\
+	(assert((DICTP)->keys.elt_size == sizeof(*(KEYP))),	\
+	 (VALUE_TYPE *)dict_find((DICTP), (KEYP)))
+
+/* Erase from DICT the entry corresponding to KEY.  Returns a negative
+ * value if the key was not found, or 0 on success.  DTOR_KEY and
+ * DTOR_VALUE, if non-NULL, are called to destroy the erased
+ * value.  */
+int dict_erase(struct dict *dict, const void *key,
+	       void (*dtor_key)(void *tgt, void *data),
+	       void (*dtor_value)(void *tgt, void *data),
+	       void *data);
+
+/* Erase from DICTP a value of type VALUE_TYPE corresponding to
+ * KEYP.  */
+#define DICT_ERASE(DICTP, KEYP, VALUE_TYPE, DTOR_KEY, DTOR_VALUE, DATA) \
+	({								\
+		struct dict *_d = (DICTP);				\
+		assert(_d->keys.elt_size == sizeof(*KEYP));		\
+		assert(_d->values.elt_size == sizeof(VALUE_TYPE));	\
+		/* Check that callbacks are typed properly.  */		\
+		void (*_value_dtor_cb)(VALUE_TYPE *, void *) = DTOR_VALUE; \
+		dict_erase(_d, (KEYP), (DTOR_KEY),			\
+			   (void (*)(void *, void *))_value_dtor_cb,	\
+			   (DATA));					\
+	})
+
+/* Destroy DICT.  If KEY_DTOR is non-NULL, then it's called on each
+ * key stored in DICT.  Similarly for VALUE_DTOR.  DATA is passed to
+ * DTOR's verbatim.  The memory pointed-to by DICT is not freed.  */
+void dict_destroy(struct dict *dict,
+		  void (*dtor_key)(void *tgt, void *data),
+		  void (*dtor_value)(void *tgt, void *data),
+		  void *data);
+
+/* Destroy DICTP, which holds keys of type KEY_TYPE and values of type
+ * VALUE_TYPE, using DTOR.  */
+#define DICT_DESTROY(DICTP, KEY_TYPE, VALUE_TYPE, DTOR_KEY, DTOR_VALUE, DATA) \
+	do {								\
+		struct dict *_d = (DICTP);				\
+		assert(_d->keys.elt_size == sizeof(KEY_TYPE));		\
+		assert(_d->values.elt_size == sizeof(VALUE_TYPE));	\
+		/* Check that callbacks are typed properly.  */		\
+		void (*_key_dtor_cb)(KEY_TYPE *, void *) = DTOR_KEY;	\
+		void (*_value_dtor_cb)(VALUE_TYPE *, void *) = DTOR_VALUE; \
+		dict_destroy(_d, (void (*)(void *, void *))_key_dtor_cb, \
+			     (void (*)(void *, void *))_value_dtor_cb,	\
+			     (DATA));					\
+	} while (0)
+
+/* Iterate through DICT.  See callback.h for notes on iteration
+ * interfaces.  Callback arguments are key, value, DATA.  Note that
+ * the iteration over DICT is more expensive than in other containers:
+ * while CB is only called for items present in the table, and is
+ * therefore O(number of elements), the iterator needs to go through
+ * all the table, which is proportional to O(size of table).
+ * START_AFTER and the returned iterator are key where the iteration
+ * stopped.  */
+void *dict_each(struct dict *dict, void *start_after,
+		enum callback_status (*cb)(void *, void *, void *), void *data);
+
+#define DICT_EACH(DICTP, KEY_TYPE, VALUE_TYPE, START_AFTER, CB, DATA)	\
+	/* xxx GCC-ism necessary to get in the safety latches.  */	\
+	({								\
+		assert((DICTP)->keys.elt_size == sizeof(KEY_TYPE));	\
+		assert((DICTP)->values.elt_size == sizeof(VALUE_TYPE));	\
+		/* Check that CB is typed properly.  */			\
+		enum callback_status (*_cb)(KEY_TYPE *, VALUE_TYPE *,	\
+					    void *) = CB;		\
+		KEY_TYPE *start_after = (START_AFTER);			\
+		(KEY_TYPE *)dict_each((DICTP), start_after,		\
+				      (enum callback_status		\
+				       (*)(void *, void *, void *))_cb,	\
+				      (DATA));				\
+	})
+
+/* A callback for hashing integers.  */
+size_t dict_hash_int(const int *key);
+
+/* An equality predicate callback for integers.  */
+int dict_eq_int(const int *key1, const int *key2);
+
+/* A callback for hashing NULL-terminated strings.  */
+size_t dict_hash_string(const char **key);
 
-typedef struct dict Dict;
-
-extern Dict *dict_init(unsigned int (*key2hash) (const void *),
-		       int (*key_cmp) (const void *, const void *));
-extern void dict_clear(Dict *d);
-extern int dict_enter(Dict *d, void *key, void *value);
-extern void *dict_remove(Dict *d, void *key);
-extern void *dict_find_entry(Dict *d, const void *key);
-extern void dict_apply_to_all(Dict *d,
-			      void (*func) (void *key, void *value, void *data),
-			      void *data);
-
-extern unsigned int dict_key2hash_string(const void *key);
-extern int dict_key_cmp_string(const void *key1, const void *key2);
-
-extern unsigned int dict_key2hash_int(const void *key);
-extern int dict_key_cmp_int(const void *key1, const void *key2);
-
-extern Dict * dict_clone(Dict *old, void * (*key_clone)(void*), void * (*value_clone)(void*));
-extern Dict * dict_clone2(Dict * old,
-			  void * (* key_clone)(void * key, void * data),
-			  void * (* value_clone)(void * value, void * data),
-			  void * data);
+/* An equality predicate callback for strings.  */
+int dict_eq_string(const char **key1, const char **key2);
 
 #endif /* _DICT_H_ */
--- library.c.ori
+++ library.c
@@ -68,31 +68,26 @@ arch_library_symbol_clone(struct library_symbol *retp,
 }
 #endif
 
-unsigned int
-target_address_hash(const void *key)
+size_t
+arch_addr_hash(const arch_addr_t *addr)
 {
-	/* XXX this assumes that key is passed by value.  */
 	union {
 		arch_addr_t addr;
-		unsigned int ints[sizeof(arch_addr_t)
-				  / sizeof(unsigned int)];
-	} u = { .addr = (arch_addr_t)key };
+		int ints[sizeof(arch_addr_t)
+			 / sizeof(unsigned int)];
+	} u = { .addr = *addr };
 
 	size_t i;
-	unsigned int h = 0;
+	size_t h = 0;
 	for (i = 0; i < sizeof(u.ints) / sizeof(*u.ints); ++i)
-		h ^= dict_key2hash_int((void *)(uintptr_t)u.ints[i]);
+		h ^= dict_hash_int(&u.ints[i]);
 	return h;
 }
 
 int
-target_address_cmp(const void *key1, const void *key2)
+arch_addr_eq(const arch_addr_t *addr1, const arch_addr_t *addr2)
 {
-	/* XXX this assumes that key is passed by value.  */
-	arch_addr_t addr1 = (arch_addr_t)key1;
-	arch_addr_t addr2 = (arch_addr_t)key2;
-	return addr1 < addr2 ? 1
-	     : addr1 > addr2 ? -1 : 0;
+	return *addr1 == *addr2;
 }
 
 /* If the other symbol owns the name, we need to make the copy, so
--- library.h.ori
+++ library.h
@@ -35,8 +35,8 @@ enum toplt {
 };
 
 /* Dict interface.  */
-unsigned int target_address_hash(const void *key);
-int target_address_cmp(const void *key1, const void *key2);
+size_t arch_addr_hash(const arch_addr_t *addr);
+int arch_addr_eq(const arch_addr_t *addr1, const arch_addr_t *addr2);
 
 /* For handling -l.  */
 struct library_exported_name {
--- output.c.ori
+++ output.c
@@ -51,7 +51,7 @@
 /* TODO FIXME XXX: include in common.h: */
 extern struct timeval current_time_spent;
 
-Dict *dict_opt_c = NULL;
+struct dict *dict_opt_c = NULL;
 
 static Process *current_proc = 0;
 static size_t current_depth = 0;
@@ -498,6 +498,12 @@ output_left(enum tof type, struct Process *proc,
 	stel->out.need_delim = need_delim;
 }
 
+static void
+free_stringp_cb(const char **stringp, void *data)
+{
+	free((char *)*stringp);
+}
+
 void
 output_right(enum tof type, struct Process *proc, struct library_symbol *libsym)
 {
@@ -506,26 +512,41 @@ output_right(enum tof type, struct Process *proc, struct library_symbol *libsym)
 	if (func == NULL)
 		return;
 
+again:
 	if (options.summary) {
-		struct opt_c_struct *st;
-		if (!dict_opt_c) {
-			dict_opt_c =
-			    dict_init(dict_key2hash_string,
-				      dict_key_cmp_string);
+		if (dict_opt_c == NULL) {
+			dict_opt_c = malloc(sizeof(*dict_opt_c));
+			if (dict_opt_c == NULL) {
+			oom:
+				fprintf(stderr,
+					"Can't allocate memory for "
+					"keeping track of -c.\n");
+				free(dict_opt_c);
+				options.summary = 0;
+				goto again;
+			}
+			DICT_INIT(dict_opt_c, const char *, struct opt_c_struct,
+				  dict_hash_string, dict_eq_string, NULL);
 		}
-		st = dict_find_entry(dict_opt_c, function_name);
-		if (!st) {
-			char *na;
-			st = malloc(sizeof(struct opt_c_struct));
-			na = strdup(function_name);
-			if (!st || !na) {
-				perror("malloc()");
-				exit(1);
+
+		struct opt_c_struct *st = DICT_FIND(dict_opt_c, &function_name,
+						    struct opt_c_struct);
+		if (st == NULL) {
+			const char *na = strdup(function_name);
+			struct opt_c_struct new_st = {.count = 0, .tv = {0, 0}};
+			if (na == NULL
+			    || DICT_INSERT(dict_opt_c, &na, &new_st) < 0) {
+				free((char *)na);
+				DICT_DESTROY(dict_opt_c, const char *,
+					     struct opt_c_struct,
+					     free_stringp_cb, NULL, NULL);
+				goto oom;
 			}
-			st->count = 0;
-			st->tv.tv_sec = st->tv.tv_usec = 0;
-			dict_enter(dict_opt_c, na, st);
+			st = DICT_FIND(dict_opt_c, &function_name,
+				       struct opt_c_struct);
+			assert(st != NULL);
 		}
+
 		if (st->tv.tv_usec + current_time_spent.tv_usec > 1000000) {
 			st->tv.tv_usec += current_time_spent.tv_usec - 1000000;
 			st->tv.tv_sec++;
@@ -534,11 +555,9 @@ output_right(enum tof type, struct Process *proc, struct library_symbol *libsym)
 		}
 		st->count++;
 		st->tv.tv_sec += current_time_spent.tv_sec;
-
-//              fprintf(options.output, "%s <%lu.%06d>\n", function_name,
-//                              current_time_spent.tv_sec, (int)current_time_spent.tv_usec);
 		return;
 	}
+
 	if (current_proc && (current_proc != proc ||
 			    current_depth != proc->callstack_depth)) {
 		fprintf(options.output, " <unfinished ...>\n");
--- proc.c.ori
+++ proc.c
@@ -121,8 +121,12 @@ process_bare_init(struct Process *proc, const char *filename,
 		if (proc->filename == NULL) {
 		fail:
 			free(proc->filename);
-			if (proc->breakpoints != NULL)
-				dict_clear(proc->breakpoints);
+			if (proc->breakpoints != NULL) {
+				dict_destroy(proc->breakpoints,
+					     NULL, NULL, NULL);
+				free(proc->breakpoints);
+				proc->breakpoints = NULL;
+			}
 			return -1;
 		}
 	}
@@ -134,10 +138,12 @@ process_bare_init(struct Process *proc, const char *filename,
 		goto fail;
 
 	if (proc->leader == proc) {
-		proc->breakpoints = dict_init(target_address_hash,
-					      target_address_cmp);
+		proc->breakpoints = malloc(sizeof(*proc->breakpoints));
 		if (proc->breakpoints == NULL)
 			goto fail;
+		DICT_INIT(proc->breakpoints,
+			  arch_addr_t, struct breakpoint *,
+			  arch_addr_hash, arch_addr_eq, NULL);
 	} else {
 		proc->breakpoints = NULL;
 	}
@@ -153,7 +159,8 @@ process_bare_init(struct Process *proc, const char *filename,
 static void
 process_bare_destroy(struct Process *proc, int was_exec)
 {
-	dict_clear(proc->breakpoints);
+	dict_destroy(proc->breakpoints, NULL, NULL, NULL);
+	free(proc->breakpoints);
 	if (!was_exec) {
 		free(proc->filename);
 		unlist_process(proc);
@@ -249,7 +256,8 @@ private_process_destroy(struct Process *proc, int was_exec)
 	/* Breakpoints.  */
 	if (proc->breakpoints != NULL) {
 		proc_each_breakpoint(proc, NULL, destroy_breakpoint_cb, NULL);
-		dict_clear(proc->breakpoints);
+		dict_destroy(proc->breakpoints, NULL, NULL, NULL);
+		free(proc->breakpoints);
 		proc->breakpoints = NULL;
 	}
 
@@ -299,31 +307,27 @@ open_program(const char *filename, pid_t pid)
 struct clone_single_bp_data {
 	struct Process *old_proc;
 	struct Process *new_proc;
-	int error;
 };
 
-static void
-clone_single_bp(void *key, void *value, void *u)
+static enum callback_status
+clone_single_bp(arch_addr_t *key, struct breakpoint **bpp, void *u)
 {
-	struct breakpoint *bp = value;
+	struct breakpoint *bp = *bpp;
 	struct clone_single_bp_data *data = u;
 
-	/* Don't bother if there were errors anyway.  */
-	if (data->error != 0)
-		return;
-
 	struct breakpoint *clone = malloc(sizeof(*clone));
 	if (clone == NULL
 	    || breakpoint_clone(clone, data->new_proc,
 				bp, data->old_proc) < 0) {
 	fail:
 		free(clone);
-		data->error = -1;
+		return CBS_STOP;
 	}
 	if (proc_add_breakpoint(data->new_proc->leader, clone) < 0) {
 		breakpoint_destroy(clone);
 		goto fail;
 	}
+	return CBS_CONT;
 }
 
 int
@@ -373,10 +377,10 @@ process_clone(struct Process *retp, struct Process *proc, pid_t pid)
 	struct clone_single_bp_data data = {
 		.old_proc = proc,
 		.new_proc = retp,
-		.error = 0,
 	};
-	dict_apply_to_all(proc->leader->breakpoints, &clone_single_bp, &data);
-	if (data.error < 0)
+	if (DICT_EACH(proc->leader->breakpoints,
+		      arch_addr_t, struct breakpoint *, NULL,
+		      clone_single_bp, &data) != NULL)
 		goto fail2;
 
 	/* And finally the call stack.  */
@@ -749,9 +753,9 @@ breakpoint_for_symbol(struct library_symbol *libsym, struct Process *proc)
 	 * be also custom-allocated, and we would really need to swap
 	 * the two: delete the one now in the dictionary, swap values
 	 * around, and put the new breakpoint back in.  */
-	struct breakpoint *bp = dict_find_entry(proc->breakpoints,
-						bp_addr);
-	if (bp != NULL) {
+	struct breakpoint **found = DICT_FIND(proc->breakpoints,
+					      &bp_addr, struct breakpoint *);
+	if (found != NULL) {
 		/* MIPS backend makes duplicate requests.  This is
 		 * likely a bug in the backend.  Currently there's no
 		 * point assigning more than one symbol to a
@@ -765,13 +769,13 @@ breakpoint_for_symbol(struct library_symbol *libsym, struct Process *proc)
 		 *   http://lists.alioth.debian.org/pipermail/ltrace-devel/2012-November/000770.html
 		 */
 #ifndef __mips__
-		assert(bp->libsym == NULL);
-		bp->libsym = libsym;
+		assert((*found)->libsym == NULL);
+		(*found)->libsym = libsym;
 #endif
 		return 0;
 	}
 
-	bp = malloc(sizeof(*bp));
+	struct breakpoint *bp = malloc(sizeof(*bp));
 	if (bp == NULL
 	    || breakpoint_init(bp, proc, bp_addr, libsym) < 0) {
 	fail:
@@ -922,9 +926,9 @@ proc_add_breakpoint(struct Process *proc, struct breakpoint *bp)
 	/* XXX We might merge bp->libsym instead of the following
 	 * assert, but that's not necessary right now.  Read the
 	 * comment in breakpoint_for_symbol.  */
-	assert(dict_find_entry(proc->breakpoints, bp->addr) == NULL);
+	assert(dict_find(proc->breakpoints, &bp->addr) == NULL);
 
-	if (dict_enter(proc->breakpoints, bp->addr, bp) < 0) {
+	if (DICT_INSERT(proc->breakpoints, &bp->addr, &bp) < 0) {
 		fprintf(stderr,
 			"couldn't enter breakpoint %s@%p to dictionary: %s\n",
 			breakpoint_name(bp), bp->addr, strerror(errno));
@@ -940,16 +944,13 @@ proc_remove_breakpoint(struct Process *proc, struct breakpoint *bp)
 	debug(DEBUG_FUNCTION, "proc_remove_breakpoint(pid=%d, %s@%p)",
 	      proc->pid, breakpoint_name(bp), bp->addr);
 	check_leader(proc);
-	struct breakpoint *removed = dict_remove(proc->breakpoints, bp->addr);
-	assert(removed == bp);
+	int rc = DICT_ERASE(proc->breakpoints, &bp->addr, struct breakpoint *,
+			    NULL, NULL, NULL);
+	assert(rc == 0);
 }
 
-/* Dict doesn't support iteration restarts, so here's this contraption
- * for now.  XXX add restarts to dict.  */
 struct each_breakpoint_data
 {
-	void *start;
-	void *end;
 	struct Process *proc;
 	enum callback_status (*cb)(struct Process *proc,
 				   struct breakpoint *bp,
@@ -957,25 +958,11 @@ struct each_breakpoint_data
 	void *cb_data;
 };
 
-static void
-each_breakpoint_cb(void *key, void *value, void *d)
+static enum callback_status
+each_breakpoint_cb(arch_addr_t *key, struct breakpoint **bpp, void *d)
 {
 	struct each_breakpoint_data *data = d;
-	if (data->end != NULL)
-		return;
-	if (data->start == key)
-		data->start = NULL;
-
-	if (data->start == NULL) {
-		switch (data->cb(data->proc, value, data->cb_data)) {
-		case CBS_FAIL:
-			/* XXX handle me */
-		case CBS_STOP:
-			data->end = key;
-		case CBS_CONT:
-			return;
-		}
-	}
+	return data->cb(data->proc, *bpp, data->cb_data);
 }
 
 void *
@@ -985,13 +972,13 @@ proc_each_breakpoint(struct Process *proc, void *start,
 						void *data), void *data)
 {
 	struct each_breakpoint_data dd = {
-		.start = start,
 		.proc = proc,
 		.cb = cb,
 		.cb_data = data,
 	};
-	dict_apply_to_all(proc->breakpoints, &each_breakpoint_cb, &dd);
-	return dd.end;
+	return DICT_EACH(proc->breakpoints,
+			 arch_addr_t, struct breakpoint *, start,
+			 &each_breakpoint_cb, &dd);
 }
 
 int
--- proc.h.ori
+++ proc.h
@@ -88,10 +88,8 @@ struct Process {
 
 	/* Dictionary of breakpoints (which is a mapping
 	 * address->breakpoint).  This is NULL for non-leader
-	 * processes.  XXX note that we store addresses (keys) by
-	 * value.  That assumes that arch_addr_t fits in host
-	 * pointer.  */
-	Dict * breakpoints;
+	 * processes.  */
+	struct dict *breakpoints;
 
 	int mask_32bit;           /* 1 if 64-bit ltrace is tracing 32-bit process */
 	unsigned int personality;
--- summary.c.ori
+++ summary.c
@@ -1,5 +1,6 @@
 /*
  * This file is part of ltrace.
+ * Copyright (C) 2012 Petr Machata, Red Hat Inc.
  * Copyright (C) 2003,2008,2009 Juan Cespedes
  * Copyright (C) 2006 Ian Wienand
  *
@@ -37,16 +38,15 @@ static struct entry_st {
 static int tot_count = 0;
 static unsigned long int tot_usecs = 0;
 
-static void fill_struct(void *key, void *value, void *data)
+static enum callback_status
+fill_struct(const char **namep, struct opt_c_struct *st, void *data)
 {
-	struct opt_c_struct *st = (struct opt_c_struct *)value;
-
 	entries = realloc(entries, (num_entries + 1) * sizeof(struct entry_st));
 	if (!entries) {
 		perror("realloc()");
 		exit(1);
 	}
-	entries[num_entries].name = (char *)key;
+	entries[num_entries].name = (char *)*namep;
 	entries[num_entries].count = st->count;
 	entries[num_entries].tv = st->tv;
 
@@ -55,6 +55,7 @@ static void fill_struct(void *key, void *value, void *data)
 	tot_usecs += st->tv.tv_usec;
 
 	num_entries++;
+	return CBS_CONT;
 }
 
 static int compar(const void *a, const void *b)
@@ -78,7 +79,8 @@ void show_summary(void)
 	num_entries = 0;
 	entries = NULL;
 
-	dict_apply_to_all(dict_opt_c, fill_struct, NULL);
+	DICT_EACH(dict_opt_c, const char *, struct opt_c_struct, NULL,
+		  fill_struct, NULL);
 
 	qsort(entries, num_entries, sizeof(*entries), compar);
 
--- sysdeps/linux-gnu/mipsel/trace.c.ori
+++ sysdeps/linux-gnu/mipsel/trace.c
@@ -275,7 +275,7 @@ arch_sw_singlestep(struct Process *proc, struct breakpoint *bp,
 	while (nr-- > 0) {
 		arch_addr_t baddr = (arch_addr_t) newpcs[nr];
 		/* Not sure what to do here. We've already got a bp?  */
-		if (dict_find_entry(proc->leader->breakpoints, baddr) != NULL) {
+		if (dict_find(proc->leader->breakpoints, &baddr) != NULL) {
 			fprintf(stderr, "skip %p %p\n", baddr, add_cb_data);
 			continue;
 		}
--- sysdeps/linux-gnu/trace.c.ori
+++ sysdeps/linux-gnu/trace.c
@@ -323,10 +323,11 @@ send_sigstop(struct Process *task, void *data)
 static void
 ugly_workaround(struct Process *proc)
 {
-	void *ip = get_instruction_pointer(proc);
-	struct breakpoint *sbp = dict_find_entry(proc->leader->breakpoints, ip);
-	if (sbp != NULL)
-		enable_breakpoint(proc, sbp);
+	arch_addr_t ip = get_instruction_pointer(proc);
+	struct breakpoint **found = DICT_FIND(proc->leader->breakpoints, &ip,
+					      struct breakpoint *);
+	if (found != NULL)
+		enable_breakpoint(proc, *found);
 	else
 		insert_breakpoint(proc, ip, NULL);
 	ptrace(PTRACE_CONT, proc->pid, 0, 0);
@@ -1035,7 +1036,7 @@ ltrace_exiting_install_handler(struct Process *proc)
 struct process_vfork_handler
 {
 	struct event_handler super;
-	void *bp_addr;
+	arch_addr_t bp_addr;
 };
 
 static Event *
@@ -1046,7 +1047,6 @@ process_vfork_on_event(struct event_handler *super, Event *event)
 	      event->proc->pid, event->type);
 
 	struct process_vfork_handler *self = (void *)super;
-	struct breakpoint *sbp;
 	assert(self != NULL);
 
 	switch (event->type) {
@@ -1062,10 +1062,12 @@ process_vfork_on_event(struct event_handler *super, Event *event)
 		/* Smuggle back in the vfork return breakpoint, so
 		 * that our parent can trip over it once again.  */
 		if (self->bp_addr != 0) {
-			sbp = dict_find_entry(event->proc->leader->breakpoints,
-					      self->bp_addr);
-			if (sbp != NULL)
-				assert(sbp->libsym == NULL);
+			struct breakpoint **found
+				= DICT_FIND(event->proc->leader->breakpoints,
+					    &self->bp_addr,
+					    struct breakpoint *);
+			if (found != NULL)
+				assert((*found)->libsym == NULL);
 			/* We don't mind failing that, it's not a big
 			 * deal to not display one extra vfork return.  */
 			insert_breakpoint(event->proc->parent,