Skip to content

Commit

Permalink
Start mixed-language library overhaul
Browse files Browse the repository at this point in the history
  • Loading branch information
dargueta committed Sep 29, 2024
1 parent f10a683 commit 3115415
Show file tree
Hide file tree
Showing 9 changed files with 382 additions and 29 deletions.
3 changes: 0 additions & 3 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,5 @@ indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true

[*.lua]
indent_size = 2

[{Makefile,*.mk}]
indent_style = unset
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ tests/c/doctest.h
.vscode/

# Autogenerated files:
csrc/*_const.cpp
src/unicorn/*_const.lua
csrc/cpp_test
csrc/basic_control_functions.cpp
csrc/registers.cpp
Expand Down
27 changes: 19 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
# fail.
.SUFFIXES:

.DELETE_ON_ERROR:

################################################################################
# DEFAULTS
# These are only used when this Makefile is run manually. You should only be
Expand Down Expand Up @@ -59,7 +61,8 @@ ARCHITECTURE_HEADERS = $(wildcard $(UNICORN_INCDIR)/unicorn/*.h)
ARCHITECTURE_SLUGS = $(filter-out platform,$(basename $(notdir $(ARCHITECTURE_HEADERS))))

SOURCE_DIR = csrc
CONSTANT_FILES = $(foreach s,$(ARCHITECTURE_SLUGS),$(SOURCE_DIR)/$(s)_const.cpp)
LUA_SOURCE_DIR = src/unicorn
CONSTANT_FILES = $(foreach s,$(ARCHITECTURE_SLUGS),src/unicorn/$(s)_const.lua)

CPP_TEMPLATE_SOURCES = $(wildcard $(SOURCE_DIR)/*.template)
AUTOGENERATED_CPP_FILES = $(CPP_TEMPLATE_SOURCES:.template=.cpp)
Expand All @@ -68,7 +71,7 @@ HEADER_TEMPLATE_SOURCES = $(wildcard $(SOURCE_DIR)/unicornlua/*.template)
AUTOGENERATED_HPP_FILES = $(HEADER_TEMPLATE_SOURCES:.template=.hpp)

LIB_BUILD_TARGET = $(SOURCE_DIR)/unicorn.$(LIB_EXTENSION)
LIB_CPP_SOURCES = $(wildcard $(SOURCE_DIR)/*.cpp) $(CONSTANT_FILES) $(AUTOGENERATED_CPP_FILES)
LIB_CPP_SOURCES = $(wildcard $(SOURCE_DIR)/*.cpp) $(AUTOGENERATED_CPP_FILES)
LIB_OBJECT_FILES = $(LIB_CPP_SOURCES:.cpp=.$(OBJ_EXTENSION))

TEST_EXECUTABLE = $(SOURCE_DIR)/cpp_test
Expand Down Expand Up @@ -148,18 +151,14 @@ export DYLD_FALLBACK_LIBRARY_PATH := $(LIBRARY_COLON_PATHS):$(DYLD_FALLBACK_LIBR


# This must be the first rule, don't move it.
$(LIB_BUILD_TARGET): $(LIB_OBJECT_FILES)
$(LINK_CMD) $(LIBFLAG) -o $@ $^ $(REQUIRED_LIBS_FLAGS)
$(LIB_BUILD_TARGET): $(LIB_OBJECT_FILES) $(CONSTANT_FILES)
$(LINK_CMD) $(LIBFLAG) -o $@ $(filter-out %.lua,$^) $(REQUIRED_LIBS_FLAGS)


$(TEST_EXECUTABLE): $(TEST_CPP_OBJECT_FILES) $(LIB_OBJECT_FILES)
$(LINK_CMD) -o $@ $^ $(REQUIRED_LIBS_FLAGS) $(LINK_TO_LUA_FLAG) -lm


$(SOURCE_DIR)/%_const.cpp: $(UNICORN_INCDIR)/unicorn/%.h
$(LUA) tools/generate_constants.lua $< $@


tests/c/%.$(OBJ_EXTENSION): tests/c/%.cpp $(AUTOGENERATED_HPP_FILES) $(TEST_HEADERS) | $(DOCTEST_HEADER)
$(CXX_CMD) $(CXXFLAGS) -c -o $@ $<

Expand All @@ -176,6 +175,18 @@ tests/c/%.$(OBJ_EXTENSION): tests/c/%.cpp $(AUTOGENERATED_HPP_FILES) $(TEST_HEAD
$(LUA) tools/render_template.lua -o $@ $^


$(LUA_SOURCE_DIR)/%_const_gen.c: $(UNICORN_INCDIR)/unicorn/%.h tools/generate_constants.lua
$(LUA) tools/generate_constants.lua $< $@


$(LUA_SOURCE_DIR)/%_const_gen: $(LUA_SOURCE_DIR)/%_const_gen.c
$(CC) $(INCLUDE_PATH_FLAGS) -o $@ $<


$(LUA_SOURCE_DIR)/%_const.lua: $(LUA_SOURCE_DIR)/%_const_gen
$< > $@


$(DOCTEST_HEADER):
$(CURL) -sSo $@ https://raw.githubusercontent.com/doctest/doctest/$(DOCTEST_TAG)/doctest/doctest.h

Expand Down
58 changes: 58 additions & 0 deletions src/unicorn/context.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
--- @module context

local uc_c = require("unicorn_c_")

local ContextMethods = {}
local ContextMeta = {__index = ContextMethods}

function Context(engine, context_handle)
if context_handle == nil then
context_handle = uc_c.context_save(engine)
end

-- We want to hold a weak reference to the engine so that this context laying around
-- won't prevent it from being collected, but we do need to hold a strong reference to
-- the handle returned to us by Unicorn. Thus, we need to put the engine into a weak
-- table instead of directly in the Context object.
local instance = {
engine_ref_ = setmetatable({engine = engine}, {__mode = "v"}),
handle_ = context_handle,
}

return setmetatable(instance, ContextMeta)
end


function ContextMeta:__close()
if self.handle_ ~= nil then
self:free()
end
end


function ContextMeta:__gc()
if self.handle_ ~= nil then
self:free()
end
end


function ContextMethods:free()
if self.handle_ == nil then
error("Attempted to free the same context twice.")
end

-- The engine reference can be nil in two cases: 1) the engine was collected, or 2)
-- this is a double free. We need to check for a double free first, as that's a user
-- bug. This is more serious.
if self.engine_ref_.engine == nil then
error("BUG: Engine was garbage collected before a context.")
end

uc_c.context_free(self.engine_ref_.engine, self.handle_)
self.engine_ref_.engine = nil
self.handle_ = nil
end


return {Context = Context}
229 changes: 229 additions & 0 deletions src/unicorn/engine.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
--- @module engine

local uc_c = require("unicorn_c_")
local uc_context = require("unicorn.context")

local EngineMethods = {}
local EngineMeta = {__index = EngineMethods}


--- Create an object-oriented wrapper around an opened Unicorn engine.
---
--- @param handle A handle returned by the Unicorn C library (not the lua binding).
function Engine(handle)
local instance = {
engine_handle_ = handle,
-- Once a context object is unreachable, it can't be used to restore the engine to
-- the state the context describes. Since there's no point to holding onto a
-- context the user can no longer use, we use a weak table to store them to allow
-- them to be garbage collected once the user can't use them anymore.
--
-- We still need this table because if there are active contexts laying around
-- when the engine is closed, we need to release those as well.
contexts_ = setmetatable({}, {__mode = "k"}),
hooks_ = {},
}

return setmetatable(instance, EngineMeta)
end

function EngineMeta:__close()
-- Only close the engine if it hasn't been closed already. We want to allow double-
-- closing here because the user may want to explicitly close an engine on some
-- control paths, but let Lua automatically close it on others.
if self.engine_handle_ ~= nil then
self:close()
end
end

function EngineMeta:__gc()
if self.engine_handle_ ~= nil then
self:close()
end
end

--- Stop the emulator engine and free all resources.
---
--- This removes all hooks, frees contexts and memory, then closes the underlying Unicorn
--- engine. The object must not be used after this is called.
function EngineMethods:close()
if self.engine_handle_ == nil then
error("Attempted to close an engine twice.")
end

self:emu_stop()
uc_c.close(self.engine_handle_)

-- We need to delete the handle so that when the garbage collector runs, we don't try
-- closing an already deallocated engine.
self.engine_handle_ = nil
end

function EngineMethods:context_restore(context)
return uc_c.context_restore(self.engine_handle_, context.context_handle_)
end

function EngineMethods:context_save(context)
local context_handle = uc_c.context_save(self.engine_handle_, context)
return uc_context.Context(self.engine_handle_, context_handle)
end

function EngineMethods:emu_start(start_addr, end_addr, timeout, n_instructions)
return uc_c.emu_start(
self.engine_handle_,
start_addr,
end_addr,
timeout or 0,
n_instructions or 0
)
end

function EngineMethods:emu_stop()
for context in pairs(self.contexts_) do
context:free()
end
self.contexts_ = {}

for hook_handle in pairs(self.hooks_) do
hook_handle:close()
end
self.hooks_ = {}

uc_c.emu_stop(self.engine_handle_)
end

function EngineMethods:errno()
return uc_c.errno(self.engine_handle_)
end

function EngineMethods:hook_add()
error("Not implemented yet")
end

function EngineMethods:hook_del()
error("Not implemented yet")
end

function EngineMethods:mem_map()
error("Not implemented yet")
end

function EngineMethods:mem_protect()
error("Not implemented yet")
end

function EngineMethods:mem_read()
error("Not implemented yet")
end

function EngineMethods:mem_regions()
error("Not implemented yet")
end

function EngineMethods:mem_unmap()
error("Not implemented yet")
end

function EngineMethods:mem_write()
error("Not implemented yet")
end

function EngineMethods:query(query_flag)
return uc_c.query(self.engine_handle_, query_flag)
end

function EngineMethods:reg_read()
error("Not implemented yet")
end

function EngineMethods:reg_read_as()
error("Not implemented yet")
end

function EngineMethods:reg_read_batch()
error("Not implemented yet")
end

function EngineMethods:reg_read_batch_as()
error("Not implemented yet")
end

function EngineMethods:reg_write()
error("Not implemented yet")
end

function EngineMethods:reg_write_as()
error("Not implemented yet")
end

function EngineMethods:reg_write_batch()
error("Not implemented yet")
end


-- These functions are only available in Unicorn 2.x.
if uc_c.version()[1] > 1 then
function EngineMethods:ctl_exits_disable()
return uc_c.ctl_exits_disable(self.engine_handle_)
end

function EngineMethods:ctl_exits_enable()
return uc_c.ctl_exits_enable(self.engine_handle_)
end

function EngineMethods:ctl_flush_tlb()
return uc_c.ctl_flush_tlb(self.engine_handle_)
end

function EngineMethods:ctl_get_arch()
return uc_c.ctl_get_arch(self.engine_handle_)
end

function EngineMethods:ctl_get_cpu_model()
return uc_c.ctl_get_cpu_model(self.engine_handle_)
end

function EngineMethods:ctl_get_exits()
error("Not implemented yet")
end

function EngineMethods:ctl_get_exits_cnt()
return uc_c.ctl_get_exits_cnt(self.engine_handle_)
end

function EngineMethods:ctl_get_mode()
return uc_c.ctl_get_mode(self.engine_handle_)
end

function EngineMethods:ctl_get_page_size()
error("Not implemented yet")
end

function EngineMethods:ctl_get_timeout()
return uc_c.ctl_get_timeout(self.engine_handle_)
end

function EngineMethods:ctl_remove_cache()
error("Not implemented yet")
end

function EngineMethods:ctl_request_cache()
error("Not implemented yet")
end

function EngineMethods:ctl_set_cpu_model()
error("Not implemented yet")
end

function EngineMethods:ctl_set_exits()
error("Not implemented yet")
end

function EngineMethods:ctl_set_page_size()
error("Not implemented yet")
end
end


--- @export
return {Engine = Engine}
1 change: 1 addition & 0 deletions src/unicorn/hooks.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--- @module hooks
Loading

0 comments on commit 3115415

Please sign in to comment.