/**************************************************************** * * * Copyright 2001, 2009 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include "gtm_unistd.h" #include "gtm_stdio.h" #include "gtm_string.h" #include #include "rtnhdr.h" #include "compiler.h" #include "urx.h" #include "objlabel.h" /* needed for masscomp.h */ #include "masscomp.h" #include "gtmio.h" #include "incr_link.h" #include "min_max.h" /* MIDENT_CMP needs MIN */ #include "cmd_qlf.h" /* needed for CQ_UTF8 */ #include "gtm_text_alloc.h" /* INCR_LINK - read and process a mumps object module. Link said module to currently executing image */ LITREF char gtm_release_name[]; LITREF int4 gtm_release_name_len; static char *code; GBLREF mident_fixed zlink_mname; GBLREF unsigned char *zl_lab_err; GBLREF boolean_t gtm_utf8_mode; #define RELREAD 50 /* number of relocation entries to buffer */ typedef struct res_list_struct { struct res_list_struct *next, *list; unsigned int addr, symnum; } res_list; void res_free(res_list *root); bool addr_fix(int file, struct exec *fhead, urx_rtnref *urx_lcl, rhdtyp *code); void zl_error(int4 file, int4 err, int4 err2, int4 len, char *addr); bool incr_link(int file_desc) { rhdtyp *hdr, *old_rhead; int code_size, save_errno, cnt; int4 rhd_diff,lab_miss_off,*olnt_ent,*olnt_top, read_size; char *literal_ptr; var_tabent *curvar; char module_name[SIZEOF(mident_fixed)]; lab_tabent *lbt_ent, *lbt_bot, *lbt_top, *olbt_ent, *olbt_bot, *olbt_top, *curlab; urx_rtnref urx_lcl_anchor; int order; struct exec file_hdr; error_def(ERR_INVOBJ); error_def(ERR_LOADRUNNING); error_def(ERR_TEXT); urx_lcl_anchor.len = 0; urx_lcl_anchor.addr = 0; urx_lcl_anchor.lab = 0; urx_lcl_anchor.next = 0; code = NULL; DOREADRL(file_desc, &file_hdr, SIZEOF(file_hdr), read_size); if (read_size != SIZEOF(file_hdr)) { if (-1 == read_size) { save_errno = errno; zl_error(file_desc, ERR_INVOBJ, ERR_TEXT, strlen(STRERROR(save_errno)), STRERROR(save_errno)); } else zl_error(file_desc, ERR_INVOBJ, ERR_TEXT, RTS_ERROR_TEXT("reading file header")); } else if (OMAGIC != file_hdr.a_magic) zl_error(file_desc, ERR_INVOBJ, ERR_TEXT, RTS_ERROR_TEXT("bad magic")); else if (OBJ_LABEL != file_hdr.a_stamp) return FALSE; /* wrong version */ assert (file_hdr.a_bss == 0); code_size = file_hdr.a_text + file_hdr.a_data; code = GTM_TEXT_ALLOC(code_size); DOREADRL(file_desc, code, code_size, read_size); if (read_size != code_size) { if (-1 == read_size) { save_errno = errno; zl_error(file_desc, ERR_INVOBJ, ERR_TEXT, strlen(STRERROR(save_errno)), STRERROR(save_errno)); } else zl_error(file_desc, ERR_INVOBJ, ERR_TEXT, RTS_ERROR_TEXT("reading code")); } hdr = (rhdtyp *)code; if (memcmp(&hdr->jsb[0], "GTM_CODE", SIZEOF(hdr->jsb))) zl_error(file_desc, ERR_INVOBJ, ERR_TEXT, RTS_ERROR_TEXT("missing GTM_CODE")); if ((hdr->compiler_qlf & CQ_UTF8) && !gtm_utf8_mode) zl_error(file_desc, ERR_INVOBJ, ERR_TEXT, RTS_ERROR_TEXT("Object compiled with CHSET=UTF-8 which is different from $ZCHSET")); if (!(hdr->compiler_qlf & CQ_UTF8) && gtm_utf8_mode) zl_error(file_desc, ERR_INVOBJ, ERR_TEXT, RTS_ERROR_TEXT("Object compiled with CHSET=M which is different from $ZCHSET")); literal_ptr = code + file_hdr.a_text; for (cnt = hdr->vartab_len, curvar = VARTAB_ADR(hdr); cnt; --cnt, ++curvar) { /* relocate the variable table */ assert(0 < curvar->var_name.len); curvar->var_name.addr += (uint4)literal_ptr; } for (cnt = hdr->labtab_len, curlab = LABTAB_ADR(hdr); cnt; --cnt, ++curlab) { /* relocate the label table */ curlab->lab_name.addr += (uint4)literal_ptr; } if (!addr_fix(file_desc, &file_hdr, &urx_lcl_anchor, hdr)) { urx_free(&urx_lcl_anchor); zl_error(file_desc, ERR_INVOBJ, ERR_TEXT, RTS_ERROR_TEXT("address fixup failure")); } if (!zlput_rname (hdr)) { urx_free(&urx_lcl_anchor); /* Copy routine name to local variable because zl_error free's it. */ memcpy(&module_name[0], hdr->routine_name.addr, hdr->routine_name.len); zl_error(file_desc, 0, ERR_LOADRUNNING, hdr->routine_name.len, &module_name[0]); } urx_add (&urx_lcl_anchor); old_rhead = (rhdtyp *) hdr->old_rhead_ptr; lbt_bot = (lab_tabent *) ((char *)hdr + hdr->labtab_ptr); lbt_top = lbt_bot + hdr->labtab_len; while (old_rhead) { rhd_diff = (char *) hdr - (char *) old_rhead; lab_miss_off = (char *)(&zl_lab_err) - rhd_diff - (char *) old_rhead; lbt_ent = lbt_bot; olnt_ent = (int4 *)((char *) old_rhead + old_rhead->lnrtab_ptr); olnt_top = olnt_ent + old_rhead->lnrtab_len; for ( ; olnt_ent < olnt_top ;olnt_ent++) *olnt_ent = lab_miss_off; olbt_bot = (lab_tabent *) ((char *) old_rhead + old_rhead->labtab_ptr); olbt_top = olbt_bot + old_rhead->labtab_len; for (olbt_ent = olbt_bot; olbt_ent < olbt_top ;olbt_ent++) { for (; lbt_ent < lbt_top; lbt_ent++) { MIDENT_CMP(&olbt_ent->lab_name, &lbt_ent->lab_name, order); if (order <= 0) break; } if ((lbt_ent < lbt_top) && !order) { olnt_ent = (int4 *)((char *) old_rhead + olbt_ent->lab_ln_ptr); assert(*olnt_ent == lab_miss_off); *olnt_ent = *((int4 *) (code + lbt_ent->lab_ln_ptr)); } } old_rhead->src_full_name = hdr->src_full_name; old_rhead->routine_name = hdr->routine_name; old_rhead->vartab_len = hdr->vartab_len; old_rhead->vartab_ptr = hdr->vartab_ptr + rhd_diff; old_rhead->ptext_ptr = hdr->ptext_ptr + rhd_diff; old_rhead->current_rhead_ptr = rhd_diff; old_rhead->temp_mvals = hdr->temp_mvals; old_rhead->temp_size = hdr->temp_size; old_rhead = (rhdtyp *) old_rhead->old_rhead_ptr; } urx_resolve (hdr, lbt_bot, lbt_top); return TRUE; } bool addr_fix(int file, struct exec *fhead, urx_rtnref *urx_lcl, rhdtyp *code) { res_list *res_root, *new_res, *res_temp, *res_temp1; char *symbols, *sym_temp, *sym_temp1, *symtop, *res_addr; struct relocation_info rel[RELREAD]; int numrel, rel_read, i, string_size, sym_size; size_t status; mident_fixed rtnid, labid; mstr rtn_str; rhdtyp *rtn; lab_tabent *label, *labtop; bool labsym; urx_rtnref *urx_rp; urx_addr *urx_tmpaddr; res_root = 0; numrel = (fhead->a_trsize + fhead->a_drsize) / SIZEOF(struct relocation_info); if (numrel * SIZEOF(struct relocation_info) != fhead->a_trsize + fhead->a_drsize) return FALSE; for ( ; numrel;) { rel_read = numrel < RELREAD ? numrel : RELREAD; DOREADRC(file, rel, rel_read * SIZEOF(struct relocation_info), status); if (0 != status) { res_free(res_root); return FALSE; } numrel -= rel_read; for (i = 0; i < rel_read; i++) { if (rel[i].r_extern) { new_res = (res_list *) malloc(SIZEOF(*new_res)); new_res->symnum = rel[i].r_symbolnum; new_res->addr = rel[i].r_address; new_res->next = new_res->list = 0; /* Insert the relocation entry in symbol number order on the unresolved chain */ if (!res_root) res_root = new_res; else { res_temp = res_root; res_temp1 = 0; while (res_temp) { if (res_temp->symnum >= new_res->symnum) break; res_temp1 = res_temp; res_temp = res_temp->next; } if (res_temp) { if (res_temp->symnum == new_res->symnum) { new_res->list = res_temp->list; res_temp->list = new_res; } else { if (res_temp1) { new_res->next = res_temp1->next; res_temp1->next = new_res; } else { assert(res_temp == res_root); new_res->next = res_root; res_root = new_res; } } } else res_temp1->next = new_res; } } else *(unsigned int *)(((char *)code) + rel[i].r_address) += (unsigned int)code; } } /* All relocations within the routine should have been done, so copy the routine_name */ assert(code->routine_name.len < SIZEOF(zlink_mname.c)); memcpy(&zlink_mname.c[0], code->routine_name.addr, code->routine_name.len); zlink_mname.c[code->routine_name.len] = 0; if (!res_root) return TRUE; if ((off_t)-1 == lseek(file, (off_t)fhead->a_syms, SEEK_CUR)) { res_free(res_root); return FALSE; } DOREADRC(file, &string_size, SIZEOF(string_size), status); if (0 != status) { res_free(res_root); return FALSE; } string_size -= SIZEOF(string_size); symbols = malloc(string_size); DOREADRC(file, symbols, string_size, status); if (0 != status) { free(symbols); res_free(res_root); return FALSE; } /* Match up unresolved entries with the null terminated symbol name entries from the * symbol text pool we just read in. */ sym_temp = sym_temp1 = symbols; symtop = symbols + string_size; for (i = 0; res_root; i++) { while (i < res_root->symnum) { /* Forward symbol space until our symnum index (i) matches the symbol we are processing in res_root */ while (*sym_temp) { if (sym_temp >= symtop) { free(symbols); res_free(res_root); return FALSE; } sym_temp++; } sym_temp++; sym_temp1 = sym_temp; i++; } assert (i == res_root->symnum); /* Find end of routine name that we care about */ while (*sym_temp1 != '.' && *sym_temp1) { if (sym_temp1 >= symtop) { free(symbols); res_free(res_root); return FALSE; } sym_temp1++; } sym_size = sym_temp1 - sym_temp; assert(sym_size <= MAX_MIDENT_LEN); memcpy(&rtnid.c[0], sym_temp, sym_size); rtnid.c[sym_size] = 0; if (rtnid.c[0] == '_') rtnid.c[0] = '%'; assert(sym_size != mid_len(&zlink_mname) || 0 != memcmp(&zlink_mname.c[0], &rtnid.c[0], sym_size)); rtn_str.addr = &rtnid.c[0]; rtn_str.len = sym_size; rtn = find_rtn_hdr(&rtn_str); /* Routine already resolved? */ sym_size = 0; labsym = FALSE; if (*sym_temp1 == '.') { /* If symbol is for a label, find the end of the label name */ sym_temp1++; sym_temp = sym_temp1; while (*sym_temp1) { if (sym_temp1 >= symtop) { free(symbols); res_free(res_root); return FALSE; } sym_temp1++; } sym_size = sym_temp1 - sym_temp; assert(sym_size <= MAX_MIDENT_LEN); memcpy(&labid.c[0], sym_temp, sym_size); labid.c[sym_size] = 0; if (labid.c[0] == '_') labid.c[0] = '%'; labsym = TRUE; } sym_temp1++; sym_temp = sym_temp1; if (rtn) { /* The routine part at least is known */ if (labsym) { /* Look our target label up in the routines label table */ label = (lab_tabent *)((char *) rtn + rtn->labtab_ptr); labtop = label + rtn->labtab_len; for (; label < labtop && (sym_size != label->lab_name.len || memcmp(&labid.c[0], label->lab_name.addr, sym_size)); label++) ; if (label < labtop) res_addr = (char *)(label->LABENT_LNR_OFFSET + (char *)rtn); else res_addr = 0; } else res_addr = (char *)rtn; if (res_addr) { /* The external symbol definition is available. Resolve all references to it */ res_temp = res_root->next; while(res_root) { *(uint4 *)(((char *)code) + res_root->addr) = (unsigned int) res_addr; res_temp1 = res_root->list; free(res_root); res_root = res_temp1; } res_root = res_temp; continue; } } /* This symbol is unknown. Put on the (local) unresolved extern chain -- either for labels or routines */ urx_rp = urx_putrtn(rtn_str.addr, rtn_str.len, urx_lcl); res_temp = res_root->next; while(res_root) { if (labsym) urx_putlab(&labid.c[0], sym_size, urx_rp, ((char *)code) + res_root->addr); else { urx_tmpaddr = (urx_addr *) malloc(SIZEOF(urx_addr)); urx_tmpaddr->next = urx_rp->addr; urx_tmpaddr->addr = (INTPTR_T *)(((char *)code) + res_root->addr); urx_rp->addr = urx_tmpaddr; } res_temp1 = res_root->list; free(res_root); res_root = res_temp1; } res_root = res_temp; } free(symbols); return TRUE; } void res_free(res_list *root) { res_list *temp; while (root) { while (root->list) { temp = root->list->list; free(root->list); root->list = temp; } temp = root->next; free(root); root = temp; } } /* ZL_ERROR - perform cleanup and signal errors found in zlinking a mumps object module * err - an error code that accepts no arguments and * err2 - an error code that accepts two arguments (!AD) */ void zl_error(int4 file, int4 err, int4 err2, int4 len, char *addr) { int rc; if (code) { GTM_TEXT_FREE(code); code = NULL; } CLOSEFILE_RESET(file, rc); /* resets "file" to FD_INVALID */ if (0 != err && 0 != err2) rts_error(VARLSTCNT(6) err, 0, err2, 2, len, addr); else if (0 != err) rts_error(VARLSTCNT(1) err); else { assert(0 != err2); rts_error(VARLSTCNT(4) err2, 2, len, addr); } }