1 --[[ 2 Copyright 2016 Marek Vavrusa <mvavrusa (a] cloudflare.com> 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 ]] 16 -- This is a tiny wrapper over libelf to extract load address 17 -- and offsets of dynamic symbols 18 19 local S = require('syscall') 20 local ffi = require('ffi') 21 ffi.cdef [[ 22 /* Type for a 16-bit quantity. */ 23 typedef uint16_t Elf32_Half; 24 typedef uint16_t Elf64_Half; 25 26 /* Types for signed and unsigned 32-bit quantities. */ 27 typedef uint32_t Elf32_Word; 28 typedef int32_t Elf32_Sword; 29 typedef uint32_t Elf64_Word; 30 typedef int32_t Elf64_Sword; 31 32 /* Types for signed and unsigned 64-bit quantities. */ 33 typedef uint64_t Elf32_Xword; 34 typedef int64_t Elf32_Sxword; 35 typedef uint64_t Elf64_Xword; 36 typedef int64_t Elf64_Sxword; 37 38 /* Type of addresses. */ 39 typedef uint32_t Elf32_Addr; 40 typedef uint64_t Elf64_Addr; 41 42 /* Type of file offsets. */ 43 typedef uint32_t Elf32_Off; 44 typedef uint64_t Elf64_Off; 45 46 /* Type for section indices, which are 16-bit quantities. */ 47 typedef uint16_t Elf32_Section; 48 typedef uint16_t Elf64_Section; 49 50 /* Constants */ 51 struct Elf_Cmd 52 { 53 static const int READ = 1; 54 static const int RDWR = 2; 55 static const int WRITE = 3; 56 static const int CLR = 4; 57 static const int SET = 5; 58 static const int FDDONE = 6; 59 static const int FDREAD = 7; 60 static const int READ_MMAP = 8; 61 static const int RDWR_MMAP = 9; 62 static const int WRITE_MMAP =10; 63 static const int READ_MMAP_PRIVATE =11; 64 static const int EMPTY =12; 65 static const int NUM =13; 66 }; 67 68 /* Descriptor for the ELF file. */ 69 typedef struct Elf Elf; 70 /* Descriptor for ELF file section. */ 71 typedef struct Elf_Scn Elf_Scn; 72 /* Container type for metatable */ 73 struct Elf_object { int fd; Elf *elf; }; 74 /* Program segment header. */ 75 typedef struct 76 { 77 Elf64_Word p_type; /* Segment type */ 78 Elf64_Word p_flags; /* Segment flags */ 79 Elf64_Off p_offset; /* Segment file offset */ 80 Elf64_Addr p_vaddr; /* Segment virtual address */ 81 Elf64_Addr p_paddr; /* Segment physical address */ 82 Elf64_Xword p_filesz; /* Segment size in file */ 83 Elf64_Xword p_memsz; /* Segment size in memory */ 84 Elf64_Xword p_align; /* Segment alignment */ 85 } Elf64_Phdr; 86 typedef Elf64_Phdr GElf_Phdr; 87 /* Section header. */ 88 typedef struct 89 { 90 Elf64_Word sh_name; /* Section name (string tbl index) */ 91 Elf64_Word sh_type; /* Section type */ 92 Elf64_Xword sh_flags; /* Section flags */ 93 Elf64_Addr sh_addr; /* Section virtual addr at execution */ 94 Elf64_Off sh_offset; /* Section file offset */ 95 Elf64_Xword sh_size; /* Section size in bytes */ 96 Elf64_Word sh_link; /* Link to another section */ 97 Elf64_Word sh_info; /* Additional section information */ 98 Elf64_Xword sh_addralign; /* Section alignment */ 99 Elf64_Xword sh_entsize; /* Entry size if section holds table */ 100 } Elf64_Shdr; 101 typedef Elf64_Shdr GElf_Shdr; 102 /* Descriptor for data to be converted to or from memory format. */ 103 typedef struct 104 { 105 void *d_buf; /* Pointer to the actual data. */ 106 int d_type; /* Type of this piece of data. */ 107 unsigned int d_version; /* ELF version. */ 108 size_t d_size; /* Size in bytes. */ 109 uint64_t d_off; /* Offset into section. */ 110 size_t d_align; /* Alignment in section. */ 111 } Elf_Data; 112 /* Symbol table entry. */ 113 typedef struct 114 { 115 Elf64_Word st_name; /* Symbol name (string tbl index) */ 116 unsigned char st_info; /* Symbol type and binding */ 117 unsigned char st_other; /* Symbol visibility */ 118 Elf64_Section st_shndx; /* Section index */ 119 Elf64_Addr st_value; /* Symbol value */ 120 Elf64_Xword st_size; /* Symbol size */ 121 } Elf64_Sym; 122 typedef Elf64_Sym GElf_Sym; 123 124 /* Coordinate ELF library and application versions. */ 125 unsigned int elf_version (unsigned int __version); 126 /* Return descriptor for ELF file to work according to CMD. */ 127 Elf *elf_begin (int __fildes, int __cmd, Elf *__ref); 128 /* Free resources allocated for ELF. */ 129 int elf_end (Elf *__elf); 130 /* Get the number of program headers in the ELF file. If the file uses 131 more headers than can be represented in the e_phnum field of the ELF 132 header the information from the sh_info field in the zeroth section 133 header is used. */ 134 int elf_getphdrnum (Elf *__elf, size_t *__dst); 135 /* Retrieve program header table entry. */ 136 GElf_Phdr *gelf_getphdr (Elf *__elf, int __ndx, GElf_Phdr *__dst); 137 /* Retrieve section header. */ 138 GElf_Shdr *gelf_getshdr (Elf_Scn *__scn, GElf_Shdr *__dst); 139 /* Retrieve symbol information from the symbol table at the given index. */ 140 GElf_Sym *gelf_getsym (Elf_Data *__data, int __ndx, GElf_Sym *__dst); 141 /* Get section with next section index. */ 142 Elf_Scn *elf_nextscn (Elf *__elf, Elf_Scn *__scn); 143 /* Get data from section while translating from file representation 144 to memory representation. */ 145 Elf_Data *elf_getdata (Elf_Scn *__scn, Elf_Data *__data); 146 /* Return pointer to string at OFFSET in section INDEX. */ 147 char *elf_strptr (Elf *__elf, size_t __index, size_t __offset); 148 ]] 149 150 local elf = ffi.load('elf') 151 local EV = { NONE=0, CURRENT=1, NUM=2 } 152 local PT = { NULL=0, LOAD=1, DYNAMIC=2, INTERP=3, NOTE=4, SHLIB=5, PHDR=6, TLS=7, NUM=8 } 153 local SHT = { NULL=0, PROGBITS=1, SYMTAB=2, STRTAB=3, RELA=4, HASH=5, DYNAMIC=6, NOTE=7, 154 NOBITS=8, REL=9, SHLIB=10, DYNSYM=11, INIT_ARRAY=14, FINI_ARRAY=15, PREINIT_ARRAY=16, 155 GROUP=17, SYMTAB_SHNDX=18, NUM=19 } 156 local ELF_C = ffi.new('struct Elf_Cmd') 157 local M = {} 158 159 -- Optional poor man's C++ demangler 160 local cpp_demangler = os.getenv('CPP_DEMANGLER') 161 if not cpp_demangler then 162 for prefix in string.gmatch(os.getenv('PATH'), '[^;:]+') do 163 if S.statfs(prefix..'/c++filt') then 164 cpp_demangler = prefix..'/c++filt' 165 break 166 end 167 end 168 end 169 local cpp_demangle = function (name) return name end 170 if cpp_demangler then 171 cpp_demangle = function (name) 172 local cmd = string.format('%s -p %s', cpp_demangler, name) 173 local fp = assert(io.popen(cmd, 'r')) 174 local output = fp:read('*all') 175 fp:close() 176 return output:match '^(.-)%s*$' 177 end 178 end 179 180 -- Metatable for ELF object 181 ffi.metatype('struct Elf_object', { 182 __gc = function (t) t:close() end, 183 __index = { 184 close = function (t) 185 if t.elf ~= nil then 186 elf.elf_end(t.elf) 187 S.close(t.fd) 188 t.elf = nil 189 end 190 end, 191 -- Load library load address 192 loadaddr = function(t) 193 local phnum = ffi.new('size_t [1]') 194 if elf.elf_getphdrnum(t.elf, phnum) == nil then 195 return nil, 'cannot get phdrnum' 196 end 197 local header = ffi.new('GElf_Phdr [1]') 198 for i = 0, tonumber(phnum[0])-1 do 199 if elf.gelf_getphdr(t.elf, i, header) ~= nil 200 and header[0].p_type == PT.LOAD then 201 return header[0].p_vaddr 202 end 203 end 204 end, 205 -- Resolve symbol address 206 resolve = function (t, k, pattern) 207 local section = elf.elf_nextscn(t.elf, nil) 208 while section ~= nil do 209 local header = ffi.new('GElf_Shdr [1]') 210 if elf.gelf_getshdr(section, header) ~= nil then 211 if header[0].sh_type == SHT.SYMTAB or header[0].sh_type == SHT.DYNSYM then 212 local data = elf.elf_getdata(section, nil) 213 while data ~= nil do 214 if data.d_size % header[0].sh_entsize > 0 then 215 return nil, 'bad section header entity size' 216 end 217 local symcount = tonumber(data.d_size / header[0].sh_entsize) 218 local sym = ffi.new('GElf_Sym [1]') 219 for i = 0, symcount - 1 do 220 if elf.gelf_getsym(data, i, sym) ~= nil then 221 local name = elf.elf_strptr(t.elf, header[0].sh_link, sym[0].st_name) 222 if name ~= nil then 223 -- Demangle C++ symbols if necessary 224 name = ffi.string(name) 225 if name:sub(1,2) == '_Z' then 226 name = cpp_demangle(name) 227 end 228 -- Match symbol name against pattern 229 if pattern and string.match(name, k) or k == name then 230 return sym[0] 231 end 232 end 233 end 234 end 235 data = elf.elf_getdata(section, data) 236 end 237 end 238 end 239 section = elf.elf_nextscn(t.elf, section) 240 end 241 end, 242 } 243 }) 244 245 -- Open an ELF object 246 function M.open(path) 247 if elf.elf_version(EV.CURRENT) == EV.NONE then 248 return nil, 'bad version' 249 end 250 local fd, err = S.open(path, 'rdonly') 251 if not fd then return nil, err end 252 local pt = ffi.new('Elf *') 253 pt = elf.elf_begin(fd:getfd(), ELF_C.READ, pt) 254 if not pt then 255 fd:close() 256 return nil, 'cannot open elf object' 257 end 258 return ffi.new('struct Elf_object', fd:nogc():getfd(), pt) 259 end 260 261 return M