root/trunk/src/libelf.c

Revision 688, 22.7 kB (checked in by walter, 2 years ago)

fix corrupt 64 bit elf file

  • Property svn:eol-style set to native
Line 
1 // Compiler implementation of the D programming language
2 // Copyright (c) 1999-2009 by Digital Mars
3 // All Rights Reserved
4 // written by Walter Bright
5 // http://www.digitalmars.com
6 // License for redistribution is by either the Artistic License
7 // in artistic.txt, or the GNU General Public License in gnu.txt.
8 // See the included readme.txt for details.
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <assert.h>
13 #include <time.h>
14 #include <unistd.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17
18 #include "rmem.h"
19 #include "root.h"
20 #include "stringtable.h"
21
22 #include "mars.h"
23 #include "lib.h"
24 #include "melf.h"
25
26 #define LOG 0
27
28 Library::Library()
29 {
30     libfile = NULL;
31 }
32
33 /***********************************
34  * Set the library file name based on the output directory
35  * and the filename.
36  * Add default library file name extension.
37  */
38
39 void Library::setFilename(char *dir, char *filename)
40 {
41 #if LOG
42     printf("Library::setFilename(dir = '%s', filename = '%s')\n",
43         dir ? dir : "", filename ? filename : "");
44 #endif
45     char *arg = filename;
46     if (!arg || !*arg)
47     {   // Generate lib file name from first obj name
48         char *n = (char *)global.params.objfiles->data[0];
49
50         n = FileName::name(n);
51         FileName *fn = FileName::forceExt(n, global.lib_ext);
52         arg = fn->toChars();
53     }
54     if (!FileName::absolute(arg))
55         arg = FileName::combine(dir, arg);
56     FileName *libfilename = FileName::defaultExt(arg, global.lib_ext);
57
58     libfile = new File(libfilename);
59 }
60
61 void Library::write()
62 {
63     if (global.params.verbose)
64         printf("library   %s\n", libfile->name->toChars());
65
66     OutBuffer libbuf;
67     WriteLibToBuffer(&libbuf);
68
69     // Transfer image to file
70     libfile->setbuffer(libbuf.data, libbuf.offset);
71     libbuf.extractData();
72
73
74     char *p = FileName::path(libfile->name->toChars());
75     FileName::ensurePathExists(p);
76     //mem.free(p);
77
78     libfile->writev();
79 }
80
81 /*****************************************************************************/
82
83 void Library::addLibrary(void *buf, size_t buflen)
84 {
85     addObject(NULL, buf, buflen);
86 }
87
88
89 /*****************************************************************************/
90 /*****************************************************************************/
91
92 static char elf[4] = { 0x7F, 'E', 'L', 'F' };   // ELF file signature
93
94 void sputl(int value, void* buffer)
95 {
96     unsigned char *p = (unsigned char*)buffer;
97     p[0] = (unsigned char)(value >> 24);
98     p[1] = (unsigned char)(value >> 16);
99     p[2] = (unsigned char)(value >> 8);
100     p[3] = (unsigned char)(value);
101 }
102
103 int sgetl(void* buffer)
104 {
105     unsigned char *p = (unsigned char*)buffer;
106     return (((((p[0] << 8) | p[1]) << 8) | p[2]) << 8) | p[3];
107 }
108
109
110 struct ObjModule
111 {
112     unsigned char *base;        // where are we holding it in memory
113     unsigned length;            // in bytes
114     unsigned offset;            // offset from start of library
115     char *name;                 // module name (file name)
116     int name_offset;            // if not -1, offset into string table of name
117     time_t file_time;           // file time
118     unsigned user_id;
119     unsigned group_id;
120     unsigned file_mode;
121     int scan;                   // 1 means scan for symbols
122 };
123
124 struct Header
125 {
126     #define OBJECT_NAME_SIZE 16
127     char object_name[OBJECT_NAME_SIZE];
128     char file_time[12];
129     char user_id[6];
130     char group_id[6];
131     char file_mode[8];          // in octal
132     char file_size[10];
133     char trailer[2];
134 };
135
136 void OmToHeader(Header *h, ObjModule *om)
137 {
138     size_t len;
139     if (om->name_offset == -1)
140     {
141         len = strlen(om->name);
142         memcpy(h->object_name, om->name, len);
143         h->object_name[len] = '/';
144     }
145     else
146     {
147         len = sprintf(h->object_name, "/%d", om->name_offset);
148         h->object_name[len] = ' ';
149     }
150     assert(len < OBJECT_NAME_SIZE);
151     memset(h->object_name + len + 1, ' ', OBJECT_NAME_SIZE - (len + 1));
152
153     /* In the following sprintf's, don't worry if the trailing 0
154      * that sprintf writes goes off the end of the field. It will
155      * write into the next field, which we will promptly overwrite
156      * anyway. (So make sure to write the fields in ascending order.)
157      */
158
159     len = sprintf(h->file_time, "%lu", om->file_time);
160     assert(len <= 12);
161     memset(h->file_time + len, ' ', 12 - len);
162
163     if (om->user_id > 999999)
164         om->user_id = 0;
165     len = sprintf(h->user_id, "%u", om->user_id);
166     assert(len <= 6);
167     memset(h->user_id + len, ' ', 6 - len);
168
169     len = sprintf(h->group_id, "%u", om->group_id);
170     assert(len <= 6);
171     memset(h->group_id + len, ' ', 6 - len);
172
173     len = sprintf(h->file_mode, "%o", om->file_mode);
174     assert(len <= 8);
175     memset(h->file_mode + len, ' ', 8 - len);
176
177     len = sprintf(h->file_size, "%u", om->length);
178     assert(len <= 10);
179     memset(h->file_size + len, ' ', 10 - len);
180
181     h->trailer[0] = '`';
182     h->trailer[1] = '\n';
183 }
184
185 void Library::addSymbol(ObjModule *om, char *name, int pickAny)
186 {
187 #if LOG
188     printf("Library::addSymbol(%s, %s, %d)\n", om->name, name, pickAny);
189 #endif
190     StringValue *s = tab.insert(name, strlen(name));
191     if (!s)
192     {   // already in table
193         if (!pickAny)
194         {   s = tab.lookup(name, strlen(name));
195             assert(s);
196             ObjSymbol *os = (ObjSymbol *)s->ptrvalue;
197             error("multiple definition of %s: %s and %s: %s",
198                 om->name, name, os->om->name, os->name);
199         }
200     }
201     else
202     {
203         ObjSymbol *os = new ObjSymbol();
204         os->name = strdup(name);
205         os->om = om;
206         s->ptrvalue = (void *)os;
207
208         objsymbols.push(os);
209     }
210 }
211
212 /************************************
213  * Scan single object module for dictionary symbols.
214  * Send those symbols to Library::addSymbol().
215  */
216
217 void Library::scanObjModule(ObjModule *om)
218 {
219 #if LOG
220     printf("Library::scanObjModule(%s)\n", om->name);
221 #endif
222     unsigned char *buf = (unsigned char *)om->base;
223     size_t buflen = om->length;
224     int reason = 0;
225
226     if (buflen < EI_NIDENT + sizeof(Elf32_Hdr))
227     {
228       Lcorrupt:
229         error("corrupt ELF object module %s %d", om->name, reason);
230         return;
231     }
232
233     if (memcmp(buf, elf, 4))
234     {   reason = 1;
235         goto Lcorrupt;
236     }
237     if (buf[EI_VERSION] != EV_CURRENT)
238     {
239         error("ELF object module %s has EI_VERSION = %d, should be %d", om->name, buf[EI_VERSION], EV_CURRENT);
240         return;
241     }
242     if (buf[EI_DATA] != ELFDATA2LSB)
243     {
244         error("ELF object module %s is byte swapped and unsupported", om->name);
245         return;
246     }
247     if (buf[EI_CLASS] == ELFCLASS32)
248     {
249         Elf32_Hdr *eh = (Elf32_Hdr *)(buf + EI_NIDENT);
250         if (eh->e_type != ET_REL)
251         {
252             error("ELF object module %s is not relocatable", om->name);
253             return;                             // not relocatable object module
254         }
255         if (eh->e_version != EV_CURRENT)
256             goto Lcorrupt;
257
258         /* For each Section
259          */
260         for (unsigned u = 0; u < eh->e_shnum; u++)
261         {   Elf32_Shdr *section = (Elf32_Shdr *)(buf + eh->e_shoff + eh->e_shentsize * u);
262
263             if (section->sh_type == SHT_SYMTAB)
264             {   /* sh_link gives the particular string table section
265                  * used for the symbol names.
266                  */
267                 Elf32_Shdr *string_section = (Elf32_Shdr *)(buf + eh->e_shoff +
268                     eh->e_shentsize * section->sh_link);
269                 if (string_section->sh_type != SHT_STRTAB)
270                 {
271                     reason = 3;
272                     goto Lcorrupt;
273                 }
274                 char *string_tab = (char *)(buf + string_section->sh_offset);
275
276                 for (unsigned offset = 0; offset < section->sh_size; offset += sizeof(Elf32_Sym))
277                 {   Elf32_Sym *sym = (Elf32_Sym *)(buf + section->sh_offset + offset);
278
279                     if (((sym->st_info >> 4) == STB_GLOBAL ||
280                          (sym->st_info >> 4) == STB_WEAK) &&
281                         sym->st_shndx != SHT_UNDEF)     // not extern
282                     {
283                         char *name = string_tab + sym->st_name;
284                         //printf("sym st_name = x%x\n", sym->st_name);
285                         addSymbol(om, name, 1);
286                     }
287                 }
288             }
289         }
290     }
291     else if (buf[EI_CLASS] == ELFCLASS64)
292     {
293         Elf64_Ehdr *eh = (Elf64_Ehdr *)(buf + EI_NIDENT);
294         if (buflen < EI_NIDENT + sizeof(Elf64_Ehdr))
295             goto Lcorrupt;
296         if (eh->e_type != ET_REL)
297         {
298             error("ELF object module %s is not relocatable", om->name);
299             return;                             // not relocatable object module
300         }
301         if (eh->e_version != EV_CURRENT)
302             goto Lcorrupt;
303
304         /* For each Section
305          */
306         for (unsigned u = 0; u < eh->e_shnum; u++)
307         {   Elf64_Shdr *section = (Elf64_Shdr *)(buf + eh->e_shoff + eh->e_shentsize * u);
308
309             if (section->sh_type == SHT_SYMTAB)
310             {   /* sh_link gives the particular string table section
311                  * used for the symbol names.
312                  */
313                 Elf64_Shdr *string_section = (Elf64_Shdr *)(buf + eh->e_shoff +
314                     eh->e_shentsize * section->sh_link);
315                 if (string_section->sh_type != SHT_STRTAB)
316                 {
317                     reason = 3;
318                     goto Lcorrupt;
319                 }
320                 char *string_tab = (char *)(buf + string_section->sh_offset);
321
322                 for (unsigned offset = 0; offset < section->sh_size; offset += sizeof(Elf64_Sym))
323                 {   Elf64_Sym *sym = (Elf64_Sym *)(buf + section->sh_offset + offset);
324
325                     if (((sym->st_info >> 4) == STB_GLOBAL ||
326                          (sym->st_info >> 4) == STB_WEAK) &&
327                         sym->st_shndx != SHT_UNDEF)     // not extern
328                     {
329                         char *name = string_tab + sym->st_name;
330                         //printf("sym st_name = x%x\n", sym->st_name);
331                         addSymbol(om, name, 1);
332                     }
333                 }
334             }
335         }
336     }
337     else
338     {
339         error("ELF object module %s is unrecognized class %d", om->name, buf[EI_CLASS]);
340         return;
341     }
342
343 #if 0
344     /* String table section
345      */
346     Elf32_Shdr *string_section = (Elf32_Shdr *)(buf + eh->e_shoff +
347         eh->e_shentsize * eh->e_shstrndx);
348     if (string_section->sh_type != SHT_STRTAB)
349     {
350         //printf("buf = %p, e_shentsize = %d, e_shstrndx = %d\n", buf, eh->e_shentsize, eh->e_shstrndx);
351         //printf("sh_type = %d, SHT_STRTAB = %d\n", string_section->sh_type, SHT_STRTAB);
352         reason = 2;
353         goto Lcorrupt;
354     }
355     printf("strtab sh_offset = x%x\n", string_section->sh_offset);
356     char *string_tab = (char *)(buf + string_section->sh_offset);
357 #endif
358
359 }
360
361 /***************************************
362  * Add object module or library to the library.
363  * Examine the buffer to see which it is.
364  * If the buffer is NULL, use module_name as the file name
365  * and load the file.
366  */
367
368 void Library::addObject(const char *module_name, void *buf, size_t buflen)
369 {
370     if (!module_name)
371         module_name = "";
372 #if LOG
373     printf("Library::addObject(%s)\n", module_name);
374 #endif
375     int fromfile = 0;
376     if (!buf)
377     {   assert(module_name[0]);
378         FileName f((char *)module_name, 0);
379         File file(&f);
380         file.readv();
381         buf = file.buffer;
382         buflen = file.len;
383         file.ref = 1;
384         fromfile = 1;
385     }
386     int reason = 0;
387
388     if (buflen < 16)
389     {
390 #if LOG
391         printf("buf = %p, buflen = %d\n", buf, buflen);
392 #endif
393       Lcorrupt:
394         error("corrupt object module %s %d", module_name, reason);
395         return;
396     }
397
398     if (memcmp(buf, "!<arch>\n", 8) == 0)
399     {   /* Library file.
400          * Pull each object module out of the library and add it
401          * to the object module array.
402          */
403 #if LOG
404         printf("archive, buf = %p, buflen = %d\n", buf, buflen);
405 #endif
406         unsigned offset = 8;
407         char *symtab = NULL;
408         unsigned symtab_size = 0;
409         char *filenametab = NULL;
410         unsigned filenametab_size = 0;
411         unsigned mstart = objmodules.dim;
412         while (offset < buflen)
413         {
414             if (offset + sizeof(Header) >= buflen)
415             {   reason = 1;
416                 goto Lcorrupt;
417             }
418             Header *header = (Header *)((unsigned char *)buf + offset);
419             offset += sizeof(Header);
420             char *endptr = NULL;
421             unsigned long size = strtoul(header->file_size, &endptr, 10);
422             if (endptr >= &header->file_size[10] || *endptr != ' ')
423             {   reason = 2;
424                 goto Lcorrupt;
425             }
426             if (offset + size > buflen)
427             {   reason = 3;
428                 goto Lcorrupt;
429             }
430
431             if (header->object_name[0] == '/' &&
432                 header->object_name[1] == ' ')
433             {
434                 /* Instead of rescanning the object modules we pull from a
435                  * library, just use the already created symbol table.
436                  */
437                 if (symtab)
438                 {   reason = 4;
439                     goto Lcorrupt;
440                 }
441                 symtab = (char *)buf + offset;
442                 symtab_size = size;
443                 if (size < 4)
444                 {   reason = 5;
445                     goto Lcorrupt;
446                 }
447             }
448             else if (header->object_name[0] == '/' &&
449                      header->object_name[1] == '/')
450             {
451                 /* This is the file name table, save it for later.
452                  */
453                 if (filenametab)
454                 {   reason = 6;
455                     goto Lcorrupt;
456                 }
457                 filenametab = (char *)buf + offset;
458                 filenametab_size = size;
459             }
460             else
461             {
462                 ObjModule *om = new ObjModule();
463                 om->base = (unsigned char *)buf + offset /*- sizeof(Header)*/;
464                 om->length = size;
465                 om->offset = 0;
466                 if (header->object_name[0] == '/')
467                 {   /* Pick long name out of file name table
468                      */
469                     unsigned foff = strtoul(header->object_name + 1, &endptr, 10);
470                     unsigned i;
471                     for (i = 0; 1; i++)
472                     {   if (foff + i >= filenametab_size)
473                         {   reason = 7;
474                             goto Lcorrupt;
475                         }
476                         char c = filenametab[foff + i];
477                         if (c == '/')
478                             break;
479                     }
480                     om->name = (char *)malloc(i + 1);
481                     assert(om->name);
482                     memcpy(om->name, filenametab + foff, i);
483                     om->name[i] = 0;
484                 }
485                 else
486                 {   /* Pick short name out of header
487                      */
488                     om->name = (char *)malloc(OBJECT_NAME_SIZE);
489                     assert(om->name);
490                     for (int i = 0; 1; i++)
491                     {   if (i == OBJECT_NAME_SIZE)
492                         {   reason = 8;
493                             goto Lcorrupt;
494                         }
495                         char c = header->object_name[i];
496                         if (c == '/')
497                         {   om->name[i] = 0;
498                             break;
499                         }
500                         om->name[i] = c;
501                     }
502                 }
503                 om->name_offset = -1;
504                 om->file_time = strtoul(header->file_time, &endptr, 10);
505                 om->user_id   = strtoul(header->user_id, &endptr, 10);
506                 om->group_id  = strtoul(header->group_id, &endptr, 10);
507                 om->file_mode = strtoul(header->file_mode, &endptr, 8);
508                 om->scan = 0;
509                 objmodules.push(om);
510             }
511             offset += (size + 1) & ~1;
512         }
513         if (offset != buflen)
514         {   reason = 9;
515             goto Lcorrupt;
516         }
517
518         /* Scan the library's symbol table, and insert it into our own.
519          * We use this instead of rescanning the object module, because
520          * the library's creator may have a different idea of what symbols
521          * go into the symbol table than we do.
522          * This is also probably faster.
523          */
524         unsigned nsymbols = sgetl(symtab);
525         char *s = symtab + 4 + nsymbols * 4;
526         if (4 + nsymbols * (4 + 1) > symtab_size)
527         {   reason = 10;
528             goto Lcorrupt;
529         }
530         for (unsigned i = 0; i < nsymbols; i++)
531         {   char *name = s;
532             s += strlen(name) + 1;
533             if (s - symtab > symtab_size)
534             {   reason = 11;
535                 goto Lcorrupt;
536             }
537             unsigned moff = sgetl(symtab + 4 + i * 4);
538 //printf("symtab[%d] moff = %x  %x, name = %s\n", i, moff, moff + sizeof(Header), name);
539             for (unsigned m = mstart; 1; m++)
540             {   if (m == objmodules.dim)
541                 {   reason = 12;
542                     goto Lcorrupt;              // didn't find it
543                 }
544                 ObjModule *om = (ObjModule *)objmodules.data[m];
545 //printf("\t%x\n", (char *)om->base - (char *)buf);
546                 if (moff + sizeof(Header) == (char *)om->base - (char *)buf)
547                 {
548                     addSymbol(om, name, 1);
549 //                  if (mstart == m)
550 //                      mstart++;
551                     break;
552                 }
553             }
554         }
555
556         return;
557     }
558
559     if (memcmp(buf, elf, 4) != 0)
560     {   reason = 13;
561         goto Lcorrupt;
562     }
563
564     /* It's an ELF object module
565      */
566     ObjModule *om = new ObjModule();
567     om->base = (unsigned char *)buf;
568     om->length = buflen;
569     om->offset = 0;
570     om->name = FileName::name(module_name);     // remove path, but not extension
571     om->name_offset = -1;
572     om->scan = 1;
573     if (fromfile)
574     {   struct stat statbuf;
575         int i = stat(module_name, &statbuf);
576         if (i == -1)            // error, errno is set
577         {   reason = 14;
578             goto Lcorrupt;
579         }
580         om->file_time = statbuf.st_ctime;
581         om->user_id   = statbuf.st_uid;
582         om->group_id  = statbuf.st_gid;
583         om->file_mode = statbuf.st_mode;
584     }
585     else
586     {   /* Mock things up for the object module file that never was
587          * actually written out.
588          */
589         static uid_t uid;
590         static gid_t gid;
591         static int init;
592         if (!init)
593         {   init = 1;
594             uid = getuid();
595             gid = getgid();
596         }
597         time(&om->file_time);
598         om->user_id = uid;
599         om->group_id = gid;
600         om->file_mode = 0100640;
601     }
602     objmodules.push(om);
603 }
604
605
606 /*****************************************************************************/
607 /*****************************************************************************/
608
609 /**********************************************
610  * Create and write library to libbuf.
611  * The library consists of:
612  *      !<arch>\n
613  *      header
614  *      dictionary
615  *      object modules...
616  */
617
618 void Library::WriteLibToBuffer(OutBuffer *libbuf)
619 {
620 #if LOG
621     printf("Library::WriteLibToBuffer()\n");
622 #endif
623
624     /************* Scan Object Modules for Symbols ******************/
625
626     for (int i = 0; i < objmodules.dim; i++)
627     {   ObjModule *om = (ObjModule *)objmodules.data[i];
628         if (om->scan)
629         {
630             scanObjModule(om);
631         }
632     }
633
634     /************* Determine string section ******************/
635
636     /* The string section is where we store long file names.
637      */
638     unsigned noffset = 0;
639     for (int i = 0; i < objmodules.dim; i++)
640     {   ObjModule *om = (ObjModule *)objmodules.data[i];
641         size_t len = strlen(om->name);
642         if (len >= OBJECT_NAME_SIZE)
643         {
644             om->name_offset = noffset;
645             noffset += len + 2;
646         }
647         else
648             om->name_offset = -1;
649     }
650
651 #if LOG
652     printf("\tnoffset = x%x\n", noffset);
653 #endif
654
655     /************* Determine module offsets ******************/
656
657     unsigned moffset = 8 + sizeof(Header) + 4;
658
659     for (int i = 0; i < objsymbols.dim; i++)
660     {   ObjSymbol *os = (ObjSymbol *)objsymbols.data[i];
661
662         moffset += 4 + strlen(os->name) + 1;
663     }
664     unsigned hoffset = moffset;
665
666 #if LOG
667     printf("\tmoffset = x%x\n", moffset);
668 #endif
669
670     moffset += moffset & 1;
671     if (noffset)
672          moffset += sizeof(Header) + noffset;
673
674     for (int i = 0; i < objmodules.dim; i++)
675     {   ObjModule *om = (ObjModule *)objmodules.data[i];
676
677         moffset += moffset & 1;
678         om->offset = moffset;
679         moffset += sizeof(Header) + om->length;
680     }
681
682     libbuf->reserve(moffset);
683
684     /************* Write the library ******************/
685     libbuf->write("!<arch>\n", 8);
686
687     ObjModule om;
688     om.name_offset = -1;
689     om.base = NULL;
690     om.length = hoffset - (8 + sizeof(Header));
691     om.offset = 8;
692     om.name = (char*)"";
693     ::time(&om.file_time);
694     om.user_id = 0;
695     om.group_id = 0;
696     om.file_mode = 0;
697
698     Header h;
699     OmToHeader(&h, &om);
700     libbuf->write(&h, sizeof(h));
701     char buf[4];
702     sputl(objsymbols.dim, buf);
703     libbuf->write(buf, 4);
704
705     for (int i = 0; i < objsymbols.dim; i++)
706     {   ObjSymbol *os = (ObjSymbol *)objsymbols.data[i];
707
708         sputl(os->om->offset, buf);
709         libbuf->write(buf, 4);
710     }
711
712     for (int i = 0; i < objsymbols.dim; i++)
713     {   ObjSymbol *os = (ObjSymbol *)objsymbols.data[i];
714
715         libbuf->writestring(os->name);
716         libbuf->writeByte(0);
717     }
718
719 #if LOG
720     printf("\tlibbuf->moffset = x%x\n", libbuf->offset);
721 #endif
722
723     /* Write out the string section
724      */
725     if (noffset)
726     {
727         if (libbuf->offset & 1)
728             libbuf->writeByte('\n');
729
730         // header
731         memset(&h, ' ', sizeof(Header));
732         h.object_name[0] = '/';
733         h.object_name[1] = '/';
734         size_t len = sprintf(h.file_size, "%u", noffset);
735         assert(len < 10);
736         h.file_size[len] = ' ';
737         h.trailer[0] = '`';
738         h.trailer[1] = '\n';
739         libbuf->write(&h, sizeof(h));
740
741         for (int i = 0; i < objmodules.dim; i++)
742         {   ObjModule *om = (ObjModule *)objmodules.data[i];
743             if (om->name_offset >= 0)
744             {   libbuf->writestring(om->name);
745                 libbuf->writeByte('/');
746                 libbuf->writeByte('\n');
747             }
748         }
749     }
750
751     /* Write out each of the object modules
752      */
753     for (int i = 0; i < objmodules.dim; i++)
754     {   ObjModule *om = (ObjModule *)objmodules.data[i];
755
756         if (libbuf->offset & 1)
757             libbuf->writeByte('\n');    // module alignment
758
759         assert(libbuf->offset == om->offset);
760
761         OmToHeader(&h, om);
762         libbuf->write(&h, sizeof(h));   // module header
763
764         libbuf->write(om->base, om->length);    // module contents
765     }
766
767 #if LOG
768     printf("moffset = x%x, libbuf->offset = x%x\n", moffset, libbuf->offset);
769 #endif
770     assert(libbuf->offset == moffset);
771 }
Note: See TracBrowser for help on using the browser.