Skip to content

Commit

Permalink
Merge pull request #1815 from kassane/zig-build
Browse files Browse the repository at this point in the history
Zig - Sample build
  • Loading branch information
wtdcode authored Aug 6, 2023
2 parents 6801e15 + 4fb4b3e commit 6ae0c97
Show file tree
Hide file tree
Showing 13 changed files with 762 additions and 7 deletions.
7 changes: 4 additions & 3 deletions .github/workflows/build-uc2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
shared: 'yes',
mingw: MINGW64,
mingw-arch: x86_64,
artifact: 'windows_mingw64.7z',
artifact: 'windows_mingw64-shared.7z',
build_type: 'Debug',
archiver: '7z a',
generators: 'Ninja'
Expand All @@ -49,7 +49,7 @@ jobs:
shared: 'no',
mingw: MINGW64,
mingw-arch: x86_64,
artifact: 'windows_mingw64.7z',
artifact: 'windows_mingw64-static.7z',
build_type: 'Debug',
archiver: '7z a',
generators: 'Ninja'
Expand Down Expand Up @@ -217,7 +217,7 @@ jobs:
#export CC=i686-w64-mingw32-gcc
export AR=gcc-ar
export RANLIB=gcc-ranlib
export CFLAGS="-m32"
export CFLAGS="-m32 -static"
export LDFLAGS="-m32"
export LDFLAGS_STATIC="-m32"
export UNICORN_QEMU_FLAGS="--cpu=i386"
Expand All @@ -230,6 +230,7 @@ jobs:
-DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} \
-G "${{ matrix.config.generators }}" \
-DCMAKE_INSTALL_PREFIX:PATH=instdir \
-DCMAKE_C_FLAGS:STRING="-static" \
-DBUILD_SHARED_LIBS=${{ matrix.config.shared }}
cmake --build . --config ${{ matrix.config.build_type }}
cmake --install . --strip
Expand Down
81 changes: 81 additions & 0 deletions .github/workflows/zigbuild.yml
Original file line number Diff line number Diff line change
@@ -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 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,5 @@ rust_build
[Oo]bj/
packages/
cmocka/
zig-cache/
zig-out/
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.15")
cmake_policy(SET CMP0092 NEW)
endif()

option(ZIG_BUILD "Enable zig build" OFF)
if(ZIG_BUILD)
include(cmake/zig.cmake)
endif()

# Workaround to fix wrong compiler on macos.
if(APPLE AND NOT CMAKE_C_COMPILER)
set(CMAKE_C_COMPILER "/usr/bin/cc")
Expand Down Expand Up @@ -47,7 +52,7 @@ set(UNICORN_VERSION_MAJOR 2)
set(UNICORN_VERSION_MINOR 0)
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
# using unicorn as a subdirectory so we have to respect BUILD_SHARED_LIBS.
Expand Down
2 changes: 1 addition & 1 deletion bindings/python/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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, "../../*.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, "../../README.md")))
Expand Down
218 changes: 218 additions & 0 deletions bindings/zig/sample/sample_riscv_zig.zig
Original file line number Diff line number Diff line change
@@ -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 changes: 3 additions & 0 deletions bindings/zig/tools/zigcc.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@echo off

zig cc -fno-sanitize=all %*
3 changes: 3 additions & 0 deletions bindings/zig/tools/zigcc.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#! /usr/bin/env bash

`which zig` cc -fno-sanitize=all $@
Loading

0 comments on commit 6ae0c97

Please sign in to comment.