├── .github └── workflows │ ├── build-extension.yml │ ├── cache.yml │ └── ci.yml ├── .gitignore ├── .gitmodules ├── AMBuildScript ├── README.md ├── buildbot ├── BreakpadSymbols ├── PackageScript ├── Versioning └── pushbuild.txt ├── configure.py ├── dockerbuild ├── .gitkeep ├── _accelerator_docker_build_internal.sh ├── _github_actions_setup.sh └── accelerator_docker_build.sh ├── extension ├── AMBuilder ├── MemoryDownloader.cpp ├── MemoryDownloader.h ├── accelerator.autoload ├── extension.cpp ├── extension.h ├── smsdk_config.h ├── version.h └── version.rc ├── gamedata └── accelerator.games.txt ├── patches ├── 0001-Ignore-invalid-modules-rather-than-bailing-on-the-en.patch ├── 0002-Write-FUNC-records-instead-of-PUBLIC-for-ELF-symbols.patch ├── 0003-Support-compilation-on-VS-2015.patch ├── 0004-Fix-Linux-a-out-include.patch └── 0005-Add-LSS.patch ├── product.version ├── third_party ├── AMBuilder ├── Configure ├── Patch └── cwd_cmd.py └── upload.py /.github/workflows/build-extension.yml: -------------------------------------------------------------------------------- 1 | name: Build extension 2 | on: 3 | workflow_call: 4 | inputs: 5 | os: 6 | type: string 7 | required: true 8 | cc: 9 | type: string 10 | required: true 11 | cxx: 12 | type: string 13 | upload: 14 | type: boolean 15 | required: false 16 | default: false 17 | upload-artifact-name: 18 | type: string 19 | required: false 20 | default: package 21 | debug: 22 | type: boolean 23 | required: false 24 | default: false 25 | cache-key: 26 | type: string 27 | required: true 28 | cache-dir: 29 | type: string 30 | required: true 31 | 32 | jobs: 33 | build: 34 | name: Accelerator Extension ${{ inputs.os }}-${{ inputs.cc }} 35 | runs-on: ${{ inputs.os }} 36 | 37 | env: 38 | SOURCEMOD: ${{ github.workspace }}/${{ inputs.cache-dir }}/sourcemod 39 | steps: 40 | - uses: actions/checkout@v4 41 | with: 42 | fetch-depth: 0 43 | submodules: true 44 | 45 | - name: Retrieve the cache 46 | id: cache 47 | uses: actions/cache@v4 48 | with: 49 | path: ${{ inputs.cache-dir }} 50 | key: ${{ inputs.cache-key }} 51 | fail-on-cache-miss: true 52 | enableCrossOsArchive: true 53 | 54 | - name: Linux dependencies 55 | if: startsWith(runner.os, 'Linux') 56 | run: | 57 | sudo dpkg --add-architecture i386 58 | sudo apt-get update 59 | sudo apt-get install -y --no-install-recommends \ 60 | gcc-multilib g++-multilib libstdc++6 lib32stdc++6 \ 61 | libc6-dev libc6-dev-i386 linux-libc-dev \ 62 | linux-libc-dev:i386 lib32z1-dev \ 63 | zlib1g-dev:i386 zlib1g-dev ${{ inputs.cc }} 64 | 65 | - name: Select clang compiler 66 | if: startsWith(runner.os, 'Linux') 67 | run: | 68 | echo "CC=${{ inputs.cc }}" >> $GITHUB_ENV 69 | echo "CXX=${{ inputs.cxx }}" >> $GITHUB_ENV 70 | ${{ inputs.cc }} --version 71 | ${{ inputs.cxx }} --version 72 | 73 | - uses: actions/setup-python@v5 74 | name: Setup Python 3.10 75 | with: 76 | python-version: '3.10' 77 | 78 | - name: Install AMBuild 79 | run: | 80 | python -m pip install --upgrade pip setuptools wheel 81 | pip install ./${{ inputs.cache-dir }}/ambuild 82 | 83 | - name: Build (Debug) 84 | if: ${{ inputs.debug }} 85 | run: | 86 | mkdir -p build && cd build 87 | python ../configure.py --enable-debug 88 | ambuild 89 | 90 | - name: Build (Release) 91 | if: not ${{ inputs.debug }} 92 | run: | 93 | mkdir -p build && cd build 94 | python ../configure.py --enable-optimize 95 | ambuild 96 | 97 | - name: Upload package 98 | if: ${{ inputs.upload }} 99 | uses: actions/upload-artifact@v4 100 | with: 101 | name: ${{ inputs.upload-artifact-name }} 102 | path: build/package -------------------------------------------------------------------------------- /.github/workflows/cache.yml: -------------------------------------------------------------------------------- 1 | name: Cache 2 | on: 3 | workflow_call: 4 | outputs: 5 | key: 6 | value: ${{ jobs.cache.outputs.key }} 7 | dir: 8 | value: .cache 9 | 10 | jobs: 11 | cache: 12 | name: Create cache 13 | runs-on: 'ubuntu-latest' 14 | outputs: 15 | key: ${{ steps.cache-key.outputs.key }} 16 | 17 | env: 18 | SOURCEMOD_REF: 'e0e018c5ebcd11df9bab91b43d40bf148805c684' 19 | AMBUILD_REF: '2d4620da4cdaf89cf25afc3577f920e6138ae7cd' 20 | steps: 21 | - uses: actions/checkout@v4 22 | 23 | - name: Create cache key 24 | run: | 25 | echo "${{ env.SOURCEMOD_REF }}" >> cache-key 26 | echo "${{ env.AMBUILD_REF }}" >> cache-key 27 | 28 | - name: Output cache key 29 | id: cache-key 30 | run: | 31 | ls -a 32 | echo "key=cache-${{ hashFiles('cache-key') }}" >> $GITHUB_OUTPUT 33 | 34 | - name: Retrieve the cache 35 | id: cache 36 | uses: actions/cache@v4 37 | with: 38 | path: .cache 39 | key: ${{ steps.cache-key.outputs.key }} 40 | enableCrossOsArchive: true 41 | 42 | - uses: actions/checkout@v4 43 | if: ${{ !steps.cache.outputs.cache-hit }} 44 | name: SourceMod checkout 45 | with: 46 | repository: alliedmodders/sourcemod 47 | ref: ${{ env.SOURCEMOD_REF }} 48 | submodules: true 49 | path: .cache/sourcemod 50 | 51 | - uses: actions/checkout@v4 52 | if: ${{ !steps.cache.outputs.cache-hit }} 53 | name: AMBuild checkout 54 | with: 55 | repository: alliedmodders/ambuild 56 | ref: ${{ env.AMBUILD_REF }} 57 | path: .cache/ambuild -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | workflow_dispatch: 4 | pull_request: 5 | branches: 6 | - master 7 | - overhaul 8 | 9 | push: 10 | branches: 11 | - master 12 | - overhaul 13 | 14 | jobs: 15 | cache: 16 | uses: ./.github/workflows/cache.yml 17 | 18 | build: 19 | name: Build 20 | needs: cache 21 | 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | os: [ 'ubuntu-22.04', 'ubuntu-latest', 'windows-2019', 'windows-latest' ] 26 | include: 27 | # we need to ship ubuntu 22.04 because of glibc reasons 28 | - os: ubuntu-22.04 29 | #cc: clang-8 30 | #cxx: clang++-8 31 | cc: clang 32 | cxx: clang++ 33 | upload: true 34 | upload-artifact-name: accelerator_linux 35 | - os: ubuntu-latest 36 | cc: clang 37 | cxx: clang++ 38 | upload: false 39 | upload-artifact-name: none 40 | - os: windows-2019 41 | cc: msvc 42 | cxx: msvc 43 | upload: false 44 | upload-artifact-name: none 45 | - os: windows-latest 46 | cc: msvc 47 | cxx: msvc 48 | upload: true 49 | upload-artifact-name: accelerator_windows 50 | 51 | uses: ./.github/workflows/build-extension.yml 52 | with: 53 | os: ${{ matrix.os }} 54 | cc: ${{ matrix.cc }} 55 | cxx: ${{ matrix.cxx }} 56 | upload: ${{ matrix.upload }} 57 | upload-artifact-name: ${{ matrix.upload-artifact-name }} 58 | cache-key: ${{ needs.cache.outputs.key }} 59 | cache-dir: ${{ needs.cache.outputs.dir }} 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /extension/version_auto.h 3 | .vscode -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "breakpad"] 2 | path = third_party/breakpad 3 | url = https://github.com/google/breakpad.git 4 | [submodule "zlib"] 5 | path = third_party/zlib 6 | url = https://github.com/madler/zlib.git 7 | -------------------------------------------------------------------------------- /AMBuildScript: -------------------------------------------------------------------------------- 1 | # vim: set ts=2 sw=2 tw=99 noet ft=python: 2 | import os, sys, shutil 3 | 4 | def ResolveEnvPath(env, folder=None): 5 | if env in os.environ: 6 | path = os.environ[env] 7 | if os.path.isdir(path): 8 | return path 9 | return None 10 | 11 | if folder: 12 | head = os.getcwd() 13 | oldhead = None 14 | while head != None and head != oldhead: 15 | path = os.path.join(head, folder) 16 | if os.path.isdir(path): 17 | return path 18 | oldhead = head 19 | head, tail = os.path.split(head) 20 | 21 | return None 22 | 23 | def Normalize(path): 24 | return os.path.abspath(os.path.normpath(path)) 25 | 26 | class AcceleratorConfig(object): 27 | def __init__(self): 28 | self.mms_root = None 29 | self.sm_root = None 30 | self.extension = None 31 | self.libz = None 32 | self.libbreakpad_client = None 33 | self.libbreakpad = None 34 | self.libdisasm = None 35 | self.breakpad_patch = None 36 | self.targets = [] 37 | self.target_archs = set() 38 | self.breakpad_config = dict() 39 | self.breakpad_patch = None 40 | 41 | if builder.options.targets: 42 | target_archs = builder.options.targets.split(',') 43 | else: 44 | target_archs = ['x86', 'x86_64'] 45 | 46 | for arch in target_archs: 47 | try: 48 | cxx = builder.DetectCxx(target_arch = arch) 49 | self.target_archs.add(cxx.target.arch) 50 | except Exception as e: 51 | if builder.options.targets: 52 | raise 53 | print('Skipping target {}: {}'.format(arch, e)) 54 | continue 55 | self.targets.append(cxx) 56 | 57 | if not self.targets: 58 | raise Exception('No suitable C/C++ compiler was found.') 59 | 60 | @property 61 | def tag(self): 62 | if builder.options.debug == '1': 63 | return 'Debug' 64 | return 'Release' 65 | 66 | def retrieve_sm(self): 67 | if builder.options.sm_path: 68 | self.sm_root = builder.options.sm_path 69 | else: 70 | self.sm_root = ResolveEnvPath('SOURCEMOD', 'sourcemod') 71 | 72 | if not self.sm_root or not os.path.isdir(self.sm_root): 73 | raise Exception('Could not find a source copy of SourceMod') 74 | self.sm_root = Normalize(self.sm_root) 75 | 76 | def use_auto_versioning(self): 77 | return not builder.options.disable_auto_versioning 78 | 79 | def configure_cxx(self, cxx): 80 | if cxx.like('gcc'): 81 | self.configure_gcc(cxx) 82 | elif cxx.family == 'msvc': 83 | self.configure_msvc(cxx) 84 | 85 | # Optimization 86 | if builder.options.opt == '1': 87 | cxx.defines += ['NDEBUG'] 88 | 89 | # Debugging 90 | if builder.options.debug == '1': 91 | cxx.defines += ['DEBUG', '_DEBUG'] 92 | 93 | # Platform-specifics 94 | if cxx.target.platform == 'linux': 95 | self.configure_linux(cxx) 96 | elif cxx.target.platform == 'windows': 97 | self.configure_windows(cxx) 98 | 99 | if self.use_auto_versioning(): 100 | cxx.defines += ['GIT_ACTION_BUILD'] 101 | 102 | def configure_gcc(self, cxx): 103 | cxx.cflags += [ 104 | '-fPIC', 105 | '-pipe', 106 | '-fno-strict-aliasing', 107 | '-fvisibility=hidden', 108 | '-fvisibility-inlines-hidden', 109 | '-Wall', 110 | '-Werror', 111 | '-msse' 112 | ] 113 | 114 | cxx.cxxflags += [ 115 | '-std=c++17', 116 | '-fno-threadsafe-statics', 117 | '-Wno-non-virtual-dtor', 118 | '-Wno-overloaded-virtual', 119 | '-Wno-implicit-exception-spec-mismatch' 120 | ] 121 | 122 | cxx.postlink += ['-pthread', '-static-libstdc++', '-static-libgcc'] 123 | 124 | if builder.options.opt == '1': 125 | cxx.cflags += ['-O3'] 126 | return 127 | 128 | def configure_msvc(self, cxx): 129 | 130 | cxx.cxxflags += [ 131 | '/EHsc', 132 | '/std:c++17' 133 | ] 134 | 135 | return 136 | 137 | def configure_linux(self, cxx): 138 | cxx.defines += ['_LINUX', 'POSIX', '_GLIBCXX_USE_CXX11_ABI=0'] 139 | return 140 | 141 | def configure_windows(self, cxx): 142 | cxx.defines += ['_WINDOWS'] 143 | if cxx.target.arch == 'x86': 144 | cxx.defines += ['WIN32'] 145 | elif cxx.target.arch == 'x86_64': 146 | cxx.defines += ['WIN64'] 147 | return 148 | 149 | def configure(self): 150 | self.retrieve_sm() 151 | 152 | for cxx in self.targets: 153 | self.configure_cxx(cxx) 154 | 155 | def configure_extension(self, compiler, context): 156 | compiler.cxxincludes += [ 157 | os.path.join(context.currentSourcePath), 158 | os.path.join(self.sm_root, 'public'), 159 | os.path.join(self.sm_root, 'public', 'extensions'), 160 | os.path.join(self.sm_root, 'public', 'amtl', 'amtl'), 161 | os.path.join(self.sm_root, 'public', 'amtl'), 162 | os.path.join(self.sm_root, 'sourcepawn', 'include') 163 | ] 164 | 165 | def link_libz(self, compiler, context): 166 | for task in self.libz: 167 | if task.target.arch == compiler.target.arch: 168 | compiler.postlink += [os.path.join(context.buildPath, task.binary.path)] 169 | compiler.linkdeps += [task.binary] 170 | return 171 | raise Exception('No suitable build of libz was found.') 172 | 173 | def link_libbreakpad_client(self, compiler, context): 174 | for task in self.libbreakpad_client: 175 | if task.target.arch == compiler.target.arch: 176 | compiler.postlink += [os.path.join(context.buildPath, task.binary.path)] 177 | compiler.linkdeps += [task.binary] 178 | return 179 | raise Exception('No suitable build of libbreakpad_client was found.') 180 | 181 | def link_libbreakpad(self, compiler, context): 182 | for task in self.libbreakpad: 183 | if task.target.arch == compiler.target.arch: 184 | compiler.postlink += [os.path.join(context.buildPath, task.binary.path)] 185 | compiler.linkdeps += [task.binary] 186 | return 187 | raise Exception('No suitable build of libbreakpad was found.') 188 | 189 | def link_libdisasm(self, compiler, context): 190 | for task in self.libdisasm: 191 | if task.target.arch == compiler.target.arch: 192 | compiler.postlink += [os.path.join(context.buildPath, task.binary.path)] 193 | compiler.linkdeps += [task.binary] 194 | return 195 | raise Exception('No suitable build of libdisasm was found.') 196 | 197 | def ConfigureLibrary(self, project, compiler, context): 198 | binary = project.Configure(compiler, project.name, '{0} - {1}'.format(self.tag, compiler.target.arch)) 199 | binary.compiler.cxxincludes += [ 200 | os.path.join(context.currentSourcePath) 201 | ] 202 | return binary 203 | 204 | def ConfigureExtension(self, project, compiler, context): 205 | binary = self.ConfigureLibrary(project, compiler, context) 206 | self.configure_extension(binary.compiler, context) 207 | return binary 208 | 209 | Accelerator = AcceleratorConfig() 210 | Accelerator.configure() 211 | 212 | builder.Build(['third_party/Patch', 'third_party/Configure', 'third_party/AMBuilder'], { 'Accelerator': Accelerator }) 213 | 214 | BuildScripts = ['extension/AMBuilder', 'buildbot/PackageScript'] 215 | builder.Build(BuildScripts, { 'Accelerator': Accelerator }) 216 | 217 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Accelerator 2 | -------------------------------------------------------------------------------- /buildbot/BreakpadSymbols: -------------------------------------------------------------------------------- 1 | # vim: set ts=2 sw=2 tw=99 noet ft=python: 2 | import os 3 | from ambuild.command import Command 4 | from ambuild.command import ShellCommand 5 | 6 | try: 7 | import urllib.request as urllib 8 | except ImportError: 9 | import urllib2 as urllib 10 | 11 | class IterateDebugInfoCommand(Command): 12 | def run(self, master, job): 13 | pdblog = open(os.path.join(AMBuild.outputFolder, 'pdblog.txt'), 'rt') 14 | for debug_info in pdblog: 15 | debug_info = os.path.join(AMBuild.outputFolder, debug_info.strip()) 16 | job.AddCommand(SymbolCommand(debug_info, symbolServer)) 17 | pdblog.close() 18 | 19 | class SymbolCommand(ShellCommand): 20 | def __init__(self, debugFile, symbolServer): 21 | self.serverResponse = None 22 | self.symbolServer = symbolServer 23 | if AMBuild.target['platform'] == 'linux': 24 | cmdstring = "dump_syms {0} {1}".format(debugFile, os.path.dirname(debugFile)) 25 | elif AMBuild.target['platform'] == 'darwin': 26 | cmdstring = "dump_syms {0}".format(debugFile) 27 | elif AMBuild.target['platform'] == 'windows': 28 | cmdstring = "dump_syms.exe {0}".format(debugFile) 29 | ShellCommand.__init__(self, cmdstring) 30 | def run(self, master, job): 31 | ShellCommand.run(self, master, job) 32 | if self.stdout != None and len(self.stdout) > 0: 33 | request = urllib.Request(symbolServer, self.stdout.encode('utf-8')) 34 | request.add_header("Content-Type", "text/plain") 35 | self.serverResponse = urllib.urlopen(request).read().decode('utf-8') 36 | def spew(self, runner): 37 | if self.stderr != None and len(self.stderr) > 0: 38 | runner.PrintOut(self.stderr) 39 | if self.serverResponse != None and len(self.serverResponse) > 0: 40 | runner.PrintOut(self.serverResponse) 41 | 42 | if 'BREAKPAD_SYMBOL_SERVER' in os.environ: 43 | symbolServer = os.environ['BREAKPAD_SYMBOL_SERVER'] 44 | job = AMBuild.AddJob('breakpad-symbols') 45 | job.AddCommand(IterateDebugInfoCommand()) 46 | -------------------------------------------------------------------------------- /buildbot/PackageScript: -------------------------------------------------------------------------------- 1 | # vim: set ts=8 sts=2 sw=2 tw=99 et ft=python: 2 | import os 3 | 4 | # This is where the files will be output to 5 | # package is the default 6 | builder.SetBuildFolder('package') 7 | 8 | # Add any folders you need to this list 9 | folder_list = [ 10 | 'addons/sourcemod/extensions', 11 | 'addons/sourcemod/gamedata', 12 | 'addons/sourcemod/configs' 13 | ] 14 | 15 | # Are we build a x86_64 extension ? 16 | for task in Accelerator.extension: 17 | if task.target.arch == 'x86_64': 18 | folder_list += ['addons/sourcemod/extensions/x64'] 19 | break 20 | 21 | # Create the distribution folder hierarchy. 22 | folder_map = {} 23 | for folder in folder_list: 24 | norm_folder = os.path.normpath(folder) 25 | folder_map[folder] = builder.AddFolder(norm_folder) 26 | 27 | # Do all straight-up file copies from the source tree. 28 | def CopyFiles(src, dest, files): 29 | if not dest: 30 | dest = src 31 | dest_entry = folder_map[dest] 32 | for source_file in files: 33 | source_path = os.path.join(builder.sourcePath, src, source_file) 34 | builder.AddCopy(source_path, dest_entry) 35 | def CopyFile(src, dest): 36 | dest_entry = folder_map[dest] 37 | source_path = os.path.join(builder.sourcePath, src) 38 | builder.AddCopy(source_path, dest_entry) 39 | def CopyDirContent(src, dest): 40 | dest_entry = folder_map[dest] 41 | for item in os.scandir(os.path.join(builder.sourcePath, src)): 42 | if item.is_file(): 43 | builder.AddCopy(item.path, dest_entry) 44 | 45 | # Copy binaries. 46 | for task in Accelerator.extension: 47 | if task.target.arch == 'x86_64': 48 | builder.AddCopy(task.binary, folder_map['addons/sourcemod/extensions/x64']) 49 | else: 50 | builder.AddCopy(task.binary, folder_map['addons/sourcemod/extensions']) 51 | 52 | CopyDirContent('gamedata', 'addons/sourcemod/gamedata') 53 | CopyFile('extension/accelerator.autoload', 'addons/sourcemod/extensions') -------------------------------------------------------------------------------- /buildbot/Versioning: -------------------------------------------------------------------------------- 1 | # vim: set ts=2 sw=2 tw=99 noet ft=python: 2 | import os 3 | import re 4 | import subprocess 5 | from ambuild.cache import Cache 6 | import ambuild.command as command 7 | 8 | #Quickly try to ascertain the current repository revision 9 | def GetVersion(): 10 | rev = command.RunDirectCommand(AMBuild, ['git', 'rev-list', '--count', 'HEAD']).stdoutText.strip() 11 | cset = command.RunDirectCommand(AMBuild, ['git', 'log', '--pretty=format:%h', '-n', '1']).stdoutText.strip() 12 | 13 | if not rev or not cset: 14 | raise Exception('Could not determine repository version') 15 | 16 | return (rev, cset) 17 | 18 | def PerformReversioning(): 19 | rev, cset = GetVersion() 20 | cacheFile = os.path.join(AMBuild.outputFolder, '.ambuild', 'hgcache') 21 | cache = Cache(cacheFile) 22 | if os.path.isfile(cacheFile): 23 | cache.LoadCache() 24 | if cache.HasVariable('cset') and cache['cset'] == cset: 25 | return False 26 | cache.CacheVariable('cset', cset) 27 | 28 | productFile = open(os.path.join(AMBuild.sourceFolder, 'product.version'), 'r') 29 | productContents = productFile.read() 30 | productFile.close() 31 | m = re.match('(\d+)\.(\d+)\.(\d+)(.*)', productContents) 32 | if m == None: 33 | raise Exception('Could not detremine product version') 34 | major, minor, release, tag = m.groups() 35 | 36 | incFolder = os.path.join(AMBuild.sourceFolder, 'extension') 37 | incFile = open(os.path.join(incFolder, 'version_auto.h'), 'w') 38 | incFile.write(""" 39 | #ifndef _AUTO_VERSION_INFORMATION_H_ 40 | #define _AUTO_VERSION_INFORMATION_H_ 41 | 42 | #define SM_BUILD_TAG \"{0}\" 43 | #define SM_BUILD_UNIQUEID \"{1}:{2}\" SM_BUILD_TAG 44 | #define SM_VERSION \"{3}.{4}.{5}\" 45 | #define SM_FULL_VERSION SM_VERSION SM_BUILD_TAG 46 | #define SM_FILE_VERSION {6},{7},{8},0 47 | 48 | #endif /* _AUTO_VERSION_INFORMATION_H_ */ 49 | 50 | """.format(tag, rev, cset, major, minor, release, major, minor, release)) 51 | incFile.close() 52 | cache.WriteCache() 53 | 54 | PerformReversioning() 55 | 56 | 57 | -------------------------------------------------------------------------------- /buildbot/pushbuild.txt: -------------------------------------------------------------------------------- 1 | Hi! 2 | Once upon a time there was a bee. 3 | -------------------------------------------------------------------------------- /configure.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from ambuild2 import run 3 | 4 | parser = run.BuildParser(sourcePath = sys.path[0], api='2.2') 5 | parser.options.add_argument('--sm-path', type=str, dest='sm_path', default=None, 6 | help='Path to SourceMod') 7 | parser.options.add_argument('--enable-debug', action='store_const', const='1', dest='debug', 8 | help='Enable debugging symbols') 9 | parser.options.add_argument('--enable-optimize', action='store_const', const='1', dest='opt', 10 | help='Enable optimization') 11 | parser.options.add_argument('--disable-auto-versioning', action='store_false', dest='disable_auto_versioning', 12 | default=True, help='Disables the auto versioning script') 13 | parser.options.add_argument('--targets', type=str, dest='targets', default=None, 14 | help="Override the target architecture (use commas to separate multiple targets).") 15 | parser.Configure() -------------------------------------------------------------------------------- /dockerbuild/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /dockerbuild/_accelerator_docker_build_internal.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euxo pipefail 4 | 5 | 6 | bootstrapEnv() 7 | { 8 | # for squashing git whining when people run with docker instead of rootless podman 9 | git config --global --add safe.directory / 10 | git config --global --add safe.directory "*" 11 | 12 | # for preventing any submodule tomfuckery 13 | git submodule update --init --recursive -f 14 | 15 | # whack build dir it's literally faster and easier to do this than to cache it 16 | rm -rfv ./build 17 | 18 | # clean up patch cruft in breakpad dir so we start from a clean slate no matter what 19 | pushd third_party/breakpad 20 | git reset --hard 21 | git clean -x -f -d 22 | git clean -X -f -d 23 | popd 24 | } 25 | 26 | 27 | bootstrapPkgs() 28 | { 29 | # we really need to slim this shit down lol 30 | dpkg --add-architecture i386 && \ 31 | apt-get update -y && \ 32 | apt-get install -y \ 33 | --no-install-recommends \ 34 | git \ 35 | clang \ 36 | make \ 37 | python3-httplib2 python3-pip \ 38 | lib32stdc++-10-dev lib32z1-dev libc6-dev-i386 linux-libc-dev:i386 \ 39 | libzstd-dev libzstd-dev:i386 zlib1g-dev zlib1g-dev:i386 40 | 41 | # force clang to be our compiler no matter what, hopefully 42 | update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100 43 | update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100 44 | 45 | # this is just for logging 46 | cc --version || true 47 | c++ --version || true 48 | } 49 | 50 | 51 | smBranch="master" 52 | amTempLocation="_am_temp" 53 | succCloneLocation="/accelerator/${amTempLocation}/successful_clone" 54 | bootstrapAM() 55 | { 56 | # need to install ambuild if we already cloned, otherwise checkout-deps will do it 4 us 57 | if test -f "${succCloneLocation}"; then 58 | pip install /accelerator/"${amTempLocation}"/ambuild 59 | return 255; 60 | fi 61 | 62 | rm -rf /accelerator/"${amTempLocation}"/ || true 63 | mkdir -p ${amTempLocation} || exit 1 64 | pushd ${amTempLocation} || exit 1 65 | git clone -b ${smBranch} --recursive --depth 1 https://github.com/alliedmodders/sourcemod sourcemod || exit 1 66 | git clone --recursive --depth 1 https://github.com/alliedmodders/ambuild ambuild || exit 1 67 | pip install ./ambuild 68 | 69 | # make a blank file so that we don't reclone everything if we don't need to 70 | true > "${succCloneLocation}" || exit 1 71 | popd 72 | } 73 | 74 | 75 | buildIt() 76 | { 77 | if test ! -d build; then 78 | mkdir -p build 79 | fi 80 | 81 | pushd build 82 | CC=clang CXX=clang++ python3 ../configure.py --sm-path=/accelerator/${amTempLocation}/sourcemod/ 83 | ambuild 84 | popd 85 | } 86 | 87 | ############################### 88 | 89 | cd /accelerator 90 | 91 | bootstrapPkgs 92 | bootstrapEnv 93 | bootstrapAM || true 94 | 95 | buildIt 96 | -------------------------------------------------------------------------------- /dockerbuild/_github_actions_setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euxo pipefail 4 | 5 | pwd 6 | ls -la 7 | 8 | mkdir /accelerator 9 | cp -av "$(pwd)"/. /accelerator 10 | cd /accelerator 11 | 12 | pwd 13 | ls -la 14 | 15 | 16 | bash ./dockerbuild/_accelerator_docker_build_internal.sh 17 | -------------------------------------------------------------------------------- /dockerbuild/accelerator_docker_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | 4 | dockerimage="debian:11-slim" 5 | 6 | 7 | # we do this so that we can be agnostic about where we're invoked from 8 | # meaning you can exec this script anywhere and it should work the same 9 | thisiswhereiam=${BASH_SOURCE[0]} 10 | # this should be /whatever/directory/structure/Open-Fortress-Source 11 | script_folder=$( cd -- "$( dirname -- "${thisiswhereiam}" )" &> /dev/null && pwd ) 12 | 13 | 14 | # this should be /whatever/directory/structure/[accelerator_root]/cicd 15 | build_dir="dockerbuild" 16 | 17 | pushd "${script_folder}" &> /dev/null || exit 99 18 | 19 | # this is relative to our source dir/build 20 | internalscript="_accelerator_docker_build_internal.sh" 21 | 22 | # this should always be our accelerator root dir 23 | pushd ../ &> /dev/null 24 | dev_srcdir=$(pwd) 25 | container_rootdir="accelerator" 26 | 27 | # add -it flags automatically if in null tty 28 | itflag="" 29 | if [ -t 0 ] ; then 30 | itflag="-it" 31 | else 32 | itflag="" 33 | fi 34 | 35 | podman run ${itflag} \ 36 | -v "${dev_srcdir}":/"${container_rootdir}" \ 37 | -w /${container_rootdir} \ 38 | ${dockerimage} \ 39 | bash ./${build_dir}/${internalscript} "$@" 40 | 41 | ecodereal=$? 42 | echo "real exit code ${ecodereal}" 43 | 44 | popd &> /dev/null || exit 45 | 46 | popd &> /dev/null || exit 47 | 48 | exit ${ecodereal} 49 | 50 | -------------------------------------------------------------------------------- /extension/AMBuilder: -------------------------------------------------------------------------------- 1 | # vim: set ts=2 sw=2 tw=99 noet ft=python: 2 | import os, sys 3 | 4 | builder.SetBuildFolder('/') 5 | 6 | project = builder.LibraryProject('accelerator.ext') 7 | project.sources = [ 8 | 'extension.cpp', 9 | 'MemoryDownloader.cpp', 10 | os.path.join(Accelerator.sm_root, 'public', 'smsdk_ext.cpp') 11 | ] 12 | 13 | def AddSourceFilesFromDir(path, files): 14 | list = [] 15 | for file in files: 16 | list.append(os.path.join(path, file)) 17 | return list 18 | 19 | for cxx in Accelerator.targets: 20 | binary = Accelerator.ConfigureExtension(project, cxx, builder) 21 | compiler = binary.compiler 22 | # Wait for breakpad to be patched 23 | compiler.sourcedeps += Accelerator.breakpad_patch 24 | # We depend on breakpad on config 25 | compiler.sourcedeps += Accelerator.breakpad_config[compiler.target.arch] 26 | 27 | compiler.defines += ['HAVE_CONFIG_H'] 28 | compiler.cxxincludes += [ 29 | os.path.join(builder.sourcePath, 'third_party', 'breakpad', 'src'), 30 | os.path.join(builder.buildPath, 'third_party', 'config', compiler.target.arch), 31 | ] 32 | 33 | if compiler.target.platform in ['linux']: 34 | binary.sources += AddSourceFilesFromDir(os.path.join(builder.currentSourcePath, '..', 'third_party', 'breakpad', 'src', 'common'), [ 35 | 'dwarf_cfi_to_module.cc', 36 | 'dwarf_cu_to_module.cc', 37 | 'dwarf_line_to_module.cc', 38 | 'dwarf_range_list_handler.cc', 39 | 'language.cc', 40 | 'module.cc', 41 | 'path_helper.cc', 42 | 'stabs_reader.cc', 43 | 'stabs_to_module.cc', 44 | 'dwarf/bytereader.cc', 45 | 'dwarf/dwarf2diehandler.cc', 46 | 'dwarf/dwarf2reader.cc', 47 | 'dwarf/elf_reader.cc', 48 | 'linux/crc32.cc', 49 | 'linux/dump_symbols.cc', 50 | 'linux/elf_symbols_to_module.cc', 51 | 'linux/breakpad_getcontext.S' 52 | ]) 53 | 54 | Accelerator.link_libbreakpad_client(compiler, builder) 55 | Accelerator.link_libbreakpad(compiler, builder) 56 | Accelerator.link_libdisasm(compiler, builder) 57 | 58 | Accelerator.extension = builder.Add(project) -------------------------------------------------------------------------------- /extension/MemoryDownloader.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * vim: set ts=4 : 3 | * ============================================================================= 4 | * SourceMod Updater Extension 5 | * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved. 6 | * ============================================================================= 7 | * 8 | * This program is free software; you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License, version 3.0, as published by the 10 | * Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | * 20 | * As a special exception, AlliedModders LLC gives you permission to link the 21 | * code of this program (as well as its derivative works) to "Half-Life 2," the 22 | * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software 23 | * by the Valve Corporation. You must obey the GNU General Public License in 24 | * all respects for all other code used. Additionally, AlliedModders LLC grants 25 | * this exception to all derivative works. AlliedModders LLC defines further 26 | * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), 27 | * or . 28 | * 29 | * Version: $Id$ 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include "MemoryDownloader.h" 36 | 37 | using namespace SourceMod; 38 | 39 | MemoryDownloader::MemoryDownloader() : buffer(NULL), bufsize(0), bufpos(0) 40 | { 41 | } 42 | 43 | MemoryDownloader::~MemoryDownloader() 44 | { 45 | free(buffer); 46 | } 47 | 48 | DownloadWriteStatus MemoryDownloader::OnDownloadWrite(IWebTransfer *session, 49 | void *userdata, 50 | void *ptr, 51 | size_t size, 52 | size_t nmemb) 53 | { 54 | size_t total = size * nmemb; 55 | 56 | if (bufpos + total > bufsize) 57 | { 58 | size_t rem = (bufpos + total) - bufsize; 59 | bufsize += rem + (rem / 2); 60 | buffer = (char *)realloc(buffer, bufsize); 61 | } 62 | 63 | assert(bufpos + total <= bufsize); 64 | 65 | memcpy(&buffer[bufpos], ptr, total); 66 | bufpos += total; 67 | 68 | return DownloadWrite_Okay; 69 | } 70 | 71 | void MemoryDownloader::Reset() 72 | { 73 | bufpos = 0; 74 | } 75 | 76 | char *MemoryDownloader::GetBuffer() 77 | { 78 | return buffer; 79 | } 80 | 81 | size_t MemoryDownloader::GetSize() 82 | { 83 | return bufpos; 84 | } 85 | 86 | -------------------------------------------------------------------------------- /extension/MemoryDownloader.h: -------------------------------------------------------------------------------- 1 | /** 2 | * vim: set ts=4 : 3 | * ============================================================================= 4 | * SourceMod Updater Extension 5 | * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved. 6 | * ============================================================================= 7 | * 8 | * This program is free software; you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License, version 3.0, as published by the 10 | * Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | * 20 | * As a special exception, AlliedModders LLC gives you permission to link the 21 | * code of this program (as well as its derivative works) to "Half-Life 2," the 22 | * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software 23 | * by the Valve Corporation. You must obey the GNU General Public License in 24 | * all respects for all other code used. Additionally, AlliedModders LLC grants 25 | * this exception to all derivative works. AlliedModders LLC defines further 26 | * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), 27 | * or . 28 | * 29 | * Version: $Id$ 30 | */ 31 | 32 | #ifndef _INCLUDE_SOURCEMOD_UPDATER_MEMORY_DOWNLOADER_H_ 33 | #define _INCLUDE_SOURCEMOD_UPDATER_MEMORY_DOWNLOADER_H_ 34 | 35 | #include 36 | 37 | namespace SourceMod 38 | { 39 | class MemoryDownloader : public ITransferHandler 40 | { 41 | public: 42 | MemoryDownloader(); 43 | ~MemoryDownloader(); 44 | public: 45 | DownloadWriteStatus OnDownloadWrite(IWebTransfer *session, 46 | void *userdata, 47 | void *ptr, 48 | size_t size, 49 | size_t nmemb); 50 | public: 51 | void Reset(); 52 | char *GetBuffer(); 53 | size_t GetSize(); 54 | private: 55 | char *buffer; 56 | size_t bufsize; 57 | size_t bufpos; 58 | }; 59 | } 60 | 61 | #endif /* _INCLUDE_SOURCEMOD_UPDATER_MEMORY_DOWNLOADER_H_ */ 62 | 63 | -------------------------------------------------------------------------------- /extension/accelerator.autoload: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asherkin/accelerator/5e319ecdc33f3cf09dad5a9e234b7dc530997ffd/extension/accelerator.autoload -------------------------------------------------------------------------------- /extension/extension.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ============================================================================= 3 | * Accelerator Extension 4 | * Copyright (C) 2011 Asher Baker (asherkin). All rights reserved. 5 | * ============================================================================= 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License, version 3.0, as published by the 9 | * Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 14 | * details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * this program. If not, see . 18 | */ 19 | 20 | #include "extension.h" 21 | 22 | #ifndef PLATFORM_ARCH_FOLDER 23 | #define PLATFORM_ARCH_FOLDER "" 24 | #endif 25 | 26 | #include 27 | 28 | #include 29 | #include "MemoryDownloader.h" 30 | 31 | #if defined _LINUX 32 | #include "client/linux/handler/exception_handler.h" 33 | #include "common/linux/linux_libc_support.h" 34 | #include "third_party/lss/linux_syscall_support.h" 35 | #include "common/linux/dump_symbols.h" 36 | #include "common/path_helper.h" 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | class StderrInhibitor 44 | { 45 | FILE *saved_stderr = nullptr; 46 | 47 | public: 48 | StderrInhibitor() { 49 | saved_stderr = fdopen(dup(fileno(stderr)), "w"); 50 | if (freopen(_PATH_DEVNULL, "w", stderr)) { 51 | // If it fails, not a lot we can (or should) do. 52 | // Add this brace section to silence gcc warnings. 53 | } 54 | } 55 | 56 | ~StderrInhibitor() { 57 | fflush(stderr); 58 | dup2(fileno(saved_stderr), fileno(stderr)); 59 | fclose(saved_stderr); 60 | } 61 | }; 62 | 63 | // Taken from https://hg.mozilla.org/mozilla-central/file/3eb7623b5e63b37823d5e9c562d56e586604c823/build/unix/stdc%2B%2Bcompat/stdc%2B%2Bcompat.cpp 64 | extern "C" void __attribute__((weak)) __cxa_throw_bad_array_new_length() { 65 | abort(); 66 | } 67 | 68 | namespace std { 69 | /* We shouldn't be throwing exceptions at all, but it sadly turns out 70 | we call STL (inline) functions that do. */ 71 | void __attribute__((weak)) __throw_out_of_range_fmt(char const* fmt, ...) { 72 | va_list ap; 73 | char buf[1024]; // That should be big enough. 74 | 75 | va_start(ap, fmt); 76 | vsnprintf(buf, sizeof(buf), fmt, ap); 77 | buf[sizeof(buf) - 1] = 0; 78 | va_end(ap); 79 | 80 | __throw_range_error(buf); 81 | } 82 | } // namespace std 83 | 84 | // Updated versions of the SM ones for C++14 85 | void operator delete(void *ptr, size_t sz) { 86 | free(ptr); 87 | } 88 | 89 | void operator delete[](void *ptr, size_t sz) { 90 | free(ptr); 91 | } 92 | 93 | #elif defined _WINDOWS 94 | #define _STDINT // ~.~ 95 | #include "client/windows/handler/exception_handler.h" 96 | 97 | #else 98 | #error Bad platform. 99 | #endif 100 | 101 | #include 102 | #include 103 | #include 104 | #include 105 | #include 106 | #include 107 | 108 | #include 109 | #include 110 | #include 111 | 112 | Accelerator g_accelerator; 113 | SMEXT_LINK(&g_accelerator); 114 | 115 | IWebternet *webternet; 116 | IGameConfig *gameconfig; 117 | 118 | typedef void (*GetSpew_t)(char *buffer, size_t length); 119 | GetSpew_t GetSpew; 120 | #if defined _WINDOWS 121 | typedef void(__fastcall *GetSpewFastcall_t)(char *buffer, size_t length); 122 | GetSpewFastcall_t GetSpewFastcall; 123 | #endif 124 | 125 | char spewBuffer[65536]; // Hi. 126 | 127 | char crashMap[256]; 128 | char crashGamePath[512]; 129 | char crashCommandLine[1024]; 130 | char crashSourceModPath[512]; 131 | char crashGameDirectory[256]; 132 | char crashSourceModVersion[32]; 133 | char steamInf[1024]; 134 | 135 | char dumpStoragePath[512]; 136 | char logPath[512]; 137 | 138 | google_breakpad::ExceptionHandler *handler = NULL; 139 | 140 | #if defined _LINUX 141 | void terminateHandler() 142 | { 143 | const char *msg = "missing exception"; 144 | std::exception_ptr pEx = std::current_exception(); 145 | if (pEx) { 146 | try { 147 | std::rethrow_exception(pEx); 148 | } catch(const std::exception &e) { 149 | msg = strdup(e.what()); 150 | } catch(...) { 151 | msg = "unknown exception"; 152 | } 153 | } 154 | 155 | size_t msgLength = strlen(msg) + 2; 156 | volatile char * volatile msgForCrashDumps = (char *)alloca(msgLength); 157 | strcpy((char *)msgForCrashDumps + 1, msg); 158 | 159 | abort(); 160 | } 161 | 162 | void (*SignalHandler)(int, siginfo_t *, void *); 163 | 164 | const int kExceptionSignals[] = { 165 | SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS 166 | }; 167 | 168 | const int kNumHandledSignals = sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]); 169 | 170 | static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded) 171 | { 172 | //printf("Wrote minidump to: %s\n", descriptor.path()); 173 | 174 | if (succeeded) { 175 | sys_write(STDOUT_FILENO, "Wrote minidump to: ", 19); 176 | } else { 177 | sys_write(STDOUT_FILENO, "Failed to write minidump to: ", 29); 178 | } 179 | 180 | sys_write(STDOUT_FILENO, descriptor.path(), my_strlen(descriptor.path())); 181 | sys_write(STDOUT_FILENO, "\n", 1); 182 | 183 | if (!succeeded) { 184 | return succeeded; 185 | } 186 | 187 | my_strlcpy(dumpStoragePath, descriptor.path(), sizeof(dumpStoragePath)); 188 | my_strlcat(dumpStoragePath, ".txt", sizeof(dumpStoragePath)); 189 | 190 | int extra = sys_open(dumpStoragePath, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); 191 | if (extra == -1) { 192 | sys_write(STDOUT_FILENO, "Failed to open metadata file!\n", 30); 193 | return succeeded; 194 | } 195 | 196 | sys_write(extra, "-------- CONFIG BEGIN --------", 30); 197 | sys_write(extra, "\nMap=", 5); 198 | sys_write(extra, crashMap, my_strlen(crashMap)); 199 | sys_write(extra, "\nGamePath=", 10); 200 | sys_write(extra, crashGamePath, my_strlen(crashGamePath)); 201 | sys_write(extra, "\nCommandLine=", 13); 202 | sys_write(extra, crashCommandLine, my_strlen(crashCommandLine)); 203 | sys_write(extra, "\nSourceModPath=", 15); 204 | sys_write(extra, crashSourceModPath, my_strlen(crashSourceModPath)); 205 | sys_write(extra, "\nGameDirectory=", 15); 206 | sys_write(extra, crashGameDirectory, my_strlen(crashGameDirectory)); 207 | if (crashSourceModVersion[0]) { 208 | sys_write(extra, "\nSourceModVersion=", 18); 209 | sys_write(extra, crashSourceModVersion, my_strlen(crashSourceModVersion)); 210 | } 211 | sys_write(extra, "\nExtensionVersion=", 18); 212 | sys_write(extra, SM_VERSION, my_strlen(SM_VERSION)); 213 | sys_write(extra, "\nExtensionBuild=", 16); 214 | sys_write(extra, SM_BUILD_UNIQUEID, my_strlen(SM_BUILD_UNIQUEID)); 215 | sys_write(extra, steamInf, my_strlen(steamInf)); 216 | sys_write(extra, "\n-------- CONFIG END --------\n", 30); 217 | 218 | if (GetSpew) { 219 | GetSpew(spewBuffer, sizeof(spewBuffer)); 220 | 221 | if (my_strlen(spewBuffer) > 0) { 222 | sys_write(extra, "-------- CONSOLE HISTORY BEGIN --------\n", 40); 223 | sys_write(extra, spewBuffer, my_strlen(spewBuffer)); 224 | sys_write(extra, "-------- CONSOLE HISTORY END --------\n", 38); 225 | } 226 | } 227 | 228 | sys_close(extra); 229 | 230 | return succeeded; 231 | } 232 | 233 | void OnGameFrame(bool simulating) 234 | { 235 | std::set_terminate(terminateHandler); 236 | 237 | bool weHaveBeenFuckedOver = false; 238 | struct sigaction oact; 239 | 240 | for (int i = 0; i < kNumHandledSignals; ++i) { 241 | sigaction(kExceptionSignals[i], NULL, &oact); 242 | 243 | if (oact.sa_sigaction != SignalHandler) { 244 | weHaveBeenFuckedOver = true; 245 | break; 246 | } 247 | } 248 | 249 | if (!weHaveBeenFuckedOver) { 250 | return; 251 | } 252 | 253 | struct sigaction act; 254 | memset(&act, 0, sizeof(act)); 255 | sigemptyset(&act.sa_mask); 256 | 257 | for (int i = 0; i < kNumHandledSignals; ++i) { 258 | sigaddset(&act.sa_mask, kExceptionSignals[i]); 259 | } 260 | 261 | act.sa_sigaction = SignalHandler; 262 | act.sa_flags = SA_ONSTACK | SA_SIGINFO; 263 | 264 | for (int i = 0; i < kNumHandledSignals; ++i) { 265 | sigaction(kExceptionSignals[i], &act, NULL); 266 | } 267 | } 268 | 269 | #elif defined _WINDOWS 270 | void *vectoredHandler = NULL; 271 | 272 | LONG CALLBACK BreakpadVectoredHandler(_In_ PEXCEPTION_POINTERS ExceptionInfo) 273 | { 274 | switch (ExceptionInfo->ExceptionRecord->ExceptionCode) 275 | { 276 | case EXCEPTION_ACCESS_VIOLATION: 277 | case EXCEPTION_INVALID_HANDLE: 278 | case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: 279 | case EXCEPTION_DATATYPE_MISALIGNMENT: 280 | case EXCEPTION_ILLEGAL_INSTRUCTION: 281 | case EXCEPTION_INT_DIVIDE_BY_ZERO: 282 | case EXCEPTION_STACK_OVERFLOW: 283 | case 0xC0000409: // STATUS_STACK_BUFFER_OVERRUN 284 | case 0xC0000374: // STATUS_HEAP_CORRUPTION 285 | break; 286 | case 0: // Valve use this for Sys_Error. 287 | if ((ExceptionInfo->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE) == 0) 288 | return EXCEPTION_CONTINUE_SEARCH; 289 | break; 290 | default: 291 | return EXCEPTION_CONTINUE_SEARCH; 292 | } 293 | 294 | if (handler->WriteMinidumpForException(ExceptionInfo)) 295 | { 296 | // Stop the handler thread from deadlocking us. 297 | delete handler; 298 | 299 | // Stop Valve's handler being called. 300 | ExceptionInfo->ExceptionRecord->ExceptionCode = EXCEPTION_BREAKPOINT; 301 | 302 | return EXCEPTION_EXECUTE_HANDLER; 303 | } else { 304 | return EXCEPTION_CONTINUE_SEARCH; 305 | } 306 | } 307 | 308 | static bool dumpCallback(const wchar_t* dump_path, 309 | const wchar_t* minidump_id, 310 | void* context, 311 | EXCEPTION_POINTERS* exinfo, 312 | MDRawAssertionInfo* assertion, 313 | bool succeeded) 314 | { 315 | if (!succeeded) { 316 | printf("Failed to write minidump to: %ls\\%ls.dmp\n", dump_path, minidump_id); 317 | return succeeded; 318 | } 319 | 320 | printf("Wrote minidump to: %ls\\%ls.dmp\n", dump_path, minidump_id); 321 | 322 | sprintf(dumpStoragePath, "%ls\\%ls.dmp.txt", dump_path, minidump_id); 323 | 324 | FILE *extra = fopen(dumpStoragePath, "wb"); 325 | if (!extra) { 326 | printf("Failed to open metadata file!\n"); 327 | return succeeded; 328 | } 329 | 330 | fprintf(extra, "-------- CONFIG BEGIN --------"); 331 | fprintf(extra, "\nMap=%s", crashMap); 332 | fprintf(extra, "\nGamePath=%s", crashGamePath); 333 | fprintf(extra, "\nCommandLine=%s", crashCommandLine); 334 | fprintf(extra, "\nSourceModPath=%s", crashSourceModPath); 335 | fprintf(extra, "\nGameDirectory=%s", crashGameDirectory); 336 | if (crashSourceModVersion[0]) { 337 | fprintf(extra, "\nSourceModVersion=%s", crashSourceModVersion); 338 | } 339 | fprintf(extra, "\nExtensionVersion=%s", SM_VERSION); 340 | fprintf(extra, "\nExtensionBuild=%s", SM_BUILD_UNIQUEID); 341 | fprintf(extra, "%s", steamInf); 342 | fprintf(extra, "\n-------- CONFIG END --------\n"); 343 | 344 | if (GetSpew || GetSpewFastcall) { 345 | if (GetSpew) { 346 | GetSpew(spewBuffer, sizeof(spewBuffer)); 347 | } else if (GetSpewFastcall) { 348 | GetSpewFastcall(spewBuffer, sizeof(spewBuffer)); 349 | } 350 | 351 | if (spewBuffer[0]) { 352 | fprintf(extra, "-------- CONSOLE HISTORY BEGIN --------\n%s-------- CONSOLE HISTORY END --------\n", spewBuffer); 353 | } 354 | } 355 | 356 | fclose(extra); 357 | 358 | return succeeded; 359 | } 360 | 361 | #else 362 | #error Bad platform. 363 | #endif 364 | 365 | class ClogInhibitor 366 | { 367 | std::streambuf *saved_clog = nullptr; 368 | 369 | public: 370 | ClogInhibitor() { 371 | saved_clog = std::clog.rdbuf(); 372 | std::clog.rdbuf(nullptr); 373 | } 374 | 375 | ~ClogInhibitor() { 376 | std::clog.rdbuf(saved_clog); 377 | } 378 | }; 379 | 380 | class UploadThread: public IThread 381 | { 382 | FILE *log = nullptr; 383 | char serverId[38] = ""; 384 | 385 | void RunThread(IThreadHandle *pHandle) { 386 | rootconsole->ConsolePrint("Accelerator upload thread started."); 387 | 388 | log = fopen(logPath, "a"); 389 | if (!log) { 390 | g_pSM->LogError(myself, "Failed to open Accelerator log file: %s", logPath); 391 | } 392 | 393 | char path[512]; 394 | g_pSM->Format(path, sizeof(path), "%s/server-id.txt", dumpStoragePath); 395 | FILE *serverIdFile = fopen(path, "r"); 396 | if (serverIdFile) { 397 | fread(serverId, 1, sizeof(serverId) - 1, serverIdFile); 398 | if (!feof(serverIdFile) || strlen(serverId) != 36) { 399 | serverId[0] = '\0'; 400 | } 401 | fclose(serverIdFile); 402 | } 403 | if (!serverId[0]) { 404 | serverIdFile = fopen(path, "w"); 405 | if (serverIdFile) { 406 | g_pSM->Format(serverId, sizeof(serverId), "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", 407 | rand() % 255, rand() % 255, rand() % 255, rand() % 255, rand() % 255, rand() % 255, 0x40 | ((rand() % 255) & 0x0F), rand() % 255, 408 | 0x80 | ((rand() % 255) & 0x3F), rand() % 255, rand() % 255, rand() % 255, rand() % 255, rand() % 255, rand() % 255, rand() % 255); 409 | fputs(serverId, serverIdFile); 410 | fclose(serverIdFile); 411 | } 412 | } 413 | 414 | IDirectory *dumps = libsys->OpenDirectory(dumpStoragePath); 415 | 416 | int skip = 0; 417 | int count = 0; 418 | int failed = 0; 419 | char metapath[512]; 420 | char presubmitToken[512]; 421 | char response[512]; 422 | 423 | while (dumps->MoreFiles()) { 424 | if (!dumps->IsEntryFile()) { 425 | dumps->NextEntry(); 426 | continue; 427 | } 428 | 429 | const char *name = dumps->GetEntryName(); 430 | 431 | int namelen = strlen(name); 432 | if (namelen < 4 || strcmp(&name[namelen-4], ".dmp") != 0) { 433 | dumps->NextEntry(); 434 | continue; 435 | } 436 | 437 | g_pSM->Format(path, sizeof(path), "%s/%s", dumpStoragePath, name); 438 | g_pSM->Format(metapath, sizeof(metapath), "%s.txt", path); 439 | 440 | if (!libsys->PathExists(metapath)) { 441 | metapath[0] = '\0'; 442 | } 443 | 444 | presubmitToken[0] = '\0'; 445 | PresubmitResponse presubmitResponse = kPRUploadCrashDumpAndMetadata; 446 | 447 | const char *presubmitOption = g_pSM->GetCoreConfigValue("MinidumpPresubmit"); 448 | bool canPresubmit = !presubmitOption || (tolower(presubmitOption[0]) == 'y' || presubmitOption[0] == '1'); 449 | 450 | if (canPresubmit) { 451 | presubmitResponse = PresubmitCrashDump(path, presubmitToken, sizeof(presubmitToken)); 452 | } 453 | 454 | switch (presubmitResponse) { 455 | case kPRLocalError: 456 | failed++; 457 | g_pSM->LogError(myself, "Accelerator failed to locally process crash dump"); 458 | if (log) fprintf(log, "Failed to locally process crash dump"); 459 | break; 460 | case kPRRemoteError: 461 | case kPRUploadCrashDumpAndMetadata: 462 | case kPRUploadMetadataOnly: 463 | if (UploadCrashDump((presubmitResponse == kPRUploadMetadataOnly) ? nullptr : path, metapath, presubmitToken, response, sizeof(response))) { 464 | count++; 465 | g_pSM->LogError(myself, "Accelerator uploaded crash dump: %s", response); 466 | if (log) fprintf(log, "Uploaded crash dump: %s\n", response); 467 | } else { 468 | failed++; 469 | g_pSM->LogError(myself, "Accelerator failed to upload crash dump: %s", response); 470 | if (log) fprintf(log, "Failed to upload crash dump: %s\n", response); 471 | } 472 | break; 473 | case kPRDontUpload: 474 | skip++; 475 | g_pSM->LogError(myself, "Accelerator crash dump upload skipped by server"); 476 | if (log) fprintf(log, "Skipped due to server request\n"); 477 | break; 478 | } 479 | 480 | if (metapath[0]) { 481 | unlink(metapath); 482 | } 483 | 484 | unlink(path); 485 | 486 | if (log) fflush(log); 487 | 488 | dumps->NextEntry(); 489 | } 490 | 491 | libsys->CloseDirectory(dumps); 492 | 493 | if (log) { 494 | fclose(log); 495 | log = nullptr; 496 | } 497 | 498 | rootconsole->ConsolePrint("Accelerator upload thread finished. (%d skipped, %d uploaded, %d failed)", skip, count, failed); 499 | } 500 | 501 | void OnTerminate(IThreadHandle *pHandle, bool cancel) { 502 | rootconsole->ConsolePrint("Accelerator upload thread terminated. (canceled = %s)", (cancel ? "true" : "false")); 503 | } 504 | 505 | #if defined _LINUX 506 | bool UploadSymbolFile(const google_breakpad::CodeModule *module, const char *presubmitToken) { 507 | if (log) fprintf(log, "UploadSymbolFile\n"); 508 | if (log) fflush(log); 509 | 510 | auto debugFile = module->debug_file(); 511 | std::string vdsoOutputPath = ""; 512 | 513 | if (false && debugFile == "linux-gate.so") { 514 | FILE *auxvFile = fopen("/proc/self/auxv", "rb"); 515 | if (auxvFile) { 516 | char vdsoOutputPathBuffer[512]; 517 | g_pSM->BuildPath(Path_SM, vdsoOutputPathBuffer, sizeof(vdsoOutputPathBuffer), "data/dumps/linux-gate.so"); 518 | vdsoOutputPath = vdsoOutputPathBuffer; 519 | 520 | while (!feof(auxvFile)) { 521 | int auxvEntryId = 0; 522 | fread(&auxvEntryId, sizeof(auxvEntryId), 1, auxvFile); 523 | long auxvEntryValue = 0; 524 | fread(&auxvEntryValue, sizeof(auxvEntryValue), 1, auxvFile); 525 | 526 | if (auxvEntryId == 0) break; 527 | if (auxvEntryId != 33) continue; // AT_SYSINFO_EHDR 528 | 529 | #ifdef PLATFORM_X64 530 | Elf64_Ehdr *vdsoHdr = (Elf64_Ehdr *)auxvEntryValue; 531 | #else 532 | Elf32_Ehdr *vdsoHdr = (Elf32_Ehdr *)auxvEntryValue; 533 | #endif 534 | auto vdsoSize = vdsoHdr->e_shoff + (vdsoHdr->e_shentsize * vdsoHdr->e_shnum); 535 | void *vdsoBuffer = malloc(vdsoSize); 536 | memcpy(vdsoBuffer, vdsoHdr, vdsoSize); 537 | 538 | FILE *vdsoFile = fopen(vdsoOutputPath.c_str(), "wb"); 539 | if (vdsoFile) { 540 | fwrite(vdsoBuffer, 1, vdsoSize, vdsoFile); 541 | fclose(vdsoFile); 542 | debugFile = vdsoOutputPath; 543 | } 544 | 545 | free(vdsoBuffer); 546 | break; 547 | } 548 | 549 | fclose(auxvFile); 550 | } 551 | } 552 | 553 | if (debugFile[0] != '/') { 554 | return false; 555 | } 556 | 557 | if (log) fprintf(log, "Submitting symbols for %s\n", debugFile.c_str()); 558 | if (log) fflush(log); 559 | 560 | auto debugFileDir = google_breakpad::DirName(debugFile); 561 | std::vector debug_dirs{ 562 | debugFileDir, 563 | debugFileDir + "/.debug", 564 | "/usr/lib/debug" + debugFileDir, 565 | }; 566 | 567 | std::ostringstream outputStream; 568 | google_breakpad::DumpOptions options(ALL_SYMBOL_DATA, true, true); 569 | 570 | { 571 | StderrInhibitor stdrrInhibitor; 572 | 573 | if (!WriteSymbolFile(debugFile, debugFile, "Linux", debug_dirs, options, outputStream)) { 574 | outputStream.str(""); 575 | outputStream.clear(); 576 | 577 | // Try again without debug dirs. 578 | if (!WriteSymbolFile(debugFile, debugFile, "Linux", {}, options, outputStream)) { 579 | if (log) fprintf(log, "Failed to process symbol file\n"); 580 | if (log) fflush(log); 581 | return false; 582 | } 583 | } 584 | } 585 | 586 | auto output = outputStream.str(); 587 | // output = output.substr(0, output.find("\n")); 588 | // printf(">>> %s\n", output.c_str()); 589 | 590 | if (debugFile == vdsoOutputPath) { 591 | unlink(vdsoOutputPath.c_str()); 592 | } 593 | 594 | IWebForm *form = webternet->CreateForm(); 595 | 596 | const char *minidumpAccount = g_pSM->GetCoreConfigValue("MinidumpAccount"); 597 | if (minidumpAccount && minidumpAccount[0]) form->AddString("UserID", minidumpAccount); 598 | 599 | form->AddString("ExtensionVersion", SMEXT_CONF_VERSION); 600 | form->AddString("ServerID", serverId); 601 | 602 | if (presubmitToken && presubmitToken[0]) { 603 | form->AddString("PresubmitToken", presubmitToken); 604 | } 605 | 606 | form->AddString("symbol_file", output.c_str()); 607 | 608 | MemoryDownloader data; 609 | IWebTransfer *xfer = webternet->CreateSession(); 610 | xfer->SetFailOnHTTPError(true); 611 | 612 | const char *symbolUrl = g_pSM->GetCoreConfigValue("MinidumpSymbolUrl"); 613 | if (!symbolUrl) symbolUrl = "http://crash.limetech.org/symbols/submit"; 614 | 615 | bool symbolUploaded = xfer->PostAndDownload(symbolUrl, form, &data, NULL); 616 | 617 | if (!symbolUploaded) { 618 | if (log) fprintf(log, "Symbol upload failed: %s (%d)\n", xfer->LastErrorMessage(), xfer->LastErrorCode()); 619 | if (log) fflush(log); 620 | return false; 621 | } 622 | 623 | int responseSize = data.GetSize(); 624 | char *response = new char[responseSize + 1]; 625 | strncpy(response, data.GetBuffer(), responseSize + 1); 626 | response[responseSize] = '\0'; 627 | while (responseSize > 0 && response[responseSize - 1] == '\n') { 628 | response[--responseSize] = '\0'; 629 | } 630 | if (log) fprintf(log, "Symbol upload complete: %s\n", response); 631 | delete[] response; 632 | if (log) fflush(log); 633 | return true; 634 | } 635 | #endif 636 | 637 | bool UploadModuleFile(const google_breakpad::CodeModule *module, const char *presubmitToken) { 638 | const auto &codeFile = module->code_file(); 639 | 640 | #ifndef WIN32 641 | if (codeFile[0] != '/') { 642 | #else 643 | if (codeFile[1] != ':') { 644 | #endif 645 | return false; 646 | } 647 | 648 | if (log) fprintf(log, "Submitting binary for %s\n", codeFile.c_str()); 649 | if (log) fflush(log); 650 | 651 | IWebForm *form = webternet->CreateForm(); 652 | 653 | const char *minidumpAccount = g_pSM->GetCoreConfigValue("MinidumpAccount"); 654 | if (minidumpAccount && minidumpAccount[0]) form->AddString("UserID", minidumpAccount); 655 | 656 | form->AddString("ExtensionVersion", SMEXT_CONF_VERSION); 657 | form->AddString("ServerID", serverId); 658 | 659 | if (presubmitToken && presubmitToken[0]) { 660 | form->AddString("PresubmitToken", presubmitToken); 661 | } 662 | 663 | form->AddString("debug_identifier", module->debug_identifier().c_str()); 664 | form->AddString("code_identifier", module->code_identifier().c_str()); 665 | 666 | form->AddFile("code_file", codeFile.c_str()); 667 | 668 | MemoryDownloader data; 669 | IWebTransfer *xfer = webternet->CreateSession(); 670 | xfer->SetFailOnHTTPError(true); 671 | 672 | const char *binaryUrl = g_pSM->GetCoreConfigValue("MinidumpBinaryUrl"); 673 | if (!binaryUrl) binaryUrl = "http://crash.limetech.org/binary/submit"; 674 | 675 | bool binaryUploaded = xfer->PostAndDownload(binaryUrl, form, &data, NULL); 676 | 677 | if (!binaryUploaded) { 678 | if (log) fprintf(log, "Binary upload failed: %s (%d)\n", xfer->LastErrorMessage(), xfer->LastErrorCode()); 679 | if (log) fflush(log); 680 | return false; 681 | } 682 | 683 | int responseSize = data.GetSize(); 684 | char *response = new char[responseSize + 1]; 685 | strncpy(response, data.GetBuffer(), responseSize + 1); 686 | response[responseSize] = '\0'; 687 | while (responseSize > 0 && response[responseSize - 1] == '\n') { 688 | response[--responseSize] = '\0'; 689 | } 690 | if (log) fprintf(log, "Binary upload complete: %s\n", response); 691 | if (log) fflush(log); 692 | delete[] response; 693 | 694 | return true; 695 | } 696 | 697 | enum ModuleType { 698 | kMTUnknown, 699 | kMTSystem, 700 | kMTGame, 701 | kMTAddon, 702 | kMTExtension, 703 | }; 704 | 705 | const char *ModuleTypeCode[5] = { 706 | "Unknown", 707 | "System", 708 | "Game", 709 | "Addon", 710 | "Extension", 711 | }; 712 | 713 | #ifndef WIN32 714 | #define PATH_SEP "/" 715 | #else 716 | #define PATH_SEP "\\" 717 | #endif 718 | 719 | bool PathPrefixMatches(const std::string &prefix, const std::string &path) { 720 | #ifndef WIN32 721 | return strncmp(prefix.c_str(), path.c_str(), prefix.length()) == 0; 722 | #else 723 | return _strnicmp(prefix.c_str(), path.c_str(), prefix.length()) == 0; 724 | #endif 725 | } 726 | 727 | struct PathComparator { 728 | struct compare { 729 | bool operator() (const unsigned char &a, const unsigned char &b) const { 730 | #ifndef WIN32 731 | return a < b; 732 | #else 733 | return tolower(a) < tolower(b); 734 | #endif 735 | } 736 | }; 737 | 738 | bool operator() (const std::string &a, const std::string &b) const { 739 | return !std::lexicographical_compare( 740 | a.begin(), a.end(), 741 | b.begin(), b.end(), 742 | compare()); 743 | }; 744 | }; 745 | 746 | std::map modulePathMap; 747 | bool InitModuleClassificationMap(const std::string &base) { 748 | if (!modulePathMap.empty()) { 749 | modulePathMap.clear(); 750 | } 751 | 752 | modulePathMap[base] = kMTGame; 753 | modulePathMap[std::string(crashGamePath) + PATH_SEP "addons" PATH_SEP] = kMTAddon; 754 | modulePathMap[std::string(crashSourceModPath) + PATH_SEP "extensions" PATH_SEP] = kMTExtension; 755 | 756 | return true; 757 | } 758 | 759 | ModuleType ClassifyModule(const google_breakpad::CodeModule *module) { 760 | if (modulePathMap.empty()) { 761 | return kMTUnknown; 762 | } 763 | 764 | const auto &codeFile = module->code_file(); 765 | 766 | #ifndef WIN32 767 | if (codeFile == "linux-gate.so") { 768 | return kMTSystem; 769 | } 770 | 771 | if (codeFile[0] != '/') { 772 | #else 773 | if (codeFile[1] != ':') { 774 | #endif 775 | return kMTUnknown; 776 | } 777 | 778 | for (decltype(modulePathMap)::const_iterator i = modulePathMap.begin(); i != modulePathMap.end(); ++i) { 779 | if (PathPrefixMatches(i->first, codeFile)) { 780 | return i->second; 781 | } 782 | } 783 | 784 | return kMTSystem; 785 | } 786 | 787 | std::string PathnameStripper_Directory(const std::string &path) { 788 | std::string::size_type slash = path.rfind('/'); 789 | std::string::size_type backslash = path.rfind('\\'); 790 | 791 | std::string::size_type file_start = 0; 792 | if (slash != std::string::npos && (backslash == std::string::npos || slash > backslash)) { 793 | file_start = slash + 1; 794 | } else if (backslash != string::npos) { 795 | file_start = backslash + 1; 796 | } 797 | 798 | return path.substr(0, file_start); 799 | } 800 | 801 | enum PresubmitResponse { 802 | kPRLocalError, 803 | kPRRemoteError, 804 | kPRDontUpload, 805 | kPRUploadCrashDumpAndMetadata, 806 | kPRUploadMetadataOnly, 807 | }; 808 | 809 | PresubmitResponse PresubmitCrashDump(const char *path, char *tokenBuffer, size_t tokenBufferLength) { 810 | google_breakpad::ProcessState processState; 811 | google_breakpad::ProcessResult processResult; 812 | google_breakpad::MinidumpProcessor minidumpProcessor(nullptr, nullptr); 813 | 814 | { 815 | ClogInhibitor clogInhibitor; 816 | processResult = minidumpProcessor.Process(path, &processState); 817 | } 818 | 819 | if (processResult != google_breakpad::PROCESS_OK) { 820 | return kPRLocalError; 821 | } 822 | 823 | std::string os_short = ""; 824 | std::string cpu_arch = ""; 825 | if (processState.system_info()) { 826 | os_short = processState.system_info()->os_short; 827 | if (os_short.empty()) { 828 | os_short = processState.system_info()->os; 829 | } 830 | cpu_arch = processState.system_info()->cpu; 831 | } 832 | 833 | int requestingThread = processState.requesting_thread(); 834 | if (requestingThread == -1) { 835 | requestingThread = 0; 836 | } 837 | 838 | const google_breakpad::CallStack *stack = processState.threads()->at(requestingThread); 839 | if (!stack) { 840 | return kPRLocalError; 841 | } 842 | 843 | int frameCount = stack->frames()->size(); 844 | if (frameCount > 1024) { 845 | frameCount = 1024; 846 | } 847 | 848 | std::ostringstream summaryStream; 849 | summaryStream << 2 << "|" << processState.time_date_stamp() << "|" << os_short << "|" << cpu_arch << "|" << processState.crashed() << "|" << processState.crash_reason() << "|" << std::hex << processState.crash_address() << std::dec << "|" << requestingThread; 850 | 851 | std::map moduleMap; 852 | 853 | unsigned int moduleCount = processState.modules() ? processState.modules()->module_count() : 0; 854 | for (unsigned int moduleIndex = 0; moduleIndex < moduleCount; ++moduleIndex) { 855 | auto module = processState.modules()->GetModuleAtIndex(moduleIndex); 856 | moduleMap[module] = moduleIndex; 857 | 858 | auto debugFile = google_breakpad::PathnameStripper::File(module->debug_file()); 859 | auto debugIdentifier = module->debug_identifier(); 860 | 861 | summaryStream << "|M|" << debugFile << "|" << debugIdentifier; 862 | } 863 | 864 | for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { 865 | auto frame = stack->frames()->at(frameIndex); 866 | 867 | int moduleIndex = -1; 868 | auto moduleOffset = frame->ReturnAddress(); 869 | if (frame->module) { 870 | moduleIndex = moduleMap[frame->module]; 871 | moduleOffset -= frame->module->base_address(); 872 | } 873 | 874 | summaryStream << "|F|" << moduleIndex << "|" << std::hex << moduleOffset << std::dec; 875 | } 876 | 877 | auto summaryLine = summaryStream.str(); 878 | // printf("%s\n", summaryLine.c_str()); 879 | 880 | IWebForm *form = webternet->CreateForm(); 881 | 882 | const char *minidumpAccount = g_pSM->GetCoreConfigValue("MinidumpAccount"); 883 | if (minidumpAccount && minidumpAccount[0]) form->AddString("UserID", minidumpAccount); 884 | 885 | form->AddString("ExtensionVersion", SMEXT_CONF_VERSION); 886 | form->AddString("ServerID", serverId); 887 | 888 | form->AddString("CrashSignature", summaryLine.c_str()); 889 | 890 | MemoryDownloader data; 891 | IWebTransfer *xfer = webternet->CreateSession(); 892 | xfer->SetFailOnHTTPError(true); 893 | 894 | const char *minidumpUrl = g_pSM->GetCoreConfigValue("MinidumpUrl"); 895 | if (!minidumpUrl) minidumpUrl = "http://crash.limetech.org/submit"; 896 | 897 | bool uploaded = xfer->PostAndDownload(minidumpUrl, form, &data, NULL); 898 | 899 | if (!uploaded) { 900 | if (log) fprintf(log, "Presubmit failed: %s (%d)\n", xfer->LastErrorMessage(), xfer->LastErrorCode()); 901 | return kPRRemoteError; 902 | } 903 | 904 | int responseSize = data.GetSize(); 905 | char *response = new char[responseSize + 1]; 906 | strncpy(response, data.GetBuffer(), responseSize + 1); 907 | response[responseSize] = '\0'; 908 | while (responseSize > 0 && response[responseSize - 1] == '\n') { 909 | response[--responseSize] = '\0'; 910 | } 911 | //if (log) fprintf(log, "Presubmit complete: %s\n", response); 912 | 913 | if (responseSize < 2) { 914 | if (log) fprintf(log, "Presubmit response too short\n"); 915 | delete[] response; 916 | return kPRRemoteError; 917 | } 918 | 919 | if (response[0] == 'E') { 920 | if (log) fprintf(log, "Presubmit error: %s\n", &response[2]); 921 | delete[] response; 922 | return kPRRemoteError; 923 | } 924 | 925 | PresubmitResponse presubmitResponse = kPRRemoteError; 926 | if (response[0] == 'Y') presubmitResponse = kPRUploadCrashDumpAndMetadata; 927 | else if (response[0] == 'N') presubmitResponse = kPRDontUpload; 928 | else if (response[0] == 'M') presubmitResponse = kPRUploadMetadataOnly; 929 | else return kPRRemoteError; 930 | 931 | if (response[1] != '|') { 932 | if (log) fprintf(log, "Response delimiter missing\n"); 933 | delete[] response; 934 | return kPRRemoteError; 935 | } 936 | 937 | unsigned int responseCount = responseSize - 2; 938 | if (responseCount < moduleCount) { 939 | if (log) fprintf(log, "Response module list doesn't match sent list (%d < %d)\n", responseCount, moduleCount); 940 | delete[] response; 941 | return presubmitResponse; 942 | } 943 | 944 | // There was a presubmit token included. 945 | if (tokenBuffer && responseCount > moduleCount && response[2 + moduleCount] == '|') { 946 | int tokenStart = 2 + moduleCount + 1; 947 | int tokenEnd = tokenStart; 948 | while (tokenEnd < responseSize && response[tokenEnd] != '|') { 949 | tokenEnd++; 950 | } 951 | 952 | size_t tokenLength = tokenEnd - tokenStart; 953 | if (tokenLength < tokenBufferLength) { 954 | strncpy(tokenBuffer, &response[tokenStart], tokenLength); 955 | tokenBuffer[tokenLength] = '\0'; 956 | } 957 | 958 | if (log) fprintf(log, "Got a presubmit token from server: %s\n", tokenBuffer); 959 | } 960 | 961 | if (moduleCount > 0) { 962 | auto mainModule = processState.modules()->GetMainModule(); 963 | auto executableBaseDir = PathnameStripper_Directory(mainModule->code_file()); 964 | InitModuleClassificationMap(executableBaseDir); 965 | 966 | // 0 = Disabled 967 | // 1 = System Only 968 | // 2 = System + Game 969 | // 3 = System + Game + Addons 970 | const char *symbolSubmitOptionStr = g_pSM->GetCoreConfigValue("MinidumpSymbolUpload"); 971 | int symbolSubmitOption = symbolSubmitOptionStr ? atoi(symbolSubmitOptionStr) : 3; 972 | 973 | const char *binarySubmitOption = g_pSM->GetCoreConfigValue("MinidumpBinaryUpload"); 974 | bool canBinarySubmit = !binarySubmitOption || (tolower(binarySubmitOption[0]) == 'y' || binarySubmitOption[0] == '1'); 975 | 976 | for (unsigned int moduleIndex = 0; moduleIndex < moduleCount; ++moduleIndex) { 977 | bool submitSymbols = false; 978 | bool submitBinary = (response[2 + moduleIndex] == 'U'); 979 | 980 | #if defined _LINUX 981 | submitSymbols = (response[2 + moduleIndex] == 'Y'); 982 | #endif 983 | 984 | if (!submitSymbols && !submitBinary) { 985 | continue; 986 | } 987 | if (log) fprintf(log, "Getting module at index %d\n", moduleIndex); 988 | if (log) fflush(log); 989 | 990 | auto module = processState.modules()->GetModuleAtIndex(moduleIndex); 991 | 992 | auto moduleType = ClassifyModule(module); 993 | if (log) fprintf(log, "Classified module %s as %s\n", module->code_file().c_str(), ModuleTypeCode[moduleType]); 994 | if (log) fflush(log); 995 | switch (moduleType) { 996 | case kMTUnknown: 997 | continue; 998 | case kMTSystem: 999 | if (symbolSubmitOption < 1) { 1000 | continue; 1001 | } 1002 | break; 1003 | case kMTGame: 1004 | if (symbolSubmitOption < 2) { 1005 | continue; 1006 | } 1007 | break; 1008 | case kMTAddon: 1009 | case kMTExtension: 1010 | if (symbolSubmitOption < 3) { 1011 | continue; 1012 | } 1013 | break; 1014 | } 1015 | 1016 | if (canBinarySubmit && submitBinary) { 1017 | UploadModuleFile(module, tokenBuffer); 1018 | } 1019 | 1020 | #if defined _LINUX 1021 | if (submitSymbols) { 1022 | UploadSymbolFile(module, tokenBuffer); 1023 | } 1024 | #endif 1025 | } 1026 | } 1027 | if (log) fprintf(log, "PresubmitCrashDump complete\n"); 1028 | if (log) fflush(log); 1029 | 1030 | delete[] response; 1031 | return presubmitResponse; 1032 | } 1033 | 1034 | bool UploadCrashDump(const char *path, const char *metapath, const char *presubmitToken, char *response, int maxlen) { 1035 | IWebForm *form = webternet->CreateForm(); 1036 | 1037 | const char *minidumpAccount = g_pSM->GetCoreConfigValue("MinidumpAccount"); 1038 | if (minidumpAccount && minidumpAccount[0]) form->AddString("UserID", minidumpAccount); 1039 | 1040 | form->AddString("GameDirectory", crashGameDirectory); 1041 | form->AddString("ExtensionVersion", SMEXT_CONF_VERSION); 1042 | form->AddString("ServerID", serverId); 1043 | 1044 | if (presubmitToken && presubmitToken[0]) { 1045 | form->AddString("PresubmitToken", presubmitToken); 1046 | } 1047 | 1048 | if (path && path[0]) { 1049 | form->AddFile("upload_file_minidump", path); 1050 | } 1051 | 1052 | if (metapath && metapath[0]) { 1053 | form->AddFile("upload_file_metadata", metapath); 1054 | } 1055 | 1056 | MemoryDownloader data; 1057 | IWebTransfer *xfer = webternet->CreateSession(); 1058 | xfer->SetFailOnHTTPError(true); 1059 | 1060 | const char *minidumpUrl = g_pSM->GetCoreConfigValue("MinidumpUrl"); 1061 | if (!minidumpUrl) minidumpUrl = "http://crash.limetech.org/submit"; 1062 | 1063 | bool uploaded = xfer->PostAndDownload(minidumpUrl, form, &data, NULL); 1064 | 1065 | if (response) { 1066 | if (uploaded) { 1067 | int responseSize = data.GetSize(); 1068 | if (responseSize >= maxlen) responseSize = maxlen - 1; 1069 | strncpy(response, data.GetBuffer(), responseSize); 1070 | response[responseSize] = '\0'; 1071 | while (responseSize > 0 && response[responseSize - 1] == '\n') { 1072 | response[--responseSize] = '\0'; 1073 | } 1074 | } else { 1075 | g_pSM->Format(response, maxlen, "%s (%d)", xfer->LastErrorMessage(), xfer->LastErrorCode()); 1076 | } 1077 | } 1078 | 1079 | return uploaded; 1080 | } 1081 | } uploadThread; 1082 | 1083 | class VFuncEmptyClass {}; 1084 | 1085 | const char *GetCmdLine() 1086 | { 1087 | static int getCmdLineOffset = 0; 1088 | if (getCmdLineOffset == 0) { 1089 | if (!gameconfig || !gameconfig->GetOffset("GetCmdLine", &getCmdLineOffset)) { 1090 | return ""; 1091 | } 1092 | if (getCmdLineOffset == 0) { 1093 | return ""; 1094 | } 1095 | } 1096 | 1097 | void *cmdline = gamehelpers->GetValveCommandLine(); 1098 | void **vtable = *(void ***)cmdline; 1099 | void *vfunc = vtable[getCmdLineOffset]; 1100 | 1101 | union { 1102 | const char *(VFuncEmptyClass::*mfpnew)(); 1103 | #ifndef WIN32 1104 | struct { 1105 | void *addr; 1106 | intptr_t adjustor; 1107 | } s; 1108 | } u; 1109 | u.s.addr = vfunc; 1110 | u.s.adjustor = 0; 1111 | #else 1112 | void *addr; 1113 | } u; 1114 | u.addr = vfunc; 1115 | #endif 1116 | 1117 | return (const char *)(reinterpret_cast(cmdline)->*u.mfpnew)(); 1118 | } 1119 | 1120 | bool Accelerator::SDK_OnLoad(char *error, size_t maxlength, bool late) 1121 | { 1122 | sharesys->AddDependency(myself, "webternet.ext", true, true); 1123 | SM_GET_IFACE(WEBTERNET, webternet); 1124 | 1125 | g_pSM->BuildPath(Path_SM, dumpStoragePath, sizeof(dumpStoragePath), "data/dumps"); 1126 | 1127 | if (!libsys->IsPathDirectory(dumpStoragePath)) 1128 | { 1129 | if (!libsys->CreateFolder(dumpStoragePath)) 1130 | { 1131 | if (error) 1132 | g_pSM->Format(error, maxlength, "%s didn't exist and we couldn't create it :(", dumpStoragePath); 1133 | return false; 1134 | } 1135 | } 1136 | 1137 | g_pSM->BuildPath(Path_SM, logPath, sizeof(logPath), "logs/accelerator.log"); 1138 | 1139 | // Get these early so the upload thread can use them. 1140 | strncpy(crashGamePath, g_pSM->GetGamePath(), sizeof(crashGamePath) - 1); 1141 | strncpy(crashSourceModPath, g_pSM->GetSourceModPath(), sizeof(crashSourceModPath) - 1); 1142 | strncpy(crashGameDirectory, g_pSM->GetGameFolderName(), sizeof(crashGameDirectory) - 1); 1143 | 1144 | threader->MakeThread(&uploadThread); 1145 | 1146 | do { 1147 | char gameconfigError[256]; 1148 | if (!gameconfs->LoadGameConfigFile("accelerator.games", &gameconfig, gameconfigError, sizeof(gameconfigError))) { 1149 | smutils->LogMessage(myself, "WARNING: Failed to load gamedata file, console output and command line will not be included in crash reports: %s", gameconfigError); 1150 | break; 1151 | } 1152 | 1153 | bool useFastcall = false; 1154 | 1155 | 1156 | #if defined _WINDOWS 1157 | const char *fastcall = gameconfig->GetKeyValue("UseFastcall"); 1158 | if (fastcall && strcmp(fastcall, "yes") == 0) { 1159 | useFastcall = true; 1160 | } 1161 | 1162 | if (useFastcall && !gameconfig->GetMemSig("GetSpewFastcall", (void **)&GetSpewFastcall)) { 1163 | smutils->LogMessage(myself, "WARNING: GetSpewFastcall not found in gamedata, console output will not be included in crash reports."); 1164 | break; 1165 | } 1166 | #endif 1167 | 1168 | if (!useFastcall && !gameconfig->GetMemSig("GetSpew", (void **)&GetSpew)) { 1169 | smutils->LogMessage(myself, "WARNING: GetSpew not found in gamedata, console output will not be included in crash reports."); 1170 | break; 1171 | } 1172 | 1173 | if (!GetSpew 1174 | #if defined _WINDOWS 1175 | && !GetSpewFastcall 1176 | #endif 1177 | ) { 1178 | smutils->LogMessage(myself, "WARNING: Sigscan for GetSpew failed, console output will not be included in crash reports."); 1179 | break; 1180 | } 1181 | } while(false); 1182 | 1183 | #if defined _LINUX 1184 | google_breakpad::MinidumpDescriptor descriptor(dumpStoragePath); 1185 | handler = new google_breakpad::ExceptionHandler(descriptor, NULL, dumpCallback, NULL, true, -1); 1186 | 1187 | struct sigaction oact; 1188 | sigaction(SIGSEGV, NULL, &oact); 1189 | SignalHandler = oact.sa_sigaction; 1190 | 1191 | g_pSM->AddGameFrameHook(OnGameFrame); 1192 | #elif defined _WINDOWS 1193 | wchar_t *buf = new wchar_t[sizeof(dumpStoragePath)]; 1194 | size_t num_chars = mbstowcs(buf, dumpStoragePath, sizeof(dumpStoragePath)); 1195 | 1196 | handler = new google_breakpad::ExceptionHandler( 1197 | std::wstring(buf, num_chars), NULL, dumpCallback, NULL, google_breakpad::ExceptionHandler::HANDLER_ALL, 1198 | static_cast(MiniDumpWithUnloadedModules | MiniDumpWithFullMemoryInfo), static_cast(NULL), NULL); 1199 | 1200 | vectoredHandler = AddVectoredExceptionHandler(0, BreakpadVectoredHandler); 1201 | 1202 | delete buf; 1203 | #else 1204 | #error Bad platform. 1205 | #endif 1206 | 1207 | do { 1208 | char spJitPath[512]; 1209 | g_pSM->BuildPath(Path_SM, spJitPath, sizeof(spJitPath), "bin/" PLATFORM_ARCH_FOLDER "sourcepawn.jit.x86." PLATFORM_LIB_EXT); 1210 | 1211 | char spJitError[255]; 1212 | std::unique_ptr spJit(libsys->OpenLibrary(spJitPath, spJitError, sizeof(spJitError))); 1213 | if (!spJit) { 1214 | smutils->LogMessage(myself, "WARNING: Failed to load SourcePawn library %s: %s", spJitPath, spJitError); 1215 | break; 1216 | } 1217 | 1218 | GetSourcePawnFactoryFn factoryFn = (GetSourcePawnFactoryFn)spJit->GetSymbolAddress("GetSourcePawnFactory"); 1219 | if (!factoryFn) { 1220 | smutils->LogMessage(myself, "WARNING: SourcePawn library is out of date: No factory function."); 1221 | break; 1222 | } 1223 | 1224 | ISourcePawnFactory *spFactory = factoryFn(0x0207); 1225 | if (!spFactory) { 1226 | smutils->LogMessage(myself, "WARNING: SourcePawn library is out of date: Failed to get version 2.7", 0x0207); 1227 | break; 1228 | } 1229 | 1230 | ISourcePawnEnvironment *spEnvironment = spFactory->CurrentEnvironment(); 1231 | if (!spEnvironment) { 1232 | smutils->LogMessage(myself, "WARNING: Could not get SourcePawn environment."); 1233 | break; 1234 | } 1235 | 1236 | ISourcePawnEngine2 *spEngine2 = spEnvironment->APIv2(); 1237 | if (!spEngine2) { 1238 | smutils->LogMessage(myself, "WARNING: Could not get SourcePawn engine2."); 1239 | break; 1240 | } 1241 | 1242 | strncpy(crashSourceModVersion, spEngine2->GetVersionString(), sizeof(crashSourceModVersion)); 1243 | } while(false); 1244 | 1245 | plsys->AddPluginsListener(this); 1246 | 1247 | IPluginIterator *iterator = plsys->GetPluginIterator(); 1248 | while (iterator->MorePlugins()) { 1249 | IPlugin *plugin = iterator->GetPlugin(); 1250 | if (plugin->GetStatus() == Plugin_Running) { 1251 | this->OnPluginLoaded(plugin); 1252 | } 1253 | iterator->NextPlugin(); 1254 | } 1255 | delete iterator; 1256 | 1257 | strncpy(crashCommandLine, GetCmdLine(), sizeof(crashCommandLine) - 1); 1258 | 1259 | char steamInfPath[512]; 1260 | g_pSM->BuildPath(Path_Game, steamInfPath, sizeof(steamInfPath), "steam.inf"); 1261 | 1262 | FILE *steamInfFile = fopen(steamInfPath, "rb"); 1263 | if (steamInfFile) { 1264 | char steamInfTemp[1024] = {0}; 1265 | fread(steamInfTemp, sizeof(char), sizeof(steamInfTemp) - 1, steamInfFile); 1266 | 1267 | fclose(steamInfFile); 1268 | 1269 | unsigned commentChars = 0; 1270 | unsigned valueChars = 0; 1271 | unsigned source = 0; 1272 | strcpy(steamInf, "\nSteam_"); 1273 | unsigned target = 7; // strlen("\nSteam_"); 1274 | while (true) { 1275 | if (steamInfTemp[source] == '\0') { 1276 | source++; 1277 | break; 1278 | } 1279 | if (steamInfTemp[source] == '/') { 1280 | source++; 1281 | commentChars++; 1282 | continue; 1283 | } 1284 | if (commentChars == 1) { 1285 | commentChars = 0; 1286 | steamInf[target++] = '/'; 1287 | valueChars++; 1288 | } 1289 | if (steamInfTemp[source] == '\r') { 1290 | source++; 1291 | continue; 1292 | } 1293 | if (steamInfTemp[source] == '\n') { 1294 | commentChars = 0; 1295 | source++; 1296 | if (steamInfTemp[source] == '\0') { 1297 | break; 1298 | } 1299 | if (valueChars > 0) { 1300 | valueChars = 0; 1301 | strcpy(&steamInf[target], "\nSteam_"); 1302 | target += 7; 1303 | } 1304 | continue; 1305 | } 1306 | if (commentChars >= 2) { 1307 | source++; 1308 | continue; 1309 | } 1310 | steamInf[target++] = steamInfTemp[source++]; 1311 | valueChars++; 1312 | } 1313 | } 1314 | 1315 | if (late) { 1316 | this->OnCoreMapStart(NULL, 0, 0); 1317 | } 1318 | 1319 | return true; 1320 | } 1321 | 1322 | void Accelerator::SDK_OnUnload() 1323 | { 1324 | plsys->RemovePluginsListener(this); 1325 | 1326 | #if defined _LINUX 1327 | g_pSM->RemoveGameFrameHook(OnGameFrame); 1328 | #elif defined _WINDOWS 1329 | if (vectoredHandler) { 1330 | RemoveVectoredExceptionHandler(vectoredHandler); 1331 | } 1332 | #else 1333 | #error Bad platform. 1334 | #endif 1335 | 1336 | delete handler; 1337 | } 1338 | 1339 | void Accelerator::OnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax) 1340 | { 1341 | strncpy(crashMap, gamehelpers->GetCurrentMap(), sizeof(crashMap) - 1); 1342 | } 1343 | 1344 | /* 010 Editor Template 1345 | uint64 headerMagic; 1346 | uint32 version; 1347 | uint32 size; 1348 | uint32 count; 1349 | struct { 1350 | uint32 size; 1351 | uint32 context ; 1352 | char file[]; 1353 | uint32 count; 1354 | struct { 1355 | uint32 pcode ; 1356 | char name[]; 1357 | } functions[count] ; 1358 | } plugins[count] ; 1359 | uint64 tailMagic; 1360 | */ 1361 | 1362 | unsigned char *serializedPluginContexts = nullptr; 1363 | std::map pluginContextMap; 1364 | 1365 | void SerializePluginContexts() 1366 | { 1367 | if (serializedPluginContexts) { 1368 | handler->UnregisterAppMemory(serializedPluginContexts); 1369 | free(serializedPluginContexts); 1370 | serializedPluginContexts = nullptr; 1371 | } 1372 | 1373 | uint32_t count = pluginContextMap.size(); 1374 | if (count == 0) { 1375 | return; 1376 | } 1377 | 1378 | uint32_t size = 0; 1379 | size += sizeof(uint64_t); // header magic 1380 | size += sizeof(uint32_t); // version 1381 | size += sizeof(uint32_t); // size 1382 | size += sizeof(uint32_t); // count 1383 | 1384 | for (auto &it : pluginContextMap) { 1385 | unsigned char *buffer = it.second; 1386 | 1387 | uint32_t bufferSize; 1388 | memcpy(&bufferSize, buffer, sizeof(uint32_t)); 1389 | 1390 | size += bufferSize; 1391 | } 1392 | 1393 | size += sizeof(uint64_t); // tail magic 1394 | 1395 | serializedPluginContexts = (unsigned char *)malloc(size); 1396 | handler->RegisterAppMemory(serializedPluginContexts, size); 1397 | unsigned char *cursor = serializedPluginContexts; 1398 | 1399 | uint64_t headerMagic = 103582791429521979ULL; 1400 | memcpy(cursor, &headerMagic, sizeof(uint64_t)); 1401 | cursor += sizeof(uint64_t); 1402 | 1403 | uint32_t version = 1; 1404 | memcpy(cursor, &version, sizeof(uint32_t)); 1405 | cursor += sizeof(uint32_t); 1406 | 1407 | memcpy(cursor, &size, sizeof(uint32_t)); 1408 | cursor += sizeof(uint32_t); 1409 | 1410 | memcpy(cursor, &count, sizeof(uint32_t)); 1411 | cursor += sizeof(uint32_t); 1412 | 1413 | for (auto &it : pluginContextMap) { 1414 | unsigned char *buffer = it.second; 1415 | 1416 | uint32_t bufferSize; 1417 | memcpy(&bufferSize, buffer, sizeof(uint32_t)); 1418 | 1419 | memcpy(cursor, buffer, bufferSize); 1420 | cursor += bufferSize; 1421 | } 1422 | 1423 | uint64_t tailMagic = 76561197987819599ULL; 1424 | memcpy(cursor, &tailMagic, sizeof(uint64_t)); 1425 | cursor += sizeof(uint64_t); 1426 | } 1427 | 1428 | void Accelerator::OnPluginLoaded(IPlugin *plugin) 1429 | { 1430 | IPluginRuntime *runtime = plugin->GetRuntime(); 1431 | IPluginContext *context = plugin->GetBaseContext(); 1432 | if (!runtime || !context) { 1433 | return; 1434 | } 1435 | 1436 | const char *filename = plugin->GetFilename(); 1437 | size_t filenameSize = strlen(filename) + 1; 1438 | 1439 | uint32_t size = 0; 1440 | size += sizeof(uint32_t); // size 1441 | size += sizeof(void *); // GetBaseContext 1442 | size += filenameSize; 1443 | 1444 | uint32_t count = runtime->GetPublicsNum(); 1445 | size += sizeof(uint32_t); // count 1446 | size += count * sizeof(uint32_t); // pubinfo->code_offs 1447 | 1448 | for (uint32_t i = 0; i < count; ++i) { 1449 | sp_public_t *pubinfo; 1450 | runtime->GetPublicByIndex(i, &pubinfo); 1451 | 1452 | size += strlen(pubinfo->name) + 1; 1453 | } 1454 | 1455 | unsigned char *buffer = (unsigned char *)malloc(size); 1456 | unsigned char *cursor = buffer; 1457 | 1458 | memcpy(cursor, &size, sizeof(uint32_t)); 1459 | cursor += sizeof(uint32_t); 1460 | 1461 | memcpy(cursor, &context, sizeof(void *)); 1462 | cursor += sizeof(void *); 1463 | 1464 | memcpy(cursor, filename, filenameSize); 1465 | cursor += filenameSize; 1466 | 1467 | memcpy(cursor, &count, sizeof(uint32_t)); 1468 | cursor += sizeof(uint32_t); 1469 | 1470 | for (uint32_t i = 0; i < count; ++i) { 1471 | sp_public_t *pubinfo; 1472 | runtime->GetPublicByIndex(i, &pubinfo); 1473 | 1474 | memcpy(cursor, &pubinfo->code_offs, sizeof(uint32_t)); 1475 | cursor += sizeof(uint32_t); 1476 | 1477 | size_t nameSize = strlen(pubinfo->name) + 1; 1478 | memcpy(cursor, pubinfo->name, nameSize); 1479 | cursor += nameSize; 1480 | } 1481 | 1482 | pluginContextMap[context] = buffer; 1483 | 1484 | SerializePluginContexts(); 1485 | } 1486 | 1487 | void Accelerator::OnPluginUnloaded(IPlugin *plugin) 1488 | { 1489 | IPluginContext *context = plugin->GetBaseContext(); 1490 | if (!context) { 1491 | return; 1492 | } 1493 | 1494 | auto it = pluginContextMap.find(context); 1495 | if (it != pluginContextMap.end()) { 1496 | free(it->second); 1497 | pluginContextMap.erase(it); 1498 | } 1499 | 1500 | SerializePluginContexts(); 1501 | } 1502 | -------------------------------------------------------------------------------- /extension/extension.h: -------------------------------------------------------------------------------- 1 | /** 2 | * ============================================================================= 3 | * Accelerator Extension 4 | * Copyright (C) 2009-2010 Asher Baker (asherkin). All rights reserved. 5 | * ============================================================================= 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License, version 3.0, as published by the 9 | * Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 14 | * details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * this program. If not, see . 18 | * 19 | */ 20 | 21 | #ifndef _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_ 22 | #define _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_ 23 | 24 | /** 25 | * @file extension.hpp 26 | * @brief Accelerator extension code header. 27 | */ 28 | 29 | #include "smsdk_ext.h" 30 | 31 | /** 32 | * @brief Sample implementation of the SDK Extension. 33 | */ 34 | class Accelerator : public SDKExtension, IPluginsListener 35 | { 36 | public: // SDKExtension 37 | /** 38 | * @brief This is called after the initial loading sequence has been processed. 39 | * 40 | * @param error Error message buffer. 41 | * @param maxlength Size of error message buffer. 42 | * @param late Whether or not the module was loaded after map load. 43 | * @return True to succeed loading, false to fail. 44 | */ 45 | virtual bool SDK_OnLoad(char *error, size_t maxlen, bool late); 46 | 47 | /** 48 | * @brief This is called right before the extension is unloaded. 49 | */ 50 | virtual void SDK_OnUnload(); 51 | 52 | /** 53 | * @brief Called on server activation before plugins receive the OnServerLoad forward. 54 | * 55 | * @param pEdictList Edicts list. 56 | * @param edictCount Number of edicts in the list. 57 | * @param clientMax Maximum number of clients allowed in the server. 58 | */ 59 | virtual void OnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax); 60 | 61 | public: // IPluginsListener 62 | /** 63 | * @brief Called when a plugin is fully loaded successfully. 64 | */ 65 | virtual void OnPluginLoaded(IPlugin *plugin); 66 | 67 | /** 68 | * @brief Called when a plugin is unloaded (only if fully loaded). 69 | */ 70 | virtual void OnPluginUnloaded(IPlugin *plugin); 71 | }; 72 | 73 | #endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_ 74 | -------------------------------------------------------------------------------- /extension/smsdk_config.h: -------------------------------------------------------------------------------- 1 | /** 2 | * ============================================================================= 3 | * Accelerator Extension 4 | * Copyright (C) 2011 Asher Baker (asherkin). All rights reserved. 5 | * ============================================================================= 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License, version 3.0, as published by the 9 | * Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 14 | * details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * this program. If not, see . 18 | * 19 | * As a special exception, AlliedModders LLC gives you permission to link the 20 | * code of this program (as well as its derivative works) to "Half-Life 2," the 21 | * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software 22 | * by the Valve Corporation. You must obey the GNU General Public License in 23 | * all respects for all other code used. Additionally, AlliedModders LLC grants 24 | * this exception to all derivative works. AlliedModders LLC defines further 25 | * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), 26 | * or . 27 | */ 28 | 29 | #ifndef _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ 30 | #define _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ 31 | 32 | /** 33 | * @file smsdk_config.hpp 34 | * @brief Contains macros for configuring basic extension information. 35 | */ 36 | 37 | #include "version.h" // SM_FULL_VERSION 38 | 39 | /* Basic information exposed publicly */ 40 | #define SMEXT_CONF_NAME "Accelerator" 41 | #define SMEXT_CONF_DESCRIPTION "SRCDS Crash Handler" 42 | #define SMEXT_CONF_VERSION SM_FULL_VERSION 43 | #define SMEXT_CONF_AUTHOR "asherkin, kenzzer" 44 | #define SMEXT_CONF_URL "https://crash.limetech.org/" 45 | #define SMEXT_CONF_LOGTAG "CRASH" 46 | #define SMEXT_CONF_LICENSE "GPL" 47 | #define SMEXT_CONF_DATESTRING __DATE__ 48 | 49 | /** 50 | * @brief Exposes plugin's main interface. 51 | */ 52 | #define SMEXT_LINK(name) SDKExtension *g_pExtensionIface = name; 53 | 54 | /** 55 | * @brief Sets whether or not this plugin required Metamod. 56 | * NOTE: Uncomment to enable, comment to disable. 57 | */ 58 | //#define SMEXT_CONF_METAMOD 59 | 60 | /** Enable interfaces you want to use here by uncommenting lines */ 61 | //#define SMEXT_ENABLE_FORWARDSYS 62 | //#define SMEXT_ENABLE_HANDLESYS 63 | //#define SMEXT_ENABLE_PLAYERHELPERS 64 | //#define SMEXT_ENABLE_DBMANAGER 65 | #define SMEXT_ENABLE_GAMECONF 66 | //#define SMEXT_ENABLE_MEMUTILS 67 | #define SMEXT_ENABLE_GAMEHELPERS 68 | //#define SMEXT_ENABLE_TIMERSYS 69 | #define SMEXT_ENABLE_THREADER 70 | #define SMEXT_ENABLE_LIBSYS 71 | //#define SMEXT_ENABLE_MENUS 72 | //#define SMEXT_ENABLE_ADTFACTORY 73 | #define SMEXT_ENABLE_PLUGINSYS 74 | //#define SMEXT_ENABLE_ADMINSYS 75 | //#define SMEXT_ENABLE_TEXTPARSERS 76 | //#define SMEXT_ENABLE_USERMSGS 77 | //#define SMEXT_ENABLE_TRANSLATOR 78 | //#define SMEXT_ENABLE_NINVOKE 79 | #define SMEXT_ENABLE_ROOTCONSOLEMENU 80 | 81 | #endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ 82 | -------------------------------------------------------------------------------- /extension/version.h: -------------------------------------------------------------------------------- 1 | #ifndef _INCLUDE_VERSION_INFORMATION_H_ 2 | #define _INCLUDE_VERSION_INFORMATION_H_ 3 | 4 | /** 5 | * @file Contains version information. 6 | * @brief This file will redirect to an autogenerated version if being compiled via 7 | * the build scripts. 8 | */ 9 | 10 | #if defined SM_GENERATED_BUILD 11 | #include "version_auto.h" 12 | #else 13 | 14 | #ifndef SM_GENERATED_BUILD 15 | #undef BINARY_NAME 16 | #define BINARY_NAME "accelerator.ext.dll\0" 17 | #endif 18 | 19 | #define SM_BUILD_TAG "-manual" 20 | #define SM_BUILD_UNIQUEID "[MANUAL BUILD]" 21 | #define SM_VERSION "2.6.0" 22 | #define SM_FULL_VERSION SM_VERSION SM_BUILD_TAG 23 | #define SM_FILE_VERSION 2,6,0,0 24 | 25 | #endif 26 | 27 | #endif /* _INCLUDE_VERSION_INFORMATION_H_ */ 28 | -------------------------------------------------------------------------------- /extension/version.rc: -------------------------------------------------------------------------------- 1 | #include "winres.h" 2 | 3 | #include 4 | 5 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 6 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 7 | #pragma code_page(1252) 8 | #endif 9 | 10 | #ifndef SM_GENERATED_BUILD 11 | #define BINARY_NAME "accelerator.ext.dll\0" 12 | #endif 13 | 14 | VS_VERSION_INFO VERSIONINFO 15 | FILEVERSION SM_FILE_VERSION 16 | PRODUCTVERSION SM_FILE_VERSION 17 | FILEFLAGSMASK 0x17L 18 | #ifdef _DEBUG 19 | FILEFLAGS 0x1L 20 | #else 21 | FILEFLAGS 0x0L 22 | #endif 23 | FILEOS 0x4L 24 | FILETYPE 0x2L 25 | FILESUBTYPE 0x0L 26 | BEGIN 27 | BLOCK "StringFileInfo" 28 | BEGIN 29 | BLOCK "040904b0" 30 | BEGIN 31 | VALUE "Comments", "Accelerator Extension" 32 | VALUE "FileDescription", "SourceMod Accelerator Extension" 33 | VALUE "FileVersion", SM_BUILD_UNIQUEID 34 | VALUE "InternalName", "Accelerator" 35 | VALUE "LegalCopyright", "Copyright (c) 2012, Asher Baker" 36 | VALUE "OriginalFilename", BINARY_NAME 37 | VALUE "ProductName", "Accelerator Extension" 38 | VALUE "ProductVersion", SM_FULL_VERSION 39 | END 40 | END 41 | BLOCK "VarFileInfo" 42 | BEGIN 43 | VALUE "Translation", 0x0409, 0x04B0 44 | END 45 | END 46 | -------------------------------------------------------------------------------- /gamedata/accelerator.games.txt: -------------------------------------------------------------------------------- 1 | "Games" 2 | { 3 | "#default" 4 | { 5 | "Offsets" 6 | { 7 | /* 8 | * This has never changed, let us hope it never does. 9 | */ 10 | "GetCmdLine" 11 | { 12 | "windows" "2" 13 | "linux" "2" 14 | "linux64" "2" 15 | } 16 | } 17 | 18 | "Signatures" 19 | { 20 | /* 21 | * String: "\nConsole History (reversed)\n\n" 22 | * It's the function in the following branch. 23 | */ 24 | "GetSpew" 25 | { 26 | "library" "engine" 27 | "linux" "@_Z7GetSpewPcj" 28 | "linux64" "@_Z7GetSpewPcm" 29 | } 30 | } 31 | } 32 | 33 | "tf" 34 | { 35 | "Signatures" 36 | { 37 | "GetSpew" 38 | { 39 | "library" "engine" 40 | "windows" "\x55\x8B\xEC\x83\xEC\x20\x8D\x45\xE0\x53\x57\x6A\x01\x68\x49\x03\x00\x00" 41 | } 42 | } 43 | } 44 | 45 | "cstrike" 46 | { 47 | "Signatures" 48 | { 49 | "GetSpew" 50 | { 51 | "library" "engine" 52 | "windows" "\x55\x8B\xEC\x53\xFF\x15\x2A\x2A\x2A\x2A\x8B\xD0\xBB\x2A\x2A\x2A\x2A\x3B\x15\x2A\x2A\x2A\x2A\x74\x2A\x8B\xCA\x33\xC0\xF0\x0F\xB1\x0B\x85\xC0\x74\x2A\xF3\x90\x6A\x00\x52\x8B\xCB\xFF\x15\x2A\x2A\x2A\x2A\xEB\x2A\xFF\x05\x2A\x2A\x2A\x2A\x0F\xB7\x05\x2A\x2A\x2A\x2A\x57" 53 | } 54 | } 55 | } 56 | 57 | "hl2mp" 58 | { 59 | "Signatures" 60 | { 61 | "GetSpew" 62 | { 63 | "library" "engine" 64 | "windows" "\x55\x8B\xEC\x53\xFF\x15\x2A\x2A\x2A\x2A\x8B\xD0\xBB\x2A\x2A\x2A\x2A\x3B\x15\x2A\x2A\x2A\x2A\x74\x2A\x8B\xCA\x33\xC0\xF0\x0F\xB1\x0B\x85\xC0\x74\x2A\xF3\x90\x6A\x00\x52\x8B\xCB\xFF\x15\x2A\x2A\x2A\x2A\xEB\x2A\xFF\x05\x2A\x2A\x2A\x2A\x0F\xB7\x05\x2A\x2A\x2A\x2A\x57" 65 | } 66 | } 67 | } 68 | 69 | "dod" 70 | { 71 | "Signatures" 72 | { 73 | "GetSpew" 74 | { 75 | "library" "engine" 76 | "windows" "\x55\x8B\xEC\x83\xEC\x20\x8D\x45\xE0\x53\x57\x6A\x01\x68\x49" 77 | } 78 | } 79 | } 80 | 81 | "csgo" 82 | { 83 | "Keys" 84 | { 85 | "UseFastcall" "yes" 86 | } 87 | 88 | "Signatures" 89 | { 90 | "GetSpew" 91 | { 92 | "library" "engine" 93 | "linux" "\x55\x89\xE5\x57\x56\x53\x83\xEC\x1C\xE8\x2A\x2A\x2A\x2A\x89\xC2\xA1\x2A\x2A\x2A\x2A\x39\xD0\x0F\x84\x2A\x2A\x2A\x2A\x31\xC0\xF0\x0F\xB1\x15\x2A\x2A\x2A\x2A\x0F\x84\x2A\x2A\x2A\x2A\xF3\x90\x83\xEC\x04\x6A\x00\x52\x68\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A\x83\xC4\x10\x8B\x45\x2A" 94 | } 95 | 96 | "GetSpewFastcall" 97 | { 98 | "library" "engine" 99 | "windows" "\x55\x8B\xEC\x51\x53\x56\x57\x8B\xFA\x8B\xD9" 100 | } 101 | } 102 | } 103 | 104 | "left4dead" 105 | { 106 | "Signatures" 107 | { 108 | "GetSpew" 109 | { 110 | "library" "engine" 111 | "windows" "\x53\x56\xFF\x15\x2A\x2A\x2A\x2A\x8B\xC8\xA1\x2A\x2A\x2A\x2A\x3B\xC8" 112 | /* 53 56 FF 15 ? ? ? ? 8B C8 A1 ? ? ? ? 3B C8 */ 113 | } 114 | } 115 | } 116 | 117 | "left4dead2" 118 | { 119 | "Signatures" 120 | { 121 | "GetSpew" 122 | { 123 | "library" "engine" 124 | "windows" "\x55\x8B\xEC\x53\x56\xFF\x15\x2A\x2A\x2A\x2A\x8B\xC8\xA1\x2A\x2A\x2A\x2A\x3B\xC8" 125 | /* 55 8B EC 53 56 FF 15 ? ? ? ? 8B C8 A1 ? ? ? ? 3B C8 */ // Thanks to "cravenge" for the signature 126 | } 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /patches/0001-Ignore-invalid-modules-rather-than-bailing-on-the-en.patch: -------------------------------------------------------------------------------- 1 | From 1f35257db226d6d0189cff0832a97cccc639c91a Mon Sep 17 00:00:00 2001 2 | From: Asher Baker 3 | Date: Sun, 13 Jan 2019 12:34:45 +0000 4 | Subject: [PATCH 1/5] Ignore invalid modules rather than bailing on the entire 5 | module list 6 | 7 | --- 8 | src/processor/minidump.cc | 13 ++++++++++--- 9 | 1 file changed, 10 insertions(+), 3 deletions(-) 10 | 11 | diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc 12 | index 83e5a868..a7d564f2 100644 13 | --- a/src/processor/minidump.cc 14 | +++ b/src/processor/minidump.cc 15 | @@ -3195,7 +3195,7 @@ bool MinidumpModuleList::Read(uint32_t expected_size) { 16 | BPLOG(ERROR) << "MinidumpModuleList could not read required module " 17 | "auxiliary data for module " << 18 | module_index << "/" << module_count; 19 | - return false; 20 | + continue; 21 | } 22 | 23 | // It is safe to use module->code_file() after successfully calling 24 | @@ -3207,7 +3207,14 @@ bool MinidumpModuleList::Read(uint32_t expected_size) { 25 | BPLOG(ERROR) << "MinidumpModuleList found bad base address for module " 26 | << module_index << "/" << module_count << ", " 27 | << module.code_file(); 28 | - return false; 29 | + continue; 30 | + } 31 | + 32 | + if (module_size == static_cast(-1)) { 33 | + BPLOG(ERROR) << "MinidumpModuleList found bad size for module " 34 | + << module_index << "/" << module_count << ", " 35 | + << module.code_file(); 36 | + continue; 37 | } 38 | 39 | // Some minidumps have additional modules in the list that are duplicates. 40 | @@ -3234,7 +3241,7 @@ bool MinidumpModuleList::Read(uint32_t expected_size) { 41 | << module_index << "/" << module_count << ", " 42 | << module.code_file() << ", " << HexString(base_address) 43 | << "+" << HexString(module_size); 44 | - return false; 45 | + continue; 46 | } 47 | 48 | // If failed due to apparent range overlap the cause may be the client 49 | -------------------------------------------------------------------------------- /patches/0002-Write-FUNC-records-instead-of-PUBLIC-for-ELF-symbols.patch: -------------------------------------------------------------------------------- 1 | From 8aaf6e84a6704eb538f68a3e6fb6c3a8c93f1d8d Mon Sep 17 00:00:00 2001 2 | From: Asher Baker 3 | Date: Sun, 13 Jan 2019 12:35:05 +0000 4 | Subject: [PATCH 2/5] Write FUNC records instead of PUBLIC for ELF symbols with 5 | sizes 6 | --- 7 | src/common/linux/elf_symbols_to_module.cc | 17 +++++++++++++---- 8 | 1 file changed, 13 insertions(+), 4 deletions(-) 9 | 10 | 11 | diff --git a/src/common/linux/elf_symbols_to_module.cc b/src/common/linux/elf_symbols_to_module.cc 12 | index 70d50f89..f21460bf 100644 13 | --- a/src/common/linux/elf_symbols_to_module.cc 14 | +++ b/src/common/linux/elf_symbols_to_module.cc 15 | @@ -163,19 +163,28 @@ bool ELFSymbolsToModule(const uint8_t* symtab_section, 16 | while(!iterator->at_end) { 17 | if (ELF32_ST_TYPE(iterator->info) == STT_FUNC && 18 | iterator->shndx != SHN_UNDEF) { 19 | - auto ext = std::make_unique(iterator->value); 20 | - ext->name = SymbolString(iterator->name_offset, strings); 21 | + string name = SymbolString(iterator->name_offset, strings); 22 | #if !defined(__ANDROID__) // Android NDK doesn't provide abi::__cxa_demangle. 23 | int status = 0; 24 | char* demangled = 25 | - abi::__cxa_demangle(ext->name.c_str(), NULL, NULL, &status); 26 | + abi::__cxa_demangle(name.c_str(), NULL, NULL, &status); 27 | if (demangled) { 28 | if (status == 0) 29 | - ext->name = demangled; 30 | + name = demangled; 31 | free(demangled); 32 | } 33 | #endif 34 | +#if 1 35 | + if (iterator->size) { 36 | + Module::Function *fun = new Module::Function(module->AddStringToPool(name), iterator->value); 37 | + fun->ranges.push_back(Module::Range(iterator->value, iterator->size)); 38 | + module->AddFunction(fun); 39 | + } 40 | +#else 41 | + Module::Extern *ext = new Module::Extern(iterator->value); 42 | + ext->name = name; 43 | module->AddExtern(std::move(ext)); 44 | +#endif 45 | } 46 | ++iterator; 47 | } 48 | -------------------------------------------------------------------------------- /patches/0003-Support-compilation-on-VS-2015.patch: -------------------------------------------------------------------------------- 1 | From 2f217812634d5e6c56f0cf9e7a4c9b7fb390e954 Mon Sep 17 00:00:00 2001 2 | From: patches 3 | Date: Sat, 20 Jul 2019 15:52:37 +0100 4 | Subject: [PATCH 3/5] Support compilation on VS 2015 5 | 6 | --- 7 | src/common/windows/pe_util.cc | 1 + 8 | src/common/windows/pe_util.h | 2 +- 9 | src/tools/windows/dump_syms/dump_syms.cc | 1 + 10 | 3 files changed, 3 insertions(+), 1 deletion(-) 11 | 12 | 13 | diff --git a/src/common/windows/pe_util.h b/src/common/windows/pe_util.h 14 | index 6c6b364f..80aba5e5 100644 15 | --- a/src/common/windows/pe_util.h 16 | +++ b/src/common/windows/pe_util.h 17 | @@ -59,7 +59,7 @@ wstring GenerateDebugIdentifier(DWORD age, DWORD signature); 18 | 19 | // Converts |machine| enum value to the corresponding string used by Breakpad. 20 | // The enum is IMAGE_FILE_MACHINE_*, contained in winnt.h. 21 | -constexpr const wchar_t* FileHeaderMachineToCpuString(WORD machine) { 22 | +static inline const wchar_t* FileHeaderMachineToCpuString(WORD machine) { 23 | switch (machine) { 24 | case IMAGE_FILE_MACHINE_I386: { 25 | return L"x86"; 26 | -------------------------------------------------------------------------------- /patches/0004-Fix-Linux-a-out-include.patch: -------------------------------------------------------------------------------- 1 | Subject: [PATCH 4/5] Fixes path to a.out.h file 2 | 3 | diff --git a/configure b/configure 4 | index 3442e796..3e7c321c 100755 5 | --- a/configure 6 | +++ b/configure 7 | @@ -7532,11 +7532,16 @@ then : 8 | printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h 9 | 10 | fi 11 | -ac_fn_c_check_header_compile "$LINENO" "a.out.h" "ac_cv_header_a_out_h" "$ac_includes_default" 12 | +ac_fn_c_check_header_compile "$LINENO" "linux/a.out.h" "ac_cv_header_a_out_h" "$ac_includes_default" 13 | if test "x$ac_cv_header_a_out_h" = xyes 14 | then : 15 | printf "%s\n" "#define HAVE_A_OUT_H 1" >>confdefs.h 16 | - 17 | +else 18 | + ac_fn_c_check_header_compile "$LINENO" "a.out.h" "ac_cv_header_a_out_h" "$ac_includes_default" 19 | + if test "x$ac_cv_header_a_out_h" = xyes 20 | + then : 21 | + printf "%s\n" "#define HAVE_A_OUT_H_EX 1" >>confdefs.h 22 | + fi 23 | fi 24 | ac_fn_c_check_header_compile "$LINENO" "sys/mman.h" "ac_cv_header_sys_mman_h" "$ac_includes_default" 25 | if test "x$ac_cv_header_sys_mman_h" = xyes 26 | diff --git a/src/common/stabs_reader.h b/src/common/stabs_reader.h 27 | index 655683f1..1e6a76b8 100644 28 | --- a/src/common/stabs_reader.h 29 | +++ b/src/common/stabs_reader.h 30 | @@ -52,6 +52,8 @@ 31 | #ifdef HAVE_MACH_O_NLIST_H 32 | #include 33 | #elif defined(HAVE_A_OUT_H) 34 | +#include 35 | +#elif defined(HAVE_A_OUT_H_EX) 36 | #include 37 | #endif 38 | 39 | -------------------------------------------------------------------------------- /product.version: -------------------------------------------------------------------------------- 1 | 2.6.0 2 | -------------------------------------------------------------------------------- /third_party/AMBuilder: -------------------------------------------------------------------------------- 1 | # vim: set ts=2 sw=2 tw=99 noet ft=python: 2 | import os, sys 3 | 4 | def AddSourceFilesFromDir(path, files): 5 | list = [] 6 | for file in files: 7 | list.append(os.path.join(path, file)) 8 | return list 9 | 10 | builder.SetBuildFolder('./third_party') 11 | 12 | libz = builder.StaticLibraryProject('libz') 13 | libz.sources = AddSourceFilesFromDir(os.path.join(builder.currentSourcePath, 'zlib'),[ 14 | 'adler32.c', 15 | 'compress.c', 16 | 'crc32.c', 17 | 'deflate.c', 18 | 'gzclose.c', 19 | 'gzlib.c', 20 | 'gzread.c', 21 | 'gzwrite.c', 22 | 'infback.c', 23 | 'inffast.c', 24 | 'inflate.c', 25 | 'inftrees.c', 26 | 'trees.c', 27 | 'uncompr.c', 28 | 'zutil.c' 29 | ]) 30 | 31 | for cxx in Accelerator.targets: 32 | if cxx.target.platform == 'linux': 33 | binary = Accelerator.ConfigureLibrary(libz, cxx, builder) 34 | binary.compiler.cflags += ['-Wno-implicit-function-declaration'] 35 | 36 | Accelerator.libz = builder.Add(libz) 37 | 38 | libbreakpad_client = builder.StaticLibraryProject('libbreakpad_client') 39 | libbreakpad_client.sources = AddSourceFilesFromDir(os.path.join(builder.currentSourcePath, 'breakpad', 'src'),[ 40 | 'common/convert_UTF.cc', 41 | 'common/string_conversion.cc', 42 | ]) 43 | 44 | libbreakpad_client_linux = AddSourceFilesFromDir(os.path.join(builder.currentSourcePath, 'breakpad', 'src'),[ 45 | 'client/minidump_file_writer.cc', 46 | 'client/linux/crash_generation/crash_generation_client.cc', 47 | 'client/linux/crash_generation/crash_generation_server.cc', 48 | 'client/linux/dump_writer_common/thread_info.cc', 49 | 'client/linux/dump_writer_common/ucontext_reader.cc', 50 | 'client/linux/handler/exception_handler.cc', 51 | 'client/linux/handler/minidump_descriptor.cc', 52 | 'client/linux/log/log.cc', 53 | 'client/linux/microdump_writer/microdump_writer.cc', 54 | 'client/linux/minidump_writer/linux_core_dumper.cc', 55 | 'client/linux/minidump_writer/linux_dumper.cc', 56 | 'client/linux/minidump_writer/linux_ptrace_dumper.cc', 57 | 'client/linux/minidump_writer/minidump_writer.cc', 58 | 'client/linux/minidump_writer/pe_file.cc', 59 | 'common/linux/elf_core_dump.cc', 60 | 'common/linux/elfutils.cc', 61 | 'common/linux/file_id.cc', 62 | 'common/linux/guid_creator.cc', 63 | 'common/linux/linux_libc_support.cc', 64 | 'common/linux/memory_mapped_file.cc', 65 | 'common/linux/safe_readlink.cc' 66 | ]) 67 | 68 | libbreakpad_client_windows = AddSourceFilesFromDir(os.path.join(builder.currentSourcePath, 'breakpad', 'src'),[ 69 | 'client/windows/crash_generation/client_info.cc', 70 | 'client/windows/crash_generation/crash_generation_client.cc', 71 | 'client/windows/crash_generation/crash_generation_server.cc', 72 | 'client/windows/crash_generation/minidump_generator.cc', 73 | 'client/windows/handler/exception_handler.cc', 74 | 'common/windows/guid_string.cc' 75 | ]) 76 | 77 | libbreakpad = builder.StaticLibraryProject('libbreakpad') 78 | libbreakpad.sources = AddSourceFilesFromDir(os.path.join(builder.currentSourcePath, 'breakpad', 'src'),[ 79 | 'processor/basic_code_modules.cc', 80 | 'processor/basic_source_line_resolver.cc', 81 | 'processor/call_stack.cc', 82 | 'processor/cfi_frame_info.cc', 83 | 'processor/convert_old_arm64_context.cc', 84 | 'processor/disassembler_x86.cc', 85 | 'processor/dump_context.cc', 86 | 'processor/dump_object.cc', 87 | 'processor/exploitability.cc', 88 | 'processor/exploitability_linux.cc', 89 | 'processor/exploitability_win.cc', 90 | 'processor/fast_source_line_resolver.cc', 91 | 'processor/logging.cc', 92 | 'processor/microdump.cc', 93 | 'processor/microdump_processor.cc', 94 | 'processor/minidump.cc', 95 | 'processor/minidump_processor.cc', 96 | 'processor/module_comparer.cc', 97 | 'processor/module_serializer.cc', 98 | 'processor/pathname_stripper.cc', 99 | 'processor/process_state.cc', 100 | 'processor/proc_maps_linux.cc', 101 | 'processor/simple_symbol_supplier.cc', 102 | 'processor/source_line_resolver_base.cc', 103 | 'processor/stack_frame_cpu.cc', 104 | 'processor/stack_frame_symbolizer.cc', 105 | 'processor/stackwalk_common.cc', 106 | 'processor/stackwalker.cc', 107 | 'processor/stackwalker_amd64.cc', 108 | 'processor/stackwalker_arm.cc', 109 | 'processor/stackwalker_arm64.cc', 110 | 'processor/stackwalker_address_list.cc', 111 | 'processor/stackwalker_mips.cc', 112 | 'processor/stackwalker_ppc.cc', 113 | 'processor/stackwalker_ppc64.cc', 114 | 'processor/stackwalker_riscv.cc', 115 | 'processor/stackwalker_riscv64.cc', 116 | 'processor/stackwalker_sparc.cc', 117 | 'processor/stackwalker_x86.cc', 118 | 'processor/symbolic_constants_win.cc', 119 | 'processor/tokenize.cc' 120 | ]) 121 | 122 | libbreakpad_linux = AddSourceFilesFromDir(os.path.join(builder.currentSourcePath, 'breakpad', 'src'),[ 123 | 'common/linux/scoped_pipe.cc', 124 | 'common/linux/scoped_tmpfile.cc', 125 | 'processor/disassembler_objdump.cc' 126 | ]) 127 | 128 | libbreakpad_windows = [] 129 | 130 | libdisasm = builder.StaticLibraryProject('libdisasm') 131 | libdisasm.sources = AddSourceFilesFromDir(os.path.join(builder.currentSourcePath, 'breakpad', 'src', 'third_party', 'libdisasm'),[ 132 | 'ia32_implicit.c', 133 | 'ia32_insn.c', 134 | 'ia32_invariant.c', 135 | 'ia32_modrm.c', 136 | 'ia32_opcode_tables.c', 137 | 'ia32_operand.c', 138 | 'ia32_reg.c', 139 | 'ia32_settings.c', 140 | 'x86_disasm.c', 141 | 'x86_format.c', 142 | 'x86_imm.c', 143 | 'x86_insn.c', 144 | 'x86_misc.c', 145 | 'x86_operand_list.c' 146 | ]) 147 | 148 | libraries = [libbreakpad_client, libbreakpad, libdisasm] 149 | libraries_platform_src = [(libbreakpad_client_linux, libbreakpad_client_windows), (libbreakpad_linux, libbreakpad_windows), ([],[])] 150 | 151 | for cxx in Accelerator.targets: 152 | for (library, (linux_src, windows_src)) in zip(libraries, libraries_platform_src): 153 | binary = Accelerator.ConfigureLibrary(library, cxx, builder) 154 | compiler = binary.compiler 155 | # Wait for breakpad to be patched 156 | compiler.sourcedeps += Accelerator.breakpad_patch 157 | 158 | if compiler.target.platform == 'linux': 159 | # Link against our zlib if linux 160 | Accelerator.link_libz(cxx, builder) 161 | binary.sources += linux_src 162 | if compiler.target.platform == 'windows': 163 | binary.sources += windows_src 164 | 165 | compiler.cxxincludes += [ 166 | os.path.join(builder.currentSourcePath, 'breakpad', 'src') 167 | ] 168 | 169 | compiler.defines += ['UNICODE'] 170 | 171 | Accelerator.libbreakpad = builder.Add(libbreakpad) 172 | Accelerator.libbreakpad_client = builder.Add(libbreakpad_client) 173 | Accelerator.libdisasm = builder.Add(libdisasm) -------------------------------------------------------------------------------- /third_party/Configure: -------------------------------------------------------------------------------- 1 | # vim: set ts=2 sw=2 tw=99 noet ft=python: 2 | import os, sys 3 | 4 | builder.SetBuildFolder('third_party/config') 5 | for compiler in Accelerator.targets: 6 | output = """ 7 | /* define if the compiler supports basic C++17 syntax */ 8 | #define HAVE_CXX17 1 9 | 10 | /* Define to 1 if you have the header file. */ 11 | #define HAVE_INTTYPES_H 1 12 | 13 | /* Define to 1 if you have the header file. */ 14 | #define HAVE_STDINT_H 1 15 | 16 | /* Define to 1 if you have the header file. */ 17 | #define HAVE_STDIO_H 1 18 | 19 | /* Define to 1 if you have the header file. */ 20 | #define HAVE_STDLIB_H 1 21 | 22 | /* Define to 1 if you have the header file. */ 23 | #define HAVE_STRINGS_H 1 24 | 25 | /* Define to 1 if you have the header file. */ 26 | #define HAVE_STRING_H 1 27 | 28 | /* Define to 1 if you have the header file. */ 29 | #define HAVE_UNISTD_H 1 30 | """ 31 | if compiler.target.platform == "linux": 32 | output += """ 33 | /* Define to 1 if you have the header file. */ 34 | #define HAVE_A_OUT_H 1 35 | 36 | /* Define to 1 if you have the `getcontext' function. */ 37 | #define HAVE_GETCONTEXT 1 38 | 39 | /* Define to 1 if you have the `getrandom' function. */ 40 | #define HAVE_GETRANDOM 1 41 | 42 | /* Define to 1 if you have the header file. */ 43 | #define HAVE_SYS_MMAN_H 1 44 | 45 | /* Define to 1 if you have the header file. */ 46 | #define HAVE_SYS_RANDOM_H 1 47 | 48 | /* Define to 1 if you have the header file. */ 49 | #define HAVE_SYS_STAT_H 1 50 | 51 | /* Define to 1 if you have the header file. */ 52 | #define HAVE_SYS_TYPES_H 1 53 | 54 | /* Define to 1 if you have the `memfd_create' function. */ 55 | #define HAVE_MEMFD_CREATE 1 56 | """ 57 | 58 | if '-lrustc_demangle' in compiler.postlink: 59 | output += """ 60 | /* Define to 1 if you have the `rustc_demangle' library (-lrustc_demangle). */ 61 | #define HAVE_LIBRUSTC_DEMANGLE 1 62 | 63 | /* Define to 1 if you have the header file. */ 64 | #define HAVE_RUSTC_DEMANGLE_H 1 65 | """ 66 | 67 | if '-pthread' in compiler.postlink: 68 | output += """ 69 | /* Define if you have POSIX threads libraries and header files. */ 70 | #define HAVE_PTHREAD 1 71 | """ 72 | 73 | if '-lzstd' in compiler.postlink: 74 | output += """ 75 | /* Define to 1 if you have the `zstd' library (-lzstd). */ 76 | #define HAVE_LIBZSTD 1 77 | """ 78 | 79 | output += """ 80 | /* Name of package */ 81 | #define PACKAGE "breakpad" 82 | 83 | /* Define to the address where bug reports for this package should be sent. */ 84 | #define PACKAGE_BUGREPORT "google-breakpad-dev@googlegroups.com" 85 | 86 | /* Define to the full name of this package. */ 87 | #define PACKAGE_NAME "breakpad" 88 | 89 | /* Define to the full name and version of this package. */ 90 | #define PACKAGE_STRING "breakpad 0.1" 91 | 92 | /* Define to the one symbol short name of this package. */ 93 | #define PACKAGE_TARNAME "breakpad" 94 | 95 | /* Define to the home page for this package. */ 96 | #define PACKAGE_URL "" 97 | 98 | /* Define to the version of this package. */ 99 | #define PACKAGE_VERSION "0.1" 100 | 101 | /* Version number of package */ 102 | #define VERSION "0.1" 103 | """ 104 | Accelerator.breakpad_config[compiler.target.arch] = [builder.AddOutputFile(os.path.join(compiler.target.arch, 'config.h'), output.encode())] -------------------------------------------------------------------------------- /third_party/Patch: -------------------------------------------------------------------------------- 1 | # vim: set ts=2 sw=2 tw=99 noet ft=python: 2 | import os, sys 3 | import re 4 | 5 | def Normalize(path): 6 | return os.path.abspath(os.path.normpath(os.path.join(path))) 7 | 8 | def AddFilesFromDir(path, files): 9 | list = [] 10 | for file in files: 11 | list.append(os.path.join(path, file)) 12 | return list 13 | 14 | def AddCwdCommand(context, cwd, inputs, 15 | argv, 16 | outputs, 17 | folder = -1, 18 | dep_type = None, 19 | weak_inputs = [], 20 | shared_outputs = [], 21 | env_data = None, 22 | fake_output = None): 23 | 24 | base_argv = [ 25 | sys.executable, 26 | Normalize(os.path.join(context.currentSourcePath, 'cwd_cmd.py')), 27 | cwd, 28 | os.path.join(context.buildPath, 'third_party', fake_output) if fake_output else '_', 29 | ] 30 | if fake_output : 31 | outputs += [fake_output] 32 | return context.AddCommand(inputs=inputs, argv=base_argv + argv, outputs=outputs, folder=folder, dep_type=dep_type, weak_inputs=weak_inputs, shared_outputs=shared_outputs, env_data=env_data) 33 | 34 | 35 | git_dir = Normalize(os.path.join(builder.currentSourcePath, 'breakpad')) 36 | patches = AddFilesFromDir(os.path.join(builder.currentSourcePath, '..', 'patches'), [ 37 | "0001-Ignore-invalid-modules-rather-than-bailing-on-the-en.patch", 38 | "0002-Write-FUNC-records-instead-of-PUBLIC-for-ELF-symbols.patch", 39 | "0003-Support-compilation-on-VS-2015.patch", 40 | "0004-Fix-Linux-a-out-include.patch", 41 | "0005-Add-LSS.patch" 42 | ]) 43 | 44 | # Reset the breakpad directory before applying patches 45 | argv = [ 46 | 'git', 47 | 'clean', 48 | '-fdx', 49 | '&&', 50 | 'git', 51 | 'reset', 52 | '--hard' 53 | ] 54 | 55 | git_reset = AddCwdCommand(builder, git_dir, 56 | inputs=patches, # We want the reset to happen again if we modify the patch files 57 | outputs=[], 58 | fake_output='git_clean.txt', 59 | argv=argv) 60 | 61 | # Apply each of the patch available to the breakpad repository 62 | argv = [ 63 | 'git', 64 | 'apply', 65 | '--reject' 66 | ] 67 | 68 | git_patches = [] 69 | num = 0 70 | for patch in patches: 71 | patch_argv = argv + [patch] 72 | git_patches += AddCwdCommand(builder, git_dir, 73 | inputs=patches, # Redo the command if the patches changed 74 | outputs=[], 75 | fake_output=str(num) + '_patch.txt', 76 | argv=patch_argv, 77 | weak_inputs=git_reset if num == 0 else [git_patches[-1]] + git_reset # We need to for the git reset to be performed 78 | ) 79 | num = num + 1 80 | 81 | Accelerator.breakpad_patch = git_reset + git_patches -------------------------------------------------------------------------------- /third_party/cwd_cmd.py: -------------------------------------------------------------------------------- 1 | # vim: set ts=2 sw=2 tw=99 noet ft=python: 2 | import subprocess, sys 3 | 4 | argv = sys.argv[3:] 5 | cwd = sys.argv[1] 6 | print('CMD: ' + ' '.join(argv)) 7 | print('CWD: ' + cwd) 8 | print('OUT: ' + sys.argv[2]) 9 | open(sys.argv[2], 'a').close() 10 | 11 | args = { 'args': ' '.join(argv), 12 | 'stdout': subprocess.PIPE, 13 | 'stderr': subprocess.PIPE, 14 | 'shell': True, 15 | 'cwd': cwd } 16 | p = subprocess.Popen(**args) 17 | stdout, stderr = p.communicate() 18 | print(stdout.decode("utf-8")) 19 | if p.returncode != 0: 20 | print(stderr.decode("utf-8")) 21 | sys.exit(p.returncode) 22 | -------------------------------------------------------------------------------- /upload.py: -------------------------------------------------------------------------------- 1 | import re, os, sys 2 | import subprocess 3 | 4 | import zipfile 5 | import base64 6 | try: 7 | import urllib.request as urllib 8 | except ImportError: 9 | import urllib2 as urllib 10 | 11 | project = 'accelerator' 12 | 13 | platform = 'unknown' 14 | if sys.platform.startswith('linux'): 15 | platform = 'linux' 16 | elif sys.platform.startswith('win32'): 17 | platform = 'windows' 18 | elif sys.platform.startswith('darwin'): 19 | platform = 'mac' 20 | 21 | def GITHash(): 22 | p = subprocess.Popen(['git', 'rev-parse', '--short', 'HEAD'], stdout = subprocess.PIPE, stderr = subprocess.PIPE) 23 | (stdout, stderr) = p.communicate() 24 | stdout = stdout.decode('UTF-8') 25 | return stdout.rstrip('\r\n') 26 | 27 | def GITBranch(): 28 | travis_branch = os.environ.get('TRAVIS_BRANCH', False) 29 | if travis_branch: 30 | return travis_branch 31 | 32 | appveyor_branch = os.environ.get('APPVEYOR_REPO_BRANCH', False) 33 | if appveyor_branch: 34 | return appveyor_branch 35 | 36 | p = subprocess.Popen(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], stdout = subprocess.PIPE, stderr = subprocess.PIPE) 37 | (stdout, stderr) = p.communicate() 38 | stdout = stdout.decode('UTF-8') 39 | return stdout.rstrip('\r\n') 40 | 41 | def GITVersion(): 42 | p = subprocess.Popen(['git', 'rev-list', '--count', '--first-parent', 'HEAD'], stdout = subprocess.PIPE, stderr = subprocess.PIPE) 43 | (stdout, stderr) = p.communicate() 44 | stdout = stdout.decode('UTF-8') 45 | return stdout.rstrip('\r\n') 46 | 47 | def ReleaseVersion(): 48 | productFile = open('../product.version', 'r') 49 | productContents = productFile.read() 50 | productFile.close() 51 | 52 | m = re.match('(\d+)\.(\d+)\.(\d+)(.*)', productContents) 53 | if m == None: 54 | raise Exception('Could not detremine product version') 55 | 56 | major, minor, release, tag = m.groups() 57 | return '.'.join([major, minor, release]) 58 | 59 | filename = '-'.join([project, ReleaseVersion(), 'git' + GITVersion(), GITHash(), platform]) 60 | 61 | debug_build = os.environ.get('is_debug_build', False) == "1" 62 | 63 | if debug_build: 64 | filename += '-debug' 65 | 66 | filename += '.zip' 67 | 68 | zip = zipfile.ZipFile(filename, 'w', zipfile.ZIP_DEFLATED) 69 | 70 | for base, dirs, files in os.walk('package'): 71 | for file in files: 72 | fn = os.path.join(base, file) 73 | fns = fn[(len('package') + 1):] 74 | 75 | zip.write(fn, fns) 76 | 77 | print("%-33s %-10s %21s %12s" % ("File Name", "CRC32", "Modified ", "Size")) 78 | for zinfo in zip.infolist(): 79 | date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time[:6] 80 | print("%-33s %-10d %21s %12d" % (zinfo.filename, zinfo.CRC, date, zinfo.file_size)) 81 | 82 | zip.close() 83 | 84 | if 'ftp_username' in os.environ: 85 | print('') 86 | 87 | request = urllib.Request("https://builds.limetech.io/upload.php?project=%s&branch=%s&filename=%s" % (project, GITBranch(), filename), open(filename, 'rb'), headers={ 88 | 'Authorization': "Basic %s" % (base64.encodestring(("%s:%s" % (os.environ['ftp_username'], os.environ['ftp_password'])).encode()).decode()[:-1]), 89 | 'Content-Type': 'application/zip', 90 | 'Content-Length': os.stat(filename).st_size, 91 | }) 92 | print(urllib.urlopen(request).read().decode('utf-8')) 93 | 94 | print('Uploaded as \'' + filename + '\'') 95 | --------------------------------------------------------------------------------