1 /**
2  * Compiler implementation of the
3  * $(LINK2 http://www.dlang.org, D programming language).
4  *
5  * Copyright:   Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved
6  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
7  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8  * Source:      $(DMDSRC _libelf.d)
9  */
10 
11 module ddmd.libelf;
12 
13 import core.stdc.time;
14 import core.stdc..string;
15 import core.stdc.stdlib;
16 import core.stdc.stdio;
17 import core.sys.posix.sys.stat;
18 import core.sys.posix.unistd;
19 
20 import ddmd.globals;
21 import ddmd.lib;
22 import ddmd.utils;
23 
24 import ddmd.root.array;
25 import ddmd.root.file;
26 import ddmd.root.filename;
27 import ddmd.root.outbuffer;
28 import ddmd.root.port;
29 import ddmd.root.rmem;
30 import ddmd.root.stringtable;
31 
32 import ddmd.scanelf;
33 
34 enum LOG = false;
35 
36 struct ElfObjSymbol
37 {
38     const(char)[] name;
39     ElfObjModule* om;
40 }
41 
42 alias ElfObjModules = Array!(ElfObjModule*);
43 alias ElfObjSymbols = Array!(ElfObjSymbol*);
44 
45 final class LibElf : Library
46 {
47     ElfObjModules objmodules; // ElfObjModule[]
48     ElfObjSymbols objsymbols; // ElfObjSymbol[]
49     StringTable tab;
50 
51     extern (D) this()
52     {
53         tab._init(14000);
54     }
55 
56     /***************************************
57      * Add object module or library to the library.
58      * Examine the buffer to see which it is.
59      * If the buffer is NULL, use module_name as the file name
60      * and load the file.
61      */
62     override void addObject(const(char)* module_name, const ubyte[] buffer)
63     {
64         if (!module_name)
65             module_name = "";
66         static if (LOG)
67         {
68             printf("LibElf::addObject(%s)\n", module_name);
69         }
70 
71         void corrupt(int reason)
72         {
73             error("corrupt ELF object module %s %d", module_name, reason);
74         }
75 
76         int fromfile = 0;
77         auto buf = buffer.ptr;
78         auto buflen = buffer.length;
79         if (!buf)
80         {
81             assert(module_name[0]);
82             File* file = File.create(cast(char*)module_name);
83             readFile(Loc(), file);
84             buf = file.buffer;
85             buflen = file.len;
86             file._ref = 1;
87             fromfile = 1;
88         }
89         int reason = 0;
90         if (buflen < 16)
91         {
92             static if (LOG)
93             {
94                 printf("buf = %p, buflen = %d\n", buf, buflen);
95             }
96             return corrupt(__LINE__);
97         }
98         if (memcmp(buf, cast(char*)"!<arch>\n", 8) == 0)
99         {
100             /* Library file.
101              * Pull each object module out of the library and add it
102              * to the object module array.
103              */
104             static if (LOG)
105             {
106                 printf("archive, buf = %p, buflen = %d\n", buf, buflen);
107             }
108             uint offset = 8;
109             char* symtab = null;
110             uint symtab_size = 0;
111             char* filenametab = null;
112             uint filenametab_size = 0;
113             uint mstart = cast(uint)objmodules.dim;
114             while (offset < buflen)
115             {
116                 if (offset + ElfLibHeader.sizeof >= buflen)
117                     return corrupt(__LINE__);
118                 ElfLibHeader* header = cast(ElfLibHeader*)(cast(ubyte*)buf + offset);
119                 offset += ElfLibHeader.sizeof;
120                 char* endptr = null;
121                 uint size = cast(uint)strtoul(header.file_size.ptr, &endptr, 10);
122                 if (endptr >= header.file_size.ptr + 10 || *endptr != ' ')
123                     return corrupt(__LINE__);
124                 if (offset + size > buflen)
125                     return corrupt(__LINE__);
126                 if (header.object_name[0] == '/' && header.object_name[1] == ' ')
127                 {
128                     /* Instead of rescanning the object modules we pull from a
129                      * library, just use the already created symbol table.
130                      */
131                     if (symtab)
132                         return corrupt(__LINE__);
133                     symtab = cast(char*)buf + offset;
134                     symtab_size = size;
135                     if (size < 4)
136                         return corrupt(__LINE__);
137                 }
138                 else if (header.object_name[0] == '/' && header.object_name[1] == '/')
139                 {
140                     /* This is the file name table, save it for later.
141                      */
142                     if (filenametab)
143                         return corrupt(__LINE__);
144                     filenametab = cast(char*)buf + offset;
145                     filenametab_size = size;
146                 }
147                 else
148                 {
149                     auto om = new ElfObjModule();
150                     om.base = cast(ubyte*)buf + offset; /*- sizeof(ElfLibHeader)*/
151                     om.length = size;
152                     om.offset = 0;
153                     if (header.object_name[0] == '/')
154                     {
155                         /* Pick long name out of file name table
156                          */
157                         uint foff = cast(uint)strtoul(header.object_name.ptr + 1, &endptr, 10);
158                         uint i;
159                         for (i = 0; 1; i++)
160                         {
161                             if (foff + i >= filenametab_size)
162                                 return corrupt(__LINE__);
163                             char c = filenametab[foff + i];
164                             if (c == '/')
165                                 break;
166                         }
167                         auto n = cast(char*)malloc(i + 1);
168                         assert(n);
169                         memcpy(n, filenametab + foff, i);
170                         n[i] = 0;
171                         om.name = n[0 .. i];
172                     }
173                     else
174                     {
175                         /* Pick short name out of header
176                          */
177                         auto n = cast(char*)malloc(ELF_OBJECT_NAME_SIZE);
178                         assert(n);
179                         for (int i = 0; 1; i++)
180                         {
181                             if (i == ELF_OBJECT_NAME_SIZE)
182                                 return corrupt(__LINE__);
183                             char c = header.object_name[i];
184                             if (c == '/')
185                             {
186                                 n[i] = 0;
187                                 om.name = n[0 .. i];
188                                 break;
189                             }
190                             n[i] = c;
191                         }
192                     }
193                     om.name_offset = -1;
194                     om.file_time = strtoul(header.file_time.ptr, &endptr, 10);
195                     om.user_id = cast(uint)strtoul(header.user_id.ptr, &endptr, 10);
196                     om.group_id = cast(uint)strtoul(header.group_id.ptr, &endptr, 10);
197                     om.file_mode = cast(uint)strtoul(header.file_mode.ptr, &endptr, 8);
198                     om.scan = 0; // don't scan object module for symbols
199                     objmodules.push(om);
200                 }
201                 offset += (size + 1) & ~1;
202             }
203             if (offset != buflen)
204                 return corrupt(__LINE__);
205             /* Scan the library's symbol table, and insert it into our own.
206              * We use this instead of rescanning the object module, because
207              * the library's creator may have a different idea of what symbols
208              * go into the symbol table than we do.
209              * This is also probably faster.
210              */
211             uint nsymbols = Port.readlongBE(symtab);
212             char* s = symtab + 4 + nsymbols * 4;
213             if (4 + nsymbols * (4 + 1) > symtab_size)
214                 return corrupt(__LINE__);
215             for (uint i = 0; i < nsymbols; i++)
216             {
217                 const(char)[] name = s[0 .. strlen(s)];
218                 s += name.length + 1;
219                 if (s - symtab > symtab_size)
220                     return corrupt(__LINE__);
221                 uint moff = Port.readlongBE(symtab + 4 + i * 4);
222                 //printf("symtab[%d] moff = %x  %x, name = %s\n", i, moff, moff + sizeof(Header), name.ptr);
223                 for (uint m = mstart; 1; m++)
224                 {
225                     if (m == objmodules.dim)
226                         return corrupt(__LINE__);  // didn't find it
227                     ElfObjModule* om = objmodules[m];
228                     //printf("\t%x\n", (char *)om->base - (char *)buf);
229                     if (moff + ElfLibHeader.sizeof == cast(char*)om.base - cast(char*)buf)
230                     {
231                         addSymbol(om, name, 1);
232                         //if (mstart == m)
233                         //    mstart++;
234                         break;
235                     }
236                 }
237             }
238             return;
239         }
240         /* It's an object module
241          */
242         auto om = new ElfObjModule();
243         om.base = cast(ubyte*)buf;
244         om.length = cast(uint)buflen;
245         om.offset = 0;
246         auto n = cast(char*)FileName.name(module_name); // remove path, but not extension
247         om.name = n[0 .. strlen(n)];
248         om.name_offset = -1;
249         om.scan = 1;
250         if (fromfile)
251         {
252             stat_t statbuf;
253             int i = stat(module_name, &statbuf);
254             if (i == -1) // error, errno is set
255                 return corrupt(__LINE__);
256             om.file_time = statbuf.st_ctime;
257             om.user_id = statbuf.st_uid;
258             om.group_id = statbuf.st_gid;
259             om.file_mode = statbuf.st_mode;
260         }
261         else
262         {
263             /* Mock things up for the object module file that never was
264              * actually written out.
265              */
266             static __gshared uid_t uid;
267             static __gshared gid_t gid;
268             static __gshared int _init;
269             if (!_init)
270             {
271                 _init = 1;
272                 uid = getuid();
273                 gid = getgid();
274             }
275             time(&om.file_time);
276             om.user_id = uid;
277             om.group_id = gid;
278             om.file_mode = (1 << 15) | (6 << 6) | (4 << 3); // 0100640
279         }
280         objmodules.push(om);
281     }
282 
283     /*****************************************************************************/
284 
285     void addSymbol(ElfObjModule* om, const(char)[] name, int pickAny = 0)
286     {
287         static if (LOG)
288         {
289             printf("LibElf::addSymbol(%s, %s, %d)\n", om.name.ptr, name.ptr, pickAny);
290         }
291         StringValue* s = tab.insert(name.ptr, name.length, null);
292         if (!s)
293         {
294             // already in table
295             if (!pickAny)
296             {
297                 s = tab.lookup(name.ptr, name.length);
298                 assert(s);
299                 ElfObjSymbol* os = cast(ElfObjSymbol*)s.ptrvalue;
300                 error("multiple definition of %s: %s and %s: %s", om.name.ptr, name.ptr, os.om.name.ptr, os.name.ptr);
301             }
302         }
303         else
304         {
305             auto os = new ElfObjSymbol();
306             os.name = xarraydup(name);
307             os.om = om;
308             s.ptrvalue = cast(void*)os;
309             objsymbols.push(os);
310         }
311     }
312 
313 private:
314     /************************************
315      * Scan single object module for dictionary symbols.
316      * Send those symbols to LibElf::addSymbol().
317      */
318     void scanObjModule(ElfObjModule* om)
319     {
320         static if (LOG)
321         {
322             printf("LibElf::scanObjModule(%s)\n", om.name.ptr);
323         }
324 
325         extern (D) void addSymbol(const(char)[] name, int pickAny)
326         {
327             this.addSymbol(om, name, pickAny);
328         }
329 
330         scanElfObjModule(&addSymbol, om.base[0 .. om.length], om.name.ptr, loc);
331     }
332 
333     /*****************************************************************************/
334     /*****************************************************************************/
335     /**********************************************
336      * Create and write library to libbuf.
337      * The library consists of:
338      *      !<arch>\n
339      *      header
340      *      dictionary
341      *      object modules...
342      */
343     protected override void WriteLibToBuffer(OutBuffer* libbuf)
344     {
345         static if (LOG)
346         {
347             printf("LibElf::WriteLibToBuffer()\n");
348         }
349         /************* Scan Object Modules for Symbols ******************/
350         for (size_t i = 0; i < objmodules.dim; i++)
351         {
352             ElfObjModule* om = objmodules[i];
353             if (om.scan)
354             {
355                 scanObjModule(om);
356             }
357         }
358         /************* Determine string section ******************/
359         /* The string section is where we store long file names.
360          */
361         uint noffset = 0;
362         for (size_t i = 0; i < objmodules.dim; i++)
363         {
364             ElfObjModule* om = objmodules[i];
365             size_t len = om.name.length;
366             if (len >= ELF_OBJECT_NAME_SIZE)
367             {
368                 om.name_offset = noffset;
369                 noffset += len + 2;
370             }
371             else
372                 om.name_offset = -1;
373         }
374         static if (LOG)
375         {
376             printf("\tnoffset = x%x\n", noffset);
377         }
378         /************* Determine module offsets ******************/
379         uint moffset = 8 + ElfLibHeader.sizeof + 4;
380         for (size_t i = 0; i < objsymbols.dim; i++)
381         {
382             ElfObjSymbol* os = objsymbols[i];
383             moffset += 4 + os.name.length + 1;
384         }
385         uint hoffset = moffset;
386         static if (LOG)
387         {
388             printf("\tmoffset = x%x\n", moffset);
389         }
390         moffset += moffset & 1;
391         if (noffset)
392             moffset += ElfLibHeader.sizeof + noffset;
393         for (size_t i = 0; i < objmodules.dim; i++)
394         {
395             ElfObjModule* om = objmodules[i];
396             moffset += moffset & 1;
397             om.offset = moffset;
398             moffset += ElfLibHeader.sizeof + om.length;
399         }
400         libbuf.reserve(moffset);
401         /************* Write the library ******************/
402         libbuf.write("!<arch>\n".ptr, 8);
403         ElfObjModule om;
404         om.name_offset = -1;
405         om.base = null;
406         om.length = cast(uint)(hoffset - (8 + ElfLibHeader.sizeof));
407         om.offset = 8;
408         om.name = "";
409         .time(&om.file_time);
410         om.user_id = 0;
411         om.group_id = 0;
412         om.file_mode = 0;
413         ElfLibHeader h;
414         ElfOmToHeader(&h, &om);
415         libbuf.write(&h, h.sizeof);
416         char[4] buf;
417         Port.writelongBE(cast(uint)objsymbols.dim, buf.ptr);
418         libbuf.write(buf.ptr, 4);
419         for (size_t i = 0; i < objsymbols.dim; i++)
420         {
421             ElfObjSymbol* os = objsymbols[i];
422             Port.writelongBE(os.om.offset, buf.ptr);
423             libbuf.write(buf.ptr, 4);
424         }
425         for (size_t i = 0; i < objsymbols.dim; i++)
426         {
427             ElfObjSymbol* os = objsymbols[i];
428             libbuf.writestring(os.name);
429             libbuf.writeByte(0);
430         }
431         static if (LOG)
432         {
433             printf("\tlibbuf->moffset = x%x\n", libbuf.offset);
434         }
435         /* Write out the string section
436          */
437         if (noffset)
438         {
439             if (libbuf.offset & 1)
440                 libbuf.writeByte('\n');
441             // header
442             memset(&h, ' ', ElfLibHeader.sizeof);
443             h.object_name[0] = '/';
444             h.object_name[1] = '/';
445             size_t len = sprintf(h.file_size.ptr, "%u", noffset);
446             assert(len < 10);
447             h.file_size[len] = ' ';
448             h.trailer[0] = '`';
449             h.trailer[1] = '\n';
450             libbuf.write(&h, h.sizeof);
451             for (size_t i = 0; i < objmodules.dim; i++)
452             {
453                 ElfObjModule* om2 = objmodules[i];
454                 if (om2.name_offset >= 0)
455                 {
456                     libbuf.writestring(om2.name);
457                     libbuf.writeByte('/');
458                     libbuf.writeByte('\n');
459                 }
460             }
461         }
462         /* Write out each of the object modules
463          */
464         for (size_t i = 0; i < objmodules.dim; i++)
465         {
466             ElfObjModule* om2 = objmodules[i];
467             if (libbuf.offset & 1)
468                 libbuf.writeByte('\n'); // module alignment
469             assert(libbuf.offset == om2.offset);
470             ElfOmToHeader(&h, om2);
471             libbuf.write(&h, h.sizeof); // module header
472             libbuf.write(om2.base, om2.length); // module contents
473         }
474         static if (LOG)
475         {
476             printf("moffset = x%x, libbuf->offset = x%x\n", moffset, libbuf.offset);
477         }
478         assert(libbuf.offset == moffset);
479     }
480 }
481 
482 extern (C++) Library LibElf_factory()
483 {
484     return new LibElf();
485 }
486 
487 /*****************************************************************************/
488 /*****************************************************************************/
489 struct ElfObjModule
490 {
491     ubyte* base; // where are we holding it in memory
492     uint length; // in bytes
493     uint offset; // offset from start of library
494     const(char)[] name; // module name (file name) with terminating 0
495     int name_offset; // if not -1, offset into string table of name
496     time_t file_time; // file time
497     uint user_id;
498     uint group_id;
499     uint file_mode;
500     int scan; // 1 means scan for symbols
501 }
502 
503 enum ELF_OBJECT_NAME_SIZE = 16;
504 
505 struct ElfLibHeader
506 {
507     char[ELF_OBJECT_NAME_SIZE] object_name;
508     char[12] file_time;
509     char[6] user_id;
510     char[6] group_id;
511     char[8] file_mode; // in octal
512     char[10] file_size;
513     char[2] trailer;
514 }
515 
516 extern (C++) void ElfOmToHeader(ElfLibHeader* h, ElfObjModule* om)
517 {
518     char* buffer = cast(char*)h;
519     // user_id and group_id are padded on 6 characters in Header struct.
520     // Squashing to 0 if more than 999999.
521     if (om.user_id > 999999)
522         om.user_id = 0;
523     if (om.group_id > 999999)
524         om.group_id = 0;
525     size_t len;
526     if (om.name_offset == -1)
527     {
528         // "name/           1423563789  5000  5000  100640  3068      `\n"
529         //  |^^^^^^^^^^^^^^^|^^^^^^^^^^^|^^^^^|^^^^^|^^^^^^^|^^^^^^^^^|^^
530         //        name       file_time   u_id gr_id  fmode    fsize   trailer
531         len = snprintf(buffer, ElfLibHeader.sizeof, "%-16s%-12llu%-6u%-6u%-8o%-10u`", om.name.ptr, cast(long)om.file_time, om.user_id, om.group_id, om.file_mode, om.length);
532         // adding '/' after the name field
533         const(size_t) name_length = om.name.length;
534         assert(name_length < ELF_OBJECT_NAME_SIZE);
535         buffer[name_length] = '/';
536     }
537     else
538     {
539         // "/162007         1423563789  5000  5000  100640  3068      `\n"
540         //  |^^^^^^^^^^^^^^^|^^^^^^^^^^^|^^^^^|^^^^^|^^^^^^^|^^^^^^^^^|^^
541         //     name_offset   file_time   u_id gr_id  fmode    fsize   trailer
542         len = snprintf(buffer, ElfLibHeader.sizeof, "/%-15d%-12llu%-6u%-6u%-8o%-10u`", om.name_offset, cast(long)om.file_time, om.user_id, om.group_id, om.file_mode, om.length);
543     }
544     assert(ElfLibHeader.sizeof > 0 && len == ElfLibHeader.sizeof - 1);
545     // replace trailing \0 with \n
546     buffer[len] = '\n';
547 }