Initial commit

This commit is contained in:
Holger Sielaff
2025-06-26 09:51:42 +02:00
commit 8bdcc52f65
76 changed files with 9488 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
/.idea
*.deb
*.swp
/build
__pycache__
*.pyc
venv
~*

14
README.md Normal file
View File

@@ -0,0 +1,14 @@
# URL Shortener
Just a small file-based url shortener.
Works with:
* NGINX
* LUA
* Flask
Since this is prepared to be a `deb` Package, `rsync src/ / -av` and @see `debscripts/*`
It requires `NGINX 1.24` and the needed artifacts for a correct run on Ubuntu 24.04 are in the repo (ok, a bit unusual)

6
apt.conf.sh Normal file
View File

@@ -0,0 +1,6 @@
PACKAGE_NAME=holger-url-shortener
INSTALLDIR=/
DEPENDENCIES="nginx,libnginx-mod-http-lua,python3-venv,python3-dev,gcc"
DESCRIPTION="A url shortener"
DEBHOST=
SUGGESTS=

30
bin/package-update Executable file
View File

@@ -0,0 +1,30 @@
#!/bin/bash
set -e
sudo apt-get install build-essential libpcre3 libpcre3-dev libssl-dev zlib1g-dev nginx-full
tmpdir=$(dirname $0)/../package-update.tmp
rm -rf $tmpdir
mkdir -p $tmpdir
cd $tmpdir
base=$(dirname $(pwd))
git clone https://github.com/openresty/luajit2
cd luajit2
sed -i 's!^ *export PREFIX=.*$!export PREFIX= '$base'/src/usr/local!' Makefile
make
make install
cd $base
rm -rf $tmpdir
git add src/
git commit -m 'Neues lua modul kompiliert'
git push

324
debian/changelog vendored Normal file
View File

@@ -0,0 +1,324 @@
brainson-nginx-url-shortener (1.24.0-2.0.7) stable; urgency=medium
* 1a28605 (HEAD -> main) ...
* f65bb21 ...
-- Holger Sielaff <holger.sielaff@brainson.de> Wed, 28 May 2025 08:30:08 +0200
brainson-nginx-url-shortener (1.24.0-2.0.6) stable; urgency=medium
* 57955d9 (HEAD -> main) ...
-- Holger Sielaff <holger.sielaff@brainson.de> Wed, 21 May 2025 13:46:30 +0200
brainson-nginx-url-shortener (1.24.0-2.0.5) stable; urgency=medium
* 503c9e8 (HEAD -> main) ...
-- Holger Sielaff <holger.sielaff@brainson.de> Wed, 21 May 2025 13:28:42 +0200
brainson-nginx-url-shortener (1.24.0-2.0.4) stable; urgency=medium
* 06fcb8b (HEAD -> main) ...
-- Holger Sielaff <holger.sielaff@brainson.de> Wed, 21 May 2025 13:12:49 +0200
brainson-nginx-url-shortener (1.24.0-2.0.3) stable; urgency=medium
* b522816 (HEAD -> main) Better error handling
-- Holger Sielaff <holger.sielaff@brainson.de> Wed, 21 May 2025 12:22:43 +0200
brainson-nginx-url-shortener (1.24.0-2.0.2) stable; urgency=medium
* c277326 (HEAD -> main) ...
-- Holger Sielaff <holger.sielaff@brainson.de> Tue, 20 May 2025 17:46:40 +0200
brainson-nginx-url-shortener (1.24.0-2.0.1) stable; urgency=medium
* ec875f2 (HEAD -> main) ...
-- Holger Sielaff <holger.sielaff@brainson.de> Tue, 20 May 2025 16:31:44 +0200
brainson-nginx-url-shortener (1.24.0-2.0.0) stable; urgency=medium
* 8aa7239 (HEAD -> main) ...
* 314ef8c ...
* f8854ee (renaming) Renaming
* 25459c1 Renaming
* 28962e0 ...
-- Holger Sielaff <holger.sielaff@brainson.de> Tue, 20 May 2025 11:02:19 +0200
brainson-nginx-url-shortener (1.24.0-1.1.11) stable; urgency=medium
* 3b9651b (HEAD -> main) moved conf
* 0c89aa6 moved conf
-- Holger Sielaff <holger.sielaff@brainson.de> Mon, 19 May 2025 16:52:28 +0200
brainson-nginx-url-shortener (1.24.0-1.1.10) stable; urgency=medium
* c2cae79 (HEAD -> main) no google
-- Holger Sielaff <holger.sielaff@brainson.de> Mon, 19 May 2025 16:49:28 +0200
brainson-nginx-url-shortener (1.24.0-1.1.9) stable; urgency=medium
* 0dcdf4e (HEAD -> main) ...
* 9bc48b6 Added deps
-- Holger Sielaff <holger.sielaff@brainson.de> Mon, 19 May 2025 15:50:02 +0200
brainson-nginx-url-shortener (1.24.0-1.1.8) stable; urgency=medium
* cdbaae8 (HEAD -> main) ...
-- Holger Sielaff <holger.sielaff@brainson.de> Mon, 19 May 2025 14:33:40 +0200
brainson-nginx-url-shortener (1.24.0-1.1.7) stable; urgency=medium
* 75ea89c (HEAD -> main) removed some files
-- Holger Sielaff <holger.sielaff@brainson.de> Mon, 19 May 2025 14:18:12 +0200
brainson-nginx-url-shortener (1.24.0-1.1.6) stable; urgency=medium
* a3ec9fc (HEAD -> main, origin/main, origin/HEAD) seo redirects
* 4abc31e maybe / hashes?
* b9434fb no "edit" hash
* bb016bb Merge remote-tracking branch 'origin/main'
* dd5e314 no raise on emtpy dir
-- Holger Sielaff <holger.sielaff@brainson.de> Mon, 19 May 2025 14:01:30 +0200
brainson-nginx-url-shortener (1.24.0-1.1.5) stable; urgency=medium
* f36f3aa (HEAD -> main) added conffiles
* 4bf0a8b added conffiles
* bb121b7 Merge branch 'main' of git.brainson.de:brainson/debian-packages/brainson-nginx-url-shortener
* b8046bd tmp
* f39b9b6 (origin/main) More styling and pagesize selector
* 7800fb7 More styling and pagesize selector
* 543e08f ...
-- Holger Sielaff <holger.sielaff@brainson.de> Mon, 19 May 2025 11:34:46 +0200
brainson-nginx-url-shortener (1.24.0-1.1.4) stable; urgency=medium
* 6b5f1fa (HEAD -> main) ...
-- Holger Sielaff <holger.sielaff@brainson.de> Sat, 17 May 2025 19:17:59 +0200
brainson-nginx-url-shortener (1.24.0-1.1.3) stable; urgency=medium
* 735b1fb (HEAD -> main) ...
-- Holger Sielaff <holger.sielaff@brainson.de> Sat, 17 May 2025 19:07:35 +0200
brainson-nginx-url-shortener (1.24.0-1.1.2) stable; urgency=medium
* ef5af6f (HEAD -> main) ...
-- Holger Sielaff <holger.sielaff@brainson.de> Sat, 17 May 2025 13:39:43 +0200
brainson-nginx-url-shortener (1.24.0-1.1.1) stable; urgency=medium
* b705fd8 (HEAD -> main) added filter >0
-- Holger Sielaff <holger.sielaff@brainson.de> Fri, 16 May 2025 20:40:19 +0200
brainson-nginx-url-shortener (1.24.0-1.1.0) stable; urgency=medium
* b3b3801 (HEAD -> main) added filter >0
-- Holger Sielaff <holger.sielaff@brainson.de> Fri, 16 May 2025 20:36:16 +0200
brainson-nginx-url-shortener (1.24.0-1.0.6) stable; urgency=medium
* 1713ded (HEAD -> main) ...
* d83d737 (origin/main) ...
-- Holger Sielaff <holger.sielaff@brainson.de> Fri, 16 May 2025 20:23:44 +0200
brainson-nginx-url-shortener (1.24.0-1.0.5) stable; urgency=medium
* 2e7ba45 (HEAD -> main) ...
-- Holger Sielaff <holger.sielaff@brainson.de> Thu, 15 May 2025 16:29:46 +0200
brainson-nginx-url-shortener (1.24.0-1.0.4) stable; urgency=medium
* 790ea3f (HEAD -> main) Added json API
-- Holger Sielaff <holger.sielaff@brainson.de> Thu, 15 May 2025 16:19:00 +0200
brainson-nginx-url-shortener (1.24.0-1.0.3) stable; urgency=medium
* 6cef7ba (HEAD -> main) changed building
* 5106640 Bugfix on remove logfile
-- Holger Sielaff <holger.sielaff@brainson.de> Thu, 15 May 2025 12:06:02 +0200
brainson-nginx-url-shortener (1.24.0-1.0.2) stable; urgency=medium
* 0530189 (HEAD -> main) Added conffile /etc/brainson/url-shortener-server.conf
-- Holger Sielaff <holger.sielaff@brainson.de> Thu, 15 May 2025 11:59:40 +0200
brainson-nginx-url-shortener (1.24.0-1.0.1) stable; urgency=medium
* 270f221 (HEAD -> main) Remove logfile too
-- Holger Sielaff <holger.sielaff@brainson.de> Thu, 15 May 2025 11:55:33 +0200
brainson-nginx-url-shortener (1.24.0-1.0.0) stable; urgency=medium
* b82c6f5 (HEAD -> main) Added counting
-- Holger Sielaff <holger.sielaff@brainson.de> Thu, 15 May 2025 11:36:49 +0200
brainson-nginx-url-shortener (1.24.0-0.2.1) stable; urgency=medium
* 587a57b (HEAD -> main) Styling
-- Holger Sielaff <holger.sielaff@brainson.de> Wed, 14 May 2025 20:16:45 +0200
brainson-nginx-url-shortener (1.24.0-0.2.0) stable; urgency=medium
* 543c9aa (HEAD -> main) Styling
-- Holger Sielaff <holger.sielaff@brainson.de> Wed, 14 May 2025 20:05:17 +0200
brainson-nginx-url-shortener (1.24.0-0.1.4) stable; urgency=medium
* 0c4c201 (HEAD -> main) Install on missing venv
-- Holger Sielaff <holger.sielaff@brainson.de> Wed, 14 May 2025 16:50:07 +0200
brainson-nginx-url-shortener (1.24.0-0.1.3) stable; urgency=medium
* 020b2d5 (HEAD -> main) Added params to app.py
-- Holger Sielaff <holger.sielaff@brainson.de> Wed, 14 May 2025 16:45:44 +0200
brainson-nginx-url-shortener (1.24.0-0.1.2) stable; urgency=medium
* 2cdc577 (HEAD -> main) More styling
-- Holger Sielaff <holger.sielaff@brainson.de> Wed, 14 May 2025 16:34:25 +0200
brainson-nginx-url-shortener (1.24.0-0.1.1) stable; urgency=medium
* 7a42101 (HEAD -> main) Added search
-- Holger Sielaff <holger.sielaff@brainson.de> Wed, 14 May 2025 16:16:09 +0200
brainson-nginx-url-shortener (1.24.0-0.1.0) stable; urgency=medium
* 60aa357 (HEAD -> main) Added Backend
* ff90aee updated buildscript submodule
* 0a1380e With location files now
-- Holger Sielaff <holger.sielaff@brainson.de> Wed, 14 May 2025 15:24:38 +0200
brainson-nginx-url-shortener (1.24.0-0.0.1) stable; urgency=medium
* initial
-- Holger Sielaff <holger.sielaff@brainson.de> Tue, 13 May 2025 17:53:12 +0200
Package created: Di 13. Mai 11:22:56 CEST 2025

9
debscripts/conffiles Normal file
View File

@@ -0,0 +1,9 @@
/usr/bin/url-shortener
/etc/nginx/conf.d/url-shortener-servers.conf
/etc/nginx/snippets/url-shortener-server-snippet.conf
/etc/systemd/system/url-shortener-gui.service
/etc/url-shortener/conf.sh
/etc/url-shortener/uwsgi.ini
/etc/cron.d/url-shortener-restart-nginx
/etc/cron.d/url-shortener-update
/etc/logrotate.d/url-shortener

39
debscripts/postinst Executable file
View File

@@ -0,0 +1,39 @@
#!/bin/bash
set -e
base=/var/cache/url-shortener
[[ -f /etc/url-shortener/conf.sh ]] && . /etc/url-shortener/conf.sh
[[ -z "$URL_SHORTENER_CACHE_BASE" ]] && base=$URL_SHORTENER_CACHE_BASE
mkdir -p /var/log/url-shortener
chmod 777 /var/log/url-shortener
# look for installed sites
if [[ -z "$(find $base -maxdepth 2 -type f -name 'server.conf')" ]]; then
read -p "$(echo -e "\e[0;31mInstall a Default Site? [Y,n]: \e[0m")" defgen
if [[ ! "$defgen" =~ n|N ]]; then
/usr/bin/url-shortener create-host default
fi
fi
installdir=/usr/share/url-shortener
py=./venv/bin/python3
pip="$py -mpip"
cd $installdir
if [[ ! -f $py ]]; then
rm -rf venv
python3 -mvenv venv
cd -
fi
if ! $pip freeze | grep -qi flask; then
$pip install -r requirements.txt
fi
cd -
systemctl daemon-reload
systemctl enable url-shortener-gui.service
systemctl restart url-shortener-gui.service || echo "Could not restart"
nginx -t && systemctl restart nginx
exit 0

2
debscripts/postrm Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
exit 0

2
debscripts/preinst Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
exit 0

4
debscripts/prerm Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/bash
systemctl stop url-shortener-gui.service
systemctl disable url-shortener-gui.service
exit 0

View File

@@ -0,0 +1,7 @@
rsync -av --exclude venv \
--exclude __pycyche__ \
--exclude '*.pyc' \
--exclude node_modules \
src/* \
"${INSTALL_DIR}"

View File

1
main-version.txt Normal file
View File

@@ -0,0 +1 @@
1.24.0

View File

@@ -0,0 +1 @@
*/15 * * * * root /usr/bin/url-shortener nginx-cron >> /var/log/url-shortener/cron.log 2>&1

View File

@@ -0,0 +1 @@
* * * * * root /usr/bin/url-shortener update-cron >> /var/log/syslog 2>&1

View File

@@ -0,0 +1 @@
*/15 * * * * root /usr/bin/urlshortener nginx-cron >> /var/log/urlshortener/cron.log 2>&1

View File

@@ -0,0 +1 @@
* * * * * root /usr/bin/urlshortener update-cron >> /var/log/syslog 2>&1

View File

@@ -0,0 +1,9 @@
/var/log/url-shortener/*.log {
weekly
missingok
rotate 14
compress
delaycompress
notifempty
create 0644 www-data adm
}

View File

@@ -0,0 +1,9 @@
/var/log/urlshortener/cron.log {
weekly
missingok
rotate 14
compress
delaycompress
notifempty
create 0644 www-data adm
}

View File

@@ -0,0 +1,34 @@
server {
server_name _;
listen 80;
# Domain and SSL Config
# -------------------------------------------------
# include snippets/brainson_minimal_ssl_snippet;
# ssl_certificate /var/lib/dehydrated/certs/{DOMAINNAME}/fullchain.pem;
# ssl_certificate_key /var/lib/dehydrated/certs/{DOMAINNAME}/privkey.pem;
# access_log /var/log/nginx/{DOMAINNAME}.access.log;
# error_log /var/log/nginx/{DOMAINNAME}.error.log;
# Shortener Config
# -------------------------------------------------
# set $shortener_data_dir "/var/cache/url-shortener"
# set $shortener_redirects_status 302;
# WARNING - the following shold NOT be done with more than 1000 Redirects
# This includes the files to not go to lua after a restart or reload
# There may exist deprecated ones, but anyway ?
#
# include "/var/cache/url-shortener/nginx/*.conf";
# The Hostname for the application
set $shortener_host "default";
# The default locations
include "snippets/urlshortener_server_snippet.conf";
}

View File

@@ -0,0 +1,2 @@
# Include all server {} files in the appropriate Domain Directories
include "/var/cache/url-shortener/*/server.conf";

View File

@@ -0,0 +1,14 @@
location = /edit {
include proxy_params;
# Security Config with ldap auth
# ---------------------------------------------
# include snippets/brainson_ldap_auth_snippet;
proxy_pass http://127.0.0.1:9000;
}
location / {
content_by_lua_file "/var/lib/nginx/lua/url-shortener.lua";
}
# this sends a 204 to bots
include snippets/brainson_nobot_location;

View File

@@ -0,0 +1,11 @@
location = /edit {
include proxy_params;
# Security Config with ldap auth
# ---------------------------------------------
# include snippets/brainson_ldap_auth_snippet;
proxy_pass http://127.0.0.1:9000;
}
location / {
content_by_lua_file "/var/lib/nginx/lua/urlshortener.lua";
}

View File

@@ -0,0 +1,14 @@
[Unit]
Description=GUI to edit shortener urls
After=network.target
[Service]
User=www-data
WorkingDirectory=/usr/share/url-shortener
# ExecStart=/usr/share/url-shortener/venv/bin/python3 /usr/share/url-shortener/app.py -H 0.0.0.0 -P 9000
ExecStart=/usr/share/url-shortener/venv/bin/uwsgi --ini /etc/url-shortener/uwsgi.ini
Restart=always
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1 @@
URL_SHORTENER_CACHE_BASE=/var/cache/url-shortener

View File

@@ -0,0 +1 @@
URL_SHORTENER_CACHE_BASE=/var/cache/url-shortener

View File

@@ -0,0 +1,10 @@
[uwsgi]
module = app:app
master = true
processes = 2
threads = 2
# socket = 0.0.0.0:9000
http = 0.0.0.0:9000
vacuum = true
die-on-term = true
buffer-size = 32768

83
src/usr/bin/url-shortener Executable file
View File

@@ -0,0 +1,83 @@
#!/bin/bash
. /etc/url-shortener/conf.sh
action=$1
shift
hostname=$1
shift
base=${URL_SHORTENER_CACHE_BASE}/$hostname
HASH=$1
set -e
case $action in
count-files)
cd $base
ls logs | wc -l
;;
get-visits)
cd $base
wc -l logs/$HASH | sed 's/ .*$//'
;;
list-visits)
cd $base/logs
find . -type f -size +0c -exec wc -l --total=never {} + | sort -r
;;
sort-cache)
cd $base
sort -r .cachefile > .cachefile.tmp
mv .cachefile.tmp .cachefile
chown www-data:www-data .cachefile
chmod 666 .cachefile
;;
update-cache)
cd $base
chown root:root .cachefile
chmod 600 .cachefile
tar czf .cachefile.tar.gz .cachefile
find logs -type f -size +0c -exec wc -l --total=never {} + | sed '/^ *0/d;s!^ *\([0-9]*\) \./\(.*\)!sed "s/^[0-9]* \2 /\1 \2 /q" .cachefile!' | bash
bash $0 sort-cache
;;
update-cron)
cd $base/logs
wc -l ${@} | sed 's/^[^0-9]*\([0-9]*\)[\t ]*\([^\t ]*\).*$/sed -i '"'"'s!^[0-9]* \2 !\1 \2 !'"'"' ../.cachefile;' | bash
cd $base/update.d
rm -f ${@}
;;
nginx-cron)
if [[ -f /var/cache/url-shortener/.restart_nginx ]]; then
nginx -t > /dev/null 2>&1 && systemctl reload nginx > /dev/null 2>&1 && rm -f /var/cache/url-shortener/.restart_nginx
fi
;;
unlink-location)
rm -v -f $base/locations.d/$HASH
;;
link-location)
[[ ! -e $base/locations.d/$HASH ]] && ln -v -s $base/nginx/$HASH $base/locations.d/
;;
create-host)
mkdir -v -p $base/{nginx,logs,locations.d};
chmod 777 $base/{nginx,logs,locations.d};
cfgfile=$base/server.conf
if [[ ! -f $cfgfile ]]; then
sed "s/{DOMAINNAME}/$hostname/g;s/server_name *default/server_name _/" \
/usr/share/url-shortener/url-shortener-server.conf.tmpl > $cfgfile
fi
touch $base/.cachefile
if [[ ! $(whoami) = "www-data" ]]; then
sudo chown -v -R www-data:www-data $base
fi
;;
*)
echo "$0 COMMAND"
echo "Where COMMAND can be"
echo " * count-files <HOSTNAME>"
echo " * get-visits <HOSTNAME> <HASH>"
echo " * list-visits <HOSTNAME>"
echo " * sort-cache <HOSTNAME>"
echo " * update-cache <HOSTNAME>"
echo " * update-cron <HOSTNAME>"
echo " * nginx-cron"
echo " * create-host <HOSTNAME>"
;;
esac

40
src/usr/bin/urlshortener Executable file
View File

@@ -0,0 +1,40 @@
#!/bin/bash
set -e
. /etc/url-shortener/url-shortener.conf.sh
action=$1
shift
base=${URL_SHORTENER_CACHE_BASE}/$1
shift
[[ ! -f $base/.cachefile ]] && echo Wrong dir $base && exit 1
cd $base
if [[ "$action" = "count-files" ]]; then
ls logs | wc -l
elif [[ "$action" = "get-visits" ]]; then
wc -l logs/$1 | sed 's/ .*$//'
elif [[ "$action" = "list-visits" ]]; then
cd logs
find . -type f -size +0c -exec wc -l --total=never {} + | sort -r
elif [[ "$action" = "sort-cache" ]]; then
sort -r .cachefile > .cachefile.tmp
mv .cachefile.tmp .cachefile
chown www-data:www-data .cachefile
chmod 666 .cachefile
elif [[ "$action" = "update-cache" ]]; then
chown root:root .cachefile
chmod 600 .cachefile
tar czf .cachefile.tar.gz .cachefile
find logs -type f -size +0c -exec wc -l --total=never {} + | sed '/^ *0/d;s!^ *\([0-9]*\) \./\(.*\)!sed "s/^[0-9]* \2 /\1 \2 /q" .cachefile!' | bash
bash $0 sort-cache
elif [[ "$action" = "update-cron" ]]; then
cd logs
wc -l ${@} | sed 's/^[^0-9]*\([0-9]*\)[\t ]*\([^\t ]*\).*$/sed -i '"'"'s!^[0-9]* \2 !\1 \2 !'"'"' ../.cachefile;' | bash
cd $base/update.d
rm -f ${@}
elif [[ "$action" = 'nginx-cron' ]]; then
if [[ -f /var/cache/url-shortener/.restart_nginx ]]; then
nginx -t && systemctl reload nginx && rm -f /var/cache/url-shortener/.restart_nginx
fi
else
echo "$0 <hostname> [<get-visits> HASH] [<list-visits>] [<update-cache>] [<count-files>] [<update-single> HASH]"
fi

1
src/usr/local/bin/luajit Symbolic link
View File

@@ -0,0 +1 @@
luajit-2.1.1744683895

Binary file not shown.

View File

@@ -0,0 +1,161 @@
/*
** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $
** Auxiliary functions for building Lua libraries
** See Copyright Notice in lua.h
*/
#ifndef lauxlib_h
#define lauxlib_h
#include <stddef.h>
#include <stdio.h>
#include "lua.h"
/* extra error code for `luaL_load' */
#define LUA_ERRFILE (LUA_ERRERR+1)
typedef struct luaL_Reg {
const char *name;
lua_CFunction func;
} luaL_Reg;
LUALIB_API void (luaL_openlib) (lua_State *L, const char *libname,
const luaL_Reg *l, int nup);
LUALIB_API void (luaL_register) (lua_State *L, const char *libname,
const luaL_Reg *l);
LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname);
LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg);
LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg,
size_t *l);
LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg,
const char *def, size_t *l);
LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg);
LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def);
LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg);
LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg,
lua_Integer def);
LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t);
LUALIB_API void (luaL_checkany) (lua_State *L, int narg);
LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
LUALIB_API void (luaL_where) (lua_State *L, int lvl);
LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def,
const char *const lst[]);
/* pre-defined references */
#define LUA_NOREF (-2)
#define LUA_REFNIL (-1)
LUALIB_API int (luaL_ref) (lua_State *L, int t);
LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename);
LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz,
const char *name);
LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
LUALIB_API lua_State *(luaL_newstate) (void);
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p,
const char *r);
LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx,
const char *fname, int szhint);
/* From Lua 5.2. */
LUALIB_API int luaL_fileresult(lua_State *L, int stat, const char *fname);
LUALIB_API int luaL_execresult(lua_State *L, int stat);
LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename,
const char *mode);
LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz,
const char *name, const char *mode);
LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg,
int level);
LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup);
LUALIB_API void (luaL_pushmodule) (lua_State *L, const char *modname,
int sizehint);
LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname);
LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname);
/*
** ===============================================================
** some useful macros
** ===============================================================
*/
#define luaL_argcheck(L, cond,numarg,extramsg) \
((void)((cond) || luaL_argerror(L, (numarg), (extramsg))))
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
#define luaL_dofile(L, fn) \
(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
#define luaL_dostring(L, s) \
(luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
/* From Lua 5.2. */
#define luaL_newlibtable(L, l) \
lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1)
#define luaL_newlib(L, l) (luaL_newlibtable(L, l), luaL_setfuncs(L, l, 0))
/*
** {======================================================
** Generic Buffer manipulation
** =======================================================
*/
typedef struct luaL_Buffer {
char *p; /* current position in buffer */
int lvl; /* number of strings in the stack (level) */
lua_State *L;
char buffer[LUAL_BUFFERSIZE];
} luaL_Buffer;
#define luaL_addchar(B,c) \
((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \
(*(B)->p++ = (char)(c)))
/* compatibility only */
#define luaL_putchar(B,c) luaL_addchar(B,c)
#define luaL_addsize(B,n) ((B)->p += (n))
LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B);
LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
/* }====================================================== */
#endif

View File

@@ -0,0 +1,410 @@
/*
** $Id: lua.h,v 1.218.1.5 2008/08/06 13:30:12 roberto Exp $
** Lua - An Extensible Extension Language
** Lua.org, PUC-Rio, Brazil (https://www.lua.org)
** See Copyright Notice at the end of this file
*/
#ifndef lua_h
#define lua_h
#include <stdarg.h>
#include <stddef.h>
#include "luaconf.h"
#define LUA_VERSION "Lua 5.1"
#define LUA_RELEASE "Lua 5.1.4"
#define LUA_VERSION_NUM 501
#define LUA_COPYRIGHT "Copyright (C) 1994-2008 Lua.org, PUC-Rio"
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes"
/* mark for precompiled code (`<esc>Lua') */
#define LUA_SIGNATURE "\033Lua"
/* option for multiple returns in `lua_pcall' and `lua_call' */
#define LUA_MULTRET (-1)
/*
** pseudo-indices
*/
#define LUA_REGISTRYINDEX (-10000)
#define LUA_ENVIRONINDEX (-10001)
#define LUA_GLOBALSINDEX (-10002)
#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i))
/* thread status */
#define LUA_OK 0
#define LUA_YIELD 1
#define LUA_ERRRUN 2
#define LUA_ERRSYNTAX 3
#define LUA_ERRMEM 4
#define LUA_ERRERR 5
typedef struct lua_State lua_State;
typedef int (*lua_CFunction) (lua_State *L);
/*
** functions that read/write blocks when loading/dumping Lua chunks
*/
typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);
typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud);
/*
** prototype for memory-allocation functions
*/
typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
/*
** basic types
*/
#define LUA_TNONE (-1)
#define LUA_TNIL 0
#define LUA_TBOOLEAN 1
#define LUA_TLIGHTUSERDATA 2
#define LUA_TNUMBER 3
#define LUA_TSTRING 4
#define LUA_TTABLE 5
#define LUA_TFUNCTION 6
#define LUA_TUSERDATA 7
#define LUA_TTHREAD 8
/* minimum Lua stack available to a C function */
#define LUA_MINSTACK 20
/*
** generic extra include file
*/
#if defined(LUA_USER_H)
#include LUA_USER_H
#endif
/* type of numbers in Lua */
typedef LUA_NUMBER lua_Number;
/* type for integer functions */
typedef LUA_INTEGER lua_Integer;
/*
** state manipulation
*/
LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
LUA_API void (lua_close) (lua_State *L);
LUA_API lua_State *(lua_newthread) (lua_State *L);
#define HAVE_LUA_RESETTHREAD 1
LUA_API void (lua_resetthread) (lua_State *L, lua_State *th);
LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
/*
** basic stack manipulation
*/
LUA_API int (lua_gettop) (lua_State *L);
LUA_API void (lua_settop) (lua_State *L, int idx);
LUA_API void (lua_pushvalue) (lua_State *L, int idx);
LUA_API void (lua_remove) (lua_State *L, int idx);
LUA_API void (lua_insert) (lua_State *L, int idx);
LUA_API void (lua_replace) (lua_State *L, int idx);
LUA_API int (lua_checkstack) (lua_State *L, int sz);
LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n);
/*
** access functions (stack -> C)
*/
LUA_API int (lua_isnumber) (lua_State *L, int idx);
LUA_API int (lua_isstring) (lua_State *L, int idx);
LUA_API int (lua_iscfunction) (lua_State *L, int idx);
LUA_API int (lua_isuserdata) (lua_State *L, int idx);
LUA_API int (lua_type) (lua_State *L, int idx);
LUA_API const char *(lua_typename) (lua_State *L, int tp);
LUA_API int (lua_equal) (lua_State *L, int idx1, int idx2);
LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2);
LUA_API int (lua_lessthan) (lua_State *L, int idx1, int idx2);
LUA_API lua_Number (lua_tonumber) (lua_State *L, int idx);
LUA_API lua_Integer (lua_tointeger) (lua_State *L, int idx);
LUA_API int (lua_toboolean) (lua_State *L, int idx);
LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len);
LUA_API size_t (lua_objlen) (lua_State *L, int idx);
LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx);
LUA_API void *(lua_touserdata) (lua_State *L, int idx);
LUA_API lua_State *(lua_tothread) (lua_State *L, int idx);
LUA_API const void *(lua_topointer) (lua_State *L, int idx);
/*
** push functions (C -> stack)
*/
LUA_API void (lua_pushnil) (lua_State *L);
LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n);
LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n);
LUA_API void (lua_pushlstring) (lua_State *L, const char *s, size_t l);
LUA_API void (lua_pushstring) (lua_State *L, const char *s);
LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,
va_list argp);
LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
LUA_API void (lua_pushboolean) (lua_State *L, int b);
LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p);
LUA_API int (lua_pushthread) (lua_State *L);
/*
** get functions (Lua -> stack)
*/
LUA_API void (lua_gettable) (lua_State *L, int idx);
LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k);
LUA_API void (lua_rawget) (lua_State *L, int idx);
LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n);
LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec);
LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);
LUA_API int (lua_getmetatable) (lua_State *L, int objindex);
LUA_API void (lua_getfenv) (lua_State *L, int idx);
/*
** set functions (stack -> Lua)
*/
LUA_API void (lua_settable) (lua_State *L, int idx);
LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k);
LUA_API void (lua_rawset) (lua_State *L, int idx);
LUA_API void (lua_rawseti) (lua_State *L, int idx, int n);
LUA_API int (lua_setmetatable) (lua_State *L, int objindex);
LUA_API int (lua_setfenv) (lua_State *L, int idx);
/*
** `load' and `call' functions (load and run Lua code)
*/
LUA_API void (lua_call) (lua_State *L, int nargs, int nresults);
LUA_API int (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc);
LUA_API int (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud);
LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt,
const char *chunkname);
LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data);
/*
** coroutine functions
*/
LUA_API int (lua_yield) (lua_State *L, int nresults);
LUA_API int (lua_resume) (lua_State *L, int narg);
LUA_API int (lua_status) (lua_State *L);
/*
** garbage-collection function and options
*/
#define LUA_GCSTOP 0
#define LUA_GCRESTART 1
#define LUA_GCCOLLECT 2
#define LUA_GCCOUNT 3
#define LUA_GCCOUNTB 4
#define LUA_GCSTEP 5
#define LUA_GCSETPAUSE 6
#define LUA_GCSETSTEPMUL 7
#define LUA_GCISRUNNING 9
LUA_API int (lua_gc) (lua_State *L, int what, int data);
/*
** miscellaneous functions
*/
LUA_API int (lua_error) (lua_State *L);
LUA_API int (lua_next) (lua_State *L, int idx);
LUA_API void (lua_concat) (lua_State *L, int n);
LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);
LUA_API void lua_setexdata(lua_State *L, void *exdata);
LUA_API void *lua_getexdata(lua_State *L);
#define HAVE_LUA_EXDATA2 1
LUA_API void lua_setexdata2(lua_State *L, void *exdata2);
LUA_API void *lua_getexdata2(lua_State *L);
/*
** ===============================================================
** some useful macros
** ===============================================================
*/
#define lua_pop(L,n) lua_settop(L, -(n)-1)
#define lua_newtable(L) lua_createtable(L, 0, 0)
#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))
#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0)
#define lua_strlen(L,i) lua_objlen(L, (i))
#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION)
#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE)
#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA)
#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL)
#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN)
#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD)
#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE)
#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0)
#define lua_pushliteral(L, s) \
lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1)
#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s))
#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s))
#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
/*
** compatibility macros and functions
*/
#define lua_open() luaL_newstate()
#define lua_getregistry(L) lua_pushvalue(L, LUA_REGISTRYINDEX)
#define lua_getgccount(L) lua_gc(L, LUA_GCCOUNT, 0)
#define lua_Chunkreader lua_Reader
#define lua_Chunkwriter lua_Writer
/* hack */
LUA_API void lua_setlevel (lua_State *from, lua_State *to);
/*
** {======================================================================
** Debug API
** =======================================================================
*/
/*
** Event codes
*/
#define LUA_HOOKCALL 0
#define LUA_HOOKRET 1
#define LUA_HOOKLINE 2
#define LUA_HOOKCOUNT 3
#define LUA_HOOKTAILRET 4
/*
** Event masks
*/
#define LUA_MASKCALL (1 << LUA_HOOKCALL)
#define LUA_MASKRET (1 << LUA_HOOKRET)
#define LUA_MASKLINE (1 << LUA_HOOKLINE)
#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT)
typedef struct lua_Debug lua_Debug; /* activation record */
/* Functions to be called by the debuger in specific events */
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar);
LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);
LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);
LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);
LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n);
LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n);
LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count);
LUA_API lua_Hook lua_gethook (lua_State *L);
LUA_API int lua_gethookmask (lua_State *L);
LUA_API int lua_gethookcount (lua_State *L);
/* From Lua 5.2. */
LUA_API void *lua_upvalueid (lua_State *L, int idx, int n);
LUA_API void lua_upvaluejoin (lua_State *L, int idx1, int n1, int idx2, int n2);
LUA_API int lua_loadx (lua_State *L, lua_Reader reader, void *dt,
const char *chunkname, const char *mode);
LUA_API const lua_Number *lua_version (lua_State *L);
LUA_API void lua_copy (lua_State *L, int fromidx, int toidx);
LUA_API lua_Number lua_tonumberx (lua_State *L, int idx, int *isnum);
LUA_API lua_Integer lua_tointegerx (lua_State *L, int idx, int *isnum);
/* From Lua 5.3. */
LUA_API int lua_isyieldable (lua_State *L);
struct lua_Debug {
int event;
const char *name; /* (n) */
const char *namewhat; /* (n) `global', `local', `field', `method' */
const char *what; /* (S) `Lua', `C', `main', `tail' */
const char *source; /* (S) */
int currentline; /* (l) */
int nups; /* (u) number of upvalues */
int linedefined; /* (S) */
int lastlinedefined; /* (S) */
char short_src[LUA_IDSIZE]; /* (S) */
/* private part */
int i_ci; /* active function */
};
/* }====================================================================== */
/******************************************************************************
* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
#endif

View File

@@ -0,0 +1,9 @@
// C++ wrapper for LuaJIT header files.
extern "C" {
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "luajit.h"
}

View File

@@ -0,0 +1,154 @@
/*
** Configuration header.
** Copyright (C) 2005-2025 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef luaconf_h
#define luaconf_h
#ifndef WINVER
#define WINVER 0x0501
#endif
#include <stddef.h>
/* Default path for loading Lua and C modules with require(). */
#if defined(_WIN32)
/*
** In Windows, any exclamation mark ('!') in the path is replaced by the
** path of the directory of the executable file of the current process.
*/
#define LUA_LDIR "!\\lua\\"
#define LUA_CDIR "!\\"
#define LUA_PATH_DEFAULT \
".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;"
#define LUA_CPATH_DEFAULT \
".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll"
#else
/*
** Note to distribution maintainers: do NOT patch the following lines!
** Please read ../doc/install.html#distro and pass PREFIX=/usr instead.
*/
#ifndef LUA_MULTILIB
#define LUA_MULTILIB "lib"
#endif
#ifndef LUA_LMULTILIB
#define LUA_LMULTILIB "lib"
#endif
#define LUA_LROOT "/usr/local"
#define LUA_LUADIR "/lua/5.1/"
#ifdef LUA_ROOT
#define LUA_JROOT LUA_ROOT
#define LUA_RLDIR LUA_ROOT "/share" LUA_LUADIR
#define LUA_RCDIR LUA_ROOT "/" LUA_MULTILIB LUA_LUADIR
#define LUA_RLPATH ";" LUA_RLDIR "?.lua;" LUA_RLDIR "?/init.lua"
#define LUA_RCPATH ";" LUA_RCDIR "?.so"
#else
#define LUA_JROOT LUA_LROOT
#define LUA_RLPATH
#define LUA_RCPATH
#endif
#ifndef LUA_LJDIR
#define LUA_LJDIR LUA_JROOT "/share/luajit-2.1"
#endif
#define LUA_JPATH ";" LUA_LJDIR "/?.lua"
#define LUA_LLDIR LUA_LROOT "/share" LUA_LUADIR
#define LUA_LCDIR LUA_LROOT "/" LUA_LMULTILIB LUA_LUADIR
#define LUA_LLPATH ";" LUA_LLDIR "?.lua;" LUA_LLDIR "?/init.lua"
#define LUA_LCPATH1 ";" LUA_LCDIR "?.so"
#define LUA_LCPATH2 ";" LUA_LCDIR "loadall.so"
#define LUA_PATH_DEFAULT "./?.lua" LUA_JPATH LUA_LLPATH LUA_RLPATH
#define LUA_CPATH_DEFAULT "./?.so" LUA_LCPATH1 LUA_RCPATH LUA_LCPATH2
#endif
/* Environment variable names for path overrides and initialization code. */
#define LUA_PATH "LUA_PATH"
#define LUA_CPATH "LUA_CPATH"
#define LUA_INIT "LUA_INIT"
/* Special file system characters. */
#if defined(_WIN32)
#define LUA_DIRSEP "\\"
#else
#define LUA_DIRSEP "/"
#endif
#define LUA_PATHSEP ";"
#define LUA_PATH_MARK "?"
#define LUA_EXECDIR "!"
#define LUA_IGMARK "-"
#define LUA_PATH_CONFIG \
LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n" \
LUA_EXECDIR "\n" LUA_IGMARK "\n"
/* Quoting in error messages. */
#define LUA_QL(x) "'" x "'"
#define LUA_QS LUA_QL("%s")
/* Various tunables. */
#define LUAI_MAXSTACK 65500 /* Max. # of stack slots for a thread (<64K). */
#define LUAI_MAXCSTACK 8000 /* Max. # of stack slots for a C func (<10K). */
#define LUAI_GCPAUSE 200 /* Pause GC until memory is at 200%. */
#define LUAI_GCMUL 200 /* Run GC at 200% of allocation speed. */
#define LUA_MAXCAPTURES 32 /* Max. pattern captures. */
/* Configuration for the frontend (the luajit executable). */
#if defined(luajit_c)
#define LUA_PROGNAME "luajit" /* Fallback frontend name. */
#define LUA_PROMPT "> " /* Interactive prompt. */
#define LUA_PROMPT2 ">> " /* Continuation prompt. */
#define LUA_MAXINPUT 512 /* Max. input line length. */
#endif
/* Note: changing the following defines breaks the Lua 5.1 ABI. */
#define LUA_INTEGER ptrdiff_t
#define LUA_IDSIZE 60 /* Size of lua_Debug.short_src. */
/*
** Size of lauxlib and io.* on-stack buffers. Weird workaround to avoid using
** unreasonable amounts of stack space, but still retain ABI compatibility.
** Blame Lua for depending on BUFSIZ in the ABI, blame **** for wrecking it.
*/
#define LUAL_BUFFERSIZE (BUFSIZ > 16384 ? 8192 : BUFSIZ)
/* The following defines are here only for compatibility with luaconf.h
** from the standard Lua distribution. They must not be changed for LuaJIT.
*/
#define LUA_NUMBER_DOUBLE
#define LUA_NUMBER double
#define LUAI_UACNUMBER double
#define LUA_NUMBER_SCAN "%lf"
#define LUA_NUMBER_FMT "%.14g"
#define lua_number2str(s, n) sprintf((s), LUA_NUMBER_FMT, (n))
#define LUAI_MAXNUMBER2STR 32
#define LUA_INTFRMLEN "l"
#define LUA_INTFRM_T long
/* Linkage of public API functions. */
#if defined(LUA_BUILD_AS_DLL)
#if defined(LUA_CORE) || defined(LUA_LIB)
#define LUA_API __declspec(dllexport)
#else
#define LUA_API __declspec(dllimport)
#endif
#else
#define LUA_API extern
#endif
#define LUALIB_API LUA_API
/* Compatibility support for assertions. */
#if defined(LUA_USE_ASSERT) || defined(LUA_USE_APICHECK)
#include <assert.h>
#endif
#ifdef LUA_USE_ASSERT
#define lua_assert(x) assert(x)
#endif
#ifdef LUA_USE_APICHECK
#define luai_apicheck(L, o) { (void)L; assert(o); }
#else
#define luai_apicheck(L, o) { (void)L; }
#endif
#endif

