1--[[ 2Copyright 2016 Marek Vavrusa <mvavrusa@cloudflare.com> 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15]] 16-- This is a tiny wrapper over libelf to extract load address 17-- and offsets of dynamic symbols 18 19local S = require('syscall') 20local ffi = require('ffi') 21ffi.cdef [[ 22/* Type for a 16-bit quantity. */ 23typedef uint16_t Elf32_Half; 24typedef uint16_t Elf64_Half; 25 26/* Types for signed and unsigned 32-bit quantities. */ 27typedef uint32_t Elf32_Word; 28typedef int32_t Elf32_Sword; 29typedef uint32_t Elf64_Word; 30typedef int32_t Elf64_Sword; 31 32/* Types for signed and unsigned 64-bit quantities. */ 33typedef uint64_t Elf32_Xword; 34typedef int64_t Elf32_Sxword; 35typedef uint64_t Elf64_Xword; 36typedef int64_t Elf64_Sxword; 37 38/* Type of addresses. */ 39typedef uint32_t Elf32_Addr; 40typedef uint64_t Elf64_Addr; 41 42/* Type of file offsets. */ 43typedef uint32_t Elf32_Off; 44typedef uint64_t Elf64_Off; 45 46/* Type for section indices, which are 16-bit quantities. */ 47typedef uint16_t Elf32_Section; 48typedef uint16_t Elf64_Section; 49 50/* Constants */ 51struct 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. */ 69typedef struct Elf Elf; 70/* Descriptor for ELF file section. */ 71typedef struct Elf_Scn Elf_Scn; 72/* Container type for metatable */ 73struct Elf_object { int fd; Elf *elf; }; 74/* Program segment header. */ 75typedef 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; 86typedef Elf64_Phdr GElf_Phdr; 87/* Section header. */ 88typedef 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; 101typedef Elf64_Shdr GElf_Shdr; 102/* Descriptor for data to be converted to or from memory format. */ 103typedef 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. */ 113typedef 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; 122typedef Elf64_Sym GElf_Sym; 123 124/* Coordinate ELF library and application versions. */ 125unsigned int elf_version (unsigned int __version); 126/* Return descriptor for ELF file to work according to CMD. */ 127Elf *elf_begin (int __fildes, int __cmd, Elf *__ref); 128/* Free resources allocated for ELF. */ 129int 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. */ 134int elf_getphdrnum (Elf *__elf, size_t *__dst); 135/* Retrieve program header table entry. */ 136GElf_Phdr *gelf_getphdr (Elf *__elf, int __ndx, GElf_Phdr *__dst); 137/* Retrieve section header. */ 138GElf_Shdr *gelf_getshdr (Elf_Scn *__scn, GElf_Shdr *__dst); 139/* Retrieve symbol information from the symbol table at the given index. */ 140GElf_Sym *gelf_getsym (Elf_Data *__data, int __ndx, GElf_Sym *__dst); 141/* Get section with next section index. */ 142Elf_Scn *elf_nextscn (Elf *__elf, Elf_Scn *__scn); 143/* Get data from section while translating from file representation 144 to memory representation. */ 145Elf_Data *elf_getdata (Elf_Scn *__scn, Elf_Data *__data); 146/* Return pointer to string at OFFSET in section INDEX. */ 147char *elf_strptr (Elf *__elf, size_t __index, size_t __offset); 148]] 149 150local elf = ffi.load('elf') 151local EV = { NONE=0, CURRENT=1, NUM=2 } 152local PT = { NULL=0, LOAD=1, DYNAMIC=2, INTERP=3, NOTE=4, SHLIB=5, PHDR=6, TLS=7, NUM=8 } 153local 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 } 156local ELF_C = ffi.new('struct Elf_Cmd') 157local M = {} 158 159-- Optional poor man's C++ demangler 160local cpp_demangler = os.getenv('CPP_DEMANGLER') 161if 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 168end 169local cpp_demangle = function (name) return name end 170if 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 178end 179 180-- Metatable for ELF object 181ffi.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 246function 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) 259end 260 261return M