Don't create PLT symbols that don't match filtering criteria

- instead of creating them, keeping them around, and then just not enabling
  breakpoints for them
diff --git a/filter.c b/filter.c
index 7f6921d..7a7bbe3 100644
--- a/filter.c
+++ b/filter.c
@@ -131,7 +131,7 @@
 		return re_match_or_error(&matcher->libname_re, lib->pathname,
 					 "library pathname");
 	case FLM_MAIN:
-		return lib->next == NULL;
+		return lib->type == LT_LIBTYPE_MAIN;
 	}
 	assert(matcher->type != matcher->type);
 	abort();
@@ -156,7 +156,8 @@
 }
 
 int
-filter_matches_symbol(struct filter *filt, struct library_symbol *sym)
+filter_matches_symbol(struct filter *filt,
+		      const char *sym_name, struct library *lib)
 {
 	if (filt == NULL)
 		return 0;
@@ -174,8 +175,8 @@
 				continue;
 		}
 
-		if (matcher_matches_library(it->lib_matcher, sym->lib)
-		    && re_match_or_error(&it->symbol_re, sym->name,
+		if (matcher_matches_library(it->lib_matcher, lib)
+		    && re_match_or_error(&it->symbol_re, sym_name,
 					 "symbol name"))
 			matches = !matches;
 	}
diff --git a/filter.h b/filter.h
index 0320036..65c575a 100644
--- a/filter.h
+++ b/filter.h
@@ -93,6 +93,7 @@
 
 /* Ask whether FILTER matches this symbol.  Returns 0 if it doesn't,
  * or non-0 value if it does.  */
-int filter_matches_symbol(struct filter *filt, struct library_symbol *sym);
+int filter_matches_symbol(struct filter *filt, const char *sym_name,
+			  struct library *lib);
 
 #endif /* FILTER_H */
diff --git a/handle_event.c b/handle_event.c
index 28992d4..c96b9e6 100644
--- a/handle_event.c
+++ b/handle_event.c
@@ -426,7 +426,7 @@
 		free(syscall);
 		return NULL;
 	}
-	library_init(syscalls);
+	library_init(syscalls, (enum library_type)-1);
 	library_set_soname(syscalls, "SYS", 0);
 	library_symbol_init(syscall, 0, name, 0, LS_TOPLT_NONE);
 	library_add_symbol(syscalls, syscall);
diff --git a/library.c b/library.c
index 4b7b002..5c80529 100644
--- a/library.c
+++ b/library.c
@@ -100,7 +100,7 @@
 }
 
 void
-library_init(struct library *lib)
+library_init(struct library *lib, enum library_type type)
 {
 	lib->next = NULL;
 	lib->soname = NULL;
@@ -108,6 +108,7 @@
 	lib->pathname = NULL;
 	lib->own_pathname = 0;
 	lib->symbols = NULL;
+	lib->type = type;
 }
 
 int
@@ -123,9 +124,9 @@
 		return -1;
 	}
 
-	library_init(retp);
-	library_set_soname(lib, soname, lib->own_soname);
-	library_set_soname(lib, pathname, lib->own_pathname);
+	library_init(retp, lib->type);
+	library_set_soname(retp, soname, lib->own_soname);
+	library_set_soname(retp, pathname, lib->own_pathname);
 
 	struct library_symbol *it;
 	struct library_symbol **nsymp = &retp->symbols;
diff --git a/library.h b/library.h
index befd344..60a4818 100644
--- a/library.h
+++ b/library.h
@@ -80,6 +80,12 @@
 enum callback_status library_symbol_equal_cb(struct library_symbol *libsym,
 					     void *standard);
 
+enum library_type {
+	LT_LIBTYPE_MAIN,
+	LT_LIBTYPE_DSO,
+	LT_LIBTYPE_DLOPEN,
+};
+
 /* XXX we might consider sharing libraries across processes.  Things
  * like libc will be opened by every single process, no point cloning
  * these everywhere.  But for now, keep the ownership structure
@@ -110,12 +116,14 @@
 	const char *soname;
 	const char *pathname;
 
+	enum library_type type;
+
 	char own_soname : 1;
 	char own_pathname : 1;
 };
 
 /* Init LIB.  */
