• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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