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 
+ <slate@us.ibm.com>
+ 
+ 
  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 <stdio.h>
  #include <unistd.h>
  #include <sys/types.h>
--- 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 <stdio.h>
  #include <unistd.h>
  #include <sys/types.h>
***************
*** 14,21 ****
--- 18,34 ----
  #include <debug/syms.h>
  #endif
  
+ #ifdef __s390__
+ #include <stdlib.h>
+ #include <string.h>
+ #include <sys/stat.h>
+ #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 <dlfcn.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
***************
*** 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__
+  