-void library_init(struct library *lib);
+void library_init(struct library *lib, enum library_type type);
 
 /* Initialize RETP to a library identical to LIB.  Symbols are not
  * shared, but copied over.  Returns 0 on success and a negative value
diff --git a/ltrace-elf.c b/ltrace-elf.c
index bec85a0..5d01581 100644
--- a/ltrace-elf.c
+++ b/ltrace-elf.c
@@ -15,6 +15,7 @@
 #include "common.h"
 #include "proc.h"
 #include "library.h"
+#include "filter.h"
 
 #ifdef PLT_REINITALISATION_BP
 extern char *PLTs_initialized_by_here;
@@ -455,30 +456,77 @@
 	close(lte->fd);
 }
 
-struct library *
-ltelf_read_library(struct Process *proc, const char *filename, GElf_Addr bias)
+static int
+populate_plt(struct Process *proc, const char *filename,
+	     struct ltelf *lte, struct library *lib)
+{
+	size_t i;
+	for (i = 0; i < lte->relplt_count; ++i) {
+		GElf_Rel rel;
+		GElf_Rela rela;
+		GElf_Sym sym;
+		void *ret;
+
+		if (lte->relplt->d_type == ELF_T_REL) {
+			ret = gelf_getrel(lte->relplt, i, &rel);
+			rela.r_offset = rel.r_offset;
+			rela.r_info = rel.r_info;
+			rela.r_addend = 0;
+		} else {
+			ret = gelf_getrela(lte->relplt, i, &rela);
+		}
+
+		if (ret == NULL
+		    || ELF64_R_SYM(rela.r_info) >= lte->dynsym_count
+		    || gelf_getsym(lte->dynsym, ELF64_R_SYM(rela.r_info),
+				   &sym) == NULL)
+			error(EXIT_FAILURE, 0,
+			      "Couldn't get relocation from \"%s\"",
+			      filename);
+
+		char const *name = lte->dynstr + sym.st_name;
+
+		if (!filter_matches_symbol(options.plt_filter, name, lib))
+			continue;
+
+		fprintf(stderr, "%s@%s matches\n", name, lib->soname);
+
+		struct library_symbol *libsym;
+		switch (arch_elf_add_plt_entry(proc, lte, name,
+					       &rela, i, &libsym)) {
+		case plt_default:
+			if (default_elf_add_plt_entry(proc, lte, name,
+						      &rela, i, &libsym) < 0)
+		case plt_fail:
+				return -1;
+		case plt_ok:
+			if (libsym != NULL)
+				library_add_symbol(lib, libsym);
+		}
+	}
+	return 0;
+}
+
+int
+ltelf_read_library(struct library *lib, struct Process *proc,
+		   const char *filename, GElf_Addr bias)
 {
 	 // XXX we leak LTE contents
 	struct ltelf lte = {};
 	if (do_init_elf(&lte, filename, bias) < 0)
-		return NULL;
+		return -1;
 	proc->e_machine = lte.ehdr.e_machine;
 
-	struct library *lib = malloc(sizeof(*lib));
-	if (lib != NULL)
-		library_init(lib);
 	filename = strdup(filename);
 	char *soname = NULL;
 	int own_soname = 0;
+	int status = 0;
 	if (lib == NULL || filename == NULL) {
 	fail:
 		if (own_soname)
 			free(soname);
-		if (lib != NULL)
-			library_destroy(lib);
 		free((char *)filename);
-		free(lib);
-		lib = NULL;
+		status = -1;
 		goto done;
 	}
 
@@ -501,59 +549,33 @@
 	lib->entry = entry;
 	lib->dyn_addr = (target_address_t)lte.dyn_addr;
 
-	size_t i;
-	for (i = 0; i < lte.relplt_count; ++i) {
-		GElf_Rel rel;
-		GElf_Rela rela;
-		GElf_Sym sym;
-		void *ret;
-
-		if (lte.relplt->d_type == ELF_T_REL) {
-			ret = gelf_getrel(lte.relplt, i, &rel);
-			rela.r_offset = rel.r_offset;
-			rela.r_info = rel.r_info;
-			rela.r_addend = 0;
-		} else {
-			ret = gelf_getrela(lte.relplt, i, &rela);
-		}
-
-		if (ret == NULL
-		    || ELF64_R_SYM(rela.r_info) >= lte.dynsym_count
-		    || gelf_getsym(lte.dynsym, ELF64_R_SYM(rela.r_info),
-				   &sym) == NULL)
-			error(EXIT_FAILURE, 0,
-			      "Couldn't get relocation from \"%s\"",
-			      filename);
-
-		char const *name = lte.dynstr + sym.st_name;
-		struct library_symbol *libsym;
-		switch (arch_elf_add_plt_entry(proc, &lte, name,
-					       &rela, i, &libsym)) {
-		case plt_default:
-			if (default_elf_add_plt_entry(proc, &lte, name,
-						      &rela, i, &libsym) < 0)
-		case plt_fail:
-				goto fail;
-		case plt_ok:
-			if (libsym != NULL)
-				library_add_symbol(lib, libsym);
-		}
-	}
+	if (filter_matches_library(options.plt_filter, lib)
+	    && populate_plt(proc, filename, &lte, lib) < 0)
+		goto fail;
 
 done:
 	do_close_elf(&lte);
-	return lib;
+	return status;
 }
 
 struct library *
 ltelf_read_main_binary(struct Process *proc, const char *path)
 {
+	struct library *lib = malloc(sizeof(*lib));
+	if (lib == NULL)
+		return NULL;
+	library_init(lib, LT_LIBTYPE_MAIN);
+
 	fprintf(stderr, "ltelf_read_main_binary %d %s\n", proc->pid, path);
 	char *fname = pid2name(proc->pid);
-	struct library *lib = ltelf_read_library(proc, fname, 0);
-	if (lib != NULL) {
-		library_set_pathname(lib, path, 0);
-		library_set_soname(lib, rindex(path, '/') + 1, 0);
+	if (ltelf_read_library(lib, proc, fname, 0) < 0) {
+		library_destroy(lib);
+		free(lib);
+		return NULL;
 	}
+
+	library_set_pathname(lib, path, 0);
+	library_set_soname(lib, rindex(path, '/') + 1, 0);
+
 	return lib;
 }
diff --git a/ltrace-elf.h b/ltrace-elf.h
index de5538a..4a27598 100644
--- a/ltrace-elf.h
+++ b/ltrace-elf.h
@@ -52,8 +52,8 @@
 /* XXX is it possible to put breakpoints in VDSO and VSYSCALL
  * pseudo-libraries?  For now we assume that all libraries can be
  * opened via a filesystem.  BASE is ignored for ET_EXEC files.  */
