blob: b279d0785ff2b84bb18b7491b795d32229c50391 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
# Maintainer: Steve Holvoet <linux@steho.be>
pkgname=ghidra-mcp-git
pkgver=6.0.0.r283.gb3f8dfe
pkgrel=2
pkgdesc="Production-ready Model Context Protocol server for Ghidra reverse engineering platform (latest git version)"
arch=('any')
url="https://github.com/bethington/ghidra-mcp"
license=('Apache-2.0')
depends=('ghidra' 'python' 'python-mcp')
makedepends=('maven' 'java-environment>=25' 'java-environment<27' 'git' 'python-build' 'python-installer' 'python-hatchling' 'python-wheel')
provides=("${pkgname%-git}")
conflicts=("${pkgname%-git}" "${pkgname}")
source=("git+${url}.git")
sha256sums=('SKIP')
install=ghidra-mcp.install
pkgver() {
cd "${pkgname%-git}"
git describe --long --tags --abbrev=7 | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g'
}
prepare() {
cd "${pkgname%-git}"
# Standard Arch Linux path for Ghidra
local _ghidra_home="/opt/ghidra"
if [ -d "/usr/share/ghidra" ]; then
_ghidra_home="/usr/share/ghidra"
fi
msg2 "Using Ghidra installation at: ${_ghidra_home}"
if [ ! -f "${_ghidra_home}/Ghidra/application.properties" ]; then
error "Ghidra properties not found at ${_ghidra_home}/Ghidra/application.properties"
return 1
fi
local _detected_version
_detected_version=$(grep "application.version=" "${_ghidra_home}/Ghidra/application.properties" | cut -d= -f2 | tr -d '\r')
if [ -n "$_detected_version" ]; then
msg2 "Found Ghidra version: ${_detected_version}"
# Patch pom.xml to target installed Ghidra version
sed -i "s|<ghidra.version>.*</ghidra.version>|<ghidra.version>${_detected_version}</ghidra.version>|g" pom.xml
else
error "Failed to detect Ghidra version from properties file."
return 1
fi
# Ghidra is compiled with the JDK selected by its builder. The MCP project
# targets Java 21, but javac must still be new enough to read Ghidra's class
# files. A Java 25 compiler cannot consume Java 26 bytecode, for example.
# Check the installed Ghidra bytecode against Maven's actual Java runtime so
# the build fails with an actionable error instead of a cryptic javac error.
local _ghidra_jar
_ghidra_jar=$(find "${_ghidra_home}" -path '*/Framework/Generic/lib/Generic.jar' -print -quit)
if [ -z "$_ghidra_jar" ]; then
error "Could not find Ghidra Generic.jar for bytecode compatibility check."
return 1
fi
local _ghidra_class_major
_ghidra_class_major=$(javap -verbose -classpath "$_ghidra_jar" \
ghidra.framework.Application 2>/dev/null |
awk '/major version:/ { print $3; exit }')
if [[ ! $_ghidra_class_major =~ ^[0-9]+$ ]]; then
error "Could not determine the Ghidra class-file version from $_ghidra_jar."
return 1
fi
local _maven_java_version _maven_java_major _maven_class_major
_maven_java_version=$(mvn -version 2>/dev/null |
awk -F': ' '/^Java version:/ { print $2; exit }')
_maven_java_major=${_maven_java_version%%.*}
if [[ ! $_maven_java_major =~ ^[0-9]+$ ]]; then
error "Could not determine the Java version used by Maven."
return 1
fi
_maven_class_major=$((_maven_java_major + 44))
msg2 "Ghidra bytecode: major ${_ghidra_class_major}; Maven JDK: Java ${_maven_java_major}"
if (( _maven_class_major < _ghidra_class_major )); then
error "Maven is using Java ${_maven_java_major}, but Ghidra requires a JDK that can read class-file major ${_ghidra_class_major}."
error "Select a compatible Java environment with archlinux-java before rebuilding."
return 1
fi
# Upstream code calls dtm.remove(dataType, null), which requires
# DataTypeManager.remove(DataType, TaskMonitor). ghidra-git >= 12.2
# dropped that overload (it only forwarded to remove(DataType)), so the
# 3 call sites fail to compile against the installed Ghidra. Rewrite them
# to the single-arg form with identical semantics. Skip if the installed
# Ghidra still provides the old overload.
local _sm_jar
_sm_jar=$(find "${_ghidra_home}" -name "SoftwareModeling.jar" | head -n 1)
if [ -n "$_sm_jar" ] && ! javap -cp "$_sm_jar" ghidra.program.model.data.DataTypeManager 2>/dev/null \
| grep -q "remove(ghidra.program.model.data.DataType, ghidra.util.task.TaskMonitor)"; then
msg2 "Patching DataTypeService.java for removed remove(DataType, TaskMonitor) API"
sed -i 's/dtm\.remove(dataType, null)/dtm.remove(dataType)/g; s/dtm\.remove(existing, null)/dtm.remove(existing)/g' \
src/main/java/com/xebyte/core/DataTypeService.java
fi
}
build() {
cd "${pkgname%-git}"
# Use the default system JDK (Ghidra JARs may require Java >= version used to compile them)
# Determine Ghidra installation directory for dependencies
local _ghidra_home="/opt/ghidra"
if [ -d "/usr/share/ghidra" ]; then
_ghidra_home="/usr/share/ghidra"
fi
export GHIDRA_INSTALL_DIR="${_ghidra_home}"
# Determine target version from pom.xml (which we patched in prepare)
local _ghidra_ver
_ghidra_ver=$(grep "<ghidra.version>" pom.xml | sed 's|.*<ghidra.version>\(.*\)</ghidra.version>.*|\1|')
# Install local Ghidra JAR dependencies required for compilation
msg2 "Installing Ghidra dependencies to local Maven repository..."
local _ghidra_jars=(
"Generic"
"SoftwareModeling"
"Project"
"Docking"
"Decompiler"
"Utility"
"Base"
"Gui"
"FileSystem"
"Help"
"Emulation"
"Debugger-api"
"Framework-TraceModeling"
"Debugger-rmi-trace"
"DB"
"Graph"
)
for _jar_name in "${_ghidra_jars[@]}"; do
local _found_jar
_found_jar=$(find "${GHIDRA_INSTALL_DIR}" -name "${_jar_name}.jar" | head -n 1)
if [ -f "$_found_jar" ]; then
mvn install:install-file \
-Dfile="$_found_jar" \
-DgroupId="ghidra" \
-DartifactId="$_jar_name" \
-Dversion="$_ghidra_ver" \
-Dpackaging="jar" \
-DgeneratePom=true \
-Dmaven.repo.local="$srcdir/.m2" \
-Dquiet=true
else
error "Required JAR '${_jar_name}.jar' not found in $GHIDRA_INSTALL_DIR"
return 1
fi
done
msg2 "Building project..."
mvn clean package assembly:single -DskipTests -Dmaven.repo.local="$srcdir/.m2"
}
package() {
cd "${pkgname%-git}"
# 1. Install MCP Bridge Python package (pyproject.toml at project root)
msg2 "Building and installing bridge Python package..."
python -m build --wheel --no-isolation
local _wheel
_wheel=$(find dist -name "*.whl" | head -n 1)
python -m installer --prefix=/usr --destdir="$pkgdir" "$_wheel"
msg2 "Bridge package installed"
# 2. Install Ghidra Extension ZIP
local _ext_zip
_ext_zip=$(find target -name "GhidraMCP-*.zip" | head -n 1)
# Standard location for extensions
local _ext_dest="$pkgdir/opt/ghidra/Extensions/Ghidra"
if [ -f "$_ext_zip" ]; then
install -d "$_ext_dest"
install -m644 "$_ext_zip" "$_ext_dest/$(basename "$_ext_zip")"
msg2 "Extension ZIP installed to $_ext_dest"
else
error "Build artifact not found."
return 1
fi
# 3. Install Documentation
install -Dm644 README.md "$pkgdir/usr/share/doc/${pkgname%-git}/README.md"
if [ -f "CLAUDE.md" ]; then
install -Dm644 CLAUDE.md "$pkgdir/usr/share/doc/${pkgname%-git}/CLAUDE.md"
fi
# 4. Clean up __pycache__ from package
find "$pkgdir" -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
}
|