breakpoints.c revision ce377d567ccc0b14693619b69ebe0ac6deb0ba90
1d44c6b8b090b8b7aa9d971d9e0bfd848732a3071Juan Cespedes#if HAVE_CONFIG_H
2d44c6b8b090b8b7aa9d971d9e0bfd848732a3071Juan Cespedes#include "config.h"
3d44c6b8b090b8b7aa9d971d9e0bfd848732a3071Juan Cespedes#endif
4d44c6b8b090b8b7aa9d971d9e0bfd848732a3071Juan Cespedes
55b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes#include <stdlib.h>
67186e2af704f4458e6383e8a92482594db29b597Juan Cespedes#include <string.h>
75b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes#include <assert.h>
85b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes
9f1bfe203f5f1c0e11a614f9d593a68406f5cb47eJuan Cespedes#ifdef __powerpc__
10f1bfe203f5f1c0e11a614f9d593a68406f5cb47eJuan Cespedes#include <sys/ptrace.h>
11f1bfe203f5f1c0e11a614f9d593a68406f5cb47eJuan Cespedes#endif
12f1bfe203f5f1c0e11a614f9d593a68406f5cb47eJuan Cespedes
13cac15c3f170b5ec2cc6304c8c0763a78103e1778Juan Cespedes#include "ltrace.h"
14cac15c3f170b5ec2cc6304c8c0763a78103e1778Juan Cespedes#include "options.h"
15cac15c3f170b5ec2cc6304c8c0763a78103e1778Juan Cespedes#include "debug.h"
16cac15c3f170b5ec2cc6304c8c0763a78103e1778Juan Cespedes#include "dict.h"
177186e2af704f4458e6383e8a92482594db29b597Juan Cespedes#include "elf.h"
185b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes
195b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes/*****************************************************************************/
205b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes
21f13505251e6402460f6cc7ec84e0d8ca91607b4fJuan Cespedesstruct breakpoint *
22f13505251e6402460f6cc7ec84e0d8ca91607b4fJuan Cespedesaddress2bpstruct(struct process *proc, void *addr) {
23cac15c3f170b5ec2cc6304c8c0763a78103e1778Juan Cespedes	return dict_find_entry(proc->breakpoints, addr);
245b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes}
255b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes
269a2ad351a1c3215dc596ff3e2e3fd4bc24445a6bIan Wienandvoid
272d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienandinsert_breakpoint(struct process *proc, void *addr,
28f13505251e6402460f6cc7ec84e0d8ca91607b4fJuan Cespedes		  struct library_symbol *libsym) {
292d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand	struct breakpoint *sbp;
30b3f8fef12fccb0914b7b28725f42192c279d31c9Petr Machata	debug(1, "symbol=%s, addr=%p", libsym?libsym->name:"(nil)", addr);
315b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes
322d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand	if (!addr)
332d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand		return;
349a2ad351a1c3215dc596ff3e2e3fd4bc24445a6bIan Wienand
352d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand	if (libsym)
369a2ad351a1c3215dc596ff3e2e3fd4bc24445a6bIan Wienand		libsym->needs_init = 0;
379a2ad351a1c3215dc596ff3e2e3fd4bc24445a6bIan Wienand
38cac15c3f170b5ec2cc6304c8c0763a78103e1778Juan Cespedes	sbp = dict_find_entry(proc->breakpoints, addr);
39cac15c3f170b5ec2cc6304c8c0763a78103e1778Juan Cespedes	if (!sbp) {
409a2ad351a1c3215dc596ff3e2e3fd4bc24445a6bIan Wienand		sbp = calloc(1, sizeof(struct breakpoint));
41cac15c3f170b5ec2cc6304c8c0763a78103e1778Juan Cespedes		if (!sbp) {
422d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand			return;	/* TODO FIXME XXX: error_mem */
43cac15c3f170b5ec2cc6304c8c0763a78103e1778Juan Cespedes		}
44cac15c3f170b5ec2cc6304c8c0763a78103e1778Juan Cespedes		dict_enter(proc->breakpoints, addr, sbp);
45cac15c3f170b5ec2cc6304c8c0763a78103e1778Juan Cespedes		sbp->addr = addr;
462d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand		sbp->libsym = libsym;
472d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand		if (libsym)
482d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand			libsym->brkpnt = sbp;
495b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes	}
5063184be8c577f5799e44db2a4e312a8240ad7751Juan Cespedes#ifdef __arm__
5163184be8c577f5799e44db2a4e312a8240ad7751Juan Cespedes	sbp->thumb_mode = proc->thumb_mode;
5263184be8c577f5799e44db2a4e312a8240ad7751Juan Cespedes	proc->thumb_mode = 0;
5363184be8c577f5799e44db2a4e312a8240ad7751Juan Cespedes#endif
545b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes	sbp->enabled++;
552d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand	if (sbp->enabled == 1 && proc->pid)
562d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand		enable_breakpoint(proc->pid, sbp);
575b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes}
585b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes
59f13505251e6402460f6cc7ec84e0d8ca91607b4fJuan Cespedesvoid
60f13505251e6402460f6cc7ec84e0d8ca91607b4fJuan Cespedesdelete_breakpoint(struct process *proc, void *addr) {
612d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand	struct breakpoint *sbp = dict_find_entry(proc->breakpoints, addr);
622d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand	assert(sbp);		/* FIXME: remove after debugging has been done. */
635b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes	/* This should only happen on out-of-memory conditions. */
642d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand	if (sbp == NULL)
652d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand		return;
665b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes
675b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes	sbp->enabled--;
682d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand	if (sbp->enabled == 0)
692d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand		disable_breakpoint(proc->pid, sbp);
705b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes	assert(sbp->enabled >= 0);
715b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes}
725b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes
73f13505251e6402460f6cc7ec84e0d8ca91607b4fJuan Cespedesstatic void
74f13505251e6402460f6cc7ec84e0d8ca91607b4fJuan Cespedesenable_bp_cb(void *addr, void *sbp, void *proc) {
75cac15c3f170b5ec2cc6304c8c0763a78103e1778Juan Cespedes	if (((struct breakpoint *)sbp)->enabled) {
76cac15c3f170b5ec2cc6304c8c0763a78103e1778Juan Cespedes		enable_breakpoint(((struct process *)proc)->pid, sbp);
77cac15c3f170b5ec2cc6304c8c0763a78103e1778Juan Cespedes	}
785b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes}
795b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes
80f13505251e6402460f6cc7ec84e0d8ca91607b4fJuan Cespedesvoid
81f13505251e6402460f6cc7ec84e0d8ca91607b4fJuan Cespedesenable_all_breakpoints(struct process *proc) {
825e01f654d83a95f2acffa86df57a4c2db9b0cae9Juan Cespedes	if (proc->breakpoints_enabled <= 0) {
83f1bfe203f5f1c0e11a614f9d593a68406f5cb47eJuan Cespedes#ifdef __powerpc__
84f1bfe203f5f1c0e11a614f9d593a68406f5cb47eJuan Cespedes		unsigned long a;
85f1bfe203f5f1c0e11a614f9d593a68406f5cb47eJuan Cespedes
86f1bfe203f5f1c0e11a614f9d593a68406f5cb47eJuan Cespedes		/*
87f1bfe203f5f1c0e11a614f9d593a68406f5cb47eJuan Cespedes		 * PPC HACK! (XXX FIXME TODO)
88f1bfe203f5f1c0e11a614f9d593a68406f5cb47eJuan Cespedes		 * If the dynamic linker hasn't populated the PLT then
89f1bfe203f5f1c0e11a614f9d593a68406f5cb47eJuan Cespedes		 * dont enable the breakpoints
90f1bfe203f5f1c0e11a614f9d593a68406f5cb47eJuan Cespedes		 */
91ce377d567ccc0b14693619b69ebe0ac6deb0ba90Juan Cespedes		if (options.libcalls) {
922d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand			a = ptrace(PTRACE_PEEKTEXT, proc->pid,
9376c61f15d7989bf7adffed2e46a44c34a80bd927Paul Gilliam				   sym2addr(proc, proc->list_of_symbols),
942d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand				   0);
95de5a7eb873c05a698e4267b554e25124dc92e7f4Juan Cespedes			if (a == 0x0)
96de5a7eb873c05a698e4267b554e25124dc92e7f4Juan Cespedes				return;
97de5a7eb873c05a698e4267b554e25124dc92e7f4Juan Cespedes		}
98f1bfe203f5f1c0e11a614f9d593a68406f5cb47eJuan Cespedes#endif
99f1bfe203f5f1c0e11a614f9d593a68406f5cb47eJuan Cespedes
100cac15c3f170b5ec2cc6304c8c0763a78103e1778Juan Cespedes		debug(1, "Enabling breakpoints for pid %u...", proc->pid);
101a0ccf39a68c0fcdf2165bde0f9b70ed12fc61cd8Juan Cespedes		if (proc->breakpoints) {
1022d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand			dict_apply_to_all(proc->breakpoints, enable_bp_cb,
1032d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand					  proc);
104a0ccf39a68c0fcdf2165bde0f9b70ed12fc61cd8Juan Cespedes		}
1051228a91e6560c5e5ac4fdd051adc9b34bf9fc047Eric Vaitl#ifdef __mips__
106a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes		{
107a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes			// I'm sure there is a nicer way to do this. We need to
108a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes			// insert breakpoints _after_ the child has been started.
109a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes			struct library_symbol *sym;
110a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes			struct library_symbol *new_sym;
111a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes			sym=proc->list_of_symbols;
112a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes			while(sym){
113a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes				void *addr= sym2addr(proc,sym);
114a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes				if(!addr){
115a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes					sym=sym->next;
116a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes					continue;
117a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes				}
118a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes				if(dict_find_entry(proc->breakpoints,addr)){
119a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes					sym=sym->next;
120a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes					continue;
121a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes				}
122a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes				debug(2,"inserting bp %p %s",addr,sym->name);
123a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes				new_sym=malloc(sizeof(*new_sym));
124a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes				memcpy(new_sym,sym,sizeof(*new_sym));
125a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes				new_sym->next=proc->list_of_symbols;
126a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes				proc->list_of_symbols=new_sym;
127a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes				new_sym->brkpnt=0;
128a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes				insert_breakpoint(proc, addr, new_sym);
129a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes				sym=sym->next;
130a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes			}
131a413e5b8880de643a83ad124d078091c0956fe1dJuan Cespedes		}
1321228a91e6560c5e5ac4fdd051adc9b34bf9fc047Eric Vaitl#endif
1335e01f654d83a95f2acffa86df57a4c2db9b0cae9Juan Cespedes	}
1345e01f654d83a95f2acffa86df57a4c2db9b0cae9Juan Cespedes	proc->breakpoints_enabled = 1;
1355e01f654d83a95f2acffa86df57a4c2db9b0cae9Juan Cespedes}
1365e01f654d83a95f2acffa86df57a4c2db9b0cae9Juan Cespedes
137f13505251e6402460f6cc7ec84e0d8ca91607b4fJuan Cespedesstatic void
138f13505251e6402460f6cc7ec84e0d8ca91607b4fJuan Cespedesdisable_bp_cb(void *addr, void *sbp, void *proc) {
139cac15c3f170b5ec2cc6304c8c0763a78103e1778Juan Cespedes	if (((struct breakpoint *)sbp)->enabled) {
140cac15c3f170b5ec2cc6304c8c0763a78103e1778Juan Cespedes		disable_breakpoint(((struct process *)proc)->pid, sbp);
141cac15c3f170b5ec2cc6304c8c0763a78103e1778Juan Cespedes	}
1425b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes}
1435b3ffdf2e696273d38434ff7b3c26349fff5a0eaJuan Cespedes
144f13505251e6402460f6cc7ec84e0d8ca91607b4fJuan Cespedesvoid
145f13505251e6402460f6cc7ec84e0d8ca91607b4fJuan Cespedesdisable_all_breakpoints(struct process *proc) {
1465e01f654d83a95f2acffa86df57a4c2db9b0cae9Juan Cespedes	if (proc->breakpoints_enabled) {
147cac15c3f170b5ec2cc6304c8c0763a78103e1778Juan Cespedes		debug(1, "Disabling breakpoints for pid %u...", proc->pid);
148cac15c3f170b5ec2cc6304c8c0763a78103e1778Juan Cespedes		dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc);
1495e01f654d83a95f2acffa86df57a4c2db9b0cae9Juan Cespedes	}
1505e01f654d83a95f2acffa86df57a4c2db9b0cae9Juan Cespedes	proc->breakpoints_enabled = 0;
1515e01f654d83a95f2acffa86df57a4c2db9b0cae9Juan Cespedes}
1527186e2af704f4458e6383e8a92482594db29b597Juan Cespedes
153f13505251e6402460f6cc7ec84e0d8ca91607b4fJuan Cespedesstatic void
154f13505251e6402460f6cc7ec84e0d8ca91607b4fJuan Cespedesfree_bp_cb(void *addr, void *sbp, void *data) {
1557186e2af704f4458e6383e8a92482594db29b597Juan Cespedes	assert(sbp);
1567186e2af704f4458e6383e8a92482594db29b597Juan Cespedes	free(sbp);
1577186e2af704f4458e6383e8a92482594db29b597Juan Cespedes}
1587186e2af704f4458e6383e8a92482594db29b597Juan Cespedes
159f13505251e6402460f6cc7ec84e0d8ca91607b4fJuan Cespedesvoid
160f13505251e6402460f6cc7ec84e0d8ca91607b4fJuan Cespedesbreakpoints_init(struct process *proc) {
1612d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand	struct library_symbol *sym;
1627186e2af704f4458e6383e8a92482594db29b597Juan Cespedes
1632d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand	if (proc->breakpoints) {	/* let's remove that struct */
1647186e2af704f4458e6383e8a92482594db29b597Juan Cespedes		dict_apply_to_all(proc->breakpoints, free_bp_cb, NULL);
1657186e2af704f4458e6383e8a92482594db29b597Juan Cespedes		dict_clear(proc->breakpoints);
1667186e2af704f4458e6383e8a92482594db29b597Juan Cespedes		proc->breakpoints = NULL;
1677186e2af704f4458e6383e8a92482594db29b597Juan Cespedes	}
16889a536028ab3b699b7f953b6b0fd7607917fc303Petr Machata	proc->breakpoints = dict_init(dict_key2hash_int, dict_key_cmp_int);
1697186e2af704f4458e6383e8a92482594db29b597Juan Cespedes
170ce377d567ccc0b14693619b69ebe0ac6deb0ba90Juan Cespedes	if (options.libcalls && proc->filename) {
1719a2ad351a1c3215dc596ff3e2e3fd4bc24445a6bIan Wienand		proc->list_of_symbols = read_elf(proc);
1727186e2af704f4458e6383e8a92482594db29b597Juan Cespedes		if (opt_e) {
1732d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand			struct library_symbol **tmp1 = &(proc->list_of_symbols);
1742d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand			while (*tmp1) {
1752d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand				struct opt_e_t *tmp2 = opt_e;
1767186e2af704f4458e6383e8a92482594db29b597Juan Cespedes				int keep = !opt_e_enable;
1777186e2af704f4458e6383e8a92482594db29b597Juan Cespedes
1782d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand				while (tmp2) {
1797186e2af704f4458e6383e8a92482594db29b597Juan Cespedes					if (!strcmp((*tmp1)->name, tmp2->name)) {
1807186e2af704f4458e6383e8a92482594db29b597Juan Cespedes						keep = opt_e_enable;
1817186e2af704f4458e6383e8a92482594db29b597Juan Cespedes					}
1827186e2af704f4458e6383e8a92482594db29b597Juan Cespedes					tmp2 = tmp2->next;
1837186e2af704f4458e6383e8a92482594db29b597Juan Cespedes				}
1847186e2af704f4458e6383e8a92482594db29b597Juan Cespedes				if (!keep) {
1857186e2af704f4458e6383e8a92482594db29b597Juan Cespedes					*tmp1 = (*tmp1)->next;
1867186e2af704f4458e6383e8a92482594db29b597Juan Cespedes				} else {
1877186e2af704f4458e6383e8a92482594db29b597Juan Cespedes					tmp1 = &((*tmp1)->next);
1887186e2af704f4458e6383e8a92482594db29b597Juan Cespedes				}
1897186e2af704f4458e6383e8a92482594db29b597Juan Cespedes			}
1907186e2af704f4458e6383e8a92482594db29b597Juan Cespedes		}
1917186e2af704f4458e6383e8a92482594db29b597Juan Cespedes	} else {
1927186e2af704f4458e6383e8a92482594db29b597Juan Cespedes		proc->list_of_symbols = NULL;
1937186e2af704f4458e6383e8a92482594db29b597Juan Cespedes	}
194b3f8fef12fccb0914b7b28725f42192c279d31c9Petr Machata	for (sym = proc->list_of_symbols; sym; sym = sym->next) {
1952d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand		/* proc->pid==0 delays enabling. */
19676c61f15d7989bf7adffed2e46a44c34a80bd927Paul Gilliam		insert_breakpoint(proc, sym2addr(proc, sym), sym);
1977186e2af704f4458e6383e8a92482594db29b597Juan Cespedes	}
1987186e2af704f4458e6383e8a92482594db29b597Juan Cespedes	proc->callstack_depth = 0;
1997186e2af704f4458e6383e8a92482594db29b597Juan Cespedes	proc->breakpoints_enabled = -1;
2007186e2af704f4458e6383e8a92482594db29b597Juan Cespedes}
2019a2ad351a1c3215dc596ff3e2e3fd4bc24445a6bIan Wienand
202f13505251e6402460f6cc7ec84e0d8ca91607b4fJuan Cespedesvoid
203f13505251e6402460f6cc7ec84e0d8ca91607b4fJuan Cespedesreinitialize_breakpoints(struct process *proc) {
2042d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand	struct library_symbol *sym = proc->list_of_symbols;
2052d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand
2062d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand	while (sym) {
2079a2ad351a1c3215dc596ff3e2e3fd4bc24445a6bIan Wienand		if (sym->needs_init) {
20876c61f15d7989bf7adffed2e46a44c34a80bd927Paul Gilliam			insert_breakpoint(proc, sym2addr(proc, sym),
2092d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand					  sym);
2102d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand			if (sym->needs_init && !sym->is_weak) {
2112d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand				fprintf(stderr,
2122d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand					"could not re-initialize breakpoint for \"%s\" in file \"%s\"\n",
2132d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand					sym->name, proc->filename);
2149a2ad351a1c3215dc596ff3e2e3fd4bc24445a6bIan Wienand				exit(1);
2152d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand			}
2162d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand		}
2179a2ad351a1c3215dc596ff3e2e3fd4bc24445a6bIan Wienand		sym = sym->next;
2182d45b1a8e26a36a9f85dc49e721c4390ca93dc40Ian Wienand	}
2199a2ad351a1c3215dc596ff3e2e3fd4bc24445a6bIan Wienand}
220