Home | History | Annotate | Download | only in bpf
      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