Date: Sun, 14 Mar 2004 10:49:53 -0800 (PST) From: Peter Wemm <peter@FreeBSD.org> To: Perforce Change Reviews <perforce@freebsd.org> Subject: PERFORCE change 48976 for review Message-ID: <200403141849.i2EInraP088119@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=48976 Change 48976 by peter@peter_overcee on 2004/03/14 10:49:23 cut out preload support for the time being and do a step 0 pass at converting this from a .so to a .o loader. It isn't as hard as it looked. Affected files ... .. //depot/projects/hammer/sys/kern/link_elf_obj.c#2 edit Differences ... ==== //depot/projects/hammer/sys/kern/link_elf_obj.c#2 (text+ko) ==== @@ -44,9 +44,6 @@ #include <sys/linker.h> #include <machine/elf.h> -#ifdef GPROF -#include <machine/profile.h> -#endif #include <vm/vm.h> #include <vm/vm_param.h> @@ -64,21 +61,13 @@ typedef struct elf_file { struct linker_file lf; /* Common fields */ - int preloaded; /* Was file pre-loaded */ caddr_t address; /* Relocation address */ #ifdef SPARSE_MAPPING vm_object_t object; /* VM object to hold file pages */ #endif - Elf_Dyn* dynamic; /* Symbol table etc. */ - Elf_Hashelt nbuckets; /* DT_HASH info */ - Elf_Hashelt nchains; - const Elf_Hashelt* buckets; - const Elf_Hashelt* chains; - caddr_t hash; caddr_t strtab; /* DT_STRTAB */ int strsz; /* DT_STRSZ */ const Elf_Sym* symtab; /* DT_SYMTAB */ - Elf_Addr* got; /* DT_PLTGOT */ const Elf_Rel* pltrel; /* DT_JMPREL */ int pltrelsize; /* DT_PLTRELSZ */ const Elf_Rela* pltrela; /* DT_JMPREL */ @@ -94,9 +83,6 @@ long ddbstrcnt; /* number of bytes in string table */ caddr_t symbase; /* malloc'ed symbold base */ caddr_t strbase; /* malloc'ed string base */ -#ifdef DDB - struct link_map gdb; /* hooks for gdb */ -#endif } *elf_file_t; static int link_elf_link_common_finish(linker_file_t); @@ -111,7 +97,6 @@ c_linker_sym_t* sym, long* diffp); static void link_elf_unload_file(linker_file_t); -static void link_elf_unload_preload(linker_file_t); static int link_elf_lookup_set(linker_file_t, const char *, void ***, void ***, int *); static int link_elf_each_function_name(linker_file_t, @@ -141,73 +126,7 @@ link_elf_methods, sizeof(struct elf_file) }; -static int parse_dynamic(elf_file_t ef); static int relocate_file(elf_file_t ef); -static int link_elf_preload_parse_symbols(elf_file_t ef); - -#ifdef DDB -static void r_debug_state(struct r_debug *dummy_one, - struct link_map *dummy_two); - -/* - * A list of loaded modules for GDB to use for loading symbols. - */ -struct r_debug r_debug; - -#define GDB_STATE(s) r_debug.r_state = s; r_debug_state(NULL, NULL); - -/* - * Function for the debugger to set a breakpoint on to gain control. - */ -static void -r_debug_state(struct r_debug *dummy_one __unused, - struct link_map *dummy_two __unused) -{ -} - -static void -link_elf_add_gdb(struct link_map *l) -{ - struct link_map *prev; - - l->l_next = NULL; - - if (r_debug.r_map == NULL) { - /* Add first. */ - l->l_prev = NULL; - r_debug.r_map = l; - } else { - /* Append to list. */ - for (prev = r_debug.r_map; prev->l_next != NULL; prev = prev->l_next) - ; - l->l_prev = prev; - prev->l_next = l; - } -} - -static void -link_elf_delete_gdb(struct link_map *l) -{ - if (l->l_prev == NULL) { - /* Remove first. */ - if ((r_debug.r_map = l->l_next) != NULL) - l->l_next->l_prev = NULL; - } else { - /* Remove any but first. */ - if ((l->l_prev->l_next = l->l_next) != NULL) - l->l_next->l_prev = l->l_prev; - } -} -#endif /* DDB */ - -#ifdef __ia64__ -Elf_Addr link_elf_get_gp(linker_file_t); -#endif - -/* - * The kernel symbol table starts here. - */ -extern struct _dynamic _DYNAMIC; static void link_elf_error(const char *s) @@ -217,15 +136,11 @@ /* * Actions performed after linking/loading both the preloaded kernel and any - * modules; whether preloaded or dynamicly loaded. + * modules; */ static int link_elf_link_common_finish(linker_file_t lf) { -#ifdef DDB - elf_file_t ef = (elf_file_t)lf; - char *newfilename; -#endif int error; /* Notify MD code that a module is being loaded. */ @@ -233,295 +148,29 @@ if (error) return (error); -#ifdef DDB - GDB_STATE(RT_ADD); - ef->gdb.l_addr = lf->address; - newfilename = malloc(strlen(lf->filename) + 1, M_LINKER, M_WAITOK); - strcpy(newfilename, lf->filename); - ef->gdb.l_name = newfilename; - ef->gdb.l_ld = ef->dynamic; - link_elf_add_gdb(&ef->gdb); - GDB_STATE(RT_CONSISTENT); -#endif - return (0); } static void link_elf_init(void* arg) { - Elf_Dyn *dp; - caddr_t modptr, baseptr, sizeptr; - elf_file_t ef; - char *modname; - linker_add_class(&link_elf_class); - - dp = (Elf_Dyn*) &_DYNAMIC; - modname = NULL; - modptr = preload_search_by_type("elf" __XSTRING(__ELF_WORD_SIZE) " kernel"); - if (modptr == NULL) - modptr = preload_search_by_type("elf kernel"); - if (modptr) - modname = (char *)preload_search_info(modptr, MODINFO_NAME); - if (modname == NULL) - modname = "kernel"; - linker_kernel_file = linker_make_file(modname, &link_elf_class); - if (linker_kernel_file == NULL) - panic("link_elf_init: Can't create linker structures for kernel"); - - ef = (elf_file_t) linker_kernel_file; - ef->preloaded = 1; - ef->address = 0; -#ifdef SPARSE_MAPPING - ef->object = 0; -#endif - ef->dynamic = dp; - - if (dp) - parse_dynamic(ef); - linker_kernel_file->address = (caddr_t) KERNBASE; - linker_kernel_file->size = -(intptr_t)linker_kernel_file->address; - - if (modptr) { - ef->modptr = modptr; - baseptr = preload_search_info(modptr, MODINFO_ADDR); - if (baseptr) - linker_kernel_file->address = *(caddr_t *)baseptr; - sizeptr = preload_search_info(modptr, MODINFO_SIZE); - if (sizeptr) - linker_kernel_file->size = *(size_t *)sizeptr; - } - (void)link_elf_preload_parse_symbols(ef); - -#ifdef DDB - r_debug.r_map = NULL; - r_debug.r_brk = r_debug_state; - r_debug.r_state = RT_CONSISTENT; -#endif - - (void)link_elf_link_common_finish(linker_kernel_file); + linker_add_class(&link_elf_class); } SYSINIT(link_elf, SI_SUB_KLD, SI_ORDER_SECOND, link_elf_init, 0); static int -link_elf_preload_parse_symbols(elf_file_t ef) -{ - caddr_t pointer; - caddr_t ssym, esym, base; - caddr_t strtab; - int strcnt; - Elf_Sym* symtab; - int symcnt; - - if (ef->modptr == NULL) - return 0; - pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_SSYM); - if (pointer == NULL) - return 0; - ssym = *(caddr_t *)pointer; - pointer = preload_search_info(ef->modptr, MODINFO_METADATA|MODINFOMD_ESYM); - if (pointer == NULL) - return 0; - esym = *(caddr_t *)pointer; - - base = ssym; - - symcnt = *(long *)base; - base += sizeof(long); - symtab = (Elf_Sym *)base; - base += roundup(symcnt, sizeof(long)); - - if (base > esym || base < ssym) { - printf("Symbols are corrupt!\n"); - return EINVAL; - } - - strcnt = *(long *)base; - base += sizeof(long); - strtab = base; - base += roundup(strcnt, sizeof(long)); - - if (base > esym || base < ssym) { - printf("Symbols are corrupt!\n"); - return EINVAL; - } - - ef->ddbsymtab = symtab; - ef->ddbsymcnt = symcnt / sizeof(Elf_Sym); - ef->ddbstrtab = strtab; - ef->ddbstrcnt = strcnt; - - return 0; -} - -static int -parse_dynamic(elf_file_t ef) -{ - Elf_Dyn *dp; - int plttype = DT_REL; - - for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { - switch (dp->d_tag) { - case DT_HASH: - { - /* From src/libexec/rtld-elf/rtld.c */ - const Elf_Hashelt *hashtab = (const Elf_Hashelt *) - (ef->address + dp->d_un.d_ptr); - ef->nbuckets = hashtab[0]; - ef->nchains = hashtab[1]; - ef->buckets = hashtab + 2; - ef->chains = ef->buckets + ef->nbuckets; - break; - } - case DT_STRTAB: - ef->strtab = (caddr_t) (ef->address + dp->d_un.d_ptr); - break; - case DT_STRSZ: - ef->strsz = dp->d_un.d_val; - break; - case DT_SYMTAB: - ef->symtab = (Elf_Sym*) (ef->address + dp->d_un.d_ptr); - break; - case DT_SYMENT: - if (dp->d_un.d_val != sizeof(Elf_Sym)) - return ENOEXEC; - break; - case DT_PLTGOT: - ef->got = (Elf_Addr *) (ef->address + dp->d_un.d_ptr); - break; - case DT_REL: - ef->rel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr); - break; - case DT_RELSZ: - ef->relsize = dp->d_un.d_val; - break; - case DT_RELENT: - if (dp->d_un.d_val != sizeof(Elf_Rel)) - return ENOEXEC; - break; - case DT_JMPREL: - ef->pltrel = (const Elf_Rel *) (ef->address + dp->d_un.d_ptr); - break; - case DT_PLTRELSZ: - ef->pltrelsize = dp->d_un.d_val; - break; - case DT_RELA: - ef->rela = (const Elf_Rela *) (ef->address + dp->d_un.d_ptr); - break; - case DT_RELASZ: - ef->relasize = dp->d_un.d_val; - break; - case DT_RELAENT: - if (dp->d_un.d_val != sizeof(Elf_Rela)) - return ENOEXEC; - break; - case DT_PLTREL: - plttype = dp->d_un.d_val; - if (plttype != DT_REL && plttype != DT_RELA) - return ENOEXEC; - break; -#ifdef DDB - case DT_DEBUG: - dp->d_un.d_ptr = (Elf_Addr) &r_debug; - break; -#endif - } - } - - if (plttype == DT_RELA) { - ef->pltrela = (const Elf_Rela *) ef->pltrel; - ef->pltrel = NULL; - ef->pltrelasize = ef->pltrelsize; - ef->pltrelsize = 0; - } - - ef->ddbsymtab = ef->symtab; - ef->ddbsymcnt = ef->nchains; - ef->ddbstrtab = ef->strtab; - ef->ddbstrcnt = ef->strsz; - - return 0; -} - -static int link_elf_link_preload(linker_class_t cls, const char* filename, linker_file_t *result) { - caddr_t modptr, baseptr, sizeptr, dynptr; - char *type; - elf_file_t ef; - linker_file_t lf; - int error; - vm_offset_t dp; - - /* Look to see if we have the file preloaded */ - modptr = preload_search_by_name(filename); - if (modptr == NULL) - return ENOENT; - - type = (char *)preload_search_info(modptr, MODINFO_TYPE); - baseptr = preload_search_info(modptr, MODINFO_ADDR); - sizeptr = preload_search_info(modptr, MODINFO_SIZE); - dynptr = preload_search_info(modptr, MODINFO_METADATA|MODINFOMD_DYNAMIC); - if (type == NULL || - (strcmp(type, "elf" __XSTRING(__ELF_WORD_SIZE) " module") != 0 && - strcmp(type, "elf module") != 0)) return (EFTYPE); - if (baseptr == NULL || sizeptr == NULL || dynptr == NULL) - return (EINVAL); - - lf = linker_make_file(filename, &link_elf_class); - if (lf == NULL) { - return ENOMEM; - } - - ef = (elf_file_t) lf; - ef->preloaded = 1; - ef->modptr = modptr; - ef->address = *(caddr_t *)baseptr; -#ifdef SPARSE_MAPPING - ef->object = 0; -#endif - dp = (vm_offset_t)ef->address + *(vm_offset_t *)dynptr; - ef->dynamic = (Elf_Dyn *)dp; - lf->address = ef->address; - lf->size = *(size_t *)sizeptr; - - error = parse_dynamic(ef); - if (error) { - linker_file_unload(lf); - return error; - } - link_elf_reloc_local(lf); - *result = lf; - return (0); } static int link_elf_link_preload_finish(linker_file_t lf) { - elf_file_t ef; - int error; - - ef = (elf_file_t) lf; -#if 0 /* this will be more trouble than it's worth for now */ - for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { - if (dp->d_tag != DT_NEEDED) - continue; - modname = ef->strtab + dp->d_un.d_val; - error = linker_load_module(modname, lf); - if (error) - goto out; - } -#endif - error = relocate_file(ef); - if (error) - return error; - (void)link_elf_preload_parse_symbols(ef); - - return (link_elf_link_common_finish(lf)); + return (EFTYPE); } static int @@ -621,6 +270,8 @@ * We rely on the program header being in the first page. This is * not strictly required by the ABI specification, but it seems to * always true in practice. And, it simplifies things considerably. + * XXX section table, not program header. And we should not depend + * XXX on this because the section table is likely to be bigger. */ if (!((hdr->e_phentsize == sizeof(Elf_Phdr)) && (hdr->e_phoff + hdr->e_phnum*sizeof(Elf_Phdr) <= PAGE_SIZE) && @@ -632,7 +283,14 @@ * * We rely on there being exactly two load segments, text and data, * in that order. + * + * XXX do several passes of section table instead. + * XXX 1) count various things needed to size arrays + * XXX 2) grab info about things like PROGBITS/REL/RELA/STRTAB/SYMTAB + * XXX 3) read the string and symbol tables so we can do relocations etc + * XXX 4) (later on) load the rest of the entries. */ +/* XXX *************** STEP 1 GOES HERE ************* XXX */ phdr = (Elf_Phdr *) (firstpage + hdr->e_phoff); phlimit = phdr + hdr->e_phnum; nsegs = 0; @@ -680,7 +338,64 @@ error = ENOEXEC; goto out; } +/* XXX *************** STEP 2 GOES HERE ************* XXX */ +/* XXX *************** STEP 3 GOES HERE ************* XXX */ + + /* Try and load the symbol table if it's present. (you can strip it!) */ + nbytes = hdr->e_shnum * hdr->e_shentsize; + if (nbytes == 0 || hdr->e_shoff == 0) + goto nosyms; + shdr = malloc(nbytes, M_LINKER, M_WAITOK | M_ZERO); + if (shdr == NULL) { + error = ENOMEM; + goto out; + } + error = vn_rdwr(UIO_READ, nd.ni_vp, + (caddr_t)shdr, nbytes, hdr->e_shoff, + UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, + &resid, td); + if (error) + goto out; + symtabindex = -1; + symstrindex = -1; + for (i = 0; i < hdr->e_shnum; i++) { + if (shdr[i].sh_type == SHT_SYMTAB) { + symtabindex = i; + symstrindex = shdr[i].sh_link; + } + } + if (symtabindex < 0 || symstrindex < 0) + goto nosyms; + + symcnt = shdr[symtabindex].sh_size; + ef->symbase = malloc(symcnt, M_LINKER, M_WAITOK); + strcnt = shdr[symstrindex].sh_size; + ef->strbase = malloc(strcnt, M_LINKER, M_WAITOK); + if (ef->symbase == NULL || ef->strbase == NULL) { + error = ENOMEM; + goto out; + } + error = vn_rdwr(UIO_READ, nd.ni_vp, + ef->symbase, symcnt, shdr[symtabindex].sh_offset, + UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, + &resid, td); + if (error) + goto out; + error = vn_rdwr(UIO_READ, nd.ni_vp, + ef->strbase, strcnt, shdr[symstrindex].sh_offset, + UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, + &resid, td); + if (error) + goto out; + + ef->ddbsymcnt = symcnt / sizeof(Elf_Sym); + ef->ddbsymtab = (const Elf_Sym *)ef->symbase; + ef->ddbstrcnt = strcnt; + ef->ddbstrtab = ef->strbase; + +/* XXX *************** STEP 4 GOES HERE ************* XXX */ + /* * Allocate the entire address space of the object, to stake out our * contiguous region, and to establish the base address for relocation. @@ -749,92 +464,20 @@ #endif } -#ifdef GPROF - /* Update profiling information with the new text segment. */ - kmupetext((uintfptr_t)(mapbase + segs[0]->p_vaddr - base_vaddr + - segs[0]->p_memsz)); -#endif - - ef->dynamic = (Elf_Dyn *) (mapbase + phdyn->p_vaddr - base_vaddr); + /* ef->dynamic = (Elf_Dyn *) (mapbase + phdyn->p_vaddr - base_vaddr); */ lf->address = ef->address; lf->size = mapsize; - error = parse_dynamic(ef); - if (error) - goto out; link_elf_reloc_local(lf); error = linker_load_dependencies(lf); if (error) goto out; -#if 0 /* this will be more trouble than it's worth for now */ - for (dp = ef->dynamic; dp->d_tag != DT_NULL; dp++) { - if (dp->d_tag != DT_NEEDED) - continue; - modname = ef->strtab + dp->d_un.d_val; - error = linker_load_module(modname, lf); - if (error) - goto out; - } -#endif error = relocate_file(ef); if (error) goto out; - /* Try and load the symbol table if it's present. (you can strip it!) */ - nbytes = hdr->e_shnum * hdr->e_shentsize; - if (nbytes == 0 || hdr->e_shoff == 0) - goto nosyms; - shdr = malloc(nbytes, M_LINKER, M_WAITOK | M_ZERO); - if (shdr == NULL) { - error = ENOMEM; - goto out; - } - error = vn_rdwr(UIO_READ, nd.ni_vp, - (caddr_t)shdr, nbytes, hdr->e_shoff, - UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, - &resid, td); - if (error) - goto out; - symtabindex = -1; - symstrindex = -1; - for (i = 0; i < hdr->e_shnum; i++) { - if (shdr[i].sh_type == SHT_SYMTAB) { - symtabindex = i; - symstrindex = shdr[i].sh_link; - } - } - if (symtabindex < 0 || symstrindex < 0) - goto nosyms; - - symcnt = shdr[symtabindex].sh_size; - ef->symbase = malloc(symcnt, M_LINKER, M_WAITOK); - strcnt = shdr[symstrindex].sh_size; - ef->strbase = malloc(strcnt, M_LINKER, M_WAITOK); - - if (ef->symbase == NULL || ef->strbase == NULL) { - error = ENOMEM; - goto out; - } - error = vn_rdwr(UIO_READ, nd.ni_vp, - ef->symbase, symcnt, shdr[symtabindex].sh_offset, - UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, - &resid, td); - if (error) - goto out; - error = vn_rdwr(UIO_READ, nd.ni_vp, - ef->strbase, strcnt, shdr[symstrindex].sh_offset, - UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, - &resid, td); - if (error) - goto out; - - ef->ddbsymcnt = symcnt / sizeof(Elf_Sym); - ef->ddbsymtab = (const Elf_Sym *)ef->symbase; - ef->ddbstrcnt = strcnt; - ef->ddbstrtab = ef->strbase; - error = link_elf_link_common_finish(lf); if (error) goto out; @@ -861,23 +504,9 @@ { elf_file_t ef = (elf_file_t) file; -#ifdef DDB - if (ef->gdb.l_ld) { - GDB_STATE(RT_DELETE); - free((void *)(uintptr_t)ef->gdb.l_name, M_LINKER); - link_elf_delete_gdb(&ef->gdb); - GDB_STATE(RT_CONSISTENT); - } -#endif - /* Notify MD code that a module is being unloaded. */ elf_cpu_unload_file(file); - if (ef->preloaded) { - link_elf_unload_preload(file); - return; - } - #ifdef SPARSE_MAPPING if (ef->object) { vm_map_remove(kernel_map, (vm_offset_t) ef->address, @@ -895,13 +524,6 @@ free(ef->strbase, M_LINKER); } -static void -link_elf_unload_preload(linker_file_t file) -{ - if (file->filename) - preload_delete_name(file->filename); -} - static const char * symbol_name(elf_file_t ef, Elf_Word r_info) { @@ -982,26 +604,6 @@ return 0; } -/* - * Hash function for symbol table lookup. Don't even think about changing - * this. It is specified by the System V ABI. - */ -static unsigned long -elf_hash(const char *name) -{ - const unsigned char *p = (const unsigned char *) name; - unsigned long h = 0; - unsigned long g; - - while (*p != '\0') { - h = (h << 4) + *p++; - if ((g = h & 0xf0000000) != 0) - h ^= g >> 24; - h &= ~g; - } - return h; -} - static int link_elf_lookup_symbol(linker_file_t lf, const char* name, c_linker_sym_t* sym) { @@ -1009,44 +611,9 @@ unsigned long symnum; const Elf_Sym* symp; const char *strp; - unsigned long hash; int i; - /* First, search hashed global symbols */ - hash = elf_hash(name); - symnum = ef->buckets[hash % ef->nbuckets]; - - while (symnum != STN_UNDEF) { - if (symnum >= ef->nchains) { - printf("link_elf_lookup_symbol: corrupt symbol table\n"); - return ENOENT; - } - - symp = ef->symtab + symnum; - if (symp->st_name == 0) { - printf("link_elf_lookup_symbol: corrupt symbol table\n"); - return ENOENT; - } - - strp = ef->strtab + symp->st_name; - - if (strcmp(name, strp) == 0) { - if (symp->st_shndx != SHN_UNDEF || - (symp->st_value != 0 && - ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { - *sym = (c_linker_sym_t) symp; - return 0; - } else - return ENOENT; - } - - symnum = ef->chains[symnum]; - } - - /* If we have not found it, look at the full table (if loaded) */ - if (ef->symtab == ef->ddbsymtab) - return ENOENT; - +/* XXX search for globals first */ /* Exhaustive search */ for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { strp = ef->ddbstrtab + symp->st_name; @@ -1070,14 +637,6 @@ elf_file_t ef = (elf_file_t) lf; const Elf_Sym* es = (const Elf_Sym*) sym; - if (es >= ef->symtab && es < (ef->symtab + ef->nchains)) { - symval->name = ef->strtab + es->st_name; - symval->value = (caddr_t) ef->address + es->st_value; - symval->size = es->st_size; - return 0; - } - if (ef->symtab == ef->ddbsymtab) - return ENOENT; if (es >= ef->ddbsymtab && es < (ef->ddbsymtab + ef->ddbsymcnt)) { symval->name = ef->ddbstrtab + es->st_name; symval->value = (caddr_t) ef->address + es->st_value; @@ -1200,21 +759,6 @@ return (0); } -#ifdef __ia64__ -/* - * Each KLD has its own GP. The GP value for each load module is given by - * DT_PLTGOT on ia64. We need GP to construct function descriptors, but - * don't have direct access to the ELF file structure. The link_elf_get_gp() - * function returns the GP given a pointer to a generic linker file struct. - */ -Elf_Addr -link_elf_get_gp(linker_file_t lf) -{ - elf_file_t ef = (elf_file_t)lf; - return (Elf_Addr)ef->got; -} -#endif - const Elf_Sym * elf_get_sym(linker_file_t lf, Elf_Word symidx) {
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200403141849.i2EInraP088119>