summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--COPYRIGHT.txt5
-rw-r--r--SConstruct96
-rw-r--r--core/SCsub4
-rw-r--r--core/extension/gdextension.cpp16
-rw-r--r--core/extension/gdextension.h1
-rw-r--r--core/extension/gdextension_interface.h3
-rw-r--r--core/object/class_db.cpp5
-rw-r--r--core/object/object.cpp7
-rw-r--r--core/object/object.h3
-rw-r--r--core/templates/ring_buffer.h4
-rw-r--r--core/templates/safe_refcount.h2
-rw-r--r--doc/classes/DisplayServer.xml4
-rw-r--r--drivers/SCsub2
-rw-r--r--drivers/backtrace/SCsub44
-rw-r--r--drivers/windows/file_access_windows.cpp65
-rw-r--r--editor/animation_track_editor.cpp1
-rw-r--r--editor/debugger/debug_adapter/debug_adapter_server.h2
-rw-r--r--editor/editor_about.cpp1
-rw-r--r--editor/editor_audio_buses.h2
-rw-r--r--editor/editor_builders.py12
-rw-r--r--editor/editor_data.cpp2
-rw-r--r--editor/editor_help.h2
-rw-r--r--editor/editor_help_search.h2
-rw-r--r--editor/editor_inspector.cpp7
-rw-r--r--editor/editor_node.cpp4
-rw-r--r--editor/editor_node.h2
-rw-r--r--editor/editor_resource_preview.cpp12
-rw-r--r--editor/editor_resource_preview.h5
-rw-r--r--editor/import/audio_stream_import_settings.h2
-rw-r--r--editor/import/resource_importer_wav.cpp4
-rw-r--r--editor/import_defaults_editor.cpp2
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.h2
-rw-r--r--editor/plugins/animation_blend_space_1d_editor.cpp6
-rw-r--r--editor/plugins/animation_blend_space_1d_editor.h2
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.cpp6
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.h2
-rw-r--r--editor/plugins/animation_library_editor.h2
-rw-r--r--editor/plugins/animation_player_editor_plugin.h2
-rw-r--r--editor/plugins/animation_tree_editor_plugin.h2
-rw-r--r--editor/plugins/asset_library_editor_plugin.h4
-rw-r--r--editor/plugins/audio_stream_editor_plugin.h2
-rw-r--r--editor/plugins/audio_stream_randomizer_editor_plugin.h2
-rw-r--r--editor/plugins/bit_map_editor_plugin.h2
-rw-r--r--editor/plugins/bone_map_editor_plugin.cpp75
-rw-r--r--editor/plugins/bone_map_editor_plugin.h2
-rw-r--r--editor/plugins/camera_3d_editor_plugin.h2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h2
-rw-r--r--editor/plugins/cast_2d_editor_plugin.h2
-rw-r--r--editor/plugins/collision_shape_2d_editor_plugin.h2
-rw-r--r--editor/plugins/control_editor_plugin.h2
-rw-r--r--editor/plugins/cpu_particles_2d_editor_plugin.h2
-rw-r--r--editor/plugins/curve_editor_plugin.h2
-rw-r--r--editor/plugins/debugger_editor_plugin.h2
-rw-r--r--editor/plugins/editor_plugin.compat.inc (renamed from editor/editor_plugin.compat.inc)0
-rw-r--r--editor/plugins/editor_plugin.cpp (renamed from editor/editor_plugin.cpp)0
-rw-r--r--editor/plugins/editor_plugin.h (renamed from editor/editor_plugin.h)0
-rw-r--r--editor/plugins/editor_plugin_settings.cpp (renamed from editor/editor_plugin_settings.cpp)0
-rw-r--r--editor/plugins/editor_plugin_settings.h (renamed from editor/editor_plugin_settings.h)2
-rw-r--r--editor/plugins/font_config_plugin.h2
-rw-r--r--editor/plugins/gpu_particles_2d_editor_plugin.h2
-rw-r--r--editor/plugins/gpu_particles_3d_editor_plugin.h2
-rw-r--r--editor/plugins/gpu_particles_collision_sdf_editor_plugin.h2
-rw-r--r--editor/plugins/gradient_editor_plugin.h2
-rw-r--r--editor/plugins/gradient_texture_2d_editor_plugin.h2
-rw-r--r--editor/plugins/input_event_editor_plugin.h2
-rw-r--r--editor/plugins/lightmap_gi_editor_plugin.h2
-rw-r--r--editor/plugins/material_editor_plugin.h2
-rw-r--r--editor/plugins/mesh_editor_plugin.h2
-rw-r--r--editor/plugins/mesh_instance_3d_editor_plugin.h2
-rw-r--r--editor/plugins/mesh_library_editor_plugin.h2
-rw-r--r--editor/plugins/multimesh_editor_plugin.h2
-rw-r--r--editor/plugins/navigation_link_2d_editor_plugin.h2
-rw-r--r--editor/plugins/navigation_obstacle_3d_editor_plugin.h2
-rw-r--r--editor/plugins/navigation_polygon_editor_plugin.h2
-rw-r--r--editor/plugins/node_3d_editor_plugin.h2
-rw-r--r--editor/plugins/occluder_instance_3d_editor_plugin.h2
-rw-r--r--editor/plugins/packed_scene_editor_plugin.h2
-rw-r--r--editor/plugins/parallax_background_editor_plugin.h2
-rw-r--r--editor/plugins/particle_process_material_editor_plugin.h2
-rw-r--r--editor/plugins/path_2d_editor_plugin.h2
-rw-r--r--editor/plugins/path_3d_editor_plugin.h2
-rw-r--r--editor/plugins/physical_bone_3d_editor_plugin.h2
-rw-r--r--editor/plugins/plugin_config_dialog.cpp (renamed from editor/plugin_config_dialog.cpp)83
-rw-r--r--editor/plugins/plugin_config_dialog.h (renamed from editor/plugin_config_dialog.h)2
-rw-r--r--editor/plugins/polygon_3d_editor_plugin.h2
-rw-r--r--editor/plugins/resource_preloader_editor_plugin.h2
-rw-r--r--editor/plugins/script_editor_plugin.h2
-rw-r--r--editor/plugins/shader_editor_plugin.h2
-rw-r--r--editor/plugins/shader_file_editor_plugin.h2
-rw-r--r--editor/plugins/skeleton_2d_editor_plugin.h2
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.h2
-rw-r--r--editor/plugins/skeleton_ik_3d_editor_plugin.h2
-rw-r--r--editor/plugins/sprite_2d_editor_plugin.h2
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.h2
-rw-r--r--editor/plugins/style_box_editor_plugin.h2
-rw-r--r--editor/plugins/sub_viewport_preview_editor_plugin.h2
-rw-r--r--editor/plugins/text_shader_editor.cpp3
-rw-r--r--editor/plugins/texture_3d_editor_plugin.h2
-rw-r--r--editor/plugins/texture_editor_plugin.h2
-rw-r--r--editor/plugins/texture_layered_editor_plugin.h2
-rw-r--r--editor/plugins/texture_region_editor_plugin.h2
-rw-r--r--editor/plugins/theme_editor_plugin.h2
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.h2
-rw-r--r--editor/plugins/version_control_editor_plugin.h2
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h2
-rw-r--r--editor/plugins/voxel_gi_editor_plugin.h2
-rw-r--r--editor/project_settings_editor.h2
-rw-r--r--editor/shader_globals_editor.h2
-rw-r--r--gles3_builders.py6
-rw-r--r--glsl_builders.py7
-rw-r--r--methods.py146
-rw-r--r--misc/dist/ios_xcode/PrivacyInfo.xcprivacy10
-rw-r--r--misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj4
-rw-r--r--modules/csg/editor/csg_gizmos.h2
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.h2
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp29
-rw-r--r--modules/gltf/editor/editor_scene_exporter_gltf_plugin.h2
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.h2
-rw-r--r--modules/interactive_music/editor/audio_stream_interactive_editor_plugin.h2
-rw-r--r--modules/mono/csharp_script.h2
-rw-r--r--modules/multiplayer/editor/multiplayer_editor_plugin.h2
-rw-r--r--modules/multiplayer/editor/replication_editor.h2
-rw-r--r--modules/navigation/editor/navigation_mesh_editor_plugin.h2
-rw-r--r--modules/noise/editor/noise_editor_plugin.h2
-rw-r--r--modules/noise/register_types.cpp2
-rw-r--r--modules/openxr/editor/openxr_action_map_editor.h2
-rw-r--r--modules/openxr/editor/openxr_editor_plugin.h2
-rw-r--r--modules/text_server_adv/gdextension_build/SConstruct27
-rw-r--r--modules/text_server_adv/gdextension_build/methods.py117
-rw-r--r--modules/text_server_fb/gdextension_build/SConstruct27
-rw-r--r--modules/text_server_fb/gdextension_build/methods.py117
-rw-r--r--platform/android/SCsub3
-rw-r--r--platform/android/detect.py21
-rw-r--r--platform/ios/detect.py8
-rw-r--r--platform/ios/doc_classes/EditorExportPlatformIOS.xml441
-rw-r--r--platform/ios/export/export_plugin.cpp225
-rw-r--r--platform/linuxbsd/detect.py56
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.cpp193
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.h62
-rw-r--r--platform/macos/detect.py8
-rw-r--r--platform/web/SCsub4
-rw-r--r--platform/web/api/web_tools_editor_plugin.h2
-rw-r--r--platform/web/detect.py16
-rw-r--r--platform/web/js/engine/features.js3
-rw-r--r--platform/windows/SCsub6
-rw-r--r--platform/windows/crash_handler_windows.h5
-rw-r--r--platform/windows/crash_handler_windows_seh.cpp (renamed from platform/windows/crash_handler_windows.cpp)2
-rw-r--r--platform/windows/crash_handler_windows_signal.cpp205
-rw-r--r--platform/windows/detect.py74
-rw-r--r--platform/windows/godot_windows.cpp2
-rw-r--r--platform_methods.py3
-rw-r--r--scene/3d/label_3d.cpp4
-rw-r--r--scene/animation/animation_player.cpp2
-rw-r--r--scene/gui/texture_progress_bar.cpp12
-rw-r--r--scene/resources/shader.cpp43
-rw-r--r--scu_builders.py7
-rw-r--r--servers/rendering/renderer_rd/shaders/particles.glsl6
-rw-r--r--servers/rendering/rendering_server_default.h4
-rw-r--r--thirdparty/README.md12
-rw-r--r--thirdparty/clipper2/include/clipper2/clipper.core.h4
-rw-r--r--thirdparty/clipper2/patches/gcc14-warning.patch22
-rw-r--r--thirdparty/libbacktrace/LICENSE29
-rw-r--r--thirdparty/libbacktrace/alloc.c167
-rw-r--r--thirdparty/libbacktrace/atomic.c113
-rw-r--r--thirdparty/libbacktrace/backtrace-supported.h66
-rw-r--r--thirdparty/libbacktrace/backtrace.c129
-rw-r--r--thirdparty/libbacktrace/backtrace.h189
-rw-r--r--thirdparty/libbacktrace/config.h170
-rw-r--r--thirdparty/libbacktrace/dwarf.c4402
-rw-r--r--thirdparty/libbacktrace/fileline.c346
-rw-r--r--thirdparty/libbacktrace/filenames.h52
-rw-r--r--thirdparty/libbacktrace/internal.h380
-rw-r--r--thirdparty/libbacktrace/pecoff.c935
-rw-r--r--thirdparty/libbacktrace/posix.c104
-rw-r--r--thirdparty/libbacktrace/print.c92
-rw-r--r--thirdparty/libbacktrace/read.c110
-rw-r--r--thirdparty/libbacktrace/simple.c108
-rw-r--r--thirdparty/libbacktrace/sort.c108
-rw-r--r--thirdparty/libbacktrace/state.c72
179 files changed, 9513 insertions, 664 deletions
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index c1711a9e81..2e246e6634 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -293,6 +293,11 @@ Comment: jpeg-compressor
Copyright: 2012, Rich Geldreich
License: public-domain or Apache-2.0
+Files: ./thirdparty/libbacktrace/
+Comment: libbacktrace
+Copyright: 2012-2021, Free Software Foundation, Inc.
+License: BSD-3-clause
+
Files: ./thirdparty/libktx/
Comment: KTX
Copyright: 2013-2020, Mark Callow
diff --git a/SConstruct b/SConstruct
index 18511ff5ee..81ce4bca52 100644
--- a/SConstruct
+++ b/SConstruct
@@ -15,6 +15,17 @@ from collections import OrderedDict
from importlib.util import spec_from_file_location, module_from_spec
from SCons import __version__ as scons_raw_version
+# Enable ANSI escape code support on Windows 10 and later (for colored console output).
+# <https://github.com/python/cpython/issues/73245>
+if sys.platform == "win32":
+ from ctypes import windll, c_int, byref
+
+ stdout_handle = windll.kernel32.GetStdHandle(c_int(-11))
+ mode = c_int(0)
+ windll.kernel32.GetConsoleMode(c_int(stdout_handle), byref(mode))
+ mode = c_int(mode.value | 4)
+ windll.kernel32.SetConsoleMode(c_int(stdout_handle), mode)
+
# Explicitly resolve the helper modules, this is done to avoid clash with
# modules of the same name that might be randomly added (e.g. someone adding
# an `editor.py` file at the root of the module creates a clash with the editor
@@ -57,6 +68,7 @@ import methods
import glsl_builders
import gles3_builders
import scu_builders
+from methods import print_warning, print_error
from platform_methods import architectures, architecture_aliases, generate_export_icons
if ARGUMENTS.get("target", "editor") == "editor":
@@ -311,38 +323,41 @@ if selected_platform == "":
selected_platform = "windows"
if selected_platform != "":
- print("Automatically detected platform: " + selected_platform)
+ print(f"Automatically detected platform: {selected_platform}")
if selected_platform == "osx":
# Deprecated alias kept for compatibility.
- print('Platform "osx" has been renamed to "macos" in Godot 4. Building for platform "macos".')
+ print_warning('Platform "osx" has been renamed to "macos" in Godot 4. Building for platform "macos".')
selected_platform = "macos"
if selected_platform == "iphone":
# Deprecated alias kept for compatibility.
- print('Platform "iphone" has been renamed to "ios" in Godot 4. Building for platform "ios".')
+ print_warning('Platform "iphone" has been renamed to "ios" in Godot 4. Building for platform "ios".')
selected_platform = "ios"
if selected_platform in ["linux", "bsd", "x11"]:
if selected_platform == "x11":
# Deprecated alias kept for compatibility.
- print('Platform "x11" has been renamed to "linuxbsd" in Godot 4. Building for platform "linuxbsd".')
+ print_warning('Platform "x11" has been renamed to "linuxbsd" in Godot 4. Building for platform "linuxbsd".')
# Alias for convenience.
selected_platform = "linuxbsd"
if selected_platform == "javascript":
# Deprecated alias kept for compatibility.
- print('Platform "javascript" has been renamed to "web" in Godot 4. Building for platform "web".')
+ print_warning('Platform "javascript" has been renamed to "web" in Godot 4. Building for platform "web".')
selected_platform = "web"
if selected_platform not in platform_list:
- if selected_platform == "":
- print("Could not detect platform automatically.")
- elif selected_platform != "list":
- print(f'Invalid target platform "{selected_platform}".')
+ text = "The following platforms are available:\n\t{}\n".format("\n\t".join(platform_list))
+ text += "Please run SCons again and select a valid platform: platform=<string>."
+
+ if selected_platform == "list":
+ print(text)
+ elif selected_platform == "":
+ print_error("Could not detect platform automatically.\n" + text)
+ else:
+ print_error(f'Invalid target platform "{selected_platform}".\n' + text)
- print("The following platforms are available:\n\t{}\n".format("\n\t".join(platform_list)))
- print("Please run SCons again and select a valid platform: platform=<string>.")
Exit(0 if selected_platform == "list" else 255)
# Make sure to update this to the found, valid platform as it's used through the buildsystem as the reference.
@@ -368,7 +383,7 @@ if env["custom_modules"]:
try:
module_search_paths.append(methods.convert_custom_modules_path(p))
except ValueError as e:
- print(e)
+ print_error(e)
Exit(255)
for path in module_search_paths:
@@ -507,7 +522,7 @@ env.SetOption("num_jobs", altered_num_jobs)
if env.GetOption("num_jobs") == altered_num_jobs:
cpu_count = os.cpu_count()
if cpu_count is None:
- print("Couldn't auto-detect CPU count to configure build parallelism. Specify it with the -j argument.")
+ print_warning("Couldn't auto-detect CPU count to configure build parallelism. Specify it with the -j argument.")
else:
safer_cpu_count = cpu_count if cpu_count <= 4 else cpu_count - 1
print(
@@ -531,7 +546,7 @@ env.Append(LINKFLAGS=env.get("linkflags", "").split())
# Feature build profile
disabled_classes = []
if env["build_profile"] != "":
- print("Using feature build profile: " + env["build_profile"])
+ print('Using feature build profile: "{}"'.format(env["build_profile"]))
import json
try:
@@ -543,7 +558,7 @@ if env["build_profile"] != "":
for c in dbo:
env[c] = dbo[c]
except:
- print("Error opening feature build profile: " + env["build_profile"])
+ print_error('Failed to open feature build profile: "{}"'.format(env["build_profile"]))
Exit(255)
methods.write_disabled_classes(disabled_classes)
@@ -605,14 +620,14 @@ cc_version_metadata1 = cc_version["metadata1"] or ""
if methods.using_gcc(env):
if cc_version_major == -1:
- print(
+ print_warning(
"Couldn't detect compiler version, skipping version checks. "
"Build may fail if the compiler doesn't support C++17 fully."
)
# GCC 8 before 8.4 has a regression in the support of guaranteed copy elision
# which causes a build failure: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86521
elif cc_version_major == 8 and cc_version_minor < 4:
- print(
+ print_error(
"Detected GCC 8 version < 8.4, which is not supported due to a "
"regression in its C++17 guaranteed copy elision support. Use a "
'newer GCC version, or Clang 6 or later by passing "use_llvm=yes" '
@@ -620,7 +635,7 @@ if methods.using_gcc(env):
)
Exit(255)
elif cc_version_major < 7:
- print(
+ print_error(
"Detected GCC version older than 7, which does not fully support "
"C++17. Supported versions are GCC 7, 9 and later. Use a newer GCC "
'version, or Clang 6 or later by passing "use_llvm=yes" to the '
@@ -628,7 +643,7 @@ if methods.using_gcc(env):
)
Exit(255)
elif cc_version_metadata1 == "win32":
- print(
+ print_error(
"Detected mingw version is not using posix threads. Only posix "
"version of mingw is supported. "
'Use "update-alternatives --config x86_64-w64-mingw32-g++" '
@@ -636,11 +651,11 @@ if methods.using_gcc(env):
)
Exit(255)
if env["debug_paths_relative"] and cc_version_major < 8:
- print("GCC < 8 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
+ print_warning("GCC < 8 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
env["debug_paths_relative"] = False
elif methods.using_clang(env):
if cc_version_major == -1:
- print(
+ print_warning(
"Couldn't detect compiler version, skipping version checks. "
"Build may fail if the compiler doesn't support C++17 fully."
)
@@ -649,28 +664,30 @@ elif methods.using_clang(env):
elif env["platform"] == "macos" or env["platform"] == "ios":
vanilla = methods.is_vanilla_clang(env)
if vanilla and cc_version_major < 6:
- print(
+ print_warning(
"Detected Clang version older than 6, which does not fully support "
"C++17. Supported versions are Clang 6 and later."
)
Exit(255)
elif not vanilla and cc_version_major < 10:
- print(
+ print_error(
"Detected Apple Clang version older than 10, which does not fully "
"support C++17. Supported versions are Apple Clang 10 and later."
)
Exit(255)
if env["debug_paths_relative"] and not vanilla and cc_version_major < 12:
- print("Apple Clang < 12 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
+ print_warning(
+ "Apple Clang < 12 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option."
+ )
env["debug_paths_relative"] = False
elif cc_version_major < 6:
- print(
+ print_error(
"Detected Clang version older than 6, which does not fully support "
"C++17. Supported versions are Clang 6 and later."
)
Exit(255)
if env["debug_paths_relative"] and cc_version_major < 10:
- print("Clang < 10 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
+ print_warning("Clang < 10 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
env["debug_paths_relative"] = False
# Set optimize and debug_symbols flags.
@@ -906,7 +923,7 @@ if env.editor_build:
# And check if they are met.
if not env.module_check_dependencies("editor"):
- print("Not all modules required by editor builds are enabled.")
+ print_error("Not all modules required by editor builds are enabled.")
Exit(255)
methods.generate_version_header(env.module_version_string)
@@ -932,14 +949,14 @@ env["SHOBJPREFIX"] = env["object_prefix"]
if env["disable_3d"]:
if env.editor_build:
- print("Build option 'disable_3d=yes' cannot be used for editor builds, only for export template builds.")
+ print_error("Build option `disable_3d=yes` cannot be used for editor builds, only for export template builds.")
Exit(255)
else:
env.Append(CPPDEFINES=["_3D_DISABLED"])
if env["disable_advanced_gui"]:
if env.editor_build:
- print(
- "Build option 'disable_advanced_gui=yes' cannot be used for editor builds, "
+ print_error(
+ "Build option `disable_advanced_gui=yes` cannot be used for editor builds, "
"only for export template builds."
)
Exit(255)
@@ -951,7 +968,7 @@ if env["brotli"]:
env.Append(CPPDEFINES=["BROTLI_ENABLED"])
if not env["verbose"]:
- methods.no_verbose(sys, env)
+ methods.no_verbose(env)
GLSL_BUILDERS = {
"RD_GLSL": env.Builder(
@@ -983,7 +1000,7 @@ if env["vsproj"]:
if env["compiledb"] and env.scons_version < (4, 0, 0):
# Generating the compilation DB (`compile_commands.json`) requires SCons 4.0.0 or later.
- print("The `compiledb=yes` option requires SCons 4.0 or later, but your version is %s." % scons_raw_version)
+ print_error("The `compiledb=yes` option requires SCons 4.0 or later, but your version is %s." % scons_raw_version)
Exit(255)
if env.scons_version >= (4, 0, 0):
env.Tool("compilation_db")
@@ -991,7 +1008,7 @@ if env.scons_version >= (4, 0, 0):
if env["ninja"]:
if env.scons_version < (4, 2, 0):
- print("The `ninja=yes` option requires SCons 4.2 or later, but your version is %s." % scons_raw_version)
+ print_error("The `ninja=yes` option requires SCons 4.2 or later, but your version is %s." % scons_raw_version)
Exit(255)
SetOption("experimental", "ninja")
@@ -1048,9 +1065,16 @@ methods.dump(env)
def print_elapsed_time():
- elapsed_time_sec = round(time.time() - time_at_start, 3)
- time_ms = round((elapsed_time_sec % 1) * 1000)
- print("[Time elapsed: {}.{:03}]".format(time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)), time_ms))
+ elapsed_time_sec = round(time.time() - time_at_start, 2)
+ time_centiseconds = round((elapsed_time_sec % 1) * 100)
+ print(
+ "{}[Time elapsed: {}.{:02}]{}".format(
+ methods.ANSI.GRAY,
+ time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)),
+ time_centiseconds,
+ methods.ANSI.RESET,
+ )
+ )
atexit.register(print_elapsed_time)
diff --git a/core/SCsub b/core/SCsub
index ec4658e8ca..91620cb075 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -29,8 +29,8 @@ if "SCRIPT_AES256_ENCRYPTION_KEY" in os.environ:
ec_valid = False
txt += txts
if not ec_valid:
- print("Error: Invalid AES256 encryption key, not 64 hexadecimal characters: '" + key + "'.")
- print(
+ methods.print_error(
+ f'Invalid AES256 encryption key, not 64 hexadecimal characters: "{key}".\n'
"Unset 'SCRIPT_AES256_ENCRYPTION_KEY' in your environment "
"or make sure that it contains exactly 64 hexadecimal characters."
)
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index 22a5df9935..b48ea97040 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -387,7 +387,7 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library
p_extension_funcs->set_func, // GDExtensionClassSet set_func;
p_extension_funcs->get_func, // GDExtensionClassGet get_func;
p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func;
- p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
+ nullptr, // GDExtensionClassFreePropertyList2 free_property_list_func;
p_extension_funcs->property_can_revert_func, // GDExtensionClassPropertyCanRevert property_can_revert_func;
p_extension_funcs->property_get_revert_func, // GDExtensionClassPropertyGetRevert property_get_revert_func;
nullptr, // GDExtensionClassValidateProperty validate_property_func;
@@ -406,7 +406,8 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library
};
const ClassCreationDeprecatedInfo legacy = {
- p_extension_funcs->notification_func,
+ p_extension_funcs->notification_func, // GDExtensionClassNotification notification_func;
+ p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
};
_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info3, &legacy);
}
@@ -420,7 +421,7 @@ void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_librar
p_extension_funcs->set_func, // GDExtensionClassSet set_func;
p_extension_funcs->get_func, // GDExtensionClassGet get_func;
p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func;
- p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
+ nullptr, // GDExtensionClassFreePropertyList2 free_property_list_func;
p_extension_funcs->property_can_revert_func, // GDExtensionClassPropertyCanRevert property_can_revert_func;
p_extension_funcs->property_get_revert_func, // GDExtensionClassPropertyGetRevert property_get_revert_func;
p_extension_funcs->validate_property_func, // GDExtensionClassValidateProperty validate_property_func;
@@ -438,7 +439,11 @@ void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_librar
p_extension_funcs->class_userdata, // void *class_userdata;
};
- _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info3);
+ const ClassCreationDeprecatedInfo legacy = {
+ nullptr, // GDExtensionClassNotification notification_func;
+ p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
+ };
+ _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info3, &legacy);
}
#endif // DISABLE_DEPRECATED
@@ -514,13 +519,14 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr
extension->gdextension.set = p_extension_funcs->set_func;
extension->gdextension.get = p_extension_funcs->get_func;
extension->gdextension.get_property_list = p_extension_funcs->get_property_list_func;
- extension->gdextension.free_property_list = p_extension_funcs->free_property_list_func;
+ extension->gdextension.free_property_list2 = p_extension_funcs->free_property_list_func;
extension->gdextension.property_can_revert = p_extension_funcs->property_can_revert_func;
extension->gdextension.property_get_revert = p_extension_funcs->property_get_revert_func;
extension->gdextension.validate_property = p_extension_funcs->validate_property_func;
#ifndef DISABLE_DEPRECATED
if (p_deprecated_funcs) {
extension->gdextension.notification = p_deprecated_funcs->notification_func;
+ extension->gdextension.free_property_list = p_deprecated_funcs->free_property_list_func;
}
#endif // DISABLE_DEPRECATED
extension->gdextension.notification2 = p_extension_funcs->notification_func;
diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h
index 23b1f51208..3b15639890 100644
--- a/core/extension/gdextension.h
+++ b/core/extension/gdextension.h
@@ -71,6 +71,7 @@ class GDExtension : public Resource {
struct ClassCreationDeprecatedInfo {
#ifndef DISABLE_DEPRECATED
GDExtensionClassNotification notification_func = nullptr;
+ GDExtensionClassFreePropertyList free_property_list_func = nullptr;
#endif // DISABLE_DEPRECATED
};
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index e9c570e994..00a98af4e2 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -256,6 +256,7 @@ typedef struct {
typedef const GDExtensionPropertyInfo *(*GDExtensionClassGetPropertyList)(GDExtensionClassInstancePtr p_instance, uint32_t *r_count);
typedef void (*GDExtensionClassFreePropertyList)(GDExtensionClassInstancePtr p_instance, const GDExtensionPropertyInfo *p_list);
+typedef void (*GDExtensionClassFreePropertyList2)(GDExtensionClassInstancePtr p_instance, const GDExtensionPropertyInfo *p_list, uint32_t p_count);
typedef GDExtensionBool (*GDExtensionClassPropertyCanRevert)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name);
typedef GDExtensionBool (*GDExtensionClassPropertyGetRevert)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret);
typedef GDExtensionBool (*GDExtensionClassValidateProperty)(GDExtensionClassInstancePtr p_instance, GDExtensionPropertyInfo *p_property);
@@ -333,7 +334,7 @@ typedef struct {
GDExtensionClassSet set_func;
GDExtensionClassGet get_func;
GDExtensionClassGetPropertyList get_property_list_func;
- GDExtensionClassFreePropertyList free_property_list_func;
+ GDExtensionClassFreePropertyList2 free_property_list_func;
GDExtensionClassPropertyCanRevert property_can_revert_func;
GDExtensionClassPropertyGetRevert property_get_revert_func;
GDExtensionClassValidateProperty validate_property_func;
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index 7ef1ce74ed..876635529c 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -138,7 +138,7 @@ public:
return nullptr;
}
- static void placeholder_instance_free_property_list(GDExtensionClassInstancePtr p_instance, const GDExtensionPropertyInfo *p_list) {
+ static void placeholder_instance_free_property_list(GDExtensionClassInstancePtr p_instance, const GDExtensionPropertyInfo *p_list, uint32_t p_count) {
}
static GDExtensionBool placeholder_instance_property_can_revert(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name) {
@@ -600,12 +600,13 @@ ObjectGDExtension *ClassDB::get_placeholder_extension(const StringName &p_class)
placeholder_extension->set = &PlaceholderExtensionInstance::placeholder_instance_set;
placeholder_extension->get = &PlaceholderExtensionInstance::placeholder_instance_get;
placeholder_extension->get_property_list = &PlaceholderExtensionInstance::placeholder_instance_get_property_list;
- placeholder_extension->free_property_list = &PlaceholderExtensionInstance::placeholder_instance_free_property_list;
+ placeholder_extension->free_property_list2 = &PlaceholderExtensionInstance::placeholder_instance_free_property_list;
placeholder_extension->property_can_revert = &PlaceholderExtensionInstance::placeholder_instance_property_can_revert;
placeholder_extension->property_get_revert = &PlaceholderExtensionInstance::placeholder_instance_property_get_revert;
placeholder_extension->validate_property = &PlaceholderExtensionInstance::placeholder_instance_validate_property;
#ifndef DISABLE_DEPRECATED
placeholder_extension->notification = nullptr;
+ placeholder_extension->free_property_list = nullptr;
#endif // DISABLE_DEPRECATED
placeholder_extension->notification2 = &PlaceholderExtensionInstance::placeholder_instance_notification;
placeholder_extension->to_string = &PlaceholderExtensionInstance::placeholder_instance_to_string;
diff --git a/core/object/object.cpp b/core/object/object.cpp
index 30629ba205..b6c8a87a22 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -503,9 +503,14 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
for (uint32_t i = 0; i < pcount; i++) {
p_list->push_back(PropertyInfo(pinfo[i]));
}
- if (current_extension->free_property_list) {
+ if (current_extension->free_property_list2) {
+ current_extension->free_property_list2(_extension_instance, pinfo, pcount);
+ }
+#ifndef DISABLE_DEPRECATED
+ else if (current_extension->free_property_list) {
current_extension->free_property_list(_extension_instance, pinfo);
}
+#endif // DISABLE_DEPRECATED
#ifdef TOOLS_ENABLED
}
#endif
diff --git a/core/object/object.h b/core/object/object.h
index 915c3a8c25..adb50268d2 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -324,12 +324,13 @@ struct ObjectGDExtension {
GDExtensionClassSet set;
GDExtensionClassGet get;
GDExtensionClassGetPropertyList get_property_list;
- GDExtensionClassFreePropertyList free_property_list;
+ GDExtensionClassFreePropertyList2 free_property_list2;
GDExtensionClassPropertyCanRevert property_can_revert;
GDExtensionClassPropertyGetRevert property_get_revert;
GDExtensionClassValidateProperty validate_property;
#ifndef DISABLE_DEPRECATED
GDExtensionClassNotification notification;
+ GDExtensionClassFreePropertyList free_property_list;
#endif // DISABLE_DEPRECATED
GDExtensionClassNotification2 notification2;
GDExtensionClassToString to_string;
diff --git a/core/templates/ring_buffer.h b/core/templates/ring_buffer.h
index 54148a59bf..f5161cefa4 100644
--- a/core/templates/ring_buffer.h
+++ b/core/templates/ring_buffer.h
@@ -211,10 +211,10 @@ public:
size_mask = mask;
}
- RingBuffer<T>(int p_power = 0) {
+ RingBuffer(int p_power = 0) {
resize(p_power);
}
- ~RingBuffer<T>() {}
+ ~RingBuffer() {}
};
#endif // RING_BUFFER_H
diff --git a/core/templates/safe_refcount.h b/core/templates/safe_refcount.h
index 637b068da9..16b605eaff 100644
--- a/core/templates/safe_refcount.h
+++ b/core/templates/safe_refcount.h
@@ -146,7 +146,7 @@ public:
}
}
- _ALWAYS_INLINE_ explicit SafeNumeric<T>(T p_value = static_cast<T>(0)) {
+ _ALWAYS_INLINE_ explicit SafeNumeric(T p_value = static_cast<T>(0)) {
set(p_value);
}
};
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index 42ca336a25..e614a897d5 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -1602,7 +1602,7 @@
<param index="0" name="max_size" type="Vector2i" />
<param index="1" name="window_id" type="int" default="0" />
<description>
- Sets the maximum size of the window specified by [param window_id] in pixels. Normally, the user will not be able to drag the window to make it smaller than the specified size. See also [method window_get_max_size].
+ Sets the maximum size of the window specified by [param window_id] in pixels. Normally, the user will not be able to drag the window to make it larger than the specified size. See also [method window_get_max_size].
[b]Note:[/b] It's recommended to change this value using [member Window.max_size] instead.
[b]Note:[/b] Using third-party tools, it is possible for users to disable window geometry restrictions and therefore bypass this limit.
</description>
@@ -1612,7 +1612,7 @@
<param index="0" name="min_size" type="Vector2i" />
<param index="1" name="window_id" type="int" default="0" />
<description>
- Sets the minimum size for the given window to [param min_size] (in pixels). Normally, the user will not be able to drag the window to make it larger than the specified size. See also [method window_get_min_size].
+ Sets the minimum size for the given window to [param min_size] in pixels. Normally, the user will not be able to drag the window to make it smaller than the specified size. See also [method window_get_min_size].
[b]Note:[/b] It's recommended to change this value using [member Window.min_size] instead.
[b]Note:[/b] By default, the main window has a minimum size of [code]Vector2i(64, 64)[/code]. This prevents issues that can arise when the window is resized to a near-zero size.
[b]Note:[/b] Using third-party tools, it is possible for users to disable window geometry restrictions and therefore bypass this limit.
diff --git a/drivers/SCsub b/drivers/SCsub
index 9c8b16d3e5..e77b96cc87 100644
--- a/drivers/SCsub
+++ b/drivers/SCsub
@@ -14,6 +14,8 @@ SConscript("coreaudio/SCsub")
SConscript("pulseaudio/SCsub")
if env["platform"] == "windows":
SConscript("wasapi/SCsub")
+ if not env.msvc:
+ SConscript("backtrace/SCsub")
if env["xaudio2"]:
SConscript("xaudio2/SCsub")
diff --git a/drivers/backtrace/SCsub b/drivers/backtrace/SCsub
new file mode 100644
index 0000000000..f61fb21581
--- /dev/null
+++ b/drivers/backtrace/SCsub
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+
+Import("env")
+
+env_backtrace = env.Clone()
+
+# Thirdparty source files
+
+thirdparty_obj = []
+
+thirdparty_dir = "#thirdparty/libbacktrace/"
+thirdparty_sources = [
+ "atomic.c",
+ "dwarf.c",
+ "fileline.c",
+ "posix.c",
+ "print.c",
+ "sort.c",
+ "state.c",
+ "backtrace.c",
+ "simple.c",
+ "pecoff.c",
+ "read.c",
+ "alloc.c",
+]
+thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+env_backtrace.Prepend(CPPPATH=[thirdparty_dir])
+
+env_thirdparty = env_backtrace.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+
+env.drivers_sources += thirdparty_obj
+
+# Godot source files
+
+driver_obj = []
+
+env_backtrace.add_source_files(driver_obj, "*.cpp")
+env.drivers_sources += driver_obj
+
+# Needed to force rebuilding the driver files when the thirdparty library is updated.
+env.Depends(driver_obj, thirdparty_obj)
diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp
index 726e0fdc5a..9885d9d7ee 100644
--- a/drivers/windows/file_access_windows.cpp
+++ b/drivers/windows/file_access_windows.cpp
@@ -32,6 +32,7 @@
#include "file_access_windows.h"
+#include "core/config/project_settings.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
@@ -121,19 +122,63 @@ Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) {
// Windows is case insensitive, but all other platforms are sensitive to it
// To ease cross-platform development, we issue a warning if users try to access
// a file using the wrong case (which *works* on Windows, but won't on other
- // platforms).
- if (p_mode_flags == READ) {
- WIN32_FIND_DATAW d;
- HANDLE fnd = FindFirstFileW((LPCWSTR)(path.utf16().get_data()), &d);
- if (fnd != INVALID_HANDLE_VALUE) {
- String fname = String::utf16((const char16_t *)(d.cFileName));
- if (!fname.is_empty()) {
- String base_file = path.get_file();
- if (base_file != fname && base_file.findn(fname) == 0) {
- WARN_PRINT("Case mismatch opening requested file '" + base_file + "', stored as '" + fname + "' in the filesystem. This file will not open when exported to other case-sensitive platforms.");
+ // platforms), we only check for relative paths, or paths in res:// or user://,
+ // other paths aren't likely to be portable anyway.
+ if (p_mode_flags == READ && (p_path.is_relative_path() || get_access_type() != ACCESS_FILESYSTEM)) {
+ String base_path = path;
+ String working_path;
+ String proper_path;
+
+ if (get_access_type() == ACCESS_RESOURCES) {
+ if (ProjectSettings::get_singleton()) {
+ working_path = ProjectSettings::get_singleton()->get_resource_path();
+ if (!working_path.is_empty()) {
+ base_path = working_path.path_to_file(base_path);
}
}
+ proper_path = "res://";
+ } else if (get_access_type() == ACCESS_USERDATA) {
+ working_path = OS::get_singleton()->get_user_data_dir();
+ if (!working_path.is_empty()) {
+ base_path = working_path.path_to_file(base_path);
+ }
+ proper_path = "user://";
+ }
+
+ WIN32_FIND_DATAW d;
+ Vector<String> parts = base_path.split("/");
+
+ bool mismatch = false;
+
+ for (const String &part : parts) {
+ working_path = working_path.path_join(part);
+
+ // Skip if relative.
+ if (part == "." || part == "..") {
+ proper_path = proper_path.path_join(part);
+ continue;
+ }
+
+ HANDLE fnd = FindFirstFileW((LPCWSTR)(working_path.utf16().get_data()), &d);
+
+ if (fnd == INVALID_HANDLE_VALUE) {
+ mismatch = false;
+ break;
+ }
+
+ const String fname = String::utf16((const char16_t *)(d.cFileName));
+
FindClose(fnd);
+
+ if (!mismatch) {
+ mismatch = (part != fname && part.findn(fname) == 0);
+ }
+
+ proper_path = proper_path.path_join(fname);
+ }
+
+ if (mismatch) {
+ WARN_PRINT("Case mismatch opening requested file '" + p_path + "', stored as '" + proper_path + "' in the filesystem. This file will not open when exported to other case-sensitive platforms.");
}
}
#endif
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index bec95d40c6..86fe9dd1fb 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -4485,6 +4485,7 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD
if (p_id.type == Animation::TYPE_VALUE) {
undo_redo->add_do_method(reset_anim, "value_track_set_update_mode", p_next_tracks.reset, update_mode);
}
+ undo_redo->add_do_method(reset_anim, "track_set_interpolation_type", p_next_tracks.reset, interp_type);
undo_redo->add_do_method(reset_anim, "track_insert_key", p_next_tracks.reset, 0.0f, value);
undo_redo->add_undo_method(reset_anim, "remove_track", reset_anim->get_track_count());
p_next_tracks.reset++;
diff --git a/editor/debugger/debug_adapter/debug_adapter_server.h b/editor/debugger/debug_adapter/debug_adapter_server.h
index c834ab2182..310bac1b6c 100644
--- a/editor/debugger/debug_adapter/debug_adapter_server.h
+++ b/editor/debugger/debug_adapter/debug_adapter_server.h
@@ -32,7 +32,7 @@
#define DEBUG_ADAPTER_SERVER_H
#include "debug_adapter_protocol.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class DebugAdapterServer : public EditorPlugin {
GDCLASS(DebugAdapterServer, EditorPlugin);
diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp
index 61c4eed669..af0419a81a 100644
--- a/editor/editor_about.cpp
+++ b/editor/editor_about.cpp
@@ -63,7 +63,6 @@ void EditorAbout::_notification(int p_what) {
_logo->set_texture(get_editor_theme_icon(SNAME("Logo")));
- Ref<StyleBoxEmpty> empty_stylebox = memnew(StyleBoxEmpty);
for (ItemList *il : name_lists) {
for (int i = 0; i < il->get_item_count(); i++) {
if (il->get_item_metadata(i)) {
diff --git a/editor/editor_audio_buses.h b/editor/editor_audio_buses.h
index 99e3214781..b1f811fbf6 100644
--- a/editor/editor_audio_buses.h
+++ b/editor/editor_audio_buses.h
@@ -31,7 +31,7 @@
#ifndef EDITOR_AUDIO_BUSES_H
#define EDITOR_AUDIO_BUSES_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/control.h"
diff --git a/editor/editor_builders.py b/editor/editor_builders.py
index a25f4949c4..cfe6e56b49 100644
--- a/editor/editor_builders.py
+++ b/editor/editor_builders.py
@@ -7,6 +7,7 @@ import subprocess
import tempfile
import uuid
import zlib
+from methods import print_warning
def make_doc_header(target, source, env):
@@ -57,7 +58,7 @@ def make_translations_header(target, source, env, category):
msgfmt_available = shutil.which("msgfmt") is not None
if not msgfmt_available:
- print("WARNING: msgfmt is not found, using .po files instead of .mo")
+ print_warning("msgfmt is not found, using .po files instead of .mo")
xl_names = []
for i in range(len(sorted_paths)):
@@ -71,8 +72,8 @@ def make_translations_header(target, source, env, category):
with open(mo_path, "rb") as f:
buf = f.read()
except OSError as e:
- print(
- "WARNING: msgfmt execution failed, using .po file instead of .mo: path=%r; [%s] %s"
+ print_warning(
+ "msgfmt execution failed, using .po file instead of .mo: path=%r; [%s] %s"
% (sorted_paths[i], e.__class__.__name__, e)
)
with open(sorted_paths[i], "rb") as f:
@@ -82,9 +83,8 @@ def make_translations_header(target, source, env, category):
os.remove(mo_path)
except OSError as e:
# Do not fail the entire build if it cannot delete a temporary file.
- print(
- "WARNING: Could not delete temporary .mo file: path=%r; [%s] %s"
- % (mo_path, e.__class__.__name__, e)
+ print_warning(
+ "Could not delete temporary .mo file: path=%r; [%s] %s" % (mo_path, e.__class__.__name__, e)
)
else:
with open(sorted_paths[i], "rb") as f:
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index bdc6504417..bd1eef8e53 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -36,9 +36,9 @@
#include "core/io/image_loader.h"
#include "core/io/resource_loader.h"
#include "editor/editor_node.h"
-#include "editor/editor_plugin.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/multi_node_edit.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor/themes/editor_scale.h"
#include "scene/resources/packed_scene.h"
diff --git a/editor/editor_help.h b/editor/editor_help.h
index 078b42b439..ca3a05275f 100644
--- a/editor/editor_help.h
+++ b/editor/editor_help.h
@@ -34,7 +34,7 @@
#include "core/os/thread.h"
#include "editor/code_editor.h"
#include "editor/doc_tools.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/popup.h"
diff --git a/editor/editor_help_search.h b/editor/editor_help_search.h
index 39ffc2f71b..58061dae4c 100644
--- a/editor/editor_help_search.h
+++ b/editor/editor_help_search.h
@@ -34,7 +34,7 @@
#include "core/templates/rb_map.h"
#include "editor/code_editor.h"
#include "editor/editor_help.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/option_button.h"
#include "scene/gui/tree.h"
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 50cc89c618..41396a2ed1 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -3379,7 +3379,12 @@ void EditorInspector::update_tree() {
if (use_doc_hints) {
// `|` separators used in `EditorHelpBit`.
if (theme_item_name.is_empty()) {
- if (p.usage & PROPERTY_USAGE_INTERNAL) {
+ if (p.name.contains("shader_parameter/")) {
+ ShaderMaterial *shader_material = Object::cast_to<ShaderMaterial>(object);
+ if (shader_material) {
+ ep->set_tooltip_text("property|" + shader_material->get_shader()->get_path() + "|" + property_prefix + p.name);
+ }
+ } else if (p.usage & PROPERTY_USAGE_INTERNAL) {
ep->set_tooltip_text("internal_property|" + classname + "|" + property_prefix + p.name);
} else {
ep->set_tooltip_text("property|" + classname + "|" + property_prefix + p.name);
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 134733fa11..b9ac7b6d42 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -90,7 +90,6 @@
#include "editor/editor_log.h"
#include "editor/editor_native_shader_source_visualizer.h"
#include "editor/editor_paths.h"
-#include "editor/editor_plugin.h"
#include "editor/editor_properties.h"
#include "editor/editor_property_name_processor.h"
#include "editor/editor_quick_open.h"
@@ -134,12 +133,12 @@
#include "editor/inspector_dock.h"
#include "editor/multi_node_edit.h"
#include "editor/node_dock.h"
-#include "editor/plugin_config_dialog.h"
#include "editor/plugins/animation_player_editor_plugin.h"
#include "editor/plugins/asset_library_editor_plugin.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "editor/plugins/debugger_editor_plugin.h"
#include "editor/plugins/dedicated_server_export_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/editor_preview_plugins.h"
#include "editor/plugins/editor_resource_conversion_plugin.h"
#include "editor/plugins/gdextension_export_plugin.h"
@@ -148,6 +147,7 @@
#include "editor/plugins/node_3d_editor_plugin.h"
#include "editor/plugins/packed_scene_translation_parser_plugin.h"
#include "editor/plugins/particle_process_material_editor_plugin.h"
+#include "editor/plugins/plugin_config_dialog.h"
#include "editor/plugins/root_motion_editor_plugin.h"
#include "editor/plugins/script_text_editor.h"
#include "editor/plugins/text_editor.h"
diff --git a/editor/editor_node.h b/editor/editor_node.h
index ad0a7ec5e0..54c84bc6a6 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -35,7 +35,7 @@
#include "core/templates/safe_refcount.h"
#include "editor/editor_data.h"
#include "editor/editor_folding.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
typedef void (*EditorNodeInitCallback)();
typedef void (*EditorPluginInitializeCallback)();
diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp
index ddf230dfdb..dd698d74b6 100644
--- a/editor/editor_resource_preview.cpp
+++ b/editor/editor_resource_preview.cpp
@@ -118,8 +118,6 @@ Variant EditorResourcePreviewGenerator::DrawRequester::_post_semaphore() const {
return Variant(); // Needed because of how the callback is used.
}
-EditorResourcePreview *EditorResourcePreview::singleton = nullptr;
-
bool EditorResourcePreview::is_threaded() const {
return RSG::texture_storage->can_create_resources_async();
}
@@ -291,13 +289,17 @@ void EditorResourcePreview::_iterate() {
bool has_small_texture;
uint64_t last_modtime;
String hash;
- _read_preview_cache(f, &tsize, &has_small_texture, &last_modtime, &hash, &preview_metadata);
+ bool outdated;
+ _read_preview_cache(f, &tsize, &has_small_texture, &last_modtime, &hash, &preview_metadata, &outdated);
bool cache_valid = true;
if (tsize != thumbnail_size) {
cache_valid = false;
f.unref();
+ } else if (outdated) {
+ cache_valid = false;
+ f.unref();
} else if (last_modtime != modtime) {
String last_md5 = f->get_line();
String md5 = FileAccess::get_md5(item.path);
@@ -357,14 +359,16 @@ void EditorResourcePreview::_write_preview_cache(Ref<FileAccess> p_file, int p_t
p_file->store_line(itos(p_modified_time));
p_file->store_line(p_hash);
p_file->store_line(VariantUtilityFunctions::var_to_str(p_metadata).replace("\n", " "));
+ p_file->store_line(itos(CURRENT_METADATA_VERSION));
}
-void EditorResourcePreview::_read_preview_cache(Ref<FileAccess> p_file, int *r_thumbnail_size, bool *r_has_small_texture, uint64_t *r_modified_time, String *r_hash, Dictionary *r_metadata) {
+void EditorResourcePreview::_read_preview_cache(Ref<FileAccess> p_file, int *r_thumbnail_size, bool *r_has_small_texture, uint64_t *r_modified_time, String *r_hash, Dictionary *r_metadata, bool *r_outdated) {
*r_thumbnail_size = p_file->get_line().to_int();
*r_has_small_texture = p_file->get_line().to_int();
*r_modified_time = p_file->get_line().to_int();
*r_hash = p_file->get_line();
*r_metadata = VariantUtilityFunctions::str_to_var(p_file->get_line());
+ *r_outdated = p_file->get_line().to_int() < CURRENT_METADATA_VERSION;
}
void EditorResourcePreview::_thread() {
diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h
index 8461651732..6b67acceaa 100644
--- a/editor/editor_resource_preview.h
+++ b/editor/editor_resource_preview.h
@@ -76,7 +76,8 @@ public:
class EditorResourcePreview : public Node {
GDCLASS(EditorResourcePreview, Node);
- static EditorResourcePreview *singleton;
+ inline static constexpr int CURRENT_METADATA_VERSION = 1; // Increment this number to invalidate all previews.
+ inline static EditorResourcePreview *singleton = nullptr;
struct QueueItem {
Ref<Resource> resource;
@@ -118,7 +119,7 @@ class EditorResourcePreview : public Node {
void _iterate();
void _write_preview_cache(Ref<FileAccess> p_file, int p_thumbnail_size, bool p_has_small_texture, uint64_t p_modified_time, const String &p_hash, const Dictionary &p_metadata);
- void _read_preview_cache(Ref<FileAccess> p_file, int *r_thumbnail_size, bool *r_has_small_texture, uint64_t *r_modified_time, String *r_hash, Dictionary *r_metadata);
+ void _read_preview_cache(Ref<FileAccess> p_file, int *r_thumbnail_size, bool *r_has_small_texture, uint64_t *r_modified_time, String *r_hash, Dictionary *r_metadata, bool *r_outdated);
Vector<Ref<EditorResourcePreviewGenerator>> preview_generators;
diff --git a/editor/import/audio_stream_import_settings.h b/editor/import/audio_stream_import_settings.h
index da6344adb9..931faf45af 100644
--- a/editor/import/audio_stream_import_settings.h
+++ b/editor/import/audio_stream_import_settings.h
@@ -31,7 +31,7 @@
#ifndef AUDIO_STREAM_IMPORT_SETTINGS_H
#define AUDIO_STREAM_IMPORT_SETTINGS_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/audio/audio_stream_player.h"
#include "scene/gui/color_rect.h"
#include "scene/gui/dialogs.h"
diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp
index ab14a5f01d..c8add6ab20 100644
--- a/editor/import/resource_importer_wav.cpp
+++ b/editor/import/resource_importer_wav.cpp
@@ -330,14 +330,12 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
for (int i = 0; i < new_data_frames; i++) {
// Cubic interpolation should be enough.
- float mu = frac;
-
float y0 = data[MAX(0, ipos - 1) * format_channels + c];
float y1 = data[ipos * format_channels + c];
float y2 = data[MIN(frames - 1, ipos + 1) * format_channels + c];
float y3 = data[MIN(frames - 1, ipos + 2) * format_channels + c];
- new_data.write[i * format_channels + c] = Math::cubic_interpolate(y1, y2, y0, y3, mu);
+ new_data.write[i * format_channels + c] = Math::cubic_interpolate(y1, y2, y0, y3, frac);
// update position and always keep fractional part within ]0...1]
// in order to avoid 32bit floating point precision errors
diff --git a/editor/import_defaults_editor.cpp b/editor/import_defaults_editor.cpp
index 569fdc091e..968f2a8915 100644
--- a/editor/import_defaults_editor.cpp
+++ b/editor/import_defaults_editor.cpp
@@ -34,9 +34,9 @@
#include "core/io/resource_importer.h"
#include "editor/action_map_editor.h"
#include "editor/editor_autoload_settings.h"
-#include "editor/editor_plugin_settings.h"
#include "editor/editor_sectioned_inspector.h"
#include "editor/localization_editor.h"
+#include "editor/plugins/editor_plugin_settings.h"
#include "editor/shader_globals_editor.h"
#include "scene/gui/center_container.h"
diff --git a/editor/plugins/abstract_polygon_2d_editor.h b/editor/plugins/abstract_polygon_2d_editor.h
index 70cccebf40..42170d9ffd 100644
--- a/editor/plugins/abstract_polygon_2d_editor.h
+++ b/editor/plugins/abstract_polygon_2d_editor.h
@@ -31,7 +31,7 @@
#ifndef ABSTRACT_POLYGON_2D_EDITOR_H
#define ABSTRACT_POLYGON_2D_EDITOR_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/2d/polygon_2d.h"
#include "scene/gui/box_container.h"
diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp
index 8c2b738549..1baeddbe86 100644
--- a/editor/plugins/animation_blend_space_1d_editor.cpp
+++ b/editor/plugins/animation_blend_space_1d_editor.cpp
@@ -583,9 +583,9 @@ void AnimationNodeBlendSpace1DEditor::_notification(int p_what) {
snap->set_icon(get_editor_theme_icon(SNAME("SnapGrid")));
open_editor->set_icon(get_editor_theme_icon(SNAME("Edit")));
interpolation->clear();
- interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackContinuous")), "", 0);
- interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackDiscrete")), "", 1);
- interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackCapture")), "", 2);
+ interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackContinuous")), TTR("Continuous"), 0);
+ interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackDiscrete")), TTR("Discrete"), 1);
+ interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackCapture")), TTR("Capture"), 2);
} break;
case NOTIFICATION_PROCESS: {
diff --git a/editor/plugins/animation_blend_space_1d_editor.h b/editor/plugins/animation_blend_space_1d_editor.h
index a0ed813100..00bd3c767d 100644
--- a/editor/plugins/animation_blend_space_1d_editor.h
+++ b/editor/plugins/animation_blend_space_1d_editor.h
@@ -31,8 +31,8 @@
#ifndef ANIMATION_BLEND_SPACE_1D_EDITOR_H
#define ANIMATION_BLEND_SPACE_1D_EDITOR_H
-#include "editor/editor_plugin.h"
#include "editor/plugins/animation_tree_editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/animation/animation_blend_space_1d.h"
#include "scene/gui/graph_edit.h"
#include "scene/gui/popup.h"
diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp
index ec67fb7254..06853f0862 100644
--- a/editor/plugins/animation_blend_space_2d_editor.cpp
+++ b/editor/plugins/animation_blend_space_2d_editor.cpp
@@ -807,9 +807,9 @@ void AnimationNodeBlendSpace2DEditor::_notification(int p_what) {
open_editor->set_icon(get_editor_theme_icon(SNAME("Edit")));
auto_triangles->set_icon(get_editor_theme_icon(SNAME("AutoTriangle")));
interpolation->clear();
- interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackContinuous")), "", 0);
- interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackDiscrete")), "", 1);
- interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackCapture")), "", 2);
+ interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackContinuous")), TTR("Continuous"), 0);
+ interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackDiscrete")), TTR("Discrete"), 1);
+ interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackCapture")), TTR("Capture"), 2);
} break;
case NOTIFICATION_PROCESS: {
diff --git a/editor/plugins/animation_blend_space_2d_editor.h b/editor/plugins/animation_blend_space_2d_editor.h
index a89a7b4511..15d81c0707 100644
--- a/editor/plugins/animation_blend_space_2d_editor.h
+++ b/editor/plugins/animation_blend_space_2d_editor.h
@@ -31,8 +31,8 @@
#ifndef ANIMATION_BLEND_SPACE_2D_EDITOR_H
#define ANIMATION_BLEND_SPACE_2D_EDITOR_H
-#include "editor/editor_plugin.h"
#include "editor/plugins/animation_tree_editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/animation/animation_blend_space_2d.h"
#include "scene/gui/graph_edit.h"
#include "scene/gui/popup.h"
diff --git a/editor/plugins/animation_library_editor.h b/editor/plugins/animation_library_editor.h
index a268e68932..c8d9274f4f 100644
--- a/editor/plugins/animation_library_editor.h
+++ b/editor/plugins/animation_library_editor.h
@@ -32,7 +32,7 @@
#define ANIMATION_LIBRARY_EDITOR_H
#include "editor/animation_track_editor.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/animation/animation_mixer.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/tree.h"
diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h
index a1175e2a0f..70b31759fc 100644
--- a/editor/plugins/animation_player_editor_plugin.h
+++ b/editor/plugins/animation_player_editor_plugin.h
@@ -32,8 +32,8 @@
#define ANIMATION_PLAYER_EDITOR_PLUGIN_H
#include "editor/animation_track_editor.h"
-#include "editor/editor_plugin.h"
#include "editor/plugins/animation_library_editor.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/animation/animation_player.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/slider.h"
diff --git a/editor/plugins/animation_tree_editor_plugin.h b/editor/plugins/animation_tree_editor_plugin.h
index da979a5315..8dc820695a 100644
--- a/editor/plugins/animation_tree_editor_plugin.h
+++ b/editor/plugins/animation_tree_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef ANIMATION_TREE_EDITOR_PLUGIN_H
#define ANIMATION_TREE_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/animation/animation_tree.h"
#include "scene/gui/graph_edit.h"
diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h
index cb38933bf4..a80e7c8f96 100644
--- a/editor/plugins/asset_library_editor_plugin.h
+++ b/editor/plugins/asset_library_editor_plugin.h
@@ -32,8 +32,8 @@
#define ASSET_LIBRARY_EDITOR_PLUGIN_H
#include "editor/editor_asset_installer.h"
-#include "editor/editor_plugin.h"
-#include "editor/editor_plugin_settings.h"
+#include "editor/plugins/editor_plugin.h"
+#include "editor/plugins/editor_plugin_settings.h"
#include "scene/gui/box_container.h"
#include "scene/gui/check_box.h"
#include "scene/gui/grid_container.h"
diff --git a/editor/plugins/audio_stream_editor_plugin.h b/editor/plugins/audio_stream_editor_plugin.h
index 52aa5f6150..0501409c17 100644
--- a/editor/plugins/audio_stream_editor_plugin.h
+++ b/editor/plugins/audio_stream_editor_plugin.h
@@ -32,7 +32,7 @@
#define AUDIO_STREAM_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/audio/audio_stream_player.h"
#include "scene/gui/button.h"
#include "scene/gui/color_rect.h"
diff --git a/editor/plugins/audio_stream_randomizer_editor_plugin.h b/editor/plugins/audio_stream_randomizer_editor_plugin.h
index 535ab4114b..9d2fc76e9e 100644
--- a/editor/plugins/audio_stream_randomizer_editor_plugin.h
+++ b/editor/plugins/audio_stream_randomizer_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef AUDIO_STREAM_RANDOMIZER_EDITOR_PLUGIN_H
#define AUDIO_STREAM_RANDOMIZER_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "servers/audio/audio_stream.h"
class AudioStreamRandomizerEditorPlugin : public EditorPlugin {
diff --git a/editor/plugins/bit_map_editor_plugin.h b/editor/plugins/bit_map_editor_plugin.h
index afab1da2f7..030536ab6b 100644
--- a/editor/plugins/bit_map_editor_plugin.h
+++ b/editor/plugins/bit_map_editor_plugin.h
@@ -32,7 +32,7 @@
#define BIT_MAP_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/resources/bit_map.h"
class TextureRect;
diff --git a/editor/plugins/bone_map_editor_plugin.cpp b/editor/plugins/bone_map_editor_plugin.cpp
index e67f3bd596..2bf9ad9e09 100644
--- a/editor/plugins/bone_map_editor_plugin.cpp
+++ b/editor/plugins/bone_map_editor_plugin.cpp
@@ -861,6 +861,8 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
picklist.clear();
// 4-1. Guess Finger
+ int tips_index = -1;
+ bool thumb_tips_size = 0;
bool named_finger_is_found = false;
LocalVector<String> fingers;
fingers.push_back("thumb|pollex");
@@ -894,6 +896,33 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
search_path.push_back(finger);
finger = skeleton->get_bone_parent(finger);
}
+ // Tips detection by name matching with "distal" from root.
+ for (int j = search_path.size() - 1; j >= 0; j--) {
+ if (RegEx("distal").search(skeleton->get_bone_name(search_path[j]).to_lower()).is_valid()) {
+ tips_index = j - 1;
+ break;
+ }
+ }
+ // Tips detection by name matching with "tip|leaf" from end.
+ if (tips_index < 0) {
+ for (int j = 0; j < search_path.size(); j++) {
+ if (RegEx("tip|leaf").search(skeleton->get_bone_name(search_path[j]).to_lower()).is_valid()) {
+ tips_index = j;
+ break;
+ }
+ }
+ }
+ // Tips detection by thumb children size.
+ if (tips_index < 0) {
+ if (i == 0) {
+ thumb_tips_size = MAX(0, search_path.size() - 3);
+ }
+ tips_index = thumb_tips_size - 1;
+ }
+ // Remove tips.
+ for (int j = 0; j <= tips_index; j++) {
+ search_path.remove_at(0);
+ }
search_path.reverse();
if (search_path.size() == 1) {
p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][0], skeleton->get_bone_name(search_path[0]));
@@ -941,6 +970,14 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
}
}
search_path.push_back(finger_root);
+ // Tips detection by thumb children size.
+ if (i == 0) {
+ thumb_tips_size = MAX(0, search_path.size() - 3);
+ }
+ tips_index = thumb_tips_size - 1;
+ for (int j = 0; j <= tips_index; j++) {
+ search_path.remove_at(0);
+ }
search_path.reverse();
if (search_path.size() == 1) {
p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][0], skeleton->get_bone_name(search_path[0]));
@@ -958,6 +995,9 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
picklist.clear();
}
}
+
+ tips_index = -1;
+ thumb_tips_size = 0;
named_finger_is_found = false;
if (right_hand_or_palm != -1) {
LocalVector<LocalVector<String>> right_fingers_map;
@@ -985,6 +1025,33 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
search_path.push_back(finger);
finger = skeleton->get_bone_parent(finger);
}
+ // Tips detection by name matching with "distal" from root.
+ for (int j = search_path.size() - 1; j >= 0; j--) {
+ if (RegEx("distal").search(skeleton->get_bone_name(search_path[j]).to_lower()).is_valid()) {
+ tips_index = j - 1;
+ break;
+ }
+ }
+ // Tips detection by name matching with "tip|leaf" from end.
+ if (tips_index < 0) {
+ for (int j = 0; j < search_path.size(); j++) {
+ if (RegEx("tip|leaf").search(skeleton->get_bone_name(search_path[j]).to_lower()).is_valid()) {
+ tips_index = j;
+ break;
+ }
+ }
+ }
+ // Tips detection by thumb children size.
+ if (tips_index < 0) {
+ if (i == 0) {
+ thumb_tips_size = MAX(0, search_path.size() - 3);
+ }
+ tips_index = thumb_tips_size - 1;
+ }
+ // Remove tips.
+ for (int j = 0; j <= tips_index; j++) {
+ search_path.remove_at(0);
+ }
search_path.reverse();
if (search_path.size() == 1) {
p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][0], skeleton->get_bone_name(search_path[0]));
@@ -1032,6 +1099,14 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
}
}
search_path.push_back(finger_root);
+ // Tips detection by thumb children size.
+ if (i == 0) {
+ thumb_tips_size = MAX(0, search_path.size() - 3);
+ }
+ tips_index = thumb_tips_size - 1;
+ for (int j = 0; j <= tips_index; j++) {
+ search_path.remove_at(0);
+ }
search_path.reverse();
if (search_path.size() == 1) {
p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][0], skeleton->get_bone_name(search_path[0]));
diff --git a/editor/plugins/bone_map_editor_plugin.h b/editor/plugins/bone_map_editor_plugin.h
index 2e7d1ff124..f3aa2fc84d 100644
--- a/editor/plugins/bone_map_editor_plugin.h
+++ b/editor/plugins/bone_map_editor_plugin.h
@@ -32,8 +32,8 @@
#define BONE_MAP_EDITOR_PLUGIN_H
#include "editor/editor_node.h"
-#include "editor/editor_plugin.h"
#include "editor/editor_properties.h"
+#include "editor/plugins/editor_plugin.h"
#include "modules/modules_enabled.gen.h" // For regex.
#ifdef MODULE_REGEX_ENABLED
diff --git a/editor/plugins/camera_3d_editor_plugin.h b/editor/plugins/camera_3d_editor_plugin.h
index 7d5fae6f2b..2e4d8a1ee3 100644
--- a/editor/plugins/camera_3d_editor_plugin.h
+++ b/editor/plugins/camera_3d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef CAMERA_3D_EDITOR_PLUGIN_H
#define CAMERA_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/camera_3d.h"
class Camera3DEditor : public Control {
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index f52ad3dc4f..6d951a77ec 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef CANVAS_ITEM_EDITOR_PLUGIN_H
#define CANVAS_ITEM_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/base_button.h"
#include "scene/gui/box_container.h"
diff --git a/editor/plugins/cast_2d_editor_plugin.h b/editor/plugins/cast_2d_editor_plugin.h
index 36b302f311..cbe7e03008 100644
--- a/editor/plugins/cast_2d_editor_plugin.h
+++ b/editor/plugins/cast_2d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef CAST_2D_EDITOR_PLUGIN_H
#define CAST_2D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/2d/node_2d.h"
class CanvasItemEditor;
diff --git a/editor/plugins/collision_shape_2d_editor_plugin.h b/editor/plugins/collision_shape_2d_editor_plugin.h
index 19b2de3821..672e1d9ce0 100644
--- a/editor/plugins/collision_shape_2d_editor_plugin.h
+++ b/editor/plugins/collision_shape_2d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef COLLISION_SHAPE_2D_EDITOR_PLUGIN_H
#define COLLISION_SHAPE_2D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/2d/physics/collision_shape_2d.h"
class CanvasItemEditor;
diff --git a/editor/plugins/control_editor_plugin.h b/editor/plugins/control_editor_plugin.h
index 4a411c0241..b87ec52931 100644
--- a/editor/plugins/control_editor_plugin.h
+++ b/editor/plugins/control_editor_plugin.h
@@ -32,7 +32,7 @@
#define CONTROL_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/check_box.h"
diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.h b/editor/plugins/cpu_particles_2d_editor_plugin.h
index a408f771eb..4d59c9981e 100644
--- a/editor/plugins/cpu_particles_2d_editor_plugin.h
+++ b/editor/plugins/cpu_particles_2d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef CPU_PARTICLES_2D_EDITOR_PLUGIN_H
#define CPU_PARTICLES_2D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/2d/cpu_particles_2d.h"
#include "scene/2d/physics/collision_polygon_2d.h"
#include "scene/gui/box_container.h"
diff --git a/editor/plugins/curve_editor_plugin.h b/editor/plugins/curve_editor_plugin.h
index b6a74d9b93..c844f42029 100644
--- a/editor/plugins/curve_editor_plugin.h
+++ b/editor/plugins/curve_editor_plugin.h
@@ -32,8 +32,8 @@
#define CURVE_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
#include "editor/editor_resource_preview.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/resources/curve.h"
class EditorSpinSlider;
diff --git a/editor/plugins/debugger_editor_plugin.h b/editor/plugins/debugger_editor_plugin.h
index b7453e3e81..a6df83496e 100644
--- a/editor/plugins/debugger_editor_plugin.h
+++ b/editor/plugins/debugger_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef DEBUGGER_EDITOR_PLUGIN_H
#define DEBUGGER_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class EditorFileServer;
class MenuButton;
diff --git a/editor/editor_plugin.compat.inc b/editor/plugins/editor_plugin.compat.inc
index 7edf938604..7edf938604 100644
--- a/editor/editor_plugin.compat.inc
+++ b/editor/plugins/editor_plugin.compat.inc
diff --git a/editor/editor_plugin.cpp b/editor/plugins/editor_plugin.cpp
index f42a1555a2..f42a1555a2 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/plugins/editor_plugin.cpp
diff --git a/editor/editor_plugin.h b/editor/plugins/editor_plugin.h
index f45a512b89..f45a512b89 100644
--- a/editor/editor_plugin.h
+++ b/editor/plugins/editor_plugin.h
diff --git a/editor/editor_plugin_settings.cpp b/editor/plugins/editor_plugin_settings.cpp
index 2920cf19c0..2920cf19c0 100644
--- a/editor/editor_plugin_settings.cpp
+++ b/editor/plugins/editor_plugin_settings.cpp
diff --git a/editor/editor_plugin_settings.h b/editor/plugins/editor_plugin_settings.h
index 8db8ffd78c..5b470b3e58 100644
--- a/editor/editor_plugin_settings.h
+++ b/editor/plugins/editor_plugin_settings.h
@@ -32,7 +32,7 @@
#define EDITOR_PLUGIN_SETTINGS_H
#include "editor/editor_data.h"
-#include "editor/plugin_config_dialog.h"
+#include "editor/plugins/plugin_config_dialog.h"
class Tree;
diff --git a/editor/plugins/font_config_plugin.h b/editor/plugins/font_config_plugin.h
index 1adb578950..7b2d26da4a 100644
--- a/editor/plugins/font_config_plugin.h
+++ b/editor/plugins/font_config_plugin.h
@@ -32,9 +32,9 @@
#define FONT_CONFIG_PLUGIN_H
#include "core/io/marshalls.h"
-#include "editor/editor_plugin.h"
#include "editor/editor_properties.h"
#include "editor/editor_properties_array_dict.h"
+#include "editor/plugins/editor_plugin.h"
/*************************************************************************/
diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.h b/editor/plugins/gpu_particles_2d_editor_plugin.h
index aad623ee60..bb0ca5de3a 100644
--- a/editor/plugins/gpu_particles_2d_editor_plugin.h
+++ b/editor/plugins/gpu_particles_2d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef GPU_PARTICLES_2D_EDITOR_PLUGIN_H
#define GPU_PARTICLES_2D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/2d/gpu_particles_2d.h"
#include "scene/2d/physics/collision_polygon_2d.h"
#include "scene/gui/box_container.h"
diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.h b/editor/plugins/gpu_particles_3d_editor_plugin.h
index 176a45df56..3b2ab2f8ca 100644
--- a/editor/plugins/gpu_particles_3d_editor_plugin.h
+++ b/editor/plugins/gpu_particles_3d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef GPU_PARTICLES_3D_EDITOR_PLUGIN_H
#define GPU_PARTICLES_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/gpu_particles_3d.h"
#include "scene/gui/spin_box.h"
diff --git a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h
index 9f59f6e2cd..bba8bd2584 100644
--- a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h
+++ b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef GPU_PARTICLES_COLLISION_SDF_EDITOR_PLUGIN_H
#define GPU_PARTICLES_COLLISION_SDF_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/gpu_particles_collision_3d.h"
#include "scene/resources/material.h"
diff --git a/editor/plugins/gradient_editor_plugin.h b/editor/plugins/gradient_editor_plugin.h
index 06d79d55ab..f211e4ef30 100644
--- a/editor/plugins/gradient_editor_plugin.h
+++ b/editor/plugins/gradient_editor_plugin.h
@@ -32,7 +32,7 @@
#define GRADIENT_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class EditorSpinSlider;
class ColorPicker;
diff --git a/editor/plugins/gradient_texture_2d_editor_plugin.h b/editor/plugins/gradient_texture_2d_editor_plugin.h
index 33570593cc..a9f247c3cc 100644
--- a/editor/plugins/gradient_texture_2d_editor_plugin.h
+++ b/editor/plugins/gradient_texture_2d_editor_plugin.h
@@ -32,7 +32,7 @@
#define GRADIENT_TEXTURE_2D_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class Button;
class EditorSpinSlider;
diff --git a/editor/plugins/input_event_editor_plugin.h b/editor/plugins/input_event_editor_plugin.h
index 779e59edd4..48c16a4807 100644
--- a/editor/plugins/input_event_editor_plugin.h
+++ b/editor/plugins/input_event_editor_plugin.h
@@ -33,7 +33,7 @@
#include "editor/action_map_editor.h"
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class InputEventConfigContainer : public VBoxContainer {
GDCLASS(InputEventConfigContainer, VBoxContainer);
diff --git a/editor/plugins/lightmap_gi_editor_plugin.h b/editor/plugins/lightmap_gi_editor_plugin.h
index 1234438c8d..3e739adf9e 100644
--- a/editor/plugins/lightmap_gi_editor_plugin.h
+++ b/editor/plugins/lightmap_gi_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef LIGHTMAP_GI_EDITOR_PLUGIN_H
#define LIGHTMAP_GI_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/lightmap_gi.h"
#include "scene/resources/material.h"
diff --git a/editor/plugins/material_editor_plugin.h b/editor/plugins/material_editor_plugin.h
index c60de1ade9..fb6bafc0ef 100644
--- a/editor/plugins/material_editor_plugin.h
+++ b/editor/plugins/material_editor_plugin.h
@@ -32,7 +32,7 @@
#define MATERIAL_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/editor_resource_conversion_plugin.h"
#include "scene/resources/3d/primitive_meshes.h"
#include "scene/resources/material.h"
diff --git a/editor/plugins/mesh_editor_plugin.h b/editor/plugins/mesh_editor_plugin.h
index a8ef476f84..85d92e7800 100644
--- a/editor/plugins/mesh_editor_plugin.h
+++ b/editor/plugins/mesh_editor_plugin.h
@@ -32,7 +32,7 @@
#define MESH_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/mesh_instance_3d.h"
diff --git a/editor/plugins/mesh_instance_3d_editor_plugin.h b/editor/plugins/mesh_instance_3d_editor_plugin.h
index ce7d23239c..20c151fb92 100644
--- a/editor/plugins/mesh_instance_3d_editor_plugin.h
+++ b/editor/plugins/mesh_instance_3d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef MESH_INSTANCE_3D_EDITOR_PLUGIN_H
#define MESH_INSTANCE_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/gui/option_button.h"
diff --git a/editor/plugins/mesh_library_editor_plugin.h b/editor/plugins/mesh_library_editor_plugin.h
index 3895d10c37..94f46beea1 100644
--- a/editor/plugins/mesh_library_editor_plugin.h
+++ b/editor/plugins/mesh_library_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef MESH_LIBRARY_EDITOR_PLUGIN_H
#define MESH_LIBRARY_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/resources/3d/mesh_library.h"
class EditorFileDialog;
diff --git a/editor/plugins/multimesh_editor_plugin.h b/editor/plugins/multimesh_editor_plugin.h
index b21a932809..5051926c64 100644
--- a/editor/plugins/multimesh_editor_plugin.h
+++ b/editor/plugins/multimesh_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef MULTIMESH_EDITOR_PLUGIN_H
#define MULTIMESH_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/multimesh_instance_3d.h"
#include "scene/gui/slider.h"
#include "scene/gui/spin_box.h"
diff --git a/editor/plugins/navigation_link_2d_editor_plugin.h b/editor/plugins/navigation_link_2d_editor_plugin.h
index ea731ca2cd..7a4be18c31 100644
--- a/editor/plugins/navigation_link_2d_editor_plugin.h
+++ b/editor/plugins/navigation_link_2d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef NAVIGATION_LINK_2D_EDITOR_PLUGIN_H
#define NAVIGATION_LINK_2D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/2d/navigation_link_2d.h"
class CanvasItemEditor;
diff --git a/editor/plugins/navigation_obstacle_3d_editor_plugin.h b/editor/plugins/navigation_obstacle_3d_editor_plugin.h
index 74094dc86f..c62a5a281b 100644
--- a/editor/plugins/navigation_obstacle_3d_editor_plugin.h
+++ b/editor/plugins/navigation_obstacle_3d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef NAVIGATION_OBSTACLE_3D_EDITOR_PLUGIN_H
#define NAVIGATION_OBSTACLE_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/physics/collision_polygon_3d.h"
#include "scene/gui/box_container.h"
diff --git a/editor/plugins/navigation_polygon_editor_plugin.h b/editor/plugins/navigation_polygon_editor_plugin.h
index bf2474bc55..4d6d245cc5 100644
--- a/editor/plugins/navigation_polygon_editor_plugin.h
+++ b/editor/plugins/navigation_polygon_editor_plugin.h
@@ -33,7 +33,7 @@
#include "editor/plugins/abstract_polygon_2d_editor.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class AcceptDialog;
class HBoxContainer;
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index 66fa932f7c..96210de403 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef NODE_3D_EDITOR_PLUGIN_H
#define NODE_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/node_3d_editor_gizmos.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/box_container.h"
diff --git a/editor/plugins/occluder_instance_3d_editor_plugin.h b/editor/plugins/occluder_instance_3d_editor_plugin.h
index 54ac95c9e1..7920ff59c9 100644
--- a/editor/plugins/occluder_instance_3d_editor_plugin.h
+++ b/editor/plugins/occluder_instance_3d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef OCCLUDER_INSTANCE_3D_EDITOR_PLUGIN_H
#define OCCLUDER_INSTANCE_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/occluder_instance_3d.h"
#include "scene/resources/material.h"
diff --git a/editor/plugins/packed_scene_editor_plugin.h b/editor/plugins/packed_scene_editor_plugin.h
index 308bcf1c05..0e18d98978 100644
--- a/editor/plugins/packed_scene_editor_plugin.h
+++ b/editor/plugins/packed_scene_editor_plugin.h
@@ -32,7 +32,7 @@
#define PACKED_SCENE_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
class PackedSceneEditor : public VBoxContainer {
diff --git a/editor/plugins/parallax_background_editor_plugin.h b/editor/plugins/parallax_background_editor_plugin.h
index ba394d04dc..07a562edac 100644
--- a/editor/plugins/parallax_background_editor_plugin.h
+++ b/editor/plugins/parallax_background_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef PARALLAX_BACKGROUND_EDITOR_PLUGIN_H
#define PARALLAX_BACKGROUND_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class HBoxContainer;
class MenuButton;
diff --git a/editor/plugins/particle_process_material_editor_plugin.h b/editor/plugins/particle_process_material_editor_plugin.h
index 0d9725397e..623d3d4ba3 100644
--- a/editor/plugins/particle_process_material_editor_plugin.h
+++ b/editor/plugins/particle_process_material_editor_plugin.h
@@ -31,8 +31,8 @@
#ifndef PARTICLE_PROCESS_MATERIAL_EDITOR_PLUGIN_H
#define PARTICLE_PROCESS_MATERIAL_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
#include "editor/editor_properties.h"
+#include "editor/plugins/editor_plugin.h"
class Button;
class EditorSpinSlider;
diff --git a/editor/plugins/path_2d_editor_plugin.h b/editor/plugins/path_2d_editor_plugin.h
index 8efd651494..7d1a64160b 100644
--- a/editor/plugins/path_2d_editor_plugin.h
+++ b/editor/plugins/path_2d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef PATH_2D_EDITOR_PLUGIN_H
#define PATH_2D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/2d/path_2d.h"
#include "scene/gui/box_container.h"
diff --git a/editor/plugins/path_3d_editor_plugin.h b/editor/plugins/path_3d_editor_plugin.h
index 6a933a419f..ee73df1617 100644
--- a/editor/plugins/path_3d_editor_plugin.h
+++ b/editor/plugins/path_3d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef PATH_3D_EDITOR_PLUGIN_H
#define PATH_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/node_3d_editor_gizmos.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/path_3d.h"
diff --git a/editor/plugins/physical_bone_3d_editor_plugin.h b/editor/plugins/physical_bone_3d_editor_plugin.h
index 5c49e641a5..fb6f30cc57 100644
--- a/editor/plugins/physical_bone_3d_editor_plugin.h
+++ b/editor/plugins/physical_bone_3d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef PHYSICAL_BONE_3D_EDITOR_PLUGIN_H
#define PHYSICAL_BONE_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
diff --git a/editor/plugin_config_dialog.cpp b/editor/plugins/plugin_config_dialog.cpp
index 184a9233a8..a535b18b9d 100644
--- a/editor/plugin_config_dialog.cpp
+++ b/editor/plugins/plugin_config_dialog.cpp
@@ -33,9 +33,10 @@
#include "core/io/config_file.h"
#include "core/io/dir_access.h"
#include "core/object/script_language.h"
+#include "editor/editor_file_system.h"
#include "editor/editor_node.h"
-#include "editor/editor_plugin.h"
#include "editor/gui/editor_validation_panel.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/project_settings_editor.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/grid_container.h"
@@ -58,30 +59,35 @@ void PluginConfigDialog::_on_confirmed() {
return;
}
}
-
- int lang_idx = script_option_edit->get_selected();
- ScriptLanguage *language = ScriptServer::get_language(lang_idx);
- if (language == nullptr) {
- return;
- }
- String ext = language->get_extension();
- String script_name = script_edit->get_text().is_empty() ? _get_subfolder() : script_edit->get_text();
- if (script_name.get_extension() != ext) {
- script_name += "." + ext;
- }
- String script_path = path.path_join(script_name);
-
+ // Create the plugin.cfg file.
Ref<ConfigFile> cf = memnew(ConfigFile);
cf->load(path.path_join("plugin.cfg"));
cf->set_value("plugin", "name", name_edit->get_text());
cf->set_value("plugin", "description", desc_edit->get_text());
cf->set_value("plugin", "author", author_edit->get_text());
cf->set_value("plugin", "version", version_edit->get_text());
- cf->set_value("plugin", "script", script_name);
-
+ // Language-specific settings.
+ int lang_index = script_option_edit->get_selected();
+ _create_script_for_plugin(path, cf, lang_index);
+ // Save and inform the editor.
cf->save(path.path_join("plugin.cfg"));
+ EditorNode::get_singleton()->get_project_settings()->update_plugins();
+ EditorFileSystem::get_singleton()->scan();
+ _clear_fields();
+}
- if (!_edit_mode) {
+void PluginConfigDialog::_create_script_for_plugin(const String &p_plugin_path, Ref<ConfigFile> p_config_file, int p_script_lang_index) {
+ ScriptLanguage *language = ScriptServer::get_language(p_script_lang_index);
+ ERR_FAIL_COND(language == nullptr);
+ String ext = language->get_extension();
+ String script_name = script_edit->get_text().is_empty() ? _get_subfolder() : script_edit->get_text();
+ if (script_name.get_extension() != ext) {
+ script_name += "." + ext;
+ }
+ String script_path = p_plugin_path.path_join(script_name);
+ p_config_file->set_value("plugin", "script", script_name);
+ // If the requested script does not exist, create it.
+ if (!_edit_mode && !FileAccess::exists(script_path)) {
String class_name = script_name.get_basename();
String template_content = "";
Vector<ScriptLanguage::ScriptTemplate> templates = language->get_built_in_templates("EditorPlugin");
@@ -91,12 +97,9 @@ void PluginConfigDialog::_on_confirmed() {
Ref<Script> scr = language->make_template(template_content, class_name, "EditorPlugin");
scr->set_path(script_path, true);
ResourceSaver::save(scr);
-
+ p_config_file->save(p_plugin_path.path_join("plugin.cfg"));
emit_signal(SNAME("plugin_ready"), scr.ptr(), active_edit->is_pressed() ? _to_absolute_plugin_path(_get_subfolder()) : "");
- } else {
- EditorNode::get_singleton()->get_project_settings()->update_plugins();
}
- _clear_fields();
}
void PluginConfigDialog::_on_canceled() {
@@ -104,19 +107,9 @@ void PluginConfigDialog::_on_canceled() {
}
void PluginConfigDialog::_on_required_text_changed() {
- int lang_idx = script_option_edit->get_selected();
- ScriptLanguage *language = ScriptServer::get_language(lang_idx);
- if (language == nullptr) {
- return;
- }
- String ext = language->get_extension();
-
if (name_edit->get_text().is_empty()) {
validation_panel->set_message(MSG_ID_PLUGIN, TTR("Plugin name cannot be blank."), EditorValidationPanel::MSG_ERROR);
}
- if ((!script_edit->get_text().get_extension().is_empty() && script_edit->get_text().get_extension() != ext) || script_edit->get_text().ends_with(".")) {
- validation_panel->set_message(MSG_ID_SCRIPT, vformat(TTR("Script extension must match chosen language extension (.%s)."), ext), EditorValidationPanel::MSG_ERROR);
- }
if (subfolder_edit->is_visible()) {
if (!subfolder_edit->get_text().is_empty() && !subfolder_edit->get_text().is_valid_filename()) {
validation_panel->set_message(MSG_ID_SUBFOLDER, TTR("Subfolder name is not a valid folder name."), EditorValidationPanel::MSG_ERROR);
@@ -129,6 +122,16 @@ void PluginConfigDialog::_on_required_text_changed() {
} else {
validation_panel->set_message(MSG_ID_SUBFOLDER, "", EditorValidationPanel::MSG_OK);
}
+ // Language and script validation.
+ int lang_idx = script_option_edit->get_selected();
+ ScriptLanguage *language = ScriptServer::get_language(lang_idx);
+ if (language == nullptr) {
+ return;
+ }
+ String ext = language->get_extension();
+ if ((!script_edit->get_text().get_extension().is_empty() && script_edit->get_text().get_extension() != ext) || script_edit->get_text().ends_with(".")) {
+ validation_panel->set_message(MSG_ID_SCRIPT, vformat(TTR("Script extension must match chosen language extension (.%s)."), ext), EditorValidationPanel::MSG_ERROR);
+ }
if (active_edit->is_visible()) {
if (language->get_name() == "C#") {
active_edit->set_pressed(false);
@@ -296,10 +299,10 @@ PluginConfigDialog::PluginConfigDialog() {
grid->add_child(script_option_edit);
// Plugin Script Name
- Label *script_lb = memnew(Label);
- script_lb->set_text(TTR("Script Name:"));
- script_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
- grid->add_child(script_lb);
+ Label *script_name_label = memnew(Label);
+ script_name_label->set_text(TTR("Script Name:"));
+ script_name_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
+ grid->add_child(script_name_label);
script_edit = memnew(LineEdit);
script_edit->set_tooltip_text(TTR("Optional. The path to the script (relative to the add-on folder). If left empty, will default to \"plugin.gd\"."));
@@ -308,11 +311,11 @@ PluginConfigDialog::PluginConfigDialog() {
grid->add_child(script_edit);
// Activate now checkbox
- Label *active_lb = memnew(Label);
- active_lb->set_text(TTR("Activate now?"));
- active_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
- grid->add_child(active_lb);
- plugin_edit_hidden_controls.push_back(active_lb);
+ Label *active_label = memnew(Label);
+ active_label->set_text(TTR("Activate now?"));
+ active_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
+ grid->add_child(active_label);
+ plugin_edit_hidden_controls.push_back(active_label);
active_edit = memnew(CheckBox);
active_edit->set_pressed(true);
diff --git a/editor/plugin_config_dialog.h b/editor/plugins/plugin_config_dialog.h
index 2fdf6368a0..7d6eab5e18 100644
--- a/editor/plugin_config_dialog.h
+++ b/editor/plugins/plugin_config_dialog.h
@@ -39,6 +39,7 @@
#include "scene/gui/text_edit.h"
#include "scene/gui/texture_rect.h"
+class ConfigFile;
class EditorValidationPanel;
class PluginConfigDialog : public ConfirmationDialog {
@@ -70,6 +71,7 @@ class PluginConfigDialog : public ConfirmationDialog {
void _on_confirmed();
void _on_canceled();
void _on_required_text_changed();
+ void _create_script_for_plugin(const String &p_plugin_path, Ref<ConfigFile> p_config_file, int p_script_lang_index);
String _get_subfolder();
static String _to_absolute_plugin_path(const String &p_plugin_name);
diff --git a/editor/plugins/polygon_3d_editor_plugin.h b/editor/plugins/polygon_3d_editor_plugin.h
index 85cfd807e4..f496638515 100644
--- a/editor/plugins/polygon_3d_editor_plugin.h
+++ b/editor/plugins/polygon_3d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef POLYGON_3D_EDITOR_PLUGIN_H
#define POLYGON_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/physics/collision_polygon_3d.h"
#include "scene/gui/box_container.h"
diff --git a/editor/plugins/resource_preloader_editor_plugin.h b/editor/plugins/resource_preloader_editor_plugin.h
index 7a4cabbb69..76ef2fe9a4 100644
--- a/editor/plugins/resource_preloader_editor_plugin.h
+++ b/editor/plugins/resource_preloader_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef RESOURCE_PRELOADER_EDITOR_PLUGIN_H
#define RESOURCE_PRELOADER_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/tree.h"
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index de3ab3fd5a..e6bb8f14a9 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -32,7 +32,7 @@
#define SCRIPT_EDITOR_PLUGIN_H
#include "core/object/script_language.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/panel_container.h"
#include "scene/resources/syntax_highlighter.h"
diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h
index 2558184982..386261d844 100644
--- a/editor/plugins/shader_editor_plugin.h
+++ b/editor/plugins/shader_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef SHADER_EDITOR_PLUGIN_H
#define SHADER_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class HSplitContainer;
class ItemList;
diff --git a/editor/plugins/shader_file_editor_plugin.h b/editor/plugins/shader_file_editor_plugin.h
index 989668e37d..9a915513ef 100644
--- a/editor/plugins/shader_file_editor_plugin.h
+++ b/editor/plugins/shader_file_editor_plugin.h
@@ -32,7 +32,7 @@
#define SHADER_FILE_EDITOR_PLUGIN_H
#include "editor/code_editor.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/rich_text_label.h"
diff --git a/editor/plugins/skeleton_2d_editor_plugin.h b/editor/plugins/skeleton_2d_editor_plugin.h
index 9f3f1c3b34..74fd59f1c4 100644
--- a/editor/plugins/skeleton_2d_editor_plugin.h
+++ b/editor/plugins/skeleton_2d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef SKELETON_2D_EDITOR_PLUGIN_H
#define SKELETON_2D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/2d/skeleton_2d.h"
class AcceptDialog;
diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h
index f62d017c40..79dc16ae2f 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.h
+++ b/editor/plugins/skeleton_3d_editor_plugin.h
@@ -31,9 +31,9 @@
#ifndef SKELETON_3D_EDITOR_PLUGIN_H
#define SKELETON_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
#include "editor/editor_properties.h"
#include "editor/gui/editor_file_dialog.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/node_3d_editor_plugin.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/mesh_instance_3d.h"
diff --git a/editor/plugins/skeleton_ik_3d_editor_plugin.h b/editor/plugins/skeleton_ik_3d_editor_plugin.h
index 3d311e581e..2ef5467263 100644
--- a/editor/plugins/skeleton_ik_3d_editor_plugin.h
+++ b/editor/plugins/skeleton_ik_3d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef SKELETON_IK_3D_EDITOR_PLUGIN_H
#define SKELETON_IK_3D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class Button;
class SkeletonIK3D;
diff --git a/editor/plugins/sprite_2d_editor_plugin.h b/editor/plugins/sprite_2d_editor_plugin.h
index 1121481341..7cbde77081 100644
--- a/editor/plugins/sprite_2d_editor_plugin.h
+++ b/editor/plugins/sprite_2d_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef SPRITE_2D_EDITOR_PLUGIN_H
#define SPRITE_2D_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/2d/sprite_2d.h"
#include "scene/gui/spin_box.h"
diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h
index e9fbaf7dde..0e26a793a7 100644
--- a/editor/plugins/sprite_frames_editor_plugin.h
+++ b/editor/plugins/sprite_frames_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef SPRITE_FRAMES_EDITOR_PLUGIN_H
#define SPRITE_FRAMES_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/2d/animated_sprite_2d.h"
#include "scene/3d/sprite_3d.h"
#include "scene/gui/button.h"
diff --git a/editor/plugins/style_box_editor_plugin.h b/editor/plugins/style_box_editor_plugin.h
index 824f8db8e4..e793c2c2f3 100644
--- a/editor/plugins/style_box_editor_plugin.h
+++ b/editor/plugins/style_box_editor_plugin.h
@@ -32,7 +32,7 @@
#define STYLE_BOX_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/texture_rect.h"
class Button;
diff --git a/editor/plugins/sub_viewport_preview_editor_plugin.h b/editor/plugins/sub_viewport_preview_editor_plugin.h
index 999d7d8b43..d05e90b61e 100644
--- a/editor/plugins/sub_viewport_preview_editor_plugin.h
+++ b/editor/plugins/sub_viewport_preview_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef SUB_VIEWPORT_PREVIEW_EDITOR_PLUGIN_H
#define SUB_VIEWPORT_PREVIEW_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/texture_editor_plugin.h"
#include "scene/main/viewport.h"
diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp
index 6e786c1d94..40bd51a442 100644
--- a/editor/plugins/text_shader_editor.cpp
+++ b/editor/plugins/text_shader_editor.cpp
@@ -311,6 +311,9 @@ void ShaderTextEditor::_load_theme_settings() {
syntax_highlighter->add_color_region("/*", "*/", comment_color, false);
syntax_highlighter->add_color_region("//", "", comment_color, true);
+ const Color doc_comment_color = EDITOR_GET("text_editor/theme/highlighting/doc_comment_color");
+ syntax_highlighter->add_color_region("/**", "*/", doc_comment_color, false);
+
// Disabled preprocessor branches use translucent text color to be easier to distinguish from comments.
syntax_highlighter->set_disabled_branch_color(Color(EDITOR_GET("text_editor/theme/highlighting/text_color")) * Color(1, 1, 1, 0.5));
diff --git a/editor/plugins/texture_3d_editor_plugin.h b/editor/plugins/texture_3d_editor_plugin.h
index 2509cf86ba..7a33a97a8f 100644
--- a/editor/plugins/texture_3d_editor_plugin.h
+++ b/editor/plugins/texture_3d_editor_plugin.h
@@ -32,7 +32,7 @@
#define TEXTURE_3D_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/spin_box.h"
#include "scene/resources/shader.h"
#include "scene/resources/texture.h"
diff --git a/editor/plugins/texture_editor_plugin.h b/editor/plugins/texture_editor_plugin.h
index f045e7b1b0..ea31429238 100644
--- a/editor/plugins/texture_editor_plugin.h
+++ b/editor/plugins/texture_editor_plugin.h
@@ -32,7 +32,7 @@
#define TEXTURE_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/margin_container.h"
#include "scene/resources/texture.h"
diff --git a/editor/plugins/texture_layered_editor_plugin.h b/editor/plugins/texture_layered_editor_plugin.h
index ea807c0080..83729f922e 100644
--- a/editor/plugins/texture_layered_editor_plugin.h
+++ b/editor/plugins/texture_layered_editor_plugin.h
@@ -32,7 +32,7 @@
#define TEXTURE_LAYERED_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/spin_box.h"
#include "scene/resources/shader.h"
#include "scene/resources/texture.h"
diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h
index 59a1b56c19..0e71ec16e0 100644
--- a/editor/plugins/texture_region_editor_plugin.h
+++ b/editor/plugins/texture_region_editor_plugin.h
@@ -32,7 +32,7 @@
#define TEXTURE_REGION_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/dialogs.h"
class AtlasTexture;
diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h
index 0bc02789aa..ba8e3a30b7 100644
--- a/editor/plugins/theme_editor_plugin.h
+++ b/editor/plugins/theme_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef THEME_EDITOR_PLUGIN_H
#define THEME_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/theme_editor_preview.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/margin_container.h"
diff --git a/editor/plugins/tiles/tiles_editor_plugin.h b/editor/plugins/tiles/tiles_editor_plugin.h
index 23a6a52a5c..f1da9e31fa 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.h
+++ b/editor/plugins/tiles/tiles_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef TILES_EDITOR_PLUGIN_H
#define TILES_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
#include "tile_atlas_view.h"
diff --git a/editor/plugins/version_control_editor_plugin.h b/editor/plugins/version_control_editor_plugin.h
index 8ecb7c5029..4e60cb0a84 100644
--- a/editor/plugins/version_control_editor_plugin.h
+++ b/editor/plugins/version_control_editor_plugin.h
@@ -31,8 +31,8 @@
#ifndef VERSION_CONTROL_EDITOR_PLUGIN_H
#define VERSION_CONTROL_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
#include "editor/editor_vcs_interface.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/check_button.h"
#include "scene/gui/container.h"
#include "scene/gui/file_dialog.h"
diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h
index e499bbde1e..246e44a40d 100644
--- a/editor/plugins/visual_shader_editor_plugin.h
+++ b/editor/plugins/visual_shader_editor_plugin.h
@@ -31,8 +31,8 @@
#ifndef VISUAL_SHADER_EDITOR_PLUGIN_H
#define VISUAL_SHADER_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
#include "editor/editor_properties.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/editor_resource_conversion_plugin.h"
#include "scene/gui/graph_edit.h"
#include "scene/resources/syntax_highlighter.h"
diff --git a/editor/plugins/voxel_gi_editor_plugin.h b/editor/plugins/voxel_gi_editor_plugin.h
index ad68ff5d91..58ef22ddc6 100644
--- a/editor/plugins/voxel_gi_editor_plugin.h
+++ b/editor/plugins/voxel_gi_editor_plugin.h
@@ -31,7 +31,7 @@
#ifndef VOXEL_GI_EDITOR_PLUGIN_H
#define VOXEL_GI_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/3d/voxel_gi.h"
#include "scene/resources/material.h"
diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h
index 55f4677fb7..fb294c6bbd 100644
--- a/editor/project_settings_editor.h
+++ b/editor/project_settings_editor.h
@@ -35,11 +35,11 @@
#include "editor/action_map_editor.h"
#include "editor/editor_autoload_settings.h"
#include "editor/editor_data.h"
-#include "editor/editor_plugin_settings.h"
#include "editor/editor_sectioned_inspector.h"
#include "editor/group_settings_editor.h"
#include "editor/import_defaults_editor.h"
#include "editor/localization_editor.h"
+#include "editor/plugins/editor_plugin_settings.h"
#include "editor/shader_globals_editor.h"
#include "scene/gui/tab_container.h"
diff --git a/editor/shader_globals_editor.h b/editor/shader_globals_editor.h
index de871f76bf..fd94e483ff 100644
--- a/editor/shader_globals_editor.h
+++ b/editor/shader_globals_editor.h
@@ -33,8 +33,8 @@
#include "editor/editor_autoload_settings.h"
#include "editor/editor_data.h"
-#include "editor/editor_plugin_settings.h"
#include "editor/editor_sectioned_inspector.h"
+#include "editor/plugins/editor_plugin_settings.h"
#include "scene/gui/tab_container.h"
class ShaderGlobalsEditorInterface;
diff --git a/gles3_builders.py b/gles3_builders.py
index cf7c74f32d..78882f97fd 100644
--- a/gles3_builders.py
+++ b/gles3_builders.py
@@ -1,7 +1,7 @@
"""Functions used to generate source files during build time"""
import os.path
-
+from methods import print_error
from typing import Optional
@@ -94,11 +94,11 @@ def include_file_in_gles3_header(filename: str, header_data: GLES3HeaderStruct,
if not included_file in header_data.vertex_included_files and header_data.reading == "vertex":
header_data.vertex_included_files += [included_file]
if include_file_in_gles3_header(included_file, header_data, depth + 1) is None:
- print("Error in file '" + filename + "': #include " + includeline + "could not be found!")
+ print_error(f'In file "{filename}": #include "{includeline}" could not be found!"')
elif not included_file in header_data.fragment_included_files and header_data.reading == "fragment":
header_data.fragment_included_files += [included_file]
if include_file_in_gles3_header(included_file, header_data, depth + 1) is None:
- print("Error in file '" + filename + "': #include " + includeline + "could not be found!")
+ print_error(f'In file "{filename}": #include "{includeline}" could not be found!"')
line = fs.readline()
diff --git a/glsl_builders.py b/glsl_builders.py
index 5a17e3ca7f..22f4de74b1 100644
--- a/glsl_builders.py
+++ b/glsl_builders.py
@@ -1,6 +1,7 @@
"""Functions used to generate source files during build time"""
import os.path
+from methods import print_error
from typing import Optional, Iterable
@@ -79,15 +80,15 @@ def include_file_in_rd_header(filename: str, header_data: RDHeaderStruct, depth:
if not included_file in header_data.vertex_included_files and header_data.reading == "vertex":
header_data.vertex_included_files += [included_file]
if include_file_in_rd_header(included_file, header_data, depth + 1) is None:
- print("Error in file '" + filename + "': #include " + includeline + "could not be found!")
+ print_error(f'In file "{filename}": #include "{includeline}" could not be found!"')
elif not included_file in header_data.fragment_included_files and header_data.reading == "fragment":
header_data.fragment_included_files += [included_file]
if include_file_in_rd_header(included_file, header_data, depth + 1) is None:
- print("Error in file '" + filename + "': #include " + includeline + "could not be found!")
+ print_error(f'In file "{filename}": #include "{includeline}" could not be found!"')
elif not included_file in header_data.compute_included_files and header_data.reading == "compute":
header_data.compute_included_files += [included_file]
if include_file_in_rd_header(included_file, header_data, depth + 1) is None:
- print("Error in file '" + filename + "': #include " + includeline + "could not be found!")
+ print_error(f'In file "{filename}": #include "{includeline}" could not be found!"')
line = fs.readline()
diff --git a/methods.py b/methods.py
index 01b127ea30..30c7cb0331 100644
--- a/methods.py
+++ b/methods.py
@@ -5,6 +5,7 @@ import glob
import subprocess
from collections import OrderedDict
from collections.abc import Mapping
+from enum import Enum
from typing import Iterator
from pathlib import Path
from os.path import normpath, basename
@@ -15,6 +16,10 @@ base_folder_only = os.path.basename(os.path.normpath(base_folder_path))
# Listing all the folders we have converted
# for SCU in scu_builders.py
_scu_folders = set()
+# Colors are disabled in non-TTY environments such as pipes. This means
+# that if output is redirected to a file, it won't contain color codes.
+# Colors are always enabled on continuous integration.
+_colorize = bool(sys.stdout.isatty() or os.environ.get("CI"))
def set_scu_folders(scu_folders):
@@ -22,13 +27,55 @@ def set_scu_folders(scu_folders):
_scu_folders = scu_folders
+class ANSI(Enum):
+ """
+ Enum class for adding ansi colorcodes directly into strings.
+ Automatically converts values to strings representing their
+ internal value, or an empty string in a non-colorized scope.
+ """
+
+ GRAY = "\x1b[0;30m"
+ RED = "\x1b[0;31m"
+ GREEN = "\x1b[0;32m"
+ YELLOW = "\x1b[0;33m"
+ BLUE = "\x1b[0;34m"
+ PURPLE = "\x1b[0;35m"
+ CYAN = "\x1b[0;36m"
+ WHITE = "\x1b[0;37m"
+
+ BOLD_GRAY = "\x1b[1;90m"
+ BOLD_RED = "\x1b[1;91m"
+ BOLD_GREEN = "\x1b[1;92m"
+ BOLD_YELLOW = "\x1b[1;93m"
+ BOLD_BLUE = "\x1b[1;94m"
+ BOLD_PURPLE = "\x1b[1;95m"
+ BOLD_CYAN = "\x1b[1;96m"
+ BOLD_WHITE = "\x1b[1;97m"
+
+ RESET = "\x1b[0m"
+
+ def __str__(self):
+ global _colorize
+ return self.value if _colorize else ""
+
+
+def print_warning(*values: object) -> None:
+ """Prints a warning message with formatting."""
+ print(f"{ANSI.BOLD_YELLOW}WARNING:{ANSI.YELLOW}", *values, ANSI.RESET, file=sys.stderr)
+
+
+def print_error(*values: object) -> None:
+ """Prints an error message with formatting."""
+ print(f"{ANSI.BOLD_RED}ERROR:{ANSI.RED}", *values, ANSI.RESET, file=sys.stderr)
+
+
def add_source_files_orig(self, sources, files, allow_gen=False):
# Convert string to list of absolute paths (including expanding wildcard)
if isinstance(files, (str, bytes)):
# Keep SCons project-absolute path as they are (no wildcard support)
if files.startswith("#"):
if "*" in files:
- print("ERROR: Wildcards can't be expanded in SCons project-absolute path: '{}'".format(files))
+ print_error("Wildcards can't be expanded in SCons project-absolute path: '{}'".format(files))
return
files = [files]
else:
@@ -44,7 +91,7 @@ def add_source_files_orig(self, sources, files, allow_gen=False):
for path in files:
obj = self.Object(path)
if obj in sources:
- print('WARNING: Object "{}" already included in environment sources.'.format(obj))
+ print_warning('Object "{}" already included in environment sources.'.format(obj))
continue
sources.append(obj)
@@ -517,7 +564,7 @@ def module_check_dependencies(self, module):
missing_deps.append(dep)
if missing_deps != []:
- print(
+ print_warning(
"Disabling '{}' module as the following dependencies are not satisfied: {}".format(
module, ", ".join(missing_deps)
)
@@ -576,9 +623,7 @@ def use_windows_spawn_fix(self, platform=None):
_, err = proc.communicate()
rv = proc.wait()
if rv:
- print("=====")
- print(err)
- print("=====")
+ print_error(err)
return rv
def mySpawn(sh, escape, cmd, args, env):
@@ -601,65 +646,34 @@ def use_windows_spawn_fix(self, platform=None):
self["SPAWN"] = mySpawn
-def no_verbose(sys, env):
- colors = {}
-
- # Colors are disabled in non-TTY environments such as pipes. This means
- # that if output is redirected to a file, it will not contain color codes
- if sys.stdout.isatty():
- colors["blue"] = "\033[0;94m"
- colors["bold_blue"] = "\033[1;94m"
- colors["reset"] = "\033[0m"
- else:
- colors["blue"] = ""
- colors["bold_blue"] = ""
- colors["reset"] = ""
+def no_verbose(env):
+ colors = [ANSI.BLUE, ANSI.BOLD_BLUE, ANSI.RESET]
# There is a space before "..." to ensure that source file names can be
# Ctrl + clicked in the VS Code terminal.
- compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- java_compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- compile_shared_source_message = "{}Compiling shared {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_program_message = "{}Linking Program {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_library_message = "{}Linking Static Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- ranlib_library_message = "{}Ranlib Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_shared_library_message = "{}Linking Shared Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- java_library_message = "{}Creating Java Archive {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- compiled_resource_message = "{}Creating Compiled Resource {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- generated_file_message = "{}Generating {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
-
- env.Append(CXXCOMSTR=[compile_source_message])
- env.Append(CCCOMSTR=[compile_source_message])
- env.Append(SHCCCOMSTR=[compile_shared_source_message])
- env.Append(SHCXXCOMSTR=[compile_shared_source_message])
- env.Append(ARCOMSTR=[link_library_message])
- env.Append(RANLIBCOMSTR=[ranlib_library_message])
- env.Append(SHLINKCOMSTR=[link_shared_library_message])
- env.Append(LINKCOMSTR=[link_program_message])
- env.Append(JARCOMSTR=[java_library_message])
- env.Append(JAVACCOMSTR=[java_compile_source_message])
- env.Append(RCCOMSTR=[compiled_resource_message])
- env.Append(GENCOMSTR=[generated_file_message])
+ compile_source_message = "{0}Compiling {1}$SOURCE{0} ...{2}".format(*colors)
+ java_compile_source_message = "{0}Compiling {1}$SOURCE{0} ...{2}".format(*colors)
+ compile_shared_source_message = "{0}Compiling shared {1}$SOURCE{0} ...{2}".format(*colors)
+ link_program_message = "{0}Linking Program {1}$TARGET{0} ...{2}".format(*colors)
+ link_library_message = "{0}Linking Static Library {1}$TARGET{0} ...{2}".format(*colors)
+ ranlib_library_message = "{0}Ranlib Library {1}$TARGET{0} ...{2}".format(*colors)
+ link_shared_library_message = "{0}Linking Shared Library {1}$TARGET{0} ...{2}".format(*colors)
+ java_library_message = "{0}Creating Java Archive {1}$TARGET{0} ...{2}".format(*colors)
+ compiled_resource_message = "{0}Creating Compiled Resource {1}$TARGET{0} ...{2}".format(*colors)
+ generated_file_message = "{0}Generating {1}$TARGET{0} ...{2}".format(*colors)
+
+ env.Append(CXXCOMSTR=compile_source_message)
+ env.Append(CCCOMSTR=compile_source_message)
+ env.Append(SHCCCOMSTR=compile_shared_source_message)
+ env.Append(SHCXXCOMSTR=compile_shared_source_message)
+ env.Append(ARCOMSTR=link_library_message)
+ env.Append(RANLIBCOMSTR=ranlib_library_message)
+ env.Append(SHLINKCOMSTR=link_shared_library_message)
+ env.Append(LINKCOMSTR=link_program_message)
+ env.Append(JARCOMSTR=java_library_message)
+ env.Append(JAVACCOMSTR=java_compile_source_message)
+ env.Append(RCCOMSTR=compiled_resource_message)
+ env.Append(GENCOMSTR=generated_file_message)
def detect_visual_c_compiler_version(tools_env):
@@ -790,7 +804,7 @@ def generate_cpp_hint_file(filename):
with open(filename, "w", encoding="utf-8", newline="\n") as fd:
fd.write("#define GDCLASS(m_class, m_inherits)\n")
except OSError:
- print("Could not write cpp.hint file.")
+ print_warning("Could not write cpp.hint file.")
def glob_recursive(pattern, node="."):
@@ -881,7 +895,7 @@ def detect_darwin_sdk_path(platform, env):
if sdk_path:
env[var_name] = sdk_path
except (subprocess.CalledProcessError, OSError):
- print("Failed to find SDK path while running xcrun --sdk {} --show-sdk-path.".format(sdk_name))
+ print_error("Failed to find SDK path while running xcrun --sdk {} --show-sdk-path.".format(sdk_name))
raise
@@ -891,7 +905,7 @@ def is_vanilla_clang(env):
try:
version = subprocess.check_output([env.subst(env["CXX"]), "--version"]).strip().decode("utf-8")
except (subprocess.CalledProcessError, OSError):
- print("Couldn't parse CXX environment variable to infer compiler version.")
+ print_warning("Couldn't parse CXX environment variable to infer compiler version.")
return False
return not version.startswith("Apple")
@@ -928,7 +942,7 @@ def get_compiler_version(env):
.decode("utf-8")
)
except (subprocess.CalledProcessError, OSError):
- print("Couldn't parse CXX environment variable to infer compiler version.")
+ print_warning("Couldn't parse CXX environment variable to infer compiler version.")
return ret
else:
# TODO: Implement for MSVC
diff --git a/misc/dist/ios_xcode/PrivacyInfo.xcprivacy b/misc/dist/ios_xcode/PrivacyInfo.xcprivacy
new file mode 100644
index 0000000000..bc4a893d58
--- /dev/null
+++ b/misc/dist/ios_xcode/PrivacyInfo.xcprivacy
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>NSPrivacyAccessedAPITypes</key>
+ $priv_api_types
+ $priv_tracking
+ $priv_collection
+</dict>
+</plist>
diff --git a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
index e9efea8809..a5de7e8872 100644
--- a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
+++ b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
@@ -16,6 +16,7 @@
9039D3BE24C093AC0020482C /* MoltenVK.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9039D3BD24C093AC0020482C /* MoltenVK.xcframework */; };
D0BCFE4618AEBDA2004A7AAE /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D0BCFE4418AEBDA2004A7AAE /* InfoPlist.strings */; };
D0BCFE7818AEBFEB004A7AAE /* $binary.pck in Resources */ = {isa = PBXBuildFile; fileRef = D0BCFE7718AEBFEB004A7AAE /* $binary.pck */; };
+ F965960D2BC2C3A800579C7E /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = F965960C2BC2C3A800579C7E /* PrivacyInfo.xcprivacy */; };
$pbx_launch_screen_build_reference
/* End PBXBuildFile section */
@@ -47,6 +48,7 @@
D0BCFE4518AEBDA2004A7AAE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
$pbx_locale_file_reference
D0BCFE7718AEBFEB004A7AAE /* $binary.pck */ = {isa = PBXFileReference; lastKnownFileType = file; path = "$binary.pck"; sourceTree = "<group>"; };
+ F965960C2BC2C3A800579C7E /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
$pbx_launch_screen_file_reference
/* End PBXFileReference section */
@@ -75,6 +77,7 @@
D0BCFE4118AEBDA2004A7AAE /* $binary */,
D0BCFE3618AEBDA2004A7AAE /* Frameworks */,
D0BCFE3518AEBDA2004A7AAE /* Products */,
+ F965960C2BC2C3A800579C7E /* PrivacyInfo.xcprivacy */,
$additional_pbx_resources_refs
);
sourceTree = "<group>";
@@ -186,6 +189,7 @@
D0BCFE7818AEBFEB004A7AAE /* $binary.pck in Resources */,
$pbx_launch_screen_build_phase
D0BCFE4618AEBDA2004A7AAE /* InfoPlist.strings in Resources */,
+ F965960D2BC2C3A800579C7E /* PrivacyInfo.xcprivacy in Resources */,
$additional_pbx_resources_build
);
runOnlyForDeploymentPostprocessing = 0;
diff --git a/modules/csg/editor/csg_gizmos.h b/modules/csg/editor/csg_gizmos.h
index 6281db0a21..de19b33e7d 100644
--- a/modules/csg/editor/csg_gizmos.h
+++ b/modules/csg/editor/csg_gizmos.h
@@ -35,7 +35,7 @@
#include "../csg_shape.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/node_3d_editor_gizmos.h"
class Gizmo3DHelper;
diff --git a/modules/gdscript/language_server/gdscript_language_server.h b/modules/gdscript/language_server/gdscript_language_server.h
index 2ace5ca446..4ae5ab6cbf 100644
--- a/modules/gdscript/language_server/gdscript_language_server.h
+++ b/modules/gdscript/language_server/gdscript_language_server.h
@@ -34,7 +34,7 @@
#include "../gdscript_parser.h"
#include "gdscript_language_protocol.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class GDScriptLanguageServer : public EditorPlugin {
GDCLASS(GDScriptLanguageServer, EditorPlugin);
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index 853a8e0f19..a63d32ef30 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -233,18 +233,25 @@ void GDScriptWorkspace::reload_all_workspace_scripts() {
void GDScriptWorkspace::list_script_files(const String &p_root_dir, List<String> &r_files) {
Error err;
Ref<DirAccess> dir = DirAccess::open(p_root_dir, &err);
- if (OK == err) {
- dir->list_dir_begin();
- String file_name = dir->get_next();
- while (file_name.length()) {
- if (dir->current_is_dir() && file_name != "." && file_name != ".." && file_name != "./") {
- list_script_files(p_root_dir.path_join(file_name), r_files);
- } else if (file_name.ends_with(".gd")) {
- String script_file = p_root_dir.path_join(file_name);
- r_files.push_back(script_file);
- }
- file_name = dir->get_next();
+ if (OK != err) {
+ return;
+ }
+
+ // Ignore scripts in directories with a .gdignore file.
+ if (dir->file_exists(".gdignore")) {
+ return;
+ }
+
+ dir->list_dir_begin();
+ String file_name = dir->get_next();
+ while (file_name.length()) {
+ if (dir->current_is_dir() && file_name != "." && file_name != ".." && file_name != "./") {
+ list_script_files(p_root_dir.path_join(file_name), r_files);
+ } else if (file_name.ends_with(".gd")) {
+ String script_file = p_root_dir.path_join(file_name);
+ r_files.push_back(script_file);
}
+ file_name = dir->get_next();
}
}
diff --git a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.h b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.h
index 683ff6d4f6..6bc6160571 100644
--- a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.h
+++ b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.h
@@ -36,7 +36,7 @@
#include "../gltf_document.h"
#include "editor_scene_exporter_gltf_settings.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class EditorFileDialog;
class EditorInspector;
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.h b/modules/gridmap/editor/grid_map_editor_plugin.h
index 924e21aef5..cfa0f0c35c 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.h
+++ b/modules/gridmap/editor/grid_map_editor_plugin.h
@@ -35,7 +35,7 @@
#include "../grid_map.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
#include "scene/gui/item_list.h"
#include "scene/gui/slider.h"
diff --git a/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.h b/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.h
index 730d1ca83b..3c50b0d5cc 100644
--- a/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.h
+++ b/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.h
@@ -32,7 +32,7 @@
#define AUDIO_STREAM_INTERACTIVE_EDITOR_PLUGIN_H
#include "editor/editor_inspector.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/dialogs.h"
class CheckBox;
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index e3f39c50f4..17df3988ee 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -41,7 +41,7 @@
#include "core/templates/self_list.h"
#ifdef TOOLS_ENABLED
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#endif
class CSharpScript;
diff --git a/modules/multiplayer/editor/multiplayer_editor_plugin.h b/modules/multiplayer/editor/multiplayer_editor_plugin.h
index a22144cdcf..e8ade539a7 100644
--- a/modules/multiplayer/editor/multiplayer_editor_plugin.h
+++ b/modules/multiplayer/editor/multiplayer_editor_plugin.h
@@ -31,8 +31,8 @@
#ifndef MULTIPLAYER_EDITOR_PLUGIN_H
#define MULTIPLAYER_EDITOR_PLUGIN_H
-#include "editor/editor_plugin.h"
#include "editor/plugins/editor_debugger_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class EditorNetworkProfiler;
class MultiplayerEditorDebugger : public EditorDebuggerPlugin {
diff --git a/modules/multiplayer/editor/replication_editor.h b/modules/multiplayer/editor/replication_editor.h
index 80c1892ec3..8f11774292 100644
--- a/modules/multiplayer/editor/replication_editor.h
+++ b/modules/multiplayer/editor/replication_editor.h
@@ -33,7 +33,7 @@
#include "../scene_replication_config.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
class ConfirmationDialog;
diff --git a/modules/navigation/editor/navigation_mesh_editor_plugin.h b/modules/navigation/editor/navigation_mesh_editor_plugin.h
index b73d8d2e69..6114c62ebf 100644
--- a/modules/navigation/editor/navigation_mesh_editor_plugin.h
+++ b/modules/navigation/editor/navigation_mesh_editor_plugin.h
@@ -33,7 +33,7 @@
#ifdef TOOLS_ENABLED
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class AcceptDialog;
class Button;
diff --git a/modules/noise/editor/noise_editor_plugin.h b/modules/noise/editor/noise_editor_plugin.h
index 948ccba29b..aa94cf4d23 100644
--- a/modules/noise/editor/noise_editor_plugin.h
+++ b/modules/noise/editor/noise_editor_plugin.h
@@ -33,7 +33,7 @@
#ifdef TOOLS_ENABLED
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class NoiseEditorPlugin : public EditorPlugin {
GDCLASS(NoiseEditorPlugin, EditorPlugin)
diff --git a/modules/noise/register_types.cpp b/modules/noise/register_types.cpp
index 29eb42522f..363b7bdc31 100644
--- a/modules/noise/register_types.cpp
+++ b/modules/noise/register_types.cpp
@@ -40,7 +40,7 @@
#endif
#ifdef TOOLS_ENABLED
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
#endif
void initialize_noise_module(ModuleInitializationLevel p_level) {
diff --git a/modules/openxr/editor/openxr_action_map_editor.h b/modules/openxr/editor/openxr_action_map_editor.h
index 22e8853c8c..cfe5fed095 100644
--- a/modules/openxr/editor/openxr_action_map_editor.h
+++ b/modules/openxr/editor/openxr_action_map_editor.h
@@ -36,8 +36,8 @@
#include "openxr_interaction_profile_editor.h"
#include "openxr_select_interaction_profile_dialog.h"
-#include "editor/editor_plugin.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/label.h"
diff --git a/modules/openxr/editor/openxr_editor_plugin.h b/modules/openxr/editor/openxr_editor_plugin.h
index b80f20d049..672df0de28 100644
--- a/modules/openxr/editor/openxr_editor_plugin.h
+++ b/modules/openxr/editor/openxr_editor_plugin.h
@@ -34,7 +34,7 @@
#include "openxr_action_map_editor.h"
#include "openxr_select_runtime.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class OpenXREditorPlugin : public EditorPlugin {
GDCLASS(OpenXREditorPlugin, EditorPlugin);
diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct
index 1d9d36fbbf..fcf3f64315 100644
--- a/modules/text_server_adv/gdextension_build/SConstruct
+++ b/modules/text_server_adv/gdextension_build/SConstruct
@@ -1,10 +1,20 @@
#!/usr/bin/env python
import atexit
-import os
import sys
import methods
import time
+# Enable ANSI escape code support on Windows 10 and later (for colored console output).
+# <https://github.com/python/cpython/issues/73245>
+if sys.platform == "win32":
+ from ctypes import windll, c_int, byref
+
+ stdout_handle = windll.kernel32.GetStdHandle(c_int(-11))
+ mode = c_int(0)
+ windll.kernel32.GetConsoleMode(c_int(stdout_handle), byref(mode))
+ mode = c_int(mode.value | 4)
+ windll.kernel32.SetConsoleMode(c_int(stdout_handle), mode)
+
# For the reference:
# - CCFLAGS are compilation flags shared between C and C++
# - CFLAGS are for C-specific compilation flags
@@ -30,7 +40,7 @@ opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", Fa
opts.Update(env)
if not env["verbose"]:
- methods.no_verbose(sys, env)
+ methods.no_verbose(env)
if env["platform"] == "windows" and not env["use_mingw"]:
env.AppendUnique(CCFLAGS=["/utf-8"]) # Force to use Unicode encoding.
@@ -764,9 +774,16 @@ Default(library)
def print_elapsed_time():
- elapsed_time_sec = round(time.time() - time_at_start, 3)
- time_ms = round((elapsed_time_sec % 1) * 1000)
- print("[Time elapsed: {}.{:03}]".format(time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)), time_ms))
+ elapsed_time_sec = round(time.time() - time_at_start, 2)
+ time_centiseconds = round((elapsed_time_sec % 1) * 100)
+ print(
+ "{}[Time elapsed: {}.{:02}]{}".format(
+ methods.ANSI.GRAY,
+ time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)),
+ time_centiseconds,
+ methods.ANSI.RESET,
+ )
+ )
atexit.register(print_elapsed_time)
diff --git a/modules/text_server_adv/gdextension_build/methods.py b/modules/text_server_adv/gdextension_build/methods.py
index 32dbc59fd4..1c43759c55 100644
--- a/modules/text_server_adv/gdextension_build/methods.py
+++ b/modules/text_server_adv/gdextension_build/methods.py
@@ -1,66 +1,73 @@
import os
import sys
+from enum import Enum
+# Colors are disabled in non-TTY environments such as pipes. This means
+# that if output is redirected to a file, it won't contain color codes.
+# Colors are always enabled on continuous integration.
+_colorize = bool(sys.stdout.isatty() or os.environ.get("CI"))
-def no_verbose(sys, env):
- colors = {}
- # Colors are disabled in non-TTY environments such as pipes. This means
- # that if output is redirected to a file, it will not contain color codes
- if sys.stdout.isatty():
- colors["blue"] = "\033[0;94m"
- colors["bold_blue"] = "\033[1;94m"
- colors["reset"] = "\033[0m"
- else:
- colors["blue"] = ""
- colors["bold_blue"] = ""
- colors["reset"] = ""
+class ANSI(Enum):
+ """
+ Enum class for adding ansi colorcodes directly into strings.
+ Automatically converts values to strings representing their
+ internal value, or an empty string in a non-colorized scope.
+ """
+
+ GRAY = "\x1b[0;30m"
+ RED = "\x1b[0;31m"
+ GREEN = "\x1b[0;32m"
+ YELLOW = "\x1b[0;33m"
+ BLUE = "\x1b[0;34m"
+ PURPLE = "\x1b[0;35m"
+ CYAN = "\x1b[0;36m"
+ WHITE = "\x1b[0;37m"
+
+ BOLD_GRAY = "\x1b[1;90m"
+ BOLD_RED = "\x1b[1;91m"
+ BOLD_GREEN = "\x1b[1;92m"
+ BOLD_YELLOW = "\x1b[1;93m"
+ BOLD_BLUE = "\x1b[1;94m"
+ BOLD_PURPLE = "\x1b[1;95m"
+ BOLD_CYAN = "\x1b[1;96m"
+ BOLD_WHITE = "\x1b[1;97m"
+
+ RESET = "\x1b[0m"
+
+ def __str__(self):
+ global _colorize
+ return self.value if _colorize else ""
+
+
+def no_verbose(env):
+ colors = [ANSI.BLUE, ANSI.BOLD_BLUE, ANSI.RESET]
# There is a space before "..." to ensure that source file names can be
# Ctrl + clicked in the VS Code terminal.
- compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- java_compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- compile_shared_source_message = "{}Compiling shared {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_program_message = "{}Linking Program {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_library_message = "{}Linking Static Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- ranlib_library_message = "{}Ranlib Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_shared_library_message = "{}Linking Shared Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- java_library_message = "{}Creating Java Archive {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- compiled_resource_message = "{}Creating Compiled Resource {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- generated_file_message = "{}Generating {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
-
- env.Append(CXXCOMSTR=[compile_source_message])
- env.Append(CCCOMSTR=[compile_source_message])
- env.Append(SHCCCOMSTR=[compile_shared_source_message])
- env.Append(SHCXXCOMSTR=[compile_shared_source_message])
- env.Append(ARCOMSTR=[link_library_message])
- env.Append(RANLIBCOMSTR=[ranlib_library_message])
- env.Append(SHLINKCOMSTR=[link_shared_library_message])
- env.Append(LINKCOMSTR=[link_program_message])
- env.Append(JARCOMSTR=[java_library_message])
- env.Append(JAVACCOMSTR=[java_compile_source_message])
- env.Append(RCCOMSTR=[compiled_resource_message])
- env.Append(GENCOMSTR=[generated_file_message])
+ compile_source_message = "{0}Compiling {1}$SOURCE{0} ...{2}".format(*colors)
+ java_compile_source_message = "{0}Compiling {1}$SOURCE{0} ...{2}".format(*colors)
+ compile_shared_source_message = "{0}Compiling shared {1}$SOURCE{0} ...{2}".format(*colors)
+ link_program_message = "{0}Linking Program {1}$TARGET{0} ...{2}".format(*colors)
+ link_library_message = "{0}Linking Static Library {1}$TARGET{0} ...{2}".format(*colors)
+ ranlib_library_message = "{0}Ranlib Library {1}$TARGET{0} ...{2}".format(*colors)
+ link_shared_library_message = "{0}Linking Shared Library {1}$TARGET{0} ...{2}".format(*colors)
+ java_library_message = "{0}Creating Java Archive {1}$TARGET{0} ...{2}".format(*colors)
+ compiled_resource_message = "{0}Creating Compiled Resource {1}$TARGET{0} ...{2}".format(*colors)
+ generated_file_message = "{0}Generating {1}$TARGET{0} ...{2}".format(*colors)
+
+ env.Append(CXXCOMSTR=compile_source_message)
+ env.Append(CCCOMSTR=compile_source_message)
+ env.Append(SHCCCOMSTR=compile_shared_source_message)
+ env.Append(SHCXXCOMSTR=compile_shared_source_message)
+ env.Append(ARCOMSTR=link_library_message)
+ env.Append(RANLIBCOMSTR=ranlib_library_message)
+ env.Append(SHLINKCOMSTR=link_shared_library_message)
+ env.Append(LINKCOMSTR=link_program_message)
+ env.Append(JARCOMSTR=java_library_message)
+ env.Append(JAVACCOMSTR=java_compile_source_message)
+ env.Append(RCCOMSTR=compiled_resource_message)
+ env.Append(GENCOMSTR=generated_file_message)
def disable_warnings(self):
diff --git a/modules/text_server_fb/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct
index 29801ede8e..07940719eb 100644
--- a/modules/text_server_fb/gdextension_build/SConstruct
+++ b/modules/text_server_fb/gdextension_build/SConstruct
@@ -1,10 +1,20 @@
#!/usr/bin/env python
import atexit
-import os
import sys
import methods
import time
+# Enable ANSI escape code support on Windows 10 and later (for colored console output).
+# <https://github.com/python/cpython/issues/73245>
+if sys.platform == "win32":
+ from ctypes import windll, c_int, byref
+
+ stdout_handle = windll.kernel32.GetStdHandle(c_int(-11))
+ mode = c_int(0)
+ windll.kernel32.GetConsoleMode(c_int(stdout_handle), byref(mode))
+ mode = c_int(mode.value | 4)
+ windll.kernel32.SetConsoleMode(c_int(stdout_handle), mode)
+
# For the reference:
# - CCFLAGS are compilation flags shared between C and C++
# - CFLAGS are for C-specific compilation flags
@@ -28,7 +38,7 @@ opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", Fa
opts.Update(env)
if not env["verbose"]:
- methods.no_verbose(sys, env)
+ methods.no_verbose(env)
# ThorVG
if env["thorvg_enabled"] and env["freetype_enabled"]:
@@ -311,9 +321,16 @@ Default(library)
def print_elapsed_time():
- elapsed_time_sec = round(time.time() - time_at_start, 3)
- time_ms = round((elapsed_time_sec % 1) * 1000)
- print("[Time elapsed: {}.{:03}]".format(time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)), time_ms))
+ elapsed_time_sec = round(time.time() - time_at_start, 2)
+ time_centiseconds = round((elapsed_time_sec % 1) * 100)
+ print(
+ "{}[Time elapsed: {}.{:02}]{}".format(
+ methods.ANSI.GRAY,
+ time.strftime("%H:%M:%S", time.gmtime(elapsed_time_sec)),
+ time_centiseconds,
+ methods.ANSI.RESET,
+ )
+ )
atexit.register(print_elapsed_time)
diff --git a/modules/text_server_fb/gdextension_build/methods.py b/modules/text_server_fb/gdextension_build/methods.py
index 32dbc59fd4..1c43759c55 100644
--- a/modules/text_server_fb/gdextension_build/methods.py
+++ b/modules/text_server_fb/gdextension_build/methods.py
@@ -1,66 +1,73 @@
import os
import sys
+from enum import Enum
+# Colors are disabled in non-TTY environments such as pipes. This means
+# that if output is redirected to a file, it won't contain color codes.
+# Colors are always enabled on continuous integration.
+_colorize = bool(sys.stdout.isatty() or os.environ.get("CI"))
-def no_verbose(sys, env):
- colors = {}
- # Colors are disabled in non-TTY environments such as pipes. This means
- # that if output is redirected to a file, it will not contain color codes
- if sys.stdout.isatty():
- colors["blue"] = "\033[0;94m"
- colors["bold_blue"] = "\033[1;94m"
- colors["reset"] = "\033[0m"
- else:
- colors["blue"] = ""
- colors["bold_blue"] = ""
- colors["reset"] = ""
+class ANSI(Enum):
+ """
+ Enum class for adding ansi colorcodes directly into strings.
+ Automatically converts values to strings representing their
+ internal value, or an empty string in a non-colorized scope.
+ """
+
+ GRAY = "\x1b[0;30m"
+ RED = "\x1b[0;31m"
+ GREEN = "\x1b[0;32m"
+ YELLOW = "\x1b[0;33m"
+ BLUE = "\x1b[0;34m"
+ PURPLE = "\x1b[0;35m"
+ CYAN = "\x1b[0;36m"
+ WHITE = "\x1b[0;37m"
+
+ BOLD_GRAY = "\x1b[1;90m"
+ BOLD_RED = "\x1b[1;91m"
+ BOLD_GREEN = "\x1b[1;92m"
+ BOLD_YELLOW = "\x1b[1;93m"
+ BOLD_BLUE = "\x1b[1;94m"
+ BOLD_PURPLE = "\x1b[1;95m"
+ BOLD_CYAN = "\x1b[1;96m"
+ BOLD_WHITE = "\x1b[1;97m"
+
+ RESET = "\x1b[0m"
+
+ def __str__(self):
+ global _colorize
+ return self.value if _colorize else ""
+
+
+def no_verbose(env):
+ colors = [ANSI.BLUE, ANSI.BOLD_BLUE, ANSI.RESET]
# There is a space before "..." to ensure that source file names can be
# Ctrl + clicked in the VS Code terminal.
- compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- java_compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- compile_shared_source_message = "{}Compiling shared {}$SOURCE{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_program_message = "{}Linking Program {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_library_message = "{}Linking Static Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- ranlib_library_message = "{}Ranlib Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- link_shared_library_message = "{}Linking Shared Library {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- java_library_message = "{}Creating Java Archive {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- compiled_resource_message = "{}Creating Compiled Resource {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
- generated_file_message = "{}Generating {}$TARGET{} ...{}".format(
- colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
- )
-
- env.Append(CXXCOMSTR=[compile_source_message])
- env.Append(CCCOMSTR=[compile_source_message])
- env.Append(SHCCCOMSTR=[compile_shared_source_message])
- env.Append(SHCXXCOMSTR=[compile_shared_source_message])
- env.Append(ARCOMSTR=[link_library_message])
- env.Append(RANLIBCOMSTR=[ranlib_library_message])
- env.Append(SHLINKCOMSTR=[link_shared_library_message])
- env.Append(LINKCOMSTR=[link_program_message])
- env.Append(JARCOMSTR=[java_library_message])
- env.Append(JAVACCOMSTR=[java_compile_source_message])
- env.Append(RCCOMSTR=[compiled_resource_message])
- env.Append(GENCOMSTR=[generated_file_message])
+ compile_source_message = "{0}Compiling {1}$SOURCE{0} ...{2}".format(*colors)
+ java_compile_source_message = "{0}Compiling {1}$SOURCE{0} ...{2}".format(*colors)
+ compile_shared_source_message = "{0}Compiling shared {1}$SOURCE{0} ...{2}".format(*colors)
+ link_program_message = "{0}Linking Program {1}$TARGET{0} ...{2}".format(*colors)
+ link_library_message = "{0}Linking Static Library {1}$TARGET{0} ...{2}".format(*colors)
+ ranlib_library_message = "{0}Ranlib Library {1}$TARGET{0} ...{2}".format(*colors)
+ link_shared_library_message = "{0}Linking Shared Library {1}$TARGET{0} ...{2}".format(*colors)
+ java_library_message = "{0}Creating Java Archive {1}$TARGET{0} ...{2}".format(*colors)
+ compiled_resource_message = "{0}Creating Compiled Resource {1}$TARGET{0} ...{2}".format(*colors)
+ generated_file_message = "{0}Generating {1}$TARGET{0} ...{2}".format(*colors)
+
+ env.Append(CXXCOMSTR=compile_source_message)
+ env.Append(CCCOMSTR=compile_source_message)
+ env.Append(SHCCCOMSTR=compile_shared_source_message)
+ env.Append(SHCXXCOMSTR=compile_shared_source_message)
+ env.Append(ARCOMSTR=link_library_message)
+ env.Append(RANLIBCOMSTR=ranlib_library_message)
+ env.Append(SHLINKCOMSTR=link_shared_library_message)
+ env.Append(LINKCOMSTR=link_program_message)
+ env.Append(JARCOMSTR=java_library_message)
+ env.Append(JAVACCOMSTR=java_compile_source_message)
+ env.Append(RCCOMSTR=compiled_resource_message)
+ env.Append(GENCOMSTR=generated_file_message)
def disable_warnings(self):
diff --git a/platform/android/SCsub b/platform/android/SCsub
index 31bc7c25b0..7380511d6d 100644
--- a/platform/android/SCsub
+++ b/platform/android/SCsub
@@ -1,6 +1,7 @@
#!/usr/bin/env python
import subprocess
+from methods import print_warning
Import("env")
@@ -52,7 +53,7 @@ elif env["arch"] == "x86_32":
elif env["arch"] == "x86_64":
lib_arch_dir = "x86_64"
else:
- print("WARN: Architecture not suitable for embedding into APK; keeping .so at \\bin")
+ print_warning("Architecture not suitable for embedding into APK; keeping .so at \\bin")
if lib_arch_dir != "":
if env.dev_build:
diff --git a/platform/android/detect.py b/platform/android/detect.py
index fea8ec3287..cbd6144182 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -2,7 +2,7 @@ import os
import sys
import platform
import subprocess
-
+from methods import print_warning, print_error
from typing import TYPE_CHECKING
if TYPE_CHECKING:
@@ -76,7 +76,6 @@ def get_flags():
# Check if Android NDK version is installed
# If not, install it.
def install_ndk_if_needed(env: "SConsEnvironment"):
- print("Checking for Android NDK...")
sdk_root = env["ANDROID_HOME"]
if not os.path.exists(get_android_ndk_root(env)):
extension = ".bat" if os.name == "nt" else ""
@@ -87,13 +86,11 @@ def install_ndk_if_needed(env: "SConsEnvironment"):
ndk_download_args = "ndk;" + get_ndk_version()
subprocess.check_call([sdkmanager, ndk_download_args])
else:
- print("Cannot find " + sdkmanager)
- print(
- "Please ensure ANDROID_HOME is correct and cmdline-tools are installed, or install NDK version "
- + get_ndk_version()
- + " manually."
+ print_error(
+ f'Cannot find "{sdkmanager}". Please ensure ANDROID_HOME is correct and cmdline-tools'
+ f'are installed, or install NDK version "{get_ndk_version()}" manually.'
)
- sys.exit()
+ sys.exit(255)
env["ANDROID_NDK_ROOT"] = get_android_ndk_root(env)
@@ -101,15 +98,15 @@ def configure(env: "SConsEnvironment"):
# Validate arch.
supported_arches = ["x86_32", "x86_64", "arm32", "arm64"]
if env["arch"] not in supported_arches:
- print(
+ print_error(
'Unsupported CPU architecture "%s" for Android. Supported architectures are: %s.'
% (env["arch"], ", ".join(supported_arches))
)
- sys.exit()
+ sys.exit(255)
if get_min_sdk_version(env["ndk_platform"]) < get_min_target_api():
- print(
- "WARNING: minimum supported Android target api is %d. Forcing target api %d."
+ print_warning(
+ "Minimum supported Android target api is %d. Forcing target api %d."
% (get_min_target_api(), get_min_target_api())
)
env["ndk_platform"] = "android-" + str(get_min_target_api())
diff --git a/platform/ios/detect.py b/platform/ios/detect.py
index 0c9b7b3204..e3bac4ec5c 100644
--- a/platform/ios/detect.py
+++ b/platform/ios/detect.py
@@ -1,6 +1,6 @@
import os
import sys
-from methods import detect_darwin_sdk_path
+from methods import print_error, detect_darwin_sdk_path
from typing import TYPE_CHECKING
@@ -60,11 +60,11 @@ def configure(env: "SConsEnvironment"):
# Validate arch.
supported_arches = ["x86_64", "arm64"]
if env["arch"] not in supported_arches:
- print(
+ print_error(
'Unsupported CPU architecture "%s" for iOS. Supported architectures are: %s.'
% (env["arch"], ", ".join(supported_arches))
)
- sys.exit()
+ sys.exit(255)
## LTO
@@ -118,7 +118,7 @@ def configure(env: "SConsEnvironment"):
if env["arch"] == "x86_64":
if not env["ios_simulator"]:
- print("ERROR: Building for iOS with 'arch=x86_64' requires 'ios_simulator=yes'.")
+ print_error("Building for iOS with 'arch=x86_64' requires 'ios_simulator=yes'.")
sys.exit(255)
env["ENV"]["MACOSX_DEPLOYMENT_TARGET"] = "10.9"
diff --git a/platform/ios/doc_classes/EditorExportPlatformIOS.xml b/platform/ios/doc_classes/EditorExportPlatformIOS.xml
index 0c0ded5fea..eb226c7993 100644
--- a/platform/ios/doc_classes/EditorExportPlatformIOS.xml
+++ b/platform/ios/doc_classes/EditorExportPlatformIOS.xml
@@ -123,12 +123,441 @@
<member name="icons/spotlight_80x80" type="String" setter="" getter="">
Spotlight icon file on iPad and iPhone (2x DPI). If left empty, it will fallback to [member ProjectSettings.application/config/icon]. See [url=https://developer.apple.com/design/human-interface-guidelines/foundations/app-icons]App icons[/url].
</member>
+ <member name="privacy/active_keyboard_access_reasons" type="int" setter="" getter="">
+ The reasons your app use active keyboard API. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api]Describing use of required reason API[/url].
+ </member>
<member name="privacy/camera_usage_description" type="String" setter="" getter="">
A message displayed when requesting access to the device's camera (in English).
</member>
<member name="privacy/camera_usage_description_localized" type="Dictionary" setter="" getter="">
A message displayed when requesting access to the device's camera (localized).
</member>
+ <member name="privacy/collected_data/advertising_data/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects advertising data.
+ </member>
+ <member name="privacy/collected_data/advertising_data/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects advertising data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/advertising_data/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links advertising data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/advertising_data/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses advertising data for tracking.
+ </member>
+ <member name="privacy/collected_data/audio_data/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects audio data data.
+ </member>
+ <member name="privacy/collected_data/audio_data/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects audio data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/audio_data/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links audio data data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/audio_data/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses audio data data for tracking.
+ </member>
+ <member name="privacy/collected_data/browsing_history/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects browsing history.
+ </member>
+ <member name="privacy/collected_data/browsing_history/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects browsing history. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/browsing_history/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links browsing history to the user's identity.
+ </member>
+ <member name="privacy/collected_data/browsing_history/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses browsing history for tracking.
+ </member>
+ <member name="privacy/collected_data/coarse_location/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects coarse location data.
+ </member>
+ <member name="privacy/collected_data/coarse_location/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects coarse location data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/coarse_location/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links coarse location data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/coarse_location/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses coarse location data for tracking.
+ </member>
+ <member name="privacy/collected_data/contacts/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects contacts.
+ </member>
+ <member name="privacy/collected_data/contacts/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects contacts. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/contacts/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links contacts to the user's identity.
+ </member>
+ <member name="privacy/collected_data/contacts/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses contacts for tracking.
+ </member>
+ <member name="privacy/collected_data/crash_data/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects crash data.
+ </member>
+ <member name="privacy/collected_data/crash_data/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects crash data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/crash_data/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links crash data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/crash_data/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses crash data for tracking.
+ </member>
+ <member name="privacy/collected_data/credit_info/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects credit information.
+ </member>
+ <member name="privacy/collected_data/credit_info/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects credit information. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/credit_info/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links credit information to the user's identity.
+ </member>
+ <member name="privacy/collected_data/credit_info/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses credit information for tracking.
+ </member>
+ <member name="privacy/collected_data/customer_support/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects customer support data.
+ </member>
+ <member name="privacy/collected_data/customer_support/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects customer support data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/customer_support/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links customer support data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/customer_support/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses customer support data for tracking.
+ </member>
+ <member name="privacy/collected_data/device_id/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects device IDs.
+ </member>
+ <member name="privacy/collected_data/device_id/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects device IDs. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/device_id/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links device IDs to the user's identity.
+ </member>
+ <member name="privacy/collected_data/device_id/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses device IDs for tracking.
+ </member>
+ <member name="privacy/collected_data/email_address/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects email address.
+ </member>
+ <member name="privacy/collected_data/email_address/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects email address. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/email_address/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links email address to the user's identity.
+ </member>
+ <member name="privacy/collected_data/email_address/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses email address for tracking.
+ </member>
+ <member name="privacy/collected_data/emails_or_text_messages/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects emails or text messages.
+ </member>
+ <member name="privacy/collected_data/emails_or_text_messages/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects emails or text messages. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/emails_or_text_messages/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links emails or text messages to the user's identity.
+ </member>
+ <member name="privacy/collected_data/emails_or_text_messages/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses emails or text messages for tracking.
+ </member>
+ <member name="privacy/collected_data/environment_scanning/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects environment scanning data.
+ </member>
+ <member name="privacy/collected_data/environment_scanning/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects environment scanning data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/environment_scanning/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links environment scanning data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/environment_scanning/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses environment scanning data for tracking.
+ </member>
+ <member name="privacy/collected_data/fitness/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects fitness and exercise data.
+ </member>
+ <member name="privacy/collected_data/fitness/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects fitness and exercise data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/fitness/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links fitness and exercise data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/fitness/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses fitness and exercise data for tracking.
+ </member>
+ <member name="privacy/collected_data/gameplay_content/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects gameplay content.
+ </member>
+ <member name="privacy/collected_data/gameplay_content/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects gameplay content. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/gameplay_content/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links gameplay content to the user's identity.
+ </member>
+ <member name="privacy/collected_data/gameplay_content/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses gameplay content for tracking.
+ </member>
+ <member name="privacy/collected_data/hands/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects user's hand structure and hand movements.
+ </member>
+ <member name="privacy/collected_data/hands/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects user's hand structure and hand movements. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/hands/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links user's hand structure and hand movements to the user's identity.
+ </member>
+ <member name="privacy/collected_data/hands/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses user's hand structure and hand movements for tracking.
+ </member>
+ <member name="privacy/collected_data/head/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects user's head movement.
+ </member>
+ <member name="privacy/collected_data/head/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects user's head movement. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/head/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links user's head movement to the user's identity.
+ </member>
+ <member name="privacy/collected_data/head/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses user's head movement for tracking.
+ </member>
+ <member name="privacy/collected_data/health/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects health and medical data.
+ </member>
+ <member name="privacy/collected_data/health/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects health and medical data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/health/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links health and medical data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/health/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses health and medical data for tracking.
+ </member>
+ <member name="privacy/collected_data/name/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects user's name.
+ </member>
+ <member name="privacy/collected_data/name/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects user's name. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/name/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links user's name to the user's identity.
+ </member>
+ <member name="privacy/collected_data/name/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses user's name for tracking.
+ </member>
+ <member name="privacy/collected_data/other_contact_info/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects any other contact information.
+ </member>
+ <member name="privacy/collected_data/other_contact_info/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects any other contact information. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/other_contact_info/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links any other contact information to the user's identity.
+ </member>
+ <member name="privacy/collected_data/other_contact_info/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses any other contact information for tracking.
+ </member>
+ <member name="privacy/collected_data/other_data_types/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects any other data.
+ </member>
+ <member name="privacy/collected_data/other_data_types/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects any other data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/other_data_types/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links any other data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/other_data_types/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses any other data for tracking.
+ </member>
+ <member name="privacy/collected_data/other_diagnostic_data/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects any other diagnostic data.
+ </member>
+ <member name="privacy/collected_data/other_diagnostic_data/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects any other diagnostic data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/other_diagnostic_data/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links any other diagnostic data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/other_diagnostic_data/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses any other diagnostic data for tracking.
+ </member>
+ <member name="privacy/collected_data/other_financial_info/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects any other financial information.
+ </member>
+ <member name="privacy/collected_data/other_financial_info/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects any other financial information. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/other_financial_info/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links any other financial information to the user's identity.
+ </member>
+ <member name="privacy/collected_data/other_financial_info/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses any other financial information for tracking.
+ </member>
+ <member name="privacy/collected_data/other_usage_data/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects any other usage data.
+ </member>
+ <member name="privacy/collected_data/other_usage_data/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects any other usage data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/other_usage_data/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links any other usage data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/other_usage_data/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses any other usage data for tracking.
+ </member>
+ <member name="privacy/collected_data/other_user_content/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects any other user generated content.
+ </member>
+ <member name="privacy/collected_data/other_user_content/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects any other user generated content. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/other_user_content/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links any other user generated content to the user's identity.
+ </member>
+ <member name="privacy/collected_data/other_user_content/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses any other user generated content for tracking.
+ </member>
+ <member name="privacy/collected_data/payment_info/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects payment information.
+ </member>
+ <member name="privacy/collected_data/payment_info/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects payment information. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/payment_info/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links payment information to the user's identity.
+ </member>
+ <member name="privacy/collected_data/payment_info/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses payment information for tracking.
+ </member>
+ <member name="privacy/collected_data/performance_data/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects performance data.
+ </member>
+ <member name="privacy/collected_data/performance_data/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects performance data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/performance_data/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links performance data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/performance_data/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses performance data for tracking.
+ </member>
+ <member name="privacy/collected_data/phone_number/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects phone number.
+ </member>
+ <member name="privacy/collected_data/phone_number/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects phone number. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/phone_number/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links phone number to the user's identity.
+ </member>
+ <member name="privacy/collected_data/phone_number/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses phone number for tracking.
+ </member>
+ <member name="privacy/collected_data/photos_or_videos/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects photos or videos.
+ </member>
+ <member name="privacy/collected_data/photos_or_videos/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects photos or videos. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/photos_or_videos/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links photos or videos to the user's identity.
+ </member>
+ <member name="privacy/collected_data/photos_or_videos/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses photos or videos for tracking.
+ </member>
+ <member name="privacy/collected_data/physical_address/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects physical address.
+ </member>
+ <member name="privacy/collected_data/physical_address/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects physical address. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/physical_address/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links physical address to the user's identity.
+ </member>
+ <member name="privacy/collected_data/physical_address/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses physical address for tracking.
+ </member>
+ <member name="privacy/collected_data/precise_location/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects precise location data.
+ </member>
+ <member name="privacy/collected_data/precise_location/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects precise location data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/precise_location/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links precise location data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/precise_location/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses precise location data for tracking.
+ </member>
+ <member name="privacy/collected_data/product_interaction/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects product interaction data.
+ </member>
+ <member name="privacy/collected_data/product_interaction/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects product interaction data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/product_interaction/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links product interaction data to the user's identity.
+ </member>
+ <member name="privacy/collected_data/product_interaction/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses product interaction data for tracking.
+ </member>
+ <member name="privacy/collected_data/purchase_history/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects purchase history.
+ </member>
+ <member name="privacy/collected_data/purchase_history/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects purchase history. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/purchase_history/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links purchase history to the user's identity.
+ </member>
+ <member name="privacy/collected_data/purchase_history/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses purchase history for tracking.
+ </member>
+ <member name="privacy/collected_data/search_hhistory/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects search history.
+ </member>
+ <member name="privacy/collected_data/search_hhistory/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects search history. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/search_hhistory/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links search history to the user's identity.
+ </member>
+ <member name="privacy/collected_data/search_hhistory/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses search history for tracking.
+ </member>
+ <member name="privacy/collected_data/sensitive_info/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects sensitive user information.
+ </member>
+ <member name="privacy/collected_data/sensitive_info/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects sensitive user information. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/sensitive_info/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links sensitive user information to the user's identity.
+ </member>
+ <member name="privacy/collected_data/sensitive_info/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses sensitive user information for tracking.
+ </member>
+ <member name="privacy/collected_data/user_id/collected" type="bool" setter="" getter="">
+ Indicates whether your app collects user IDs.
+ </member>
+ <member name="privacy/collected_data/user_id/collection_purposes" type="int" setter="" getter="">
+ The reasons your app collects user IDs. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
+ </member>
+ <member name="privacy/collected_data/user_id/linked_to_user" type="bool" setter="" getter="">
+ Indicates whether your app links user IDs to the user's identity.
+ </member>
+ <member name="privacy/collected_data/user_id/used_for_tracking" type="bool" setter="" getter="">
+ Indicates whether your app uses user IDs for tracking.
+ </member>
+ <member name="privacy/disk_space_access_reasons" type="int" setter="" getter="">
+ The reasons your app use free disk space API. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api]Describing use of required reason API[/url].
+ </member>
+ <member name="privacy/file_timestamp_access_reasons" type="int" setter="" getter="">
+ The reasons your app use file timestamp/metadata API. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api]Describing use of required reason API[/url].
+ </member>
<member name="privacy/microphone_usage_description" type="String" setter="" getter="">
A message displayed when requesting access to the device's microphone (in English).
</member>
@@ -141,6 +570,18 @@
<member name="privacy/photolibrary_usage_description_localized" type="Dictionary" setter="" getter="">
A message displayed when requesting access to the user's photo library (localized).
</member>
+ <member name="privacy/system_boot_time_access_reasons" type="int" setter="" getter="">
+ The reasons your app use system boot time / absolute time API. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api]Describing use of required reason API[/url].
+ </member>
+ <member name="privacy/tracking_domains" type="PackedStringArray" setter="" getter="">
+ The list of internet domains your app connects to that engage in tracking. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files]Privacy manifest files[/url].
+ </member>
+ <member name="privacy/tracking_enabled" type="bool" setter="" getter="">
+ Indicates whether your app uses data for tracking. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files]Privacy manifest files[/url].
+ </member>
+ <member name="privacy/user_defaults_access_reasons" type="int" setter="" getter="">
+ The reasons your app use user defaults API. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api]Describing use of required reason API[/url].
+ </member>
<member name="storyboard/custom_bg_color" type="Color" setter="" getter="">
A custom background color of the storyboard launch screen.
</member>
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index 33389129b7..ab0018d321 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -106,6 +106,94 @@ static const IconInfo icon_infos[] = {
{ PNAME("icons/notification_60x60"), "iphone", "Icon-60.png", "60", "3x", "20x20", false }
};
+struct APIAccessInfo {
+ String prop_name;
+ String type_name;
+ Vector<String> prop_flag_value;
+ Vector<String> prop_flag_name;
+ int default_value;
+};
+
+static const APIAccessInfo api_info[] = {
+ { "file_timestamp",
+ "NSPrivacyAccessedAPICategoryFileTimestamp",
+ { "DDA9.1", "C617.1", "3B52.1" },
+ { "Display to user on-device:", "Inside app or group container", "Files provided to app by user" },
+ 3 },
+ { "system_boot_time",
+ "NSPrivacyAccessedAPICategorySystemBootTime",
+ { "35F9.1", "8FFB.1", "3D61.1" },
+ { "Measure time on-device", "Calculate absolute event timestamps", "User-initiated bug report" },
+ 1 },
+ { "disk_space",
+ "NSPrivacyAccessedAPICategoryDiskSpace",
+ { "E174.1", "85F4.1", "7D9E.1", "B728.1" },
+ { "Write or delete file on-device", "Display to user on-device", "User-initiated bug report", "Health research app" },
+ 3 },
+ { "active_keyboard",
+ "NSPrivacyAccessedAPICategoryActiveKeyboards",
+ { "3EC4.1", "54BD.1" },
+ { "Custom keyboard app on-device", "Customize UI on-device:2" },
+ 0 },
+ { "user_defaults",
+ "NSPrivacyAccessedAPICategoryUserDefaults",
+ { "1C8F.1", "AC6B.1", "CA92.1" },
+ { "Access info from same App Group", "Access managed app configuration", "Access info from same app" },
+ 0 }
+};
+
+struct DataCollectionInfo {
+ String prop_name;
+ String type_name;
+};
+
+static const DataCollectionInfo data_collect_type_info[] = {
+ { "name", "NSPrivacyCollectedDataTypeName" },
+ { "email_address", "NSPrivacyCollectedDataTypeEmailAddress" },
+ { "phone_number", "NSPrivacyCollectedDataTypePhoneNumber" },
+ { "physical_address", "NSPrivacyCollectedDataTypePhysicalAddress" },
+ { "other_contact_info", "NSPrivacyCollectedDataTypeOtherUserContactInfo" },
+ { "health", "NSPrivacyCollectedDataTypeHealth" },
+ { "fitness", "NSPrivacyCollectedDataTypeFitness" },
+ { "payment_info", "NSPrivacyCollectedDataTypePaymentInfo" },
+ { "credit_info", "NSPrivacyCollectedDataTypeCreditInfo" },
+ { "other_financial_info", "NSPrivacyCollectedDataTypeOtherFinancialInfo" },
+ { "precise_location", "NSPrivacyCollectedDataTypePreciseLocation" },
+ { "coarse_location", "NSPrivacyCollectedDataTypeCoarseLocation" },
+ { "sensitive_info", "NSPrivacyCollectedDataTypeSensitiveInfo" },
+ { "contacts", "NSPrivacyCollectedDataTypeContacts" },
+ { "emails_or_text_messages", "NSPrivacyCollectedDataTypeEmailsOrTextMessages" },
+ { "photos_or_videos", "NSPrivacyCollectedDataTypePhotosorVideos" },
+ { "audio_data", "NSPrivacyCollectedDataTypeAudioData" },
+ { "gameplay_content", "NSPrivacyCollectedDataTypeGameplayContent" },
+ { "customer_support", "NSPrivacyCollectedDataTypeCustomerSupport" },
+ { "other_user_content", "NSPrivacyCollectedDataTypeOtherUserContent" },
+ { "browsing_history", "NSPrivacyCollectedDataTypeBrowsingHistory" },
+ { "search_hhistory", "NSPrivacyCollectedDataTypeSearchHistory" },
+ { "user_id", "NSPrivacyCollectedDataTypeUserID" },
+ { "device_id", "NSPrivacyCollectedDataTypeDeviceID" },
+ { "purchase_history", "NSPrivacyCollectedDataTypePurchaseHistory" },
+ { "product_interaction", "NSPrivacyCollectedDataTypeProductInteraction" },
+ { "advertising_data", "NSPrivacyCollectedDataTypeAdvertisingData" },
+ { "other_usage_data", "NSPrivacyCollectedDataTypeOtherUsageData" },
+ { "crash_data", "NSPrivacyCollectedDataTypeCrashData" },
+ { "performance_data", "NSPrivacyCollectedDataTypePerformanceData" },
+ { "other_diagnostic_data", "NSPrivacyCollectedDataTypeOtherDiagnosticData" },
+ { "environment_scanning", "NSPrivacyCollectedDataTypeEnvironmentScanning" },
+ { "hands", "NSPrivacyCollectedDataTypeHands" },
+ { "head", "NSPrivacyCollectedDataTypeHead" },
+ { "other_data_types", "NSPrivacyCollectedDataTypeOtherDataTypes" },
+};
+
+static const DataCollectionInfo data_collect_purpose_info[] = {
+ { "Analytics", "NSPrivacyCollectedDataTypePurposeAnalytics" },
+ { "App Functionality", "NSPrivacyCollectedDataTypePurposeAppFunctionality" },
+ { "Developer Advertising", "NSPrivacyCollectedDataTypePurposeDeveloperAdvertising" },
+ { "Third-party Advertising", "NSPrivacyCollectedDataTypePurposeThirdPartyAdvertising" },
+ { "Product Personalization", "NSPrivacyCollectedDataTypePurposeProductPersonalization" },
+ { "Other", "NSPrivacyCollectedDataTypePurposeOther" },
+};
+
String EditorExportPlatformIOS::get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const {
if (p_preset) {
if (p_name == "application/app_store_team_id") {
@@ -119,6 +207,21 @@ String EditorExportPlatformIOS::get_export_option_warning(const EditorExportPres
if (!is_package_name_valid(identifier, &pn_err)) {
return TTR("Invalid Identifier:") + " " + pn_err;
}
+ } else if (p_name == "privacy/file_timestamp_access_reasons") {
+ int access = p_preset->get("privacy/file_timestamp_access_reasons");
+ if (access == 0) {
+ return TTR("At least one file timestamp access reason should be selected.");
+ }
+ } else if (p_name == "privacy/disk_space_access_reasons") {
+ int access = p_preset->get("privacy/disk_space_access_reasons");
+ if (access == 0) {
+ return TTR("At least one disk space access reason should be selected.");
+ }
+ } else if (p_name == "privacy/system_boot_time_access_reasons") {
+ int access = p_preset->get("privacy/system_boot_time_access_reasons");
+ if (access == 0) {
+ return TTR("At least one system boot time access reason should be selected.");
+ }
}
}
return String();
@@ -140,6 +243,15 @@ bool EditorExportPlatformIOS::get_export_option_visibility(const EditorExportPre
return false;
}
+ if (p_preset == nullptr) {
+ return true;
+ }
+
+ bool advanced_options_enabled = p_preset->are_advanced_options_enabled();
+ if (p_option.begins_with("privacy")) {
+ return advanced_options_enabled;
+ }
+
return true;
}
@@ -220,6 +332,37 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/photolibrary_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need access to the photo library"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/photolibrary_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary()));
+ for (uint64_t i = 0; i < sizeof(api_info) / sizeof(api_info[0]); ++i) {
+ String prop_name = vformat("privacy/%s_access_reasons", api_info[i].prop_name);
+ String hint;
+ for (int j = 0; j < api_info[i].prop_flag_value.size(); j++) {
+ if (j != 0) {
+ hint += ",";
+ }
+ hint += vformat("%s - %s:%d", api_info[i].prop_flag_value[j], api_info[i].prop_flag_name[j], (1 << j));
+ }
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, prop_name, PROPERTY_HINT_FLAGS, hint), api_info[i].default_value));
+ }
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "privacy/tracking_enabled"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "privacy/tracking_domains"), Vector<String>()));
+
+ {
+ String hint;
+ for (uint64_t i = 0; i < sizeof(data_collect_purpose_info) / sizeof(data_collect_purpose_info[0]); ++i) {
+ if (i != 0) {
+ hint += ",";
+ }
+ hint += vformat("%s:%d", data_collect_purpose_info[i].prop_name, (1 << i));
+ }
+ for (uint64_t i = 0; i < sizeof(data_collect_type_info) / sizeof(data_collect_type_info[0]); ++i) {
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("privacy/collected_data/%s/collected", data_collect_type_info[i].prop_name)), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("privacy/collected_data/%s/linked_to_user", data_collect_type_info[i].prop_name)), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("privacy/collected_data/%s/used_for_tracking", data_collect_type_info[i].prop_name)), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, vformat("privacy/collected_data/%s/collection_purposes", data_collect_type_info[i].prop_name), PROPERTY_HINT_FLAGS, hint), 0));
+ }
+ }
+
HashSet<String> used_names;
for (uint64_t i = 0; i < sizeof(icon_infos) / sizeof(icon_infos[0]); ++i) {
if (!used_names.has(icon_infos[i].preset_key)) {
@@ -522,6 +665,87 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
} else if (lines[i].find("$swift_runtime_build_phase") != -1) {
String value = !p_config.use_swift_runtime ? "" : "90B4C2B62680C7E90039117A /* dummy.swift */,";
strnew += lines[i].replace("$swift_runtime_build_phase", value) + "\n";
+ } else if (lines[i].find("$priv_collection") != -1) {
+ bool section_opened = false;
+ for (uint64_t j = 0; j < sizeof(data_collect_type_info) / sizeof(data_collect_type_info[0]); ++j) {
+ bool data_collected = p_preset->get(vformat("privacy/collected_data/%s/collected", data_collect_type_info[j].prop_name));
+ bool linked = p_preset->get(vformat("privacy/collected_data/%s/linked_to_user", data_collect_type_info[j].prop_name));
+ bool tracking = p_preset->get(vformat("privacy/collected_data/%s/used_for_tracking", data_collect_type_info[j].prop_name));
+ int purposes = p_preset->get(vformat("privacy/collected_data/%s/collection_purposes", data_collect_type_info[j].prop_name));
+ if (data_collected) {
+ if (!section_opened) {
+ section_opened = true;
+ strnew += "\t<key>NSPrivacyCollectedDataTypes</key>\n";
+ strnew += "\t<array>\n";
+ }
+ strnew += "\t\t<dict>\n";
+ strnew += "\t\t\t<key>NSPrivacyCollectedDataType</key>\n";
+ strnew += vformat("\t\t\t<string>%s</string>\n", data_collect_type_info[j].type_name);
+ strnew += "\t\t\t\t<key>NSPrivacyCollectedDataTypeLinked</key>\n";
+ if (linked) {
+ strnew += "\t\t\t\t<true/>\n";
+ } else {
+ strnew += "\t\t\t\t<false/>\n";
+ }
+ strnew += "\t\t\t\t<key>NSPrivacyCollectedDataTypeTracking</key>\n";
+ if (tracking) {
+ strnew += "\t\t\t\t<true/>\n";
+ } else {
+ strnew += "\t\t\t\t<false/>\n";
+ }
+ if (purposes != 0) {
+ strnew += "\t\t\t\t<key>NSPrivacyCollectedDataTypePurposes</key>\n";
+ strnew += "\t\t\t\t<array>\n";
+ for (uint64_t k = 0; k < sizeof(data_collect_purpose_info) / sizeof(data_collect_purpose_info[0]); ++k) {
+ if (purposes & (1 << k)) {
+ strnew += vformat("\t\t\t\t\t<string>%s</string>\n", data_collect_purpose_info[k].type_name);
+ }
+ }
+ strnew += "\t\t\t\t</array>\n";
+ }
+ strnew += "\t\t\t</dict>\n";
+ }
+ }
+ if (section_opened) {
+ strnew += "\t</array>\n";
+ }
+ } else if (lines[i].find("$priv_tracking") != -1) {
+ bool tracking = p_preset->get("privacy/tracking_enabled");
+ strnew += "\t<key>NSPrivacyTracking</key>\n";
+ if (tracking) {
+ strnew += "\t<true/>\n";
+ } else {
+ strnew += "\t<false/>\n";
+ }
+ Vector<String> tracking_domains = p_preset->get("privacy/tracking_domains");
+ if (!tracking_domains.is_empty()) {
+ strnew += "\t<key>NSPrivacyTrackingDomains</key>\n";
+ strnew += "\t<array>\n";
+ for (const String &E : tracking_domains) {
+ strnew += "\t\t<string>" + E + "</string>\n";
+ }
+ strnew += "\t</array>\n";
+ }
+ } else if (lines[i].find("$priv_api_types") != -1) {
+ strnew += "\t<array>\n";
+ for (uint64_t j = 0; j < sizeof(api_info) / sizeof(api_info[0]); ++j) {
+ int api_access = p_preset->get(vformat("privacy/%s_access_reasons", api_info[j].prop_name));
+ if (api_access != 0) {
+ strnew += "\t\t<dict>\n";
+ strnew += "\t\t\t<key>NSPrivacyAccessedAPITypeReasons</key>\n";
+ strnew += "\t\t\t<array>\n";
+ for (int k = 0; k < api_info[j].prop_flag_value.size(); k++) {
+ if (api_access & (1 << k)) {
+ strnew += vformat("\t\t\t\t<string>%s</string>\n", api_info[j].prop_flag_value[k]);
+ }
+ }
+ strnew += "\t\t\t</array>\n";
+ strnew += "\t\t\t<key>NSPrivacyAccessedAPIType</key>\n";
+ strnew += vformat("\t\t\t<string>%s</string>\n", api_info[j].type_name);
+ strnew += "\t\t</dict>\n";
+ }
+ }
+ strnew += "\t</array>\n";
} else {
strnew += lines[i] + "\n";
}
@@ -1694,6 +1918,7 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
files_to_parse.insert("godot_ios.xcodeproj/xcshareddata/xcschemes/godot_ios.xcscheme");
files_to_parse.insert("godot_ios/godot_ios.entitlements");
files_to_parse.insert("godot_ios/Launch Screen.storyboard");
+ files_to_parse.insert("PrivacyInfo.xcprivacy");
IOSConfigData config_data = {
pkg_name,
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 27dec73b65..afc9d25a80 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -1,7 +1,7 @@
import os
import platform
import sys
-from methods import get_compiler_version, using_gcc
+from methods import print_warning, print_error, get_compiler_version, using_gcc
from platform_methods import detect_arch
from typing import TYPE_CHECKING
@@ -20,7 +20,7 @@ def can_build():
pkgconf_error = os.system("pkg-config --version > /dev/null")
if pkgconf_error:
- print("Error: pkg-config not found. Aborting.")
+ print_error("pkg-config not found. Aborting.")
return False
return True
@@ -75,7 +75,7 @@ def configure(env: "SConsEnvironment"):
# Validate arch.
supported_arches = ["x86_32", "x86_64", "arm32", "arm64", "rv64", "ppc32", "ppc64"]
if env["arch"] not in supported_arches:
- print(
+ print_error(
'Unsupported CPU architecture "%s" for Linux / *BSD. Supported architectures are: %s.'
% (env["arch"], ", ".join(supported_arches))
)
@@ -128,7 +128,9 @@ def configure(env: "SConsEnvironment"):
found_wrapper = True
break
if not found_wrapper:
- print("Couldn't locate mold installation path. Make sure it's installed in /usr or /usr/local.")
+ print_error(
+ "Couldn't locate mold installation path. Make sure it's installed in /usr or /usr/local."
+ )
sys.exit(255)
else:
env.Append(LINKFLAGS=["-fuse-ld=mold"])
@@ -185,7 +187,7 @@ def configure(env: "SConsEnvironment"):
if env["lto"] != "none":
if env["lto"] == "thin":
if not env["use_llvm"]:
- print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
+ print_error("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
sys.exit(255)
env.Append(CCFLAGS=["-flto=thin"])
env.Append(LINKFLAGS=["-flto=thin"])
@@ -209,7 +211,7 @@ def configure(env: "SConsEnvironment"):
if env["wayland"]:
if os.system("wayland-scanner -v 2>/dev/null") != 0:
- print("wayland-scanner not found. Disabling Wayland support.")
+ print_warning("wayland-scanner not found. Disabling Wayland support.")
env["wayland"] = False
if env["touch"]:
@@ -227,7 +229,7 @@ def configure(env: "SConsEnvironment"):
env["builtin_harfbuzz"],
]
if (not all(ft_linked_deps)) and any(ft_linked_deps): # All or nothing.
- print(
+ print_error(
"These libraries should be either all builtin, or all system provided:\n"
"freetype, libpng, zlib, graphite, harfbuzz.\n"
"Please specify `builtin_<name>=no` for all of them, or none."
@@ -318,7 +320,7 @@ def configure(env: "SConsEnvironment"):
env.ParseConfig("pkg-config fontconfig --cflags --libs")
env.Append(CPPDEFINES=["FONTCONFIG_ENABLED"])
else:
- print("Warning: fontconfig development libraries not found. Disabling the system fonts support.")
+ print_warning("fontconfig development libraries not found. Disabling the system fonts support.")
env["fontconfig"] = False
else:
env.Append(CPPDEFINES=["FONTCONFIG_ENABLED"])
@@ -329,7 +331,7 @@ def configure(env: "SConsEnvironment"):
env.ParseConfig("pkg-config alsa --cflags --libs")
env.Append(CPPDEFINES=["ALSA_ENABLED", "ALSAMIDI_ENABLED"])
else:
- print("Warning: ALSA development libraries not found. Disabling the ALSA audio driver.")
+ print_warning("ALSA development libraries not found. Disabling the ALSA audio driver.")
env["alsa"] = False
else:
env.Append(CPPDEFINES=["ALSA_ENABLED", "ALSAMIDI_ENABLED"])
@@ -340,7 +342,7 @@ def configure(env: "SConsEnvironment"):
env.ParseConfig("pkg-config libpulse --cflags --libs")
env.Append(CPPDEFINES=["PULSEAUDIO_ENABLED"])
else:
- print("Warning: PulseAudio development libraries not found. Disabling the PulseAudio audio driver.")
+ print_warning("PulseAudio development libraries not found. Disabling the PulseAudio audio driver.")
env["pulseaudio"] = False
else:
env.Append(CPPDEFINES=["PULSEAUDIO_ENABLED", "_REENTRANT"])
@@ -351,7 +353,7 @@ def configure(env: "SConsEnvironment"):
env.ParseConfig("pkg-config dbus-1 --cflags --libs")
env.Append(CPPDEFINES=["DBUS_ENABLED"])
else:
- print("Warning: D-Bus development libraries not found. Disabling screensaver prevention.")
+ print_warning("D-Bus development libraries not found. Disabling screensaver prevention.")
env["dbus"] = False
else:
env.Append(CPPDEFINES=["DBUS_ENABLED"])
@@ -362,7 +364,7 @@ def configure(env: "SConsEnvironment"):
env.ParseConfig("pkg-config speech-dispatcher --cflags --libs")
env.Append(CPPDEFINES=["SPEECHD_ENABLED"])
else:
- print("Warning: speech-dispatcher development libraries not found. Disabling text to speech support.")
+ print_warning("speech-dispatcher development libraries not found. Disabling text to speech support.")
env["speechd"] = False
else:
env.Append(CPPDEFINES=["SPEECHD_ENABLED"])
@@ -373,11 +375,11 @@ def configure(env: "SConsEnvironment"):
env.Append(CPPDEFINES=["XKB_ENABLED"])
else:
if env["wayland"]:
- print("Error: libxkbcommon development libraries required by Wayland not found. Aborting.")
+ print_error("libxkbcommon development libraries required by Wayland not found. Aborting.")
sys.exit(255)
else:
- print(
- "Warning: libxkbcommon development libraries not found. Disabling dead key composition and key label support."
+ print_warning(
+ "libxkbcommon development libraries not found. Disabling dead key composition and key label support."
)
else:
env.Append(CPPDEFINES=["XKB_ENABLED"])
@@ -390,7 +392,7 @@ def configure(env: "SConsEnvironment"):
env.ParseConfig("pkg-config libudev --cflags --libs")
env.Append(CPPDEFINES=["UDEV_ENABLED"])
else:
- print("Warning: libudev development libraries not found. Disabling controller hotplugging support.")
+ print_warning("libudev development libraries not found. Disabling controller hotplugging support.")
env["udev"] = False
else:
env.Append(CPPDEFINES=["UDEV_ENABLED"])
@@ -416,31 +418,31 @@ def configure(env: "SConsEnvironment"):
if env["x11"]:
if not env["use_sowrap"]:
if os.system("pkg-config --exists x11"):
- print("Error: X11 libraries not found. Aborting.")
+ print_error("X11 libraries not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config x11 --cflags --libs")
if os.system("pkg-config --exists xcursor"):
- print("Error: Xcursor library not found. Aborting.")
+ print_error("Xcursor library not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config xcursor --cflags --libs")
if os.system("pkg-config --exists xinerama"):
- print("Error: Xinerama library not found. Aborting.")
+ print_error("Xinerama library not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config xinerama --cflags --libs")
if os.system("pkg-config --exists xext"):
- print("Error: Xext library not found. Aborting.")
+ print_error("Xext library not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config xext --cflags --libs")
if os.system("pkg-config --exists xrandr"):
- print("Error: XrandR library not found. Aborting.")
+ print_error("XrandR library not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config xrandr --cflags --libs")
if os.system("pkg-config --exists xrender"):
- print("Error: XRender library not found. Aborting.")
+ print_error("XRender library not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config xrender --cflags --libs")
if os.system("pkg-config --exists xi"):
- print("Error: Xi library not found. Aborting.")
+ print_error("Xi library not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config xi --cflags --libs")
env.Append(CPPDEFINES=["X11_ENABLED"])
@@ -448,20 +450,20 @@ def configure(env: "SConsEnvironment"):
if env["wayland"]:
if not env["use_sowrap"]:
if os.system("pkg-config --exists libdecor-0"):
- print("Warning: libdecor development libraries not found. Disabling client-side decorations.")
+ print_warning("libdecor development libraries not found. Disabling client-side decorations.")
env["libdecor"] = False
else:
env.ParseConfig("pkg-config libdecor-0 --cflags --libs")
if os.system("pkg-config --exists wayland-client"):
- print("Error: Wayland client library not found. Aborting.")
+ print_error("Wayland client library not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config wayland-client --cflags --libs")
if os.system("pkg-config --exists wayland-cursor"):
- print("Error: Wayland cursor library not found. Aborting.")
+ print_error("Wayland cursor library not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config wayland-cursor --cflags --libs")
if os.system("pkg-config --exists wayland-egl"):
- print("Error: Wayland EGL library not found. Aborting.")
+ print_error("Wayland EGL library not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config wayland-egl --cflags --libs")
diff --git a/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp
index 7f9008e952..3a63b87c7a 100644
--- a/platform/linuxbsd/wayland/wayland_thread.cpp
+++ b/platform/linuxbsd/wayland/wayland_thread.cpp
@@ -371,28 +371,22 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
}
if (strcmp(interface, zxdg_exporter_v1_interface.name) == 0) {
- registry->wl_exporter = (struct zxdg_exporter_v1 *)wl_registry_bind(wl_registry, name, &zxdg_exporter_v1_interface, 1);
- registry->wl_exporter_name = name;
+ registry->xdg_exporter = (struct zxdg_exporter_v1 *)wl_registry_bind(wl_registry, name, &zxdg_exporter_v1_interface, 1);
+ registry->xdg_exporter_name = name;
return;
}
if (strcmp(interface, wl_compositor_interface.name) == 0) {
- registry->wl_compositor = (struct wl_compositor *)wl_registry_bind(wl_registry, name, &wl_compositor_interface, 4);
+ registry->wl_compositor = (struct wl_compositor *)wl_registry_bind(wl_registry, name, &wl_compositor_interface, CLAMP((int)version, 1, 6));
registry->wl_compositor_name = name;
return;
}
- if (strcmp(interface, wl_subcompositor_interface.name) == 0) {
- registry->wl_subcompositor = (struct wl_subcompositor *)wl_registry_bind(wl_registry, name, &wl_subcompositor_interface, 1);
- registry->wl_subcompositor_name = name;
- return;
- }
-
if (strcmp(interface, wl_data_device_manager_interface.name) == 0) {
- registry->wl_data_device_manager = (struct wl_data_device_manager *)wl_registry_bind(wl_registry, name, &wl_data_device_manager_interface, 3);
+ registry->wl_data_device_manager = (struct wl_data_device_manager *)wl_registry_bind(wl_registry, name, &wl_data_device_manager_interface, CLAMP((int)version, 1, 3));
registry->wl_data_device_manager_name = name;
- // This global creates some seats data. Let's do that for the ones already available.
+ // This global creates some seat data. Let's do that for the ones already available.
for (struct wl_seat *wl_seat : registry->wl_seats) {
SeatState *ss = wl_seat_get_seat_state(wl_seat);
ERR_FAIL_NULL(ss);
@@ -406,7 +400,7 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
}
if (strcmp(interface, wl_output_interface.name) == 0) {
- struct wl_output *wl_output = (struct wl_output *)wl_registry_bind(wl_registry, name, &wl_output_interface, 2);
+ struct wl_output *wl_output = (struct wl_output *)wl_registry_bind(wl_registry, name, &wl_output_interface, CLAMP((int)version, 1, 4));
wl_proxy_tag_godot((struct wl_proxy *)wl_output);
registry->wl_outputs.push_back(wl_output);
@@ -421,7 +415,7 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
}
if (strcmp(interface, wl_seat_interface.name) == 0) {
- struct wl_seat *wl_seat = (struct wl_seat *)wl_registry_bind(wl_registry, name, &wl_seat_interface, 5);
+ struct wl_seat *wl_seat = (struct wl_seat *)wl_registry_bind(wl_registry, name, &wl_seat_interface, CLAMP((int)version, 1, 9));
wl_proxy_tag_godot((struct wl_proxy *)wl_seat);
SeatState *ss = memnew(SeatState);
@@ -466,7 +460,7 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
}
if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
- registry->xdg_wm_base = (struct xdg_wm_base *)wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, MAX(2, MIN(6, (int)version)));
+ registry->xdg_wm_base = (struct xdg_wm_base *)wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, CLAMP((int)version, 1, 6));
registry->xdg_wm_base_name = name;
xdg_wm_base_add_listener(registry->xdg_wm_base, &xdg_wm_base_listener, nullptr);
@@ -502,7 +496,7 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
if (strcmp(interface, zwp_primary_selection_device_manager_v1_interface.name) == 0) {
registry->wp_primary_selection_device_manager = (struct zwp_primary_selection_device_manager_v1 *)wl_registry_bind(wl_registry, name, &zwp_primary_selection_device_manager_v1_interface, 1);
- // This global creates some seats data. Let's do that for the ones already available.
+ // This global creates some seat data. Let's do that for the ones already available.
for (struct wl_seat *wl_seat : registry->wl_seats) {
SeatState *ss = wl_seat_get_seat_state(wl_seat);
ERR_FAIL_NULL(ss);
@@ -570,13 +564,13 @@ void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry
return;
}
- if (name == registry->wl_exporter_name) {
- if (registry->wl_exporter) {
- zxdg_exporter_v1_destroy(registry->wl_exporter);
- registry->wl_exporter = nullptr;
+ if (name == registry->xdg_exporter_name) {
+ if (registry->xdg_exporter) {
+ zxdg_exporter_v1_destroy(registry->xdg_exporter);
+ registry->xdg_exporter = nullptr;
}
- registry->wl_exporter_name = 0;
+ registry->xdg_exporter_name = 0;
return;
}
@@ -592,17 +586,6 @@ void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry
return;
}
- if (name == registry->wl_subcompositor_name) {
- if (registry->wl_subcompositor) {
- wl_subcompositor_destroy(registry->wl_subcompositor);
- registry->wl_subcompositor = nullptr;
- }
-
- registry->wl_subcompositor_name = 0;
-
- return;
- }
-
if (name == registry->wl_data_device_manager_name) {
if (registry->wl_data_device_manager) {
wl_data_device_manager_destroy(registry->wl_data_device_manager);
@@ -1000,6 +983,12 @@ void WaylandThread::_wl_output_on_geometry(void *data, struct wl_output *wl_outp
ss->pending_data.make.parse_utf8(make);
ss->pending_data.model.parse_utf8(model);
+
+ // `wl_output::done` is a version 2 addition. We'll directly update the data
+ // for compatibility.
+ if (wl_output_get_version(wl_output) == 1) {
+ ss->data = ss->pending_data;
+ }
}
void WaylandThread::_wl_output_on_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
@@ -1010,8 +999,17 @@ void WaylandThread::_wl_output_on_mode(void *data, struct wl_output *wl_output,
ss->pending_data.size.height = height;
ss->pending_data.refresh_rate = refresh ? refresh / 1000.0f : -1;
+
+ // `wl_output::done` is a version 2 addition. We'll directly update the data
+ // for compatibility.
+ if (wl_output_get_version(wl_output) == 1) {
+ ss->data = ss->pending_data;
+ }
}
+// NOTE: The following `wl_output` events are only for version 2 onwards, so we
+// can assume that they're "atomic" (i.e. rely on the `wl_output::done` event).
+
void WaylandThread::_wl_output_on_done(void *data, struct wl_output *wl_output) {
ScreenState *ss = (ScreenState *)data;
ERR_FAIL_NULL(ss);
@@ -1523,7 +1521,7 @@ void WaylandThread::_wl_pointer_on_frame(void *data, struct wl_pointer *wl_point
wayland_thread->push_message(msg);
}
- if (pd.discrete_scroll_vector - old_pd.discrete_scroll_vector != Vector2i()) {
+ if (pd.discrete_scroll_vector_120 - old_pd.discrete_scroll_vector_120 != Vector2i()) {
// This is a discrete scroll (eg. from a scroll wheel), so we'll just emit
// scroll wheel buttons.
if (pd.scroll_vector.y != 0) {
@@ -1596,13 +1594,13 @@ void WaylandThread::_wl_pointer_on_frame(void *data, struct wl_pointer *wl_point
if (test_button == MouseButton::WHEEL_UP || test_button == MouseButton::WHEEL_DOWN) {
// If this is a discrete scroll, specify how many "clicks" it did for this
// pointer frame.
- mb->set_factor(abs(pd.discrete_scroll_vector.y));
+ mb->set_factor(Math::abs(pd.discrete_scroll_vector_120.y / (float)120));
}
if (test_button == MouseButton::WHEEL_RIGHT || test_button == MouseButton::WHEEL_LEFT) {
// If this is a discrete scroll, specify how many "clicks" it did for this
// pointer frame.
- mb->set_factor(abs(pd.discrete_scroll_vector.x));
+ mb->set_factor(fabs(pd.discrete_scroll_vector_120.x / (float)120));
}
mb->set_button_mask(pd.pressed_button_mask);
@@ -1661,7 +1659,7 @@ void WaylandThread::_wl_pointer_on_frame(void *data, struct wl_pointer *wl_point
// Reset the scroll vectors as we already handled them.
pd.scroll_vector = Vector2();
- pd.discrete_scroll_vector = Vector2();
+ pd.discrete_scroll_vector_120 = Vector2i();
// Update the data all getters read. Wayland's specification requires us to do
// this, since all pointer actions are sent in individual events.
@@ -1683,6 +1681,9 @@ void WaylandThread::_wl_pointer_on_axis_source(void *data, struct wl_pointer *wl
void WaylandThread::_wl_pointer_on_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) {
}
+// NOTE: This event is deprecated since version 8 and superseded by
+// `wl_pointer::axis_value120`. This thus converts the data to its
+// fraction-of-120 format.
void WaylandThread::_wl_pointer_on_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) {
SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);
@@ -1694,17 +1695,37 @@ void WaylandThread::_wl_pointer_on_axis_discrete(void *data, struct wl_pointer *
PointerData &pd = ss->pointer_data_buffer;
+ // NOTE: We can allow ourselves to not accumulate this data (and thus just
+ // assign it) as the spec guarantees only one event per axis type.
+
if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
- pd.discrete_scroll_vector.y = discrete;
+ pd.discrete_scroll_vector_120.y = discrete * 120;
}
if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
- pd.discrete_scroll_vector.x = discrete;
+ pd.discrete_scroll_vector_120.x = discrete * 120;
}
}
-// TODO: Add support to this event.
+// Supersedes `wl_pointer::axis_discrete` Since version 8.
void WaylandThread::_wl_pointer_on_axis_value120(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t value120) {
+ SeatState *ss = (SeatState *)data;
+ ERR_FAIL_NULL(ss);
+
+ if (!ss->pointed_surface) {
+ // We're probably on a decoration or some other third-party thing.
+ return;
+ }
+
+ PointerData &pd = ss->pointer_data_buffer;
+
+ if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
+ pd.discrete_scroll_vector_120.y += value120;
+ }
+
+ if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
+ pd.discrete_scroll_vector_120.x += value120;
+ }
}
// TODO: Add support to this event.
@@ -1999,7 +2020,7 @@ void WaylandThread::_wp_relative_pointer_on_relative_motion(void *data, struct z
pd.relative_motion_time = uptime_lo;
}
-void WaylandThread::_wp_pointer_gesture_pinch_on_begin(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, struct wl_surface *surface, uint32_t fingers) {
+void WaylandThread::_wp_pointer_gesture_pinch_on_begin(void *data, struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, struct wl_surface *surface, uint32_t fingers) {
SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);
@@ -2009,7 +2030,7 @@ void WaylandThread::_wp_pointer_gesture_pinch_on_begin(void *data, struct zwp_po
}
}
-void WaylandThread::_wp_pointer_gesture_pinch_on_update(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation) {
+void WaylandThread::_wp_pointer_gesture_pinch_on_update(void *data, struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation) {
SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);
@@ -2068,7 +2089,7 @@ void WaylandThread::_wp_pointer_gesture_pinch_on_update(void *data, struct zwp_p
}
}
-void WaylandThread::_wp_pointer_gesture_pinch_on_end(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, int32_t cancelled) {
+void WaylandThread::_wp_pointer_gesture_pinch_on_end(void *data, struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, int32_t cancelled) {
SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);
@@ -2093,7 +2114,7 @@ void WaylandThread::_wp_primary_selection_device_on_selection(void *data, struct
ss->wp_primary_selection_offer = id;
}
-void WaylandThread::_wp_primary_selection_offer_on_offer(void *data, struct zwp_primary_selection_offer_v1 *zwp_primary_selection_offer_v1, const char *mime_type) {
+void WaylandThread::_wp_primary_selection_offer_on_offer(void *data, struct zwp_primary_selection_offer_v1 *wp_primary_selection_offer_v1, const char *mime_type) {
OfferState *os = (OfferState *)data;
ERR_FAIL_NULL(os);
@@ -2147,10 +2168,10 @@ void WaylandThread::_wp_primary_selection_source_on_cancelled(void *data, struct
}
}
-void WaylandThread::_wp_tablet_seat_on_tablet_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_v2 *id) {
+void WaylandThread::_wp_tablet_seat_on_tablet_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_v2 *id) {
}
-void WaylandThread::_wp_tablet_seat_on_tool_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_tool_v2 *id) {
+void WaylandThread::_wp_tablet_seat_on_tool_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_tool_v2 *id) {
SeatState *ss = (SeatState *)data;
ERR_FAIL_NULL(ss);
@@ -2163,31 +2184,31 @@ void WaylandThread::_wp_tablet_seat_on_tool_added(void *data, struct zwp_tablet_
ss->tablet_tools.push_back(id);
}
-void WaylandThread::_wp_tablet_seat_on_pad_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_pad_v2 *id) {
+void WaylandThread::_wp_tablet_seat_on_pad_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_pad_v2 *id) {
}
-void WaylandThread::_wp_tablet_tool_on_type(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t tool_type) {
- TabletToolState *state = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_type(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t tool_type) {
+ TabletToolState *state = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (state && tool_type == ZWP_TABLET_TOOL_V2_TYPE_ERASER) {
state->is_eraser = true;
}
}
-void WaylandThread::_wp_tablet_tool_on_hardware_serial(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t hardware_serial_hi, uint32_t hardware_serial_lo) {
+void WaylandThread::_wp_tablet_tool_on_hardware_serial(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t hardware_serial_hi, uint32_t hardware_serial_lo) {
}
-void WaylandThread::_wp_tablet_tool_on_hardware_id_wacom(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t hardware_id_hi, uint32_t hardware_id_lo) {
+void WaylandThread::_wp_tablet_tool_on_hardware_id_wacom(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t hardware_id_hi, uint32_t hardware_id_lo) {
}
-void WaylandThread::_wp_tablet_tool_on_capability(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t capability) {
+void WaylandThread::_wp_tablet_tool_on_capability(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t capability) {
}
-void WaylandThread::_wp_tablet_tool_on_done(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) {
+void WaylandThread::_wp_tablet_tool_on_done(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2) {
}
-void WaylandThread::_wp_tablet_tool_on_removed(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) {
- TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_removed(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2) {
+ TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (!ts) {
return;
@@ -2199,7 +2220,7 @@ void WaylandThread::_wp_tablet_tool_on_removed(void *data, struct zwp_tablet_too
return;
}
- List<struct zwp_tablet_tool_v2 *>::Element *E = ss->tablet_tools.find(zwp_tablet_tool_v2);
+ List<struct zwp_tablet_tool_v2 *>::Element *E = ss->tablet_tools.find(wp_tablet_tool_v2);
if (E && E->get()) {
struct zwp_tablet_tool_v2 *tool = E->get();
@@ -2213,8 +2234,8 @@ void WaylandThread::_wp_tablet_tool_on_removed(void *data, struct zwp_tablet_too
}
}
-void WaylandThread::_wp_tablet_tool_on_proximity_in(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface) {
- TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_proximity_in(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface) {
+ TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (!ts) {
return;
@@ -2241,8 +2262,8 @@ void WaylandThread::_wp_tablet_tool_on_proximity_in(void *data, struct zwp_table
DEBUG_LOG_WAYLAND_THREAD("Tablet tool entered window.");
}
-void WaylandThread::_wp_tablet_tool_on_proximity_out(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) {
- TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_proximity_out(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2) {
+ TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (!ts) {
return;
@@ -2268,8 +2289,8 @@ void WaylandThread::_wp_tablet_tool_on_proximity_out(void *data, struct zwp_tabl
DEBUG_LOG_WAYLAND_THREAD("Tablet tool left window.");
}
-void WaylandThread::_wp_tablet_tool_on_down(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t serial) {
- TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_down(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial) {
+ TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (!ts) {
return;
@@ -2286,8 +2307,8 @@ void WaylandThread::_wp_tablet_tool_on_down(void *data, struct zwp_tablet_tool_v
td.button_time = OS::get_singleton()->get_ticks_msec();
}
-void WaylandThread::_wp_tablet_tool_on_up(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) {
- TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_up(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2) {
+ TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (!ts) {
return;
@@ -2302,8 +2323,8 @@ void WaylandThread::_wp_tablet_tool_on_up(void *data, struct zwp_tablet_tool_v2
td.button_time = OS::get_singleton()->get_ticks_msec();
}
-void WaylandThread::_wp_tablet_tool_on_motion(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t x, wl_fixed_t y) {
- TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_motion(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t x, wl_fixed_t y) {
+ TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (!ts) {
return;
@@ -2323,8 +2344,8 @@ void WaylandThread::_wp_tablet_tool_on_motion(void *data, struct zwp_tablet_tool
td.motion_time = OS::get_singleton()->get_ticks_msec();
}
-void WaylandThread::_wp_tablet_tool_on_pressure(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t pressure) {
- TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_pressure(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t pressure) {
+ TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (!ts) {
return;
@@ -2333,12 +2354,12 @@ void WaylandThread::_wp_tablet_tool_on_pressure(void *data, struct zwp_tablet_to
ts->data_pending.pressure = pressure;
}
-void WaylandThread::_wp_tablet_tool_on_distance(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t distance) {
+void WaylandThread::_wp_tablet_tool_on_distance(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t distance) {
// Unsupported
}
-void WaylandThread::_wp_tablet_tool_on_tilt(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t tilt_x, wl_fixed_t tilt_y) {
- TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_tilt(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t tilt_x, wl_fixed_t tilt_y) {
+ TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (!ts) {
return;
@@ -2350,20 +2371,20 @@ void WaylandThread::_wp_tablet_tool_on_tilt(void *data, struct zwp_tablet_tool_v
td.tilt.y = wl_fixed_to_double(tilt_y);
}
-void WaylandThread::_wp_tablet_tool_on_rotation(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t degrees) {
+void WaylandThread::_wp_tablet_tool_on_rotation(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t degrees) {
// Unsupported.
}
-void WaylandThread::_wp_tablet_tool_on_slider(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, int32_t position) {
+void WaylandThread::_wp_tablet_tool_on_slider(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, int32_t position) {
// Unsupported.
}
-void WaylandThread::_wp_tablet_tool_on_wheel(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t degrees, int32_t clicks) {
+void WaylandThread::_wp_tablet_tool_on_wheel(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t degrees, int32_t clicks) {
// TODO
}
-void WaylandThread::_wp_tablet_tool_on_button(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t serial, uint32_t button, uint32_t state) {
- TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_button(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial, uint32_t button, uint32_t state) {
+ TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (!ts) {
return;
@@ -2398,8 +2419,8 @@ void WaylandThread::_wp_tablet_tool_on_button(void *data, struct zwp_tablet_tool
}
}
-void WaylandThread::_wp_tablet_tool_on_frame(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t time) {
- TabletToolState *ts = wp_tablet_tool_get_state(zwp_tablet_tool_v2);
+void WaylandThread::_wp_tablet_tool_on_frame(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t time) {
+ TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2);
if (!ts) {
return;
@@ -3071,8 +3092,8 @@ void WaylandThread::window_create(DisplayServer::WindowID p_window_id, int p_wid
// "loop".
wl_surface_commit(ws.wl_surface);
- if (registry.wl_exporter) {
- ws.xdg_exported = zxdg_exporter_v1_export(registry.wl_exporter, ws.wl_surface);
+ if (registry.xdg_exporter) {
+ ws.xdg_exported = zxdg_exporter_v1_export(registry.xdg_exporter, ws.wl_surface);
zxdg_exported_v1_add_listener(ws.xdg_exported, &xdg_exported_listener, &ws);
}
@@ -3529,9 +3550,6 @@ Error WaylandThread::init() {
ERR_FAIL_NULL_V_MSG(registry.wl_shm, ERR_UNAVAILABLE, "Can't obtain the Wayland shared memory global.");
ERR_FAIL_NULL_V_MSG(registry.wl_compositor, ERR_UNAVAILABLE, "Can't obtain the Wayland compositor global.");
- ERR_FAIL_NULL_V_MSG(registry.wl_subcompositor, ERR_UNAVAILABLE, "Can't obtain the Wayland subcompositor global.");
- ERR_FAIL_NULL_V_MSG(registry.wl_data_device_manager, ERR_UNAVAILABLE, "Can't obtain the Wayland data device manager global.");
- ERR_FAIL_NULL_V_MSG(registry.wp_pointer_constraints, ERR_UNAVAILABLE, "Can't obtain the Wayland pointer constraints global.");
ERR_FAIL_NULL_V_MSG(registry.xdg_wm_base, ERR_UNAVAILABLE, "Can't obtain the Wayland XDG shell global.");
if (!registry.xdg_decoration_manager) {
@@ -3660,7 +3678,10 @@ void WaylandThread::cursor_shape_set_custom_image(DisplayServer::CursorShape p_c
munmap(cursor.buffer_data, cursor.buffer_data_size);
}
- cursor.buffer_data = (uint32_t *)mmap(nullptr, data_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ // NOTE: From `wl_keyboard`s of version 7 or later, the spec requires the mmap
+ // operation to be done with MAP_PRIVATE, as "MAP_SHARED may fail". We'll do it
+ // regardless of global version.
+ cursor.buffer_data = (uint32_t *)mmap(nullptr, data_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (cursor.wl_buffer) {
// Clean up the old Wayland buffer.
@@ -4179,18 +4200,14 @@ void WaylandThread::destroy() {
xdg_wm_base_destroy(registry.xdg_wm_base);
}
- if (registry.wl_exporter) {
- zxdg_exporter_v1_destroy(registry.wl_exporter);
+ if (registry.xdg_exporter) {
+ zxdg_exporter_v1_destroy(registry.xdg_exporter);
}
if (registry.wl_shm) {
wl_shm_destroy(registry.wl_shm);
}
- if (registry.wl_subcompositor) {
- wl_subcompositor_destroy(registry.wl_subcompositor);
- }
-
if (registry.wl_compositor) {
wl_compositor_destroy(registry.wl_compositor);
}
diff --git a/platform/linuxbsd/wayland/wayland_thread.h b/platform/linuxbsd/wayland/wayland_thread.h
index d49f0c9d34..d35a5b7139 100644
--- a/platform/linuxbsd/wayland/wayland_thread.h
+++ b/platform/linuxbsd/wayland/wayland_thread.h
@@ -133,8 +133,8 @@ public:
struct xdg_wm_base *xdg_wm_base = nullptr;
uint32_t xdg_wm_base_name = 0;
- struct zxdg_exporter_v1 *wl_exporter = nullptr;
- uint32_t wl_exporter_name = 0;
+ struct zxdg_exporter_v1 *xdg_exporter = nullptr;
+ uint32_t xdg_exporter_name = 0;
// wayland-protocols globals.
@@ -300,8 +300,8 @@ public:
// The amount "scrolled" in pixels, in each direction.
Vector2 scroll_vector;
- // The amount of scroll "clicks" in each direction.
- Vector2i discrete_scroll_vector;
+ // The amount of scroll "clicks" in each direction, in fractions of 120.
+ Vector2i discrete_scroll_vector_120;
uint32_t pinch_scale = 1;
};
@@ -579,41 +579,41 @@ private:
static void _wp_relative_pointer_on_relative_motion(void *data, struct zwp_relative_pointer_v1 *wp_relative_pointer_v1, uint32_t uptime_hi, uint32_t uptime_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel);
- static void _wp_pointer_gesture_pinch_on_begin(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, struct wl_surface *surface, uint32_t fingers);
- static void _wp_pointer_gesture_pinch_on_update(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation);
- static void _wp_pointer_gesture_pinch_on_end(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, int32_t cancelled);
+ static void _wp_pointer_gesture_pinch_on_begin(void *data, struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, struct wl_surface *surface, uint32_t fingers);
+ static void _wp_pointer_gesture_pinch_on_update(void *data, struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation);
+ static void _wp_pointer_gesture_pinch_on_end(void *data, struct zwp_pointer_gesture_pinch_v1 *zp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, int32_t cancelled);
static void _wp_primary_selection_device_on_data_offer(void *data, struct zwp_primary_selection_device_v1 *wp_primary_selection_device_v1, struct zwp_primary_selection_offer_v1 *offer);
static void _wp_primary_selection_device_on_selection(void *data, struct zwp_primary_selection_device_v1 *wp_primary_selection_device_v1, struct zwp_primary_selection_offer_v1 *id);
- static void _wp_primary_selection_offer_on_offer(void *data, struct zwp_primary_selection_offer_v1 *zwp_primary_selection_offer_v1, const char *mime_type);
+ static void _wp_primary_selection_offer_on_offer(void *data, struct zwp_primary_selection_offer_v1 *wp_primary_selection_offer_v1, const char *mime_type);
static void _wp_primary_selection_source_on_send(void *data, struct zwp_primary_selection_source_v1 *wp_primary_selection_source_v1, const char *mime_type, int32_t fd);
static void _wp_primary_selection_source_on_cancelled(void *data, struct zwp_primary_selection_source_v1 *wp_primary_selection_source_v1);
- static void _wp_tablet_seat_on_tablet_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_v2 *id);
- static void _wp_tablet_seat_on_tool_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_tool_v2 *id);
- static void _wp_tablet_seat_on_pad_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_pad_v2 *id);
-
- static void _wp_tablet_tool_on_type(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t tool_type);
- static void _wp_tablet_tool_on_hardware_serial(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t hardware_serial_hi, uint32_t hardware_serial_lo);
- static void _wp_tablet_tool_on_hardware_id_wacom(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t hardware_id_hi, uint32_t hardware_id_lo);
- static void _wp_tablet_tool_on_capability(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t capability);
- static void _wp_tablet_tool_on_done(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2);
- static void _wp_tablet_tool_on_removed(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2);
- static void _wp_tablet_tool_on_proximity_in(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface);
- static void _wp_tablet_tool_on_proximity_out(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2);
- static void _wp_tablet_tool_on_down(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t serial);
- static void _wp_tablet_tool_on_up(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2);
- static void _wp_tablet_tool_on_motion(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t x, wl_fixed_t y);
- static void _wp_tablet_tool_on_pressure(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t pressure);
- static void _wp_tablet_tool_on_distance(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t distance);
- static void _wp_tablet_tool_on_tilt(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t tilt_x, wl_fixed_t tilt_y);
- static void _wp_tablet_tool_on_rotation(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t degrees);
- static void _wp_tablet_tool_on_slider(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, int32_t position);
- static void _wp_tablet_tool_on_wheel(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t degrees, int32_t clicks);
- static void _wp_tablet_tool_on_button(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t serial, uint32_t button, uint32_t state);
- static void _wp_tablet_tool_on_frame(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t time);
+ static void _wp_tablet_seat_on_tablet_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_v2 *id);
+ static void _wp_tablet_seat_on_tool_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_tool_v2 *id);
+ static void _wp_tablet_seat_on_pad_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_pad_v2 *id);
+
+ static void _wp_tablet_tool_on_type(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t tool_type);
+ static void _wp_tablet_tool_on_hardware_serial(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t hardware_serial_hi, uint32_t hardware_serial_lo);
+ static void _wp_tablet_tool_on_hardware_id_wacom(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t hardware_id_hi, uint32_t hardware_id_lo);
+ static void _wp_tablet_tool_on_capability(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t capability);
+ static void _wp_tablet_tool_on_done(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2);
+ static void _wp_tablet_tool_on_removed(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2);
+ static void _wp_tablet_tool_on_proximity_in(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface);
+ static void _wp_tablet_tool_on_proximity_out(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2);
+ static void _wp_tablet_tool_on_down(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial);
+ static void _wp_tablet_tool_on_up(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2);
+ static void _wp_tablet_tool_on_motion(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t x, wl_fixed_t y);
+ static void _wp_tablet_tool_on_pressure(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t pressure);
+ static void _wp_tablet_tool_on_distance(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t distance);
+ static void _wp_tablet_tool_on_tilt(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t tilt_x, wl_fixed_t tilt_y);
+ static void _wp_tablet_tool_on_rotation(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t degrees);
+ static void _wp_tablet_tool_on_slider(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, int32_t position);
+ static void _wp_tablet_tool_on_wheel(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t degrees, int32_t clicks);
+ static void _wp_tablet_tool_on_button(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial, uint32_t button, uint32_t state);
+ static void _wp_tablet_tool_on_frame(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t time);
static void _xdg_toplevel_decoration_on_configure(void *data, struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration, uint32_t mode);
diff --git a/platform/macos/detect.py b/platform/macos/detect.py
index 3c8b1ebee1..a5ef29e34f 100644
--- a/platform/macos/detect.py
+++ b/platform/macos/detect.py
@@ -1,6 +1,6 @@
import os
import sys
-from methods import detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang
+from methods import print_error, detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang
from platform_methods import detect_arch, detect_mvk
from typing import TYPE_CHECKING
@@ -64,11 +64,11 @@ def configure(env: "SConsEnvironment"):
# Validate arch.
supported_arches = ["x86_64", "arm64"]
if env["arch"] not in supported_arches:
- print(
+ print_error(
'Unsupported CPU architecture "%s" for macOS. Supported architectures are: %s.'
% (env["arch"], ", ".join(supported_arches))
)
- sys.exit()
+ sys.exit(255)
## Build type
@@ -254,7 +254,7 @@ def configure(env: "SConsEnvironment"):
if mvk_path != "":
env.Append(LINKFLAGS=["-L" + mvk_path])
else:
- print(
+ print_error(
"MoltenVK SDK installation directory not found, use 'vulkan_sdk_path' SCons parameter to specify SDK path."
)
sys.exit(255)
diff --git a/platform/web/SCsub b/platform/web/SCsub
index 3e0cc9ac4a..bc5893ab3a 100644
--- a/platform/web/SCsub
+++ b/platform/web/SCsub
@@ -1,5 +1,7 @@
#!/usr/bin/env python
+from methods import print_error
+
Import("env")
# The HTTP server "targets". Run with "scons p=web serve", or "scons p=web run"
@@ -11,7 +13,7 @@ if "serve" in COMMAND_LINE_TARGETS or "run" in COMMAND_LINE_TARGETS:
try:
port = int(port)
except Exception:
- print("GODOT_WEB_TEST_PORT must be a valid integer")
+ print_error("GODOT_WEB_TEST_PORT must be a valid integer")
sys.exit(255)
serve(env.Dir(env.GetTemplateZipPath()).abspath, port, "run" in COMMAND_LINE_TARGETS)
sys.exit(0)
diff --git a/platform/web/api/web_tools_editor_plugin.h b/platform/web/api/web_tools_editor_plugin.h
index ac0d5e20ec..2902f60f24 100644
--- a/platform/web/api/web_tools_editor_plugin.h
+++ b/platform/web/api/web_tools_editor_plugin.h
@@ -34,7 +34,7 @@
#if defined(TOOLS_ENABLED) && defined(WEB_ENABLED)
#include "core/io/zip_io.h"
-#include "editor/editor_plugin.h"
+#include "editor/plugins/editor_plugin.h"
class WebToolsEditorPlugin : public EditorPlugin {
GDCLASS(WebToolsEditorPlugin, EditorPlugin);
diff --git a/platform/web/detect.py b/platform/web/detect.py
index 2d2cc288a1..ccd884b225 100644
--- a/platform/web/detect.py
+++ b/platform/web/detect.py
@@ -10,7 +10,7 @@ from emscripten_helpers import (
create_template_zip,
get_template_zip_path,
)
-from methods import get_compiler_version
+from methods import print_warning, print_error, get_compiler_version
from SCons.Util import WhereIs
from typing import TYPE_CHECKING
@@ -85,16 +85,16 @@ def configure(env: "SConsEnvironment"):
# Validate arch.
supported_arches = ["wasm32"]
if env["arch"] not in supported_arches:
- print(
+ print_error(
'Unsupported CPU architecture "%s" for Web. Supported architectures are: %s.'
% (env["arch"], ", ".join(supported_arches))
)
- sys.exit()
+ sys.exit(255)
try:
env["initial_memory"] = int(env["initial_memory"])
except Exception:
- print("Initial memory must be a valid integer")
+ print_error("Initial memory must be a valid integer")
sys.exit(255)
## Build type
@@ -109,7 +109,7 @@ def configure(env: "SConsEnvironment"):
env.Append(LINKFLAGS=["-s", "ASSERTIONS=1"])
if env.editor_build and env["initial_memory"] < 64:
- print('Note: Forcing "initial_memory=64" as it is required for the web editor.')
+ print("Note: Forcing `initial_memory=64` as it is required for the web editor.")
env["initial_memory"] = 64
env.Append(LINKFLAGS=["-s", "INITIAL_MEMORY=%sMB" % env["initial_memory"]])
@@ -227,7 +227,7 @@ def configure(env: "SConsEnvironment"):
env.Append(LINKFLAGS=["-s", "PTHREAD_POOL_SIZE=8"])
env.Append(LINKFLAGS=["-s", "WASM_MEM_MAX=2048MB"])
elif env["proxy_to_pthread"]:
- print('"threads=no" support requires "proxy_to_pthread=no", disabling proxy to pthread.')
+ print_warning('"threads=no" support requires "proxy_to_pthread=no", disabling proxy to pthread.')
env["proxy_to_pthread"] = False
if env["lto"] != "none":
@@ -240,11 +240,11 @@ def configure(env: "SConsEnvironment"):
if env["dlink_enabled"]:
if env["proxy_to_pthread"]:
- print("GDExtension support requires proxy_to_pthread=no, disabling proxy to pthread.")
+ print_warning("GDExtension support requires proxy_to_pthread=no, disabling proxy to pthread.")
env["proxy_to_pthread"] = False
if cc_semver < (3, 1, 14):
- print("GDExtension support requires emscripten >= 3.1.14, detected: %s.%s.%s" % cc_semver)
+ print_error("GDExtension support requires emscripten >= 3.1.14, detected: %s.%s.%s" % cc_semver)
sys.exit(255)
env.Append(CCFLAGS=["-s", "SIDE_MODULE=2"])
diff --git a/platform/web/js/engine/features.js b/platform/web/js/engine/features.js
index 81bc82f3c6..263ea6ac88 100644
--- a/platform/web/js/engine/features.js
+++ b/platform/web/js/engine/features.js
@@ -72,8 +72,7 @@ const Features = { // eslint-disable-line no-unused-vars
*
* @returns {Array<string>} A list of human-readable missing features.
* @function Engine.getMissingFeatures
- * @typedef {{ threads: boolean }} SupportedFeatures
- * @param {SupportedFeatures} supportedFeatures
+ * @param {{threads: (boolean|undefined)}} supportedFeatures
*/
getMissingFeatures: function (supportedFeatures = {}) {
const {
diff --git a/platform/windows/SCsub b/platform/windows/SCsub
index 159a273e70..435c501956 100644
--- a/platform/windows/SCsub
+++ b/platform/windows/SCsub
@@ -10,7 +10,6 @@ sources = []
common_win = [
"godot_windows.cpp",
- "crash_handler_windows.cpp",
"os_windows.cpp",
"display_server_windows.cpp",
"key_mapping_windows.cpp",
@@ -25,6 +24,11 @@ common_win = [
"rendering_context_driver_vulkan_windows.cpp",
]
+if env.msvc:
+ common_win += ["crash_handler_windows_seh.cpp"]
+else:
+ common_win += ["crash_handler_windows_signal.cpp"]
+
common_win_wrap = [
"console_wrapper_windows.cpp",
]
diff --git a/platform/windows/crash_handler_windows.h b/platform/windows/crash_handler_windows.h
index 3871210977..a0a0b610d0 100644
--- a/platform/windows/crash_handler_windows.h
+++ b/platform/windows/crash_handler_windows.h
@@ -35,12 +35,15 @@
#include <windows.h>
// Crash handler exception only enabled with MSVC
-#if defined(DEBUG_ENABLED) && defined(_MSC_VER)
+#if defined(DEBUG_ENABLED)
#define CRASH_HANDLER_EXCEPTION 1
+#ifdef _MSC_VER
extern DWORD CrashHandlerException(EXCEPTION_POINTERS *ep);
#endif
+#endif
+
class CrashHandler {
bool disabled;
diff --git a/platform/windows/crash_handler_windows.cpp b/platform/windows/crash_handler_windows_seh.cpp
index 133d36aa0d..2abe285d31 100644
--- a/platform/windows/crash_handler_windows.cpp
+++ b/platform/windows/crash_handler_windows_seh.cpp
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* crash_handler_windows.cpp */
+/* crash_handler_windows_seh.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
diff --git a/platform/windows/crash_handler_windows_signal.cpp b/platform/windows/crash_handler_windows_signal.cpp
new file mode 100644
index 0000000000..e11a60bdc7
--- /dev/null
+++ b/platform/windows/crash_handler_windows_signal.cpp
@@ -0,0 +1,205 @@
+/**************************************************************************/
+/* crash_handler_windows_signal.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "crash_handler_windows.h"
+
+#include "core/config/project_settings.h"
+#include "core/os/os.h"
+#include "core/string/print_string.h"
+#include "core/version.h"
+#include "main/main.h"
+
+#ifdef CRASH_HANDLER_EXCEPTION
+
+#include <cxxabi.h>
+#include <signal.h>
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include <psapi.h>
+
+#include "thirdparty/libbacktrace/backtrace.h"
+
+struct CrashHandlerData {
+ int64_t index = 0;
+ backtrace_state *state = nullptr;
+ int64_t offset = 0;
+};
+
+int symbol_callback(void *data, uintptr_t pc, const char *filename, int lineno, const char *function) {
+ CrashHandlerData *ch_data = reinterpret_cast<CrashHandlerData *>(data);
+ if (!function) {
+ return 0;
+ }
+
+ char fname[1024];
+ snprintf(fname, 1024, "%s", function);
+
+ if (function[0] == '_') {
+ int status;
+ char *demangled = abi::__cxa_demangle(function, nullptr, nullptr, &status);
+
+ if (status == 0 && demangled) {
+ snprintf(fname, 1024, "%s", demangled);
+ }
+
+ if (demangled) {
+ free(demangled);
+ }
+ }
+
+ print_error(vformat("[%d] %s (%s:%d)", ch_data->index++, String::utf8(fname), String::utf8(filename), lineno));
+ return 0;
+}
+
+void error_callback(void *data, const char *msg, int errnum) {
+ CrashHandlerData *ch_data = reinterpret_cast<CrashHandlerData *>(data);
+ if (ch_data->index == 0) {
+ print_error(vformat("Error(%d): %s", errnum, String::utf8(msg)));
+ } else {
+ print_error(vformat("[%d] error(%d): %s", ch_data->index++, errnum, String::utf8(msg)));
+ }
+}
+
+int trace_callback(void *data, uintptr_t pc) {
+ CrashHandlerData *ch_data = reinterpret_cast<CrashHandlerData *>(data);
+ backtrace_pcinfo(ch_data->state, pc - ch_data->offset, &symbol_callback, &error_callback, data);
+ return 0;
+}
+
+int64_t get_image_base(const String &p_path) {
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ if (f.is_null()) {
+ return 0;
+ }
+ {
+ f->seek(0x3c);
+ uint32_t pe_pos = f->get_32();
+
+ f->seek(pe_pos);
+ uint32_t magic = f->get_32();
+ if (magic != 0x00004550) {
+ return 0;
+ }
+ }
+ int64_t opt_header_pos = f->get_position() + 0x14;
+ f->seek(opt_header_pos);
+
+ uint16_t opt_header_magic = f->get_16();
+ if (opt_header_magic == 0x10B) {
+ f->seek(opt_header_pos + 0x1C);
+ return f->get_32();
+ } else if (opt_header_magic == 0x20B) {
+ f->seek(opt_header_pos + 0x18);
+ return f->get_64();
+ } else {
+ return 0;
+ }
+}
+
+extern void CrashHandlerException(int signal) {
+ CrashHandlerData data;
+
+ if (OS::get_singleton() == nullptr || OS::get_singleton()->is_disable_crash_handler() || IsDebuggerPresent()) {
+ return;
+ }
+
+ String msg;
+ const ProjectSettings *proj_settings = ProjectSettings::get_singleton();
+ if (proj_settings) {
+ msg = proj_settings->get("debug/settings/crash_handler/message");
+ }
+
+ // Tell MainLoop about the crash. This can be handled by users too in Node.
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
+ }
+
+ print_error("\n================================================================");
+ print_error(vformat("%s: Program crashed with signal %d", __FUNCTION__, signal));
+
+ // Print the engine version just before, so that people are reminded to include the version in backtrace reports.
+ if (String(VERSION_HASH).is_empty()) {
+ print_error(vformat("Engine version: %s", VERSION_FULL_NAME));
+ } else {
+ print_error(vformat("Engine version: %s (%s)", VERSION_FULL_NAME, VERSION_HASH));
+ }
+ print_error(vformat("Dumping the backtrace. %s", msg));
+
+ String _execpath = OS::get_singleton()->get_executable_path();
+
+ // Load process and image info to determine ASLR addresses offset.
+ MODULEINFO mi;
+ GetModuleInformation(GetCurrentProcess(), GetModuleHandle(NULL), &mi, sizeof(mi));
+ int64_t image_mem_base = reinterpret_cast<int64_t>(mi.lpBaseOfDll);
+ int64_t image_file_base = get_image_base(_execpath);
+ data.offset = image_mem_base - image_file_base;
+
+ data.state = backtrace_create_state(_execpath.utf8().get_data(), 0, &error_callback, reinterpret_cast<void *>(&data));
+ if (data.state != nullptr) {
+ data.index = 1;
+ backtrace_simple(data.state, 1, &trace_callback, &error_callback, reinterpret_cast<void *>(&data));
+ }
+
+ print_error("-- END OF BACKTRACE --");
+ print_error("================================================================");
+}
+#endif
+
+CrashHandler::CrashHandler() {
+ disabled = false;
+}
+
+CrashHandler::~CrashHandler() {
+}
+
+void CrashHandler::disable() {
+ if (disabled) {
+ return;
+ }
+
+#if defined(CRASH_HANDLER_EXCEPTION)
+ signal(SIGSEGV, nullptr);
+ signal(SIGFPE, nullptr);
+ signal(SIGILL, nullptr);
+#endif
+
+ disabled = true;
+}
+
+void CrashHandler::initialize() {
+#if defined(CRASH_HANDLER_EXCEPTION)
+ signal(SIGSEGV, CrashHandlerException);
+ signal(SIGFPE, CrashHandlerException);
+ signal(SIGILL, CrashHandlerException);
+#endif
+}
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index f34d479345..93eb34001e 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -2,6 +2,7 @@ import methods
import os
import subprocess
import sys
+from methods import print_warning, print_error
from platform_methods import detect_arch
from typing import TYPE_CHECKING
@@ -293,16 +294,14 @@ def setup_msvc_manual(env: "SConsEnvironment"):
env_arch = detect_build_env_arch()
if env["arch"] != env_arch:
- print(
- """
- Arch argument (%s) is not matching Native/Cross Compile Tools Prompt/Developer Console (or Visual Studio settings) that is being used to run SCons (%s).
- Run SCons again without arch argument (example: scons p=windows) and SCons will attempt to detect what MSVC compiler will be executed and inform you.
- """
+ print_error(
+ "Arch argument (%s) is not matching Native/Cross Compile Tools Prompt/Developer Console (or Visual Studio settings) that is being used to run SCons (%s).\n"
+ "Run SCons again without arch argument (example: scons p=windows) and SCons will attempt to detect what MSVC compiler will be executed and inform you."
% (env["arch"], env_arch)
)
- sys.exit(200)
+ sys.exit(255)
- print("Found MSVC, arch %s" % (env_arch))
+ print("Using VCVARS-determined MSVC, arch %s" % (env_arch))
def setup_msvc_auto(env: "SConsEnvironment"):
@@ -338,7 +337,7 @@ def setup_msvc_auto(env: "SConsEnvironment"):
env.Tool("mssdk") # we want the MS SDK
# Note: actual compiler version can be found in env['MSVC_VERSION'], e.g. "14.1" for VS2015
- print("Found MSVC version %s, arch %s" % (env["MSVC_VERSION"], env["arch"]))
+ print("Using SCons-detected MSVC version %s, arch %s" % (env["MSVC_VERSION"], env["arch"]))
def setup_mingw(env: "SConsEnvironment"):
@@ -346,32 +345,24 @@ def setup_mingw(env: "SConsEnvironment"):
env_arch = detect_build_env_arch()
if os.getenv("MSYSTEM") == "MSYS":
- print(
- """
- Running from base MSYS2 console/environment, use target specific environment instead (e.g., mingw32, mingw64, clang32, clang64).
- """
+ print_error(
+ "Running from base MSYS2 console/environment, use target specific environment instead (e.g., mingw32, mingw64, clang32, clang64)."
)
- sys.exit(201)
+ sys.exit(255)
if env_arch != "" and env["arch"] != env_arch:
- print(
- """
- Arch argument (%s) is not matching MSYS2 console/environment that is being used to run SCons (%s).
- Run SCons again without arch argument (example: scons p=windows) and SCons will attempt to detect what MSYS2 compiler will be executed and inform you.
- """
+ print_error(
+ "Arch argument (%s) is not matching MSYS2 console/environment that is being used to run SCons (%s).\n"
+ "Run SCons again without arch argument (example: scons p=windows) and SCons will attempt to detect what MSYS2 compiler will be executed and inform you."
% (env["arch"], env_arch)
)
- sys.exit(202)
+ sys.exit(255)
if not try_cmd("gcc --version", env["mingw_prefix"], env["arch"]) and not try_cmd(
"clang --version", env["mingw_prefix"], env["arch"]
):
- print(
- """
- No valid compilers found, use MINGW_PREFIX environment variable to set MinGW path.
- """
- )
- sys.exit(202)
+ print_error("No valid compilers found, use MINGW_PREFIX environment variable to set MinGW path.")
+ sys.exit(255)
print("Using MinGW, arch %s" % (env["arch"]))
@@ -454,10 +445,10 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config):
if os.getenv("WindowsSdkDir") is not None:
env.Prepend(CPPPATH=[str(os.getenv("WindowsSdkDir")) + "/Include"])
else:
- print("Missing environment variable: WindowsSdkDir")
+ print_warning("Missing environment variable: WindowsSdkDir")
if int(env["target_win_version"], 16) < 0x0601:
- print("`target_win_version` should be 0x0601 or higher (Windows 7).")
+ print_error("`target_win_version` should be 0x0601 or higher (Windows 7).")
sys.exit(255)
env.AppendUnique(
@@ -515,10 +506,10 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config):
if env["d3d12"]:
# Check whether we have d3d12 dependencies installed.
if not os.path.exists(env["mesa_libs"]):
- print("The Direct3D 12 rendering driver requires dependencies to be installed.")
- print(r"You can install them by running `python misc\scripts\install_d3d12_sdk_windows.py`.")
- print("See the documentation for more information:")
- print(
+ print_error(
+ "The Direct3D 12 rendering driver requires dependencies to be installed.\n"
+ "You can install them by running `python misc\\scripts\\install_d3d12_sdk_windows.py`.\n"
+ "See the documentation for more information:\n\t"
"https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_windows.html"
)
sys.exit(255)
@@ -557,13 +548,16 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config):
LIBS += ["dxgi", "d3d9", "d3d11"]
env.Prepend(CPPPATH=["#thirdparty/angle/include"])
+ if env["target"] in ["editor", "template_debug"]:
+ LIBS += ["psapi", "dbghelp"]
+
env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS])
if vcvars_msvc_config:
if os.getenv("WindowsSdkDir") is not None:
env.Append(LIBPATH=[str(os.getenv("WindowsSdkDir")) + "/Lib"])
else:
- print("Missing environment variable: WindowsSdkDir")
+ print_warning("Missing environment variable: WindowsSdkDir")
## LTO
@@ -572,7 +566,7 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config):
if env["lto"] != "none":
if env["lto"] == "thin":
- print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
+ print_error("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
sys.exit(255)
env.AppendUnique(CCFLAGS=["/GL"])
env.AppendUnique(ARFLAGS=["/LTCG"])
@@ -690,7 +684,7 @@ def configure_mingw(env: "SConsEnvironment"):
## Compile flags
if int(env["target_win_version"], 16) < 0x0601:
- print("`target_win_version` should be 0x0601 or higher (Windows 7).")
+ print_error("`target_win_version` should be 0x0601 or higher (Windows 7).")
sys.exit(255)
if not env["use_llvm"]:
@@ -744,10 +738,10 @@ def configure_mingw(env: "SConsEnvironment"):
if env["d3d12"]:
# Check whether we have d3d12 dependencies installed.
if not os.path.exists(env["mesa_libs"]):
- print("The Direct3D 12 rendering driver requires dependencies to be installed.")
- print(r"You can install them by running `python misc\scripts\install_d3d12_sdk_windows.py`.")
- print("See the documentation for more information:")
- print(
+ print_error(
+ "The Direct3D 12 rendering driver requires dependencies to be installed.\n"
+ "You can install them by running `python misc\\scripts\\install_d3d12_sdk_windows.py`.\n"
+ "See the documentation for more information:\n\t"
"https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_windows.html"
)
sys.exit(255)
@@ -794,11 +788,11 @@ def configure(env: "SConsEnvironment"):
# Validate arch.
supported_arches = ["x86_32", "x86_64", "arm32", "arm64"]
if env["arch"] not in supported_arches:
- print(
+ print_error(
'Unsupported CPU architecture "%s" for Windows. Supported architectures are: %s.'
% (env["arch"], ", ".join(supported_arches))
)
- sys.exit()
+ sys.exit(255)
# At this point the env has been set up with basic tools/compilers.
env.Prepend(CPPPATH=["#platform/windows"])
diff --git a/platform/windows/godot_windows.cpp b/platform/windows/godot_windows.cpp
index 5f41b4e568..486c3120fc 100644
--- a/platform/windows/godot_windows.cpp
+++ b/platform/windows/godot_windows.cpp
@@ -215,7 +215,7 @@ int main(int argc, char **argv) {
// _argc and _argv are ignored
// we are going to use the WideChar version of them instead
-#ifdef CRASH_HANDLER_EXCEPTION
+#if defined(CRASH_HANDLER_EXCEPTION) && defined(_MSC_VER)
__try {
return _main();
} __except (CrashHandlerException(GetExceptionInformation())) {
diff --git a/platform_methods.py b/platform_methods.py
index 56115db4a4..57b11d1a47 100644
--- a/platform_methods.py
+++ b/platform_methods.py
@@ -39,8 +39,7 @@ def detect_arch():
# Catches x86, i386, i486, i586, i686, etc.
return "x86_32"
else:
- print("Unsupported CPU architecture: " + host_machine)
- print("Falling back to x86_64.")
+ methods.print_warning(f'Unsupported CPU architecture: "{host_machine}". Falling back to x86_64.')
return "x86_64"
diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp
index 9f71c881a9..54370f42da 100644
--- a/scene/3d/label_3d.cpp
+++ b/scene/3d/label_3d.cpp
@@ -636,6 +636,10 @@ void Label3D::_shape() {
}
void Label3D::set_text(const String &p_string) {
+ if (text == p_string) {
+ return;
+ }
+
text = p_string;
xl_text = atr(p_string);
dirty_text = true;
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index d46470282f..7140161eca 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -149,7 +149,7 @@ void AnimationPlayer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
if (!Engine::get_singleton()->is_editor_hint() && animation_set.has(autoplay)) {
- set_active(true);
+ set_active(active);
play(autoplay);
_check_immediately_after_start();
}
diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp
index 5261cbe3eb..fc9ac2ab18 100644
--- a/scene/gui/texture_progress_bar.cpp
+++ b/scene/gui/texture_progress_bar.cpp
@@ -494,7 +494,7 @@ void TextureProgressBar::_notification(int p_what) {
Rect2 source = Rect2(Point2(), progress->get_size());
draw_texture_rect_region(progress, region, source, tint_progress);
} else if (val != 0) {
- Array pts;
+ LocalVector<float> pts;
float direction = mode == FILL_COUNTER_CLOCKWISE ? -1 : 1;
float start;
@@ -507,11 +507,11 @@ void TextureProgressBar::_notification(int p_what) {
float end = start + direction * val;
float from = MIN(start, end);
float to = MAX(start, end);
- pts.append(from);
+ pts.push_back(from);
for (float corner = Math::floor(from * 4 + 0.5) * 0.25 + 0.125; corner < to; corner += 0.25) {
- pts.append(corner);
+ pts.push_back(corner);
}
- pts.append(to);
+ pts.push_back(to);
Ref<AtlasTexture> atlas_progress = progress;
bool valid_atlas_progress = atlas_progress.is_valid() && atlas_progress->get_atlas().is_valid();
@@ -524,8 +524,8 @@ void TextureProgressBar::_notification(int p_what) {
Vector<Point2> uvs;
Vector<Point2> points;
- for (int i = 0; i < pts.size(); i++) {
- Point2 uv = unit_val_to_uv(pts[i]);
+ for (const float &f : pts) {
+ Point2 uv = unit_val_to_uv(f);
if (uvs.find(uv) >= 0) {
continue;
}
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 5b375905cc..296c914c65 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -37,6 +37,15 @@
#include "servers/rendering_server.h"
#include "texture.h"
+#ifdef TOOLS_ENABLED
+#include "editor/editor_help.h"
+
+#include "modules/modules_enabled.gen.h" // For regex.
+#ifdef MODULE_REGEX_ENABLED
+#include "modules/regex/regex.h"
+#endif
+#endif
+
Shader::Mode Shader::get_mode() const {
return mode;
}
@@ -121,6 +130,11 @@ void Shader::get_shader_uniform_list(List<PropertyInfo> *p_params, bool p_get_gr
List<PropertyInfo> local;
RenderingServer::get_singleton()->get_shader_parameter_list(shader, &local);
+#ifdef TOOLS_ENABLED
+ DocData::ClassDoc class_doc;
+ class_doc.name = get_path();
+ class_doc.is_script_doc = true;
+#endif
for (PropertyInfo &pi : local) {
bool is_group = pi.usage == PROPERTY_USAGE_GROUP || pi.usage == PROPERTY_USAGE_SUBGROUP;
if (!p_get_groups && is_group) {
@@ -136,9 +150,38 @@ void Shader::get_shader_uniform_list(List<PropertyInfo> *p_params, bool p_get_gr
if (pi.type == Variant::RID) {
pi.type = Variant::OBJECT;
}
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+#ifdef MODULE_REGEX_ENABLED
+ const RegEx pattern("/\\*\\*([^*]|[\\r\\n]|(\\*+([^*/]|[\\r\\n])))*\\*+/\\s*uniform\\s+\\w+\\s+" + pi.name + "(?=[\\s:;=])");
+ Ref<RegExMatch> pattern_ref = pattern.search(code);
+ if (pattern_ref != nullptr) {
+ RegExMatch *match = pattern_ref.ptr();
+ const RegEx pattern_tip("\\/\\*\\*([\\s\\S]*?)\\*/");
+ Ref<RegExMatch> pattern_tip_ref = pattern_tip.search(match->get_string(0));
+ RegExMatch *match_tip = pattern_tip_ref.ptr();
+ const RegEx pattern_stripped("\\n\\s*\\*\\s*");
+ DocData::PropertyDoc prop_doc;
+ prop_doc.name = "shader_parameter/" + pi.name;
+ prop_doc.description = pattern_stripped.sub(match_tip->get_string(1), "\n", true);
+ class_doc.properties.push_back(prop_doc);
+ }
+#else
+ DocData::PropertyDoc prop_doc;
+ prop_doc.name = "shader_parameter/" + pi.name;
+ // prop_doc.description = "(Regex module is not enabled, shader parameter documentation will not be available.)";
+ class_doc.properties.push_back(prop_doc);
+#endif
+ }
+#endif
p_params->push_back(pi);
}
}
+#ifdef TOOLS_ENABLED
+ if (!class_doc.name.is_empty() && p_params) {
+ EditorHelp::get_doc_data()->add_doc(class_doc);
+ }
+#endif
}
RID Shader::get_rid() const {
diff --git a/scu_builders.py b/scu_builders.py
index e6adf6543c..a9ae428222 100644
--- a/scu_builders.py
+++ b/scu_builders.py
@@ -3,6 +3,7 @@
import glob, os
import math
+from methods import print_error
from pathlib import Path
from os.path import normpath, basename
@@ -38,7 +39,7 @@ def find_files_in_folder(folder, sub_folder, include_list, extension, sought_exc
abs_folder = base_folder_path + folder + "/" + sub_folder
if not os.path.isdir(abs_folder):
- print("SCU: ERROR: %s not found." % abs_folder)
+ print_error(f'SCU: "{abs_folder}" not found.')
return include_list, found_exceptions
os.chdir(abs_folder)
@@ -70,7 +71,7 @@ def write_output_file(file_count, include_list, start_line, end_line, output_fol
# create
os.mkdir(output_folder)
if not os.path.isdir(output_folder):
- print("SCU: ERROR: %s could not be created." % output_folder)
+ print_error(f'SCU: "{output_folder}" could not be created.')
return
if _verbose:
print("SCU: Creating folder: %s" % output_folder)
@@ -104,7 +105,7 @@ def write_output_file(file_count, include_list, start_line, end_line, output_fol
def write_exception_output_file(file_count, exception_string, output_folder, output_filename_prefix, extension):
output_folder = os.path.abspath(output_folder)
if not os.path.isdir(output_folder):
- print("SCU: ERROR: %s does not exist." % output_folder)
+ print_error(f"SCU: {output_folder} does not exist.")
return
file_text = exception_string + "\n"
diff --git a/servers/rendering/renderer_rd/shaders/particles.glsl b/servers/rendering/renderer_rd/shaders/particles.glsl
index 5fa4154727..efdf1c2278 100644
--- a/servers/rendering/renderer_rd/shaders/particles.glsl
+++ b/servers/rendering/renderer_rd/shaders/particles.glsl
@@ -612,14 +612,14 @@ void main() {
vec3 uvw_pos = vec3(local_pos_bottom / FRAME.colliders[i].extents) * 0.5 + 0.5;
- float y = 1.0 - texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uvw_pos.xz).r;
+ float y = texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uvw_pos.xz).r;
if (y > uvw_pos.y) {
//inside heightfield
vec3 pos1 = (vec3(uvw_pos.x, y, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents;
- vec3 pos2 = (vec3(uvw_pos.x + DELTA, 1.0 - texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents;
- vec3 pos3 = (vec3(uvw_pos.x, 1.0 - texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * FRAME.colliders[i].extents;
+ vec3 pos2 = (vec3(uvw_pos.x + DELTA, texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents;
+ vec3 pos3 = (vec3(uvw_pos.x, texture(sampler2D(height_field_texture, SAMPLER_LINEAR_CLAMP), uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * FRAME.colliders[i].extents;
normal = normalize(cross(pos1 - pos2, pos1 - pos3));
float local_y = (vec3(local_pos / FRAME.colliders[i].extents) * 0.5 + 0.5).y;
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index f94323f198..8de76c7dbc 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -105,10 +105,6 @@ public:
_changes_changed();
}
-#define DISPLAY_CHANGED \
- changes++; \
- _changes_changed();
-
#else
_FORCE_INLINE_ static void redraw_request() {
changes++;
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 4c44a9b6f6..f8b95cec0a 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -428,6 +428,18 @@ Files extracted from upstream source:
- `jpge*.{c,h}`
+## libbacktrace
+
+- Upstream: https://github.com/ianlancetaylor/libbacktrace
+- Version: git (4d2dd0b172f2c9192f83ba93425f868f2a13c553, 2022)
+- License: BSD-3-Clause
+
+Files extracted from upstream source:
+
+- `*.{c,h}` files for Windows platform
+- `LICENSE`
+
+
## libktx
- Upstream: https://github.com/KhronosGroup/KTX-Software
diff --git a/thirdparty/clipper2/include/clipper2/clipper.core.h b/thirdparty/clipper2/include/clipper2/clipper.core.h
index a77cdad5f4..0de7c3720e 100644
--- a/thirdparty/clipper2/include/clipper2/clipper.core.h
+++ b/thirdparty/clipper2/include/clipper2/clipper.core.h
@@ -138,7 +138,7 @@ namespace Clipper2Lib
}
template <typename T2>
- explicit Point<T>(const Point<T2>& p)
+ explicit Point(const Point<T2>& p)
{
Init(p.x, p.y, p.z);
}
@@ -180,7 +180,7 @@ namespace Clipper2Lib
Point(const T2 x_, const T2 y_) { Init(x_, y_); }
template <typename T2>
- explicit Point<T>(const Point<T2>& p) { Init(p.x, p.y); }
+ explicit Point(const Point<T2>& p) { Init(p.x, p.y); }
Point operator * (const double scale) const
{
diff --git a/thirdparty/clipper2/patches/gcc14-warning.patch b/thirdparty/clipper2/patches/gcc14-warning.patch
new file mode 100644
index 0000000000..a4f06ef37e
--- /dev/null
+++ b/thirdparty/clipper2/patches/gcc14-warning.patch
@@ -0,0 +1,22 @@
+diff --git a/thirdparty/clipper2/include/clipper2/clipper.core.h b/thirdparty/clipper2/include/clipper2/clipper.core.h
+index a77cdad5f4..0de7c3720e 100644
+--- a/thirdparty/clipper2/include/clipper2/clipper.core.h
++++ b/thirdparty/clipper2/include/clipper2/clipper.core.h
+@@ -138,7 +138,7 @@ namespace Clipper2Lib
+ }
+
+ template <typename T2>
+- explicit Point<T>(const Point<T2>& p)
++ explicit Point(const Point<T2>& p)
+ {
+ Init(p.x, p.y, p.z);
+ }
+@@ -180,7 +180,7 @@ namespace Clipper2Lib
+ Point(const T2 x_, const T2 y_) { Init(x_, y_); }
+
+ template <typename T2>
+- explicit Point<T>(const Point<T2>& p) { Init(p.x, p.y); }
++ explicit Point(const Point<T2>& p) { Init(p.x, p.y); }
+
+ Point operator * (const double scale) const
+ {
diff --git a/thirdparty/libbacktrace/LICENSE b/thirdparty/libbacktrace/LICENSE
new file mode 100644
index 0000000000..097d2774e5
--- /dev/null
+++ b/thirdparty/libbacktrace/LICENSE
@@ -0,0 +1,29 @@
+# Copyright (C) 2012-2016 Free Software Foundation, Inc.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+
+# (1) Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+
+# (2) Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+
+# (3) The name of the author may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
diff --git a/thirdparty/libbacktrace/alloc.c b/thirdparty/libbacktrace/alloc.c
new file mode 100644
index 0000000000..ff2c8677c0
--- /dev/null
+++ b/thirdparty/libbacktrace/alloc.c
@@ -0,0 +1,167 @@
+/* alloc.c -- Memory allocation without mmap.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* Allocation routines to use on systems that do not support anonymous
+ mmap. This implementation just uses malloc, which means that the
+ backtrace functions may not be safely invoked from a signal
+ handler. */
+
+/* Allocate memory like malloc. If ERROR_CALLBACK is NULL, don't
+ report an error. */
+
+void *
+backtrace_alloc (struct backtrace_state *state ATTRIBUTE_UNUSED,
+ size_t size, backtrace_error_callback error_callback,
+ void *data)
+{
+ void *ret;
+
+ ret = malloc (size);
+ if (ret == NULL)
+ {
+ if (error_callback)
+ error_callback (data, "malloc", errno);
+ }
+ return ret;
+}
+
+/* Free memory. */
+
+void
+backtrace_free (struct backtrace_state *state ATTRIBUTE_UNUSED,
+ void *p, size_t size ATTRIBUTE_UNUSED,
+ backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
+ void *data ATTRIBUTE_UNUSED)
+{
+ free (p);
+}
+
+/* Grow VEC by SIZE bytes. */
+
+void *
+backtrace_vector_grow (struct backtrace_state *state ATTRIBUTE_UNUSED,
+ size_t size, backtrace_error_callback error_callback,
+ void *data, struct backtrace_vector *vec)
+{
+ void *ret;
+
+ if (size > vec->alc)
+ {
+ size_t alc;
+ void *base;
+
+ if (vec->size == 0)
+ alc = 32 * size;
+ else if (vec->size >= 4096)
+ alc = vec->size + 4096;
+ else
+ alc = 2 * vec->size;
+
+ if (alc < vec->size + size)
+ alc = vec->size + size;
+
+ base = realloc (vec->base, alc);
+ if (base == NULL)
+ {
+ error_callback (data, "realloc", errno);
+ return NULL;
+ }
+
+ vec->base = base;
+ vec->alc = alc - vec->size;
+ }
+
+ ret = (char *) vec->base + vec->size;
+ vec->size += size;
+ vec->alc -= size;
+ return ret;
+}
+
+/* Finish the current allocation on VEC. */
+
+void *
+backtrace_vector_finish (struct backtrace_state *state,
+ struct backtrace_vector *vec,
+ backtrace_error_callback error_callback,
+ void *data)
+{
+ void *ret;
+
+ /* With this allocator we call realloc in backtrace_vector_grow,
+ which means we can't easily reuse the memory here. So just
+ release it. */
+ if (!backtrace_vector_release (state, vec, error_callback, data))
+ return NULL;
+ ret = vec->base;
+ vec->base = NULL;
+ vec->size = 0;
+ vec->alc = 0;
+ return ret;
+}
+
+/* Release any extra space allocated for VEC. */
+
+int
+backtrace_vector_release (struct backtrace_state *state ATTRIBUTE_UNUSED,
+ struct backtrace_vector *vec,
+ backtrace_error_callback error_callback,
+ void *data)
+{
+ vec->alc = 0;
+
+ if (vec->size == 0)
+ {
+ /* As of C17, realloc with size 0 is marked as an obsolescent feature, use
+ free instead. */
+ free (vec->base);
+ vec->base = NULL;
+ return 1;
+ }
+
+ vec->base = realloc (vec->base, vec->size);
+ if (vec->base == NULL)
+ {
+ error_callback (data, "realloc", errno);
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/thirdparty/libbacktrace/atomic.c b/thirdparty/libbacktrace/atomic.c
new file mode 100644
index 0000000000..fcac485b23
--- /dev/null
+++ b/thirdparty/libbacktrace/atomic.c
@@ -0,0 +1,113 @@
+/* atomic.c -- Support for atomic functions if not present.
+ Copyright (C) 2013-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#include "backtrace.h"
+#include "backtrace-supported.h"
+#include "internal.h"
+
+/* This file holds implementations of the atomic functions that are
+ used if the host compiler has the sync functions but not the atomic
+ functions, as is true of versions of GCC before 4.7. */
+
+#if !defined (HAVE_ATOMIC_FUNCTIONS) && defined (HAVE_SYNC_FUNCTIONS)
+
+/* Do an atomic load of a pointer. */
+
+void *
+backtrace_atomic_load_pointer (void *arg)
+{
+ void **pp;
+ void *p;
+
+ pp = (void **) arg;
+ p = *pp;
+ while (!__sync_bool_compare_and_swap (pp, p, p))
+ p = *pp;
+ return p;
+}
+
+/* Do an atomic load of an int. */
+
+int
+backtrace_atomic_load_int (int *p)
+{
+ int i;
+
+ i = *p;
+ while (!__sync_bool_compare_and_swap (p, i, i))
+ i = *p;
+ return i;
+}
+
+/* Do an atomic store of a pointer. */
+
+void
+backtrace_atomic_store_pointer (void *arg, void *p)
+{
+ void **pp;
+ void *old;
+
+ pp = (void **) arg;
+ old = *pp;
+ while (!__sync_bool_compare_and_swap (pp, old, p))
+ old = *pp;
+}
+
+/* Do an atomic store of a size_t value. */
+
+void
+backtrace_atomic_store_size_t (size_t *p, size_t v)
+{
+ size_t old;
+
+ old = *p;
+ while (!__sync_bool_compare_and_swap (p, old, v))
+ old = *p;
+}
+
+/* Do an atomic store of a int value. */
+
+void
+backtrace_atomic_store_int (int *p, int v)
+{
+ size_t old;
+
+ old = *p;
+ while (!__sync_bool_compare_and_swap (p, old, v))
+ old = *p;
+}
+
+#endif
diff --git a/thirdparty/libbacktrace/backtrace-supported.h b/thirdparty/libbacktrace/backtrace-supported.h
new file mode 100644
index 0000000000..f597195f13
--- /dev/null
+++ b/thirdparty/libbacktrace/backtrace-supported.h
@@ -0,0 +1,66 @@
+/* backtrace-supported.h.in -- Whether stack backtrace is supported.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+/* The file backtrace-supported.h.in is used by configure to generate
+ the file backtrace-supported.h. The file backtrace-supported.h may
+ be #include'd to see whether the backtrace library will be able to
+ get a backtrace and produce symbolic information. */
+
+
+/* BACKTRACE_SUPPORTED will be #define'd as 1 if the backtrace library
+ should work, 0 if it will not. Libraries may #include this to make
+ other arrangements. */
+
+#define BACKTRACE_SUPPORTED 1
+
+/* BACKTRACE_USES_MALLOC will be #define'd as 1 if the backtrace
+ library will call malloc as it works, 0 if it will call mmap
+ instead. This may be used to determine whether it is safe to call
+ the backtrace functions from a signal handler. In general this
+ only applies to calls like backtrace and backtrace_pcinfo. It does
+ not apply to backtrace_simple, which never calls malloc. It does
+ not apply to backtrace_print, which always calls fprintf and
+ therefore malloc. */
+
+#define BACKTRACE_USES_MALLOC 1
+
+/* BACKTRACE_SUPPORTS_THREADS will be #define'd as 1 if the backtrace
+ library is configured with threading support, 0 if not. If this is
+ 0, the threaded parameter to backtrace_create_state must be passed
+ as 0. */
+
+#define BACKTRACE_SUPPORTS_THREADS 1
+
+/* BACKTRACE_SUPPORTS_DATA will be #defined'd as 1 if the backtrace_syminfo
+ will work for variables. It will always work for functions. */
+
+#define BACKTRACE_SUPPORTS_DATA 0
diff --git a/thirdparty/libbacktrace/backtrace.c b/thirdparty/libbacktrace/backtrace.c
new file mode 100644
index 0000000000..7b62900852
--- /dev/null
+++ b/thirdparty/libbacktrace/backtrace.c
@@ -0,0 +1,129 @@
+/* backtrace.c -- Entry point for stack backtrace library.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#include "unwind.h"
+#include "backtrace.h"
+#include "internal.h"
+
+/* The main backtrace_full routine. */
+
+/* Data passed through _Unwind_Backtrace. */
+
+struct backtrace_data
+{
+ /* Number of frames to skip. */
+ int skip;
+ /* Library state. */
+ struct backtrace_state *state;
+ /* Callback routine. */
+ backtrace_full_callback callback;
+ /* Error callback routine. */
+ backtrace_error_callback error_callback;
+ /* Data to pass to callback routines. */
+ void *data;
+ /* Value to return from backtrace_full. */
+ int ret;
+ /* Whether there is any memory available. */
+ int can_alloc;
+};
+
+/* Unwind library callback routine. This is passed to
+ _Unwind_Backtrace. */
+
+static _Unwind_Reason_Code
+unwind (struct _Unwind_Context *context, void *vdata)
+{
+ struct backtrace_data *bdata = (struct backtrace_data *) vdata;
+ uintptr_t pc;
+ int ip_before_insn = 0;
+
+#ifdef HAVE_GETIPINFO
+ pc = _Unwind_GetIPInfo (context, &ip_before_insn);
+#else
+ pc = _Unwind_GetIP (context);
+#endif
+
+ if (bdata->skip > 0)
+ {
+ --bdata->skip;
+ return _URC_NO_REASON;
+ }
+
+ if (!ip_before_insn)
+ --pc;
+
+ if (!bdata->can_alloc)
+ bdata->ret = bdata->callback (bdata->data, pc, NULL, 0, NULL);
+ else
+ bdata->ret = backtrace_pcinfo (bdata->state, pc, bdata->callback,
+ bdata->error_callback, bdata->data);
+ if (bdata->ret != 0)
+ return _URC_END_OF_STACK;
+
+ return _URC_NO_REASON;
+}
+
+/* Get a stack backtrace. */
+
+int __attribute__((noinline))
+backtrace_full (struct backtrace_state *state, int skip,
+ backtrace_full_callback callback,
+ backtrace_error_callback error_callback, void *data)
+{
+ struct backtrace_data bdata;
+ void *p;
+
+ bdata.skip = skip + 1;
+ bdata.state = state;
+ bdata.callback = callback;
+ bdata.error_callback = error_callback;
+ bdata.data = data;
+ bdata.ret = 0;
+
+ /* If we can't allocate any memory at all, don't try to produce
+ file/line information. */
+ p = backtrace_alloc (state, 4096, NULL, NULL);
+ if (p == NULL)
+ bdata.can_alloc = 0;
+ else
+ {
+ backtrace_free (state, p, 4096, NULL, NULL);
+ bdata.can_alloc = 1;
+ }
+
+ _Unwind_Backtrace (unwind, &bdata);
+ return bdata.ret;
+}
diff --git a/thirdparty/libbacktrace/backtrace.h b/thirdparty/libbacktrace/backtrace.h
new file mode 100644
index 0000000000..69cea4ca1e
--- /dev/null
+++ b/thirdparty/libbacktrace/backtrace.h
@@ -0,0 +1,189 @@
+/* backtrace.h -- Public header file for stack backtrace library.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#ifndef BACKTRACE_H
+#define BACKTRACE_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The backtrace state. This struct is intentionally not defined in
+ the public interface. */
+
+struct backtrace_state;
+
+/* The type of the error callback argument to backtrace functions.
+ This function, if not NULL, will be called for certain error cases.
+ The DATA argument is passed to the function that calls this one.
+ The MSG argument is an error message. The ERRNUM argument, if
+ greater than 0, holds an errno value. The MSG buffer may become
+ invalid after this function returns.
+
+ As a special case, the ERRNUM argument will be passed as -1 if no
+ debug info can be found for the executable, or if the debug info
+ exists but has an unsupported version, but the function requires
+ debug info (e.g., backtrace_full, backtrace_pcinfo). The MSG in
+ this case will be something along the lines of "no debug info".
+ Similarly, ERRNUM will be passed as -1 if there is no symbol table,
+ but the function requires a symbol table (e.g., backtrace_syminfo).
+ This may be used as a signal that some other approach should be
+ tried. */
+
+typedef void (*backtrace_error_callback) (void *data, const char *msg,
+ int errnum);
+
+/* Create state information for the backtrace routines. This must be
+ called before any of the other routines, and its return value must
+ be passed to all of the other routines. FILENAME is the path name
+ of the executable file; if it is NULL the library will try
+ system-specific path names. If not NULL, FILENAME must point to a
+ permanent buffer. If THREADED is non-zero the state may be
+ accessed by multiple threads simultaneously, and the library will
+ use appropriate atomic operations. If THREADED is zero the state
+ may only be accessed by one thread at a time. This returns a state
+ pointer on success, NULL on error. If an error occurs, this will
+ call the ERROR_CALLBACK routine.
+
+ Calling this function allocates resources that cannot be freed.
+ There is no backtrace_free_state function. The state is used to
+ cache information that is expensive to recompute. Programs are
+ expected to call this function at most once and to save the return
+ value for all later calls to backtrace functions. */
+
+extern struct backtrace_state *backtrace_create_state (
+ const char *filename, int threaded,
+ backtrace_error_callback error_callback, void *data);
+
+/* The type of the callback argument to the backtrace_full function.
+ DATA is the argument passed to backtrace_full. PC is the program
+ counter. FILENAME is the name of the file containing PC, or NULL
+ if not available. LINENO is the line number in FILENAME containing
+ PC, or 0 if not available. FUNCTION is the name of the function
+ containing PC, or NULL if not available. This should return 0 to
+ continuing tracing. The FILENAME and FUNCTION buffers may become
+ invalid after this function returns. */
+
+typedef int (*backtrace_full_callback) (void *data, uintptr_t pc,
+ const char *filename, int lineno,
+ const char *function);
+
+/* Get a full stack backtrace. SKIP is the number of frames to skip;
+ passing 0 will start the trace with the function calling
+ backtrace_full. DATA is passed to the callback routine. If any
+ call to CALLBACK returns a non-zero value, the stack backtrace
+ stops, and backtrace returns that value; this may be used to limit
+ the number of stack frames desired. If all calls to CALLBACK
+ return 0, backtrace returns 0. The backtrace_full function will
+ make at least one call to either CALLBACK or ERROR_CALLBACK. This
+ function requires debug info for the executable. */
+
+extern int backtrace_full (struct backtrace_state *state, int skip,
+ backtrace_full_callback callback,
+ backtrace_error_callback error_callback,
+ void *data);
+
+/* The type of the callback argument to the backtrace_simple function.
+ DATA is the argument passed to simple_backtrace. PC is the program
+ counter. This should return 0 to continue tracing. */
+
+typedef int (*backtrace_simple_callback) (void *data, uintptr_t pc);
+
+/* Get a simple backtrace. SKIP is the number of frames to skip, as
+ in backtrace. DATA is passed to the callback routine. If any call
+ to CALLBACK returns a non-zero value, the stack backtrace stops,
+ and backtrace_simple returns that value. Otherwise
+ backtrace_simple returns 0. The backtrace_simple function will
+ make at least one call to either CALLBACK or ERROR_CALLBACK. This
+ function does not require any debug info for the executable. */
+
+extern int backtrace_simple (struct backtrace_state *state, int skip,
+ backtrace_simple_callback callback,
+ backtrace_error_callback error_callback,
+ void *data);
+
+/* Print the current backtrace in a user readable format to a FILE.
+ SKIP is the number of frames to skip, as in backtrace_full. Any
+ error messages are printed to stderr. This function requires debug
+ info for the executable. */
+
+extern void backtrace_print (struct backtrace_state *state, int skip, FILE *);
+
+/* Given PC, a program counter in the current program, call the
+ callback function with filename, line number, and function name
+ information. This will normally call the callback function exactly
+ once. However, if the PC happens to describe an inlined call, and
+ the debugging information contains the necessary information, then
+ this may call the callback function multiple times. This will make
+ at least one call to either CALLBACK or ERROR_CALLBACK. This
+ returns the first non-zero value returned by CALLBACK, or 0. */
+
+extern int backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc,
+ backtrace_full_callback callback,
+ backtrace_error_callback error_callback,
+ void *data);
+
+/* The type of the callback argument to backtrace_syminfo. DATA and
+ PC are the arguments passed to backtrace_syminfo. SYMNAME is the
+ name of the symbol for the corresponding code. SYMVAL is the
+ value and SYMSIZE is the size of the symbol. SYMNAME will be NULL
+ if no error occurred but the symbol could not be found. */
+
+typedef void (*backtrace_syminfo_callback) (void *data, uintptr_t pc,
+ const char *symname,
+ uintptr_t symval,
+ uintptr_t symsize);
+
+/* Given ADDR, an address or program counter in the current program,
+ call the callback information with the symbol name and value
+ describing the function or variable in which ADDR may be found.
+ This will call either CALLBACK or ERROR_CALLBACK exactly once.
+ This returns 1 on success, 0 on failure. This function requires
+ the symbol table but does not require the debug info. Note that if
+ the symbol table is present but ADDR could not be found in the
+ table, CALLBACK will be called with a NULL SYMNAME argument.
+ Returns 1 on success, 0 on error. */
+
+extern int backtrace_syminfo (struct backtrace_state *state, uintptr_t addr,
+ backtrace_syminfo_callback callback,
+ backtrace_error_callback error_callback,
+ void *data);
+
+#ifdef __cplusplus
+} /* End extern "C". */
+#endif
+
+#endif
diff --git a/thirdparty/libbacktrace/config.h b/thirdparty/libbacktrace/config.h
new file mode 100644
index 0000000000..0c745c191b
--- /dev/null
+++ b/thirdparty/libbacktrace/config.h
@@ -0,0 +1,170 @@
+/* config.h. Generated from config.h.in by configure. */
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* ELF size: 32 or 64 */
+#define BACKTRACE_ELF_SIZE unused
+
+/* XCOFF size: 32 or 64 */
+#define BACKTRACE_XCOFF_SIZE unused
+
+/* Define to 1 if you have the __atomic functions */
+#define HAVE_ATOMIC_FUNCTIONS 1
+
+/* Define to 1 if you have the `clock_gettime' function. */
+#define HAVE_CLOCK_GETTIME 1
+
+/* Define to 1 if you have the declaration of `getpagesize', and to 0 if you
+ don't. */
+#define HAVE_DECL_GETPAGESIZE 0
+
+/* Define to 1 if you have the declaration of `strnlen', and to 0 if you
+ don't. */
+#define HAVE_DECL_STRNLEN 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+/* #undef HAVE_DLFCN_H */
+
+/* Define if dl_iterate_phdr is available. */
+/* #undef HAVE_DL_ITERATE_PHDR */
+
+/* Define to 1 if you have the fcntl function */
+/* #undef HAVE_FCNTL */
+
+/* Define if getexecname is available. */
+/* #undef HAVE_GETEXECNAME */
+
+/* Define if _Unwind_GetIPInfo is available. */
+#define HAVE_GETIPINFO 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have KERN_PROC and KERN_PROC_PATHNAME in <sys/sysctl.h>.
+ */
+/* #undef HAVE_KERN_PROC */
+
+/* Define to 1 if you have KERN_PROCARGS and KERN_PROC_PATHNAME in
+ <sys/sysctl.h>. */
+/* #undef HAVE_KERN_PROC_ARGS */
+
+/* Define if -llzma is available. */
+#define HAVE_LIBLZMA 1
+
+/* Define to 1 if you have the <link.h> header file. */
+/* #undef HAVE_LINK_H */
+
+/* Define if AIX loadquery is available. */
+/* #undef HAVE_LOADQUERY */
+
+/* Define to 1 if you have the `lstat' function. */
+/* #undef HAVE_LSTAT */
+
+/* Define to 1 if you have the <mach-o/dyld.h> header file. */
+/* #undef HAVE_MACH_O_DYLD_H */
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `readlink' function. */
+/* #undef HAVE_READLINK */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the __sync functions */
+#define HAVE_SYNC_FUNCTIONS 1
+
+/* Define to 1 if you have the <sys/ldr.h> header file. */
+/* #undef HAVE_SYS_LDR_H */
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+/* #undef HAVE_SYS_MMAN_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define if -lz is available. */
+#define HAVE_ZLIB 1
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+ */
+#define LT_OBJDIR ".libs/"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "package-unused"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "package-unused version-unused"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libbacktrace"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "version-unused"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Enable extensions on AIX 3, Interix. */
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+/* Enable threading extensions on Solaris. */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+/* Enable extensions on HP NonStop. */
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+
+
+/* Enable large inode numbers on Mac OS X 10.5. */
+#ifndef _DARWIN_USE_64_BIT_INODE
+# define _DARWIN_USE_64_BIT_INODE 1
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#define _FILE_OFFSET_BITS 64
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to 1 if on MINIX. */
+/* #undef _MINIX */
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+ this defined. */
+/* #undef _POSIX_1_SOURCE */
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+/* #undef _POSIX_SOURCE */
diff --git a/thirdparty/libbacktrace/dwarf.c b/thirdparty/libbacktrace/dwarf.c
new file mode 100644
index 0000000000..5b2724e6a7
--- /dev/null
+++ b/thirdparty/libbacktrace/dwarf.c
@@ -0,0 +1,4402 @@
+/* dwarf.c -- Get file/line information from DWARF for backtraces.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "filenames.h"
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* DWARF constants. */
+
+enum dwarf_tag {
+ DW_TAG_entry_point = 0x3,
+ DW_TAG_compile_unit = 0x11,
+ DW_TAG_inlined_subroutine = 0x1d,
+ DW_TAG_subprogram = 0x2e,
+ DW_TAG_skeleton_unit = 0x4a,
+};
+
+enum dwarf_form {
+ DW_FORM_addr = 0x01,
+ DW_FORM_block2 = 0x03,
+ DW_FORM_block4 = 0x04,
+ DW_FORM_data2 = 0x05,
+ DW_FORM_data4 = 0x06,
+ DW_FORM_data8 = 0x07,
+ DW_FORM_string = 0x08,
+ DW_FORM_block = 0x09,
+ DW_FORM_block1 = 0x0a,
+ DW_FORM_data1 = 0x0b,
+ DW_FORM_flag = 0x0c,
+ DW_FORM_sdata = 0x0d,
+ DW_FORM_strp = 0x0e,
+ DW_FORM_udata = 0x0f,
+ DW_FORM_ref_addr = 0x10,
+ DW_FORM_ref1 = 0x11,
+ DW_FORM_ref2 = 0x12,
+ DW_FORM_ref4 = 0x13,
+ DW_FORM_ref8 = 0x14,
+ DW_FORM_ref_udata = 0x15,
+ DW_FORM_indirect = 0x16,
+ DW_FORM_sec_offset = 0x17,
+ DW_FORM_exprloc = 0x18,
+ DW_FORM_flag_present = 0x19,
+ DW_FORM_ref_sig8 = 0x20,
+ DW_FORM_strx = 0x1a,
+ DW_FORM_addrx = 0x1b,
+ DW_FORM_ref_sup4 = 0x1c,
+ DW_FORM_strp_sup = 0x1d,
+ DW_FORM_data16 = 0x1e,
+ DW_FORM_line_strp = 0x1f,
+ DW_FORM_implicit_const = 0x21,
+ DW_FORM_loclistx = 0x22,
+ DW_FORM_rnglistx = 0x23,
+ DW_FORM_ref_sup8 = 0x24,
+ DW_FORM_strx1 = 0x25,
+ DW_FORM_strx2 = 0x26,
+ DW_FORM_strx3 = 0x27,
+ DW_FORM_strx4 = 0x28,
+ DW_FORM_addrx1 = 0x29,
+ DW_FORM_addrx2 = 0x2a,
+ DW_FORM_addrx3 = 0x2b,
+ DW_FORM_addrx4 = 0x2c,
+ DW_FORM_GNU_addr_index = 0x1f01,
+ DW_FORM_GNU_str_index = 0x1f02,
+ DW_FORM_GNU_ref_alt = 0x1f20,
+ DW_FORM_GNU_strp_alt = 0x1f21
+};
+
+enum dwarf_attribute {
+ DW_AT_sibling = 0x01,
+ DW_AT_location = 0x02,
+ DW_AT_name = 0x03,
+ DW_AT_ordering = 0x09,
+ DW_AT_subscr_data = 0x0a,
+ DW_AT_byte_size = 0x0b,
+ DW_AT_bit_offset = 0x0c,
+ DW_AT_bit_size = 0x0d,
+ DW_AT_element_list = 0x0f,
+ DW_AT_stmt_list = 0x10,
+ DW_AT_low_pc = 0x11,
+ DW_AT_high_pc = 0x12,
+ DW_AT_language = 0x13,
+ DW_AT_member = 0x14,
+ DW_AT_discr = 0x15,
+ DW_AT_discr_value = 0x16,
+ DW_AT_visibility = 0x17,
+ DW_AT_import = 0x18,
+ DW_AT_string_length = 0x19,
+ DW_AT_common_reference = 0x1a,
+ DW_AT_comp_dir = 0x1b,
+ DW_AT_const_value = 0x1c,
+ DW_AT_containing_type = 0x1d,
+ DW_AT_default_value = 0x1e,
+ DW_AT_inline = 0x20,
+ DW_AT_is_optional = 0x21,
+ DW_AT_lower_bound = 0x22,
+ DW_AT_producer = 0x25,
+ DW_AT_prototyped = 0x27,
+ DW_AT_return_addr = 0x2a,
+ DW_AT_start_scope = 0x2c,
+ DW_AT_bit_stride = 0x2e,
+ DW_AT_upper_bound = 0x2f,
+ DW_AT_abstract_origin = 0x31,
+ DW_AT_accessibility = 0x32,
+ DW_AT_address_class = 0x33,
+ DW_AT_artificial = 0x34,
+ DW_AT_base_types = 0x35,
+ DW_AT_calling_convention = 0x36,
+ DW_AT_count = 0x37,
+ DW_AT_data_member_location = 0x38,
+ DW_AT_decl_column = 0x39,
+ DW_AT_decl_file = 0x3a,
+ DW_AT_decl_line = 0x3b,
+ DW_AT_declaration = 0x3c,
+ DW_AT_discr_list = 0x3d,
+ DW_AT_encoding = 0x3e,
+ DW_AT_external = 0x3f,
+ DW_AT_frame_base = 0x40,
+ DW_AT_friend = 0x41,
+ DW_AT_identifier_case = 0x42,
+ DW_AT_macro_info = 0x43,
+ DW_AT_namelist_items = 0x44,
+ DW_AT_priority = 0x45,
+ DW_AT_segment = 0x46,
+ DW_AT_specification = 0x47,
+ DW_AT_static_link = 0x48,
+ DW_AT_type = 0x49,
+ DW_AT_use_location = 0x4a,
+ DW_AT_variable_parameter = 0x4b,
+ DW_AT_virtuality = 0x4c,
+ DW_AT_vtable_elem_location = 0x4d,
+ DW_AT_allocated = 0x4e,
+ DW_AT_associated = 0x4f,
+ DW_AT_data_location = 0x50,
+ DW_AT_byte_stride = 0x51,
+ DW_AT_entry_pc = 0x52,
+ DW_AT_use_UTF8 = 0x53,
+ DW_AT_extension = 0x54,
+ DW_AT_ranges = 0x55,
+ DW_AT_trampoline = 0x56,
+ DW_AT_call_column = 0x57,
+ DW_AT_call_file = 0x58,
+ DW_AT_call_line = 0x59,
+ DW_AT_description = 0x5a,
+ DW_AT_binary_scale = 0x5b,
+ DW_AT_decimal_scale = 0x5c,
+ DW_AT_small = 0x5d,
+ DW_AT_decimal_sign = 0x5e,
+ DW_AT_digit_count = 0x5f,
+ DW_AT_picture_string = 0x60,
+ DW_AT_mutable = 0x61,
+ DW_AT_threads_scaled = 0x62,
+ DW_AT_explicit = 0x63,
+ DW_AT_object_pointer = 0x64,
+ DW_AT_endianity = 0x65,
+ DW_AT_elemental = 0x66,
+ DW_AT_pure = 0x67,
+ DW_AT_recursive = 0x68,
+ DW_AT_signature = 0x69,
+ DW_AT_main_subprogram = 0x6a,
+ DW_AT_data_bit_offset = 0x6b,
+ DW_AT_const_expr = 0x6c,
+ DW_AT_enum_class = 0x6d,
+ DW_AT_linkage_name = 0x6e,
+ DW_AT_string_length_bit_size = 0x6f,
+ DW_AT_string_length_byte_size = 0x70,
+ DW_AT_rank = 0x71,
+ DW_AT_str_offsets_base = 0x72,
+ DW_AT_addr_base = 0x73,
+ DW_AT_rnglists_base = 0x74,
+ DW_AT_dwo_name = 0x76,
+ DW_AT_reference = 0x77,
+ DW_AT_rvalue_reference = 0x78,
+ DW_AT_macros = 0x79,
+ DW_AT_call_all_calls = 0x7a,
+ DW_AT_call_all_source_calls = 0x7b,
+ DW_AT_call_all_tail_calls = 0x7c,
+ DW_AT_call_return_pc = 0x7d,
+ DW_AT_call_value = 0x7e,
+ DW_AT_call_origin = 0x7f,
+ DW_AT_call_parameter = 0x80,
+ DW_AT_call_pc = 0x81,
+ DW_AT_call_tail_call = 0x82,
+ DW_AT_call_target = 0x83,
+ DW_AT_call_target_clobbered = 0x84,
+ DW_AT_call_data_location = 0x85,
+ DW_AT_call_data_value = 0x86,
+ DW_AT_noreturn = 0x87,
+ DW_AT_alignment = 0x88,
+ DW_AT_export_symbols = 0x89,
+ DW_AT_deleted = 0x8a,
+ DW_AT_defaulted = 0x8b,
+ DW_AT_loclists_base = 0x8c,
+ DW_AT_lo_user = 0x2000,
+ DW_AT_hi_user = 0x3fff,
+ DW_AT_MIPS_fde = 0x2001,
+ DW_AT_MIPS_loop_begin = 0x2002,
+ DW_AT_MIPS_tail_loop_begin = 0x2003,
+ DW_AT_MIPS_epilog_begin = 0x2004,
+ DW_AT_MIPS_loop_unroll_factor = 0x2005,
+ DW_AT_MIPS_software_pipeline_depth = 0x2006,
+ DW_AT_MIPS_linkage_name = 0x2007,
+ DW_AT_MIPS_stride = 0x2008,
+ DW_AT_MIPS_abstract_name = 0x2009,
+ DW_AT_MIPS_clone_origin = 0x200a,
+ DW_AT_MIPS_has_inlines = 0x200b,
+ DW_AT_HP_block_index = 0x2000,
+ DW_AT_HP_unmodifiable = 0x2001,
+ DW_AT_HP_prologue = 0x2005,
+ DW_AT_HP_epilogue = 0x2008,
+ DW_AT_HP_actuals_stmt_list = 0x2010,
+ DW_AT_HP_proc_per_section = 0x2011,
+ DW_AT_HP_raw_data_ptr = 0x2012,
+ DW_AT_HP_pass_by_reference = 0x2013,
+ DW_AT_HP_opt_level = 0x2014,
+ DW_AT_HP_prof_version_id = 0x2015,
+ DW_AT_HP_opt_flags = 0x2016,
+ DW_AT_HP_cold_region_low_pc = 0x2017,
+ DW_AT_HP_cold_region_high_pc = 0x2018,
+ DW_AT_HP_all_variables_modifiable = 0x2019,
+ DW_AT_HP_linkage_name = 0x201a,
+ DW_AT_HP_prof_flags = 0x201b,
+ DW_AT_HP_unit_name = 0x201f,
+ DW_AT_HP_unit_size = 0x2020,
+ DW_AT_HP_widened_byte_size = 0x2021,
+ DW_AT_HP_definition_points = 0x2022,
+ DW_AT_HP_default_location = 0x2023,
+ DW_AT_HP_is_result_param = 0x2029,
+ DW_AT_sf_names = 0x2101,
+ DW_AT_src_info = 0x2102,
+ DW_AT_mac_info = 0x2103,
+ DW_AT_src_coords = 0x2104,
+ DW_AT_body_begin = 0x2105,
+ DW_AT_body_end = 0x2106,
+ DW_AT_GNU_vector = 0x2107,
+ DW_AT_GNU_guarded_by = 0x2108,
+ DW_AT_GNU_pt_guarded_by = 0x2109,
+ DW_AT_GNU_guarded = 0x210a,
+ DW_AT_GNU_pt_guarded = 0x210b,
+ DW_AT_GNU_locks_excluded = 0x210c,
+ DW_AT_GNU_exclusive_locks_required = 0x210d,
+ DW_AT_GNU_shared_locks_required = 0x210e,
+ DW_AT_GNU_odr_signature = 0x210f,
+ DW_AT_GNU_template_name = 0x2110,
+ DW_AT_GNU_call_site_value = 0x2111,
+ DW_AT_GNU_call_site_data_value = 0x2112,
+ DW_AT_GNU_call_site_target = 0x2113,
+ DW_AT_GNU_call_site_target_clobbered = 0x2114,
+ DW_AT_GNU_tail_call = 0x2115,
+ DW_AT_GNU_all_tail_call_sites = 0x2116,
+ DW_AT_GNU_all_call_sites = 0x2117,
+ DW_AT_GNU_all_source_call_sites = 0x2118,
+ DW_AT_GNU_macros = 0x2119,
+ DW_AT_GNU_deleted = 0x211a,
+ DW_AT_GNU_dwo_name = 0x2130,
+ DW_AT_GNU_dwo_id = 0x2131,
+ DW_AT_GNU_ranges_base = 0x2132,
+ DW_AT_GNU_addr_base = 0x2133,
+ DW_AT_GNU_pubnames = 0x2134,
+ DW_AT_GNU_pubtypes = 0x2135,
+ DW_AT_GNU_discriminator = 0x2136,
+ DW_AT_GNU_locviews = 0x2137,
+ DW_AT_GNU_entry_view = 0x2138,
+ DW_AT_VMS_rtnbeg_pd_address = 0x2201,
+ DW_AT_use_GNAT_descriptive_type = 0x2301,
+ DW_AT_GNAT_descriptive_type = 0x2302,
+ DW_AT_GNU_numerator = 0x2303,
+ DW_AT_GNU_denominator = 0x2304,
+ DW_AT_GNU_bias = 0x2305,
+ DW_AT_upc_threads_scaled = 0x3210,
+ DW_AT_PGI_lbase = 0x3a00,
+ DW_AT_PGI_soffset = 0x3a01,
+ DW_AT_PGI_lstride = 0x3a02,
+ DW_AT_APPLE_optimized = 0x3fe1,
+ DW_AT_APPLE_flags = 0x3fe2,
+ DW_AT_APPLE_isa = 0x3fe3,
+ DW_AT_APPLE_block = 0x3fe4,
+ DW_AT_APPLE_major_runtime_vers = 0x3fe5,
+ DW_AT_APPLE_runtime_class = 0x3fe6,
+ DW_AT_APPLE_omit_frame_ptr = 0x3fe7,
+ DW_AT_APPLE_property_name = 0x3fe8,
+ DW_AT_APPLE_property_getter = 0x3fe9,
+ DW_AT_APPLE_property_setter = 0x3fea,
+ DW_AT_APPLE_property_attribute = 0x3feb,
+ DW_AT_APPLE_objc_complete_type = 0x3fec,
+ DW_AT_APPLE_property = 0x3fed
+};
+
+enum dwarf_line_number_op {
+ DW_LNS_extended_op = 0x0,
+ DW_LNS_copy = 0x1,
+ DW_LNS_advance_pc = 0x2,
+ DW_LNS_advance_line = 0x3,
+ DW_LNS_set_file = 0x4,
+ DW_LNS_set_column = 0x5,
+ DW_LNS_negate_stmt = 0x6,
+ DW_LNS_set_basic_block = 0x7,
+ DW_LNS_const_add_pc = 0x8,
+ DW_LNS_fixed_advance_pc = 0x9,
+ DW_LNS_set_prologue_end = 0xa,
+ DW_LNS_set_epilogue_begin = 0xb,
+ DW_LNS_set_isa = 0xc,
+};
+
+enum dwarf_extended_line_number_op {
+ DW_LNE_end_sequence = 0x1,
+ DW_LNE_set_address = 0x2,
+ DW_LNE_define_file = 0x3,
+ DW_LNE_set_discriminator = 0x4,
+};
+
+enum dwarf_line_number_content_type {
+ DW_LNCT_path = 0x1,
+ DW_LNCT_directory_index = 0x2,
+ DW_LNCT_timestamp = 0x3,
+ DW_LNCT_size = 0x4,
+ DW_LNCT_MD5 = 0x5,
+ DW_LNCT_lo_user = 0x2000,
+ DW_LNCT_hi_user = 0x3fff
+};
+
+enum dwarf_range_list_entry {
+ DW_RLE_end_of_list = 0x00,
+ DW_RLE_base_addressx = 0x01,
+ DW_RLE_startx_endx = 0x02,
+ DW_RLE_startx_length = 0x03,
+ DW_RLE_offset_pair = 0x04,
+ DW_RLE_base_address = 0x05,
+ DW_RLE_start_end = 0x06,
+ DW_RLE_start_length = 0x07
+};
+
+enum dwarf_unit_type {
+ DW_UT_compile = 0x01,
+ DW_UT_type = 0x02,
+ DW_UT_partial = 0x03,
+ DW_UT_skeleton = 0x04,
+ DW_UT_split_compile = 0x05,
+ DW_UT_split_type = 0x06,
+ DW_UT_lo_user = 0x80,
+ DW_UT_hi_user = 0xff
+};
+
+#if !defined(HAVE_DECL_STRNLEN) || !HAVE_DECL_STRNLEN
+
+/* If strnlen is not declared, provide our own version. */
+
+static size_t
+xstrnlen (const char *s, size_t maxlen)
+{
+ size_t i;
+
+ for (i = 0; i < maxlen; ++i)
+ if (s[i] == '\0')
+ break;
+ return i;
+}
+
+#define strnlen xstrnlen
+
+#endif
+
+/* A buffer to read DWARF info. */
+
+struct dwarf_buf
+{
+ /* Buffer name for error messages. */
+ const char *name;
+ /* Start of the buffer. */
+ const unsigned char *start;
+ /* Next byte to read. */
+ const unsigned char *buf;
+ /* The number of bytes remaining. */
+ size_t left;
+ /* Whether the data is big-endian. */
+ int is_bigendian;
+ /* Error callback routine. */
+ backtrace_error_callback error_callback;
+ /* Data for error_callback. */
+ void *data;
+ /* Non-zero if we've reported an underflow error. */
+ int reported_underflow;
+};
+
+/* A single attribute in a DWARF abbreviation. */
+
+struct attr
+{
+ /* The attribute name. */
+ enum dwarf_attribute name;
+ /* The attribute form. */
+ enum dwarf_form form;
+ /* The attribute value, for DW_FORM_implicit_const. */
+ int64_t val;
+};
+
+/* A single DWARF abbreviation. */
+
+struct abbrev
+{
+ /* The abbrev code--the number used to refer to the abbrev. */
+ uint64_t code;
+ /* The entry tag. */
+ enum dwarf_tag tag;
+ /* Non-zero if this abbrev has child entries. */
+ int has_children;
+ /* The number of attributes. */
+ size_t num_attrs;
+ /* The attributes. */
+ struct attr *attrs;
+};
+
+/* The DWARF abbreviations for a compilation unit. This structure
+ only exists while reading the compilation unit. Most DWARF readers
+ seem to a hash table to map abbrev ID's to abbrev entries.
+ However, we primarily care about GCC, and GCC simply issues ID's in
+ numerical order starting at 1. So we simply keep a sorted vector,
+ and try to just look up the code. */
+
+struct abbrevs
+{
+ /* The number of abbrevs in the vector. */
+ size_t num_abbrevs;
+ /* The abbrevs, sorted by the code field. */
+ struct abbrev *abbrevs;
+};
+
+/* The different kinds of attribute values. */
+
+enum attr_val_encoding
+{
+ /* No attribute value. */
+ ATTR_VAL_NONE,
+ /* An address. */
+ ATTR_VAL_ADDRESS,
+ /* An index into the .debug_addr section, whose value is relative to
+ * the DW_AT_addr_base attribute of the compilation unit. */
+ ATTR_VAL_ADDRESS_INDEX,
+ /* A unsigned integer. */
+ ATTR_VAL_UINT,
+ /* A sigd integer. */
+ ATTR_VAL_SINT,
+ /* A string. */
+ ATTR_VAL_STRING,
+ /* An index into the .debug_str_offsets section. */
+ ATTR_VAL_STRING_INDEX,
+ /* An offset to other data in the containing unit. */
+ ATTR_VAL_REF_UNIT,
+ /* An offset to other data within the .debug_info section. */
+ ATTR_VAL_REF_INFO,
+ /* An offset to other data within the alt .debug_info section. */
+ ATTR_VAL_REF_ALT_INFO,
+ /* An offset to data in some other section. */
+ ATTR_VAL_REF_SECTION,
+ /* A type signature. */
+ ATTR_VAL_REF_TYPE,
+ /* An index into the .debug_rnglists section. */
+ ATTR_VAL_RNGLISTS_INDEX,
+ /* A block of data (not represented). */
+ ATTR_VAL_BLOCK,
+ /* An expression (not represented). */
+ ATTR_VAL_EXPR,
+};
+
+/* An attribute value. */
+
+struct attr_val
+{
+ /* How the value is stored in the field u. */
+ enum attr_val_encoding encoding;
+ union
+ {
+ /* ATTR_VAL_ADDRESS*, ATTR_VAL_UINT, ATTR_VAL_REF*. */
+ uint64_t uint;
+ /* ATTR_VAL_SINT. */
+ int64_t sint;
+ /* ATTR_VAL_STRING. */
+ const char *string;
+ /* ATTR_VAL_BLOCK not stored. */
+ } u;
+};
+
+/* The line number program header. */
+
+struct line_header
+{
+ /* The version of the line number information. */
+ int version;
+ /* Address size. */
+ int addrsize;
+ /* The minimum instruction length. */
+ unsigned int min_insn_len;
+ /* The maximum number of ops per instruction. */
+ unsigned int max_ops_per_insn;
+ /* The line base for special opcodes. */
+ int line_base;
+ /* The line range for special opcodes. */
+ unsigned int line_range;
+ /* The opcode base--the first special opcode. */
+ unsigned int opcode_base;
+ /* Opcode lengths, indexed by opcode - 1. */
+ const unsigned char *opcode_lengths;
+ /* The number of directory entries. */
+ size_t dirs_count;
+ /* The directory entries. */
+ const char **dirs;
+ /* The number of filenames. */
+ size_t filenames_count;
+ /* The filenames. */
+ const char **filenames;
+};
+
+/* A format description from a line header. */
+
+struct line_header_format
+{
+ int lnct; /* LNCT code. */
+ enum dwarf_form form; /* Form of entry data. */
+};
+
+/* Map a single PC value to a file/line. We will keep a vector of
+ these sorted by PC value. Each file/line will be correct from the
+ PC up to the PC of the next entry if there is one. We allocate one
+ extra entry at the end so that we can use bsearch. */
+
+struct line
+{
+ /* PC. */
+ uintptr_t pc;
+ /* File name. Many entries in the array are expected to point to
+ the same file name. */
+ const char *filename;
+ /* Line number. */
+ int lineno;
+ /* Index of the object in the original array read from the DWARF
+ section, before it has been sorted. The index makes it possible
+ to use Quicksort and maintain stability. */
+ int idx;
+};
+
+/* A growable vector of line number information. This is used while
+ reading the line numbers. */
+
+struct line_vector
+{
+ /* Memory. This is an array of struct line. */
+ struct backtrace_vector vec;
+ /* Number of valid mappings. */
+ size_t count;
+};
+
+/* A function described in the debug info. */
+
+struct function
+{
+ /* The name of the function. */
+ const char *name;
+ /* If this is an inlined function, the filename of the call
+ site. */
+ const char *caller_filename;
+ /* If this is an inlined function, the line number of the call
+ site. */
+ int caller_lineno;
+ /* Map PC ranges to inlined functions. */
+ struct function_addrs *function_addrs;
+ size_t function_addrs_count;
+};
+
+/* An address range for a function. This maps a PC value to a
+ specific function. */
+
+struct function_addrs
+{
+ /* Range is LOW <= PC < HIGH. */
+ uint64_t low;
+ uint64_t high;
+ /* Function for this address range. */
+ struct function *function;
+};
+
+/* A growable vector of function address ranges. */
+
+struct function_vector
+{
+ /* Memory. This is an array of struct function_addrs. */
+ struct backtrace_vector vec;
+ /* Number of address ranges present. */
+ size_t count;
+};
+
+/* A DWARF compilation unit. This only holds the information we need
+ to map a PC to a file and line. */
+
+struct unit
+{
+ /* The first entry for this compilation unit. */
+ const unsigned char *unit_data;
+ /* The length of the data for this compilation unit. */
+ size_t unit_data_len;
+ /* The offset of UNIT_DATA from the start of the information for
+ this compilation unit. */
+ size_t unit_data_offset;
+ /* Offset of the start of the compilation unit from the start of the
+ .debug_info section. */
+ size_t low_offset;
+ /* Offset of the end of the compilation unit from the start of the
+ .debug_info section. */
+ size_t high_offset;
+ /* DWARF version. */
+ int version;
+ /* Whether unit is DWARF64. */
+ int is_dwarf64;
+ /* Address size. */
+ int addrsize;
+ /* Offset into line number information. */
+ off_t lineoff;
+ /* Offset of compilation unit in .debug_str_offsets. */
+ uint64_t str_offsets_base;
+ /* Offset of compilation unit in .debug_addr. */
+ uint64_t addr_base;
+ /* Offset of compilation unit in .debug_rnglists. */
+ uint64_t rnglists_base;
+ /* Primary source file. */
+ const char *filename;
+ /* Compilation command working directory. */
+ const char *comp_dir;
+ /* Absolute file name, only set if needed. */
+ const char *abs_filename;
+ /* The abbreviations for this unit. */
+ struct abbrevs abbrevs;
+
+ /* The fields above this point are read in during initialization and
+ may be accessed freely. The fields below this point are read in
+ as needed, and therefore require care, as different threads may
+ try to initialize them simultaneously. */
+
+ /* PC to line number mapping. This is NULL if the values have not
+ been read. This is (struct line *) -1 if there was an error
+ reading the values. */
+ struct line *lines;
+ /* Number of entries in lines. */
+ size_t lines_count;
+ /* PC ranges to function. */
+ struct function_addrs *function_addrs;
+ size_t function_addrs_count;
+};
+
+/* An address range for a compilation unit. This maps a PC value to a
+ specific compilation unit. Note that we invert the representation
+ in DWARF: instead of listing the units and attaching a list of
+ ranges, we list the ranges and have each one point to the unit.
+ This lets us do a binary search to find the unit. */
+
+struct unit_addrs
+{
+ /* Range is LOW <= PC < HIGH. */
+ uint64_t low;
+ uint64_t high;
+ /* Compilation unit for this address range. */
+ struct unit *u;
+};
+
+/* A growable vector of compilation unit address ranges. */
+
+struct unit_addrs_vector
+{
+ /* Memory. This is an array of struct unit_addrs. */
+ struct backtrace_vector vec;
+ /* Number of address ranges present. */
+ size_t count;
+};
+
+/* A growable vector of compilation unit pointer. */
+
+struct unit_vector
+{
+ struct backtrace_vector vec;
+ size_t count;
+};
+
+/* The information we need to map a PC to a file and line. */
+
+struct dwarf_data
+{
+ /* The data for the next file we know about. */
+ struct dwarf_data *next;
+ /* The data for .gnu_debugaltlink. */
+ struct dwarf_data *altlink;
+ /* The base address for this file. */
+ uintptr_t base_address;
+ /* A sorted list of address ranges. */
+ struct unit_addrs *addrs;
+ /* Number of address ranges in list. */
+ size_t addrs_count;
+ /* A sorted list of units. */
+ struct unit **units;
+ /* Number of units in the list. */
+ size_t units_count;
+ /* The unparsed DWARF debug data. */
+ struct dwarf_sections dwarf_sections;
+ /* Whether the data is big-endian or not. */
+ int is_bigendian;
+ /* A vector used for function addresses. We keep this here so that
+ we can grow the vector as we read more functions. */
+ struct function_vector fvec;
+};
+
+/* Report an error for a DWARF buffer. */
+
+static void
+dwarf_buf_error (struct dwarf_buf *buf, const char *msg, int errnum)
+{
+ char b[200];
+
+ snprintf (b, sizeof b, "%s in %s at %d",
+ msg, buf->name, (int) (buf->buf - buf->start));
+ buf->error_callback (buf->data, b, errnum);
+}
+
+/* Require at least COUNT bytes in BUF. Return 1 if all is well, 0 on
+ error. */
+
+static int
+require (struct dwarf_buf *buf, size_t count)
+{
+ if (buf->left >= count)
+ return 1;
+
+ if (!buf->reported_underflow)
+ {
+ dwarf_buf_error (buf, "DWARF underflow", 0);
+ buf->reported_underflow = 1;
+ }
+
+ return 0;
+}
+
+/* Advance COUNT bytes in BUF. Return 1 if all is well, 0 on
+ error. */
+
+static int
+advance (struct dwarf_buf *buf, size_t count)
+{
+ if (!require (buf, count))
+ return 0;
+ buf->buf += count;
+ buf->left -= count;
+ return 1;
+}
+
+/* Read one zero-terminated string from BUF and advance past the string. */
+
+static const char *
+read_string (struct dwarf_buf *buf)
+{
+ const char *p = (const char *)buf->buf;
+ size_t len = strnlen (p, buf->left);
+
+ /* - If len == left, we ran out of buffer before finding the zero terminator.
+ Generate an error by advancing len + 1.
+ - If len < left, advance by len + 1 to skip past the zero terminator. */
+ size_t count = len + 1;
+
+ if (!advance (buf, count))
+ return NULL;
+
+ return p;
+}
+
+/* Read one byte from BUF and advance 1 byte. */
+
+static unsigned char
+read_byte (struct dwarf_buf *buf)
+{
+ const unsigned char *p = buf->buf;
+
+ if (!advance (buf, 1))
+ return 0;
+ return p[0];
+}
+
+/* Read a signed char from BUF and advance 1 byte. */
+
+static signed char
+read_sbyte (struct dwarf_buf *buf)
+{
+ const unsigned char *p = buf->buf;
+
+ if (!advance (buf, 1))
+ return 0;
+ return (*p ^ 0x80) - 0x80;
+}
+
+/* Read a uint16 from BUF and advance 2 bytes. */
+
+static uint16_t
+read_uint16 (struct dwarf_buf *buf)
+{
+ const unsigned char *p = buf->buf;
+
+ if (!advance (buf, 2))
+ return 0;
+ if (buf->is_bigendian)
+ return ((uint16_t) p[0] << 8) | (uint16_t) p[1];
+ else
+ return ((uint16_t) p[1] << 8) | (uint16_t) p[0];
+}
+
+/* Read a 24 bit value from BUF and advance 3 bytes. */
+
+static uint32_t
+read_uint24 (struct dwarf_buf *buf)
+{
+ const unsigned char *p = buf->buf;
+
+ if (!advance (buf, 3))
+ return 0;
+ if (buf->is_bigendian)
+ return (((uint32_t) p[0] << 16) | ((uint32_t) p[1] << 8)
+ | (uint32_t) p[2]);
+ else
+ return (((uint32_t) p[2] << 16) | ((uint32_t) p[1] << 8)
+ | (uint32_t) p[0]);
+}
+
+/* Read a uint32 from BUF and advance 4 bytes. */
+
+static uint32_t
+read_uint32 (struct dwarf_buf *buf)
+{
+ const unsigned char *p = buf->buf;
+
+ if (!advance (buf, 4))
+ return 0;
+ if (buf->is_bigendian)
+ return (((uint32_t) p[0] << 24) | ((uint32_t) p[1] << 16)
+ | ((uint32_t) p[2] << 8) | (uint32_t) p[3]);
+ else
+ return (((uint32_t) p[3] << 24) | ((uint32_t) p[2] << 16)
+ | ((uint32_t) p[1] << 8) | (uint32_t) p[0]);
+}
+
+/* Read a uint64 from BUF and advance 8 bytes. */
+
+static uint64_t
+read_uint64 (struct dwarf_buf *buf)
+{
+ const unsigned char *p = buf->buf;
+
+ if (!advance (buf, 8))
+ return 0;
+ if (buf->is_bigendian)
+ return (((uint64_t) p[0] << 56) | ((uint64_t) p[1] << 48)
+ | ((uint64_t) p[2] << 40) | ((uint64_t) p[3] << 32)
+ | ((uint64_t) p[4] << 24) | ((uint64_t) p[5] << 16)
+ | ((uint64_t) p[6] << 8) | (uint64_t) p[7]);
+ else
+ return (((uint64_t) p[7] << 56) | ((uint64_t) p[6] << 48)
+ | ((uint64_t) p[5] << 40) | ((uint64_t) p[4] << 32)
+ | ((uint64_t) p[3] << 24) | ((uint64_t) p[2] << 16)
+ | ((uint64_t) p[1] << 8) | (uint64_t) p[0]);
+}
+
+/* Read an offset from BUF and advance the appropriate number of
+ bytes. */
+
+static uint64_t
+read_offset (struct dwarf_buf *buf, int is_dwarf64)
+{
+ if (is_dwarf64)
+ return read_uint64 (buf);
+ else
+ return read_uint32 (buf);
+}
+
+/* Read an address from BUF and advance the appropriate number of
+ bytes. */
+
+static uint64_t
+read_address (struct dwarf_buf *buf, int addrsize)
+{
+ switch (addrsize)
+ {
+ case 1:
+ return read_byte (buf);
+ case 2:
+ return read_uint16 (buf);
+ case 4:
+ return read_uint32 (buf);
+ case 8:
+ return read_uint64 (buf);
+ default:
+ dwarf_buf_error (buf, "unrecognized address size", 0);
+ return 0;
+ }
+}
+
+/* Return whether a value is the highest possible address, given the
+ address size. */
+
+static int
+is_highest_address (uint64_t address, int addrsize)
+{
+ switch (addrsize)
+ {
+ case 1:
+ return address == (unsigned char) -1;
+ case 2:
+ return address == (uint16_t) -1;
+ case 4:
+ return address == (uint32_t) -1;
+ case 8:
+ return address == (uint64_t) -1;
+ default:
+ return 0;
+ }
+}
+
+/* Read an unsigned LEB128 number. */
+
+static uint64_t
+read_uleb128 (struct dwarf_buf *buf)
+{
+ uint64_t ret;
+ unsigned int shift;
+ int overflow;
+ unsigned char b;
+
+ ret = 0;
+ shift = 0;
+ overflow = 0;
+ do
+ {
+ const unsigned char *p;
+
+ p = buf->buf;
+ if (!advance (buf, 1))
+ return 0;
+ b = *p;
+ if (shift < 64)
+ ret |= ((uint64_t) (b & 0x7f)) << shift;
+ else if (!overflow)
+ {
+ dwarf_buf_error (buf, "LEB128 overflows uint64_t", 0);
+ overflow = 1;
+ }
+ shift += 7;
+ }
+ while ((b & 0x80) != 0);
+
+ return ret;
+}
+
+/* Read a signed LEB128 number. */
+
+static int64_t
+read_sleb128 (struct dwarf_buf *buf)
+{
+ uint64_t val;
+ unsigned int shift;
+ int overflow;
+ unsigned char b;
+
+ val = 0;
+ shift = 0;
+ overflow = 0;
+ do
+ {
+ const unsigned char *p;
+
+ p = buf->buf;
+ if (!advance (buf, 1))
+ return 0;
+ b = *p;
+ if (shift < 64)
+ val |= ((uint64_t) (b & 0x7f)) << shift;
+ else if (!overflow)
+ {
+ dwarf_buf_error (buf, "signed LEB128 overflows uint64_t", 0);
+ overflow = 1;
+ }
+ shift += 7;
+ }
+ while ((b & 0x80) != 0);
+
+ if ((b & 0x40) != 0 && shift < 64)
+ val |= ((uint64_t) -1) << shift;
+
+ return (int64_t) val;
+}
+
+/* Return the length of an LEB128 number. */
+
+static size_t
+leb128_len (const unsigned char *p)
+{
+ size_t ret;
+
+ ret = 1;
+ while ((*p & 0x80) != 0)
+ {
+ ++p;
+ ++ret;
+ }
+ return ret;
+}
+
+/* Read initial_length from BUF and advance the appropriate number of bytes. */
+
+static uint64_t
+read_initial_length (struct dwarf_buf *buf, int *is_dwarf64)
+{
+ uint64_t len;
+
+ len = read_uint32 (buf);
+ if (len == 0xffffffff)
+ {
+ len = read_uint64 (buf);
+ *is_dwarf64 = 1;
+ }
+ else
+ *is_dwarf64 = 0;
+
+ return len;
+}
+
+/* Free an abbreviations structure. */
+
+static void
+free_abbrevs (struct backtrace_state *state, struct abbrevs *abbrevs,
+ backtrace_error_callback error_callback, void *data)
+{
+ size_t i;
+
+ for (i = 0; i < abbrevs->num_abbrevs; ++i)
+ backtrace_free (state, abbrevs->abbrevs[i].attrs,
+ abbrevs->abbrevs[i].num_attrs * sizeof (struct attr),
+ error_callback, data);
+ backtrace_free (state, abbrevs->abbrevs,
+ abbrevs->num_abbrevs * sizeof (struct abbrev),
+ error_callback, data);
+ abbrevs->num_abbrevs = 0;
+ abbrevs->abbrevs = NULL;
+}
+
+/* Read an attribute value. Returns 1 on success, 0 on failure. If
+ the value can be represented as a uint64_t, sets *VAL and sets
+ *IS_VALID to 1. We don't try to store the value of other attribute
+ forms, because we don't care about them. */
+
+static int
+read_attribute (enum dwarf_form form, uint64_t implicit_val,
+ struct dwarf_buf *buf, int is_dwarf64, int version,
+ int addrsize, const struct dwarf_sections *dwarf_sections,
+ struct dwarf_data *altlink, struct attr_val *val)
+{
+ /* Avoid warnings about val.u.FIELD may be used uninitialized if
+ this function is inlined. The warnings aren't valid but can
+ occur because the different fields are set and used
+ conditionally. */
+ memset (val, 0, sizeof *val);
+
+ switch (form)
+ {
+ case DW_FORM_addr:
+ val->encoding = ATTR_VAL_ADDRESS;
+ val->u.uint = read_address (buf, addrsize);
+ return 1;
+ case DW_FORM_block2:
+ val->encoding = ATTR_VAL_BLOCK;
+ return advance (buf, read_uint16 (buf));
+ case DW_FORM_block4:
+ val->encoding = ATTR_VAL_BLOCK;
+ return advance (buf, read_uint32 (buf));
+ case DW_FORM_data2:
+ val->encoding = ATTR_VAL_UINT;
+ val->u.uint = read_uint16 (buf);
+ return 1;
+ case DW_FORM_data4:
+ val->encoding = ATTR_VAL_UINT;
+ val->u.uint = read_uint32 (buf);
+ return 1;
+ case DW_FORM_data8:
+ val->encoding = ATTR_VAL_UINT;
+ val->u.uint = read_uint64 (buf);
+ return 1;
+ case DW_FORM_data16:
+ val->encoding = ATTR_VAL_BLOCK;
+ return advance (buf, 16);
+ case DW_FORM_string:
+ val->encoding = ATTR_VAL_STRING;
+ val->u.string = read_string (buf);
+ return val->u.string == NULL ? 0 : 1;
+ case DW_FORM_block:
+ val->encoding = ATTR_VAL_BLOCK;
+ return advance (buf, read_uleb128 (buf));
+ case DW_FORM_block1:
+ val->encoding = ATTR_VAL_BLOCK;
+ return advance (buf, read_byte (buf));
+ case DW_FORM_data1:
+ val->encoding = ATTR_VAL_UINT;
+ val->u.uint = read_byte (buf);
+ return 1;
+ case DW_FORM_flag:
+ val->encoding = ATTR_VAL_UINT;
+ val->u.uint = read_byte (buf);
+ return 1;
+ case DW_FORM_sdata:
+ val->encoding = ATTR_VAL_SINT;
+ val->u.sint = read_sleb128 (buf);
+ return 1;
+ case DW_FORM_strp:
+ {
+ uint64_t offset;
+
+ offset = read_offset (buf, is_dwarf64);
+ if (offset >= dwarf_sections->size[DEBUG_STR])
+ {
+ dwarf_buf_error (buf, "DW_FORM_strp out of range", 0);
+ return 0;
+ }
+ val->encoding = ATTR_VAL_STRING;
+ val->u.string =
+ (const char *) dwarf_sections->data[DEBUG_STR] + offset;
+ return 1;
+ }
+ case DW_FORM_line_strp:
+ {
+ uint64_t offset;
+
+ offset = read_offset (buf, is_dwarf64);
+ if (offset >= dwarf_sections->size[DEBUG_LINE_STR])
+ {
+ dwarf_buf_error (buf, "DW_FORM_line_strp out of range", 0);
+ return 0;
+ }
+ val->encoding = ATTR_VAL_STRING;
+ val->u.string =
+ (const char *) dwarf_sections->data[DEBUG_LINE_STR] + offset;
+ return 1;
+ }
+ case DW_FORM_udata:
+ val->encoding = ATTR_VAL_UINT;
+ val->u.uint = read_uleb128 (buf);
+ return 1;
+ case DW_FORM_ref_addr:
+ val->encoding = ATTR_VAL_REF_INFO;
+ if (version == 2)
+ val->u.uint = read_address (buf, addrsize);
+ else
+ val->u.uint = read_offset (buf, is_dwarf64);
+ return 1;
+ case DW_FORM_ref1:
+ val->encoding = ATTR_VAL_REF_UNIT;
+ val->u.uint = read_byte (buf);
+ return 1;
+ case DW_FORM_ref2:
+ val->encoding = ATTR_VAL_REF_UNIT;
+ val->u.uint = read_uint16 (buf);
+ return 1;
+ case DW_FORM_ref4:
+ val->encoding = ATTR_VAL_REF_UNIT;
+ val->u.uint = read_uint32 (buf);
+ return 1;
+ case DW_FORM_ref8:
+ val->encoding = ATTR_VAL_REF_UNIT;
+ val->u.uint = read_uint64 (buf);
+ return 1;
+ case DW_FORM_ref_udata:
+ val->encoding = ATTR_VAL_REF_UNIT;
+ val->u.uint = read_uleb128 (buf);
+ return 1;
+ case DW_FORM_indirect:
+ {
+ uint64_t form;
+
+ form = read_uleb128 (buf);
+ if (form == DW_FORM_implicit_const)
+ {
+ dwarf_buf_error (buf,
+ "DW_FORM_indirect to DW_FORM_implicit_const",
+ 0);
+ return 0;
+ }
+ return read_attribute ((enum dwarf_form) form, 0, buf, is_dwarf64,
+ version, addrsize, dwarf_sections, altlink,
+ val);
+ }
+ case DW_FORM_sec_offset:
+ val->encoding = ATTR_VAL_REF_SECTION;
+ val->u.uint = read_offset (buf, is_dwarf64);
+ return 1;
+ case DW_FORM_exprloc:
+ val->encoding = ATTR_VAL_EXPR;
+ return advance (buf, read_uleb128 (buf));
+ case DW_FORM_flag_present:
+ val->encoding = ATTR_VAL_UINT;
+ val->u.uint = 1;
+ return 1;
+ case DW_FORM_ref_sig8:
+ val->encoding = ATTR_VAL_REF_TYPE;
+ val->u.uint = read_uint64 (buf);
+ return 1;
+ case DW_FORM_strx: case DW_FORM_strx1: case DW_FORM_strx2:
+ case DW_FORM_strx3: case DW_FORM_strx4:
+ {
+ uint64_t offset;
+
+ switch (form)
+ {
+ case DW_FORM_strx:
+ offset = read_uleb128 (buf);
+ break;
+ case DW_FORM_strx1:
+ offset = read_byte (buf);
+ break;
+ case DW_FORM_strx2:
+ offset = read_uint16 (buf);
+ break;
+ case DW_FORM_strx3:
+ offset = read_uint24 (buf);
+ break;
+ case DW_FORM_strx4:
+ offset = read_uint32 (buf);
+ break;
+ default:
+ /* This case can't happen. */
+ return 0;
+ }
+ val->encoding = ATTR_VAL_STRING_INDEX;
+ val->u.uint = offset;
+ return 1;
+ }
+ case DW_FORM_addrx: case DW_FORM_addrx1: case DW_FORM_addrx2:
+ case DW_FORM_addrx3: case DW_FORM_addrx4:
+ {
+ uint64_t offset;
+
+ switch (form)
+ {
+ case DW_FORM_addrx:
+ offset = read_uleb128 (buf);
+ break;
+ case DW_FORM_addrx1:
+ offset = read_byte (buf);
+ break;
+ case DW_FORM_addrx2:
+ offset = read_uint16 (buf);
+ break;
+ case DW_FORM_addrx3:
+ offset = read_uint24 (buf);
+ break;
+ case DW_FORM_addrx4:
+ offset = read_uint32 (buf);
+ break;
+ default:
+ /* This case can't happen. */
+ return 0;
+ }
+ val->encoding = ATTR_VAL_ADDRESS_INDEX;
+ val->u.uint = offset;
+ return 1;
+ }
+ case DW_FORM_ref_sup4:
+ val->encoding = ATTR_VAL_REF_SECTION;
+ val->u.uint = read_uint32 (buf);
+ return 1;
+ case DW_FORM_ref_sup8:
+ val->encoding = ATTR_VAL_REF_SECTION;
+ val->u.uint = read_uint64 (buf);
+ return 1;
+ case DW_FORM_implicit_const:
+ val->encoding = ATTR_VAL_UINT;
+ val->u.uint = implicit_val;
+ return 1;
+ case DW_FORM_loclistx:
+ /* We don't distinguish this from DW_FORM_sec_offset. It
+ * shouldn't matter since we don't care about loclists. */
+ val->encoding = ATTR_VAL_REF_SECTION;
+ val->u.uint = read_uleb128 (buf);
+ return 1;
+ case DW_FORM_rnglistx:
+ val->encoding = ATTR_VAL_RNGLISTS_INDEX;
+ val->u.uint = read_uleb128 (buf);
+ return 1;
+ case DW_FORM_GNU_addr_index:
+ val->encoding = ATTR_VAL_REF_SECTION;
+ val->u.uint = read_uleb128 (buf);
+ return 1;
+ case DW_FORM_GNU_str_index:
+ val->encoding = ATTR_VAL_REF_SECTION;
+ val->u.uint = read_uleb128 (buf);
+ return 1;
+ case DW_FORM_GNU_ref_alt:
+ val->u.uint = read_offset (buf, is_dwarf64);
+ if (altlink == NULL)
+ {
+ val->encoding = ATTR_VAL_NONE;
+ return 1;
+ }
+ val->encoding = ATTR_VAL_REF_ALT_INFO;
+ return 1;
+ case DW_FORM_strp_sup: case DW_FORM_GNU_strp_alt:
+ {
+ uint64_t offset;
+
+ offset = read_offset (buf, is_dwarf64);
+ if (altlink == NULL)
+ {
+ val->encoding = ATTR_VAL_NONE;
+ return 1;
+ }
+ if (offset >= altlink->dwarf_sections.size[DEBUG_STR])
+ {
+ dwarf_buf_error (buf, "DW_FORM_strp_sup out of range", 0);
+ return 0;
+ }
+ val->encoding = ATTR_VAL_STRING;
+ val->u.string =
+ (const char *) altlink->dwarf_sections.data[DEBUG_STR] + offset;
+ return 1;
+ }
+ default:
+ dwarf_buf_error (buf, "unrecognized DWARF form", -1);
+ return 0;
+ }
+}
+
+/* If we can determine the value of a string attribute, set *STRING to
+ point to the string. Return 1 on success, 0 on error. If we don't
+ know the value, we consider that a success, and we don't change
+ *STRING. An error is only reported for some sort of out of range
+ offset. */
+
+static int
+resolve_string (const struct dwarf_sections *dwarf_sections, int is_dwarf64,
+ int is_bigendian, uint64_t str_offsets_base,
+ const struct attr_val *val,
+ backtrace_error_callback error_callback, void *data,
+ const char **string)
+{
+ switch (val->encoding)
+ {
+ case ATTR_VAL_STRING:
+ *string = val->u.string;
+ return 1;
+
+ case ATTR_VAL_STRING_INDEX:
+ {
+ uint64_t offset;
+ struct dwarf_buf offset_buf;
+
+ offset = val->u.uint * (is_dwarf64 ? 8 : 4) + str_offsets_base;
+ if (offset + (is_dwarf64 ? 8 : 4)
+ > dwarf_sections->size[DEBUG_STR_OFFSETS])
+ {
+ error_callback (data, "DW_FORM_strx value out of range", 0);
+ return 0;
+ }
+
+ offset_buf.name = ".debug_str_offsets";
+ offset_buf.start = dwarf_sections->data[DEBUG_STR_OFFSETS];
+ offset_buf.buf = dwarf_sections->data[DEBUG_STR_OFFSETS] + offset;
+ offset_buf.left = dwarf_sections->size[DEBUG_STR_OFFSETS] - offset;
+ offset_buf.is_bigendian = is_bigendian;
+ offset_buf.error_callback = error_callback;
+ offset_buf.data = data;
+ offset_buf.reported_underflow = 0;
+
+ offset = read_offset (&offset_buf, is_dwarf64);
+ if (offset >= dwarf_sections->size[DEBUG_STR])
+ {
+ dwarf_buf_error (&offset_buf,
+ "DW_FORM_strx offset out of range",
+ 0);
+ return 0;
+ }
+ *string = (const char *) dwarf_sections->data[DEBUG_STR] + offset;
+ return 1;
+ }
+
+ default:
+ return 1;
+ }
+}
+
+/* Set *ADDRESS to the real address for a ATTR_VAL_ADDRESS_INDEX.
+ Return 1 on success, 0 on error. */
+
+static int
+resolve_addr_index (const struct dwarf_sections *dwarf_sections,
+ uint64_t addr_base, int addrsize, int is_bigendian,
+ uint64_t addr_index,
+ backtrace_error_callback error_callback, void *data,
+ uint64_t *address)
+{
+ uint64_t offset;
+ struct dwarf_buf addr_buf;
+
+ offset = addr_index * addrsize + addr_base;
+ if (offset + addrsize > dwarf_sections->size[DEBUG_ADDR])
+ {
+ error_callback (data, "DW_FORM_addrx value out of range", 0);
+ return 0;
+ }
+
+ addr_buf.name = ".debug_addr";
+ addr_buf.start = dwarf_sections->data[DEBUG_ADDR];
+ addr_buf.buf = dwarf_sections->data[DEBUG_ADDR] + offset;
+ addr_buf.left = dwarf_sections->size[DEBUG_ADDR] - offset;
+ addr_buf.is_bigendian = is_bigendian;
+ addr_buf.error_callback = error_callback;
+ addr_buf.data = data;
+ addr_buf.reported_underflow = 0;
+
+ *address = read_address (&addr_buf, addrsize);
+ return 1;
+}
+
+/* Compare a unit offset against a unit for bsearch. */
+
+static int
+units_search (const void *vkey, const void *ventry)
+{
+ const size_t *key = (const size_t *) vkey;
+ const struct unit *entry = *((const struct unit *const *) ventry);
+ size_t offset;
+
+ offset = *key;
+ if (offset < entry->low_offset)
+ return -1;
+ else if (offset >= entry->high_offset)
+ return 1;
+ else
+ return 0;
+}
+
+/* Find a unit in PU containing OFFSET. */
+
+static struct unit *
+find_unit (struct unit **pu, size_t units_count, size_t offset)
+{
+ struct unit **u;
+ u = bsearch (&offset, pu, units_count, sizeof (struct unit *), units_search);
+ return u == NULL ? NULL : *u;
+}
+
+/* Compare function_addrs for qsort. When ranges are nested, make the
+ smallest one sort last. */
+
+static int
+function_addrs_compare (const void *v1, const void *v2)
+{
+ const struct function_addrs *a1 = (const struct function_addrs *) v1;
+ const struct function_addrs *a2 = (const struct function_addrs *) v2;
+
+ if (a1->low < a2->low)
+ return -1;
+ if (a1->low > a2->low)
+ return 1;
+ if (a1->high < a2->high)
+ return 1;
+ if (a1->high > a2->high)
+ return -1;
+ return strcmp (a1->function->name, a2->function->name);
+}
+
+/* Compare a PC against a function_addrs for bsearch. We always
+ allocate an entra entry at the end of the vector, so that this
+ routine can safely look at the next entry. Note that if there are
+ multiple ranges containing PC, which one will be returned is
+ unpredictable. We compensate for that in dwarf_fileline. */
+
+static int
+function_addrs_search (const void *vkey, const void *ventry)
+{
+ const uintptr_t *key = (const uintptr_t *) vkey;
+ const struct function_addrs *entry = (const struct function_addrs *) ventry;
+ uintptr_t pc;
+
+ pc = *key;
+ if (pc < entry->low)
+ return -1;
+ else if (pc > (entry + 1)->low)
+ return 1;
+ else
+ return 0;
+}
+
+/* Add a new compilation unit address range to a vector. This is
+ called via add_ranges. Returns 1 on success, 0 on failure. */
+
+static int
+add_unit_addr (struct backtrace_state *state, void *rdata,
+ uint64_t lowpc, uint64_t highpc,
+ backtrace_error_callback error_callback, void *data,
+ void *pvec)
+{
+ struct unit *u = (struct unit *) rdata;
+ struct unit_addrs_vector *vec = (struct unit_addrs_vector *) pvec;
+ struct unit_addrs *p;
+
+ /* Try to merge with the last entry. */
+ if (vec->count > 0)
+ {
+ p = (struct unit_addrs *) vec->vec.base + (vec->count - 1);
+ if ((lowpc == p->high || lowpc == p->high + 1)
+ && u == p->u)
+ {
+ if (highpc > p->high)
+ p->high = highpc;
+ return 1;
+ }
+ }
+
+ p = ((struct unit_addrs *)
+ backtrace_vector_grow (state, sizeof (struct unit_addrs),
+ error_callback, data, &vec->vec));
+ if (p == NULL)
+ return 0;
+
+ p->low = lowpc;
+ p->high = highpc;
+ p->u = u;
+
+ ++vec->count;
+
+ return 1;
+}
+
+/* Compare unit_addrs for qsort. When ranges are nested, make the
+ smallest one sort last. */
+
+static int
+unit_addrs_compare (const void *v1, const void *v2)
+{
+ const struct unit_addrs *a1 = (const struct unit_addrs *) v1;
+ const struct unit_addrs *a2 = (const struct unit_addrs *) v2;
+
+ if (a1->low < a2->low)
+ return -1;
+ if (a1->low > a2->low)
+ return 1;
+ if (a1->high < a2->high)
+ return 1;
+ if (a1->high > a2->high)
+ return -1;
+ if (a1->u->lineoff < a2->u->lineoff)
+ return -1;
+ if (a1->u->lineoff > a2->u->lineoff)
+ return 1;
+ return 0;
+}
+
+/* Compare a PC against a unit_addrs for bsearch. We always allocate
+ an entry entry at the end of the vector, so that this routine can
+ safely look at the next entry. Note that if there are multiple
+ ranges containing PC, which one will be returned is unpredictable.
+ We compensate for that in dwarf_fileline. */
+
+static int
+unit_addrs_search (const void *vkey, const void *ventry)
+{
+ const uintptr_t *key = (const uintptr_t *) vkey;
+ const struct unit_addrs *entry = (const struct unit_addrs *) ventry;
+ uintptr_t pc;
+
+ pc = *key;
+ if (pc < entry->low)
+ return -1;
+ else if (pc > (entry + 1)->low)
+ return 1;
+ else
+ return 0;
+}
+
+/* Sort the line vector by PC. We want a stable sort here to maintain
+ the order of lines for the same PC values. Since the sequence is
+ being sorted in place, their addresses cannot be relied on to
+ maintain stability. That is the purpose of the index member. */
+
+static int
+line_compare (const void *v1, const void *v2)
+{
+ const struct line *ln1 = (const struct line *) v1;
+ const struct line *ln2 = (const struct line *) v2;
+
+ if (ln1->pc < ln2->pc)
+ return -1;
+ else if (ln1->pc > ln2->pc)
+ return 1;
+ else if (ln1->idx < ln2->idx)
+ return -1;
+ else if (ln1->idx > ln2->idx)
+ return 1;
+ else
+ return 0;
+}
+
+/* Find a PC in a line vector. We always allocate an extra entry at
+ the end of the lines vector, so that this routine can safely look
+ at the next entry. Note that when there are multiple mappings for
+ the same PC value, this will return the last one. */
+
+static int
+line_search (const void *vkey, const void *ventry)
+{
+ const uintptr_t *key = (const uintptr_t *) vkey;
+ const struct line *entry = (const struct line *) ventry;
+ uintptr_t pc;
+
+ pc = *key;
+ if (pc < entry->pc)
+ return -1;
+ else if (pc >= (entry + 1)->pc)
+ return 1;
+ else
+ return 0;
+}
+
+/* Sort the abbrevs by the abbrev code. This function is passed to
+ both qsort and bsearch. */
+
+static int
+abbrev_compare (const void *v1, const void *v2)
+{
+ const struct abbrev *a1 = (const struct abbrev *) v1;
+ const struct abbrev *a2 = (const struct abbrev *) v2;
+
+ if (a1->code < a2->code)
+ return -1;
+ else if (a1->code > a2->code)
+ return 1;
+ else
+ {
+ /* This really shouldn't happen. It means there are two
+ different abbrevs with the same code, and that means we don't
+ know which one lookup_abbrev should return. */
+ return 0;
+ }
+}
+
+/* Read the abbreviation table for a compilation unit. Returns 1 on
+ success, 0 on failure. */
+
+static int
+read_abbrevs (struct backtrace_state *state, uint64_t abbrev_offset,
+ const unsigned char *dwarf_abbrev, size_t dwarf_abbrev_size,
+ int is_bigendian, backtrace_error_callback error_callback,
+ void *data, struct abbrevs *abbrevs)
+{
+ struct dwarf_buf abbrev_buf;
+ struct dwarf_buf count_buf;
+ size_t num_abbrevs;
+
+ abbrevs->num_abbrevs = 0;
+ abbrevs->abbrevs = NULL;
+
+ if (abbrev_offset >= dwarf_abbrev_size)
+ {
+ error_callback (data, "abbrev offset out of range", 0);
+ return 0;
+ }
+
+ abbrev_buf.name = ".debug_abbrev";
+ abbrev_buf.start = dwarf_abbrev;
+ abbrev_buf.buf = dwarf_abbrev + abbrev_offset;
+ abbrev_buf.left = dwarf_abbrev_size - abbrev_offset;
+ abbrev_buf.is_bigendian = is_bigendian;
+ abbrev_buf.error_callback = error_callback;
+ abbrev_buf.data = data;
+ abbrev_buf.reported_underflow = 0;
+
+ /* Count the number of abbrevs in this list. */
+
+ count_buf = abbrev_buf;
+ num_abbrevs = 0;
+ while (read_uleb128 (&count_buf) != 0)
+ {
+ if (count_buf.reported_underflow)
+ return 0;
+ ++num_abbrevs;
+ // Skip tag.
+ read_uleb128 (&count_buf);
+ // Skip has_children.
+ read_byte (&count_buf);
+ // Skip attributes.
+ while (read_uleb128 (&count_buf) != 0)
+ {
+ uint64_t form;
+
+ form = read_uleb128 (&count_buf);
+ if ((enum dwarf_form) form == DW_FORM_implicit_const)
+ read_sleb128 (&count_buf);
+ }
+ // Skip form of last attribute.
+ read_uleb128 (&count_buf);
+ }
+
+ if (count_buf.reported_underflow)
+ return 0;
+
+ if (num_abbrevs == 0)
+ return 1;
+
+ abbrevs->abbrevs = ((struct abbrev *)
+ backtrace_alloc (state,
+ num_abbrevs * sizeof (struct abbrev),
+ error_callback, data));
+ if (abbrevs->abbrevs == NULL)
+ return 0;
+ abbrevs->num_abbrevs = num_abbrevs;
+ memset (abbrevs->abbrevs, 0, num_abbrevs * sizeof (struct abbrev));
+
+ num_abbrevs = 0;
+ while (1)
+ {
+ uint64_t code;
+ struct abbrev a;
+ size_t num_attrs;
+ struct attr *attrs;
+
+ if (abbrev_buf.reported_underflow)
+ goto fail;
+
+ code = read_uleb128 (&abbrev_buf);
+ if (code == 0)
+ break;
+
+ a.code = code;
+ a.tag = (enum dwarf_tag) read_uleb128 (&abbrev_buf);
+ a.has_children = read_byte (&abbrev_buf);
+
+ count_buf = abbrev_buf;
+ num_attrs = 0;
+ while (read_uleb128 (&count_buf) != 0)
+ {
+ uint64_t form;
+
+ ++num_attrs;
+ form = read_uleb128 (&count_buf);
+ if ((enum dwarf_form) form == DW_FORM_implicit_const)
+ read_sleb128 (&count_buf);
+ }
+
+ if (num_attrs == 0)
+ {
+ attrs = NULL;
+ read_uleb128 (&abbrev_buf);
+ read_uleb128 (&abbrev_buf);
+ }
+ else
+ {
+ attrs = ((struct attr *)
+ backtrace_alloc (state, num_attrs * sizeof *attrs,
+ error_callback, data));
+ if (attrs == NULL)
+ goto fail;
+ num_attrs = 0;
+ while (1)
+ {
+ uint64_t name;
+ uint64_t form;
+
+ name = read_uleb128 (&abbrev_buf);
+ form = read_uleb128 (&abbrev_buf);
+ if (name == 0)
+ break;
+ attrs[num_attrs].name = (enum dwarf_attribute) name;
+ attrs[num_attrs].form = (enum dwarf_form) form;
+ if ((enum dwarf_form) form == DW_FORM_implicit_const)
+ attrs[num_attrs].val = read_sleb128 (&abbrev_buf);
+ else
+ attrs[num_attrs].val = 0;
+ ++num_attrs;
+ }
+ }
+
+ a.num_attrs = num_attrs;
+ a.attrs = attrs;
+
+ abbrevs->abbrevs[num_abbrevs] = a;
+ ++num_abbrevs;
+ }
+
+ backtrace_qsort (abbrevs->abbrevs, abbrevs->num_abbrevs,
+ sizeof (struct abbrev), abbrev_compare);
+
+ return 1;
+
+ fail:
+ free_abbrevs (state, abbrevs, error_callback, data);
+ return 0;
+}
+
+/* Return the abbrev information for an abbrev code. */
+
+static const struct abbrev *
+lookup_abbrev (struct abbrevs *abbrevs, uint64_t code,
+ backtrace_error_callback error_callback, void *data)
+{
+ struct abbrev key;
+ void *p;
+
+ /* With GCC, where abbrevs are simply numbered in order, we should
+ be able to just look up the entry. */
+ if (code - 1 < abbrevs->num_abbrevs
+ && abbrevs->abbrevs[code - 1].code == code)
+ return &abbrevs->abbrevs[code - 1];
+
+ /* Otherwise we have to search. */
+ memset (&key, 0, sizeof key);
+ key.code = code;
+ p = bsearch (&key, abbrevs->abbrevs, abbrevs->num_abbrevs,
+ sizeof (struct abbrev), abbrev_compare);
+ if (p == NULL)
+ {
+ error_callback (data, "invalid abbreviation code", 0);
+ return NULL;
+ }
+ return (const struct abbrev *) p;
+}
+
+/* This struct is used to gather address range information while
+ reading attributes. We use this while building a mapping from
+ address ranges to compilation units and then again while mapping
+ from address ranges to function entries. Normally either
+ lowpc/highpc is set or ranges is set. */
+
+struct pcrange {
+ uint64_t lowpc; /* The low PC value. */
+ int have_lowpc; /* Whether a low PC value was found. */
+ int lowpc_is_addr_index; /* Whether lowpc is in .debug_addr. */
+ uint64_t highpc; /* The high PC value. */
+ int have_highpc; /* Whether a high PC value was found. */
+ int highpc_is_relative; /* Whether highpc is relative to lowpc. */
+ int highpc_is_addr_index; /* Whether highpc is in .debug_addr. */
+ uint64_t ranges; /* Offset in ranges section. */
+ int have_ranges; /* Whether ranges is valid. */
+ int ranges_is_index; /* Whether ranges is DW_FORM_rnglistx. */
+};
+
+/* Update PCRANGE from an attribute value. */
+
+static void
+update_pcrange (const struct attr* attr, const struct attr_val* val,
+ struct pcrange *pcrange)
+{
+ switch (attr->name)
+ {
+ case DW_AT_low_pc:
+ if (val->encoding == ATTR_VAL_ADDRESS)
+ {
+ pcrange->lowpc = val->u.uint;
+ pcrange->have_lowpc = 1;
+ }
+ else if (val->encoding == ATTR_VAL_ADDRESS_INDEX)
+ {
+ pcrange->lowpc = val->u.uint;
+ pcrange->have_lowpc = 1;
+ pcrange->lowpc_is_addr_index = 1;
+ }
+ break;
+
+ case DW_AT_high_pc:
+ if (val->encoding == ATTR_VAL_ADDRESS)
+ {
+ pcrange->highpc = val->u.uint;
+ pcrange->have_highpc = 1;
+ }
+ else if (val->encoding == ATTR_VAL_UINT)
+ {
+ pcrange->highpc = val->u.uint;
+ pcrange->have_highpc = 1;
+ pcrange->highpc_is_relative = 1;
+ }
+ else if (val->encoding == ATTR_VAL_ADDRESS_INDEX)
+ {
+ pcrange->highpc = val->u.uint;
+ pcrange->have_highpc = 1;
+ pcrange->highpc_is_addr_index = 1;
+ }
+ break;
+
+ case DW_AT_ranges:
+ if (val->encoding == ATTR_VAL_UINT
+ || val->encoding == ATTR_VAL_REF_SECTION)
+ {
+ pcrange->ranges = val->u.uint;
+ pcrange->have_ranges = 1;
+ }
+ else if (val->encoding == ATTR_VAL_RNGLISTS_INDEX)
+ {
+ pcrange->ranges = val->u.uint;
+ pcrange->have_ranges = 1;
+ pcrange->ranges_is_index = 1;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Call ADD_RANGE for a low/high PC pair. Returns 1 on success, 0 on
+ error. */
+
+static int
+add_low_high_range (struct backtrace_state *state,
+ const struct dwarf_sections *dwarf_sections,
+ uintptr_t base_address, int is_bigendian,
+ struct unit *u, const struct pcrange *pcrange,
+ int (*add_range) (struct backtrace_state *state,
+ void *rdata, uint64_t lowpc,
+ uint64_t highpc,
+ backtrace_error_callback error_callback,
+ void *data, void *vec),
+ void *rdata,
+ backtrace_error_callback error_callback, void *data,
+ void *vec)
+{
+ uint64_t lowpc;
+ uint64_t highpc;
+
+ lowpc = pcrange->lowpc;
+ if (pcrange->lowpc_is_addr_index)
+ {
+ if (!resolve_addr_index (dwarf_sections, u->addr_base, u->addrsize,
+ is_bigendian, lowpc, error_callback, data,
+ &lowpc))
+ return 0;
+ }
+
+ highpc = pcrange->highpc;
+ if (pcrange->highpc_is_addr_index)
+ {
+ if (!resolve_addr_index (dwarf_sections, u->addr_base, u->addrsize,
+ is_bigendian, highpc, error_callback, data,
+ &highpc))
+ return 0;
+ }
+ if (pcrange->highpc_is_relative)
+ highpc += lowpc;
+
+ /* Add in the base address of the module when recording PC values,
+ so that we can look up the PC directly. */
+ lowpc += base_address;
+ highpc += base_address;
+
+ return add_range (state, rdata, lowpc, highpc, error_callback, data, vec);
+}
+
+/* Call ADD_RANGE for each range read from .debug_ranges, as used in
+ DWARF versions 2 through 4. */
+
+static int
+add_ranges_from_ranges (
+ struct backtrace_state *state,
+ const struct dwarf_sections *dwarf_sections,
+ uintptr_t base_address, int is_bigendian,
+ struct unit *u, uint64_t base,
+ const struct pcrange *pcrange,
+ int (*add_range) (struct backtrace_state *state, void *rdata,
+ uint64_t lowpc, uint64_t highpc,
+ backtrace_error_callback error_callback, void *data,
+ void *vec),
+ void *rdata,
+ backtrace_error_callback error_callback, void *data,
+ void *vec)
+{
+ struct dwarf_buf ranges_buf;
+
+ if (pcrange->ranges >= dwarf_sections->size[DEBUG_RANGES])
+ {
+ error_callback (data, "ranges offset out of range", 0);
+ return 0;
+ }
+
+ ranges_buf.name = ".debug_ranges";
+ ranges_buf.start = dwarf_sections->data[DEBUG_RANGES];
+ ranges_buf.buf = dwarf_sections->data[DEBUG_RANGES] + pcrange->ranges;
+ ranges_buf.left = dwarf_sections->size[DEBUG_RANGES] - pcrange->ranges;
+ ranges_buf.is_bigendian = is_bigendian;
+ ranges_buf.error_callback = error_callback;
+ ranges_buf.data = data;
+ ranges_buf.reported_underflow = 0;
+
+ while (1)
+ {
+ uint64_t low;
+ uint64_t high;
+
+ if (ranges_buf.reported_underflow)
+ return 0;
+
+ low = read_address (&ranges_buf, u->addrsize);
+ high = read_address (&ranges_buf, u->addrsize);
+
+ if (low == 0 && high == 0)
+ break;
+
+ if (is_highest_address (low, u->addrsize))
+ base = high;
+ else
+ {
+ if (!add_range (state, rdata,
+ low + base + base_address,
+ high + base + base_address,
+ error_callback, data, vec))
+ return 0;
+ }
+ }
+
+ if (ranges_buf.reported_underflow)
+ return 0;
+
+ return 1;
+}
+
+/* Call ADD_RANGE for each range read from .debug_rnglists, as used in
+ DWARF version 5. */
+
+static int
+add_ranges_from_rnglists (
+ struct backtrace_state *state,
+ const struct dwarf_sections *dwarf_sections,
+ uintptr_t base_address, int is_bigendian,
+ struct unit *u, uint64_t base,
+ const struct pcrange *pcrange,
+ int (*add_range) (struct backtrace_state *state, void *rdata,
+ uint64_t lowpc, uint64_t highpc,
+ backtrace_error_callback error_callback, void *data,
+ void *vec),
+ void *rdata,
+ backtrace_error_callback error_callback, void *data,
+ void *vec)
+{
+ uint64_t offset;
+ struct dwarf_buf rnglists_buf;
+
+ if (!pcrange->ranges_is_index)
+ offset = pcrange->ranges;
+ else
+ offset = u->rnglists_base + pcrange->ranges * (u->is_dwarf64 ? 8 : 4);
+ if (offset >= dwarf_sections->size[DEBUG_RNGLISTS])
+ {
+ error_callback (data, "rnglists offset out of range", 0);
+ return 0;
+ }
+
+ rnglists_buf.name = ".debug_rnglists";
+ rnglists_buf.start = dwarf_sections->data[DEBUG_RNGLISTS];
+ rnglists_buf.buf = dwarf_sections->data[DEBUG_RNGLISTS] + offset;
+ rnglists_buf.left = dwarf_sections->size[DEBUG_RNGLISTS] - offset;
+ rnglists_buf.is_bigendian = is_bigendian;
+ rnglists_buf.error_callback = error_callback;
+ rnglists_buf.data = data;
+ rnglists_buf.reported_underflow = 0;
+
+ if (pcrange->ranges_is_index)
+ {
+ offset = read_offset (&rnglists_buf, u->is_dwarf64);
+ offset += u->rnglists_base;
+ if (offset >= dwarf_sections->size[DEBUG_RNGLISTS])
+ {
+ error_callback (data, "rnglists index offset out of range", 0);
+ return 0;
+ }
+ rnglists_buf.buf = dwarf_sections->data[DEBUG_RNGLISTS] + offset;
+ rnglists_buf.left = dwarf_sections->size[DEBUG_RNGLISTS] - offset;
+ }
+
+ while (1)
+ {
+ unsigned char rle;
+
+ rle = read_byte (&rnglists_buf);
+ if (rle == DW_RLE_end_of_list)
+ break;
+ switch (rle)
+ {
+ case DW_RLE_base_addressx:
+ {
+ uint64_t index;
+
+ index = read_uleb128 (&rnglists_buf);
+ if (!resolve_addr_index (dwarf_sections, u->addr_base,
+ u->addrsize, is_bigendian, index,
+ error_callback, data, &base))
+ return 0;
+ }
+ break;
+
+ case DW_RLE_startx_endx:
+ {
+ uint64_t index;
+ uint64_t low;
+ uint64_t high;
+
+ index = read_uleb128 (&rnglists_buf);
+ if (!resolve_addr_index (dwarf_sections, u->addr_base,
+ u->addrsize, is_bigendian, index,
+ error_callback, data, &low))
+ return 0;
+ index = read_uleb128 (&rnglists_buf);
+ if (!resolve_addr_index (dwarf_sections, u->addr_base,
+ u->addrsize, is_bigendian, index,
+ error_callback, data, &high))
+ return 0;
+ if (!add_range (state, rdata, low + base_address,
+ high + base_address, error_callback, data,
+ vec))
+ return 0;
+ }
+ break;
+
+ case DW_RLE_startx_length:
+ {
+ uint64_t index;
+ uint64_t low;
+ uint64_t length;
+
+ index = read_uleb128 (&rnglists_buf);
+ if (!resolve_addr_index (dwarf_sections, u->addr_base,
+ u->addrsize, is_bigendian, index,
+ error_callback, data, &low))
+ return 0;
+ length = read_uleb128 (&rnglists_buf);
+ low += base_address;
+ if (!add_range (state, rdata, low, low + length,
+ error_callback, data, vec))
+ return 0;
+ }
+ break;
+
+ case DW_RLE_offset_pair:
+ {
+ uint64_t low;
+ uint64_t high;
+
+ low = read_uleb128 (&rnglists_buf);
+ high = read_uleb128 (&rnglists_buf);
+ if (!add_range (state, rdata, low + base + base_address,
+ high + base + base_address,
+ error_callback, data, vec))
+ return 0;
+ }
+ break;
+
+ case DW_RLE_base_address:
+ base = read_address (&rnglists_buf, u->addrsize);
+ break;
+
+ case DW_RLE_start_end:
+ {
+ uint64_t low;
+ uint64_t high;
+
+ low = read_address (&rnglists_buf, u->addrsize);
+ high = read_address (&rnglists_buf, u->addrsize);
+ if (!add_range (state, rdata, low + base_address,
+ high + base_address, error_callback, data,
+ vec))
+ return 0;
+ }
+ break;
+
+ case DW_RLE_start_length:
+ {
+ uint64_t low;
+ uint64_t length;
+
+ low = read_address (&rnglists_buf, u->addrsize);
+ length = read_uleb128 (&rnglists_buf);
+ low += base_address;
+ if (!add_range (state, rdata, low, low + length,
+ error_callback, data, vec))
+ return 0;
+ }
+ break;
+
+ default:
+ dwarf_buf_error (&rnglists_buf, "unrecognized DW_RLE value", -1);
+ return 0;
+ }
+ }
+
+ if (rnglists_buf.reported_underflow)
+ return 0;
+
+ return 1;
+}
+
+/* Call ADD_RANGE for each lowpc/highpc pair in PCRANGE. RDATA is
+ passed to ADD_RANGE, and is either a struct unit * or a struct
+ function *. VEC is the vector we are adding ranges to, and is
+ either a struct unit_addrs_vector * or a struct function_vector *.
+ Returns 1 on success, 0 on error. */
+
+static int
+add_ranges (struct backtrace_state *state,
+ const struct dwarf_sections *dwarf_sections,
+ uintptr_t base_address, int is_bigendian,
+ struct unit *u, uint64_t base, const struct pcrange *pcrange,
+ int (*add_range) (struct backtrace_state *state, void *rdata,
+ uint64_t lowpc, uint64_t highpc,
+ backtrace_error_callback error_callback,
+ void *data, void *vec),
+ void *rdata,
+ backtrace_error_callback error_callback, void *data,
+ void *vec)
+{
+ if (pcrange->have_lowpc && pcrange->have_highpc)
+ return add_low_high_range (state, dwarf_sections, base_address,
+ is_bigendian, u, pcrange, add_range, rdata,
+ error_callback, data, vec);
+
+ if (!pcrange->have_ranges)
+ {
+ /* Did not find any address ranges to add. */
+ return 1;
+ }
+
+ if (u->version < 5)
+ return add_ranges_from_ranges (state, dwarf_sections, base_address,
+ is_bigendian, u, base, pcrange, add_range,
+ rdata, error_callback, data, vec);
+ else
+ return add_ranges_from_rnglists (state, dwarf_sections, base_address,
+ is_bigendian, u, base, pcrange, add_range,
+ rdata, error_callback, data, vec);
+}
+
+/* Find the address range covered by a compilation unit, reading from
+ UNIT_BUF and adding values to U. Returns 1 if all data could be
+ read, 0 if there is some error. */
+
+static int
+find_address_ranges (struct backtrace_state *state, uintptr_t base_address,
+ struct dwarf_buf *unit_buf,
+ const struct dwarf_sections *dwarf_sections,
+ int is_bigendian, struct dwarf_data *altlink,
+ backtrace_error_callback error_callback, void *data,
+ struct unit *u, struct unit_addrs_vector *addrs,
+ enum dwarf_tag *unit_tag)
+{
+ while (unit_buf->left > 0)
+ {
+ uint64_t code;
+ const struct abbrev *abbrev;
+ struct pcrange pcrange;
+ struct attr_val name_val;
+ int have_name_val;
+ struct attr_val comp_dir_val;
+ int have_comp_dir_val;
+ size_t i;
+
+ code = read_uleb128 (unit_buf);
+ if (code == 0)
+ return 1;
+
+ abbrev = lookup_abbrev (&u->abbrevs, code, error_callback, data);
+ if (abbrev == NULL)
+ return 0;
+
+ if (unit_tag != NULL)
+ *unit_tag = abbrev->tag;
+
+ memset (&pcrange, 0, sizeof pcrange);
+ memset (&name_val, 0, sizeof name_val);
+ have_name_val = 0;
+ memset (&comp_dir_val, 0, sizeof comp_dir_val);
+ have_comp_dir_val = 0;
+ for (i = 0; i < abbrev->num_attrs; ++i)
+ {
+ struct attr_val val;
+
+ if (!read_attribute (abbrev->attrs[i].form, abbrev->attrs[i].val,
+ unit_buf, u->is_dwarf64, u->version,
+ u->addrsize, dwarf_sections, altlink, &val))
+ return 0;
+
+ switch (abbrev->attrs[i].name)
+ {
+ case DW_AT_low_pc: case DW_AT_high_pc: case DW_AT_ranges:
+ update_pcrange (&abbrev->attrs[i], &val, &pcrange);
+ break;
+
+ case DW_AT_stmt_list:
+ if ((abbrev->tag == DW_TAG_compile_unit
+ || abbrev->tag == DW_TAG_skeleton_unit)
+ && (val.encoding == ATTR_VAL_UINT
+ || val.encoding == ATTR_VAL_REF_SECTION))
+ u->lineoff = val.u.uint;
+ break;
+
+ case DW_AT_name:
+ if (abbrev->tag == DW_TAG_compile_unit
+ || abbrev->tag == DW_TAG_skeleton_unit)
+ {
+ name_val = val;
+ have_name_val = 1;
+ }
+ break;
+
+ case DW_AT_comp_dir:
+ if (abbrev->tag == DW_TAG_compile_unit
+ || abbrev->tag == DW_TAG_skeleton_unit)
+ {
+ comp_dir_val = val;
+ have_comp_dir_val = 1;
+ }
+ break;
+
+ case DW_AT_str_offsets_base:
+ if ((abbrev->tag == DW_TAG_compile_unit
+ || abbrev->tag == DW_TAG_skeleton_unit)
+ && val.encoding == ATTR_VAL_REF_SECTION)
+ u->str_offsets_base = val.u.uint;
+ break;
+
+ case DW_AT_addr_base:
+ if ((abbrev->tag == DW_TAG_compile_unit
+ || abbrev->tag == DW_TAG_skeleton_unit)
+ && val.encoding == ATTR_VAL_REF_SECTION)
+ u->addr_base = val.u.uint;
+ break;
+
+ case DW_AT_rnglists_base:
+ if ((abbrev->tag == DW_TAG_compile_unit
+ || abbrev->tag == DW_TAG_skeleton_unit)
+ && val.encoding == ATTR_VAL_REF_SECTION)
+ u->rnglists_base = val.u.uint;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // Resolve strings after we're sure that we have seen
+ // DW_AT_str_offsets_base.
+ if (have_name_val)
+ {
+ if (!resolve_string (dwarf_sections, u->is_dwarf64, is_bigendian,
+ u->str_offsets_base, &name_val,
+ error_callback, data, &u->filename))
+ return 0;
+ }
+ if (have_comp_dir_val)
+ {
+ if (!resolve_string (dwarf_sections, u->is_dwarf64, is_bigendian,
+ u->str_offsets_base, &comp_dir_val,
+ error_callback, data, &u->comp_dir))
+ return 0;
+ }
+
+ if (abbrev->tag == DW_TAG_compile_unit
+ || abbrev->tag == DW_TAG_subprogram
+ || abbrev->tag == DW_TAG_skeleton_unit)
+ {
+ if (!add_ranges (state, dwarf_sections, base_address,
+ is_bigendian, u, pcrange.lowpc, &pcrange,
+ add_unit_addr, (void *) u, error_callback, data,
+ (void *) addrs))
+ return 0;
+
+ /* If we found the PC range in the DW_TAG_compile_unit or
+ DW_TAG_skeleton_unit, we can stop now. */
+ if ((abbrev->tag == DW_TAG_compile_unit
+ || abbrev->tag == DW_TAG_skeleton_unit)
+ && (pcrange.have_ranges
+ || (pcrange.have_lowpc && pcrange.have_highpc)))
+ return 1;
+ }
+
+ if (abbrev->has_children)
+ {
+ if (!find_address_ranges (state, base_address, unit_buf,
+ dwarf_sections, is_bigendian, altlink,
+ error_callback, data, u, addrs, NULL))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Build a mapping from address ranges to the compilation units where
+ the line number information for that range can be found. Returns 1
+ on success, 0 on failure. */
+
+static int
+build_address_map (struct backtrace_state *state, uintptr_t base_address,
+ const struct dwarf_sections *dwarf_sections,
+ int is_bigendian, struct dwarf_data *altlink,
+ backtrace_error_callback error_callback, void *data,
+ struct unit_addrs_vector *addrs,
+ struct unit_vector *unit_vec)
+{
+ struct dwarf_buf info;
+ struct backtrace_vector units;
+ size_t units_count;
+ size_t i;
+ struct unit **pu;
+ size_t unit_offset = 0;
+ struct unit_addrs *pa;
+
+ memset (&addrs->vec, 0, sizeof addrs->vec);
+ memset (&unit_vec->vec, 0, sizeof unit_vec->vec);
+ addrs->count = 0;
+ unit_vec->count = 0;
+
+ /* Read through the .debug_info section. FIXME: Should we use the
+ .debug_aranges section? gdb and addr2line don't use it, but I'm
+ not sure why. */
+
+ info.name = ".debug_info";
+ info.start = dwarf_sections->data[DEBUG_INFO];
+ info.buf = info.start;
+ info.left = dwarf_sections->size[DEBUG_INFO];
+ info.is_bigendian = is_bigendian;
+ info.error_callback = error_callback;
+ info.data = data;
+ info.reported_underflow = 0;
+
+ memset (&units, 0, sizeof units);
+ units_count = 0;
+
+ while (info.left > 0)
+ {
+ const unsigned char *unit_data_start;
+ uint64_t len;
+ int is_dwarf64;
+ struct dwarf_buf unit_buf;
+ int version;
+ int unit_type;
+ uint64_t abbrev_offset;
+ int addrsize;
+ struct unit *u;
+ enum dwarf_tag unit_tag;
+
+ if (info.reported_underflow)
+ goto fail;
+
+ unit_data_start = info.buf;
+
+ len = read_initial_length (&info, &is_dwarf64);
+ unit_buf = info;
+ unit_buf.left = len;
+
+ if (!advance (&info, len))
+ goto fail;
+
+ version = read_uint16 (&unit_buf);
+ if (version < 2 || version > 5)
+ {
+ dwarf_buf_error (&unit_buf, "unrecognized DWARF version", -1);
+ goto fail;
+ }
+
+ if (version < 5)
+ unit_type = 0;
+ else
+ {
+ unit_type = read_byte (&unit_buf);
+ if (unit_type == DW_UT_type || unit_type == DW_UT_split_type)
+ {
+ /* This unit doesn't have anything we need. */
+ continue;
+ }
+ }
+
+ pu = ((struct unit **)
+ backtrace_vector_grow (state, sizeof (struct unit *),
+ error_callback, data, &units));
+ if (pu == NULL)
+ goto fail;
+
+ u = ((struct unit *)
+ backtrace_alloc (state, sizeof *u, error_callback, data));
+ if (u == NULL)
+ goto fail;
+
+ *pu = u;
+ ++units_count;
+
+ if (version < 5)
+ addrsize = 0; /* Set below. */
+ else
+ addrsize = read_byte (&unit_buf);
+
+ memset (&u->abbrevs, 0, sizeof u->abbrevs);
+ abbrev_offset = read_offset (&unit_buf, is_dwarf64);
+ if (!read_abbrevs (state, abbrev_offset,
+ dwarf_sections->data[DEBUG_ABBREV],
+ dwarf_sections->size[DEBUG_ABBREV],
+ is_bigendian, error_callback, data, &u->abbrevs))
+ goto fail;
+
+ if (version < 5)
+ addrsize = read_byte (&unit_buf);
+
+ switch (unit_type)
+ {
+ case 0:
+ break;
+ case DW_UT_compile: case DW_UT_partial:
+ break;
+ case DW_UT_skeleton: case DW_UT_split_compile:
+ read_uint64 (&unit_buf); /* dwo_id */
+ break;
+ default:
+ break;
+ }
+
+ u->low_offset = unit_offset;
+ unit_offset += len + (is_dwarf64 ? 12 : 4);
+ u->high_offset = unit_offset;
+ u->unit_data = unit_buf.buf;
+ u->unit_data_len = unit_buf.left;
+ u->unit_data_offset = unit_buf.buf - unit_data_start;
+ u->version = version;
+ u->is_dwarf64 = is_dwarf64;
+ u->addrsize = addrsize;
+ u->filename = NULL;
+ u->comp_dir = NULL;
+ u->abs_filename = NULL;
+ u->lineoff = 0;
+ u->str_offsets_base = 0;
+ u->addr_base = 0;
+ u->rnglists_base = 0;
+
+ /* The actual line number mappings will be read as needed. */
+ u->lines = NULL;
+ u->lines_count = 0;
+ u->function_addrs = NULL;
+ u->function_addrs_count = 0;
+
+ if (!find_address_ranges (state, base_address, &unit_buf, dwarf_sections,
+ is_bigendian, altlink, error_callback, data,
+ u, addrs, &unit_tag))
+ goto fail;
+
+ if (unit_buf.reported_underflow)
+ goto fail;
+ }
+ if (info.reported_underflow)
+ goto fail;
+
+ /* Add a trailing addrs entry, but don't include it in addrs->count. */
+ pa = ((struct unit_addrs *)
+ backtrace_vector_grow (state, sizeof (struct unit_addrs),
+ error_callback, data, &addrs->vec));
+ if (pa == NULL)
+ goto fail;
+ pa->low = 0;
+ --pa->low;
+ pa->high = pa->low;
+ pa->u = NULL;
+
+ unit_vec->vec = units;
+ unit_vec->count = units_count;
+ return 1;
+
+ fail:
+ if (units_count > 0)
+ {
+ pu = (struct unit **) units.base;
+ for (i = 0; i < units_count; i++)
+ {
+ free_abbrevs (state, &pu[i]->abbrevs, error_callback, data);
+ backtrace_free (state, pu[i], sizeof **pu, error_callback, data);
+ }
+ backtrace_vector_free (state, &units, error_callback, data);
+ }
+ if (addrs->count > 0)
+ {
+ backtrace_vector_free (state, &addrs->vec, error_callback, data);
+ addrs->count = 0;
+ }
+ return 0;
+}
+
+/* Add a new mapping to the vector of line mappings that we are
+ building. Returns 1 on success, 0 on failure. */
+
+static int
+add_line (struct backtrace_state *state, struct dwarf_data *ddata,
+ uintptr_t pc, const char *filename, int lineno,
+ backtrace_error_callback error_callback, void *data,
+ struct line_vector *vec)
+{
+ struct line *ln;
+
+ /* If we are adding the same mapping, ignore it. This can happen
+ when using discriminators. */
+ if (vec->count > 0)
+ {
+ ln = (struct line *) vec->vec.base + (vec->count - 1);
+ if (pc == ln->pc && filename == ln->filename && lineno == ln->lineno)
+ return 1;
+ }
+
+ ln = ((struct line *)
+ backtrace_vector_grow (state, sizeof (struct line), error_callback,
+ data, &vec->vec));
+ if (ln == NULL)
+ return 0;
+
+ /* Add in the base address here, so that we can look up the PC
+ directly. */
+ ln->pc = pc + ddata->base_address;
+
+ ln->filename = filename;
+ ln->lineno = lineno;
+ ln->idx = vec->count;
+
+ ++vec->count;
+
+ return 1;
+}
+
+/* Free the line header information. */
+
+static void
+free_line_header (struct backtrace_state *state, struct line_header *hdr,
+ backtrace_error_callback error_callback, void *data)
+{
+ if (hdr->dirs_count != 0)
+ backtrace_free (state, hdr->dirs, hdr->dirs_count * sizeof (const char *),
+ error_callback, data);
+ backtrace_free (state, hdr->filenames,
+ hdr->filenames_count * sizeof (char *),
+ error_callback, data);
+}
+
+/* Read the directories and file names for a line header for version
+ 2, setting fields in HDR. Return 1 on success, 0 on failure. */
+
+static int
+read_v2_paths (struct backtrace_state *state, struct unit *u,
+ struct dwarf_buf *hdr_buf, struct line_header *hdr)
+{
+ const unsigned char *p;
+ const unsigned char *pend;
+ size_t i;
+
+ /* Count the number of directory entries. */
+ hdr->dirs_count = 0;
+ p = hdr_buf->buf;
+ pend = p + hdr_buf->left;
+ while (p < pend && *p != '\0')
+ {
+ p += strnlen((const char *) p, pend - p) + 1;
+ ++hdr->dirs_count;
+ }
+
+ /* The index of the first entry in the list of directories is 1. Index 0 is
+ used for the current directory of the compilation. To simplify index
+ handling, we set entry 0 to the compilation unit directory. */
+ ++hdr->dirs_count;
+ hdr->dirs = ((const char **)
+ backtrace_alloc (state,
+ hdr->dirs_count * sizeof (const char *),
+ hdr_buf->error_callback,
+ hdr_buf->data));
+ if (hdr->dirs == NULL)
+ return 0;
+
+ hdr->dirs[0] = u->comp_dir;
+ i = 1;
+ while (*hdr_buf->buf != '\0')
+ {
+ if (hdr_buf->reported_underflow)
+ return 0;
+
+ hdr->dirs[i] = read_string (hdr_buf);
+ if (hdr->dirs[i] == NULL)
+ return 0;
+ ++i;
+ }
+ if (!advance (hdr_buf, 1))
+ return 0;
+
+ /* Count the number of file entries. */
+ hdr->filenames_count = 0;
+ p = hdr_buf->buf;
+ pend = p + hdr_buf->left;
+ while (p < pend && *p != '\0')
+ {
+ p += strnlen ((const char *) p, pend - p) + 1;
+ p += leb128_len (p);
+ p += leb128_len (p);
+ p += leb128_len (p);
+ ++hdr->filenames_count;
+ }
+
+ /* The index of the first entry in the list of file names is 1. Index 0 is
+ used for the DW_AT_name of the compilation unit. To simplify index
+ handling, we set entry 0 to the compilation unit file name. */
+ ++hdr->filenames_count;
+ hdr->filenames = ((const char **)
+ backtrace_alloc (state,
+ hdr->filenames_count * sizeof (char *),
+ hdr_buf->error_callback,
+ hdr_buf->data));
+ if (hdr->filenames == NULL)
+ return 0;
+ hdr->filenames[0] = u->filename;
+ i = 1;
+ while (*hdr_buf->buf != '\0')
+ {
+ const char *filename;
+ uint64_t dir_index;
+
+ if (hdr_buf->reported_underflow)
+ return 0;
+
+ filename = read_string (hdr_buf);
+ if (filename == NULL)
+ return 0;
+ dir_index = read_uleb128 (hdr_buf);
+ if (IS_ABSOLUTE_PATH (filename)
+ || (dir_index < hdr->dirs_count && hdr->dirs[dir_index] == NULL))
+ hdr->filenames[i] = filename;
+ else
+ {
+ const char *dir;
+ size_t dir_len;
+ size_t filename_len;
+ char *s;
+
+ if (dir_index < hdr->dirs_count)
+ dir = hdr->dirs[dir_index];
+ else
+ {
+ dwarf_buf_error (hdr_buf,
+ ("invalid directory index in "
+ "line number program header"),
+ 0);
+ return 0;
+ }
+ dir_len = strlen (dir);
+ filename_len = strlen (filename);
+ s = ((char *) backtrace_alloc (state, dir_len + filename_len + 2,
+ hdr_buf->error_callback,
+ hdr_buf->data));
+ if (s == NULL)
+ return 0;
+ memcpy (s, dir, dir_len);
+ /* FIXME: If we are on a DOS-based file system, and the
+ directory or the file name use backslashes, then we
+ should use a backslash here. */
+ s[dir_len] = '/';
+ memcpy (s + dir_len + 1, filename, filename_len + 1);
+ hdr->filenames[i] = s;
+ }
+
+ /* Ignore the modification time and size. */
+ read_uleb128 (hdr_buf);
+ read_uleb128 (hdr_buf);
+
+ ++i;
+ }
+
+ return 1;
+}
+
+/* Read a single version 5 LNCT entry for a directory or file name in a
+ line header. Sets *STRING to the resulting name, ignoring other
+ data. Return 1 on success, 0 on failure. */
+
+static int
+read_lnct (struct backtrace_state *state, struct dwarf_data *ddata,
+ struct unit *u, struct dwarf_buf *hdr_buf,
+ const struct line_header *hdr, size_t formats_count,
+ const struct line_header_format *formats, const char **string)
+{
+ size_t i;
+ const char *dir;
+ const char *path;
+
+ dir = NULL;
+ path = NULL;
+ for (i = 0; i < formats_count; i++)
+ {
+ struct attr_val val;
+
+ if (!read_attribute (formats[i].form, 0, hdr_buf, u->is_dwarf64,
+ u->version, hdr->addrsize, &ddata->dwarf_sections,
+ ddata->altlink, &val))
+ return 0;
+ switch (formats[i].lnct)
+ {
+ case DW_LNCT_path:
+ if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64,
+ ddata->is_bigendian, u->str_offsets_base,
+ &val, hdr_buf->error_callback, hdr_buf->data,
+ &path))
+ return 0;
+ break;
+ case DW_LNCT_directory_index:
+ if (val.encoding == ATTR_VAL_UINT)
+ {
+ if (val.u.uint >= hdr->dirs_count)
+ {
+ dwarf_buf_error (hdr_buf,
+ ("invalid directory index in "
+ "line number program header"),
+ 0);
+ return 0;
+ }
+ dir = hdr->dirs[val.u.uint];
+ }
+ break;
+ default:
+ /* We don't care about timestamps or sizes or hashes. */
+ break;
+ }
+ }
+
+ if (path == NULL)
+ {
+ dwarf_buf_error (hdr_buf,
+ "missing file name in line number program header",
+ 0);
+ return 0;
+ }
+
+ if (dir == NULL)
+ *string = path;
+ else
+ {
+ size_t dir_len;
+ size_t path_len;
+ char *s;
+
+ dir_len = strlen (dir);
+ path_len = strlen (path);
+ s = (char *) backtrace_alloc (state, dir_len + path_len + 2,
+ hdr_buf->error_callback, hdr_buf->data);
+ if (s == NULL)
+ return 0;
+ memcpy (s, dir, dir_len);
+ /* FIXME: If we are on a DOS-based file system, and the
+ directory or the path name use backslashes, then we should
+ use a backslash here. */
+ s[dir_len] = '/';
+ memcpy (s + dir_len + 1, path, path_len + 1);
+ *string = s;
+ }
+
+ return 1;
+}
+
+/* Read a set of DWARF 5 line header format entries, setting *PCOUNT
+ and *PPATHS. Return 1 on success, 0 on failure. */
+
+static int
+read_line_header_format_entries (struct backtrace_state *state,
+ struct dwarf_data *ddata,
+ struct unit *u,
+ struct dwarf_buf *hdr_buf,
+ struct line_header *hdr,
+ size_t *pcount,
+ const char ***ppaths)
+{
+ size_t formats_count;
+ struct line_header_format *formats;
+ size_t paths_count;
+ const char **paths;
+ size_t i;
+ int ret;
+
+ formats_count = read_byte (hdr_buf);
+ if (formats_count == 0)
+ formats = NULL;
+ else
+ {
+ formats = ((struct line_header_format *)
+ backtrace_alloc (state,
+ (formats_count
+ * sizeof (struct line_header_format)),
+ hdr_buf->error_callback,
+ hdr_buf->data));
+ if (formats == NULL)
+ return 0;
+
+ for (i = 0; i < formats_count; i++)
+ {
+ formats[i].lnct = (int) read_uleb128(hdr_buf);
+ formats[i].form = (enum dwarf_form) read_uleb128 (hdr_buf);
+ }
+ }
+
+ paths_count = read_uleb128 (hdr_buf);
+ if (paths_count == 0)
+ {
+ *pcount = 0;
+ *ppaths = NULL;
+ ret = 1;
+ goto exit;
+ }
+
+ paths = ((const char **)
+ backtrace_alloc (state, paths_count * sizeof (const char *),
+ hdr_buf->error_callback, hdr_buf->data));
+ if (paths == NULL)
+ {
+ ret = 0;
+ goto exit;
+ }
+ for (i = 0; i < paths_count; i++)
+ {
+ if (!read_lnct (state, ddata, u, hdr_buf, hdr, formats_count,
+ formats, &paths[i]))
+ {
+ backtrace_free (state, paths,
+ paths_count * sizeof (const char *),
+ hdr_buf->error_callback, hdr_buf->data);
+ ret = 0;
+ goto exit;
+ }
+ }
+
+ *pcount = paths_count;
+ *ppaths = paths;
+
+ ret = 1;
+
+ exit:
+ if (formats != NULL)
+ backtrace_free (state, formats,
+ formats_count * sizeof (struct line_header_format),
+ hdr_buf->error_callback, hdr_buf->data);
+
+ return ret;
+}
+
+/* Read the line header. Return 1 on success, 0 on failure. */
+
+static int
+read_line_header (struct backtrace_state *state, struct dwarf_data *ddata,
+ struct unit *u, int is_dwarf64, struct dwarf_buf *line_buf,
+ struct line_header *hdr)
+{
+ uint64_t hdrlen;
+ struct dwarf_buf hdr_buf;
+
+ hdr->version = read_uint16 (line_buf);
+ if (hdr->version < 2 || hdr->version > 5)
+ {
+ dwarf_buf_error (line_buf, "unsupported line number version", -1);
+ return 0;
+ }
+
+ if (hdr->version < 5)
+ hdr->addrsize = u->addrsize;
+ else
+ {
+ hdr->addrsize = read_byte (line_buf);
+ /* We could support a non-zero segment_selector_size but I doubt
+ we'll ever see it. */
+ if (read_byte (line_buf) != 0)
+ {
+ dwarf_buf_error (line_buf,
+ "non-zero segment_selector_size not supported",
+ -1);
+ return 0;
+ }
+ }
+
+ hdrlen = read_offset (line_buf, is_dwarf64);
+
+ hdr_buf = *line_buf;
+ hdr_buf.left = hdrlen;
+
+ if (!advance (line_buf, hdrlen))
+ return 0;
+
+ hdr->min_insn_len = read_byte (&hdr_buf);
+ if (hdr->version < 4)
+ hdr->max_ops_per_insn = 1;
+ else
+ hdr->max_ops_per_insn = read_byte (&hdr_buf);
+
+ /* We don't care about default_is_stmt. */
+ read_byte (&hdr_buf);
+
+ hdr->line_base = read_sbyte (&hdr_buf);
+ hdr->line_range = read_byte (&hdr_buf);
+
+ hdr->opcode_base = read_byte (&hdr_buf);
+ hdr->opcode_lengths = hdr_buf.buf;
+ if (!advance (&hdr_buf, hdr->opcode_base - 1))
+ return 0;
+
+ if (hdr->version < 5)
+ {
+ if (!read_v2_paths (state, u, &hdr_buf, hdr))
+ return 0;
+ }
+ else
+ {
+ if (!read_line_header_format_entries (state, ddata, u, &hdr_buf, hdr,
+ &hdr->dirs_count,
+ &hdr->dirs))
+ return 0;
+ if (!read_line_header_format_entries (state, ddata, u, &hdr_buf, hdr,
+ &hdr->filenames_count,
+ &hdr->filenames))
+ return 0;
+ }
+
+ if (hdr_buf.reported_underflow)
+ return 0;
+
+ return 1;
+}
+
+/* Read the line program, adding line mappings to VEC. Return 1 on
+ success, 0 on failure. */
+
+static int
+read_line_program (struct backtrace_state *state, struct dwarf_data *ddata,
+ const struct line_header *hdr, struct dwarf_buf *line_buf,
+ struct line_vector *vec)
+{
+ uint64_t address;
+ unsigned int op_index;
+ const char *reset_filename;
+ const char *filename;
+ int lineno;
+
+ address = 0;
+ op_index = 0;
+ if (hdr->filenames_count > 1)
+ reset_filename = hdr->filenames[1];
+ else
+ reset_filename = "";
+ filename = reset_filename;
+ lineno = 1;
+ while (line_buf->left > 0)
+ {
+ unsigned int op;
+
+ op = read_byte (line_buf);
+ if (op >= hdr->opcode_base)
+ {
+ unsigned int advance;
+
+ /* Special opcode. */
+ op -= hdr->opcode_base;
+ advance = op / hdr->line_range;
+ address += (hdr->min_insn_len * (op_index + advance)
+ / hdr->max_ops_per_insn);
+ op_index = (op_index + advance) % hdr->max_ops_per_insn;
+ lineno += hdr->line_base + (int) (op % hdr->line_range);
+ add_line (state, ddata, address, filename, lineno,
+ line_buf->error_callback, line_buf->data, vec);
+ }
+ else if (op == DW_LNS_extended_op)
+ {
+ uint64_t len;
+
+ len = read_uleb128 (line_buf);
+ op = read_byte (line_buf);
+ switch (op)
+ {
+ case DW_LNE_end_sequence:
+ /* FIXME: Should we mark the high PC here? It seems
+ that we already have that information from the
+ compilation unit. */
+ address = 0;
+ op_index = 0;
+ filename = reset_filename;
+ lineno = 1;
+ break;
+ case DW_LNE_set_address:
+ address = read_address (line_buf, hdr->addrsize);
+ break;
+ case DW_LNE_define_file:
+ {
+ const char *f;
+ unsigned int dir_index;
+
+ f = read_string (line_buf);
+ if (f == NULL)
+ return 0;
+ dir_index = read_uleb128 (line_buf);
+ /* Ignore that time and length. */
+ read_uleb128 (line_buf);
+ read_uleb128 (line_buf);
+ if (IS_ABSOLUTE_PATH (f))
+ filename = f;
+ else
+ {
+ const char *dir;
+ size_t dir_len;
+ size_t f_len;
+ char *p;
+
+ if (dir_index < hdr->dirs_count)
+ dir = hdr->dirs[dir_index];
+ else
+ {
+ dwarf_buf_error (line_buf,
+ ("invalid directory index "
+ "in line number program"),
+ 0);
+ return 0;
+ }
+ dir_len = strlen (dir);
+ f_len = strlen (f);
+ p = ((char *)
+ backtrace_alloc (state, dir_len + f_len + 2,
+ line_buf->error_callback,
+ line_buf->data));
+ if (p == NULL)
+ return 0;
+ memcpy (p, dir, dir_len);
+ /* FIXME: If we are on a DOS-based file system,
+ and the directory or the file name use
+ backslashes, then we should use a backslash
+ here. */
+ p[dir_len] = '/';
+ memcpy (p + dir_len + 1, f, f_len + 1);
+ filename = p;
+ }
+ }
+ break;
+ case DW_LNE_set_discriminator:
+ /* We don't care about discriminators. */
+ read_uleb128 (line_buf);
+ break;
+ default:
+ if (!advance (line_buf, len - 1))
+ return 0;
+ break;
+ }
+ }
+ else
+ {
+ switch (op)
+ {
+ case DW_LNS_copy:
+ add_line (state, ddata, address, filename, lineno,
+ line_buf->error_callback, line_buf->data, vec);
+ break;
+ case DW_LNS_advance_pc:
+ {
+ uint64_t advance;
+
+ advance = read_uleb128 (line_buf);
+ address += (hdr->min_insn_len * (op_index + advance)
+ / hdr->max_ops_per_insn);
+ op_index = (op_index + advance) % hdr->max_ops_per_insn;
+ }
+ break;
+ case DW_LNS_advance_line:
+ lineno += (int) read_sleb128 (line_buf);
+ break;
+ case DW_LNS_set_file:
+ {
+ uint64_t fileno;
+
+ fileno = read_uleb128 (line_buf);
+ if (fileno >= hdr->filenames_count)
+ {
+ dwarf_buf_error (line_buf,
+ ("invalid file number in "
+ "line number program"),
+ 0);
+ return 0;
+ }
+ filename = hdr->filenames[fileno];
+ }
+ break;
+ case DW_LNS_set_column:
+ read_uleb128 (line_buf);
+ break;
+ case DW_LNS_negate_stmt:
+ break;
+ case DW_LNS_set_basic_block:
+ break;
+ case DW_LNS_const_add_pc:
+ {
+ unsigned int advance;
+
+ op = 255 - hdr->opcode_base;
+ advance = op / hdr->line_range;
+ address += (hdr->min_insn_len * (op_index + advance)
+ / hdr->max_ops_per_insn);
+ op_index = (op_index + advance) % hdr->max_ops_per_insn;
+ }
+ break;
+ case DW_LNS_fixed_advance_pc:
+ address += read_uint16 (line_buf);
+ op_index = 0;
+ break;
+ case DW_LNS_set_prologue_end:
+ break;
+ case DW_LNS_set_epilogue_begin:
+ break;
+ case DW_LNS_set_isa:
+ read_uleb128 (line_buf);
+ break;
+ default:
+ {
+ unsigned int i;
+
+ for (i = hdr->opcode_lengths[op - 1]; i > 0; --i)
+ read_uleb128 (line_buf);
+ }
+ break;
+ }
+ }
+ }
+
+ return 1;
+}
+
+/* Read the line number information for a compilation unit. Returns 1
+ on success, 0 on failure. */
+
+static int
+read_line_info (struct backtrace_state *state, struct dwarf_data *ddata,
+ backtrace_error_callback error_callback, void *data,
+ struct unit *u, struct line_header *hdr, struct line **lines,
+ size_t *lines_count)
+{
+ struct line_vector vec;
+ struct dwarf_buf line_buf;
+ uint64_t len;
+ int is_dwarf64;
+ struct line *ln;
+
+ memset (&vec.vec, 0, sizeof vec.vec);
+ vec.count = 0;
+
+ memset (hdr, 0, sizeof *hdr);
+
+ if (u->lineoff != (off_t) (size_t) u->lineoff
+ || (size_t) u->lineoff >= ddata->dwarf_sections.size[DEBUG_LINE])
+ {
+ error_callback (data, "unit line offset out of range", 0);
+ goto fail;
+ }
+
+ line_buf.name = ".debug_line";
+ line_buf.start = ddata->dwarf_sections.data[DEBUG_LINE];
+ line_buf.buf = ddata->dwarf_sections.data[DEBUG_LINE] + u->lineoff;
+ line_buf.left = ddata->dwarf_sections.size[DEBUG_LINE] - u->lineoff;
+ line_buf.is_bigendian = ddata->is_bigendian;
+ line_buf.error_callback = error_callback;
+ line_buf.data = data;
+ line_buf.reported_underflow = 0;
+
+ len = read_initial_length (&line_buf, &is_dwarf64);
+ line_buf.left = len;
+
+ if (!read_line_header (state, ddata, u, is_dwarf64, &line_buf, hdr))
+ goto fail;
+
+ if (!read_line_program (state, ddata, hdr, &line_buf, &vec))
+ goto fail;
+
+ if (line_buf.reported_underflow)
+ goto fail;
+
+ if (vec.count == 0)
+ {
+ /* This is not a failure in the sense of a generating an error,
+ but it is a failure in that sense that we have no useful
+ information. */
+ goto fail;
+ }
+
+ /* Allocate one extra entry at the end. */
+ ln = ((struct line *)
+ backtrace_vector_grow (state, sizeof (struct line), error_callback,
+ data, &vec.vec));
+ if (ln == NULL)
+ goto fail;
+ ln->pc = (uintptr_t) -1;
+ ln->filename = NULL;
+ ln->lineno = 0;
+ ln->idx = 0;
+
+ if (!backtrace_vector_release (state, &vec.vec, error_callback, data))
+ goto fail;
+
+ ln = (struct line *) vec.vec.base;
+ backtrace_qsort (ln, vec.count, sizeof (struct line), line_compare);
+
+ *lines = ln;
+ *lines_count = vec.count;
+
+ return 1;
+
+ fail:
+ backtrace_vector_free (state, &vec.vec, error_callback, data);
+ free_line_header (state, hdr, error_callback, data);
+ *lines = (struct line *) (uintptr_t) -1;
+ *lines_count = 0;
+ return 0;
+}
+
+static const char *read_referenced_name (struct dwarf_data *, struct unit *,
+ uint64_t, backtrace_error_callback,
+ void *);
+
+/* Read the name of a function from a DIE referenced by ATTR with VAL. */
+
+static const char *
+read_referenced_name_from_attr (struct dwarf_data *ddata, struct unit *u,
+ struct attr *attr, struct attr_val *val,
+ backtrace_error_callback error_callback,
+ void *data)
+{
+ switch (attr->name)
+ {
+ case DW_AT_abstract_origin:
+ case DW_AT_specification:
+ break;
+ default:
+ return NULL;
+ }
+
+ if (attr->form == DW_FORM_ref_sig8)
+ return NULL;
+
+ if (val->encoding == ATTR_VAL_REF_INFO)
+ {
+ struct unit *unit
+ = find_unit (ddata->units, ddata->units_count,
+ val->u.uint);
+ if (unit == NULL)
+ return NULL;
+
+ uint64_t offset = val->u.uint - unit->low_offset;
+ return read_referenced_name (ddata, unit, offset, error_callback, data);
+ }
+
+ if (val->encoding == ATTR_VAL_UINT
+ || val->encoding == ATTR_VAL_REF_UNIT)
+ return read_referenced_name (ddata, u, val->u.uint, error_callback, data);
+
+ if (val->encoding == ATTR_VAL_REF_ALT_INFO)
+ {
+ struct unit *alt_unit
+ = find_unit (ddata->altlink->units, ddata->altlink->units_count,
+ val->u.uint);
+ if (alt_unit == NULL)
+ return NULL;
+
+ uint64_t offset = val->u.uint - alt_unit->low_offset;
+ return read_referenced_name (ddata->altlink, alt_unit, offset,
+ error_callback, data);
+ }
+
+ return NULL;
+}
+
+/* Read the name of a function from a DIE referenced by a
+ DW_AT_abstract_origin or DW_AT_specification tag. OFFSET is within
+ the same compilation unit. */
+
+static const char *
+read_referenced_name (struct dwarf_data *ddata, struct unit *u,
+ uint64_t offset, backtrace_error_callback error_callback,
+ void *data)
+{
+ struct dwarf_buf unit_buf;
+ uint64_t code;
+ const struct abbrev *abbrev;
+ const char *ret;
+ size_t i;
+
+ /* OFFSET is from the start of the data for this compilation unit.
+ U->unit_data is the data, but it starts U->unit_data_offset bytes
+ from the beginning. */
+
+ if (offset < u->unit_data_offset
+ || offset - u->unit_data_offset >= u->unit_data_len)
+ {
+ error_callback (data,
+ "abstract origin or specification out of range",
+ 0);
+ return NULL;
+ }
+
+ offset -= u->unit_data_offset;
+
+ unit_buf.name = ".debug_info";
+ unit_buf.start = ddata->dwarf_sections.data[DEBUG_INFO];
+ unit_buf.buf = u->unit_data + offset;
+ unit_buf.left = u->unit_data_len - offset;
+ unit_buf.is_bigendian = ddata->is_bigendian;
+ unit_buf.error_callback = error_callback;
+ unit_buf.data = data;
+ unit_buf.reported_underflow = 0;
+
+ code = read_uleb128 (&unit_buf);
+ if (code == 0)
+ {
+ dwarf_buf_error (&unit_buf,
+ "invalid abstract origin or specification",
+ 0);
+ return NULL;
+ }
+
+ abbrev = lookup_abbrev (&u->abbrevs, code, error_callback, data);
+ if (abbrev == NULL)
+ return NULL;
+
+ ret = NULL;
+ for (i = 0; i < abbrev->num_attrs; ++i)
+ {
+ struct attr_val val;
+
+ if (!read_attribute (abbrev->attrs[i].form, abbrev->attrs[i].val,
+ &unit_buf, u->is_dwarf64, u->version, u->addrsize,
+ &ddata->dwarf_sections, ddata->altlink, &val))
+ return NULL;
+
+ switch (abbrev->attrs[i].name)
+ {
+ case DW_AT_name:
+ /* Third name preference: don't override. A name we found in some
+ other way, will normally be more useful -- e.g., this name is
+ normally not mangled. */
+ if (ret != NULL)
+ break;
+ if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64,
+ ddata->is_bigendian, u->str_offsets_base,
+ &val, error_callback, data, &ret))
+ return NULL;
+ break;
+
+ case DW_AT_linkage_name:
+ case DW_AT_MIPS_linkage_name:
+ /* First name preference: override all. */
+ {
+ const char *s;
+
+ s = NULL;
+ if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64,
+ ddata->is_bigendian, u->str_offsets_base,
+ &val, error_callback, data, &s))
+ return NULL;
+ if (s != NULL)
+ return s;
+ }
+ break;
+
+ case DW_AT_specification:
+ /* Second name preference: override DW_AT_name, don't override
+ DW_AT_linkage_name. */
+ {
+ const char *name;
+
+ name = read_referenced_name_from_attr (ddata, u, &abbrev->attrs[i],
+ &val, error_callback, data);
+ if (name != NULL)
+ ret = name;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/* Add a range to a unit that maps to a function. This is called via
+ add_ranges. Returns 1 on success, 0 on error. */
+
+static int
+add_function_range (struct backtrace_state *state, void *rdata,
+ uint64_t lowpc, uint64_t highpc,
+ backtrace_error_callback error_callback, void *data,
+ void *pvec)
+{
+ struct function *function = (struct function *) rdata;
+ struct function_vector *vec = (struct function_vector *) pvec;
+ struct function_addrs *p;
+
+ if (vec->count > 0)
+ {
+ p = (struct function_addrs *) vec->vec.base + (vec->count - 1);
+ if ((lowpc == p->high || lowpc == p->high + 1)
+ && function == p->function)
+ {
+ if (highpc > p->high)
+ p->high = highpc;
+ return 1;
+ }
+ }
+
+ p = ((struct function_addrs *)
+ backtrace_vector_grow (state, sizeof (struct function_addrs),
+ error_callback, data, &vec->vec));
+ if (p == NULL)
+ return 0;
+
+ p->low = lowpc;
+ p->high = highpc;
+ p->function = function;
+
+ ++vec->count;
+
+ return 1;
+}
+
+/* Read one entry plus all its children. Add function addresses to
+ VEC. Returns 1 on success, 0 on error. */
+
+static int
+read_function_entry (struct backtrace_state *state, struct dwarf_data *ddata,
+ struct unit *u, uint64_t base, struct dwarf_buf *unit_buf,
+ const struct line_header *lhdr,
+ backtrace_error_callback error_callback, void *data,
+ struct function_vector *vec_function,
+ struct function_vector *vec_inlined)
+{
+ while (unit_buf->left > 0)
+ {
+ uint64_t code;
+ const struct abbrev *abbrev;
+ int is_function;
+ struct function *function;
+ struct function_vector *vec;
+ size_t i;
+ struct pcrange pcrange;
+ int have_linkage_name;
+
+ code = read_uleb128 (unit_buf);
+ if (code == 0)
+ return 1;
+
+ abbrev = lookup_abbrev (&u->abbrevs, code, error_callback, data);
+ if (abbrev == NULL)
+ return 0;
+
+ is_function = (abbrev->tag == DW_TAG_subprogram
+ || abbrev->tag == DW_TAG_entry_point
+ || abbrev->tag == DW_TAG_inlined_subroutine);
+
+ if (abbrev->tag == DW_TAG_inlined_subroutine)
+ vec = vec_inlined;
+ else
+ vec = vec_function;
+
+ function = NULL;
+ if (is_function)
+ {
+ function = ((struct function *)
+ backtrace_alloc (state, sizeof *function,
+ error_callback, data));
+ if (function == NULL)
+ return 0;
+ memset (function, 0, sizeof *function);
+ }
+
+ memset (&pcrange, 0, sizeof pcrange);
+ have_linkage_name = 0;
+ for (i = 0; i < abbrev->num_attrs; ++i)
+ {
+ struct attr_val val;
+
+ if (!read_attribute (abbrev->attrs[i].form, abbrev->attrs[i].val,
+ unit_buf, u->is_dwarf64, u->version,
+ u->addrsize, &ddata->dwarf_sections,
+ ddata->altlink, &val))
+ return 0;
+
+ /* The compile unit sets the base address for any address
+ ranges in the function entries. */
+ if ((abbrev->tag == DW_TAG_compile_unit
+ || abbrev->tag == DW_TAG_skeleton_unit)
+ && abbrev->attrs[i].name == DW_AT_low_pc)
+ {
+ if (val.encoding == ATTR_VAL_ADDRESS)
+ base = val.u.uint;
+ else if (val.encoding == ATTR_VAL_ADDRESS_INDEX)
+ {
+ if (!resolve_addr_index (&ddata->dwarf_sections,
+ u->addr_base, u->addrsize,
+ ddata->is_bigendian, val.u.uint,
+ error_callback, data, &base))
+ return 0;
+ }
+ }
+
+ if (is_function)
+ {
+ switch (abbrev->attrs[i].name)
+ {
+ case DW_AT_call_file:
+ if (val.encoding == ATTR_VAL_UINT)
+ {
+ if (val.u.uint >= lhdr->filenames_count)
+ {
+ dwarf_buf_error (unit_buf,
+ ("invalid file number in "
+ "DW_AT_call_file attribute"),
+ 0);
+ return 0;
+ }
+ function->caller_filename = lhdr->filenames[val.u.uint];
+ }
+ break;
+
+ case DW_AT_call_line:
+ if (val.encoding == ATTR_VAL_UINT)
+ function->caller_lineno = val.u.uint;
+ break;
+
+ case DW_AT_abstract_origin:
+ case DW_AT_specification:
+ /* Second name preference: override DW_AT_name, don't override
+ DW_AT_linkage_name. */
+ if (have_linkage_name)
+ break;
+ {
+ const char *name;
+
+ name
+ = read_referenced_name_from_attr (ddata, u,
+ &abbrev->attrs[i], &val,
+ error_callback, data);
+ if (name != NULL)
+ function->name = name;
+ }
+ break;
+
+ case DW_AT_name:
+ /* Third name preference: don't override. */
+ if (function->name != NULL)
+ break;
+ if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64,
+ ddata->is_bigendian,
+ u->str_offsets_base, &val,
+ error_callback, data, &function->name))
+ return 0;
+ break;
+
+ case DW_AT_linkage_name:
+ case DW_AT_MIPS_linkage_name:
+ /* First name preference: override all. */
+ {
+ const char *s;
+
+ s = NULL;
+ if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64,
+ ddata->is_bigendian,
+ u->str_offsets_base, &val,
+ error_callback, data, &s))
+ return 0;
+ if (s != NULL)
+ {
+ function->name = s;
+ have_linkage_name = 1;
+ }
+ }
+ break;
+
+ case DW_AT_low_pc: case DW_AT_high_pc: case DW_AT_ranges:
+ update_pcrange (&abbrev->attrs[i], &val, &pcrange);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ /* If we couldn't find a name for the function, we have no use
+ for it. */
+ if (is_function && function->name == NULL)
+ {
+ backtrace_free (state, function, sizeof *function,
+ error_callback, data);
+ is_function = 0;
+ }
+
+ if (is_function)
+ {
+ if (pcrange.have_ranges
+ || (pcrange.have_lowpc && pcrange.have_highpc))
+ {
+ if (!add_ranges (state, &ddata->dwarf_sections,
+ ddata->base_address, ddata->is_bigendian,
+ u, base, &pcrange, add_function_range,
+ (void *) function, error_callback, data,
+ (void *) vec))
+ return 0;
+ }
+ else
+ {
+ backtrace_free (state, function, sizeof *function,
+ error_callback, data);
+ is_function = 0;
+ }
+ }
+
+ if (abbrev->has_children)
+ {
+ if (!is_function)
+ {
+ if (!read_function_entry (state, ddata, u, base, unit_buf, lhdr,
+ error_callback, data, vec_function,
+ vec_inlined))
+ return 0;
+ }
+ else
+ {
+ struct function_vector fvec;
+
+ /* Gather any information for inlined functions in
+ FVEC. */
+
+ memset (&fvec, 0, sizeof fvec);
+
+ if (!read_function_entry (state, ddata, u, base, unit_buf, lhdr,
+ error_callback, data, vec_function,
+ &fvec))
+ return 0;
+
+ if (fvec.count > 0)
+ {
+ struct function_addrs *p;
+ struct function_addrs *faddrs;
+
+ /* Allocate a trailing entry, but don't include it
+ in fvec.count. */
+ p = ((struct function_addrs *)
+ backtrace_vector_grow (state,
+ sizeof (struct function_addrs),
+ error_callback, data,
+ &fvec.vec));
+ if (p == NULL)
+ return 0;
+ p->low = 0;
+ --p->low;
+ p->high = p->low;
+ p->function = NULL;
+
+ if (!backtrace_vector_release (state, &fvec.vec,
+ error_callback, data))
+ return 0;
+
+ faddrs = (struct function_addrs *) fvec.vec.base;
+ backtrace_qsort (faddrs, fvec.count,
+ sizeof (struct function_addrs),
+ function_addrs_compare);
+
+ function->function_addrs = faddrs;
+ function->function_addrs_count = fvec.count;
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+/* Read function name information for a compilation unit. We look
+ through the whole unit looking for function tags. */
+
+static void
+read_function_info (struct backtrace_state *state, struct dwarf_data *ddata,
+ const struct line_header *lhdr,
+ backtrace_error_callback error_callback, void *data,
+ struct unit *u, struct function_vector *fvec,
+ struct function_addrs **ret_addrs,
+ size_t *ret_addrs_count)
+{
+ struct function_vector lvec;
+ struct function_vector *pfvec;
+ struct dwarf_buf unit_buf;
+ struct function_addrs *p;
+ struct function_addrs *addrs;
+ size_t addrs_count;
+
+ /* Use FVEC if it is not NULL. Otherwise use our own vector. */
+ if (fvec != NULL)
+ pfvec = fvec;
+ else
+ {
+ memset (&lvec, 0, sizeof lvec);
+ pfvec = &lvec;
+ }
+
+ unit_buf.name = ".debug_info";
+ unit_buf.start = ddata->dwarf_sections.data[DEBUG_INFO];
+ unit_buf.buf = u->unit_data;
+ unit_buf.left = u->unit_data_len;
+ unit_buf.is_bigendian = ddata->is_bigendian;
+ unit_buf.error_callback = error_callback;
+ unit_buf.data = data;
+ unit_buf.reported_underflow = 0;
+
+ while (unit_buf.left > 0)
+ {
+ if (!read_function_entry (state, ddata, u, 0, &unit_buf, lhdr,
+ error_callback, data, pfvec, pfvec))
+ return;
+ }
+
+ if (pfvec->count == 0)
+ return;
+
+ /* Allocate a trailing entry, but don't include it in
+ pfvec->count. */
+ p = ((struct function_addrs *)
+ backtrace_vector_grow (state, sizeof (struct function_addrs),
+ error_callback, data, &pfvec->vec));
+ if (p == NULL)
+ return;
+ p->low = 0;
+ --p->low;
+ p->high = p->low;
+ p->function = NULL;
+
+ addrs_count = pfvec->count;
+
+ if (fvec == NULL)
+ {
+ if (!backtrace_vector_release (state, &lvec.vec, error_callback, data))
+ return;
+ addrs = (struct function_addrs *) pfvec->vec.base;
+ }
+ else
+ {
+ /* Finish this list of addresses, but leave the remaining space in
+ the vector available for the next function unit. */
+ addrs = ((struct function_addrs *)
+ backtrace_vector_finish (state, &fvec->vec,
+ error_callback, data));
+ if (addrs == NULL)
+ return;
+ fvec->count = 0;
+ }
+
+ backtrace_qsort (addrs, addrs_count, sizeof (struct function_addrs),
+ function_addrs_compare);
+
+ *ret_addrs = addrs;
+ *ret_addrs_count = addrs_count;
+}
+
+/* See if PC is inlined in FUNCTION. If it is, print out the inlined
+ information, and update FILENAME and LINENO for the caller.
+ Returns whatever CALLBACK returns, or 0 to keep going. */
+
+static int
+report_inlined_functions (uintptr_t pc, struct function *function,
+ backtrace_full_callback callback, void *data,
+ const char **filename, int *lineno)
+{
+ struct function_addrs *p;
+ struct function_addrs *match;
+ struct function *inlined;
+ int ret;
+
+ if (function->function_addrs_count == 0)
+ return 0;
+
+ /* Our search isn't safe if pc == -1, as that is the sentinel
+ value. */
+ if (pc + 1 == 0)
+ return 0;
+
+ p = ((struct function_addrs *)
+ bsearch (&pc, function->function_addrs,
+ function->function_addrs_count,
+ sizeof (struct function_addrs),
+ function_addrs_search));
+ if (p == NULL)
+ return 0;
+
+ /* Here pc >= p->low && pc < (p + 1)->low. The function_addrs are
+ sorted by low, so if pc > p->low we are at the end of a range of
+ function_addrs with the same low value. If pc == p->low walk
+ forward to the end of the range with that low value. Then walk
+ backward and use the first range that includes pc. */
+ while (pc == (p + 1)->low)
+ ++p;
+ match = NULL;
+ while (1)
+ {
+ if (pc < p->high)
+ {
+ match = p;
+ break;
+ }
+ if (p == function->function_addrs)
+ break;
+ if ((p - 1)->low < p->low)
+ break;
+ --p;
+ }
+ if (match == NULL)
+ return 0;
+
+ /* We found an inlined call. */
+
+ inlined = match->function;
+
+ /* Report any calls inlined into this one. */
+ ret = report_inlined_functions (pc, inlined, callback, data,
+ filename, lineno);
+ if (ret != 0)
+ return ret;
+
+ /* Report this inlined call. */
+ ret = callback (data, pc, *filename, *lineno, inlined->name);
+ if (ret != 0)
+ return ret;
+
+ /* Our caller will report the caller of the inlined function; tell
+ it the appropriate filename and line number. */
+ *filename = inlined->caller_filename;
+ *lineno = inlined->caller_lineno;
+
+ return 0;
+}
+
+/* Look for a PC in the DWARF mapping for one module. On success,
+ call CALLBACK and return whatever it returns. On error, call
+ ERROR_CALLBACK and return 0. Sets *FOUND to 1 if the PC is found,
+ 0 if not. */
+
+static int
+dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
+ uintptr_t pc, backtrace_full_callback callback,
+ backtrace_error_callback error_callback, void *data,
+ int *found)
+{
+ struct unit_addrs *entry;
+ int found_entry;
+ struct unit *u;
+ int new_data;
+ struct line *lines;
+ struct line *ln;
+ struct function_addrs *p;
+ struct function_addrs *fmatch;
+ struct function *function;
+ const char *filename;
+ int lineno;
+ int ret;
+
+ *found = 1;
+
+ /* Find an address range that includes PC. Our search isn't safe if
+ PC == -1, as we use that as a sentinel value, so skip the search
+ in that case. */
+ entry = (ddata->addrs_count == 0 || pc + 1 == 0
+ ? NULL
+ : bsearch (&pc, ddata->addrs, ddata->addrs_count,
+ sizeof (struct unit_addrs), unit_addrs_search));
+
+ if (entry == NULL)
+ {
+ *found = 0;
+ return 0;
+ }
+
+ /* Here pc >= entry->low && pc < (entry + 1)->low. The unit_addrs
+ are sorted by low, so if pc > p->low we are at the end of a range
+ of unit_addrs with the same low value. If pc == p->low walk
+ forward to the end of the range with that low value. Then walk
+ backward and use the first range that includes pc. */
+ while (pc == (entry + 1)->low)
+ ++entry;
+ found_entry = 0;
+ while (1)
+ {
+ if (pc < entry->high)
+ {
+ found_entry = 1;
+ break;
+ }
+ if (entry == ddata->addrs)
+ break;
+ if ((entry - 1)->low < entry->low)
+ break;
+ --entry;
+ }
+ if (!found_entry)
+ {
+ *found = 0;
+ return 0;
+ }
+
+ /* We need the lines, lines_count, function_addrs,
+ function_addrs_count fields of u. If they are not set, we need
+ to set them. When running in threaded mode, we need to allow for
+ the possibility that some other thread is setting them
+ simultaneously. */
+
+ u = entry->u;
+ lines = u->lines;
+
+ /* Skip units with no useful line number information by walking
+ backward. Useless line number information is marked by setting
+ lines == -1. */
+ while (entry > ddata->addrs
+ && pc >= (entry - 1)->low
+ && pc < (entry - 1)->high)
+ {
+ if (state->threaded)
+ lines = (struct line *) backtrace_atomic_load_pointer (&u->lines);
+
+ if (lines != (struct line *) (uintptr_t) -1)
+ break;
+
+ --entry;
+
+ u = entry->u;
+ lines = u->lines;
+ }
+
+ if (state->threaded)
+ lines = backtrace_atomic_load_pointer (&u->lines);
+
+ new_data = 0;
+ if (lines == NULL)
+ {
+ struct function_addrs *function_addrs;
+ size_t function_addrs_count;
+ struct line_header lhdr;
+ size_t count;
+
+ /* We have never read the line information for this unit. Read
+ it now. */
+
+ function_addrs = NULL;
+ function_addrs_count = 0;
+ if (read_line_info (state, ddata, error_callback, data, entry->u, &lhdr,
+ &lines, &count))
+ {
+ struct function_vector *pfvec;
+
+ /* If not threaded, reuse DDATA->FVEC for better memory
+ consumption. */
+ if (state->threaded)
+ pfvec = NULL;
+ else
+ pfvec = &ddata->fvec;
+ read_function_info (state, ddata, &lhdr, error_callback, data,
+ entry->u, pfvec, &function_addrs,
+ &function_addrs_count);
+ free_line_header (state, &lhdr, error_callback, data);
+ new_data = 1;
+ }
+
+ /* Atomically store the information we just read into the unit.
+ If another thread is simultaneously writing, it presumably
+ read the same information, and we don't care which one we
+ wind up with; we just leak the other one. We do have to
+ write the lines field last, so that the acquire-loads above
+ ensure that the other fields are set. */
+
+ if (!state->threaded)
+ {
+ u->lines_count = count;
+ u->function_addrs = function_addrs;
+ u->function_addrs_count = function_addrs_count;
+ u->lines = lines;
+ }
+ else
+ {
+ backtrace_atomic_store_size_t (&u->lines_count, count);
+ backtrace_atomic_store_pointer (&u->function_addrs, function_addrs);
+ backtrace_atomic_store_size_t (&u->function_addrs_count,
+ function_addrs_count);
+ backtrace_atomic_store_pointer (&u->lines, lines);
+ }
+ }
+
+ /* Now all fields of U have been initialized. */
+
+ if (lines == (struct line *) (uintptr_t) -1)
+ {
+ /* If reading the line number information failed in some way,
+ try again to see if there is a better compilation unit for
+ this PC. */
+ if (new_data)
+ return dwarf_lookup_pc (state, ddata, pc, callback, error_callback,
+ data, found);
+ return callback (data, pc, NULL, 0, NULL);
+ }
+
+ /* Search for PC within this unit. */
+
+ ln = (struct line *) bsearch (&pc, lines, entry->u->lines_count,
+ sizeof (struct line), line_search);
+ if (ln == NULL)
+ {
+ /* The PC is between the low_pc and high_pc attributes of the
+ compilation unit, but no entry in the line table covers it.
+ This implies that the start of the compilation unit has no
+ line number information. */
+
+ if (entry->u->abs_filename == NULL)
+ {
+ const char *filename;
+
+ filename = entry->u->filename;
+ if (filename != NULL
+ && !IS_ABSOLUTE_PATH (filename)
+ && entry->u->comp_dir != NULL)
+ {
+ size_t filename_len;
+ const char *dir;
+ size_t dir_len;
+ char *s;
+
+ filename_len = strlen (filename);
+ dir = entry->u->comp_dir;
+ dir_len = strlen (dir);
+ s = (char *) backtrace_alloc (state, dir_len + filename_len + 2,
+ error_callback, data);
+ if (s == NULL)
+ {
+ *found = 0;
+ return 0;
+ }
+ memcpy (s, dir, dir_len);
+ /* FIXME: Should use backslash if DOS file system. */
+ s[dir_len] = '/';
+ memcpy (s + dir_len + 1, filename, filename_len + 1);
+ filename = s;
+ }
+ entry->u->abs_filename = filename;
+ }
+
+ return callback (data, pc, entry->u->abs_filename, 0, NULL);
+ }
+
+ /* Search for function name within this unit. */
+
+ if (entry->u->function_addrs_count == 0)
+ return callback (data, pc, ln->filename, ln->lineno, NULL);
+
+ p = ((struct function_addrs *)
+ bsearch (&pc, entry->u->function_addrs,
+ entry->u->function_addrs_count,
+ sizeof (struct function_addrs),
+ function_addrs_search));
+ if (p == NULL)
+ return callback (data, pc, ln->filename, ln->lineno, NULL);
+
+ /* Here pc >= p->low && pc < (p + 1)->low. The function_addrs are
+ sorted by low, so if pc > p->low we are at the end of a range of
+ function_addrs with the same low value. If pc == p->low walk
+ forward to the end of the range with that low value. Then walk
+ backward and use the first range that includes pc. */
+ while (pc == (p + 1)->low)
+ ++p;
+ fmatch = NULL;
+ while (1)
+ {
+ if (pc < p->high)
+ {
+ fmatch = p;
+ break;
+ }
+ if (p == entry->u->function_addrs)
+ break;
+ if ((p - 1)->low < p->low)
+ break;
+ --p;
+ }
+ if (fmatch == NULL)
+ return callback (data, pc, ln->filename, ln->lineno, NULL);
+
+ function = fmatch->function;
+
+ filename = ln->filename;
+ lineno = ln->lineno;
+
+ ret = report_inlined_functions (pc, function, callback, data,
+ &filename, &lineno);
+ if (ret != 0)
+ return ret;
+
+ return callback (data, pc, filename, lineno, function->name);
+}
+
+
+/* Return the file/line information for a PC using the DWARF mapping
+ we built earlier. */
+
+static int
+dwarf_fileline (struct backtrace_state *state, uintptr_t pc,
+ backtrace_full_callback callback,
+ backtrace_error_callback error_callback, void *data)
+{
+ struct dwarf_data *ddata;
+ int found;
+ int ret;
+
+ if (!state->threaded)
+ {
+ for (ddata = (struct dwarf_data *) state->fileline_data;
+ ddata != NULL;
+ ddata = ddata->next)
+ {
+ ret = dwarf_lookup_pc (state, ddata, pc, callback, error_callback,
+ data, &found);
+ if (ret != 0 || found)
+ return ret;
+ }
+ }
+ else
+ {
+ struct dwarf_data **pp;
+
+ pp = (struct dwarf_data **) (void *) &state->fileline_data;
+ while (1)
+ {
+ ddata = backtrace_atomic_load_pointer (pp);
+ if (ddata == NULL)
+ break;
+
+ ret = dwarf_lookup_pc (state, ddata, pc, callback, error_callback,
+ data, &found);
+ if (ret != 0 || found)
+ return ret;
+
+ pp = &ddata->next;
+ }
+ }
+
+ /* FIXME: See if any libraries have been dlopen'ed. */
+
+ return callback (data, pc, NULL, 0, NULL);
+}
+
+/* Initialize our data structures from the DWARF debug info for a
+ file. Return NULL on failure. */
+
+static struct dwarf_data *
+build_dwarf_data (struct backtrace_state *state,
+ uintptr_t base_address,
+ const struct dwarf_sections *dwarf_sections,
+ int is_bigendian,
+ struct dwarf_data *altlink,
+ backtrace_error_callback error_callback,
+ void *data)
+{
+ struct unit_addrs_vector addrs_vec;
+ struct unit_addrs *addrs;
+ size_t addrs_count;
+ struct unit_vector units_vec;
+ struct unit **units;
+ size_t units_count;
+ struct dwarf_data *fdata;
+
+ if (!build_address_map (state, base_address, dwarf_sections, is_bigendian,
+ altlink, error_callback, data, &addrs_vec,
+ &units_vec))
+ return NULL;
+
+ if (!backtrace_vector_release (state, &addrs_vec.vec, error_callback, data))
+ return NULL;
+ if (!backtrace_vector_release (state, &units_vec.vec, error_callback, data))
+ return NULL;
+ addrs = (struct unit_addrs *) addrs_vec.vec.base;
+ units = (struct unit **) units_vec.vec.base;
+ addrs_count = addrs_vec.count;
+ units_count = units_vec.count;
+ backtrace_qsort (addrs, addrs_count, sizeof (struct unit_addrs),
+ unit_addrs_compare);
+ /* No qsort for units required, already sorted. */
+
+ fdata = ((struct dwarf_data *)
+ backtrace_alloc (state, sizeof (struct dwarf_data),
+ error_callback, data));
+ if (fdata == NULL)
+ return NULL;
+
+ fdata->next = NULL;
+ fdata->altlink = altlink;
+ fdata->base_address = base_address;
+ fdata->addrs = addrs;
+ fdata->addrs_count = addrs_count;
+ fdata->units = units;
+ fdata->units_count = units_count;
+ fdata->dwarf_sections = *dwarf_sections;
+ fdata->is_bigendian = is_bigendian;
+ memset (&fdata->fvec, 0, sizeof fdata->fvec);
+
+ return fdata;
+}
+
+/* Build our data structures from the DWARF sections for a module.
+ Set FILELINE_FN and STATE->FILELINE_DATA. Return 1 on success, 0
+ on failure. */
+
+int
+backtrace_dwarf_add (struct backtrace_state *state,
+ uintptr_t base_address,
+ const struct dwarf_sections *dwarf_sections,
+ int is_bigendian,
+ struct dwarf_data *fileline_altlink,
+ backtrace_error_callback error_callback,
+ void *data, fileline *fileline_fn,
+ struct dwarf_data **fileline_entry)
+{
+ struct dwarf_data *fdata;
+
+ fdata = build_dwarf_data (state, base_address, dwarf_sections, is_bigendian,
+ fileline_altlink, error_callback, data);
+ if (fdata == NULL)
+ return 0;
+
+ if (fileline_entry != NULL)
+ *fileline_entry = fdata;
+
+ if (!state->threaded)
+ {
+ struct dwarf_data **pp;
+
+ for (pp = (struct dwarf_data **) (void *) &state->fileline_data;
+ *pp != NULL;
+ pp = &(*pp)->next)
+ ;
+ *pp = fdata;
+ }
+ else
+ {
+ while (1)
+ {
+ struct dwarf_data **pp;
+
+ pp = (struct dwarf_data **) (void *) &state->fileline_data;
+
+ while (1)
+ {
+ struct dwarf_data *p;
+
+ p = backtrace_atomic_load_pointer (pp);
+
+ if (p == NULL)
+ break;
+
+ pp = &p->next;
+ }
+
+ if (__sync_bool_compare_and_swap (pp, NULL, fdata))
+ break;
+ }
+ }
+
+ *fileline_fn = dwarf_fileline;
+
+ return 1;
+}
diff --git a/thirdparty/libbacktrace/fileline.c b/thirdparty/libbacktrace/fileline.c
new file mode 100644
index 0000000000..0472f4721a
--- /dev/null
+++ b/thirdparty/libbacktrace/fileline.c
@@ -0,0 +1,346 @@
+/* fileline.c -- Get file and line number information in a backtrace.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#if defined (HAVE_KERN_PROC_ARGS) || defined (HAVE_KERN_PROC)
+#include <sys/sysctl.h>
+#endif
+
+#ifdef HAVE_MACH_O_DYLD_H
+#include <mach-o/dyld.h>
+#endif
+
+#include "backtrace.h"
+#include "internal.h"
+
+#ifndef HAVE_GETEXECNAME
+#define getexecname() NULL
+#endif
+
+#if !defined (HAVE_KERN_PROC_ARGS) && !defined (HAVE_KERN_PROC)
+
+#define sysctl_exec_name1(state, error_callback, data) NULL
+#define sysctl_exec_name2(state, error_callback, data) NULL
+
+#else /* defined (HAVE_KERN_PROC_ARGS) || |defined (HAVE_KERN_PROC) */
+
+static char *
+sysctl_exec_name (struct backtrace_state *state,
+ int mib0, int mib1, int mib2, int mib3,
+ backtrace_error_callback error_callback, void *data)
+{
+ int mib[4];
+ size_t len;
+ char *name;
+ size_t rlen;
+
+ mib[0] = mib0;
+ mib[1] = mib1;
+ mib[2] = mib2;
+ mib[3] = mib3;
+
+ if (sysctl (mib, 4, NULL, &len, NULL, 0) < 0)
+ return NULL;
+ name = (char *) backtrace_alloc (state, len, error_callback, data);
+ if (name == NULL)
+ return NULL;
+ rlen = len;
+ if (sysctl (mib, 4, name, &rlen, NULL, 0) < 0)
+ {
+ backtrace_free (state, name, len, error_callback, data);
+ return NULL;
+ }
+ return name;
+}
+
+#ifdef HAVE_KERN_PROC_ARGS
+
+static char *
+sysctl_exec_name1 (struct backtrace_state *state,
+ backtrace_error_callback error_callback, void *data)
+{
+ /* This variant is used on NetBSD. */
+ return sysctl_exec_name (state, CTL_KERN, KERN_PROC_ARGS, -1,
+ KERN_PROC_PATHNAME, error_callback, data);
+}
+
+#else
+
+#define sysctl_exec_name1(state, error_callback, data) NULL
+
+#endif
+
+#ifdef HAVE_KERN_PROC
+
+static char *
+sysctl_exec_name2 (struct backtrace_state *state,
+ backtrace_error_callback error_callback, void *data)
+{
+ /* This variant is used on FreeBSD. */
+ return sysctl_exec_name (state, CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1,
+ error_callback, data);
+}
+
+#else
+
+#define sysctl_exec_name2(state, error_callback, data) NULL
+
+#endif
+
+#endif /* defined (HAVE_KERN_PROC_ARGS) || |defined (HAVE_KERN_PROC) */
+
+#ifdef HAVE_MACH_O_DYLD_H
+
+static char *
+macho_get_executable_path (struct backtrace_state *state,
+ backtrace_error_callback error_callback, void *data)
+{
+ uint32_t len;
+ char *name;
+
+ len = 0;
+ if (_NSGetExecutablePath (NULL, &len) == 0)
+ return NULL;
+ name = (char *) backtrace_alloc (state, len, error_callback, data);
+ if (name == NULL)
+ return NULL;
+ if (_NSGetExecutablePath (name, &len) != 0)
+ {
+ backtrace_free (state, name, len, error_callback, data);
+ return NULL;
+ }
+ return name;
+}
+
+#else /* !defined (HAVE_MACH_O_DYLD_H) */
+
+#define macho_get_executable_path(state, error_callback, data) NULL
+
+#endif /* !defined (HAVE_MACH_O_DYLD_H) */
+
+/* Initialize the fileline information from the executable. Returns 1
+ on success, 0 on failure. */
+
+static int
+fileline_initialize (struct backtrace_state *state,
+ backtrace_error_callback error_callback, void *data)
+{
+ int failed;
+ fileline fileline_fn;
+ int pass;
+ int called_error_callback;
+ int descriptor;
+ const char *filename;
+ char buf[64];
+
+ if (!state->threaded)
+ failed = state->fileline_initialization_failed;
+ else
+ failed = backtrace_atomic_load_int (&state->fileline_initialization_failed);
+
+ if (failed)
+ {
+ error_callback (data, "failed to read executable information", -1);
+ return 0;
+ }
+
+ if (!state->threaded)
+ fileline_fn = state->fileline_fn;
+ else
+ fileline_fn = backtrace_atomic_load_pointer (&state->fileline_fn);
+ if (fileline_fn != NULL)
+ return 1;
+
+ /* We have not initialized the information. Do it now. */
+
+ descriptor = -1;
+ called_error_callback = 0;
+ for (pass = 0; pass < 8; ++pass)
+ {
+ int does_not_exist;
+
+ switch (pass)
+ {
+ case 0:
+ filename = state->filename;
+ break;
+ case 1:
+ filename = getexecname ();
+ break;
+ case 2:
+ filename = "/proc/self/exe";
+ break;
+ case 3:
+ filename = "/proc/curproc/file";
+ break;
+ case 4:
+ snprintf (buf, sizeof (buf), "/proc/%ld/object/a.out",
+ (long) getpid ());
+ filename = buf;
+ break;
+ case 5:
+ filename = sysctl_exec_name1 (state, error_callback, data);
+ break;
+ case 6:
+ filename = sysctl_exec_name2 (state, error_callback, data);
+ break;
+ case 7:
+ filename = macho_get_executable_path (state, error_callback, data);
+ break;
+ default:
+ abort ();
+ }
+
+ if (filename == NULL)
+ continue;
+
+ descriptor = backtrace_open (filename, error_callback, data,
+ &does_not_exist);
+ if (descriptor < 0 && !does_not_exist)
+ {
+ called_error_callback = 1;
+ break;
+ }
+ if (descriptor >= 0)
+ break;
+ }
+
+ if (descriptor < 0)
+ {
+ if (!called_error_callback)
+ {
+ if (state->filename != NULL)
+ error_callback (data, state->filename, ENOENT);
+ else
+ error_callback (data,
+ "libbacktrace could not find executable to open",
+ 0);
+ }
+ failed = 1;
+ }
+
+ if (!failed)
+ {
+ if (!backtrace_initialize (state, filename, descriptor, error_callback,
+ data, &fileline_fn))
+ failed = 1;
+ }
+
+ if (failed)
+ {
+ if (!state->threaded)
+ state->fileline_initialization_failed = 1;
+ else
+ backtrace_atomic_store_int (&state->fileline_initialization_failed, 1);
+ return 0;
+ }
+
+ if (!state->threaded)
+ state->fileline_fn = fileline_fn;
+ else
+ {
+ backtrace_atomic_store_pointer (&state->fileline_fn, fileline_fn);
+
+ /* Note that if two threads initialize at once, one of the data
+ sets may be leaked. */
+ }
+
+ return 1;
+}
+
+/* Given a PC, find the file name, line number, and function name. */
+
+int
+backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc,
+ backtrace_full_callback callback,
+ backtrace_error_callback error_callback, void *data)
+{
+ if (!fileline_initialize (state, error_callback, data))
+ return 0;
+
+ if (state->fileline_initialization_failed)
+ return 0;
+
+ return state->fileline_fn (state, pc, callback, error_callback, data);
+}
+
+/* Given a PC, find the symbol for it, and its value. */
+
+int
+backtrace_syminfo (struct backtrace_state *state, uintptr_t pc,
+ backtrace_syminfo_callback callback,
+ backtrace_error_callback error_callback, void *data)
+{
+ if (!fileline_initialize (state, error_callback, data))
+ return 0;
+
+ if (state->fileline_initialization_failed)
+ return 0;
+
+ state->syminfo_fn (state, pc, callback, error_callback, data);
+ return 1;
+}
+
+/* A backtrace_syminfo_callback that can call into a
+ backtrace_full_callback, used when we have a symbol table but no
+ debug info. */
+
+void
+backtrace_syminfo_to_full_callback (void *data, uintptr_t pc,
+ const char *symname,
+ uintptr_t symval ATTRIBUTE_UNUSED,
+ uintptr_t symsize ATTRIBUTE_UNUSED)
+{
+ struct backtrace_call_full *bdata = (struct backtrace_call_full *) data;
+
+ bdata->ret = bdata->full_callback (bdata->full_data, pc, NULL, 0, symname);
+}
+
+/* An error callback that corresponds to
+ backtrace_syminfo_to_full_callback. */
+
+void
+backtrace_syminfo_to_full_error_callback (void *data, const char *msg,
+ int errnum)
+{
+ struct backtrace_call_full *bdata = (struct backtrace_call_full *) data;
+
+ bdata->full_error_callback (bdata->full_data, msg, errnum);
+}
diff --git a/thirdparty/libbacktrace/filenames.h b/thirdparty/libbacktrace/filenames.h
new file mode 100644
index 0000000000..aa7bd7adff
--- /dev/null
+++ b/thirdparty/libbacktrace/filenames.h
@@ -0,0 +1,52 @@
+/* btest.c -- Filename header for libbacktrace library
+ Copyright (C) 2012-2018 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#ifndef GCC_VERSION
+# define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__)
+#endif
+
+#if (GCC_VERSION < 2007)
+# define __attribute__(x)
+#endif
+
+#ifndef ATTRIBUTE_UNUSED
+# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+#endif
+
+#if defined(__MSDOS__) || defined(_WIN32) || defined(__OS2__) || defined (__CYGWIN__)
+# define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == '\\')
+# define HAS_DRIVE_SPEC(f) ((f)[0] != '\0' && (f)[1] == ':')
+# define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0]) || HAS_DRIVE_SPEC(f))
+#else
+# define IS_DIR_SEPARATOR(c) ((c) == '/')
+# define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0]))
+#endif
diff --git a/thirdparty/libbacktrace/internal.h b/thirdparty/libbacktrace/internal.h
new file mode 100644
index 0000000000..bb481f373b
--- /dev/null
+++ b/thirdparty/libbacktrace/internal.h
@@ -0,0 +1,380 @@
+/* internal.h -- Internal header file for stack backtrace library.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#ifndef BACKTRACE_INTERNAL_H
+#define BACKTRACE_INTERNAL_H
+
+/* We assume that <sys/types.h> and "backtrace.h" have already been
+ included. */
+
+#ifndef GCC_VERSION
+# define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__)
+#endif
+
+#if (GCC_VERSION < 2007)
+# define __attribute__(x)
+#endif
+
+#ifndef ATTRIBUTE_UNUSED
+# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+#endif
+
+#ifndef ATTRIBUTE_MALLOC
+# if (GCC_VERSION >= 2096)
+# define ATTRIBUTE_MALLOC __attribute__ ((__malloc__))
+# else
+# define ATTRIBUTE_MALLOC
+# endif
+#endif
+
+#ifndef ATTRIBUTE_FALLTHROUGH
+# if (GCC_VERSION >= 7000)
+# define ATTRIBUTE_FALLTHROUGH __attribute__ ((__fallthrough__))
+# else
+# define ATTRIBUTE_FALLTHROUGH
+# endif
+#endif
+
+#ifndef HAVE_SYNC_FUNCTIONS
+
+/* Define out the sync functions. These should never be called if
+ they are not available. */
+
+#define __sync_bool_compare_and_swap(A, B, C) (abort(), 1)
+#define __sync_lock_test_and_set(A, B) (abort(), 0)
+#define __sync_lock_release(A) abort()
+
+#endif /* !defined (HAVE_SYNC_FUNCTIONS) */
+
+#ifdef HAVE_ATOMIC_FUNCTIONS
+
+/* We have the atomic builtin functions. */
+
+#define backtrace_atomic_load_pointer(p) \
+ __atomic_load_n ((p), __ATOMIC_ACQUIRE)
+#define backtrace_atomic_load_int(p) \
+ __atomic_load_n ((p), __ATOMIC_ACQUIRE)
+#define backtrace_atomic_store_pointer(p, v) \
+ __atomic_store_n ((p), (v), __ATOMIC_RELEASE)
+#define backtrace_atomic_store_size_t(p, v) \
+ __atomic_store_n ((p), (v), __ATOMIC_RELEASE)
+#define backtrace_atomic_store_int(p, v) \
+ __atomic_store_n ((p), (v), __ATOMIC_RELEASE)
+
+#else /* !defined (HAVE_ATOMIC_FUNCTIONS) */
+#ifdef HAVE_SYNC_FUNCTIONS
+
+/* We have the sync functions but not the atomic functions. Define
+ the atomic ones in terms of the sync ones. */
+
+extern void *backtrace_atomic_load_pointer (void *);
+extern int backtrace_atomic_load_int (int *);
+extern void backtrace_atomic_store_pointer (void *, void *);
+extern void backtrace_atomic_store_size_t (size_t *, size_t);
+extern void backtrace_atomic_store_int (int *, int);
+
+#else /* !defined (HAVE_SYNC_FUNCTIONS) */
+
+/* We have neither the sync nor the atomic functions. These will
+ never be called. */
+
+#define backtrace_atomic_load_pointer(p) (abort(), (void *) NULL)
+#define backtrace_atomic_load_int(p) (abort(), 0)
+#define backtrace_atomic_store_pointer(p, v) abort()
+#define backtrace_atomic_store_size_t(p, v) abort()
+#define backtrace_atomic_store_int(p, v) abort()
+
+#endif /* !defined (HAVE_SYNC_FUNCTIONS) */
+#endif /* !defined (HAVE_ATOMIC_FUNCTIONS) */
+
+/* The type of the function that collects file/line information. This
+ is like backtrace_pcinfo. */
+
+typedef int (*fileline) (struct backtrace_state *state, uintptr_t pc,
+ backtrace_full_callback callback,
+ backtrace_error_callback error_callback, void *data);
+
+/* The type of the function that collects symbol information. This is
+ like backtrace_syminfo. */
+
+typedef void (*syminfo) (struct backtrace_state *state, uintptr_t pc,
+ backtrace_syminfo_callback callback,
+ backtrace_error_callback error_callback, void *data);
+
+/* What the backtrace state pointer points to. */
+
+struct backtrace_state
+{
+ /* The name of the executable. */
+ const char *filename;
+ /* Non-zero if threaded. */
+ int threaded;
+ /* The master lock for fileline_fn, fileline_data, syminfo_fn,
+ syminfo_data, fileline_initialization_failed and everything the
+ data pointers point to. */
+ void *lock;
+ /* The function that returns file/line information. */
+ fileline fileline_fn;
+ /* The data to pass to FILELINE_FN. */
+ void *fileline_data;
+ /* The function that returns symbol information. */
+ syminfo syminfo_fn;
+ /* The data to pass to SYMINFO_FN. */
+ void *syminfo_data;
+ /* Whether initializing the file/line information failed. */
+ int fileline_initialization_failed;
+ /* The lock for the freelist. */
+ int lock_alloc;
+ /* The freelist when using mmap. */
+ struct backtrace_freelist_struct *freelist;
+};
+
+/* Open a file for reading. Returns -1 on error. If DOES_NOT_EXIST
+ is not NULL, *DOES_NOT_EXIST will be set to 0 normally and set to 1
+ if the file does not exist. If the file does not exist and
+ DOES_NOT_EXIST is not NULL, the function will return -1 and will
+ not call ERROR_CALLBACK. On other errors, or if DOES_NOT_EXIST is
+ NULL, the function will call ERROR_CALLBACK before returning. */
+extern int backtrace_open (const char *filename,
+ backtrace_error_callback error_callback,
+ void *data,
+ int *does_not_exist);
+
+/* A view of the contents of a file. This supports mmap when
+ available. A view will remain in memory even after backtrace_close
+ is called on the file descriptor from which the view was
+ obtained. */
+
+struct backtrace_view
+{
+ /* The data that the caller requested. */
+ const void *data;
+ /* The base of the view. */
+ void *base;
+ /* The total length of the view. */
+ size_t len;
+};
+
+/* Create a view of SIZE bytes from DESCRIPTOR at OFFSET. Store the
+ result in *VIEW. Returns 1 on success, 0 on error. */
+extern int backtrace_get_view (struct backtrace_state *state, int descriptor,
+ off_t offset, uint64_t size,
+ backtrace_error_callback error_callback,
+ void *data, struct backtrace_view *view);
+
+/* Release a view created by backtrace_get_view. */
+extern void backtrace_release_view (struct backtrace_state *state,
+ struct backtrace_view *view,
+ backtrace_error_callback error_callback,
+ void *data);
+
+/* Close a file opened by backtrace_open. Returns 1 on success, 0 on
+ error. */
+
+extern int backtrace_close (int descriptor,
+ backtrace_error_callback error_callback,
+ void *data);
+
+/* Sort without using memory. */
+
+extern void backtrace_qsort (void *base, size_t count, size_t size,
+ int (*compar) (const void *, const void *));
+
+/* Allocate memory. This is like malloc. If ERROR_CALLBACK is NULL,
+ this does not report an error, it just returns NULL. */
+
+extern void *backtrace_alloc (struct backtrace_state *state, size_t size,
+ backtrace_error_callback error_callback,
+ void *data) ATTRIBUTE_MALLOC;
+
+/* Free memory allocated by backtrace_alloc. If ERROR_CALLBACK is
+ NULL, this does not report an error. */
+
+extern void backtrace_free (struct backtrace_state *state, void *mem,
+ size_t size,
+ backtrace_error_callback error_callback,
+ void *data);
+
+/* A growable vector of some struct. This is used for more efficient
+ allocation when we don't know the final size of some group of data
+ that we want to represent as an array. */
+
+struct backtrace_vector
+{
+ /* The base of the vector. */
+ void *base;
+ /* The number of bytes in the vector. */
+ size_t size;
+ /* The number of bytes available at the current allocation. */
+ size_t alc;
+};
+
+/* Grow VEC by SIZE bytes. Return a pointer to the newly allocated
+ bytes. Note that this may move the entire vector to a new memory
+ location. Returns NULL on failure. */
+
+extern void *backtrace_vector_grow (struct backtrace_state *state, size_t size,
+ backtrace_error_callback error_callback,
+ void *data,
+ struct backtrace_vector *vec);
+
+/* Finish the current allocation on VEC. Prepare to start a new
+ allocation. The finished allocation will never be freed. Returns
+ a pointer to the base of the finished entries, or NULL on
+ failure. */
+
+extern void* backtrace_vector_finish (struct backtrace_state *state,
+ struct backtrace_vector *vec,
+ backtrace_error_callback error_callback,
+ void *data);
+
+/* Release any extra space allocated for VEC. This may change
+ VEC->base. Returns 1 on success, 0 on failure. */
+
+extern int backtrace_vector_release (struct backtrace_state *state,
+ struct backtrace_vector *vec,
+ backtrace_error_callback error_callback,
+ void *data);
+
+/* Free the space managed by VEC. This will reset VEC. */
+
+static inline void
+backtrace_vector_free (struct backtrace_state *state,
+ struct backtrace_vector *vec,
+ backtrace_error_callback error_callback, void *data)
+{
+ vec->alc += vec->size;
+ vec->size = 0;
+ backtrace_vector_release (state, vec, error_callback, data);
+}
+
+/* Read initial debug data from a descriptor, and set the
+ fileline_data, syminfo_fn, and syminfo_data fields of STATE.
+ Return the fileln_fn field in *FILELN_FN--this is done this way so
+ that the synchronization code is only implemented once. This is
+ called after the descriptor has first been opened. It will close
+ the descriptor if it is no longer needed. Returns 1 on success, 0
+ on error. There will be multiple implementations of this function,
+ for different file formats. Each system will compile the
+ appropriate one. */
+
+extern int backtrace_initialize (struct backtrace_state *state,
+ const char *filename,
+ int descriptor,
+ backtrace_error_callback error_callback,
+ void *data,
+ fileline *fileline_fn);
+
+/* An enum for the DWARF sections we care about. */
+
+enum dwarf_section
+{
+ DEBUG_INFO,
+ DEBUG_LINE,
+ DEBUG_ABBREV,
+ DEBUG_RANGES,
+ DEBUG_STR,
+ DEBUG_ADDR,
+ DEBUG_STR_OFFSETS,
+ DEBUG_LINE_STR,
+ DEBUG_RNGLISTS,
+
+ DEBUG_MAX
+};
+
+/* Data for the DWARF sections we care about. */
+
+struct dwarf_sections
+{
+ const unsigned char *data[DEBUG_MAX];
+ size_t size[DEBUG_MAX];
+};
+
+/* DWARF data read from a file, used for .gnu_debugaltlink. */
+
+struct dwarf_data;
+
+/* Add file/line information for a DWARF module. */
+
+extern int backtrace_dwarf_add (struct backtrace_state *state,
+ uintptr_t base_address,
+ const struct dwarf_sections *dwarf_sections,
+ int is_bigendian,
+ struct dwarf_data *fileline_altlink,
+ backtrace_error_callback error_callback,
+ void *data, fileline *fileline_fn,
+ struct dwarf_data **fileline_entry);
+
+/* A data structure to pass to backtrace_syminfo_to_full. */
+
+struct backtrace_call_full
+{
+ backtrace_full_callback full_callback;
+ backtrace_error_callback full_error_callback;
+ void *full_data;
+ int ret;
+};
+
+/* A backtrace_syminfo_callback that can call into a
+ backtrace_full_callback, used when we have a symbol table but no
+ debug info. */
+
+extern void backtrace_syminfo_to_full_callback (void *data, uintptr_t pc,
+ const char *symname,
+ uintptr_t symval,
+ uintptr_t symsize);
+
+/* An error callback that corresponds to
+ backtrace_syminfo_to_full_callback. */
+
+extern void backtrace_syminfo_to_full_error_callback (void *, const char *,
+ int);
+
+/* A test-only hook for elf_uncompress_zdebug. */
+
+extern int backtrace_uncompress_zdebug (struct backtrace_state *,
+ const unsigned char *compressed,
+ size_t compressed_size,
+ backtrace_error_callback, void *data,
+ unsigned char **uncompressed,
+ size_t *uncompressed_size);
+
+/* A test-only hook for elf_uncompress_lzma. */
+
+extern int backtrace_uncompress_lzma (struct backtrace_state *,
+ const unsigned char *compressed,
+ size_t compressed_size,
+ backtrace_error_callback, void *data,
+ unsigned char **uncompressed,
+ size_t *uncompressed_size);
+
+#endif
diff --git a/thirdparty/libbacktrace/pecoff.c b/thirdparty/libbacktrace/pecoff.c
new file mode 100644
index 0000000000..720251900b
--- /dev/null
+++ b/thirdparty/libbacktrace/pecoff.c
@@ -0,0 +1,935 @@
+/* pecoff.c -- Get debug data from a PE/COFFF file for backtraces.
+ Copyright (C) 2015-2021 Free Software Foundation, Inc.
+ Adapted from elf.c by Tristan Gingold, AdaCore.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* Coff file header. */
+
+typedef struct {
+ uint16_t machine;
+ uint16_t number_of_sections;
+ uint32_t time_date_stamp;
+ uint32_t pointer_to_symbol_table;
+ uint32_t number_of_symbols;
+ uint16_t size_of_optional_header;
+ uint16_t characteristics;
+} b_coff_file_header;
+
+/* Coff optional header. */
+
+typedef struct {
+ uint16_t magic;
+ uint8_t major_linker_version;
+ uint8_t minor_linker_version;
+ uint32_t size_of_code;
+ uint32_t size_of_initialized_data;
+ uint32_t size_of_uninitialized_data;
+ uint32_t address_of_entry_point;
+ uint32_t base_of_code;
+ union {
+ struct {
+ uint32_t base_of_data;
+ uint32_t image_base;
+ } pe;
+ struct {
+ uint64_t image_base;
+ } pep;
+ } u;
+} b_coff_optional_header;
+
+/* Values of magic in optional header. */
+
+#define PE_MAGIC 0x10b /* PE32 executable. */
+#define PEP_MAGIC 0x20b /* PE32+ executable (for 64bit targets). */
+
+/* Coff section header. */
+
+typedef struct {
+ char name[8];
+ uint32_t virtual_size;
+ uint32_t virtual_address;
+ uint32_t size_of_raw_data;
+ uint32_t pointer_to_raw_data;
+ uint32_t pointer_to_relocations;
+ uint32_t pointer_to_line_numbers;
+ uint16_t number_of_relocations;
+ uint16_t number_of_line_numbers;
+ uint32_t characteristics;
+} b_coff_section_header;
+
+/* Coff symbol name. */
+
+typedef union {
+ char short_name[8];
+ struct {
+ unsigned char zeroes[4];
+ unsigned char off[4];
+ } long_name;
+} b_coff_name;
+
+/* Coff symbol (external representation which is unaligned). */
+
+typedef struct {
+ b_coff_name name;
+ unsigned char value[4];
+ unsigned char section_number[2];
+ unsigned char type[2];
+ unsigned char storage_class;
+ unsigned char number_of_aux_symbols;
+} b_coff_external_symbol;
+
+/* Symbol types. */
+
+#define N_TBSHFT 4 /* Shift for the derived type. */
+#define IMAGE_SYM_DTYPE_FUNCTION 2 /* Function derived type. */
+
+/* Size of a coff symbol. */
+
+#define SYM_SZ 18
+
+/* Coff symbol, internal representation (aligned). */
+
+typedef struct {
+ const char *name;
+ uint32_t value;
+ int16_t sec;
+ uint16_t type;
+ uint16_t sc;
+} b_coff_internal_symbol;
+
+/* Names of sections, indexed by enum dwarf_section in internal.h. */
+
+static const char * const debug_section_names[DEBUG_MAX] =
+{
+ ".debug_info",
+ ".debug_line",
+ ".debug_abbrev",
+ ".debug_ranges",
+ ".debug_str",
+ ".debug_addr",
+ ".debug_str_offsets",
+ ".debug_line_str",
+ ".debug_rnglists"
+};
+
+/* Information we gather for the sections we care about. */
+
+struct debug_section_info
+{
+ /* Section file offset. */
+ off_t offset;
+ /* Section size. */
+ size_t size;
+};
+
+/* Information we keep for an coff symbol. */
+
+struct coff_symbol
+{
+ /* The name of the symbol. */
+ const char *name;
+ /* The address of the symbol. */
+ uintptr_t address;
+};
+
+/* Information to pass to coff_syminfo. */
+
+struct coff_syminfo_data
+{
+ /* Symbols for the next module. */
+ struct coff_syminfo_data *next;
+ /* The COFF symbols, sorted by address. */
+ struct coff_symbol *symbols;
+ /* The number of symbols. */
+ size_t count;
+};
+
+/* A dummy callback function used when we can't find any debug info. */
+
+static int
+coff_nodebug (struct backtrace_state *state ATTRIBUTE_UNUSED,
+ uintptr_t pc ATTRIBUTE_UNUSED,
+ backtrace_full_callback callback ATTRIBUTE_UNUSED,
+ backtrace_error_callback error_callback, void *data)
+{
+ error_callback (data, "no debug info in PE/COFF executable", -1);
+ return 0;
+}
+
+/* A dummy callback function used when we can't find a symbol
+ table. */
+
+static void
+coff_nosyms (struct backtrace_state *state ATTRIBUTE_UNUSED,
+ uintptr_t addr ATTRIBUTE_UNUSED,
+ backtrace_syminfo_callback callback ATTRIBUTE_UNUSED,
+ backtrace_error_callback error_callback, void *data)
+{
+ error_callback (data, "no symbol table in PE/COFF executable", -1);
+}
+
+/* Read a potentially unaligned 4 byte word at P, using native endianness. */
+
+static uint32_t
+coff_read4 (const unsigned char *p)
+{
+ uint32_t res;
+
+ memcpy (&res, p, 4);
+ return res;
+}
+
+/* Read a potentially unaligned 2 byte word at P, using native endianness.
+ All 2 byte word in symbols are always aligned, but for coherency all
+ fields are declared as char arrays. */
+
+static uint16_t
+coff_read2 (const unsigned char *p)
+{
+ uint16_t res;
+
+ memcpy (&res, p, sizeof (res));
+ return res;
+}
+
+/* Return the length (without the trailing 0) of a COFF short name. */
+
+static size_t
+coff_short_name_len (const char *name)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ if (name[i] == 0)
+ return i;
+ return 8;
+}
+
+/* Return true iff COFF short name CNAME is the same as NAME (a NUL-terminated
+ string). */
+
+static int
+coff_short_name_eq (const char *name, const char *cname)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ {
+ if (name[i] != cname[i])
+ return 0;
+ if (name[i] == 0)
+ return 1;
+ }
+ return name[8] == 0;
+}
+
+/* Return true iff NAME is the same as string at offset OFF. */
+
+static int
+coff_long_name_eq (const char *name, unsigned int off,
+ struct backtrace_view *str_view)
+{
+ if (off >= str_view->len)
+ return 0;
+ return strcmp (name, (const char *)str_view->data + off) == 0;
+}
+
+/* Compare struct coff_symbol for qsort. */
+
+static int
+coff_symbol_compare (const void *v1, const void *v2)
+{
+ const struct coff_symbol *e1 = (const struct coff_symbol *) v1;
+ const struct coff_symbol *e2 = (const struct coff_symbol *) v2;
+
+ if (e1->address < e2->address)
+ return -1;
+ else if (e1->address > e2->address)
+ return 1;
+ else
+ return 0;
+}
+
+/* Convert SYM to internal (and aligned) format ISYM, using string table
+ from STRTAB and STRTAB_SIZE, and number of sections SECTS_NUM.
+ Return -1 in case of error (invalid section number or string index). */
+
+static int
+coff_expand_symbol (b_coff_internal_symbol *isym,
+ const b_coff_external_symbol *sym,
+ uint16_t sects_num,
+ const unsigned char *strtab, size_t strtab_size)
+{
+ isym->type = coff_read2 (sym->type);
+ isym->sec = coff_read2 (sym->section_number);
+ isym->sc = sym->storage_class;
+
+ if (isym->sec > 0 && (uint16_t) isym->sec > sects_num)
+ return -1;
+ if (sym->name.short_name[0] != 0)
+ isym->name = sym->name.short_name;
+ else
+ {
+ uint32_t off = coff_read4 (sym->name.long_name.off);
+
+ if (off >= strtab_size)
+ return -1;
+ isym->name = (const char *) strtab + off;
+ }
+ return 0;
+}
+
+/* Return true iff SYM is a defined symbol for a function. Data symbols
+ aren't considered because they aren't easily identified (same type as
+ section names, presence of symbols defined by the linker script). */
+
+static int
+coff_is_function_symbol (const b_coff_internal_symbol *isym)
+{
+ return (isym->type >> N_TBSHFT) == IMAGE_SYM_DTYPE_FUNCTION
+ && isym->sec > 0;
+}
+
+/* Initialize the symbol table info for coff_syminfo. */
+
+static int
+coff_initialize_syminfo (struct backtrace_state *state,
+ uintptr_t base_address, int is_64,
+ const b_coff_section_header *sects, size_t sects_num,
+ const b_coff_external_symbol *syms, size_t syms_size,
+ const unsigned char *strtab, size_t strtab_size,
+ backtrace_error_callback error_callback,
+ void *data, struct coff_syminfo_data *sdata)
+{
+ size_t syms_count;
+ char *coff_symstr;
+ size_t coff_symstr_len;
+ size_t coff_symbol_count;
+ size_t coff_symbol_size;
+ struct coff_symbol *coff_symbols;
+ struct coff_symbol *coff_sym;
+ char *coff_str;
+ size_t i;
+
+ syms_count = syms_size / SYM_SZ;
+
+ /* We only care about function symbols. Count them. Also count size of
+ strings for in-symbol names. */
+ coff_symbol_count = 0;
+ coff_symstr_len = 0;
+ for (i = 0; i < syms_count; ++i)
+ {
+ const b_coff_external_symbol *asym = &syms[i];
+ b_coff_internal_symbol isym;
+
+ if (coff_expand_symbol (&isym, asym, sects_num, strtab, strtab_size) < 0)
+ {
+ error_callback (data, "invalid section or offset in coff symbol", 0);
+ return 0;
+ }
+ if (coff_is_function_symbol (&isym))
+ {
+ ++coff_symbol_count;
+ if (asym->name.short_name[0] != 0)
+ coff_symstr_len += coff_short_name_len (asym->name.short_name) + 1;
+ }
+
+ i += asym->number_of_aux_symbols;
+ }
+
+ coff_symbol_size = (coff_symbol_count + 1) * sizeof (struct coff_symbol);
+ coff_symbols = ((struct coff_symbol *)
+ backtrace_alloc (state, coff_symbol_size, error_callback,
+ data));
+ if (coff_symbols == NULL)
+ return 0;
+
+ /* Allocate memory for symbols strings. */
+ if (coff_symstr_len > 0)
+ {
+ coff_symstr = ((char *)
+ backtrace_alloc (state, coff_symstr_len, error_callback,
+ data));
+ if (coff_symstr == NULL)
+ {
+ backtrace_free (state, coff_symbols, coff_symbol_size,
+ error_callback, data);
+ return 0;
+ }
+ }
+ else
+ coff_symstr = NULL;
+
+ /* Copy symbols. */
+ coff_sym = coff_symbols;
+ coff_str = coff_symstr;
+ for (i = 0; i < syms_count; ++i)
+ {
+ const b_coff_external_symbol *asym = &syms[i];
+ b_coff_internal_symbol isym;
+
+ if (coff_expand_symbol (&isym, asym, sects_num, strtab, strtab_size))
+ {
+ /* Should not fail, as it was already tested in the previous
+ loop. */
+ abort ();
+ }
+ if (coff_is_function_symbol (&isym))
+ {
+ const char *name;
+ int16_t secnum;
+
+ if (asym->name.short_name[0] != 0)
+ {
+ size_t len = coff_short_name_len (isym.name);
+ name = coff_str;
+ memcpy (coff_str, isym.name, len);
+ coff_str[len] = 0;
+ coff_str += len + 1;
+ }
+ else
+ name = isym.name;
+
+ if (!is_64)
+ {
+ /* Strip leading '_'. */
+ if (name[0] == '_')
+ name++;
+ }
+
+ /* Symbol value is section relative, so we need to read the address
+ of its section. */
+ secnum = coff_read2 (asym->section_number);
+
+ coff_sym->name = name;
+ coff_sym->address = (coff_read4 (asym->value)
+ + sects[secnum - 1].virtual_address
+ + base_address);
+ coff_sym++;
+ }
+
+ i += asym->number_of_aux_symbols;
+ }
+
+ /* End of symbols marker. */
+ coff_sym->name = NULL;
+ coff_sym->address = -1;
+
+ backtrace_qsort (coff_symbols, coff_symbol_count,
+ sizeof (struct coff_symbol), coff_symbol_compare);
+
+ sdata->next = NULL;
+ sdata->symbols = coff_symbols;
+ sdata->count = coff_symbol_count;
+
+ return 1;
+}
+
+/* Add EDATA to the list in STATE. */
+
+static void
+coff_add_syminfo_data (struct backtrace_state *state,
+ struct coff_syminfo_data *sdata)
+{
+ if (!state->threaded)
+ {
+ struct coff_syminfo_data **pp;
+
+ for (pp = (struct coff_syminfo_data **) (void *) &state->syminfo_data;
+ *pp != NULL;
+ pp = &(*pp)->next)
+ ;
+ *pp = sdata;
+ }
+ else
+ {
+ while (1)
+ {
+ struct coff_syminfo_data **pp;
+
+ pp = (struct coff_syminfo_data **) (void *) &state->syminfo_data;
+
+ while (1)
+ {
+ struct coff_syminfo_data *p;
+
+ p = backtrace_atomic_load_pointer (pp);
+
+ if (p == NULL)
+ break;
+
+ pp = &p->next;
+ }
+
+ if (__sync_bool_compare_and_swap (pp, NULL, sdata))
+ break;
+ }
+ }
+}
+
+/* Compare an ADDR against an elf_symbol for bsearch. We allocate one
+ extra entry in the array so that this can look safely at the next
+ entry. */
+
+static int
+coff_symbol_search (const void *vkey, const void *ventry)
+{
+ const uintptr_t *key = (const uintptr_t *) vkey;
+ const struct coff_symbol *entry = (const struct coff_symbol *) ventry;
+ uintptr_t addr;
+
+ addr = *key;
+ if (addr < entry->address)
+ return -1;
+ else if (addr >= entry[1].address)
+ return 1;
+ else
+ return 0;
+}
+
+/* Return the symbol name and value for an ADDR. */
+
+static void
+coff_syminfo (struct backtrace_state *state, uintptr_t addr,
+ backtrace_syminfo_callback callback,
+ backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
+ void *data)
+{
+ struct coff_syminfo_data *sdata;
+ struct coff_symbol *sym = NULL;
+
+ if (!state->threaded)
+ {
+ for (sdata = (struct coff_syminfo_data *) state->syminfo_data;
+ sdata != NULL;
+ sdata = sdata->next)
+ {
+ sym = ((struct coff_symbol *)
+ bsearch (&addr, sdata->symbols, sdata->count,
+ sizeof (struct coff_symbol), coff_symbol_search));
+ if (sym != NULL)
+ break;
+ }
+ }
+ else
+ {
+ struct coff_syminfo_data **pp;
+
+ pp = (struct coff_syminfo_data **) (void *) &state->syminfo_data;
+ while (1)
+ {
+ sdata = backtrace_atomic_load_pointer (pp);
+ if (sdata == NULL)
+ break;
+
+ sym = ((struct coff_symbol *)
+ bsearch (&addr, sdata->symbols, sdata->count,
+ sizeof (struct coff_symbol), coff_symbol_search));
+ if (sym != NULL)
+ break;
+
+ pp = &sdata->next;
+ }
+ }
+
+ if (sym == NULL)
+ callback (data, addr, NULL, 0, 0);
+ else
+ callback (data, addr, sym->name, sym->address, 0);
+}
+
+/* Add the backtrace data for one PE/COFF file. Returns 1 on success,
+ 0 on failure (in both cases descriptor is closed). */
+
+static int
+coff_add (struct backtrace_state *state, int descriptor,
+ backtrace_error_callback error_callback, void *data,
+ fileline *fileline_fn, int *found_sym, int *found_dwarf)
+{
+ struct backtrace_view fhdr_view;
+ off_t fhdr_off;
+ int magic_ok;
+ b_coff_file_header fhdr;
+ off_t opt_sects_off;
+ size_t opt_sects_size;
+ unsigned int sects_num;
+ struct backtrace_view sects_view;
+ int sects_view_valid;
+ const b_coff_optional_header *opt_hdr;
+ const b_coff_section_header *sects;
+ struct backtrace_view str_view;
+ int str_view_valid;
+ size_t str_size;
+ off_t str_off;
+ struct backtrace_view syms_view;
+ off_t syms_off;
+ size_t syms_size;
+ int syms_view_valid;
+ unsigned int syms_num;
+ unsigned int i;
+ struct debug_section_info sections[DEBUG_MAX];
+ off_t min_offset;
+ off_t max_offset;
+ struct backtrace_view debug_view;
+ int debug_view_valid;
+ int is_64;
+ uintptr_t image_base;
+ struct dwarf_sections dwarf_sections;
+
+ *found_sym = 0;
+ *found_dwarf = 0;
+
+ sects_view_valid = 0;
+ syms_view_valid = 0;
+ str_view_valid = 0;
+ debug_view_valid = 0;
+
+ /* Map the MS-DOS stub (if any) and extract file header offset. */
+ if (!backtrace_get_view (state, descriptor, 0, 0x40, error_callback,
+ data, &fhdr_view))
+ goto fail;
+
+ {
+ const unsigned char *vptr = fhdr_view.data;
+
+ if (vptr[0] == 'M' && vptr[1] == 'Z')
+ fhdr_off = coff_read4 (vptr + 0x3c);
+ else
+ fhdr_off = 0;
+ }
+
+ backtrace_release_view (state, &fhdr_view, error_callback, data);
+
+ /* Map the coff file header. */
+ if (!backtrace_get_view (state, descriptor, fhdr_off,
+ sizeof (b_coff_file_header) + 4,
+ error_callback, data, &fhdr_view))
+ goto fail;
+
+ if (fhdr_off != 0)
+ {
+ const char *magic = (const char *) fhdr_view.data;
+ magic_ok = memcmp (magic, "PE\0", 4) == 0;
+ fhdr_off += 4;
+
+ memcpy (&fhdr, fhdr_view.data + 4, sizeof fhdr);
+ }
+ else
+ {
+ memcpy (&fhdr, fhdr_view.data, sizeof fhdr);
+ /* TODO: test fhdr.machine for coff but non-PE platforms. */
+ magic_ok = 0;
+ }
+ backtrace_release_view (state, &fhdr_view, error_callback, data);
+
+ if (!magic_ok)
+ {
+ error_callback (data, "executable file is not COFF", 0);
+ goto fail;
+ }
+
+ sects_num = fhdr.number_of_sections;
+ syms_num = fhdr.number_of_symbols;
+
+ opt_sects_off = fhdr_off + sizeof (fhdr);
+ opt_sects_size = (fhdr.size_of_optional_header
+ + sects_num * sizeof (b_coff_section_header));
+
+ /* To translate PC to file/line when using DWARF, we need to find
+ the .debug_info and .debug_line sections. */
+
+ /* Read the optional header and the section headers. */
+
+ if (!backtrace_get_view (state, descriptor, opt_sects_off, opt_sects_size,
+ error_callback, data, &sects_view))
+ goto fail;
+ sects_view_valid = 1;
+ opt_hdr = (const b_coff_optional_header *) sects_view.data;
+ sects = (const b_coff_section_header *)
+ (sects_view.data + fhdr.size_of_optional_header);
+
+ is_64 = 0;
+ if (fhdr.size_of_optional_header > sizeof (*opt_hdr))
+ {
+ if (opt_hdr->magic == PE_MAGIC)
+ image_base = opt_hdr->u.pe.image_base;
+ else if (opt_hdr->magic == PEP_MAGIC)
+ {
+ image_base = opt_hdr->u.pep.image_base;
+ is_64 = 1;
+ }
+ else
+ {
+ error_callback (data, "bad magic in PE optional header", 0);
+ goto fail;
+ }
+ }
+ else
+ image_base = 0;
+
+ /* Read the symbol table and the string table. */
+
+ if (fhdr.pointer_to_symbol_table == 0)
+ {
+ /* No symbol table, no string table. */
+ str_off = 0;
+ str_size = 0;
+ syms_num = 0;
+ syms_size = 0;
+ }
+ else
+ {
+ /* Symbol table is followed by the string table. The string table
+ starts with its length (on 4 bytes).
+ Map the symbol table and the length of the string table. */
+ syms_off = fhdr.pointer_to_symbol_table;
+ syms_size = syms_num * SYM_SZ;
+
+ if (!backtrace_get_view (state, descriptor, syms_off, syms_size + 4,
+ error_callback, data, &syms_view))
+ goto fail;
+ syms_view_valid = 1;
+
+ str_size = coff_read4 (syms_view.data + syms_size);
+
+ str_off = syms_off + syms_size;
+
+ if (str_size > 4)
+ {
+ /* Map string table (including the length word). */
+
+ if (!backtrace_get_view (state, descriptor, str_off, str_size,
+ error_callback, data, &str_view))
+ goto fail;
+ str_view_valid = 1;
+ }
+ }
+
+ memset (sections, 0, sizeof sections);
+
+ /* Look for the symbol table. */
+ for (i = 0; i < sects_num; ++i)
+ {
+ const b_coff_section_header *s = sects + i;
+ unsigned int str_off;
+ int j;
+
+ if (s->name[0] == '/')
+ {
+ /* Extended section name. */
+ str_off = atoi (s->name + 1);
+ }
+ else
+ str_off = 0;
+
+ for (j = 0; j < (int) DEBUG_MAX; ++j)
+ {
+ const char *dbg_name = debug_section_names[j];
+ int match;
+
+ if (str_off != 0)
+ match = coff_long_name_eq (dbg_name, str_off, &str_view);
+ else
+ match = coff_short_name_eq (dbg_name, s->name);
+ if (match)
+ {
+ sections[j].offset = s->pointer_to_raw_data;
+ sections[j].size = s->virtual_size <= s->size_of_raw_data ?
+ s->virtual_size : s->size_of_raw_data;
+ break;
+ }
+ }
+ }
+
+ if (syms_num != 0)
+ {
+ struct coff_syminfo_data *sdata;
+
+ sdata = ((struct coff_syminfo_data *)
+ backtrace_alloc (state, sizeof *sdata, error_callback, data));
+ if (sdata == NULL)
+ goto fail;
+
+ if (!coff_initialize_syminfo (state, image_base, is_64,
+ sects, sects_num,
+ syms_view.data, syms_size,
+ str_view.data, str_size,
+ error_callback, data, sdata))
+ {
+ backtrace_free (state, sdata, sizeof *sdata, error_callback, data);
+ goto fail;
+ }
+
+ *found_sym = 1;
+
+ coff_add_syminfo_data (state, sdata);
+ }
+
+ backtrace_release_view (state, &sects_view, error_callback, data);
+ sects_view_valid = 0;
+ if (syms_view_valid)
+ {
+ backtrace_release_view (state, &syms_view, error_callback, data);
+ syms_view_valid = 0;
+ }
+
+ /* Read all the debug sections in a single view, since they are
+ probably adjacent in the file. We never release this view. */
+
+ min_offset = 0;
+ max_offset = 0;
+ for (i = 0; i < (int) DEBUG_MAX; ++i)
+ {
+ off_t end;
+
+ if (sections[i].size == 0)
+ continue;
+ if (min_offset == 0 || sections[i].offset < min_offset)
+ min_offset = sections[i].offset;
+ end = sections[i].offset + sections[i].size;
+ if (end > max_offset)
+ max_offset = end;
+ }
+ if (min_offset == 0 || max_offset == 0)
+ {
+ if (!backtrace_close (descriptor, error_callback, data))
+ goto fail;
+ *fileline_fn = coff_nodebug;
+ return 1;
+ }
+
+ if (!backtrace_get_view (state, descriptor, min_offset,
+ max_offset - min_offset,
+ error_callback, data, &debug_view))
+ goto fail;
+ debug_view_valid = 1;
+
+ /* We've read all we need from the executable. */
+ if (!backtrace_close (descriptor, error_callback, data))
+ goto fail;
+ descriptor = -1;
+
+ for (i = 0; i < (int) DEBUG_MAX; ++i)
+ {
+ size_t size = sections[i].size;
+ dwarf_sections.size[i] = size;
+ if (size == 0)
+ dwarf_sections.data[i] = NULL;
+ else
+ dwarf_sections.data[i] = ((const unsigned char *) debug_view.data
+ + (sections[i].offset - min_offset));
+ }
+
+ if (!backtrace_dwarf_add (state, /* base_address */ 0, &dwarf_sections,
+ 0, /* FIXME: is_bigendian */
+ NULL, /* altlink */
+ error_callback, data, fileline_fn,
+ NULL /* returned fileline_entry */))
+ goto fail;
+
+ *found_dwarf = 1;
+
+ return 1;
+
+ fail:
+ if (sects_view_valid)
+ backtrace_release_view (state, &sects_view, error_callback, data);
+ if (str_view_valid)
+ backtrace_release_view (state, &str_view, error_callback, data);
+ if (syms_view_valid)
+ backtrace_release_view (state, &syms_view, error_callback, data);
+ if (debug_view_valid)
+ backtrace_release_view (state, &debug_view, error_callback, data);
+ if (descriptor != -1)
+ backtrace_close (descriptor, error_callback, data);
+ return 0;
+}
+
+/* Initialize the backtrace data we need from an ELF executable. At
+ the ELF level, all we need to do is find the debug info
+ sections. */
+
+int
+backtrace_initialize (struct backtrace_state *state,
+ const char *filename ATTRIBUTE_UNUSED, int descriptor,
+ backtrace_error_callback error_callback,
+ void *data, fileline *fileline_fn)
+{
+ int ret;
+ int found_sym;
+ int found_dwarf;
+ fileline coff_fileline_fn;
+
+ ret = coff_add (state, descriptor, error_callback, data,
+ &coff_fileline_fn, &found_sym, &found_dwarf);
+ if (!ret)
+ return 0;
+
+ if (!state->threaded)
+ {
+ if (found_sym)
+ state->syminfo_fn = coff_syminfo;
+ else if (state->syminfo_fn == NULL)
+ state->syminfo_fn = coff_nosyms;
+ }
+ else
+ {
+ if (found_sym)
+ backtrace_atomic_store_pointer (&state->syminfo_fn, coff_syminfo);
+ else
+ (void) __sync_bool_compare_and_swap (&state->syminfo_fn, NULL,
+ coff_nosyms);
+ }
+
+ if (!state->threaded)
+ {
+ if (state->fileline_fn == NULL || state->fileline_fn == coff_nodebug)
+ *fileline_fn = coff_fileline_fn;
+ }
+ else
+ {
+ fileline current_fn;
+
+ current_fn = backtrace_atomic_load_pointer (&state->fileline_fn);
+ if (current_fn == NULL || current_fn == coff_nodebug)
+ *fileline_fn = coff_fileline_fn;
+ }
+
+ return 1;
+}
diff --git a/thirdparty/libbacktrace/posix.c b/thirdparty/libbacktrace/posix.c
new file mode 100644
index 0000000000..924631d2e6
--- /dev/null
+++ b/thirdparty/libbacktrace/posix.c
@@ -0,0 +1,104 @@
+/* posix.c -- POSIX file I/O routines for the backtrace library.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+#ifndef FD_CLOEXEC
+#define FD_CLOEXEC 1
+#endif
+
+/* Open a file for reading. */
+
+int
+backtrace_open (const char *filename, backtrace_error_callback error_callback,
+ void *data, int *does_not_exist)
+{
+ int descriptor;
+
+ if (does_not_exist != NULL)
+ *does_not_exist = 0;
+
+ descriptor = open (filename, (int) (O_RDONLY | O_BINARY | O_CLOEXEC));
+ if (descriptor < 0)
+ {
+ /* If DOES_NOT_EXIST is not NULL, then don't call ERROR_CALLBACK
+ if the file does not exist. We treat lacking permission to
+ open the file as the file not existing; this case arises when
+ running the libgo syscall package tests as root. */
+ if (does_not_exist != NULL && (errno == ENOENT || errno == EACCES))
+ *does_not_exist = 1;
+ else
+ error_callback (data, filename, errno);
+ return -1;
+ }
+
+#ifdef HAVE_FCNTL
+ /* Set FD_CLOEXEC just in case the kernel does not support
+ O_CLOEXEC. It doesn't matter if this fails for some reason.
+ FIXME: At some point it should be safe to only do this if
+ O_CLOEXEC == 0. */
+ fcntl (descriptor, F_SETFD, FD_CLOEXEC);
+#endif
+
+ return descriptor;
+}
+
+/* Close DESCRIPTOR. */
+
+int
+backtrace_close (int descriptor, backtrace_error_callback error_callback,
+ void *data)
+{
+ if (close (descriptor) < 0)
+ {
+ error_callback (data, "close", errno);
+ return 0;
+ }
+ return 1;
+}
diff --git a/thirdparty/libbacktrace/print.c b/thirdparty/libbacktrace/print.c
new file mode 100644
index 0000000000..93d0d3abb4
--- /dev/null
+++ b/thirdparty/libbacktrace/print.c
@@ -0,0 +1,92 @@
+/* print.c -- Print the current backtrace.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* Passed to callbacks. */
+
+struct print_data
+{
+ struct backtrace_state *state;
+ FILE *f;
+};
+
+/* Print one level of a backtrace. */
+
+static int
+print_callback (void *data, uintptr_t pc, const char *filename, int lineno,
+ const char *function)
+{
+ struct print_data *pdata = (struct print_data *) data;
+
+ fprintf (pdata->f, "0x%lx %s\n\t%s:%d\n",
+ (unsigned long) pc,
+ function == NULL ? "???" : function,
+ filename == NULL ? "???" : filename,
+ lineno);
+ return 0;
+}
+
+/* Print errors to stderr. */
+
+static void
+error_callback (void *data, const char *msg, int errnum)
+{
+ struct print_data *pdata = (struct print_data *) data;
+
+ if (pdata->state->filename != NULL)
+ fprintf (stderr, "%s: ", pdata->state->filename);
+ fprintf (stderr, "libbacktrace: %s", msg);
+ if (errnum > 0)
+ fprintf (stderr, ": %s", strerror (errnum));
+ fputc ('\n', stderr);
+}
+
+/* Print a backtrace. */
+
+void __attribute__((noinline))
+backtrace_print (struct backtrace_state *state, int skip, FILE *f)
+{
+ struct print_data data;
+
+ data.state = state;
+ data.f = f;
+ backtrace_full (state, skip + 1, print_callback, error_callback,
+ (void *) &data);
+}
diff --git a/thirdparty/libbacktrace/read.c b/thirdparty/libbacktrace/read.c
new file mode 100644
index 0000000000..1811c8d2e0
--- /dev/null
+++ b/thirdparty/libbacktrace/read.c
@@ -0,0 +1,110 @@
+/* read.c -- File views without mmap.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* This file implements file views when mmap is not available. */
+
+/* Create a view of SIZE bytes from DESCRIPTOR at OFFSET. */
+
+int
+backtrace_get_view (struct backtrace_state *state, int descriptor,
+ off_t offset, uint64_t size,
+ backtrace_error_callback error_callback,
+ void *data, struct backtrace_view *view)
+{
+ uint64_t got;
+ ssize_t r;
+
+ if ((uint64_t) (size_t) size != size)
+ {
+ error_callback (data, "file size too large", 0);
+ return 0;
+ }
+
+ if (lseek (descriptor, offset, SEEK_SET) < 0)
+ {
+ error_callback (data, "lseek", errno);
+ return 0;
+ }
+
+ view->base = backtrace_alloc (state, size, error_callback, data);
+ if (view->base == NULL)
+ return 0;
+ view->data = view->base;
+ view->len = size;
+
+ got = 0;
+ while (got < size)
+ {
+ r = read (descriptor, view->base, size - got);
+ if (r < 0)
+ {
+ error_callback (data, "read", errno);
+ free (view->base);
+ return 0;
+ }
+ if (r == 0)
+ break;
+ got += (uint64_t) r;
+ }
+
+ if (got < size)
+ {
+ error_callback (data, "file too short", 0);
+ free (view->base);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Release a view read by backtrace_get_view. */
+
+void
+backtrace_release_view (struct backtrace_state *state,
+ struct backtrace_view *view,
+ backtrace_error_callback error_callback,
+ void *data)
+{
+ backtrace_free (state, view->base, view->len, error_callback, data);
+ view->data = NULL;
+ view->base = NULL;
+}
diff --git a/thirdparty/libbacktrace/simple.c b/thirdparty/libbacktrace/simple.c
new file mode 100644
index 0000000000..785e726e6b
--- /dev/null
+++ b/thirdparty/libbacktrace/simple.c
@@ -0,0 +1,108 @@
+/* simple.c -- The backtrace_simple function.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include "unwind.h"
+#include "backtrace.h"
+
+/* The simple_backtrace routine. */
+
+/* Data passed through _Unwind_Backtrace. */
+
+struct backtrace_simple_data
+{
+ /* Number of frames to skip. */
+ int skip;
+ /* Library state. */
+ struct backtrace_state *state;
+ /* Callback routine. */
+ backtrace_simple_callback callback;
+ /* Error callback routine. */
+ backtrace_error_callback error_callback;
+ /* Data to pass to callback routine. */
+ void *data;
+ /* Value to return from backtrace. */
+ int ret;
+};
+
+/* Unwind library callback routine. This is passed to
+ _Unwind_Backtrace. */
+
+static _Unwind_Reason_Code
+simple_unwind (struct _Unwind_Context *context, void *vdata)
+{
+ struct backtrace_simple_data *bdata = (struct backtrace_simple_data *) vdata;
+ uintptr_t pc;
+ int ip_before_insn = 0;
+
+#ifdef HAVE_GETIPINFO
+ pc = _Unwind_GetIPInfo (context, &ip_before_insn);
+#else
+ pc = _Unwind_GetIP (context);
+#endif
+
+ if (bdata->skip > 0)
+ {
+ --bdata->skip;
+ return _URC_NO_REASON;
+ }
+
+ if (!ip_before_insn)
+ --pc;
+
+ bdata->ret = bdata->callback (bdata->data, pc);
+
+ if (bdata->ret != 0)
+ return _URC_END_OF_STACK;
+
+ return _URC_NO_REASON;
+}
+
+/* Get a simple stack backtrace. */
+
+int __attribute__((noinline))
+backtrace_simple (struct backtrace_state *state, int skip,
+ backtrace_simple_callback callback,
+ backtrace_error_callback error_callback, void *data)
+{
+ struct backtrace_simple_data bdata;
+
+ bdata.skip = skip + 1;
+ bdata.state = state;
+ bdata.callback = callback;
+ bdata.error_callback = error_callback;
+ bdata.data = data;
+ bdata.ret = 0;
+ _Unwind_Backtrace (simple_unwind, &bdata);
+ return bdata.ret;
+}
diff --git a/thirdparty/libbacktrace/sort.c b/thirdparty/libbacktrace/sort.c
new file mode 100644
index 0000000000..a60a980e65
--- /dev/null
+++ b/thirdparty/libbacktrace/sort.c
@@ -0,0 +1,108 @@
+/* sort.c -- Sort without allocating memory
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <stddef.h>
+#include <sys/types.h>
+
+#include "backtrace.h"
+#include "internal.h"
+
+/* The GNU glibc version of qsort allocates memory, which we must not
+ do if we are invoked by a signal handler. So provide our own
+ sort. */
+
+static void
+swap (char *a, char *b, size_t size)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++, a++, b++)
+ {
+ char t;
+
+ t = *a;
+ *a = *b;
+ *b = t;
+ }
+}
+
+void
+backtrace_qsort (void *basearg, size_t count, size_t size,
+ int (*compar) (const void *, const void *))
+{
+ char *base = (char *) basearg;
+ size_t i;
+ size_t mid;
+
+ tail_recurse:
+ if (count < 2)
+ return;
+
+ /* The symbol table and DWARF tables, which is all we use this
+ routine for, tend to be roughly sorted. Pick the middle element
+ in the array as our pivot point, so that we are more likely to
+ cut the array in half for each recursion step. */
+ swap (base, base + (count / 2) * size, size);
+
+ mid = 0;
+ for (i = 1; i < count; i++)
+ {
+ if ((*compar) (base, base + i * size) > 0)
+ {
+ ++mid;
+ if (i != mid)
+ swap (base + mid * size, base + i * size, size);
+ }
+ }
+
+ if (mid > 0)
+ swap (base, base + mid * size, size);
+
+ /* Recurse with the smaller array, loop with the larger one. That
+ ensures that our maximum stack depth is log count. */
+ if (2 * mid < count)
+ {
+ backtrace_qsort (base, mid, size, compar);
+ base += (mid + 1) * size;
+ count -= mid + 1;
+ goto tail_recurse;
+ }
+ else
+ {
+ backtrace_qsort (base + (mid + 1) * size, count - (mid + 1),
+ size, compar);
+ count = mid;
+ goto tail_recurse;
+ }
+}
diff --git a/thirdparty/libbacktrace/state.c b/thirdparty/libbacktrace/state.c
new file mode 100644
index 0000000000..0f368a2390
--- /dev/null
+++ b/thirdparty/libbacktrace/state.c
@@ -0,0 +1,72 @@
+/* state.c -- Create the backtrace state.
+ Copyright (C) 2012-2021 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Google.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ (1) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (2) Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ (3) The name of the author may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE. */
+
+#include "config.h"
+
+#include <string.h>
+#include <sys/types.h>
+
+#include "backtrace.h"
+#include "backtrace-supported.h"
+#include "internal.h"
+
+/* Create the backtrace state. This will then be passed to all the
+ other routines. */
+
+struct backtrace_state *
+backtrace_create_state (const char *filename, int threaded,
+ backtrace_error_callback error_callback,
+ void *data)
+{
+ struct backtrace_state init_state;
+ struct backtrace_state *state;
+
+#ifndef HAVE_SYNC_FUNCTIONS
+ if (threaded)
+ {
+ error_callback (data, "backtrace library does not support threads", 0);
+ return NULL;
+ }
+#endif
+
+ memset (&init_state, 0, sizeof init_state);
+ init_state.filename = filename;
+ init_state.threaded = threaded;
+
+ state = ((struct backtrace_state *)
+ backtrace_alloc (&init_state, sizeof *state, error_callback, data));
+ if (state == NULL)
+ return NULL;
+ *state = init_state;
+
+ return state;
+}