about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Makefile5
-rw-r--r--measure-stack.cc134
2 files changed, 138 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index c6de20c..9de3e0d 100644
--- a/Makefile
+++ b/Makefile
@@ -8,7 +8,7 @@ PREFIX ?= /usr/local
 BIN_PREFIX ::= $(DESTDIR)$(PREFIX)/bin/taosc-
 DATA_DIR ::= $(DESTDIR)$(PREFIX)/share/taosc
 
-BIN ::= fix scout synth trace-call
+BIN ::= fix measure-stack scout synth trace-call
 DATA ::= collect collection jump patch
 
 all: $(BIN) $(DATA)
@@ -19,6 +19,9 @@ clean:
 fix: fix.m4
 	m4 -D DATA_DIR=$(DATA_DIR) $< > $@
 
+measure-stack: measure-stack.o helpers.o
+	$(CXX) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
+
 scout: scout.o helpers.o
 	$(CXX) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
 
diff --git a/measure-stack.cc b/measure-stack.cc
new file mode 100644
index 0000000..5151772
--- /dev/null
+++ b/measure-stack.cc
@@ -0,0 +1,134 @@
+// Function stack size counter
+// Copyright (C) 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/>.
+
+#include "helpers.hh"
+
+// Dyninst headers
+#include <Architecture.h>
+#include <CFG.h>
+#include <CodeObject.h>
+#include <InstructionDecoder.h>
+#include <Register.h>
+
+using Architecture = Dyninst::Architecture;
+using Address = Dyninst::Address;
+using Function = Dyninst::ParseAPI::Function;
+using CodeObject = Dyninst::ParseAPI::CodeObject;
+using CodeSource = Dyninst::ParseAPI::SymtabCodeSource;
+using Instruction = Dyninst::InstructionAPI::Instruction;
+using InstructionDecoder = Dyninst::InstructionAPI::InstructionDecoder;
+using MachRegister = Dyninst::MachRegister;
+using Operand = Dyninst::InstructionAPI::Operand;
+using RegisterAST = Dyninst::InstructionAPI::RegisterAST;
+
+#include <cassert>
+#include <cstdint>
+#include <iostream>
+#include <vector>
+#include <utility>
+
+static int64_t
+immediate (Operand const& operand)
+{
+  auto const& result = operand.getValue ().get ()->eval ();
+  assert (result.defined);
+  switch (result.type)
+    {
+    case Dyninst::InstructionAPI::s8:
+    case Dyninst::InstructionAPI::u8:
+      return result.val.s8val;
+    case Dyninst::InstructionAPI::s16:
+    case Dyninst::InstructionAPI::u16:
+      return result.val.s16val;
+    case Dyninst::InstructionAPI::s32:
+    case Dyninst::InstructionAPI::u32:
+      return result.val.s32val;
+    case Dyninst::InstructionAPI::s64:
+    case Dyninst::InstructionAPI::u64:
+      return result.val.s64val;
+    default:
+      std::unreachable ();
+    }
+}
+
+static int64_t
+stack_offset (Instruction const& instruction,
+              Architecture architecture)
+{
+  switch (instruction.getOperation ().getID ())
+    {
+    case e_push:
+      return Dyninst::getArchAddressWidth (architecture);
+    case e_pop:
+      return -Dyninst::getArchAddressWidth (architecture);
+    case e_sub:
+      return immediate (instruction.getOperand (1));
+    case e_add:
+      return -immediate (instruction.getOperand (1));
+    default:
+      std::unreachable ();
+    }
+}
+
+int
+main (int argc, char** argv)
+{
+  CodeSource cs {parse_args (argc, argv)};
+  auto const architecture = cs.getArch ();
+  RegisterAST::Ptr const stack_pointer
+    {new RegisterAST(MachRegister::getStackPointer (architecture))};
+  CodeObject co {&cs};
+  co.parse (); // parsed functions have same lifetime as co
+  while (!std::cin.eof ())
+    {
+      Address address;
+      std::cin >> std::hex >> address;
+      if (std::cin.fail ())
+        break;
+      auto* const block = find_block (cs, co, address);
+      if (block->containingFuncs () < 1)
+        die_for (address, "no function found containing instruction at");
+      std::vector <Function*> functions;
+      block->getFuncs (functions);
+      size_t stack_size = 0;
+      for (auto* const fun : functions)
+        {
+          auto const entry = fun->addr ();
+          auto const* buffer = (char*) cs.getPtrToInstruction (entry);
+          auto const length = fun->entry ()->end () - entry;
+          InstructionDecoder decoder {buffer, length, architecture};
+          size_t s = 0;
+          for (auto insn = decoder.decode ();
+               insn.isValid ();
+               insn = decoder.decode ())
+            if (insn.isWritten (stack_pointer))
+              s += stack_offset (insn, architecture);
+          if (s == 0)
+            continue;
+          if (stack_size == 0)
+            stack_size = s;
+          else if (s != stack_size)
+            die_for (address, "functions with different stack sizes spanning");
+        }
+      std::cout << stack_size << '\n';
+    }
+  if (std::cin.eof ())
+    return 0;
+  std::cerr << "invalid input\n";
+  return -1;
+}