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