Files
yusufcanislek 7b9029a2ca fix: filter forwarded exports + validate --outline= empty value
P1: Export parsing now skips forwarded exports (RVA within the export
directory range points to an ASCII forwarder string, not code).

P2: --outline= (with equals but no value) now produces an explicit
error instead of silently consuming the next positional argument.
2026-03-26 12:35:26 +03:00

244 lines
6.3 KiB
C++

#include "Utils.h"
#include "llvm/IR/Value.h"
#include <chrono>
#include <iostream>
#include <sstream>
#include <llvm/Analysis/ValueLattice.h>
#include <llvm/Support/KnownBits.h>
#include <map>
#include <utility>
// #include <z3++.h>
/*
float intBitsToFloat(int bits) {
// Extract components from the int using IEEE 754 single precision format
int sign = (bits >> 31) & 0x1;
int exponent = (bits >> 23) & 0xFF;
int mantissa = bits & 0x7FFFFF;
// Build float value according to IEEE 754 formula
float value = 0;
if (exponent == 0) {
if (mantissa == 0) {
value = sign ? -0.0f : 0.0f;
} else {
// Denormalized number
value = (sign ? -1.0f : 1.0f) * (mantissa / (float)(1 << 23)) *
powf(2.0f, -126);
}
} else if (exponent == 0xFF) {
if (mantissa == 0) {
value = sign ? -INFINITY : INFINITY;
} else {
value = NAN;
}
} else {
// Normalized number
value = (sign ? -1.0f : 1.0f) * (1.0f + mantissa / (float)(1 << 23)) *
powf(2.0f, exponent - 127);
}
return value;
}
int floatBitsToInt(float f) {
if (f == 0.0f) {
return (std::signbit(f) ? 0x80000000 : 0);
}
if (std::isinf(f)) {
return (f < 0 ? 0xFF800000 : 0x7F800000);
}
if (std::isnan(f)) {
return 0x7FC00000; // One common NaN pattern
}
int sign = std::signbit(f) ? 1 : 0;
float abs_f = std::fabs(f);
int exponent = std::ilogbf(abs_f) + 127; // Get biased exponent
// Handle denormals
if (exponent <= 0) {
float mantissa_f = abs_f * powf(2.0f, 149); // 126 + 23
int mantissa = (int)mantissa_f;
return (sign << 31) | mantissa;
}
// Extract mantissa (23 bits of precision)
float mantissa_f = (abs_f / powf(2.0f, std::ilogbf(abs_f)) - 1.0f) *
(float)(1 << 23); int mantissa = (int)mantissa_f;
return (sign << 31) | (exponent << 23) | mantissa;
}
*/
namespace debugging {
int ic = 1;
int increaseInstCounter() { return ++ic; }
bool shouldDebug = false;
llvm::raw_ostream* debugStream = nullptr;
std::unique_ptr<llvm::raw_fd_ostream> fileStream;
void enableDebug(const std::string& filename = "") {
shouldDebug = true;
if (!filename.empty()) {
std::error_code EC;
fileStream = std::make_unique<llvm::raw_fd_ostream>(filename, EC);
if (EC) {
llvm::errs() << "Error opening debug file: " << EC.message() << "\n";
fileStream.reset();
debugStream = &llvm::errs();
shouldDebug = false;
return;
}
debugStream = fileStream.get();
} else {
debugStream = &llvm::outs();
}
llvm::outs() << "Debugging enabled\n";
}
// Other functions remain the same, but use debugStream instead of
// llvm::outs() For example:
void doIfDebug(const std::function<void(void)>& dothis) {
if (!shouldDebug)
return;
(dothis)();
}
} // namespace debugging
namespace argparser {
namespace {
bool isOptionToken(const std::string& arg) {
return !arg.empty() && arg.front() == '-';
}
} // namespace
void printHelp() {
std::cerr << "Options:\n"
<< " -d, --enable-debug Enable debugging mode\n"
<< " -h, --help Display this help message\n"
<< " --concretize-unsafe-reads Concretizes potentially unsafe "
"reads to writable sections\n"
<< " --outline <addrs> Comma-separated addresses to outline (not inline)\n"
<< " -- Stop option parsing\n";
}
ParseResult parseArguments(const std::vector<std::string>& args,
bool allowUnknownOptions) {
ParseResult result;
if (args.empty()) {
return result;
}
result.positionalArgs.reserve(args.size());
result.positionalArgs.push_back(args.front());
bool parsingOptions = true;
for (size_t index = 1; index < args.size(); ++index) {
const auto& arg = args[index];
if (parsingOptions && arg == "--") {
parsingOptions = false;
continue;
}
if (parsingOptions) {
if (arg == "-h" || arg == "--help") {
result.showHelp = true;
continue;
}
if (arg == "-d" || arg == "--enable-debug") {
result.enableDebug = true;
continue;
}
if (arg == "--concretize-unsafe-reads") {
result.concretizeUnsafeReads = true;
continue;
}
if (arg == "--outline" || arg.rfind("--outline=", 0) == 0) {
std::string value;
if (arg.rfind("--outline=", 0) == 0) {
value = arg.substr(10);
if (value.empty()) {
result.errors.push_back("--outline= requires a value after '='");
continue;
}
} else if (index + 1 < args.size()) {
value = args[++index];
} else {
result.errors.push_back("--outline requires an argument");
continue;
}
std::istringstream stream(value);
std::string token;
while (std::getline(stream, token, ',')) {
if (token.empty()) continue;
try {
result.outlineAddresses.push_back(std::stoull(token, nullptr, 0));
} catch (const std::exception&) {
result.errors.push_back("Invalid outline address: " + token);
}
}
continue;
}
if (isOptionToken(arg)) {
if (allowUnknownOptions) {
result.positionalArgs.push_back(arg);
} else {
result.errors.push_back("Unknown option: " + arg);
}
continue;
}
}
result.positionalArgs.push_back(arg);
}
return result;
}
} // namespace argparser
namespace timer {
using clock = std::chrono::high_resolution_clock;
using time_point = std::chrono::time_point<clock>;
using duration = std::chrono::duration<double, std::milli>;
time_point startTime;
bool running = false;
void startTimer() {
startTime = clock::now();
running = true;
}
double getTimer() {
if (running) {
return std::chrono::duration_cast<duration>(clock::now() - startTime)
.count();
}
return 0.0;
}
double stopTimer() {
if (running) {
running = false;
return std::chrono::duration_cast<duration>(clock::now() - startTime)
.count();
}
return 0.0;
}
void resetTimer() {
startTime = clock::now();
running = true;
}
} // namespace timer