View File

@@ -0,0 +1,81 @@
/*
** LuaJIT -- a Just-In-Time Compiler for Lua. https://luajit.org/
**
** Copyright (C) 2005-2025 Mike Pall. All rights reserved.
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be
** included in all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**
** [ MIT license: https://www.opensource.org/licenses/mit-license.php ]
*/
#ifndef _LUAJIT_H
#define _LUAJIT_H
#include "lua.h"
#define OPENRESTY_LUAJIT
#define LUAJIT_VERSION "LuaJIT 2.1.1744683895"
#define LUAJIT_VERSION_NUM 20199 /* Deprecated. */
#define LUAJIT_VERSION_SYM luaJIT_version_2_1_1744683895
#define LUAJIT_COPYRIGHT "Copyright (C) 2005-2025 Mike Pall"
#define LUAJIT_URL "https://luajit.org/"
/* Modes for luaJIT_setmode. */
#define LUAJIT_MODE_MASK 0x00ff
enum {
LUAJIT_MODE_ENGINE, /* Set mode for whole JIT engine. */
LUAJIT_MODE_DEBUG, /* Set debug mode (idx = level). */
LUAJIT_MODE_FUNC, /* Change mode for a function. */
LUAJIT_MODE_ALLFUNC, /* Recurse into subroutine protos. */
LUAJIT_MODE_ALLSUBFUNC, /* Change only the subroutines. */
LUAJIT_MODE_TRACE, /* Flush a compiled trace. */
LUAJIT_MODE_WRAPCFUNC = 0x10, /* Set wrapper mode for C function calls. */
LUAJIT_MODE_MAX
};
/* Flags or'ed in to the mode. */
#define LUAJIT_MODE_OFF 0x0000 /* Turn feature off. */
#define LUAJIT_MODE_ON 0x0100 /* Turn feature on. */
#define LUAJIT_MODE_FLUSH 0x0200 /* Flush JIT-compiled code. */
/* LuaJIT public C API. */
/* Control the JIT engine. */
LUA_API int luaJIT_setmode(lua_State *L, int idx, int mode);
/* Low-overhead profiling API. */
typedef void (*luaJIT_profile_callback)(void *data, lua_State *L,
int samples, int vmstate);
LUA_API void luaJIT_profile_start(lua_State *L, const char *mode,
luaJIT_profile_callback cb, void *data);
LUA_API void luaJIT_profile_stop(lua_State *L);
LUA_API const char *luaJIT_profile_dumpstack(lua_State *L, const char *fmt,
int depth, size_t *len);
/* Enforce (dynamic) linker error for version mismatches. Call from main. */
LUA_API void LUAJIT_VERSION_SYM(void);
#endif

View File

