(CU+5909): Apply new conventions for glyph granularity for components
[chise/xemacs-chise.git.1] / src / unexelfsgi.c
1 /* Copyright (C) 1985, 1986, 1987, 1988, 1990, 1992, 1999, 2000
2    Free Software Foundation, Inc.
3
4    This file is part of XEmacs.
5
6    XEmacs is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2, or (at your option)
9    any later version.
10
11    GNU Emacs is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with GNU Emacs; see the file COPYING.  If not, write to the
18    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.
20
21    In other words, you are welcome to use, share and improve this
22    program.  You are forbidden to forbid anyone else to use, share and
23    improve what you give them.  Help stamp out software-hoarding!  */
24
25
26 /*
27  * unexec.c - Convert a running program into an a.out file.
28  *
29  * Author:      Spencer W. Thomas
30  *              Computer Science Dept.
31  *              University of Utah
32  * Date:        Tue Mar  2 1982
33  * Modified heavily since then.
34  *
35  * Synopsis:
36  * void
37  * unexec (char *new_name,
38  *         char *old_name,
39  *         uintptr_t data_start,
40  *         uintptr_t bss_start,
41  *         uintptr_t entry_address)
42  *
43  * The basic idea is that we start with an ELF file which contains
44  * .bss (uninitialized global data) section which is normally not in
45  * the file. As we load lisp the variables, which were first set to 0,
46  * will change their values. We want to save those changed values into
47  * another ELF file, which will become a new xemacs image. To do this,
48  * we need to change several structures in the ELF file.
49  *
50  *   First of all, we need to change the programm header which tells
51  *   the linker how to load stuff into memory so that data will come
52  *   from the file and not from the /dev/zero. To do this, we find the
53  *   segment, which is marked as loadable (type PT_LOAD) and which
54  *   covers the old .bss section. We will next change the filesz and
55  *   memsz for that segment to extend over the new data section.
56  *
57  *   Next we have to make sure that section header for the stuff which
58  *   used to be uninitialized is changed to be initialized and to come
59  *   from the file. To do this, we change the size and the type of the old
60  *   .bss section (and all other section of the type SHT_NOBITS) to cover the
61  *   new section and to be of type SHT_PROCBITS.
62  *
63  *   We also insert a new SHT_NOBITS section to keep some tools, which expect
64  *   .bss happy.
65  *
66  *   Finally we need to patch up some references to the section
67  *   indexes since we change the order and undo the relocation info to
68  *   be the same as it was "before" because we actually used the data
69  *   from the memory which were changed by the run-time linker.
70  */
71 \f
72 #ifndef emacs
73 #define fatal(a, b, c) fprintf (stderr, a, b, c), exit (1)
74 #include <string.h>
75 #else
76 #include <config.h>
77 extern void fatal (const char *, ...);
78 #endif
79
80 #include <sys/types.h>
81 #include <stdio.h>
82 #include <sys/stat.h>
83 #include <memory.h>
84 #include <errno.h>
85 #include <unistd.h>
86 #include <fcntl.h>
87 #ifdef HAVE_ELF_H
88 #include <elf.h>
89 #endif
90 #include <sys/mman.h>
91 #if defined (__sony_news) && defined (_SYSTYPE_SYSV)
92 #include <sys/elf_mips.h>
93 #include <sym.h>
94 #endif /* __sony_news && _SYSTYPE_SYSV */
95 #if __sgi
96 #include <syms.h> /* for HDRR declaration */
97 #endif /* __sgi */
98
99 #if __GNU_LIBRARY__ - 0 >= 6
100 # include <link.h>      /* get ElfW etc */
101 #endif
102
103 #ifndef ElfW
104 # ifdef __STDC__
105 #  define ElfBitsW(bits, type) Elf##bits##_##type
106 # else
107 #  define ElfBitsW(bits, type) Elf/**/bits/**/_/**/type
108 # endif
109 # ifdef _LP64
110 #  define ELFSIZE 64
111 # else
112 #  define ELFSIZE 32
113 # endif
114   /* This macro expands `bits' before invoking ElfBitsW.  */
115 # define ElfExpandBitsW(bits, type) ElfBitsW (bits, type)
116 # define ElfW(type) ElfExpandBitsW (ELFSIZE, type)
117 #endif
118
119 #ifndef ELF_BSS_SECTION_NAME
120 #define ELF_BSS_SECTION_NAME ".bss"
121 #endif
122
123 /* Get the address of a particular section or program header entry,
124  * accounting for the size of the entries. */
125
126 #define OLD_SECTION_H(n) \
127      (*(ElfW(Shdr) *) ((byte *) old_section_h + old_file_h->e_shentsize * (n)))
128 #define NEW_SECTION_H(n) \
129      (*(ElfW(Shdr) *) ((byte *) new_section_h + new_file_h->e_shentsize * (n)))
130 #define OLD_PROGRAM_H(n) \
131      (*(ElfW(Phdr) *) ((byte *) old_program_h + old_file_h->e_phentsize * (n)))
132 #define NEW_PROGRAM_H(n) \
133      (*(ElfW(Phdr) *) ((byte *) new_program_h + new_file_h->e_phentsize * (n)))
134
135 #define PATCH_INDEX(n) \
136   do { \
137          if ((int) (n) >= growme_index) \
138            (n)++; } while (0)
139
140 typedef unsigned char byte;
141
142 /* Round X up to a multiple of Y.  */
143
144 static ElfW(Addr)
145 round_up (ElfW(Addr) x, ElfW(Addr) y)
146 {
147   int rem = x % y;
148   if (rem == 0)
149     return x;
150   return x - rem + y;
151 }
152
153 /* Return the index of the section named NAME.
154    SECTION_NAMES, FILE_NAME and FILE_H give information
155    about the file we are looking in.
156
157    If we don't find the section NAME, that is a fatal error
158    if NOERROR is 0; we return -1 if NOERROR is nonzero.  */
159
160 static int
161 find_section (char *name,
162               const char *section_names,
163               char *file_name,
164               ElfW(Ehdr) *old_file_h,
165               ElfW(Shdr) *old_section_h,
166               int noerror)
167 {
168   int idx;
169
170   for (idx = 1; idx < old_file_h->e_shnum; idx++)
171     {
172 #ifdef DEBUG
173       fprintf (stderr, "Looking for %s - found %s\n", name,
174                section_names + OLD_SECTION_H (idx).sh_name);
175 #endif
176       if (!strcmp (section_names + OLD_SECTION_H (idx).sh_name,
177                    name))
178           return idx;
179     }
180
181   /* If we're here, we found nothing or return did not work */
182   if ( ! noerror)
183       fatal ("Can't find %s in %s.\n", name, file_name);
184
185   return -1;
186 }
187
188 /* ****************************************************************
189  * unexec
190  *
191  * driving logic.
192  *
193  * In ELF, this works by replacing the old .bss section with a new
194  * .data section, and inserting an empty .bss immediately afterwards.
195  *
196  */
197 void
198 unexec (char *new_name,
199         char *old_name,
200         uintptr_t data_start,
201         uintptr_t bss_start,
202         uintptr_t entry_address)
203 {
204   int old_file;
205
206   struct stat stat_buf;
207   caddr_t old_base, new_base;
208
209   ElfW(Ehdr) *old_file_h, * new_file_h;
210   ElfW(Phdr) *old_program_h, * new_program_h;
211   ElfW(Shdr) *old_section_h, * new_section_h;
212   ElfW(Shdr) * growme = NULL, * grown = NULL;
213   ElfW(Addr) old_bss_addr = 0,  new_data2_addr = 0;
214
215   int growme_index = -1;
216   int n, nn;
217   const char *old_section_names;
218   int old_mdebug_index, old_data_index;
219   int new_bss_addr, new_data2_size, new_data2_offset, new_file, new_file_size;
220
221   /* Open the old file */
222   if ( (old_file = open (old_name, O_RDONLY)) < 0 )
223       fatal ("Can't open %s for reading: errno %d\n", old_name, errno);
224
225   if (fstat (old_file, &stat_buf) == -1)
226       fatal ("Can't fstat (%s): errno %d\n", old_name, errno);
227
228   /* map old file into the address space. */
229   old_base = (caddr_t) mmap ((caddr_t) 0, stat_buf.st_size,
230                              PROT_READ, MAP_SHARED, old_file, 0);
231   if (old_base == (caddr_t) MAP_FAILED)
232     fatal ("Can't mmap (%s): errno %d\n", old_name, errno);
233
234   old_file_h    = (ElfW(Ehdr) *) old_base;
235   old_program_h = (ElfW(Phdr) *) ((byte *) old_base + old_file_h->e_phoff);
236   old_section_h = (ElfW(Shdr) *) ((byte *) old_base + old_file_h->e_shoff);
237   old_section_names = (const char *) old_base
238       + OLD_SECTION_H (old_file_h->e_shstrndx).sh_offset;
239
240   /* Find a section which we will grow by looking for the SHT_NOBITS
241    * section with ALLOCATE flag and with the biggest address. */
242   for (n = 1; n < old_file_h->e_shnum; n++) {
243       ElfW(Shdr) * sh = & OLD_SECTION_H(n);
244
245       if ((sh->sh_type == SHT_NOBITS) && (sh->sh_flags & SHF_ALLOC)) {
246           if ( old_bss_addr < sh->sh_addr ) {
247               growme = sh;
248               growme_index = n;
249               new_data2_addr = old_bss_addr =  sh->sh_addr;
250           }
251       }
252   }
253
254   if (growme == NULL )
255       fatal ("Can't find a section to grow\n", 0, 0);
256
257   old_data_index = find_section (".data", old_section_names,
258                                  old_name, old_file_h, old_section_h, 0);
259
260   new_bss_addr = (ElfW(Addr)) sbrk (0);
261   new_data2_size = new_bss_addr - old_bss_addr;
262   new_data2_offset  = OLD_SECTION_H (old_data_index).sh_offset +
263       (new_data2_addr - OLD_SECTION_H (old_data_index).sh_addr);
264
265   if ( new_bss_addr < old_bss_addr + growme->sh_size )
266       fatal (".bss shrank when undumping???\n", 0, 0);
267
268   /* Set the output file to the right size and mmap it. */
269   if ( (new_file = open (new_name, O_RDWR | O_CREAT, 0666)) < 0 )
270       fatal ("Can't create (%s): errno %d\n", new_name, errno);
271
272   new_file_size = stat_buf.st_size +  old_file_h->e_shentsize + new_data2_size;
273
274   if (ftruncate (new_file, new_file_size))
275       fatal ("Can't ftruncate (%s): errno %d\n", new_name, errno);
276
277   new_base = (caddr_t) mmap ((caddr_t) 0, new_file_size,
278                              PROT_READ | PROT_WRITE,
279 #ifdef UNEXEC_USE_MAP_PRIVATE
280                              MAP_PRIVATE,
281 #else
282                              MAP_SHARED,
283 #endif
284                              new_file, 0);
285
286   if (new_base == (caddr_t) -1)
287       fatal ("Can't mmap (%s): errno %d\n", new_name, errno);
288
289   new_file_h = (ElfW(Ehdr) *) new_base;
290   new_program_h = (ElfW(Phdr) *) ((byte *) new_base + old_file_h->e_phoff);
291   new_section_h = (ElfW(Shdr) *) ((byte *) new_base + old_file_h->e_shoff +
292                                   new_data2_size);
293
294   /* Make our new file, program and section headers as copies of the
295    * originals.  */
296   memcpy (new_file_h, old_file_h, old_file_h->e_ehsize);
297   memcpy (new_program_h, old_program_h,
298           old_file_h->e_phnum * old_file_h->e_phentsize);
299
300   /* Modify the e_shstrndx if necessary. */
301   PATCH_INDEX (new_file_h->e_shstrndx);
302
303   /* Fix up file header.  We'll add one section.  Section header is
304    * further away now.  */
305   new_file_h->e_shoff += new_data2_size;
306   new_file_h->e_shnum += 1;
307
308   /* Fix up a new program header by extending the writable data
309    * segment so that the bss area is covered too. Find that segment by
310    * looking for one that starts before and ends after the .bss and is
311    * PT_LOADable. */
312   for (n = new_file_h->e_phnum - 1; n >= 0; n--) {
313       ElfW(Phdr) * ph = & NEW_PROGRAM_H(n);
314 #ifdef DEBUG
315       printf ("%d @ %0x + %0x against %0x + %0x",
316               n, ph->p_vaddr, ph->p_memsz,growme->sh_addr, growme->sh_size);
317 #endif
318       if ((ph->p_type == PT_LOAD) &&
319           (ph->p_vaddr <= growme->sh_addr) &&
320           ((ph->p_vaddr+ph->p_memsz) >= (growme->sh_addr + growme->sh_size))) {
321           /* Make sure that the size includes any padding before the
322            * old .bss section.  */
323           ph->p_memsz = ph->p_filesz = new_bss_addr - ph->p_vaddr;
324 #ifdef DEBUG
325           puts (" That's the one!");
326 #endif
327           break;
328       }
329 #ifdef DEBUG
330       putchar ('\n');
331 #endif
332   }
333
334   if (n < 0)
335       fatal ("Couldn't find segment which covers %s",
336              old_section_names + growme->sh_name);
337
338   /* Walk through all section headers, insert the new data2 section
339    * right before the new bss section. */
340   for (n = 1, nn = 1; n < (int) old_file_h->e_shnum;  n++, nn++) {
341       ElfW(Shdr) * nsec = & NEW_SECTION_H(nn);
342       ElfW(Shdr) * osec = & OLD_SECTION_H(n);
343
344       /* If this is the section we want to grow, insert the new data
345        * section before it. */
346       if ( osec == growme ) {
347           /* Steal the data section header for this data2 section but
348            * use the * 'grow' section's alignment. This * will assure
349            * that the new section * always be placed in the same spot
350            * * as the old section by any other * application. */
351           ElfW(Shdr) * od = &OLD_SECTION_H(old_data_index);
352
353           memcpy (nsec, od, new_file_h->e_shentsize);
354
355           nsec->sh_addr = new_data2_addr;
356           nsec->sh_offset =  new_data2_offset;
357           nsec->sh_size = new_data2_size;
358           nsec->sh_addralign = osec->sh_addralign;
359
360           /* Copy over what we have in memory now. */
361           memcpy (nsec->sh_offset + new_base, (caddr_t) osec->sh_addr,
362                   new_data2_size);
363           nn++;
364           grown = nsec++;
365       }
366
367       memcpy (nsec, osec, old_file_h->e_shentsize);
368
369       if ( osec == growme ) {
370           /* The new bss section's size is zero, and its file offset
371            * and virtual address should be off by NEW_DATA2_SIZE.  */
372           nsec->sh_offset = grown->sh_offset + new_data2_size;
373           nsec->sh_addr = grown->sh_addr + new_data2_size;
374
375           /* Let the new bss section address alignment be the same as
376            * the section address alignment followed the old bss
377            * section, so this section will be placed in exactly the
378            * same place. */
379           nsec->sh_addralign = osec->sh_addralign;
380           nsec->sh_size = 0;
381       } else {
382           /* Any section that was originally placed AFTER the bss
383            * section should now be off by NEW_DATA2_SIZE. */
384           if ( round_up (nsec->sh_offset, growme->sh_addralign) >=
385                new_data2_offset)
386               nsec->sh_offset += new_data2_size;
387       }
388
389       /* Any section that was originally placed after the section *
390        * header table should now be off by the size of one section
391        * header table entry.  */
392       if (nsec->sh_offset > new_file_h->e_shoff)
393           nsec->sh_offset += new_file_h->e_shentsize;
394
395
396       /* If any section hdr refers to the section after the new .data
397        * section, make it refer to next one because we have inserted a
398        * new section in between.  */
399       PATCH_INDEX (nsec->sh_link);
400
401       /* For symbol tables, info is a symbol table index, so don't
402        * change it.  */
403       if (nsec->sh_type != SHT_SYMTAB && nsec->sh_type != SHT_DYNSYM)
404           PATCH_INDEX (nsec->sh_info);
405
406       /* Any section which used to be NOBITS will now becomes PROGBITS
407        * if it's ALLOC-atable, unless, of cause, it's not the one we
408        * decided to grow */
409       if ( (osec->sh_type == SHT_NOBITS) && (osec->sh_flags & SHF_ALLOC) &&
410            (osec != growme ) ) {
411           nsec->sh_type = SHT_PROGBITS;
412       }
413
414       /* Now, start to copy the content of sections */
415       if ( nsec->sh_type != SHT_NULL || nsec->sh_type != SHT_NOBITS ) {
416
417           /* Write out the sections. .data and .data1 (and data2,
418            * called ".data" in the strings table) get copied from the
419            * current process instead of the old file.  */
420           caddr_t src =  old_base + osec->sh_offset;
421           const char * secname = old_section_names + nsec->sh_name;
422           const char * names[] = {
423               ".data",".sdata", ".lit4", ".lit8", ".sdata1", ".data1",
424               ".sbss", NULL};
425           int i;
426
427           for ( i=0; names[i] != NULL; i++ ) {
428               if ( ! strcmp (secname, names[i]) ) {
429                   src = (caddr_t) osec->sh_addr;
430                   break;
431               }
432           }
433
434           memcpy (nsec->sh_offset + new_base, src, nsec->sh_size);
435       }
436
437       old_mdebug_index = find_section (".mdebug", old_section_names,
438                                        old_name, old_file_h, old_section_h, 1);
439
440 #if defined (__sony_news) && defined (_SYSTYPE_SYSV)
441       if (nsec->sh_type == SHT_MIPS_DEBUG && old_mdebug_index != -1) {
442           int diff = nsec->sh_offset-OLD_SECTION_H(old_mdebug_index).sh_offset;
443           HDRR *phdr = (HDRR *)(nsec->sh_offset + new_base);
444
445           if (diff) {
446               phdr->cbLineOffset += diff;
447               phdr->cbDnOffset   += diff;
448               phdr->cbPdOffset   += diff;
449               phdr->cbSymOffset  += diff;
450               phdr->cbOptOffset  += diff;
451               phdr->cbAuxOffset  += diff;
452               phdr->cbSsOffset   += diff;
453               phdr->cbSsExtOffset += diff;
454               phdr->cbFdOffset   += diff;
455               phdr->cbRfdOffset  += diff;
456               phdr->cbExtOffset  += diff;
457           }
458       }
459 #endif /* __sony_news && _SYSTYPE_SYSV */
460
461 #if __sgi
462       /* Adjust the HDRR offsets in .mdebug and copy the line data if
463        * it's in its usual 'hole' in the object.  Makes the new file
464        * debuggable with dbx.  patches up two problems: the absolute
465        * file offsets in the HDRR record of .mdebug (see
466        * /usr/include/syms.h), and the ld bug that gets the line table
467        * in a hole in the elf file rather than in the .mdebug section
468        * proper.
469        *
470        * David Anderson. davea@sgi.com Jan 16,1994 */
471 #define MDEBUGADJUST(__ct,__fileaddr)           \
472   if (n_phdrr->__ct > 0)                        \
473     {                                           \
474       n_phdrr->__fileaddr += movement;          \
475     }
476
477       if (n == old_mdebug_index) {
478           HDRR * o_phdrr = (HDRR *)((byte *)old_base + osec->sh_offset);
479           HDRR * n_phdrr = (HDRR *)((byte *)new_base + nsec->sh_offset);
480           unsigned movement = new_data2_size;
481
482           MDEBUGADJUST (idnMax, cbDnOffset);
483           MDEBUGADJUST (ipdMax, cbPdOffset);
484           MDEBUGADJUST (isymMax, cbSymOffset);
485           MDEBUGADJUST (ioptMax, cbOptOffset);
486           MDEBUGADJUST (iauxMax, cbAuxOffset);
487           MDEBUGADJUST (issMax, cbSsOffset);
488           MDEBUGADJUST (issExtMax, cbSsExtOffset);
489           MDEBUGADJUST (ifdMax, cbFdOffset);
490           MDEBUGADJUST (crfd, cbRfdOffset);
491           MDEBUGADJUST (iextMax, cbExtOffset);
492
493           /* The Line Section, being possible off in a hole of the
494            * object, requires special handling.  */
495           if (n_phdrr->cbLine > 0) {
496               if (o_phdrr->cbLineOffset >
497                   osec->sh_offset+ osec->sh_size){
498                   /* line data is in a hole in elf. do special copy
499                    * and adjust for this ld mistake.  */
500                   n_phdrr->cbLineOffset += movement;
501
502                   memcpy (n_phdrr->cbLineOffset + new_base,
503                           o_phdrr->cbLineOffset + old_base, n_phdrr->cbLine);
504               } else {
505                   /* somehow line data is in .mdebug as it is supposed
506                    * to be.  */
507                   MDEBUGADJUST (cbLine, cbLineOffset);
508               }
509           }
510       }
511 #endif /* __sgi */
512       /* If it is the symbol table, its st_shndx field needs to be
513        * patched.  */
514       if (nsec->sh_type == SHT_SYMTAB || nsec->sh_type == SHT_DYNSYM) {
515           unsigned int num = nsec->sh_size / nsec->sh_entsize;
516           ElfW(Sym) * sym = (ElfW(Sym) *)(nsec->sh_offset + new_base);
517           byte *symnames = ((byte *) new_base +
518                             NEW_SECTION_H (nsec->sh_link).sh_offset);
519
520           for (; num--; sym++) {
521               const char * symnam = (char *) (symnames + sym->st_name);
522
523               /* Update the symbol values of _edata and _end. */
524               if (strcmp (symnam, "_end") == 0
525                   || strcmp (symnam, "end") == 0
526                   || strcmp (symnam, "_edata") == 0
527                   || strcmp (symnam, "edata") == 0)
528                   memcpy (&sym->st_value, &new_bss_addr,sizeof (new_bss_addr));
529
530
531               if ((sym->st_shndx == SHN_UNDEF) || (sym->st_shndx == SHN_ABS)
532                   || (sym->st_shndx == SHN_COMMON)
533                   || (sym->st_shndx >= SHN_LOPROC &&
534                       sym->st_shndx <= SHN_HIPROC))
535                   continue;
536
537               PATCH_INDEX (sym->st_shndx);
538           }
539       }
540   }
541
542   /* This loop seeks out relocation sections for the data section, so
543    * that it can undo relocations performed by the runtime linker.  */
544   for (n = new_file_h->e_shnum - 1; n; n--) {
545       ElfW(Shdr) section = NEW_SECTION_H (n);
546
547       if ( section.sh_type == SHT_REL || section.sh_type == SHT_RELA ) {
548           /* This code handles two different size structs, but there
549            * should be no harm in that provided that r_offset is
550            * always the first member.  */
551           ElfW(Shdr) * info = & NEW_SECTION_H(section.sh_info);
552           const char * nm = old_section_names + info->sh_name;
553
554           if (!strcmp (nm, ".data") || !strcmp (nm, ".sdata")
555               || !strcmp (nm, ".lit4") || !strcmp (nm, ".lit8")
556               || !strcmp (nm, ".sdata1") || !strcmp (nm, ".data1")) {
557               ElfW(Addr) offset =  info->sh_addr - info->sh_offset;
558               caddr_t end, reloc = old_base + section.sh_offset;
559
560               for (end = reloc + section.sh_size; reloc < end;
561                    reloc += section.sh_entsize) {
562                   ElfW(Addr) addr = ((ElfW(Rel) *) reloc)->r_offset - offset;
563 #ifdef __alpha__
564                   /* The Alpha ELF binutils currently have a bug that
565                    * sometimes results in relocs that contain all
566                    * zeroes.  Work around this for now...  */
567                   if (((ElfW(Rel) *) reloc)->r_offset == 0)
568                       continue;
569 #endif
570                   memcpy (new_base + addr, old_base + addr,
571                           sizeof(ElfW(Addr)));
572               }
573           }
574       }
575   }
576
577 #ifdef UNEXEC_USE_MAP_PRIVATE
578   if (lseek (new_file, 0, SEEK_SET) == -1)
579       fatal ("Can't rewind (%s): errno %d\n", new_name, errno);
580
581   if (write (new_file, new_base, new_file_size) != new_file_size)
582       fatal ("Can't write (%s): errno %d\n", new_name, errno);
583 #endif
584
585   /* Close the files and make the new file executable.  */
586   if (close (old_file))
587       fatal ("Can't close (%s): errno %d\n", old_name, errno);
588
589   if (close (new_file))
590       fatal ("Can't close (%s): errno %d\n", new_name, errno);
591
592   if (stat (new_name, &stat_buf) == -1)
593       fatal ("Can't stat (%s): errno %d\n", new_name, errno);
594
595   n = umask (777);
596   umask (n);
597   stat_buf.st_mode |= 0111 & ~n;
598   if (chmod (new_name, stat_buf.st_mode) == -1)
599       fatal ("Can't chmod (%s): errno %d\n", new_name, errno);
600 }