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 = §ions[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 }