@@ -0,0 +1,45 @@
/*
** Standard library header.
** Copyright (C) 2005-2025 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LUALIB_H
#define _LUALIB_H
#include "lua.h"
#define LUA_FILEHANDLE "FILE*"
#define LUA_COLIBNAME "coroutine"
#define LUA_MATHLIBNAME "math"
#define LUA_STRLIBNAME "string"
#define LUA_TABLIBNAME "table"
#define LUA_IOLIBNAME "io"
#define LUA_OSLIBNAME "os"
#define LUA_LOADLIBNAME "package"
#define LUA_DBLIBNAME "debug"
#define LUA_BITLIBNAME "bit"
#define LUA_JITLIBNAME "jit"
#define LUA_FFILIBNAME "ffi"
#define LUA_THRLIBNAME "thread"
LUALIB_API int luaopen_base(lua_State *L);
LUALIB_API int luaopen_math(lua_State *L);
LUALIB_API int luaopen_string(lua_State *L);
LUALIB_API int luaopen_table(lua_State *L);
LUALIB_API int luaopen_io(lua_State *L);
LUALIB_API int luaopen_os(lua_State *L);
LUALIB_API int luaopen_package(lua_State *L);
LUALIB_API int luaopen_debug(lua_State *L);
LUALIB_API int luaopen_bit(lua_State *L);
LUALIB_API int luaopen_jit(lua_State *L);
LUALIB_API int luaopen_ffi(lua_State *L);
LUALIB_API int luaopen_string_buffer(lua_State *L);
LUALIB_API void luaL_openlibs(lua_State *L);
#ifndef lua_assert
#define lua_assert(x) ((void)0)
#endif
#endif

Binary file not shown.

View File

@@ -0,0 +1 @@
libluajit-5.1.so.2.1.1744683895

View File

@@ -0,0 +1 @@
libluajit-5.1.so.2.1.1744683895

Binary file not shown.

View File

@@ -0,0 +1,25 @@
# Package information for LuaJIT to be used by pkg-config.
majver=2
minver=1
relver=1744683895
version=${majver}.${minver}.${relver}
abiver=5.1
prefix=/home/user/brainson-nginx-url-shortener/src/usr/local
multilib=lib
exec_prefix=${prefix}
libdir=${exec_prefix}/${multilib}
libname=luajit-${abiver}
includedir=${prefix}/include/luajit-${majver}.${minver}
INSTALL_LMOD=${prefix}/share/lua/${abiver}
INSTALL_CMOD=${prefix}/${multilib}/lua/${abiver}
Name: LuaJIT
Description: Just-in-time compiler for Lua
URL: https://luajit.org
Version: ${version}
Requires:
Libs: -L${libdir} -l${libname}
Libs.private: -Wl,-E -lm -ldl
Cflags: -I${includedir}

View File

@@ -0,0 +1,227 @@
----------------------------------------------------------------------------
-- LuaJIT bytecode listing module.
--
-- Copyright (C) 2005-2025 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
--
-- This module lists the bytecode of a Lua function. If it's loaded by -jbc
-- it hooks into the parser and lists all functions of a chunk as they
-- are parsed.
--
-- Example usage:
--
-- luajit -jbc -e 'local x=0; for i=1,1e6 do x=x+i end; print(x)'
-- luajit -jbc=- foo.lua
-- luajit -jbc=foo.list foo.lua
--
-- Default output is to stderr. To redirect the output to a file, pass a
-- filename as an argument (use '-' for stdout) or set the environment
-- variable LUAJIT_LISTFILE. The file is overwritten every time the module
-- is started.
--
-- This module can also be used programmatically:
--
-- local bc = require("jit.bc")
--
-- local function foo() print("hello") end
--
-- bc.dump(foo) --> -- BYTECODE -- [...]
-- print(bc.line(foo, 2)) --> 0002 KSTR 1 1 ; "hello"
--
-- local out = {
-- -- Do something with each line:
-- write = function(t, ...) io.write(...) end,
-- close = function(t) end,
-- flush = function(t) end,
-- }
-- bc.dump(foo, out)
--
------------------------------------------------------------------------------
-- Cache some library functions and objects.
local jit = require("jit")
local jutil = require("jit.util")
local vmdef = require("jit.vmdef")
local bit = require("bit")
local sub, gsub, format = string.sub, string.gsub, string.format
local byte, band, shr = string.byte, bit.band, bit.rshift
local funcinfo, funcbc, funck = jutil.funcinfo, jutil.funcbc, jutil.funck
local funcuvname = jutil.funcuvname
local bcnames = vmdef.bcnames
local stdout, stderr = io.stdout, io.stderr
------------------------------------------------------------------------------
local function ctlsub(c)
if c == "\n" then return "\\n"
elseif c == "\r" then return "\\r"
elseif c == "\t" then return "\\t"
else return format("\\%03d", byte(c))
end
end
-- Return one bytecode line.
local function bcline(func, pc, prefix, lineinfo)
local ins, m, l = funcbc(func, pc, lineinfo and 1 or 0)
if not ins then return end
local ma, mb, mc = band(m, 7), band(m, 15*8), band(m, 15*128)
local a = band(shr(ins, 8), 0xff)
local oidx = 6*band(ins, 0xff)
local op = sub(bcnames, oidx+1, oidx+6)
local s
if lineinfo then
s = format("%04d %7s %s %-6s %3s ",
pc, "["..l.."]", prefix or " ", op, ma == 0 and "" or a)
else
s = format("%04d %s %-6s %3s ",
pc, prefix or " ", op, ma == 0 and "" or a)
end
local d = shr(ins, 16)
if mc == 13*128 then -- BCMjump
return format("%s=> %04d\n", s, pc+d-0x7fff)
end
if mb ~= 0 then
d = band(d, 0xff)
elseif mc == 0 then
return s.."\n"
end
local kc
if mc == 10*128 then -- BCMstr
kc = funck(func, -d-1)
kc = format(#kc > 40 and '"%.40s"~' or '"%s"', gsub(kc, "%c", ctlsub))
elseif mc == 9*128 then -- BCMnum
kc = funck(func, d)
if op == "TSETM " then kc = kc - 2^52 end
elseif mc == 12*128 then -- BCMfunc
local fi = funcinfo(funck(func, -d-1))
if fi.ffid then
kc = vmdef.ffnames[fi.ffid]
else
kc = fi.loc
end
elseif mc == 5*128 then -- BCMuv
kc = funcuvname(func, d)
end
if ma == 5 then -- BCMuv
local ka = funcuvname(func, a)
if kc then kc = ka.." ; "..kc else kc = ka end
end
if mb ~= 0 then
local b = shr(ins, 24)
if kc then return format("%s%3d %3d ; %s\n", s, b, d, kc) end
return format("%s%3d %3d\n", s, b, d)
end
if kc then return format("%s%3d ; %s\n", s, d, kc) end
if mc == 7*128 and d > 32767 then d = d - 65536 end -- BCMlits
return format("%s%3d\n", s, d)
end
-- Collect branch targets of a function.
local function bctargets(func)
local target = {}
for pc=1,1000000000 do
local ins, m = funcbc(func, pc)
if not ins then break end
if band(m, 15*128) == 13*128 then target[pc+shr(ins, 16)-0x7fff] = true end
end
return target
end
-- Dump bytecode instructions of a function.
local function bcdump(func, out, all, lineinfo)
if not out then out = stdout end
local fi = funcinfo(func)
if all and fi.children then
for n=-1,-1000000000,-1 do
local k = funck(func, n)
if not k then break end
if type(k) == "proto" then bcdump(k, out, true, lineinfo) end
end
end
out:write(format("-- BYTECODE -- %s-%d\n", fi.loc, fi.lastlinedefined))
for n=-1,-1000000000,-1 do
local kc = funck(func, n)
if not kc then break end
local typ = type(kc)
if typ == "string" then
kc = format(#kc > 40 and '"%.40s"~' or '"%s"', gsub(kc, "%c", ctlsub))
out:write(format("KGC %d %s\n", -(n + 1), kc))
elseif typ == "proto" then
local fi = funcinfo(kc)
if fi.ffid then
kc = vmdef.ffnames[fi.ffid]
else
kc = fi.loc
end
out:write(format("KGC %d %s\n", -(n + 1), kc))
elseif typ == "table" then
out:write(format("KGC %d table\n", -(n + 1)))
else
-- error("unknown KGC type: " .. typ)
end
end
for n=1,1000000000 do
local kc = funck(func, n)
if not kc then break end
if type(kc) == "number" then
out:write(format("KN %d %s\n", n, kc))
end
end
local target = bctargets(func)
for pc=1,1000000000 do
local s = bcline(func, pc, target[pc] and "=>", lineinfo)
if not s then break end
out:write(s)
end
out:write("\n")
out:flush()
end
------------------------------------------------------------------------------
-- Active flag and output file handle.
local active, out
-- List handler.
local function h_list(func)
return bcdump(func, out)
end
-- Detach list handler.
local function bclistoff()
if active then
active = false
jit.attach(h_list)
if out and out ~= stdout and out ~= stderr then out:close() end
out = nil
end
end
-- Open the output file and attach list handler.
local function bcliston(outfile)
if active then bclistoff() end
if not outfile then outfile = os.getenv("LUAJIT_LISTFILE") end
if outfile then
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
else
out = stderr
end
jit.attach(h_list, "bc")
active = true
end
-- Public module functions.
return {
line = bcline,
dump = bcdump,
targets = bctargets,
on = bcliston,
off = bclistoff,
start = bcliston -- For -j command line option.
}

View File

@@ -0,0 +1,635 @@
----------------------------------------------------------------------------
-- LuaJIT module to save/list bytecode.
--
-- Copyright (C) 2005-2025 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
--
-- This module saves or lists the bytecode for an input file.
-- It's run by the -b command line option.
--
------------------------------------------------------------------------------
local jit = require("jit")
assert(jit.version_num == 20199, "LuaJIT core/library version mismatch")
local bit = require("bit")
-- Symbol name prefix for LuaJIT bytecode.
local LJBC_PREFIX = "luaJIT_BC_"
local type, assert = type, assert
local format = string.format
local tremove, tconcat = table.remove, table.concat
------------------------------------------------------------------------------
local function usage()
io.stderr:write[[
Save LuaJIT bytecode: luajit -b[options] input output
-l Only list bytecode.
-L Only list bytecode with lineinfo.
-s Strip debug info (default).
-g Keep debug info.
-W Generate 32 bit (non-GC64) bytecode.
-X Generate 64 bit (GC64) bytecode.
-d Generate bytecode in deterministic manner.
-n name Set module name (default: auto-detect from input name).
-t type Set output file type (default: auto-detect from output name).
-a arch Override architecture for object files (default: native).
-o os Override OS for object files (default: native).
-F name Override filename (default: input filename).
-e chunk Use chunk string as input.
-- Stop handling options.
- Use stdin as input and/or stdout as output.
File types: c cc h obj o raw (default)
]]
os.exit(1)
end
local function check(ok, ...)
if ok then return ok, ... end
io.stderr:write("luajit: ", ...)
io.stderr:write("\n")
os.exit(1)
end
local function readfile(ctx, input)
if ctx.string then
return check(loadstring(input, nil, ctx.mode))
elseif ctx.filename then
local data
if input == "-" then
data = io.stdin:read("*a")
else
local fp = assert(io.open(input, "rb"))
data = assert(fp:read("*a"))
assert(fp:close())
end
return check(load(data, ctx.filename, ctx.mode))
else
if input == "-" then input = nil end
return check(loadfile(input, ctx.mode))
end
end
local function savefile(name, mode)
if name == "-" then return io.stdout end
return check(io.open(name, mode))
end
local function set_stdout_binary(ffi)
ffi.cdef[[int _setmode(int fd, int mode);]]
ffi.C._setmode(1, 0x8000)
end
------------------------------------------------------------------------------
local map_type = {
raw = "raw", c = "c", cc = "c", h = "h", o = "obj", obj = "obj",
}
local map_arch = {
x86 = { e = "le", b = 32, m = 3, p = 0x14c, },
x64 = { e = "le", b = 64, m = 62, p = 0x8664, },
arm = { e = "le", b = 32, m = 40, p = 0x1c0, },
arm64 = { e = "le", b = 64, m = 183, p = 0xaa64, },
arm64be = { e = "be", b = 64, m = 183, },
ppc = { e = "be", b = 32, m = 20, },
mips = { e = "be", b = 32, m = 8, f = 0x50001006, },
mipsel = { e = "le", b = 32, m = 8, f = 0x50001006, },
mips64 = { e = "be", b = 64, m = 8, f = 0x80000007, },
mips64el = { e = "le", b = 64, m = 8, f = 0x80000007, },
mips64r6 = { e = "be", b = 64, m = 8, f = 0xa0000407, },
mips64r6el = { e = "le", b = 64, m = 8, f = 0xa0000407, },
s390x = { e = "be", b = 64, m = 22, },
}
local map_os = {
linux = true, windows = true, osx = true, freebsd = true, netbsd = true,
openbsd = true, dragonfly = true, solaris = true,
}
local function checkarg(str, map, err)
str = str:lower()
local s = check(map[str], "unknown ", err)
return type(s) == "string" and s or str
end
local function detecttype(str)
local ext = str:lower():match("%.(%a+)$")
return map_type[ext] or "raw"
end
local function checkmodname(str)
check(str:match("^[%w_.%-]+$"), "bad module name")
return str:gsub("[%.%-]", "_")
end
local function detectmodname(str)
if type(str) == "string" then
local tail = str:match("[^/\\]+$")
if tail then str = tail end
local head = str:match("^(.*)%.[^.]*$")
if head then str = head end
str = str:match("^[%w_.%-]+")
else
str = nil
end
check(str, "cannot derive module name, use -n name")
return str:gsub("[%.%-]", "_")
end
------------------------------------------------------------------------------
local function bcsave_tail(fp, output, s)
local ok, err = fp:write(s)
if ok and output ~= "-" then ok, err = fp:close() end
check(ok, "cannot write ", output, ": ", err)
end
local function bcsave_raw(output, s)
if output == "-" and jit.os == "Windows" then
local ok, ffi = pcall(require, "ffi")
check(ok, "FFI library required to write binary file to stdout")
set_stdout_binary(ffi)
end
local fp = savefile(output, "wb")
bcsave_tail(fp, output, s)
end
local function bcsave_c(ctx, output, s)
local fp = savefile(output, "w")
if ctx.type == "c" then
fp:write(format([[
#ifdef __cplusplus
extern "C"
#endif
#ifdef _WIN32
__declspec(dllexport)
#endif
const unsigned char %s%s[] = {
]], LJBC_PREFIX, ctx.modname))
else
fp:write(format([[
#define %s%s_SIZE %d
static const unsigned char %s%s[] = {
]], LJBC_PREFIX, ctx.modname, #s, LJBC_PREFIX, ctx.modname))
end
local t, n, m = {}, 0, 0
for i=1,#s do
local b = tostring(string.byte(s, i))
m = m + #b + 1
if m > 78 then
fp:write(tconcat(t, ",", 1, n), ",\n")
n, m = 0, #b + 1
end
n = n + 1
t[n] = b
end
bcsave_tail(fp, output, tconcat(t, ",", 1, n).."\n};\n")
end
local function bcsave_elfobj(ctx, output, s, ffi)
ffi.cdef[[
typedef struct {
uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];
uint16_t type, machine;
uint32_t version;
uint32_t entry, phofs, shofs;
uint32_t flags;
uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;
} ELF32header;
typedef struct {
uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];
uint16_t type, machine;
uint32_t version;
uint64_t entry, phofs, shofs;
uint32_t flags;
uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;
} ELF64header;
typedef struct {
uint32_t name, type, flags, addr, ofs, size, link, info, align, entsize;
} ELF32sectheader;
typedef struct {
uint32_t name, type;
uint64_t flags, addr, ofs, size;
uint32_t link, info;
uint64_t align, entsize;
} ELF64sectheader;
typedef struct {
uint32_t name, value, size;
uint8_t info, other;
uint16_t sectidx;
} ELF32symbol;
typedef struct {
uint32_t name;
uint8_t info, other;
uint16_t sectidx;
uint64_t value, size;
} ELF64symbol;
typedef struct {
ELF32header hdr;
ELF32sectheader sect[6];
ELF32symbol sym[2];
uint8_t space[4096];
} ELF32obj;
typedef struct {
ELF64header hdr;
ELF64sectheader sect[6];
ELF64symbol sym[2];
uint8_t space[4096];
} ELF64obj;
]]
local symname = LJBC_PREFIX..ctx.modname
local ai = assert(map_arch[ctx.arch])
local is64, isbe = ai.b == 64, ai.e == "be"
-- Handle different host/target endianess.
local function f32(x) return x end
local f16, fofs = f32, f32
if ffi.abi("be") ~= isbe then
f32 = bit.bswap
function f16(x) return bit.rshift(bit.bswap(x), 16) end
if is64 then
local two32 = ffi.cast("int64_t", 2^32)
function fofs(x) return bit.bswap(x)*two32 end
else
fofs = f32
end
end
-- Create ELF object and fill in header.
local o = ffi.new(is64 and "ELF64obj" or "ELF32obj")
local hdr = o.hdr
if ctx.os == "bsd" or ctx.os == "other" then -- Determine native hdr.eosabi.
local bf = assert(io.open("/bin/ls", "rb"))
local bs = bf:read(9)
bf:close()
ffi.copy(o, bs, 9)
check(hdr.emagic[0] == 127, "no support for writing native object files")
else
hdr.emagic = "\127ELF"
hdr.eosabi = ({ freebsd=9, netbsd=2, openbsd=12, solaris=6 })[ctx.os] or 0
end
hdr.eclass = is64 and 2 or 1
hdr.eendian = isbe and 2 or 1
hdr.eversion = 1
hdr.type = f16(1)
hdr.machine = f16(ai.m)
hdr.flags = f32(ai.f or 0)
hdr.version = f32(1)
hdr.shofs = fofs(ffi.offsetof(o, "sect"))
hdr.ehsize = f16(ffi.sizeof(hdr))
hdr.shentsize = f16(ffi.sizeof(o.sect[0]))
hdr.shnum = f16(6)
hdr.shstridx = f16(2)
-- Fill in sections and symbols.
local sofs, ofs = ffi.offsetof(o, "space"), 1
for i,name in ipairs{
".symtab", ".shstrtab", ".strtab", ".rodata", ".note.GNU-stack",
} do
local sect = o.sect[i]
sect.align = fofs(1)
sect.name = f32(ofs)
ffi.copy(o.space+ofs, name)
ofs = ofs + #name+1
end
o.sect[1].type = f32(2) -- .symtab
o.sect[1].link = f32(3)
o.sect[1].info = f32(1)
o.sect[1].align = fofs(8)
o.sect[1].ofs = fofs(ffi.offsetof(o, "sym"))
o.sect[1].entsize = fofs(ffi.sizeof(o.sym[0]))
o.sect[1].size = fofs(ffi.sizeof(o.sym))
o.sym[1].name = f32(1)
o.sym[1].sectidx = f16(4)
o.sym[1].size = fofs(#s)
o.sym[1].info = 17
o.sect[2].type = f32(3) -- .shstrtab
o.sect[2].ofs = fofs(sofs)
o.sect[2].size = fofs(ofs)
o.sect[3].type = f32(3) -- .strtab
o.sect[3].ofs = fofs(sofs + ofs)
o.sect[3].size = fofs(#symname+2)
ffi.copy(o.space+ofs+1, symname)
ofs = ofs + #symname + 2
o.sect[4].type = f32(1) -- .rodata
o.sect[4].flags = fofs(2)
o.sect[4].ofs = fofs(sofs + ofs)
o.sect[4].size = fofs(#s)
o.sect[5].type = f32(1) -- .note.GNU-stack
o.sect[5].ofs = fofs(sofs + ofs + #s)
-- Write ELF object file.
local fp = savefile(output, "wb")
fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs))
bcsave_tail(fp, output, s)
end
local function bcsave_peobj(ctx, output, s, ffi)
ffi.cdef[[
typedef struct {
uint16_t arch, nsects;
uint32_t time, symtabofs, nsyms;
uint16_t opthdrsz, flags;
} PEheader;
typedef struct {
char name[8];
uint32_t vsize, vaddr, size, ofs, relocofs, lineofs;
uint16_t nreloc, nline;
uint32_t flags;
} PEsection;
typedef struct __attribute((packed)) {
union {
char name[8];
uint32_t nameref[2];
};
uint32_t value;
int16_t sect;
uint16_t type;
uint8_t scl, naux;
} PEsym;
typedef struct __attribute((packed)) {
uint32_t size;
uint16_t nreloc, nline;
uint32_t cksum;
uint16_t assoc;
uint8_t comdatsel, unused[3];
} PEsymaux;
typedef struct {
PEheader hdr;
PEsection sect[2];
// Must be an even number of symbol structs.
PEsym sym0;
PEsymaux sym0aux;
PEsym sym1;
PEsymaux sym1aux;
PEsym sym2;
PEsym sym3;
uint32_t strtabsize;
uint8_t space[4096];
} PEobj;
]]
local symname = LJBC_PREFIX..ctx.modname
local ai = assert(map_arch[ctx.arch])
local is64 = ai.b == 64
local symexport = " /EXPORT:"..symname..",DATA "
-- The file format is always little-endian. Swap if the host is big-endian.
local function f32(x) return x end
local f16 = f32
if ffi.abi("be") then
f32 = bit.bswap
function f16(x) return bit.rshift(bit.bswap(x), 16) end
end
-- Create PE object and fill in header.
local o = ffi.new("PEobj")
local hdr = o.hdr
hdr.arch = f16(assert(ai.p))
hdr.nsects = f16(2)
hdr.symtabofs = f32(ffi.offsetof(o, "sym0"))
hdr.nsyms = f32(6)
-- Fill in sections and symbols.
o.sect[0].name = ".drectve"
o.sect[0].size = f32(#symexport)
o.sect[0].flags = f32(0x00100a00)
o.sym0.sect = f16(1)
o.sym0.scl = 3
o.sym0.name = ".drectve"
o.sym0.naux = 1
o.sym0aux.size = f32(#symexport)
o.sect[1].name = ".rdata"
o.sect[1].size = f32(#s)
o.sect[1].flags = f32(0x40300040)
o.sym1.sect = f16(2)
o.sym1.scl = 3
o.sym1.name = ".rdata"
o.sym1.naux = 1
o.sym1aux.size = f32(#s)
o.sym2.sect = f16(2)
o.sym2.scl = 2
o.sym2.nameref[1] = f32(4)
o.sym3.sect = f16(-1)
o.sym3.scl = 2
o.sym3.value = f32(1)
o.sym3.name = "@feat.00" -- Mark as SafeSEH compliant.
ffi.copy(o.space, symname)
local ofs = #symname + 1
o.strtabsize = f32(ofs + 4)
o.sect[0].ofs = f32(ffi.offsetof(o, "space") + ofs)
ffi.copy(o.space + ofs, symexport)
ofs = ofs + #symexport
o.sect[1].ofs = f32(ffi.offsetof(o, "space") + ofs)
-- Write PE object file.
local fp = savefile(output, "wb")
fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs))
bcsave_tail(fp, output, s)
end
local function bcsave_machobj(ctx, output, s, ffi)
ffi.cdef[[
typedef struct
{
uint32_t magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags;
} mach_header;
typedef struct
{
mach_header; uint32_t reserved;
} mach_header_64;
typedef struct {
uint32_t cmd, cmdsize;
char segname[16];
uint64_t vmaddr, vmsize, fileoff, filesize;
uint32_t maxprot, initprot, nsects, flags;
} mach_segment_command_64;
typedef struct {
char sectname[16], segname[16];
uint64_t addr, size;
uint32_t offset, align, reloff, nreloc, flags;
uint32_t reserved1, reserved2, reserved3;
} mach_section_64;
typedef struct {
uint32_t cmd, cmdsize, symoff, nsyms, stroff, strsize;
} mach_symtab_command;
typedef struct {
int32_t strx;
uint8_t type, sect;
uint16_t desc;
uint64_t value;
} mach_nlist_64;
typedef struct {
mach_header_64 hdr;
mach_segment_command_64 seg;
mach_section_64 sec;
mach_symtab_command sym;
mach_nlist_64 sym_entry;
uint8_t space[4096];
} mach_obj_64;
]]
local symname = '_'..LJBC_PREFIX..ctx.modname
local cputype, cpusubtype = 0x01000007, 3
if ctx.arch ~= "x64" then
check(ctx.arch == "arm64", "unsupported architecture for OSX")
cputype, cpusubtype = 0x0100000c, 0
end
local function aligned(v, a) return bit.band(v+a-1, -a) end
-- Create Mach-O object and fill in header.
local o = ffi.new("mach_obj_64")
local mach_size = aligned(ffi.offsetof(o, "space")+#symname+2, 8)
-- Fill in sections and symbols.
o.hdr.magic = 0xfeedfacf
o.hdr.cputype = cputype
o.hdr.cpusubtype = cpusubtype
o.hdr.filetype = 1
o.hdr.ncmds = 2
o.hdr.sizeofcmds = ffi.sizeof(o.seg)+ffi.sizeof(o.sec)+ffi.sizeof(o.sym)
o.seg.cmd = 0x19
o.seg.cmdsize = ffi.sizeof(o.seg)+ffi.sizeof(o.sec)
o.seg.vmsize = #s
o.seg.fileoff = mach_size
o.seg.filesize = #s
o.seg.maxprot = 1
o.seg.initprot = 1
o.seg.nsects = 1
ffi.copy(o.sec.sectname, "__data")
ffi.copy(o.sec.segname, "__DATA")
o.sec.size = #s
o.sec.offset = mach_size
o.sym.cmd = 2
o.sym.cmdsize = ffi.sizeof(o.sym)
o.sym.symoff = ffi.offsetof(o, "sym_entry")
o.sym.nsyms = 1
o.sym.stroff = ffi.offsetof(o, "sym_entry")+ffi.sizeof(o.sym_entry)
o.sym.strsize = aligned(#symname+2, 8)
o.sym_entry.type = 0xf
o.sym_entry.sect = 1
o.sym_entry.strx = 1
ffi.copy(o.space+1, symname)
-- Write Mach-O object file.
local fp = savefile(output, "wb")
fp:write(ffi.string(o, mach_size))
bcsave_tail(fp, output, s)
end
local function bcsave_obj(ctx, output, s)
local ok, ffi = pcall(require, "ffi")
check(ok, "FFI library required to write this file type")
if output == "-" and jit.os == "Windows" then
set_stdout_binary(ffi)
end
if ctx.os == "windows" then
return bcsave_peobj(ctx, output, s, ffi)
elseif ctx.os == "osx" then
return bcsave_machobj(ctx, output, s, ffi)
else
return bcsave_elfobj(ctx, output, s, ffi)
end
end
------------------------------------------------------------------------------
local function bclist(ctx, input, output, lineinfo)
local f = readfile(ctx, input)
require("jit.bc").dump(f, savefile(output, "w"), true, lineinfo)
end
local function bcsave(ctx, input, output)
local f = readfile(ctx, input)
local s = string.dump(f, ctx.mode)
local t = ctx.type
if not t then
t = detecttype(output)
ctx.type = t
end
if t == "raw" then
bcsave_raw(output, s)
else
if not ctx.modname then ctx.modname = detectmodname(input) end
if t == "obj" then
bcsave_obj(ctx, output, s)
else
bcsave_c(ctx, output, s)
end
end
end
local function docmd(...)
local arg = {...}
local n = 1
local list = false
local lineinfo = false
local ctx = {
mode = "bt", arch = jit.arch, os = jit.os:lower(),
type = false, modname = false, string = false,
}
local strip = "s"
local gc64 = ""
while n <= #arg do
local a = arg[n]
if type(a) == "string" and a:sub(1, 1) == "-" and a ~= "-" then
tremove(arg, n)
if a == "--" then break end
for m=2,#a do
local opt = a:sub(m, m)
if opt == "l" then
list = true
elseif opt == "L" then
list = true
lineinfo = true
elseif opt == "s" then
strip = "s"
elseif opt == "g" then
strip = ""
elseif opt == "W" or opt == "X" then
gc64 = opt
elseif opt == "d" then
ctx.mode = ctx.mode .. opt
else
if arg[n] == nil or m ~= #a then usage() end
if opt == "e" then
if n ~= 1 then usage() end
ctx.string = true
elseif opt == "n" then
ctx.modname = checkmodname(tremove(arg, n))
elseif opt == "t" then
ctx.type = checkarg(tremove(arg, n), map_type, "file type")
elseif opt == "a" then
ctx.arch = checkarg(tremove(arg, n), map_arch, "architecture")
elseif opt == "o" then
ctx.os = checkarg(tremove(arg, n), map_os, "OS name")
elseif opt == "F" then
ctx.filename = "@"..tremove(arg, n)
else
usage()
end
end
end
else
n = n + 1
end
end
ctx.mode = ctx.mode .. strip .. gc64
if list then
if #arg == 0 or #arg > 2 then usage() end
bclist(ctx, arg[1], arg[2] or "-", lineinfo)
else
if #arg ~= 2 then usage() end
bcsave(ctx, arg[1], arg[2])
end
end
------------------------------------------------------------------------------
-- Public module functions.
return {
start = docmd -- Process -b command line option.
}

View File

@@ -0,0 +1,689 @@
----------------------------------------------------------------------------
-- LuaJIT ARM disassembler module.
--
-- Copyright (C) 2005-2025 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This is a helper module used by the LuaJIT machine code dumper module.
--
-- It disassembles most user-mode ARMv7 instructions
-- NYI: Advanced SIMD and VFP instructions.
------------------------------------------------------------------------------
local type = type
local sub, byte, format = string.sub, string.byte, string.format
local match, gmatch = string.match, string.gmatch
local concat = table.concat
local bit = require("bit")
local band, bor, ror, tohex = bit.band, bit.bor, bit.ror, bit.tohex
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
------------------------------------------------------------------------------
-- Opcode maps
------------------------------------------------------------------------------
local map_loadc = {
shift = 8, mask = 15,
[10] = {
shift = 20, mask = 1,
[0] = {
shift = 23, mask = 3,
[0] = "vmovFmDN", "vstmFNdr",
_ = {
shift = 21, mask = 1,
[0] = "vstrFdl",
{ shift = 16, mask = 15, [13] = "vpushFdr", _ = "vstmdbFNdr", }
},
},
{
shift = 23, mask = 3,
[0] = "vmovFDNm",
{ shift = 16, mask = 15, [13] = "vpopFdr", _ = "vldmFNdr", },
_ = {
shift = 21, mask = 1,
[0] = "vldrFdl", "vldmdbFNdr",
},
},
},
[11] = {
shift = 20, mask = 1,
[0] = {
shift = 23, mask = 3,
[0] = "vmovGmDN", "vstmGNdr",
_ = {
shift = 21, mask = 1,
[0] = "vstrGdl",
{ shift = 16, mask = 15, [13] = "vpushGdr", _ = "vstmdbGNdr", }
},
},
{
shift = 23, mask = 3,
[0] = "vmovGDNm",
{ shift = 16, mask = 15, [13] = "vpopGdr", _ = "vldmGNdr", },
_ = {
shift = 21, mask = 1,
[0] = "vldrGdl", "vldmdbGNdr",
},
},
},
_ = {
shift = 0, mask = 0 -- NYI ldc, mcrr, mrrc.
},
}
local map_vfps = {
shift = 6, mask = 0x2c001,
[0] = "vmlaF.dnm", "vmlsF.dnm",
[0x04000] = "vnmlsF.dnm", [0x04001] = "vnmlaF.dnm",
[0x08000] = "vmulF.dnm", [0x08001] = "vnmulF.dnm",
[0x0c000] = "vaddF.dnm", [0x0c001] = "vsubF.dnm",
[0x20000] = "vdivF.dnm",
[0x24000] = "vfnmsF.dnm", [0x24001] = "vfnmaF.dnm",
[0x28000] = "vfmaF.dnm", [0x28001] = "vfmsF.dnm",
[0x2c000] = "vmovF.dY",
[0x2c001] = {
shift = 7, mask = 0x1e01,
[0] = "vmovF.dm", "vabsF.dm",
[0x0200] = "vnegF.dm", [0x0201] = "vsqrtF.dm",
[0x0800] = "vcmpF.dm", [0x0801] = "vcmpeF.dm",
[0x0a00] = "vcmpzF.d", [0x0a01] = "vcmpzeF.d",
[0x0e01] = "vcvtG.dF.m",
[0x1000] = "vcvt.f32.u32Fdm", [0x1001] = "vcvt.f32.s32Fdm",
[0x1800] = "vcvtr.u32F.dm", [0x1801] = "vcvt.u32F.dm",
[0x1a00] = "vcvtr.s32F.dm", [0x1a01] = "vcvt.s32F.dm",
},
}
local map_vfpd = {
shift = 6, mask = 0x2c001,
[0] = "vmlaG.dnm", "vmlsG.dnm",
[0x04000] = "vnmlsG.dnm", [0x04001] = "vnmlaG.dnm",
[0x08000] = "vmulG.dnm", [0x08001] = "vnmulG.dnm",
[0x0c000] = "vaddG.dnm", [0x0c001] = "vsubG.dnm",
[0x20000] = "vdivG.dnm",
[0x24000] = "vfnmsG.dnm", [0x24001] = "vfnmaG.dnm",
[0x28000] = "vfmaG.dnm", [0x28001] = "vfmsG.dnm",
[0x2c000] = "vmovG.dY",
[0x2c001] = {
shift = 7, mask = 0x1e01,
[0] = "vmovG.dm", "vabsG.dm",
[0x0200] = "vnegG.dm", [0x0201] = "vsqrtG.dm",
[0x0800] = "vcmpG.dm", [0x0801] = "vcmpeG.dm",
[0x0a00] = "vcmpzG.d", [0x0a01] = "vcmpzeG.d",
[0x0e01] = "vcvtF.dG.m",
[0x1000] = "vcvt.f64.u32GdFm", [0x1001] = "vcvt.f64.s32GdFm",
[0x1800] = "vcvtr.u32FdG.m", [0x1801] = "vcvt.u32FdG.m",
[0x1a00] = "vcvtr.s32FdG.m", [0x1a01] = "vcvt.s32FdG.m",
},
}
local map_datac = {
shift = 24, mask = 1,
[0] = {
shift = 4, mask = 1,
[0] = {
shift = 8, mask = 15,
[10] = map_vfps,
[11] = map_vfpd,
-- NYI cdp, mcr, mrc.
},
{
shift = 8, mask = 15,
[10] = {
shift = 20, mask = 15,
[0] = "vmovFnD", "vmovFDn",
[14] = "vmsrD",
[15] = { shift = 12, mask = 15, [15] = "vmrs", _ = "vmrsD", },
},
},
},
"svcT",
}
local map_loadcu = {
shift = 0, mask = 0, -- NYI unconditional CP load/store.
}
local map_datacu = {
shift = 0, mask = 0, -- NYI unconditional CP data.
}
local map_simddata = {
shift = 0, mask = 0, -- NYI SIMD data.
}
local map_simdload = {
shift = 0, mask = 0, -- NYI SIMD load/store, preload.
}
local map_preload = {
shift = 0, mask = 0, -- NYI preload.
}
local map_media = {
shift = 20, mask = 31,
[0] = false,
{ --01
shift = 5, mask = 7,
[0] = "sadd16DNM", "sasxDNM", "ssaxDNM", "ssub16DNM",
"sadd8DNM", false, false, "ssub8DNM",
},
{ --02
shift = 5, mask = 7,
[0] = "qadd16DNM", "qasxDNM", "qsaxDNM", "qsub16DNM",
"qadd8DNM", false, false, "qsub8DNM",
},
{ --03
shift = 5, mask = 7,
[0] = "shadd16DNM", "shasxDNM", "shsaxDNM", "shsub16DNM",
"shadd8DNM", false, false, "shsub8DNM",
},
false,
{ --05
shift = 5, mask = 7,
[0] = "uadd16DNM", "uasxDNM", "usaxDNM", "usub16DNM",
"uadd8DNM", false, false, "usub8DNM",
},
{ --06
shift = 5, mask = 7,
[0] = "uqadd16DNM", "uqasxDNM", "uqsaxDNM", "uqsub16DNM",
"uqadd8DNM", false, false, "uqsub8DNM",
},
{ --07
shift = 5, mask = 7,
[0] = "uhadd16DNM", "uhasxDNM", "uhsaxDNM", "uhsub16DNM",
"uhadd8DNM", false, false, "uhsub8DNM",
},
{ --08
shift = 5, mask = 7,
[0] = "pkhbtDNMU", false, "pkhtbDNMU",
{ shift = 16, mask = 15, [15] = "sxtb16DMU", _ = "sxtab16DNMU", },
"pkhbtDNMU", "selDNM", "pkhtbDNMU",
},
false,
{ --0a
shift = 5, mask = 7,
[0] = "ssatDxMu", "ssat16DxM", "ssatDxMu",
{ shift = 16, mask = 15, [15] = "sxtbDMU", _ = "sxtabDNMU", },
"ssatDxMu", false, "ssatDxMu",
},
{ --0b
shift = 5, mask = 7,
[0] = "ssatDxMu", "revDM", "ssatDxMu",
{ shift = 16, mask = 15, [15] = "sxthDMU", _ = "sxtahDNMU", },
"ssatDxMu", "rev16DM", "ssatDxMu",
},
{ --0c
shift = 5, mask = 7,
[3] = { shift = 16, mask = 15, [15] = "uxtb16DMU", _ = "uxtab16DNMU", },
},
false,
{ --0e
shift = 5, mask = 7,
[0] = "usatDwMu", "usat16DwM", "usatDwMu",
{ shift = 16, mask = 15, [15] = "uxtbDMU", _ = "uxtabDNMU", },
"usatDwMu", false, "usatDwMu",
},
{ --0f
shift = 5, mask = 7,
[0] = "usatDwMu", "rbitDM", "usatDwMu",
{ shift = 16, mask = 15, [15] = "uxthDMU", _ = "uxtahDNMU", },
"usatDwMu", "revshDM", "usatDwMu",
},
{ --10
shift = 12, mask = 15,
[15] = {
shift = 5, mask = 7,
"smuadNMS", "smuadxNMS", "smusdNMS", "smusdxNMS",
},
_ = {
shift = 5, mask = 7,
[0] = "smladNMSD", "smladxNMSD", "smlsdNMSD", "smlsdxNMSD",
},
},
false, false, false,
{ --14
shift = 5, mask = 7,
[0] = "smlaldDNMS", "smlaldxDNMS", "smlsldDNMS", "smlsldxDNMS",
},
{ --15
shift = 5, mask = 7,
[0] = { shift = 12, mask = 15, [15] = "smmulNMS", _ = "smmlaNMSD", },
{ shift = 12, mask = 15, [15] = "smmulrNMS", _ = "smmlarNMSD", },
false, false, false, false,
"smmlsNMSD", "smmlsrNMSD",
},
false, false,
{ --18
shift = 5, mask = 7,
[0] = { shift = 12, mask = 15, [15] = "usad8NMS", _ = "usada8NMSD", },
},
false,
{ --1a
shift = 5, mask = 3, [2] = "sbfxDMvw",
},
{ --1b
shift = 5, mask = 3, [2] = "sbfxDMvw",
},
{ --1c
shift = 5, mask = 3,
[0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", },
},
{ --1d
shift = 5, mask = 3,
[0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", },
},
{ --1e
shift = 5, mask = 3, [2] = "ubfxDMvw",
},
{ --1f
shift = 5, mask = 3, [2] = "ubfxDMvw",
},
}
local map_load = {
shift = 21, mask = 9,
{
shift = 20, mask = 5,
[0] = "strtDL", "ldrtDL", [4] = "strbtDL", [5] = "ldrbtDL",
},
_ = {
shift = 20, mask = 5,
[0] = "strDL", "ldrDL", [4] = "strbDL", [5] = "ldrbDL",
}
}
local map_load1 = {
shift = 4, mask = 1,
[0] = map_load, map_media,
}
local map_loadm = {
shift = 20, mask = 1,
[0] = {
shift = 23, mask = 3,
[0] = "stmdaNR", "stmNR",
{ shift = 16, mask = 63, [45] = "pushR", _ = "stmdbNR", }, "stmibNR",
},
{
shift = 23, mask = 3,
[0] = "ldmdaNR", { shift = 16, mask = 63, [61] = "popR", _ = "ldmNR", },
"ldmdbNR", "ldmibNR",
},
}
local map_data = {
shift = 21, mask = 15,
[0] = "andDNPs", "eorDNPs", "subDNPs", "rsbDNPs",
"addDNPs", "adcDNPs", "sbcDNPs", "rscDNPs",
"tstNP", "teqNP", "cmpNP", "cmnNP",
"orrDNPs", "movDPs", "bicDNPs", "mvnDPs",
}
local map_mul = {
shift = 21, mask = 7,
[0] = "mulNMSs", "mlaNMSDs", "umaalDNMS", "mlsDNMS",
"umullDNMSs", "umlalDNMSs", "smullDNMSs", "smlalDNMSs",
}
local map_sync = {
shift = 20, mask = 15, -- NYI: brackets around N. R(D+1) for ldrexd/strexd.
[0] = "swpDMN", false, false, false,
"swpbDMN", false, false, false,
"strexDMN", "ldrexDN", "strexdDN", "ldrexdDN",
"strexbDMN", "ldrexbDN", "strexhDN", "ldrexhDN",
}
local map_mulh = {
shift = 21, mask = 3,
[0] = { shift = 5, mask = 3,
[0] = "smlabbNMSD", "smlatbNMSD", "smlabtNMSD", "smlattNMSD", },
{ shift = 5, mask = 3,
[0] = "smlawbNMSD", "smulwbNMS", "smlawtNMSD", "smulwtNMS", },
{ shift = 5, mask = 3,
[0] = "smlalbbDNMS", "smlaltbDNMS", "smlalbtDNMS", "smlalttDNMS", },
{ shift = 5, mask = 3,
[0] = "smulbbNMS", "smultbNMS", "smulbtNMS", "smulttNMS", },
}
local map_misc = {
shift = 4, mask = 7,
-- NYI: decode PSR bits of msr.
[0] = { shift = 21, mask = 1, [0] = "mrsD", "msrM", },
{ shift = 21, mask = 3, "bxM", false, "clzDM", },
{ shift = 21, mask = 3, "bxjM", },
{ shift = 21, mask = 3, "blxM", },
false,
{ shift = 21, mask = 3, [0] = "qaddDMN", "qsubDMN", "qdaddDMN", "qdsubDMN", },
false,
{ shift = 21, mask = 3, "bkptK", },
}
local map_datar = {
shift = 4, mask = 9,
[9] = {
shift = 5, mask = 3,
[0] = { shift = 24, mask = 1, [0] = map_mul, map_sync, },
{ shift = 20, mask = 1, [0] = "strhDL", "ldrhDL", },
{ shift = 20, mask = 1, [0] = "ldrdDL", "ldrsbDL", },
{ shift = 20, mask = 1, [0] = "strdDL", "ldrshDL", },
},
_ = {
shift = 20, mask = 25,
[16] = { shift = 7, mask = 1, [0] = map_misc, map_mulh, },
_ = {
shift = 0, mask = 0xffffffff,
[bor(0xe1a00000)] = "nop",
_ = map_data,
}
},
}
local map_datai = {
shift = 20, mask = 31, -- NYI: decode PSR bits of msr. Decode imm12.
[16] = "movwDW", [20] = "movtDW",
[18] = { shift = 0, mask = 0xf00ff, [0] = "nopv6", _ = "msrNW", },
[22] = "msrNW",
_ = map_data,
}
local map_branch = {
shift = 24, mask = 1,
[0] = "bB", "blB"
}
local map_condins = {
[0] = map_datar, map_datai, map_load, map_load1,
map_loadm, map_branch, map_loadc, map_datac
}
-- NYI: setend.
local map_uncondins = {
[0] = false, map_simddata, map_simdload, map_preload,
false, "blxB", map_loadcu, map_datacu,
}
------------------------------------------------------------------------------
local map_gpr = {
[0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
}
local map_cond = {
[0] = "eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc",
"hi", "ls", "ge", "lt", "gt", "le", "al",
}
local map_shift = { [0] = "lsl", "lsr", "asr", "ror", }
------------------------------------------------------------------------------
-- Output a nicely formatted line with an opcode and operands.
local function putop(ctx, text, operands)
local pos = ctx.pos
local extra = ""
if ctx.rel then
local sym = ctx.symtab[ctx.rel]
if sym then
extra = "\t->"..sym
elseif band(ctx.op, 0x0e000000) ~= 0x0a000000 then
extra = "\t; 0x"..tohex(ctx.rel)
end
end
if ctx.hexdump > 0 then
ctx.out(format("%08x %s %-5s %s%s\n",
ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
else
ctx.out(format("%08x %-5s %s%s\n",
ctx.addr+pos, text, concat(operands, ", "), extra))
end
ctx.pos = pos + 4
end
-- Fallback for unknown opcodes.
local function unknown(ctx)
return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
end
-- Format operand 2 of load/store opcodes.
local function fmtload(ctx, op, pos)
local base = map_gpr[band(rshift(op, 16), 15)]
local x, ofs
local ext = (band(op, 0x04000000) == 0)
if not ext and band(op, 0x02000000) == 0 then
ofs = band(op, 4095)
if band(op, 0x00800000) == 0 then ofs = -ofs end
if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
ofs = "#"..ofs
elseif ext and band(op, 0x00400000) ~= 0 then
ofs = band(op, 15) + band(rshift(op, 4), 0xf0)
if band(op, 0x00800000) == 0 then ofs = -ofs end
if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
ofs = "#"..ofs
else
ofs = map_gpr[band(op, 15)]
if ext or band(op, 0xfe0) == 0 then
elseif band(op, 0xfe0) == 0x60 then
ofs = format("%s, rrx", ofs)
else
local sh = band(rshift(op, 7), 31)
if sh == 0 then sh = 32 end
ofs = format("%s, %s #%d", ofs, map_shift[band(rshift(op, 5), 3)], sh)
end
if band(op, 0x00800000) == 0 then ofs = "-"..ofs end
end
if ofs == "#0" then
x = format("[%s]", base)
elseif band(op, 0x01000000) == 0 then
x = format("[%s], %s", base, ofs)
else
x = format("[%s, %s]", base, ofs)
end
if band(op, 0x01200000) == 0x01200000 then x = x.."!" end
return x
end
-- Format operand 2 of vector load/store opcodes.
local function fmtvload(ctx, op, pos)
local base = map_gpr[band(rshift(op, 16), 15)]
local ofs = band(op, 255)*4
if band(op, 0x00800000) == 0 then ofs = -ofs end
if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
if ofs == 0 then
return format("[%s]", base)
else
return format("[%s, #%d]", base, ofs)
end
end
local function fmtvr(op, vr, sh0, sh1)
if vr == "s" then
return format("s%d", 2*band(rshift(op, sh0), 15)+band(rshift(op, sh1), 1))
else
return format("d%d", band(rshift(op, sh0), 15)+band(rshift(op, sh1-4), 16))
end
end
-- Disassemble a single instruction.
local function disass_ins(ctx)
local pos = ctx.pos
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
local op = bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0)
local operands = {}
local suffix = ""
local last, name, pat
local vr
ctx.op = op
ctx.rel = nil
local cond = rshift(op, 28)
local opat
if cond == 15 then
opat = map_uncondins[band(rshift(op, 25), 7)]
else
if cond ~= 14 then suffix = map_cond[cond] end
opat = map_condins[band(rshift(op, 25), 7)]
end
while type(opat) ~= "string" do
if not opat then return unknown(ctx) end
opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._
end
name, pat = match(opat, "^([a-z0-9]*)(.*)")
if sub(pat, 1, 1) == "." then
local s2, p2 = match(pat, "^([a-z0-9.]*)(.*)")
suffix = suffix..s2
pat = p2
end
for p in gmatch(pat, ".") do
local x = nil
if p == "D" then
x = map_gpr[band(rshift(op, 12), 15)]
elseif p == "N" then
x = map_gpr[band(rshift(op, 16), 15)]
elseif p == "S" then
x = map_gpr[band(rshift(op, 8), 15)]
elseif p == "M" then
x = map_gpr[band(op, 15)]
elseif p == "d" then
x = fmtvr(op, vr, 12, 22)
elseif p == "n" then
x = fmtvr(op, vr, 16, 7)
elseif p == "m" then
x = fmtvr(op, vr, 0, 5)
elseif p == "P" then
if band(op, 0x02000000) ~= 0 then
x = ror(band(op, 255), 2*band(rshift(op, 8), 15))
else
x = map_gpr[band(op, 15)]
if band(op, 0xff0) ~= 0 then
operands[#operands+1] = x
local s = map_shift[band(rshift(op, 5), 3)]
local r = nil
if band(op, 0xf90) == 0 then
if s == "ror" then s = "rrx" else r = "#32" end
elseif band(op, 0x10) == 0 then
r = "#"..band(rshift(op, 7), 31)
else
r = map_gpr[band(rshift(op, 8), 15)]
end
if name == "mov" then name = s; x = r
elseif r then x = format("%s %s", s, r)
else x = s end
end
end
elseif p == "L" then
x = fmtload(ctx, op, pos)
elseif p == "l" then
x = fmtvload(ctx, op, pos)
elseif p == "B" then
local addr = ctx.addr + pos + 8 + arshift(lshift(op, 8), 6)
if cond == 15 then addr = addr + band(rshift(op, 23), 2) end
ctx.rel = addr
x = "0x"..tohex(addr)
elseif p == "F" then
vr = "s"
elseif p == "G" then
vr = "d"
elseif p == "." then
suffix = suffix..(vr == "s" and ".f32" or ".f64")
elseif p == "R" then
if band(op, 0x00200000) ~= 0 and #operands == 1 then
operands[1] = operands[1].."!"
end
local t = {}
for i=0,15 do
if band(rshift(op, i), 1) == 1 then t[#t+1] = map_gpr[i] end
end
x = "{"..concat(t, ", ").."}"
elseif p == "r" then
if band(op, 0x00200000) ~= 0 and #operands == 2 then
operands[1] = operands[1].."!"
end
local s = tonumber(sub(last, 2))
local n = band(op, 255)
if vr == "d" then n = rshift(n, 1) end
operands[#operands] = format("{%s-%s%d}", last, vr, s+n-1)
elseif p == "W" then
x = band(op, 0x0fff) + band(rshift(op, 4), 0xf000)
elseif p == "T" then
x = "#0x"..tohex(band(op, 0x00ffffff), 6)
elseif p == "U" then
x = band(rshift(op, 7), 31)
if x == 0 then x = nil end
elseif p == "u" then
x = band(rshift(op, 7), 31)
if band(op, 0x40) == 0 then
if x == 0 then x = nil else x = "lsl #"..x end
else
if x == 0 then x = "asr #32" else x = "asr #"..x end
end
elseif p == "v" then
x = band(rshift(op, 7), 31)
elseif p == "w" then
x = band(rshift(op, 16), 31)
elseif p == "x" then
x = band(rshift(op, 16), 31) + 1
elseif p == "X" then
x = band(rshift(op, 16), 31) - last + 1
elseif p == "Y" then
x = band(rshift(op, 12), 0xf0) + band(op, 0x0f)
elseif p == "K" then
x = "#0x"..tohex(band(rshift(op, 4), 0x0000fff0) + band(op, 15), 4)
elseif p == "s" then
if band(op, 0x00100000) ~= 0 then suffix = "s"..suffix end
else
assert(false)
end
if x then
last = x
if type(x) == "number" then x = "#"..x end
operands[#operands+1] = x
end
end
return putop(ctx, name..suffix, operands)
end
------------------------------------------------------------------------------
-- Disassemble a block of code.
local function disass_block(ctx, ofs, len)
if not ofs then ofs = 0 end
local stop = len and ofs+len or #ctx.code
ctx.pos = ofs
ctx.rel = nil
while ctx.pos < stop do disass_ins(ctx) end
end
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
local function create(code, addr, out)
local ctx = {}
ctx.code = code
ctx.addr = addr or 0
ctx.out = out or io.write
ctx.symtab = {}
ctx.disass = disass_block
ctx.hexdump = 8
return ctx
end
-- Simple API: disassemble code (a string) at address and output via out.
local function disass(code, addr, out)
create(code, addr, out):disass()
end
-- Return register name for RID.
local function regname(r)
if r < 16 then return map_gpr[r] end
return "d"..(r-16)
end
-- Public module functions.
return {
create = create,
disass = disass,
regname = regname
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
----------------------------------------------------------------------------
-- LuaJIT ARM64BE disassembler wrapper module.
--
-- Copyright (C) 2005-2025 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- ARM64 instructions are always little-endian. So just forward to the
-- common ARM64 disassembler module. All the interesting stuff is there.
------------------------------------------------------------------------------
return require((string.match(..., ".*%.") or "").."dis_arm64")

View File

@@ -0,0 +1,694 @@
----------------------------------------------------------------------------
-- LuaJIT MIPS disassembler module.
--
-- Copyright (C) 2005-2025 Mike Pall. All rights reserved.
-- Released under the MIT/X license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This is a helper module used by the LuaJIT machine code dumper module.
--
-- It disassembles all standard MIPS32R1/R2 instructions.
-- Default mode is big-endian, but see: dis_mipsel.lua
------------------------------------------------------------------------------
local type = type
local byte, format = string.byte, string.format
local match, gmatch = string.match, string.gmatch
local concat = table.concat
local bit = require("bit")
local band, bor, tohex = bit.band, bit.bor, bit.tohex
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
------------------------------------------------------------------------------
-- Extended opcode maps common to all MIPS releases
------------------------------------------------------------------------------
local map_srl = { shift = 21, mask = 1, [0] = "srlDTA", "rotrDTA", }
local map_srlv = { shift = 6, mask = 1, [0] = "srlvDTS", "rotrvDTS", }
local map_cop0 = {
shift = 25, mask = 1,
[0] = {
shift = 21, mask = 15,
[0] = "mfc0TDW", [4] = "mtc0TDW",
[10] = "rdpgprDT",
[11] = { shift = 5, mask = 1, [0] = "diT0", "eiT0", },
[14] = "wrpgprDT",
}, {
shift = 0, mask = 63,
[1] = "tlbr", [2] = "tlbwi", [6] = "tlbwr", [8] = "tlbp",
[24] = "eret", [31] = "deret",
[32] = "wait",
},
}
------------------------------------------------------------------------------
-- Primary and extended opcode maps for MIPS R1-R5
------------------------------------------------------------------------------
local map_movci = { shift = 16, mask = 1, [0] = "movfDSC", "movtDSC", }
local map_special = {
shift = 0, mask = 63,
[0] = { shift = 0, mask = -1, [0] = "nop", _ = "sllDTA" },
map_movci, map_srl, "sraDTA",
"sllvDTS", false, map_srlv, "sravDTS",
"jrS", "jalrD1S", "movzDST", "movnDST",
"syscallY", "breakY", false, "sync",
"mfhiD", "mthiS", "mfloD", "mtloS",
"dsllvDST", false, "dsrlvDST", "dsravDST",
"multST", "multuST", "divST", "divuST",
"dmultST", "dmultuST", "ddivST", "ddivuST",
"addDST", "addu|moveDST0", "subDST", "subu|neguDS0T",
"andDST", "or|moveDST0", "xorDST", "nor|notDST0",
false, false, "sltDST", "sltuDST",
"daddDST", "dadduDST", "dsubDST", "dsubuDST",
"tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ",
"teqSTZ", false, "tneSTZ", false,
"dsllDTA", false, "dsrlDTA", "dsraDTA",
"dsll32DTA", false, "dsrl32DTA", "dsra32DTA",
}
local map_special2 = {
shift = 0, mask = 63,
[0] = "maddST", "madduST", "mulDST", false,
"msubST", "msubuST",
[32] = "clzDS", [33] = "cloDS",
[63] = "sdbbpY",
}
local map_bshfl = {
shift = 6, mask = 31,
[2] = "wsbhDT",
[16] = "sebDT",
[24] = "sehDT",
}
local map_dbshfl = {
shift = 6, mask = 31,
[2] = "dsbhDT",
[5] = "dshdDT",
}
local map_special3 = {
shift = 0, mask = 63,
[0] = "extTSAK", [1] = "dextmTSAP", [3] = "dextTSAK",
[4] = "insTSAL", [6] = "dinsuTSEQ", [7] = "dinsTSAL",
[32] = map_bshfl, [36] = map_dbshfl, [59] = "rdhwrTD",
}
local map_regimm = {
shift = 16, mask = 31,
[0] = "bltzSB", "bgezSB", "bltzlSB", "bgezlSB",
false, false, false, false,
"tgeiSI", "tgeiuSI", "tltiSI", "tltiuSI",
"teqiSI", false, "tneiSI", false,
"bltzalSB", "bgezalSB", "bltzallSB", "bgezallSB",
false, false, false, false,
false, false, false, false,
false, false, false, "synciSO",
}
local map_cop1s = {
shift = 0, mask = 63,
[0] = "add.sFGH", "sub.sFGH", "mul.sFGH", "div.sFGH",
"sqrt.sFG", "abs.sFG", "mov.sFG", "neg.sFG",
"round.l.sFG", "trunc.l.sFG", "ceil.l.sFG", "floor.l.sFG",
"round.w.sFG", "trunc.w.sFG", "ceil.w.sFG", "floor.w.sFG",
false,
{ shift = 16, mask = 1, [0] = "movf.sFGC", "movt.sFGC" },
"movz.sFGT", "movn.sFGT",
false, "recip.sFG", "rsqrt.sFG", false,
false, false, false, false,
false, false, false, false,
false, "cvt.d.sFG", false, false,
"cvt.w.sFG", "cvt.l.sFG", "cvt.ps.sFGH", false,
false, false, false, false,
false, false, false, false,
"c.f.sVGH", "c.un.sVGH", "c.eq.sVGH", "c.ueq.sVGH",
"c.olt.sVGH", "c.ult.sVGH", "c.ole.sVGH", "c.ule.sVGH",
"c.sf.sVGH", "c.ngle.sVGH", "c.seq.sVGH", "c.ngl.sVGH",
"c.lt.sVGH", "c.nge.sVGH", "c.le.sVGH", "c.ngt.sVGH",
}
local map_cop1d = {
shift = 0, mask = 63,
[0] = "add.dFGH", "sub.dFGH", "mul.dFGH", "div.dFGH",
"sqrt.dFG", "abs.dFG", "mov.dFG", "neg.dFG",
"round.l.dFG", "trunc.l.dFG", "ceil.l.dFG", "floor.l.dFG",
"round.w.dFG", "trunc.w.dFG", "ceil.w.dFG", "floor.w.dFG",
false,
{ shift = 16, mask = 1, [0] = "movf.dFGC", "movt.dFGC" },
"movz.dFGT", "movn.dFGT",
false, "recip.dFG", "rsqrt.dFG", false,
false, false, false, false,
false, false, false, false,
"cvt.s.dFG", false, false, false,
"cvt.w.dFG", "cvt.l.dFG", false, false,
false, false, false, false,
false, false, false, false,
"c.f.dVGH", "c.un.dVGH", "c.eq.dVGH", "c.ueq.dVGH",
"c.olt.dVGH", "c.ult.dVGH", "c.ole.dVGH", "c.ule.dVGH",
"c.df.dVGH", "c.ngle.dVGH", "c.deq.dVGH", "c.ngl.dVGH",
"c.lt.dVGH", "c.nge.dVGH", "c.le.dVGH", "c.ngt.dVGH",
}
local map_cop1ps = {
shift = 0, mask = 63,
[0] = "add.psFGH", "sub.psFGH", "mul.psFGH", false,
false, "abs.psFG", "mov.psFG", "neg.psFG",
false, false, false, false,
false, false, false, false,
false,
{ shift = 16, mask = 1, [0] = "movf.psFGC", "movt.psFGC" },
"movz.psFGT", "movn.psFGT",
false, false, false, false,
false, false, false, false,
false, false, false, false,
"cvt.s.puFG", false, false, false,
false, false, false, false,
"cvt.s.plFG", false, false, false,
"pll.psFGH", "plu.psFGH", "pul.psFGH", "puu.psFGH",
"c.f.psVGH", "c.un.psVGH", "c.eq.psVGH", "c.ueq.psVGH",
"c.olt.psVGH", "c.ult.psVGH", "c.ole.psVGH", "c.ule.psVGH",
"c.psf.psVGH", "c.ngle.psVGH", "c.pseq.psVGH", "c.ngl.psVGH",
"c.lt.psVGH", "c.nge.psVGH", "c.le.psVGH", "c.ngt.psVGH",
}
local map_cop1w = {
shift = 0, mask = 63,
[32] = "cvt.s.wFG", [33] = "cvt.d.wFG",
}
local map_cop1l = {
shift = 0, mask = 63,
[32] = "cvt.s.lFG", [33] = "cvt.d.lFG",
}
local map_cop1bc = {
shift = 16, mask = 3,
[0] = "bc1fCB", "bc1tCB", "bc1flCB", "bc1tlCB",
}
local map_cop1 = {
shift = 21, mask = 31,
[0] = "mfc1TG", "dmfc1TG", "cfc1TG", "mfhc1TG",
"mtc1TG", "dmtc1TG", "ctc1TG", "mthc1TG",
map_cop1bc, false, false, false,
false, false, false, false,
map_cop1s, map_cop1d, false, false,
map_cop1w, map_cop1l, map_cop1ps,
}
local map_cop1x = {
shift = 0, mask = 63,
[0] = "lwxc1FSX", "ldxc1FSX", false, false,
false, "luxc1FSX", false, false,
"swxc1FSX", "sdxc1FSX", false, false,
false, "suxc1FSX", false, "prefxMSX",
false, false, false, false,
false, false, false, false,
false, false, false, false,
false, false, "alnv.psFGHS", false,
"madd.sFRGH", "madd.dFRGH", false, false,
false, false, "madd.psFRGH", false,
"msub.sFRGH", "msub.dFRGH", false, false,
false, false, "msub.psFRGH", false,
"nmadd.sFRGH", "nmadd.dFRGH", false, false,
false, false, "nmadd.psFRGH", false,
"nmsub.sFRGH", "nmsub.dFRGH", false, false,
false, false, "nmsub.psFRGH", false,
}
local map_pri = {
[0] = map_special, map_regimm, "jJ", "jalJ",
"beq|beqz|bST00B", "bne|bnezST0B", "blezSB", "bgtzSB",
"addiTSI", "addiu|liTS0I", "sltiTSI", "sltiuTSI",
"andiTSU", "ori|liTS0U", "xoriTSU", "luiTU",
map_cop0, map_cop1, false, map_cop1x,
"beql|beqzlST0B", "bnel|bnezlST0B", "blezlSB", "bgtzlSB",
"daddiTSI", "daddiuTSI", false, false,
map_special2, "jalxJ", false, map_special3,
"lbTSO", "lhTSO", "lwlTSO", "lwTSO",
"lbuTSO", "lhuTSO", "lwrTSO", false,
"sbTSO", "shTSO", "swlTSO", "swTSO",
false, false, "swrTSO", "cacheNSO",
"llTSO", "lwc1HSO", "lwc2TSO", "prefNSO",
false, "ldc1HSO", "ldc2TSO", "ldTSO",
"scTSO", "swc1HSO", "swc2TSO", false,
false, "sdc1HSO", "sdc2TSO", "sdTSO",
}
------------------------------------------------------------------------------
-- Primary and extended opcode maps for MIPS R6
------------------------------------------------------------------------------
local map_mul_r6 = { shift = 6, mask = 3, [2] = "mulDST", [3] = "muhDST" }
local map_mulu_r6 = { shift = 6, mask = 3, [2] = "muluDST", [3] = "muhuDST" }
local map_div_r6 = { shift = 6, mask = 3, [2] = "divDST", [3] = "modDST" }
local map_divu_r6 = { shift = 6, mask = 3, [2] = "divuDST", [3] = "moduDST" }
local map_dmul_r6 = { shift = 6, mask = 3, [2] = "dmulDST", [3] = "dmuhDST" }
local map_dmulu_r6 = { shift = 6, mask = 3, [2] = "dmuluDST", [3] = "dmuhuDST" }
local map_ddiv_r6 = { shift = 6, mask = 3, [2] = "ddivDST", [3] = "dmodDST" }
local map_ddivu_r6 = { shift = 6, mask = 3, [2] = "ddivuDST", [3] = "dmoduDST" }
local map_special_r6 = {
shift = 0, mask = 63,
[0] = { shift = 0, mask = -1, [0] = "nop", _ = "sllDTA" },
false, map_srl, "sraDTA",
"sllvDTS", false, map_srlv, "sravDTS",
"jrS", "jalrD1S", false, false,
"syscallY", "breakY", false, "sync",
"clzDS", "cloDS", "dclzDS", "dcloDS",
"dsllvDST", "dlsaDSTA", "dsrlvDST", "dsravDST",
map_mul_r6, map_mulu_r6, map_div_r6, map_divu_r6,
map_dmul_r6, map_dmulu_r6, map_ddiv_r6, map_ddivu_r6,
"addDST", "addu|moveDST0", "subDST", "subu|neguDS0T",
"andDST", "or|moveDST0", "xorDST", "nor|notDST0",
false, false, "sltDST", "sltuDST",
"daddDST", "dadduDST", "dsubDST", "dsubuDST",
"tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ",
"teqSTZ", "seleqzDST", "tneSTZ", "selnezDST",
"dsllDTA", false, "dsrlDTA", "dsraDTA",
"dsll32DTA", false, "dsrl32DTA", "dsra32DTA",
}
local map_bshfl_r6 = {
shift = 9, mask = 3,
[1] = "alignDSTa",
_ = {
shift = 6, mask = 31,
[0] = "bitswapDT",
[2] = "wsbhDT",
[16] = "sebDT",
[24] = "sehDT",
}
}
local map_dbshfl_r6 = {
shift = 9, mask = 3,
[1] = "dalignDSTa",
_ = {
shift = 6, mask = 31,
[0] = "dbitswapDT",
[2] = "dsbhDT",
[5] = "dshdDT",
}
}
local map_special3_r6 = {
shift = 0, mask = 63,
[0] = "extTSAK", [1] = "dextmTSAP", [3] = "dextTSAK",
[4] = "insTSAL", [6] = "dinsuTSEQ", [7] = "dinsTSAL",
[32] = map_bshfl_r6, [36] = map_dbshfl_r6, [59] = "rdhwrTD",
}
local map_regimm_r6 = {
shift = 16, mask = 31,
[0] = "bltzSB", [1] = "bgezSB",
[6] = "dahiSI", [30] = "datiSI",
[23] = "sigrieI", [31] = "synciSO",
}
local map_pcrel_r6 = {
shift = 19, mask = 3,
[0] = "addiupcS2", "lwpcS2", "lwupcS2", {
shift = 18, mask = 1,
[0] = "ldpcS3", { shift = 16, mask = 3, [2] = "auipcSI", [3] = "aluipcSI" }
}
}
local map_cop1s_r6 = {
shift = 0, mask = 63,
[0] = "add.sFGH", "sub.sFGH", "mul.sFGH", "div.sFGH",
"sqrt.sFG", "abs.sFG", "mov.sFG", "neg.sFG",
"round.l.sFG", "trunc.l.sFG", "ceil.l.sFG", "floor.l.sFG",
"round.w.sFG", "trunc.w.sFG", "ceil.w.sFG", "floor.w.sFG",
"sel.sFGH", false, false, false,
"seleqz.sFGH", "recip.sFG", "rsqrt.sFG", "selnez.sFGH",
"maddf.sFGH", "msubf.sFGH", "rint.sFG", "class.sFG",
"min.sFGH", "mina.sFGH", "max.sFGH", "maxa.sFGH",
false, "cvt.d.sFG", false, false,
"cvt.w.sFG", "cvt.l.sFG",
}
local map_cop1d_r6 = {
shift = 0, mask = 63,
[0] = "add.dFGH", "sub.dFGH", "mul.dFGH", "div.dFGH",
"sqrt.dFG", "abs.dFG", "mov.dFG", "neg.dFG",
"round.l.dFG", "trunc.l.dFG", "ceil.l.dFG", "floor.l.dFG",
"round.w.dFG", "trunc.w.dFG", "ceil.w.dFG", "floor.w.dFG",
"sel.dFGH", false, false, false,
"seleqz.dFGH", "recip.dFG", "rsqrt.dFG", "selnez.dFGH",
"maddf.dFGH", "msubf.dFGH", "rint.dFG", "class.dFG",
"min.dFGH", "mina.dFGH", "max.dFGH", "maxa.dFGH",
"cvt.s.dFG", false, false, false,
"cvt.w.dFG", "cvt.l.dFG",
}
local map_cop1w_r6 = {
shift = 0, mask = 63,
[0] = "cmp.af.sFGH", "cmp.un.sFGH", "cmp.eq.sFGH", "cmp.ueq.sFGH",
"cmp.lt.sFGH", "cmp.ult.sFGH", "cmp.le.sFGH", "cmp.ule.sFGH",
"cmp.saf.sFGH", "cmp.sun.sFGH", "cmp.seq.sFGH", "cmp.sueq.sFGH",
"cmp.slt.sFGH", "cmp.sult.sFGH", "cmp.sle.sFGH", "cmp.sule.sFGH",
false, "cmp.or.sFGH", "cmp.une.sFGH", "cmp.ne.sFGH",
false, false, false, false,
false, "cmp.sor.sFGH", "cmp.sune.sFGH", "cmp.sne.sFGH",
false, false, false, false,
"cvt.s.wFG", "cvt.d.wFG",
}
local map_cop1l_r6 = {
shift = 0, mask = 63,
[0] = "cmp.af.dFGH", "cmp.un.dFGH", "cmp.eq.dFGH", "cmp.ueq.dFGH",
"cmp.lt.dFGH", "cmp.ult.dFGH", "cmp.le.dFGH", "cmp.ule.dFGH",
"cmp.saf.dFGH", "cmp.sun.dFGH", "cmp.seq.dFGH", "cmp.sueq.dFGH",
"cmp.slt.dFGH", "cmp.sult.dFGH", "cmp.sle.dFGH", "cmp.sule.dFGH",
false, "cmp.or.dFGH", "cmp.une.dFGH", "cmp.ne.dFGH",
false, false, false, false,
false, "cmp.sor.dFGH", "cmp.sune.dFGH", "cmp.sne.dFGH",
false, false, false, false,
"cvt.s.lFG", "cvt.d.lFG",
}
local map_cop1_r6 = {
shift = 21, mask = 31,
[0] = "mfc1TG", "dmfc1TG", "cfc1TG", "mfhc1TG",
"mtc1TG", "dmtc1TG", "ctc1TG", "mthc1TG",
false, "bc1eqzHB", false, false,
false, "bc1nezHB", false, false,
map_cop1s_r6, map_cop1d_r6, false, false,
map_cop1w_r6, map_cop1l_r6,
}
local function maprs_popTS(rs, rt)
if rt == 0 then return 0 elseif rs == 0 then return 1
elseif rs == rt then return 2 else return 3 end
end
local map_pop06_r6 = {
maprs = maprs_popTS, [0] = "blezSB", "blezalcTB", "bgezalcTB", "bgeucSTB"
}
local map_pop07_r6 = {
maprs = maprs_popTS, [0] = "bgtzSB", "bgtzalcTB", "bltzalcTB", "bltucSTB"
}
local map_pop26_r6 = {
maprs = maprs_popTS, "blezcTB", "bgezcTB", "bgecSTB"
}
local map_pop27_r6 = {
maprs = maprs_popTS, "bgtzcTB", "bltzcTB", "bltcSTB"
}
local function maprs_popS(rs, rt)
if rs == 0 then return 0 else return 1 end
end
local map_pop66_r6 = {
maprs = maprs_popS, [0] = "jicTI", "beqzcSb"
}
local map_pop76_r6 = {
maprs = maprs_popS, [0] = "jialcTI", "bnezcSb"
}
local function maprs_popST(rs, rt)
if rs >= rt then return 0 elseif rs == 0 then return 1 else return 2 end
end
local map_pop10_r6 = {
maprs = maprs_popST, [0] = "bovcSTB", "beqzalcTB", "beqcSTB"
}
local map_pop30_r6 = {
maprs = maprs_popST, [0] = "bnvcSTB", "bnezalcTB", "bnecSTB"
}
local map_pri_r6 = {
[0] = map_special_r6, map_regimm_r6, "jJ", "jalJ",
"beq|beqz|bST00B", "bne|bnezST0B", map_pop06_r6, map_pop07_r6,
map_pop10_r6, "addiu|liTS0I", "sltiTSI", "sltiuTSI",
"andiTSU", "ori|liTS0U", "xoriTSU", "aui|luiTS0U",
map_cop0, map_cop1_r6, false, false,
false, false, map_pop26_r6, map_pop27_r6,
map_pop30_r6, "daddiuTSI", false, false,
false, "dauiTSI", false, map_special3_r6,
"lbTSO", "lhTSO", false, "lwTSO",
"lbuTSO", "lhuTSO", false, false,
"sbTSO", "shTSO", false, "swTSO",
false, false, false, false,
false, "lwc1HSO", "bc#", false,
false, "ldc1HSO", map_pop66_r6, "ldTSO",
false, "swc1HSO", "balc#", map_pcrel_r6,
false, "sdc1HSO", map_pop76_r6, "sdTSO",
}
------------------------------------------------------------------------------
local map_gpr = {
[0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
"r24", "r25", "r26", "r27", "r28", "sp", "r30", "ra",
}
------------------------------------------------------------------------------
-- Output a nicely formatted line with an opcode and operands.
local function putop(ctx, text, operands)
local pos = ctx.pos
local extra = ""
if ctx.rel then
local sym = ctx.symtab[ctx.rel]
if sym then extra = "\t->"..sym end
end
if ctx.hexdump > 0 then
ctx.out(format("%08x %s %-7s %s%s\n",
ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
else
ctx.out(format("%08x %-7s %s%s\n",
ctx.addr+pos, text, concat(operands, ", "), extra))
end
ctx.pos = pos + 4
end
-- Fallback for unknown opcodes.
local function unknown(ctx)
return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
end
local function get_be(ctx)
local pos = ctx.pos
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
return bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3)
end
local function get_le(ctx)
local pos = ctx.pos
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
return bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0)
end
-- Disassemble a single instruction.
local function disass_ins(ctx)
local op = ctx:get()
local operands = {}
local last = nil
ctx.op = op
ctx.rel = nil
local opat = ctx.map_pri[rshift(op, 26)]
while type(opat) ~= "string" do
if not opat then return unknown(ctx) end
if opat.maprs then
opat = opat[opat.maprs(band(rshift(op,21),31), band(rshift(op,16),31))]
else
opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._
end
end
local name, pat = match(opat, "^([a-z0-9_.]*)(.*)")
local altname, pat2 = match(pat, "|([a-z0-9_.|]*)(.*)")
if altname then pat = pat2 end
for p in gmatch(pat, ".") do
local x = nil
if p == "S" then
x = map_gpr[band(rshift(op, 21), 31)]
elseif p == "T" then
x = map_gpr[band(rshift(op, 16), 31)]
elseif p == "D" then
x = map_gpr[band(rshift(op, 11), 31)]
elseif p == "F" then
x = "f"..band(rshift(op, 6), 31)
elseif p == "G" then
x = "f"..band(rshift(op, 11), 31)
elseif p == "H" then
x = "f"..band(rshift(op, 16), 31)
elseif p == "R" then
x = "f"..band(rshift(op, 21), 31)
elseif p == "A" then
x = band(rshift(op, 6), 31)
elseif p == "a" then
x = band(rshift(op, 6), 7)
elseif p == "E" then
x = band(rshift(op, 6), 31) + 32
elseif p == "M" then
x = band(rshift(op, 11), 31)
elseif p == "N" then
x = band(rshift(op, 16), 31)
elseif p == "C" then
x = band(rshift(op, 18), 7)
if x == 0 then x = nil end
elseif p == "K" then
x = band(rshift(op, 11), 31) + 1
elseif p == "P" then
x = band(rshift(op, 11), 31) + 33
elseif p == "L" then
x = band(rshift(op, 11), 31) - last + 1
elseif p == "Q" then
x = band(rshift(op, 11), 31) - last + 33
elseif p == "I" then
x = arshift(lshift(op, 16), 16)
elseif p == "2" then
x = arshift(lshift(op, 13), 11)
elseif p == "3" then
x = arshift(lshift(op, 14), 11)
elseif p == "U" then
x = band(op, 0xffff)
elseif p == "O" then
local disp = arshift(lshift(op, 16), 16)
operands[#operands] = format("%d(%s)", disp, last)
elseif p == "X" then
local index = map_gpr[band(rshift(op, 16), 31)]
operands[#operands] = format("%s(%s)", index, last)
elseif p == "B" then
x = ctx.addr + ctx.pos + arshift(lshift(op, 16), 14) + 4
ctx.rel = x
x = format("0x%08x", x)
elseif p == "b" then
x = ctx.addr + ctx.pos + arshift(lshift(op, 11), 9) + 4
ctx.rel = x
x = format("0x%08x", x)
elseif p == "#" then
x = ctx.addr + ctx.pos + arshift(lshift(op, 6), 4) + 4
ctx.rel = x
x = format("0x%08x", x)
elseif p == "J" then
local a = ctx.addr + ctx.pos
x = a - band(a, 0x0fffffff) + band(op, 0x03ffffff)*4
ctx.rel = x
x = format("0x%08x", x)
elseif p == "V" then
x = band(rshift(op, 8), 7)
if x == 0 then x = nil end
elseif p == "W" then
x = band(op, 7)
if x == 0 then x = nil end
elseif p == "Y" then
x = band(rshift(op, 6), 0x000fffff)
if x == 0 then x = nil end
elseif p == "Z" then
x = band(rshift(op, 6), 1023)
if x == 0 then x = nil end
elseif p == "0" then
if last == "r0" or last == 0 then
local n = #operands
operands[n] = nil
last = operands[n-1]
if altname then
local a1, a2 = match(altname, "([^|]*)|(.*)")
if a1 then name, altname = a1, a2
else name = altname end
end
end
elseif p == "1" then
if last == "ra" then
operands[#operands] = nil
end
else
assert(false)
end
if x then operands[#operands+1] = x; last = x end
end
return putop(ctx, name, operands)
end
------------------------------------------------------------------------------
-- Disassemble a block of code.
local function disass_block(ctx, ofs, len)
if not ofs then ofs = 0 end
local stop = len and ofs+len or #ctx.code
stop = stop - stop % 4
ctx.pos = ofs - ofs % 4
ctx.rel = nil
while ctx.pos < stop do disass_ins(ctx) end
end
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
local function create(code, addr, out)
local ctx = {}
ctx.code = code
ctx.addr = addr or 0
ctx.out = out or io.write
ctx.symtab = {}
ctx.disass = disass_block
ctx.hexdump = 8
ctx.get = get_be
ctx.map_pri = map_pri
return ctx
end
local function create_el(code, addr, out)
local ctx = create(code, addr, out)
ctx.get = get_le
return ctx
end
local function create_r6(code, addr, out)
local ctx = create(code, addr, out)
ctx.map_pri = map_pri_r6
return ctx
end
local function create_r6_el(code, addr, out)
local ctx = create(code, addr, out)
ctx.get = get_le
ctx.map_pri = map_pri_r6
return ctx
end
-- Simple API: disassemble code (a string) at address and output via out.
local function disass(code, addr, out)
create(code, addr, out):disass()
end
local function disass_el(code, addr, out)
create_el(code, addr, out):disass()
end
local function disass_r6(code, addr, out)
create_r6(code, addr, out):disass()
end
local function disass_r6_el(code, addr, out)
create_r6_el(code, addr, out):disass()
end
-- Return register name for RID.
local function regname(r)
if r < 32 then return map_gpr[r] end
return "f"..(r-32)
end
-- Public module functions.
return {
create = create,
create_el = create_el,
create_r6 = create_r6,
create_r6_el = create_r6_el,
disass = disass,
disass_el = disass_el,
disass_r6 = disass_r6,
disass_r6_el = disass_r6_el,
regname = regname
}

View File

@@ -0,0 +1,17 @@
----------------------------------------------------------------------------
-- LuaJIT MIPS64 disassembler wrapper module.
--
-- Copyright (C) 2005-2025 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This module just exports the big-endian functions from the
-- MIPS disassembler module. All the interesting stuff is there.
------------------------------------------------------------------------------
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
return {
create = dis_mips.create,
disass = dis_mips.disass,
regname = dis_mips.regname
}

View File

@@ -0,0 +1,17 @@
----------------------------------------------------------------------------
-- LuaJIT MIPS64EL disassembler wrapper module.
--
-- Copyright (C) 2005-2025 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This module just exports the little-endian functions from the
-- MIPS disassembler module. All the interesting stuff is there.
------------------------------------------------------------------------------
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
return {
create = dis_mips.create_el,
disass = dis_mips.disass_el,
regname = dis_mips.regname
}

View File

@@ -0,0 +1,17 @@
----------------------------------------------------------------------------
-- LuaJIT MIPS64R6 disassembler wrapper module.
--
-- Copyright (C) 2005-2025 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This module just exports the r6 big-endian functions from the
-- MIPS disassembler module. All the interesting stuff is there.
------------------------------------------------------------------------------
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
return {
create = dis_mips.create_r6,
disass = dis_mips.disass_r6,
regname = dis_mips.regname
}

View File

@@ -0,0 +1,17 @@
----------------------------------------------------------------------------
-- LuaJIT MIPS64R6EL disassembler wrapper module.
--
-- Copyright (C) 2005-2025 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This module just exports the r6 little-endian functions from the
-- MIPS disassembler module. All the interesting stuff is there.
------------------------------------------------------------------------------
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
return {
create = dis_mips.create_r6_el,
disass = dis_mips.disass_r6_el,
regname = dis_mips.regname
}

View File

@@ -0,0 +1,17 @@
----------------------------------------------------------------------------
-- LuaJIT MIPSEL disassembler wrapper module.
--
-- Copyright (C) 2005-2025 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This module just exports the little-endian functions from the
-- MIPS disassembler module. All the interesting stuff is there.
------------------------------------------------------------------------------
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
return {
create = dis_mips.create_el,
disass = dis_mips.disass_el,
regname = dis_mips.regname
}

View File

@@ -0,0 +1,591 @@
----------------------------------------------------------------------------
-- LuaJIT PPC disassembler module.
--
-- Copyright (C) 2005-2025 Mike Pall. All rights reserved.
-- Released under the MIT/X license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This is a helper module used by the LuaJIT machine code dumper module.
--
-- It disassembles all common, non-privileged 32/64 bit PowerPC instructions
-- plus the e500 SPE instructions and some Cell/Xenon extensions.
--
-- NYI: VMX, VMX128
------------------------------------------------------------------------------
local type = type
local byte, format = string.byte, string.format
local match, gmatch, gsub = string.match, string.gmatch, string.gsub
local concat = table.concat
local bit = require("bit")
local band, bor, tohex = bit.band, bit.bor, bit.tohex
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
------------------------------------------------------------------------------
-- Primary and extended opcode maps
------------------------------------------------------------------------------
local map_crops = {
shift = 1, mask = 1023,
[0] = "mcrfXX",
[33] = "crnor|crnotCCC=", [129] = "crandcCCC",
[193] = "crxor|crclrCCC%", [225] = "crnandCCC",
[257] = "crandCCC", [289] = "creqv|crsetCCC%",
[417] = "crorcCCC", [449] = "cror|crmoveCCC=",
[16] = "b_lrKB", [528] = "b_ctrKB",
[150] = "isync",
}
local map_rlwinm = setmetatable({
shift = 0, mask = -1,
},
{ __index = function(t, x)
local rot = band(rshift(x, 11), 31)
local mb = band(rshift(x, 6), 31)
local me = band(rshift(x, 1), 31)
if mb == 0 and me == 31-rot then
return "slwiRR~A."
elseif me == 31 and mb == 32-rot then
return "srwiRR~-A."
else
return "rlwinmRR~AAA."
end
end
})
local map_rld = {
shift = 2, mask = 7,
[0] = "rldiclRR~HM.", "rldicrRR~HM.", "rldicRR~HM.", "rldimiRR~HM.",
{
shift = 1, mask = 1,
[0] = "rldclRR~RM.", "rldcrRR~RM.",
},
}
local map_ext = setmetatable({
shift = 1, mask = 1023,
[0] = "cmp_YLRR", [32] = "cmpl_YLRR",
[4] = "twARR", [68] = "tdARR",
[8] = "subfcRRR.", [40] = "subfRRR.",
[104] = "negRR.", [136] = "subfeRRR.",
[200] = "subfzeRR.", [232] = "subfmeRR.",
[520] = "subfcoRRR.", [552] = "subfoRRR.",
[616] = "negoRR.", [648] = "subfeoRRR.",
[712] = "subfzeoRR.", [744] = "subfmeoRR.",
[9] = "mulhduRRR.", [73] = "mulhdRRR.", [233] = "mulldRRR.",
[457] = "divduRRR.", [489] = "divdRRR.",
[745] = "mulldoRRR.",
[969] = "divduoRRR.", [1001] = "divdoRRR.",
[10] = "addcRRR.", [138] = "addeRRR.",
[202] = "addzeRR.", [234] = "addmeRR.", [266] = "addRRR.",
[522] = "addcoRRR.", [650] = "addeoRRR.",
[714] = "addzeoRR.", [746] = "addmeoRR.", [778] = "addoRRR.",
[11] = "mulhwuRRR.", [75] = "mulhwRRR.", [235] = "mullwRRR.",
[459] = "divwuRRR.", [491] = "divwRRR.",
[747] = "mullwoRRR.",
[971] = "divwouRRR.", [1003] = "divwoRRR.",
[15] = "iselltRRR", [47] = "iselgtRRR", [79] = "iseleqRRR",
[144] = { shift = 20, mask = 1, [0] = "mtcrfRZ~", "mtocrfRZ~", },
[19] = { shift = 20, mask = 1, [0] = "mfcrR", "mfocrfRZ", },
[371] = { shift = 11, mask = 1023, [392] = "mftbR", [424] = "mftbuR", },
[339] = {
shift = 11, mask = 1023,
[32] = "mferR", [256] = "mflrR", [288] = "mfctrR", [16] = "mfspefscrR",
},
[467] = {
shift = 11, mask = 1023,
[32] = "mtxerR", [256] = "mtlrR", [288] = "mtctrR", [16] = "mtspefscrR",
},
[20] = "lwarxRR0R", [84] = "ldarxRR0R",
[21] = "ldxRR0R", [53] = "lduxRRR",
[149] = "stdxRR0R", [181] = "stduxRRR",
[341] = "lwaxRR0R", [373] = "lwauxRRR",
[23] = "lwzxRR0R", [55] = "lwzuxRRR",
[87] = "lbzxRR0R", [119] = "lbzuxRRR",
[151] = "stwxRR0R", [183] = "stwuxRRR",
[215] = "stbxRR0R", [247] = "stbuxRRR",
[279] = "lhzxRR0R", [311] = "lhzuxRRR",
[343] = "lhaxRR0R", [375] = "lhauxRRR",
[407] = "sthxRR0R", [439] = "sthuxRRR",
[54] = "dcbst-R0R", [86] = "dcbf-R0R",
[150] = "stwcxRR0R.", [214] = "stdcxRR0R.",
[246] = "dcbtst-R0R", [278] = "dcbt-R0R",
[310] = "eciwxRR0R", [438] = "ecowxRR0R",
[470] = "dcbi-RR",
[598] = {
shift = 21, mask = 3,
[0] = "sync", "lwsync", "ptesync",
},
[758] = "dcba-RR",
[854] = "eieio", [982] = "icbi-R0R", [1014] = "dcbz-R0R",
[26] = "cntlzwRR~", [58] = "cntlzdRR~",
[122] = "popcntbRR~",
[154] = "prtywRR~", [186] = "prtydRR~",
[28] = "andRR~R.", [60] = "andcRR~R.", [124] = "nor|notRR~R=.",
[284] = "eqvRR~R.", [316] = "xorRR~R.",
[412] = "orcRR~R.", [444] = "or|mrRR~R=.", [476] = "nandRR~R.",
[508] = "cmpbRR~R",
[512] = "mcrxrX",
[532] = "ldbrxRR0R", [660] = "stdbrxRR0R",
[533] = "lswxRR0R", [597] = "lswiRR0A",
[661] = "stswxRR0R", [725] = "stswiRR0A",
[534] = "lwbrxRR0R", [662] = "stwbrxRR0R",
[790] = "lhbrxRR0R", [918] = "sthbrxRR0R",
[535] = "lfsxFR0R", [567] = "lfsuxFRR",
[599] = "lfdxFR0R", [631] = "lfduxFRR",
[663] = "stfsxFR0R", [695] = "stfsuxFRR",
[727] = "stfdxFR0R", [759] = "stfduxFR0R",
[855] = "lfiwaxFR0R",
[983] = "stfiwxFR0R",
[24] = "slwRR~R.",
[27] = "sldRR~R.", [536] = "srwRR~R.",
[792] = "srawRR~R.", [824] = "srawiRR~A.",
[794] = "sradRR~R.", [826] = "sradiRR~H.", [827] = "sradiRR~H.",
[922] = "extshRR~.", [954] = "extsbRR~.", [986] = "extswRR~.",
[539] = "srdRR~R.",
},
{ __index = function(t, x)
if band(x, 31) == 15 then return "iselRRRC" end
end
})
local map_ld = {
shift = 0, mask = 3,
[0] = "ldRRE", "lduRRE", "lwaRRE",
}
local map_std = {
shift = 0, mask = 3,
[0] = "stdRRE", "stduRRE",
}
local map_fps = {
shift = 5, mask = 1,
{
shift = 1, mask = 15,
[0] = false, false, "fdivsFFF.", false,
"fsubsFFF.", "faddsFFF.", "fsqrtsF-F.", false,
"fresF-F.", "fmulsFF-F.", "frsqrtesF-F.", false,
"fmsubsFFFF~.", "fmaddsFFFF~.", "fnmsubsFFFF~.", "fnmaddsFFFF~.",
}
}
local map_fpd = {
shift = 5, mask = 1,
[0] = {
shift = 1, mask = 1023,
[0] = "fcmpuXFF", [32] = "fcmpoXFF", [64] = "mcrfsXX",
[38] = "mtfsb1A.", [70] = "mtfsb0A.", [134] = "mtfsfiA>>-A>",
[8] = "fcpsgnFFF.", [40] = "fnegF-F.", [72] = "fmrF-F.",
[136] = "fnabsF-F.", [264] = "fabsF-F.",
[12] = "frspF-F.",
[14] = "fctiwF-F.", [15] = "fctiwzF-F.",
[583] = "mffsF.", [711] = "mtfsfZF.",
[392] = "frinF-F.", [424] = "frizF-F.",
[456] = "fripF-F.", [488] = "frimF-F.",
[814] = "fctidF-F.", [815] = "fctidzF-F.", [846] = "fcfidF-F.",
},
{
shift = 1, mask = 15,
[0] = false, false, "fdivFFF.", false,
"fsubFFF.", "faddFFF.", "fsqrtF-F.", "fselFFFF~.",
"freF-F.", "fmulFF-F.", "frsqrteF-F.", false,
"fmsubFFFF~.", "fmaddFFFF~.", "fnmsubFFFF~.", "fnmaddFFFF~.",
}
}
local map_spe = {
shift = 0, mask = 2047,
[512] = "evaddwRRR", [514] = "evaddiwRAR~",
[516] = "evsubwRRR~", [518] = "evsubiwRAR~",
[520] = "evabsRR", [521] = "evnegRR",
[522] = "evextsbRR", [523] = "evextshRR", [524] = "evrndwRR",
[525] = "evcntlzwRR", [526] = "evcntlswRR",
[527] = "brincRRR",
[529] = "evandRRR", [530] = "evandcRRR", [534] = "evxorRRR",
[535] = "evor|evmrRRR=", [536] = "evnor|evnotRRR=",
[537] = "eveqvRRR", [539] = "evorcRRR", [542] = "evnandRRR",
[544] = "evsrwuRRR", [545] = "evsrwsRRR",
[546] = "evsrwiuRRA", [547] = "evsrwisRRA",
[548] = "evslwRRR", [550] = "evslwiRRA",
[552] = "evrlwRRR", [553] = "evsplatiRS",
[554] = "evrlwiRRA", [555] = "evsplatfiRS",
[556] = "evmergehiRRR", [557] = "evmergeloRRR",
[558] = "evmergehiloRRR", [559] = "evmergelohiRRR",
[560] = "evcmpgtuYRR", [561] = "evcmpgtsYRR",
[562] = "evcmpltuYRR", [563] = "evcmpltsYRR",
[564] = "evcmpeqYRR",
[632] = "evselRRR", [633] = "evselRRRW",
[634] = "evselRRRW", [635] = "evselRRRW",
[636] = "evselRRRW", [637] = "evselRRRW",
[638] = "evselRRRW", [639] = "evselRRRW",
[640] = "evfsaddRRR", [641] = "evfssubRRR",
[644] = "evfsabsRR", [645] = "evfsnabsRR", [646] = "evfsnegRR",
[648] = "evfsmulRRR", [649] = "evfsdivRRR",
[652] = "evfscmpgtYRR", [653] = "evfscmpltYRR", [654] = "evfscmpeqYRR",
[656] = "evfscfuiR-R", [657] = "evfscfsiR-R",
[658] = "evfscfufR-R", [659] = "evfscfsfR-R",
[660] = "evfsctuiR-R", [661] = "evfsctsiR-R",
[662] = "evfsctufR-R", [663] = "evfsctsfR-R",
[664] = "evfsctuizR-R", [666] = "evfsctsizR-R",
[668] = "evfststgtYRR", [669] = "evfststltYRR", [670] = "evfststeqYRR",
[704] = "efsaddRRR", [705] = "efssubRRR",
[708] = "efsabsRR", [709] = "efsnabsRR", [710] = "efsnegRR",
[712] = "efsmulRRR", [713] = "efsdivRRR",
[716] = "efscmpgtYRR", [717] = "efscmpltYRR", [718] = "efscmpeqYRR",
[719] = "efscfdR-R",
[720] = "efscfuiR-R", [721] = "efscfsiR-R",
[722] = "efscfufR-R", [723] = "efscfsfR-R",
[724] = "efsctuiR-R", [725] = "efsctsiR-R",
[726] = "efsctufR-R", [727] = "efsctsfR-R",
[728] = "efsctuizR-R", [730] = "efsctsizR-R",
[732] = "efststgtYRR", [733] = "efststltYRR", [734] = "efststeqYRR",
[736] = "efdaddRRR", [737] = "efdsubRRR",
[738] = "efdcfuidR-R", [739] = "efdcfsidR-R",
[740] = "efdabsRR", [741] = "efdnabsRR", [742] = "efdnegRR",
[744] = "efdmulRRR", [745] = "efddivRRR",
[746] = "efdctuidzR-R", [747] = "efdctsidzR-R",
[748] = "efdcmpgtYRR", [749] = "efdcmpltYRR", [750] = "efdcmpeqYRR",
[751] = "efdcfsR-R",
[752] = "efdcfuiR-R", [753] = "efdcfsiR-R",
[754] = "efdcfufR-R", [755] = "efdcfsfR-R",
[756] = "efdctuiR-R", [757] = "efdctsiR-R",
[758] = "efdctufR-R", [759] = "efdctsfR-R",
[760] = "efdctuizR-R", [762] = "efdctsizR-R",
[764] = "efdtstgtYRR", [765] = "efdtstltYRR", [766] = "efdtsteqYRR",
[768] = "evlddxRR0R", [769] = "evlddRR8",
[770] = "evldwxRR0R", [771] = "evldwRR8",
[772] = "evldhxRR0R", [773] = "evldhRR8",
[776] = "evlhhesplatxRR0R", [777] = "evlhhesplatRR2",
[780] = "evlhhousplatxRR0R", [781] = "evlhhousplatRR2",
[782] = "evlhhossplatxRR0R", [783] = "evlhhossplatRR2",
[784] = "evlwhexRR0R", [785] = "evlwheRR4",
[788] = "evlwhouxRR0R", [789] = "evlwhouRR4",
[790] = "evlwhosxRR0R", [791] = "evlwhosRR4",
[792] = "evlwwsplatxRR0R", [793] = "evlwwsplatRR4",
[796] = "evlwhsplatxRR0R", [797] = "evlwhsplatRR4",
[800] = "evstddxRR0R", [801] = "evstddRR8",
[802] = "evstdwxRR0R", [803] = "evstdwRR8",
[804] = "evstdhxRR0R", [805] = "evstdhRR8",
[816] = "evstwhexRR0R", [817] = "evstwheRR4",
[820] = "evstwhoxRR0R", [821] = "evstwhoRR4",
[824] = "evstwwexRR0R", [825] = "evstwweRR4",
[828] = "evstwwoxRR0R", [829] = "evstwwoRR4",
[1027] = "evmhessfRRR", [1031] = "evmhossfRRR", [1032] = "evmheumiRRR",
[1033] = "evmhesmiRRR", [1035] = "evmhesmfRRR", [1036] = "evmhoumiRRR",
[1037] = "evmhosmiRRR", [1039] = "evmhosmfRRR", [1059] = "evmhessfaRRR",
[1063] = "evmhossfaRRR", [1064] = "evmheumiaRRR", [1065] = "evmhesmiaRRR",
[1067] = "evmhesmfaRRR", [1068] = "evmhoumiaRRR", [1069] = "evmhosmiaRRR",
[1071] = "evmhosmfaRRR", [1095] = "evmwhssfRRR", [1096] = "evmwlumiRRR",
[1100] = "evmwhumiRRR", [1101] = "evmwhsmiRRR", [1103] = "evmwhsmfRRR",
[1107] = "evmwssfRRR", [1112] = "evmwumiRRR", [1113] = "evmwsmiRRR",
[1115] = "evmwsmfRRR", [1127] = "evmwhssfaRRR", [1128] = "evmwlumiaRRR",
[1132] = "evmwhumiaRRR", [1133] = "evmwhsmiaRRR", [1135] = "evmwhsmfaRRR",
[1139] = "evmwssfaRRR", [1144] = "evmwumiaRRR", [1145] = "evmwsmiaRRR",
[1147] = "evmwsmfaRRR",
[1216] = "evaddusiaawRR", [1217] = "evaddssiaawRR",
[1218] = "evsubfusiaawRR", [1219] = "evsubfssiaawRR",
[1220] = "evmraRR",
[1222] = "evdivwsRRR", [1223] = "evdivwuRRR",
[1224] = "evaddumiaawRR", [1225] = "evaddsmiaawRR",
[1226] = "evsubfumiaawRR", [1227] = "evsubfsmiaawRR",
[1280] = "evmheusiaawRRR", [1281] = "evmhessiaawRRR",
[1283] = "evmhessfaawRRR", [1284] = "evmhousiaawRRR",
[1285] = "evmhossiaawRRR", [1287] = "evmhossfaawRRR",
[1288] = "evmheumiaawRRR", [1289] = "evmhesmiaawRRR",
[1291] = "evmhesmfaawRRR", [1292] = "evmhoumiaawRRR",
[1293] = "evmhosmiaawRRR", [1295] = "evmhosmfaawRRR",
[1320] = "evmhegumiaaRRR", [1321] = "evmhegsmiaaRRR",
[1323] = "evmhegsmfaaRRR", [1324] = "evmhogumiaaRRR",
[1325] = "evmhogsmiaaRRR", [1327] = "evmhogsmfaaRRR",
[1344] = "evmwlusiaawRRR", [1345] = "evmwlssiaawRRR",
[1352] = "evmwlumiaawRRR", [1353] = "evmwlsmiaawRRR",
[1363] = "evmwssfaaRRR", [1368] = "evmwumiaaRRR",
[1369] = "evmwsmiaaRRR", [1371] = "evmwsmfaaRRR",
[1408] = "evmheusianwRRR", [1409] = "evmhessianwRRR",
[1411] = "evmhessfanwRRR", [1412] = "evmhousianwRRR",
[1413] = "evmhossianwRRR", [1415] = "evmhossfanwRRR",
[1416] = "evmheumianwRRR", [1417] = "evmhesmianwRRR",
[1419] = "evmhesmfanwRRR", [1420] = "evmhoumianwRRR",
[1421] = "evmhosmianwRRR", [1423] = "evmhosmfanwRRR",
[1448] = "evmhegumianRRR", [1449] = "evmhegsmianRRR",
[1451] = "evmhegsmfanRRR", [1452] = "evmhogumianRRR",
[1453] = "evmhogsmianRRR", [1455] = "evmhogsmfanRRR",
[1472] = "evmwlusianwRRR", [1473] = "evmwlssianwRRR",
[1480] = "evmwlumianwRRR", [1481] = "evmwlsmianwRRR",
[1491] = "evmwssfanRRR", [1496] = "evmwumianRRR",
[1497] = "evmwsmianRRR", [1499] = "evmwsmfanRRR",
}
local map_pri = {
[0] = false, false, "tdiARI", "twiARI",
map_spe, false, false, "mulliRRI",
"subficRRI", false, "cmpl_iYLRU", "cmp_iYLRI",
"addicRRI", "addic.RRI", "addi|liRR0I", "addis|lisRR0I",
"b_KBJ", "sc", "bKJ", map_crops,
"rlwimiRR~AAA.", map_rlwinm, false, "rlwnmRR~RAA.",
"oriNRR~U", "orisRR~U", "xoriRR~U", "xorisRR~U",
"andi.RR~U", "andis.RR~U", map_rld, map_ext,
"lwzRRD", "lwzuRRD", "lbzRRD", "lbzuRRD",
"stwRRD", "stwuRRD", "stbRRD", "stbuRRD",
"lhzRRD", "lhzuRRD", "lhaRRD", "lhauRRD",
"sthRRD", "sthuRRD", "lmwRRD", "stmwRRD",
"lfsFRD", "lfsuFRD", "lfdFRD", "lfduFRD",
"stfsFRD", "stfsuFRD", "stfdFRD", "stfduFRD",
false, false, map_ld, map_fps,
false, false, map_std, map_fpd,
}
------------------------------------------------------------------------------
local map_gpr = {
[0] = "r0", "sp", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
"r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
}
local map_cond = { [0] = "lt", "gt", "eq", "so", "ge", "le", "ne", "ns", }
-- Format a condition bit.
local function condfmt(cond)
if cond <= 3 then
return map_cond[band(cond, 3)]
else
return format("4*cr%d+%s", rshift(cond, 2), map_cond[band(cond, 3)])
end
end
------------------------------------------------------------------------------
-- Output a nicely formatted line with an opcode and operands.
local function putop(ctx, text, operands)
local pos = ctx.pos
local extra = ""
if ctx.rel then
local sym = ctx.symtab[ctx.rel]
if sym then extra = "\t->"..sym end
end
if ctx.hexdump > 0 then
ctx.out(format("%08x %s %-7s %s%s\n",
ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
else
ctx.out(format("%08x %-7s %s%s\n",
ctx.addr+pos, text, concat(operands, ", "), extra))
end
ctx.pos = pos + 4
end
-- Fallback for unknown opcodes.
local function unknown(ctx)
return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
end
-- Disassemble a single instruction.
local function disass_ins(ctx)
local pos = ctx.pos
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
local op = bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3)
local operands = {}
local last = nil
local rs = 21
ctx.op = op
ctx.rel = nil
local opat = map_pri[rshift(b0, 2)]
while type(opat) ~= "string" do
if not opat then return unknown(ctx) end
opat = opat[band(rshift(op, opat.shift), opat.mask)]
end
local name, pat = match(opat, "^([a-z0-9_.]*)(.*)")
local altname, pat2 = match(pat, "|([a-z0-9_.]*)(.*)")
if altname then pat = pat2 end
for p in gmatch(pat, ".") do
local x = nil
if p == "R" then
x = map_gpr[band(rshift(op, rs), 31)]
rs = rs - 5
elseif p == "F" then
x = "f"..band(rshift(op, rs), 31)
rs = rs - 5
elseif p == "A" then
x = band(rshift(op, rs), 31)
rs = rs - 5
elseif p == "S" then
x = arshift(lshift(op, 27-rs), 27)
rs = rs - 5
elseif p == "I" then
x = arshift(lshift(op, 16), 16)
elseif p == "U" then
x = band(op, 0xffff)
elseif p == "D" or p == "E" then
local disp = arshift(lshift(op, 16), 16)
if p == "E" then disp = band(disp, -4) end
if last == "r0" then last = "0" end
operands[#operands] = format("%d(%s)", disp, last)
elseif p >= "2" and p <= "8" then
local disp = band(rshift(op, rs), 31) * p
if last == "r0" then last = "0" end
operands[#operands] = format("%d(%s)", disp, last)
elseif p == "H" then
x = band(rshift(op, rs), 31) + lshift(band(op, 2), 4)
rs = rs - 5
elseif p == "M" then
x = band(rshift(op, rs), 31) + band(op, 0x20)
elseif p == "C" then
x = condfmt(band(rshift(op, rs), 31))
rs = rs - 5
elseif p == "B" then
local bo = rshift(op, 21)
local cond = band(rshift(op, 16), 31)
local cn = ""
rs = rs - 10
if band(bo, 4) == 0 then
cn = band(bo, 2) == 0 and "dnz" or "dz"
if band(bo, 0x10) == 0 then
cn = cn..(band(bo, 8) == 0 and "f" or "t")
end
if band(bo, 0x10) == 0 then x = condfmt(cond) end
name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+")
elseif band(bo, 0x10) == 0 then
cn = map_cond[band(cond, 3) + (band(bo, 8) == 0 and 4 or 0)]
if cond > 3 then x = "cr"..rshift(cond, 2) end
name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+")
end
name = gsub(name, "_", cn)
elseif p == "J" then
x = arshift(lshift(op, 27-rs), 29-rs)*4
if band(op, 2) == 0 then x = ctx.addr + pos + x end
ctx.rel = x
x = "0x"..tohex(x)
elseif p == "K" then
if band(op, 1) ~= 0 then name = name.."l" end
if band(op, 2) ~= 0 then name = name.."a" end
elseif p == "X" or p == "Y" then
x = band(rshift(op, rs+2), 7)
if x == 0 and p == "Y" then x = nil else x = "cr"..x end
rs = rs - 5
elseif p == "W" then
x = "cr"..band(op, 7)
elseif p == "Z" then
x = band(rshift(op, rs-4), 255)
rs = rs - 10
elseif p == ">" then
operands[#operands] = rshift(operands[#operands], 1)
elseif p == "0" then
if last == "r0" then
operands[#operands] = nil
if altname then name = altname end
end
elseif p == "L" then
name = gsub(name, "_", band(op, 0x00200000) ~= 0 and "d" or "w")
elseif p == "." then
if band(op, 1) == 1 then name = name.."." end
elseif p == "N" then
if op == 0x60000000 then name = "nop"; break end
elseif p == "~" then
local n = #operands
operands[n-1], operands[n] = operands[n], operands[n-1]
elseif p == "=" then
local n = #operands
if last == operands[n-1] then
operands[n] = nil
name = altname
end
elseif p == "%" then
local n = #operands
if last == operands[n-1] and last == operands[n-2] then
operands[n] = nil
operands[n-1] = nil
name = altname
end
elseif p == "-" then
rs = rs - 5
else
assert(false)
end
if x then operands[#operands+1] = x; last = x end
end
return putop(ctx, name, operands)
end
------------------------------------------------------------------------------
-- Disassemble a block of code.
local function disass_block(ctx, ofs, len)
if not ofs then ofs = 0 end
local stop = len and ofs+len or #ctx.code
stop = stop - stop % 4
ctx.pos = ofs - ofs % 4
ctx.rel = nil
while ctx.pos < stop do disass_ins(ctx) end
end
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
local function create(code, addr, out)
local ctx = {}
ctx.code = code
ctx.addr = addr or 0
ctx.out = out or io.write
ctx.symtab = {}
ctx.disass = disass_block
ctx.hexdump = 8
return ctx
end
-- Simple API: disassemble code (a string) at address and output via out.
local function disass(code, addr, out)
create(code, addr, out):disass()
end
-- Return register name for RID.
local function regname(r)
if r < 32 then return map_gpr[r] end
return "f"..(r-32)
end
-- Public module functions.
return {
create = create,
disass = disass,
regname = regname
}

View File

@@ -0,0 +1,17 @@
----------------------------------------------------------------------------
-- LuaJIT x64 disassembler wrapper module.
--
-- Copyright (C) 2005-2025 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This module just exports the 64 bit functions from the combined
-- x86/x64 disassembler module. All the interesting stuff is there.
------------------------------------------------------------------------------
local dis_x86 = require((string.match(..., ".*%.") or "").."dis_x86")
return {
create = dis_x86.create64,
disass = dis_x86.disass64,
regname = dis_x86.regname64
}

View File

@@ -0,0 +1,953 @@
----------------------------------------------------------------------------
-- LuaJIT x86/x64 disassembler module.
--
-- Copyright (C) 2005-2025 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This is a helper module used by the LuaJIT machine code dumper module.
--
-- Sending small code snippets to an external disassembler and mixing the
-- output with our own stuff was too fragile. So I had to bite the bullet
-- and write yet another x86 disassembler. Oh well ...
--
-- The output format is very similar to what ndisasm generates. But it has
-- been developed independently by looking at the opcode tables from the
-- Intel and AMD manuals. The supported instruction set is quite extensive
-- and reflects what a current generation Intel or AMD CPU implements in
-- 32 bit and 64 bit mode. Yes, this includes MMX, SSE, SSE2, SSE3, SSSE3,
-- SSE4.1, SSE4.2, SSE4a, AVX, AVX2 and even privileged and hypervisor
-- (VMX/SVM) instructions.
--
-- Notes:
-- * The (useless) a16 prefix, 3DNow and pre-586 opcodes are unsupported.
-- * No attempt at optimization has been made -- it's fast enough for my needs.
------------------------------------------------------------------------------
local type = type
local sub, byte, format = string.sub, string.byte, string.format
local match, gmatch, gsub = string.match, string.gmatch, string.gsub
local lower, rep = string.lower, string.rep
local bit = require("bit")
local tohex = bit.tohex
-- Map for 1st opcode byte in 32 bit mode. Ugly? Well ... read on.
local map_opc1_32 = {
--0x
[0]="addBmr","addVmr","addBrm","addVrm","addBai","addVai","push es","pop es",
"orBmr","orVmr","orBrm","orVrm","orBai","orVai","push cs","opc2*",
--1x
"adcBmr","adcVmr","adcBrm","adcVrm","adcBai","adcVai","push ss","pop ss",
"sbbBmr","sbbVmr","sbbBrm","sbbVrm","sbbBai","sbbVai","push ds","pop ds",
--2x
"andBmr","andVmr","andBrm","andVrm","andBai","andVai","es:seg","daa",
"subBmr","subVmr","subBrm","subVrm","subBai","subVai","cs:seg","das",
--3x
"xorBmr","xorVmr","xorBrm","xorVrm","xorBai","xorVai","ss:seg","aaa",
"cmpBmr","cmpVmr","cmpBrm","cmpVrm","cmpBai","cmpVai","ds:seg","aas",
--4x
"incVR","incVR","incVR","incVR","incVR","incVR","incVR","incVR",
"decVR","decVR","decVR","decVR","decVR","decVR","decVR","decVR",
--5x
"pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR",
"popUR","popUR","popUR","popUR","popUR","popUR","popUR","popUR",
--6x
"sz*pushaw,pusha","sz*popaw,popa","boundVrm","arplWmr",
"fs:seg","gs:seg","o16:","a16",
"pushUi","imulVrmi","pushBs","imulVrms",
"insb","insVS","outsb","outsVS",
--7x
"joBj","jnoBj","jbBj","jnbBj","jzBj","jnzBj","jbeBj","jaBj",
"jsBj","jnsBj","jpeBj","jpoBj","jlBj","jgeBj","jleBj","jgBj",
--8x
"arith!Bmi","arith!Vmi","arith!Bmi","arith!Vms",
"testBmr","testVmr","xchgBrm","xchgVrm",
"movBmr","movVmr","movBrm","movVrm",
"movVmg","leaVrm","movWgm","popUm",
--9x
"nop*xchgVaR|pause|xchgWaR|repne nop","xchgVaR","xchgVaR","xchgVaR",
"xchgVaR","xchgVaR","xchgVaR","xchgVaR",
"sz*cbw,cwde,cdqe","sz*cwd,cdq,cqo","call farViw","wait",
"sz*pushfw,pushf","sz*popfw,popf","sahf","lahf",
--Ax
"movBao","movVao","movBoa","movVoa",
"movsb","movsVS","cmpsb","cmpsVS",
"testBai","testVai","stosb","stosVS",
"lodsb","lodsVS","scasb","scasVS",
--Bx
"movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi",
"movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI",
--Cx
"shift!Bmu","shift!Vmu","retBw","ret","vex*3$lesVrm","vex*2$ldsVrm","movBmi","movVmi",
"enterBwu","leave","retfBw","retf","int3","intBu","into","iretVS",
--Dx
"shift!Bm1","shift!Vm1","shift!Bmc","shift!Vmc","aamBu","aadBu","salc","xlatb",
"fp*0","fp*1","fp*2","fp*3","fp*4","fp*5","fp*6","fp*7",
--Ex
"loopneBj","loopeBj","loopBj","sz*jcxzBj,jecxzBj,jrcxzBj",
"inBau","inVau","outBua","outVua",
"callVj","jmpVj","jmp farViw","jmpBj","inBad","inVad","outBda","outVda",
--Fx
"lock:","int1","repne:rep","rep:","hlt","cmc","testb!Bm","testv!Vm",
"clc","stc","cli","sti","cld","std","incb!Bm","incd!Vm",
}
assert(#map_opc1_32 == 255)
-- Map for 1st opcode byte in 64 bit mode (overrides only).
local map_opc1_64 = setmetatable({
[0x06]=false, [0x07]=false, [0x0e]=false,
[0x16]=false, [0x17]=false, [0x1e]=false, [0x1f]=false,
[0x27]=false, [0x2f]=false, [0x37]=false, [0x3f]=false,
[0x60]=false, [0x61]=false, [0x62]=false, [0x63]="movsxdVrDmt", [0x67]="a32:",
[0x40]="rex*", [0x41]="rex*b", [0x42]="rex*x", [0x43]="rex*xb",
[0x44]="rex*r", [0x45]="rex*rb", [0x46]="rex*rx", [0x47]="rex*rxb",
[0x48]="rex*w", [0x49]="rex*wb", [0x4a]="rex*wx", [0x4b]="rex*wxb",
[0x4c]="rex*wr", [0x4d]="rex*wrb", [0x4e]="rex*wrx", [0x4f]="rex*wrxb",
[0x82]=false, [0x9a]=false, [0xc4]="vex*3", [0xc5]="vex*2", [0xce]=false,
[0xd4]=false, [0xd5]=false, [0xd6]=false, [0xea]=false,
}, { __index = map_opc1_32 })
-- Map for 2nd opcode byte (0F xx). True CISC hell. Hey, I told you.
-- Prefix dependent MMX/SSE opcodes: (none)|rep|o16|repne, -|F3|66|F2
local map_opc2 = {
--0x
[0]="sldt!Dmp","sgdt!Ump","larVrm","lslVrm",nil,"syscall","clts","sysret",
"invd","wbinvd",nil,"ud1",nil,"$prefetch!Bm","femms","3dnowMrmu",
--1x
"movupsXrm|movssXrvm|movupdXrm|movsdXrvm",
"movupsXmr|movssXmvr|movupdXmr|movsdXmvr",
"movhlpsXrm$movlpsXrm|movsldupXrm|movlpdXrm|movddupXrm",
"movlpsXmr||movlpdXmr",
"unpcklpsXrvm||unpcklpdXrvm",
"unpckhpsXrvm||unpckhpdXrvm",
"movlhpsXrm$movhpsXrm|movshdupXrm|movhpdXrm",
"movhpsXmr||movhpdXmr",
"$prefetcht!Bm","hintnopVm","hintnopVm","hintnopVm",
"hintnopVm","hintnopVm","hintnopVm","hintnopVm",
--2x
"movUmx$","movUmy$","movUxm$","movUym$","movUmz$",nil,"movUzm$",nil,
"movapsXrm||movapdXrm",
"movapsXmr||movapdXmr",
"cvtpi2psXrMm|cvtsi2ssXrvVmt|cvtpi2pdXrMm|cvtsi2sdXrvVmt",
"movntpsXmr|movntssXmr|movntpdXmr|movntsdXmr",
"cvttps2piMrXm|cvttss2siVrXm|cvttpd2piMrXm|cvttsd2siVrXm",
"cvtps2piMrXm|cvtss2siVrXm|cvtpd2piMrXm|cvtsd2siVrXm",
"ucomissXrm||ucomisdXrm",
"comissXrm||comisdXrm",
--3x
"wrmsr","rdtsc","rdmsr","rdpmc","sysenter","sysexit",nil,"getsec",
"opc3*38",nil,"opc3*3a",nil,nil,nil,nil,nil,
--4x
"cmovoVrm","cmovnoVrm","cmovbVrm","cmovnbVrm",
"cmovzVrm","cmovnzVrm","cmovbeVrm","cmovaVrm",
"cmovsVrm","cmovnsVrm","cmovpeVrm","cmovpoVrm",
"cmovlVrm","cmovgeVrm","cmovleVrm","cmovgVrm",
--5x
"movmskpsVrXm$||movmskpdVrXm$","sqrtpsXrm|sqrtssXrm|sqrtpdXrm|sqrtsdXrm",
"rsqrtpsXrm|rsqrtssXrvm","rcppsXrm|rcpssXrvm",
"andpsXrvm||andpdXrvm","andnpsXrvm||andnpdXrvm",
"orpsXrvm||orpdXrvm","xorpsXrvm||xorpdXrvm",
"addpsXrvm|addssXrvm|addpdXrvm|addsdXrvm","mulpsXrvm|mulssXrvm|mulpdXrvm|mulsdXrvm",
"cvtps2pdXrm|cvtss2sdXrvm|cvtpd2psXrm|cvtsd2ssXrvm",
"cvtdq2psXrm|cvttps2dqXrm|cvtps2dqXrm",
"subpsXrvm|subssXrvm|subpdXrvm|subsdXrvm","minpsXrvm|minssXrvm|minpdXrvm|minsdXrvm",
"divpsXrvm|divssXrvm|divpdXrvm|divsdXrvm","maxpsXrvm|maxssXrvm|maxpdXrvm|maxsdXrvm",
--6x
"punpcklbwPrvm","punpcklwdPrvm","punpckldqPrvm","packsswbPrvm",
"pcmpgtbPrvm","pcmpgtwPrvm","pcmpgtdPrvm","packuswbPrvm",
"punpckhbwPrvm","punpckhwdPrvm","punpckhdqPrvm","packssdwPrvm",
"||punpcklqdqXrvm","||punpckhqdqXrvm",
"movPrVSm","movqMrm|movdquXrm|movdqaXrm",
--7x
"pshufwMrmu|pshufhwXrmu|pshufdXrmu|pshuflwXrmu","pshiftw!Pvmu",
"pshiftd!Pvmu","pshiftq!Mvmu||pshiftdq!Xvmu",
"pcmpeqbPrvm","pcmpeqwPrvm","pcmpeqdPrvm","emms*|",
"vmreadUmr||extrqXmuu$|insertqXrmuu$","vmwriteUrm||extrqXrm$|insertqXrm$",
nil,nil,
"||haddpdXrvm|haddpsXrvm","||hsubpdXrvm|hsubpsXrvm",
"movVSmMr|movqXrm|movVSmXr","movqMmr|movdquXmr|movdqaXmr",
--8x
"joVj","jnoVj","jbVj","jnbVj","jzVj","jnzVj","jbeVj","jaVj",
"jsVj","jnsVj","jpeVj","jpoVj","jlVj","jgeVj","jleVj","jgVj",
--9x
"setoBm","setnoBm","setbBm","setnbBm","setzBm","setnzBm","setbeBm","setaBm",
"setsBm","setnsBm","setpeBm","setpoBm","setlBm","setgeBm","setleBm","setgBm",
--Ax
"push fs","pop fs","cpuid","btVmr","shldVmru","shldVmrc",nil,nil,
"push gs","pop gs","rsm","btsVmr","shrdVmru","shrdVmrc","fxsave!Dmp","imulVrm",
--Bx
"cmpxchgBmr","cmpxchgVmr","$lssVrm","btrVmr",
"$lfsVrm","$lgsVrm","movzxVrBmt","movzxVrWmt",
"|popcntVrm","ud2Dp","bt!Vmu","btcVmr",
"bsfVrm","bsrVrm|lzcntVrm|bsrWrm","movsxVrBmt","movsxVrWmt",
--Cx
"xaddBmr","xaddVmr",
"cmppsXrvmu|cmpssXrvmu|cmppdXrvmu|cmpsdXrvmu","$movntiVmr|",
"pinsrwPrvWmu","pextrwDrPmu",
"shufpsXrvmu||shufpdXrvmu","$cmpxchg!Qmp",
"bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR",
--Dx
"||addsubpdXrvm|addsubpsXrvm","psrlwPrvm","psrldPrvm","psrlqPrvm",
"paddqPrvm","pmullwPrvm",
"|movq2dqXrMm|movqXmr|movdq2qMrXm$","pmovmskbVrMm||pmovmskbVrXm",
"psubusbPrvm","psubuswPrvm","pminubPrvm","pandPrvm",
"paddusbPrvm","padduswPrvm","pmaxubPrvm","pandnPrvm",
--Ex
"pavgbPrvm","psrawPrvm","psradPrvm","pavgwPrvm",
"pmulhuwPrvm","pmulhwPrvm",
"|cvtdq2pdXrm|cvttpd2dqXrm|cvtpd2dqXrm","$movntqMmr||$movntdqXmr",
"psubsbPrvm","psubswPrvm","pminswPrvm","porPrvm",
"paddsbPrvm","paddswPrvm","pmaxswPrvm","pxorPrvm",
--Fx
"|||lddquXrm","psllwPrvm","pslldPrvm","psllqPrvm",
"pmuludqPrvm","pmaddwdPrvm","psadbwPrvm","maskmovqMrm||maskmovdquXrm$",
"psubbPrvm","psubwPrvm","psubdPrvm","psubqPrvm",
"paddbPrvm","paddwPrvm","padddPrvm","ud",
}
assert(map_opc2[255] == "ud")
-- Map for three-byte opcodes. Can't wait for their next invention.
local map_opc3 = {
["38"] = { -- [66] 0f 38 xx
--0x
[0]="pshufbPrvm","phaddwPrvm","phadddPrvm","phaddswPrvm",
"pmaddubswPrvm","phsubwPrvm","phsubdPrvm","phsubswPrvm",
"psignbPrvm","psignwPrvm","psigndPrvm","pmulhrswPrvm",
"||permilpsXrvm","||permilpdXrvm",nil,nil,
--1x
"||pblendvbXrma",nil,nil,nil,
"||blendvpsXrma","||blendvpdXrma","||permpsXrvm","||ptestXrm",
"||broadcastssXrm","||broadcastsdXrm","||broadcastf128XrlXm",nil,
"pabsbPrm","pabswPrm","pabsdPrm",nil,
--2x
"||pmovsxbwXrm","||pmovsxbdXrm","||pmovsxbqXrm","||pmovsxwdXrm",
"||pmovsxwqXrm","||pmovsxdqXrm",nil,nil,
"||pmuldqXrvm","||pcmpeqqXrvm","||$movntdqaXrm","||packusdwXrvm",
"||maskmovpsXrvm","||maskmovpdXrvm","||maskmovpsXmvr","||maskmovpdXmvr",
--3x
"||pmovzxbwXrm","||pmovzxbdXrm","||pmovzxbqXrm","||pmovzxwdXrm",
"||pmovzxwqXrm","||pmovzxdqXrm","||permdXrvm","||pcmpgtqXrvm",
"||pminsbXrvm","||pminsdXrvm","||pminuwXrvm","||pminudXrvm",
"||pmaxsbXrvm","||pmaxsdXrvm","||pmaxuwXrvm","||pmaxudXrvm",
--4x
"||pmulddXrvm","||phminposuwXrm",nil,nil,
nil,"||psrlvVSXrvm","||psravdXrvm","||psllvVSXrvm",
--5x
[0x58] = "||pbroadcastdXrlXm",[0x59] = "||pbroadcastqXrlXm",
[0x5a] = "||broadcasti128XrlXm",
--7x
[0x78] = "||pbroadcastbXrlXm",[0x79] = "||pbroadcastwXrlXm",
--8x
[0x8c] = "||pmaskmovXrvVSm",
[0x8e] = "||pmaskmovVSmXvr",
--9x
[0x96] = "||fmaddsub132pHXrvm",[0x97] = "||fmsubadd132pHXrvm",
[0x98] = "||fmadd132pHXrvm",[0x99] = "||fmadd132sHXrvm",
[0x9a] = "||fmsub132pHXrvm",[0x9b] = "||fmsub132sHXrvm",
[0x9c] = "||fnmadd132pHXrvm",[0x9d] = "||fnmadd132sHXrvm",
[0x9e] = "||fnmsub132pHXrvm",[0x9f] = "||fnmsub132sHXrvm",
--Ax
[0xa6] = "||fmaddsub213pHXrvm",[0xa7] = "||fmsubadd213pHXrvm",
[0xa8] = "||fmadd213pHXrvm",[0xa9] = "||fmadd213sHXrvm",
[0xaa] = "||fmsub213pHXrvm",[0xab] = "||fmsub213sHXrvm",
[0xac] = "||fnmadd213pHXrvm",[0xad] = "||fnmadd213sHXrvm",
[0xae] = "||fnmsub213pHXrvm",[0xaf] = "||fnmsub213sHXrvm",
--Bx
[0xb6] = "||fmaddsub231pHXrvm",[0xb7] = "||fmsubadd231pHXrvm",
[0xb8] = "||fmadd231pHXrvm",[0xb9] = "||fmadd231sHXrvm",
[0xba] = "||fmsub231pHXrvm",[0xbb] = "||fmsub231sHXrvm",
[0xbc] = "||fnmadd231pHXrvm",[0xbd] = "||fnmadd231sHXrvm",
[0xbe] = "||fnmsub231pHXrvm",[0xbf] = "||fnmsub231sHXrvm",
--Dx
[0xdc] = "||aesencXrvm", [0xdd] = "||aesenclastXrvm",
[0xde] = "||aesdecXrvm", [0xdf] = "||aesdeclastXrvm",
--Fx
[0xf0] = "|||crc32TrBmt",[0xf1] = "|||crc32TrVmt",
[0xf7] = "| sarxVrmv| shlxVrmv| shrxVrmv",
},
["3a"] = { -- [66] 0f 3a xx
--0x
[0x00]="||permqXrmu","||permpdXrmu","||pblenddXrvmu",nil,
"||permilpsXrmu","||permilpdXrmu","||perm2f128Xrvmu",nil,
"||roundpsXrmu","||roundpdXrmu","||roundssXrvmu","||roundsdXrvmu",
"||blendpsXrvmu","||blendpdXrvmu","||pblendwXrvmu","palignrPrvmu",
--1x
nil,nil,nil,nil,
"||pextrbVmXru","||pextrwVmXru","||pextrVmSXru","||extractpsVmXru",
"||insertf128XrvlXmu","||extractf128XlXmYru",nil,nil,
nil,nil,nil,nil,
--2x
"||pinsrbXrvVmu","||insertpsXrvmu","||pinsrXrvVmuS",nil,
--3x
[0x38] = "||inserti128Xrvmu",[0x39] = "||extracti128XlXmYru",
--4x
[0x40] = "||dppsXrvmu",
[0x41] = "||dppdXrvmu",
[0x42] = "||mpsadbwXrvmu",
[0x44] = "||pclmulqdqXrvmu",
[0x46] = "||perm2i128Xrvmu",
[0x4a] = "||blendvpsXrvmb",[0x4b] = "||blendvpdXrvmb",
[0x4c] = "||pblendvbXrvmb",
--6x
[0x60] = "||pcmpestrmXrmu",[0x61] = "||pcmpestriXrmu",
[0x62] = "||pcmpistrmXrmu",[0x63] = "||pcmpistriXrmu",
[0xdf] = "||aeskeygenassistXrmu",
--Fx
[0xf0] = "||| rorxVrmu",
},
}
-- Map for VMX/SVM opcodes 0F 01 C0-FF (sgdt group with register operands).
local map_opcvm = {
[0xc1]="vmcall",[0xc2]="vmlaunch",[0xc3]="vmresume",[0xc4]="vmxoff",
[0xc8]="monitor",[0xc9]="mwait",
[0xd8]="vmrun",[0xd9]="vmmcall",[0xda]="vmload",[0xdb]="vmsave",
[0xdc]="stgi",[0xdd]="clgi",[0xde]="skinit",[0xdf]="invlpga",
[0xf8]="swapgs",[0xf9]="rdtscp",
}
-- Map for FP opcodes. And you thought stack machines are simple?
local map_opcfp = {
-- D8-DF 00-BF: opcodes with a memory operand.
-- D8
[0]="faddFm","fmulFm","fcomFm","fcompFm","fsubFm","fsubrFm","fdivFm","fdivrFm",
"fldFm",nil,"fstFm","fstpFm","fldenvVm","fldcwWm","fnstenvVm","fnstcwWm",
-- DA
"fiaddDm","fimulDm","ficomDm","ficompDm",
"fisubDm","fisubrDm","fidivDm","fidivrDm",
-- DB
"fildDm","fisttpDm","fistDm","fistpDm",nil,"fld twordFmp",nil,"fstp twordFmp",
-- DC
"faddGm","fmulGm","fcomGm","fcompGm","fsubGm","fsubrGm","fdivGm","fdivrGm",
-- DD
"fldGm","fisttpQm","fstGm","fstpGm","frstorDmp",nil,"fnsaveDmp","fnstswWm",
-- DE
"fiaddWm","fimulWm","ficomWm","ficompWm",
"fisubWm","fisubrWm","fidivWm","fidivrWm",
-- DF
"fildWm","fisttpWm","fistWm","fistpWm",
"fbld twordFmp","fildQm","fbstp twordFmp","fistpQm",
-- xx C0-FF: opcodes with a pseudo-register operand.
-- D8
"faddFf","fmulFf","fcomFf","fcompFf","fsubFf","fsubrFf","fdivFf","fdivrFf",
-- D9
"fldFf","fxchFf",{"fnop"},nil,
{"fchs","fabs",nil,nil,"ftst","fxam"},
{"fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz"},
{"f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp"},
{"fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos"},
-- DA
"fcmovbFf","fcmoveFf","fcmovbeFf","fcmovuFf",nil,{nil,"fucompp"},nil,nil,
-- DB
"fcmovnbFf","fcmovneFf","fcmovnbeFf","fcmovnuFf",
{nil,nil,"fnclex","fninit"},"fucomiFf","fcomiFf",nil,
-- DC
"fadd toFf","fmul toFf",nil,nil,
"fsub toFf","fsubr toFf","fdivr toFf","fdiv toFf",
-- DD
"ffreeFf",nil,"fstFf","fstpFf","fucomFf","fucompFf",nil,nil,
-- DE
"faddpFf","fmulpFf",nil,{nil,"fcompp"},
"fsubrpFf","fsubpFf","fdivrpFf","fdivpFf",
-- DF
nil,nil,nil,nil,{"fnstsw ax"},"fucomipFf","fcomipFf",nil,
}
assert(map_opcfp[126] == "fcomipFf")
-- Map for opcode groups. The subkey is sp from the ModRM byte.
local map_opcgroup = {
arith = { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" },
shift = { "rol", "ror", "rcl", "rcr", "shl", "shr", "sal", "sar" },
testb = { "testBmi", "testBmi", "not", "neg", "mul", "imul", "div", "idiv" },
testv = { "testVmi", "testVmi", "not", "neg", "mul", "imul", "div", "idiv" },
incb = { "inc", "dec" },
incd = { "inc", "dec", "callUmp", "$call farDmp",
"jmpUmp", "$jmp farDmp", "pushUm" },
sldt = { "sldt", "str", "lldt", "ltr", "verr", "verw" },
sgdt = { "vm*$sgdt", "vm*$sidt", "$lgdt", "vm*$lidt",
"smsw", nil, "lmsw", "vm*$invlpg" },
bt = { nil, nil, nil, nil, "bt", "bts", "btr", "btc" },
cmpxchg = { nil, "sz*,cmpxchg8bQmp,cmpxchg16bXmp", nil, nil,
nil, nil, "vmptrld|vmxon|vmclear", "vmptrst" },
pshiftw = { nil, nil, "psrlw", nil, "psraw", nil, "psllw" },
pshiftd = { nil, nil, "psrld", nil, "psrad", nil, "pslld" },
pshiftq = { nil, nil, "psrlq", nil, nil, nil, "psllq" },
pshiftdq = { nil, nil, "psrlq", "psrldq", nil, nil, "psllq", "pslldq" },
fxsave = { "$fxsave", "$fxrstor", "$ldmxcsr", "$stmxcsr",
nil, "lfenceDp$", "mfenceDp$", "sfenceDp$clflush" },
prefetch = { "prefetch", "prefetchw" },
prefetcht = { "prefetchnta", "prefetcht0", "prefetcht1", "prefetcht2" },
}
------------------------------------------------------------------------------
-- Maps for register names.
local map_regs = {
B = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
B64 = { "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
W = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
"r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" },
D = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
"r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" },
Q = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" },
M = { "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
"mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" }, -- No x64 ext!
X = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
"xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" },
Y = { "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7",
"ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15" },
}
local map_segregs = { "es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7" }
-- Maps for size names.
local map_sz2n = {
B = 1, W = 2, D = 4, Q = 8, M = 8, X = 16, Y = 32,
}
local map_sz2prefix = {
B = "byte", W = "word", D = "dword",
Q = "qword",
M = "qword", X = "xword", Y = "yword",
F = "dword", G = "qword", -- No need for sizes/register names for these two.
}
------------------------------------------------------------------------------
-- Output a nicely formatted line with an opcode and operands.
local function putop(ctx, text, operands)
local code, pos, hex = ctx.code, ctx.pos, ""
local hmax = ctx.hexdump
if hmax > 0 then
for i=ctx.start,pos-1 do
hex = hex..format("%02X", byte(code, i, i))
end
if #hex > hmax then hex = sub(hex, 1, hmax)..". "
else hex = hex..rep(" ", hmax-#hex+2) end
end
if operands then text = text.." "..operands end
if ctx.o16 then text = "o16 "..text; ctx.o16 = false end
if ctx.a32 then text = "a32 "..text; ctx.a32 = false end
if ctx.rep then text = ctx.rep.." "..text; ctx.rep = false end
if ctx.rex then
local t = (ctx.rexw and "w" or "")..(ctx.rexr and "r" or "")..
(ctx.rexx and "x" or "")..(ctx.rexb and "b" or "")..
(ctx.vexl and "l" or "")
if ctx.vexv and ctx.vexv ~= 0 then t = t.."v"..ctx.vexv end
if t ~= "" then text = ctx.rex.."."..t.." "..gsub(text, "^ ", "")
elseif ctx.rex == "vex" then text = gsub("v"..text, "^v ", "") end
ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
ctx.rex = false; ctx.vexl = false; ctx.vexv = false
end
if ctx.seg then
local text2, n = gsub(text, "%[", "["..ctx.seg..":")
if n == 0 then text = ctx.seg.." "..text else text = text2 end
ctx.seg = false
end
if ctx.lock then text = "lock "..text; ctx.lock = false end
local imm = ctx.imm
if imm then
local sym = ctx.symtab[imm]
if sym then text = text.."\t->"..sym end
end
ctx.out(format("%08x %s%s\n", ctx.addr+ctx.start, hex, text))
ctx.mrm = false
ctx.vexv = false
ctx.start = pos
ctx.imm = nil
end
-- Clear all prefix flags.
local function clearprefixes(ctx)
ctx.o16 = false; ctx.seg = false; ctx.lock = false; ctx.rep = false
ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
ctx.rex = false; ctx.a32 = false; ctx.vexl = false
end
-- Fallback for incomplete opcodes at the end.
local function incomplete(ctx)
ctx.pos = ctx.stop+1
clearprefixes(ctx)
return putop(ctx, "(incomplete)")
end
-- Fallback for unknown opcodes.
local function unknown(ctx)
clearprefixes(ctx)
return putop(ctx, "(unknown)")
end
-- Return an immediate of the specified size.
local function getimm(ctx, pos, n)
if pos+n-1 > ctx.stop then return incomplete(ctx) end
local code = ctx.code
if n == 1 then
local b1 = byte(code, pos, pos)
return b1
elseif n == 2 then
local b1, b2 = byte(code, pos, pos+1)
return b1+b2*256
else
local b1, b2, b3, b4 = byte(code, pos, pos+3)
local imm = b1+b2*256+b3*65536+b4*16777216
ctx.imm = imm
return imm
end
end
-- Process pattern string and generate the operands.
local function putpat(ctx, name, pat)
local operands, regs, sz, mode, sp, rm, sc, rx, sdisp
local code, pos, stop, vexl = ctx.code, ctx.pos, ctx.stop, ctx.vexl
-- Chars used: 1DFGHIMPQRSTUVWXYabcdfgijlmoprstuvwxyz
for p in gmatch(pat, ".") do
local x = nil
if p == "V" or p == "U" then
if ctx.rexw then sz = "Q"; ctx.rexw = false
elseif ctx.o16 then sz = "W"; ctx.o16 = false
elseif p == "U" and ctx.x64 then sz = "Q"
else sz = "D" end
regs = map_regs[sz]
elseif p == "T" then
if ctx.rexw then sz = "Q"; ctx.rexw = false else sz = "D" end
regs = map_regs[sz]
elseif p == "B" then
sz = "B"
regs = ctx.rex and map_regs.B64 or map_regs.B
elseif match(p, "[WDQMXYFG]") then
sz = p
if sz == "X" and vexl then sz = "Y"; ctx.vexl = false end
regs = map_regs[sz]
elseif p == "P" then
sz = ctx.o16 and "X" or "M"; ctx.o16 = false
if sz == "X" and vexl then sz = "Y"; ctx.vexl = false end
regs = map_regs[sz]
elseif p == "H" then
name = name..(ctx.rexw and "d" or "s")
ctx.rexw = false
elseif p == "S" then
name = name..lower(sz)
elseif p == "s" then
local imm = getimm(ctx, pos, 1); if not imm then return end
x = imm <= 127 and format("+0x%02x", imm)
or format("-0x%02x", 256-imm)
pos = pos+1
elseif p == "u" then
local imm = getimm(ctx, pos, 1); if not imm then return end
x = format("0x%02x", imm)
pos = pos+1
elseif p == "b" then
local imm = getimm(ctx, pos, 1); if not imm then return end
x = regs[imm/16+1]
pos = pos+1
elseif p == "w" then
local imm = getimm(ctx, pos, 2); if not imm then return end
x = format("0x%x", imm)
pos = pos+2
elseif p == "o" then -- [offset]
if ctx.x64 then
local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
x = format("[0x%08x%08x]", imm2, imm1)
pos = pos+8
else
local imm = getimm(ctx, pos, 4); if not imm then return end
x = format("[0x%08x]", imm)
pos = pos+4
end
elseif p == "i" or p == "I" then
local n = map_sz2n[sz]
if n == 8 and ctx.x64 and p == "I" then
local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
x = format("0x%08x%08x", imm2, imm1)
else
if n == 8 then n = 4 end
local imm = getimm(ctx, pos, n); if not imm then return end
if sz == "Q" and (imm < 0 or imm > 0x7fffffff) then
imm = (0xffffffff+1)-imm
x = format(imm > 65535 and "-0x%08x" or "-0x%x", imm)
else
x = format(imm > 65535 and "0x%08x" or "0x%x", imm)
end
end
pos = pos+n
elseif p == "j" then
local n = map_sz2n[sz]
if n == 8 then n = 4 end
local imm = getimm(ctx, pos, n); if not imm then return end
if sz == "B" and imm > 127 then imm = imm-256
elseif imm > 2147483647 then imm = imm-4294967296 end
pos = pos+n
imm = imm + pos + ctx.addr
if imm > 4294967295 and not ctx.x64 then imm = imm-4294967296 end
ctx.imm = imm
if sz == "W" then
x = format("word 0x%04x", imm%65536)
elseif ctx.x64 then
local lo = imm % 0x1000000
x = format("0x%02x%06x", (imm-lo) / 0x1000000, lo)
else
x = "0x"..tohex(imm)
end
elseif p == "R" then
local r = byte(code, pos-1, pos-1)%8
if ctx.rexb then r = r + 8; ctx.rexb = false end
x = regs[r+1]
elseif p == "a" then x = regs[1]
elseif p == "c" then x = "cl"
elseif p == "d" then x = "dx"
elseif p == "1" then x = "1"
else
if not mode then
mode = ctx.mrm
if not mode then
if pos > stop then return incomplete(ctx) end
mode = byte(code, pos, pos)
pos = pos+1
end
rm = mode%8; mode = (mode-rm)/8
sp = mode%8; mode = (mode-sp)/8
sdisp = ""
if mode < 3 then
if rm == 4 then
if pos > stop then return incomplete(ctx) end
sc = byte(code, pos, pos)
pos = pos+1
rm = sc%8; sc = (sc-rm)/8
rx = sc%8; sc = (sc-rx)/8
if ctx.rexx then rx = rx + 8; ctx.rexx = false end
if rx == 4 then rx = nil end
end
if mode > 0 or rm == 5 then
local dsz = mode
if dsz ~= 1 then dsz = 4 end
local disp = getimm(ctx, pos, dsz); if not disp then return end
if mode == 0 then rm = nil end
if rm or rx or (not sc and ctx.x64 and not ctx.a32) then
if dsz == 1 and disp > 127 then
sdisp = format("-0x%x", 256-disp)
elseif disp >= 0 and disp <= 0x7fffffff then
sdisp = format("+0x%x", disp)
else
sdisp = format("-0x%x", (0xffffffff+1)-disp)
end
else
sdisp = format(ctx.x64 and not ctx.a32 and
not (disp >= 0 and disp <= 0x7fffffff)
and "0xffffffff%08x" or "0x%08x", disp)
end
pos = pos+dsz
end
end
if rm and ctx.rexb then rm = rm + 8; ctx.rexb = false end
if ctx.rexr then sp = sp + 8; ctx.rexr = false end
end
if p == "m" then
if mode == 3 then x = regs[rm+1]
else
local aregs = ctx.a32 and map_regs.D or ctx.aregs
local srm, srx = "", ""
if rm then srm = aregs[rm+1]
elseif not sc and ctx.x64 and not ctx.a32 then srm = "rip" end
ctx.a32 = false
if rx then
if rm then srm = srm.."+" end
srx = aregs[rx+1]
if sc > 0 then srx = srx.."*"..(2^sc) end
end
x = format("[%s%s%s]", srm, srx, sdisp)
end
if mode < 3 and
(not match(pat, "[aRrgp]") or match(pat, "t")) then -- Yuck.
x = map_sz2prefix[sz].." "..x
end
elseif p == "r" then x = regs[sp+1]
elseif p == "g" then x = map_segregs[sp+1]
elseif p == "p" then -- Suppress prefix.
elseif p == "f" then x = "st"..rm
elseif p == "x" then
if sp == 0 and ctx.lock and not ctx.x64 then
x = "CR8"; ctx.lock = false
else
x = "CR"..sp
end
elseif p == "v" then
if ctx.vexv then
x = regs[ctx.vexv+1]; ctx.vexv = false
end
elseif p == "y" then x = "DR"..sp
elseif p == "z" then x = "TR"..sp
elseif p == "l" then vexl = false
elseif p == "t" then
else
error("bad pattern `"..pat.."'")
end
end
if x then operands = operands and operands..", "..x or x end
end
ctx.pos = pos
return putop(ctx, name, operands)
end
-- Forward declaration.
local map_act
-- Fetch and cache MRM byte.
local function getmrm(ctx)
local mrm = ctx.mrm
if not mrm then
local pos = ctx.pos
if pos > ctx.stop then return nil end
mrm = byte(ctx.code, pos, pos)
ctx.pos = pos+1
ctx.mrm = mrm
end
return mrm
end
-- Dispatch to handler depending on pattern.
local function dispatch(ctx, opat, patgrp)
if not opat then return unknown(ctx) end
if match(opat, "%|") then -- MMX/SSE variants depending on prefix.
local p
if ctx.rep then
p = ctx.rep=="rep" and "%|([^%|]*)" or "%|[^%|]*%|[^%|]*%|([^%|]*)"
ctx.rep = false
elseif ctx.o16 then p = "%|[^%|]*%|([^%|]*)"; ctx.o16 = false
else p = "^[^%|]*" end
opat = match(opat, p)
if not opat then return unknown(ctx) end
-- ctx.rep = false; ctx.o16 = false
--XXX fails for 66 f2 0f 38 f1 06 crc32 eax,WORD PTR [esi]
--XXX remove in branches?
end
if match(opat, "%$") then -- reg$mem variants.
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
opat = match(opat, mrm >= 192 and "^[^%$]*" or "%$(.*)")
if opat == "" then return unknown(ctx) end
end
if opat == "" then return unknown(ctx) end
local name, pat = match(opat, "^([a-z0-9 ]*)(.*)")
if pat == "" and patgrp then pat = patgrp end
return map_act[sub(pat, 1, 1)](ctx, name, pat)
end
-- Get a pattern from an opcode map and dispatch to handler.
local function dispatchmap(ctx, opcmap)
local pos = ctx.pos
local opat = opcmap[byte(ctx.code, pos, pos)]
pos = pos + 1
ctx.pos = pos
return dispatch(ctx, opat)
end
-- Map for action codes. The key is the first char after the name.
map_act = {
-- Simple opcodes without operands.
[""] = function(ctx, name, pat)
return putop(ctx, name)
end,
-- Operand size chars fall right through.
B = putpat, W = putpat, D = putpat, Q = putpat,
V = putpat, U = putpat, T = putpat,
M = putpat, X = putpat, P = putpat,
F = putpat, G = putpat, Y = putpat,
H = putpat,
-- Collect prefixes.
[":"] = function(ctx, name, pat)
ctx[pat == ":" and name or sub(pat, 2)] = name
if ctx.pos - ctx.start > 5 then return unknown(ctx) end -- Limit #prefixes.
end,
-- Chain to special handler specified by name.
["*"] = function(ctx, name, pat)
return map_act[name](ctx, name, sub(pat, 2))
end,
-- Use named subtable for opcode group.
["!"] = function(ctx, name, pat)
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
return dispatch(ctx, map_opcgroup[name][((mrm-(mrm%8))/8)%8+1], sub(pat, 2))
end,
-- o16,o32[,o64] variants.
sz = function(ctx, name, pat)
if ctx.o16 then ctx.o16 = false
else
pat = match(pat, ",(.*)")
if ctx.rexw then
local p = match(pat, ",(.*)")
if p then pat = p; ctx.rexw = false end
end
end
pat = match(pat, "^[^,]*")
return dispatch(ctx, pat)
end,
-- Two-byte opcode dispatch.
opc2 = function(ctx, name, pat)
return dispatchmap(ctx, map_opc2)
end,
-- Three-byte opcode dispatch.
opc3 = function(ctx, name, pat)
return dispatchmap(ctx, map_opc3[pat])
end,
-- VMX/SVM dispatch.
vm = function(ctx, name, pat)
return dispatch(ctx, map_opcvm[ctx.mrm])
end,
-- Floating point opcode dispatch.
fp = function(ctx, name, pat)
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
local rm = mrm%8
local idx = pat*8 + ((mrm-rm)/8)%8
if mrm >= 192 then idx = idx + 64 end
local opat = map_opcfp[idx]
if type(opat) == "table" then opat = opat[rm+1] end
return dispatch(ctx, opat)
end,
-- REX prefix.
rex = function(ctx, name, pat)
if ctx.rex then return unknown(ctx) end -- Only 1 REX or VEX prefix allowed.
for p in gmatch(pat, ".") do ctx["rex"..p] = true end
ctx.rex = "rex"
end,
-- VEX prefix.
vex = function(ctx, name, pat)
if ctx.rex then return unknown(ctx) end -- Only 1 REX or VEX prefix allowed.
ctx.rex = "vex"
local pos = ctx.pos
if ctx.mrm then
ctx.mrm = nil
pos = pos-1
end
local b = byte(ctx.code, pos, pos)
if not b then return incomplete(ctx) end
pos = pos+1
if b < 128 then ctx.rexr = true end
local m = 1
if pat == "3" then
m = b%32; b = (b-m)/32
local nb = b%2; b = (b-nb)/2
if nb == 0 then ctx.rexb = true end
local nx = b%2
if nx == 0 then ctx.rexx = true end
b = byte(ctx.code, pos, pos)
if not b then return incomplete(ctx) end
pos = pos+1
if b >= 128 then ctx.rexw = true end
end
ctx.pos = pos
local map
if m == 1 then map = map_opc2
elseif m == 2 then map = map_opc3["38"]
elseif m == 3 then map = map_opc3["3a"]
else return unknown(ctx) end
local p = b%4; b = (b-p)/4
if p == 1 then ctx.o16 = "o16"
elseif p == 2 then ctx.rep = "rep"
elseif p == 3 then ctx.rep = "repne" end
local l = b%2; b = (b-l)/2
if l ~= 0 then ctx.vexl = true end
ctx.vexv = (-1-b)%16
return dispatchmap(ctx, map)
end,
-- Special case for nop with REX prefix.
nop = function(ctx, name, pat)
return dispatch(ctx, ctx.rex and pat or "nop")
end,
-- Special case for 0F 77.
emms = function(ctx, name, pat)
if ctx.rex ~= "vex" then
return putop(ctx, "emms")
elseif ctx.vexl then
ctx.vexl = false
return putop(ctx, "zeroall")
else
return putop(ctx, "zeroupper")
end
end,
}
------------------------------------------------------------------------------
-- Disassemble a block of code.
local function disass_block(ctx, ofs, len)
if not ofs then ofs = 0 end
local stop = len and ofs+len or #ctx.code
ofs = ofs + 1
ctx.start = ofs
ctx.pos = ofs
ctx.stop = stop
ctx.imm = nil
ctx.mrm = false
clearprefixes(ctx)
while ctx.pos <= stop do dispatchmap(ctx, ctx.map1) end
if ctx.pos ~= ctx.start then incomplete(ctx) end
end
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
local function create(code, addr, out)
local ctx = {}
ctx.code = code
ctx.addr = (addr or 0) - 1
ctx.out = out or io.write
ctx.symtab = {}
ctx.disass = disass_block
ctx.hexdump = 16
ctx.x64 = false
ctx.map1 = map_opc1_32
ctx.aregs = map_regs.D
return ctx
end
local function create64(code, addr, out)
local ctx = create(code, addr, out)
ctx.x64 = true
ctx.map1 = map_opc1_64
ctx.aregs = map_regs.Q
return ctx
end
-- Simple API: disassemble code (a string) at address and output via out.
local function disass(code, addr, out)
create(code, addr, out):disass()
end
local function disass64(code, addr, out)
create64(code, addr, out):disass()
end
-- Return register name for RID.
local function regname(r)
if r < 8 then return map_regs.D[r+1] end
return map_regs.X[r-7]
end
local function regname64(r)
if r < 16 then return map_regs.Q[r+1] end
return map_regs.X[r-15]
end
-- Public module functions.
return {
create = create,
create64 = create64,
disass = disass,
disass64 = disass64,
regname = regname,
regname64 = regname64
}

View File

@@ -0,0 +1,733 @@
----------------------------------------------------------------------------
-- LuaJIT compiler dump module.
--
-- Copyright (C) 2005-2025 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
--
-- This module can be used to debug the JIT compiler itself. It dumps the
-- code representations and structures used in various compiler stages.
--
-- Example usage:
--
-- luajit -jdump -e "local x=0; for i=1,1e6 do x=x+i end; print(x)"
-- luajit -jdump=im -e "for i=1,1000 do for j=1,1000 do end end" | less -R
-- luajit -jdump=is myapp.lua | less -R
-- luajit -jdump=-b myapp.lua
-- luajit -jdump=+aH,myapp.html myapp.lua
-- luajit -jdump=ixT,myapp.dump myapp.lua
--
-- The first argument specifies the dump mode. The second argument gives
-- the output file name. Default output is to stdout, unless the environment
-- variable LUAJIT_DUMPFILE is set. The file is overwritten every time the
-- module is started.
--
-- Different features can be turned on or off with the dump mode. If the
-- mode starts with a '+', the following features are added to the default
-- set of features; a '-' removes them. Otherwise the features are replaced.
--
-- The following dump features are available (* marks the default):
--
-- * t Print a line for each started, ended or aborted trace (see also -jv).
-- * b Dump the traced bytecode.
-- * i Dump the IR (intermediate representation).
-- r Augment the IR with register/stack slots.
-- s Dump the snapshot map.
-- * m Dump the generated machine code.
-- x Print each taken trace exit.
-- X Print each taken trace exit and the contents of all registers.
-- a Print the IR of aborted traces, too.
--
-- The output format can be set with the following characters:
--
-- T Plain text output.
-- A ANSI-colored text output
-- H Colorized HTML + CSS output.
--
-- The default output format is plain text. It's set to ANSI-colored text
-- if the COLORTERM variable is set. Note: this is independent of any output
-- redirection, which is actually considered a feature.
--
-- You probably want to use less -R to enjoy viewing ANSI-colored text from
-- a pipe or a file. Add this to your ~/.bashrc: export LESS="-R"
--
------------------------------------------------------------------------------
-- Cache some library functions and objects.
local jit = require("jit")
local jutil = require("jit.util")
local vmdef = require("jit.vmdef")
local funcinfo, funcbc = jutil.funcinfo, jutil.funcbc
local traceinfo, traceir, tracek = jutil.traceinfo, jutil.traceir, jutil.tracek
local tracemc, tracesnap = jutil.tracemc, jutil.tracesnap
local traceexitstub, ircalladdr = jutil.traceexitstub, jutil.ircalladdr
local bit = require("bit")
local band, shr, tohex = bit.band, bit.rshift, bit.tohex
local sub, gsub, format = string.sub, string.gsub, string.format
local byte, rep = string.byte, string.rep
local type, tostring = type, tostring
local stdout, stderr = io.stdout, io.stderr
-- Load other modules on-demand.
local bcline, disass
-- Active flag, output file handle and dump mode.
local active, out, dumpmode
------------------------------------------------------------------------------
local symtabmt = { __index = false }
local symtab = {}
local nexitsym = 0
-- Fill nested symbol table with per-trace exit stub addresses.
local function fillsymtab_tr(tr, nexit)
local t = {}
symtabmt.__index = t
if jit.arch:sub(1, 4) == "mips" then
t[traceexitstub(tr, 0)] = "exit"
return
end
for i=0,nexit-1 do
local addr = traceexitstub(tr, i)
if addr < 0 then addr = addr + 2^32 end
t[addr] = tostring(i)
end
local addr = traceexitstub(tr, nexit)
if addr then t[addr] = "stack_check" end
end
-- Fill symbol table with trace exit stub addresses.
local function fillsymtab(tr, nexit)
local t = symtab
if nexitsym == 0 then
local maskaddr = jit.arch == "arm" and -2
local ircall = vmdef.ircall
for i=0,#ircall do
local addr = ircalladdr(i)
if addr ~= 0 then
if maskaddr then addr = band(addr, maskaddr) end
if addr < 0 then addr = addr + 2^32 end
t[addr] = ircall[i]
end
end
end
if nexitsym == 1000000 then -- Per-trace exit stubs.
fillsymtab_tr(tr, nexit)
elseif nexit > nexitsym then -- Shared exit stubs.
for i=nexitsym,nexit-1 do
local addr = traceexitstub(i)
if addr == nil then -- Fall back to per-trace exit stubs.
fillsymtab_tr(tr, nexit)
setmetatable(symtab, symtabmt)
nexit = 1000000
break
end
if addr < 0 then addr = addr + 2^32 end
t[addr] = tostring(i)
end
nexitsym = nexit
end
return t
end
local function dumpwrite(s)
out:write(s)
end
-- Disassemble machine code.
local function dump_mcode(tr)
local info = traceinfo(tr)
if not info then return end
local mcode, addr, loop = tracemc(tr)
if not mcode then return end
if not disass then disass = require("jit.dis_"..jit.arch) end
if addr < 0 then addr = addr + 2^32 end
out:write("---- TRACE ", tr, " mcode ", #mcode, "\n")
local ctx = disass.create(mcode, addr, dumpwrite)
ctx.hexdump = 0
ctx.symtab = fillsymtab(tr, info.nexit)
if loop ~= 0 then
symtab[addr+loop] = "LOOP"
ctx:disass(0, loop)
out:write("->LOOP:\n")
ctx:disass(loop, #mcode-loop)
symtab[addr+loop] = nil
else
ctx:disass(0, #mcode)
end
end
------------------------------------------------------------------------------
local irtype_text = {
[0] = "nil",
"fal",
"tru",
"lud",
"str",
"p32",
"thr",
"pro",
"fun",
"p64",
"cdt",
"tab",
"udt",
"flt",
"num",
"i8 ",
"u8 ",
"i16",
"u16",
"int",
"u32",
"i64",
"u64",
"sfp",
}
local colortype_ansi = {
[0] = "%s",
"%s",
"%s",
"\027[36m%s\027[m",
"\027[32m%s\027[m",
"%s",
"\027[1m%s\027[m",
"%s",
"\027[1m%s\027[m",
"%s",
"\027[33m%s\027[m",
"\027[31m%s\027[m",
"\027[36m%s\027[m",
"\027[34m%s\027[m",
"\027[34m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
}
local function colorize_text(s)
return s
end
local function colorize_ansi(s, t, extra)
local out = format(colortype_ansi[t], s)
if extra then out = "\027[3m"..out end
return out
end
local irtype_ansi = setmetatable({},
{ __index = function(tab, t)
local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end })
local html_escape = { ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;", }
local function colorize_html(s, t, extra)
s = gsub(s, "[<>&]", html_escape)
return format('<span class="irt_%s%s">%s</span>',
irtype_text[t], extra and " irt_extra" or "", s)
end
local irtype_html = setmetatable({},
{ __index = function(tab, t)
local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end })
local header_html = [[
<style type="text/css">
background { background: #ffffff; color: #000000; }
pre.ljdump {
font-size: 10pt;
background: #f0f4ff;
color: #000000;
border: 1px solid #bfcfff;
padding: 0.5em;
margin-left: 2em;
margin-right: 2em;
}
span.irt_str { color: #00a000; }
span.irt_thr, span.irt_fun { color: #404040; font-weight: bold; }
span.irt_tab { color: #c00000; }
span.irt_udt, span.irt_lud { color: #00c0c0; }
span.irt_num { color: #4040c0; }
span.irt_int, span.irt_i8, span.irt_u8, span.irt_i16, span.irt_u16 { color: #b040b0; }
span.irt_extra { font-style: italic; }
</style>
]]
local colorize, irtype
-- Lookup tables to convert some literals into names.
local litname = {
["SLOAD "] = setmetatable({}, { __index = function(t, mode)
local s = ""
if band(mode, 1) ~= 0 then s = s.."P" end
if band(mode, 2) ~= 0 then s = s.."F" end
if band(mode, 4) ~= 0 then s = s.."T" end
if band(mode, 8) ~= 0 then s = s.."C" end
if band(mode, 16) ~= 0 then s = s.."R" end
if band(mode, 32) ~= 0 then s = s.."I" end
if band(mode, 64) ~= 0 then s = s.."K" end
t[mode] = s
return s
end}),
["XLOAD "] = { [0] = "", "R", "V", "RV", "U", "RU", "VU", "RVU", },
["CONV "] = setmetatable({}, { __index = function(t, mode)
local s = irtype[band(mode, 31)]
s = irtype[band(shr(mode, 5), 31)].."."..s
if band(mode, 0x800) ~= 0 then s = s.." sext" end
local c = shr(mode, 12)
if c == 1 then s = s.." none"
elseif c == 2 then s = s.." index"
elseif c == 3 then s = s.." check" end
t[mode] = s
return s
end}),
["FLOAD "] = vmdef.irfield,
["FREF "] = vmdef.irfield,
["FPMATH"] = vmdef.irfpm,
["TMPREF"] = { [0] = "", "IN", "OUT", "INOUT", "", "", "OUT2", "INOUT2" },
["BUFHDR"] = { [0] = "RESET", "APPEND", "WRITE" },
["TOSTR "] = { [0] = "INT", "NUM", "CHAR" },
}
local function ctlsub(c)
if c == "\n" then return "\\n"
elseif c == "\r" then return "\\r"
elseif c == "\t" then return "\\t"
else return format("\\%03d", byte(c))
end
end
local function fmtfunc(func, pc)
local fi = funcinfo(func, pc)
if fi.loc then
return fi.loc
elseif fi.ffid then
return vmdef.ffnames[fi.ffid]
elseif fi.addr then
return format("C:%x", fi.addr)
else
return "(?)"
end
end
local function formatk(tr, idx, sn)
local k, t, slot = tracek(tr, idx)
local tn = type(k)
local s
if tn == "number" then
if t < 12 then
s = k == 0 and "NULL" or format("[0x%08x]", k)
elseif band(sn or 0, 0x30000) ~= 0 then
s = band(sn, 0x20000) ~= 0 and "contpc" or "ftsz"
elseif k == 2^52+2^51 then
s = "bias"
else
s = format(0 < k and k < 0x1p-1026 and "%+a" or "%+.14g", k)
end
elseif tn == "string" then
s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub))
elseif tn == "function" then
s = fmtfunc(k)
elseif tn == "table" then
s = format("{%p}", k)
elseif tn == "userdata" then
if t == 12 then
s = format("userdata:%p", k)
else
s = format("[%p]", k)
if s == "[NULL]" then s = "NULL" end
end
elseif t == 21 then -- int64_t
s = sub(tostring(k), 1, -3)
if sub(s, 1, 1) ~= "-" then s = "+"..s end
elseif sn == 0x1057fff then -- SNAP(1, SNAP_FRAME | SNAP_NORESTORE, REF_NIL)
return "----" -- Special case for LJ_FR2 slot 1.
else
s = tostring(k) -- For primitives.
end
s = colorize(format("%-4s", s), t, band(sn or 0, 0x100000) ~= 0)
if slot then
s = format("%s @%d", s, slot)
end
return s
end
local function printsnap(tr, snap)
local n = 2
for s=0,snap[1]-1 do
local sn = snap[n]
if shr(sn, 24) == s then
n = n + 1
local ref = band(sn, 0xffff) - 0x8000 -- REF_BIAS
if ref < 0 then
out:write(formatk(tr, ref, sn))
elseif band(sn, 0x80000) ~= 0 then -- SNAP_SOFTFPNUM
out:write(colorize(format("%04d/%04d", ref, ref+1), 14))
else
local m, ot, op1, op2 = traceir(tr, ref)
out:write(colorize(format("%04d", ref), band(ot, 31), band(sn, 0x100000) ~= 0))
end
out:write(band(sn, 0x10000) == 0 and " " or "|") -- SNAP_FRAME
else
out:write("---- ")
end
end
out:write("]\n")
end
-- Dump snapshots (not interleaved with IR).
local function dump_snap(tr)
out:write("---- TRACE ", tr, " snapshots\n")
for i=0,1000000000 do
local snap = tracesnap(tr, i)
if not snap then break end
out:write(format("#%-3d %04d [ ", i, snap[0]))
printsnap(tr, snap)
end
end
-- Return a register name or stack slot for a rid/sp location.
local function ridsp_name(ridsp, ins)
if not disass then disass = require("jit.dis_"..jit.arch) end
local rid, slot = band(ridsp, 0xff), shr(ridsp, 8)
if rid == 253 or rid == 254 then
return (slot == 0 or slot == 255) and " {sink" or format(" {%04d", ins-slot)
end
if ridsp > 255 then return format("[%x]", slot*4) end
if rid < 128 then return disass.regname(rid) end
return ""
end
-- Dump CALL* function ref and return optional ctype.
local function dumpcallfunc(tr, ins)
local ctype
if ins > 0 then
local m, ot, op1, op2 = traceir(tr, ins)
if band(ot, 31) == 0 then -- nil type means CARG(func, ctype).
ins = op1
ctype = formatk(tr, op2)
end
end
if ins < 0 then
out:write(format("[0x%x](", tonumber((tracek(tr, ins)))))
else
out:write(format("%04d (", ins))
end
return ctype
end
-- Recursively gather CALL* args and dump them.
local function dumpcallargs(tr, ins)
if ins < 0 then
out:write(formatk(tr, ins))
else
local m, ot, op1, op2 = traceir(tr, ins)
local oidx = 6*shr(ot, 8)
local op = sub(vmdef.irnames, oidx+1, oidx+6)
if op == "CARG " then
dumpcallargs(tr, op1)
if op2 < 0 then
out:write(" ", formatk(tr, op2))
else
out:write(" ", format("%04d", op2))
end
else
out:write(format("%04d", ins))
end
end
end
-- Dump IR and interleaved snapshots.
local function dump_ir(tr, dumpsnap, dumpreg)
local info = traceinfo(tr)
if not info then return end
local nins = info.nins
out:write("---- TRACE ", tr, " IR\n")
local irnames = vmdef.irnames
local snapref = 65536
local snap, snapno
if dumpsnap then
snap = tracesnap(tr, 0)
snapref = snap[0]
snapno = 0
end
for ins=1,nins do
if ins >= snapref then
if dumpreg then
out:write(format(".... SNAP #%-3d [ ", snapno))
else
out:write(format(".... SNAP #%-3d [ ", snapno))
end
printsnap(tr, snap)
snapno = snapno + 1
snap = tracesnap(tr, snapno)
snapref = snap and snap[0] or 65536
end
local m, ot, op1, op2, ridsp = traceir(tr, ins)
local oidx, t = 6*shr(ot, 8), band(ot, 31)
local op = sub(irnames, oidx+1, oidx+6)
if op == "LOOP " then
if dumpreg then
out:write(format("%04d ------------ LOOP ------------\n", ins))
else
out:write(format("%04d ------ LOOP ------------\n", ins))
end
elseif op ~= "NOP " and op ~= "CARG " and
(dumpreg or op ~= "RENAME") then
local rid = band(ridsp, 255)
if dumpreg then
out:write(format("%04d %-6s", ins, ridsp_name(ridsp, ins)))
else
out:write(format("%04d ", ins))
end
out:write(format("%s%s %s %s ",
(rid == 254 or rid == 253) and "}" or
(band(ot, 128) == 0 and " " or ">"),
band(ot, 64) == 0 and " " or "+",
irtype[t], op))
local m1, m2 = band(m, 3), band(m, 3*4)
if sub(op, 1, 4) == "CALL" then
local ctype
if m2 == 1*4 then -- op2 == IRMlit
out:write(format("%-10s (", vmdef.ircall[op2]))
else
ctype = dumpcallfunc(tr, op2)
end
if op1 ~= -1 then dumpcallargs(tr, op1) end
out:write(")")
if ctype then out:write(" ctype ", ctype) end
elseif op == "CNEW " and op2 == -1 then
out:write(formatk(tr, op1))
elseif m1 ~= 3 then -- op1 != IRMnone
if op1 < 0 then
out:write(formatk(tr, op1))
else
out:write(format(m1 == 0 and "%04d" or "#%-3d", op1))
end
if m2 ~= 3*4 then -- op2 != IRMnone
if m2 == 1*4 then -- op2 == IRMlit
local litn = litname[op]
if litn and litn[op2] then
out:write(" ", litn[op2])
elseif op == "UREFO " or op == "UREFC " then
out:write(format(" #%-3d", shr(op2, 8)))
else
out:write(format(" #%-3d", op2))
end
elseif op2 < 0 then
out:write(" ", formatk(tr, op2))
else
out:write(format(" %04d", op2))
end
end
end
out:write("\n")
end
end
if snap then
if dumpreg then
out:write(format(".... SNAP #%-3d [ ", snapno))
else
out:write(format(".... SNAP #%-3d [ ", snapno))
end
printsnap(tr, snap)
end
end
------------------------------------------------------------------------------
local recprefix = ""
local recdepth = 0
-- Format trace error message.
local function fmterr(err, info)
if type(err) == "number" then
if type(info) == "function" then info = fmtfunc(info) end
local fmt = vmdef.traceerr[err]
if fmt == "NYI: bytecode %s" then
local oidx = 6 * info
info = sub(vmdef.bcnames, oidx+1, oidx+6)
end
err = format(fmt, info)
end
return err
end
-- Dump trace states.
local function dump_trace(what, tr, func, pc, otr, oex)
if what == "stop" or (what == "abort" and dumpmode.a) then
if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop")
elseif dumpmode.s then dump_snap(tr) end
if dumpmode.m then dump_mcode(tr) end
end
if what == "start" then
if dumpmode.H then out:write('<pre class="ljdump">\n') end
out:write("---- TRACE ", tr, " ", what)
if otr then out:write(" ", otr, "/", oex == -1 and "stitch" or oex) end
out:write(" ", fmtfunc(func, pc), "\n")
elseif what == "stop" or what == "abort" then
out:write("---- TRACE ", tr, " ", what)
if what == "abort" then
out:write(" ", fmtfunc(func, pc), " -- ", fmterr(otr, oex), "\n")
else
local info = traceinfo(tr)
local link, ltype = info.link, info.linktype
if link == tr or link == 0 then
out:write(" -> ", ltype, "\n")
elseif ltype == "root" then
out:write(" -> ", link, "\n")
else
out:write(" -> ", link, " ", ltype, "\n")
end
end
if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end
else
if what == "flush" then symtab, nexitsym = {}, 0 end
out:write("---- TRACE ", what, "\n\n")
end
out:flush()
end
-- Dump recorded bytecode.
local function dump_record(tr, func, pc, depth)
if depth ~= recdepth then
recdepth = depth
recprefix = rep(" .", depth)
end
local line
if pc >= 0 then
line = bcline(func, pc, recprefix)
if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end
if pc > 0 then
line = sub(line, 1, -2) .. " (" .. fmtfunc(func, pc) .. ")\n"
end
else
line = "0000 "..recprefix.." FUNCC \n"
end
if pc <= 0 then
out:write(sub(line, 1, -2), " ; ", fmtfunc(func), "\n")
else
out:write(line)
end
if pc >= 0 and band(funcbc(func, pc), 0xff) < 16 then -- ORDER BC
out:write(bcline(func, pc+1, recprefix)) -- Write JMP for cond.
end
end
------------------------------------------------------------------------------
local gpr64 = jit.arch:match("64")
local fprmips32 = jit.arch == "mips" or jit.arch == "mipsel"
-- Dump taken trace exits.
local function dump_texit(tr, ex, ngpr, nfpr, ...)
out:write("---- TRACE ", tr, " exit ", ex, "\n")
if dumpmode.X then
local regs = {...}
if gpr64 then
for i=1,ngpr do
out:write(format(" %016x", regs[i]))
if i % 4 == 0 then out:write("\n") end
end
else
for i=1,ngpr do
out:write(" ", tohex(regs[i]))
if i % 8 == 0 then out:write("\n") end
end
end
if fprmips32 then
for i=1,nfpr,2 do
out:write(format(" %+17.14g", regs[ngpr+i]))
if i % 8 == 7 then out:write("\n") end
end
else
for i=1,nfpr do
out:write(format(" %+17.14g", regs[ngpr+i]))
if i % 4 == 0 then out:write("\n") end
end
end
end
end
------------------------------------------------------------------------------
-- Detach dump handlers.
local function dumpoff()
if active then
active = false
jit.attach(dump_texit)
jit.attach(dump_record)
jit.attach(dump_trace)
if out and out ~= stdout and out ~= stderr then out:close() end
out = nil
end
end
-- Open the output file and attach dump handlers.
local function dumpon(opt, outfile)
if active then dumpoff() end
local term = os.getenv("TERM")
local colormode = (term and term:match("color") or os.getenv("COLORTERM")) and "A" or "T"
if opt then
opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end)
end
local m = { t=true, b=true, i=true, m=true, }
if opt and opt ~= "" then
local o = sub(opt, 1, 1)
if o ~= "+" and o ~= "-" then m = {} end
for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end
end
dumpmode = m
if m.t or m.b or m.i or m.s or m.m then
jit.attach(dump_trace, "trace")
end
if m.b then
jit.attach(dump_record, "record")
if not bcline then bcline = require("jit.bc").line end
end
if m.x or m.X then
jit.attach(dump_texit, "texit")
end
if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end
if outfile then
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
else
out = stdout
end
m[colormode] = true
if colormode == "A" then
colorize = colorize_ansi
irtype = irtype_ansi
elseif colormode == "H" then
colorize = colorize_html
irtype = irtype_html
out:write(header_html)
else
colorize = colorize_text
irtype = irtype_text
end
active = true
end
-- Public module functions.
return {
on = dumpon,
off = dumpoff,
start = dumpon -- For -j command line option.
}

View File

@@ -0,0 +1,309 @@
----------------------------------------------------------------------------
-- LuaJIT profiler.
--
-- Copyright (C) 2005-2025 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
--
-- This module is a simple command line interface to the built-in
-- low-overhead profiler of LuaJIT.
--
-- The lower-level API of the profiler is accessible via the "jit.profile"
-- module or the luaJIT_profile_* C API.
--
-- Example usage:
--
-- luajit -jp myapp.lua
-- luajit -jp=s myapp.lua
-- luajit -jp=-s myapp.lua
-- luajit -jp=vl myapp.lua
-- luajit -jp=G,profile.txt myapp.lua
--
-- The following dump features are available:
--
-- f Stack dump: function name, otherwise module:line. Default mode.
-- F Stack dump: ditto, but always prepend module.
-- l Stack dump: module:line.
-- <number> stack dump depth (callee < caller). Default: 1.
-- -<number> Inverse stack dump depth (caller > callee).
-- s Split stack dump after first stack level. Implies abs(depth) >= 2.
-- p Show full path for module names.
-- v Show VM states. Can be combined with stack dumps, e.g. vf or fv.
-- z Show zones. Can be combined with stack dumps, e.g. zf or fz.
-- r Show raw sample counts. Default: show percentages.
-- a Annotate excerpts from source code files.
-- A Annotate complete source code files.
-- G Produce raw output suitable for graphical tools (e.g. flame graphs).
-- m<number> Minimum sample percentage to be shown. Default: 3.
-- i<number> Sampling interval in milliseconds. Default: 10.
--
----------------------------------------------------------------------------
-- Cache some library functions and objects.
local jit = require("jit")
local profile = require("jit.profile")
local vmdef = require("jit.vmdef")
local math = math
local pairs, ipairs, tonumber, floor = pairs, ipairs, tonumber, math.floor
local sort, format = table.sort, string.format
local stdout = io.stdout
local zone -- Load jit.zone module on demand.
-- Output file handle.
local out
------------------------------------------------------------------------------
local prof_ud
local prof_states, prof_split, prof_min, prof_raw, prof_fmt, prof_depth
local prof_ann, prof_count1, prof_count2, prof_samples
local map_vmmode = {
N = "Compiled",
I = "Interpreted",
C = "C code",
G = "Garbage Collector",
J = "JIT Compiler",
}
-- Profiler callback.
local function prof_cb(th, samples, vmmode)
prof_samples = prof_samples + samples
local key_stack, key_stack2, key_state
-- Collect keys for sample.
if prof_states then
if prof_states == "v" then
key_state = map_vmmode[vmmode] or vmmode
else
key_state = zone:get() or "(none)"
end
end
if prof_fmt then
key_stack = profile.dumpstack(th, prof_fmt, prof_depth)
key_stack = key_stack:gsub("%[builtin#(%d+)%]", function(x)
return vmdef.ffnames[tonumber(x)]
end)
if prof_split == 2 then
local k1, k2 = key_stack:match("(.-) [<>] (.*)")
if k2 then key_stack, key_stack2 = k1, k2 end
elseif prof_split == 3 then
key_stack2 = profile.dumpstack(th, "l", 1)
end
end
-- Order keys.
local k1, k2
if prof_split == 1 then
if key_state then
k1 = key_state
if key_stack then k2 = key_stack end
end
elseif key_stack then
k1 = key_stack
if key_stack2 then k2 = key_stack2 elseif key_state then k2 = key_state end
end
-- Coalesce samples in one or two levels.
if k1 then
local t1 = prof_count1
t1[k1] = (t1[k1] or 0) + samples
if k2 then
local t2 = prof_count2
local t3 = t2[k1]
if not t3 then t3 = {}; t2[k1] = t3 end
t3[k2] = (t3[k2] or 0) + samples
end
end
end
------------------------------------------------------------------------------
-- Show top N list.
local function prof_top(count1, count2, samples, indent)
local t, n = {}, 0
for k in pairs(count1) do
n = n + 1
t[n] = k
end
sort(t, function(a, b) return count1[a] > count1[b] end)
for i=1,n do
local k = t[i]
local v = count1[k]
local pct = floor(v*100/samples + 0.5)
if pct < prof_min then break end
if not prof_raw then
out:write(format("%s%2d%% %s\n", indent, pct, k))
elseif prof_raw == "r" then
out:write(format("%s%5d %s\n", indent, v, k))
else
out:write(format("%s %d\n", k, v))
end
if count2 then
local r = count2[k]
if r then
prof_top(r, nil, v, (prof_split == 3 or prof_split == 1) and " -- " or
(prof_depth < 0 and " -> " or " <- "))
end
end
end
end
-- Annotate source code
local function prof_annotate(count1, samples)
local files = {}
local ms = 0
for k, v in pairs(count1) do
local pct = floor(v*100/samples + 0.5)
ms = math.max(ms, v)
if pct >= prof_min then
local file, line = k:match("^(.*):(%d+)$")
if not file then file = k; line = 0 end
local fl = files[file]
if not fl then fl = {}; files[file] = fl; files[#files+1] = file end
line = tonumber(line)
fl[line] = prof_raw and v or pct
end
end
sort(files)
local fmtv, fmtn = " %3d%% | %s\n", " | %s\n"
if prof_raw then
local n = math.max(5, math.ceil(math.log10(ms)))
fmtv = "%"..n.."d | %s\n"
fmtn = (" "):rep(n).." | %s\n"
end
local ann = prof_ann
for _, file in ipairs(files) do
local f0 = file:byte()
if f0 == 40 or f0 == 91 then
out:write(format("\n====== %s ======\n[Cannot annotate non-file]\n", file))
break
end
local fp, err = io.open(file)
if not fp then
out:write(format("====== ERROR: %s: %s\n", file, err))
break
end
out:write(format("\n====== %s ======\n", file))
local fl = files[file]
local n, show = 1, false
if ann ~= 0 then
for i=1,ann do
if fl[i] then show = true; out:write("@@ 1 @@\n"); break end
end
end
for line in fp:lines() do
if line:byte() == 27 then
out:write("[Cannot annotate bytecode file]\n")
break
end
local v = fl[n]
if ann ~= 0 then
local v2 = fl[n+ann]
if show then
if v2 then show = n+ann elseif v then show = n
elseif show+ann < n then show = false end
elseif v2 then
show = n+ann
out:write(format("@@ %d @@\n", n))
end
if not show then goto next end
end
if v then
out:write(format(fmtv, v, line))
else
out:write(format(fmtn, line))
end
::next::
n = n + 1
end
fp:close()
end
end
------------------------------------------------------------------------------
-- Finish profiling and dump result.
local function prof_finish()
if prof_ud then
profile.stop()
local samples = prof_samples
if samples == 0 then
if prof_raw ~= true then out:write("[No samples collected]\n") end
elseif prof_ann then
prof_annotate(prof_count1, samples)
else
prof_top(prof_count1, prof_count2, samples, "")
end
prof_count1 = nil
prof_count2 = nil
prof_ud = nil
if out ~= stdout then out:close() end
end
end
-- Start profiling.
local function prof_start(mode)
local interval = ""
mode = mode:gsub("i%d*", function(s) interval = s; return "" end)
prof_min = 3
mode = mode:gsub("m(%d+)", function(s) prof_min = tonumber(s); return "" end)
prof_depth = 1
mode = mode:gsub("%-?%d+", function(s) prof_depth = tonumber(s); return "" end)
local m = {}
for c in mode:gmatch(".") do m[c] = c end
prof_states = m.z or m.v
if prof_states == "z" then zone = require("jit.zone") end
local scope = m.l or m.f or m.F or (prof_states and "" or "f")
local flags = (m.p or "")
prof_raw = m.r
if m.s then
prof_split = 2
if prof_depth == -1 or m["-"] then prof_depth = -2
elseif prof_depth == 1 then prof_depth = 2 end
elseif mode:find("[fF].*l") then
scope = "l"
prof_split = 3
else
prof_split = (scope == "" or mode:find("[zv].*[lfF]")) and 1 or 0
end
prof_ann = m.A and 0 or (m.a and 3)
if prof_ann then
scope = "l"
prof_fmt = "pl"
prof_split = 0
prof_depth = 1
elseif m.G and scope ~= "" then
prof_fmt = flags..scope.."Z;"
prof_depth = -100
prof_raw = true
prof_min = 0
elseif scope == "" then
prof_fmt = false
else
local sc = prof_split == 3 and m.f or m.F or scope
prof_fmt = flags..sc..(prof_depth >= 0 and "Z < " or "Z > ")
end
prof_count1 = {}
prof_count2 = {}
prof_samples = 0
profile.start(scope:lower()..interval, prof_cb)
prof_ud = newproxy(true)
getmetatable(prof_ud).__gc = prof_finish
end
------------------------------------------------------------------------------
local function start(mode, outfile)
if not outfile then outfile = os.getenv("LUAJIT_PROFILEFILE") end
if outfile then
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
else
out = stdout
end
prof_start(mode or "f")
end
-- Public module functions.
return {
start = start, -- For -j command line option.
stop = prof_finish
}

View File

@@ -0,0 +1,174 @@
----------------------------------------------------------------------------
-- Verbose mode of the LuaJIT compiler.
--
-- Copyright (C) 2005-2025 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
--
-- This module shows verbose information about the progress of the
-- JIT compiler. It prints one line for each generated trace. This module
-- is useful to see which code has been compiled or where the compiler
-- punts and falls back to the interpreter.
--
-- Example usage:
--
-- luajit -jv -e "for i=1,1000 do for j=1,1000 do end end"
-- luajit -jv=myapp.out myapp.lua
--
-- Default output is to stderr. To redirect the output to a file, pass a
-- filename as an argument (use '-' for stdout) or set the environment
-- variable LUAJIT_VERBOSEFILE. The file is overwritten every time the
-- module is started.
--
-- The output from the first example should look like this:
--
-- [TRACE 1 (command line):1 loop]
-- [TRACE 2 (1/3) (command line):1 -> 1]
--
-- The first number in each line is the internal trace number. Next are
-- the file name ('(command line)') and the line number (':1') where the
-- trace has started. Side traces also show the parent trace number and
-- the exit number where they are attached to in parentheses ('(1/3)').
-- An arrow at the end shows where the trace links to ('-> 1'), unless
-- it loops to itself.
--
-- In this case the inner loop gets hot and is traced first, generating
-- a root trace. Then the last exit from the 1st trace gets hot, too,
-- and triggers generation of the 2nd trace. The side trace follows the
-- path along the outer loop and *around* the inner loop, back to its
-- start, and then links to the 1st trace. Yes, this may seem unusual,
-- if you know how traditional compilers work. Trace compilers are full
-- of surprises like this -- have fun! :-)
--
-- Aborted traces are shown like this:
--
-- [TRACE --- foo.lua:44 -- leaving loop in root trace at foo:lua:50]
--
-- Don't worry -- trace aborts are quite common, even in programs which
-- can be fully compiled. The compiler may retry several times until it
-- finds a suitable trace.
--
-- Of course this doesn't work with features that are not-yet-implemented
-- (NYI error messages). The VM simply falls back to the interpreter. This
-- may not matter at all if the particular trace is not very high up in
-- the CPU usage profile. Oh, and the interpreter is quite fast, too.
--
-- Also check out the -jdump module, which prints all the gory details.
--
------------------------------------------------------------------------------
-- Cache some library functions and objects.
local jit = require("jit")
local jutil = require("jit.util")
local vmdef = require("jit.vmdef")
local funcinfo, traceinfo = jutil.funcinfo, jutil.traceinfo
local type, sub, format = type, string.sub, string.format
local stdout, stderr = io.stdout, io.stderr
-- Active flag and output file handle.
local active, out
------------------------------------------------------------------------------
local startloc, startex
local function fmtfunc(func, pc)
local fi = funcinfo(func, pc)
if fi.loc then
return fi.loc
elseif fi.ffid then
return vmdef.ffnames[fi.ffid]
elseif fi.addr then
return format("C:%x", fi.addr)
else
return "(?)"
end
end
-- Format trace error message.
local function fmterr(err, info)
if type(err) == "number" then
if type(info) == "function" then info = fmtfunc(info) end
local fmt = vmdef.traceerr[err]
if fmt == "NYI: bytecode %s" then
local oidx = 6 * info
info = sub(vmdef.bcnames, oidx+1, oidx+6)
end
err = format(fmt, info)
end
return err
end
-- Dump trace states.
local function dump_trace(what, tr, func, pc, otr, oex)
if what == "start" then
startloc = fmtfunc(func, pc)
startex = otr and "("..otr.."/"..(oex == -1 and "stitch" or oex)..") " or ""
else
if what == "abort" then
local loc = fmtfunc(func, pc)
if loc ~= startloc then
out:write(format("[TRACE --- %s%s -- %s at %s]\n",
startex, startloc, fmterr(otr, oex), loc))
else
out:write(format("[TRACE --- %s%s -- %s]\n",
startex, startloc, fmterr(otr, oex)))
end
elseif what == "stop" then
local info = traceinfo(tr)
local link, ltype = info.link, info.linktype
if ltype == "interpreter" then
out:write(format("[TRACE %3s %s%s -- fallback to interpreter]\n",
tr, startex, startloc))
elseif ltype == "stitch" then
out:write(format("[TRACE %3s %s%s %s %s]\n",
tr, startex, startloc, ltype, fmtfunc(func, pc)))
elseif link == tr or link == 0 then
out:write(format("[TRACE %3s %s%s %s]\n",
tr, startex, startloc, ltype))
elseif ltype == "root" then
out:write(format("[TRACE %3s %s%s -> %d]\n",
tr, startex, startloc, link))
else
out:write(format("[TRACE %3s %s%s -> %d %s]\n",
tr, startex, startloc, link, ltype))
end
else
out:write(format("[TRACE %s]\n", what))
end
out:flush()
end
end
------------------------------------------------------------------------------
-- Detach dump handlers.
local function dumpoff()
if active then
active = false
jit.attach(dump_trace)
if out and out ~= stdout and out ~= stderr then out:close() end
out = nil
end
end
-- Open the output file and attach dump handlers.
local function dumpon(outfile)
if active then dumpoff() end
if not outfile then outfile = os.getenv("LUAJIT_VERBOSEFILE") end
if outfile then
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
else
out = stderr
end
jit.attach(dump_trace, "trace")
active = true
end
-- Public module functions.
return {
on = dumpon,
off = dumpoff,
start = dumpon -- For -j command line option.
}

View File

@@ -0,0 +1,407 @@
-- This is a generated file. DO NOT EDIT!
assert(require("jit").version == "LuaJIT 2.1.1744683895", "LuaJIT core/library version mismatch")
return {
bcnames = "ISLT ISGE ISLE ISGT ISEQV ISNEV ISEQS ISNES ISEQN ISNEN ISEQP ISNEP ISTC ISFC IST ISF ISTYPEISNUM MOV NOT UNM LEN ADDVN SUBVN MULVN DIVVN MODVN ADDNV SUBNV MULNV DIVNV MODNV ADDVV SUBVV MULVV DIVVV MODVV POW CAT KSTR KCDATAKSHORTKNUM KPRI KNIL UGET USETV USETS USETN USETP UCLO FNEW TNEW TDUP GGET GSET TGETV TGETS TGETB TGETR TSETV TSETS TSETB TSETM TSETR CALLM CALL CALLMTCALLT ITERC ITERN VARG ISNEXTRETM RET RET0 RET1 FORI JFORI FORL IFORL JFORL ITERL IITERLJITERLLOOP ILOOP JLOOP JMP FUNCF IFUNCFJFUNCFFUNCV IFUNCVJFUNCVFUNCC FUNCCW",
irnames = "LT GE LE GT ULT UGE ULE UGT EQ NE ABC RETF NOP BASE PVAL GCSTEPHIOP LOOP USE PHI RENAMEPROF KPRI KINT KGC KPTR KKPTR KNULL KNUM KINT64KSLOT BNOT BSWAP BAND BOR BXOR BSHL BSHR BSAR BROL BROR ADD SUB MUL DIV MOD POW NEG ABS LDEXP MIN MAX FPMATHADDOV SUBOV MULOV AREF HREFK HREF NEWREFUREFO UREFC FREF TMPREFSTRREFLREF ALOAD HLOAD ULOAD FLOAD XLOAD SLOAD VLOAD ALEN ASTOREHSTOREUSTOREFSTOREXSTORESNEW XSNEW TNEW TDUP CNEW CNEWI BUFHDRBUFPUTBUFSTRTBAR OBAR XBAR CONV TOBIT TOSTR STRTO CALLN CALLA CALLL CALLS CALLXSCARG ",
irfpm = { [0]="floor", "ceil", "trunc", "sqrt", "log", "log2", "other", },
irfield = { [0]="str.len", "func.env", "func.pc", "func.ffid", "thread.env", "thread.exdata", "thread.exdata2", "tab.meta", "tab.array", "tab.node", "tab.asize", "tab.hmask", "tab.nomm", "udata.meta", "udata.udtype", "udata.file", "sbuf.w", "sbuf.e", "sbuf.b", "sbuf.l", "sbuf.ref", "sbuf.r", "cdata.ctypeid", "cdata.ptr", "cdata.int", "cdata.int64", "cdata.int64_4", },
ircall = {
[0]="lj_str_cmp",
"lj_str_find",
"lj_str_new",
"lj_strscan_num",
"lj_strfmt_int",
"lj_strfmt_num",
"lj_strfmt_char",
"lj_strfmt_putint",
"lj_strfmt_putnum",
"lj_strfmt_putquoted",
"lj_strfmt_putfxint",
"lj_strfmt_putfnum_int",
"lj_strfmt_putfnum_uint",
"lj_strfmt_putfnum",
"lj_strfmt_putfstr",
"lj_strfmt_putfchar",
"lj_buf_putmem",
"lj_buf_putstr",
"lj_buf_putchar",
"lj_buf_putstr_reverse",
"lj_buf_putstr_lower",
"lj_buf_putstr_upper",
"lj_buf_putstr_rep",
"lj_buf_puttab",
"lj_bufx_set",
"lj_bufx_more",
"lj_serialize_put",
"lj_serialize_get",
"lj_serialize_encode",
"lj_serialize_decode",
"lj_buf_tostr",
"lj_tab_new_ah",
"lj_tab_new1",
"lj_tab_dup",
"lj_tab_clear",
"lj_tab_newkey",
"lj_tab_keyindex",
"lj_vm_next",
"lj_tab_len",
"lj_tab_clone",
"lj_tab_isarray",
"lj_tab_nkeys",
"lj_tab_isempty",
"lj_tab_len_hint",
"lj_gc_step_jit",
"lj_gc_barrieruv",
"lj_mem_newgco",
"lj_prng_u64d",
"lj_vm_modi",
"log10",
"exp",
"sin",
"cos",
"tan",
"asin",
"acos",
"atan",
"sinh",
"cosh",
"tanh",
"fputc",
"fwrite",
"fflush",
"lj_vm_floor",
"lj_vm_ceil",
"lj_vm_trunc",
"sqrt",
"log",
"lj_vm_log2",
"pow",
"atan2",
"ldexp",
"lj_vm_tobit",
"softfp_add",
"softfp_sub",
"softfp_mul",
"softfp_div",
"softfp_cmp",
"softfp_i2d",
"softfp_d2i",
"lj_vm_sfmin",
"lj_vm_sfmax",
"lj_vm_tointg",
"softfp_ui2d",
"softfp_f2d",
"softfp_d2ui",
"softfp_d2f",
"softfp_i2f",
"softfp_ui2f",
"softfp_f2i",
"softfp_f2ui",
"fp64_l2d",
"fp64_ul2d",
"fp64_l2f",
"fp64_ul2f",
"fp64_d2l",
"fp64_d2ul",
"fp64_f2l",
"fp64_f2ul",
"lj_carith_divi64",
"lj_carith_divu64",
"lj_carith_modi64",
"lj_carith_modu64",
"lj_carith_powi64",
"lj_carith_powu64",
"lj_cdata_newv",
"lj_cdata_setfin",
"strlen",
"memcpy",
"memset",
"lj_vm_errno",
"lj_carith_mul64",
"lj_carith_shl64",
"lj_carith_shr64",
"lj_carith_sar64",
"lj_carith_rol64",
"lj_carith_ror64",
},
traceerr = {
[0]="error thrown or hook called during recording",
"trace too short",
"trace too long",
"trace too deep",
"too many snapshots",
"blacklisted",
"retry recording",
"NYI: bytecode %s",
"leaving loop in root trace",
"inner loop in root trace",
"loop unroll limit reached",
"bad argument type",
"JIT compilation disabled for function",
"call unroll limit reached",
"down-recursion, restarting",
"NYI: unsupported variant of FastFunc %s",
"NYI: return to lower frame",
"store with nil or NaN key",
"missing metamethod",
"looping index lookup",
"NYI: mixed sparse/dense table",
"symbol not in cache",
"NYI: unsupported C type conversion",
"NYI: unsupported C function type",
"guard would always fail",
"too many PHIs",
"persistent type instability",
"failed to allocate mcode memory",
"machine code too long",
"hit mcode limit (retrying)",
"too many spill slots",
"inconsistent register allocation",
"NYI: cannot assemble IR instruction %d",
"NYI: PHI shuffling too complex",
"NYI: register coalescing too complex",
},
ffnames = {
[0]="Lua",
"C",
"assert",
"type",
"next",
"pairs",
"ipairs_aux",
"ipairs",
"getmetatable",
"setmetatable",
"getfenv",
"setfenv",
"rawget",
"rawset",
"rawequal",
"unpack",
"select",
"tonumber",
"tostring",
"error",
"pcall",
"xpcall",
"loadfile",
"load",
"loadstring",
"dofile",
"gcinfo",
"collectgarbage",
"newproxy",
"print",
"coroutine.status",
"coroutine.running",
"coroutine.isyieldable",
"coroutine.create",
"coroutine.yield",
"coroutine.resume",
"coroutine.wrap_aux",
"coroutine.wrap",
"thread.exdata",
"thread.exdata2",
"math.abs",
"math.floor",
"math.ceil",
"math.sqrt",
"math.log10",
"math.exp",
"math.sin",
"math.cos",
"math.tan",
"math.asin",
"math.acos",
"math.atan",
"math.sinh",
"math.cosh",
"math.tanh",
"math.frexp",
"math.modf",
"math.log",
"math.atan2",
"math.pow",
"math.fmod",
"math.ldexp",
"math.min",
"math.max",
"math.random",
"math.randomseed",
"bit.tobit",
"bit.bnot",
"bit.bswap",
"bit.lshift",
"bit.rshift",
"bit.arshift",
"bit.rol",
"bit.ror",
"bit.band",
"bit.bor",
"bit.bxor",
"bit.tohex",
"string.byte",
"string.char",
"string.sub",
"string.rep",
"string.reverse",
"string.lower",
"string.upper",
"string.dump",
"string.find",
"string.match",
"string.gmatch_aux",
"string.gmatch",
"string.gsub",
"string.format",
"table.maxn",
"table.insert",
"table.concat",
"table.clone",
"table.isarray",
"table.nkeys",
"table.isempty",
"table.sort",
"table.new",
"table.clear",
"io.method.close",
"io.method.read",
"io.method.write",
"io.method.flush",
"io.method.seek",
"io.method.setvbuf",
"io.method.lines",
"io.method.__gc",
"io.method.__tostring",
"io.open",
"io.popen",
"io.tmpfile",
"io.close",
"io.read",
"io.write",
"io.flush",
"io.input",
"io.output",
"io.lines",
"io.type",
"os.execute",
"os.remove",
"os.rename",
"os.tmpname",
"os.getenv",
"os.exit",
"os.clock",
"os.date",
"os.time",
"os.difftime",
"os.setlocale",
"debug.getregistry",
"debug.getmetatable",
"debug.setmetatable",
"debug.getfenv",
"debug.setfenv",
"debug.getinfo",
"debug.getlocal",
"debug.setlocal",
"debug.getupvalue",
"debug.setupvalue",
"debug.upvalueid",
"debug.upvaluejoin",
"debug.sethook",
"debug.gethook",
"debug.debug",
"debug.traceback",
"jit.on",
"jit.off",
"jit.flush",
"jit.status",
"jit.security",
"jit.attach",
"jit.prngstate",
"jit.util.funcinfo",
"jit.util.funcbc",
"jit.util.funck",
"jit.util.funcuvname",
"jit.util.traceinfo",
"jit.util.traceir",
"jit.util.tracek",
"jit.util.tracesnap",
"jit.util.tracemc",
"jit.util.traceexitstub",
"jit.util.ircalladdr",
"jit.opt.start",
"jit.profile.start",
"jit.profile.stop",
"jit.profile.dumpstack",
"ffi.meta.__index",
"ffi.meta.__newindex",
"ffi.meta.__eq",
"ffi.meta.__len",
"ffi.meta.__lt",
"ffi.meta.__le",
"ffi.meta.__concat",
"ffi.meta.__call",
"ffi.meta.__add",
"ffi.meta.__sub",
"ffi.meta.__mul",
"ffi.meta.__div",
"ffi.meta.__mod",
"ffi.meta.__pow",
"ffi.meta.__unm",
"ffi.meta.__tostring",
"ffi.meta.__pairs",
"ffi.meta.__ipairs",
"ffi.clib.__index",
"ffi.clib.__newindex",
"ffi.clib.__gc",
"ffi.callback.free",
"ffi.callback.set",
"ffi.cdef",
"ffi.new",
"ffi.cast",
"ffi.typeof",
"ffi.typeinfo",
"ffi.istype",
"ffi.sizeof",
"ffi.alignof",
"ffi.offsetof",
"ffi.errno",
"ffi.string",
"ffi.copy",
"ffi.fill",
"ffi.abi",
"ffi.metatype",
"ffi.gc",
"ffi.load",
"buffer.method.free",
"buffer.method.reset",
"buffer.method.skip",
"buffer.method.set",
"buffer.method.put",
"buffer.method.putf",
"buffer.method.get",
"buffer.method.putcdata",
"buffer.method.reserve",
"buffer.method.commit",
"buffer.method.ref",
"buffer.method.encode",
"buffer.method.decode",
"buffer.method.__gc",
"buffer.method.__tostring",
"buffer.method.__len",
"buffer.new",
"buffer.encode",
"buffer.decode",
},
}

View File

@@ -0,0 +1,45 @@
----------------------------------------------------------------------------
-- LuaJIT profiler zones.
--
-- Copyright (C) 2005-2025 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
--
-- This module implements a simple hierarchical zone model.
--
-- Example usage:
--
-- local zone = require("jit.zone")
-- zone("AI")
-- ...
-- zone("A*")
-- ...
-- print(zone:get()) --> "A*"
-- ...
-- zone()
-- ...
-- print(zone:get()) --> "AI"
-- ...
-- zone()
--
----------------------------------------------------------------------------
local remove = table.remove
return setmetatable({
flush = function(t)
for i=#t,1,-1 do t[i] = nil end
end,
get = function(t)
return t[#t]
end
}, {
__call = function(t, zone)
if zone then
t[#t+1] = zone
else
return (assert(remove(t), "empty zone stack"))
end
end
})

View File

@@ -0,0 +1,88 @@
.TH luajit 1 "" "" "LuaJIT documentation"
.SH NAME
luajit \- Just-In-Time Compiler for the Lua Language
\fB
.SH SYNOPSIS
.B luajit
[\fIoptions\fR]... [\fIscript\fR [\fIargs\fR]...]
.SH "WEB SITE"
.IR https://luajit.org
.SH DESCRIPTION
.PP
This is the command-line program to run Lua programs with \fBLuaJIT\fR.
.PP
\fBLuaJIT\fR is a just-in-time (JIT) compiler for the Lua language.
The virtual machine (VM) is based on a fast interpreter combined with
a trace compiler. It can significantly improve the performance of Lua programs.
.PP
\fBLuaJIT\fR is API\- and ABI-compatible with the VM of the standard
Lua\ 5.1 interpreter. When embedding the VM into an application,
the built library can be used as a drop-in replacement.
.SH OPTIONS
.TP
.BI "\-e " chunk
Run the given chunk of Lua code.
.TP
.BI "\-l " library
Load the named library, just like \fBrequire("\fR\fIlibrary\fR\fB")\fR.
.TP
.BI "\-b " ...
Save or list bytecode. Run without arguments to get help on options.
.TP
.BI "\-j " command
Perform LuaJIT control command (optional space after \fB\-j\fR).
.TP
.BI "\-O" [opt]
Control LuaJIT optimizations.
.TP
.B "\-i"
Run in interactive mode.
.TP
.B "\-v"
Show \fBLuaJIT\fR version.
.TP
.B "\-E"
Ignore environment variables.
.TP
.B "\-\-"
Stop processing options.
.TP
.B "\-"
Read script from stdin instead.
.PP
After all options are processed, the given \fIscript\fR is run.
The arguments are passed in the global \fIarg\fR table.
.PP
Interactive mode is only entered, if no \fIscript\fR and no \fB\-e\fR
option is given. Interactive mode can be left with EOF (\fICtrl\-Z\fB).
.SH EXAMPLES
.TP
luajit hello.lua world
Prints "Hello world", assuming \fIhello.lua\fR contains:
.br
print("Hello", arg[1])
.TP
luajit \-e "local x=0; for i=1,1e9 do x=x+i end; print(x)"
Calculates the sum of the numbers from 1 to 1000000000.
.br
And finishes in a reasonable amount of time, too.
.TP
luajit \-jv \-e "for i=1,10 do for j=1,10 do for k=1,100 do end end end"
Runs some nested loops and shows the resulting traces.
.SH COPYRIGHT
.PP
\fBLuaJIT\fR is Copyright \(co 2005-2025 Mike Pall.
.br
\fBLuaJIT\fR is open source software, released under the MIT license.
.SH SEE ALSO
.PP
More details in the provided HTML docs or at:
.IR https://luajit.org
.br
More about the Lua language can be found at:
.IR https://lua.org/docs.html
.PP
lua(1)

View File

@@ -0,0 +1 @@
venv

View File

View File

@@ -0,0 +1,568 @@
from flask import (
Flask,
render_template,
request,
redirect,
session,
jsonify,
url_for,
flash
)
import os
import subprocess
import random
import string
import logging
import requests
import secrets
import datetime
import itertools
import re
app = Flask(__name__)
app.secret_key = secrets.token_hex(16)
@app.template_filter('format_number')
def format_number(value):
return "{:,}".format(value).replace(",", ".")
@app.template_filter('normalized_active')
def normalized_active(value):
return os.path.isfile(os.path.join(get_hostfolder(), 'locations.d', value))
class Config:
def __init__(self):
self.FOLDER = '/var/cache/url-shortener'
if os.path.isfile('/etc/url-shortener/conf.sh'):
cfgmap = {
'URL_SHORTENER_CACHE_BASE': 'FOLDER',
}
with open('/etc/url-shortener/conf.sh') as cf:
while line := cf.readline().strip():
if not line or line.startswith('#'):
continue
mapname, value = line.split('=', 1)
if mapname in cfgmap:
setattr(self, cfgmap[mapname], value.strip('"\''))
logger = logging.getLogger(__name__)
config = Config()
FOLDER = config.FOLDER
def session_host():
try:
h = session['host']
except:
host, _err = exec_cmd(f"ls -1 {FOLDER}", return_first=True)
session['host'] = host or 'default'
return session['host']
def get_hostfolder():
return f"{FOLDER}/{session_host()}"
def generate_random_hash(length=10):
"""Just generates a Hash"""
characters = string.ascii_letters + string.digits
hash_value = ''.join(random.choice(characters) for _ in range(length))
return hash_value
def get_paginator(pages_total, page, page_limit=10):
"""Makes a paginator, that shows maximum 10 Items"""
if not pages_total:
return [], 0, 0
paginator = []
# Wenn die aktuelle Seite die erste oder letzte ist, zeige die ersten/letzten 10 Seiten an
if page <= 5:
paginator = list(range(1, min(pages_total + 1, 11)))
elif page >= pages_total - 4:
paginator = list(range(max(1, pages_total - 9), pages_total + 1))
else:
# Ansonsten zeige die aktuelle Seite an der 3. Stelle und die 7 folgenden Seiten
paginator = [page - 2, page - 1, page, page + 1, page + 2, page + 3, page + 4, page + 5, page + 6, page + 7]
return paginator, max(1, page - 1), min(pages_total, page + 1)
def exec_cmd(cmd, return_first=False):
"""Executes a shell command"""
if request.args.get('verbose', 0, type=int):
print(cmd)
resp = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
msg = resp.stdout.decode().strip().split("\n")
err = resp.stderr.decode().strip().split("\n")
msg = list(filter(None, msg))
err = list(filter(None, err))
if return_first:
return (msg[0], err) if msg else ('', err)
return msg, err
def get_hosts():
r = []
for d in os.listdir(FOLDER):
if os.path.isfile(f"{FOLDER}/{d}/.cachefile"):
r.append(d)
return r
def minimize_messages(msgs, max_length=20):
if isinstance(msgs, str):
return [msgs]
if len(msgs) > max_length:
cutter = (max_length // 2) - 1
return msgs[:cutter] + ['[...]'] + msgs[-cutter:]
return msgs
def ucfirst(self):
s = str(self)
return s[:1].upper() + s[1:]
def edit_response(message=[], url_for_kw={}, **kwargs):
_msg = message or kwargs.get('message', [])
msg=[]
errors = []
if isinstance(_msg, tuple):
msg, errors = _msg
if isinstance(_msg, str):
msg = [_msg]
kwargs['message'] = msg
kwargs['error'] = set(list(kwargs.get('error', []) or []) + errors)
if not request.is_json:
message = kwargs.pop('message', [])
error = kwargs.pop('error', [])
if message:
for m in minimize_messages(message):
flash(ucfirst(m), 'success')
if error:
for e in minimize_messages(error):
flash(ucfirst(e), 'danger')
return redirect(url_for('edit', **url_for_kw))
return jsonify(kwargs)
def get_request_data():
if not request.is_json:
return dict(
action=request.form.get('action'),
new_host=request.form.get('new_host'),
hash=request.form.get('hash', '', type=str),
status_code=request.form.get('status_code', 302),
url=request.form.get('url', '', type=str).replace('$', ''),
page=request.args.get('page', 1, type=int),
term=request.args.get('term', '', type=str),
error=request.args.get('error', ''),
message=request.args.get('message', ''),
restart=request.args.get('restart_nginx', 0, type=int),
)
else:
try:
if request.method not in ('GET', 'DELETE'):
return request.get_json()
else:
return dict(
page=request.args.get('page', 1, type=int),
term=request.args.get('term', '', type=str),
hash=request.args.get('hash', '', type=str),
)
except Exception as e:
logger.exception(e)
return {
'page': 1,
'status_code': 302,
'url': '',
'term': '',
'restart': False,
}
def check_url(url):
try:
response = requests.get(url)
if not response.ok:
raise ValueError(f"{url} has no valid response")
except Exception as e:
logger.warning(str(e))
raise ValueError(f"{url} has no valid response")
def get_actionname(request_data):
if request.is_json:
return {
'PUT': 'edit',
'PATCH': 'edit',
'POST': 'create',
'DELETE': 'delete',
'GET': 'list',
}[request.method]
elif request.method == 'POST':
return request_data.get('action')
else:
return 'list'
def restart_nginx():
with open(f'{FOLDER}/.restart_nginx', 'w') as ng:
ng.write(f"{datetime.datetime.now()}")
return edit_response(message='Requested NGINX reload')
def create_host(hostname):
return exec_cmd(f'/usr/bin/url-shortener create-host {hostname} 2>&1')
class Reloader:
reload = False
reloader = Reloader()
normalize_re = re.compile('[^A-Za-z0-9]')
def normalize_filename(filename):
return normalize_re.sub('-', filename)
class Editor:
def __init__(self, hash_value=''):
self.host = session_host()
self.hostfolder = os.path.join(FOLDER, self.host)
self.cachefile = f"{self.hostfolder}/.cachefile"
self.conf_dir = self.hostfolder + '/nginx'
self.locations_d_dir = self.hostfolder + '/locations.d'
self.log_dir = self.hostfolder + '/logs'
if hash_value:
self.init_hash(hash_value)
def init_hash(self, hash_value='', force_hash=False):
if hash_value == 'edit':
raise ValueError("Hash 'edit' is not allowed")
if force_hash and not hash_value:
raise ValueError("No hash given")
hash_value = (hash_value.lstrip('/') or self.generate_new_hash())
self.hash_value = hash_value.lstrip('/').replace('\\', '')
self.normalized_hash = normalize_filename(self.hash_value)
self.conf_file = f"{self.conf_dir}/{self.normalized_hash}"
self.log_file = f"{self.log_dir}/{self.normalized_hash}"
def link_location(self, unlink=False):
if unlink:
return exec_cmd(f"/usr/bin/url-shortener unlink-location {self.host} {self.normalized_hash}")
return exec_cmd(f"/usr/bin/url-shortener link-location {self.host} {self.normalized_hash}")
def remove_from_cache(self):
_f = os.path.basename(self.cachefile)
return exec_cmd(
f"grep -v '{self.hash_value}' '{self.cachefile}' > /tmp/{_f}.tmp && mv /tmp/{_f}.tmp {self.cachefile}")
def generate_new_hash(self, length=10):
"""Generates a Hash, that is not used"""
hash_value = generate_random_hash(length)
while os.path.isfile(self.conf_dir + '/' + hash_value) or os.path.isfile(self.log_dir + '/' + hash_value):
hash_value = generate_random_hash(length)
return hash_value
def get_visits(self):
"""Get visits per Hash"""
resp, _err = exec_cmd(f"wc -l {self.log_file} | sed 's/ .*$//'", return_first=True)
return int(resp or 0)
def get_files(self, page=1, per_page=100, term='', with_reload=False):
# 0 elMnuj25s6 302 https://www.heise.de
if term:
gte0 = ''
if term.endswith('>0'):
gte0 = '/^[^0-9]*0[^0-9]/d;'
term = term.replace('>0', '')
lscommand = f"sed '{gte0}/{term.replace('/', r'\/')}/!d' {self.cachefile}"
else:
lscommand = f"cat {self.cachefile}"
_cnt, _err = exec_cmd(lscommand + ' | wc -l', return_first=True)
cnt = int(_cnt)
if page > 1:
tailstart = per_page * (page - 1) + 1
tailer = f" | tail -n +{tailstart}"
else:
tailer = ""
files_command = f"{lscommand} {tailer} | head -n {per_page}"
_files, _err = exec_cmd(files_command)
_files = map(lambda x: re.split(' +', x), _files)
returns = list(_files)
name_map = {}
files = []
sizes = []
sizes_map = {}
nginx_activated = []
if returns:
nginx_activated, _err = exec_cmd(f"ls -1 {self.locations_d_dir}")
name_map = {normalize_filename(f[1]):f[1] for f in returns}
files = [f"{self.conf_dir}/{x}" for x in name_map]
sizes, _err = exec_cmd(f"wc -l --total=never " + " ".join(
map(lambda x: x.replace('/nginx/', '/logs/'), files)
) + " | sed '/^[^0-9]*0[^0-9]/d'")
reload = False
if sizes and sizes[0] != '0':
for line in sizes:
s, f = line.strip().split(' ')
hash_value = name_map[os.path.basename(f)]
sizes_map[hash_value] = (int(s), f)
for _r in returns:
_norm = normalize_filename(_r[1])
if name_map.get(_r[1]) in sizes_map:
_r[0] = sizes_map[name_map.get(_r[1])][0]
_r.append(_norm)
_r.append(_norm in nginx_activated)
returns = sorted(returns, key=lambda x: int(x[0]), reverse=True)
if request.args.get('update_cache', 0, type=int):
_msg = []
_err = []
for hash_value, (s, f) in sizes_map.items():
_msg_tmp, _err_tmp = exec_cmd(f"sed -i 's!^[^ ]* {hash_value} !{s} {hash_value} !' {self.cachefile}")
if _err_tmp:
_err.extend(_err_tmp)
else:
_msg.append(f"{s} from {f} updated in .cachefile")
_tmpm, _tmpe = exec_cmd(f"/usr/bin/url-shortener sort-cache {self.host} 2>&1")
_msg.extend(_tmpm)
_err.extend(_tmpe)
if _err:
max_length = 10 if _msg else 20
for _e in minimize_messages(_err, max_length):
flash(_e, 'danger')
if _msg:
max_length = 10 if _err else 20
for _e in minimize_messages(_msg, max_length):
flash(_e, 'success')
reload = True
data = [cnt, list(files), returns]
if with_reload:
data.append(reload)
return data
def file_content(self, url, status=302):
return (
f"# {url}\n"
f"location = /{self.hash_value} {{\n"
f" access_log {self.log_file};\n"
f" return {status} '{url}';\n"
f"}}\n"
)
def edit_file(self, url, status_code, create=False, no_url_check=False):
if not no_url_check:
check_url(url)
msg = "Added"
if not create:
cmd = f"sed -i 's!^[^ ]* {self.hash_value} .*!{self.get_visits()} {self.hash_value} {status_code} {url}!' {self.cachefile}"
exec_cmd(cmd)
msg = 'Changed'
try:
with open(self.conf_file, 'w') as file:
file.write(self.file_content(url, status_code))
return f"{msg} '{url}' as <a href=\"/{self.hash_value}\" target=\"_blank\">/{self.hash_value}</a>"
except Exception as e:
raise ValueError(str(e))
def append_new_to_cache(self, status_code, url):
try:
with open(self.cachefile, 'a') as ch:
ch.write(f"0 {self.hash_value} {str(status_code).strip()} {url.strip()}\n")
except Exception as e:
raise ValueError(str(e))
def create_file(self, url, status_code, no_url_check=False):
if os.path.isfile(self.conf_file):
with open(self.conf_file, 'r') as fn:
for line in fn:
url = line.strip().replace('#', '')
raise ValueError(f"/{self.hash_value} exists with URL {url}")
try:
if err := exec_cmd(f"touch {self.log_file}", return_first=True)[1]:
raise ValueError(f"Could not touch {self.log_file}")
except Exception as e:
raise ValueError(str(e))
self.append_new_to_cache(status_code, url)
return self.edit_file(url, status_code, create=True, no_url_check=no_url_check)
def delete_file(self):
try:
try:
os.remove(self.log_file)
except Exception as e:
logger.exception(e)
try:
self.remove_from_cache()
except Exception as e:
logger.exception(e)
os.remove(self.conf_file)
return f"{self.hash_value} gelöscht"
except Exception as e:
logger.exception(e)
raise ValueError(str(e))
def purge(self):
exec_cmd(f"find {self.conf_dir} -type f -delete")
exec_cmd(f"find {self.log_dir} -type f -delete")
restart_nginx()
return "Purge successful"
def generate(self, length=10):
msg = []
err = []
domains = ['https://www.brainson.de', 'https://www.heise.de'] + \
[f'https://www.sixbid.com/{u}' for u in ['de', 'en', 'fr']]
for i in range(0, length + 1):
url = random.choice(domains)
try:
self.init_hash()
msg.append(self.create_file(url, 302, no_url_check=True))
except ValueError as e:
err.append(str(e))
return dict(message=msg, error=err)
@app.route('/edit', methods=['GET', 'POST', 'DELETE', 'PATCH', 'PUT'])
def edit():
if _host := request.args.get('host', '', type=str):
session['host'] = _host
if not request.is_json:
return edit_response()
editor = Editor()
per_page = request.args.get('per_page', 100, type=int)
if request.args.get('purge', 0, type=int):
return edit_response(editor.purge())
if link_value := request.args.get('link_location', '', type=str):
editor.init_hash(link_value, force_hash=True)
return edit_response(message=editor.link_location())
if link_value := request.args.get('unlink_location', '', type=str):
editor.init_hash(link_value, force_hash=True)
return edit_response(message=editor.link_location(unlink=True))
if request.args.get('generate', 0, type=int):
return edit_response(**editor.generate())
request_data = get_request_data()
if new_host := request_data.get('new_host', None):
return edit_response(message=create_host(new_host), url_for_kw={'host': new_host})
if request_data.get('restart_nginx'):
return restart_nginx()
page = request_data.get('page')
term = request_data.get('term')
hash_value = request_data.get('hash')
status_code = request_data.get('status_code')
url = request_data.get('url')
error = request_data.get('error')
message = request_data.get('message')
action = get_actionname(request_data)
try:
editor.init_hash(hash_value, force_hash=True)
except ValueError as e:
if action in ('create', 'edit', 'delete'):
return edit_response(error=str(e))
try:
if action in ('create', 'edit'):
if url:
if action == 'create':
return edit_response(message=editor.create_file(url, status_code))
else:
return edit_response(message=editor.edit_file(url, status_code))
else:
raise ValueError("URL must be set")
elif action == 'delete':
return edit_response(message=editor.delete_file())
if action != 'list':
raise ValueError(f"Action '{action}' unknown")
except ValueError as e:
return edit_response(error=str(e))
cnt, files, filedata, reload = editor.get_files(
page=page,
per_page=per_page,
term=term,
with_reload=True
)
if reload:
return edit_response()
total_pages = (int(cnt) + per_page - 1) // per_page
start = (page - 1) * per_page
datadict = {
'cnt': cnt,
'per_page': per_page,
'newhash': editor.generate_new_hash(),
'start': start,
'end': start + per_page,
'total_items': cnt,
'files': files,
'filedata': filedata,
'message': message,
'error': error,
'current_page': page,
'total_pages': total_pages,
'term': term,
'paginator': get_paginator(total_pages, page, per_page),
'hosts': get_hosts(),
'current_host': session_host(),
}
if request.is_json:
return jsonify(datadict)
return render_template('edit.html', pagesizes=[10, 25, 50, 100, 250, 500], **datadict)
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-P', '--port', default=9000, dest="port", required=False)
parser.add_argument('-H', '--host', default='0.0.0.0', dest="host", required=False)
parser.add_argument('-D', '--directory', default=FOLDER, dest="folder", required=False)
args = parser.parse_args()
FOLDER = args.folder
app.run(debug=True, port=int(args.port), host=args.host)

View File

@@ -0,0 +1,4 @@
Flask
requests
uwsgi
wheel

View File

@@ -0,0 +1,3 @@
#!/usr/bin/env bash
cd $(dirname $(realpath 0))
sudo -uwww-data ./venv/bin/python3 app.py

View File

@@ -0,0 +1,324 @@
{% macro nginx_svg(size, color='') %}<svg width="{{ size }}px" height="{{ size }}px" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><path d="M15.948,2h.065a10.418,10.418,0,0,1,.972.528Q22.414,5.65,27.843,8.774a.792.792,0,0,1,.414.788c-.008,4.389,0,8.777-.005,13.164a.813.813,0,0,1-.356.507q-5.773,3.324-11.547,6.644a.587.587,0,0,1-.657.037Q9.912,26.6,4.143,23.274a.7.7,0,0,1-.4-.666q0-6.582,0-13.163a.693.693,0,0,1,.387-.67Q9.552,5.657,14.974,2.535c.322-.184.638-.379.974-.535" style="fill:{{ color or '#019639' }}"/><path d="M8.767,10.538q0,5.429,0,10.859a1.509,1.509,0,0,0,.427,1.087,1.647,1.647,0,0,0,2.06.206,1.564,1.564,0,0,0,.685-1.293c0-2.62-.005-5.24,0-7.86q3.583,4.29,7.181,8.568a2.833,2.833,0,0,0,2.6.782,1.561,1.561,0,0,0,1.251-1.371q.008-5.541,0-11.081a1.582,1.582,0,0,0-3.152,0c0,2.662-.016,5.321,0,7.982-2.346-2.766-4.663-5.556-7-8.332A2.817,2.817,0,0,0,10.17,9.033,1.579,1.579,0,0,0,8.767,10.538Z" style="fill:#fff"/></svg>{% endmacro %}
<!DOCTYPE html>
<html>
<head>
<title>Edit / Create</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.6/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-4Q6Gf2aSP4eDXB8Miphtr37CMZZQ5oXLH2yaXMJ2w8e2ZtHTl7GptT4jmndRuHDT"
crossorigin="anonymous"
/>
<style type="text/css" rel="stylesheet">
body {
/* background-color: #DDD; */
}
#subm {
background-color: #DEDEDE;
}
#subm input {
background: none;
border-radius: 3px;
color: darkred;
border: none;
width: 150px;
padding: 0;
}
#subm input:hover,
#subm input:focus {
background: white;
border: 0 !important;
outline: none;
}
.abtn:hover {
text-decoration: underline;
}
form.row {
margin: 0 !important;
padding: 0 !important;
}
select {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
}
</style>
<script type="text/javascript">
function confirmDelete(hash_val) {
if (confirm("Möchten Sie " + hash_val + ' wirklich löschen?')) {
return true;
} else {
return false;
}
}
function toggleNewHost(onoff){
if(onoff){
document.getElementById('newhost_form').style.display = 'inline-block';
document.getElementById('newhost_adder').style.display = 'none';
document.getElementById('newhost_chooser').style.display = 'none';
}else{
document.getElementById('newhost_form').style.display = 'none';
document.getElementById('newhost_adder').style.display = 'initial';
document.getElementById('newhost_chooser').style.display = 'initial';
}
}
</script>
</head>
<body>
<div class="container pt-3">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class="row mt-3 border rounded border-secondary p-3" onclick="this.parentNode.removeChild(this);"
style="cursor:pointer">
{% for categ, message in messages %}
<div class="col col-12 text-{{ categ }} p-1">{{ message | safe }}</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<div class="row mt-3 rounded p-2" style="background-color: #CCC !important">
<div class="col col-12 pt-3 pe-1" style="text-align:right">
<a href="/edit" style="text-decoration:none">
<h1>
<div style="display:inline-block;color:#1C274C;opacity:0.5;" class="mt-1">
URL Shortener
</div>
<svg width="50px" style="display: inline-block;margin-top: -30" height="50px" viewBox="0 0 24 24"
fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.5"
d="M14.1625 18.4876L13.4417 19.2084C11.053 21.5971 7.18019 21.5971 4.79151 19.2084C2.40283 16.8198 2.40283 12.9469 4.79151 10.5583L5.51236 9.8374"
stroke="#1C274C" stroke-width="1.5" stroke-linecap="round"/>
<path d="M9.8374 14.1625L14.1625 9.8374" stroke="#1C274C" stroke-width="1.5"
stroke-linecap="round"/>
<path opacity="0.5"
d="M9.8374 5.51236L10.5583 4.79151C12.9469 2.40283 16.8198 2.40283 19.2084 4.79151C21.5971 7.18019 21.5971 11.053 19.2084 13.4417L18.4876 14.1625"
stroke="#1C274C" stroke-width="1.5" stroke-linecap="round"/>
</svg>
</h1>
</a>
</div>
<div class="col col-12">
<form method="post" style="display:none;text-align:right;float:right;" class="ms-1" id="newhost_form">
<input type="hidden" name="action" value="create-host" />
<input type="text" name="new_host" class="me-1" class="form-control" style="width:300px" />
<button type="submit" class="btn btn-success btn-sm">Add</button>
<button type="reset" class="btn btn-danger btn-sm" onclick="toggleNewHost(false);">Cancel</button>
</form>
<span id="newhost_adder"
onclick="toggleNewHost(true);"
style="cursor:pointer;float:right;margin-left:5px;" title="Add Host"
>+</span>
<select onchange="location.href='{{ url_for('edit', term=term, per_page=per_page) }}&host=' + this.value"
class="form-control"
id="newhost_chooser"
style="width:auto !important;float:right;max-width:100px;font-size:small;padding:2px 4px 2px 2px;text-align:right;background:transparent;border:0;float:right;">
<option value="default">Choose</option>
{% for host in hosts %}
<option value="{{ host }}" {% if host== current_host %}selected="selected" {% endif %}>{{ host }}
</option>
{% endfor %}
</select>
</div>
</div>
<div class="row mt-0 mb-5 border-bottom border-secondary rounded p-3"
style="border-color:#CCC !important;background:#DDD;">
<form method="post" class="row">
<input class="form-control" type="hidden" name="action" value="create">
<div class="col-7">
<input class="form-control" type="url" name="url" placeholder="URL"/>
</div>
<div class="col-1">
<select class="form-control" name="status_code">
<option value="302">302</option>
<option value="301">301</option>
</select>
</div>
<div class="col-4">
<span class="btn border" id="subm">
Create as <span style="color:darkred;" class="ms-1">/</span><input type="text" name="hash"
class="me-1"
value="{{ newhash }}"/>
</span>
<button type="submit" class="btn btn-light" style="background-color:darkgreen;color:white">&gt;&gt;
</button>
<a href="{{ url_for('edit', update_cache=1, term=term, per_page=per_page) }}"
title="Update cachefile with files here"
style="float:right;text-decoration:none;font-size:2em"
class="text-secondary ms-2">&bullet;</a>
<a href="/edit?restart_nginx=1" title="Request to restart nginx" style="float:right">
{{ nginx_svg(40, '#AAA') }}
</a>
</div>
</form>
</div>
<!--
<div class="row mt-3">
<div class="col-12">
<h2>Edit</h2>
</div>
</div>
-->
<div class="row mt-3 pb-3 mb-1">
<div class="col col-12">
<h5>{{ total_items | format_number }} Items in total</h5>
</div>
<div class="col-2">
<span style="font-size:smaller;">
Page {{ current_page }}/{{ total_pages }}
</span>
</div>
<div class="col-10" style="text-align: right">
<span style="margin-right:10px;font-size:smaller;">
<select onchange="location.href='{{ url_for('edit', term=term) }}&per_page=' + this.value"
style="width:auto !important;font-size:small;padding:2px 4px 2px 2px;text-align:right;background:transparent;border:0">
{% for ps in pagesizes %}
<option value="{{ ps }}" {% if ps== per_page %}selected="selected" {% endif %}>{{ ps }}</option>
{% endfor %}
</select>&nbsp;/&nbsp;Page
</span>
{% if paginator[0] %}
<a class="btn btn-outline btn-sm"
href="{{ url_for('edit', page=paginator[1], term=term, per_page=per_page) }}">&nbsp;&lt;</a>
{% if paginator[0][0] != 1 %}
<a class="btn btn-outline-secondary btn-sm"
href="{{ url_for('edit', page=1, term=term, per_page=per_page) }}">1</a>
...
{% endif %}
{% for page_num in paginator[0] %}
{% if page_num == current_page %}
<span class="btn btn-secondary btn-sm">{{ page_num }}</span>
{% else %}
<a class="btn btn-outline-secondary btn-sm"
href="{{ url_for('edit', page=page_num, term=term, per_page=per_page) }}">{{
page_num }}</a>
{% endif %}
{% endfor %}
{% if paginator[0][-1] != total_pages %}
...
<a class="btn btn-outline-secondary btn-sm"
href="{{ url_for('edit', page=total_pages, term=term, per_page=per_page) }}">{{
total_pages }}</a>
{% endif %}
<a class="btn btn-outline btn-sm"
href="{{ url_for('edit', page=paginator[2], term=term, per_page=per_page) }}">&nbsp;&gt;</a>
{% endif %}
</div>
</div>
<div class="row mb-3">
<form class="row mb-1" method="get">
<!--
<div class="col col-1">
<a class="btn btn-outline-info" href="/edit">Clear</a>
</div>
-->
<div class="col col-11">
<input type="text" class="form-control" name="term" value="{{ term }}"
placeholder="Search in URL (may take a long time)"/>
</div>
<div class="col col-1">
<a href="{{ url_for('edit', per_page=per_page) }}" class="btn btn-sm">
<svg fill="#999" height="20px" width="20px" version="1.1" id="Capa_1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 25.283 25.283" xml:space="preserve">
<g>
<path d="M4.153,4.757v20.526h17V4.757H4.153z M8.569,22.386H5.775V7.82H8.57L8.569,22.386L8.569,22.386z M13.886,22.386h-2.797 V7.82h2.797C13.886,7.82,13.886,22.386,13.886,22.386z M19.202,22.386h-2.795V7.82h2.795V22.386z"/>
<path d="M19.931,3.704L21.13,3.7l-0.002-2.4l-5.463-0.006l-0.01-0.657c-1.643-0.875-4.76-0.82-6.014-0.014v0.702H4.13l0.004,2.402 h1.199L19.931,3.704z"/>
</g>
</a>
</div>
<!--
<div class="col col-1">
<input type="submit" class="btn btn-outline-secondary" value="Search" />
</div>
-->
</form>
</div>
{% for visits, hash, status_code, url, normalized, nginx_activated in filedata %}
<div class="row mt-2">
<div class="col-11">
<form method="post" class="row">
<input class="form-control" type="hidden" name="action" value="edit">
<input type="hidden" name="hash" value="{{ hash }}"/>
<div class="col-3">
{% if nginx_activated %}
<a href="{{ url_for('edit', unlink_location=normalized, term=term, page=page) }}">{{ nginx_svg(15) }}</a>
{% else %}
<a href="{{ url_for('edit', link_location=normalized, term=term, page=page) }}">{{ nginx_svg(15, '#DDD') }}</a>
{% endif %}
<a class="btn text-primary abtn ms-0 ps-0" href="/{{ hash }}" target="_blank">/{{ hash }}</a>
</div>
<div class="col-1 pe-0 me-0 btn" style="text-align:right;color:#999" title="Visits">
{{ visits }}
</div>
<div class="col-5">
<input class="form-control" type="url" name="url" value="{{ url }}"/>
</div>
<div class="col-1">
<select class="form-control" name="status_code">
<option value="301" {% if status_code == '301' %}selected="selected"{% endif %}>301</option>
<option value="302" {% if status_code == '302' %}selected="selected"{% endif %}>302</option>
</select>
</div>
<div class="col-2" style="text-align:right">
<input type="submit" value="Save" class="btn btn-secondary"/>
<input type="reset" value="Reset" class="btn btn-outline-secondary"/>
</div>
</form>
</div>
<div class="col-1">
<form method="post" class="row" onsubmit="return confirm('{{ hash }} löschen?')">
<div class="col-1">
<input type="hidden" name="action" value="delete"/>
<input type="hidden" name="hash" value="{{ hash }}"/>
<input type="submit" value="Delete" class="btn btn-outline-danger"/>
</div>
</form>
</div>
</div>
{% endfor %}
<div class="row mt-3 border-top pt-3">
<div class="col-12" style="text-align: right">
<span style="margin-right:10px;font-size:smaller;">
<select onchange="location.href='{{ url_for('edit', term=term) }}&per_page=' + this.value"
style="width:auto !important;font-size:small;padding:2px 4px 2px 2px;text-align:right;background:transparent;border:0">
{% for ps in pagesizes %}
<option value="{{ ps }}" {% if ps== per_page %}selected="selected" {% endif %}>{{ ps }}</option>
{% endfor %}
</select>&nbsp;/&nbsp;Page
{% if paginator[0] %}
<a class="btn btn-outline btn-sm"
href="{{ url_for('edit', page=paginator[1], term=term, per_page=per_page) }}">&nbsp;&lt;</a>
{% if paginator[0][0] != 1 %}
<a class="btn btn-outline-secondary btn-sm"
href="{{ url_for('edit', page=1, term=term, per_page=per_page) }}">1</a>
...
{% endif %}
{% for page_num in paginator[0] %}
{% if page_num == current_page %}
<span class="btn btn-secondary btn-sm">{{ page_num }}</span>
{% else %}
<a class="btn btn-outline-secondary btn-sm"
href="{{ url_for('edit', page=page_num, term=term, per_page=per_page) }}">{{
page_num }}</a>
{% endif %}
{% endfor %}
{% if paginator[0][-1] != total_pages %}
...
<a class="btn btn-outline-secondary btn-sm"
href="{{ url_for('edit', page=total_pages, term=term, per_page=per_page) }}">{{
total_pages }}</a>
{% endif %}
<a class="btn btn-outline btn-sm"
href="{{ url_for('edit', page=paginator[2], term=term, per_page=per_page) }}">&nbsp;&gt;</a>
{% endif %}
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,66 @@
server {
server_name {DOMAINNAME};
listen 80;
# Domain and SSL Config
# -------------------------------------------------
# include snippets/brainson_minimal_ssl_snippet;
# ssl_certificate /var/lib/dehydrated/certs/{DOMAINNAME}/fullchain.pem;
# ssl_certificate_key /var/lib/dehydrated/certs/{DOMAINNAME}/privkey.pem;
# access_log /var/log/nginx/{DOMAINNAME}.access.log;
# error_log /var/log/nginx/{DOMAINNAME}.error.log;
# Shortener Config
# -------------------------------------------------
# set $shortener_data_dir "/var/cache/url-shortener"
# set $shortener_redirects_status 302;
# WARNING - the following shold NOT be done with more than 1000 Redirects
# This includes the files to not go to lua after a restart or reload
# There may exist deprecated ones, but anyway ?
#
# include "/var/cache/url-shortener/{DOMAINNAME}/nginx/*.conf";
# Maybe we can include a important list and symlink from nginx/ to locations.d/
#
include "/var/cache/url-shortener/{DOMAINNAME}/locations.d/*.conf";
# The Hostname for the application
set $shortener_host "{DOMAINNAME}";
# The default locations
include "snippets/url-shortener-server-snippet.conf";
# or
# -------------------------------------------------
# location = /edit {
# include proxy_params;
# # Security Config with ldap auth
# # ---------------------------------------------
# # include snippets/brainson_ldap_auth_snippet;
# proxy_pass http://127.0.0.1:9000;
# }
#
# location = /robots.txt {
# return 200 "User-agent: *\nDisallow: /\n";
# }
#
# location / {
# content_by_lua_file "/var/lib/nginx/lua/url-shortener.lua";
# }
#
# if ($is_bot) {
# return 204 "";
# }
# -------------------------------------------------
}

