diff -cr yamd-0.32/Makefile yamd-s390/Makefile *** yamd-0.32/Makefile Thu Oct 14 17:44:51 1999 --- yamd-s390/Makefile Tue Mar 20 08:48:37 2001 *************** *** 5,11 **** CXX = PREFIX = ! CFLAGS = -Wall -W -O2 CXXFLAGS = $(CFLAGS) LDFLAGS= --- 5,11 ---- CXX = PREFIX = ! CFLAGS = -Wall -W -g CXXFLAGS = $(CFLAGS) LDFLAGS= diff -cr yamd-0.32/README yamd-s390/README *** yamd-0.32/README Thu Oct 14 17:44:51 1999 --- yamd-s390/README Tue Mar 20 08:48:37 2001 *************** *** 295,300 **** --- 295,321 ---- I have not tested the run-yamd shell script with any shell except bash. If it needs changes, please let me know. + + S390 Notes + ========== + + YAMD requires a kernel patch to pass the values of trap_no and prot_addr to YAMD. + The patch is called linux-2.2.16-signal and currently is included with other + patches in a gzipped tar file called alpha-patches.tar.gz which is located at + http://www10.software.ibm.com/developerworks/opensource/linux390/alpha_src.html. + You must apply the patch and rebuild the kernel to run YAMD. + + YAMD for S390 has an extra environment variable, YAMD_SRC_DIRS, which may be + set which will show source code which corresponds to the file name and line + numbers indicated in the YAMD traceback. YAMD_SRC_DIRS is a colon delimited + list of directories containing the source files from which your executable was + built. Each source directory must be specified; YAMD will not search the + sub-directories of the directories specified in YAMD_SRC_DIRS. + + Problems and comments with the S390 changes can be directed to + + + Bugs ---- diff -cr yamd-0.32/do-syms.c yamd-s390/do-syms.c *** yamd-0.32/do-syms.c Thu Oct 14 17:44:51 1999 --- yamd-s390/do-syms.c Tue Mar 20 08:48:37 2001 *************** *** 1,11 **** /* do-syms.c: Utility program to symificate yamd output. */ - - /* This file and the rest of YAMD is copyright (C) 1999 by Nate Eldredge. */ /* There is no warranty whatever; I disclaim responsibility for any */ /* damage caused. Released under the GNU General Public License (see the */ /* file COPYING). */ #include #include #include --- 1,15 ---- /* do-syms.c: Utility program to symificate yamd output. */ /* This file and the rest of YAMD is copyright (C) 1999 by Nate Eldredge. */ /* There is no warranty whatever; I disclaim responsibility for any */ /* damage caused. Released under the GNU General Public License (see the */ /* file COPYING). */ + /* + * s390 changes: + * (C)Copyright 2001 IBM Poughkeepsie, IBM Corporation + * Author(s): Greg Lambert (slate@us.ibm.com) + */ + #include #include #include *************** *** 14,21 **** --- 18,34 ---- #include #endif + #ifdef __s390__ + #include + #include + #include + #define MAX_FILENAME 255 + #define MAX_FQFILENAME 3000 + #endif + #define MAXLINE 500 + typedef unsigned long addr; #ifdef __linux__ *************** *** 125,130 **** --- 138,227 ---- } #endif + + #ifdef __s390__ + /* This function will search directories specified by and environment variable(YAMD_SRC_DIRS) in order to show + * the source code associated with mallocs, frees, errors, etc. This is function was created for s390 because + * the s390 architecture does not capture whether an attempted access of protected memory was a read or a write + * the way i386 does so showing the source code should help the user determine that information */ + + void parse(char* symbuf) + { + char *tok = (char *)malloc(MAX_FQFILENAME); + char *fqfname = (char *)malloc(MAX_FQFILENAME); + char *path, *spath, *dir, *fname, *line; + char srcpath_buf[MAX_FQFILENAME], readline_buf[MAXLINE]; + int count, source_line, ct, found_file_flag = 0; + FILE *fileptr; + + if(symbuf[0] != '/' || symbuf[strlen(symbuf)-1] != ')') + return; + + tok= strtok(symbuf, ":"); /* symbuf is the fully qualified file name + * followed by a : then the line number. This + * statement set tok to the fully qualiefied file + * name (fqfname). */ + + /* find position of '\0' in tok string to determine number of characters to + * copy to fqfname */ + for(ct = 0; *(tok+ct) != '\0'; ct++); + + memcpy(fqfname,tok,ct+1); /* copy to fqfname then continue to tokenize tok + to get the line number */ + line = strtok( NULL, "("); + + + tok = strtok(tok, "/"); /* tok is still a pointer to the beginning of symbuf + now we are tokenizing it to get just to the file + name. */ + while(tok) + { + fname = tok; + tok = strtok(NULL, "/"); + } + + + + if( !(spath = getenv("YAMD_SRC_DIRS")) || (spath[0] == '\0')) + /* if the environment variable YAMD_SRC_DIRS is not set or if it is empty + print a message indicating this and exit the function */ + { + + printf("\tCannot Locate Source Code - YAMD_SRC_DIRS is not set\n\n"); + + } + else /* Search the directories indicated by the YAMD_SRC_DIRS environment variable*/ + { + path = (char *)malloc(strlen(spath) + 1); + strcpy(path, spath); + + for(dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) + { + sprintf(srcpath_buf, "%s/%s", dir, fname); + fileptr = fopen( srcpath_buf, "r" ); + + if( fileptr != NULL ) + { + found_file_flag = 1; /*file found and opened*/ + source_line = atoi(line); + for(count = 0; count < source_line; count++) + fgets(readline_buf, MAXLINE, fileptr); + fclose(fileptr); + printf("\n\t%s:%i\t\t%s\n", fname, source_line, readline_buf); + break; + } + } /* end of for(dir = strtok(path, ":"); ... loop */ + + if(!found_file_flag) /* source file is not found or cannot be opened */ + { + printf("\tCannot Locate Source Code\n\n"); + found_file_flag = 0; + } + } /* end of else Search the directories indicated by the YAMD_SRC_DIRS .. */ + + } /* end of parse function */ + #endif + int process(char *exe, FILE *in, FILE *out) { char inbuf[MAXLINE]; *************** *** 151,160 **** || (sscanf(inbuf, " 0x%lx\n", &a) == 1)) { if (get_sym(a, symbuf) >= 0) ! fputs(symbuf, out); } fputs(inbuf, out); ! } end_syms(); return 0; } --- 248,262 ---- || (sscanf(inbuf, " 0x%lx\n", &a) == 1)) { if (get_sym(a, symbuf) >= 0) ! { ! fputs(symbuf, out); ! } } fputs(inbuf, out); ! #ifdef __s390__ ! parse(symbuf); ! #endif ! } end_syms(); return 0; } diff -cr yamd-0.32/yamd.c yamd-s390/yamd.c *** yamd-0.32/yamd.c Thu Oct 14 17:44:51 1999 --- yamd-s390/yamd.c Mon May 21 11:02:05 2001 *************** *** 8,18 **** --- 8,25 ---- /* damage caused. Released under the GNU General Public License (see the */ /* file COPYING). */ + /* + * s390 changes: + * (C)Copyright 2001 IBM Poughkeepsie, IBM Corporation + * Author(s): Greg Lambert (slate@us.ibm.com) + */ + /* Headers */ #define _GNU_SOURCE + #include #include #include #include *************** *** 261,266 **** --- 268,286 ---- WRAPPER_LINKAGE void * WRAP(memalign) (size_t align, size_t size); #endif + #ifdef __s390__ + /* + * Headers and flags for wrappers for functions that do not use standard linkage + */ + unsigned long saved_next_instr; + addr *t_o_prot_addr; + unsigned long reg; + + const int one = 1; + + int one_step(void); + #endif //__s390__ + /* Hook to ensure we get linked. The asm is to avoid underscore troubles. */ int __yamd_hook_1 asm ("__yamd_hook_1") = 0; *************** *** 300,306 **** --- 320,330 ---- static void do_free_block(block *b, addr orig_caller, unsigned by_who); static void print_footer(void); static void print_header(void); + #ifdef __s390__ + static void handle_page_fault(addr address, addr eip, addr ebp); + #else static void handle_page_fault(addr address, int write, addr eip, addr ebp); + #endif static void describe_address(int level, addr a); static void *lite_malloc(size_t n); static void lite_free(void *p); *************** *** 412,418 **** zap(addr p, size_t nbytes) { int v; ! /* fprintf(stderr, "Doing mprotect(%p, %u, PROT_NONE)\n", p, nb); */ v = mprotect((void *)p, nbytes, PROT_NONE); if (v != 0) perror("unmap: mprotect"); --- 436,442 ---- zap(addr p, size_t nbytes) { int v; ! /*fprintf(stderr, "Doing mprotect(%p, %u, PROT_NONE)\n", p, nb);*/ v = mprotect((void *)p, nbytes, PROT_NONE); if (v != 0) perror("unmap: mprotect"); *************** *** 760,765 **** --- 784,850 ---- `addr *', so we aren't referring to the same object via pointers of different types. Anyone who disagrees, please let me know. */ + #ifdef __s390__ + static void + generate_any_traceback(TRACEBACK tb, addr start_eip, addr start_ebp, + int eip_on_stack) + { + /* Wow. Here be lots of ugly typecasts. */ + addr ebp; + addr last_ebp; + addr eip; + addr cip; + size_t i; + + if (eip_on_stack) + { + last_ebp = 0; + ebp = start_ebp; + eip = 0; /* In case we abort immediately */ + /* The last test really needs to be done only once, but this + is cleaner */ + while (ebp > last_ebp && STACK_ADDR_OK(ebp)) + { + eip = *((addr *)ebp + 14) & 0x7fffffff; + last_ebp = ebp; + ebp = (*(addr *)ebp); + if ( eip == start_eip) + break; + } + if ( eip != start_eip) + { + /* We broke out because the frame address went wrong, or maybe + we reached the top. Assume start_eip is right, but don't + go any farther than that. */ + tb[0] = start_eip; + tb[1] = 0; + return; + } + } + else + { + eip = start_eip; + ebp = start_ebp; + } + + i = 0; + last_ebp = 0; + if( eip < 0x7f000000 ) + tb[i++] = eip - 2; /* Log the first one */ + + /* The last test really needs to be done only once, but this + is cleaner */ + while (i < MAX_TRACEBACK_LEVELS - 1 && ebp > last_ebp && STACK_ADDR_OK(ebp)) + { + cip = (*((addr *)ebp + 14) - 2) & 0x7fffffff; + if( cip < 0x7f000000 ) // eliminate obviously incorrect instruction addresses + tb[i++] = cip; + last_ebp = ebp; + ebp = *(addr *)ebp; + } + tb[i] = 0; + } + #else static void generate_any_traceback(TRACEBACK tb, addr start_eip, addr start_ebp, int eip_on_stack) *************** *** 815,820 **** --- 900,906 ---- } tb[i] = 0; } + #endif /* The standard case, where we want a traceback of our callers */ static void *************** *** 843,849 **** #endif log_detail(level, "END TRACEBACK\n"); } - static void do_any_traceback(int level, addr eip, addr ebp, int eip_os) { --- 929,934 ---- *************** *** 1109,1115 **** --- 1194,1207 ---- if (WAS_LITE_MODE) p = lite_malloc(n); else + #ifdef __s390__ + p = do_malloc(n, default_alignment, + ((addr)__builtin_return_address(0) & 0x7fffffff), + BY_MALLOC, NULL); + #else p = do_malloc(n, default_alignment, (addr)__builtin_return_address(0), BY_MALLOC, NULL); + #endif + LEAVE(); return p; } *************** *** 1170,1176 **** --- 1262,1273 ---- { log_event(LOG_ERR, "Ridiculous alignment"); log_detail(LOG_ERR, "At\n"); + #ifdef __s390__ + do_traceback(LOG_ERR, + (addr)__builtin_return_address(0) & 0x7fffffff); + #else do_traceback(LOG_ERR, (addr)__builtin_return_address(0)); + #endif log_detail(LOG_ERR, "attempt to memalign with %u byte alignment, which is way too big\n", alignment); LEAVE(); *************** *** 1182,1188 **** --- 1279,1290 ---- size_t new_alignment = 1UL << (t + 1); log_event(LOG_ERR, "Alignment not power of 2"); log_detail(LOG_ERR, "At\n"); + #ifdef __s390__ + do_traceback(LOG_ERR, + (addr)__builtin_return_address(0) & 0x7fffffff); + #else do_traceback(LOG_ERR, (addr)__builtin_return_address(0)); + #endif log_detail(LOG_ERR, "Attempt to memalign with %u byte alignment, which is not a power of 2\n", alignment); log_detail(LOG_ERR, "Using alignment of %u instead\n", new_alignment); if (new_alignment > WAY_TOO_BIG) *************** *** 1193,1199 **** --- 1295,1307 ---- } alignment = new_alignment; } + #ifdef __s390__ + p = do_malloc(size, alignment, + (addr)__builtin_return_address(0) & 0x7fffffff, + BY_MEMALIGN, NULL); + #else p = do_malloc(size, alignment, (addr)__builtin_return_address(0), BY_MEMALIGN, NULL); + #endif } LEAVE(); return p; *************** *** 1300,1309 **** --- 1408,1428 ---- return; } ENTER(); + #ifdef __s390__ + if (WAS_LITE_MODE) + lite_free(p); + else if ((b = block_to_free(p, + (addr)__builtin_return_address(0) & 0x7fffffff, + BY_FREE)) != NULL) + do_free_block(b, + ((addr)__builtin_return_address(0) & 0x7fffffff), + BY_FREE); + #else if (WAS_LITE_MODE) lite_free(p); else if ((b = block_to_free(p, (addr)__builtin_return_address(0), BY_FREE)) != NULL) do_free_block(b, (addr)__builtin_return_address(0), BY_FREE); + #endif LEAVE(); } *************** *** 1381,1387 **** --- 1500,1510 ---- if (WAS_LITE_MODE) q = lite_realloc(p, s); else + #ifdef __s390__ + q = do_realloc(p, s, (addr)__builtin_return_address(0) & 0x7fffffff); + #else q = do_realloc(p, s, (addr)__builtin_return_address(0)); + #endif LEAVE(); return q; } *************** *** 1456,1466 **** --- 1579,1660 ---- } /* Note that using cr2 here assumes that the base of DS is 0. Thank God for systems with real memory mapping... */ + + + + handle_page_fault(ctx.cr2, ctx.err & 2, ctx.eip, ctx.ebp); return; /* Resume the faulting instruction; it may or may not have been fixed up */ } #endif + #ifdef __s390__ + static void sigsegv_handler(int signum, struct sigcontext ctx, int trap_no, + unsigned long prot_addr) + { + unsigned long g = 0x42100201; // This instruction is a stc to real + // address 513. It will cause a seg + // fault that will return control to + // this handler. We insert this as a + // breakpoint after a failing instruction + // so that we can turn the protection + // back on. + + int ilc = 0; // S390 instruction length code + int instr_len = 0; + + + (void)signum; /* shut the compiler up */ + /* Find out if this is a fault we're trying to catch */ + if (!(trap_no == 0x04 || trap_no == 0x10 || trap_no == 0x11) /* Not a page fault */ + || !(ctx.sregs->regs.psw.mask & 0x00010000)) /* Not a user access - + problem state not set*/ + { + disclaimer(); + return; + } + + reg = ctx.sregs->regs.psw.addr&0x7fffffff; // get the address of the failing + // instruction + + ilc = (int)(*(addr*)reg >> 30); // get the ilc + + switch(ilc) { // determine the failing + case 0: // instruction length based on + instr_len = 2; // the ilc + break; + case 1: case 2: + instr_len = 4; + break; + case 3: + instr_len = 6; + break; + } + + if((int)*(addr*)reg == (int)g) { // check if failing instruction + // is the 'breakpoint' we inserted + + mprotect(t_o_prot_addr,4096,PROT_NONE); // turn protection back on + + memcpy((addr*)reg, &saved_next_instr, instr_len); // copy real next + // instruction back in + + signal(SIGSEGV, (void (*)(int))sigsegv_handler); // re-register the signal handler + return; + } + + memcpy((addr*)&saved_next_instr,(addr*)(reg + instr_len),// save the real next instruction + instr_len); + + memcpy((addr*)(reg + instr_len),&g, instr_len); // copy the 'breakpoint' in as the + // next instruction + + handle_page_fault(prot_addr, ctx.sregs->regs.psw.addr&0x7fffffff, + ctx.sregs->regs.gprs[15]); + return; /* Resume the faulting instruction; it may or may not have + been fixed up */ + } + #endif /*__s390__ */ #endif #ifdef DJGPP_SIGNAL *************** *** 1522,1527 **** --- 1716,1767 ---- } #endif + #ifdef __s390__ + static void + handle_page_fault(addr address, addr eip, addr ebp) + { + block *b; + if (SHOULD_NOT_CATCH) + { + /* If YAMD shouldn't run, we're probably inside it and so + the crash was caused by us. */ + signal(SIGSEGV, SIG_DFL); + return; + } + NO_CATCH(); /* is this really correct? */ + if (!(b = find_block_by_any_addr(address))) + { + disclaimer(); + return; + } + signal(SIGSEGV, SIG_DFL); + log_event(LOG_ERR, "Crash"); + do_any_traceback(LOG_ERR, eip, ebp, 0); + log_detail(LOG_ERR, "Tried to access address on page at " + POINTER_FORMAT "\n", address); + describe_address(LOG_ERR, address); + + + log_detail(LOG_ERR, "Will dump core after checking heap.\n"); + check_heap(); + #if 0 /* Tread lightly on the core dump */ + print_footer(); + #endif + log_flush(); + + mprotect((void *)address, 4096, PROT_READ|PROT_WRITE); // turn memory + // protection off + + t_o_prot_addr = (addr*)address; // save address where protection + // where protection needs to be + // turned back on + OK_CATCH(); + + signal(SIGSEGV, (void (*)(int))sigsegv_handler); // re-register signal handler + + return; /* Resume the faulting instruction */ + } + #else static void handle_page_fault(addr address, int write, addr eip, addr ebp) { *************** *** 1553,1558 **** --- 1793,1799 ---- log_flush(); return; /* Resume the faulting instruction */ } + #endif #define ENVIRON_INT 1 #define ENVIRON_STRING 2 *************** *** 1873,1875 **** --- 2114,2313 ---- log_flush(); close(log_fd); } + + #ifdef __s390__ + /*WRAPPERS FOR FUNCTIONS THAT DO NOT USE STANDARD LINKAGE + These wrappers are for funtions that may read or write outside the boundries + of allocated memory. These functions are implemented in assembler without + standard entry and exit linkage so the back stack trace is incorrect. These + wrappers place a stack frame between the caller of these fuctions and the + actual assembler implementation so that the caller is indicated correctly in + the stack trace. */ + + __ptr_t memcpy(__ptr_t to, __const __ptr_t from, size_t N) + { + asm("ltr %r5,%r4\n\t" + "jz .Lb\n\t" + "lr %r4,%r3\n\t" // %r4/%r5 = source ptr/len + "lr %r3,%r5\n\t" // %r2/%r3 = dest ptr/len + "lr %r0,%r2\n\t" // save source address + ".La: mvcle %r2,%r4,0\n\t" // that's it MVCLE is your friend + "jo .La\n\t" + "lr %r2,%r0\n\t" // return value is source address + ".Lb:\n\t"); + + from = from; N = N; // use these variables explicitly so there + // are no compiler warnings about unused variables + + return to; + } + + __ptr_t memset(__ptr_t to, int c, size_t N) + { + asm("ltr %r4,%r4\n\t" + "jz .Ld\n\t" + "lr %r0,%r2\n\t" // save source address + "lr %r1,%r3\n\t" // move pad byte to R1 + "lr %r3,%r4\n\t" + "sr %r4,%r4\n\t" // no source for MVCLE, only a pad byte + "sr %r5,%r5\n\t" + ".Lc: mvcle %r2,%r4,0(%r1)\n\t" // thats it, MVCLE is your friend + "jo .Lc\n\t" + "lr %r2,%r0\n\t" // return value is source address + ".Ld:\n\t"); + + c = c; N = N; // use these variables explicitly so there + // are no compiler warnings about unused variables + + return to; + } + + __ptr_t memchr(__const __ptr_t to, int c, size_t N) + { + asm(" lr %r0,%r3\n\t" + " lr %r1,%r2\n\t" + " la %r2,0(%r4,%r1)\n\t" + "2: srst %r2,%r1\n\t" + " jo 2b\n\t" + " brc 13,3f\n\t" + " slr %r2,%r2\n\t" + "3:\n\t"); + + + c = c; N = N; // use these variables explicitly so there + // are no compiler warnings about unused variables + + return to; + } + + char *strcpy(char *__dest, __const char *__src) + { + asm ("slr 0,0\n\t" + "lr 1,2\n\t" + "0: mvst 1,3\n\t" + "jo 0b\n\t" + ); + + __src = __src; // use this variable explicitly so there + // are no compiler warnings about unused variables + + return __dest; + } + + + char *strncpy(char *__dest, __const char *__src, size_t __n) + { + asm("st %r2,24(%r15)\n\t" // save dst pointer + "slr %r2,%r3\n\t" // %r3 points to src, %r2+%r3 to dst + "lhi %r1,3\n\t" + "nr %r1,%r4\n\t" // last 2 bits of # bytes + "srl %r4,2\n\t" + "ltr %r4,%r4\n\t" // less than 4 bytes to copy ? + "jz .L1\n\t" + "bras %r5,.L0\n\t" // enter loop & load address of a 0 + ".long 0\n\t" + ".L0: icm %r0,8,0(%r3)\n\t" // first byte + "jz .L3\n\t" + "icm %r0,4,1(%r3)\n\t" // second byte + "jz .L4\n\t" + "icm %r0,2,2(%r3)\n\t" // third byte + "jz .L5\n\t" + "icm %r0,1,3(%r3)\n\t" // fourth byte + "jz .L6\n\t" + "st %r0,0(%r2,%r3)\n\t" // store all four to dest. + "la %r3,4(%r3)\n\t" + "brct %r4,.L0\n\t" + ".L1: ltr %r1,%r1\n\t" + "jz .Lexit\n\t" + ".L2: icm %r0,1,0(%r3)\n\t" + "stc %r0,0(%r2,%r3)\n\t" + "la %r3,1(%r3)\n\t" + "jz .L7\n\t" + "brct %r1,.L2\n\t" + "j .Lexit\n\t" + ".L3: icm %r0,4,0(%r5)\n\t" + ".L4: icm %r0,2,0(%r5)\n\t" + ".L5: icm %r0,1,0(%r5)\n\t" + ".L6: st %r0,0(%r2,%r3)\n\t" + "la %r3,4(%r3)\n\t" + "ahi %r4,-1\n\t" + "j .L8\n\t" + ".L7: ahi %r1,-1\n\t" + ".L8: sll %r4,2\n\t" + "alr %r4,%r1\n\t" + "alr %r2,%r3\n\t" // start of dst area to be zeroed + "lr %r3,%r4\n\t" + "slr %r4,%r4\n\t" + "slr %r5,%r5\n\t" + ".Lg: mvcle %r2,%r4,0\n\t" // pad dst with zeroes + "jo .Lg\n\t" + ".Lexit: l %r2,24(%r15)\n\t" // return dst pointer + ); + + __src = __src; __n = __n; // use these variables explicitly so there + // are no compiler warnings about unused variables + + return __dest; + } + + void bzero(__ptr_t __s, size_t __n) + { + asm( "ltr %r3,%r3\n\t" + "jz .Ln\n\t" + "sr %r1,%r1\n\t" // set pad byte to zero + "sr %r4,%r4\n\t" // no source for MVCLE, only a pad byte + "sr %r5,%r5\n\t" + ".Lm: mvcle %r2,%r4,0(%r1)\n\t" // thats it, MVCLE is your friend + "jo .Lm\n\t" + ".Ln:\n\t" + ); + __s = __s; __n = __n; // use these variables explicitly so there + // are no compiler warnings about unused variables + } + + void bcopy(__const __ptr_t __s,__ptr_t __d, size_t __n) + { + asm( "clr %r2,%r3\n\t" // check against destructive overlap + "jnl .Le\n\t" + "lr %r1,%r2\n\t" + "alr %r1,%r4\n\t" + "clr %r1,%r3\n\t" + "jh .Lh\n\t " + ".Le:\n\t" + "lr %r5,%r4\n\t" // source length + "lr %r4,%r2\n\t" // source address + "sr %r1,%r1\n\t" // set pad byte to zero + "lr %r2,%r3\n\t" // set destination + "lr %r3,%r5\n\t" // destination length = source length + ".Lf: mvcle %r2,%r4,0(%r1)\n\t" // thats it, MVCLE is your friend + "jo .Lf\n\t" + "br %r14\n\t" + ".Lh:\n\t" // destructive overlay, can not use mvcle + "lr %r1,%r2\n\t" // bcopy is called with source,dest + "lr %r2,%r3\n\t" // memmove with dest,source! Oh, well... + "lr %r3,%r1\n\t" + "basr %r1,0\n\t" + ".Li:\n\t" + ); + + #ifdef PIC + asm( "al %r1,.Lj-.Li(%r1)\n\t" // get address of global offset table + // load address of memmove + "l %r1,memmove@GOT12(%r1)\n\t" + "br %r1\n\t" + ".Lj: .long _GLOBAL_OFFSET_TABLE_-.Li\n\t" + ); + #else + asm( "al %r1,.Lj-.Li(%r1)\n\t" // load address of memmove + "br %r1\n\t" // jump to memmove + ".Lj: .long memmove-.Li\n\t" + ); + #endif + + __s = __s; __d = __d; __n = __n; // use these variables explicitly so there + // are no compiler warnings about unused variables + + } + + #endif //__s390__ +