diff --git a/.github/workflows/CITest.yml b/.github/workflows/CITest.yml index 318edf3c22..90a761c515 100644 --- a/.github/workflows/CITest.yml +++ b/.github/workflows/CITest.yml @@ -54,7 +54,7 @@ jobs: - uses: actions/checkout@v3 - name: prepare - shell: 'script -q -e -c "bash {0}"' + shell: 'script -q -e -c "bash -e {0}"' run: | export LD_LIBRARY_PATH=`pwd`/tests/:$LD_LIBRARY_PATH wget https://github.com/groundx/capstonefuzz/raw/master/corpus/corpus-libFuzzer-capstone_fuzz_disasmnext-latest.zip @@ -64,7 +64,7 @@ jobs: - name: make if: startsWith(matrix.config.build-system, 'make') - shell: 'script -q -e -c "bash {0}"' + shell: 'script -q -e -c "bash -e {0}"' run: | ./make.sh; make check; @@ -73,7 +73,7 @@ jobs: - name: cmake if: startsWith(matrix.config.build-system, 'cmake') - shell: 'script -q -e -c "bash {0}"' + shell: 'script -q -e -c "bash -e {0}"' run: | mkdir build && cd build; cmake -DCAPSTONE_INSTALL=1 -DBUILD_SHARED_LIBS=1 ..; @@ -82,9 +82,10 @@ jobs: # sudo make install; - name: cstest - shell: 'script -q -e -c "bash {0}"' + shell: 'script -q -e -c "bash -e {0}"' run: | cd suite/cstest && ./build_cstest.sh; + export LD_LIBRARY_PATH="${PWD}/../..:${LD_LIBRARY_PATH:-}"; python cstest_report.py -D -t build/cstest -d ../MC; python cstest_report.py -D -t build/cstest -f issues.cs; cd ..; diff --git a/.github/workflows/CrossBuilds.yml b/.github/workflows/CrossBuilds.yml new file mode 100644 index 0000000000..c0482cd599 --- /dev/null +++ b/.github/workflows/CrossBuilds.yml @@ -0,0 +1,252 @@ +name: Cross Build Tests +on: + push: + paths-ignore: + - ".gitignore" + - "docs/**" + - "ChangeLog" + - "CREDITS.TXT" + - "COMPILE.TXT" + - "COMPILE_CMAKE.TXT" + - "COMPILE_MSVC.TXT" + - "HACK.TXT" + - "LICENSE.TXT" + - "LICENSE_LLVM.TXT" + - "README.md" + - "RELEASE_NOTES" + - "SPONSORS.TXT" + pull_request: + +# Stop previous runs on the same branch on new push +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + CI: true + +jobs: + Linux: + runs-on: ${{ matrix.config.os }} + name: ${{ matrix.config.name }} + strategy: + fail-fast: false + matrix: + config: + - { + name: 'QEMU Linux s390x', + os: ubuntu-24.04, + system: 'Linux', + processor: 's390x', + cc: 's390x-linux-gnu-gcc', + cxx: 's390x-linux-gnu-g++', + sysroot: '/usr/s390x-linux-gnu', + qemu: 'qemu-s390x-static', + build_type: 'Debug', + packages: 'gcc-s390x-linux-gnu g++-s390x-linux-gnu binutils-s390x-linux-gnu libc6-dev-s390x-cross qemu-user-static', + build_tests: 'ON', + diet_build: 'OFF', + toolchain_file: 'toolchain.cmake', + } + - { + name: 'QEMU Linux MIPS 32 BE', + os: ubuntu-24.04, + system: 'Linux', + processor: 'mips', + cc: 'mips-linux-gnu-gcc', + cxx: 'mips-linux-gnu-g++', + sysroot: '/usr/mips-linux-gnu', + qemu: 'qemu-mips-static', + build_type: 'Debug', + packages: 'gcc-mips-linux-gnu g++-mips-linux-gnu binutils-mips-linux-gnu libc6-dev-mips-cross qemu-user-static', + build_tests: 'ON', + diet_build: 'OFF', + toolchain_file: 'toolchain.cmake', + } + - { + name: 'QEMU Linux Mips64el', + os: ubuntu-24.04, + system: 'Linux', + processor: 'mips64el', + cc: 'mips64el-linux-gnuabi64-gcc', + cxx: 'mips64el-linux-gnuabi64-g++', + sysroot: '/usr/mips64el-linux-gnuabi64', + qemu: 'qemu-mips64el-static', + build_type: 'Debug', + packages: 'gcc-mips64el-linux-gnuabi64 g++-mips64el-linux-gnuabi64 binutils-mips64el-linux-gnuabi64 libc6-dev-mips64el-cross qemu-user-static', + build_tests: 'ON', + diet_build: 'OFF', + toolchain_file: 'toolchain.cmake', + } + - { + name: 'QEMU Linux PPC64', + os: ubuntu-24.04, + system: 'Linux', + processor: 'ppc64', + cc: 'powerpc64-linux-gnu-gcc', + cxx: 'powerpc64-linux-gnu-g++', + sysroot: '/usr/powerpc64-linux-gnu', + qemu: 'qemu-ppc64-static', + build_type: 'Debug', + packages: 'gcc-powerpc64-linux-gnu g++-powerpc64-linux-gnu binutils-powerpc64-linux-gnu libc6-dev-ppc64-cross qemu-user-static', + build_tests: 'ON', + diet_build: 'OFF', + toolchain_file: 'toolchain.cmake', + } + - { + name: 'QEMU Linux ARM', + os: ubuntu-24.04, + system: 'Linux', + processor: 'arm', + cc: 'arm-linux-gnueabihf-gcc', + cxx: 'arm-linux-gnueabihf-g++', + sysroot: '/usr/arm-linux-gnueabihf', + qemu: 'qemu-arm-static', + build_type: 'Debug', + packages: 'gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf binutils-arm-linux-gnueabihf libc6-armhf-cross libc6-dev-armhf-cross qemu-user-static', + build_tests: 'ON', + diet_build: 'OFF', + toolchain_file: 'toolchain.cmake', + } + - { + name: '[BUILD ONLY] Windows i686 mingw', + os: ubuntu-24.04, + system: 'Windows', + processor: 'i686', + cc: 'i686-w64-mingw32-gcc', + cxx: 'i686-w64-mingw32-g++', + rc: 'i686-w64-mingw32-windres', + find_root: '/usr/i686-w64-mingw32', + build_type: 'Debug', + skip_tests: true, + packages: 'gcc-mingw-w64-i686 g++-mingw-w64-i686 mingw-w64-tools', + build_tests: 'OFF', + diet_build: 'OFF', + toolchain_file: 'toolchain.cmake', + } + - { + name: '[BUILD ONLY] Android 35 (arm64_v8a) NDK 29', + os: ubuntu-24.04, + system: 'Android', + processor: 'aarch64', + build_option: '-DANDROID_PLATFORM=android-35 -DANDROID_ABI=arm64-v8a', + build_type: 'Debug', + # QEMU alone can't emulate the binaries, because the NDK doesn't + # provide dynamic linker. + skip_tests: true, + packages: 'qemu-user-static unzip wget', + ndk_version: 'r29', + build_tests: 'OFF', + diet_build: 'OFF', + toolchain_file: 'ndk/build/cmake/android.toolchain.cmake', + } + + steps: + - uses: actions/checkout@v6 + + - name: Install cross build dependencies + if: ${{ matrix.config.packages != '' }} + env: + packages: ${{ matrix.config.packages }} + run: | + sudo apt-get update + sudo apt-get install -y ${packages} + + - name: Setup Android NDK + if: contains(matrix.config.name, 'Android') + env: + ndk_version: ${{ matrix.config.ndk_version }} + run: | + wget -q https://dl.google.com/android/repository/android-ndk-${ndk_version}-linux.zip + unzip -q android-ndk-${ndk_version}-linux.zip + mv android-ndk-${ndk_version} ndk + cat ndk/source.properties + + - name: Build cmocka + if: ${{ matrix.config.skip_tests != true }} + env: + system: ${{ matrix.config.system }} + processor: ${{ matrix.config.processor }} + cc: ${{ matrix.config.cc }} + cxx: ${{ matrix.config.cxx }} + sysroot: ${{ matrix.config.sysroot }} + qemu: ${{ matrix.config.qemu }} + build_type: ${{ matrix.config.build_type }} + toolchain_file: ${{ matrix.config.toolchain_file }} + run: | + git clone --depth 1 https://git.cryptomilk.org/projects/cmocka.git suite/cstest/cmocka + cmake -S suite/cstest/cmocka \ + -B build/cmocka \ + -DCMAKE_BUILD_TYPE=${build_type} \ + -DCMAKE_TOOLCHAIN_FILE="${PWD}/${toolchain_file}" \ + -DCMAKE_INSTALL_PREFIX="${PWD}/build/cmocka-install" \ + -DCMAKE_INSTALL_LIBDIR=lib \ + -DBUILD_SHARED_LIBS=OFF \ + -DWITH_STATIC_LIB=ON \ + -DWITH_EXAMPLES=OFF \ + -DUNIT_TESTING=OFF + cmake --build build/cmocka --config ${build_type} --parallel + cmake --install build/cmocka --config ${build_type} + + - name: cmake (cross build) + env: + system: ${{ matrix.config.system }} + processor: ${{ matrix.config.processor }} + cc: ${{ matrix.config.cc }} + cxx: ${{ matrix.config.cxx }} + rc: ${{ matrix.config.rc }} + find_root: ${{ matrix.config.find_root }} + sysroot: ${{ matrix.config.sysroot }} + qemu: ${{ matrix.config.qemu }} + build_option: ${{ matrix.config.build_option }} + build_type: ${{ matrix.config.build_type }} + build_tests: ${{ matrix.config.build_tests }} + diet_build: ${{ matrix.config.diet_build }} + toolchain_file: ${{ matrix.config.toolchain_file }} + run: | + cmake -DCMAKE_BUILD_TYPE=${build_type} \ + -DBUILD_STATIC_LIBS=ON \ + -DBUILD_SHARED_LIBS=OFF \ + -DCAPSTONE_BUILD_TESTS=${build_tests} \ + -DCAPSTONE_BUILD_CSTOOL=ON \ + -DCAPSTONE_BUILD_CSTEST=OFF \ + -S . \ + -DCAPSTONE_BUILD_DIET=${diet_build} \ + -DCMAKE_TOOLCHAIN_FILE=${toolchain_file} \ + ${build_option} \ + -B build + cmake --build build --config ${build_type} --parallel + + - name: C tests + if: ${{ matrix.config.skip_tests != true }} + run: | + ctest --test-dir build --output-on-failure -R '^capstone_' + + - name: cstest + if: ${{ matrix.config.skip_tests != true }} + shell: 'script -q -e -c "bash -e {0}"' + env: + cc: ${{ matrix.config.cc }} + qemu: ${{ matrix.config.qemu }} + sysroot: ${{ matrix.config.sysroot }} + run: | + cmocka_lib="$(find build/cmocka build/cmocka-install -type f \( -name 'libcmocka*.a' -o -name 'libcmocka*.so' \) | sort | head -n 1)" + test -n "${cmocka_lib}" + ${cc} suite/cstest/src/*.c \ + -Iinclude \ + -Isuite/cstest/include \ + -Ibuild/cmocka-install/include \ + -o build/cstest \ + build/libcapstone.a \ + "${cmocka_lib}" \ + -pthread + cat > build/run-cstest <<'EOF' + #!/bin/sh + export LD_LIBRARY_PATH="${CMOCKA_PREFIX}/lib:${CMOCKA_PREFIX}/lib64:${LD_LIBRARY_PATH}" + exec "${qemu}" -L "${sysroot}" "${cstest_bin}" "$@" + EOF + chmod +x build/run-cstest + export CMOCKA_PREFIX="${PWD}/build/cmocka-install" + export cstest_bin="${PWD}/build/cstest" + python suite/cstest/cstest_report.py -D -t build/run-cstest -d suite/MC + python suite/cstest/cstest_report.py -D -t build/run-cstest -f suite/cstest/issues.cs diff --git a/arch/AArch64/AArch64InstPrinter.c b/arch/AArch64/AArch64InstPrinter.c index 517db25107..f1f8e6ea42 100644 --- a/arch/AArch64/AArch64InstPrinter.c +++ b/arch/AArch64/AArch64InstPrinter.c @@ -1224,8 +1224,9 @@ static void printVRegOperand(MCInst *MI, unsigned OpNum, SStream *O) static void printSysCROperand(MCInst *MI, unsigned OpNum, SStream *O) { MCOperand *Op = MCInst_getOperand(MI, OpNum); + unsigned Val = (unsigned)MCOperand_getImm(Op); //assert(Op.isImm() && "System instruction C[nm] operands must be immediates!"); - SStream_concat(O, "c%u", MCOperand_getImm(Op)); + SStream_concat(O, "c%u", Val); if (MI->csh->detail) { #ifndef CAPSTONE_DIET @@ -1236,7 +1237,7 @@ static void printSysCROperand(MCInst *MI, unsigned OpNum, SStream *O) MI->ac_idx++; #endif MI->flat_insn->detail->arm64.operands[MI->flat_insn->detail->arm64.op_count].type = ARM64_OP_CIMM; - MI->flat_insn->detail->arm64.operands[MI->flat_insn->detail->arm64.op_count].imm = MCOperand_getImm(Op); + MI->flat_insn->detail->arm64.operands[MI->flat_insn->detail->arm64.op_count].imm = Val; MI->flat_insn->detail->arm64.op_count++; } } diff --git a/arch/M68K/M68KInstPrinter.c b/arch/M68K/M68KInstPrinter.c index 061edb30a7..2e1548fdba 100644 --- a/arch/M68K/M68KInstPrinter.c +++ b/arch/M68K/M68KInstPrinter.c @@ -154,8 +154,8 @@ static void printAddressingMode(SStream* O, unsigned int pc, const cs_m68k* inst case M68K_AM_REGI_ADDR_PRE_DEC: SStream_concat(O, "-(a%d)", (op->reg - M68K_REG_A0)); break; case M68K_AM_REGI_ADDR_DISP: SStream_concat(O, "%s$%x(a%d)", op->mem.disp < 0 ? "-" : "", abs(op->mem.disp), (op->mem.base_reg - M68K_REG_A0)); break; case M68K_AM_PCI_DISP: SStream_concat(O, "$%x(pc)", pc + 2 + op->mem.disp); break; - case M68K_AM_ABSOLUTE_DATA_SHORT: SStream_concat(O, "$%x.w", op->imm); break; - case M68K_AM_ABSOLUTE_DATA_LONG: SStream_concat(O, "$%x.l", op->imm); break; + case M68K_AM_ABSOLUTE_DATA_SHORT: SStream_concat(O, "$%x.w", (unsigned int)op->imm); break; + case M68K_AM_ABSOLUTE_DATA_LONG: SStream_concat(O, "$%x.l", (unsigned int)op->imm); break; case M68K_AM_IMMEDIATE: if (inst->op_size.type == M68K_SIZE_TYPE_FPU) { #if defined(_KERNEL_MODE) @@ -172,7 +172,7 @@ static void printAddressingMode(SStream* O, unsigned int pc, const cs_m68k* inst break; #endif } - SStream_concat(O, "#$%x", op->imm); + SStream_concat(O, "#$%x", (unsigned int)op->imm); break; case M68K_AM_PCI_INDEX_8_BIT_DISP: SStream_concat(O, "$%x(pc,%s%s.%c)", pc + 2 + op->mem.disp, s_spacing, getRegName(op->mem.index_reg), op->mem.index_size ? 'l' : 'w'); @@ -287,7 +287,7 @@ void M68K_printInst(MCInst* MI, SStream* O, void* PrinterInfo) if (MI->Opcode == M68K_INS_INVALID) { if (ext->op_count) - SStream_concat(O, "dc.w $%x", ext->operands[0].imm); + SStream_concat(O, "dc.w $%x", (unsigned int)ext->operands[0].imm); else SStream_concat(O, "dc.w $"); return; diff --git a/arch/MOS65XX/MOS65XXDisassembler.c b/arch/MOS65XX/MOS65XXDisassembler.c index c3d8c429b5..2723f6fa60 100644 --- a/arch/MOS65XX/MOS65XXDisassembler.c +++ b/arch/MOS65XX/MOS65XXDisassembler.c @@ -330,7 +330,7 @@ void MOS65XX_printInst(MCInst *MI, struct SStream *O, void *PrinterInfo) value = 3 + (signed short)value; SStream_concat(O, " %s%04x", prefix, - (MI->address + value) & 0xffff); + (unsigned int)((MI->address + value) & 0xffff)); break; case MOS65XX_AM_ABS_IND: @@ -375,16 +375,16 @@ void MOS65XX_printInst(MCInst *MI, struct SStream *O, void *PrinterInfo) case MOS65XX_AM_BLOCK: SStream_concat(O, " %s%02x, %s%02x", - prefix, MI->Operands[0].ImmVal, - prefix, MI->Operands[1].ImmVal); + prefix, (unsigned int)MI->Operands[0].ImmVal, + prefix, (unsigned int)MI->Operands[1].ImmVal); break; case MOS65XX_AM_ZP_REL: value = 3 + (signed char)MI->Operands[1].ImmVal; /* BBR0, zp, rel and BBS0, zp, rel */ SStream_concat(O, " %s%02x, %s%04x", - prefix, MI->Operands[0].ImmVal, - prefix, (MI->address + value) & 0xffff); + prefix, (unsigned int)MI->Operands[0].ImmVal, + prefix, (unsigned int)((MI->address + value) & 0xffff)); break; } diff --git a/arch/X86/X86ATTInstPrinter.c b/arch/X86/X86ATTInstPrinter.c index b2f8412859..d3283c2cc1 100644 --- a/arch/X86/X86ATTInstPrinter.c +++ b/arch/X86/X86ATTInstPrinter.c @@ -487,7 +487,7 @@ static void printU8Imm(MCInst *MI, unsigned Op, SStream *O) if (val > HEX_THRESHOLD) SStream_concat(O, "$0x%x", val); else - SStream_concat(O, "$%u", val); + SStream_concat(O, "$%"PRIu8, val); if (MI->csh->detail) { MI->flat_insn->detail->x86.operands[MI->flat_insn->detail->x86.op_count].type = X86_OP_IMM; @@ -636,7 +636,7 @@ static void printOperand(MCInst *MI, unsigned OpNo, SStream *O) // do not print number in negative form imm = imm & 0xff; if (imm >= 0 && imm <= HEX_THRESHOLD) - SStream_concat(O, "$%u", imm); + SStream_concat(O, "$%"PRIu64, imm); else { SStream_concat(O, "$0x%x", imm); } @@ -659,7 +659,7 @@ static void printOperand(MCInst *MI, unsigned OpNo, SStream *O) case X86_INS_XOR: // do not print number in negative form if (imm >= 0 && imm <= HEX_THRESHOLD) - SStream_concat(O, "$%u", imm); + SStream_concat(O, "$%"PRIu64, imm); else { imm = arch_masks[opsize? opsize : MI->imm_size] & imm; SStream_concat(O, "$0x%"PRIx64, imm); @@ -670,7 +670,7 @@ static void printOperand(MCInst *MI, unsigned OpNo, SStream *O) case X86_INS_RETF: // RET imm16 if (imm >= 0 && imm <= HEX_THRESHOLD) - SStream_concat(O, "$%u", imm); + SStream_concat(O, "$%"PRIu64, imm); else { imm = 0xffff & imm; SStream_concat(O, "$0x%x", imm); @@ -773,7 +773,7 @@ static void printMemReference(MCInst *MI, unsigned Op, SStream *O) if (MI->csh->detail) MI->flat_insn->detail->x86.operands[MI->flat_insn->detail->x86.op_count].mem.scale = (int)ScaleVal; if (ScaleVal != 1) { - SStream_concat(O, ", %u", ScaleVal); + SStream_concat(O, ", %"PRIu64, ScaleVal); } } diff --git a/arch/X86/X86IntelInstPrinter.c b/arch/X86/X86IntelInstPrinter.c index 05d4141bad..eb1c290b56 100644 --- a/arch/X86/X86IntelInstPrinter.c +++ b/arch/X86/X86IntelInstPrinter.c @@ -929,7 +929,7 @@ static void printMemReference(MCInst *MI, unsigned Op, SStream *O) { bool NeedPlus = false; MCOperand *BaseReg = MCInst_getOperand(MI, Op + X86_AddrBaseReg); - uint64_t ScaleVal = MCOperand_getImm(MCInst_getOperand(MI, Op + X86_AddrScaleAmt)); + unsigned int ScaleVal = (unsigned int)MCOperand_getImm(MCInst_getOperand(MI, Op + X86_AddrScaleAmt)); MCOperand *IndexReg = MCInst_getOperand(MI, Op + X86_AddrIndexReg); MCOperand *DispSpec = MCInst_getOperand(MI, Op + X86_AddrDisp); MCOperand *SegReg = MCInst_getOperand(MI, Op + X86_AddrSegmentReg); diff --git a/suite/cstest/cstest_report.py b/suite/cstest/cstest_report.py index c3046cc168..1ce36b161b 100755 --- a/suite/cstest/cstest_report.py +++ b/suite/cstest/cstest_report.py @@ -14,21 +14,80 @@ def Usage(s): print('Usage: {} -t [-f ] [-d ]'.format(s)) sys.exit(-1) +def decode_output(output): + if _python3: + return bytes.decode(output) + return output + +def print_tool_failure(filepath, returncode, stdout, stderr): + print('\n[-] cstest failed for {} with exit code {}'.format(filepath, returncode)) + if stdout: + print('[-] stdout:\n{}'.format(stdout)) + if stderr: + print('[-] stderr:\n{}'.format(stderr)) + +def get_failed_tests(output): + failed_tests = [] + summary_tests = [] + in_failed_summary = False + for line in output.split('\n'): + match = re.match(r'\[\s+FAILED\s+\]\s+(.*)', line) + if match is None: + continue + name = match.group(1).strip() + if re.match(r'\d+\s+test\(s\)', name): + in_failed_summary = 'listed below' in name + continue + if in_failed_summary: + summary_tests.append(name) + else: + failed_tests.append(name) + + return summary_tests if summary_tests else failed_tests + +def normalize_line(line): + return re.sub(r'\s+', '', line) + +def get_test_name_from_file(filepath, code): + try: + with open(filepath, 'r') as f: + lines = f.readlines() + except OSError: + return 'Unknown test' + + test_name = '' + needle = normalize_line(code) + for i, line in enumerate(lines): + line = line.strip() + if line.startswith('!# issue') or line.startswith('// !# issue'): + test_name = line + if needle and normalize_line(line).startswith(needle): + return test_name if test_name else 'Line {}'.format(i + 1) + + return 'Unknown test' + def get_report_file(toolpath, filepath, getDetails, cmt_out): cmd = [toolpath, '-f', filepath] - process = Popen(cmd, stdout=PIPE, stderr=PIPE) + try: + process = Popen(cmd, stdout=PIPE, stderr=PIPE) + except OSError as e: + print('\n[-] Failed to run {}: {}'.format(toolpath, e)) + return 0 + stdout, stderr = process.communicate() # stdout failed_tests = [] - if _python3: - stdout = bytes.decode(stdout) - stderr = bytes.decode(stderr) + stdout = decode_output(stdout) + stderr = decode_output(stderr) + + if process.returncode != 0: + print_tool_failure(filepath, process.returncode, stdout, stderr) + return 0 + # print('---> stdout\n', stdout) # print('---> stderr\n', stderr) - matches = re.finditer(r'\[\s+RUN\s+\]\s+(.*)\n\[\s+FAILED\s+\]', stdout) - for match in matches: - failed_tests.append(match.group(1)) + failed_tests = get_failed_tests(stdout + '\n' + stderr) # stderr counter = 0 details = [] @@ -38,11 +97,14 @@ def get_report_file(toolpath, filepath, getDetails, cmt_out): elif 'LINE' in line: continue elif 'ERROR' in line and ' --- ' in line: - parts = line.split(' --- ') - try: - details.append((parts[1], failed_tests[counter], parts[2])) - except IndexError: - details.append(('', 'Unknown test', line.split(' --- ')[1])) + parts = line.split(' --- ', 2) + code = parts[1] if len(parts) > 1 else '' + report = parts[2] if len(parts) > 2 else line + if counter < len(failed_tests): + test_name = failed_tests[counter] + else: + test_name = get_test_name_from_file(filepath, code) + details.append((code, test_name, report)) counter += 1 else: continue @@ -56,7 +118,7 @@ def get_report_file(toolpath, filepath, getDetails, cmt_out): elif len(details) > 0: for c, f, d in details: if len(f) > 0 and cmt_out is True: - tmp_cmd = ['sed', '-E', '-i.bak', 's/({})(.*)/\/\/ \\1\\2/g'.format(c), filepath] + tmp_cmd = ['sed', '-E', '-i.bak', r's/({})(.*)/\/\/ \1\2/g'.format(c), filepath] sed_proc = Popen(tmp_cmd, stdout=PIPE, stderr=PIPE) sed_proc.communicate() tmp_cmd2 = ['rm', '-f', filepath + '.bak'] @@ -75,24 +137,33 @@ def get_report_folder(toolpath, folderpath, details, cmt_out): print('[-] Target:', f,) result *= get_report_file(toolpath, os.sep.join(x for x in path) + os.sep + f, details, cmt_out) - sys.exit(result ^ 1) + return result + +def validate_tool(toolpath): + if not toolpath: + print('[-] Missing cstest path') + return False + if not os.path.isfile(toolpath): + print('[-] cstest path does not exist: {}'.format(toolpath)) + return False + if not os.access(toolpath, os.X_OK): + print('[-] cstest path is not executable: {}'.format(toolpath)) + return False + return True if __name__ == '__main__': - Done = False details = False toolpath = '' cmt_out = False + files = [] + folders = [] try: opts, args = getopt.getopt(sys.argv[1:], "ct:f:d:D") for opt, arg in opts: if opt == '-f': - result = get_report_file(toolpath, arg, details, cmt_out) - if result == 0: - sys.exit(1) - Done = True + files.append(arg) elif opt == '-d': - get_report_folder(toolpath, arg, details, cmt_out) - Done = True + folders.append(arg) elif opt == '-t': toolpath = arg elif opt == '-D': @@ -103,5 +174,15 @@ def get_report_folder(toolpath, folderpath, details, cmt_out): except getopt.GetoptError: Usage(sys.argv[0]) - if Done is False: + if not files and not folders: Usage(sys.argv[0]) + if not validate_tool(toolpath): + sys.exit(1) + + result = 1 + for f in files: + result *= get_report_file(toolpath, f, details, cmt_out) + for d in folders: + result *= get_report_folder(toolpath, d, details, cmt_out) + + sys.exit(result ^ 1) diff --git a/suite/cstest/include/factory.h b/suite/cstest/include/factory.h index e2a1c4c6d7..58e4d872e2 100644 --- a/suite/cstest/include/factory.h +++ b/suite/cstest/include/factory.h @@ -24,5 +24,6 @@ char *get_detail_mos65xx(csh *handle, cs_mode mode, cs_insn *ins); char *get_detail_tms320c64x(csh *handle, cs_mode mode, cs_insn *ins); char *get_detail_bpf(csh *handle, cs_mode mode, cs_insn *ins); char *get_detail_tricore(csh *handle, cs_mode mode, cs_insn *ins); +char *get_detail_sh(csh *handle, cs_mode mode, cs_insn *ins); #endif /* FACTORY_H */ diff --git a/suite/cstest/issues.cs b/suite/cstest/issues.cs index 5aee561a49..51c98fd666 100644 --- a/suite/cstest/issues.cs +++ b/suite/cstest/issues.cs @@ -253,6 +253,22 @@ 0xf2,0x1f,0x54,0x80 == fmove.d (a7)+, fp1 0x4e,0x75 == rts +!# issue M68K printer integer immediates on 32-bit big endian hosts +!# CS_ARCH_M68K, CS_MODE_BIG_ENDIAN | CS_MODE_M68K_040, None +0x00,0x80,0x12,0x34,0x56,0x78 == ori.l #$12345678, d0 + +!# issue M68K printer absolute long addresses on 32-bit big endian hosts +!# CS_ARCH_M68K, CS_MODE_BIG_ENDIAN | CS_MODE_M68K_040, None +0x4e,0xb9,0x12,0x34,0x56,0x78 == jsr $12345678.l + +!# issue M68K printer absolute short addresses on 32-bit big endian hosts +!# CS_ARCH_M68K, CS_MODE_BIG_ENDIAN | CS_MODE_M68K_040, None +0x4e,0xb8,0x12,0x34 == jsr $1234.w + +!# issue M68K printer invalid words on 32-bit big endian hosts +!# CS_ARCH_M68K, CS_MODE_BIG_ENDIAN | CS_MODE_M68K_040, None +0xff,0xff == dc.w $ffff + !# issue 1661 M68K invalid transfer direction in MOVEC instruction !# CS_ARCH_M68K, CS_MODE_BIG_ENDIAN | CS_MODE_M68K_040, None 0x4E,0x7A,0x00,0x02 == movec cacr, d0 @@ -627,6 +643,10 @@ !# CS_ARCH_X86, CS_MODE_64, CS_OPT_DETAIL 0x0: 0x0f,0x94,0x44,0x24,0x1f == sete byte ptr [rsp + 0x1f] ; Prefix:0x00 0x00 0x00 0x00 ; Opcode:0x0f 0x94 0x00 0x00 ; rex: 0x0 ; addr_size: 8 ; modrm: 0x44 ; disp: 0x1f ; sib: 0x24 ; sib_base: rsp ; sib_scale: 1 ; op_count: 1 ; operands[0].type: MEM ; operands[0].mem.base: REG = rsp ; operands[0].mem.disp: 0x1f ; operands[0].size: 1 ; operands[0].access: WRITE ; Registers read: rflags rsp ; EFLAGS: TEST_ZF +!# issue x86 SIB scale is encoded as log2(scale) +!# CS_ARCH_X86, CS_MODE_64, None +0x0: 0x8b,0x34,0x82 == mov esi, dword ptr [rdx + rax*4] + !# issue 1263 !# CS_ARCH_X86, CS_MODE_64, None 0x0: 0x67,0x48,0x89,0x18 == mov qword ptr [eax], rbx @@ -1065,7 +1085,7 @@ !# issue 2424 !# CS_ARCH_SH, CS_MODE_SH2A | CS_MODE_BIG_ENDIAN, CS_OPT_DETAIL -0x0: 0x32,0x11,0x92,0x00 == movu.w @(1024,r1),r2 ; operands[0].type: MEM ; operands[0].mem.reg: REG = r1 ; operands[0].mem.disp: 0x400 ; address mode: Register Indirect with Predecrement ; operands[1].type: REG = r2 +0x0: 0x32,0x11,0x92,0x00 == movu.w @(1024,r1),r2 ; operands[0].type: MEM ; operands[0].mem.reg: REG = r1 ; operands[0].mem.disp: 0x400 ; address mode: Register Indirect with displacement ; operands[1].type: REG = r2 !# issue 2646 !# CS_ARCH_TMS320C64X, CS_MODE_LITTLE_ENDIAN, CS_OPT_DETAIL diff --git a/suite/cstest/src/capstone_test.c b/suite/cstest/src/capstone_test.c index aca1236828..32a431cea5 100644 --- a/suite/cstest/src/capstone_test.c +++ b/suite/cstest/src/capstone_test.c @@ -6,6 +6,17 @@ char *(*function)(csh *, cs_mode, cs_insn*) = NULL; +static void remove_char(char *src, char c) +{ + char *dst = src; + + for (; *src; src++) { + if (*src != c) + *dst++ = *src; + } + *dst = '\0'; +} + void test_single_MC(csh *handle, int mc_mode, char *line) { char **list_part, **list_byte; @@ -59,6 +70,7 @@ void test_single_MC(csh *handle, int mc_mode, char *line) strcpy(tmp_mc, list_part[1]); replace_hex(tmp_mc); replace_negative(tmp_mc, mc_mode); + remove_char(tmp_mc, '#'); strcpy(tmp, insn[0].mnemonic); if (strlen(insn[0].op_str) > 0) { @@ -70,6 +82,7 @@ void test_single_MC(csh *handle, int mc_mode, char *line) strcpy(origin, tmp); replace_hex(tmp); replace_negative(tmp, mc_mode); + remove_char(tmp, '#'); if (cs_option(*handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_NOREGNAME) == CS_ERR_OK) { cs_disasm(*handle, code, size_byte, offset, 0, &insn); @@ -82,6 +95,7 @@ void test_single_MC(csh *handle, int mc_mode, char *line) trim_str(tmp_noreg); replace_hex(tmp_noreg); replace_negative(tmp_noreg, mc_mode); + remove_char(tmp_noreg, '#'); if (strcmp(tmp, tmp_mc) && strcmp(tmp_noreg, tmp_mc)) { fprintf(stderr, "[ ERROR ] --- %s --- \"%s\" != \"%s\" ( \"%s\" != \"%s\" and \"%s\" != \"%s\" )\n", list_part[0], origin, list_part[1], tmp, tmp_mc, tmp_noreg, tmp_mc); @@ -184,6 +198,9 @@ int set_function(int arch) case CS_ARCH_TRICORE: function = get_detail_tricore; break; + case CS_ARCH_SH: + function = get_detail_sh; + break; default: return -1; } @@ -246,6 +263,7 @@ void test_single_issue(csh *handle, cs_mode mode, char *line, int detail) } } + for (p = cs_result; *p; ++p) if (*p == '\t') *p = ' '; trim_str(cs_result); add_str(&cs_result, " ;"); // list_part_cs_result = split(cs_result, " ; ", &size_part_cs_result); diff --git a/suite/cstest/src/helper.c b/suite/cstest/src/helper.c index a70dca3d4f..bcb0e42dbd 100644 --- a/suite/cstest/src/helper.c +++ b/suite/cstest/src/helper.c @@ -157,9 +157,7 @@ void replace_negative(char *src, int mode) char *tmp, *result, *found, *origin, *orig_found; int cnt, valid; char *value, *tmp_tmp; - unsigned short int tmp_short; - unsigned int tmp_int; - unsigned long int tmp_long; + unsigned long long tmp_value; result = (char *)malloc(sizeof(char)); result[0] = '\0'; @@ -186,15 +184,13 @@ void replace_negative(char *src, int mode) tmp_tmp = strndup(tmp, orig_found - tmp); if (valid == 1) { *orig_found = '\0'; + tmp_value = strtoull(value, NULL, 0); if (mode == X86_16) { - sscanf(value, "%hu", &tmp_short); - add_str(&result, "%s%hu", tmp_tmp, tmp_short); + add_str(&result, "%s%hu", tmp_tmp, (unsigned short)tmp_value); } else if (mode == X86_32) { - sscanf(value, "%u", &tmp_int); - add_str(&result, "%s%u", tmp_tmp, tmp_int); + add_str(&result, "%s%u", tmp_tmp, (unsigned int)tmp_value); } else if (mode == X86_64) { - sscanf(value, "%lu", &tmp_long); - add_str(&result, "%s%lu", tmp_tmp, tmp_long); + add_str(&result, "%s%llu", tmp_tmp, tmp_value); } } else add_str(&result, "%s-", tmp_tmp); diff --git a/suite/cstest/src/main.c b/suite/cstest/src/main.c index 09591e1de2..d3745da8d0 100644 --- a/suite/cstest/src/main.c +++ b/suite/cstest/src/main.c @@ -21,6 +21,8 @@ static single_dict arches[] = { {"CS_ARCH_BPF", CS_ARCH_BPF}, {"CS_ARCH_RISCV", CS_ARCH_RISCV}, {"CS_ARCH_TRICORE", CS_ARCH_TRICORE}, + {"CS_ARCH_SH", CS_ARCH_SH}, + {"CS_ARCH_TMS320C64X", CS_ARCH_TMS320C64X}, }; static single_dict modes[] = { @@ -70,6 +72,13 @@ static single_dict arches[] = { {"CS_MODE_TRICORE_160", CS_MODE_TRICORE_160}, {"CS_MODE_TRICORE_161", CS_MODE_TRICORE_161}, {"CS_MODE_TRICORE_162", CS_MODE_TRICORE_162}, + {"CS_MODE_SH2", CS_MODE_SH2}, + {"CS_MODE_SH2A", CS_MODE_SH2A}, + {"CS_MODE_SH3", CS_MODE_SH3}, + {"CS_MODE_SH4", CS_MODE_SH4}, + {"CS_MODE_SH4A", CS_MODE_SH4A}, + {"CS_MODE_SHFPU", CS_MODE_SHFPU}, + {"CS_MODE_SHDSP", CS_MODE_SHDSP}, }; static double_dict options[] = { @@ -116,6 +125,7 @@ static single_dict arches[] = { {"CS_MODE_M680X_HCS08", CS_OPT_MODE, CS_MODE_M680X_HCS08}, {"CS_MODE_RISCV32", CS_OPT_MODE, CS_MODE_RISCV32}, {"CS_MODE_RISCV64", CS_OPT_MODE, CS_MODE_RISCV64}, + {"CS_MODE_RISCVC", CS_OPT_MODE, CS_MODE_RISCVC}, {"CS_MODE_TRICORE_110", CS_OPT_MODE, CS_MODE_TRICORE_110}, {"CS_MODE_TRICORE_120", CS_OPT_MODE, CS_MODE_TRICORE_120}, {"CS_MODE_TRICORE_130", CS_OPT_MODE, CS_MODE_TRICORE_130}, @@ -123,6 +133,13 @@ static single_dict arches[] = { {"CS_MODE_TRICORE_160", CS_OPT_MODE, CS_MODE_TRICORE_160}, {"CS_MODE_TRICORE_161", CS_OPT_MODE, CS_MODE_TRICORE_161}, {"CS_MODE_TRICORE_162", CS_OPT_MODE, CS_MODE_TRICORE_162}, + {"CS_MODE_SH2", CS_OPT_MODE, CS_MODE_SH2}, + {"CS_MODE_SH2A", CS_OPT_MODE, CS_MODE_SH2A}, + {"CS_MODE_SH3", CS_OPT_MODE, CS_MODE_SH3}, + {"CS_MODE_SH4", CS_OPT_MODE, CS_MODE_SH4}, + {"CS_MODE_SH4A", CS_OPT_MODE, CS_MODE_SH4A}, + {"CS_MODE_SHFPU", CS_OPT_MODE, CS_MODE_SHFPU}, + {"CS_MODE_SHDSP", CS_OPT_MODE, CS_MODE_SHDSP}, {"CS_OPT_UNSIGNED", CS_OPT_UNSIGNED, CS_OPT_ON}, }; @@ -135,6 +152,46 @@ static int getDetail; static int mc_mode; static int e_flag; +static int is_token_char(int c) +{ + return isalnum((unsigned char)c) || c == '_'; +} + +static int has_token(const char *tokens, const char *token) +{ + const char *p; + size_t len; + + len = strlen(token); + p = tokens; + while ((p = strstr(p, token)) != NULL) { + if ((p == tokens || !is_token_char((unsigned char)p[-1])) && + !is_token_char((unsigned char)p[len])) + return 1; + p += len; + } + return 0; +} + +static int get_mc_mode(const char *arch, const char *mode) +{ + if (!strcmp(arch, "CS_ARCH_ARM64")) + return X86_64; + if (!strcmp(arch, "CS_ARCH_X86")) { + if (has_token(mode, "CS_MODE_16")) + return X86_16; + if (has_token(mode, "CS_MODE_64")) + return X86_64; + } + if (!strcmp(arch, "CS_ARCH_MIPS") && has_token(mode, "CS_MODE_MIPS64")) + return X86_64; + if (!strcmp(arch, "CS_ARCH_PPC") && has_token(mode, "CS_MODE_64")) + return X86_64; + if (!strcmp(arch, "CS_ARCH_RISCV") && has_token(mode, "CS_MODE_RISCV64")) + return X86_64; + return X86_32; +} + static int setup_MC(void **state) { csh *handle; @@ -160,28 +217,12 @@ static int setup_MC(void **state) } arch = get_value(arches, ARR_SIZE(arches), list_params[0]); - if (!strcmp(list_params[0], "CS_ARCH_ARM64")) - mc_mode = 2; - else - mc_mode = 1; + mc_mode = get_mc_mode(list_params[0], list_params[1]); mode = 0; for (i = 0; i < ARR_SIZE(modes); ++i) { - if (strstr(list_params[1], modes[i].str)) { + if (has_token(list_params[1], modes[i].str)) { mode += modes[i].value; - switch (modes[i].value) { - case CS_MODE_16: - mc_mode = 0; - break; - case CS_MODE_64: - mc_mode = 2; - break; - case CS_MODE_THUMB: - mc_mode = 1; - break; - default: - break; - } } } @@ -199,7 +240,7 @@ static int setup_MC(void **state) } for (i = 0; i < ARR_SIZE(options); ++i) { - if (strstr(list_params[2], options[i].str)) { + if (has_token(list_params[2], options[i].str)) { if (cs_option(*handle, options[i].first_value, options[i].second_value) != CS_ERR_OK) { fprintf(stderr, "[ ERROR ] --- Option is not supported for this arch/mode\n"); failed_setup = 1; @@ -268,29 +309,12 @@ static int setup_issue(void **state) list_params = split(list_lines[counter] + 6, ", ", &size_params); arch = get_value(arches, ARR_SIZE(arches), list_params[0]); - - if (!strcmp(list_params[0], "CS_ARCH_ARM64")) - mc_mode = 2; - else - mc_mode = 1; + mc_mode = get_mc_mode(list_params[0], list_params[1]); mode = 0; for (i = 0; i < ARR_SIZE(modes); ++i) { - if (strstr(list_params[1], modes[i].str)) { + if (has_token(list_params[1], modes[i].str)) { mode += modes[i].value; - switch (modes[i].value) { - case CS_MODE_16: - mc_mode = 0; - break; - case CS_MODE_64: - mc_mode = 2; - break; - case CS_MODE_THUMB: - mc_mode = 1; - break; - default: - break; - } } } @@ -308,7 +332,7 @@ static int setup_issue(void **state) } for (i = 0; i < ARR_SIZE(options); ++i) { - if (strstr(list_params[2], options[i].str)) { + if (has_token(list_params[2], options[i].str)) { if (cs_option(*handle, options[i].first_value, options[i].second_value) != CS_ERR_OK) { fprintf(stderr, "[ ERROR ] --- Option is not supported for this arch/mode\n"); failed_setup = 1; diff --git a/suite/cstest/src/riscv_detail.c b/suite/cstest/src/riscv_detail.c index 70cc770e2d..13afb61e67 100644 --- a/suite/cstest/src/riscv_detail.c +++ b/suite/cstest/src/riscv_detail.c @@ -29,7 +29,7 @@ char *get_detail_riscv(csh *handle, cs_mode mode, cs_insn *ins) add_str(&result, " ; operands[%u].type: REG = %s", i, cs_reg_name(*handle, op->reg)); break; case RISCV_OP_IMM: - add_str(&result, " ; operands[%u].type: IMM = 0x%x", i, op->imm); + add_str(&result, " ; operands[%u].type: IMM = 0x%" PRIx64, i, op->imm); break; case RISCV_OP_MEM: add_str(&result, " ; operands[%u].type: MEM", i); @@ -44,4 +44,3 @@ char *get_detail_riscv(csh *handle, cs_mode mode, cs_insn *ins) return result; } - diff --git a/suite/cstest/src/sh_detail.c b/suite/cstest/src/sh_detail.c new file mode 100644 index 0000000000..b84a28d9ee --- /dev/null +++ b/suite/cstest/src/sh_detail.c @@ -0,0 +1,78 @@ +/* Capstone testing regression */ +/* By Do Minh Tuan , 02-2019 */ + + +#include "factory.h" + +static const char *get_mem_address_name(sh_op_mem_type address) +{ + switch (address) { + default: + break; + case SH_OP_MEM_REG_IND: + return "Register Indirect"; + case SH_OP_MEM_REG_POST: + return "Register Indirect with Postincrement"; + case SH_OP_MEM_REG_PRE: + return "Register Indirect with Predecrement"; + case SH_OP_MEM_REG_DISP: + return "Register Indirect with displacement"; + case SH_OP_MEM_REG_R0: + return "R0 indexed"; + case SH_OP_MEM_GBR_DISP: + return "GBR based displacement"; + case SH_OP_MEM_GBR_R0: + return "GBR based R0 indexed"; + case SH_OP_MEM_PCR: + return "PC relative"; + case SH_OP_MEM_TBR_DISP: + return "TBR based displacement"; + } + return "Invalid"; +} + +char *get_detail_sh(csh *handle, cs_mode mode, cs_insn *ins) +{ + cs_sh *sh; + int i; + char *result; + + result = (char *)malloc(sizeof(char)); + result[0] = '\0'; + + if (ins->detail == NULL) + return result; + + sh = &(ins->detail->sh); + if (sh->op_count) + add_str(&result, " ; op_count: %u", sh->op_count); + + for (i = 0; i < sh->op_count; i++) { + cs_sh_op *op = &(sh->operands[i]); + switch ((int)op->type) { + default: + break; + case SH_OP_REG: + add_str(&result, " ; operands[%u].type: REG = %s", i, + cs_reg_name(*handle, op->reg)); + break; + case SH_OP_IMM: + add_str(&result, " ; operands[%u].type: IMM = 0x%llx", i, + (unsigned long long)op->imm); + break; + case SH_OP_MEM: + add_str(&result, " ; operands[%u].type: MEM", i); + if (op->mem.reg != SH_REG_INVALID) + add_str(&result, " ; operands[%u].mem.reg: REG = %s", + i, cs_reg_name(*handle, op->mem.reg)); + if (op->mem.disp != 0) + add_str(&result, " ; operands[%u].mem.disp: 0x%x", + i, op->mem.disp); + add_str(&result, " ; address mode: %s", + get_mem_address_name(op->mem.address)); + break; + } + } + + return result; +} diff --git a/tests/test_arm64.c b/tests/test_arm64.c index f4651fc599..651434286a 100644 --- a/tests/test_arm64.c +++ b/tests/test_arm64.c @@ -248,11 +248,11 @@ void test_macros() { detail.arm64 = arm64_detail; CS_aarch64_op() op = { 0 }; detail.CS_aarch64_.operands[0] = op; - CS_aarch64_reg() reg = 1; - CS_aarch64_cc() cc = ARM64_CC_AL; - CS_aarch64_extender() arm64_extender = ARM64_EXT_SXTB; - CS_aarch64_shifter() arm64_shifter = ARM64_SFT_LSL; - CS_aarch64_vas() arm64_vas = ARM64_VAS_16B; + (void)(CS_aarch64_reg())1; + (void)(CS_aarch64_cc())ARM64_CC_AL; + (void)(CS_aarch64_extender())ARM64_EXT_SXTB; + (void)(CS_aarch64_shifter())ARM64_SFT_LSL; + (void)(CS_aarch64_vas())ARM64_VAS_16B; } int main() @@ -262,4 +262,3 @@ int main() return 0; } - diff --git a/toolchain.cmake b/toolchain.cmake new file mode 100644 index 0000000000..4ab2bcdd96 --- /dev/null +++ b/toolchain.cmake @@ -0,0 +1,41 @@ +if("$ENV{system}" STREQUAL "") + message(FATAL_ERROR "toolchain.cmake requires the system environment variable") +endif() +if("$ENV{processor}" STREQUAL "") + message(FATAL_ERROR "toolchain.cmake requires the processor environment variable") +endif() +if("$ENV{cc}" STREQUAL "") + message(FATAL_ERROR "toolchain.cmake requires the cc environment variable") +endif() +if("$ENV{cxx}" STREQUAL "") + message(FATAL_ERROR "toolchain.cmake requires the cxx environment variable") +endif() + +set(CMAKE_SYSTEM_NAME "$ENV{system}") +set(CMAKE_SYSTEM_PROCESSOR "$ENV{processor}") +set(CMAKE_C_COMPILER "$ENV{cc}") +set(CMAKE_CXX_COMPILER "$ENV{cxx}") +set(CMAKE_ASM_COMPILER "$ENV{cc}") + +if(NOT "$ENV{rc}" STREQUAL "") + set(CMAKE_RC_COMPILER "$ENV{rc}") +endif() + +if(NOT "$ENV{find_root}" STREQUAL "") + set(CMAKE_FIND_ROOT_PATH "$ENV{find_root}") +endif() + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +if("$ENV{system}" STREQUAL "Linux") + if("$ENV{sysroot}" STREQUAL "") + message(FATAL_ERROR "toolchain.cmake requires the sysroot environment variable for Linux") + endif() + if("$ENV{qemu}" STREQUAL "") + message(FATAL_ERROR "toolchain.cmake requires the qemu environment variable for Linux") + endif() + set(CMAKE_SYSROOT "$ENV{sysroot}/usr") + set(CMAKE_CROSSCOMPILING_EMULATOR "$ENV{qemu}" "-L" "$ENV{sysroot}") +endif()