View File

@@ -0,0 +1,9 @@
[uwsgi]
module = app:app
master = true
processes = 2
threads = 2
socket = 0.0.0.0:9000
vacuum = true
die-on-term = true
buffer-size = 32768

View File

View File

View File

View File

@@ -0,0 +1,60 @@
local redirects_dir = ngx.var.shortener_data_dir or "/var/cache/url-shortener"
local redirect_status = ngx.var.shortener_redirects_status or 302
local sh_hostname = ngx.var.shortener_host or "default"
local hostdir = redirects_dir .. "/" .. sh_hostname
local function handle_request()
local request_uri = ngx.var.request_uri
local hash = request_uri:match("^/([^?]+)")
local return_code = 302
if hash then
local normalized_hash = string.gsub(hash, "[^%w]", "-")
local config_file = hostdir .. "/nginx/" .. normalized_hash
local log_file = hostdir .. "/logs/" .. normalized_hash
local urlfile = io.open(config_file, "r")
if urlfile then
local url = urlfile:read():match("# (.+)")
if url then
for line in urlfile:lines() do
if line:match("return%s+(%d+)%s+(.+)") then
redirect_status = tonumber(line:match("return%s+(%d+)"))
break
end
end
local remote_addr = ngx.var.remote_addr
local time_local = ngx.var.time_local
local request = ngx.var.request
local status = ngx.var.status
local body_bytes_sent = ngx.var.body_bytes_sent
local http_referer = ngx.var.http_referer
local http_user_agent = ngx.var.http_user_agent
local log_entry = string.format("%s - %s [%s] \"%s\" %s %s \"%s\" \"%s\"",
remote_addr, "-", time_local, request, status, body_bytes_sent,
http_referer, http_user_agent)
local file = io.open(log_file, "a")
if file then
os.execute("/bin/touch " .. hostdir .. "/update.d/" .. normalized_hash)
file:write(log_entry .. " - from lua\n")
file:close()
else
ngx.log(ngx.ERR, "Could not read " .. log_file)
ngx.exit(ngx.HTTP_NOT_FOUND)
end
ngx.redirect(string.gsub(url, "\n", ""), redirect_status)
else
ngx.exit(ngx.HTTP_NOT_FOUND)
end
else
ngx.exit(ngx.HTTP_NOT_FOUND)
end
else
ngx.exit(ngx.HTTP_NOT_FOUND)
end
end
handle_request()

View File

1
version.txt Normal file
View File

@@ -0,0 +1 @@
1.24.0-2.0.7