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 1
    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 1
    extern (D) this()
66
    {
67 1
        tab._init(14_000);
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 1
            error("corrupt ELF object module %.*s %d",
87
                  cast(int)module_name.length, module_name.ptr, reason);
88
        }
89

90 1
        int fromfile = 0;
91 1
        auto buf = buffer.ptr;
92 1
        auto buflen = buffer.length;
93 1
        if (!buf)
94
        {
95 1
            assert(module_name.length);
96
            // read file and take buffer ownership
97 1
            auto data = readFile(Loc.initial, module_name).extractSlice();
98 1
            buf = data.ptr;
99 1
            buflen = data.length;
100 1
            fromfile = 1;
101
        }
102 1
        if (buflen < 16)
103
        {
104
            static if (LOG)
105
            {
106
                printf("buf = %p, buflen = %d\n", buf, buflen);
107
            }
108 1
            return corrupt(__LINE__);
109
        }
110 1
        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 1
            uint offset = 8;
121 1
            char* symtab = null;
122 1
            uint symtab_size = 0;
123 1
            char* filenametab = null;
124 1
            uint filenametab_size = 0;
125 1
            uint mstart = cast(uint)objmodules.dim;
126 1
            while (offset < buflen)
127
            {
128 1
                if (offset + ElfLibHeader.sizeof >= buflen)
129 0
                    return corrupt(__LINE__);
130 1
                ElfLibHeader* header = cast(ElfLibHeader*)(cast(ubyte*)buf + offset);
131 1
                offset += ElfLibHeader.sizeof;
132 1
                char* endptr = null;
133 1
                uint size = cast(uint)strtoul(header.file_size.ptr, &endptr, 10);
134 1
                if (endptr >= header.file_size.ptr + 10 || *endptr != ' ')
135 0
                    return corrupt(__LINE__);
136 1
                if (offset + size > buflen)
137 0
                    return corrupt(__LINE__);
138 1
                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 1
                    if (symtab)
144 0
                        return corrupt(__LINE__);
145 1
                    symtab = cast(char*)buf + offset;
146 1
                    symtab_size = size;
147 1
                    if (size < 4)
148 0
                        return corrupt(__LINE__);
149
                }
150 1
                else if (header.object_name[0] == '/' && header.object_name[1] == '/')
151
                {
152
                    /* This is the file name table, save it for later.
153
                     */
154 1
                    if (filenametab)
155 0
                        return corrupt(__LINE__);
156 1
                    filenametab = cast(char*)buf + offset;
157 1
                    filenametab_size = size;
158
                }
159
                else
160
                {
161 1
                    auto om = new ElfObjModule();
162 1
                    om.base = cast(ubyte*)buf + offset; /*- sizeof(ElfLibHeader)*/
163 1
                    om.length = size;
164 1
                    om.offset = 0;
165 1
                    if (header.object_name[0] == '/')
166
                    {
167
                        /* Pick long name out of file name table
168
                         */
169 1
                        uint foff = cast(uint)strtoul(header.object_name.ptr + 1, &endptr, 10);
170 1
                        uint i;
171 1
                        for (i = 0; 1; i++)
172
                        {
173 1
                            if (foff + i >= filenametab_size)
174 0
                                return corrupt(__LINE__);
175 1
                            char c = filenametab[foff + i];
176 1
                            if (c == '/')
177 1
                                break;
178
                        }
179 1
                        auto n = cast(char*)Mem.check(malloc(i + 1));
180 1
                        memcpy(n, filenametab + foff, i);
181 1
                        n[i] = 0;
182 1
                        om.name = n[0 .. i];
183
                    }
184
                    else
185
                    {
186
                        /* Pick short name out of header
187
                         */
188 1
                        auto n = cast(char*)Mem.check(malloc(ELF_OBJECT_NAME_SIZE));
189 1
                        for (int i = 0; 1; i++)
190
                        {
191 1
                            if (i == ELF_OBJECT_NAME_SIZE)
192 0
                                return corrupt(__LINE__);
193 1
                            char c = header.object_name[i];
194 1
                            if (c == '/')
195
                            {
196 1
                                n[i] = 0;
197 1
                                om.name = n[0 .. i];
198 1
                                break;
199
                            }
200 1
                            n[i] = c;
201
                        }
202
                    }
203 1
                    om.name_offset = -1;
204 1
                    om.file_time = strtoul(header.file_time.ptr, &endptr, 10);
205 1
                    om.user_id = cast(uint)strtoul(header.user_id.ptr, &endptr, 10);
206 1
                    om.group_id = cast(uint)strtoul(header.group_id.ptr, &endptr, 10);
207 1
                    om.file_mode = cast(uint)strtoul(header.file_mode.ptr, &endptr, 8);
208 1
                    om.scan = 0; // don't scan object module for symbols
209 1
                    objmodules.push(om);
210
                }
