link_elf.c revision 38514
138514Sdfr/*-
238514Sdfr * Copyright (c) 1998 Doug Rabson
338514Sdfr * All rights reserved.
438514Sdfr *
538514Sdfr * Redistribution and use in source and binary forms, with or without
638514Sdfr * modification, are permitted provided that the following conditions
738514Sdfr * are met:
838514Sdfr * 1. Redistributions of source code must retain the above copyright
938514Sdfr *    notice, this list of conditions and the following disclaimer.
1038514Sdfr * 2. Redistributions in binary form must reproduce the above copyright
1138514Sdfr *    notice, this list of conditions and the following disclaimer in the
1238514Sdfr *    documentation and/or other materials provided with the distribution.
1338514Sdfr *
1438514Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1538514Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1638514Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1738514Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1838514Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1938514Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2038514Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2138514Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2238514Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2338514Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2438514Sdfr * SUCH DAMAGE.
2538514Sdfr *
2638514Sdfr *	$Id$
2738514Sdfr */
2838514Sdfr
2938514Sdfr#include <sys/param.h>
3038514Sdfr#include <sys/kernel.h>
3138514Sdfr#include <sys/systm.h>
3238514Sdfr#include <sys/malloc.h>
3338514Sdfr#include <sys/proc.h>
3438514Sdfr#include <sys/namei.h>
3538514Sdfr#include <sys/fcntl.h>
3638514Sdfr#include <sys/vnode.h>
3738514Sdfr#include <sys/linker.h>
3838514Sdfr#include <machine/elf.h>
3938514Sdfr
4038514Sdfrstatic int	link_elf_load_file(const char*, linker_file_t*);
4138514Sdfrstatic int	link_elf_lookup_symbol(linker_file_t, const char*,
4238514Sdfr				       linker_sym_t*);
4338514Sdfrstatic void	link_elf_symbol_values(linker_file_t, linker_sym_t, linker_symval_t*);
4438514Sdfrstatic int	link_elf_search_symbol(linker_file_t, caddr_t value,
4538514Sdfr				       linker_sym_t* sym, long* diffp);
4638514Sdfr
4738514Sdfrstatic void	link_elf_unload(linker_file_t);
4838514Sdfr
4938514Sdfr/*
5038514Sdfr * The file representing the currently running kernel.  This contains
5138514Sdfr * the global symbol table.
5238514Sdfr */
5338514Sdfr
5438514Sdfrlinker_file_t linker_kernel_file;
5538514Sdfr
5638514Sdfrstatic struct linker_class_ops link_elf_class_ops = {
5738514Sdfr    link_elf_load_file,
5838514Sdfr};
5938514Sdfr
6038514Sdfrstatic struct linker_file_ops link_elf_file_ops = {
6138514Sdfr    link_elf_lookup_symbol,
6238514Sdfr    link_elf_symbol_values,
6338514Sdfr    link_elf_search_symbol,
6438514Sdfr    link_elf_unload,
6538514Sdfr};
6638514Sdfr
6738514Sdfrtypedef struct elf_file {
6838514Sdfr    caddr_t		address;	/* Load address */
6938514Sdfr    Elf_Dyn*		dynamic;	/* Symbol table etc. */
7038514Sdfr    Elf_Off		nbuckets;	/* DT_HASH info */
7138514Sdfr    Elf_Off		nchains;
7238514Sdfr    const Elf_Off*	buckets;
7338514Sdfr    const Elf_Off*	chains;
7438514Sdfr    caddr_t		hash;
7538514Sdfr    caddr_t		strtab;		/* DT_STRTAB */
7638514Sdfr    Elf_Sym*		symtab;		/* DT_SYMTAB */
7738514Sdfr} *elf_file_t;
7838514Sdfr
7938514Sdfrstatic int		parse_dynamic(linker_file_t lf);
8038514Sdfrstatic int		load_dependancies(linker_file_t lf);
8138514Sdfrstatic int		relocate_file(linker_file_t lf);
8238514Sdfr
8338514Sdfr/*
8438514Sdfr * The kernel symbol table starts here.
8538514Sdfr */
8638514Sdfrextern struct _dynamic _DYNAMIC;
8738514Sdfr
8838514Sdfrstatic void
8938514Sdfrlink_elf_init(void* arg)
9038514Sdfr{
9138514Sdfr    Elf_Dyn* dp = (Elf_Dyn*) &_DYNAMIC;
9238514Sdfr
9338514Sdfr#if ELF_TARG_CLASS == ELFCLASS32
9438514Sdfr    linker_add_class("elf32", NULL, &link_elf_class_ops);
9538514Sdfr#else
9638514Sdfr    linker_add_class("elf64", NULL, &link_elf_class_ops);
9738514Sdfr#endif
9838514Sdfr
9938514Sdfr    if (dp) {
10038514Sdfr	elf_file_t ef;
10138514Sdfr
10238514Sdfr	ef = malloc(sizeof(struct elf_file), M_LINKER, M_NOWAIT);
10338514Sdfr	if (ef == NULL)
10438514Sdfr	    panic("link_elf_init: Can't create linker structures for kernel");
10538514Sdfr
10638514Sdfr	ef->address = 0;
10738514Sdfr	ef->dynamic = dp;
10838514Sdfr	linker_kernel_file =
10938514Sdfr	    linker_make_file(kernelname, ef, &link_elf_file_ops);
11038514Sdfr	if (linker_kernel_file == NULL)
11138514Sdfr	    panic("link_elf_init: Can't create linker structures for kernel");
11238514Sdfr	parse_dynamic(linker_kernel_file);
11338514Sdfr	/*
11438514Sdfr	 * XXX there must be a better way of getting these constants.
11538514Sdfr	 */
11638514Sdfr#ifdef __alpha__
11738514Sdfr	linker_kernel_file->address = (caddr_t) 0xfffffc0000300000;
11838514Sdfr#else
11938514Sdfr	linker_kernel_file->address = (caddr_t) 0xf0100000;
12038514Sdfr#endif
12138514Sdfr	linker_kernel_file->size = -(long)linker_kernel_file->address;
12238514Sdfr	linker_current_file = linker_kernel_file;
12338514Sdfr    }
12438514Sdfr}
12538514Sdfr
12638514SdfrSYSINIT(link_elf, SI_SUB_KMEM, SI_ORDER_THIRD, link_elf_init, 0);
12738514Sdfr
12838514Sdfrstatic int
12938514Sdfrparse_dynamic(linker_file_t lf)
13038514Sdfr{
13138514Sdfr    elf_file_t ef = lf->priv;
13238514Sdfr    Elf_Dyn *dp;
13338514Sdfr
13438514Sdfr    for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) {
13538514Sdfr	switch (dp->d_tag) {
13638514Sdfr	case DT_HASH:
13738514Sdfr	{
13838514Sdfr	    /* From src/libexec/rtld-elf/rtld.c */
13938514Sdfr	    const Elf_Off *hashtab = (const Elf_Off *)
14038514Sdfr		(ef->address + dp->d_un.d_ptr);
14138514Sdfr	    ef->nbuckets = hashtab[0];
14238514Sdfr	    ef->nchains = hashtab[1];
14338514Sdfr	    ef->buckets = hashtab + 2;
14438514Sdfr	    ef->chains = ef->buckets + ef->nbuckets;
14538514Sdfr	    break;
14638514Sdfr	}
14738514Sdfr	case DT_STRTAB:
14838514Sdfr	    ef->strtab = (caddr_t) dp->d_un.d_ptr;
14938514Sdfr	    break;
15038514Sdfr	case DT_SYMTAB:
15138514Sdfr	    ef->symtab = (Elf_Sym*) dp->d_un.d_ptr;
15238514Sdfr	    break;
15338514Sdfr	case DT_SYMENT:
15438514Sdfr	    if (dp->d_un.d_val != sizeof(Elf_Sym))
15538514Sdfr		return ENOEXEC;
15638514Sdfr	}
15738514Sdfr    }
15838514Sdfr    return 0;
15938514Sdfr}
16038514Sdfr
16138514Sdfrstatic int
16238514Sdfrlink_elf_load_file(const char* filename, linker_file_t* result)
16338514Sdfr{
16438514Sdfr#if 0
16538514Sdfr    struct nameidata nd;
16638514Sdfr    struct proc* p = curproc;	/* XXX */
16738514Sdfr    int error = 0;
16838514Sdfr    int resid;
16938514Sdfr    struct exec header;
17038514Sdfr    elf_file_t ef;
17138514Sdfr    linker_file_t lf;
17238514Sdfr
17338514Sdfr    NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, p);
17438514Sdfr    error = vn_open(&nd, FREAD, 0);
17538514Sdfr    if (error)
17638514Sdfr	return error;
17738514Sdfr
17838514Sdfr    /*
17938514Sdfr     * Read the a.out header from the file.
18038514Sdfr     */
18138514Sdfr    error = vn_rdwr(UIO_READ, nd.ni_vp, (void*) &header, sizeof header, 0,
18238514Sdfr		    UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p);
18338514Sdfr    if (error)
18438514Sdfr	goto out;
18538514Sdfr
18638514Sdfr    if (N_BADMAG(header) || !(N_GETFLAG(header) & EX_DYNAMIC))
18738514Sdfr	goto out;
18838514Sdfr
18938514Sdfr    /*
19038514Sdfr     * We have an a.out file, so make some space to read it in.
19138514Sdfr     */
19238514Sdfr    ef = malloc(sizeof(struct elf_file), M_LINKER, M_WAITOK);
19338514Sdfr    ef->address = malloc(header.a_text + header.a_data + header.a_bss,
19438514Sdfr			 M_LINKER, M_WAITOK);
19538514Sdfr
19638514Sdfr    /*
19738514Sdfr     * Read the text and data sections and zero the bss.
19838514Sdfr     */
19938514Sdfr    error = vn_rdwr(UIO_READ, nd.ni_vp, (void*) ef->address,
20038514Sdfr		    header.a_text + header.a_data, 0,
20138514Sdfr		    UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &resid, p);
20238514Sdfr    if (error)
20338514Sdfr	goto out;
20438514Sdfr    bzero(ef->address + header.a_text + header.a_data, header.a_bss);
20538514Sdfr
20638514Sdfr    /*
20738514Sdfr     * Assume _DYNAMIC is the first data item.
20838514Sdfr     */
20938514Sdfr    ef->dynamic = (struct _dynamic*) (ef->address + header.a_text);
21038514Sdfr    if (ef->dynamic->d_version != LD_VERSION_BSD) {
21138514Sdfr	free(ef->address, M_LINKER);
21238514Sdfr	free(ef, M_LINKER);
21338514Sdfr	goto out;
21438514Sdfr    }
21538514Sdfr    (long) ef->dynamic->d_un.d_sdt += ef->address;
21638514Sdfr
21738514Sdfr    lf = linker_make_file(filename, ef, &link_elf_file_ops);
21838514Sdfr    if (lf == NULL) {
21938514Sdfr	free(ef->address, M_LINKER);
22038514Sdfr	free(ef, M_LINKER);
22138514Sdfr	error = ENOMEM;
22238514Sdfr	goto out;
22338514Sdfr    }
22438514Sdfr    lf->address = ef->address;
22538514Sdfr    lf->size = header.a_text + header.a_data + header.a_bss;
22638514Sdfr
22738514Sdfr    if ((error = load_dependancies(lf)) != 0
22838514Sdfr	|| (error = relocate_file(lf)) != 0) {
22938514Sdfr	linker_file_unload(lf);
23038514Sdfr	goto out;
23138514Sdfr    }
23238514Sdfr
23338514Sdfr    *result = lf;
23438514Sdfr
23538514Sdfrout:
23638514Sdfr    VOP_UNLOCK(nd.ni_vp, 0, p);
23738514Sdfr    vn_close(nd.ni_vp, FREAD, p->p_ucred, p);
23838514Sdfr
23938514Sdfr    return error;
24038514Sdfr#else
24138514Sdfr    return ENOEXEC;
24238514Sdfr#endif
24338514Sdfr}
24438514Sdfr
24538514Sdfrstatic void
24638514Sdfrlink_elf_unload(linker_file_t file)
24738514Sdfr{
24838514Sdfr    elf_file_t ef = file->priv;
24938514Sdfr
25038514Sdfr    if (ef) {
25138514Sdfr	if (ef->address)
25238514Sdfr	    free(ef->address, M_LINKER);
25338514Sdfr	free(ef, M_LINKER);
25438514Sdfr    }
25538514Sdfr}
25638514Sdfr
25738514Sdfr#define ELF_RELOC(ef, type, off) (type*) ((ef)->address + (off))
25838514Sdfr
25938514Sdfrstatic int
26038514Sdfrload_dependancies(linker_file_t lf)
26138514Sdfr{
26238514Sdfr#if 0
26338514Sdfr    elf_file_t ef = lf->priv;
26438514Sdfr    linker_file_t lfdep;
26538514Sdfr    long off;
26638514Sdfr    struct sod* sodp;
26738514Sdfr    char* name;
26838514Sdfr    char* filename = 0;
26938514Sdfr    int error = 0;
27038514Sdfr
27138514Sdfr    /*
27238514Sdfr     * All files are dependant on /kernel.
27338514Sdfr     */
27438514Sdfr    linker_kernel_file->refs++;
27538514Sdfr    linker_file_add_dependancy(lf, linker_kernel_file);
27638514Sdfr
27738514Sdfr    off = LD_NEED(ef->dynamic);
27838514Sdfr
27938514Sdfr    /*
28038514Sdfr     * Load the dependancies.
28138514Sdfr     */
28238514Sdfr    while (off != 0) {
28338514Sdfr	sodp = ELF_RELOC(ef, struct sod, off);
28438514Sdfr	name = ELF_RELOC(ef, char, sodp->sod_name);
28538514Sdfr
28638514Sdfr	/*
28738514Sdfr	 * Prepend pathname if dep is not an absolute filename.
28838514Sdfr	 */
28938514Sdfr	if (name[0] != '/') {
29038514Sdfr	    char* p;
29138514Sdfr	    filename = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
29238514Sdfr	    p = lf->filename + strlen(lf->filename) - 1;
29338514Sdfr	    while (p >= lf->filename && *p != '/')
29438514Sdfr		p--;
29538514Sdfr	    if (p >= lf->filename) {
29638514Sdfr		strncpy(filename, lf->filename, p - lf->filename);
29738514Sdfr		filename[p - lf->filename] = '\0';
29838514Sdfr		strcat(filename, "/");
29938514Sdfr		strcat(filename, name);
30038514Sdfr		name = filename;
30138514Sdfr	    }
30238514Sdfr	}
30338514Sdfr	error = linker_load_file(name, &lfdep);
30438514Sdfr	if (error)
30538514Sdfr	    goto out;
30638514Sdfr	error = linker_file_add_dependancy(lf, lfdep);
30738514Sdfr	if (error)
30838514Sdfr	    goto out;
30938514Sdfr	off = sodp->sod_next;
31038514Sdfr    }
31138514Sdfr
31238514Sdfrout:
31338514Sdfr    if (filename)
31438514Sdfr	free(filename, M_TEMP);
31538514Sdfr    return error;
31638514Sdfr#else
31738514Sdfr    return ENOEXEC;
31838514Sdfr#endif
31938514Sdfr}
32038514Sdfr
32138514Sdfr#if 0
32238514Sdfr/*
32338514Sdfr * XXX i386 dependant.
32438514Sdfr */
32538514Sdfrstatic long
32638514Sdfrread_relocation(struct relocation_info* r, char* addr)
32738514Sdfr{
32838514Sdfr    int length = r->r_length;
32938514Sdfr    if (length == 0)
33038514Sdfr	return *(u_char*) addr;
33138514Sdfr    else if (length == 1)
33238514Sdfr	return *(u_short*) addr;
33338514Sdfr    else if (length == 2)
33438514Sdfr	return *(u_int*) addr;
33538514Sdfr    else
33638514Sdfr	printf("link_elf: unsupported relocation size %d\n", r->r_length);
33738514Sdfr    return 0;
33838514Sdfr}
33938514Sdfr
34038514Sdfrstatic void
34138514Sdfrwrite_relocation(struct relocation_info* r, char* addr, long value)
34238514Sdfr{
34338514Sdfr    int length = r->r_length;
34438514Sdfr    if (length == 0)
34538514Sdfr	*(u_char*) addr = value;
34638514Sdfr    else if (length == 1)
34738514Sdfr	*(u_short*) addr = value;
34838514Sdfr    else if (length == 2)
34938514Sdfr	*(u_int*) addr = value;
35038514Sdfr    else
35138514Sdfr	printf("link_elf: unsupported relocation size %d\n", r->r_length);
35238514Sdfr}
35338514Sdfr
35438514Sdfrstatic int
35538514Sdfrrelocate_file(linker_file_t lf)
35638514Sdfr{
35738514Sdfr    elf_file_t ef = lf->priv;
35838514Sdfr    struct relocation_info* rel;
35938514Sdfr    struct relocation_info* erel;
36038514Sdfr    struct relocation_info* r;
36138514Sdfr    struct nzlist* symbolbase;
36238514Sdfr    char* stringbase;
36338514Sdfr    struct nzlist* np;
36438514Sdfr    char* sym;
36538514Sdfr    long relocation;
36638514Sdfr
36738514Sdfr    rel = ELF_RELOC(ef, struct relocation_info, LD_REL(ef->dynamic));
36838514Sdfr    erel = ELF_RELOC(ef, struct relocation_info,
36938514Sdfr		      LD_REL(ef->dynamic) + LD_RELSZ(ef->dynamic));
37038514Sdfr    symbolbase = ELF_RELOC(ef, struct nzlist, LD_SYMBOL(ef->dynamic));
37138514Sdfr    stringbase = ELF_RELOC(ef, char, LD_STRINGS(ef->dynamic));
37238514Sdfr
37338514Sdfr    for (r = rel; r < erel; r++) {
37438514Sdfr	char* addr;
37538514Sdfr
37638514Sdfr	if (r->r_address == 0)
37738514Sdfr	    break;
37838514Sdfr
37938514Sdfr	addr = ELF_RELOC(ef, char, r->r_address);
38038514Sdfr	if (r->r_extern) {
38138514Sdfr	    np = &symbolbase[r->r_symbolnum];
38238514Sdfr	    sym = &stringbase[np->nz_strx];
38338514Sdfr
38438514Sdfr	    if (sym[0] != '_') {
38538514Sdfr		printf("link_elf: bad symbol name %s\n", sym);
38638514Sdfr		relocation = 0;
38738514Sdfr	    } else
38838514Sdfr		relocation = (long)
38938514Sdfr		    linker_file_lookup_symbol(lf, sym + 1,
39038514Sdfr					      np->nz_type != (N_SETV+N_EXT));
39138514Sdfr	    if (!relocation) {
39238514Sdfr		printf("link_elf: symbol %s not found\n", sym);
39338514Sdfr		return ENOENT;
39438514Sdfr	    }
39538514Sdfr
39638514Sdfr	    relocation += read_relocation(r, addr);
39738514Sdfr
39838514Sdfr	    if (r->r_jmptable) {
39938514Sdfr		printf("link_elf: can't cope with jump table relocations\n");
40038514Sdfr		continue;
40138514Sdfr	    }
40238514Sdfr
40338514Sdfr	    if (r->r_pcrel)
40438514Sdfr		relocation -= (long) ef->address;
40538514Sdfr
40638514Sdfr	    if (r->r_copy) {
40738514Sdfr		printf("link_elf: can't cope with copy relocations\n");
40838514Sdfr		continue;
40938514Sdfr	    }
41038514Sdfr
41138514Sdfr	    write_relocation(r, addr, relocation);
41238514Sdfr	} else {
41338514Sdfr	    write_relocation(r, addr,
41438514Sdfr			     (long)(read_relocation(r, addr) + ef->address));
41538514Sdfr	}
41638514Sdfr
41738514Sdfr    }
41838514Sdfr
41938514Sdfr    return 0;
42038514Sdfr}
42138514Sdfr
42238514Sdfrstatic long
42338514Sdfrsymbol_hash_value(elf_file_t ef, const char* name)
42438514Sdfr{
42538514Sdfr    long hashval;
42638514Sdfr    const char* p;
42738514Sdfr
42838514Sdfr    hashval = '_';		/* fake a starting '_' for C symbols */
42938514Sdfr    for (p = name; *p; p++)
43038514Sdfr	hashval = (hashval << 1) + *p;
43138514Sdfr
43238514Sdfr    return (hashval & 0x7fffffff) % LD_BUCKETS(ef->dynamic);
43338514Sdfr}
43438514Sdfr
43538514Sdfr#endif
43638514Sdfr
43738514Sdfrint
43838514Sdfrlink_elf_lookup_symbol(linker_file_t lf, const char* name, linker_sym_t* sym)
43938514Sdfr{
44038514Sdfr	elf_file_t ef = lf->priv;
44138514Sdfr	int symcount = ef->nchains;
44238514Sdfr	Elf_Sym* es;
44338514Sdfr	int i;
44438514Sdfr
44538514Sdfr	/* XXX use hash table */
44638514Sdfr	for (i = 0, es = ef->symtab; i < ef->nchains; i++, es++) {
44738514Sdfr		if (es->st_name == 0)
44838514Sdfr			continue;
44938514Sdfr		if (!strcmp(ef->strtab + es->st_name, name)) {
45038514Sdfr			*sym = (linker_sym_t) es;
45138514Sdfr			return 0;
45238514Sdfr		}
45338514Sdfr	}
45438514Sdfr
45538514Sdfr	return ENOENT;
45638514Sdfr}
45738514Sdfr
45838514Sdfrstatic void
45938514Sdfrlink_elf_symbol_values(linker_file_t lf, linker_sym_t sym, linker_symval_t* symval)
46038514Sdfr{
46138514Sdfr	elf_file_t ef = lf->priv;
46238514Sdfr	Elf_Sym* es = (Elf_Sym*) sym;
46338514Sdfr
46438514Sdfr	symval->name = ef->strtab + es->st_name;
46538514Sdfr	symval->value = (caddr_t) es->st_value;
46638514Sdfr	symval->size = es->st_size;
46738514Sdfr}
46838514Sdfr
46938514Sdfrstatic int
47038514Sdfrlink_elf_search_symbol(linker_file_t lf, caddr_t value,
47138514Sdfr		       linker_sym_t* sym, long* diffp)
47238514Sdfr{
47338514Sdfr	elf_file_t ef = lf->priv;
47438514Sdfr	u_long off = (u_long) value;
47538514Sdfr	u_long diff = off;
47638514Sdfr	int symcount = ef->nchains;
47738514Sdfr	Elf_Sym* es;
47838514Sdfr	Elf_Sym* best = 0;
47938514Sdfr	int i;
48038514Sdfr
48138514Sdfr	for (i = 0, es = ef->symtab; i < ef->nchains; i++, es++) {
48238514Sdfr		if (es->st_name == 0)
48338514Sdfr			continue;
48438514Sdfr		if (off >= es->st_value) {
48538514Sdfr			if (off - es->st_value < diff) {
48638514Sdfr				diff = off - es->st_value;
48738514Sdfr				best = es;
48838514Sdfr				if (diff == 0)
48938514Sdfr					break;
49038514Sdfr			} else if (off - es->st_value == diff) {
49138514Sdfr				best = es;
49238514Sdfr			}
49338514Sdfr		}
49438514Sdfr	}
49538514Sdfr	if (best == 0)
49638514Sdfr		*diffp = off;
49738514Sdfr	else
49838514Sdfr		*diffp = diff;
49938514Sdfr	*sym = (linker_sym_t) best;
50038514Sdfr
50138514Sdfr	return 0;
50238514Sdfr}
50338514Sdfr
504