diff --git a/.azure-pipeline.yml b/.azure-pipeline.yml new file mode 100644 index 0000000000..ce96652d2f --- /dev/null +++ b/.azure-pipeline.yml @@ -0,0 +1,126 @@ +trigger: + branches: + include: + - master + +pr: + branches: + include: + - '*' + +strategy: + matrix: + Linux amd64 GCC: + VM_IMAGE: 'ubuntu-22.04' + SDK_PLATFORM: linux-amd64 + APT_PACKAGES: g++-10 + SCONS_BUILD_ARGS: platform=x86-64 --mode=opt-host,nacl --no-clang werror=0 + SCONS_TESTS: small_tests medium_tests large_tests + VPYTHON: pyenv/bin/python + Linux i686 GCC: + VM_IMAGE: 'ubuntu-22.04' + SDK_PLATFORM: linux-amd64 + APT_PACKAGES: g++-i686-linux-gnu + SCONS_BUILD_ARGS: platform=x86-32 --mode=opt-host,nacl --no-clang werror=0 + VPYTHON: pyenv/bin/python + Linux armhf GCC: + # There is an IO bug in qemu-arm from ubuntu-22.04. + VM_IMAGE: 'ubuntu-24.04' + SDK_PLATFORM: arm-xxx + APT_PACKAGES: g++-arm-linux-gnueabihf qemu-user + SCONS_BUILD_ARGS: platform=arm --mode=opt-host,nacl --no-clang werror=0 + SCONS_TESTS: no_gdb_tests=1 small_tests medium_tests + VPYTHON: pyenv/bin/python + Linux amd64 Clang: + VM_IMAGE: 'ubuntu-22.04' + SDK_PLATFORM: linux-amd64 + SCONS_BUILD_ARGS: platform=x86-64 --mode=opt-host,nacl --clang werror=1 + SCONS_TESTS: small_tests medium_tests large_tests + VPYTHON: pyenv/bin/python + macOS amd64 AppleClang: + VM_IMAGE: 'macOS-15' + SDK_PLATFORM: macos-amd64 + SCONS_BUILD_ARGS: platform=x86-64 --mode=opt-host,nacl --clang werror=1 + SCONS_TESTS: all_tests + VPYTHON: pyenv/bin/python + NPROC_COMMAND: sysctl -n hw.logicalcpu + Windows amd64 MSVC: + VM_IMAGE: 'windows-2025' + SDK_PLATFORM: windows-amd64 + SCONS_BUILD_ARGS: platform=x86-64 --mode=opt-host,nacl --no-clang werror=1 mingw_dir=mingw/mingw + SCONS_TESTS: small_tests medium_tests large_tests disable_tests=run_toolchain_python_tests + VPYTHON: pyenv/Scripts/python + PYTHON_PACKAGES: pywin32 + MINGW_URL: https://github.com/niXman/mingw-builds-binaries/releases/download/16.1.0-rt_v14-rev1/x86_64-16.1.0-release-posix-seh-msvcrt-rt_v14-rev1.7z + Windows i686 MSVC: + VM_IMAGE: 'windows-2025' + SDK_PLATFORM: windows-amd64 + SCONS_BUILD_ARGS: platform=x86-32 --mode=opt-host,nacl --no-clang werror=1 mingw_dir=mingw/mingw + VPYTHON: pyenv/Scripts/python + PYTHON_PACKAGES: pywin32 + MINGW_URL: https://github.com/niXman/mingw-builds-binaries/releases/download/16.1.0-rt_v14-rev1/i686-16.1.0-release-posix-dwarf-msvcrt-rt_v14-rev1.7z + Windows amd64 MinGW: + VM_IMAGE: 'windows-2025' + SDK_PLATFORM: windows-amd64 + SCONS_BUILD_ARGS: platform=x86-64 --mode=opt-host,nacl --no-clang mingw=1 werror=1 mingw_dir=mingw/mingw + SCONS_TESTS: small_tests medium_tests large_tests disable_tests=run_toolchain_python_tests + VPYTHON: pyenv/Scripts/python + PYTHON_PACKAGES: pywin32 + MINGW_URL: https://github.com/niXman/mingw-builds-binaries/releases/download/16.1.0-rt_v14-rev1/x86_64-16.1.0-release-posix-seh-msvcrt-rt_v14-rev1.7z + +pool: + vmImage: $(VM_IMAGE) + +steps: +- bash: | + set -xue + CURL() { + curl -fsSL --retry 8 "$@" + } + if [ -n "${APT_ARCHITECTURE:-}" ]; then + sudo dpkg --add-architecture "${APT_ARCHITECTURE}" + fi + if [ -n "${APT_PACKAGES:-}" ]; then + sudo apt-get update && sudo apt-get -y -q --no-install-recommends install ${APT_PACKAGES} + fi + python3 -m venv pyenv + # TODO pin scons to a specific version? + $(VPYTHON) -m pip install SCons ${PYTHON_PACKAGES:-} + mkdir sdk + ( + cd sdk + if [ "$(SDK_PLATFORM)" = "arm-xxx" ]; then + sdk_url='https://dl.illwieckz.net/b/saigo/preview/saigo_newlib_20241119.txz' + else + sdk_url='https://github.com/DaemonEngine/saigo-nacl-sdk/releases/download/v21.0-20260805/saigosdk-$(SDK_PLATFORM)_21.0-20260805.tar.xz' + fi + CURL "$sdk_url" -o sdk.tar.xz + tar -xJf sdk.tar.xz + rm sdk.tar.xz + mv * sdk # Rename the top-level dir to sdk + ) + if [ -n "${MINGW_URL:-}" ]; then + mkdir mingw + ( + cd mingw + CURL $(MINGW_URL) -o mingw.7z + 7z x mingw.7z + rm mingw.7z + mv * mingw # Rename the top-level dir to mingw + ) + fi + displayName: 'Setup' + +- bash: | + set -xue + parallelism="$(${NPROC_COMMAND:-nproc})" + echo "Build parallelism: ${parallelism}" + $(VPYTHON) -m SCons saigo=1 saigo_newlib_dir=sdk/sdk ${SCONS_BUILD_ARGS} all_programs -j${parallelism} --verbose + displayName: 'Build' + +- bash: | + set -xue + parallelism="$(${NPROC_COMMAND:-nproc})" + $(VPYTHON) -m SCons saigo=1 saigo_newlib_dir=sdk/sdk ${SCONS_BUILD_ARGS} ${SCONS_TESTS} -j${parallelism} --verbose --keep-going disable_flaky_tests=1 + condition: and(succeeded(), ne(variables['SCONS_TESTS'], '')) + displayName: 'Test' diff --git a/SConstruct b/SConstruct index a579737e29..6e0754f909 100755 --- a/SConstruct +++ b/SConstruct @@ -388,7 +388,7 @@ def SetUpArgumentBits(env): 'to run the specified test(s) without actually running them. This ' 'argument is a counterpart to built_elsewhere.') - BitFromArgument(env, 'no_gdb_tests', default=True, + BitFromArgument(env, 'no_gdb_tests', default=env.Bit('host_mac_arm64'), desc='Prevents GDB tests from running. If GDB is not available, you can ' 'test everything else by specifying this flag.') diff --git a/pynacl/platform.py b/pynacl/platform.py index ca637936d5..b0e1ec60d4 100644 --- a/pynacl/platform.py +++ b/pynacl/platform.py @@ -200,7 +200,7 @@ def PlatformTripleSaigo(platform=None, machine=None): def KillSubprocessAndChildren(proc): """Kill a subprocess and all children. - While this is trivial on Posix platforms, on Windows this requires some + On Windows this requires some method for walking the process tree. Relying on this functionality in the taskkill.exe utility for now. @@ -210,8 +210,10 @@ def KillSubprocessAndChildren(proc): if IsWindows(): # Do subprocess call as the process may terminate before we manage # to invoke taskkill. + # TODO: use job object instead? subprocess.call( [os.path.join(os.environ['SYSTEMROOT'], 'System32', 'taskkill.exe'), '/F', '/T', '/PID', str(proc.pid)]) else: + # TODO: implement for *nix. This kills only the one process proc.kill() diff --git a/run.py b/run.py index 30fc773c69..dcfa67eee5 100755 --- a/run.py +++ b/run.py @@ -470,6 +470,9 @@ def Run(args, cwd=None, verbose=True, exit_on_failure=False, # PNaCl toolchain executables (pnacl-translate, readelf) are scripts # not binaries, so it doesn't want to run on Windows without a shell. use_shell = True if pynacl.platform.IsWindows() else False + if use_shell: + args = args[:] + args[0] = os.path.normpath(args[0]) # Must use \ not / p = subprocess.Popen(args, stdin=stdin_redir, stdout=stdout_redir, stderr=stderr_redir, cwd=cwd, shell=use_shell, encoding='utf-8') @@ -590,7 +593,7 @@ def FindReadElf(): # Look for Saigo or PNaCl readelf or the system one # The architecture the toolchain was built for generally doesn't matter. - readelves = ['x86_64-nacl-readelf', 'pnacl-readelf', 'readelf'] + readelves = ['nacl-readelf', 'x86_64-nacl-readelf', 'pnacl-readelf', 'readelf'] toolchain_paths = [os.path.join(env.saigo_base, 'bin'), os.path.join(env.pnacl_base, 'bin')] diff --git a/site_scons/site_tools/naclsdk.py b/site_scons/site_tools/naclsdk.py index 28ad4aa639..2777a9e637 100755 --- a/site_scons/site_tools/naclsdk.py +++ b/site_scons/site_tools/naclsdk.py @@ -112,6 +112,13 @@ def _SetEnvForNativeSdk(env, sdk_path): cc = 'clang' if env.Bit('nacl_clang') else 'gcc' cxx = 'clang++' if env.Bit('nacl_clang') else 'g++' + def FindRenamedTool(tool): + # For ones renamed in https://github.com/DaemonEngine/saigo-nacl-sdk/releases/tag/v21.0-20260805 + newname = os.path.join(bin_path, 'nacl-' + tool) + if os.path.exists(newname) or os.path.exists(newname + '.exe'): + return newname + return os.path.join(bin_path, '%s-%s' % (tool_prefix, tool)) + env.Replace(# Replace header and lib paths. # where to put nacl extra sdk headers # TODO(robertm): switch to using the mechanism that @@ -125,18 +132,17 @@ def _SetEnvForNativeSdk(env, sdk_path): AR=os.path.join(bin_path, '%s-ar' % tool_prefix), AS=os.path.join(bin_path, '%s-as' % tool_prefix), ASPP=os.path.join(bin_path, '%s-%s' % (tool_prefix, cc)), - FILECHECK=os.path.join(bin_path, 'FileCheck'), - GDB=os.path.join(bin_path, '%s-gdb' % tool_prefix), + GDB=os.path.join(bin_path, 'nacl-gdb'), # NOTE: use g++ for linking so we can handle C AND C++. LINK=os.path.join(bin_path, '%s-%s' % (tool_prefix, cxx)), # Grrr... and sometimes we really need ld. LD=os.path.join(bin_path, '%s-ld' % tool_prefix) + ld_mode_flag, - RANLIB=os.path.join(bin_path, '%s-ranlib' % tool_prefix), - NM=os.path.join(bin_path, '%s-nm' % tool_prefix), - OBJDUMP=os.path.join(bin_path, '%s-objdump' % tool_prefix), - OBJCOPY=os.path.join(bin_path, '%s-objcopy' % tool_prefix), + RANLIB=FindRenamedTool('ranlib'), + NM=FindRenamedTool('nm'), + OBJDUMP=FindRenamedTool('objdump'), + OBJCOPY='false', STRIP=os.path.join(bin_path, '%s-strip' % tool_prefix), - ADDR2LINE=os.path.join(bin_path, '%s-addr2line' % tool_prefix), + ADDR2LINE='false', BASE_LINKFLAGS=[cc_mode_flag], BASE_CFLAGS=[cc_mode_flag], BASE_CXXFLAGS=[cc_mode_flag], @@ -755,21 +761,6 @@ def FakeInstall(dest, source, env): else: _SetEnvForNativeSdk(env, root) - # Daemon: don't depend on a second NaCl toolchain! - if (env.Bit('bitcode') or env.Bit('nacl_clang')) and env.Bit('build_x86') and \ - not env.Bit('no_gdb_tests') and 'nacl_gdb' not in SCons.Script.ARGUMENTS: - # Get GDB from the nacl-gcc glibc toolchain even when using PNaCl. - # TODO(mseaborn): We really want the nacl-gdb binary to be in a - # separate tarball from the nacl-gcc toolchain, then this step - # will not be necessary. - # See http://code.google.com/p/nativeclient/issues/detail?id=2773 - temp_env = env.Clone() - temp_env.ClearBits('bitcode', 'nacl_clang', 'saigo') - temp_env.SetBits('nacl_glibc') - temp_root = temp_env.GetToolchainDir() - _SetEnvForNativeSdk(temp_env, temp_root) - env.Replace(GDB=temp_env['GDB']) - env.Prepend(LIBPATH='${NACL_SDK_LIB}') # Install our scanner for (potential) linker scripts. diff --git a/src/trusted/platform_qualify/build.scons b/src/trusted/platform_qualify/build.scons index c3f867d003..4afcf85534 100644 --- a/src/trusted/platform_qualify/build.scons +++ b/src/trusted/platform_qualify/build.scons @@ -63,7 +63,7 @@ if env.Bit('build_x86') and env.Bit('build_x86'): node = env.CommandTest('platform_qual_test.out', [platform_qual_test]) env.AddNodeToTestSuite( node, ['small_tests'], 'run_platform_qual_test', - is_broken=env.IsRunningUnderValgrind()) + is_broken=env.IsRunningUnderValgrind() or env.Bit('mingw')) cpuallowlist_test = env.ComponentProgram( 'cpuallowlist_test', 'arch/x86/nacl_cpuallowlist_test.c', diff --git a/src/trusted/service_runtime/build.scons b/src/trusted/service_runtime/build.scons index d5bdbb7cda..5f5d653188 100644 --- a/src/trusted/service_runtime/build.scons +++ b/src/trusted/service_runtime/build.scons @@ -533,7 +533,7 @@ hello_world_nexe = untrusted_env.File('$STAGING_DIR/hello_world.nexe') # Doesn't work on windows under coverage. # TODO(bradnelson): fix this to work on windows under coverage. if ((not env.Bit('windows') or not env.Bit('coverage_enabled')) and - env.Bit('nacl_static_link')): + env.Bit('nacl_static_link') and UsingNaclMode()): # NOTE: uses validator mmap_test_objs = [env.ComponentObject('mmap_test.c')] mmap_test_exe = env.ComponentProgram( @@ -666,7 +666,7 @@ if env.Bit('windows') and env.Bit('build_x86_64'): node = env.CommandTest( 'ntdll_fallback_test.out', command=[intercept_test_prog, 'test_fallback'], - exit_status='untrusted_segfault', + exit_status=0xC0000409, stdout_golden=env.File('win/exception_patch/fallback_test.stdout')) env.AddNodeToTestSuite(node, ['small_tests'], 'run_ntdll_fallback_test') @@ -759,17 +759,18 @@ node = env.CommandSelLdrTestNacl( exit_status='1') env.AddNodeToTestSuite(node, ['small_tests'], 'run_sel_ldr_exe_not_found_test') -# Check that "-F" makes sel_ldr stop after loading the nexe but before running -# it. -nullptr_nexe = untrusted_env.GetTranslatedNexe( - untrusted_env.File('$STAGING_DIR/nullptr$PROGSUFFIX')) +if UsingNaclMode(): + # Check that "-F" makes sel_ldr stop after loading the nexe but before running + # it. + nullptr_nexe = untrusted_env.GetTranslatedNexe( + untrusted_env.File('$STAGING_DIR/nullptr$PROGSUFFIX')) -node = env.CommandSelLdrTestNacl( - 'fuzz_nullptr_test.out', - nullptr_nexe, - size='large', - sel_ldr_flags=['-F']) -env.AddNodeToTestSuite(node, ['large_tests'], 'run_fuzz_nullptr_test') + node = env.CommandSelLdrTestNacl( + 'fuzz_nullptr_test.out', + nullptr_nexe, + size='large', + sel_ldr_flags=['-F']) + env.AddNodeToTestSuite(node, ['large_tests'], 'run_fuzz_nullptr_test') if env.Bit('build_mips32'): text_region_start = 0x00020000 @@ -836,7 +837,7 @@ if env.Bit('build_x86_64'): ) env.AddNodeToTestSuite(node, ['small_tests'], 'run_hello_x32_test') -if env.Bit('build_x86') and env.Bit('nacl_static_link'): +if env.Bit('build_x86') and env.Bit('nacl_static_link') and UsingNaclMode(): RE_HELLO = '^(Hello, World!)$' RE_IDENT = r'^\[[0-9,:.]*\] (e_ident\+1 = ELF)$' diff --git a/src/trusted/validator_ragel/build.scons b/src/trusted/validator_ragel/build.scons index 0ecf5b70b1..9a93b539bc 100644 --- a/src/trusted/validator_ragel/build.scons +++ b/src/trusted/validator_ragel/build.scons @@ -545,7 +545,7 @@ for bits in ['32', '64']: '--bits', bits, tests_mask] + update_option) - env.AddNodeToTestSuite( + if UsingNaclMode(): env.AddNodeToTestSuite( dis_section_test, ['small_tests', 'validator_tests'], node_name='run_dis_section_test_%s' % bits) diff --git a/tests/app_lib/.gitattributes b/tests/app_lib/.gitattributes new file mode 100644 index 0000000000..b2e75b19cf --- /dev/null +++ b/tests/app_lib/.gitattributes @@ -0,0 +1,2 @@ +app_lib_test.stdin eol=lf +app_lib_test.stdout eol=lf diff --git a/tests/gdb/gdb_test.py b/tests/gdb/gdb_test.py index e2f98a9da6..0a2425c79a 100644 --- a/tests/gdb/gdb_test.py +++ b/tests/gdb/gdb_test.py @@ -3,6 +3,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import atexit import json import optparse import os @@ -205,6 +206,7 @@ def __init__(self, options, name): self._gdb = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) + atexit.register(self.KillProcess) self._expected_success = True def Wait(self): @@ -289,6 +291,7 @@ def Kill(self): def KillProcess(self): self._expected_success = False KillProcess(self._gdb) + atexit.unregister(self.KillProcess) def Eval(self, expression): return self.Command('-data-evaluate-expression ' + expression)[b'value'] diff --git a/tests/gdb/nacl.scons b/tests/gdb/nacl.scons index 2ef028f0f8..7919ea7b93 100644 --- a/tests/gdb/nacl.scons +++ b/tests/gdb/nacl.scons @@ -17,24 +17,6 @@ if env.UnderWindowsCoverage(): if 'nacl_gdb' in SCons.Script.ARGUMENTS: env.Replace(GDB=SCons.Script.ARGUMENTS['nacl_gdb']) -elif env.Bit('build_arm') or env.Bit('build_mips32'): - if env.UsingEmulator(): - # nacl-gdb is built with ARM support but not MIPS support. - if env.Bit('build_mips32'): - Return() - nacl_x86_toolchain_dir = env.GetToolchainDir(target_arch='x86', - is_pnacl=False, - lib_name='glibc') - nacl_gdb_path = os.path.join(nacl_x86_toolchain_dir, 'bin', 'i686-nacl-gdb') - env.Replace(GDB=nacl_gdb_path) - else: - # Use the system's ARM/MIPS GDB because the NaCl toolchain does not - # include a copy of GDB built to run on ARM/MIPS. - env.Replace(GDB='gdb') - # Unlike nacl-gdb, the system version of GDB does not support the - # "nacl-irt" command. - if env.Bit('tests_use_irt'): - Return() # Disable finalization (which would strip debug metadata), but allow # the sandbox translator where it makes sense. @@ -123,6 +105,10 @@ def AddGdbTest(name, is_broken=False, is_thread_test=False): # is created after continuing (https://github.com/DaemonEngine/native_client/issues/57). no_step = env.Bit('build_arm') or env.Bit('build_mips32') +# Currently GDB builds lack XML support which is needed for some ARM register +# info - see https://github.com/DaemonEngine/saigo-nacl-sdk/issues/9 +no_xml = env.Bit('build_arm') and env.Bit('saigo') + AddGdbTest('complete') AddGdbTest('detach') @@ -133,7 +119,7 @@ using_clang = env.Bit('bitcode') or (env.Bit('nacl_clang') and not env.Bit('saig # https://code.google.com/p/nativeclient/issues/detail?id=4059 AddGdbTest('invalid_memory', - is_broken = (using_clang and not env.UsingEmulator())) + is_broken = no_xml or (using_clang and not env.UsingEmulator())) AddGdbTest('kill') AddGdbTest('remote_get') @@ -147,7 +133,7 @@ AddGdbTest('print_symbol', is_broken=no_step) # TODO(mseaborn): Investigate and enable this test. # http://code.google.com/p/nativeclient/issues/detail?id=3252 AddGdbTest('stack_trace', - is_broken=using_clang and env.Bit('build_arm')) + is_broken=no_xml or (using_clang and env.Bit('build_arm'))) AddGdbTest('step_from_func_start', is_broken=no_step) diff --git a/tests/threads/nacl.scons b/tests/threads/nacl.scons index 69ffc2dec1..b002ece53a 100644 --- a/tests/threads/nacl.scons +++ b/tests/threads/nacl.scons @@ -41,6 +41,7 @@ node = env.CommandSelLdrTestNacl( # NOTE: this should be a pretty slow test, but its been sped up # to not tickle bug 853 env.AddNodeToTestSuite(node, ['small_tests'], 'run_thread_test', + is_flaky=env.UsingEmulator(), # TODO(khim): reenable it when cause of failure on 32bit Windows glibc # will be found. # See: http://code.google.com/p/nativeclient/issues/detail?id=1690 diff --git a/tools/test_lib.py b/tools/test_lib.py index 23739369d9..5e35504b4e 100755 --- a/tools/test_lib.py +++ b/tools/test_lib.py @@ -115,29 +115,27 @@ def CommunicateWithTimeout(proc, input_data=None, timeout=None): if timeout == 0: timeout = None - result = [] - def Target(): - result.append(list(proc.communicate(input_data))) - - thread = threading.Thread(target=Target) - thread.start() - thread.join(timeout) - if thread.is_alive(): + try: + out, err = proc.communicate(input_data, timeout) + except subprocess.TimeoutExpired: sys.stderr.write('\nAttempting to kill test due to timeout!\n') - # This will kill the process which should force communicate to return with - # any partial output. - pynacl.platform.KillSubprocessAndChildren(proc) - # Thus result should ALWAYS contain something after this join. - thread.join() + try: + if pynacl.platform.IsWindows(): + raise # Skip to the kill all part + proc.send_signal(signal.SIGINT) + out, err = result = proc.communicate(timeout=3) + except subprocess.TimeoutExpired: + sys.stderr.write('\nForcibly killing test due to timeout!\n') + pynacl.platform.KillSubprocessAndChildren(proc) + out, err = proc.communicate() msg = '\n\nKilled test due to timeout!\n' sys.stderr.write(msg) # Also append to stderr. - result[0][1] += (msg.encode('ascii') if isinstance(result[0][1], bytes) else msg) + err += (msg.encode('ascii') if isinstance(err, bytes) else msg) returncode = -9 else: returncode = proc.returncode - assert len(result) == 1 - return tuple(result[0]) + (returncode,) + return out, err, returncode def RunTestWithInput(cmd, input_data, timeout=None):