211 1
                offset += (size + 1) & ~1;
212
            }
213 1
            if (offset != buflen)
214 0
                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 1
            uint nsymbols = Port.readlongBE(symtab);
222 1
            char* s = symtab + 4 + nsymbols * 4;
223 1
            if (4 + nsymbols * (4 + 1) > symtab_size)
224 0
                return corrupt(__LINE__);
225 1
            for (uint i = 0; i < nsymbols; i++)
226
            {
227 1
                const(char)[] name = s.toDString();
228 1
                s += name.length + 1;
229 1
                if (s - symtab > symtab_size)
230 0
                    return corrupt(__LINE__);
231 1
                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 1
                for (uint m = mstart; 1; m++)
234
                {
235 1
                    if (m == objmodules.dim)
236 0
                        return corrupt(__LINE__);  // didn't find it
237 1
                    ElfObjModule* om = objmodules[m];
238
                    //printf("\t%x\n", (char *)om.base - (char *)buf);
239 1
                    if (moff + ElfLibHeader.sizeof == cast(char*)om.base - cast(char*)buf)
240
                    {
241 1
                        addSymbol(om, name, 1);
242
                        //if (mstart == m)
243
                        //    mstart++;
244 1
                        break;
245
                    }
246
                }
247
            }
248 1
            return;
249
        }
250
        /* It's an object module
251
         */
252 1
        auto om = new ElfObjModule();
253 1
        om.base = cast(ubyte*)buf;
254 1
        om.length = cast(uint)buflen;
255 1
        om.offset = 0;
256
        // remove path, but not extension
257 1
        om.name = FileName.name(module_name);
258 1
        om.name_offset = -1;
259 1
        om.scan = 1;
260 1
        if (fromfile)
261
        {
262 0
            stat_t statbuf;
263 0
            int i = module_name.toCStringThen!(slice => stat(slice.ptr, &statbuf));
264 0
            if (i == -1) // error, errno is set
265 0
                return corrupt(__LINE__);
266 0
            om.file_time = statbuf.st_ctime;
267 0
            om.user_id = statbuf.st_uid;
268 0
            om.group_id = statbuf.st_gid;
269 0
            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 1
            __gshared uid_t uid;
277 1
            __gshared gid_t gid;
278 1
            __gshared int _init;
279 1
            if (!_init)
280
            {
281 1
                _init = 1;
282 1
                uid = getuid();
283 1
                gid = getgid();
284
            }
285 1
            time(&om.file_time);
286 1
            om.user_id = uid;
287 1
            om.group_id = gid;
288 1
            om.file_mode = (1 << 15) | (6 << 6) | (4 << 3); // 0100640
289
        }
290 1
        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 1
        auto s = tab.insert(name.ptr, name.length, null);
302 1
        if (!s)