-struct library *ltelf_read_library(struct Process *proc,
-				   const char *filename, GElf_Addr bias);
+int ltelf_read_library(struct library *lib, struct Process *proc,
+		       const char *filename, GElf_Addr bias);
 
 /* Create a library object representing the main binary.  The entry
  * point address is stored to *ENTRYP.  */
diff --git a/options.c b/options.c
index 5651048..d4e16a8 100644
--- a/options.c
+++ b/options.c
@@ -392,14 +392,14 @@
 }
 
 static void
-parse_filter_chain(struct options_t *options, const char *expr)
+parse_filter_chain(const char *expr, struct filter **retp)
 {
 	char *str = strdup(expr);
 	if (str == NULL) {
 		error(0, errno, "filter '%s' will be ignored", expr);
 		return;
 	}
-	options->filter = recursive_parse_chain(str);
+	*retp = recursive_parse_chain(str);
 }
 
 char **
@@ -481,7 +481,7 @@
 			break;
 
 		case 'e':
-			parse_filter_chain(&options, optarg);
+			parse_filter_chain(optarg, &options.plt_filter);
 			break;
 
 		case 'f':
@@ -640,8 +640,8 @@
 	/* Set default filter.  Use @MAIN for now, as that's what
 	 * ltrace used to have in the past.  XXX Maybe we should make
 	 * this "*" instead.  */
-	if (options.filter == NULL) {
-		parse_filter_chain(&options, "@MAIN");
+	if (options.plt_filter == NULL) {
+		parse_filter_chain("@MAIN", &options.plt_filter);
 		options.hide_caller = 1;
 	}
 
diff --git a/options.h b/options.h
index 9cd0bfc..82b13c8 100644
--- a/options.h
+++ b/options.h
@@ -21,7 +21,7 @@
 #if defined(HAVE_LIBUNWIND)
 	int bt_depth;	 /* how may levels of stack frames to show */
 #endif /* defined(HAVE_LIBUNWIND) */
-	struct filter *filter;
+	struct filter *plt_filter;
 	int hide_caller; /* Whether caller library should be hidden.  */
 };
 extern struct options_t options;
diff --git a/proc.c b/proc.c
index eb97f05..01689bb 100644
--- a/proc.c
+++ b/proc.c
@@ -16,7 +16,6 @@
 #include "common.h"
 #include "breakpoint.h"
 #include "proc.h"
-#include "filter.h"
 
 static int
 process_bare_init(struct Process *proc, const char *filename, pid_t pid)
@@ -487,9 +486,6 @@
 {
 	struct Process *proc = data;
 
-	if (!filter_matches_symbol(options.filter, libsym))
-		return CBS_CONT;
-
 	struct breakpoint *bp = malloc(sizeof(*bp));
 	if (bp == NULL
 	    || breakpoint_init(bp, proc, libsym->enter_addr, libsym) < 0) {
@@ -523,9 +519,6 @@
 	fprintf(stderr, "=== Added library %s@%p (%s) to %d:\n",
 		lib->soname, lib->base, lib->pathname, proc->pid);
 
-	if (!filter_matches_library(options.filter, lib))
-		return;
-
 	struct library_symbol *libsym = NULL;
 	while ((libsym = library_each_symbol(lib, libsym, breakpoint_for_symbol,
 					     proc)) != NULL)
diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c
index 95a67e7..6002a1c 100644
--- a/sysdeps/linux-gnu/proc.c
+++ b/sysdeps/linux-gnu/proc.c
@@ -459,13 +459,21 @@
 
 		fprintf(stderr, "DSO addr=%#lx, name='%s'\n",
 			rlm.l_addr, lib_name);
-		struct library *lib
-			= ltelf_read_library(proc, lib_name, rlm.l_addr);
+
+		struct library *lib = malloc(sizeof(*lib));
 		if (lib == NULL) {
+		fail:
+			if (lib != NULL)
+				library_destroy(lib);
 			error(0, errno, "Couldn't load ELF object %s\n",
 			      lib_name);
 			continue;
 		}
+		library_init(lib, LT_LIBTYPE_DSO);
+
+		if (ltelf_read_library(lib, proc, lib_name, rlm.l_addr) < 0)
+			goto fail;
+
 		lib->key = key;
 		proc_add_library(proc, lib);
 	}