Zig binding
* zig binding - sample added * zig build CI * split mingw (shared/static) CI/CD * unicorn log added * build C/C++ samples
This commit is contained in:
parent
0619deeafd
commit
4fb4b3e4b0
7
.github/workflows/build-uc2.yml
vendored
7
.github/workflows/build-uc2.yml
vendored
@ -35,7 +35,7 @@ jobs:
|
|||||||
shared: 'yes',
|
shared: 'yes',
|
||||||
mingw: MINGW64,
|
mingw: MINGW64,
|
||||||
mingw-arch: x86_64,
|
mingw-arch: x86_64,
|
||||||
artifact: 'windows_mingw64.7z',
|
artifact: 'windows_mingw64-shared.7z',
|
||||||
build_type: 'Debug',
|
build_type: 'Debug',
|
||||||
archiver: '7z a',
|
archiver: '7z a',
|
||||||
generators: 'Ninja'
|
generators: 'Ninja'
|
||||||
@ -49,7 +49,7 @@ jobs:
|
|||||||
shared: 'no',
|
shared: 'no',
|
||||||
mingw: MINGW64,
|
mingw: MINGW64,
|
||||||
mingw-arch: x86_64,
|
mingw-arch: x86_64,
|
||||||
artifact: 'windows_mingw64.7z',
|
artifact: 'windows_mingw64-static.7z',
|
||||||
build_type: 'Debug',
|
build_type: 'Debug',
|
||||||
archiver: '7z a',
|
archiver: '7z a',
|
||||||
generators: 'Ninja'
|
generators: 'Ninja'
|
||||||
@ -217,7 +217,7 @@ jobs:
|
|||||||
#export CC=i686-w64-mingw32-gcc
|
#export CC=i686-w64-mingw32-gcc
|
||||||
export AR=gcc-ar
|
export AR=gcc-ar
|
||||||
export RANLIB=gcc-ranlib
|
export RANLIB=gcc-ranlib
|
||||||
export CFLAGS="-m32"
|
export CFLAGS="-m32 -static"
|
||||||
export LDFLAGS="-m32"
|
export LDFLAGS="-m32"
|
||||||
export LDFLAGS_STATIC="-m32"
|
export LDFLAGS_STATIC="-m32"
|
||||||
export UNICORN_QEMU_FLAGS="--cpu=i386"
|
export UNICORN_QEMU_FLAGS="--cpu=i386"
|
||||||
@ -230,6 +230,7 @@ jobs:
|
|||||||
-DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} \
|
-DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} \
|
||||||
-G "${{ matrix.config.generators }}" \
|
-G "${{ matrix.config.generators }}" \
|
||||||
-DCMAKE_INSTALL_PREFIX:PATH=instdir \
|
-DCMAKE_INSTALL_PREFIX:PATH=instdir \
|
||||||
|
-DCMAKE_C_FLAGS:STRING="-static" \
|
||||||
-DBUILD_SHARED_LIBS=${{ matrix.config.shared }}
|
-DBUILD_SHARED_LIBS=${{ matrix.config.shared }}
|
||||||
cmake --build . --config ${{ matrix.config.build_type }}
|
cmake --build . --config ${{ matrix.config.build_type }}
|
||||||
cmake --install . --strip
|
cmake --install . --strip
|
||||||
|
81
.github/workflows/zigbuild.yml
vendored
Normal file
81
.github/workflows/zigbuild.yml
vendored
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
name: Zig Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- ".gitignore"
|
||||||
|
- "docs/**"
|
||||||
|
- "README"
|
||||||
|
- "CREDITS.TXT"
|
||||||
|
- "COPYING_GLIB"
|
||||||
|
- "COPYING.LGPL2"
|
||||||
|
- "AUTHORS.TXT"
|
||||||
|
- "CHANGELOG"
|
||||||
|
- "COPYING"
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
runs-on: [ubuntu-latest, macos-latest]
|
||||||
|
runs-on: ${{ matrix.runs-on }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: goto-bus-stop/setup-zig@v2
|
||||||
|
with:
|
||||||
|
version: 0.11.0
|
||||||
|
- uses: lukka/get-cmake@latest
|
||||||
|
with:
|
||||||
|
cmakeVersion: latest
|
||||||
|
ninjaVersion: latest
|
||||||
|
|
||||||
|
- name: CMake Build
|
||||||
|
run: zig build cmake
|
||||||
|
|
||||||
|
- name: Build Summary
|
||||||
|
run: zig build --summary all -freference-trace
|
||||||
|
|
||||||
|
# ===================================================================
|
||||||
|
# zig-mingw:
|
||||||
|
# runs-on: windows-latest
|
||||||
|
# strategy:
|
||||||
|
# fail-fast: false
|
||||||
|
# matrix:
|
||||||
|
# include: [{ msystem: CLANG64, arch: x86_64, prefix: /clang64 }, { msystem: CLANG32, arch: i686, prefix: /clang32 }, { msystem: CLANGARM64, arch: aarch64, prefix: /clangarm64 }]
|
||||||
|
# steps:
|
||||||
|
# - uses: actions/checkout@v3
|
||||||
|
# with:
|
||||||
|
# path: temp
|
||||||
|
# submodules: recursive
|
||||||
|
# fetch-depth: 0
|
||||||
|
# - uses: goto-bus-stop/setup-zig@v2
|
||||||
|
# with:
|
||||||
|
# version: master
|
||||||
|
# - uses: msys2/setup-msys2@v2
|
||||||
|
# with:
|
||||||
|
# msystem: ${{ matrix.msystem }}
|
||||||
|
# path-type: inherit
|
||||||
|
# location: D:\
|
||||||
|
# install: git mingw-w64-clang-${{ matrix.arch }}-cmake
|
||||||
|
# update: true
|
||||||
|
|
||||||
|
# - name: Move Checkout
|
||||||
|
# run: |
|
||||||
|
# Copy-Item -Path ".\temp" -Destination "C:\_" -Recurse
|
||||||
|
|
||||||
|
# - name: Build Summary - ${{ matrix.arch }}
|
||||||
|
# shell: msys2 {0}
|
||||||
|
# run: |
|
||||||
|
# cd /C/_
|
||||||
|
# zig build cmake
|
||||||
|
# if [${{ matrix.config.arch }} == 'i686' ]; then
|
||||||
|
# zig build --summary all -freference-trace -Dtarget=x86-windows
|
||||||
|
# else
|
||||||
|
# zig build --summary all -freference-trace -Dtarget=${{ matrix.arch }}-windows
|
||||||
|
# fi
|
||||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -89,3 +89,5 @@ rust_build
|
|||||||
[Oo]bj/
|
[Oo]bj/
|
||||||
packages/
|
packages/
|
||||||
cmocka/
|
cmocka/
|
||||||
|
zig-cache/
|
||||||
|
zig-out/
|
@ -17,6 +17,11 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.15")
|
|||||||
cmake_policy(SET CMP0092 NEW)
|
cmake_policy(SET CMP0092 NEW)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
option(ZIG_BUILD "Enable zig build" OFF)
|
||||||
|
if(ZIG_BUILD)
|
||||||
|
include(cmake/zig.cmake)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Workaround to fix wrong compiler on macos.
|
# Workaround to fix wrong compiler on macos.
|
||||||
if(APPLE AND NOT CMAKE_C_COMPILER)
|
if(APPLE AND NOT CMAKE_C_COMPILER)
|
||||||
set(CMAKE_C_COMPILER "/usr/bin/cc")
|
set(CMAKE_C_COMPILER "/usr/bin/cc")
|
||||||
@ -47,7 +52,7 @@ set(UNICORN_VERSION_MAJOR 2)
|
|||||||
set(UNICORN_VERSION_MINOR 0)
|
set(UNICORN_VERSION_MINOR 0)
|
||||||
set(UNICORN_VERSION_PATCH 2)
|
set(UNICORN_VERSION_PATCH 2)
|
||||||
|
|
||||||
include(bundle_static.cmake)
|
include(cmake/bundle_static.cmake)
|
||||||
|
|
||||||
# Even though we generate shared lib and static archive at the same time, we still support
|
# Even though we generate shared lib and static archive at the same time, we still support
|
||||||
# using unicorn as a subdirectory so we have to respect BUILD_SHARED_LIBS.
|
# using unicorn as a subdirectory so we have to respect BUILD_SHARED_LIBS.
|
||||||
|
@ -73,7 +73,7 @@ def copy_sources():
|
|||||||
|
|
||||||
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../*.[ch]")))
|
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../*.[ch]")))
|
||||||
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../*.mk")))
|
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../*.mk")))
|
||||||
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../*.cmake")))
|
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../cmake/*.cmake")))
|
||||||
|
|
||||||
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../LICENSE*")))
|
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../LICENSE*")))
|
||||||
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../README.md")))
|
src.extend(glob.glob(os.path.join(ROOT_DIR, "../../README.md")))
|
||||||
|
218
bindings/zig/sample/sample_riscv_zig.zig
Normal file
218
bindings/zig/sample/sample_riscv_zig.zig
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
//! Based on: ../../../samples/sample_riscv.c
|
||||||
|
|
||||||
|
const unicorn = @import("unicorn");
|
||||||
|
const unicornC = unicorn.c;
|
||||||
|
const log = unicorn.log;
|
||||||
|
|
||||||
|
const RISCV_CODE = "\x13\x05\x10\x00\x93\x85\x05\x02";
|
||||||
|
const ADDRESS = 0x10000;
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
try test_recover_from_illegal();
|
||||||
|
log.info("------------------", .{});
|
||||||
|
try test_riscv2();
|
||||||
|
log.info("------------------", .{});
|
||||||
|
try test_riscv_func_return();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hook_block(uc: ?*unicornC.uc_engine, address: u64, size: u32, user_data: ?*anyopaque) callconv(.C) void {
|
||||||
|
_ = user_data;
|
||||||
|
_ = uc;
|
||||||
|
log.info(">>> Tracing basic block at 0x{}, block size = 0x{}", .{ address, size });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hook_code(uc: ?*unicornC.uc_engine, address: u64, size: u32, user_data: ?*anyopaque) callconv(.C) void {
|
||||||
|
_ = user_data;
|
||||||
|
_ = uc;
|
||||||
|
log.info(">>> Tracing instruction at 0x{}, instruction size = 0x{}", .{ address, size });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hook_code3(uc: ?*unicornC.uc_engine, address: u64, size: u32, user_data: ?*anyopaque) callconv(.C) void {
|
||||||
|
_ = user_data;
|
||||||
|
log.info(">>> Tracing instruction at 0x{}, instruction size = 0x{}", .{ address, size });
|
||||||
|
if (address == ADDRESS) {
|
||||||
|
log.info("stop emulation");
|
||||||
|
unicorn.uc_emu_stop(uc) catch |err| log.err("Error: {}", .{err});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn hook_memalloc(uc: ?*unicornC.uc_engine, @"type": unicornC.uc_mem_type, address: u64, size: u32, user_data: ?*anyopaque) callconv(.C) bool {
|
||||||
|
_ = user_data;
|
||||||
|
_ = @"type";
|
||||||
|
var algined_address = address & 0xFFFFFFFFFFFFF000;
|
||||||
|
var aligned_size = (@as(u32, @intCast(size / 0x1000)) + 1) * 0x1000;
|
||||||
|
|
||||||
|
log.info(">>> Allocating block at 0x{} (0x{}), block size = 0x{} (0x{})", .{ address, algined_address, size, aligned_size });
|
||||||
|
|
||||||
|
unicorn.uc_mem_map(uc, algined_address, aligned_size, unicornC.UC_PROT_ALL) catch |err| log.err("Error: {}", .{err});
|
||||||
|
|
||||||
|
// this recovers from missing memory, so we return true
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_recover_from_illegal() !void {
|
||||||
|
var uc: ?*unicornC.uc_engine = null;
|
||||||
|
var trace1: unicornC.uc_hook = undefined;
|
||||||
|
var trace2: unicornC.uc_hook = undefined;
|
||||||
|
var mem_alloc: unicornC.uc_hook = undefined;
|
||||||
|
var a0: u64 = 0x1234;
|
||||||
|
var a1: u64 = 0x7890;
|
||||||
|
|
||||||
|
log.info("Emulate RISCV code: recover_from_illegal", .{});
|
||||||
|
|
||||||
|
// Initialize emulator in RISCV64 mode
|
||||||
|
unicorn.uc_open(unicornC.UC_ARCH_RISCV, unicornC.UC_MODE_RISCV64, &uc) catch |err| {
|
||||||
|
log.err("Failed on uc_open() with error returned: {}", .{err});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
try unicorn.uc_reg_write(uc, unicornC.UC_RISCV_REG_A0, &a0);
|
||||||
|
try unicorn.uc_reg_write(uc, unicornC.UC_RISCV_REG_A1, &a1);
|
||||||
|
|
||||||
|
// map 2MB memory for this emulation
|
||||||
|
try unicorn.uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, unicornC.UC_PROT_ALL);
|
||||||
|
|
||||||
|
// auto-allocate memory on access
|
||||||
|
try unicorn.uc_hook_add(uc, &mem_alloc, unicornC.UC_HOOK_MEM_UNMAPPED, @as(?*anyopaque, @ptrCast(@constCast(&hook_memalloc))), null, 1, 0);
|
||||||
|
|
||||||
|
// tracing all basic blocks with customized callback
|
||||||
|
try unicorn.uc_hook_add(uc, &trace1, unicornC.UC_HOOK_BLOCK, @as(?*anyopaque, @ptrCast(@constCast(&hook_block))), null, 1, 0);
|
||||||
|
|
||||||
|
// tracing all instruction
|
||||||
|
try unicorn.uc_hook_add(uc, &trace2, unicornC.UC_HOOK_CODE, @as(?*anyopaque, @ptrCast(@constCast(&hook_code))), null, 1, 0);
|
||||||
|
|
||||||
|
// write machine code to be emulated to memory
|
||||||
|
try unicorn.uc_mem_write(uc, ADDRESS, RISCV_CODE, RISCV_CODE.len - 1);
|
||||||
|
|
||||||
|
// emulate 1 instruction, wrong address, illegal code
|
||||||
|
unicorn.uc_emu_start(uc, 0x1000, @as(u64, @bitCast(@as(i64, -1))), 0, 1) catch |err|
|
||||||
|
log.err("Expected Illegal Instruction error, got: {} ({s})", .{ err, unicorn.uc_strerror(err) });
|
||||||
|
|
||||||
|
// emulate 1 instruction, correct address, valid code
|
||||||
|
unicorn.uc_emu_start(uc, ADDRESS, @as(u64, @bitCast(@as(i64, -1))), 0, 1) catch |err|
|
||||||
|
log.err("Failed on uc_emu_start() with error returned: {}", .{err});
|
||||||
|
|
||||||
|
// now print out some registers
|
||||||
|
log.info(">>> Emulation done. Below is the CPU context", .{});
|
||||||
|
|
||||||
|
try unicorn.uc_reg_read(uc, unicornC.UC_RISCV_REG_A0, @as(?*anyopaque, @ptrCast(@constCast(&a0))));
|
||||||
|
try unicorn.uc_reg_read(uc, unicornC.UC_RISCV_REG_A1, @as(?*anyopaque, @ptrCast(@constCast(&a1))));
|
||||||
|
|
||||||
|
log.info(">>> A0 = 0x{}", .{a0});
|
||||||
|
log.info(">>> A1 = 0x{}", .{a1});
|
||||||
|
|
||||||
|
try unicorn.uc_close(uc);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_riscv_func_return() !void {
|
||||||
|
var uc: ?*unicornC.uc_engine = null;
|
||||||
|
var trace1: unicornC.uc_hook = undefined;
|
||||||
|
var trace2: unicornC.uc_hook = undefined;
|
||||||
|
var pc: u64 = 0;
|
||||||
|
var ra: u64 = 0;
|
||||||
|
|
||||||
|
const CODE = "\x67\x80\x00\x00\x82\x80\x01\x00\x01\x00";
|
||||||
|
|
||||||
|
log.info("Emulate RISCV code: return from func", .{});
|
||||||
|
|
||||||
|
// Initialize emulator in RISCV64 mode
|
||||||
|
unicorn.uc_open(unicornC.UC_ARCH_RISCV, unicornC.UC_MODE_RISCV64, &uc) catch |err| {
|
||||||
|
log.err("Failed on uc_open() with error returned: {} ({s})", .{ err, unicorn.uc_strerror(err) });
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// map 2MB memory for this emulation
|
||||||
|
try unicorn.uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, unicornC.UC_PROT_ALL);
|
||||||
|
|
||||||
|
// write machine code to be emulated to memory
|
||||||
|
try unicorn.uc_mem_write(uc, ADDRESS, CODE, CODE.len - 1);
|
||||||
|
|
||||||
|
// tracing all basic blocks with customized callback
|
||||||
|
try unicorn.uc_hook_add(uc, &trace1, unicornC.UC_HOOK_BLOCK, @as(?*anyopaque, @ptrCast(@constCast(&hook_block))), null, 1, 0);
|
||||||
|
|
||||||
|
// tracing all instruction
|
||||||
|
try unicorn.uc_hook_add(uc, &trace2, unicornC.UC_HOOK_CODE, @as(?*anyopaque, @ptrCast(@constCast(&hook_code))), null, 1, 0);
|
||||||
|
|
||||||
|
ra = 0x10006;
|
||||||
|
try unicorn.uc_reg_write(uc, unicornC.UC_RISCV_REG_RA, @as(?*anyopaque, @ptrCast(@constCast(&ra))));
|
||||||
|
|
||||||
|
log.info("========", .{});
|
||||||
|
// execute c.ret instruction
|
||||||
|
unicorn.uc_emu_start(uc, 0x10004, @as(u64, @bitCast(@as(i64, -1))), 0, 1) catch |err| {
|
||||||
|
log.err("Failed on uc_emu_start() with error returned: {}", .{err});
|
||||||
|
};
|
||||||
|
|
||||||
|
try unicorn.uc_reg_read(uc, unicornC.UC_RISCV_REG_PC, @as(?*anyopaque, @ptrCast(@constCast(&pc))));
|
||||||
|
if (pc != ra) {
|
||||||
|
log.info("Error after execution: PC is: 0x{}, expected was 0x{}", .{ pc, ra });
|
||||||
|
if (pc == 0x10004) {
|
||||||
|
log.info(" PC did not change during execution", .{});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.info("Good, PC == RA", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
// now print out some registers
|
||||||
|
log.info(">>> Emulation done.", .{});
|
||||||
|
|
||||||
|
try unicorn.uc_close(uc);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_riscv2() !void {
|
||||||
|
var uc: ?*unicornC.uc_engine = null;
|
||||||
|
var trace1: unicornC.uc_hook = undefined;
|
||||||
|
var trace2: unicornC.uc_hook = undefined;
|
||||||
|
|
||||||
|
var a0: u32 = 0x1234;
|
||||||
|
var a1: u32 = 0x7890;
|
||||||
|
|
||||||
|
log.info("Emulate RISCV code: split emulation", .{});
|
||||||
|
|
||||||
|
// Initialize emulator in RISCV64 mode
|
||||||
|
unicorn.uc_open(unicornC.UC_ARCH_RISCV, unicornC.UC_MODE_RISCV32, &uc) catch |err| {
|
||||||
|
log.err("Failed on unicornC.uc_open() with error returned: {} ({s})", .{ err, unicorn.uc_strerror(err) });
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// map 2MB memory for this emulation
|
||||||
|
try unicorn.uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, unicornC.UC_PROT_ALL);
|
||||||
|
|
||||||
|
// write machine code to be emulated to memory
|
||||||
|
try unicorn.uc_mem_write(uc, ADDRESS, RISCV_CODE, RISCV_CODE.len - 1);
|
||||||
|
|
||||||
|
// initialize machine registers
|
||||||
|
try unicorn.uc_reg_write(uc, unicornC.UC_RISCV_REG_A0, @as(?*anyopaque, @ptrCast(@constCast(&a0))));
|
||||||
|
try unicorn.uc_reg_write(uc, unicornC.UC_RISCV_REG_A1, @as(?*anyopaque, @ptrCast(@constCast(&a1))));
|
||||||
|
|
||||||
|
// tracing all basic blocks with customized callback
|
||||||
|
try unicorn.uc_hook_add(uc, &trace1, unicornC.UC_HOOK_BLOCK, @as(?*anyopaque, @ptrCast(@constCast(&hook_block))), null, 1, 0);
|
||||||
|
|
||||||
|
// tracing all instruction
|
||||||
|
try unicorn.uc_hook_add(uc, &trace2, unicornC.UC_HOOK_CODE, @as(?*anyopaque, @ptrCast(@constCast(&hook_block))), null, 1, 0);
|
||||||
|
|
||||||
|
// emulate 1 instruction
|
||||||
|
unicorn.uc_emu_start(uc, ADDRESS, ADDRESS + 4, 0, 0) catch |err| {
|
||||||
|
log.err("Failed on unicornC.uc_emu_start() with error returned: {}", .{err});
|
||||||
|
};
|
||||||
|
|
||||||
|
try unicorn.uc_reg_read(uc, unicornC.UC_RISCV_REG_A0, @as(?*anyopaque, @ptrCast(@constCast(&a0))));
|
||||||
|
try unicorn.uc_reg_read(uc, unicornC.UC_RISCV_REG_A1, @as(?*anyopaque, @ptrCast(@constCast(&a1))));
|
||||||
|
|
||||||
|
log.info(">>> A0 = 0x{}", .{a0});
|
||||||
|
log.info(">>> A1 = 0x{}", .{a1});
|
||||||
|
|
||||||
|
// emulate one more instruction
|
||||||
|
unicorn.uc_emu_start(uc, ADDRESS + 4, ADDRESS + 8, 0, 0) catch |err| {
|
||||||
|
log.err("Failed on unicornC.uc_emu_start() with error returned: {}", .{err});
|
||||||
|
};
|
||||||
|
|
||||||
|
// now print out some registers
|
||||||
|
log.info(">>> Emulation done. Below is the CPU context", .{});
|
||||||
|
|
||||||
|
try unicorn.uc_reg_read(uc, unicornC.UC_RISCV_REG_A0, @as(?*anyopaque, @ptrCast(@constCast(&a0))));
|
||||||
|
try unicorn.uc_reg_read(uc, unicornC.UC_RISCV_REG_A1, @as(?*anyopaque, @ptrCast(@constCast(&a1))));
|
||||||
|
|
||||||
|
log.info(">>> A0 = 0x{}", .{a0});
|
||||||
|
log.info(">>> A1 = 0x{}", .{a1});
|
||||||
|
|
||||||
|
try unicorn.uc_close(uc);
|
||||||
|
}
|
3
bindings/zig/tools/zigcc.cmd
Normal file
3
bindings/zig/tools/zigcc.cmd
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
zig cc -fno-sanitize=all %*
|
3
bindings/zig/tools/zigcc.sh
Executable file
3
bindings/zig/tools/zigcc.sh
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#! /usr/bin/env bash
|
||||||
|
|
||||||
|
`which zig` cc -fno-sanitize=all $@
|
@ -1,3 +1,5 @@
|
|||||||
|
/// Public include's
|
||||||
|
|
||||||
// Architectures
|
// Architectures
|
||||||
pub const arm = @import("arm_const.zig");
|
pub const arm = @import("arm_const.zig");
|
||||||
pub const arm64 = @import("arm64_const.zig");
|
pub const arm64 = @import("arm64_const.zig");
|
||||||
@ -9,5 +11,192 @@ pub const tricore = @import("tricore_const.zig");
|
|||||||
pub const sparc = @import("sparc_const.zig");
|
pub const sparc = @import("sparc_const.zig");
|
||||||
pub const s390x = @import("s390x_const.zig");
|
pub const s390x = @import("s390x_const.zig");
|
||||||
pub const x86 = @import("x86_const.zig");
|
pub const x86 = @import("x86_const.zig");
|
||||||
// Unicorn
|
|
||||||
pub const unicorn = @import("unicorn_const.zig");
|
// Unicorn consts
|
||||||
|
pub usingnamespace @import("unicorn_const.zig");
|
||||||
|
// C include
|
||||||
|
pub const c = @cImport(@cInclude("unicorn/unicorn.h"));
|
||||||
|
|
||||||
|
pub fn uc_version(major: [*c]c_uint, minor: [*c]c_uint) c_uint {
|
||||||
|
return c.uc_version(major, minor);
|
||||||
|
}
|
||||||
|
pub fn uc_arch_supported(arch: c.uc_arch) bool {
|
||||||
|
return c.uc_arch_supported(arch);
|
||||||
|
}
|
||||||
|
pub fn uc_open(arch: c.uc_arch, mode: c.uc_mode, uc: [*c]?*c.uc_engine) !void {
|
||||||
|
try getErrors(c.uc_open(arch, mode, uc));
|
||||||
|
}
|
||||||
|
pub fn uc_close(uc: ?*c.uc_engine) !void {
|
||||||
|
try getErrors(c.uc_close(uc));
|
||||||
|
}
|
||||||
|
pub fn uc_query(uc: ?*c.uc_engine, @"type": c.uc_query_type, result: [*c]usize) !void {
|
||||||
|
try getErrors(c.uc_query(uc, @"type", result));
|
||||||
|
}
|
||||||
|
pub fn uc_ctl(uc: ?*c.uc_engine, control: c.uc_control_type) !void {
|
||||||
|
try getErrors(c.uc_ctl(uc, control));
|
||||||
|
}
|
||||||
|
pub fn uc_errno(uc: ?*c.uc_engine) !void {
|
||||||
|
try getErrors(c.uc_errno(uc));
|
||||||
|
}
|
||||||
|
pub fn uc_strerror(code: Error) [*:0]const u8 {
|
||||||
|
return switch (code) {
|
||||||
|
error.ucErrNoMemory => c.uc_strerror(c.UC_ERR_NOMEM),
|
||||||
|
error.ucErrArch => c.uc_strerror(c.UC_ERR_ARCH),
|
||||||
|
error.ucErrHandle => c.uc_strerror(c.UC_ERR_HANDLE),
|
||||||
|
error.ucErrMode => c.uc_strerror(c.UC_ERR_MODE),
|
||||||
|
error.ucErrVersion => c.uc_strerror(c.UC_ERR_VERSION),
|
||||||
|
error.ucErrReadUnmapped => c.uc_strerror(c.UC_ERR_READ_UNMAPPED),
|
||||||
|
error.ucErrWriteUnmapped => c.uc_strerror(c.UC_ERR_WRITE_UNMAPPED),
|
||||||
|
error.ucErrFetchUnmapped => c.uc_strerror(c.UC_ERR_FETCH_UNMAPPED),
|
||||||
|
error.ucErrHook => c.uc_strerror(c.UC_ERR_HOOK),
|
||||||
|
error.ucErrInvalidInstruction => c.uc_strerror(c.UC_ERR_INSN_INVALID),
|
||||||
|
error.ucErrMap => c.uc_strerror(c.UC_ERR_MAP),
|
||||||
|
error.ucErrWriteProtected => c.uc_strerror(c.UC_ERR_WRITE_PROT),
|
||||||
|
error.ucErrReadProtected => c.uc_strerror(c.UC_ERR_READ_PROT),
|
||||||
|
error.ucErrFetchProtected => c.uc_strerror(c.UC_ERR_FETCH_PROT),
|
||||||
|
error.ucErrInvalidArgument => c.uc_strerror(c.UC_ERR_ARG),
|
||||||
|
error.ucErrReadUnaligned => c.uc_strerror(c.UC_ERR_READ_UNALIGNED),
|
||||||
|
error.ucErrWriteUnaligned => c.uc_strerror(c.UC_ERR_WRITE_UNALIGNED),
|
||||||
|
error.ucErrFetchUnaligned => c.uc_strerror(c.UC_ERR_FETCH_UNALIGNED),
|
||||||
|
error.ucErrHookAlreadyExists => c.uc_strerror(c.UC_ERR_HOOK_EXIST),
|
||||||
|
error.ucErrResource => c.uc_strerror(c.UC_ERR_RESOURCE),
|
||||||
|
error.ucErrException => c.uc_strerror(c.UC_ERR_EXCEPTION),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub fn uc_reg_write(uc: ?*c.uc_engine, regid: c_int, value: ?*const anyopaque) !void {
|
||||||
|
try getErrors(c.uc_reg_write(uc, regid, value));
|
||||||
|
}
|
||||||
|
pub fn uc_reg_read(uc: ?*c.uc_engine, regid: c_int, value: ?*anyopaque) !void {
|
||||||
|
try getErrors(c.uc_reg_read(uc, regid, value));
|
||||||
|
}
|
||||||
|
pub fn uc_reg_write_batch(uc: ?*c.uc_engine, regs: [*c]c_int, vals: [*c]const ?*anyopaque, count: c_int) !void {
|
||||||
|
try getErrors(c.uc_reg_write_batch(uc, regs, vals, count));
|
||||||
|
}
|
||||||
|
pub fn uc_reg_read_batch(uc: ?*c.uc_engine, regs: [*c]c_int, vals: [*c]?*anyopaque, count: c_int) !void {
|
||||||
|
try getErrors(c.uc_reg_read_batch(uc, regs, vals, count));
|
||||||
|
}
|
||||||
|
pub fn uc_mem_write(uc: ?*c.uc_engine, address: u64, bytes: ?*const anyopaque, size: usize) !void {
|
||||||
|
try getErrors(c.uc_mem_write(uc, address, bytes, size));
|
||||||
|
}
|
||||||
|
pub fn uc_mem_read(uc: ?*c.uc_engine, address: u64, bytes: ?*anyopaque, size: usize) !void {
|
||||||
|
try getErrors(c.uc_mem_read(uc, address, bytes, size));
|
||||||
|
}
|
||||||
|
pub fn uc_emu_start(uc: ?*c.uc_engine, begin: u64, until: u64, timeout: u64, count: usize) !void {
|
||||||
|
try getErrors(c.uc_emu_start(uc, begin, until, timeout, count));
|
||||||
|
}
|
||||||
|
pub fn uc_emu_stop(uc: ?*c.uc_engine) !void {
|
||||||
|
try getErrors(c.uc_emu_stop(uc));
|
||||||
|
}
|
||||||
|
pub fn uc_hook_add(uc: ?*c.uc_engine, hh: [*c]c.uc_hook, @"type": c_int, callback: ?*anyopaque, user_data: ?*anyopaque, begin: u64, end: u64) !void {
|
||||||
|
try getErrors(c.uc_hook_add(uc, hh, @"type", callback, user_data, begin, end));
|
||||||
|
}
|
||||||
|
pub fn uc_hook_del(uc: ?*c.uc_engine, hh: c.uc_hook) !void {
|
||||||
|
try getErrors(c.uc_hook_del(uc, hh));
|
||||||
|
}
|
||||||
|
pub fn uc_mem_map(uc: ?*c.uc_engine, address: u64, size: usize, perms: u32) !void {
|
||||||
|
try getErrors(c.uc_mem_map(uc, address, size, perms));
|
||||||
|
}
|
||||||
|
pub fn uc_mem_map_ptr(uc: ?*c.uc_engine, address: u64, size: usize, perms: u32, ptr: ?*anyopaque) !void {
|
||||||
|
try getErrors(c.uc_mem_map_ptr(uc, address, size, perms, ptr));
|
||||||
|
}
|
||||||
|
pub fn uc_mmio_map(uc: ?*c.uc_engine, address: u64, size: usize, read_cb: c.uc_cb_mmio_read_t, user_data_read: ?*anyopaque, write_cb: c.uc_cb_mmio_write_t, user_data_write: ?*anyopaque) !void {
|
||||||
|
try getErrors(c.uc_mmio_map(uc, address, size, read_cb, user_data_read, write_cb, user_data_write));
|
||||||
|
}
|
||||||
|
pub fn uc_mem_unmap(uc: ?*c.uc_engine, address: u64, size: usize) !void {
|
||||||
|
try getErrors(c.uc_mem_unmap(uc, address, size));
|
||||||
|
}
|
||||||
|
pub fn uc_mem_protect(uc: ?*c.uc_engine, address: u64, size: usize, perms: u32) !void {
|
||||||
|
try getErrors(c.uc_mem_protect(uc, address, size, perms));
|
||||||
|
}
|
||||||
|
pub fn uc_mem_regions(uc: ?*c.uc_engine, regions: [*c][*c]c.uc_mem_region, count: [*c]u32) !void {
|
||||||
|
try getErrors(c.uc_mem_regions(uc, regions, count));
|
||||||
|
}
|
||||||
|
pub fn uc_context_alloc(uc: ?*c.uc_engine, context: [*c]?*c.uc_context) !void {
|
||||||
|
try getErrors(c.uc_context_alloc(uc, context));
|
||||||
|
}
|
||||||
|
pub fn uc_free(mem: ?*anyopaque) !void {
|
||||||
|
try getErrors(c.uc_free(mem));
|
||||||
|
}
|
||||||
|
pub fn uc_context_save(uc: ?*c.uc_engine, context: ?*c.uc_context) !void {
|
||||||
|
try getErrors(c.uc_context_save(uc, context));
|
||||||
|
}
|
||||||
|
pub fn uc_context_reg_write(ctx: ?*c.uc_context, regid: c_int, value: ?*const anyopaque) !void {
|
||||||
|
try getErrors(c.uc_context_reg_write(ctx, regid, value));
|
||||||
|
}
|
||||||
|
pub fn uc_context_reg_read(ctx: ?*c.uc_context, regid: c_int, value: ?*anyopaque) !void {
|
||||||
|
try getErrors(c.uc_context_reg_read(ctx, regid, value));
|
||||||
|
}
|
||||||
|
pub fn uc_context_reg_write_batch(ctx: ?*c.uc_context, regs: [*c]c_int, vals: [*c]const ?*anyopaque, count: c_int) !void {
|
||||||
|
try getErrors(c.uc_context_reg_write_batch(ctx, regs, vals, count));
|
||||||
|
}
|
||||||
|
pub fn uc_context_reg_read_batch(ctx: ?*c.uc_context, regs: [*c]c_int, vals: [*c]?*anyopaque, count: c_int) !void {
|
||||||
|
try getErrors(c.uc_context_reg_read_batch(ctx, regs, vals, count));
|
||||||
|
}
|
||||||
|
pub fn uc_context_restore(uc: ?*c.uc_engine, context: ?*c.uc_context) !void {
|
||||||
|
try getErrors(c.uc_context_restore(uc, context));
|
||||||
|
}
|
||||||
|
pub fn uc_context_size(uc: ?*c.uc_engine) usize {
|
||||||
|
try getErrors(c.uc_context_size(uc));
|
||||||
|
}
|
||||||
|
pub fn uc_context_free(context: ?*c.uc_context) !void {
|
||||||
|
try getErrors(c.uc_context_free(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Error = error{
|
||||||
|
ucErrNoMemory,
|
||||||
|
ucErrArch,
|
||||||
|
ucErrHandle,
|
||||||
|
ucErrMode,
|
||||||
|
ucErrVersion,
|
||||||
|
ucErrReadUnmapped,
|
||||||
|
ucErrWriteUnmapped,
|
||||||
|
ucErrFetchUnmapped,
|
||||||
|
ucErrHook,
|
||||||
|
ucErrInvalidInstruction,
|
||||||
|
ucErrMap,
|
||||||
|
ucErrWriteProtected,
|
||||||
|
ucErrReadProtected,
|
||||||
|
ucErrFetchProtected,
|
||||||
|
ucErrInvalidArgument,
|
||||||
|
ucErrReadUnaligned,
|
||||||
|
ucErrWriteUnaligned,
|
||||||
|
ucErrFetchUnaligned,
|
||||||
|
ucErrHookAlreadyExists,
|
||||||
|
ucErrResource,
|
||||||
|
ucErrException,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn errorsToZig(err: c.uc_err) Error!c_int {
|
||||||
|
return switch (err) {
|
||||||
|
//c.UC_ERR_OK - isn't error
|
||||||
|
c.UC_ERR_NOMEM => error.ucErrNoMemory,
|
||||||
|
c.UC_ERR_ARCH => error.ucErrArch,
|
||||||
|
c.UC_ERR_HANDLE => error.ucErrHandle,
|
||||||
|
c.UC_ERR_MODE => error.ucErrMode,
|
||||||
|
c.UC_ERR_VERSION => error.ucErrVersion,
|
||||||
|
c.UC_ERR_READ_UNMAPPED => error.ucErrReadUnmapped,
|
||||||
|
c.UC_ERR_WRITE_UNMAPPED => error.ucErrWriteUnmapped,
|
||||||
|
c.UC_ERR_FETCH_UNMAPPED => error.ucErrFetchUnmapped,
|
||||||
|
c.UC_ERR_HOOK => error.ucErrHook,
|
||||||
|
c.UC_ERR_INSN_INVALID => error.ucErrInvalidInstruction,
|
||||||
|
c.UC_ERR_MAP => error.ucErrMap,
|
||||||
|
c.UC_ERR_WRITE_PROT => error.ucErrWriteProtected,
|
||||||
|
c.UC_ERR_READ_PROT => error.ucErrReadProtected,
|
||||||
|
c.UC_ERR_FETCH_PROT => error.ucErrFetchProtected,
|
||||||
|
c.UC_ERR_ARG => error.ucErrInvalidArgument,
|
||||||
|
c.UC_ERR_READ_UNALIGNED => error.ucErrReadUnaligned,
|
||||||
|
c.UC_ERR_WRITE_UNALIGNED => error.ucErrWriteUnaligned,
|
||||||
|
c.UC_ERR_FETCH_UNALIGNED => error.ucErrFetchUnaligned,
|
||||||
|
c.UC_ERR_HOOK_EXIST => error.ucErrHookAlreadyExists,
|
||||||
|
c.UC_ERR_RESOURCE => error.ucErrResource,
|
||||||
|
c.UC_ERR_EXCEPTION => error.ucErrException,
|
||||||
|
else => -1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getErrors(err: c.uc_err) !void {
|
||||||
|
if (try errorsToZig(err) == c.UC_ERR_OK)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const log = @import("std").log.scoped(.unicorn);
|
||||||
|
244
build.zig
Normal file
244
build.zig
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
//! License: GNU GENERAL PUBLIC LICENSE Version 2
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
// Although this function looks imperative, note that its job is to
|
||||||
|
// declaratively construct a build graph that will be executed by an external
|
||||||
|
// runner.
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
if (comptime !checkVersion())
|
||||||
|
@compileError("Please! Update zig toolchain to >= v0.11!");
|
||||||
|
// Standard target options allows the person running `zig build` to choose
|
||||||
|
// what target to build for. Here we do not override the defaults, which
|
||||||
|
// means any target is allowed, and the default is native. Other options
|
||||||
|
// for restricting supported target set are available.
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
|
||||||
|
// Standard optimization options allow the person running `zig build` to select
|
||||||
|
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
|
||||||
|
// set a preferred release mode, allowing the user to decide how to optimize.
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
// flag: -DSamples=True/False
|
||||||
|
const samples = b.option(bool, "Samples", "Build all Samples [default: true]") orelse true;
|
||||||
|
|
||||||
|
// Build Samples
|
||||||
|
|
||||||
|
if (samples) {
|
||||||
|
// Zig
|
||||||
|
buildExe(b, .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.filetype = .zig,
|
||||||
|
.filepath = "bindings/zig/sample/sample_riscv_zig.zig",
|
||||||
|
});
|
||||||
|
|
||||||
|
// C
|
||||||
|
buildExe(b, .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.filetype = .c,
|
||||||
|
.filepath = "samples/sample_arm.c",
|
||||||
|
});
|
||||||
|
buildExe(b, .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.filetype = .c,
|
||||||
|
.filepath = "samples/sample_arm64.c",
|
||||||
|
});
|
||||||
|
buildExe(b, .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.filetype = .c,
|
||||||
|
.filepath = "samples/sample_ctl.c",
|
||||||
|
});
|
||||||
|
buildExe(b, .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.filetype = .c,
|
||||||
|
.filepath = "samples/sample_batch_reg.c",
|
||||||
|
});
|
||||||
|
buildExe(b, .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.filetype = .c,
|
||||||
|
.filepath = "samples/sample_m68k.c",
|
||||||
|
});
|
||||||
|
buildExe(b, .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.filetype = .c,
|
||||||
|
.filepath = "samples/sample_riscv.c",
|
||||||
|
});
|
||||||
|
buildExe(b, .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.filetype = .c,
|
||||||
|
.filepath = "samples/sample_sparc.c",
|
||||||
|
});
|
||||||
|
buildExe(b, .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.filetype = .c,
|
||||||
|
.filepath = "samples/sample_s390x.c",
|
||||||
|
});
|
||||||
|
buildExe(b, .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.filetype = .c,
|
||||||
|
.filepath = "samples/shellcode.c",
|
||||||
|
});
|
||||||
|
buildExe(b, .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.filetype = .c,
|
||||||
|
.filepath = "samples/sample_tricore.c",
|
||||||
|
});
|
||||||
|
buildExe(b, .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.filetype = .c,
|
||||||
|
.filepath = "samples/sample_x86.c",
|
||||||
|
});
|
||||||
|
buildExe(b, .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.filetype = .c,
|
||||||
|
.filepath = "samples/sample_x86_32_gdt_and_seg_regs.c",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// CMake Build
|
||||||
|
const cmake = cmakeBuild(b);
|
||||||
|
const cmake_step = b.step("cmake", "Run cmake build");
|
||||||
|
cmake_step.dependOn(&cmake.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn buildExe(b: *std.Build, info: BuildInfo) void {
|
||||||
|
const execonfig: std.Build.ExecutableOptions = switch (info.filetype) {
|
||||||
|
.c, .cpp => .{
|
||||||
|
.name = info.filename(),
|
||||||
|
.target = info.target,
|
||||||
|
.optimize = info.optimize,
|
||||||
|
},
|
||||||
|
else => .{
|
||||||
|
.name = info.filename(),
|
||||||
|
.target = info.target,
|
||||||
|
.optimize = info.optimize,
|
||||||
|
.root_source_file = .{
|
||||||
|
.path = info.filepath,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const unicornBuild = b.addExecutable(execonfig);
|
||||||
|
|
||||||
|
if (info.filetype != .zig)
|
||||||
|
unicornBuild.addCSourceFile(.{
|
||||||
|
.file = .{ .path = info.filepath },
|
||||||
|
.flags = &.{
|
||||||
|
"-Wall",
|
||||||
|
"-Werror",
|
||||||
|
"-fno-sanitize=all",
|
||||||
|
"-Wshadow",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
else
|
||||||
|
unicornBuild.addAnonymousModule("unicorn", .{
|
||||||
|
.source_file = .{
|
||||||
|
.path = "bindings/zig/unicorn/unicorn.zig",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
unicornBuild.addIncludePath(.{ .path = "include" });
|
||||||
|
// need run cmake before
|
||||||
|
unicornBuild.addLibraryPath(.{ .path = "build" });
|
||||||
|
if (info.target.isWindows()) {
|
||||||
|
unicornBuild.want_lto = false;
|
||||||
|
unicornBuild.linkSystemLibraryName("unicorn.dll");
|
||||||
|
} else unicornBuild.linkSystemLibrary("unicorn");
|
||||||
|
|
||||||
|
// linking to OS-LibC or static-linking for:
|
||||||
|
// Musl(Linux) [e.g: -Dtarget=native-linux-musl]
|
||||||
|
// MinGW(Windows) [e.g: -Dtarget=native-windows-gnu (default)]
|
||||||
|
if (info.filetype == .cpp and info.target.getAbi() != .msvc)
|
||||||
|
unicornBuild.linkLibCpp() // static-linking LLVM-libcxx (all targets) + libC
|
||||||
|
else
|
||||||
|
unicornBuild.linkLibC();
|
||||||
|
|
||||||
|
// This declares intent for the executable to be installed into the
|
||||||
|
// standard location when the user invokes the "install" step (the default
|
||||||
|
// step when running `zig build`).
|
||||||
|
b.installArtifact(unicornBuild);
|
||||||
|
|
||||||
|
// This *creates* a RunStep in the build graph, to be executed when another
|
||||||
|
// step is evaluated that depends on it. The next line below will establish
|
||||||
|
// such a dependency.
|
||||||
|
const run_cmd = b.addRunArtifact(unicornBuild);
|
||||||
|
|
||||||
|
// By making the run step depend on the install step, it will be run from the
|
||||||
|
// installation directory rather than directly from within the cache directory.
|
||||||
|
// This is not necessary, however, if the application depends on other installed
|
||||||
|
// files, this ensures they will be present and in the expected location.
|
||||||
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
|
||||||
|
// This allows the user to pass arguments to the application in the build
|
||||||
|
// command itself, like this: `zig build run -- arg1 arg2 etc`
|
||||||
|
if (b.args) |args| {
|
||||||
|
run_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This creates a build step. It will be visible in the `zig build --help` menu,
|
||||||
|
// and can be selected like this: `zig build run`
|
||||||
|
// This will evaluate the `run` step rather than the default, which is "install".
|
||||||
|
const run_step = b.step(info.filename(), b.fmt("Run the {s}.", .{info.filename()}));
|
||||||
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cmakeBuild(b: *std.Build) *std.Build.Step.Run {
|
||||||
|
const preconf = b.addSystemCommand(&.{
|
||||||
|
"cmake",
|
||||||
|
"-B",
|
||||||
|
"build",
|
||||||
|
"-DZIG_BUILD=ON",
|
||||||
|
"-DUNICORN_BUILD_TESTS=OFF",
|
||||||
|
"-DUNICORN_INSTALL=OFF",
|
||||||
|
"-DCMAKE_BUILD_TYPE=Release",
|
||||||
|
});
|
||||||
|
const cmakebuild = b.addSystemCommand(&.{
|
||||||
|
"cmake",
|
||||||
|
"--build",
|
||||||
|
"build",
|
||||||
|
"--config",
|
||||||
|
"release",
|
||||||
|
"--parallel",
|
||||||
|
});
|
||||||
|
cmakebuild.step.dependOn(&preconf.step);
|
||||||
|
return cmakebuild;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn checkVersion() bool {
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
if (!@hasDecl(builtin, "zig_version")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const needed_version = std.SemanticVersion.parse("0.11.0") catch unreachable;
|
||||||
|
const version = builtin.zig_version;
|
||||||
|
const order = version.order(needed_version);
|
||||||
|
return order != .lt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BuildInfo = struct {
|
||||||
|
filepath: []const u8,
|
||||||
|
filetype: enum {
|
||||||
|
c,
|
||||||
|
cpp,
|
||||||
|
zig,
|
||||||
|
},
|
||||||
|
target: std.zig.CrossTarget,
|
||||||
|
optimize: std.builtin.OptimizeMode,
|
||||||
|
|
||||||
|
fn filename(self: BuildInfo) []const u8 {
|
||||||
|
var split = std.mem.splitSequence(u8, std.fs.path.basename(self.filepath), ".");
|
||||||
|
return split.first();
|
||||||
|
}
|
||||||
|
};
|
9
cmake/zig.cmake
Normal file
9
cmake/zig.cmake
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
set(CMAKE_CROSSCOMPILING TRUE)
|
||||||
|
# set the compiler
|
||||||
|
if(WIN32)
|
||||||
|
SET(ZIG_CC ${CMAKE_SOURCE_DIR}/bindings/zig/tools/zigcc.cmd)
|
||||||
|
else()
|
||||||
|
SET(ZIG_CC ${CMAKE_SOURCE_DIR}/bindings/zig/tools/zigcc.sh)
|
||||||
|
endif()
|
||||||
|
SET(CMAKE_C_COMPILER_ID ${ZIG_CC})
|
||||||
|
SET(CMAKE_C_COMPILER ${ZIG_CC})
|
Loading…
Reference in New Issue
Block a user