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