From c88c6abc546f73a18b3e8cdc1f44e94e6537886f Mon Sep 17 00:00:00 2001 From: Stas Sergeev Date: Mon, 9 Oct 2023 23:02:15 +0500 Subject: [PATCH 1/3] thunks: derive symtab from elf After the switch to small memory model, we can derive the symtab directly from elf. This simplifies plt a lot. --- fdpp/elf.c | 22 ++++++++++++++++++---- fdpp/elf_priv.h | 1 + fdpp/plt.S | 20 -------------------- fdpp/thunks.cc | 42 +++++++++++++----------------------------- fdpp/thunks_a.cc | 19 +++++++++++-------- fdpp/thunks_a.h | 1 + 6 files changed, 44 insertions(+), 61 deletions(-) diff --git a/fdpp/elf.c b/fdpp/elf.c index 0c0489dc..1ed04a7a 100644 --- a/fdpp/elf.c +++ b/fdpp/elf.c @@ -142,9 +142,8 @@ void elf_close(void *arg) free(state); } -void *elf_getsym(void *arg, const char *name) +static int do_getsymoff(struct elfstate *state, const char *name) { - struct elfstate *state = (struct elfstate *)arg; Elf_Data *data; int count, i; @@ -156,8 +155,23 @@ void *elf_getsym(void *arg, const char *name) gelf_getsym(data, i, &sym); if (strcmp(elf_strptr(state->elf, state->symtab_shdr.sh_link, sym.st_name), name) == 0) - return state->addr + state->load_offs + sym.st_value; + return sym.st_value; } - return NULL; + return -1; +} + +void *elf_getsym(void *arg, const char *name) +{ + struct elfstate *state = (struct elfstate *)arg; + int off = do_getsymoff(state, name); + if (off == -1) + return NULL; + return state->addr + state->load_offs + off; +} + +int elf_getsymoff(void *arg, const char *name) +{ + struct elfstate *state = (struct elfstate *)arg; + return do_getsymoff(state, name); } diff --git a/fdpp/elf_priv.h b/fdpp/elf_priv.h index 9931c673..4ceb2129 100644 --- a/fdpp/elf_priv.h +++ b/fdpp/elf_priv.h @@ -2,3 +2,4 @@ void *elf_open(const char *name); void elf_reloc(void *arg, uint16_t seg); void elf_close(void *arg); void *elf_getsym(void *arg, const char *name); +int elf_getsymoff(void *arg, const char *name); diff --git a/fdpp/plt.S b/fdpp/plt.S index 743eafe9..e2f38227 100644 --- a/fdpp/plt.S +++ b/fdpp/plt.S @@ -72,11 +72,6 @@ segment %3 dw %1, _SEG(%1) %endmacro -%macro asmsym 1 - extern _%1 - dw _%1, _SEG(_%1) -%endmacro - %macro nearwrp 0 push bp mov bp, sp @@ -123,18 +118,6 @@ __asm_callsymtab_start: %include "plt_asmp.inc" __asm_callsymtab_end: -#define __ASM(t, n) asmsym n -#define __ASM_FAR(t, n) asmsym n -#define __ASM_NEAR(t, n) asmsym n -#define __ASM_ARR(t, n, l) asmsym n -#define __ASM_ARRI(t, n) asmsym n -#define __ASM_ARRI_F(t, n) asmsym n -#define __ASM_FUNC(n) asmsym n -#define SEMIC -__asm_symtab_start: -#include -__asm_symtab_end: - segment HMA_TEXT near_wrp: @@ -169,9 +152,6 @@ plt_init: push word __asm_callsymtab_end - __asm_callsymtab_start ; calltab_len push word IGROUP push word __asm_callsymtab_start ; calltab - push word __asm_symtab_end - __asm_symtab_start ; symtab_len - push word IGROUP - push word __asm_symtab_start ; symtab ; fdpp_symtab filled mov si, sp diff --git a/fdpp/thunks.cc b/fdpp/thunks.cc index 7f2b6c9e..34759f2e 100644 --- a/fdpp/thunks.cc +++ b/fdpp/thunks.cc @@ -132,30 +132,7 @@ int is_dos_space(const void *ptr) return fdpp->is_dos_space(ptr); } -static int FdppSetAsmThunks(struct far_s *ptrs, int len) -{ - int i; - int exp = num_athunks; - - if (len != exp) { - fdprintf("len=%i expected %i\n", len, exp); - return -1; - } - - farhlp_init(&sym_tab); - for (i = 0; i < len; i++) { - *asm_thunks[i].ptr = ptrs[i]; - /* there are conflicts, for example InitTextStart will collide - * with the first sym. So use _replace. */ - store_far_replace(&sym_tab, resolve_segoff(ptrs[i]), ptrs[i]); - } - - return 0; -} - struct fdpp_symtab { - struct far_s symtab; - uint16_t symtab_len; struct far_s calltab; uint16_t calltab_len; struct far_s near_wrp[2]; @@ -202,10 +179,6 @@ static void do_relocs(UWORD old_seg, uint8_t *start_p, uint8_t *end_p, static void FdppSetSymTab(struct fdpp_symtab *symtab) { - int err; - struct far_s *thtab = (struct far_s *)resolve_segoff(symtab->symtab); - int stab_len = symtab->symtab_len / sizeof(struct far_s); - free(near_wrp); near_wrp = (struct far_s *)malloc(sizeof(struct far_s) * num_wrps); memcpy(near_wrp, symtab->near_wrp, sizeof(struct far_s) * num_wrps); @@ -213,8 +186,6 @@ static void FdppSetSymTab(struct fdpp_symtab *symtab) asm_tab = (struct asm_dsc_s *)malloc(symtab->calltab_len); memcpy(asm_tab, resolve_segoff(symtab->calltab), symtab->calltab_len); asm_tab_len = symtab->calltab_len / sizeof(struct asm_dsc_s); - err = FdppSetAsmThunks(thtab, stab_len); - ___assert(!err); } int FdppCtrl(int idx, struct vm86_regs *regs) @@ -784,8 +755,21 @@ void *FdppKernelLoad(const char *dname, int *len, struct fdpp_bss_list **bss) const void *FdppKernelReloc(void *handle, uint16_t seg) { + int i; + far_s f; struct krnl_hndl *h = (struct krnl_hndl *)handle; elf_reloc(h->elf, seg); + + farhlp_init(&sym_tab); + f.seg = seg; + for (i = 0; i < num_athunks; i++) { + int off = elf_getsymoff(h->elf, asm_thunks[i].name); + assert(off != -1); + f.off = off; + *asm_thunks[i].ptr = f; + store_far_replace(&sym_tab, resolve_segoff(f), f); + } + return h->start; } diff --git a/fdpp/thunks_a.cc b/fdpp/thunks_a.cc index cdf1e367..a818cc08 100644 --- a/fdpp/thunks_a.cc +++ b/fdpp/thunks_a.cc @@ -25,16 +25,19 @@ #include "glob_tmpl.h" #undef _E +#define __S(x) #x +#define _S(x) __S(x) + struct athunk asm_thunks[] = { -#define _A(v) { __ASMREF(v), 0 } +#define _A(v, w) { _S(v), __ASMREF(w), 0 } #define SEMIC , -#define __ASM(t, v) _A(__##v) -#define __ASM_FAR(t, v) _A(__##v) -#define __ASM_NEAR(t, v) { __ASMREF(__##v), THUNKF_SHORT | THUNKF_DEEP } -#define __ASM_ARR(t, v, l) _A(__##v) -#define __ASM_ARRI(t, v) _A(__##v) -#define __ASM_ARRI_F(t, v) _A(__##v) -#define __ASM_FUNC(v) _A(__##v) +#define __ASM(t, v) _A(_##v, __##v) +#define __ASM_FAR(t, v) _A(_##v, __##v) +#define __ASM_NEAR(t, v) { _S(_##v), __ASMREF(__##v), THUNKF_SHORT | THUNKF_DEEP } +#define __ASM_ARR(t, v, l) _A(_##v, __##v) +#define __ASM_ARRI(t, v) _A(_##v, __##v) +#define __ASM_ARRI_F(t, v) _A(_##v, __##v) +#define __ASM_FUNC(v) _A(_##v, __##v) #include #undef __ASM #undef __ASM_FAR diff --git a/fdpp/thunks_a.h b/fdpp/thunks_a.h index 94786bdb..b311ed57 100644 --- a/fdpp/thunks_a.h +++ b/fdpp/thunks_a.h @@ -1,4 +1,5 @@ struct athunk { + const char *name; struct far_s *ptr; #define THUNKF_SHORT 1 #define THUNKF_DEEP 2 From bd8e48cac6bbff2fe629d24d20f229a25d8aa539 Mon Sep 17 00:00:00 2001 From: Stas Sergeev Date: Tue, 10 Oct 2023 00:02:40 +0500 Subject: [PATCH 2/3] thunks: derive calltab from elf This simplifies plt and thunks a great deal! --- fdpp/clang.mak | 2 +- fdpp/makefile | 8 +++--- fdpp/parsers/parse_decls.sh | 4 +-- fdpp/plt.S | 24 ----------------- fdpp/thunks.cc | 53 ++++++++++++++----------------------- fdpp/thunks_a.cc | 9 +++++++ fdpp/thunks_a.h | 2 ++ 7 files changed, 38 insertions(+), 64 deletions(-) diff --git a/fdpp/clang.mak b/fdpp/clang.mak index ee9801cc..71692554 100644 --- a/fdpp/clang.mak +++ b/fdpp/clang.mak @@ -31,7 +31,7 @@ PKG_CONFIG ?= pkg-config TARGETOPT = -std=c++11 -c -fno-threadsafe-statics -fpic # _XTRA should go at the end of cmd line -TARGETOPT_XTRA = -Wno-format-invalid-specifier +TARGETOPT_XTRA = -Wno-format-invalid-specifier -Wno-c99-designator DEBUG_MODE ?= 1 EXTRA_DEBUG ?= 0 diff --git a/fdpp/makefile b/fdpp/makefile index 5b5a6e50..47117746 100644 --- a/fdpp/makefile +++ b/fdpp/makefile @@ -121,7 +121,7 @@ GEN_HEADERS = thunk_calls.h thunk_asms.h rel.h GEN_HEADERS_FD = glob_asmdefs.h GEN_ASMS = plt.asm cdata.asm # dont change file order in GEN_TMP as it matches the gen script -GEN_TMP = thunk_calls.tmp thunk_asms.tmp plt.inc plt_asmc.inc plt_asmp.inc +GEN_TMP = thunk_calls.tmp thunk_asms.tmp plt.inc plt_asmc.h plt_asmp.h GEN_CC = $(CFILES:.c=.cc) INITHEADERS = $(SRC)/init-mod.h $(SRC)/init-dat.h HEADERS=$(HDRS) $(SRC)/globals.h $(SRC)/proto.h $(INITHEADERS) $(PPHDRS) @@ -179,7 +179,7 @@ $(FDPP_COBJS): %.o: $(srcdir)/%.c $(PPHDRS) $(srcdir)/makefile $(CC) $(CLCFLAGS) -I . -o $@ $< thunks.o: $(EXT_H) $(GIT_REV) -thunks_a.o: $(SRC)/glob_asm.h $(srcdir)/glob_tmpl.h +thunks_a.o: $(SRC)/glob_asm.h $(srcdir)/glob_tmpl.h plt_asmc.h plt_asmp.h $(FDPP_CCOBJS): %.o: $(srcdir)/%.cc $(GEN_HEADERS) $(PPHDRS) $(srcdir)/makefile $(CC) $(CFLAGS) -o $@ $< @@ -205,7 +205,7 @@ $(FDPPLIB): $(OBJECTS) $(FDPP_COBJS) $(FDPP_CCOBJS) $(FDPP_CPPOBJS) $(FDPPDEVL): $(FDPPLIB) ln -sf $< $@ -plt.o: plt.asm plt.inc plt_asmc.inc plt_asmp.inc $(SRC)/segs.inc +plt.o: plt.asm plt.inc $(SRC)/segs.inc plt.asm: $(srcdir)/plt.S $(SRC)/glob_asm.h $(TOP)/include/fdpp/bprm.h \ $(srcdir)/thunks_priv.h $(srcdir)/makefile @@ -224,7 +224,7 @@ $(filter %.tmp,$(GEN_TMP)): $(SRC)/proto.h $(pars) plt.inc: thunk_calls.tmp $(pars) -plt_asmc.inc plt_asmp.inc: thunk_asms.tmp +plt_asmc.h plt_asmp.h: thunk_asms.tmp $(pars) thunk_calls.h: thunk_calls.tmp parsers/thunk_gen diff --git a/fdpp/parsers/parse_decls.sh b/fdpp/parsers/parse_decls.sh index d22f43f2..55d9050d 100755 --- a/fdpp/parsers/parse_decls.sh +++ b/fdpp/parsers/parse_decls.sh @@ -19,12 +19,12 @@ gen_asms_tmp() { gen_plt_asmc() { grep ASMFUNC $1 | \ - sed -E 's/([0-9]+)[[:blank:]]+([^[:blank:]\(]+[[:blank:]]+)+([^ \(]+) *\(.+/asmcsym \3, \1/' + sed -E 's/([0-9]+)[[:blank:]]+([^[:blank:]\(]+[[:blank:]]+)+([^ \(]+) *\(.+/ASMCSYM\(\3, \1\)/' } gen_plt_asmp() { grep ASMPASCAL $1 | tr '[:lower:]' '[:upper:]' | \ - sed -E 's/([0-9]+)[[:blank:]]+([^[:blank:]\(]+[[:blank:]]+)+([^ \(]+) *\(.+/asmpsym \3, \1/' + sed -E 's/([0-9]+)[[:blank:]]+([^[:blank:]\(]+[[:blank:]]+)+([^ \(]+) *\(.+/ASMPSYM\(\3, \1\)/' } case "$1" in diff --git a/fdpp/plt.S b/fdpp/plt.S index e2f38227..f8bf0ce0 100644 --- a/fdpp/plt.S +++ b/fdpp/plt.S @@ -20,8 +20,6 @@ #include "bprm.h" %include "segs.inc" -%define _SEG(s) TGROUP - extern _BootParamSeg segment _BSS @@ -60,18 +58,6 @@ segment %3 %include "plt.inc" -%macro asmcsym 2 - dw %2 - extern _%1 - dw _%1, _SEG(_%1) -%endmacro - -%macro asmpsym 2 - dw %2 - extern %1 - dw %1, _SEG(%1) -%endmacro - %macro nearwrp 0 push bp mov bp, sp @@ -111,13 +97,6 @@ segment %3 retf 2 %endmacro -segment INIT_TEXT - -__asm_callsymtab_start: -%include "plt_asmc.inc" -%include "plt_asmp.inc" -__asm_callsymtab_end: - segment HMA_TEXT near_wrp: @@ -149,9 +128,6 @@ plt_init: push word init_near_wrp ; near_wrp[1] push word TGROUP push word near_wrp ; near_wrp[0] - push word __asm_callsymtab_end - __asm_callsymtab_start ; calltab_len - push word IGROUP - push word __asm_callsymtab_start ; calltab ; fdpp_symtab filled mov si, sp diff --git a/fdpp/thunks.cc b/fdpp/thunks.cc index 34759f2e..2c447b68 100644 --- a/fdpp/thunks.cc +++ b/fdpp/thunks.cc @@ -36,12 +36,11 @@ static struct fdpp_api *fdpp; struct asm_dsc_s { - UWORD num; UWORD off; UWORD seg; }; struct asm_dsc_s *asm_tab; -static int asm_tab_len; +#define asm_tab_len num_cthunks static farhlp sym_tab; static struct far_s *near_wrp; #define num_wrps 2 @@ -133,8 +132,6 @@ int is_dos_space(const void *ptr) } struct fdpp_symtab { - struct far_s calltab; - uint16_t calltab_len; struct far_s near_wrp[2]; }; @@ -182,10 +179,6 @@ static void FdppSetSymTab(struct fdpp_symtab *symtab) free(near_wrp); near_wrp = (struct far_s *)malloc(sizeof(struct far_s) * num_wrps); memcpy(near_wrp, symtab->near_wrp, sizeof(struct far_s) * num_wrps); - free(asm_tab); - asm_tab = (struct asm_dsc_s *)malloc(symtab->calltab_len); - memcpy(asm_tab, resolve_segoff(symtab->calltab), symtab->calltab_len); - asm_tab_len = symtab->calltab_len / sizeof(struct asm_dsc_s); } int FdppCtrl(int idx, struct vm86_regs *regs) @@ -325,16 +318,8 @@ static void _call_wrp(FdppAsmCall_t call, struct vm86_regs *regs, static uint32_t _do_asm_call_far(int num, uint8_t *sp, uint8_t len, FdppAsmCall_t call) { - int i; - - for (i = 0; i < asm_tab_len; i++) { - if (asm_tab[i].num == num) { - _call_wrp(call, &s_regs, asm_tab[i].seg, asm_tab[i].off, sp, len); - return (LO_WORD(s_regs.edx) << 16) | LO_WORD(s_regs.eax); - } - } - ___assert(0); - return -1; + _call_wrp(call, &s_regs, asm_tab[num].seg, asm_tab[num].off, sp, len); + return (LO_WORD(s_regs.edx) << 16) | LO_WORD(s_regs.eax); } static uint16_t find_wrp(int init, uint16_t seg) @@ -346,21 +331,13 @@ static uint16_t find_wrp(int init, uint16_t seg) static uint32_t _do_asm_call(int num, int init, uint8_t *sp, uint8_t len, FdppAsmCall_t call) { - int i; - - for (i = 0; i < asm_tab_len; i++) { - if (asm_tab[i].num == num) { - uint16_t wrp = find_wrp(init, asm_tab[i].seg); - LO_WORD(s_regs.eax) = asm_tab[i].off; - /* argpack should be aligned */ - ___assert(!(len & 1)); - LO_WORD(s_regs.ecx) = len >> 1; - _call_wrp(call, &s_regs, asm_tab[i].seg, wrp, sp, len); - return (LO_WORD(s_regs.edx) << 16) | LO_WORD(s_regs.eax); - } - } - ___assert(0); - return -1; + uint16_t wrp = find_wrp(init, asm_tab[num].seg); + LO_WORD(s_regs.eax) = asm_tab[num].off; + /* argpack should be aligned */ + ___assert(!(len & 1)); + LO_WORD(s_regs.ecx) = len >> 1; + _call_wrp(call, &s_regs, asm_tab[num].seg, wrp, sp, len); + return (LO_WORD(s_regs.edx) << 16) | LO_WORD(s_regs.eax); } static void asm_call(struct vm86_regs *regs, uint16_t seg, @@ -770,6 +747,16 @@ const void *FdppKernelReloc(void *handle, uint16_t seg) store_far_replace(&sym_tab, resolve_segoff(f), f); } + asm_tab = (struct asm_dsc_s *)malloc(num_cthunks * + sizeof(struct asm_dsc_s *)); + for (i = 0; i < num_cthunks; i++) { + int off = elf_getsymoff(h->elf, asm_cthunks[i].name); + assert(off != -1); + assert(asm_cthunks[i].name); + asm_tab[i].seg = seg; + asm_tab[i].off = off; + } + return h->start; } diff --git a/fdpp/thunks_a.cc b/fdpp/thunks_a.cc index a818cc08..2511287d 100644 --- a/fdpp/thunks_a.cc +++ b/fdpp/thunks_a.cc @@ -50,3 +50,12 @@ struct athunk asm_thunks[] = { }; const int num_athunks = _countof(asm_thunks); + +struct athunk asm_cthunks[] = { +#define ASMCSYM(s, n) [n] = { _S(_##s), NULL, 0 }, +#include "plt_asmc.h" +#define ASMPSYM(s, n) [n] = { _S(s), NULL, 0 }, +#include "plt_asmp.h" +}; + +const int num_cthunks = _countof(asm_cthunks); diff --git a/fdpp/thunks_a.h b/fdpp/thunks_a.h index b311ed57..3be9d620 100644 --- a/fdpp/thunks_a.h +++ b/fdpp/thunks_a.h @@ -8,3 +8,5 @@ struct athunk { extern struct athunk asm_thunks[]; extern const int num_athunks; +extern struct athunk asm_cthunks[]; +extern const int num_cthunks; From 7e1b1e893b180d97736fdd61e8d9d52be079141d Mon Sep 17 00:00:00 2001 From: Stas Sergeev Date: Tue, 10 Oct 2023 00:14:18 +0500 Subject: [PATCH 3/3] thunks: derive remaining plt bits from elf This simplifies plt considerably. Completely removed SetSymTab() and FdppCtrl()! The intention is to make that infrastructure easily portable to other projects. Unfortunately, that requires those "other projects" to switch to elf, too. Which they haven't done just yet... But I'd rather write yet another custom elf loader, than to port this silly plt stuff. --- fdpp/plt.S | 25 +------------------------ fdpp/thunks.cc | 33 ++++++++------------------------- 2 files changed, 9 insertions(+), 49 deletions(-) diff --git a/fdpp/plt.S b/fdpp/plt.S index f8bf0ce0..660ac928 100644 --- a/fdpp/plt.S +++ b/fdpp/plt.S @@ -1,6 +1,6 @@ /* * FDPP - freedos port to modern C++ - * Copyright (C) 2018 Stas Sergeev (stsp) + * Copyright (C) 2018-2023 Stas Sergeev (stsp) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -109,9 +109,6 @@ init_near_wrp: global plt_init plt_init: - push bp - mov bp, sp - ; copy plt entry mov ax, FDPP_BS_SEG mov es, ax @@ -119,24 +116,4 @@ plt_init: mov bx, [es:FDPP_BS_OFF+FDPP_PLT_OFFSET+2] mov [fdpp_plt], ax mov [fdpp_plt + 2], bx - - mov al, 0 ; reserved - mov ah, FDPP_KERNEL_VERSION - mov bx, 0 ; reserved - ; fill in struct fdpp_symtab - push word IGROUP - push word init_near_wrp ; near_wrp[1] - push word TGROUP - push word near_wrp ; near_wrp[0] - ; fdpp_symtab filled - mov si, sp - - push ds - mov di, DGROUP - mov ds, di - call far [es:FDPP_BS_OFF+FDPP_PLT_OFFSET+4] - pop ds - - mov sp, bp - pop bp ret diff --git a/fdpp/thunks.cc b/fdpp/thunks.cc index 2c447b68..e1da9218 100644 --- a/fdpp/thunks.cc +++ b/fdpp/thunks.cc @@ -1,6 +1,6 @@ /* * FDPP - freedos port to modern C++ - * Copyright (C) 2018 Stas Sergeev (stsp) + * Copyright (C) 2018-2023 Stas Sergeev (stsp) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -42,8 +42,8 @@ struct asm_dsc_s { struct asm_dsc_s *asm_tab; #define asm_tab_len num_cthunks static farhlp sym_tab; -static struct far_s *near_wrp; #define num_wrps 2 +static struct far_s near_wrp[num_wrps]; static int recur_cnt; enum { ASM_OK, ASM_NORET, ASM_ABORT, PING_ABORT }; @@ -131,10 +131,6 @@ int is_dos_space(const void *ptr) return fdpp->is_dos_space(ptr); } -struct fdpp_symtab { - struct far_s near_wrp[2]; -}; - static void do_relocs(UWORD old_seg, uint8_t *start_p, uint8_t *end_p, uint16_t delta) { @@ -174,27 +170,9 @@ static void do_relocs(UWORD old_seg, uint8_t *start_p, uint8_t *end_p, fdlogprintf("processed %i relocs\n", reloc); } -static void FdppSetSymTab(struct fdpp_symtab *symtab) -{ - free(near_wrp); - near_wrp = (struct far_s *)malloc(sizeof(struct far_s) * num_wrps); - memcpy(near_wrp, symtab->near_wrp, sizeof(struct far_s) * num_wrps); -} - int FdppCtrl(int idx, struct vm86_regs *regs) { -#define DL_SET_SYMTAB 0 - switch (idx) { - case DL_SET_SYMTAB: - if (HI_BYTE(regs->eax) != FDPP_KERNEL_VERSION) { - fdloudprintf("\nfdpp version mismatch: expected %i, got %i\n", - FDPP_KERNEL_VERSION, HI_BYTE(regs->eax)); - _fail(); - } - FdppSetSymTab( - (struct fdpp_symtab *)so2lin(regs->ss, LO_WORD(regs->esi))); - return 0; - } + // so empty then??? return -1; } @@ -757,6 +735,11 @@ const void *FdppKernelReloc(void *handle, uint16_t seg) asm_tab[i].off = off; } + f.off = elf_getsymoff(h->elf, "near_wrp"); + near_wrp[0] = f; + f.off = elf_getsymoff(h->elf, "init_near_wrp"); + near_wrp[1] = f; + return h->start; }