]> Git Repo - binutils.git/blob - gprofng/src/Disasm.cc
Automatic date update in version.in
[binutils.git] / gprofng / src / Disasm.cc
1 /* Copyright (C) 2021 Free Software Foundation, Inc.
2    Contributed by Oracle.
3
4    This file is part of GNU Binutils.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3, or (at your option)
9    any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, 51 Franklin Street - Fifth Floor, Boston,
19    MA 02110-1301, USA.  */
20
21 #include "config.h"
22 #include <stdio.h>
23 #include <string.h>
24 #include <sys/param.h>
25
26 #include "disassemble.h"
27 #include "dis-asm.h"
28 #include "demangle.h"
29 #include "dbe_types.h"
30 #include "DbeSession.h"
31 #include "Elf.h"
32 #include "Disasm.h"
33 #include "Stabs.h"
34 #include "i18n.h"
35 #include "util.h"
36 #include "StringBuilder.h"
37
38 struct DisContext
39 {
40   bool is_Intel;
41   Stabs *stabs;
42   uint64_t pc;          // first_pc <= pc < last_pc
43   uint64_t first_pc;
44   uint64_t last_pc;
45   uint64_t f_offset;    // file offset for first_pc
46   int codeptr[4];       // longest instruction length may not be > 16
47   Data_window *elf;
48 };
49
50 static const int MAX_DISASM_STR     = 2048;
51 static const int MAX_INSTR_SIZE     = 8;
52
53 Disasm::Disasm (char *fname)
54 {
55   dwin = NULL;
56   dis_str = NULL;
57   need_swap_endian = false;
58   my_stabs = Stabs::NewStabs (fname, fname);
59   if (my_stabs == NULL)
60     return;
61   stabs = my_stabs;
62   platform = stabs->get_platform ();
63   disasm_open ();
64 }
65
66 Disasm::Disasm (Platform_t _platform, Stabs *_stabs)
67 {
68   dwin = NULL;
69   dis_str = NULL;
70   need_swap_endian = false;
71   stabs = _stabs;
72   platform = _platform;
73   my_stabs = NULL;
74   disasm_open ();
75 }
76
77 static int
78 fprintf_func (void *arg, const char *fmt, ...)
79 {
80   char buf[512];
81   va_list vp;
82   va_start (vp, fmt);
83   int cnt = vsnprintf (buf, sizeof (buf), fmt, vp);
84   va_end (vp);
85
86   Disasm *dis = (Disasm *) arg;
87   dis->dis_str->append (buf);
88   return cnt;
89 }
90
91 static int
92 fprintf_styled_func (void *arg, enum disassembler_style st ATTRIBUTE_UNUSED,
93                       const char *fmt, ...)
94 {
95   char buf[512];
96   va_list vp;
97   va_start (vp, fmt);
98   int cnt = vsnprintf (buf, sizeof (buf), fmt, vp);
99   va_end (vp);
100
101   Disasm *dis = (Disasm *) arg;
102   dis->dis_str->append (buf);
103   return cnt;
104 }
105
106 /* Get LENGTH bytes from info's buffer, at target address memaddr.
107    Transfer them to myaddr.  */
108 static int
109 read_memory_func (bfd_vma memaddr, bfd_byte *myaddr, unsigned int length,
110                   disassemble_info *info)
111 {
112   unsigned int opb = info->octets_per_byte;
113   size_t end_addr_offset = length / opb;
114   size_t max_addr_offset = info->buffer_length / opb;
115   size_t octets = (memaddr - info->buffer_vma) * opb;
116   if (memaddr < info->buffer_vma
117       || memaddr - info->buffer_vma > max_addr_offset
118       || memaddr - info->buffer_vma + end_addr_offset > max_addr_offset
119       || (info->stop_vma && (memaddr >= info->stop_vma
120                              || memaddr + end_addr_offset > info->stop_vma)))
121     return -1;
122   memcpy (myaddr, info->buffer + octets, length);
123   return 0;
124 }
125
126 static void
127 print_address_func (bfd_vma addr, disassemble_info *info)
128 {
129   (*info->fprintf_func) (info->stream, "0x%llx", (unsigned long long) addr);
130 }
131
132 static asymbol *
133 symbol_at_address_func (bfd_vma addr ATTRIBUTE_UNUSED,
134                         disassemble_info *info ATTRIBUTE_UNUSED)
135 {
136   return NULL;
137 }
138
139 static bfd_boolean
140 symbol_is_valid (asymbol * sym ATTRIBUTE_UNUSED,
141                  disassemble_info *info ATTRIBUTE_UNUSED)
142 {
143   return TRUE;
144 }
145
146 static void
147 memory_error_func (int status, bfd_vma addr, disassemble_info *info)
148 {
149   info->fprintf_func (info->stream, "Address 0x%llx is out of bounds.\n",
150                       (unsigned long long) addr);
151 }
152
153 void
154 Disasm::disasm_open ()
155 {
156   hex_visible = 1;
157   snprintf (addr_fmt, sizeof (addr_fmt), NTXT ("%s"), NTXT ("%8llx:  "));
158   if (dis_str == NULL)
159     dis_str = new StringBuilder;
160
161   switch (platform)
162     {
163     case Aarch64:
164     case Intel:
165     case Amd64:
166       need_swap_endian = (DbeSession::platform == Sparc);
167       break;
168     case Sparcv8plus:
169     case Sparcv9:
170     case Sparc:
171     default:
172       need_swap_endian = (DbeSession::platform != Sparc);
173       break;
174     }
175
176   memset (&dis_info, 0, sizeof (dis_info));
177   dis_info.flavour = bfd_target_unknown_flavour;
178   dis_info.endian = BFD_ENDIAN_UNKNOWN;
179   dis_info.endian_code = dis_info.endian;
180   dis_info.octets_per_byte = 1;
181   dis_info.disassembler_needs_relocs = FALSE;
182   dis_info.fprintf_func = fprintf_func;
183   dis_info.fprintf_styled_func = fprintf_styled_func;
184   dis_info.stream = this;
185   dis_info.disassembler_options = NULL;
186   dis_info.read_memory_func = read_memory_func;
187   dis_info.memory_error_func = memory_error_func;
188   dis_info.print_address_func = print_address_func;
189   dis_info.symbol_at_address_func = symbol_at_address_func;
190   dis_info.symbol_is_valid = symbol_is_valid;
191   dis_info.display_endian = BFD_ENDIAN_UNKNOWN;
192   dis_info.symtab = NULL;
193   dis_info.symtab_size = 0;
194   dis_info.buffer_vma = 0;
195   switch (platform)
196     {
197     case Aarch64:
198       dis_info.arch = bfd_arch_aarch64;
199       dis_info.mach = bfd_mach_aarch64;
200       break;
201     case Intel:
202     case Amd64:
203       dis_info.arch = bfd_arch_i386;
204       dis_info.mach = bfd_mach_x86_64;
205       break;
206     case Sparcv8plus:
207     case Sparcv9:
208     case Sparc:
209     default:
210       dis_info.arch = bfd_arch_unknown;
211       dis_info.endian = BFD_ENDIAN_UNKNOWN;
212       break;
213     }
214   dis_info.display_endian = dis_info.endian = BFD_ENDIAN_BIG;
215   dis_info.display_endian = dis_info.endian = BFD_ENDIAN_LITTLE;
216   dis_info.display_endian = dis_info.endian = BFD_ENDIAN_UNKNOWN;
217   disassemble_init_for_target (&dis_info);
218 }
219
220 Disasm::~Disasm ()
221 {
222   delete my_stabs;
223   delete dwin;
224   delete dis_str;
225 }
226
227 void
228 Disasm::set_img_name (char *img_fname)
229 {
230   if (stabs == NULL && img_fname && dwin == NULL)
231     {
232       dwin = new Data_window (img_fname);
233       if (dwin->not_opened ())
234         {
235           delete dwin;
236           dwin = NULL;
237           return;
238         }
239       dwin->need_swap_endian = need_swap_endian;
240     }
241 }
242
243 void
244 Disasm::remove_disasm_hndl (void *hndl)
245 {
246   DisContext *ctx = (DisContext *) hndl;
247   delete ctx;
248 }
249
250 #if 0
251 int
252 Disasm::get_instr_size (uint64_t vaddr, void *hndl)
253 {
254   DisContext *ctx = (DisContext *) hndl;
255   if (ctx == NULL || vaddr < ctx->first_pc || vaddr >= ctx->last_pc)
256     return -1;
257   ctx->pc = vaddr;
258   size_t sz = ctx->is_Intel ? sizeof (ctx->codeptr) : 4;
259   if (sz > ctx->last_pc - vaddr)
260     sz = (size_t) (ctx->last_pc - vaddr);
261   if (ctx->elf->get_data (ctx->f_offset + (vaddr - ctx->first_pc),
262                           sz, ctx->codeptr) == NULL)
263     return -1;
264
265   char buf[MAX_DISASM_STR];
266   *buf = 0;
267   uint64_t inst_vaddr = vaddr;
268 #if MEZ_NEED_TO_FIX
269   size_t instrs_cnt = 0;
270   disasm_err_code_t status = disasm (handle, &inst_vaddr, ctx->last_pc, 1,
271                                      ctx, buf, sizeof (buf), &instrs_cnt);
272   if (instrs_cnt != 1 || status != disasm_err_ok)
273     return -1;
274 #endif
275   return (int) (inst_vaddr - vaddr);
276 }
277 #endif
278
279 void
280 Disasm::set_addr_end (uint64_t end_address)
281 {
282   char buf[32];
283   int len = snprintf (buf, sizeof (buf), "%llx", (long long) end_address);
284   snprintf (addr_fmt, sizeof (addr_fmt), "%%%dllx:  ", len < 8 ? 8 : len);
285 }
286
287 char *
288 Disasm::get_disasm (uint64_t inst_address, uint64_t end_address,
289                   uint64_t start_address, uint64_t f_offset, int64_t &inst_size)
290 {
291   inst_size = 0;
292   if (inst_address >= end_address)
293     return NULL;
294   Data_window *dw = stabs ? stabs->openElf (false) : dwin;
295   if (dw == NULL)
296     return NULL;
297
298   unsigned char buffer[MAX_DISASM_STR];
299   dis_info.buffer = buffer;
300   dis_info.buffer_length = end_address - inst_address;
301   if (dis_info.buffer_length > sizeof (buffer))
302     dis_info.buffer_length = sizeof (buffer);
303   dw->get_data (f_offset + (inst_address - start_address),
304                 dis_info.buffer_length, dis_info.buffer);
305
306   dis_str->setLength (0);
307   bfd abfd;
308   disassembler_ftype disassemble = disassembler (dis_info.arch, dis_info.endian,
309                                                  dis_info.mach, &abfd);
310   if (disassemble == NULL)
311     {
312       printf ("ERROR: unsupported disassemble\n");
313       return NULL;
314     }
315   inst_size = disassemble (0, &dis_info);
316   if (inst_size <= 0)
317     {
318       inst_size = 0;
319       return NULL;
320     }
321   StringBuilder sb;
322   sb.appendf (addr_fmt, inst_address); // Write address
323
324   // Write hex bytes of instruction
325   if (hex_visible)
326     {
327       char bytes[64];
328       *bytes = '\0';
329       for (int i = 0; i < inst_size; i++)
330         {
331           unsigned int hex_value = buffer[i] & 0xff;
332           snprintf (bytes + 3 * i, sizeof (bytes) - 3 * i, "%02x ", hex_value);
333         }
334       const char *fmt = "%s   ";
335       if (platform == Intel)
336         fmt = "%-21s   "; // 21 = 3 * 7 - maximum instruction length on Intel
337       sb.appendf (fmt, bytes);
338     }
339   sb.append (dis_str);
340 #if MEZ_NEED_TO_FIX
341   // Write instruction
342   if (ctx.is_Intel)  // longest instruction length for Intel is 7
343     sb.appendf (NTXT ("%-7s %s"), parts_array[1], parts_array[2]);
344   else  // longest instruction length for SPARC is 11
345     sb.appendf (NTXT ("%-11s %s"), parts_array[1], parts_array[2]);
346   if (strcmp (parts_array[1], NTXT ("call")) == 0)
347     {
348       if (strncmp (parts_array[2], NTXT ("0x"), 2) == 0)
349         sb.append (GTXT ("\t! (Unable to determine target symbol)"));
350     }
351 #endif
352   return sb.toString ();
353 }
354
355 #if MEZ_NEED_TO_FIX
356 void *
357 Disasm::get_inst_ptr (disasm_handle_t, uint64_t vaddr, void *pass_through)
358 {
359   // Actually it fetches only one instruction at a time for sparc,
360   // and one byte at a time for intel.
361   DisContext *ctx = (DisContext*) pass_through;
362   size_t sz = ctx->is_Intel ? 1 : 4;
363   if (vaddr + sz > ctx->last_pc)
364     {
365       ctx->codeptr[0] = -1;
366       return ctx->codeptr;
367     }
368   if (ctx->elf->get_data (ctx->f_offset + (vaddr - ctx->first_pc), sz, ctx->codeptr) == NULL)
369     {
370       ctx->codeptr[0] = -1;
371       return ctx->codeptr;
372     }
373   if (ctx->elf->need_swap_endian && !ctx->is_Intel)
374     ctx->codeptr[0] = ctx->elf->decode (ctx->codeptr[0]);
375   return ctx->codeptr;
376 }
377
378 // get a symbol name for an address
379 disasm_err_code_t
380 Disasm::get_sym_name (disasm_handle_t,          // an open disassembler handle
381                       uint64_t target_address,  // the target virtual address
382                       uint64_t inst_address,  // virtual address of instruction
383                                               // being disassembled
384                       int use_relocation, // flag to use relocation information
385                       char *buffer,             // places the symbol here
386                       size_t buffer_size,       // limit on symbol length
387                       int *,                    // sys/elf_{SPARC.386}.h
388                       uint64_t *offset,       // from the symbol to the address
389                       void *pass_through)       // disassembler context
390 {
391   char buf[MAXPATHLEN];
392   if (!use_relocation)
393     return disasm_err_symbol;
394
395   DisContext *ctxp = (DisContext*) pass_through;
396   char *name = NULL;
397   if (ctxp->stabs)
398     {
399       uint64_t addr = ctxp->f_offset + (inst_address - ctxp->first_pc);
400       name = ctxp->stabs->sym_name (target_address, addr, use_relocation);
401     }
402   if (name == NULL)
403     return disasm_err_symbol;
404
405   char *s = NULL;
406   if (*name == '_')
407     s = cplus_demangle (name, DMGL_PARAMS);
408   if (s)
409     {
410       snprintf (buffer, buffer_size, NTXT ("%s"), s);
411       free (s);
412     }
413   else
414     snprintf (buffer, buffer_size, NTXT ("%s"), name);
415
416   *offset = 0;
417   return disasm_err_ok;
418 }
419 #endif
This page took 0.045734 seconds and 4 git commands to generate.