xref: /dflybsd-src/contrib/binutils-2.27/libiberty/simple-object.c (revision e656dc90e3d65d744d534af2f5ea88cf8101ebcf)
1*a9fa9459Szrj /* simple-object.c -- simple routines to read and write object files.
2*a9fa9459Szrj    Copyright 2010 Free Software Foundation, Inc.
3*a9fa9459Szrj    Written by Ian Lance Taylor, Google.
4*a9fa9459Szrj 
5*a9fa9459Szrj This program is free software; you can redistribute it and/or modify it
6*a9fa9459Szrj under the terms of the GNU General Public License as published by the
7*a9fa9459Szrj Free Software Foundation; either version 2, or (at your option) any
8*a9fa9459Szrj later version.
9*a9fa9459Szrj 
10*a9fa9459Szrj This program is distributed in the hope that it will be useful,
11*a9fa9459Szrj but WITHOUT ANY WARRANTY; without even the implied warranty of
12*a9fa9459Szrj MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13*a9fa9459Szrj GNU General Public License for more details.
14*a9fa9459Szrj 
15*a9fa9459Szrj You should have received a copy of the GNU General Public License
16*a9fa9459Szrj along with this program; if not, write to the Free Software
17*a9fa9459Szrj Foundation, 51 Franklin Street - Fifth Floor,
18*a9fa9459Szrj Boston, MA 02110-1301, USA.  */
19*a9fa9459Szrj 
20*a9fa9459Szrj #include "config.h"
21*a9fa9459Szrj #include "libiberty.h"
22*a9fa9459Szrj #include "simple-object.h"
23*a9fa9459Szrj 
24*a9fa9459Szrj #include <errno.h>
25*a9fa9459Szrj 
26*a9fa9459Szrj #ifdef HAVE_STDLIB_H
27*a9fa9459Szrj #include <stdlib.h>
28*a9fa9459Szrj #endif
29*a9fa9459Szrj 
30*a9fa9459Szrj #ifdef HAVE_STDINT_H
31*a9fa9459Szrj #include <stdint.h>
32*a9fa9459Szrj #endif
33*a9fa9459Szrj 
34*a9fa9459Szrj #ifdef HAVE_STRING_H
35*a9fa9459Szrj #include <string.h>
36*a9fa9459Szrj #endif
37*a9fa9459Szrj 
38*a9fa9459Szrj #ifdef HAVE_INTTYPES_H
39*a9fa9459Szrj #include <inttypes.h>
40*a9fa9459Szrj #endif
41*a9fa9459Szrj 
42*a9fa9459Szrj #ifndef SEEK_SET
43*a9fa9459Szrj #define SEEK_SET 0
44*a9fa9459Szrj #endif
45*a9fa9459Szrj 
46*a9fa9459Szrj #include "simple-object-common.h"
47*a9fa9459Szrj 
48*a9fa9459Szrj /* The known object file formats.  */
49*a9fa9459Szrj 
50*a9fa9459Szrj static const struct simple_object_functions * const format_functions[] =
51*a9fa9459Szrj {
52*a9fa9459Szrj   &simple_object_elf_functions,
53*a9fa9459Szrj   &simple_object_mach_o_functions,
54*a9fa9459Szrj   &simple_object_coff_functions,
55*a9fa9459Szrj   &simple_object_xcoff_functions
56*a9fa9459Szrj };
57*a9fa9459Szrj 
58*a9fa9459Szrj /* Read data from a file using the simple_object error reporting
59*a9fa9459Szrj    conventions.  */
60*a9fa9459Szrj 
61*a9fa9459Szrj int
simple_object_internal_read(int descriptor,off_t offset,unsigned char * buffer,size_t size,const char ** errmsg,int * err)62*a9fa9459Szrj simple_object_internal_read (int descriptor, off_t offset,
63*a9fa9459Szrj 			     unsigned char *buffer, size_t size,
64*a9fa9459Szrj 			     const char **errmsg, int *err)
65*a9fa9459Szrj {
66*a9fa9459Szrj   if (lseek (descriptor, offset, SEEK_SET) < 0)
67*a9fa9459Szrj     {
68*a9fa9459Szrj       *errmsg = "lseek";
69*a9fa9459Szrj       *err = errno;
70*a9fa9459Szrj       return 0;
71*a9fa9459Szrj     }
72*a9fa9459Szrj 
73*a9fa9459Szrj   do
74*a9fa9459Szrj     {
75*a9fa9459Szrj       ssize_t got = read (descriptor, buffer, size);
76*a9fa9459Szrj       if (got == 0)
77*a9fa9459Szrj 	break;
78*a9fa9459Szrj       else if (got > 0)
79*a9fa9459Szrj 	{
80*a9fa9459Szrj 	  buffer += got;
81*a9fa9459Szrj 	  size -= got;
82*a9fa9459Szrj 	}
83*a9fa9459Szrj       else if (errno != EINTR)
84*a9fa9459Szrj 	{
85*a9fa9459Szrj 	  *errmsg = "read";
86*a9fa9459Szrj 	  *err = errno;
87*a9fa9459Szrj 	  return 0;
88*a9fa9459Szrj 	}
89*a9fa9459Szrj     }
90*a9fa9459Szrj   while (size > 0);
91*a9fa9459Szrj 
92*a9fa9459Szrj   if (size > 0)
93*a9fa9459Szrj     {
94*a9fa9459Szrj       *errmsg = "file too short";
95*a9fa9459Szrj       *err = 0;
96*a9fa9459Szrj       return 0;
97*a9fa9459Szrj     }
98*a9fa9459Szrj 
99*a9fa9459Szrj   return 1;
100*a9fa9459Szrj }
101*a9fa9459Szrj 
102*a9fa9459Szrj /* Write data to a file using the simple_object error reporting
103*a9fa9459Szrj    conventions.  */
104*a9fa9459Szrj 
105*a9fa9459Szrj int
simple_object_internal_write(int descriptor,off_t offset,const unsigned char * buffer,size_t size,const char ** errmsg,int * err)106*a9fa9459Szrj simple_object_internal_write (int descriptor, off_t offset,
107*a9fa9459Szrj 			      const unsigned char *buffer, size_t size,
108*a9fa9459Szrj 			      const char **errmsg, int *err)
109*a9fa9459Szrj {
110*a9fa9459Szrj   if (lseek (descriptor, offset, SEEK_SET) < 0)
111*a9fa9459Szrj     {
112*a9fa9459Szrj       *errmsg = "lseek";
113*a9fa9459Szrj       *err = errno;
114*a9fa9459Szrj       return 0;
115*a9fa9459Szrj     }
116*a9fa9459Szrj 
117*a9fa9459Szrj   do
118*a9fa9459Szrj     {
119*a9fa9459Szrj       ssize_t wrote = write (descriptor, buffer, size);
120*a9fa9459Szrj       if (wrote == 0)
121*a9fa9459Szrj 	break;
122*a9fa9459Szrj       else if (wrote > 0)
123*a9fa9459Szrj 	{
124*a9fa9459Szrj 	  buffer += wrote;
125*a9fa9459Szrj 	  size -= wrote;
126*a9fa9459Szrj 	}
127*a9fa9459Szrj       else if (errno != EINTR)
128*a9fa9459Szrj 	{
129*a9fa9459Szrj 	  *errmsg = "write";
130*a9fa9459Szrj 	  *err = errno;
131*a9fa9459Szrj 	  return 0;
132*a9fa9459Szrj 	}
133*a9fa9459Szrj     }
134*a9fa9459Szrj   while (size > 0);
135*a9fa9459Szrj 
136*a9fa9459Szrj   if (size > 0)
137*a9fa9459Szrj     {
138*a9fa9459Szrj       *errmsg = "short write";
139*a9fa9459Szrj       *err = 0;
140*a9fa9459Szrj       return 0;
141*a9fa9459Szrj     }
142*a9fa9459Szrj 
143*a9fa9459Szrj   return 1;
144*a9fa9459Szrj }
145*a9fa9459Szrj 
146*a9fa9459Szrj /* Open for read.  */
147*a9fa9459Szrj 
148*a9fa9459Szrj simple_object_read *
simple_object_start_read(int descriptor,off_t offset,const char * segment_name,const char ** errmsg,int * err)149*a9fa9459Szrj simple_object_start_read (int descriptor, off_t offset,
150*a9fa9459Szrj 			  const char *segment_name, const char **errmsg,
151*a9fa9459Szrj 			  int *err)
152*a9fa9459Szrj {
153*a9fa9459Szrj   unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN];
154*a9fa9459Szrj   size_t len, i;
155*a9fa9459Szrj 
156*a9fa9459Szrj   if (!simple_object_internal_read (descriptor, offset, header,
157*a9fa9459Szrj 				    SIMPLE_OBJECT_MATCH_HEADER_LEN,
158*a9fa9459Szrj 				    errmsg, err))
159*a9fa9459Szrj     return NULL;
160*a9fa9459Szrj 
161*a9fa9459Szrj   len = sizeof (format_functions) / sizeof (format_functions[0]);
162*a9fa9459Szrj   for (i = 0; i < len; ++i)
163*a9fa9459Szrj     {
164*a9fa9459Szrj       void *data;
165*a9fa9459Szrj 
166*a9fa9459Szrj       data = format_functions[i]->match (header, descriptor, offset,
167*a9fa9459Szrj 					 segment_name, errmsg, err);
168*a9fa9459Szrj       if (data != NULL)
169*a9fa9459Szrj 	{
170*a9fa9459Szrj 	  simple_object_read *ret;
171*a9fa9459Szrj 
172*a9fa9459Szrj 	  ret = XNEW (simple_object_read);
173*a9fa9459Szrj 	  ret->descriptor = descriptor;
174*a9fa9459Szrj 	  ret->offset = offset;
175*a9fa9459Szrj 	  ret->functions = format_functions[i];
176*a9fa9459Szrj 	  ret->data = data;
177*a9fa9459Szrj 	  return ret;
178*a9fa9459Szrj 	}
179*a9fa9459Szrj     }
180*a9fa9459Szrj 
181*a9fa9459Szrj   *errmsg = "file not recognized";
182*a9fa9459Szrj   *err = 0;
183*a9fa9459Szrj   return NULL;
184*a9fa9459Szrj }
185*a9fa9459Szrj 
186*a9fa9459Szrj /* Find all sections.  */
187*a9fa9459Szrj 
188*a9fa9459Szrj const char *
simple_object_find_sections(simple_object_read * sobj,int (* pfn)(void *,const char *,off_t,off_t),void * data,int * err)189*a9fa9459Szrj simple_object_find_sections (simple_object_read *sobj,
190*a9fa9459Szrj 			     int (*pfn) (void *, const char *, off_t, off_t),
191*a9fa9459Szrj 			     void *data,
192*a9fa9459Szrj 			     int *err)
193*a9fa9459Szrj {
194*a9fa9459Szrj   return sobj->functions->find_sections (sobj, pfn, data, err);
195*a9fa9459Szrj }
196*a9fa9459Szrj 
197*a9fa9459Szrj /* Internal data passed to find_one_section.  */
198*a9fa9459Szrj 
199*a9fa9459Szrj struct find_one_section_data
200*a9fa9459Szrj {
201*a9fa9459Szrj   /* The section we are looking for.  */
202*a9fa9459Szrj   const char *name;
203*a9fa9459Szrj   /* Where to store the section offset.  */
204*a9fa9459Szrj   off_t *offset;
205*a9fa9459Szrj   /* Where to store the section length.  */
206*a9fa9459Szrj   off_t *length;
207*a9fa9459Szrj   /* Set if the name is found.  */
208*a9fa9459Szrj   int found;
209*a9fa9459Szrj };
210*a9fa9459Szrj 
211*a9fa9459Szrj /* Internal function passed to find_sections.  */
212*a9fa9459Szrj 
213*a9fa9459Szrj static int
find_one_section(void * data,const char * name,off_t offset,off_t length)214*a9fa9459Szrj find_one_section (void *data, const char *name, off_t offset, off_t length)
215*a9fa9459Szrj {
216*a9fa9459Szrj   struct find_one_section_data *fosd = (struct find_one_section_data *) data;
217*a9fa9459Szrj 
218*a9fa9459Szrj   if (strcmp (name, fosd->name) != 0)
219*a9fa9459Szrj     return 1;
220*a9fa9459Szrj 
221*a9fa9459Szrj   *fosd->offset = offset;
222*a9fa9459Szrj   *fosd->length = length;
223*a9fa9459Szrj   fosd->found = 1;
224*a9fa9459Szrj 
225*a9fa9459Szrj   /* Stop iteration.  */
226*a9fa9459Szrj   return 0;
227*a9fa9459Szrj }
228*a9fa9459Szrj 
229*a9fa9459Szrj /* Find a section.  */
230*a9fa9459Szrj 
231*a9fa9459Szrj int
simple_object_find_section(simple_object_read * sobj,const char * name,off_t * offset,off_t * length,const char ** errmsg,int * err)232*a9fa9459Szrj simple_object_find_section (simple_object_read *sobj, const char *name,
233*a9fa9459Szrj 			    off_t *offset, off_t *length,
234*a9fa9459Szrj 			    const char **errmsg, int *err)
235*a9fa9459Szrj {
236*a9fa9459Szrj   struct find_one_section_data fosd;
237*a9fa9459Szrj 
238*a9fa9459Szrj   fosd.name = name;
239*a9fa9459Szrj   fosd.offset = offset;
240*a9fa9459Szrj   fosd.length = length;
241*a9fa9459Szrj   fosd.found = 0;
242*a9fa9459Szrj 
243*a9fa9459Szrj   *errmsg = simple_object_find_sections (sobj, find_one_section,
244*a9fa9459Szrj 					 (void *) &fosd, err);
245*a9fa9459Szrj   if (*errmsg != NULL)
246*a9fa9459Szrj     return 0;
247*a9fa9459Szrj   if (!fosd.found)
248*a9fa9459Szrj     return 0;
249*a9fa9459Szrj   return 1;
250*a9fa9459Szrj }
251*a9fa9459Szrj 
252*a9fa9459Szrj /* Fetch attributes.  */
253*a9fa9459Szrj 
254*a9fa9459Szrj simple_object_attributes *
simple_object_fetch_attributes(simple_object_read * sobj,const char ** errmsg,int * err)255*a9fa9459Szrj simple_object_fetch_attributes (simple_object_read *sobj, const char **errmsg,
256*a9fa9459Szrj 				int *err)
257*a9fa9459Szrj {
258*a9fa9459Szrj   void *data;
259*a9fa9459Szrj   simple_object_attributes *ret;
260*a9fa9459Szrj 
261*a9fa9459Szrj   data = sobj->functions->fetch_attributes (sobj, errmsg, err);
262*a9fa9459Szrj   if (data == NULL)
263*a9fa9459Szrj     return NULL;
264*a9fa9459Szrj   ret = XNEW (simple_object_attributes);
265*a9fa9459Szrj   ret->functions = sobj->functions;
266*a9fa9459Szrj   ret->data = data;
267*a9fa9459Szrj   return ret;
268*a9fa9459Szrj }
269*a9fa9459Szrj 
270*a9fa9459Szrj /* Release an simple_object_read.  */
271*a9fa9459Szrj 
272*a9fa9459Szrj void
simple_object_release_read(simple_object_read * sobj)273*a9fa9459Szrj simple_object_release_read (simple_object_read *sobj)
274*a9fa9459Szrj {
275*a9fa9459Szrj   sobj->functions->release_read (sobj->data);
276*a9fa9459Szrj   XDELETE (sobj);
277*a9fa9459Szrj }
278*a9fa9459Szrj 
279*a9fa9459Szrj /* Merge attributes.  */
280*a9fa9459Szrj 
281*a9fa9459Szrj const char *
simple_object_attributes_merge(simple_object_attributes * to,simple_object_attributes * from,int * err)282*a9fa9459Szrj simple_object_attributes_merge (simple_object_attributes *to,
283*a9fa9459Szrj 				simple_object_attributes *from,
284*a9fa9459Szrj 				int *err)
285*a9fa9459Szrj {
286*a9fa9459Szrj   if (to->functions != from->functions)
287*a9fa9459Szrj     {
288*a9fa9459Szrj       *err = 0;
289*a9fa9459Szrj       return "different object file format";
290*a9fa9459Szrj     }
291*a9fa9459Szrj   return to->functions->attributes_merge (to->data, from->data, err);
292*a9fa9459Szrj }
293*a9fa9459Szrj 
294*a9fa9459Szrj /* Release an attributes structure.  */
295*a9fa9459Szrj 
296*a9fa9459Szrj void
simple_object_release_attributes(simple_object_attributes * attrs)297*a9fa9459Szrj simple_object_release_attributes (simple_object_attributes *attrs)
298*a9fa9459Szrj {
299*a9fa9459Szrj   attrs->functions->release_attributes (attrs->data);
300*a9fa9459Szrj   XDELETE (attrs);
301*a9fa9459Szrj }
302*a9fa9459Szrj 
303*a9fa9459Szrj /* Start creating an object file.  */
304*a9fa9459Szrj 
305*a9fa9459Szrj simple_object_write *
simple_object_start_write(simple_object_attributes * attrs,const char * segment_name,const char ** errmsg,int * err)306*a9fa9459Szrj simple_object_start_write (simple_object_attributes *attrs,
307*a9fa9459Szrj 			   const char *segment_name, const char **errmsg,
308*a9fa9459Szrj 			   int *err)
309*a9fa9459Szrj {
310*a9fa9459Szrj   void *data;
311*a9fa9459Szrj   simple_object_write *ret;
312*a9fa9459Szrj 
313*a9fa9459Szrj   data = attrs->functions->start_write (attrs->data, errmsg, err);
314*a9fa9459Szrj   if (data == NULL)
315*a9fa9459Szrj     return NULL;
316*a9fa9459Szrj   ret = XNEW (simple_object_write);
317*a9fa9459Szrj   ret->functions = attrs->functions;
318*a9fa9459Szrj   ret->segment_name = xstrdup (segment_name);
319*a9fa9459Szrj   ret->sections = NULL;
320*a9fa9459Szrj   ret->last_section = NULL;
321*a9fa9459Szrj   ret->data = data;
322*a9fa9459Szrj   return ret;
323*a9fa9459Szrj }
324*a9fa9459Szrj 
325*a9fa9459Szrj /* Start creating a section.  */
326*a9fa9459Szrj 
327*a9fa9459Szrj simple_object_write_section *
simple_object_write_create_section(simple_object_write * sobj,const char * name,unsigned int align,const char ** errmsg ATTRIBUTE_UNUSED,int * err ATTRIBUTE_UNUSED)328*a9fa9459Szrj simple_object_write_create_section (simple_object_write *sobj, const char *name,
329*a9fa9459Szrj 				    unsigned int align,
330*a9fa9459Szrj 				    const char **errmsg ATTRIBUTE_UNUSED,
331*a9fa9459Szrj 				    int *err ATTRIBUTE_UNUSED)
332*a9fa9459Szrj {
333*a9fa9459Szrj   simple_object_write_section *ret;
334*a9fa9459Szrj 
335*a9fa9459Szrj   ret = XNEW (simple_object_write_section);
336*a9fa9459Szrj   ret->next = NULL;
337*a9fa9459Szrj   ret->name = xstrdup (name);
338*a9fa9459Szrj   ret->align = align;
339*a9fa9459Szrj   ret->buffers = NULL;
340*a9fa9459Szrj   ret->last_buffer = NULL;
341*a9fa9459Szrj 
342*a9fa9459Szrj   if (sobj->last_section == NULL)
343*a9fa9459Szrj     {
344*a9fa9459Szrj       sobj->sections = ret;
345*a9fa9459Szrj       sobj->last_section = ret;
346*a9fa9459Szrj     }
347*a9fa9459Szrj   else
348*a9fa9459Szrj     {
349*a9fa9459Szrj       sobj->last_section->next = ret;
350*a9fa9459Szrj       sobj->last_section = ret;
351*a9fa9459Szrj     }
352*a9fa9459Szrj 
353*a9fa9459Szrj   return ret;
354*a9fa9459Szrj }
355*a9fa9459Szrj 
356*a9fa9459Szrj /* Add data to a section.  */
357*a9fa9459Szrj 
358*a9fa9459Szrj const char *
simple_object_write_add_data(simple_object_write * sobj ATTRIBUTE_UNUSED,simple_object_write_section * section,const void * buffer,size_t size,int copy,int * err ATTRIBUTE_UNUSED)359*a9fa9459Szrj simple_object_write_add_data (simple_object_write *sobj ATTRIBUTE_UNUSED,
360*a9fa9459Szrj 			      simple_object_write_section *section,
361*a9fa9459Szrj 			      const void *buffer,
362*a9fa9459Szrj 			      size_t size, int copy,
363*a9fa9459Szrj 			      int *err ATTRIBUTE_UNUSED)
364*a9fa9459Szrj {
365*a9fa9459Szrj   struct simple_object_write_section_buffer *wsb;
366*a9fa9459Szrj 
367*a9fa9459Szrj   wsb = XNEW (struct simple_object_write_section_buffer);
368*a9fa9459Szrj   wsb->next = NULL;
369*a9fa9459Szrj   wsb->size = size;
370*a9fa9459Szrj 
371*a9fa9459Szrj   if (!copy)
372*a9fa9459Szrj     {
373*a9fa9459Szrj       wsb->buffer = buffer;
374*a9fa9459Szrj       wsb->free_buffer = NULL;
375*a9fa9459Szrj     }
376*a9fa9459Szrj   else
377*a9fa9459Szrj     {
378*a9fa9459Szrj       wsb->free_buffer = (void *) XNEWVEC (char, size);
379*a9fa9459Szrj       memcpy (wsb->free_buffer, buffer, size);
380*a9fa9459Szrj       wsb->buffer = wsb->free_buffer;
381*a9fa9459Szrj     }
382*a9fa9459Szrj 
383*a9fa9459Szrj   if (section->last_buffer == NULL)
384*a9fa9459Szrj     {
385*a9fa9459Szrj       section->buffers = wsb;
386*a9fa9459Szrj       section->last_buffer = wsb;
387*a9fa9459Szrj     }
388*a9fa9459Szrj   else
389*a9fa9459Szrj     {
390*a9fa9459Szrj       section->last_buffer->next = wsb;
391*a9fa9459Szrj       section->last_buffer = wsb;
392*a9fa9459Szrj     }
393*a9fa9459Szrj 
394*a9fa9459Szrj   return NULL;
395*a9fa9459Szrj }
396*a9fa9459Szrj 
397*a9fa9459Szrj /* Write the complete object file.  */
398*a9fa9459Szrj 
399*a9fa9459Szrj const char *
simple_object_write_to_file(simple_object_write * sobj,int descriptor,int * err)400*a9fa9459Szrj simple_object_write_to_file (simple_object_write *sobj, int descriptor,
401*a9fa9459Szrj 			     int *err)
402*a9fa9459Szrj {
403*a9fa9459Szrj   return sobj->functions->write_to_file (sobj, descriptor, err);
404*a9fa9459Szrj }
405*a9fa9459Szrj 
406*a9fa9459Szrj /* Release an simple_object_write.  */
407*a9fa9459Szrj 
408*a9fa9459Szrj void
simple_object_release_write(simple_object_write * sobj)409*a9fa9459Szrj simple_object_release_write (simple_object_write *sobj)
410*a9fa9459Szrj {
411*a9fa9459Szrj   simple_object_write_section *section;
412*a9fa9459Szrj 
413*a9fa9459Szrj   free (sobj->segment_name);
414*a9fa9459Szrj 
415*a9fa9459Szrj   section = sobj->sections;
416*a9fa9459Szrj   while (section != NULL)
417*a9fa9459Szrj     {
418*a9fa9459Szrj       struct simple_object_write_section_buffer *buffer;
419*a9fa9459Szrj       simple_object_write_section *next_section;
420*a9fa9459Szrj 
421*a9fa9459Szrj       buffer = section->buffers;
422*a9fa9459Szrj       while (buffer != NULL)
423*a9fa9459Szrj 	{
424*a9fa9459Szrj 	  struct simple_object_write_section_buffer *next_buffer;
425*a9fa9459Szrj 
426*a9fa9459Szrj 	  if (buffer->free_buffer != NULL)
427*a9fa9459Szrj 	    XDELETEVEC (buffer->free_buffer);
428*a9fa9459Szrj 	  next_buffer = buffer->next;
429*a9fa9459Szrj 	  XDELETE (buffer);
430*a9fa9459Szrj 	  buffer = next_buffer;
431*a9fa9459Szrj 	}
432*a9fa9459Szrj 
433*a9fa9459Szrj       next_section = section->next;
434*a9fa9459Szrj       free (section->name);
435*a9fa9459Szrj       XDELETE (section);
436*a9fa9459Szrj       section = next_section;
437*a9fa9459Szrj     }
438*a9fa9459Szrj 
439*a9fa9459Szrj   sobj->functions->release_write (sobj->data);
440*a9fa9459Szrj   XDELETE (sobj);
441*a9fa9459Szrj }
442