diff --git a/.venv312/Scripts/Activate.ps1 b/.venv312/Scripts/Activate.ps1
new file mode 100644
index 0000000000..918eac3e8e
--- /dev/null
+++ b/.venv312/Scripts/Activate.ps1
@@ -0,0 +1,528 @@
+<#
+.Synopsis
+Activate a Python virtual environment for the current PowerShell session.
+
+.Description
+Pushes the python executable for a virtual environment to the front of the
+$Env:PATH environment variable and sets the prompt to signify that you are
+in a Python virtual environment. Makes use of the command line switches as
+well as the `pyvenv.cfg` file values present in the virtual environment.
+
+.Parameter VenvDir
+Path to the directory that contains the virtual environment to activate. The
+default value for this is the parent of the directory that the Activate.ps1
+script is located within.
+
+.Parameter Prompt
+The prompt prefix to display when this virtual environment is activated. By
+default, this prompt is the name of the virtual environment folder (VenvDir)
+surrounded by parentheses and followed by a single space (ie. '(.venv) ').
+
+.Example
+Activate.ps1
+Activates the Python virtual environment that contains the Activate.ps1 script.
+
+.Example
+Activate.ps1 -Verbose
+Activates the Python virtual environment that contains the Activate.ps1 script,
+and shows extra information about the activation as it executes.
+
+.Example
+Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
+Activates the Python virtual environment located in the specified location.
+
+.Example
+Activate.ps1 -Prompt "MyPython"
+Activates the Python virtual environment that contains the Activate.ps1 script,
+and prefixes the current prompt with the specified string (surrounded in
+parentheses) while the virtual environment is active.
+
+.Notes
+On Windows, it may be required to enable this Activate.ps1 script by setting the
+execution policy for the user. You can do this by issuing the following PowerShell
+command:
+
+PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
+
+For more information on Execution Policies:
+https://go.microsoft.com/fwlink/?LinkID=135170
+
+#>
+Param(
+ [Parameter(Mandatory = $false)]
+ [String]
+ $VenvDir,
+ [Parameter(Mandatory = $false)]
+ [String]
+ $Prompt
+)
+
+<# Function declarations --------------------------------------------------- #>
+
+<#
+.Synopsis
+Remove all shell session elements added by the Activate script, including the
+addition of the virtual environment's Python executable from the beginning of
+the PATH variable.
+
+.Parameter NonDestructive
+If present, do not remove this function from the global namespace for the
+session.
+
+#>
+function global:deactivate ([switch]$NonDestructive) {
+ # Revert to original values
+
+ # The prior prompt:
+ if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
+ Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
+ Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
+ }
+
+ # The prior PYTHONHOME:
+ if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
+ Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
+ Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
+ }
+
+ # The prior PATH:
+ if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
+ Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
+ Remove-Item -Path Env:_OLD_VIRTUAL_PATH
+ }
+
+ # Just remove the VIRTUAL_ENV altogether:
+ if (Test-Path -Path Env:VIRTUAL_ENV) {
+ Remove-Item -Path env:VIRTUAL_ENV
+ }
+
+ # Just remove VIRTUAL_ENV_PROMPT altogether.
+ if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
+ Remove-Item -Path env:VIRTUAL_ENV_PROMPT
+ }
+
+ # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
+ if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
+ Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
+ }
+
+ # Leave deactivate function in the global namespace if requested:
+ if (-not $NonDestructive) {
+ Remove-Item -Path function:deactivate
+ }
+}
+
+<#
+.Description
+Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
+given folder, and returns them in a map.
+
+For each line in the pyvenv.cfg file, if that line can be parsed into exactly
+two strings separated by `=` (with any amount of whitespace surrounding the =)
+then it is considered a `key = value` line. The left hand string is the key,
+the right hand is the value.
+
+If the value starts with a `'` or a `"` then the first and last character is
+stripped from the value before being captured.
+
+.Parameter ConfigDir
+Path to the directory that contains the `pyvenv.cfg` file.
+#>
+function Get-PyVenvConfig(
+ [String]
+ $ConfigDir
+) {
+ Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
+
+ # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
+ $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
+
+ # An empty map will be returned if no config file is found.
+ $pyvenvConfig = @{ }
+
+ if ($pyvenvConfigPath) {
+
+ Write-Verbose "File exists, parse `key = value` lines"
+ $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
+
+ $pyvenvConfigContent | ForEach-Object {
+ $keyval = $PSItem -split "\s*=\s*", 2
+ if ($keyval[0] -and $keyval[1]) {
+ $val = $keyval[1]
+
+ # Remove extraneous quotations around a string value.
+ if ("'""".Contains($val.Substring(0, 1))) {
+ $val = $val.Substring(1, $val.Length - 2)
+ }
+
+ $pyvenvConfig[$keyval[0]] = $val
+ Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
+ }
+ }
+ }
+ return $pyvenvConfig
+}
+
+
+<# Begin Activate script --------------------------------------------------- #>
+
+# Determine the containing directory of this script
+$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
+$VenvExecDir = Get-Item -Path $VenvExecPath
+
+Write-Verbose "Activation script is located in path: '$VenvExecPath'"
+Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
+Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
+
+# Set values required in priority: CmdLine, ConfigFile, Default
+# First, get the location of the virtual environment, it might not be
+# VenvExecDir if specified on the command line.
+if ($VenvDir) {
+ Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
+}
+else {
+ Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
+ $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
+ Write-Verbose "VenvDir=$VenvDir"
+}
+
+# Next, read the `pyvenv.cfg` file to determine any required value such
+# as `prompt`.
+$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
+
+# Next, set the prompt from the command line, or the config file, or
+# just use the name of the virtual environment folder.
+if ($Prompt) {
+ Write-Verbose "Prompt specified as argument, using '$Prompt'"
+}
+else {
+ Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
+ if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
+ Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
+ $Prompt = $pyvenvCfg['prompt'];
+ }
+ else {
+ Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
+ Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
+ $Prompt = Split-Path -Path $venvDir -Leaf
+ }
+}
+
+Write-Verbose "Prompt = '$Prompt'"
+Write-Verbose "VenvDir='$VenvDir'"
+
+# Deactivate any currently active virtual environment, but leave the
+# deactivate function in place.
+deactivate -nondestructive
+
+# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
+# that there is an activated venv.
+$env:VIRTUAL_ENV = $VenvDir
+
+if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
+
+ Write-Verbose "Setting prompt to '$Prompt'"
+
+ # Set the prompt to include the env name
+ # Make sure _OLD_VIRTUAL_PROMPT is global
+ function global:_OLD_VIRTUAL_PROMPT { "" }
+ Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
+ New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
+
+ function global:prompt {
+ Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
+ _OLD_VIRTUAL_PROMPT
+ }
+ $env:VIRTUAL_ENV_PROMPT = $Prompt
+}
+
+# Clear PYTHONHOME
+if (Test-Path -Path Env:PYTHONHOME) {
+ Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
+ Remove-Item -Path Env:PYTHONHOME
+}
+
+# Add the venv to the PATH
+Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
+$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"
+
+# SIG # Begin signature block
+# MII0CQYJKoZIhvcNAQcCoIIz+jCCM/YCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBnL745ElCYk8vk
+# dBtMuQhLeWJ3ZGfzKW4DHCYzAn+QB6CCG9IwggXMMIIDtKADAgECAhBUmNLR1FsZ
+# lUgTecgRwIeZMA0GCSqGSIb3DQEBDAUAMHcxCzAJBgNVBAYTAlVTMR4wHAYDVQQK
+# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xSDBGBgNVBAMTP01pY3Jvc29mdCBJZGVu
+# dGl0eSBWZXJpZmljYXRpb24gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAy
+# MDAeFw0yMDA0MTYxODM2MTZaFw00NTA0MTYxODQ0NDBaMHcxCzAJBgNVBAYTAlVT
+# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xSDBGBgNVBAMTP01pY3Jv
+# c29mdCBJZGVudGl0eSBWZXJpZmljYXRpb24gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRo
+# b3JpdHkgMjAyMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALORKgeD
+# Bmf9np3gx8C3pOZCBH8Ppttf+9Va10Wg+3cL8IDzpm1aTXlT2KCGhFdFIMeiVPvH
+# or+Kx24186IVxC9O40qFlkkN/76Z2BT2vCcH7kKbK/ULkgbk/WkTZaiRcvKYhOuD
+# PQ7k13ESSCHLDe32R0m3m/nJxxe2hE//uKya13NnSYXjhr03QNAlhtTetcJtYmrV
+# qXi8LW9J+eVsFBT9FMfTZRY33stuvF4pjf1imxUs1gXmuYkyM6Nix9fWUmcIxC70
+# ViueC4fM7Ke0pqrrBc0ZV6U6CwQnHJFnni1iLS8evtrAIMsEGcoz+4m+mOJyoHI1
+# vnnhnINv5G0Xb5DzPQCGdTiO0OBJmrvb0/gwytVXiGhNctO/bX9x2P29Da6SZEi3
+# W295JrXNm5UhhNHvDzI9e1eM80UHTHzgXhgONXaLbZ7LNnSrBfjgc10yVpRnlyUK
+# xjU9lJfnwUSLgP3B+PR0GeUw9gb7IVc+BhyLaxWGJ0l7gpPKWeh1R+g/OPTHU3mg
+# trTiXFHvvV84wRPmeAyVWi7FQFkozA8kwOy6CXcjmTimthzax7ogttc32H83rwjj
+# O3HbbnMbfZlysOSGM1l0tRYAe1BtxoYT2v3EOYI9JACaYNq6lMAFUSw0rFCZE4e7
+# swWAsk0wAly4JoNdtGNz764jlU9gKL431VulAgMBAAGjVDBSMA4GA1UdDwEB/wQE
+# AwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTIftJqhSobyhmYBAcnz1AQ
+# T2ioojAQBgkrBgEEAYI3FQEEAwIBADANBgkqhkiG9w0BAQwFAAOCAgEAr2rd5hnn
+# LZRDGU7L6VCVZKUDkQKL4jaAOxWiUsIWGbZqWl10QzD0m/9gdAmxIR6QFm3FJI9c
+# Zohj9E/MffISTEAQiwGf2qnIrvKVG8+dBetJPnSgaFvlVixlHIJ+U9pW2UYXeZJF
+# xBA2CFIpF8svpvJ+1Gkkih6PsHMNzBxKq7Kq7aeRYwFkIqgyuH4yKLNncy2RtNwx
+# AQv3Rwqm8ddK7VZgxCwIo3tAsLx0J1KH1r6I3TeKiW5niB31yV2g/rarOoDXGpc8
+# FzYiQR6sTdWD5jw4vU8w6VSp07YEwzJ2YbuwGMUrGLPAgNW3lbBeUU0i/OxYqujY
+# lLSlLu2S3ucYfCFX3VVj979tzR/SpncocMfiWzpbCNJbTsgAlrPhgzavhgplXHT2
+# 6ux6anSg8Evu75SjrFDyh+3XOjCDyft9V77l4/hByuVkrrOj7FjshZrM77nq81YY
+# uVxzmq/FdxeDWds3GhhyVKVB0rYjdaNDmuV3fJZ5t0GNv+zcgKCf0Xd1WF81E+Al
+# GmcLfc4l+gcK5GEh2NQc5QfGNpn0ltDGFf5Ozdeui53bFv0ExpK91IjmqaOqu/dk
+# ODtfzAzQNb50GQOmxapMomE2gj4d8yu8l13bS3g7LfU772Aj6PXsCyM2la+YZr9T
+# 03u4aUoqlmZpxJTG9F9urJh4iIAGXKKy7aIwggb+MIIE5qADAgECAhMzAAM/y2Wy
+# WWnFfpZcAAAAAz/LMA0GCSqGSIb3DQEBDAUAMFoxCzAJBgNVBAYTAlVTMR4wHAYD
+# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKzApBgNVBAMTIk1pY3Jvc29mdCBJ
+# RCBWZXJpZmllZCBDUyBBT0MgQ0EgMDEwHhcNMjUwNDA4MDEwNzI0WhcNMjUwNDEx
+# MDEwNzI0WjB8MQswCQYDVQQGEwJVUzEPMA0GA1UECBMGT3JlZ29uMRIwEAYDVQQH
+# EwlCZWF2ZXJ0b24xIzAhBgNVBAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9u
+# MSMwIQYDVQQDExpQeXRob24gU29mdHdhcmUgRm91bmRhdGlvbjCCAaIwDQYJKoZI
+# hvcNAQEBBQADggGPADCCAYoCggGBAI0elXEcbTdGLOszMU2fzimHGM9Y4EjwFgC2
+# iGPdieHc0dK1DyEIdtnvjKxnG/KICC3J2MrhePGzMEkie3yQjx05B5leG0q8YoGU
+# m9z9K67V6k3DSXX0vQe9FbaNVuyXed31MEf/qek7Zo4ELxu8n/LO3ibURBLRHNoW
+# Dz9zr4DcU+hha0bdIL6SnKMLwHqRj59gtFFEPqXcOVO7kobkzQS3O1T5KNL/zGuW
+# UGQln7fS4YI9bj24bfrSeG/QzLgChVYScxnUgjAANfT1+SnSxrT4/esMtfbcvfID
+# BIvOWk+FPPj9IQWsAMEG/LLG4cF/pQ/TozUXKx362GJBbe6paTM/RCUTcffd83h2
+# bXo9vXO/roZYk6H0ecd2h2FFzLUQn/0i4RQQSOp6zt1eDf28h6F8ev+YYKcChph8
+# iRt32bJPcLQVbUzhehzT4C0pz6oAqPz8s0BGvlj1G6r4CY1Cs2YiMU09/Fl64pWf
+# IsA/ReaYj6yNsgQZNUcvzobK2mTxMwIDAQABo4ICGTCCAhUwDAYDVR0TAQH/BAIw
+# ADAOBgNVHQ8BAf8EBAMCB4AwPAYDVR0lBDUwMwYKKwYBBAGCN2EBAAYIKwYBBQUH
+# AwMGGysGAQQBgjdhgqKNuwqmkohkgZH0oEWCk/3hbzAdBgNVHQ4EFgQU4Y4Xr/Xn
+# zEXblXrNC0ZLdaPEJYUwHwYDVR0jBBgwFoAU6IPEM9fcnwycdpoKptTfh6ZeWO4w
+# ZwYDVR0fBGAwXjBcoFqgWIZWaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9w
+# cy9jcmwvTWljcm9zb2Z0JTIwSUQlMjBWZXJpZmllZCUyMENTJTIwQU9DJTIwQ0El
+# MjAwMS5jcmwwgaUGCCsGAQUFBwEBBIGYMIGVMGQGCCsGAQUFBzAChlhodHRwOi8v
+# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMElEJTIw
+# VmVyaWZpZWQlMjBDUyUyMEFPQyUyMENBJTIwMDEuY3J0MC0GCCsGAQUFBzABhiFo
+# dHRwOi8vb25lb2NzcC5taWNyb3NvZnQuY29tL29jc3AwZgYDVR0gBF8wXTBRBgwr
+# BgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQu
+# Y29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMAgGBmeBDAEEATANBgkqhkiG
+# 9w0BAQwFAAOCAgEAKTeVGPXsDKqQLe1OuKx6K6q711FPxNQyLOOqeenH8zybHwNo
+# k05cMk39HQ7u+R9BQIL0bWexb7wa3XeKaX06p7aY/OQs+ycvUi/fC6RGlaLWmQ9D
+# YhZn2TBz5znimvSf3P+aidCuXeDU5c8GpBFog6fjEa/k+n7TILi0spuYZ4yC9R48
+# R63/VvpLi2SqxfJbx5n92bY6driNzAntjoravF25BSejXVrdzefbnqbQnZPB39g8
+# XHygGPb0912fIuNKPLQa/uCnmYdXJnPb0ZgMxxA8fyxvL2Q30Qf5xpFDssPDElvD
+# DoAbvR24CWvuHbu+CMMr2SJUpX4RRvDioO7JeB6wZb+64MXyPUSSf6QwkKNsHPIa
+# e9tSfREh86sYn5bOA0Wd+Igk0RpA5jDRTu3GgPOPWbm1PU+VoeqThtHt6R3l17pr
+# aQ5wIuuLXgxi1K4ZWgtvXw8BtIXfZz24qCtoo0+3kEGUpEHBgkF1SClbRb8uAzx+
+# 0ROGniLPJRU20Xfn7CgipeKLcNn33JPFwQHk1zpbGS0090mi0erOQCz0S47YdHmm
+# RJcbkNIL9DeNAglTZ/TFxrYUM1NRS1Cp4e63MgBKcWh9VJNokInzzmS+bofZz+u1
+# mm8YNtiJjdT8fmizXdUEk68EXQhOs0+HBNvc9nMRK6R28MZu/J+PaUcPL84wggda
+# MIIFQqADAgECAhMzAAAABzeMW6HZW4zUAAAAAAAHMA0GCSqGSIb3DQEBDAUAMGMx
+# CzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xNDAy
+# BgNVBAMTK01pY3Jvc29mdCBJRCBWZXJpZmllZCBDb2RlIFNpZ25pbmcgUENBIDIw
+# MjEwHhcNMjEwNDEzMTczMTU0WhcNMjYwNDEzMTczMTU0WjBaMQswCQYDVQQGEwJV
+# UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSswKQYDVQQDEyJNaWNy
+# b3NvZnQgSUQgVmVyaWZpZWQgQ1MgQU9DIENBIDAxMIICIjANBgkqhkiG9w0BAQEF
+# AAOCAg8AMIICCgKCAgEAt/fAAygHxbo+jxA04hNI8bz+EqbWvSu9dRgAawjCZau1
+# Y54IQal5ArpJWi8cIj0WA+mpwix8iTRguq9JELZvTMo2Z1U6AtE1Tn3mvq3mywZ9
+# SexVd+rPOTr+uda6GVgwLA80LhRf82AvrSwxmZpCH/laT08dn7+Gt0cXYVNKJORm
+# 1hSrAjjDQiZ1Jiq/SqiDoHN6PGmT5hXKs22E79MeFWYB4y0UlNqW0Z2LPNua8k0r
+# bERdiNS+nTP/xsESZUnrbmyXZaHvcyEKYK85WBz3Sr6Et8Vlbdid/pjBpcHI+Hyt
+# oaUAGE6rSWqmh7/aEZeDDUkz9uMKOGasIgYnenUk5E0b2U//bQqDv3qdhj9UJYWA
+# DNYC/3i3ixcW1VELaU+wTqXTxLAFelCi/lRHSjaWipDeE/TbBb0zTCiLnc9nmOjZ
+# PKlutMNho91wxo4itcJoIk2bPot9t+AV+UwNaDRIbcEaQaBycl9pcYwWmf0bJ4IF
+# n/CmYMVG1ekCBxByyRNkFkHmuMXLX6PMXcveE46jMr9syC3M8JHRddR4zVjd/FxB
+# nS5HOro3pg6StuEPshrp7I/Kk1cTG8yOWl8aqf6OJeAVyG4lyJ9V+ZxClYmaU5yv
+# tKYKk1FLBnEBfDWw+UAzQV0vcLp6AVx2Fc8n0vpoyudr3SwZmckJuz7R+S79BzMC
+# AwEAAaOCAg4wggIKMA4GA1UdDwEB/wQEAwIBhjAQBgkrBgEEAYI3FQEEAwIBADAd
+# BgNVHQ4EFgQU6IPEM9fcnwycdpoKptTfh6ZeWO4wVAYDVR0gBE0wSzBJBgRVHSAA
+# MEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv
+# RG9jcy9SZXBvc2l0b3J5Lmh0bTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAS
+# BgNVHRMBAf8ECDAGAQH/AgEAMB8GA1UdIwQYMBaAFNlBKbAPD2Ns72nX9c0pnqRI
+# ajDmMHAGA1UdHwRpMGcwZaBjoGGGX2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w
+# a2lvcHMvY3JsL01pY3Jvc29mdCUyMElEJTIwVmVyaWZpZWQlMjBDb2RlJTIwU2ln
+# bmluZyUyMFBDQSUyMDIwMjEuY3JsMIGuBggrBgEFBQcBAQSBoTCBnjBtBggrBgEF
+# BQcwAoZhaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNy
+# b3NvZnQlMjBJRCUyMFZlcmlmaWVkJTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAy
+# MDIxLmNydDAtBggrBgEFBQcwAYYhaHR0cDovL29uZW9jc3AubWljcm9zb2Z0LmNv
+# bS9vY3NwMA0GCSqGSIb3DQEBDAUAA4ICAQB3/utLItkwLTp4Nfh99vrbpSsL8NwP
+# Ij2+TBnZGL3C8etTGYs+HZUxNG+rNeZa+Rzu9oEcAZJDiGjEWytzMavD6Bih3nEW
+# FsIW4aGh4gB4n/pRPeeVrK4i1LG7jJ3kPLRhNOHZiLUQtmrF4V6IxtUFjvBnijaZ
+# 9oIxsSSQP8iHMjP92pjQrHBFWHGDbkmx+yO6Ian3QN3YmbdfewzSvnQmKbkiTibJ
+# gcJ1L0TZ7BwmsDvm+0XRsPOfFgnzhLVqZdEyWww10bflOeBKqkb3SaCNQTz8nsha
+# UZhrxVU5qNgYjaaDQQm+P2SEpBF7RolEC3lllfuL4AOGCtoNdPOWrx9vBZTXAVdT
+# E2r0IDk8+5y1kLGTLKzmNFn6kVCc5BddM7xoDWQ4aUoCRXcsBeRhsclk7kVXP+zJ
+# GPOXwjUJbnz2Kt9iF/8B6FDO4blGuGrogMpyXkuwCC2Z4XcfyMjPDhqZYAPGGTUI
+# NMtFbau5RtGG1DOWE9edCahtuPMDgByfPixvhy3sn7zUHgIC/YsOTMxVuMQi/bga
+# memo/VNKZrsZaS0nzmOxKpg9qDefj5fJ9gIHXcp2F0OHcVwe3KnEXa8kqzMDfrRl
+# /wwKrNSFn3p7g0b44Ad1ONDmWt61MLQvF54LG62i6ffhTCeoFT9Z9pbUo2gxlyTF
+# g7Bm0fgOlnRfGDCCB54wggWGoAMCAQICEzMAAAAHh6M0o3uljhwAAAAAAAcwDQYJ
+# KoZIhvcNAQEMBQAwdzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBD
+# b3Jwb3JhdGlvbjFIMEYGA1UEAxM/TWljcm9zb2Z0IElkZW50aXR5IFZlcmlmaWNh
+# dGlvbiBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDIwMB4XDTIxMDQwMTIw
+# MDUyMFoXDTM2MDQwMTIwMTUyMFowYzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1p
+# Y3Jvc29mdCBDb3Jwb3JhdGlvbjE0MDIGA1UEAxMrTWljcm9zb2Z0IElEIFZlcmlm
+# aWVkIENvZGUgU2lnbmluZyBQQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
+# ADCCAgoCggIBALLwwK8ZiCji3VR6TElsaQhVCbRS/3pK+MHrJSj3Zxd3KU3rlfL3
+# qrZilYKJNqztA9OQacr1AwoNcHbKBLbsQAhBnIB34zxf52bDpIO3NJlfIaTE/xrw
+# eLoQ71lzCHkD7A4As1Bs076Iu+mA6cQzsYYH/Cbl1icwQ6C65rU4V9NQhNUwgrx9
+# rGQ//h890Q8JdjLLw0nV+ayQ2Fbkd242o9kH82RZsH3HEyqjAB5a8+Ae2nPIPc8s
+# ZU6ZE7iRrRZywRmrKDp5+TcmJX9MRff241UaOBs4NmHOyke8oU1TYrkxh+YeHgfW
+# o5tTgkoSMoayqoDpHOLJs+qG8Tvh8SnifW2Jj3+ii11TS8/FGngEaNAWrbyfNrC6
+# 9oKpRQXY9bGH6jn9NEJv9weFxhTwyvx9OJLXmRGbAUXN1U9nf4lXezky6Uh/cgjk
+# Vd6CGUAf0K+Jw+GE/5VpIVbcNr9rNE50Sbmy/4RTCEGvOq3GhjITbCa4crCzTTHg
+# YYjHs1NbOc6brH+eKpWLtr+bGecy9CrwQyx7S/BfYJ+ozst7+yZtG2wR461uckFu
+# 0t+gCwLdN0A6cFtSRtR8bvxVFyWwTtgMMFRuBa3vmUOTnfKLsLefRaQcVTgRnzeL
+# zdpt32cdYKp+dhr2ogc+qM6K4CBI5/j4VFyC4QFeUP2YAidLtvpXRRo3AgMBAAGj
+# ggI1MIICMTAOBgNVHQ8BAf8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0O
+# BBYEFNlBKbAPD2Ns72nX9c0pnqRIajDmMFQGA1UdIARNMEswSQYEVR0gADBBMD8G
+# CCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3Mv
+# UmVwb3NpdG9yeS5odG0wGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwDwYDVR0T
+# AQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTIftJqhSobyhmYBAcnz1AQT2ioojCBhAYD
+# VR0fBH0wezB5oHegdYZzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j
+# cmwvTWljcm9zb2Z0JTIwSWRlbnRpdHklMjBWZXJpZmljYXRpb24lMjBSb290JTIw
+# Q2VydGlmaWNhdGUlMjBBdXRob3JpdHklMjAyMDIwLmNybDCBwwYIKwYBBQUHAQEE
+# gbYwgbMwgYEGCCsGAQUFBzAChnVodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtp
+# b3BzL2NlcnRzL01pY3Jvc29mdCUyMElkZW50aXR5JTIwVmVyaWZpY2F0aW9uJTIw
+# Um9vdCUyMENlcnRpZmljYXRlJTIwQXV0aG9yaXR5JTIwMjAyMC5jcnQwLQYIKwYB
+# BQUHMAGGIWh0dHA6Ly9vbmVvY3NwLm1pY3Jvc29mdC5jb20vb2NzcDANBgkqhkiG
+# 9w0BAQwFAAOCAgEAfyUqnv7Uq+rdZgrbVyNMul5skONbhls5fccPlmIbzi+OwVdP
+# Q4H55v7VOInnmezQEeW4LqK0wja+fBznANbXLB0KrdMCbHQpbLvG6UA/Xv2pfpVI
+# E1CRFfNF4XKO8XYEa3oW8oVH+KZHgIQRIwAbyFKQ9iyj4aOWeAzwk+f9E5StNp5T
+# 8FG7/VEURIVWArbAzPt9ThVN3w1fAZkF7+YU9kbq1bCR2YD+MtunSQ1Rft6XG7b4
+# e0ejRA7mB2IoX5hNh3UEauY0byxNRG+fT2MCEhQl9g2i2fs6VOG19CNep7SquKaB
+# jhWmirYyANb0RJSLWjinMLXNOAga10n8i9jqeprzSMU5ODmrMCJE12xS/NWShg/t
+# uLjAsKP6SzYZ+1Ry358ZTFcx0FS/mx2vSoU8s8HRvy+rnXqyUJ9HBqS0DErVLjQw
+# K8VtsBdekBmdTbQVoCgPCqr+PDPB3xajYnzevs7eidBsM71PINK2BoE2UfMwxCCX
+# 3mccFgx6UsQeRSdVVVNSyALQe6PT12418xon2iDGE81OGCreLzDcMAZnrUAx4XQL
+# Uz6ZTl65yPUiOh3k7Yww94lDf+8oG2oZmDh5O1Qe38E+M3vhKwmzIeoB1dVLlz4i
+# 3IpaDcR+iuGjH2TdaC1ZOmBXiCRKJLj4DT2uhJ04ji+tHD6n58vhavFIrmcxgheN
+# MIIXiQIBATBxMFoxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
+# cG9yYXRpb24xKzApBgNVBAMTIk1pY3Jvc29mdCBJRCBWZXJpZmllZCBDUyBBT0Mg
+# Q0EgMDECEzMAAz/LZbJZacV+llwAAAADP8swDQYJYIZIAWUDBAIBBQCggcowGQYJ
+# KoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQB
+# gjcCARUwLwYJKoZIhvcNAQkEMSIEIGcBno/ti9PCrR9sXrajsTvlHQvGxbk63JiI
+# URJByQuGMF4GCisGAQQBgjcCAQwxUDBOoEiARgBCAHUAaQBsAHQAOgAgAFIAZQBs
+# AGUAYQBzAGUAXwB2ADMALgAxADIALgAxADAAXwAyADAAMgA1ADAANAAwADgALgAw
+# ADKhAoAAMA0GCSqGSIb3DQEBAQUABIIBgE9xMVem4h5iAbvBzmB1pTdA4LYNkvd/
+# hSbYmJRt5oJqBR0RGbUmcfYAgTlhdb/S84aGvI3N62I8qeMApnH89q+UF0i8p6+U
+# Qza6Mu1cAHCq0NkHH6+N8g7nIfe5Cn+BBCBJ6kuYfQm9bx1JwEm5/yVCwG9I6+XV
+# 3WonOeA8djuZFfB9OIW6N9ubX7X+nYqWaeT6w6/lDs8mL+s0Fumy4mJ8B15pd9mr
+# N6dIRFokzhuALq6G0USKFzYf3qJQ4GyCos/Luez3cr8sE/78ds6vah5IlLP6qXMM
+# ETwAdoymIYSm3Dly3lflodd4d7/nkMhfHITOxSUDoBbCP6MO1rhChX591rJy/omK
+# 0RdM9ZpMl6VXHhzZ+lB8U/6j7xJGlxJSJHet7HFEuTnJEjY9dDy2bUgzk0vK1Rs2
+# l7VLOP3X87p9iVz5vDAOQB0fcsMDJvhIzJlmIb5z2uZ6hqD4UZdTDMLIBWe9H7Kv
+# rhmGDPHPRboFKtTrKoKcWaf4fJJ2NUtYlKGCFKAwghScBgorBgEEAYI3AwMBMYIU
+# jDCCFIgGCSqGSIb3DQEHAqCCFHkwghR1AgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFh
+# BgsqhkiG9w0BCRABBKCCAVAEggFMMIIBSAIBAQYKKwYBBAGEWQoDATAxMA0GCWCG
+# SAFlAwQCAQUABCAY3nVyqXzzboHwsVGd+j5FjG9eaMv+O3mJKpX+3EJ43AIGZ9gU
+# uyvYGBMyMDI1MDQwODEyNDEyMi40MTNaMASAAgH0oIHgpIHdMIHaMQswCQYDVQQG
+# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
+# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQg
+# QW1lcmljYSBPcGVyYXRpb25zMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjozREE1
+# LTk2M0ItRTFGNDE1MDMGA1UEAxMsTWljcm9zb2Z0IFB1YmxpYyBSU0EgVGltZSBT
+# dGFtcGluZyBBdXRob3JpdHmggg8gMIIHgjCCBWqgAwIBAgITMwAAAAXlzw//Zi7J
+# hwAAAAAABTANBgkqhkiG9w0BAQwFADB3MQswCQYDVQQGEwJVUzEeMBwGA1UEChMV
+# TWljcm9zb2Z0IENvcnBvcmF0aW9uMUgwRgYDVQQDEz9NaWNyb3NvZnQgSWRlbnRp
+# dHkgVmVyaWZpY2F0aW9uIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMjAw
+# HhcNMjAxMTE5MjAzMjMxWhcNMzUxMTE5MjA0MjMxWjBhMQswCQYDVQQGEwJVUzEe
+# MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3Nv
+# ZnQgUHVibGljIFJTQSBUaW1lc3RhbXBpbmcgQ0EgMjAyMDCCAiIwDQYJKoZIhvcN
+# AQEBBQADggIPADCCAgoCggIBAJ5851Jj/eDFnwV9Y7UGIqMcHtfnlzPREwW9ZUZH
+# d5HBXXBvf7KrQ5cMSqFSHGqg2/qJhYqOQxwuEQXG8kB41wsDJP5d0zmLYKAY8Zxv
+# 3lYkuLDsfMuIEqvGYOPURAH+Ybl4SJEESnt0MbPEoKdNihwM5xGv0rGofJ1qOYST
+# Ncc55EbBT7uq3wx3mXhtVmtcCEr5ZKTkKKE1CxZvNPWdGWJUPC6e4uRfWHIhZcgC
+# sJ+sozf5EeH5KrlFnxpjKKTavwfFP6XaGZGWUG8TZaiTogRoAlqcevbiqioUz1Yt
+# 4FRK53P6ovnUfANjIgM9JDdJ4e0qiDRm5sOTiEQtBLGd9Vhd1MadxoGcHrRCsS5r
+# O9yhv2fjJHrmlQ0EIXmp4DhDBieKUGR+eZ4CNE3ctW4uvSDQVeSp9h1SaPV8UWEf
+# yTxgGjOsRpeexIveR1MPTVf7gt8hY64XNPO6iyUGsEgt8c2PxF87E+CO7A28TpjN
+# q5eLiiunhKbq0XbjkNoU5JhtYUrlmAbpxRjb9tSreDdtACpm3rkpxp7AQndnI0Sh
+# u/fk1/rE3oWsDqMX3jjv40e8KN5YsJBnczyWB4JyeeFMW3JBfdeAKhzohFe8U5w9
+# WuvcP1E8cIxLoKSDzCCBOu0hWdjzKNu8Y5SwB1lt5dQhABYyzR3dxEO/T1K/BVF3
+# rV69AgMBAAGjggIbMIICFzAOBgNVHQ8BAf8EBAMCAYYwEAYJKwYBBAGCNxUBBAMC
+# AQAwHQYDVR0OBBYEFGtpKDo1L0hjQM972K9J6T7ZPdshMFQGA1UdIARNMEswSQYE
+# VR0gADBBMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtp
+# b3BzL0RvY3MvUmVwb3NpdG9yeS5odG0wEwYDVR0lBAwwCgYIKwYBBQUHAwgwGQYJ
+# KwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSME
+# GDAWgBTIftJqhSobyhmYBAcnz1AQT2ioojCBhAYDVR0fBH0wezB5oHegdYZzaHR0
+# cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwSWRl
+# bnRpdHklMjBWZXJpZmljYXRpb24lMjBSb290JTIwQ2VydGlmaWNhdGUlMjBBdXRo
+# b3JpdHklMjAyMDIwLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwgYEGCCsGAQUFBzAC
+# hnVodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29m
+# dCUyMElkZW50aXR5JTIwVmVyaWZpY2F0aW9uJTIwUm9vdCUyMENlcnRpZmljYXRl
+# JTIwQXV0aG9yaXR5JTIwMjAyMC5jcnQwDQYJKoZIhvcNAQEMBQADggIBAF+Idsd+
+# bbVaFXXnTHho+k7h2ESZJRWluLE0Oa/pO+4ge/XEizXvhs0Y7+KVYyb4nHlugBes
+# nFqBGEdC2IWmtKMyS1OWIviwpnK3aL5JedwzbeBF7POyg6IGG/XhhJ3UqWeWTO+C
+# zb1c2NP5zyEh89F72u9UIw+IfvM9lzDmc2O2END7MPnrcjWdQnrLn1Ntday7JSyr
+# DvBdmgbNnCKNZPmhzoa8PccOiQljjTW6GePe5sGFuRHzdFt8y+bN2neF7Zu8hTO1
+# I64XNGqst8S+w+RUdie8fXC1jKu3m9KGIqF4aldrYBamyh3g4nJPj/LR2CBaLyD+
+# 2BuGZCVmoNR/dSpRCxlot0i79dKOChmoONqbMI8m04uLaEHAv4qwKHQ1vBzbV/nG
+# 89LDKbRSSvijmwJwxRxLLpMQ/u4xXxFfR4f/gksSkbJp7oqLwliDm/h+w0aJ/U5c
+# cnYhYb7vPKNMN+SZDWycU5ODIRfyoGl59BsXR/HpRGtiJquOYGmvA/pk5vC1lcnb
+# eMrcWD/26ozePQ/TWfNXKBOmkFpvPE8CH+EeGGWzqTCjdAsno2jzTeNSxlx3glDG
+# Jgcdz5D/AAxw9Sdgq/+rY7jjgs7X6fqPTXPmaCAJKVHAP19oEjJIBwD1LyHbaEgB
+# xFCogYSOiUIr0Xqcr1nJfiWG2GwYe6ZoAF1bMIIHljCCBX6gAwIBAgITMwAAAEYX
+# 5HV6yv3a5QAAAAAARjANBgkqhkiG9w0BAQwFADBhMQswCQYDVQQGEwJVUzEeMBwG
+# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQg
+# UHVibGljIFJTQSBUaW1lc3RhbXBpbmcgQ0EgMjAyMDAeFw0yNDExMjYxODQ4NDla
+# Fw0yNTExMTkxODQ4NDlaMIHaMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
+# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
+# cmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMSYw
+# JAYDVQQLEx1UaGFsZXMgVFNTIEVTTjozREE1LTk2M0ItRTFGNDE1MDMGA1UEAxMs
+# TWljcm9zb2Z0IFB1YmxpYyBSU0EgVGltZSBTdGFtcGluZyBBdXRob3JpdHkwggIi
+# MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwlXzoj/MNL1BfnV+gg4d0fZum
+# 1HdUJidSNTcDzpHJvmIBqH566zBYcV0TyN7+3qOnJjpoTx6JBMgNYnL5BmTX9Hrm
+# X0WdNMLf74u7NtBSuAD2sf6n2qUUrz7i8f7r0JiZixKJnkvA/1akLHppQMDCug1o
+# C0AYjd753b5vy1vWdrHXE9hL71BZe5DCq5/4LBny8aOQZlzvjewgONkiZm+Sfctk
+# Jjh9LxdkDlq5EvGE6YU0uC37XF7qkHvIksD2+XgBP0lEMfmPJo2fI9FwIA9YMX7K
+# IINEM5OY6nkvKryM9s5bK6LV4z48NYpiI1xvH15YDps+19nHCtKMVTZdB4cYhA0d
+# VqJ7dAu4VcxUwD1AEcMxWbIOR1z6OFkVY9GX5oH8k17d9t35PWfn0XuxW4SG/rim
+# gtFgpE/shRsy5nMCbHyeCdW0He1plrYQqTsSHP2n/lz2DCgIlnx+uvPLVf5+JG/1
+# d1i/LdwbC2WH6UEEJyZIl3a0YwM4rdzoR+P4dO9I/2oWOxXCYqFytYdCy9ljELUw
+# byLjrjRddteR8QTxrCfadKpKfFY6Ak/HNZPUHaAPak3baOIvV7Q8axo3DWQy2ib3
+# zXV6hMPNt1v90pv+q9daQdwUzUrgcbwThdrRhWHwlRIVg2sR668HPn4/8l9ikGok
+# rL6gAmVxNswEZ9awCwIDAQABo4IByzCCAccwHQYDVR0OBBYEFBE20NSvdrC6Z6cm
+# 6RPGP8YbqIrxMB8GA1UdIwQYMBaAFGtpKDo1L0hjQM972K9J6T7ZPdshMGwGA1Ud
+# HwRlMGMwYaBfoF2GW2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3Js
+# L01pY3Jvc29mdCUyMFB1YmxpYyUyMFJTQSUyMFRpbWVzdGFtcGluZyUyMENBJTIw
+# MjAyMC5jcmwweQYIKwYBBQUHAQEEbTBrMGkGCCsGAQUFBzAChl1odHRwOi8vd3d3
+# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFB1YmxpYyUy
+# MFJTQSUyMFRpbWVzdGFtcGluZyUyMENBJTIwMjAyMC5jcnQwDAYDVR0TAQH/BAIw
+# ADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAOBgNVHQ8BAf8EBAMCB4AwZgYDVR0g
+# BF8wXTBRBgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5t
+# aWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMAgGBmeBDAEE
+# AjANBgkqhkiG9w0BAQwFAAOCAgEAFIW5L+gGzX4gyHorS33YKXuK9iC91iZTpm30
+# x/EdHG6U8NAu2qityxjZVq6MDq300gspG0ntzLYqVhjfku7iNzE78k6tNgFCr9wv
+# GkIHeK+Q2RAO9/s5R8rhNC+lywOB+6K5Zi0kfO0agVXf7Nk2O6F6D9AEzNLijG+c
+# Oe5Ef2F5l4ZsVSkLFCI5jELC+r4KnNZjunc+qvjSz2DkNsXfrjFhyk+K7v7U7+JF
+# Z8kZ58yFuxEX0cxDKpJLxiNh/ODCOL2UxYkhyfI3AR0EhfxX9QZHVgxyZwnavR35
+# FxqLSiGTeAJsK7YN3bIxyuP6eCcnkX8TMdpu9kPD97sHnM7po0UQDrjaN7etviLD
+# xnax2nemdvJW3BewOLFrD1nSnd7ZHdPGPB3oWTCaK9/3XwQERLi3Xj+HZc89RP50
+# Nt7h7+3G6oq2kXYNidI9iWd+gL+lvkQZH9YTIfBCLWjvuXvUUUU+AvFI00Utqrvd
+# rIdqCFaqE9HHQgSfXeQ53xLWdMCztUP/YnMXiJxNBkc6UE2px/o6+/LXJDIpwIXR
+# 4HSodLfkfsNQl6FFrJ1xsOYGSHvcFkH8389RmUvrjr1NBbdesc4Bu4kox+3cabOZ
+# c1zm89G+1RRL2tReFzSMlYSGO3iKn3GGXmQiRmFlBb3CpbUVQz+fgxVMfeL0j4Lm
+# KQfT1jIxggPUMIID0AIBATB4MGExCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBQdWJsaWMgUlNB
+# IFRpbWVzdGFtcGluZyBDQSAyMDIwAhMzAAAARhfkdXrK/drlAAAAAABGMA0GCWCG
+# SAFlAwQCAQUAoIIBLTAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZI
+# hvcNAQkEMSIEIHgwQkiMhul6IrfEKmPaCFR+R91oZOlPqVgP/9PPcfn+MIHdBgsq
+# hkiG9w0BCRACLzGBzTCByjCBxzCBoAQgEid2SJpUPj5xQm73M4vqDmVh1QR6TiuT
+# UVkL3P8Wis4wfDBlpGMwYTELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29m
+# dCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFB1YmxpYyBSU0EgVGlt
+# ZXN0YW1waW5nIENBIDIwMjACEzMAAABGF+R1esr92uUAAAAAAEYwIgQgVp6I1YBM
+# Mni0rCuD57vEK/tzWZypHqWFikWLFVY11RwwDQYJKoZIhvcNAQELBQAEggIAnRBH
+# voM5+wbJp+aOwrrL8fi8Rv/eFV820Nhr+jMny73UscN60OWdcdcZDbjDlnDX1KEP
+# sNcEOFvaruHHrF4kDK8N0yemElNz63IgqhUoGoXXQKT2RgVg7T/kiQJH7zuaEjgB
+# YNniAZdXXJJ1C+uv2ZQzkGIEVIEA6pB5/xo4kFhrfkOrdGzqL8HXT/RZQDMn5Uzk
+# W+Sl2JmsyYBS4sgI9Ay3qT5nv+frzngbWlqx1dre21uj37Fgk5mWHJEdmY1nqTTd
+# 25j6oDLGPC8AS9wtgZBXggemKAXwyeOFFahXUFN7X7cbwTALy5aWjE/rqp+N5J7M
+# +YApl3aknUZ13KTXz9pfAF0uhmZimngvBHjijyctleF8HUP2RNAhS/l68OqW7oKi
+# Dqvb7tSHJbcnYkxo7dUq6ppfN51ah61ZsyMVG6SaH015+5QO1k50ohXcFff2GOuZ
+# d3Z9JOoAjIkeiVTNeRlPDlHtS0CSYu4ZKsWsst+0VY2R9rJBeoii9Xa0oiIggkYL
+# 1pHAPH0B1uLlvFcI6B+fAXe0OiCJodbO5lk8ZpvCG5WWYbjzp2c3B8PZGSBgEpSf
+# KYlVavvBAvaJCORUO7j8PyzzDINuzQorP9+i399ORjOnqeC92Cb0V12LcoqqtJaf
+# 7oSB86VOI0lfHnPUlLWvoiLHrFR5PsYkltOuPqU=
+# SIG # End signature block
diff --git a/.venv312/Scripts/about.exe b/.venv312/Scripts/about.exe
new file mode 100644
index 0000000000..5e03832b23
Binary files /dev/null and b/.venv312/Scripts/about.exe differ
diff --git a/.venv312/Scripts/activate b/.venv312/Scripts/activate
new file mode 100644
index 0000000000..0e5c587a10
--- /dev/null
+++ b/.venv312/Scripts/activate
@@ -0,0 +1,76 @@
+# This file must be used with "source bin/activate" *from bash*
+# You cannot run it directly
+
+deactivate () {
+ # reset old environment variables
+ if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
+ PATH="${_OLD_VIRTUAL_PATH:-}"
+ export PATH
+ unset _OLD_VIRTUAL_PATH
+ fi
+ if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
+ PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
+ export PYTHONHOME
+ unset _OLD_VIRTUAL_PYTHONHOME
+ fi
+
+ # Call hash to forget past locations. Without forgetting
+ # past locations the $PATH changes we made may not be respected.
+ # See "man bash" for more details. hash is usually a builtin of your shell
+ hash -r 2> /dev/null
+
+ if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
+ PS1="${_OLD_VIRTUAL_PS1:-}"
+ export PS1
+ unset _OLD_VIRTUAL_PS1
+ fi
+
+ unset VIRTUAL_ENV
+ unset VIRTUAL_ENV_PROMPT
+ if [ ! "${1:-}" = "nondestructive" ] ; then
+ # Self destruct!
+ unset -f deactivate
+ fi
+}
+
+# unset irrelevant variables
+deactivate nondestructive
+
+# on Windows, a path can contain colons and backslashes and has to be converted:
+case "$(uname)" in
+ CYGWIN*|MSYS*|MINGW*)
+ # transform D:\path\to\venv to /d/path/to/venv on MSYS and MINGW
+ # and to /cygdrive/d/path/to/venv on Cygwin
+ VIRTUAL_ENV=$(cygpath 'E:\hell\scancode.io\.venv312')
+ export VIRTUAL_ENV
+ ;;
+ *)
+ # use the path as-is
+ export VIRTUAL_ENV='E:\hell\scancode.io\.venv312'
+ ;;
+esac
+
+_OLD_VIRTUAL_PATH="$PATH"
+PATH="$VIRTUAL_ENV/"Scripts":$PATH"
+export PATH
+
+VIRTUAL_ENV_PROMPT='(.venv312) '
+export VIRTUAL_ENV_PROMPT
+
+# unset PYTHONHOME if set
+# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
+# could use `if (set -u; : $PYTHONHOME) ;` in bash
+if [ -n "${PYTHONHOME:-}" ] ; then
+ _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
+ unset PYTHONHOME
+fi
+
+if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
+ _OLD_VIRTUAL_PS1="${PS1:-}"
+ PS1="("'(.venv312) '") ${PS1:-}"
+ export PS1
+fi
+
+# Call hash to forget past commands. Without forgetting
+# past commands the $PATH changes we made may not be respected
+hash -r 2> /dev/null
diff --git a/.venv312/Scripts/activate.bat b/.venv312/Scripts/activate.bat
new file mode 100644
index 0000000000..d206c98b59
--- /dev/null
+++ b/.venv312/Scripts/activate.bat
@@ -0,0 +1,34 @@
+@echo off
+
+rem This file is UTF-8 encoded, so we need to update the current code page while executing it
+for /f "tokens=2 delims=:." %%a in ('"%SystemRoot%\System32\chcp.com"') do (
+ set _OLD_CODEPAGE=%%a
+)
+if defined _OLD_CODEPAGE (
+ "%SystemRoot%\System32\chcp.com" 65001 > nul
+)
+
+set "VIRTUAL_ENV=E:\hell\scancode.io\.venv312"
+
+if not defined PROMPT set PROMPT=$P$G
+
+if defined _OLD_VIRTUAL_PROMPT set PROMPT=%_OLD_VIRTUAL_PROMPT%
+if defined _OLD_VIRTUAL_PYTHONHOME set PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%
+
+set _OLD_VIRTUAL_PROMPT=%PROMPT%
+set PROMPT=(.venv312) %PROMPT%
+
+if defined PYTHONHOME set _OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME%
+set PYTHONHOME=
+
+if defined _OLD_VIRTUAL_PATH set PATH=%_OLD_VIRTUAL_PATH%
+if not defined _OLD_VIRTUAL_PATH set _OLD_VIRTUAL_PATH=%PATH%
+
+set "PATH=%VIRTUAL_ENV%\Scripts;%PATH%"
+set "VIRTUAL_ENV_PROMPT=(.venv312) "
+
+:END
+if defined _OLD_CODEPAGE (
+ "%SystemRoot%\System32\chcp.com" %_OLD_CODEPAGE% > nul
+ set _OLD_CODEPAGE=
+)
diff --git a/.venv312/Scripts/add-required-phrases.exe b/.venv312/Scripts/add-required-phrases.exe
new file mode 100644
index 0000000000..e01d28bc99
Binary files /dev/null and b/.venv312/Scripts/add-required-phrases.exe differ
diff --git a/.venv312/Scripts/bids-analyser.exe b/.venv312/Scripts/bids-analyser.exe
new file mode 100644
index 0000000000..5fe293df71
Binary files /dev/null and b/.venv312/Scripts/bids-analyser.exe differ
diff --git a/.venv312/Scripts/bids-scan.exe b/.venv312/Scripts/bids-scan.exe
new file mode 100644
index 0000000000..976b85518f
Binary files /dev/null and b/.venv312/Scripts/bids-scan.exe differ
diff --git a/.venv312/Scripts/bids-search.exe b/.venv312/Scripts/bids-search.exe
new file mode 100644
index 0000000000..548bb80a88
Binary files /dev/null and b/.venv312/Scripts/bids-search.exe differ
diff --git a/.venv312/Scripts/bids-ui.exe b/.venv312/Scripts/bids-ui.exe
new file mode 100644
index 0000000000..118301470d
Binary files /dev/null and b/.venv312/Scripts/bids-ui.exe differ
diff --git a/.venv312/Scripts/chardetect.exe b/.venv312/Scripts/chardetect.exe
new file mode 100644
index 0000000000..094a5abbce
Binary files /dev/null and b/.venv312/Scripts/chardetect.exe differ
diff --git a/.venv312/Scripts/container_inspector.exe b/.venv312/Scripts/container_inspector.exe
new file mode 100644
index 0000000000..a11e3a1e9b
Binary files /dev/null and b/.venv312/Scripts/container_inspector.exe differ
diff --git a/.venv312/Scripts/container_inspector_dockerfile.exe b/.venv312/Scripts/container_inspector_dockerfile.exe
new file mode 100644
index 0000000000..849f442b40
Binary files /dev/null and b/.venv312/Scripts/container_inspector_dockerfile.exe differ
diff --git a/.venv312/Scripts/container_inspector_squash.exe b/.venv312/Scripts/container_inspector_squash.exe
new file mode 100644
index 0000000000..7913c483b9
Binary files /dev/null and b/.venv312/Scripts/container_inspector_squash.exe differ
diff --git a/.venv312/Scripts/csv2rdf.exe b/.venv312/Scripts/csv2rdf.exe
new file mode 100644
index 0000000000..19ab127354
Binary files /dev/null and b/.venv312/Scripts/csv2rdf.exe differ
diff --git a/.venv312/Scripts/deactivate.bat b/.venv312/Scripts/deactivate.bat
new file mode 100644
index 0000000000..62a39a7584
--- /dev/null
+++ b/.venv312/Scripts/deactivate.bat
@@ -0,0 +1,22 @@
+@echo off
+
+if defined _OLD_VIRTUAL_PROMPT (
+ set "PROMPT=%_OLD_VIRTUAL_PROMPT%"
+)
+set _OLD_VIRTUAL_PROMPT=
+
+if defined _OLD_VIRTUAL_PYTHONHOME (
+ set "PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%"
+ set _OLD_VIRTUAL_PYTHONHOME=
+)
+
+if defined _OLD_VIRTUAL_PATH (
+ set "PATH=%_OLD_VIRTUAL_PATH%"
+)
+
+set _OLD_VIRTUAL_PATH=
+
+set VIRTUAL_ENV=
+set VIRTUAL_ENV_PROMPT=
+
+:END
diff --git a/.venv312/Scripts/django-admin.exe b/.venv312/Scripts/django-admin.exe
new file mode 100644
index 0000000000..cdac90cd4d
Binary files /dev/null and b/.venv312/Scripts/django-admin.exe differ
diff --git a/.venv312/Scripts/dotenv.exe b/.venv312/Scripts/dotenv.exe
new file mode 100644
index 0000000000..75cba52ab8
Binary files /dev/null and b/.venv312/Scripts/dotenv.exe differ
diff --git a/.venv312/Scripts/dumppdf.py b/.venv312/Scripts/dumppdf.py
new file mode 100644
index 0000000000..e009845bda
--- /dev/null
+++ b/.venv312/Scripts/dumppdf.py
@@ -0,0 +1,468 @@
+#!E:\hell\scancode.io\.venv312\Scripts\python.exe
+"""Extract pdf structure in XML format"""
+
+import logging
+import os.path
+import re
+import sys
+from argparse import ArgumentParser
+from collections.abc import Container, Iterable
+from typing import Any, TextIO, cast
+
+import pdfminer
+from pdfminer.pdfdocument import PDFDocument, PDFNoOutlines, PDFXRefFallback
+from pdfminer.pdfexceptions import (
+ PDFIOError,
+ PDFObjectNotFound,
+ PDFTypeError,
+ PDFValueError,
+)
+from pdfminer.pdfpage import PDFPage
+from pdfminer.pdfparser import PDFParser
+from pdfminer.pdftypes import PDFObjRef, PDFStream, resolve1, stream_value
+from pdfminer.psparser import LIT, PSKeyword, PSLiteral
+from pdfminer.utils import isnumber
+
+logging.basicConfig()
+logger = logging.getLogger(__name__)
+
+ESC_PAT = re.compile(r'[\000-\037&<>()"\042\047\134\177-\377]')
+
+
+def escape(s: str | bytes) -> str:
+ us = str(s, "latin-1") if isinstance(s, bytes) else s
+ return ESC_PAT.sub(lambda m: f"{ord(m.group(0))};", us)
+
+
+def dumpxml(out: TextIO, obj: object, codec: str | None = None) -> None:
+ if obj is None:
+ out.write("")
+ return
+
+ if isinstance(obj, dict):
+ out.write(f'\n')
+ for k, v in obj.items():
+ out.write(f"{k}\n")
+ out.write("")
+ dumpxml(out, v)
+ out.write("\n")
+ out.write("")
+ return
+
+ if isinstance(obj, list):
+ out.write(f'\n')
+ for v in obj:
+ dumpxml(out, v)
+ out.write("\n")
+ out.write("
")
+ return
+
+ if isinstance(obj, (str, bytes)):
+ out.write(f'{escape(obj)}')
+ return
+
+ if isinstance(obj, PDFStream):
+ if codec == "raw":
+ # Bug: writing bytes to text I/O. This will raise TypeError.
+ out.write(obj.get_rawdata()) # type: ignore [arg-type]
+ elif codec == "binary":
+ # Bug: writing bytes to text I/O. This will raise TypeError.
+ out.write(obj.get_data()) # type: ignore [arg-type]
+ else:
+ out.write("\n\n")
+ dumpxml(out, obj.attrs)
+ out.write("\n\n")
+ if codec == "text":
+ data = obj.get_data()
+ out.write(f'{escape(data)}\n')
+ out.write("")
+ return
+
+ if isinstance(obj, PDFObjRef):
+ out.write(f'')
+ return
+
+ if isinstance(obj, PSKeyword):
+ # Likely bug: obj.name is bytes, not str
+ out.write(f"{obj.name}") # type: ignore [str-bytes-safe]
+ return
+
+ if isinstance(obj, PSLiteral):
+ # Likely bug: obj.name may be bytes, not str
+ out.write(f"{obj.name}") # type: ignore [str-bytes-safe]
+ return
+
+ if isnumber(obj):
+ out.write(f"{obj}")
+ return
+
+ raise PDFTypeError(obj)
+
+
+def dumptrailers(
+ out: TextIO,
+ doc: PDFDocument,
+ show_fallback_xref: bool = False,
+) -> None:
+ for xref in doc.xrefs:
+ if not isinstance(xref, PDFXRefFallback) or show_fallback_xref:
+ out.write("\n")
+ dumpxml(out, xref.get_trailer())
+ out.write("\n\n\n")
+ no_xrefs = all(isinstance(xref, PDFXRefFallback) for xref in doc.xrefs)
+ if no_xrefs and not show_fallback_xref:
+ msg = (
+ "This PDF does not have an xref. Use --show-fallback-xref if "
+ "you want to display the content of a fallback xref that "
+ "contains all objects."
+ )
+ logger.warning(msg)
+
+
+def dumpallobjs(
+ out: TextIO,
+ doc: PDFDocument,
+ codec: str | None = None,
+ show_fallback_xref: bool = False,
+) -> None:
+ visited = set()
+ out.write("")
+ for xref in doc.xrefs:
+ for objid in xref.get_objids():
+ if objid in visited:
+ continue
+ visited.add(objid)
+ try:
+ obj = doc.getobj(objid)
+ if obj is None:
+ continue
+ out.write(f'\n\n")
+ except PDFObjectNotFound as e:
+ print(f"not found: {e!r}")
+ dumptrailers(out, doc, show_fallback_xref)
+ out.write("")
+
+
+def dumpoutline(
+ outfp: TextIO,
+ fname: str,
+ objids: Any,
+ pagenos: Container[int],
+ password: str = "",
+ dumpall: bool = False,
+ codec: str | None = None,
+ extractdir: str | None = None,
+) -> None:
+ with open(fname, "rb") as fp:
+ parser = PDFParser(fp)
+ doc = PDFDocument(parser, password)
+ pages = {
+ page.pageid: pageno
+ for (pageno, page) in enumerate(PDFPage.create_pages(doc), 1)
+ }
+
+ def resolve_dest(dest: object) -> Any:
+ if isinstance(dest, (str, bytes)):
+ dest = resolve1(doc.get_dest(dest))
+ elif isinstance(dest, PSLiteral):
+ dest = resolve1(doc.get_dest(dest.name))
+ if isinstance(dest, dict):
+ dest = dest["D"]
+ if isinstance(dest, PDFObjRef):
+ dest = dest.resolve()
+ return dest
+
+ try:
+ outlines = doc.get_outlines()
+ outfp.write("\n")
+ for level, title, dest, a, _se in outlines:
+ pageno = None
+ if dest:
+ dest = resolve_dest(dest)
+ pageno = pages[dest[0].objid]
+ elif a:
+ action = a
+ if isinstance(action, dict):
+ subtype = action.get("S")
+ if subtype and repr(subtype) == "/'GoTo'" and action.get("D"):
+ dest = resolve_dest(action["D"])
+ pageno = pages[dest[0].objid]
+ s = escape(title)
+ outfp.write(f'\n')
+ if dest is not None:
+ outfp.write("")
+ dumpxml(outfp, dest)
+ outfp.write("\n")
+ if pageno is not None:
+ outfp.write(f"{pageno!r}\n")
+ outfp.write("\n")
+ outfp.write("\n")
+ except PDFNoOutlines:
+ pass
+ parser.flush()
+
+
+LITERAL_FILESPEC = LIT("Filespec")
+LITERAL_EMBEDDEDFILE = LIT("EmbeddedFile")
+
+
+def extractembedded(fname: str, password: str, extractdir: str) -> None:
+ def extract1(objid: int, obj: dict[str, Any]) -> None:
+ filename = os.path.basename(obj.get("UF") or cast(bytes, obj.get("F")).decode())
+ fileref = obj["EF"].get("UF") or obj["EF"].get("F")
+ fileobj = doc.getobj(fileref.objid)
+ if not isinstance(fileobj, PDFStream):
+ error_msg = (
+ f"unable to process PDF: reference for {filename!r} is not a PDFStream"
+ )
+ raise PDFValueError(error_msg)
+ if fileobj.get("Type") is not LITERAL_EMBEDDEDFILE:
+ raise PDFValueError(
+ f"unable to process PDF: reference for {filename!r} "
+ "is not an EmbeddedFile",
+ )
+ path = os.path.join(extractdir, f"{objid:06d}-{filename}")
+ if os.path.exists(path):
+ raise PDFIOError(f"file exists: {path!r}")
+ print(f"extracting: {path!r}")
+ os.makedirs(os.path.dirname(path), exist_ok=True)
+ with open(path, "wb") as out:
+ out.write(fileobj.get_data())
+
+ with open(fname, "rb") as fp:
+ parser = PDFParser(fp)
+ doc = PDFDocument(parser, password)
+ extracted_objids = set()
+ for xref in doc.xrefs:
+ for objid in xref.get_objids():
+ obj = doc.getobj(objid)
+ if (
+ objid not in extracted_objids
+ and isinstance(obj, dict)
+ and obj.get("Type") is LITERAL_FILESPEC
+ ):
+ extracted_objids.add(objid)
+ extract1(objid, obj)
+
+
+def dumppdf(
+ outfp: TextIO,
+ fname: str,
+ objids: Iterable[int],
+ pagenos: Container[int],
+ password: str = "",
+ dumpall: bool = False,
+ codec: str | None = None,
+ extractdir: str | None = None,
+ show_fallback_xref: bool = False,
+) -> None:
+ with open(fname, "rb") as fp:
+ parser = PDFParser(fp)
+ doc = PDFDocument(parser, password)
+ if objids:
+ for objid in objids:
+ obj = doc.getobj(objid)
+ dumpxml(outfp, obj, codec=codec)
+ if pagenos:
+ for pageno, page in enumerate(PDFPage.create_pages(doc)):
+ if pageno in pagenos:
+ if codec:
+ for obj in page.contents:
+ obj = stream_value(obj)
+ dumpxml(outfp, obj, codec=codec)
+ else:
+ dumpxml(outfp, page.attrs)
+ if dumpall:
+ dumpallobjs(outfp, doc, codec, show_fallback_xref)
+ if (not objids) and (not pagenos) and (not dumpall):
+ dumptrailers(outfp, doc, show_fallback_xref)
+ if codec not in ("raw", "binary"):
+ outfp.write("\n")
+
+
+def create_parser() -> ArgumentParser:
+ parser = ArgumentParser(description=__doc__, add_help=True)
+ parser.add_argument(
+ "files",
+ type=str,
+ default=None,
+ nargs="+",
+ help="One or more paths to PDF files.",
+ )
+
+ parser.add_argument(
+ "--version",
+ "-v",
+ action="version",
+ version=f"pdfminer.six v{pdfminer.__version__}",
+ )
+ parser.add_argument(
+ "--debug",
+ "-d",
+ default=False,
+ action="store_true",
+ help="Use debug logging level.",
+ )
+ procedure_parser = parser.add_mutually_exclusive_group()
+ procedure_parser.add_argument(
+ "--extract-toc",
+ "-T",
+ default=False,
+ action="store_true",
+ help="Extract structure of outline",
+ )
+ procedure_parser.add_argument(
+ "--extract-embedded",
+ "-E",
+ type=str,
+ help="Extract embedded files",
+ )
+
+ parse_params = parser.add_argument_group(
+ "Parser",
+ description="Used during PDF parsing",
+ )
+ parse_params.add_argument(
+ "--page-numbers",
+ type=int,
+ default=None,
+ nargs="+",
+ help="A space-seperated list of page numbers to parse.",
+ )
+ parse_params.add_argument(
+ "--pagenos",
+ "-p",
+ type=str,
+ help="A comma-separated list of page numbers to parse. Included for "
+ "legacy applications, use --page-numbers for more idiomatic "
+ "argument entry.",
+ )
+ parse_params.add_argument(
+ "--objects",
+ "-i",
+ type=str,
+ help="Comma separated list of object numbers to extract",
+ )
+ parse_params.add_argument(
+ "--all",
+ "-a",
+ default=False,
+ action="store_true",
+ help="If the structure of all objects should be extracted",
+ )
+ parse_params.add_argument(
+ "--show-fallback-xref",
+ action="store_true",
+ help="Additionally show the fallback xref. Use this if the PDF "
+ "has zero or only invalid xref's. This setting is ignored if "
+ "--extract-toc or --extract-embedded is used.",
+ )
+ parse_params.add_argument(
+ "--password",
+ "-P",
+ type=str,
+ default="",
+ help="The password to use for decrypting PDF file.",
+ )
+
+ output_params = parser.add_argument_group(
+ "Output",
+ description="Used during output generation.",
+ )
+ output_params.add_argument(
+ "--outfile",
+ "-o",
+ type=str,
+ default="-",
+ help='Path to file where output is written. Or "-" (default) to '
+ "write to stdout.",
+ )
+ codec_parser = output_params.add_mutually_exclusive_group()
+ codec_parser.add_argument(
+ "--raw-stream",
+ "-r",
+ default=False,
+ action="store_true",
+ help="Write stream objects without encoding",
+ )
+ codec_parser.add_argument(
+ "--binary-stream",
+ "-b",
+ default=False,
+ action="store_true",
+ help="Write stream objects with binary encoding",
+ )
+ codec_parser.add_argument(
+ "--text-stream",
+ "-t",
+ default=False,
+ action="store_true",
+ help="Write stream objects as plain text",
+ )
+
+ return parser
+
+
+def main(argv: list[str] | None = None) -> None:
+ parser = create_parser()
+ args = parser.parse_args(args=argv)
+
+ if args.debug:
+ logging.getLogger().setLevel(logging.DEBUG)
+
+ objids = [int(x) for x in args.objects.split(",")] if args.objects else []
+
+ if args.page_numbers:
+ pagenos = {x - 1 for x in args.page_numbers}
+ elif args.pagenos:
+ pagenos = {int(x) - 1 for x in args.pagenos.split(",")}
+ else:
+ pagenos = set()
+
+ password = args.password
+
+ if args.raw_stream:
+ codec: str | None = "raw"
+ elif args.binary_stream:
+ codec = "binary"
+ elif args.text_stream:
+ codec = "text"
+ else:
+ codec = None
+
+ # Use context manager for file output, ensuring proper cleanup
+ with sys.stdout if args.outfile == "-" else open(args.outfile, "w") as outfp:
+ for fname in args.files:
+ if args.extract_toc:
+ dumpoutline(
+ outfp,
+ fname,
+ objids,
+ pagenos,
+ password=password,
+ dumpall=args.all,
+ codec=codec,
+ extractdir=None,
+ )
+ elif args.extract_embedded:
+ extractembedded(
+ fname, password=password, extractdir=args.extract_embedded
+ )
+ else:
+ dumppdf(
+ outfp,
+ fname,
+ objids,
+ pagenos,
+ password=password,
+ dumpall=args.all,
+ codec=codec,
+ extractdir=None,
+ show_fallback_xref=args.show_fallback_xref,
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/.venv312/Scripts/extractcode.exe b/.venv312/Scripts/extractcode.exe
new file mode 100644
index 0000000000..15c1d32753
Binary files /dev/null and b/.venv312/Scripts/extractcode.exe differ
diff --git a/.venv312/Scripts/ftfy.exe b/.venv312/Scripts/ftfy.exe
new file mode 100644
index 0000000000..02c56bf7fc
Binary files /dev/null and b/.venv312/Scripts/ftfy.exe differ
diff --git a/.venv312/Scripts/gen-new-required-phrases-rules.exe b/.venv312/Scripts/gen-new-required-phrases-rules.exe
new file mode 100644
index 0000000000..a78db0936f
Binary files /dev/null and b/.venv312/Scripts/gen-new-required-phrases-rules.exe differ
diff --git a/.venv312/Scripts/jsonschema.exe b/.venv312/Scripts/jsonschema.exe
new file mode 100644
index 0000000000..9928bd7cc7
Binary files /dev/null and b/.venv312/Scripts/jsonschema.exe differ
diff --git a/.venv312/Scripts/markdown-it.exe b/.venv312/Scripts/markdown-it.exe
new file mode 100644
index 0000000000..e8ec391077
Binary files /dev/null and b/.venv312/Scripts/markdown-it.exe differ
diff --git a/.venv312/Scripts/normalizer.exe b/.venv312/Scripts/normalizer.exe
new file mode 100644
index 0000000000..06419d539e
Binary files /dev/null and b/.venv312/Scripts/normalizer.exe differ
diff --git a/.venv312/Scripts/pdf2txt.py b/.venv312/Scripts/pdf2txt.py
new file mode 100644
index 0000000000..dbe6a8af6c
--- /dev/null
+++ b/.venv312/Scripts/pdf2txt.py
@@ -0,0 +1,324 @@
+#!E:\hell\scancode.io\.venv312\Scripts\python.exe
+"""A command line tool for extracting text and images from PDF and
+output it to plain text, html, xml or tags.
+"""
+
+import argparse
+import logging
+import sys
+from collections.abc import Container, Iterable
+from typing import Any
+
+import pdfminer.high_level
+from pdfminer.layout import LAParams
+from pdfminer.pdfexceptions import PDFValueError
+from pdfminer.utils import AnyIO
+
+logging.basicConfig()
+
+OUTPUT_TYPES = ((".htm", "html"), (".html", "html"), (".xml", "xml"), (".tag", "tag"))
+
+
+def float_or_disabled(x: str) -> float | None:
+ if x.lower().strip() == "disabled":
+ return None
+ try:
+ return float(x)
+ except ValueError as err:
+ raise argparse.ArgumentTypeError(f"invalid float value: {x}") from err
+
+
+def extract_text(
+ files: Iterable[str] = [],
+ outfile: str = "-",
+ laparams: LAParams | None = None,
+ output_type: str = "text",
+ codec: str = "utf-8",
+ strip_control: bool = False,
+ maxpages: int = 0,
+ page_numbers: Container[int] | None = None,
+ password: str = "",
+ scale: float = 1.0,
+ rotation: int = 0,
+ layoutmode: str = "normal",
+ output_dir: str | None = None,
+ debug: bool = False,
+ disable_caching: bool = False,
+ **kwargs: Any,
+) -> None:
+ if not files:
+ raise PDFValueError("Must provide files to work upon!")
+
+ if output_type == "text" and outfile != "-":
+ for override, alttype in OUTPUT_TYPES:
+ if outfile.endswith(override):
+ output_type = alttype
+
+ if outfile == "-":
+ outfp: AnyIO = sys.stdout
+ if sys.stdout.encoding is not None:
+ codec = "utf-8"
+ for fname in files:
+ with open(fname, "rb") as fp:
+ pdfminer.high_level.extract_text_to_fp(fp, **locals())
+ else:
+ # Use context manager for file output, ensuring proper cleanup
+ with open(outfile, "wb") as outfp:
+ for fname in files:
+ with open(fname, "rb") as fp:
+ pdfminer.high_level.extract_text_to_fp(fp, **locals())
+
+
+def create_parser() -> argparse.ArgumentParser:
+ parser = argparse.ArgumentParser(description=__doc__, add_help=True)
+ parser.add_argument(
+ "files",
+ type=str,
+ default=None,
+ nargs="+",
+ help="One or more paths to PDF files.",
+ )
+
+ parser.add_argument(
+ "--version",
+ "-v",
+ action="version",
+ version=f"pdfminer.six v{pdfminer.__version__}",
+ )
+ parser.add_argument(
+ "--debug",
+ "-d",
+ default=False,
+ action="store_true",
+ help="Use debug logging level.",
+ )
+ parser.add_argument(
+ "--disable-caching",
+ "-C",
+ default=False,
+ action="store_true",
+ help="If caching or resources, such as fonts, should be disabled.",
+ )
+
+ parse_params = parser.add_argument_group(
+ "Parser",
+ description="Used during PDF parsing",
+ )
+ parse_params.add_argument(
+ "--page-numbers",
+ type=int,
+ default=None,
+ nargs="+",
+ help="A space-seperated list of page numbers to parse.",
+ )
+ parse_params.add_argument(
+ "--pagenos",
+ "-p",
+ type=str,
+ help="A comma-separated list of page numbers to parse. "
+ "Included for legacy applications, use --page-numbers "
+ "for more idiomatic argument entry.",
+ )
+ parse_params.add_argument(
+ "--maxpages",
+ "-m",
+ type=int,
+ default=0,
+ help="The maximum number of pages to parse.",
+ )
+ parse_params.add_argument(
+ "--password",
+ "-P",
+ type=str,
+ default="",
+ help="The password to use for decrypting PDF file.",
+ )
+ parse_params.add_argument(
+ "--rotation",
+ "-R",
+ default=0,
+ type=int,
+ help="The number of degrees to rotate the PDF "
+ "before other types of processing.",
+ )
+
+ la_params = LAParams() # will be used for defaults
+ la_param_group = parser.add_argument_group(
+ "Layout analysis",
+ description="Used during layout analysis.",
+ )
+ la_param_group.add_argument(
+ "--no-laparams",
+ "-n",
+ default=False,
+ action="store_true",
+ help="If layout analysis parameters should be ignored.",
+ )
+ la_param_group.add_argument(
+ "--detect-vertical",
+ "-V",
+ default=la_params.detect_vertical,
+ action="store_true",
+ help="If vertical text should be considered during layout analysis",
+ )
+ la_param_group.add_argument(
+ "--line-overlap",
+ type=float,
+ default=la_params.line_overlap,
+ help="If two characters have more overlap than this they "
+ "are considered to be on the same line. The overlap is specified "
+ "relative to the minimum height of both characters.",
+ )
+ la_param_group.add_argument(
+ "--char-margin",
+ "-M",
+ type=float,
+ default=la_params.char_margin,
+ help="If two characters are closer together than this margin they "
+ "are considered to be part of the same line. The margin is "
+ "specified relative to the width of the character.",
+ )
+ la_param_group.add_argument(
+ "--word-margin",
+ "-W",
+ type=float,
+ default=la_params.word_margin,
+ help="If two characters on the same line are further apart than this "
+ "margin then they are considered to be two separate words, and "
+ "an intermediate space will be added for readability. The margin "
+ "is specified relative to the width of the character.",
+ )
+ la_param_group.add_argument(
+ "--line-margin",
+ "-L",
+ type=float,
+ default=la_params.line_margin,
+ help="If two lines are close together they are considered to "
+ "be part of the same paragraph. The margin is specified "
+ "relative to the height of a line.",
+ )
+ la_param_group.add_argument(
+ "--boxes-flow",
+ "-F",
+ type=float_or_disabled,
+ default=la_params.boxes_flow,
+ help="Specifies how much a horizontal and vertical position of a "
+ "text matters when determining the order of lines. The value "
+ "should be within the range of -1.0 (only horizontal position "
+ "matters) to +1.0 (only vertical position matters). You can also "
+ "pass `disabled` to disable advanced layout analysis, and "
+ "instead return text based on the position of the bottom left "
+ "corner of the text box.",
+ )
+ la_param_group.add_argument(
+ "--all-texts",
+ "-A",
+ default=la_params.all_texts,
+ action="store_true",
+ help="If layout analysis should be performed on text in figures.",
+ )
+
+ output_params = parser.add_argument_group(
+ "Output",
+ description="Used during output generation.",
+ )
+ output_params.add_argument(
+ "--outfile",
+ "-o",
+ type=str,
+ default="-",
+ help="Path to file where output is written. "
+ 'Or "-" (default) to write to stdout.',
+ )
+ output_params.add_argument(
+ "--output_type",
+ "-t",
+ type=str,
+ default="text",
+ help="Type of output to generate {text,html,xml,tag}.",
+ )
+ output_params.add_argument(
+ "--codec",
+ "-c",
+ type=str,
+ default="utf-8",
+ help="Text encoding to use in output file.",
+ )
+ output_params.add_argument(
+ "--output-dir",
+ "-O",
+ default=None,
+ help="The output directory to put extracted images in. If not given, "
+ "images are not extracted.",
+ )
+ output_params.add_argument(
+ "--layoutmode",
+ "-Y",
+ default="normal",
+ type=str,
+ help="Type of layout to use when generating html "
+ "{normal,exact,loose}. If normal,each line is"
+ " positioned separately in the html. If exact"
+ ", each character is positioned separately in"
+ " the html. If loose, same result as normal "
+ "but with an additional newline after each "
+ "text line. Only used when output_type is html.",
+ )
+ output_params.add_argument(
+ "--scale",
+ "-s",
+ type=float,
+ default=1.0,
+ help="The amount of zoom to use when generating html file. "
+ "Only used when output_type is html.",
+ )
+ output_params.add_argument(
+ "--strip-control",
+ "-S",
+ default=False,
+ action="store_true",
+ help="Remove control statement from text. Only used when output_type is xml.",
+ )
+
+ return parser
+
+
+def parse_args(args: list[str] | None) -> argparse.Namespace:
+ parsed_args = create_parser().parse_args(args=args)
+
+ # Propagate parsed layout parameters to LAParams object
+ if parsed_args.no_laparams:
+ parsed_args.laparams = None
+ else:
+ parsed_args.laparams = LAParams(
+ line_overlap=parsed_args.line_overlap,
+ char_margin=parsed_args.char_margin,
+ line_margin=parsed_args.line_margin,
+ word_margin=parsed_args.word_margin,
+ boxes_flow=parsed_args.boxes_flow,
+ detect_vertical=parsed_args.detect_vertical,
+ all_texts=parsed_args.all_texts,
+ )
+
+ if parsed_args.page_numbers:
+ parsed_args.page_numbers = {x - 1 for x in parsed_args.page_numbers}
+
+ if parsed_args.pagenos:
+ parsed_args.page_numbers = {int(x) - 1 for x in parsed_args.pagenos.split(",")}
+
+ if parsed_args.output_type == "text" and parsed_args.outfile != "-":
+ for override, alttype in OUTPUT_TYPES:
+ if parsed_args.outfile.endswith(override):
+ parsed_args.output_type = alttype
+
+ return parsed_args
+
+
+def main(args: list[str] | None = None) -> int:
+ parsed_args = parse_args(args)
+ extract_text(**vars(parsed_args))
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/.venv312/Scripts/pip.exe b/.venv312/Scripts/pip.exe
new file mode 100644
index 0000000000..68f707af83
Binary files /dev/null and b/.venv312/Scripts/pip.exe differ
diff --git a/.venv312/Scripts/pip3.12.exe b/.venv312/Scripts/pip3.12.exe
new file mode 100644
index 0000000000..68f707af83
Binary files /dev/null and b/.venv312/Scripts/pip3.12.exe differ
diff --git a/.venv312/Scripts/pip3.exe b/.venv312/Scripts/pip3.exe
new file mode 100644
index 0000000000..68f707af83
Binary files /dev/null and b/.venv312/Scripts/pip3.exe differ
diff --git a/.venv312/Scripts/pkginfo2.exe b/.venv312/Scripts/pkginfo2.exe
new file mode 100644
index 0000000000..35ea0e0f3d
Binary files /dev/null and b/.venv312/Scripts/pkginfo2.exe differ
diff --git a/.venv312/Scripts/pygmentize.exe b/.venv312/Scripts/pygmentize.exe
new file mode 100644
index 0000000000..68a2ac6138
Binary files /dev/null and b/.venv312/Scripts/pygmentize.exe differ
diff --git a/.venv312/Scripts/pyinstrument.exe b/.venv312/Scripts/pyinstrument.exe
new file mode 100644
index 0000000000..9892a21349
Binary files /dev/null and b/.venv312/Scripts/pyinstrument.exe differ
diff --git a/.venv312/Scripts/pysemver.exe b/.venv312/Scripts/pysemver.exe
new file mode 100644
index 0000000000..aac2251dcc
Binary files /dev/null and b/.venv312/Scripts/pysemver.exe differ
diff --git a/.venv312/Scripts/pyspdxtools.exe b/.venv312/Scripts/pyspdxtools.exe
new file mode 100644
index 0000000000..e8c4886dba
Binary files /dev/null and b/.venv312/Scripts/pyspdxtools.exe differ
diff --git a/.venv312/Scripts/pyspdxtools3.exe b/.venv312/Scripts/pyspdxtools3.exe
new file mode 100644
index 0000000000..923f4a2991
Binary files /dev/null and b/.venv312/Scripts/pyspdxtools3.exe differ
diff --git a/.venv312/Scripts/python-inspector.exe b/.venv312/Scripts/python-inspector.exe
new file mode 100644
index 0000000000..bc86b3f3a9
Binary files /dev/null and b/.venv312/Scripts/python-inspector.exe differ
diff --git a/.venv312/Scripts/python.exe b/.venv312/Scripts/python.exe
new file mode 100644
index 0000000000..ba0cd04091
Binary files /dev/null and b/.venv312/Scripts/python.exe differ
diff --git a/.venv312/Scripts/pythonw.exe b/.venv312/Scripts/pythonw.exe
new file mode 100644
index 0000000000..68b3cfe988
Binary files /dev/null and b/.venv312/Scripts/pythonw.exe differ
diff --git a/.venv312/Scripts/rdf2dot.exe b/.venv312/Scripts/rdf2dot.exe
new file mode 100644
index 0000000000..5c3d428544
Binary files /dev/null and b/.venv312/Scripts/rdf2dot.exe differ
diff --git a/.venv312/Scripts/rdfgraphisomorphism.exe b/.venv312/Scripts/rdfgraphisomorphism.exe
new file mode 100644
index 0000000000..7abbff76a1
Binary files /dev/null and b/.venv312/Scripts/rdfgraphisomorphism.exe differ
diff --git a/.venv312/Scripts/rdfpipe.exe b/.venv312/Scripts/rdfpipe.exe
new file mode 100644
index 0000000000..7694572985
Binary files /dev/null and b/.venv312/Scripts/rdfpipe.exe differ
diff --git a/.venv312/Scripts/rdfs2dot.exe b/.venv312/Scripts/rdfs2dot.exe
new file mode 100644
index 0000000000..924608fa9e
Binary files /dev/null and b/.venv312/Scripts/rdfs2dot.exe differ
diff --git a/.venv312/Scripts/readelf.py b/.venv312/Scripts/readelf.py
new file mode 100644
index 0000000000..a5edaca08e
--- /dev/null
+++ b/.venv312/Scripts/readelf.py
@@ -0,0 +1,1996 @@
+#!E:\hell\scancode.io\.venv312\Scripts\python.exe
+#-------------------------------------------------------------------------------
+# scripts/readelf.py
+#
+# A clone of 'readelf' in Python, based on the pyelftools library
+#
+# Eli Bendersky (eliben@gmail.com)
+# This code is in the public domain
+#-------------------------------------------------------------------------------
+import argparse
+import os, sys
+import re
+import string
+import traceback
+import itertools
+# Note: zip has different behaviour between Python 2.x and 3.x.
+# - Using izip ensures compatibility.
+try:
+ from itertools import izip
+except:
+ izip = zip
+
+# For running from development directory. It should take precedence over the
+# installed pyelftools.
+sys.path.insert(0, '.')
+
+
+from elftools import __version__
+from elftools.common.exceptions import ELFError
+from elftools.common.utils import bytes2str, iterbytes
+from elftools.elf.elffile import ELFFile
+from elftools.elf.dynamic import DynamicSection, DynamicSegment
+from elftools.elf.enums import ENUM_D_TAG
+from elftools.elf.segments import InterpSegment
+from elftools.elf.sections import (
+ NoteSection, SymbolTableSection, SymbolTableIndexSection
+)
+from elftools.elf.gnuversions import (
+ GNUVerSymSection, GNUVerDefSection,
+ GNUVerNeedSection,
+ )
+from elftools.elf.relocation import RelocationSection
+from elftools.elf.descriptions import (
+ describe_ei_class, describe_ei_data, describe_ei_version,
+ describe_ei_osabi, describe_e_type, describe_e_machine,
+ describe_e_version_numeric, describe_p_type, describe_p_flags,
+ describe_rh_flags, describe_sh_type, describe_sh_flags,
+ describe_symbol_type, describe_symbol_bind, describe_symbol_visibility,
+ describe_symbol_shndx, describe_reloc_type, describe_dyn_tag,
+ describe_dt_flags, describe_dt_flags_1, describe_ver_flags, describe_note,
+ describe_attr_tag_arm, describe_attr_tag_riscv, describe_symbol_other
+ )
+from elftools.elf.constants import E_FLAGS
+from elftools.elf.constants import E_FLAGS_MASKS
+from elftools.elf.constants import SH_FLAGS
+from elftools.elf.constants import SHN_INDICES
+from elftools.dwarf.dwarfinfo import DWARFInfo
+from elftools.dwarf.descriptions import (
+ describe_reg_name, describe_attr_value, set_global_machine_arch,
+ describe_CFI_instructions, describe_CFI_register_rule,
+ describe_CFI_CFA_rule, describe_DWARF_expr
+ )
+from elftools.dwarf.constants import (
+ DW_LNS_copy, DW_LNS_set_file, DW_LNE_define_file)
+from elftools.dwarf.locationlists import LocationParser, LocationEntry, LocationViewPair, BaseAddressEntry as LocBaseAddressEntry, LocationListsPair
+from elftools.dwarf.ranges import RangeEntry, BaseAddressEntry as RangeBaseAddressEntry, RangeListsPair
+from elftools.dwarf.callframe import CIE, FDE, ZERO
+from elftools.ehabi.ehabiinfo import CorruptEHABIEntry, CannotUnwindEHABIEntry, GenericEHABIEntry
+from elftools.dwarf.enums import ENUM_DW_UT
+
+def _get_cu_base(cu):
+ top_die = cu.get_top_DIE()
+ attr = top_die.attributes
+ if 'DW_AT_low_pc' in attr:
+ return attr['DW_AT_low_pc'].value
+ elif 'DW_AT_entry_pc' in attr:
+ return attr['DW_AT_entry_pc'].value
+ elif 'DW_AT_ranges' in attr:
+ # Rare case but happens: rangelist in the top DIE.
+ # If there is a base or at least one absolute entry,
+ # this will give us the base IP for the CU.
+ rl = cu.dwarfinfo.range_lists().get_range_list_at_offset(attr['DW_AT_ranges'].value, cu)
+ base_ip = None
+ for r in rl:
+ if isinstance(r, RangeBaseAddressEntry):
+ ip = r.base_address
+ elif isinstance(r, RangeEntry) and r.is_absolute:
+ ip = r.begin_offset
+ else:
+ ip = None
+ if ip is not None and (base_ip is None or ip < base_ip):
+ base_ip = ip
+ if base_ip is None:
+ raise ValueError("Can't find the base IP (low_pc) for a CU")
+ return base_ip
+ else:
+ raise ValueError("Can't find the base IP (low_pc) for a CU")
+
+# Matcher for all control characters, for transforming them into "^X" form when
+# formatting symbol names for display.
+_CONTROL_CHAR_RE = re.compile(r'[\x01-\x1f]')
+
+def _format_symbol_name(s):
+ return _CONTROL_CHAR_RE.sub(lambda match: '^' + chr(0x40 + ord(match[0])), s)
+
+class ReadElf(object):
+ """ display_* methods are used to emit output into the output stream
+ """
+ def __init__(self, file, output):
+ """ file:
+ stream object with the ELF file to read
+
+ output:
+ output stream to write to
+ """
+ self.elffile = ELFFile(file)
+ self.output = output
+
+ # Lazily initialized if a debug dump is requested
+ self._dwarfinfo = None
+
+ self._versioninfo = None
+
+ self._shndx_sections = None
+
+ def display_file_header(self):
+ """ Display the ELF file header
+ """
+ self._emitline('ELF Header:')
+ self._emit(' Magic: ')
+ self._emit(' '.join('%2.2x' % b
+ for b in self.elffile.e_ident_raw))
+ self._emitline(' ')
+ header = self.elffile.header
+ e_ident = header['e_ident']
+ self._emitline(' Class: %s' %
+ describe_ei_class(e_ident['EI_CLASS']))
+ self._emitline(' Data: %s' %
+ describe_ei_data(e_ident['EI_DATA']))
+ self._emitline(' Version: %s' %
+ describe_ei_version(e_ident['EI_VERSION']))
+ self._emitline(' OS/ABI: %s' %
+ describe_ei_osabi(e_ident['EI_OSABI']))
+ self._emitline(' ABI Version: %d' %
+ e_ident['EI_ABIVERSION'])
+ self._emitline(' Type: %s' %
+ describe_e_type(header['e_type'], self.elffile))
+ self._emitline(' Machine: %s' %
+ describe_e_machine(header['e_machine']))
+ self._emitline(' Version: %s' %
+ describe_e_version_numeric(header['e_version']))
+ self._emitline(' Entry point address: %s' %
+ self._format_hex(header['e_entry']))
+ self._emit(' Start of program headers: %s' %
+ header['e_phoff'])
+ self._emitline(' (bytes into file)')
+ self._emit(' Start of section headers: %s' %
+ header['e_shoff'])
+ self._emitline(' (bytes into file)')
+ self._emitline(' Flags: %s%s' %
+ (self._format_hex(header['e_flags']),
+ self.decode_flags(header['e_flags'])))
+ self._emitline(' Size of this header: %s (bytes)' %
+ header['e_ehsize'])
+ self._emitline(' Size of program headers: %s (bytes)' %
+ header['e_phentsize'])
+ self._emitline(' Number of program headers: %s' %
+ header['e_phnum'])
+ self._emitline(' Size of section headers: %s (bytes)' %
+ header['e_shentsize'])
+ self._emit(' Number of section headers: %s' %
+ header['e_shnum'])
+ if header['e_shnum'] == 0 and self.elffile.num_sections() != 0:
+ self._emitline(' (%d)' % self.elffile.num_sections())
+ else:
+ self._emitline('')
+ self._emit(' Section header string table index: %s' %
+ header['e_shstrndx'])
+ if header['e_shstrndx'] == SHN_INDICES.SHN_XINDEX:
+ self._emitline(' (%d)' % self.elffile.get_shstrndx())
+ else:
+ self._emitline('')
+
+ def decode_flags(self, flags):
+ description = ""
+ if self.elffile['e_machine'] == "EM_ARM":
+ eabi = flags & E_FLAGS.EF_ARM_EABIMASK
+ flags &= ~E_FLAGS.EF_ARM_EABIMASK
+
+ if flags & E_FLAGS.EF_ARM_RELEXEC:
+ description += ', relocatable executabl'
+ flags &= ~E_FLAGS.EF_ARM_RELEXEC
+
+ if eabi == E_FLAGS.EF_ARM_EABI_VER5:
+ EF_ARM_KNOWN_FLAGS = E_FLAGS.EF_ARM_ABI_FLOAT_SOFT|E_FLAGS.EF_ARM_ABI_FLOAT_HARD|E_FLAGS.EF_ARM_LE8|E_FLAGS.EF_ARM_BE8
+ description += ', Version5 EABI'
+ if flags & E_FLAGS.EF_ARM_ABI_FLOAT_SOFT:
+ description += ", soft-float ABI"
+ elif flags & E_FLAGS.EF_ARM_ABI_FLOAT_HARD:
+ description += ", hard-float ABI"
+
+ if flags & E_FLAGS.EF_ARM_BE8:
+ description += ", BE8"
+ elif flags & E_FLAGS.EF_ARM_LE8:
+ description += ", LE8"
+
+ if flags & ~EF_ARM_KNOWN_FLAGS:
+ description += ', '
+ else:
+ description += ', '
+
+ elif self.elffile['e_machine'] == 'EM_PPC64':
+ if flags & E_FLAGS.EF_PPC64_ABI_V2:
+ description += ', abiv2'
+
+ elif self.elffile['e_machine'] == "EM_MIPS":
+ if flags & E_FLAGS.EF_MIPS_NOREORDER:
+ description += ", noreorder"
+ if flags & E_FLAGS.EF_MIPS_PIC:
+ description += ", pic"
+ if flags & E_FLAGS.EF_MIPS_CPIC:
+ description += ", cpic"
+ if (flags & E_FLAGS.EF_MIPS_ABI2):
+ description += ", abi2"
+ if (flags & E_FLAGS.EF_MIPS_32BITMODE):
+ description += ", 32bitmode"
+ if (flags & E_FLAGS_MASKS.EFM_MIPS_ABI_O32):
+ description += ", o32"
+ elif (flags & E_FLAGS_MASKS.EFM_MIPS_ABI_O64):
+ description += ", o64"
+ elif (flags & E_FLAGS_MASKS.EFM_MIPS_ABI_EABI32):
+ description += ", eabi32"
+ elif (flags & E_FLAGS_MASKS.EFM_MIPS_ABI_EABI64):
+ description += ", eabi64"
+ if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_1:
+ description += ", mips1"
+ if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_2:
+ description += ", mips2"
+ if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_3:
+ description += ", mips3"
+ if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_4:
+ description += ", mips4"
+ if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_5:
+ description += ", mips5"
+ if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_32R2:
+ description += ", mips32r2"
+ if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_64R2:
+ description += ", mips64r2"
+ if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_32:
+ description += ", mips32"
+ if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_64:
+ description += ", mips64"
+
+ elif self.elffile['e_machine'] == "EM_RISCV":
+ if flags & E_FLAGS.EF_RISCV_RVC:
+ description += ", RVC"
+ if (flags & E_FLAGS.EF_RISCV_RVE):
+ description += ", RVE"
+ if (flags & E_FLAGS.EF_RISCV_TSO):
+ description += ", TSO"
+ if (flags & E_FLAGS.EF_RISCV_FLOAT_ABI) == E_FLAGS.EF_RISCV_FLOAT_ABI_SOFT:
+ description += ", soft-float ABI"
+ if (flags & E_FLAGS.EF_RISCV_FLOAT_ABI) == E_FLAGS.EF_RISCV_FLOAT_ABI_SINGLE:
+ description += ", single-float ABI"
+ if (flags & E_FLAGS.EF_RISCV_FLOAT_ABI) == E_FLAGS.EF_RISCV_FLOAT_ABI_DOUBLE:
+ description += ", double-float ABI"
+ if (flags & E_FLAGS.EF_RISCV_FLOAT_ABI) == E_FLAGS.EF_RISCV_FLOAT_ABI_QUAD:
+ description += ", quad-float ABI"
+
+ elif self.elffile['e_machine'] == "EM_LOONGARCH":
+ if (flags & E_FLAGS.EF_LOONGARCH_ABI_MODIFIER_MASK) == E_FLAGS.EF_LOONGARCH_ABI_SOFT_FLOAT:
+ description += ", SOFT-FLOAT"
+ if (flags & E_FLAGS.EF_LOONGARCH_ABI_MODIFIER_MASK) == E_FLAGS.EF_LOONGARCH_ABI_SINGLE_FLOAT:
+ description += ", SINGLE-FLOAT"
+ if (flags & E_FLAGS.EF_LOONGARCH_ABI_MODIFIER_MASK) == E_FLAGS.EF_LOONGARCH_ABI_DOUBLE_FLOAT:
+ description += ", DOUBLE-FLOAT"
+ if (flags & E_FLAGS.EF_LOONGARCH_OBJABI_MASK) == E_FLAGS.EF_LOONGARCH_OBJABI_V0:
+ description += ", OBJ-v0"
+ if (flags & E_FLAGS.EF_LOONGARCH_OBJABI_MASK) == E_FLAGS.EF_LOONGARCH_OBJABI_V1:
+ description += ", OBJ-v1"
+
+ return description
+
+ def display_program_headers(self, show_heading=True):
+ """ Display the ELF program headers.
+ If show_heading is True, displays the heading for this information
+ (Elf file type is...)
+ """
+ self._emitline()
+ if self.elffile.num_segments() == 0:
+ self._emitline('There are no program headers in this file.')
+ return
+
+ elfheader = self.elffile.header
+ if show_heading:
+ self._emitline('Elf file type is %s' %
+ describe_e_type(elfheader['e_type'], self.elffile))
+ self._emitline('Entry point is %s' %
+ self._format_hex(elfheader['e_entry']))
+ # readelf weirness - why isn't e_phoff printed as hex? (for section
+ # headers, it is...)
+ self._emitline('There are %s program headers, starting at offset %s' % (
+ self.elffile.num_segments(), elfheader['e_phoff']))
+ self._emitline()
+
+ self._emitline('Program Headers:')
+
+ # Now comes the table of program headers with their attributes. Note
+ # that due to different formatting constraints of 32-bit and 64-bit
+ # addresses, there are some conditions on elfclass here.
+ #
+ # First comes the table heading
+ #
+ if self.elffile.elfclass == 32:
+ self._emitline(' Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align')
+ else:
+ self._emitline(' Type Offset VirtAddr PhysAddr')
+ self._emitline(' FileSiz MemSiz Flags Align')
+
+ # Now the entries
+ #
+ for segment in self.elffile.iter_segments():
+ self._emit(' %-14s ' % describe_p_type(segment['p_type']))
+
+ if self.elffile.elfclass == 32:
+ self._emitline('%s %s %s %s %s %-3s %s' % (
+ self._format_hex(segment['p_offset'], fieldsize=6),
+ self._format_hex(segment['p_vaddr'], fullhex=True),
+ self._format_hex(segment['p_paddr'], fullhex=True),
+ self._format_hex(segment['p_filesz'], fieldsize=5),
+ self._format_hex(segment['p_memsz'], fieldsize=5),
+ describe_p_flags(segment['p_flags']),
+ self._format_hex(segment['p_align'])))
+ else: # 64
+ self._emitline('%s %s %s' % (
+ self._format_hex(segment['p_offset'], fullhex=True),
+ self._format_hex(segment['p_vaddr'], fullhex=True),
+ self._format_hex(segment['p_paddr'], fullhex=True)))
+ self._emitline(' %s %s %-3s %s' % (
+ self._format_hex(segment['p_filesz'], fullhex=True),
+ self._format_hex(segment['p_memsz'], fullhex=True),
+ describe_p_flags(segment['p_flags']),
+ # lead0x set to False for p_align, to mimic readelf.
+ # No idea why the difference from 32-bit mode :-|
+ self._format_hex(segment['p_align'], lead0x=False)))
+
+ if isinstance(segment, InterpSegment):
+ self._emitline(' [Requesting program interpreter: %s]' %
+ segment.get_interp_name())
+
+ # Sections to segments mapping
+ #
+ if self.elffile.num_sections() == 0:
+ # No sections? We're done
+ return
+
+ self._emitline('\n Section to Segment mapping:')
+ self._emitline(' Segment Sections...')
+
+ for nseg, segment in enumerate(self.elffile.iter_segments()):
+ self._emit(' %2.2d ' % nseg)
+
+ for section in self.elffile.iter_sections():
+ if ( not section.is_null() and
+ not ((section['sh_flags'] & SH_FLAGS.SHF_TLS) != 0 and
+ section['sh_type'] == 'SHT_NOBITS' and
+ segment['p_type'] != 'PT_TLS') and
+ segment.section_in_segment(section)):
+ self._emit('%s ' % section.name)
+
+ self._emitline('')
+
+ def display_section_headers(self, show_heading=True):
+ """ Display the ELF section headers
+ """
+ elfheader = self.elffile.header
+ if show_heading:
+ self._emitline('There are %s section headers, starting at offset %s' % (
+ elfheader['e_shnum'], self._format_hex(elfheader['e_shoff'])))
+
+ if self.elffile.num_sections() == 0:
+ self._emitline('There are no sections in this file.')
+ return
+
+ self._emitline('\nSection Header%s:' % (
+ 's' if self.elffile.num_sections() > 1 else ''))
+
+ # Different formatting constraints of 32-bit and 64-bit addresses
+ #
+ if self.elffile.elfclass == 32:
+ self._emitline(' [Nr] Name Type Addr Off Size ES Flg Lk Inf Al')
+ else:
+ self._emitline(' [Nr] Name Type Address Offset')
+ self._emitline(' Size EntSize Flags Link Info Align')
+
+ # Now the entries
+ #
+ for nsec, section in enumerate(self.elffile.iter_sections()):
+ self._emit(' [%2u] %-17.17s %-15.15s ' % (
+ nsec, section.name, describe_sh_type(section['sh_type'])))
+
+ if self.elffile.elfclass == 32:
+ self._emitline('%s %s %s %s %3s %2s %3s %2s' % (
+ self._format_hex(section['sh_addr'], fieldsize=8, lead0x=False),
+ self._format_hex(section['sh_offset'], fieldsize=6, lead0x=False),
+ self._format_hex(section['sh_size'], fieldsize=6, lead0x=False),
+ self._format_hex(section['sh_entsize'], fieldsize=2, lead0x=False),
+ describe_sh_flags(section['sh_flags']),
+ section['sh_link'], section['sh_info'],
+ section['sh_addralign']))
+ else: # 64
+ self._emitline(' %s %s' % (
+ self._format_hex(section['sh_addr'], fullhex=True, lead0x=False),
+ self._format_hex(section['sh_offset'],
+ fieldsize=16 if section['sh_offset'] > 0xffffffff else 8,
+ lead0x=False)))
+ self._emitline(' %s %s %3s %2s %3s %s' % (
+ self._format_hex(section['sh_size'], fullhex=True, lead0x=False),
+ self._format_hex(section['sh_entsize'], fullhex=True, lead0x=False),
+ describe_sh_flags(section['sh_flags']),
+ section['sh_link'], section['sh_info'],
+ section['sh_addralign']))
+
+ self._emitline('Key to Flags:')
+ self._emitline(' W (write), A (alloc), X (execute), M (merge),'
+ ' S (strings), I (info),')
+ self._emitline(' L (link order), O (extra OS processing required),'
+ ' G (group), T (TLS),')
+ self._emitline(' C (compressed), x (unknown), o (OS specific),'
+ ' E (exclude),')
+ self._emit(' ')
+ if self.elffile['e_machine'] == 'EM_ARM':
+ self._emit('y (purecode), ')
+ self._emitline('p (processor specific)')
+
+ def display_symbol_tables(self):
+ """ Display the symbol tables contained in the file
+ """
+ self._init_versioninfo()
+
+ symbol_tables = [(idx, s) for idx, s in enumerate(self.elffile.iter_sections())
+ if isinstance(s, SymbolTableSection)]
+
+ if not symbol_tables and self.elffile.num_sections() == 0:
+ self._emitline('')
+ self._emitline('Dynamic symbol information is not available for'
+ ' displaying symbols.')
+
+ for section_index, section in symbol_tables:
+ if not isinstance(section, SymbolTableSection):
+ continue
+
+ if section['sh_entsize'] == 0:
+ self._emitline("\nSymbol table '%s' has a sh_entsize of zero!" % (
+ section.name))
+ continue
+
+ self._emitline("\nSymbol table '%s' contains %d %s:" % (
+ section.name,
+ section.num_symbols(),
+ 'entry' if section.num_symbols() == 1 else 'entries'))
+
+ if self.elffile.elfclass == 32:
+ self._emitline(' Num: Value Size Type Bind Vis Ndx Name')
+ else: # 64
+ self._emitline(' Num: Value Size Type Bind Vis Ndx Name')
+
+ for nsym, symbol in enumerate(section.iter_symbols()):
+ version_info = ''
+ # readelf doesn't display version info for Solaris versioning
+ if (section['sh_type'] == 'SHT_DYNSYM' and
+ self._versioninfo['type'] == 'GNU'):
+ version = self._symbol_version(nsym)
+ if (version['name'] != symbol.name and
+ version['index'] not in ('VER_NDX_LOCAL',
+ 'VER_NDX_GLOBAL')):
+ if version['filename']:
+ # external symbol
+ version_info = '@%(name)s (%(index)i)' % version
+ else:
+ # internal symbol
+ if version['hidden']:
+ version_info = '@%(name)s' % version
+ else:
+ version_info = '@@%(name)s' % version
+
+ symbol_name = symbol.name
+ # Print section names for STT_SECTION symbols as readelf does
+ if (symbol['st_info']['type'] == 'STT_SECTION'
+ and symbol['st_shndx'] != 'SHN_UNDEF'
+ and symbol['st_shndx'] < self.elffile.num_sections()
+ and symbol['st_name'] == 0):
+ symbol_name = self.elffile.get_section(symbol['st_shndx']).name
+
+ # symbol names are truncated to 25 chars, similarly to readelf
+ self._emitline('%6d: %s %s %-7s %-6s %-7s %4s %.25s%s' % (
+ nsym,
+ self._format_hex(
+ symbol['st_value'], fullhex=True, lead0x=False),
+ "%5d" % symbol['st_size'] if symbol['st_size'] < 100000 else hex(symbol['st_size']),
+ describe_symbol_type(symbol['st_info']['type']),
+ describe_symbol_bind(symbol['st_info']['bind']),
+ describe_symbol_other(symbol['st_other']),
+ describe_symbol_shndx(self._get_symbol_shndx(symbol,
+ nsym,
+ section_index)),
+ _format_symbol_name(symbol_name),
+ version_info))
+
+ def display_dynamic_tags(self):
+ """ Display the dynamic tags contained in the file
+ """
+ has_dynamic_sections = False
+ for section in self.elffile.iter_sections():
+ if not isinstance(section, DynamicSection):
+ continue
+
+ has_dynamic_sections = True
+ self._emitline("\nDynamic section at offset %s contains %d %s:" % (
+ self._format_hex(section['sh_offset']),
+ section.num_tags(),
+ 'entry' if section.num_tags() == 1 else 'entries'))
+ self._emitline(" Tag Type Name/Value")
+
+ padding = 20 + (8 if self.elffile.elfclass == 32 else 0)
+ for tag in section.iter_tags():
+ if tag.entry.d_tag == 'DT_NEEDED':
+ parsed = 'Shared library: [%s]' % tag.needed
+ elif tag.entry.d_tag == 'DT_RPATH':
+ parsed = 'Library rpath: [%s]' % tag.rpath
+ elif tag.entry.d_tag == 'DT_RUNPATH':
+ parsed = 'Library runpath: [%s]' % tag.runpath
+ elif tag.entry.d_tag == 'DT_SONAME':
+ parsed = 'Library soname: [%s]' % tag.soname
+ elif tag.entry.d_tag.endswith(('SZ', 'ENT')):
+ parsed = '%i (bytes)' % tag['d_val']
+ elif tag.entry.d_tag == 'DT_FLAGS':
+ parsed = describe_dt_flags(tag.entry.d_val)
+ elif tag.entry.d_tag == 'DT_FLAGS_1':
+ parsed = 'Flags: %s' % describe_dt_flags_1(tag.entry.d_val)
+ elif tag.entry.d_tag.endswith(('NUM', 'COUNT')):
+ parsed = '%i' % tag['d_val']
+ elif tag.entry.d_tag == 'DT_PLTREL':
+ s = describe_dyn_tag(tag.entry.d_val)
+ if s.startswith('DT_'):
+ s = s[3:]
+ parsed = '%s' % s
+ elif tag.entry.d_tag == 'DT_MIPS_FLAGS':
+ parsed = describe_rh_flags(tag.entry.d_val)
+ elif tag.entry.d_tag in ('DT_MIPS_SYMTABNO',
+ 'DT_MIPS_LOCAL_GOTNO'):
+ parsed = str(tag.entry.d_val)
+ elif tag.entry.d_tag == 'DT_AARCH64_BTI_PLT':
+ parsed = ''
+ else:
+ parsed = '%#x' % tag['d_val']
+
+ self._emitline(" %s %-*s %s" % (
+ self._format_hex(ENUM_D_TAG.get(tag.entry.d_tag, tag.entry.d_tag),
+ fullhex=True, lead0x=True),
+ padding,
+ '(%s)' % (tag.entry.d_tag[3:],),
+ parsed))
+ if not has_dynamic_sections:
+ self._emitline("\nThere is no dynamic section in this file.")
+
+ def display_notes(self):
+ """ Display the notes contained in the file
+ """
+ for section in self.elffile.iter_sections():
+ if isinstance(section, NoteSection):
+ for note in section.iter_notes():
+ self._emitline("\nDisplaying notes found in: {}".format(
+ section.name))
+ self._emitline(' Owner Data size Description')
+ self._emitline(' %s %s\t%s' % (
+ note['n_name'].ljust(20),
+ self._format_hex(note['n_descsz'], fieldsize=8),
+ describe_note(note, self.elffile.header.e_machine)))
+
+ def display_relocations(self):
+ """ Display the relocations contained in the file
+ """
+ has_relocation_sections = False
+ for section in self.elffile.iter_sections():
+ if not isinstance(section, RelocationSection):
+ continue
+
+ has_relocation_sections = True
+ self._emitline("\nRelocation section '%.128s' at offset %s contains %d %s:" % (
+ section.name,
+ self._format_hex(section['sh_offset']),
+ section.num_relocations(),
+ 'entry' if section.num_relocations() == 1 else 'entries'))
+ if section.is_RELA():
+ self._emitline(" Offset Info Type Sym. Value Sym. Name + Addend")
+ else:
+ self._emitline(" Offset Info Type Sym.Value Sym. Name")
+
+ # The symbol table section pointed to in sh_link
+ symtable = self.elffile.get_section(section['sh_link'])
+
+ for rel in section.iter_relocations():
+ hexwidth = 8 if self.elffile.elfclass == 32 else 12
+ self._emit('%s %s %-17.17s' % (
+ self._format_hex(rel['r_offset'],
+ fieldsize=hexwidth, lead0x=False),
+ self._format_hex(rel['r_info'],
+ fieldsize=hexwidth, lead0x=False),
+ describe_reloc_type(
+ rel['r_info_type'], self.elffile)))
+
+ if rel['r_info_sym'] == 0:
+ if section.is_RELA():
+ fieldsize = 8 if self.elffile.elfclass == 32 else 16
+ addend = self._format_hex(rel['r_addend'], lead0x=False)
+ self._emit(' %s %s' % (' ' * fieldsize, addend))
+ self._emitline()
+
+ else:
+ symbol = symtable.get_symbol(rel['r_info_sym'])
+ # Some symbols have zero 'st_name', so instead what's used
+ # is the name of the section they point at. Truncate symbol
+ # names (excluding version info) to 22 chars, similarly to
+ # readelf.
+ if symbol['st_name'] == 0:
+ symsecidx = self._get_symbol_shndx(symbol,
+ rel['r_info_sym'],
+ section['sh_link'])
+ symsec = self.elffile.get_section(symsecidx)
+ symbol_name = symsec.name
+ version = ''
+ else:
+ symbol_name = symbol.name
+ version = self._symbol_version(rel['r_info_sym'])
+ version = (version['name']
+ if version and version['name'] else '')
+ symbol_name = '%.22s' % symbol_name
+ if version:
+ symbol_name += '@' + version
+
+ self._emit(' %s %s' % (
+ self._format_hex(
+ symbol['st_value'],
+ fullhex=True, lead0x=False),
+ _format_symbol_name(symbol_name)))
+ if section.is_RELA():
+ self._emit(' %s %x' % (
+ '+' if rel['r_addend'] >= 0 else '-',
+ abs(rel['r_addend'])))
+ self._emitline()
+
+ # Emit the two additional relocation types for ELF64 MIPS
+ # binaries.
+ if (self.elffile.elfclass == 64 and
+ self.elffile['e_machine'] == 'EM_MIPS'):
+ for i in (2, 3):
+ rtype = rel['r_info_type%s' % i]
+ self._emit(' Type%s: %s' % (
+ i,
+ describe_reloc_type(rtype, self.elffile)))
+ self._emitline()
+
+ if not has_relocation_sections:
+ self._emitline('\nThere are no relocations in this file.')
+
+ def display_arm_unwind(self):
+ if not self.elffile.has_ehabi_info():
+ self._emitline('There are no .ARM.idx sections in this file.')
+ return
+ for ehabi_info in self.elffile.get_ehabi_infos():
+ # Unwind section '.ARM.exidx' at offset 0x203e8 contains 1009 entries:
+ self._emitline("\nUnwind section '%s' at offset 0x%x contains %d %s" % (
+ ehabi_info.section_name(),
+ ehabi_info.section_offset(),
+ ehabi_info.num_entry(),
+ 'entry' if ehabi_info.num_entry() == 1 else 'entries'))
+
+ for i in range(ehabi_info.num_entry()):
+ entry = ehabi_info.get_entry(i)
+ self._emitline()
+ self._emitline("Entry %d:" % i)
+ if isinstance(entry, CorruptEHABIEntry):
+ self._emitline(" [corrupt] %s" % entry.reason)
+ continue
+ self._emit(" Function offset 0x%x: " % entry.function_offset)
+ if isinstance(entry, CannotUnwindEHABIEntry):
+ self._emitline("[cantunwind]")
+ continue
+ elif entry.eh_table_offset:
+ self._emitline("@0x%x" % entry.eh_table_offset)
+ else:
+ self._emitline("Compact (inline)")
+ if isinstance(entry, GenericEHABIEntry):
+ self._emitline(" Personality: 0x%x" % entry.personality)
+ else:
+ self._emitline(" Compact model index: %d" % entry.personality)
+ for mnemonic_item in entry.mnmemonic_array():
+ self._emit(' ')
+ self._emitline(mnemonic_item)
+
+ def display_version_info(self):
+ """ Display the version info contained in the file
+ """
+ self._init_versioninfo()
+
+ if not self._versioninfo['type']:
+ self._emitline("\nNo version information found in this file.")
+ return
+
+ for section in self.elffile.iter_sections():
+ if isinstance(section, GNUVerSymSection):
+ self._print_version_section_header(section, 'Version symbols')
+ num_symbols = section.num_symbols()
+
+ # Symbol version info are printed four by four entries
+ for idx_by_4 in range(0, num_symbols, 4):
+
+ self._emit(' %03x:' % idx_by_4)
+
+ for idx in range(idx_by_4, min(idx_by_4 + 4, num_symbols)):
+
+ symbol_version = self._symbol_version(idx)
+ if symbol_version['index'] == 'VER_NDX_LOCAL':
+ version_index = 0
+ version_name = '(*local*)'
+ elif symbol_version['index'] == 'VER_NDX_GLOBAL':
+ version_index = 1
+ version_name = '(*global*)'
+ else:
+ version_index = symbol_version['index']
+ version_name = '(%(name)s)' % symbol_version
+
+ visibility = 'h' if symbol_version['hidden'] else ' '
+
+ self._emit('%4x%s%-13s' % (
+ version_index, visibility, version_name))
+
+ self._emitline()
+
+ elif isinstance(section, GNUVerDefSection):
+ self._print_version_section_header(
+ section, 'Version definition', indent=2)
+
+ offset = 0
+ for verdef, verdaux_iter in section.iter_versions():
+ verdaux = next(verdaux_iter)
+
+ name = verdaux.name
+ if verdef['vd_flags']:
+ flags = describe_ver_flags(verdef['vd_flags'])
+ # Mimic exactly the readelf output
+ flags += ' '
+ else:
+ flags = 'none'
+
+ self._emitline(' %s: Rev: %i Flags: %s Index: %i'
+ ' Cnt: %i Name: %s' % (
+ self._format_hex(offset, fieldsize=6,
+ alternate=True),
+ verdef['vd_version'], flags, verdef['vd_ndx'],
+ verdef['vd_cnt'], name))
+
+ verdaux_offset = (
+ offset + verdef['vd_aux'] + verdaux['vda_next'])
+ for idx, verdaux in enumerate(verdaux_iter, start=1):
+ self._emitline(' %s: Parent %i: %s' %
+ (self._format_hex(verdaux_offset, fieldsize=4),
+ idx, verdaux.name))
+ verdaux_offset += verdaux['vda_next']
+
+ offset += verdef['vd_next']
+
+ elif isinstance(section, GNUVerNeedSection):
+ self._print_version_section_header(section, 'Version needs')
+
+ offset = 0
+ for verneed, verneed_iter in section.iter_versions():
+
+ self._emitline(' %s: Version: %i File: %s Cnt: %i' % (
+ self._format_hex(offset, fieldsize=6,
+ alternate=True),
+ verneed['vn_version'], verneed.name,
+ verneed['vn_cnt']))
+
+ vernaux_offset = offset + verneed['vn_aux']
+ for idx, vernaux in enumerate(verneed_iter, start=1):
+ if vernaux['vna_flags']:
+ flags = describe_ver_flags(vernaux['vna_flags'])
+ # Mimic exactly the readelf output
+ flags += ' '
+ else:
+ flags = 'none'
+
+ self._emitline(
+ ' %s: Name: %s Flags: %s Version: %i' % (
+ self._format_hex(vernaux_offset, fieldsize=4),
+ vernaux.name, flags,
+ vernaux['vna_other']))
+
+ vernaux_offset += vernaux['vna_next']
+
+ offset += verneed['vn_next']
+
+ def display_arch_specific(self):
+ """ Display the architecture-specific info contained in the file.
+ """
+ if self.elffile['e_machine'] == 'EM_ARM':
+ self._display_arch_specific_arm()
+ elif self.elffile['e_machine'] == 'EM_RISCV':
+ self._display_arch_specific_riscv()
+
+ def display_hex_dump(self, section_spec):
+ """ Display a hex dump of a section. section_spec is either a section
+ number or a name.
+ """
+ section = self._section_from_spec(section_spec)
+ if section is None:
+ # readelf prints the warning to stderr. Even though stderrs are not compared
+ # in tests, we comply with that behavior.
+ sys.stderr.write('readelf: Warning: Section \'%s\' was not dumped because it does not exist!\n' % (
+ section_spec))
+ return
+ if section['sh_type'] == 'SHT_NOBITS':
+ self._emitline("\nSection '%s' has no data to dump." % (
+ section_spec))
+ return
+
+ self._emitline("\nHex dump of section '%s':" % section.name)
+ self._note_relocs_for_section(section)
+ addr = section['sh_addr']
+ data = section.data()
+ dataptr = 0
+
+ while dataptr < len(data):
+ bytesleft = len(data) - dataptr
+ # chunks of 16 bytes per line
+ linebytes = 16 if bytesleft > 16 else bytesleft
+
+ self._emit(' %s ' % self._format_hex(addr, fieldsize=8))
+ for i in range(16):
+ if i < linebytes:
+ self._emit('%2.2x' % data[dataptr + i])
+ else:
+ self._emit(' ')
+ if i % 4 == 3:
+ self._emit(' ')
+
+ for i in range(linebytes):
+ c = data[dataptr + i : dataptr + i + 1]
+ if c[0] >= 32 and c[0] < 0x7f:
+ self._emit(bytes2str(c))
+ else:
+ self._emit(bytes2str(b'.'))
+
+ self._emitline()
+ addr += linebytes
+ dataptr += linebytes
+
+ self._emitline()
+
+ def display_string_dump(self, section_spec):
+ """ Display a strings dump of a section. section_spec is either a
+ section number or a name.
+ """
+ section = self._section_from_spec(section_spec)
+ if section is None:
+ # readelf prints the warning to stderr. Even though stderrs are not compared
+ # in tests, we comply with that behavior.
+ sys.stderr.write('readelf.py: Warning: Section \'%s\' was not dumped because it does not exist!\n' % (
+ section_spec))
+ return
+ if section['sh_type'] == 'SHT_NOBITS':
+ self._emitline("\nSection '%s' has no data to dump." % (
+ section_spec))
+ return
+
+ self._emitline("\nString dump of section '%s':" % section.name)
+
+ found = False
+ data = section.data()
+ dataptr = 0
+
+ while dataptr < len(data):
+ while ( dataptr < len(data) and
+ not (32 <= data[dataptr] <= 127)):
+ dataptr += 1
+
+ if dataptr >= len(data):
+ break
+
+ endptr = dataptr
+ while endptr < len(data) and data[endptr] != 0:
+ endptr += 1
+
+ found = True
+ self._emitline(' [%6x] %s' % (
+ dataptr, bytes2str(data[dataptr:endptr])))
+
+ dataptr = endptr
+
+ if not found:
+ self._emitline(' No strings found in this section.')
+ else:
+ self._emitline()
+
+ def display_debug_dump(self, dump_what):
+ """ Dump a DWARF section
+ """
+ self._init_dwarfinfo()
+ if self._dwarfinfo is None:
+ return
+
+ set_global_machine_arch(self.elffile.get_machine_arch())
+
+ if dump_what == 'info':
+ self._dump_debug_info()
+ self._dump_debug_types()
+ elif dump_what == 'decodedline':
+ self._dump_debug_line_programs()
+ elif dump_what == 'frames':
+ self._dump_debug_frames()
+ elif dump_what == 'frames-interp':
+ self._dump_debug_frames_interp()
+ elif dump_what == 'aranges':
+ self._dump_debug_aranges()
+ elif dump_what in { 'pubtypes', 'pubnames' }:
+ self._dump_debug_namelut(dump_what)
+ elif dump_what == 'loc':
+ self._dump_debug_locations()
+ elif dump_what == 'Ranges':
+ self._dump_debug_ranges()
+ else:
+ self._emitline('debug dump not yet supported for "%s"' % dump_what)
+
+ def _format_hex(self, addr, fieldsize=None, fullhex=False, lead0x=True,
+ alternate=False):
+ """ Format an address into a hexadecimal string.
+
+ fieldsize:
+ Size of the hexadecimal field (with leading zeros to fit the
+ address into. For example with fieldsize=8, the format will
+ be %08x
+ If None, the minimal required field size will be used.
+
+ fullhex:
+ If True, override fieldsize to set it to the maximal size
+ needed for the elfclass
+
+ lead0x:
+ If True, leading 0x is added
+
+ alternate:
+ If True, override lead0x to emulate the alternate
+ hexadecimal form specified in format string with the #
+ character: only non-zero values are prefixed with 0x.
+ This form is used by readelf.
+ """
+ if alternate:
+ if addr == 0:
+ lead0x = False
+ else:
+ lead0x = True
+ if fieldsize is not None:
+ fieldsize -= 2
+
+ s = '0x' if lead0x else ''
+ if fullhex:
+ fieldsize = 8 if self.elffile.elfclass == 32 else 16
+ if fieldsize is None:
+ field = '%x'
+ else:
+ field = '%' + '0%sx' % fieldsize
+ return s + field % addr
+
+ def _print_version_section_header(self, version_section, name, lead0x=True,
+ indent=1):
+ """ Print a section header of one version related section (versym,
+ verneed or verdef) with some options to accomodate readelf
+ little differences between each header (e.g. indentation
+ and 0x prefixing).
+ """
+ if hasattr(version_section, 'num_versions'):
+ num_entries = version_section.num_versions()
+ else:
+ num_entries = version_section.num_symbols()
+
+ self._emitline("\n%s section '%s' contains %d %s:" % (
+ name, version_section.name, num_entries,
+ 'entry' if num_entries == 1 else 'entries'))
+ self._emitline('%sAddr: %s Offset: %s Link: %i (%s)' % (
+ ' ' * indent,
+ self._format_hex(
+ version_section['sh_addr'], fieldsize=16, lead0x=lead0x),
+ self._format_hex(
+ version_section['sh_offset'], fieldsize=8, lead0x=True),
+ version_section['sh_link'],
+ self.elffile.get_section(version_section['sh_link']).name
+ )
+ )
+
+ def _init_versioninfo(self):
+ """ Search and initialize informations about version related sections
+ and the kind of versioning used (GNU or Solaris).
+ """
+ if self._versioninfo is not None:
+ return
+
+ self._versioninfo = {'versym': None, 'verdef': None,
+ 'verneed': None, 'type': None}
+
+ for section in self.elffile.iter_sections():
+ if isinstance(section, GNUVerSymSection):
+ self._versioninfo['versym'] = section
+ elif isinstance(section, GNUVerDefSection):
+ self._versioninfo['verdef'] = section
+ elif isinstance(section, GNUVerNeedSection):
+ self._versioninfo['verneed'] = section
+ elif isinstance(section, DynamicSection):
+ for tag in section.iter_tags():
+ if tag['d_tag'] == 'DT_VERSYM':
+ self._versioninfo['type'] = 'GNU'
+ break
+
+ if not self._versioninfo['type'] and (
+ self._versioninfo['verneed'] or self._versioninfo['verdef']):
+ self._versioninfo['type'] = 'Solaris'
+
+ def _symbol_version(self, nsym):
+ """ Return a dict containing information on the
+ or None if no version information is available
+ """
+ self._init_versioninfo()
+
+ symbol_version = dict.fromkeys(('index', 'name', 'filename', 'hidden'))
+
+ if (not self._versioninfo['versym'] or
+ nsym >= self._versioninfo['versym'].num_symbols()):
+ return None
+
+ symbol = self._versioninfo['versym'].get_symbol(nsym)
+ index = symbol.entry['ndx']
+ if not index in ('VER_NDX_LOCAL', 'VER_NDX_GLOBAL'):
+ index = int(index)
+
+ if self._versioninfo['type'] == 'GNU':
+ # In GNU versioning mode, the highest bit is used to
+ # store whether the symbol is hidden or not
+ if index & 0x8000:
+ index &= ~0x8000
+ symbol_version['hidden'] = True
+
+ if (self._versioninfo['verdef'] and
+ index <= self._versioninfo['verdef'].num_versions()):
+ _, verdaux_iter = \
+ self._versioninfo['verdef'].get_version(index)
+ symbol_version['name'] = next(verdaux_iter).name
+ else:
+ verneed, vernaux = \
+ self._versioninfo['verneed'].get_version(index)
+ symbol_version['name'] = vernaux.name
+ symbol_version['filename'] = verneed.name
+
+ symbol_version['index'] = index
+ return symbol_version
+
+ def _section_from_spec(self, spec):
+ """ Retrieve a section given a "spec" (either number or name).
+ Return None if no such section exists in the file.
+ """
+ try:
+ num = int(spec)
+ if num < self.elffile.num_sections():
+ return self.elffile.get_section(num)
+ else:
+ return None
+ except ValueError:
+ # Not a number. Must be a name then
+ return self.elffile.get_section_by_name(spec)
+
+ def _get_symbol_shndx(self, symbol, symbol_index, symtab_index):
+ """ Get the index into the section header table for the "symbol"
+ at "symbol_index" located in the symbol table with section index
+ "symtab_index".
+ """
+ symbol_shndx = symbol['st_shndx']
+ if symbol_shndx != SHN_INDICES.SHN_XINDEX:
+ return symbol_shndx
+
+ # Check for or lazily construct index section mapping (symbol table
+ # index -> corresponding symbol table index section object)
+ if self._shndx_sections is None:
+ self._shndx_sections = {sec.symboltable: sec for sec in self.elffile.iter_sections()
+ if isinstance(sec, SymbolTableIndexSection)}
+ return self._shndx_sections[symtab_index].get_section_index(symbol_index)
+
+ def _note_relocs_for_section(self, section):
+ """ If there are relocation sections pointing to the givne section,
+ emit a note about it.
+ """
+ for relsec in self.elffile.iter_sections():
+ if isinstance(relsec, RelocationSection):
+ info_idx = relsec['sh_info']
+ if self.elffile.get_section(info_idx) == section:
+ self._emitline(' Note: This section has relocations against it, but these have NOT been applied to this dump.')
+ return
+
+ def _init_dwarfinfo(self):
+ """ Initialize the DWARF info contained in the file and assign it to
+ self._dwarfinfo.
+ Leave self._dwarfinfo at None if no DWARF info was found in the file
+ """
+ if self._dwarfinfo is not None:
+ return
+
+ if self.elffile.has_dwarf_info():
+ self._dwarfinfo = self.elffile.get_dwarf_info()
+ else:
+ self._dwarfinfo = None
+
+ def _dump_debug_info(self):
+ """ Dump the debugging info section.
+ """
+ if not self._dwarfinfo.has_debug_info:
+ return
+ self._emitline('Contents of the %s section:\n' % self._dwarfinfo.debug_info_sec.name)
+
+ # Offset of the .debug_info section in the stream
+ section_offset = self._dwarfinfo.debug_info_sec.global_offset
+
+ for cu in self._dwarfinfo.iter_CUs():
+ self._emitline(' Compilation Unit @ offset %s:' %
+ self._format_hex(cu.cu_offset, alternate=True))
+ self._emitline(' Length: %s (%s)' % (
+ self._format_hex(cu['unit_length']),
+ '%s-bit' % cu.dwarf_format()))
+ self._emitline(' Version: %s' % cu['version'])
+ if cu['version'] >= 5:
+ if cu.header.get("unit_type", ''):
+ unit_type = cu.header.unit_type
+ self._emitline(' Unit Type: %s (%d)' % (
+ unit_type, ENUM_DW_UT.get(cu.header.unit_type, 0)))
+ self._emitline(' Abbrev Offset: %s' % (
+ self._format_hex(cu['debug_abbrev_offset'], alternate=True)))
+ self._emitline(' Pointer Size: %s' % cu['address_size'])
+ if unit_type in ('DW_UT_skeleton', 'DW_UT_split_compile'):
+ self._emitline(' Dwo id: %s' % cu['dwo_id'])
+ elif unit_type in ('DW_UT_type', 'DW_UT_split_type'):
+ self._emitline(' Signature: 0x%x' % cu['type_signature'])
+ self._emitline(' Type Offset: 0x%x' % cu['type_offset'])
+ else:
+ self._emitline(' Abbrev Offset: %s' % (
+ self._format_hex(cu['debug_abbrev_offset'], alternate=True))),
+ self._emitline(' Pointer Size: %s' % cu['address_size'])
+
+ # The nesting depth of each DIE within the tree of DIEs must be
+ # displayed. To implement this, a counter is incremented each time
+ # the current DIE has children, and decremented when a null die is
+ # encountered. Due to the way the DIE tree is serialized, this will
+ # correctly reflect the nesting depth
+ #
+ die_depth = 0
+ current_function = None
+ for die in cu.iter_DIEs():
+ if die.tag == 'DW_TAG_subprogram':
+ current_function = die
+ self._emitline(' <%s><%x>: Abbrev Number: %s%s' % (
+ die_depth,
+ die.offset,
+ die.abbrev_code,
+ (' (%s)' % die.tag) if not die.is_null() else ''))
+ if die.is_null():
+ die_depth -= 1
+ continue
+
+ for attr in die.attributes.values():
+ name = attr.name
+ # Unknown attribute values are passed-through as integers
+ if isinstance(name, int):
+ name = 'Unknown AT value: %x' % name
+
+ attr_desc = describe_attr_value(attr, die, section_offset)
+
+ if 'DW_OP_fbreg' in attr_desc and current_function and not 'DW_AT_frame_base' in current_function.attributes:
+ postfix = ' [without dw_at_frame_base]'
+ else:
+ postfix = ''
+
+ self._emitline(' <%x> %-18s: %s%s' % (
+ attr.offset,
+ name,
+ attr_desc,
+ postfix))
+
+ if die.has_children:
+ die_depth += 1
+
+ self._emitline()
+
+ def _dump_debug_types(self):
+ """Dump the debug types section
+ """
+ if not self._dwarfinfo.has_debug_info:
+ return
+ if self._dwarfinfo.debug_types_sec is None:
+ return
+ self._emitline('Contents of the %s section:\n' % self._dwarfinfo.debug_types_sec.name)
+
+ # Offset of the .debug_types section in the stream
+ section_offset = self._dwarfinfo.debug_types_sec.global_offset
+
+ for tu in self._dwarfinfo.iter_TUs():
+ self._emitline(' Compilation Unit @ offset %s:' %
+ self._format_hex(tu.tu_offset, alternate=True))
+ self._emitline(' Length: %s (%s)' % (self._format_hex(tu['unit_length']),
+ '%s-bit' % tu.dwarf_format()))
+ self._emitline(' Version: %s' % tu['version'])
+ self._emitline(' Abbrev Offset: %s' % (self._format_hex(tu['debug_abbrev_offset'], alternate=True)))
+ self._emitline(' Pointer Size: %s' % tu['address_size'])
+ self._emitline(' Signature: 0x%x' % tu['signature'])
+ self._emitline(' Type Offset: 0x%x' % tu['type_offset'])
+
+ die_depth = 0
+ for die in tu.iter_DIEs():
+ self._emitline(' <%s><%x>: Abbrev Number: %s%s' % (
+ die_depth,
+ die.offset,
+ die.abbrev_code,
+ (' (%s)' % die.tag) if not die.is_null() else ''))
+ if die.is_null():
+ die_depth -= 1
+ continue
+
+ for attr in die.attributes.values():
+ name = attr.name
+ # Unknown attribute values are passed-through as integers
+ if isinstance(name, int):
+ name = 'Unknown AT value: %x' % name
+
+ attr_desc = describe_attr_value(attr, die, section_offset)
+
+ self._emitline(' <%x> %-18s: %s' % (
+ attr.offset,
+ name,
+ attr_desc))
+
+ if die.has_children:
+ die_depth += 1
+
+ self._emitline()
+
+ def _dump_debug_line_programs(self):
+ """ Dump the (decoded) line programs from .debug_line
+ The programs are dumped in the order of the CUs they belong to.
+ """
+ if not self._dwarfinfo.has_debug_info:
+ return
+ self._emitline('Contents of the %s section:' % self._dwarfinfo.debug_line_sec.name)
+ self._emitline()
+ lineprogram_list = []
+
+ for cu in self._dwarfinfo.iter_CUs():
+ # Avoid dumping same lineprogram multiple times
+ lineprogram = self._dwarfinfo.line_program_for_CU(cu)
+
+ if lineprogram is None or lineprogram in lineprogram_list:
+ continue
+
+ lineprogram_list.append(lineprogram)
+ ver5 = lineprogram.header.version >= 5
+
+ cu_filename = bytes2str(lineprogram['file_entry'][0].name)
+ if len(lineprogram['include_directory']) > 0:
+ # GNU readelf 2.38 only outputs directory in wide mode
+ self._emitline('%s:' % cu_filename)
+ else:
+ self._emitline('CU: %s:' % cu_filename)
+
+ self._emitline('File name Line number Starting address Stmt')
+ # GNU readelf has a View column that we don't try to replicate
+ # The autotest has logic in place to ignore that
+
+ # Print each state's file, line and address information. For some
+ # instructions other output is needed to be compatible with
+ # readelf.
+ for entry in lineprogram.get_entries():
+ state = entry.state
+ if state is None:
+ # Special handling for commands that don't set a new state
+ if entry.command == DW_LNS_set_file:
+ file_entry = lineprogram['file_entry'][entry.args[0] - 1]
+ if file_entry.dir_index == 0:
+ # current directory
+ self._emitline('\n./%s:[++]' % (
+ bytes2str(file_entry.name)))
+ else:
+ self._emitline('\n%s/%s:' % (
+ bytes2str(lineprogram['include_directory'][file_entry.dir_index - 1]),
+ bytes2str(file_entry.name)))
+ elif entry.command == DW_LNE_define_file:
+ self._emitline('%s:' % (
+ bytes2str(lineprogram['include_directory'][entry.args[0].dir_index])))
+ elif lineprogram['version'] < 4 or self.elffile['e_machine'] == 'EM_PPC64':
+ self._emitline('%-35s %11s %18s %s' % (
+ bytes2str(lineprogram['file_entry'][state.file - 1].name),
+ state.line if not state.end_sequence else '-',
+ '0' if state.address == 0 else self._format_hex(state.address),
+ 'x' if state.is_stmt and not state.end_sequence else ''))
+ else:
+ # In readelf, on non-VLIW machines there is no op_index postfix after address.
+ # It used to be unconditional.
+ self._emitline('%-35s %s %18s%s %s' % (
+ bytes2str(lineprogram['file_entry'][state.file - 1].name),
+ "%11d" % (state.line,) if not state.end_sequence else '-',
+ '0' if state.address == 0 else self._format_hex(state.address),
+ '' if lineprogram.header.maximum_operations_per_instruction == 1 else '[%d]' % (state.op_index,),
+ 'x' if state.is_stmt and not state.end_sequence else ''))
+ if entry.command == DW_LNS_copy:
+ # Another readelf oddity...
+ self._emitline()
+
+ def _dump_frames_info(self, section, cfi_entries):
+ """ Dump the raw call frame info in a section.
+
+ `section` is the Section instance that contains the call frame info
+ while `cfi_entries` must be an iterable that yields the sequence of
+ CIE or FDE instances.
+ """
+ self._emitline('Contents of the %s section:' % section.name)
+
+ for entry in cfi_entries:
+ if isinstance(entry, CIE):
+ self._emitline('\n%08x %s %s CIE' % (
+ entry.offset,
+ self._format_hex(entry['length'], fullhex=True, lead0x=False),
+ self._format_hex(entry['CIE_id'], fieldsize=8, lead0x=False)))
+ self._emitline(' Version: %d' % entry['version'])
+ self._emitline(' Augmentation: "%s"' % bytes2str(entry['augmentation']))
+ if(entry['version'] >= 4):
+ self._emitline(' Pointer Size: %d' % entry['address_size'])
+ self._emitline(' Segment Size: %d' % entry['segment_size'])
+ self._emitline(' Code alignment factor: %u' % entry['code_alignment_factor'])
+ self._emitline(' Data alignment factor: %d' % entry['data_alignment_factor'])
+ self._emitline(' Return address column: %d' % entry['return_address_register'])
+ if entry.augmentation_bytes:
+ self._emitline(' Augmentation data: {}'.format(' '.join(
+ '{:02x}'.format(ord(b))
+ for b in iterbytes(entry.augmentation_bytes)
+ )))
+ self._emitline()
+
+ elif isinstance(entry, FDE):
+ # Readelf bug #31973
+ length = entry['length'] if entry.cie.offset < entry.offset else entry.cie['length']
+ self._emitline('\n%08x %s %s FDE cie=%08x pc=%s..%s' % (
+ entry.offset,
+ self._format_hex(length, fullhex=True, lead0x=False),
+ self._format_hex(entry['CIE_pointer'], fieldsize=8, lead0x=False),
+ entry.cie.offset,
+ self._format_hex(entry['initial_location'], fullhex=True, lead0x=False),
+ self._format_hex(
+ entry['initial_location'] + entry['address_range'],
+ fullhex=True, lead0x=False)))
+ if entry.augmentation_bytes:
+ self._emitline(' Augmentation data: {}'.format(' '.join(
+ '{:02x}'.format(ord(b))
+ for b in iterbytes(entry.augmentation_bytes)
+ )))
+
+ else: # ZERO terminator
+ assert isinstance(entry, ZERO)
+ self._emitline('\n%08x ZERO terminator' % entry.offset)
+ continue
+
+ self._emit(describe_CFI_instructions(entry))
+ self._emitline()
+
+ def _dump_debug_frames(self):
+ """ Dump the raw frame info from .debug_frame and .eh_frame sections.
+ """
+ if self._dwarfinfo.has_EH_CFI():
+ self._dump_frames_info(
+ self._dwarfinfo.eh_frame_sec,
+ self._dwarfinfo.EH_CFI_entries())
+ self._emitline()
+
+ if self._dwarfinfo.has_CFI():
+ self._dump_frames_info(
+ self._dwarfinfo.debug_frame_sec,
+ self._dwarfinfo.CFI_entries())
+
+ def _dump_debug_namelut(self, what):
+ """
+ Dump the debug pubnames section.
+ """
+ if what == 'pubnames':
+ namelut = self._dwarfinfo.get_pubnames()
+ section = self._dwarfinfo.debug_pubnames_sec
+ else:
+ namelut = self._dwarfinfo.get_pubtypes()
+ section = self._dwarfinfo.debug_pubtypes_sec
+
+ # readelf prints nothing if the section is not present.
+ if namelut is None or len(namelut) == 0:
+ return
+
+ self._emitline('Contents of the %s section:' % section.name)
+ self._emitline()
+
+ cu_headers = namelut.get_cu_headers()
+
+ # go over CU-by-CU first and item-by-item next.
+ for (cu_hdr, (cu_ofs, items)) in izip(cu_headers, itertools.groupby(
+ namelut.items(), key = lambda x: x[1].cu_ofs)):
+
+ self._emitline(' Length: %d' % cu_hdr.unit_length)
+ self._emitline(' Version: %d' % cu_hdr.version)
+ self._emitline(' Offset into .debug_info section: 0x%x' % cu_hdr.debug_info_offset)
+ self._emitline(' Size of area in .debug_info section: %d' % cu_hdr.debug_info_length)
+ self._emitline()
+ self._emitline(' Offset Name')
+ for item in items:
+ self._emitline(' %x %s' % (item[1].die_ofs - cu_ofs, item[0]))
+ self._emitline()
+
+ def _dump_debug_aranges(self):
+ """ Dump the aranges table
+ """
+ aranges_table = self._dwarfinfo.get_aranges()
+ if aranges_table == None:
+ return
+ # Seems redundant, but we need to get the unsorted set of entries
+ # to match system readelf.
+ # Also, sometimes there are blank sections in aranges, but readelf
+ # dumps them, so we should too.
+ unordered_entries = aranges_table._get_entries(need_empty=True)
+
+ if len(unordered_entries) == 0:
+ self._emitline()
+ self._emitline("Section '.debug_aranges' has no debugging data.")
+ return
+
+ self._emitline('Contents of the %s section:' % self._dwarfinfo.debug_aranges_sec.name)
+ self._emitline()
+ prev_offset = None
+ for entry in unordered_entries:
+ if prev_offset != entry.info_offset:
+ if entry != unordered_entries[0]:
+ self._emitline(' %s %s' % (
+ self._format_hex(0, fullhex=True, lead0x=False),
+ self._format_hex(0, fullhex=True, lead0x=False)))
+ self._emitline(' Length: %d' % (entry.unit_length))
+ self._emitline(' Version: %d' % (entry.version))
+ self._emitline(' Offset into .debug_info: 0x%x' % (entry.info_offset))
+ self._emitline(' Pointer Size: %d' % (entry.address_size))
+ self._emitline(' Segment Size: %d' % (entry.segment_size))
+ self._emitline()
+ self._emitline(' Address Length')
+ if entry.begin_addr != 0 or entry.length != 0:
+ self._emitline(' %s %s' % (
+ self._format_hex(entry.begin_addr, fullhex=True, lead0x=False),
+ self._format_hex(entry.length, fullhex=True, lead0x=False)))
+ prev_offset = entry.info_offset
+ self._emitline(' %s %s' % (
+ self._format_hex(0, fullhex=True, lead0x=False),
+ self._format_hex(0, fullhex=True, lead0x=False)))
+
+ def _dump_frames_interp_info(self, section, cfi_entries):
+ """ Dump interpreted (decoded) frame information in a section.
+
+ `section` is the Section instance that contains the call frame info
+ while `cfi_entries` must be an iterable that yields the sequence of
+ CIE or FDE instances.
+ """
+ self._emitline('Contents of the %s section:' % section.name)
+
+ for entry in cfi_entries:
+ if isinstance(entry, CIE):
+ self._emitline('\n%08x %s %s CIE "%s" cf=%d df=%d ra=%d' % (
+ entry.offset,
+ self._format_hex(entry['length'], fullhex=True, lead0x=False),
+ self._format_hex(entry['CIE_id'], fieldsize=8, lead0x=False),
+ bytes2str(entry['augmentation']),
+ entry['code_alignment_factor'],
+ entry['data_alignment_factor'],
+ entry['return_address_register']))
+ ra_regnum = entry['return_address_register']
+
+ elif isinstance(entry, FDE):
+ # Readelf bug #31973 - FDE length misreported if FDE precedes its CIE
+ length = entry['length'] if entry.cie.offset < entry.offset else entry.cie['length']
+ self._emitline('\n%08x %s %s FDE cie=%08x pc=%s..%s' % (
+ entry.offset,
+ self._format_hex(length, fullhex=True, lead0x=False),
+ self._format_hex(entry['CIE_pointer'], fieldsize=8, lead0x=False),
+ entry.cie.offset,
+ self._format_hex(entry['initial_location'], fullhex=True, lead0x=False),
+ self._format_hex(entry['initial_location'] + entry['address_range'],
+ fullhex=True, lead0x=False)))
+ ra_regnum = entry.cie['return_address_register']
+
+ # If the FDE brings adds no unwinding information compared to
+ # its CIE, omit its table.
+ if (len(entry.get_decoded().table) ==
+ len(entry.cie.get_decoded().table)):
+ continue
+
+ else: # ZERO terminator
+ assert isinstance(entry, ZERO)
+ self._emitline('\n%08x ZERO terminator' % entry.offset)
+ continue
+
+ # Decode the table.
+ decoded_table = entry.get_decoded()
+ if len(decoded_table.table) == 0:
+ continue
+
+ # Print the heading row for the decoded table
+ self._emit(' LOC')
+ self._emit(' ' if entry.structs.address_size == 4 else ' ')
+ self._emit(' CFA ')
+
+ # Look at the registers the decoded table describes.
+ # We build reg_order here to match readelf's order. In particular,
+ # registers are sorted by their number, so that the register
+ # matching ra_regnum is usually listed last with a special heading.
+ # (LoongArch is a notable exception in that its return register's
+ # DWARF register number is not greater than other GPRs.)
+ decoded_table = entry.get_decoded()
+ reg_order = sorted(decoded_table.reg_order)
+ if len(decoded_table.reg_order):
+ # Headings for the registers
+ for regnum in reg_order:
+ if regnum == ra_regnum:
+ self._emit('ra ')
+ continue
+ self._emit('%-6s' % describe_reg_name(regnum))
+ self._emitline()
+
+ for line in decoded_table.table:
+ self._emit(self._format_hex(
+ line['pc'], fullhex=True, lead0x=False))
+
+ if line['cfa'] is not None:
+ s = describe_CFI_CFA_rule(line['cfa'])
+ else:
+ s = 'u'
+ self._emit(' %-9s' % s)
+
+ for regnum in reg_order:
+ if regnum in line:
+ s = describe_CFI_register_rule(line[regnum])
+ else:
+ s = 'u'
+ self._emit('%-6s' % s)
+ self._emitline()
+ self._emitline()
+
+ def _dump_debug_frames_interp(self):
+ """ Dump the interpreted (decoded) frame information from .debug_frame
+ and .eh_frame sections.
+ """
+ if self._dwarfinfo.has_EH_CFI():
+ self._dump_frames_interp_info(
+ self._dwarfinfo.eh_frame_sec,
+ self._dwarfinfo.EH_CFI_entries())
+ self._emitline()
+
+ if self._dwarfinfo.has_CFI():
+ self._dump_frames_interp_info(
+ self._dwarfinfo.debug_frame_sec,
+ self._dwarfinfo.CFI_entries())
+
+ def _dump_debug_locations(self):
+ """ Dump the location lists from .debug_loc/.debug_loclists section
+ """
+ di = self._dwarfinfo
+ loc_lists_sec = di.location_lists()
+ if not loc_lists_sec: # No locations section - readelf outputs nothing
+ return
+
+ if isinstance(loc_lists_sec, LocationListsPair):
+ self._dump_debug_locsection(di, loc_lists_sec._loc)
+ self._dump_debug_locsection(di, loc_lists_sec._loclists)
+ else:
+ self._dump_debug_locsection(di, loc_lists_sec)
+
+ def _dump_debug_locsection(self, di, loc_lists_sec):
+ """ Dump the location lists from .debug_loc/.debug_loclists section
+ """
+ ver5 = loc_lists_sec.version >= 5
+ section_name = (di.debug_loclists_sec if ver5 else di.debug_loc_sec).name
+
+ # To dump a location list, one needs to know the CU.
+ # Scroll through DIEs once, list the known location list offsets.
+ # Don't need this CU/DIE scan if all entries are absolute or prefixed by base,
+ # but let's not optimize for that yet.
+ cu_map = dict() # Loc list offset => CU
+ for cu in di.iter_CUs():
+ for die in cu.iter_DIEs():
+ for key in die.attributes:
+ attr = die.attributes[key]
+ if (LocationParser.attribute_has_location(attr, cu['version']) and
+ LocationParser._attribute_has_loc_list(attr, cu['version'])):
+ cu_map[attr.value] = cu
+
+ addr_size = di.config.default_address_size # In bytes, 4 or 8
+ addr_width = addr_size * 2 # In hex digits, 8 or 16
+ line_template = " %%08x %%0%dx %%0%dx %%s%%s" % (addr_width, addr_width)
+
+ loc_lists = list(loc_lists_sec.iter_location_lists())
+ if len(loc_lists) == 0:
+ # Present but empty locations section - readelf outputs a message
+ self._emitline("\nSection '%s' has no debugging data." % (section_name,))
+ return
+
+ # The v5 loclists section consists of CUs - small header, and
+ # an optional loclist offset table. Readelf prints out the header data dump
+ # on top of every CU, so we should too. But we don't scroll through
+ # the loclists section, we are effectively scrolling through the info
+ # section (as to not stumble upon gaps in the secion, and not miss the
+ # GNU locviews, so we have to keep track which loclists-CU we are in.
+ # Same logic in v5 rnglists section dump.
+ lcus = list(loc_lists_sec.iter_CUs()) if ver5 else None
+ lcu_index = 0
+ next_lcu_offset = 0
+
+ self._emitline('Contents of the %s section:\n' % (section_name,))
+ if not ver5:
+ self._emitline(' Offset Begin End Expression')
+
+ for loc_list in loc_lists:
+ # Emit CU headers before the current loclist, if we've moved to the next CU
+ if ver5 and loc_list[0].entry_offset > next_lcu_offset:
+ while loc_list[0].entry_offset > next_lcu_offset:
+ lcu = lcus[lcu_index]
+ self._dump_debug_loclists_CU_header(lcu)
+ next_lcu_offset = lcu.offset_after_length + lcu.unit_length
+ lcu_index += 1
+ self._emitline(' Offset Begin End Expression')
+ self._dump_loclist(loc_list, line_template, cu_map)
+
+ def _dump_loclist(self, loc_list, line_template, cu_map):
+ in_views = False
+ has_views = False
+ base_ip = None
+ loc_entry_count = 0
+ cu = None
+ for entry in loc_list:
+ if isinstance(entry, LocationViewPair):
+ has_views = in_views = True
+ # The "v" before address is conditional in binutils, haven't figured out how
+ self._emitline(" %08x v%015x v%015x location view pair" % (entry.entry_offset, entry.begin, entry.end))
+ else:
+ if in_views:
+ in_views = False
+ self._emitline("")
+
+ # Readelf quirk: indexed loclists don't show the real base IP
+ if cu_map is None:
+ base_ip = 0
+ elif cu is None:
+ cu = cu_map.get(entry.entry_offset, False)
+ if not cu:
+ raise ValueError("Location list can't be tracked to a CU")
+
+ if isinstance(entry, LocationEntry):
+ if base_ip is None and not entry.is_absolute:
+ base_ip = _get_cu_base(cu)
+
+ begin_offset = (0 if entry.is_absolute else base_ip) + entry.begin_offset
+ end_offset = (0 if entry.is_absolute else base_ip) + entry.end_offset
+ expr = describe_DWARF_expr(entry.loc_expr, cu.structs, cu.cu_offset)
+ if has_views:
+ view = loc_list[loc_entry_count]
+ postfix = ' (start == end)' if entry.begin_offset == entry.end_offset and view.begin == view.end else ''
+ self._emitline(' %08x v%015x v%015x views at %08x for:' %(
+ entry.entry_offset,
+ view.begin,
+ view.end,
+ view.entry_offset))
+ self._emitline(' %016x %016x %s%s' %(
+ begin_offset,
+ end_offset,
+ expr,
+ postfix))
+ loc_entry_count += 1
+ else:
+ postfix = ' (start == end)' if entry.begin_offset == entry.end_offset else ''
+ self._emitline(line_template % (
+ entry.entry_offset,
+ begin_offset,
+ end_offset,
+ expr,
+ postfix))
+ elif isinstance(entry, LocBaseAddressEntry):
+ base_ip = entry.base_address
+ self._emitline(" %08x %016x (base address)" % (entry.entry_offset, entry.base_address))
+
+ # Pyelftools doesn't store the terminating entry,
+ # but readelf emits its offset, so this should too.
+ last = loc_list[-1]
+ self._emitline(" %08x " % (last.entry_offset + last.entry_length))
+
+ def _dump_debug_loclists_CU_header(self, cu):
+ # Header slightly different from that of v5 rangelist in-section CU header dump
+ self._emitline('Table at Offset %s' % self._format_hex(cu.cu_offset, alternate=True))
+ self._emitline(' Length: %s' % self._format_hex(cu.unit_length, alternate=True))
+ self._emitline(' DWARF version: %d' % cu.version)
+ self._emitline(' Address size: %d' % cu.address_size)
+ self._emitline(' Segment size: %d' % cu.segment_selector_size)
+ self._emitline(' Offset entries: %d\n' % cu.offset_count)
+ if cu.offsets and len(cu.offsets):
+ self._emitline(' Offsets starting at 0x%x:' % cu.offset_table_offset)
+ for i_offset in enumerate(cu.offsets):
+ self._emitline(' [%6d] 0x%x' % i_offset)
+
+ def _dump_debug_ranges(self):
+ # TODO: GNU readelf format doesn't need entry_length?
+ di = self._dwarfinfo
+ range_lists_sec = di.range_lists()
+ if not range_lists_sec: # No ranges section - readelf outputs nothing
+ return
+
+ if isinstance(range_lists_sec, RangeListsPair):
+ self._dump_debug_rangesection(di, range_lists_sec._ranges)
+ self._dump_debug_rangesection(di, range_lists_sec._rnglists)
+ else:
+ self._dump_debug_rangesection(di, range_lists_sec)
+
+ def _dump_debug_rnglists_CU_header(self, cu):
+ self._emitline(' Table at Offset: %s:' % self._format_hex(cu.cu_offset, alternate=True))
+ self._emitline(' Length: %s' % self._format_hex(cu.unit_length, alternate=True))
+ self._emitline(' DWARF version: %d' % cu.version)
+ self._emitline(' Address size: %d' % cu.address_size)
+ self._emitline(' Segment size: %d' % cu.segment_selector_size)
+ self._emitline(' Offset entries: %d\n' % cu.offset_count)
+ if cu.offsets and len(cu.offsets):
+ self._emitline(' Offsets starting at 0x%x:' % cu.offset_table_offset)
+ for i_offset in enumerate(cu.offsets):
+ self._emitline(' [%6d] 0x%x' % i_offset)
+
+ def _dump_debug_rangesection(self, di, range_lists_sec):
+ # Last amended to match readelf 2.41
+ ver5 = range_lists_sec.version >= 5
+ section_name = (di.debug_rnglists_sec if ver5 else di.debug_ranges_sec).name
+ addr_size = di.config.default_address_size # In bytes, 4 or 8
+ addr_width = addr_size * 2 # In hex digits, 8 or 16
+ line_template = " %%08x %%0%dx %%0%dx %%s" % (addr_width, addr_width)
+ base_template = " %%08x %%0%dx (base address)" % (addr_width)
+ base_template_indexed = " %%08x %%0%dx (base address index) %%0%dx (base address)" % (addr_width, addr_width)
+
+ # In order to determine the base address of the range
+ # We need to know the corresponding CU.
+ cu_map = {die.attributes['DW_AT_ranges'].value : cu # Range list offset => CU
+ for cu in di.iter_CUs()
+ for die in cu.iter_DIEs()
+ if 'DW_AT_ranges' in die.attributes}
+
+ rcus = list(range_lists_sec.iter_CUs()) if ver5 else None
+ rcu_index = 0
+ next_rcu_offset = 0
+
+ range_lists = list(range_lists_sec.iter_range_lists())
+ if len(range_lists) == 0:
+ # Present but empty ranges section - readelf outputs a message
+ self._emitline("\nSection '%s' has no debugging data." % section_name)
+ return
+
+ self._emitline('Contents of the %s section:\n\n\n' % section_name)
+ if not ver5:
+ self._emitline(' Offset Begin End')
+
+ for range_list in range_lists:
+ # Emit CU headers before the curernt rangelist
+ if ver5 and range_list[0].entry_offset > next_rcu_offset:
+ while range_list[0].entry_offset > next_rcu_offset:
+ rcu = rcus[rcu_index]
+ self._dump_debug_rnglists_CU_header(rcu)
+ next_rcu_offset = rcu.offset_after_length + rcu.unit_length
+ rcu_index += 1
+ self._emitline(' Offset Begin End')
+ self._dump_rangelist(range_list, cu_map, ver5, line_template, base_template, base_template_indexed, range_lists_sec)
+
+ # TODO: trailing empty CUs, if any?
+
+ def _dump_rangelist(self, range_list, cu_map, ver5, line_template, base_template, base_template_indexed, range_lists_sec):
+ # Weird discrepancy in binutils: for DWARFv5 it outputs entry offset,
+ # for DWARF<=4 list offset.
+ first = range_list[0]
+ base_ip = _get_cu_base(cu_map[first.entry_offset])
+ raw_v5_rangelist = None
+ for entry in range_list:
+ if isinstance(entry, RangeEntry):
+ postfix = ' (start == end)' if entry.begin_offset == entry.end_offset else ''
+ self._emitline(line_template % (
+ entry.entry_offset if ver5 else first.entry_offset,
+ (0 if entry.is_absolute else base_ip) + entry.begin_offset,
+ (0 if entry.is_absolute else base_ip) + entry.end_offset,
+ postfix))
+ elif isinstance(entry,RangeBaseAddressEntry):
+ base_ip = entry.base_address
+ # V5 base entries with index are reported differently in readelf - need to go back to the raw V5 format
+ # Maybe other subtypes too, but no such cases in the test corpus
+ raw_v5_entry = None
+ if ver5:
+ if not raw_v5_rangelist:
+ raw_v5_rangelist = range_lists_sec.get_range_list_at_offset_ex(range_list[0].entry_offset)
+ raw_v5_entry = next(re for re in raw_v5_rangelist if re.entry_offset == entry.entry_offset)
+ if raw_v5_entry and raw_v5_entry.entry_type == 'DW_RLE_base_addressx':
+ self._emitline(base_template_indexed % (
+ entry.entry_offset,
+ raw_v5_entry.index,
+ entry.base_address))
+ else:
+ self._emitline(base_template % (
+ entry.entry_offset if ver5 else first.entry_offset,
+ entry.base_address))
+ else:
+ raise NotImplementedError("Unknown object in a range list")
+ last = range_list[-1]
+ self._emitline(' %08x ' % (last.entry_offset + last.entry_length if ver5 else first.entry_offset))
+
+ def _display_attributes(self, attr_sec, descriptor):
+ """ Display the attributes contained in the section.
+ """
+ for s in attr_sec.iter_subsections():
+ self._emitline("Attribute Section: %s" % s.header['vendor_name'])
+ for ss in s.iter_subsubsections():
+ h_val = "" if ss.header.extra is None else " ".join("%d" % x for x in ss.header.extra)
+ self._emitline(descriptor(ss.header.tag, h_val, None))
+
+ for attr in ss.iter_attributes():
+ self._emit(' ')
+ self._emitline(descriptor(attr.tag, attr.value, attr.extra))
+
+ def _display_arch_specific_arm(self):
+ """ Display the ARM architecture-specific info contained in the file.
+ """
+ attr_sec = self.elffile.get_section_by_name('.ARM.attributes')
+ self._display_attributes(attr_sec, describe_attr_tag_arm)
+
+ def _display_arch_specific_riscv(self):
+ """ Display the RISC-V architecture-specific info contained in the file.
+ """
+ attr_sec = self.elffile.get_section_by_name('.riscv.attributes')
+ self._display_attributes(attr_sec, describe_attr_tag_riscv)
+
+ def _emit(self, s=''):
+ """ Emit an object to output
+ """
+ self.output.write(str(s))
+
+ def _emitline(self, s=''):
+ """ Emit an object to output, followed by a newline
+ """
+ self.output.write(str(s).rstrip() + '\n')
+
+
+SCRIPT_DESCRIPTION = 'Display information about the contents of ELF format files'
+VERSION_STRING = '%%(prog)s: based on pyelftools %s' % __version__
+
+
+def main(stream=None):
+ # parse the command-line arguments and invoke ReadElf
+ argparser = argparse.ArgumentParser(
+ usage='usage: %(prog)s [options] ',
+ description=SCRIPT_DESCRIPTION,
+ add_help=False, # -h is a real option of readelf
+ prog='readelf.py')
+ argparser.add_argument('file',
+ nargs='?', default=None,
+ help='ELF file to parse')
+ argparser.add_argument('-v', '--version',
+ action='version', version=VERSION_STRING)
+ argparser.add_argument('-d', '--dynamic',
+ action='store_true', dest='show_dynamic_tags',
+ help='Display the dynamic section')
+ argparser.add_argument('-H', '--help',
+ action='store_true', dest='help',
+ help='Display this information')
+ argparser.add_argument('-h', '--file-header',
+ action='store_true', dest='show_file_header',
+ help='Display the ELF file header')
+ argparser.add_argument('-l', '--program-headers', '--segments',
+ action='store_true', dest='show_program_header',
+ help='Display the program headers')
+ argparser.add_argument('-S', '--section-headers', '--sections',
+ action='store_true', dest='show_section_header',
+ help="Display the sections' headers")
+ argparser.add_argument('-e', '--headers',
+ action='store_true', dest='show_all_headers',
+ help='Equivalent to: -h -l -S')
+ argparser.add_argument('-s', '--symbols', '--syms',
+ action='store_true', dest='show_symbols',
+ help='Display the symbol table')
+ argparser.add_argument('-n', '--notes',
+ action='store_true', dest='show_notes',
+ help='Display the core notes (if present)')
+ argparser.add_argument('-r', '--relocs',
+ action='store_true', dest='show_relocs',
+ help='Display the relocations (if present)')
+ argparser.add_argument('-au', '--arm-unwind',
+ action='store_true', dest='show_arm_unwind',
+ help='Display the armeabi unwind information (if present)')
+ argparser.add_argument('-x', '--hex-dump',
+ action='store', dest='show_hex_dump', metavar='',
+ help='Dump the contents of section as bytes')
+ argparser.add_argument('-p', '--string-dump',
+ action='store', dest='show_string_dump', metavar='',
+ help='Dump the contents of section as strings')
+ argparser.add_argument('-V', '--version-info',
+ action='store_true', dest='show_version_info',
+ help='Display the version sections (if present)')
+ argparser.add_argument('-A', '--arch-specific',
+ action='store_true', dest='show_arch_specific',
+ help='Display the architecture-specific information (if present)')
+ argparser.add_argument('--debug-dump',
+ action='store', dest='debug_dump_what', metavar='',
+ help=(
+ 'Display the contents of DWARF debug sections. can ' +
+ 'one of {info,decodedline,frames,frames-interp,aranges,pubtypes,pubnames,loc,Ranges}'))
+ argparser.add_argument('--traceback',
+ action='store_true', dest='show_traceback',
+ help='Dump the Python traceback on ELFError'
+ ' exceptions from elftools')
+
+ args = argparser.parse_args()
+
+ if args.help or not args.file:
+ argparser.print_help()
+ sys.exit(0)
+
+ if args.show_all_headers:
+ do_file_header = do_section_header = do_program_header = True
+ else:
+ do_file_header = args.show_file_header
+ do_section_header = args.show_section_header
+ do_program_header = args.show_program_header
+
+ with open(args.file, 'rb') as file:
+ try:
+ readelf = ReadElf(file, stream or sys.stdout)
+ if do_file_header:
+ readelf.display_file_header()
+ if do_section_header:
+ readelf.display_section_headers(
+ show_heading=not do_file_header)
+ if do_program_header:
+ readelf.display_program_headers(
+ show_heading=not do_file_header)
+ if args.show_dynamic_tags:
+ readelf.display_dynamic_tags()
+ if args.show_symbols:
+ readelf.display_symbol_tables()
+ if args.show_notes:
+ readelf.display_notes()
+ if args.show_relocs:
+ readelf.display_relocations()
+ if args.show_arm_unwind:
+ readelf.display_arm_unwind()
+ if args.show_version_info:
+ readelf.display_version_info()
+ if args.show_arch_specific:
+ readelf.display_arch_specific()
+ if args.show_hex_dump:
+ readelf.display_hex_dump(args.show_hex_dump)
+ if args.show_string_dump:
+ readelf.display_string_dump(args.show_string_dump)
+ if args.debug_dump_what:
+ readelf.display_debug_dump(args.debug_dump_what)
+ except ELFError as ex:
+ sys.stdout.flush()
+ sys.stderr.write('ELF error: %s\n' % ex)
+ if args.show_traceback:
+ traceback.print_exc()
+ sys.exit(1)
+
+
+def profile_main():
+ # Run 'main' redirecting its output to readelfout.txt
+ # Saves profiling information in readelf.profile
+ PROFFILE = 'readelf.profile'
+ import cProfile
+ cProfile.run('main(open("readelfout.txt", "w"))', PROFFILE)
+
+ # Dig in some profiling stats
+ import pstats
+ p = pstats.Stats(PROFFILE)
+ p.sort_stats('cumulative').print_stats(25)
+
+
+#-------------------------------------------------------------------------------
+if __name__ == '__main__':
+ main()
+ #profile_main()
diff --git a/.venv312/Scripts/regen-package-docs.exe b/.venv312/Scripts/regen-package-docs.exe
new file mode 100644
index 0000000000..65ed5aaf45
Binary files /dev/null and b/.venv312/Scripts/regen-package-docs.exe differ
diff --git a/.venv312/Scripts/rehttpfs.exe b/.venv312/Scripts/rehttpfs.exe
new file mode 100644
index 0000000000..642cab48f2
Binary files /dev/null and b/.venv312/Scripts/rehttpfs.exe differ
diff --git a/.venv312/Scripts/rq.exe b/.venv312/Scripts/rq.exe
new file mode 100644
index 0000000000..4246f5dec1
Binary files /dev/null and b/.venv312/Scripts/rq.exe differ
diff --git a/.venv312/Scripts/rqinfo.exe b/.venv312/Scripts/rqinfo.exe
new file mode 100644
index 0000000000..75b9df79bc
Binary files /dev/null and b/.venv312/Scripts/rqinfo.exe differ
diff --git a/.venv312/Scripts/rqworker.exe b/.venv312/Scripts/rqworker.exe
new file mode 100644
index 0000000000..1fd61b6602
Binary files /dev/null and b/.venv312/Scripts/rqworker.exe differ
diff --git a/.venv312/Scripts/run.exe b/.venv312/Scripts/run.exe
new file mode 100644
index 0000000000..0842fb8170
Binary files /dev/null and b/.venv312/Scripts/run.exe differ
diff --git a/.venv312/Scripts/sbom4bids.exe b/.venv312/Scripts/sbom4bids.exe
new file mode 100644
index 0000000000..88d782e220
Binary files /dev/null and b/.venv312/Scripts/sbom4bids.exe differ
diff --git a/.venv312/Scripts/scancode-license-data.exe b/.venv312/Scripts/scancode-license-data.exe
new file mode 100644
index 0000000000..575f86f62f
Binary files /dev/null and b/.venv312/Scripts/scancode-license-data.exe differ
diff --git a/.venv312/Scripts/scancode-reindex-licenses.exe b/.venv312/Scripts/scancode-reindex-licenses.exe
new file mode 100644
index 0000000000..74e23f93e4
Binary files /dev/null and b/.venv312/Scripts/scancode-reindex-licenses.exe differ
diff --git a/.venv312/Scripts/scancode-reindex-package-patterns.exe b/.venv312/Scripts/scancode-reindex-package-patterns.exe
new file mode 100644
index 0000000000..935cef88ba
Binary files /dev/null and b/.venv312/Scripts/scancode-reindex-package-patterns.exe differ
diff --git a/.venv312/Scripts/scancode-train-gibberish-model.exe b/.venv312/Scripts/scancode-train-gibberish-model.exe
new file mode 100644
index 0000000000..a1ca54fc54
Binary files /dev/null and b/.venv312/Scripts/scancode-train-gibberish-model.exe differ
diff --git a/.venv312/Scripts/scancode.exe b/.venv312/Scripts/scancode.exe
new file mode 100644
index 0000000000..232f70fc5b
Binary files /dev/null and b/.venv312/Scripts/scancode.exe differ
diff --git a/.venv312/Scripts/scanpipe.exe b/.venv312/Scripts/scanpipe.exe
new file mode 100644
index 0000000000..4205369bda
Binary files /dev/null and b/.venv312/Scripts/scanpipe.exe differ
diff --git a/.venv312/Scripts/sparqlquery.exe b/.venv312/Scripts/sparqlquery.exe
new file mode 100644
index 0000000000..90994eb1c9
Binary files /dev/null and b/.venv312/Scripts/sparqlquery.exe differ
diff --git a/.venv312/Scripts/sqlformat.exe b/.venv312/Scripts/sqlformat.exe
new file mode 100644
index 0000000000..44f21833ff
Binary files /dev/null and b/.venv312/Scripts/sqlformat.exe differ
diff --git a/.venv312/Scripts/vba_extract.py b/.venv312/Scripts/vba_extract.py
new file mode 100644
index 0000000000..6cfc0c57d8
--- /dev/null
+++ b/.venv312/Scripts/vba_extract.py
@@ -0,0 +1,79 @@
+#!E:\hell\scancode.io\.venv312\Scripts\python.exe
+
+##############################################################################
+#
+# vba_extract - A simple utility to extract a vbaProject.bin binary from an
+# Excel 2007+ xlsm file for insertion into an XlsxWriter file.
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2013-2025, John McNamara, jmcnamara@cpan.org
+#
+
+import sys
+from zipfile import BadZipFile, ZipFile
+
+
+def extract_file(xlsm_zip, filename):
+ # Extract a single file from an Excel xlsm macro file.
+ data = xlsm_zip.read("xl/" + filename)
+
+ # Write the data to a local file.
+ file = open(filename, "wb")
+ file.write(data)
+ file.close()
+
+
+# The VBA project file and project signature file we want to extract.
+vba_filename = "vbaProject.bin"
+vba_signature_filename = "vbaProjectSignature.bin"
+
+# Get the xlsm file name from the commandline.
+if len(sys.argv) > 1:
+ xlsm_file = sys.argv[1]
+else:
+ print(
+ "\nUtility to extract a vbaProject.bin binary from an Excel 2007+ "
+ "xlsm macro file for insertion into an XlsxWriter file.\n"
+ "If the macros are digitally signed, extracts also a vbaProjectSignature.bin "
+ "file.\n"
+ "\n"
+ "See: https://xlsxwriter.readthedocs.io/working_with_macros.html\n"
+ "\n"
+ "Usage: vba_extract file.xlsm\n"
+ )
+ sys.exit()
+
+try:
+ # Open the Excel xlsm file as a zip file.
+ xlsm_zip = ZipFile(xlsm_file, "r")
+
+ # Read the xl/vbaProject.bin file.
+ extract_file(xlsm_zip, vba_filename)
+ print(f"Extracted: {vba_filename}")
+
+ if "xl/" + vba_signature_filename in xlsm_zip.namelist():
+ extract_file(xlsm_zip, vba_signature_filename)
+ print(f"Extracted: {vba_signature_filename}")
+
+
+except IOError as e:
+ print(f"File error: {str(e)}")
+ sys.exit()
+
+except KeyError as e:
+ # Usually when there isn't a xl/vbaProject.bin member in the file.
+ print(f"File error: {str(e)}")
+ print(f"File may not be an Excel xlsm macro file: '{xlsm_file}'")
+ sys.exit()
+
+except BadZipFile as e:
+ # Usually if the file is an xls file and not an xlsm file.
+ print(f"File error: {str(e)}: '{xlsm_file}'")
+ print("File may not be an Excel xlsm macro file.")
+ sys.exit()
+
+except Exception as e:
+ # Catch any other exceptions.
+ print(f"File error: {str(e)}")
+ sys.exit()
diff --git a/.venv312/Scripts/xmlschema-json2xml.exe b/.venv312/Scripts/xmlschema-json2xml.exe
new file mode 100644
index 0000000000..990f753f67
Binary files /dev/null and b/.venv312/Scripts/xmlschema-json2xml.exe differ
diff --git a/.venv312/Scripts/xmlschema-validate.exe b/.venv312/Scripts/xmlschema-validate.exe
new file mode 100644
index 0000000000..7eafcedf59
Binary files /dev/null and b/.venv312/Scripts/xmlschema-validate.exe differ
diff --git a/.venv312/Scripts/xmlschema-xml2json.exe b/.venv312/Scripts/xmlschema-xml2json.exe
new file mode 100644
index 0000000000..c9a71b5580
Binary files /dev/null and b/.venv312/Scripts/xmlschema-xml2json.exe differ
diff --git a/docs/images/tutorial-web-ui-messages-list.png b/docs/images/tutorial-web-ui-messages-list.png
new file mode 100644
index 0000000000..8e71ef69f9
Binary files /dev/null and b/docs/images/tutorial-web-ui-messages-list.png differ
diff --git a/docs/tutorial_web_ui_review_scan_results.rst b/docs/tutorial_web_ui_review_scan_results.rst
index 790401bb47..7a5ff25060 100644
--- a/docs/tutorial_web_ui_review_scan_results.rst
+++ b/docs/tutorial_web_ui_review_scan_results.rst
@@ -17,7 +17,7 @@ results using the ScanCode.io web interface.
On the homepage, you can click on the project name in the summary table to
access a detailed project output page. You can also click any of the numbers
-underneath the **Packages**, **Resources**, or **Errors** fields to be directed
+underneath the **Packages**, **Resources**, or **Messages** fields to be directed
to the field's corresponding information. Further, clicking on the pipeline's
execution status —**Success** in this case— expands some extra pipeline details,
Resources status, and Run log.
@@ -68,13 +68,23 @@ resources by **Programming Language**, **Mime Type**, **Holder**, **Copyright**,
.. image:: images/tutorial-web-ui-resources-filter.png
-Errors
-------
+Messages
+--------
In addition to discovered packages and codebase resources, the ScanCode.io
-homepage shows the number of existing errors, which you can click for detailed
-description of each error.
+homepage shows the number of project messages. These messages may include
+different severity levels such as **INFO**, **WARNING**, and **ERROR**.
+
+Clicking on the **Messages** count will display a detailed list of all
+project messages along with their severity and description.
+
+.. note::
+
+ In earlier versions of ScanCode.io, project issues were displayed under
+ an **Errors** section. In recent versions (v36.x and later), this has been
+ generalized into a **Messages** section that includes INFO, WARNING,
+ and ERROR severities.
-.. image:: images/tutorial-web-ui-errors-list.png
+.. image:: images/tutorial-web-ui-messages-list.png
Other Information
-----------------