about summary refs log tree commit diff
path: root/helpers.cc
blob: 0aa2fe90f4dc994bac45e30cc89f9292b80b9e44 (plain) (blame)
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
// Helper functions
// Copyright (C) 2024-2025  Nguyễn Gia Phong
//
// This file is part of taosc.
//
// Taosc is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Taosc is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with taosc.  If not, see <https://www.gnu.org/licenses/>.

// Dyninst headers
#include <CFG.h>
#include <CodeObject.h>

using Address = Dyninst::Address;
using Block = Dyninst::ParseAPI::Block;
using CodeObject = Dyninst::ParseAPI::CodeObject;
using CodeRegion = Dyninst::ParseAPI::CodeRegion;
using CodeSource = Dyninst::ParseAPI::SymtabCodeSource;

#include <cassert>
#include <cstdlib>
#include <filesystem>
#include <iostream>
#include <set>
#include <string>
#include <utility>

char const*
parse_args (int argc, char const* const* argv)
{
  if (argc == 2)
    return argv[1];
  std::filesystem::path prog {argv[0]};
  std::cerr << "Usage: " << prog.filename ().string () << " EXECUTABLE\n";
  std::exit (1);
}

void
warn (Address address, std::string const& message)
{
  std::cerr << message << ' ' << std::hex << address << '\n';
}

[[noreturn]] void
die_for (Address address, std::string const& message)
{
  warn (address, message);
  std::exit (1);
}

static CodeRegion*
find_region (CodeSource& cs, Address address)
{
  if (!cs.isCode (address))
    die_for (address, "no instruction at");
  std::set <CodeRegion*> regions;
  if (cs.findRegions (address, regions) != 1)
    die_for (address, "not exactly one region found spanning");
  for (auto* const cr : regions)
    return cr;
  std::unreachable ();
}

/// Find next basic block's entry after given address, reparsing if necessary
static Block*
next_block (CodeObject& co, CodeRegion* cr, Address address)
{
  auto* blk = co.findBlockByEntry (cr, address);
  if (blk != nullptr)
    return blk;
  co.parse (address, true);
  blk = co.findBlockByEntry (cr, address);
  return (blk != nullptr) ? blk : co.findNextBlock (cr, address);
}

Block*
find_block (CodeSource& cs, CodeObject& co, Address address)
{
  auto* const cr = find_region (cs, address);
  std::set <Block*> blocks;
  if (co.findBlocks (cr, address, blocks) > 0)
    {
      if (blocks.size () > 1)
        die_for (address, "more than one block found spanning");
      for (auto* const blk : blocks)
        return blk;
    }
  auto* blk = next_block (co, cr, cr->low ());
  while (blk != nullptr && address > blk->last ())
    blk = next_block (co, cr, blk->end ());
  if (blk == nullptr)
    die_for (address, "no block found spanning");
  assert (address >= blk->start () && address < blk->end ());
  return blk;
}