303
        {
304
            // already in table
305 1
            if (!pickAny)
306
            {
307 0
                s = tab.lookup(name.ptr, name.length);
308 0
                assert(s);
309 0
                ElfObjSymbol* os = s.value;
310 0
                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 1
            auto os = new ElfObjSymbol();
316 1
            os.name = xarraydup(name);
317 1
            os.om = om;
318 1
            s.value = os;
319 1
            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 1
            this.addSymbol(om, name, pickAny);
338
        }
339

340 1
        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 1
        foreach (om; objmodules)
361
        {
362 1
            if (om.scan)
363
            {
364 1
                scanObjModule(om);
365
            }
366
        }
367
        /************* Determine string section ******************/
368
        /* The string section is where we store long file names.
369
         */
370 1
        uint noffset = 0;
371 1
        foreach (om; objmodules)
372
        {
373 1
            size_t len = om.name.length;
374 1
            if (len >= ELF_OBJECT_NAME_SIZE)
375
            {
376 1
                om.name_offset = noffset;
377 1
                noffset += len + 2;
378
            }
379
            else
380 1
                om.name_offset = -1;
381
        }
382
        static if (LOG)
383
        {
384
            printf("\tnoffset = x%x\n", noffset);
385
        }
386
        /************* Determine module offsets ******************/
387 1
        uint moffset = 8 + ElfLibHeader.sizeof + 4;
388 1
        foreach (os; objsymbols)
389
        {
390 1
            moffset += 4 + os.name.length + 1;
391
        }
392 1
        uint hoffset = moffset;
393
        static if (LOG)
394
        {
395
            printf("\tmoffset = x%x\n", moffset);
396
        }
397 1
        moffset += moffset & 1;
398 1
        if (noffset)
399 1
            moffset += ElfLibHeader.sizeof + noffset;
400 1
        foreach (om; objmodules)
401
        {
402 1
            moffset += moffset & 1;
403 1
            om.offset = moffset;
404 1
            moffset += ElfLibHeader.sizeof + om.length;
405
        }
406 1
        libbuf.reserve(moffset);
407
        /************* Write the library ******************/
408 1
        libbuf.write("!<arch>\n");
409 1
        ElfObjModule om;
410 1
        om.name_offset = -1;
411 1
        om.base = null;
412 1
        om.length = cast(uint)(hoffset - (8 + ElfLibHeader.sizeof));
413 1
        om.offset = 8;
414 1
        om.name = "";
415 1
        .time(&om.file_time);
416 1
        om.user_id = 0;
417 1
        om.group_id = 0;
418 1
        om.file_mode = 0;
419 1
        ElfLibHeader h;
420 1
        ElfOmToHeader(&h, &om);
421 1
        libbuf.write((&h)[0 .. 1]);
422 1
        char[4] buf;
423 1
        Port.writelongBE(cast(uint)objsymbols.dim, buf.ptr);
424 1
        libbuf.write(buf[0 .. 4]);
425 1
        foreach (os; objsymbols)
426
        {
427 1
            Port.writelongBE(os.om.offset, buf.ptr);
428 1
            libbuf.write(buf[0 .. 4]);
429
        }
430 1
        foreach (os; objsymbols)
431
        {
432 1
            libbuf.writestring(os.name);
433 1
            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 1
        if (noffset)
442
        {
443 1
            if (libbuf.length & 1)
444 1
                libbuf.writeByte('\n');
445
            // header
446 1
            memset(&h, ' ', ElfLibHeader.sizeof);
447 1
            h.object_name[0] = '/';
448 1
            h.object_name[1] = '/';
449 1
            size_t len = sprintf(h.file_size.ptr, "%u", noffset);
450 1
            assert(len < 10);
451 1
            h.file_size[len] = ' ';
452 1
            h.trailer[0] = '`';
453 1
            h.trailer[1] = '\n';
454 1
            libbuf.write((&h)[0 .. 1]);
455 1
            foreach (om2; objmodules)
456
            {
457 1
                if (om2.name_offset >= 0)
458
                {
459 1
                    libbuf.writestring(om2.name);
460 1
                    libbuf.writeByte('/');
461 1
                    libbuf.writeByte('\n');
462
                }
463
            }
464
        }
465
        /* Write out each of the object modules
466
         */
467 1
        foreach (om2; objmodules)
468
        {
469 1
            if (libbuf.length & 1)
470 1
                libbuf.writeByte('\n'); // module alignment
471 1
            assert(libbuf.length == om2.offset);
472 1
            ElfOmToHeader(&h, om2);
473 1
            libbuf.write((&h)[0 .. 1]); // module header
474 1
            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 1
        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 1
    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 1
    if (om.user_id > 999_999)
519 0
        om.user_id = 0;
520 1
    if (om.group_id > 999_999)
521 0
        om.group_id = 0;
522 1
    size_t len;
523 1
    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 1
        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 1
        const(size_t) name_length = om.name.length;
531 1
        assert(name_length < ELF_OBJECT_NAME_SIZE);
532 1
        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 1
        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 1
    assert(ElfLibHeader.sizeof > 0 && len == ElfLibHeader.sizeof - 1);
542
    // replace trailing \0 with \n
543 1
    buffer[len] = '\n';
544
}

Read our documentation on viewing source code .

Loading