1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
|
// Copyright (c) 2013 Austin T. Clements. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
#include "internal.hh"
#include <cassert>
using namespace std;
DWARFPP_BEGIN_NAMESPACE
// The expected number of arguments for standard opcodes. This is
// used to check the opcode_lengths header field for compatibility.
static const int opcode_lengths[] = {
0,
// DW_LNS::copy
0, 1, 1, 1, 1,
// DW_LNS::negate_stmt
0, 0, 0, 1, 0,
// DW_LNS::set_epilogue_begin
0, 1
};
struct line_table::impl
{
shared_ptr<section> sec;
// Header information
section_offset program_offset;
ubyte minimum_instruction_length;
ubyte maximum_operations_per_instruction;
bool default_is_stmt;
sbyte line_base;
ubyte line_range;
ubyte opcode_base;
vector<ubyte> standard_opcode_lengths;
vector<string> include_directories;
vector<file> file_names;
// The offset in sec following the last read file name entry.
// File name entries can appear both in the line table header
// and in the line number program itself. Since we can
// iterate over the line number program repeatedly, this keeps
// track of how far we've gotten so we don't add the same
// entry twice.
section_offset last_file_name_end;
// If an iterator has traversed the entire program, then we
// know we've gathered all file names.
bool file_names_complete;
impl() : last_file_name_end(0), file_names_complete(false) {};
bool read_file_entry(cursor *cur, bool in_header);
};
line_table::line_table(const shared_ptr<section> &sec, section_offset offset,
unsigned cu_addr_size, const string &cu_comp_dir,
const string &cu_name)
: m(make_shared<impl>())
{
// XXX DWARF2 and 3 give a weird specification for DW_AT_comp_dir
string comp_dir, abs_path;
if (cu_comp_dir.empty() || cu_comp_dir.back() == '/')
comp_dir = cu_comp_dir;
else
comp_dir = cu_comp_dir + '/';
// Read the line table header (DWARF2 section 6.2.4, DWARF3
// section 6.2.4, DWARF4 section 6.2.3)
cursor cur(sec, offset);
m->sec = cur.subsection();
cur = cursor(m->sec);
cur.skip_initial_length();
m->sec->addr_size = cu_addr_size;
// Basic header information
uhalf version = cur.fixed<uhalf>();
if (version < 2 || version > 4)
throw format_error("unknown line number table version " +
std::to_string(version));
section_length header_length = cur.offset();
m->program_offset = cur.get_section_offset() + header_length;
m->minimum_instruction_length = cur.fixed<ubyte>();
m->maximum_operations_per_instruction = 1;
if (version == 4)
m->maximum_operations_per_instruction = cur.fixed<ubyte>();
if (m->maximum_operations_per_instruction == 0)
throw format_error("maximum_operations_per_instruction cannot"
" be 0 in line number table");
m->default_is_stmt = cur.fixed<ubyte>();
m->line_base = cur.fixed<sbyte>();
m->line_range = cur.fixed<ubyte>();
if (m->line_range == 0)
throw format_error("line_range cannot be 0 in line number table");
m->opcode_base = cur.fixed<ubyte>();
static_assert(sizeof(opcode_lengths) / sizeof(opcode_lengths[0]) == 13,
"opcode_lengths table has wrong length");
// Opcode length table
m->standard_opcode_lengths.resize(m->opcode_base);
m->standard_opcode_lengths[0] = 0;
for (unsigned i = 1; i < m->opcode_base; i++) {
ubyte length = cur.fixed<ubyte>();
if (length != opcode_lengths[i])
// The spec never says what to do if the
// opcode length of a standard opcode doesn't
// match the header. Do the safe thing.
throw format_error(
"expected " +
std::to_string(opcode_lengths[i]) +
" arguments for line number opcode " +
std::to_string(i) + ", got " +
std::to_string(length));
m->standard_opcode_lengths[i] = length;
}
// Include directories list
string incdir;
// Include directory 0 is implicitly the compilation unit
// current directory
m->include_directories.push_back(comp_dir);
while (true) {
cur.string(incdir);
if (incdir.empty())
break;
if (incdir.back() != '/')
incdir += '/';
if (incdir[0] == '/')
m->include_directories.push_back(move(incdir));
else
m->include_directories.push_back(comp_dir + incdir);
}
// File name list
string file_name;
// File name 0 is implicitly the compilation unit file name.
// cu_name can be relative to comp_dir or absolute.
if (!cu_name.empty() && cu_name[0] == '/')
m->file_names.emplace_back(cu_name);
else
m->file_names.emplace_back(comp_dir + cu_name);
while (m->read_file_entry(&cur, true));
}
line_table::iterator
line_table::begin() const
{
if (!valid())
return iterator(nullptr, 0);
return iterator(this, m->program_offset);
}
line_table::iterator
line_table::end() const
{
if (!valid())
return iterator(nullptr, 0);
return iterator(this, m->sec->size());
}
line_table::iterator
line_table::find_address(taddr addr) const
{
iterator prev = begin(), e = end();
if (prev == e)
return prev;
iterator it = prev;
for (++it; it != e; prev = it++) {
if (prev->address <= addr && it->address > addr &&
!prev->end_sequence)
return prev;
}
prev = e;
return prev;
}
const line_table::file *
line_table::get_file(unsigned index) const
{
if (index >= m->file_names.size()) {
// It could be declared in the line table program.
// This is unlikely, so we don't have to be
// super-efficient about this. Just force our way
// through the whole line table program.
if (!m->file_names_complete) {
for (auto &ent : *this)
(void)ent;
}
if (index >= m->file_names.size())
throw out_of_range
("file name index " + std::to_string(index) +
" exceeds file table size of " +
std::to_string(m->file_names.size()));
}
return &m->file_names[index];
}
bool
line_table::impl::read_file_entry(cursor *cur, bool in_header)
{
assert(cur->sec == sec);
string file_name;
cur->string(file_name);
if (in_header && file_name.empty())
return false;
uint64_t dir_index = cur->uleb128();
uint64_t mtime = cur->uleb128();
uint64_t length = cur->uleb128();
// Have we already processed this file entry?
if (cur->get_section_offset() <= last_file_name_end)
return true;
last_file_name_end = cur->get_section_offset();
if (file_name[0] == '/')
file_names.emplace_back(move(file_name), mtime, length);
else if (dir_index < include_directories.size())
file_names.emplace_back(
include_directories[dir_index] + file_name,
mtime, length);
else
throw format_error("file name directory index out of range: " +
std::to_string(dir_index));
return true;
}
line_table::file::file(string path, uint64_t mtime, uint64_t length)
: path(path), mtime(mtime), length(length)
{
}
void
line_table::entry::reset(bool is_stmt)
{
address = op_index = 0;
file = nullptr;
file_index = line = 1;
column = 0;
this->is_stmt = is_stmt;
basic_block = end_sequence = prologue_end = epilogue_begin = false;
isa = discriminator = 0;
}
string
line_table::entry::get_description() const
{
string res = file->path;
if (line) {
res.append(":").append(std::to_string(line));
if (column)
res.append(":").append(std::to_string(column));
}
return res;
}
line_table::iterator::iterator(const line_table *table, section_offset pos)
: table(table), pos(pos)
{
if (table) {
regs.reset(table->m->default_is_stmt);
++(*this);
}
}
line_table::iterator &
line_table::iterator::operator++()
{
cursor cur(table->m->sec, pos);
// Execute opcodes until we reach the end of the stream or an
// opcode emits a line table row
bool stepped = false, output = false;
while (!cur.end() && !output) {
output = step(&cur);
stepped = true;
}
if (stepped && !output)
throw format_error("unexpected end of line table");
if (stepped && cur.end()) {
// Record that all file names must be known now
table->m->file_names_complete = true;
}
if (output) {
// Resolve file name of entry
if (entry.file_index < table->m->file_names.size())
entry.file = &table->m->file_names[entry.file_index];
else
throw format_error("bad file index " +
std::to_string(entry.file_index) +
" in line table");
}
pos = cur.get_section_offset();
return *this;
}
bool
line_table::iterator::step(cursor *cur)
{
struct line_table::impl *m = table->m.get();
// Read the opcode (DWARF4 section 6.2.3)
ubyte opcode = cur->fixed<ubyte>();
if (opcode >= m->opcode_base) {
// Special opcode (DWARF4 section 6.2.5.1)
ubyte adjusted_opcode = opcode - m->opcode_base;
unsigned op_advance = adjusted_opcode / m->line_range;
signed line_inc = m->line_base + (signed)adjusted_opcode % m->line_range;
regs.line += line_inc;
regs.address += m->minimum_instruction_length *
((regs.op_index + op_advance)
/ m->maximum_operations_per_instruction);
regs.op_index = (regs.op_index + op_advance)
% m->maximum_operations_per_instruction;
entry = regs;
regs.basic_block = regs.prologue_end =
regs.epilogue_begin = false;
regs.discriminator = 0;
return true;
} else if (opcode != 0) {
// Standard opcode (DWARF4 sections 6.2.3 and 6.2.5.2)
//
// According to the standard, any opcode between the
// highest defined opcode for a given DWARF version
// and opcode_base should be treated as a
// vendor-specific opcode. However, the de facto
// standard seems to be to process these as standard
// opcodes even if they're from a later version of the
// standard than the line table header claims.
uint64_t uarg;
#pragma GCC diagnostic push
#pragma GCC diagnostic warning "-Wswitch-enum"
switch ((DW_LNS)opcode) {
case DW_LNS::copy:
entry = regs;
regs.basic_block = regs.prologue_end =
regs.epilogue_begin = false;
regs.discriminator = 0;
break;
case DW_LNS::advance_pc:
// Opcode advance (as for special opcodes)
uarg = cur->uleb128();
advance_pc:
regs.address += m->minimum_instruction_length *
((regs.op_index + uarg)
/ m->maximum_operations_per_instruction);
regs.op_index = (regs.op_index + uarg)
% m->maximum_operations_per_instruction;
break;
case DW_LNS::advance_line:
regs.line = (signed)regs.line + cur->sleb128();
break;
case DW_LNS::set_file:
regs.file_index = cur->uleb128();
break;
case DW_LNS::set_column:
regs.column = cur->uleb128();
break;
case DW_LNS::negate_stmt:
regs.is_stmt = !regs.is_stmt;
break;
case DW_LNS::set_basic_block:
regs.basic_block = true;
break;
case DW_LNS::const_add_pc:
uarg = (255 - m->opcode_base) / m->line_range;
goto advance_pc;
case DW_LNS::fixed_advance_pc:
regs.address += cur->fixed<uhalf>();
regs.op_index = 0;
break;
case DW_LNS::set_prologue_end:
regs.prologue_end = true;
break;
case DW_LNS::set_epilogue_begin:
regs.epilogue_begin = true;
break;
case DW_LNS::set_isa:
regs.isa = cur->uleb128();
break;
default:
// XXX Vendor extensions
throw format_error("unknown line number opcode " +
to_string((DW_LNS)opcode));
}
return ((DW_LNS)opcode == DW_LNS::copy);
} else { // opcode == 0
// Extended opcode (DWARF4 sections 6.2.3 and 6.2.5.3)
assert(opcode == 0);
uint64_t length = cur->uleb128();
section_offset end = cur->get_section_offset() + length;
opcode = cur->fixed<ubyte>();
switch ((DW_LNE)opcode) {
case DW_LNE::end_sequence:
regs.end_sequence = true;
entry = regs;
regs.reset(m->default_is_stmt);
break;
case DW_LNE::set_address:
regs.address = cur->address();
regs.op_index = 0;
break;
case DW_LNE::define_file:
m->read_file_entry(cur, false);
break;
case DW_LNE::set_discriminator:
// XXX Only DWARF4
regs.discriminator = cur->uleb128();
break;
case DW_LNE::lo_user...DW_LNE::hi_user:
// XXX Vendor extensions
throw runtime_error("vendor line number opcode " +
to_string((DW_LNE)opcode) +
" not implemented");
default:
// XXX Prior to DWARF4, any opcode number
// could be a vendor extension
throw format_error("unknown line number opcode " +
to_string((DW_LNE)opcode));
}
#pragma GCC diagnostic pop
if (cur->get_section_offset() > end)
throw format_error("extended line number opcode exceeded its size");
cur += end - cur->get_section_offset();
return ((DW_LNE)opcode == DW_LNE::end_sequence);
}
}
DWARFPP_END_NAMESPACE
|