1 /**
2  * Extract symbols from an ELF object file.
3  *
4  * Copyright:   Copyright (C) 1999-2021 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/scanelf.d, _scanelf.d)
8  * Documentation:  https://dlang.org/phobos/dmd_scanelf.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/scanelf.d
10  */
11 
12 module dmd.scanelf;
13 
14 version(Windows) {}
15 else version(OSX) {}
16 else:
17 
18 version (linux)
19     import core.sys.linux.elf;
20 else version (FreeBSD)
21     import core.sys.freebsd.sys.elf;
22 else version (DragonFlyBSD)
23     import core.sys.dragonflybsd.sys.elf;
24 else version (Solaris)
25     import core.sys.solaris.elf;
26 
27 import core.stdc.string;
28 import core.checkedint;
29 
30 import dmd.globals;
31 import dmd.errors;
32 
33 enum LOG = false;
34 
35 /*****************************************
36  * Reads an object module from base[] and passes the names
37  * of any exported symbols to (*pAddSymbol)().
38  * Params:
39  *      pAddSymbol =  function to pass the names to
40  *      base =        array of contents of object module
41  *      module_name = name of the object module (used for error messages)
42  *      loc =         location to use for error printing
43  */
44 void scanElfObjModule(void delegate(const(char)[] name, int pickAny) pAddSymbol,
45         const(ubyte)[] base, const(char)* module_name, Loc loc)
46 {
47     static if (LOG)
48     {
49         printf("scanElfObjModule(%s)\n", module_name);
50     }
51 
52     void corrupt(int reason)
53     {
54         error(loc, "corrupt ELF object module `%s` %d", module_name, reason);
55     }
56 
57     if (base.length < Elf32_Ehdr.sizeof)
58         return corrupt(__LINE__); // must be at least large enough for ELF32
59     static immutable ubyte[4] elf = [0x7F, 'E', 'L', 'F']; // ELF file signature
60     if (base[0 .. elf.length] != elf[])
61         return corrupt(__LINE__);
62 
63     if (base[EI_VERSION] != EV_CURRENT)
64     {
65         return error(loc, "ELF object module `%s` has EI_VERSION = %d, should be %d",
66             module_name, base[EI_VERSION], EV_CURRENT);
67     }
68     if (base[EI_DATA] != ELFDATA2LSB)
69     {
70         return error(loc, "ELF object module `%s` is byte swapped and unsupported", module_name);
71     }
72     if (base[EI_CLASS] != ELFCLASS32 && base[EI_CLASS] != ELFCLASS64)
73     {
74         return error(loc, "ELF object module `%s` is unrecognized class %d", module_name, base[EI_CLASS]);
75     }
76 
77     void scanELF(uint model)()
78     {
79         static if (model == 32)
80         {
81             alias ElfXX_Ehdr = Elf32_Ehdr;
82             alias ElfXX_Shdr = Elf32_Shdr;
83             alias ElfXX_Sym = Elf32_Sym;
84         }
85         else
86         {
87             static assert(model == 64);
88             alias ElfXX_Ehdr = Elf64_Ehdr;
89             alias ElfXX_Shdr = Elf64_Shdr;
90             alias ElfXX_Sym = Elf64_Sym;
91         }
92 
93         if (base.length < ElfXX_Ehdr.sizeof)
94             return corrupt(__LINE__);
95 
96         const eh = cast(const(ElfXX_Ehdr)*) base.ptr;
97         if (eh.e_type != ET_REL)
98             return error(loc, "ELF object module `%s` is not relocatable", module_name);
99         if (eh.e_version != EV_CURRENT)
100             return corrupt(__LINE__);
101 
102         bool overflow;
103         const end = addu(eh.e_shoff, mulu(eh.e_shentsize, eh.e_shnum, overflow), overflow);
104         if (overflow || end > base.length)
105             return corrupt(__LINE__);
106 
107         /* For each Section
108          */
109         const sections = (cast(const(ElfXX_Shdr)*)(base.ptr + eh.e_shoff))[0 .. eh.e_shnum];
110         foreach (ref const section; sections)
111         {
112             if (section.sh_type != SHT_SYMTAB)
113                 continue;
114 
115             bool checkShdrXX(const ref ElfXX_Shdr shdr)
116             {
117                 bool overflow;
118                 return addu(shdr.sh_offset, shdr.sh_size, overflow) > base.length || overflow;
119             }
120 
121             if (checkShdrXX(section))
122                 return corrupt(__LINE__);
123 
124             /* sh_link gives the particular string table section
125              * used for the symbol names.
126              */
127             if (section.sh_link >= eh.e_shnum)
128                 return corrupt(__LINE__);
129 
130             const string_section = &sections[section.sh_link];
131             if (string_section.sh_type != SHT_STRTAB)
132                 return corrupt(__LINE__);
133 
134             if (checkShdrXX(*string_section))
135                 return corrupt(__LINE__);
136 
137             const string_tab = (cast(const(char)[])base)
138                 [cast(size_t)string_section.sh_offset ..
139                  cast(size_t)(string_section.sh_offset + string_section.sh_size)];
140 
141             /* Get the array of symbols this section refers to
142              */
143             const symbols = (cast(ElfXX_Sym*)(base.ptr + cast(size_t)section.sh_offset))
144                 [0 .. cast(size_t)(section.sh_size / ElfXX_Sym.sizeof)];
145 
146             foreach (ref const sym; symbols)
147             {
148                 const stb = sym.st_info >> 4;
149                 if (stb != STB_GLOBAL && stb != STB_WEAK || sym.st_shndx == SHN_UNDEF)
150                     continue; // it's extern
151 
152                 if (sym.st_name >= string_tab.length)
153                     return corrupt(__LINE__);
154 
155                 const name = &string_tab[sym.st_name];
156                 //printf("sym st_name = x%x\n", sym.st_name);
157                 const pend = cast(const(char*)) memchr(name, 0, string_tab.length - sym.st_name);
158                 if (!pend)       // if didn't find terminating 0 inside the string section
159                     return corrupt(__LINE__);
160                 pAddSymbol(name[0 .. pend - name], 1);
161             }
162         }
163     }
164 
165     if (base[EI_CLASS] == ELFCLASS32)
166     {
167         scanELF!32;
168     }
169     else
170     {
171         assert(base[EI_CLASS] == ELFCLASS64);
172         scanELF!64;
173     }
174 }