├── .github └── workflows │ └── gradle.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src ├── main └── java │ └── net │ └── fornwall │ └── jelf │ ├── BackingFile.java │ ├── ByteArrayAsFile.java │ ├── ElfDynamicSection.java │ ├── ElfException.java │ ├── ElfFile.java │ ├── ElfGnuHashTable.java │ ├── ElfHashTable.java │ ├── ElfNoteSection.java │ ├── ElfParser.java │ ├── ElfRelocation.java │ ├── ElfRelocationAddend.java │ ├── ElfRelocationAddendSection.java │ ├── ElfRelocationSection.java │ ├── ElfRelocationTypes.java │ ├── ElfSection.java │ ├── ElfSectionHeader.java │ ├── ElfSegment.java │ ├── ElfStringTable.java │ ├── ElfSymbol.java │ ├── ElfSymbolTableSection.java │ ├── MappedFile.java │ └── MemoizedObject.java └── test ├── java └── net │ └── fornwall │ └── jelf │ ├── BasicTest.java │ ├── ElfGnuHashTableTest.java │ ├── ElfHashTableTest.java │ ├── EndianProblemTest.java │ ├── ReadmeTest.java │ └── TestHelper.java └── resources ├── .swp ├── android_arm_libncurses ├── android_arm_tset ├── linux_amd64_bindash ├── little-endian-test ├── objectFile-64.o ├── objectFile.o └── usr-bin-yes /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | java: [8, 11, 17] 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Set up JDK ${{ matrix.java }} 17 | uses: actions/setup-java@v3 18 | with: 19 | distribution: 'zulu' 20 | java-version: ${{ matrix.java }} 21 | - name: Build with Gradle 22 | run: ./gradlew build 23 | 24 | actionlint: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - uses: actions/checkout@v3 28 | - name: Download actionlint 29 | id: get_actionlint 30 | run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) 31 | - name: Check workflow files 32 | run: ${{ steps.get_actionlint.outputs.executable }} -color 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Intellij project files 2 | *.iml 3 | *.ipr 4 | *.iws 5 | .idea/ 6 | 7 | #Gradle 8 | .gradletasknamecache 9 | .gradle/ 10 | build/ 11 | bin/ 12 | gradle.properties 13 | 14 | # Eclipse 15 | .classpath 16 | .project 17 | .settings 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## v0.9.0 (2023-07-30) 4 | - Make BackingData public and add ElfFile.from(BackingData). [#20](https://github.com/fornwall/jelf/pull/20) 5 | 6 | ## v0.8.0 (2023-07-21) 7 | - Add a `getData` method to `ElfSection` for getting the bytes of an ELF section. [#17](https://github.com/fornwall/jelf/issues/17) 8 | - Make the `getSymbol` and `getSymbolIndex` methods in `ElfRelocation` respect `EI_CLASS`. [#18](https://github.com/fornwall/jelf/issues/18) 9 | - Fix `getELFSymbol(symbolName)` not working in certain cases. [#19](https://github.com/fornwall/jelf/issues/19) 10 | 11 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2020 Fredrik Fornwall. 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JElf 2 | Java library for parsing [Executable and Linkable Format (ELF)](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format) files. 3 | 4 | [![Build Status](https://github.com/fornwall/jelf/workflows/Java%20CI/badge.svg)](https://github.com/fornwall/jelf/actions?query=workflow%3A%22Java+CI%22) 5 | [![MIT licensed](http://img.shields.io/:license-MIT-blue.svg)](LICENSE.txt) 6 | [![Package on Maven Central](https://img.shields.io/maven-central/v/net.fornwall/jelf)](https://search.maven.org/artifact/net.fornwall/jelf/) 7 | [![javadoc](https://www.javadoc.io/badge/net.fornwall/jelf.svg)](https://www.javadoc.io/doc/net.fornwall/jelf) 8 | 9 | ## Adding JElf to your build 10 | 11 | JElf's Maven group ID is `net.fornwall` and its artifact ID is `jelf`. 12 | 13 | To add a dependency on JElf using Maven, use the following: 14 | 15 | ```xml 16 | 17 | net.fornwall 18 | jelf 19 | 0.9.0 20 | 21 | ``` 22 | 23 | To add a dependency using Gradle: 24 | 25 | ```gradle 26 | dependencies { 27 | implementation 'net.fornwall:jelf:0.9.0' 28 | } 29 | ``` 30 | 31 | ## Using JElf 32 | See the [ElfFile](https://www.javadoc.io/doc/net.fornwall/jelf/latest/net/fornwall/jelf/ElfFile.html) class for how to parse and query an ELF file. 33 | 34 | ## ELF Resources 35 | - [Wikipedia entry on the ELF format](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format) 36 | - [elf(5) man page](http://man7.org/linux/man-pages/man5/elf.5.html) 37 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'signing' 3 | id "maven-publish" 4 | id "java-library" 5 | id "io.github.gradle-nexus.publish-plugin" version "1.3.0" 6 | } 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: '5.9.3' 14 | } 15 | 16 | java { 17 | group = "net.fornwall" 18 | version = "0.9.0" 19 | sourceCompatibility = 1.8 20 | withJavadocJar() 21 | withSourcesJar() 22 | } 23 | 24 | test { 25 | systemProperty "jelf.version", project.version.toString() 26 | useJUnitPlatform() 27 | afterTest { desc, result -> 28 | println "Test ${desc.name} [${desc.className}] result: ${result.resultType}" 29 | } 30 | } 31 | 32 | // See https://docs.gradle.org/current/userguide/publishing_maven.html 33 | // and https://github.com/gradle-nexus/publish-plugin 34 | /* 35 | gpg --keyring secring.gpg --export-secret-keys > ~/.gnupg/secring.gpg 36 | 37 | gradle --info \ 38 | -PsonatypeUsername=xxx \ 39 | -PsonatypePassword=xxx \ 40 | -Psigning.keyId=xxx \ 41 | -Psigning.password=xxx \ 42 | -Psigning.secretKeyRingFile=$HOME/.gnupg/secring.gpg \ 43 | publishToSonatype \ 44 | closeAndReleaseSonatypeStagingRepository 45 | */ 46 | nexusPublishing { 47 | repositories { 48 | sonatype() 49 | } 50 | } 51 | 52 | publishing { 53 | publications { 54 | mavenJava(MavenPublication) { 55 | artifactId = 'jelf' 56 | from components.java 57 | pom { 58 | name = 'JElf' 59 | description = 'ELF parsing library in java' 60 | url = 'https://github.com/fornwall/jelf' 61 | licenses { 62 | license { 63 | name = 'The MIT License' 64 | url = 'https://opensource.org/licenses/MIT' 65 | } 66 | } 67 | developers { 68 | developer { 69 | id = 'fornwall' 70 | name = 'Fredrik Fornwall' 71 | email = 'fredrik@fornwall.net' 72 | } 73 | } 74 | scm { 75 | connection = 'scm:git:git://github.com/fornwall/jelf.git' 76 | developerConnection = 'scm:git:ssh://git@github.com/fornwall/jelf.git' 77 | url = 'https://github.com/fornwall/jelf/' 78 | } 79 | } 80 | } 81 | } 82 | } 83 | 84 | signing { 85 | sign publishing.publications.mavenJava 86 | } 87 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fornwall/jelf/546d87c3ed46efe291070477a3b4bd7cf9f729ea/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s 90 | ' "$PWD" ) || exit 91 | 92 | # Use the maximum available, or set MAX_FD != -1 to use that value. 93 | MAX_FD=maximum 94 | 95 | warn () { 96 | echo "$*" 97 | } >&2 98 | 99 | die () { 100 | echo 101 | echo "$*" 102 | echo 103 | exit 1 104 | } >&2 105 | 106 | # OS specific support (must be 'true' or 'false'). 107 | cygwin=false 108 | msys=false 109 | darwin=false 110 | nonstop=false 111 | case "$( uname )" in #( 112 | CYGWIN* ) cygwin=true ;; #( 113 | Darwin* ) darwin=true ;; #( 114 | MSYS* | MINGW* ) msys=true ;; #( 115 | NONSTOP* ) nonstop=true ;; 116 | esac 117 | 118 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 119 | 120 | 121 | # Determine the Java command to use to start the JVM. 122 | if [ -n "$JAVA_HOME" ] ; then 123 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 124 | # IBM's JDK on AIX uses strange locations for the executables 125 | JAVACMD=$JAVA_HOME/jre/sh/java 126 | else 127 | JAVACMD=$JAVA_HOME/bin/java 128 | fi 129 | if [ ! -x "$JAVACMD" ] ; then 130 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 131 | 132 | Please set the JAVA_HOME variable in your environment to match the 133 | location of your Java installation." 134 | fi 135 | else 136 | JAVACMD=java 137 | if ! command -v java >/dev/null 2>&1 138 | then 139 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 140 | 141 | Please set the JAVA_HOME variable in your environment to match the 142 | location of your Java installation." 143 | fi 144 | fi 145 | 146 | # Increase the maximum file descriptors if we can. 147 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 148 | case $MAX_FD in #( 149 | max*) 150 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 151 | # shellcheck disable=SC2039,SC3045 152 | MAX_FD=$( ulimit -H -n ) || 153 | warn "Could not query maximum file descriptor limit" 154 | esac 155 | case $MAX_FD in #( 156 | '' | soft) :;; #( 157 | *) 158 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 159 | # shellcheck disable=SC2039,SC3045 160 | ulimit -n "$MAX_FD" || 161 | warn "Could not set maximum file descriptor limit to $MAX_FD" 162 | esac 163 | fi 164 | 165 | # Collect all arguments for the java command, stacking in reverse order: 166 | # * args from the command line 167 | # * the main class name 168 | # * -classpath 169 | # * -D...appname settings 170 | # * --module-path (only if needed) 171 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 172 | 173 | # For Cygwin or MSYS, switch paths to Windows format before running java 174 | if "$cygwin" || "$msys" ; then 175 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 176 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 177 | 178 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 179 | 180 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 181 | for arg do 182 | if 183 | case $arg in #( 184 | -*) false ;; # don't mess with options #( 185 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 186 | [ -e "$t" ] ;; #( 187 | *) false ;; 188 | esac 189 | then 190 | arg=$( cygpath --path --ignore --mixed "$arg" ) 191 | fi 192 | # Roll the args list around exactly as many times as the number of 193 | # args, so each arg winds up back in the position where it started, but 194 | # possibly modified. 195 | # 196 | # NB: a `for` loop captures its iteration list before it begins, so 197 | # changing the positional parameters here affects neither the number of 198 | # iterations, nor the values presented in `arg`. 199 | shift # remove old arg 200 | set -- "$@" "$arg" # push replacement arg 201 | done 202 | fi 203 | 204 | 205 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 206 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 207 | 208 | # Collect all arguments for the java command: 209 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 210 | # and any embedded shellness will be escaped. 211 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 212 | # treated as '${Hostname}' itself on the command line. 213 | 214 | set -- \ 215 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 216 | -classpath "$CLASSPATH" \ 217 | org.gradle.wrapper.GradleWrapperMain \ 218 | "$@" 219 | 220 | # Stop when "xargs" is not available. 221 | if ! command -v xargs >/dev/null 2>&1 222 | then 223 | die "xargs is not available" 224 | fi 225 | 226 | # Use "xargs" to parse quoted args. 227 | # 228 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 229 | # 230 | # In Bash we could simply go: 231 | # 232 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 233 | # set -- "${ARGS[@]}" "$@" 234 | # 235 | # but POSIX shell has neither arrays nor command substitution, so instead we 236 | # post-process each arg (as a line of input to sed) to backslash-escape any 237 | # character that might be a shell metacharacter, then use eval to reverse 238 | # that process (while maintaining the separation between arguments), and wrap 239 | # the whole thing up as a single "set" statement. 240 | # 241 | # This will of course break if any of these variables contains a newline or 242 | # an unmatched quote. 243 | # 244 | 245 | eval "set -- $( 246 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 247 | xargs -n1 | 248 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 249 | tr '\n' ' ' 250 | )" '"$@"' 251 | 252 | exec "$JAVACMD" "$@" 253 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/BackingFile.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | public interface BackingFile { 4 | 5 | void seek(long offset); 6 | void skip(int bytesToSkip); 7 | short readUnsignedByte(); 8 | int read(byte[] data); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/ByteArrayAsFile.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.IOException; 5 | 6 | class ByteArrayAsFile implements BackingFile{ 7 | private final ByteArrayInputStream byteArray; 8 | 9 | public ByteArrayAsFile(byte[] buffer) { 10 | this(new ByteArrayInputStream(buffer)); 11 | } 12 | 13 | public ByteArrayAsFile(ByteArrayInputStream byteArray) { 14 | this.byteArray = byteArray; 15 | } 16 | 17 | public void seek(long offset) { 18 | byteArray.reset(); 19 | if (byteArray.skip(offset) != offset) throw new ElfException("seeking outside file"); 20 | } 21 | 22 | public void skip(int bytesToSkip) { 23 | long skipped = byteArray.skip(bytesToSkip); 24 | if (skipped != bytesToSkip) { 25 | throw new IllegalArgumentException("Wanted to skip " + bytesToSkip + " bytes, but only able to skip " + skipped); 26 | } 27 | } 28 | 29 | public short readUnsignedByte() { 30 | int val = -1; 31 | val = byteArray.read(); 32 | if (val < 0) throw new ElfException("Trying to read outside file"); 33 | return (short) val; 34 | } 35 | 36 | public int read(byte[] data) { 37 | try { 38 | return byteArray.read(data); 39 | } catch (IOException e) { 40 | throw new RuntimeException("Error reading " + data.length + " bytes", e); 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/ElfDynamicSection.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * An {@link ElfSection} with information necessary for dynamic linking. 8 | *

9 | * Given an {@link ElfFile}, use {@link ElfFile#getDynamicSection()} to obtain the dynamic section for it if one exists, 10 | * which it only does if the ELF file is an object file participating in dynamic linking. 11 | *

12 | * This dynamic linking section contains a list of {@link ElfDynamicStructure}:s. 13 | *

 14 |  * Name                     Value  d_un         Executable   Shared Object
 15 |  * ----------------------------------------------------------------------
 16 |  * DT_NULL                      0  ignored      mandatory    mandatory
 17 |  * DT_NEEDED                    1  d_val        optional     optional
 18 |  * DT_PLTRELSZ                  2  d_val        optional     optional
 19 |  * DT_PLTGOT                    3  d_ptr        optional     optional
 20 |  * DT_HASH                      4  d_ptr        mandatory    mandatory
 21 |  * DT_STRTAB                    5  d_ptr        mandatory    mandatory
 22 |  * DT_SYMTAB                    6  d_ptr        mandatory    mandatory
 23 |  * DT_RELA                      7  d_ptr        mandatory    optional
 24 |  * DT_RELASZ                    8  d_val        mandatory    optional
 25 |  * DT_RELAENT                   9  d_val        mandatory    optional
 26 |  * DT_STRSZ                    10  d_val        mandatory    mandatory
 27 |  * DT_SYMENT                   11  d_val        mandatory    mandatory
 28 |  * DT_INIT                     12  d_ptr        optional     optional
 29 |  * DT_FINI                     13  d_ptr        optional     optional
 30 |  * DT_SONAME                   14  d_val        ignored      optional
 31 |  * DT_RPATH*                   15  d_val        optional     ignored
 32 |  * DT_SYMBOLIC*                16  ignored      ignored      optional
 33 |  * DT_REL                      17  d_ptr        mandatory    optional
 34 |  * DT_RELSZ                    18  d_val        mandatory    optional
 35 |  * DT_RELENT                   19  d_val        mandatory    optional
 36 |  * DT_PLTREL                   20  d_val        optional     optional
 37 |  * DT_DEBUG                    21  d_ptr        optional     ignored
 38 |  * DT_TEXTREL*                 22  ignored      optional     optional
 39 |  * DT_JMPREL                   23  d_ptr        optional     optional
 40 |  * DT_BIND_NOW*                24  ignored      optional     optional
 41 |  * DT_INIT_ARRAY               25  d_ptr        optional     optional
 42 |  * DT_FINI_ARRAY               26  d_ptr        optional     optional
 43 |  * DT_INIT_ARRAYSZ             27  d_val        optional     optional
 44 |  * DT_FINI_ARRAYSZ             28  d_val        optional     optional
 45 |  * DT_RUNPATH                  29  d_val        optional     optional
 46 |  * DT_FLAGS                    30  d_val        optional     optional
 47 |  * DT_ENCODING                 32  unspecified  unspecified  unspecified
 48 |  * DT_PREINIT_ARRAY            32  d_ptr        optional     ignored
 49 |  * DT_PREINIT_ARRAYSZ          33  d_val        optional     ignored
 50 |  * DT_LOOS             0x6000000D  unspecified  unspecified  unspecified
 51 |  * DT_HIOS             0x6ffff000  unspecified  unspecified  unspecified
 52 |  * DT_LOPROC           0x70000000  unspecified  unspecified  unspecified
 53 |  * DT_HIPROC           0x7fffffff  unspecified  unspecified  unspecified
 54 |  * "*" Signifies an entry that is at level 2.
 55 |  * 
56 | *

57 | * Read more about dynamic sections at Dynamic Section. 58 | */ 59 | public class ElfDynamicSection extends ElfSection { 60 | 61 | /** 62 | * An entry with a DT_NULL tag marks the end of the _DYNAMIC array. 63 | */ 64 | public static final int DT_NULL = 0; 65 | /** 66 | * This element holds the string table offset of a null-terminated string, giving the 67 | * name of a needed library. The offset is an index into the table recorded in the 68 | * {@link #DT_STRTAB} code. 69 | *

70 | * See Shared Object Dependencies for more information about these names. 71 | *

72 | * The dynamic array may contain multiple entries with this type. 73 | *

74 | * These entries' relative order is significant, though their relation to entries of other types is not. 75 | */ 76 | public static final int DT_NEEDED = 1; 77 | public static final int DT_PLTRELSZ = 2; 78 | public static final int DT_PLTGOT = 3; 79 | public static final int DT_HASH = 4; 80 | /** 81 | * DT_STRTAB entry holds the address, not offset, of the dynamic string table. 82 | */ 83 | public static final int DT_STRTAB = 5; 84 | public static final int DT_SYMTAB = 6; 85 | public static final int DT_RELA = 7; 86 | public static final int DT_RELASZ = 8; 87 | public static final int DT_RELAENT = 9; 88 | /** 89 | * The size in bytes of the {@link #DT_STRTAB} string table. 90 | */ 91 | public static final int DT_STRSZ = 10; 92 | public static final int DT_SYMENT = 11; 93 | public static final int DT_INIT = 12; 94 | public static final int DT_FINI = 13; 95 | public static final int DT_SONAME = 14; 96 | public static final int DT_RPATH = 15; 97 | public static final int DT_SYMBOLIC = 16; 98 | public static final int DT_REL = 17; 99 | public static final int DT_RELSZ = 18; 100 | public static final int DT_RELENT = 19; 101 | public static final int DT_PLTREL = 20; 102 | public static final int DT_DEBUG = 21; 103 | public static final int DT_TEXTREL = 22; 104 | public static final int DT_JMPREL = 23; 105 | public static final int DT_BIND_NOW = 24; 106 | public static final int DT_INIT_ARRAY = 25; 107 | public static final int DT_FINI_ARRAY = 26; 108 | public static final int DT_INIT_ARRAYSZ = 27; 109 | public static final int DT_FINI_ARRAYSZ = 28; 110 | public static final int DT_RUNPATH = 29; 111 | public static final int DT_FLAGS = 30; 112 | public static final int DT_PREINIT_ARRAY = 32; 113 | public static final int DT_GNU_HASH = 0x6ffffef5; 114 | public static final int DT_FLAGS_1 = 0x6ffffffb; 115 | public static final int DT_VERDEF = 0x6ffffffc; /* Address of version definition */ 116 | public static final int DT_VERDEFNUM = 0x6ffffffd; /* Number of version definitions */ 117 | public static final int DT_VERNEEDED = 0x6ffffffe; 118 | public static final int DT_VERNEEDNUM = 0x6fffffff; 119 | 120 | public static final int DF_ORIGIN = 0x1; 121 | public static final int DF_SYMBOLIC = 0x2; 122 | public static final int DF_TEXTREL = 0x4; 123 | public static final int DF_BIND_NOW = 0x8; 124 | 125 | /** 126 | * Set RTLD_NOW for this object. 127 | */ 128 | public static final int DF_1_NOW = 0x00000001; 129 | /** 130 | * Set RTLD_GLOBAL for this object. 131 | */ 132 | public static final int DF_1_GLOBAL = 0x00000002; 133 | /** 134 | * Set RTLD_GROUP for this object. 135 | */ 136 | public static final int DF_1_GROUP = 0x00000004; 137 | /** 138 | * Set RTLD_NODELETE for this object. 139 | */ 140 | public static final int DF_1_NODELETE = 0x00000008; 141 | public static final int DF_1_LOADFLTR = 0x00000010; 142 | public static final int DF_1_INITFIRST = 0x00000020; 143 | /** 144 | * Object can not be used with dlopen(3) 145 | */ 146 | public static final int DF_1_NOOPEN = 0x00000040; 147 | public static final int DF_1_ORIGIN = 0x00000080; 148 | public static final int DF_1_DIRECT = 0x00000100; 149 | public static final int DF_1_TRANS = 0x00000200; 150 | public static final int DF_1_INTERPOSE = 0x00000400; 151 | public static final int DF_1_NODEFLIB = 0x00000800; 152 | /** 153 | * Object cannot be dumped with dldump(3) 154 | */ 155 | public static final int DF_1_NODUMP = 0x00001000; 156 | public static final int DF_1_CONFALT = 0x00002000; 157 | public static final int DF_1_ENDFILTEE = 0x00004000; 158 | public static final int DF_1_DISPRELDNE = 0x00008000; 159 | public static final int DF_1_DISPRELPND = 0x00010000; 160 | public static final int DF_1_NODIRECT = 0x00020000; 161 | public static final int DF_1_IGNMULDEF = 0x00040000; 162 | public static final int DF_1_NOKSYMS = 0x00080000; 163 | public static final int DF_1_NOHDR = 0x00100000; 164 | public static final int DF_1_EDITED = 0x00200000; 165 | public static final int DF_1_NORELOC = 0x00400000; 166 | public static final int DF_1_SYMINTPOSE = 0x00800000; 167 | public static final int DF_1_GLOBAUDIT = 0x01000000; 168 | public static final int DF_1_SINGLETON = 0x02000000; 169 | public static final int DF_1_STUB = 0x04000000; 170 | public static final int DF_1_PIE = 0x08000000; 171 | 172 | /** 173 | * For the {@link #DT_STRTAB}. Mandatory. 174 | */ 175 | public long dt_strtab_offset; 176 | 177 | /** 178 | * For the {@link #DT_STRSZ}. Mandatory. 179 | */ 180 | public int dt_strtab_size; 181 | 182 | private MemoizedObject dtStringTable; 183 | public final List entries = new ArrayList<>(); 184 | 185 | /** 186 | * An entry in the {@link #entries} of a {@link ElfDynamicSection}. 187 | *

188 | * In the elf.h header file this represents either of the following structures: 189 | * 190 | *

191 |      * typedef struct {
192 |      *     Elf32_Sword d_tag;
193 |      *     union {
194 |      *         Elf32_Word      d_val;
195 |      *         Elf32_Addr      d_ptr;
196 |      *         Elf32_Off       d_off;
197 |      *     } d_un;
198 |      * } Elf32_Dyn;
199 |      *
200 |      * typedef struct {
201 |      *     Elf64_Xword d_tag;
202 |      *     union {
203 |      *         Elf64_Xword d_val;
204 |      *         Elf64_Addr d_ptr;
205 |      *     } d_un;
206 |      * } Elf64_Dyn;
207 |      * 
208 | */ 209 | public static class ElfDynamicStructure { 210 | public ElfDynamicStructure(long d_tag, long d_val_or_ptr) { 211 | this.d_tag = d_tag; 212 | this.d_val_or_ptr = d_val_or_ptr; 213 | } 214 | 215 | /** 216 | * A tag value whose value defines how to interpret {@link #d_val_or_ptr}. 217 | *

218 | * One of the DT_* constants in {@link ElfDynamicSection}. 219 | */ 220 | public final long d_tag; 221 | /** 222 | * A field whose value is to be interpreted as specified by the {@link #d_tag}. 223 | */ 224 | public final long d_val_or_ptr; 225 | 226 | @Override 227 | public int hashCode() { 228 | final int prime = 31; 229 | int result = 1; 230 | result = prime * result + (int) (d_tag ^ (d_tag >>> 32)); 231 | result = prime * result + (int) (d_val_or_ptr ^ (d_val_or_ptr >>> 32)); 232 | return result; 233 | } 234 | 235 | @Override 236 | public boolean equals(Object obj) { 237 | if (this == obj) return true; 238 | if (obj == null) return false; 239 | if (getClass() != obj.getClass()) return false; 240 | ElfDynamicStructure other = (ElfDynamicStructure) obj; 241 | if (d_tag != other.d_tag) return false; 242 | return d_val_or_ptr == other.d_val_or_ptr; 243 | } 244 | 245 | @Override 246 | public String toString() { 247 | return "ElfDynamicSectionEntry{tag=" + d_tag + ", d_val_or_ptr=" + d_val_or_ptr + "}"; 248 | } 249 | } 250 | 251 | public ElfDynamicSection(final ElfParser parser, ElfSectionHeader header) { 252 | super(parser, header); 253 | 254 | parser.seek(header.sh_offset); 255 | int numEntries = (int) (header.sh_size / 8); 256 | 257 | // Except for the DT_NULL element at the end of the array, and the relative order of DT_NEEDED elements, entries 258 | // may appear in any order. So important to use lazy evaluation to only evaluating e.g. DT_STRTAB after the 259 | // necessary DT_STRSZ is read. 260 | loop: 261 | for (int i = 0; i < numEntries; i++) { 262 | long d_tag = parser.readIntOrLong(); 263 | final long d_val_or_ptr = parser.readIntOrLong(); 264 | entries.add(new ElfDynamicStructure(d_tag, d_val_or_ptr)); 265 | switch ((int) d_tag) { 266 | case DT_NULL: 267 | // A DT_NULL element ends the array (may be following DT_NULL values, but no need to look at them). 268 | break loop; 269 | case DT_STRTAB: { 270 | dtStringTable = new MemoizedObject() { 271 | @Override 272 | protected ElfStringTable computeValue() throws ElfException { 273 | long fileOffsetForStringTable = parser.virtualMemoryAddrToFileOffset(d_val_or_ptr); 274 | return new ElfStringTable(parser, fileOffsetForStringTable, dt_strtab_size, null); // FIXME: null header 275 | } 276 | }; 277 | dt_strtab_offset = d_val_or_ptr; 278 | } 279 | break; 280 | case DT_STRSZ: 281 | if (d_val_or_ptr > Integer.MAX_VALUE) throw new ElfException("Too large DT_STRSZ: " + d_val_or_ptr); 282 | dt_strtab_size = (int) d_val_or_ptr; 283 | break; 284 | } 285 | } 286 | 287 | } 288 | 289 | private ElfDynamicStructure firstEntryWithTag(long desiredTag) { 290 | for (ElfDynamicStructure entry : this.entries) { 291 | if (entry.d_tag == desiredTag) return entry; 292 | } 293 | return null; 294 | } 295 | 296 | public List getNeededLibraries() throws ElfException { 297 | ElfStringTable stringTable = dtStringTable.getValue(); 298 | List result = new ArrayList<>(); 299 | for (ElfDynamicStructure entry : this.entries) { 300 | if (entry.d_tag == DT_NEEDED) result.add(stringTable.get((int) entry.d_val_or_ptr)); 301 | } 302 | return result; 303 | } 304 | 305 | public String getRunPath() { 306 | ElfDynamicStructure runPathEntry = firstEntryWithTag(DT_RUNPATH); 307 | return runPathEntry == null ? null : dtStringTable.getValue().get((int) runPathEntry.d_val_or_ptr); 308 | } 309 | 310 | public long getFlags() { 311 | ElfDynamicStructure flagsEntry = firstEntryWithTag(DT_FLAGS); 312 | return flagsEntry == null ? 0 : flagsEntry.d_val_or_ptr; 313 | } 314 | 315 | public long getFlags1() { 316 | ElfDynamicStructure flagsEntry = firstEntryWithTag(DT_FLAGS_1); 317 | return flagsEntry == null ? 0 : flagsEntry.d_val_or_ptr; 318 | } 319 | 320 | @Override 321 | public String toString() { 322 | return "ElfDynamicStructure{entries=" + this.entries + "}"; 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/ElfException.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | /** 4 | * Generic exception class for all exceptions which occur in this package. Since 5 | * there is no mechanism built into this library for recovering from errors, the 6 | * best clients can do is display the error string. 7 | */ 8 | public class ElfException extends RuntimeException { 9 | 10 | private static final long serialVersionUID = 1L; 11 | 12 | public ElfException(String message) { 13 | super(message); 14 | } 15 | 16 | public ElfException(Throwable cause) { 17 | super(cause); 18 | } 19 | 20 | public ElfException(String message, Throwable cause) { 21 | super(message, cause); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/ElfFile.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.nio.MappedByteBuffer; 9 | import java.util.ArrayList; 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | /** 14 | * An ELF (Executable and Linkable Format) file that can be a relocatable, executable, shared or core file. 15 | *

16 | * Use one of the following methods to parse input to get an instance of this class: 17 | *

23 | *

24 | * Resources about ELF files: 25 | *

30 | */ 31 | public final class ElfFile { 32 | 33 | /** 34 | * Relocatable file type. A possible value of {@link #e_type}. 35 | */ 36 | public static final int ET_REL = 1; 37 | /** 38 | * Executable file type. A possible value of {@link #e_type}. 39 | */ 40 | public static final int ET_EXEC = 2; 41 | /** 42 | * Shared object file type. A possible value of {@link #e_type}. 43 | */ 44 | public static final int ET_DYN = 3; 45 | /** 46 | * Core file file type. A possible value of {@link #e_type}. 47 | */ 48 | public static final int ET_CORE = 4; 49 | 50 | /** 51 | * 32-bit objects. A possible value of {@link #ei_class}. 52 | */ 53 | public static final byte CLASS_32 = 1; 54 | /** 55 | * 64-bit objects. A possible value of {@link #ei_class}. 56 | */ 57 | public static final byte CLASS_64 = 2; 58 | 59 | /** 60 | * System V application binary interface. A possible value of {@link #ei_osabi}. 61 | */ 62 | public static final byte ABI_SYSTEMV = 0x00; 63 | /** 64 | * HP-UX application binary interface. A possible value of {@link #ei_osabi}. 65 | */ 66 | public static final byte ABI_HPUX = 0x01; 67 | /** 68 | * NetBSD application binary interface. A possible value of {@link #ei_osabi}. 69 | */ 70 | public static final byte ABI_NETBSD = 0x02; 71 | /** 72 | * Linux application binary interface. A possible value of {@link #ei_osabi}. 73 | */ 74 | public static final byte ABI_LINUX = 0x03; 75 | /** 76 | * GNU Hurd application binary interface. A possible value of {@link #ei_osabi}. 77 | */ 78 | public static final byte ABI_GNUHERD = 0x04; 79 | /** 80 | * Solaris application binary interface. A possible value of {@link #ei_osabi}. 81 | */ 82 | public static final byte ABI_SOLARIS = 0x06; 83 | /** 84 | * AIX application binary interface. A possible value of {@link #ei_osabi}. 85 | */ 86 | public static final byte ABI_AIX = 0x07; 87 | /** 88 | * IRIX application binary interface. A possible value of {@link #ei_osabi}. 89 | */ 90 | public static final byte ABI_IRIX = 0x08; 91 | /** 92 | * FreeBSD application binary interface. A possible value of {@link #ei_osabi}. 93 | */ 94 | public static final byte ABI_FREEBSD = 0x09; 95 | /** 96 | * Tru64 application binary interface. A possible value of {@link #ei_osabi}. 97 | */ 98 | public static final byte ABI_TRU64 = 0x0A; 99 | /** 100 | * Novell Modesto application binary interface. A possible value of {@link #ei_osabi}. 101 | */ 102 | public static final byte ABI_MODESTO = 0x0B; 103 | /** 104 | * OpenBSD application binary interface. A possible value of {@link #ei_osabi}. 105 | */ 106 | public static final byte ABI_OPENBSD = 0x0C; 107 | /** 108 | * OpenVMS application binary interface. A possible value of {@link #ei_osabi}. 109 | */ 110 | public static final byte ABI_OPENVMS = 0x0D; 111 | /** 112 | * NonStop Kernel application binary interface. A possible value of {@link #ei_osabi}. 113 | */ 114 | public static final byte ABI_NONSTOP = 0x0E; 115 | /** 116 | * AROS application binary interface. A possible value of {@link #ei_osabi}. 117 | */ 118 | public static final byte ABI_AROS = 0x0F; 119 | /** 120 | * Fenix OS application binary interface. A possible value of {@link #ei_osabi}. 121 | */ 122 | public static final byte ABI_FENIX = 0x10; 123 | /** 124 | * CloudABI application binary interface. A possible value of {@link #ei_osabi}. 125 | */ 126 | public static final byte ABI_CLOUD = 0x11; 127 | /** 128 | * Stratus Technologies OpenVOS application binary interface. A possible value of {@link #ei_osabi}. 129 | */ 130 | public static final byte ABI_OPENVOS = 0x12; 131 | 132 | /** 133 | * LSB data encoding. A possible value of {@link #ei_data}. 134 | */ 135 | public static final byte DATA_LSB = 1; 136 | /** 137 | * MSB data encoding. A possible value of {@link #ei_data}. 138 | */ 139 | public static final byte DATA_MSB = 2; 140 | 141 | /** 142 | * No architecture type. A possible value of {@link #e_machine}. 143 | */ 144 | public static final int ARCH_NONE = 0; 145 | /** 146 | * AT&T architecture type. A possible value of {@link #e_machine}. 147 | */ 148 | public static final int ARCH_ATT = 1; 149 | /** 150 | * SPARC architecture type. A possible value of {@link #e_machine}. 151 | */ 152 | public static final int ARCH_SPARC = 2; 153 | /** 154 | * Intel 386 architecture type. A possible value of {@link #e_machine}. 155 | */ 156 | public static final int ARCH_i386 = 3; 157 | /** 158 | * Motorola 68000 architecture type. A possible value of {@link #e_machine}. 159 | */ 160 | public static final int ARCH_68k = 4; 161 | /** 162 | * Motorola 88000 architecture type. A possible value of {@link #e_machine}. 163 | */ 164 | public static final int ARCH_88k = 5; 165 | /** 166 | * Intel 860 architecture type. A possible value of {@link #e_machine}. 167 | */ 168 | public static final int ARCH_i860 = 7; 169 | /** 170 | * MIPS architecture type. A possible value of {@link #e_machine}. 171 | */ 172 | public static final int ARCH_MIPS = 8; 173 | public static final int ARCH_ARM = 0x28; 174 | public static final int ARCH_X86_64 = 0x3E; 175 | public static final int ARCH_AARCH64 = 0xB7; 176 | 177 | /** 178 | * Byte identifying the size of objects, either {@link #CLASS_32} or {link {@value #CLASS_64}. 179 | */ 180 | public final byte ei_class; 181 | 182 | /** 183 | * Returns a byte identifying the data encoding of the processor specific data. This byte will be either 184 | * DATA_INVALID, DATA_LSB or DATA_MSB. 185 | */ 186 | public final byte ei_data; 187 | 188 | /** 189 | * Set to 1 for the original and current (as of writing) version of ELF. 190 | */ 191 | public final byte ei_version; 192 | 193 | /** 194 | * Identifies the target operating system ABI. 195 | */ 196 | public final byte ei_osabi; 197 | 198 | /** 199 | * Further specifies the ABI version. Its interpretation depends on the target ABI. 200 | */ 201 | public final byte es_abiversion; 202 | 203 | /** 204 | * Identifies the object file type. One of the ET_* constants in the class. 205 | */ 206 | public final short e_type; // Elf32_Half 207 | 208 | /** 209 | * The required architecture. One of the ARCH_* constants in the class. 210 | */ 211 | public final short e_machine; // Elf32_Half 212 | /** 213 | * Version 214 | */ 215 | public final int e_version; // Elf32_Word 216 | /** 217 | * Virtual address to which the system first transfers control. If there is no entry point for the file the value is 218 | * 0. 219 | */ 220 | public final long e_entry; // Elf32_Addr 221 | /** 222 | * e_phoff. Program header table offset in bytes. If there is no program header table the value is 0. 223 | */ 224 | public final long e_phoff; // Elf32_Off 225 | /** 226 | * e_shoff. Section header table offset in bytes. If there is no section header table the value is 0. 227 | */ 228 | public final long e_shoff; // Elf32_Off 229 | /** 230 | * e_flags. Processor specific flags. 231 | */ 232 | public final int e_flags; // Elf32_Word 233 | /** 234 | * e_ehsize. ELF header size in bytes. 235 | */ 236 | public final short e_ehsize; // Elf32_Half 237 | /** 238 | * e_phentsize. Size of one entry in the file's program header table in bytes. All entries are the same size. 239 | */ 240 | public final short e_phentsize; // Elf32_Half 241 | /** 242 | * e_phnum. Number of {@link ElfSegment} entries in the program header table, 0 if no entries. 243 | */ 244 | public final short e_phnum; // Elf32_Half 245 | /** 246 | * e_shentsize. Section header entry size in bytes - all entries are the same size. 247 | */ 248 | public final short e_shentsize; // Elf32_Half 249 | /** 250 | * e_shnum. Number of entries in the section header table, 0 if no entries. 251 | */ 252 | public final short e_shnum; // Elf32_Half 253 | 254 | /** 255 | * Elf{32,64}_Ehdr#e_shstrndx. Index into the section header table associated with the section name string table. 256 | * SH_UNDEF if there is no section name string table. 257 | */ 258 | public final short e_shstrndx; // Elf32_Half 259 | 260 | /** 261 | * MemoizedObject array of section headers associated with this ELF file. 262 | */ 263 | private final MemoizedObject[] sections; 264 | /** 265 | * MemoizedObject array of program headers associated with this ELF file. 266 | */ 267 | private final MemoizedObject[] programHeaders; 268 | 269 | /** 270 | * Used to cache symbol table lookup. 271 | */ 272 | private ElfSymbolTableSection symbolTableSection; 273 | /** 274 | * Used to cache dynamic symbol table lookup. 275 | */ 276 | private ElfSymbolTableSection dynamicSymbolTableSection; 277 | 278 | private ElfDynamicSection dynamicSection; 279 | 280 | public boolean is32Bits() { 281 | return ei_class == CLASS_32; 282 | } 283 | 284 | /** 285 | * Returns the section header at the specified index. The section header at index 0 is defined as being a undefined 286 | * section. 287 | * 288 | * @param index the index of the ELF section to fetch 289 | * @return the ELF section at the specified index 290 | */ 291 | public ElfSection getSection(int index) throws ElfException { 292 | return sections[index].getValue(); 293 | } 294 | 295 | public List sectionsOfType(int sectionType) throws ElfException { 296 | if (e_shnum < 2) return Collections.emptyList(); 297 | List result = new ArrayList<>(); 298 | for (int i = 1; i < e_shnum; i++) { 299 | ElfSection section = getSection(i); 300 | if (section.header.sh_type == sectionType) { 301 | result.add(section); 302 | } 303 | } 304 | return result; 305 | } 306 | 307 | 308 | /** 309 | * Returns the section header string table associated with this ELF file. 310 | * 311 | * @return the section header string table for this file 312 | */ 313 | public ElfStringTable getSectionNameStringTable() throws ElfException { 314 | return (ElfStringTable) getSection(e_shstrndx); 315 | } 316 | 317 | /** 318 | * Returns the string table associated with this ELF file. 319 | * 320 | * @return the string table for this file 321 | */ 322 | public ElfStringTable getStringTable() throws ElfException { 323 | return findStringTableWithName(ElfSectionHeader.NAME_STRTAB); 324 | } 325 | 326 | /** 327 | * Returns the dynamic symbol table associated with this ELF file, or null if one does not exist. 328 | * 329 | * @return the dynamic symbol table for this file, if any 330 | */ 331 | public ElfStringTable getDynamicStringTable() throws ElfException { 332 | return findStringTableWithName(ElfSectionHeader.NAME_DYNSTR); 333 | } 334 | 335 | private ElfStringTable findStringTableWithName(String tableName) throws ElfException { 336 | // Loop through the section header and look for a section 337 | // header with the name "tableName". We can ignore entry 0 338 | // since it is defined as being undefined. 339 | return (ElfStringTable) firstSectionByName(tableName); 340 | } 341 | 342 | /** 343 | * The {@link ElfSectionHeader#SHT_SYMTAB} section (of which there may be only one), if any. 344 | * 345 | * @return the symbol table section for this file, if any 346 | */ 347 | public ElfSymbolTableSection getSymbolTableSection() throws ElfException { 348 | return (symbolTableSection != null) ? symbolTableSection : (symbolTableSection = (ElfSymbolTableSection) firstSectionByType(ElfSectionHeader.SHT_SYMTAB)); 349 | } 350 | 351 | /** 352 | * The {@link ElfSectionHeader#SHT_DYNSYM} section (of which there may be only one), if any. 353 | * 354 | * @return the dynamic symbol table section for this file, if any 355 | */ 356 | public ElfSymbolTableSection getDynamicSymbolTableSection() throws ElfException { 357 | return (dynamicSymbolTableSection != null) ? dynamicSymbolTableSection : (dynamicSymbolTableSection = (ElfSymbolTableSection) firstSectionByType(ElfSectionHeader.SHT_DYNSYM)); 358 | } 359 | 360 | /** 361 | * The {@link ElfSectionHeader#SHT_DYNAMIC} section (of which there may be only one). Named ".dynamic". 362 | * 363 | * @return the dynamic section for this file, if any 364 | */ 365 | public ElfDynamicSection getDynamicSection() { 366 | return (dynamicSection != null) ? dynamicSection : (dynamicSection = (ElfDynamicSection) firstSectionByType(ElfSectionHeader.SHT_DYNAMIC)); 367 | } 368 | 369 | public ElfSection firstSectionByType(int type) throws ElfException { 370 | for (int i = 1; i < e_shnum; i++) { 371 | ElfSection sh = getSection(i); 372 | if (sh.header.sh_type == type) return sh; 373 | } 374 | return null; 375 | } 376 | 377 | public T firstSectionByType(Class type) throws ElfException { 378 | for (int i = 1; i < e_shnum; i++) { 379 | ElfSection sh = getSection(i); 380 | if (type.isInstance(sh)) return type.cast(sh); 381 | } 382 | return null; 383 | } 384 | 385 | public ElfSection firstSectionByName(String sectionName) throws ElfException { 386 | for (int i = 1; i < e_shnum; i++) { 387 | ElfSection sh = getSection(i); 388 | if (sectionName.equals(sh.header.getName())) return sh; 389 | } 390 | return null; 391 | } 392 | 393 | /** 394 | * Returns the elf symbol with the specified name or null if one is not found. 395 | * 396 | * @param symbolName the name of the symbol to fetch 397 | * @return information about the specified symbol 398 | */ 399 | public ElfSymbol getELFSymbol(String symbolName) throws ElfException { 400 | if (symbolName == null) return null; 401 | 402 | // Check dynamic symbol table for symbol name. 403 | ElfSymbolTableSection sh = getDynamicSymbolTableSection(); 404 | if (sh != null) { 405 | final int numSymbols = sh.symbols.length; 406 | for (int i = 0; i < numSymbols; i++) { 407 | ElfSymbol symbol = sh.symbols[i]; 408 | if (symbolName.equals(symbol.getName())) { 409 | return symbol; 410 | } 411 | } 412 | } 413 | 414 | // Check symbol table for symbol name. 415 | sh = getSymbolTableSection(); 416 | if (sh != null) { 417 | final int numSymbols = sh.symbols.length; 418 | for (int i = 0; i < numSymbols; i++) { 419 | ElfSymbol symbol = sh.symbols[i]; 420 | if (symbolName.equals(symbol.getName())) { 421 | return symbol; 422 | } 423 | } 424 | } 425 | return null; 426 | } 427 | 428 | /** 429 | * Returns the elf symbol with the specified address or null if one is not found. 'address' is relative to base of 430 | * shared object for .so's. 431 | * 432 | * @param address the address of the symbol to fetch 433 | * @return the symbol at the specified address, if any 434 | */ 435 | public ElfSymbol getELFSymbol(long address) throws ElfException { 436 | // Check dynamic symbol table for address. 437 | ElfSymbol symbol; 438 | long value; 439 | 440 | ElfSymbolTableSection sh = getDynamicSymbolTableSection(); 441 | if (sh != null) { 442 | final int numSymbols = sh.symbols.length; 443 | for (int i = 0; i < numSymbols; i++) { 444 | symbol = sh.symbols[i]; 445 | value = symbol.st_value; 446 | if (address >= value && address < value + symbol.st_size) return symbol; 447 | } 448 | } 449 | 450 | // Check symbol table for symbol name. 451 | sh = getSymbolTableSection(); 452 | if (sh != null) { 453 | final int numSymbols = sh.symbols.length; 454 | for (int i = 0; i < numSymbols; i++) { 455 | symbol = sh.symbols[i]; 456 | value = symbol.st_value; 457 | if (address >= value && address < value + symbol.st_size) return symbol; 458 | } 459 | } 460 | return null; 461 | } 462 | 463 | public ElfSegment getProgramHeader(int index) { 464 | return programHeaders[index].getValue(); 465 | } 466 | 467 | public static ElfFile from(InputStream in) throws IOException { 468 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 469 | int totalRead = 0; 470 | byte[] buffer = new byte[8096]; 471 | boolean firstRead = true; 472 | while (true) { 473 | int readNow = in.read(buffer, totalRead, buffer.length - totalRead); 474 | if (readNow == -1) { 475 | return from(baos.toByteArray()); 476 | } else { 477 | if (firstRead) { 478 | // Abort early. 479 | if (readNow < 4) { 480 | throw new ElfException("Bad first read"); 481 | } else { 482 | if (!(0x7f == buffer[0] && 'E' == buffer[1] && 'L' == buffer[2] && 'F' == buffer[3])) 483 | throw new ElfException("Bad magic number for file"); 484 | } 485 | firstRead = false; 486 | } 487 | baos.write(buffer, 0, readNow); 488 | } 489 | } 490 | } 491 | 492 | public static ElfFile from(File file) throws ElfException, IOException { 493 | byte[] buffer = new byte[(int) file.length()]; 494 | try (FileInputStream in = new FileInputStream(file)) { 495 | int totalRead = 0; 496 | while (totalRead < buffer.length) { 497 | int readNow = in.read(buffer, totalRead, buffer.length - totalRead); 498 | if (readNow == -1) { 499 | throw new ElfException("Premature end of file"); 500 | } else { 501 | totalRead += readNow; 502 | } 503 | } 504 | } 505 | return from(buffer); 506 | } 507 | 508 | public static ElfFile from(byte[] buffer) throws ElfException { 509 | return new ElfFile(new ByteArrayAsFile(buffer)); 510 | } 511 | 512 | public static ElfFile from(MappedByteBuffer mappedByteBuffer) throws ElfException { 513 | return new ElfFile(new MappedFile(mappedByteBuffer)); 514 | } 515 | 516 | public static ElfFile from(BackingFile backingFile) throws ElfException { 517 | return new ElfFile(backingFile); 518 | } 519 | 520 | ElfFile(BackingFile backingFile) throws ElfException { 521 | final ElfParser parser = new ElfParser(this, backingFile); 522 | 523 | byte[] ident = new byte[16]; 524 | int bytesRead = parser.read(ident); 525 | if (bytesRead != ident.length) 526 | throw new ElfException("Error reading elf header (read " + bytesRead + "bytes - expected to read " + ident.length + "bytes)"); 527 | 528 | if (!(0x7f == ident[0] && 'E' == ident[1] && 'L' == ident[2] && 'F' == ident[3])) 529 | throw new ElfException("Bad magic number for file"); 530 | 531 | ei_class = ident[4]; 532 | if (!(ei_class == CLASS_32 || ei_class == CLASS_64)) 533 | throw new ElfException("Invalid object size class: " + ei_class); 534 | ei_data = ident[5]; 535 | if (!(ei_data == DATA_LSB || ei_data == DATA_MSB)) throw new ElfException("Invalid encoding: " + ei_data); 536 | ei_version = ident[6]; 537 | if (ei_version != 1) throw new ElfException("Invalid elf version: " + ei_version); 538 | ei_osabi = ident[7]; // EI_OSABI, target operating system ABI 539 | es_abiversion = ident[8]; // EI_ABIVERSION, ABI version. Linux kernel (after at least 2.6) has no definition of it. 540 | // ident[9-15] // EI_PAD, currently unused. 541 | 542 | e_type = parser.readShort(); 543 | e_machine = parser.readShort(); 544 | e_version = parser.readInt(); 545 | e_entry = parser.readIntOrLong(); 546 | e_phoff = parser.readIntOrLong(); 547 | e_shoff = parser.readIntOrLong(); 548 | e_flags = parser.readInt(); 549 | e_ehsize = parser.readShort(); 550 | e_phentsize = parser.readShort(); 551 | e_phnum = parser.readShort(); 552 | e_shentsize = parser.readShort(); 553 | e_shnum = parser.readShort(); 554 | if (e_shnum == 0) { 555 | throw new ElfException("e_shnum is SHN_UNDEF(0), which is not supported yet" 556 | + " (the actual number of section header table entries is contained in the sh_size field of the section header at index 0)"); 557 | } 558 | e_shstrndx = parser.readShort(); 559 | if (e_shstrndx == /* SHN_XINDEX= */0xffff) { 560 | throw new ElfException("e_shstrndx is SHN_XINDEX(0xffff), which is not supported yet" 561 | + " (the actual index of the section name string table section is contained in the sh_link field of the section header at index 0)"); 562 | } 563 | 564 | sections = MemoizedObject.uncheckedArray(e_shnum); 565 | for (int i = 0; i < e_shnum; i++) { 566 | final long sectionHeaderOffset = e_shoff + (i * e_shentsize); 567 | sections[i] = new MemoizedObject() { 568 | @Override 569 | public ElfSection computeValue() throws ElfException { 570 | ElfSectionHeader elfSectionHeader = new ElfSectionHeader(parser, sectionHeaderOffset); 571 | switch (elfSectionHeader.sh_type) { 572 | case ElfSectionHeader.SHT_DYNAMIC: 573 | return new ElfDynamicSection(parser, elfSectionHeader); 574 | case ElfSectionHeader.SHT_SYMTAB: 575 | case ElfSectionHeader.SHT_DYNSYM: 576 | return new ElfSymbolTableSection(parser, elfSectionHeader); 577 | case ElfSectionHeader.SHT_STRTAB: 578 | return new ElfStringTable(parser, elfSectionHeader.sh_offset, (int) elfSectionHeader.sh_size, elfSectionHeader); 579 | case ElfSectionHeader.SHT_HASH: 580 | return new ElfHashTable(parser, elfSectionHeader); 581 | case ElfSectionHeader.SHT_NOTE: 582 | return new ElfNoteSection(parser, elfSectionHeader); 583 | case ElfSectionHeader.SHT_RELA: 584 | return new ElfRelocationAddendSection(parser, elfSectionHeader); 585 | case ElfSectionHeader.SHT_REL: 586 | return new ElfRelocationSection(parser, elfSectionHeader); 587 | case ElfSectionHeader.SHT_GNU_HASH: 588 | return new ElfGnuHashTable(parser, elfSectionHeader); 589 | default: 590 | return new ElfSection(parser, elfSectionHeader); 591 | } 592 | } 593 | }; 594 | } 595 | 596 | programHeaders = MemoizedObject.uncheckedArray(e_phnum); 597 | for (int i = 0; i < e_phnum; i++) { 598 | final long programHeaderOffset = e_phoff + (i * e_phentsize); 599 | programHeaders[i] = new MemoizedObject() { 600 | @Override 601 | public ElfSegment computeValue() { 602 | return new ElfSegment(parser, programHeaderOffset); 603 | } 604 | }; 605 | } 606 | } 607 | 608 | /** 609 | * The interpreter specified by the {@link ElfSegment#PT_INTERP} program header, if any. 610 | * 611 | * @return the interpreter for this file, if any 612 | */ 613 | public String getInterpreter() { 614 | for (MemoizedObject programHeader : programHeaders) { 615 | ElfSegment ph = programHeader.getValue(); 616 | if (ph.p_type == ElfSegment.PT_INTERP) return ph.getIntepreter(); 617 | } 618 | return null; 619 | } 620 | 621 | } 622 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/ElfGnuHashTable.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | /** 4 | * An ELF section containing a hash table for lookup of dynamic symbols. 5 | * 6 | * Has the section type {@link ElfSectionHeader#SHT_GNU_HASH}. 7 | * 8 | * Replaces {@link ElfHashTable} on almost all modern Linux systems. 9 | * 10 | * See https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/ 11 | */ 12 | public class ElfGnuHashTable extends ElfSection { 13 | 14 | private final int ELFCLASS_BITS; 15 | // The number of .dynsym symbols skipped. 16 | final int symoffset; 17 | final int bloom_shift; 18 | final long[] bloom; 19 | final int[] buckets; 20 | int[] chain; 21 | 22 | ElfGnuHashTable(ElfParser parser, ElfSectionHeader header) { 23 | super(parser, header); 24 | 25 | ELFCLASS_BITS = parser.elfFile.ei_class == ElfFile.CLASS_32 ? 32 : 64; 26 | 27 | parser.seek(header.sh_offset); 28 | int numberOfBuckets = parser.readInt(); 29 | symoffset = parser.readInt(); 30 | int bloomSize = parser.readInt(); 31 | bloom_shift = parser.readInt(); 32 | bloom = new long[bloomSize]; 33 | buckets = new int[numberOfBuckets]; 34 | 35 | for (int i = 0; i < bloomSize; i++) { 36 | bloom[i] = parser.readIntOrLong(); 37 | } 38 | for (int i = 0; i < numberOfBuckets; i++) { 39 | buckets[i] = parser.readInt(); 40 | } 41 | // The chain is initialized on first use in lookupSymbol() due to it requiring .dynsym size. 42 | } 43 | 44 | ElfSymbol lookupSymbol(String symbolName, ElfSymbolTableSection symbolTable) { 45 | if (chain == null) { 46 | int chainSize = ((ElfSymbolTableSection) parser.elfFile.firstSectionByType(ElfSectionHeader.SHT_DYNSYM)).symbols.length - symoffset; 47 | chain = new int[chainSize]; 48 | parser.seek(header.sh_offset + 4*4 + bloom.length*(ELFCLASS_BITS/8) + buckets.length * 4); 49 | for (int i = 0; i < chainSize; i++) { 50 | chain[i] = parser.readInt(); 51 | } 52 | } 53 | 54 | final int nameHash = gnuHash(symbolName); 55 | 56 | long word = bloom[(Integer.remainderUnsigned(Integer.divideUnsigned(nameHash, ELFCLASS_BITS), bloom.length))]; 57 | long mask = 1L << (long) (Integer.remainderUnsigned(nameHash, ELFCLASS_BITS)) 58 | | 1L << (long) (Integer.remainderUnsigned((nameHash >>> bloom_shift), ELFCLASS_BITS)); 59 | 60 | if ((word & mask) != mask) { 61 | // If at least one bit is not set, a symbol is surely missing. 62 | return null; 63 | } 64 | 65 | int symix = buckets[Integer.remainderUnsigned(nameHash, buckets.length)]; 66 | if (symix < symoffset) { 67 | return null; 68 | } 69 | 70 | while (true) { 71 | int hash = chain[symix - symoffset]; 72 | 73 | if ((((long) nameHash)|1L) == (((long) hash)|1L)) { 74 | // The chain contains contiguous sequences of hashes for symbols hashing to the same index, 75 | // with the lowest bit discarded (used to signal end of chain). 76 | ElfSymbol symbol = symbolTable.symbols[symix]; 77 | if (symbolName.equals(symbol.getName())) return symbol; 78 | } 79 | ElfSymbol symbol = symbolTable.symbols[symix]; 80 | 81 | if ((hash & 1) != 0) { 82 | // Chain ends with an element with the lowest bit set to 1. 83 | break; 84 | } 85 | 86 | symix++; 87 | } 88 | 89 | return null; 90 | } 91 | 92 | static int gnuHash(String name) { 93 | int h = 5381; 94 | int nameLength = name.length(); 95 | for (int i = 0; i < nameLength; i++) { 96 | char c = name.charAt(i); 97 | h = (h << 5) + h + c; 98 | } 99 | return h; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/ElfHashTable.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | /** 4 | * An ELF section containing a hash table for lookup of dynamic symbols. 5 | * 6 | * Note that this has been replaced with {@link ElfGnuHashTable} on modern Linux systems. 7 | * 8 | * See https://flapenguin.me/2017/04/24/elf-lookup-dt-hash/ 9 | */ 10 | public class ElfHashTable extends ElfSection { 11 | 12 | private final int[] buckets; 13 | private final int[] chain; 14 | 15 | ElfHashTable(ElfParser parser, ElfSectionHeader header) { 16 | super(parser, header); 17 | 18 | parser.seek(header.sh_offset); 19 | 20 | int num_buckets = parser.readInt(); 21 | int num_chains = parser.readInt(); 22 | 23 | buckets = new int[num_buckets]; 24 | for (int i = 0; i < num_buckets; i++) { 25 | buckets[i] = parser.readInt(); 26 | } 27 | 28 | chain = new int[num_chains]; 29 | for (int i = 0; i < num_chains; i++) { 30 | chain[i] = parser.readInt(); 31 | } 32 | 33 | // Make sure that the amount of bytes we were supposed to read 34 | // was what we actually read. 35 | int actual = num_buckets * 4 + num_chains * 4 + 8; 36 | if (header.sh_size != actual) { 37 | throw new ElfException("Error reading string table (read " + actual + "bytes, expected to read " + header.sh_size + "bytes)."); 38 | } 39 | } 40 | 41 | public ElfSymbol lookupSymbol(String name, ElfSymbolTableSection symbolTable) { 42 | long hashValue = elfHash(name); 43 | int index = buckets[(int) (hashValue % buckets.length)]; 44 | while (true) { 45 | if (index == 0) return null; 46 | ElfSymbol symbol = symbolTable.symbols[index]; 47 | if (name.equals(symbol.getName())) return symbol; 48 | index = chain[index]; 49 | } 50 | } 51 | 52 | static long elfHash(String name) { 53 | long hash = 0; 54 | int nameLength = name.length(); 55 | for (int i = 0; i < nameLength; i++) { 56 | hash = (hash << 4) + name.charAt(i); 57 | long x = hash & 0xF0000000L; 58 | if (x != 0) hash ^= (x >> 24); 59 | hash &= ~x; 60 | } 61 | return hash; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/ElfNoteSection.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | public class ElfNoteSection extends ElfSection { 4 | 5 | /** 6 | * A possible value of the {@link #n_type} where the description should contain {@link GnuAbiDescriptor}. 7 | */ 8 | public static final int NT_GNU_ABI_TAG = 1; 9 | /** 10 | * A possible value of the {@link #n_type} for a note containing synthetic hwcap information. 11 | *

12 | * The descriptor begins with two words: 13 | * word 0: number of entries 14 | * word 1: bitmask of enabled entries 15 | * Then follow variable-length entries, one byte followed by a '\0'-terminated hwcap name string. The byte gives the bit 16 | * number to test if enabled, (1U << bit) & bitmask. 17 | */ 18 | public static final int NT_GNU_HWCAP = 2; 19 | /** 20 | * A possible value of the {@link #n_type} for a note containing build ID bits as generated by "ld --build-id". 21 | *

22 | * The descriptor consists of any nonzero number of bytes. 23 | */ 24 | public static final int NT_GNU_BUILD_ID = 3; 25 | 26 | /** 27 | * A possible value of the {@link #n_type} for a note containing a version string generated by GNU gold. 28 | */ 29 | public static final int NT_GNU_GOLD_VERSION = 4; 30 | 31 | /** 32 | * The descriptor content of a link {@link #NT_GNU_ABI_TAG} type note. 33 | *

34 | * Accessible in {@link #descriptorAsGnuAbi()}. 35 | */ 36 | public final static class GnuAbiDescriptor { 37 | 38 | /** 39 | * A possible value of {@link #operatingSystem}. 40 | */ 41 | public static final int ELF_NOTE_OS_LINUX = 0; 42 | /** 43 | * A possible value of {@link #operatingSystem}. 44 | */ 45 | public static final int ELF_NOTE_OS_GNU = 1; 46 | /** 47 | * A possible value of {@link #operatingSystem}. 48 | */ 49 | public static final int ELF_NOTE_OS_SOLARIS2 = 2; 50 | /** 51 | * A possible value of {@link #operatingSystem}. 52 | */ 53 | public static final int ELF_NOTE_OS_FREEBSD = 3; 54 | 55 | /** 56 | * One of the ELF_NOTE_OS_* constants in this class. 57 | */ 58 | public final int operatingSystem; 59 | /** 60 | * Major version of the required ABI. 61 | */ 62 | public final int majorVersion; 63 | /** 64 | * Minor version of the required ABI. 65 | */ 66 | public final int minorVersion; 67 | /** 68 | * Subminor version of the required ABI. 69 | */ 70 | public final int subminorVersion; 71 | 72 | public GnuAbiDescriptor(int operatingSystem, int majorVersion, int minorVersion, int subminorVersion) { 73 | this.operatingSystem = operatingSystem; 74 | this.majorVersion = majorVersion; 75 | this.minorVersion = minorVersion; 76 | this.subminorVersion = subminorVersion; 77 | } 78 | } 79 | 80 | public final /* uint32_t */ int n_namesz; 81 | public final /* uint32_t */ int n_descsz; 82 | public final /* uint32_t */ int n_type; 83 | private final String n_name; 84 | private final byte[] descriptorBytes; 85 | private final GnuAbiDescriptor gnuAbiDescriptor; 86 | 87 | ElfNoteSection(ElfParser parser, ElfSectionHeader header) throws ElfException { 88 | super(parser, header); 89 | 90 | parser.seek(header.sh_offset); 91 | n_namesz = parser.readInt(); 92 | n_descsz = parser.readInt(); 93 | n_type = parser.readInt(); 94 | byte[] nameBytes = new byte[n_namesz]; 95 | descriptorBytes = new byte[n_descsz]; 96 | int bytesRead = parser.read(nameBytes); 97 | if (bytesRead != n_namesz) { 98 | throw new ElfException("Error reading note name (read=" + bytesRead + ", expected=" + n_namesz + ")"); 99 | } 100 | parser.skip(bytesRead % 4); 101 | 102 | switch (n_type) { 103 | case NT_GNU_ABI_TAG: 104 | gnuAbiDescriptor = new GnuAbiDescriptor(parser.readInt(), parser.readInt(), parser.readInt(), parser.readInt()); 105 | break; 106 | default: 107 | gnuAbiDescriptor = null; 108 | } 109 | 110 | bytesRead = parser.read(descriptorBytes); 111 | if (bytesRead != n_descsz) { 112 | throw new ElfException("Error reading note name (read=" + bytesRead + ", expected=" + n_descsz + ")"); 113 | } 114 | 115 | n_name = new String(nameBytes, 0, n_namesz - 1); // unnecessary trailing 0 116 | } 117 | 118 | public String getName() { 119 | return n_name; 120 | } 121 | 122 | public byte[] descriptorBytes() { 123 | return descriptorBytes; 124 | } 125 | 126 | public String descriptorAsString() { 127 | return new String(descriptorBytes); 128 | } 129 | 130 | public GnuAbiDescriptor descriptorAsGnuAbi() { 131 | return gnuAbiDescriptor; 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/ElfParser.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | /** Package internal class used for parsing ELF files. */ 4 | class ElfParser { 5 | 6 | final ElfFile elfFile; 7 | private final BackingFile backingFile; 8 | 9 | ElfParser(ElfFile elfFile, BackingFile backingFile) { 10 | this.elfFile = elfFile; 11 | this.backingFile = backingFile; 12 | } 13 | 14 | public void seek(long offset) { 15 | backingFile.seek(offset); 16 | } 17 | 18 | public void skip(int bytesToSkip) { 19 | backingFile.skip(bytesToSkip); 20 | } 21 | 22 | short readUnsignedByte() { 23 | return backingFile.readUnsignedByte(); 24 | } 25 | 26 | short readShort() throws ElfException { 27 | int ch1 = readUnsignedByte(); 28 | int ch2 = readUnsignedByte(); 29 | if (elfFile.ei_data == ElfFile.DATA_LSB) { 30 | return (short) (((short) ch2 & 0xff) << 8 | ((short) ch1 & 0xff)); 31 | } else { 32 | return (short) (((short) ch1 & 0xff) << 8 | ((short) ch2 & 0xff)); 33 | } 34 | } 35 | 36 | int readInt() throws ElfException { 37 | int ch1 = readUnsignedByte(); 38 | int ch2 = readUnsignedByte(); 39 | int ch3 = readUnsignedByte(); 40 | int ch4 = readUnsignedByte(); 41 | if (elfFile.ei_data == ElfFile.DATA_LSB) { 42 | return ((int) ch4 & 0xff) << 24 | ((int) ch3 & 0xff) << 16 | ((int) ch2 & 0xff) << 8 | ((int) ch1 & 0xff); 43 | } else { 44 | return ((int) ch1 & 0xff) << 24 | ((int) ch2 & 0xff) << 16 | ((int) ch3 & 0xff) << 8 | ((int) ch4 & 0xff); 45 | } 46 | } 47 | 48 | long readLong() { 49 | int ch1 = readUnsignedByte(); 50 | int ch2 = readUnsignedByte(); 51 | int ch3 = readUnsignedByte(); 52 | int ch4 = readUnsignedByte(); 53 | int ch5 = readUnsignedByte(); 54 | int ch6 = readUnsignedByte(); 55 | int ch7 = readUnsignedByte(); 56 | int ch8 = readUnsignedByte(); 57 | 58 | if (elfFile.ei_data == ElfFile.DATA_LSB) { 59 | return ((long) ch8 << 56) | ((long) ch7 & 0xff) << 48 | ((long) ch6 & 0xff) << 40 60 | | ((long) ch5 & 0xff) << 32 | ((long) ch4 & 0xff) << 24 | ((long) ch3 & 0xff) << 16 61 | | ((long) ch2 & 0xff) << 8 | ((long) ch1 & 0xff); 62 | } else { 63 | return ((long) ch1 << 56) | ((long) ch2 & 0xff) << 48 | ((long) ch3 & 0xff) << 40 64 | | ((long) ch4 & 0xff) << 32 | ((long) ch5 & 0xff) << 24 | ((long) ch6 & 0xff) << 16 65 | | ((long) ch7 & 0xff) << 8 | ((long) ch8 & 0xff); 66 | } 67 | } 68 | 69 | /** Read four-byte int or eight-byte long depending on if {@link ElfFile#ei_class}. */ 70 | long readIntOrLong() { 71 | return elfFile.ei_class == ElfFile.CLASS_32 ? readInt() : readLong(); 72 | } 73 | 74 | /** Returns a big-endian unsigned representation of the int. */ 75 | long unsignedByte(int arg) { 76 | long val; 77 | if (arg >= 0) { 78 | val = arg; 79 | } else { 80 | val = (unsignedByte((short) (arg >>> 16)) << 16) | ((short) arg); 81 | } 82 | return val; 83 | } 84 | 85 | /** 86 | * Find the file offset from a virtual address by looking up the {@link ElfSegment} segment containing the 87 | * address and computing the resulting file offset. 88 | */ 89 | long virtualMemoryAddrToFileOffset(long address) { 90 | for (int i = 0; i < elfFile.e_phnum; i++) { 91 | ElfSegment ph = elfFile.getProgramHeader(i); 92 | if (address >= ph.p_vaddr && address < (ph.p_vaddr + ph.p_memsz)) { 93 | long relativeOffset = address - ph.p_vaddr; 94 | if (relativeOffset >= ph.p_filesz) 95 | throw new ElfException("Can not convert virtual memory address " + Long.toHexString(address) + " to file offset -" + " found segment " + ph 96 | + " but address maps to memory outside file range"); 97 | return ph.p_offset + relativeOffset; 98 | } 99 | } 100 | throw new ElfException("Cannot find segment for address " + Long.toHexString(address)); 101 | } 102 | 103 | public int read(byte[] data) { 104 | return backingFile.read(data); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/ElfRelocation.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | /** 4 | * A relocation connects a symbolic reference with its actual definition. 5 | * Relocatable files must have information that describes how to modify their 6 | * section contents, thus allowing executable and shared object files to hold 7 | * the right information for a process's program image. Relocation entries are 8 | * these data. 9 | *

10 | * These elf relocation entries can be obtained from {@link ElfRelocationSection}:s. 11 | *

12 | * Corresponds to the below C structs: 13 | *


14 |  * typedef struct {
15 |  *   Elf32_Addr r_offset;
16 |  *   Elf32_Word r_info;
17 |  * } Elf32_Rel;
18 |  *
19 |  * typedef struct {
20 |  *   Elf64_Addr	r_offset;
21 |  *   Elf64_Xword r_info;
22 |  * } Elf64_Rel;
23 |  * 
24 | */ 25 | public final class ElfRelocation { 26 | /** 27 | * The location at which to apply the relocation. For a relocatable file, 28 | * this is the byte offset from the beginning of the section. For an 29 | * executable or shared object, the value is the virtual address. 30 | */ 31 | public final long r_offset; // Elf32_Addr or Elf64_Addr 32 | 33 | /** 34 | * This member gives both the symbol table index to which the relocation 35 | * must be made, and the type of relocation to apply. Relocation types are 36 | * processor specific. 37 | */ 38 | public final long r_info; // Elf32_Word or Elf64_Xword 39 | private final ElfFile elfFile; 40 | 41 | ElfRelocation(ElfParser parser, long offset) { 42 | parser.seek(offset); 43 | 44 | r_offset = parser.readIntOrLong(); 45 | r_info = parser.readIntOrLong(); 46 | elfFile = parser.elfFile; 47 | } 48 | 49 | /** 50 | * Corresponds to the ELF32_R_TYPE / ELF64_R_TYPE macros. 51 | * 52 | * @see ElfRelocationTypes 53 | */ 54 | public long getType() { 55 | return elfFile.is32Bits() ? (r_info & 0xFF) : ((int) r_info); 56 | } 57 | 58 | /** 59 | * The symbol table index, with respect to which the relocation must be made. 60 | * Use {@link #getSymbol()} to get the resolved {@link ElfSymbol} from this index. 61 | *

62 | * Corresponds to the ELF32_R_SYM / ELF64_R_SYM macros. 63 | */ 64 | public int getSymbolIndex() { 65 | return (int) (r_info >> (elfFile.is32Bits() ? 8 : 32)); 66 | } 67 | 68 | /** 69 | * The symbol with respect to which the relocation must be made. 70 | * Use {@link #getSymbolIndex()}} to get the resolved {@link ElfSymbol} from this index. 71 | */ 72 | public ElfSymbol getSymbol() { 73 | return elfFile.getSymbolTableSection().symbols[getSymbolIndex()]; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/ElfRelocationAddend.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | /** 4 | * Relocation is the process of connecting symbolic references with symbolic definitions. 5 | * For example, when a program calls a function, the associated call instruction must transfer 6 | * control to the proper destination address at execution. Relocatable files must have 7 | * information that describes how to modify their section contents. This information allows 8 | * executable and shared object files to hold the right information for a process's program 9 | * image. Relocation entries are these data. 10 | *

11 | * These elf relocation entries can be obtained from {@link ElfRelocationAddend}:s. 12 | *

13 | * Corresponds to the below C structs: 14 | *


15 |  * typedef struct {
16 |  *     Elf32_Addr r_offset;
17 |  *     uint32_t   r_info;
18 |  *     int32_t    r_addend;
19 |  * } Elf32_Rela;
20 |  *
21 |  * typedef struct {
22 |  *     Elf64_Addr r_offset;
23 |  *     uint64_t   r_info;
24 |  *     int64_t    r_addend;
25 |  * } Elf64_Rela;
26 |  * 
27 | */ 28 | public final class ElfRelocationAddend { 29 | /** 30 | * This member gives the location at which to apply the 31 | * relocation action. For a relocatable file, the value is 32 | * the byte offset from the beginning of the section to the 33 | * storage unit affected by the relocation. For an 34 | * executable file or shared object, the value is the virtual 35 | * address of the storage unit affected by the relocation. 36 | */ 37 | public final long r_offset; 38 | 39 | /** 40 | * This member gives both the symbol table index with respect 41 | * to which the relocation must be made and the type of 42 | * relocation to apply. Relocation types are processor- 43 | * specific. When the text refers to a relocation entry's 44 | * relocation type or symbol table index, it means the result 45 | * of applying ELF[32|64]_R_TYPE or ELF[32|64]_R_SYM, 46 | * respectively, to the entry's r_info member. 47 | */ 48 | public final long r_info; 49 | 50 | /** 51 | * This member specifies a constant addend used to compute 52 | * the value to be stored into the relocatable field. 53 | */ 54 | public final long r_addend; // int32_t or int64_t 55 | private final ElfFile elfFile; 56 | 57 | ElfRelocationAddend(ElfParser parser, long offset) { 58 | parser.seek(offset); 59 | 60 | r_offset = parser.readIntOrLong(); 61 | r_info = parser.readIntOrLong(); 62 | r_addend = parser.readIntOrLong(); 63 | elfFile = parser.elfFile; 64 | } 65 | 66 | /** 67 | * Corresponds to the ELF32_R_TYPE / ELF64_R_TYPE macros. 68 | * 69 | * @see ElfRelocationTypes 70 | */ 71 | public long getType() { 72 | return elfFile.is32Bits() ? (r_info & 0xFF) : ((int) r_info); 73 | } 74 | 75 | /** 76 | * The symbol table index, with respect to which the relocation must be made. 77 | * Use {@link #getSymbol()} to get the resolved {@link ElfSymbol} from this index. 78 | *

79 | * Corresponds to the ELF32_R_SYM / ELF64_R_SYM macros. 80 | */ 81 | public int getSymbolIndex() { 82 | return (int) (r_info >> (elfFile.is32Bits() ? 8 : 32)); 83 | } 84 | 85 | /** 86 | * The symbol table index, with respect to which the relocation must be made. 87 | * Use {@link #getSymbolIndex()}} to get the resolved {@link ElfSymbol} from this index. 88 | */ 89 | public ElfSymbol getSymbol() { 90 | return elfFile.getSymbolTableSection().symbols[getSymbolIndex()]; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/ElfRelocationAddendSection.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | public final class ElfRelocationAddendSection extends ElfSection { 4 | public final ElfRelocationAddend[] relocations; 5 | 6 | public ElfRelocationAddendSection(ElfParser parser, ElfSectionHeader header) { 7 | super(parser, header); 8 | 9 | int num_entries = (int) (header.sh_size / header.sh_entsize); 10 | relocations = new ElfRelocationAddend[num_entries]; 11 | for (int i = 0; i < num_entries; i++) { 12 | final long relOffset = header.sh_offset + (i * header.sh_entsize); 13 | relocations[i] = new ElfRelocationAddend(parser, relOffset); 14 | } 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/ElfRelocationSection.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | public final class ElfRelocationSection extends ElfSection { 4 | public final ElfRelocation[] relocations; 5 | 6 | public ElfRelocationSection(ElfParser parser, ElfSectionHeader header) { 7 | super(parser, header); 8 | 9 | int num_entries = (int) (header.sh_size / header.sh_entsize); 10 | relocations = new ElfRelocation[num_entries]; 11 | for (int i = 0; i < num_entries; i++) { 12 | final long relOffset = header.sh_offset + (i * header.sh_entsize); 13 | relocations[i] = new ElfRelocation(parser, relOffset); 14 | } 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/ElfRelocationTypes.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | /** 4 | * @see ElfRelocation#getType() 5 | * @see ElfRelocationAddend#getType() 6 | */ 7 | public final class ElfRelocationTypes { 8 | /** 9 | * AMD x86-64: No reloc 10 | */ 11 | public static final int R_X86_64_NONE = 0; 12 | /** 13 | * AMD x86-64: Direct 64 bit. 14 | */ 15 | public static final int R_X86_64_64 = 1; 16 | /** 17 | * AMD x86-64: PC relative 32 bit signed. 18 | */ 19 | public static final int R_X86_64_PC32 = 2; 20 | /** 21 | * AMD x86-64: 32 bit GOT entry. 22 | */ 23 | public static final int R_X86_64_GOT32 = 3; 24 | /** 25 | * AMD x86-64: 32 bit PLT address. 26 | */ 27 | public static final int R_X86_64_PLT32 = 4; 28 | /** 29 | * AMD x86-64: Copy symbol at runtime. 30 | */ 31 | public static final int R_X86_64_COPY = 5; 32 | /** 33 | * AMD x86-64: Create GOT entry. 34 | */ 35 | public static final int R_X86_64_GLOB_DAT = 6; 36 | /** 37 | * AMD x86-64: Create PLT entry. 38 | */ 39 | public static final int R_X86_64_JUMP_SLOT = 7; 40 | /** 41 | * AMD x86-64: Adjust by program base. 42 | */ 43 | public static final int R_X86_64_RELATIVE = 8; 44 | /** 45 | * AMD x86-64: 32 bit signed PC relative offset to GOT. 46 | */ 47 | public static final int R_X86_64_GOTPCREL = 9; 48 | /** 49 | * AMD x86-64: Direct 32 bit zero extended. 50 | */ 51 | public static final int R_X86_64_32 = 10; 52 | /** 53 | * AMD x86-64: Direct 32 bit sign extended. 54 | */ 55 | public static final int R_X86_64_32S = 11; 56 | /** 57 | * AMD x86-64: Direct 16 bit zero extended. 58 | */ 59 | public static final int R_X86_64_16 = 12; 60 | /** 61 | * AMD x86-64: 16 bit sign extended pc relative. 62 | */ 63 | public static final int R_X86_64_PC16 = 13; 64 | /** 65 | * AMD x86-64: Direct 8 bit sign extended. 66 | */ 67 | public static final int R_X86_64_8 = 14; 68 | /** 69 | * AMD x86-64: 8 bit sign extended pc relative. 70 | */ 71 | public static final int R_X86_64_PC8 = 15; 72 | /** 73 | * AMD x86-64: ID of module containing symbol. 74 | */ 75 | public static final int R_X86_64_DTPMOD64 = 16; 76 | /** 77 | * AMD x86-64: Offset in module's TLS block. 78 | */ 79 | public static final int R_X86_64_DTPOFF64 = 17; 80 | /** 81 | * AMD x86-64: Offset in initial TLS block 82 | */ 83 | public static final int R_X86_64_TPOFF64 = 18; 84 | /** 85 | * AMD x86-64: 32 bit signed PC relative offset to two GOT entries for GD symbol. 86 | */ 87 | public static final int R_X86_64_TLSGD = 19; 88 | /** 89 | * AMD x86-64: 32 bit signed PC relative offset to two GOT entries for LD symbol. 90 | */ 91 | public static final int R_X86_64_TLSLD = 20; 92 | /** 93 | * AMD x86-64: Offset in TLS block. 94 | */ 95 | public static final int R_X86_64_DTPOFF32 = 21; 96 | /** 97 | * AMD x86-64: 32 bit signed PC relative offset to GOT entry for IE symbol. 98 | */ 99 | public static final int R_X86_64_GOTTPOFF = 22; 100 | /** 101 | * AMD x86-64: Offset in initial TLS block. 102 | */ 103 | public static final int R_X86_64_TPOFF32 = 23; 104 | /** 105 | * AMD x86-64: PC relative 64 bit. 106 | */ 107 | public static final int R_X86_64_PC64 = 24; 108 | /** 109 | * AMD x86-64: 64 bit offset to GOT. 110 | */ 111 | public static final int R_X86_64_GOTOFF64 = 25; 112 | /** 113 | * AMD x86-64: 32 bit signed pc relative offset to GOT. 114 | */ 115 | public static final int R_X86_64_GOTPC32 = 26; 116 | /** 117 | * AMD x86-64: 64-bit GOT entry offset. 118 | */ 119 | public static final int R_X86_64_GOT64 = 27; 120 | /** 121 | * AMD x86-64:64-bit PC relative offset to GOT entry. 122 | */ 123 | public static final int R_X86_64_GOTPCREL64 = 28; 124 | /** 125 | * AMD x86-64:64-bit PC relative offset to GOT. 126 | */ 127 | public static final int R_X86_64_GOTPC64 = 29; 128 | /** 129 | * AMD x86-64:like GOT64, says PLT entry needed. 130 | */ 131 | public static final int R_X86_64_GOTPLT64 = 30; 132 | /** 133 | * AMD x86-64:64-bit GOT relative offset to PLT entry. 134 | */ 135 | public static final int R_X86_64_PLTOFF64 = 31; 136 | /** 137 | * AMD x86-64:Size of symbol plus 32-bit addend. 138 | */ 139 | public static final int R_X86_64_SIZE32 = 32; 140 | /** 141 | * AMD x86-64:Size of symbol plus 64-bit addend. 142 | */ 143 | public static final int R_X86_64_SIZE64 = 33; 144 | /** 145 | * AMD x86-64:GOT offset for TLS descriptor. 146 | */ 147 | public static final int R_X86_64_GOTPC32_TLSDESC = 34; 148 | /** 149 | * AMD x86-64:Marker for call through TLS descriptor. 150 | */ 151 | public static final int R_X86_64_TLSDESC_CALL = 35; 152 | /** 153 | * AMD x86-64:TLS descriptor. 154 | */ 155 | public static final int R_X86_64_TLSDESC = 36; 156 | /** 157 | * AMD x86-64:Adjust indirectly by program base 158 | */ 159 | public static final int R_X86_64_IRELATIVE = 37; 160 | /** 161 | * AMD x86-64:64-bit adjust by program base. 162 | */ 163 | public static final int R_X86_64_RELATIVE64 = 38; 164 | /** 165 | * AMD x86-64:Load from 32 bit signed pc relative offset to GOT entry without REX prefix, relaxable. 166 | */ 167 | public static final int R_X86_64_GOTPCRELX = 41; 168 | /** 169 | * AMD x86-64:Load from 32 bit signed pc relative offset to GOT entry with REX prefix, relaxable. 170 | */ 171 | public static final int R_X86_64_REX_GOTPCRELX = 42; 172 | public static final int R_X86_64_NUM = 43; 173 | 174 | public static final int R_ARM_NONE = 0; 175 | public static final int R_ARM_PC24 = 1; 176 | public static final int R_ARM_ABS32 = 2; 177 | public static final int R_ARM_REL32 = 3; 178 | public static final int R_ARM_THM_CALL = 10; 179 | public static final int R_ARM_CALL = 28; 180 | public static final int R_ARM_JUMP24 = 29; 181 | public static final int R_ARM_THM_JUMP24 = 30; 182 | public static final int R_ARM_TARGET1 = 38; 183 | public static final int R_ARM_V4BX = 40; 184 | public static final int R_ARM_PREL31 = 42; 185 | public static final int R_ARM_MOVW_ABS_NC = 43; 186 | public static final int R_ARM_MOVT_ABS = 44; 187 | public static final int R_ARM_MOVW_PREL_NC = 45; 188 | public static final int R_ARM_MOVT_PREL = 46; 189 | public static final int R_ARM_THM_MOVW_ABS_NC = 47; 190 | public static final int R_ARM_THM_MOVT_ABS = 48; 191 | public static final int R_ARM_THM_MOVW_PREL_NC = 49; 192 | public static final int R_ARM_THM_MOVT_PREL = 50; 193 | 194 | } 195 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/ElfSection.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | public class ElfSection { 4 | public final ElfSectionHeader header; 5 | protected final ElfParser parser; 6 | 7 | ElfSection(ElfParser parser, ElfSectionHeader header) { 8 | this.header = header; 9 | this.parser = parser; 10 | } 11 | 12 | /** 13 | * Get the bytes contained in this ELF section. 14 | */ 15 | public byte[] getData() { 16 | if (header.sh_size == 0 || header.sh_type == ElfSectionHeader.SHT_NOBITS || header.sh_type == ElfSectionHeader.SHT_NULL) { 17 | return new byte[0]; 18 | } else if (header.sh_size > (long) Integer.MAX_VALUE) { 19 | throw new ElfException("Too big section: " + header.sh_size); 20 | } 21 | 22 | byte[] result = new byte[(int) header.sh_size]; 23 | parser.seek(header.sh_offset); 24 | parser.read(result); 25 | return result; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/ElfSectionHeader.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | /** 4 | * Class corresponding to the Elf32_Shdr/Elf64_Shdr struct. 5 | * 6 | *

7 | * An object file's section header table lets one locate all the file's sections. The section header table is an array 8 | * of Elf32_Shdr or Elf64_Shdr structures. A section header table index is a subscript into this array. The ELF header's 9 | * {@link ElfFile#e_shoff e_shoff member} gives the byte offset from the beginning of the file to the section header 10 | * table with each section header entry being {@link ElfFile#e_shentsize e_shentsize} bytes big. 11 | * 12 | *

13 | * {@link ElfFile#e_shnum e_shnum} normally tells how many entries the section header table contains, but if the number 14 | * of sections is greater than or equal to SHN_LORESERVE (0xff00), e_shnum has the value SHN_UNDEF (0) and the actual 15 | * number of section header table entries is contained in the sh_size field of the section header at index 0 (otherwise, 16 | * the sh_size member of the initial entry contains 0). 17 | * 18 | *

19 | * Some section header table indexes are reserved in contexts where index size is restricted, for example, the st_shndx 20 | * member of a symbol table entry and the e_shnum and e_shstrndx members of the ELF header. In such contexts, the 21 | * reserved values do not represent actual sections in the object file. Also in such contexts, an escape value indicates 22 | * that the actual section index is to be found elsewhere, in a larger field. 23 | */ 24 | public class ElfSectionHeader { 25 | 26 | /** 27 | * Marks the section header as inactive; it does not have an associated section. Other members of the section header 28 | * have undefined values. 29 | */ 30 | public static final int SHT_NULL = 0; 31 | /** 32 | * Section holds information defined by the program. 33 | */ 34 | public static final int SHT_PROGBITS = 1; 35 | /** 36 | * The {@link #sh_type} value for a section containing complete symbol table information necessary for link editing. 37 | *

38 | * See {@link ElfSymbolTableSection}, which is the class representing sections of this type, for more information. 39 | */ 40 | public static final int SHT_SYMTAB = 2; 41 | /** 42 | * Section holds string table information. 43 | */ 44 | public static final int SHT_STRTAB = 3; 45 | /** 46 | * Section holds relocation entries with explicit addends. 47 | */ 48 | public static final int SHT_RELA = 4; 49 | /** 50 | * Section holds symbol hash table. 51 | */ 52 | public static final int SHT_HASH = 5; 53 | /** 54 | * Section holds information for dynamic linking. Only one per ELF file. The dynsym is allocable, and contains the 55 | * symbols needed to support runtime operation. 56 | */ 57 | public static final int SHT_DYNAMIC = 6; 58 | /** 59 | * Section holds information that marks the file. 60 | */ 61 | public static final int SHT_NOTE = 7; 62 | /** 63 | * Section occupies no space but resembles TYPE_PROGBITS. 64 | */ 65 | public static final int SHT_NOBITS = 8; 66 | /** 67 | * Section holds relocation entries without explicit addends. 68 | */ 69 | public static final int SHT_REL = 9; 70 | /** 71 | * Section is reserved but has unspecified semantics. 72 | */ 73 | public static final int SHT_SHLIB = 10; 74 | /** 75 | * The {@link #sh_type} value for a section containing a minimal set of symbols needed for dynamic linking at runtime. 76 | *

77 | * See {@link ElfSymbolTableSection}, which is the class representing sections of this type, for more information. 78 | */ 79 | public static final int SHT_DYNSYM = 11; 80 | public static final int SHT_INIT_ARRAY = 14; 81 | public static final int SHT_FINI_ARRAY = 15; 82 | public static final int SHT_PREINIT_ARRAY = 16; 83 | public static final int SHT_GROUP = 17; 84 | public static final int SHT_SYMTAB_SHNDX = 18; 85 | 86 | /** 87 | * A hash table for fast lookup of dynamic symbols. 88 | *

89 | * See {@link ElfGnuHashTable}. 90 | */ 91 | public static final int SHT_GNU_HASH = 0x6ffffff6; 92 | public static final int SHT_GNU_verdef = 0x6ffffffd; 93 | public static final int SHT_GNU_verneed = 0x6ffffffe; 94 | public static final int SHT_GNU_versym = 0x6fffffff; 95 | 96 | /** 97 | * Lower bound of the range of indexes reserved for operating system-specific semantics. 98 | */ 99 | public static final int SHT_LOOS = 0x60000000; 100 | /** 101 | * Upper bound of the range of indexes reserved for operating system-specific semantics. 102 | */ 103 | public static final int SHT_HIOS = 0x6fffffff; 104 | /** 105 | * Lower bound of the range of indexes reserved for processor-specific semantics. 106 | */ 107 | public static final int SHT_LOPROC = 0x70000000; 108 | /** 109 | * Upper bound of the range of indexes reserved for processor-specific semantics. 110 | */ 111 | public static final int SHT_HIPROC = 0x7fffffff; 112 | /** 113 | * Lower bound of the range of indexes reserved for application programs. 114 | */ 115 | public static final int SHT_LOUSER = 0x80000000; 116 | /** 117 | * Upper bound of the range of indexes reserved for application programs. 118 | */ 119 | public static final int SHT_HIUSER = 0xffffffff; 120 | 121 | /** 122 | * Flag informing that this section contains data that should be writable during process execution. 123 | */ 124 | public static final int FLAG_WRITE = 0x1; 125 | /** 126 | * Flag informing that section occupies memory during process execution. 127 | */ 128 | public static final int FLAG_ALLOC = 0x2; 129 | /** 130 | * Flag informing that section contains executable machine instructions. 131 | */ 132 | public static final int FLAG_EXEC_INSTR = 0x4; 133 | /** 134 | * Flag informing that all the bits in the mask are reserved for processor specific semantics. 135 | */ 136 | public static final int FLAG_MASK = 0xf0000000; 137 | 138 | /** 139 | * Name for the section containing the string table. 140 | *

141 | * This section contains a string table which contains names for symbol structures 142 | * by being indexed by the {@link ElfSymbol#st_name} field. 143 | */ 144 | public static final String NAME_STRTAB = ".strtab"; 145 | /** 146 | * Name for the section containing the dynamic string table. 147 | */ 148 | public static final String NAME_DYNSTR = ".dynstr"; 149 | /** 150 | * Name for the section containing read-only initialized data. 151 | */ 152 | public static final String NAME_RODATA = ".rodata"; 153 | 154 | /** 155 | * Index into the section header string table which gives the name of the section. 156 | */ 157 | public final int sh_name; // Elf32_Word or Elf64_Word - 4 bytes in both. 158 | /** 159 | * Section content and semantics. 160 | */ 161 | public final int sh_type; // Elf32_Word or Elf64_Word - 4 bytes in both. 162 | /** 163 | * Flags. 164 | */ 165 | public final long sh_flags; // Elf32_Word or Elf64_Xword. 166 | /** 167 | * sh_addr. If the section will be in the memory image of a process this will be the address at which the first byte 168 | * of section will be loaded. Otherwise, this value is 0. 169 | */ 170 | public final long sh_addr; // Elf32_Addr 171 | /** 172 | * Offset from beginning of file to first byte of the section. 173 | */ 174 | public final long sh_offset; // Elf32_Off 175 | /** 176 | * Size in bytes of the section. TYPE_NOBITS is a special case. 177 | */ 178 | public final /* uint32_t */ long sh_size; 179 | /** 180 | * Section header table index link. 181 | */ 182 | public final /* uint32_t */ int sh_link; 183 | /** 184 | * Extra information determined by the section type. 185 | */ 186 | public final /* uint32_t */ int sh_info; 187 | /** 188 | * Address alignment constraints for the section. 189 | */ 190 | public final /* uint32_t */ long sh_addralign; 191 | /** 192 | * Size of a fixed-size entry, 0 if none. 193 | */ 194 | public final long sh_entsize; // Elf32_Word 195 | 196 | private final ElfFile elfHeader; 197 | 198 | /** 199 | * Reads the section header information located at offset. 200 | */ 201 | ElfSectionHeader(final ElfParser parser, long offset) { 202 | this.elfHeader = parser.elfFile; 203 | parser.seek(offset); 204 | 205 | sh_name = parser.readInt(); 206 | sh_type = parser.readInt(); 207 | sh_flags = parser.readIntOrLong(); 208 | sh_addr = parser.readIntOrLong(); 209 | sh_offset = parser.readIntOrLong(); 210 | sh_size = parser.readIntOrLong(); 211 | sh_link = parser.readInt(); 212 | sh_info = parser.readInt(); 213 | sh_addralign = parser.readIntOrLong(); 214 | sh_entsize = parser.readIntOrLong(); 215 | } 216 | 217 | /** 218 | * Returns the name of the section or null if the section has no name. 219 | * 220 | * @return the name of the section, if any 221 | */ 222 | public String getName() { 223 | if (sh_name == 0) return null; 224 | ElfStringTable tbl = elfHeader.getSectionNameStringTable(); 225 | return tbl.get(sh_name); 226 | } 227 | 228 | @Override 229 | public String toString() { 230 | return "ElfSectionHeader[name=" + getName() + ", type=0x" + Long.toHexString(sh_type) + "]"; 231 | } 232 | 233 | } 234 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/ElfSegment.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | /** 4 | * Class corresponding to the Elf32_Phdr/Elf64_Phdr struct. 5 | *

6 | * An executable or shared object file's program header table is an array of structures, each describing a segment or 7 | * other information the system needs to prepare the program for execution. An object file segment contains one or more 8 | * sections. Program headers are meaningful only for executable and shared object files. A file specifies its own 9 | * program header size with the ELF header's {@link ElfFile#e_phentsize e_phentsize} and {@link ElfFile#e_phnum 10 | * e_phnum} members. 11 | *

12 | * http://www.sco.com/developers/gabi/latest/ch5.pheader.html#p_type 13 | * http://stackoverflow.com/questions/22612735/how-can-i-find-the-dynamic-libraries-required-by-an-elf-binary-in-c 14 | */ 15 | public class ElfSegment { 16 | 17 | /** 18 | * Type defining that the array element is unused. Other member values are undefined. 19 | */ 20 | public static final int PT_NULL = 0; 21 | /** 22 | * Type defining that the array element specifies a loadable segment. 23 | */ 24 | public static final int PT_LOAD = 1; 25 | /** 26 | * The array element specifies dynamic linking information. 27 | */ 28 | public static final int PT_DYNAMIC = 2; 29 | /** 30 | * The array element specifies the location and size of a null-terminated path name to invoke as an interpreter. 31 | * Meaningful only for executable files (though it may occur for shared objects); it may not occur more than once in 32 | * a file. If it is present, it must precede any loadable segment entry. 33 | */ 34 | public static final int PT_INTERP = 3; 35 | /** 36 | * The array element specifies the location and size of auxiliary information. 37 | */ 38 | public static final int PT_NOTE = 4; 39 | /** 40 | * This segment type is reserved but has unspecified semantics. 41 | */ 42 | public static final int PT_SHLIB = 5; 43 | /** 44 | * The array element, if present, specifies the location and size of the program header table itself, both in the 45 | * file and in the memory image of the program. This segment type may not occur more than once in a file. 46 | */ 47 | public static final int PT_PHDR = 6; 48 | /** 49 | * The array element specifies the Thread-Local Storage template. 50 | */ 51 | public static final int PT_TLS = 7; 52 | 53 | /** 54 | * Lower bound of the range reserved for operating system-specific semantics. 55 | */ 56 | public static final int PT_LOOS = 0x60000000; 57 | /** 58 | * Upper bound of the range reserved for operating system-specific semantics. 59 | */ 60 | public static final int PT_HIOS = 0x6fffffff; 61 | /** 62 | * Lower bound of the range reserved for processor-specific semantics. 63 | */ 64 | public static final int PT_LOPROC = 0x70000000; 65 | /** 66 | * Upper bound of the range reserved for processor-specific semantics. 67 | */ 68 | public static final int PT_HIPROC = 0x7fffffff; 69 | 70 | /** 71 | * Elf{32,64}_Phdr#p_type. Kind of segment this element describes. 72 | */ 73 | public final int p_type; // Elf32_Word/Elf64_Word - 4 bytes in both. 74 | /** 75 | * Flags relevant to this segment. Values for flags are defined in ELFSectionHeader. 76 | */ 77 | public final int p_flags; // Elf32_Word 78 | /** 79 | * Elf{32,64}_Phdr#p_offset. File offset at which the first byte of the segment resides. 80 | */ 81 | public final long p_offset; // Elf32_Off/Elf64_Off - 4 or 8 bytes. 82 | /** 83 | * Elf{32,64}_Phdr#p_vaddr. Virtual address at which the first byte of the segment resides in memory. 84 | */ 85 | public final long p_vaddr; // Elf32_Addr/Elf64_Addr - 4 or 8 bytes. 86 | /** 87 | * Reserved for the physical address of the segment on systems where physical addressing is relevant. 88 | */ 89 | public final long p_paddr; // Elf32_addr/Elf64_Addr - 4 or 8 bytes. 90 | /** 91 | * Elf{32,64}_Phdr#p_filesz. File image size of segment in bytes, may be 0. 92 | */ 93 | public final long p_filesz; // Elf32_Word/Elf64_Xword - 94 | /** 95 | * Elf{32,64}_Phdr#p_memsz. Memory image size of segment in bytes, may be 0. 96 | */ 97 | public final long p_memsz; // Elf32_Word 98 | /** 99 | * Elf{32,64}_Phdr#p_align. The value to which the segments are aligned in memory and in the file. 100 | */ 101 | public final long p_align; // Elf32_Word 102 | 103 | private MemoizedObject ptInterpreter; 104 | 105 | ElfSegment(final ElfParser parser, long offset) { 106 | parser.seek(offset); 107 | if (parser.elfFile.ei_class == ElfFile.CLASS_32) { 108 | // typedef struct { 109 | // Elf32_Word p_type; 110 | // Elf32_Off p_offset; 111 | // Elf32_Addr p_vaddr; 112 | // Elf32_Addr p_paddr; 113 | // Elf32_Word p_filesz; 114 | // Elf32_Word p_memsz; 115 | // Elf32_Word p_flags; 116 | // Elf32_Word p_align; 117 | // } Elf32_Phdr; 118 | p_type = parser.readInt(); 119 | this.p_offset = parser.readInt(); 120 | p_vaddr = parser.readInt(); 121 | p_paddr = parser.readInt(); 122 | p_filesz = parser.readInt(); 123 | p_memsz = parser.readInt(); 124 | p_flags = parser.readInt(); 125 | p_align = parser.readInt(); 126 | } else { 127 | // typedef struct { 128 | // Elf64_Word p_type; 129 | // Elf64_Word p_flags; 130 | // Elf64_Off p_offset; 131 | // Elf64_Addr p_vaddr; 132 | // Elf64_Addr p_paddr; 133 | // Elf64_Xword p_filesz; 134 | // Elf64_Xword p_memsz; 135 | // Elf64_Xword p_align; 136 | // } Elf64_Phdr; 137 | p_type = parser.readInt(); 138 | p_flags = parser.readInt(); 139 | this.p_offset = parser.readLong(); 140 | p_vaddr = parser.readLong(); 141 | p_paddr = parser.readLong(); 142 | p_filesz = parser.readLong(); 143 | p_memsz = parser.readLong(); 144 | p_align = parser.readLong(); 145 | } 146 | 147 | switch (p_type) { 148 | case PT_INTERP: 149 | ptInterpreter = new MemoizedObject() { 150 | @Override 151 | protected String computeValue() throws ElfException { 152 | parser.seek(ElfSegment.this.p_offset); 153 | StringBuilder buffer = new StringBuilder(); 154 | int b; 155 | while ((b = parser.readUnsignedByte()) != 0) 156 | buffer.append((char) b); 157 | return buffer.toString(); 158 | } 159 | }; 160 | break; 161 | } 162 | } 163 | 164 | @Override 165 | public String toString() { 166 | String typeString; 167 | switch (p_type) { 168 | case PT_NULL: 169 | typeString = "PT_NULL"; 170 | break; 171 | case PT_LOAD: 172 | typeString = "PT_LOAD"; 173 | break; 174 | case PT_DYNAMIC: 175 | typeString = "PT_DYNAMIC"; 176 | break; 177 | case PT_INTERP: 178 | typeString = "PT_INTERP"; 179 | break; 180 | case PT_NOTE: 181 | typeString = "PT_NOTE"; 182 | break; 183 | case PT_SHLIB: 184 | typeString = "PT_SHLIB"; 185 | break; 186 | case PT_PHDR: 187 | typeString = "PT_PHDR"; 188 | break; 189 | default: 190 | typeString = "0x" + Long.toHexString(p_type); 191 | break; 192 | } 193 | 194 | String pFlagsString = ""; 195 | if (isReadable()) pFlagsString += (pFlagsString.isEmpty() ? "" : "|") + "read"; 196 | if (isWriteable()) pFlagsString += (pFlagsString.isEmpty() ? "" : "|") + "write"; 197 | if (isExecutable()) pFlagsString += (pFlagsString.isEmpty() ? "" : "|") + "execute"; 198 | 199 | if (pFlagsString.isEmpty()) pFlagsString = "0x" + Long.toHexString(p_flags); 200 | 201 | return "ElfProgramHeader[p_type=" + typeString + ", p_filesz=" + p_filesz + ", p_memsz=" + p_memsz + ", p_flags=" + pFlagsString + ", p_align=" 202 | + p_align + ", range=[0x" + Long.toHexString(p_vaddr) + "-0x" + Long.toHexString(p_vaddr + p_memsz) + "]]"; 203 | } 204 | 205 | /** 206 | * Only for {@link #PT_INTERP} headers. 207 | * 208 | * @return the interpreter path, if any 209 | */ 210 | public String getIntepreter() { 211 | return (ptInterpreter == null) ? null : ptInterpreter.getValue(); 212 | } 213 | 214 | public boolean isReadable() { 215 | return (p_flags & /* PF_R= */4) != 0; 216 | } 217 | 218 | public boolean isWriteable() { 219 | return (p_flags & /* PF_W= */2) != 0; 220 | } 221 | 222 | public boolean isExecutable() { 223 | return (p_flags & /* PF_X= */1) != 0; 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/ElfStringTable.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | /** 4 | * String table sections hold null-terminated character sequences, commonly called strings. 5 | * 6 | * The object file uses these strings to represent symbol and section names. 7 | * 8 | * You reference a string as an index into the string table section. 9 | */ 10 | final public class ElfStringTable extends ElfSection { 11 | 12 | /** The string table data. */ 13 | private final byte[] data; 14 | public final int numStrings; 15 | 16 | /** Reads all the strings from [offset, length]. */ 17 | ElfStringTable(ElfParser parser, long offset, int length, ElfSectionHeader header) throws ElfException { 18 | super(parser, header); 19 | 20 | parser.seek(offset); 21 | data = new byte[length]; 22 | int bytesRead = parser.read(data); 23 | if (bytesRead != length) 24 | throw new ElfException("Error reading string table (read " + bytesRead + "bytes - expected to " + "read " + data.length + "bytes)"); 25 | 26 | int stringsCount = 0; 27 | for (byte datum : data) if (datum == '\0') stringsCount++; 28 | numStrings = stringsCount; 29 | } 30 | 31 | public String get(int index) { 32 | int endPtr = index; 33 | while (data[endPtr] != '\0') 34 | endPtr++; 35 | return new String(data, index, endPtr - index); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/ElfSymbol.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | /** 4 | * An entry in the {@link ElfSymbolTableSection}, which holds information needed to locate and relocate a program's symbolic definitions and references. 5 | *

6 | * In the elf.h header file the struct definitions are: 7 | * 8 | *

  9 |  * typedef struct {
 10 |  *     uint32_t      st_name;
 11 |  *     Elf32_Addr    st_value;
 12 |  *     uint32_t      st_size;
 13 |  *     unsigned char st_info;
 14 |  *     unsigned char st_other;
 15 |  *     uint16_t      st_shndx;
 16 |  * } Elf32_Sym;
 17 |  *
 18 |  * typedef struct {
 19 |  *     uint32_t      st_name;
 20 |  *     unsigned char st_info;
 21 |  *     unsigned char st_other;
 22 |  *     uint16_t      st_shndx;
 23 |  *     Elf64_Addr    st_value;
 24 |  *     uint64_t      st_size;
 25 |  * } Elf64_Sym;
 26 |  * 
27 | */ 28 | public final class ElfSymbol { 29 | 30 | enum Visibility { 31 | /** 32 | * The visibility of symbols with the STV_DEFAULT attribute is as specified by the symbol's binding type. 33 | *

34 | * That is, global and weak symbols are visible outside of their defining component, the executable file or shared object. 35 | * Local symbols are hidden. Global and weak symbols can also be preempted, that is, they may by interposed by definitions 36 | * of the same name in another component. 37 | */ 38 | STV_DEFAULT, 39 | /** 40 | * This visibility attribute is currently reserved. 41 | */ 42 | STV_INTERNAL, 43 | /** 44 | * A symbol defined in the current component is hidden if its name is not visible to other components. Such a symbol is necessarily protected. 45 | *

46 | * This attribute is used to control the external interface of a component. An object named by such a symbol may still be referenced from another component if its address is passed outside. 47 | *

48 | * A hidden symbol contained in a relocatable object is either removed or converted to STB_LOCAL binding by the link-editor when the relocatable object is included in an executable file or shared object. 49 | */ 50 | STV_HIDDEN, 51 | /** 52 | * A symbol defined in the current component is protected if it is visible in other components but cannot be preempted. 53 | *

54 | * Any reference to such a symbol from within the defining component must be resolved to the definition in that component, even if there is a definition in another component that would interpose by the default rules. A symbol with STB_LOCAL binding will not have STV_PROTECTED visibility. 55 | */ 56 | STV_PROTECTED 57 | } 58 | 59 | /** 60 | * Binding specifying that local symbols are not visible outside the object file that contains its definition. 61 | */ 62 | public static final int BINDING_LOCAL = 0; 63 | /** 64 | * Binding specifying that global symbols are visible to all object files being combined. 65 | */ 66 | public static final int BINDING_GLOBAL = 1; 67 | /** 68 | * Binding specifying that the symbol resembles a global symbol, but has a lower precedence. 69 | */ 70 | public static final int BINDING_WEAK = 2; 71 | /** 72 | * Lower bound binding values reserved for processor specific semantics. 73 | */ 74 | public static final int BINDING_LOPROC = 13; 75 | /** 76 | * Upper bound binding values reserved for processor specific semantics. 77 | */ 78 | public static final int BINDING_HIPROC = 15; 79 | 80 | /** 81 | * Type specifying that the symbol is unspecified. 82 | */ 83 | public static final byte STT_NOTYPE = 0; 84 | /** 85 | * Type specifying that the symbol is associated with an object. 86 | */ 87 | public static final byte STT_OBJECT = 1; 88 | /** 89 | * Type specifying that the symbol is associated with a function or other executable code. 90 | */ 91 | public static final byte STT_FUNC = 2; 92 | /** 93 | * Type specifying that the symbol is associated with a section. Symbol table entries of this type exist for 94 | * relocation and normally have the binding BINDING_LOCAL. 95 | */ 96 | public static final byte STT_SECTION = 3; 97 | /** 98 | * Type defining that the symbol is associated with a file. 99 | */ 100 | public static final byte STT_FILE = 4; 101 | /** 102 | * The symbol labels an uninitialized common block. 103 | */ 104 | public static final byte STT_COMMON = 5; 105 | /** 106 | * The symbol specifies a Thread-Local Storage entity. 107 | */ 108 | public static final byte STT_TLS = 6; 109 | 110 | /** 111 | * Lower bound for range reserved for operating system-specific semantics. 112 | */ 113 | public static final byte STT_LOOS = 10; 114 | /** 115 | * Upper bound for range reserved for operating system-specific semantics. 116 | */ 117 | public static final byte STT_HIOS = 12; 118 | /** 119 | * Lower bound for range reserved for processor-specific semantics. 120 | */ 121 | public static final byte STT_LOPROC = 13; 122 | /** 123 | * Upper bound for range reserved for processor-specific semantics. 124 | */ 125 | public static final byte STT_HIPROC = 15; 126 | 127 | /** 128 | * Index into the symbol string table that holds the character representation of the symbols. 0 means the symbol has 129 | * no character name. 130 | */ 131 | public final int st_name; // Elf32_Word 132 | /** 133 | * Value of the associated symbol. This may be a relative address for .so or absolute address for other ELFs. 134 | */ 135 | public final long st_value; // Elf32_Addr 136 | /** 137 | * Size of the symbol. 0 if the symbol has no size or the size is unknown. 138 | */ 139 | public final long st_size; // Elf32_Word 140 | /** 141 | * Specifies the symbol type and binding attributes. 142 | */ 143 | public final short st_info; // unsigned char 144 | /** 145 | * Currently holds the value of 0 and has no meaning. 146 | */ 147 | public final short st_other; // unsigned char 148 | /** 149 | * Index to the associated section header. This value will need to be read as an unsigned short if we compare it to 150 | * ELFSectionHeader.NDX_LORESERVE and ELFSectionHeader.NDX_HIRESERVE. 151 | */ 152 | public final /* Elf32_Half */ short st_shndx; 153 | 154 | public final int section_type; 155 | 156 | /** 157 | * Offset from the beginning of the file to this symbol. 158 | */ 159 | public final long offset; 160 | 161 | private final ElfFile elfHeader; 162 | 163 | ElfSymbol(ElfParser parser, long offset, int section_type) { 164 | this.elfHeader = parser.elfFile; 165 | parser.seek(offset); 166 | this.offset = offset; 167 | if (parser.elfFile.ei_class == ElfFile.CLASS_32) { 168 | st_name = parser.readInt(); 169 | st_value = parser.readInt(); 170 | st_size = parser.readInt(); 171 | st_info = parser.readUnsignedByte(); 172 | st_other = parser.readUnsignedByte(); 173 | st_shndx = parser.readShort(); 174 | } else { 175 | st_name = parser.readInt(); 176 | st_info = parser.readUnsignedByte(); 177 | st_other = parser.readUnsignedByte(); 178 | st_shndx = parser.readShort(); 179 | st_value = parser.readLong(); 180 | st_size = parser.readLong(); 181 | } 182 | 183 | this.section_type = section_type; 184 | 185 | switch (getType()) { 186 | case STT_NOTYPE: 187 | break; 188 | case STT_OBJECT: 189 | break; 190 | case STT_FUNC: 191 | break; 192 | case STT_SECTION: 193 | break; 194 | case STT_FILE: 195 | break; 196 | case STT_LOPROC: 197 | break; 198 | case STT_HIPROC: 199 | break; 200 | default: 201 | break; 202 | } 203 | } 204 | 205 | /** 206 | * Returns the binding for this symbol, extracted from the {@link #st_info} field. 207 | * 208 | * @return the binding for this symbol 209 | */ 210 | public int getBinding() { 211 | return st_info >> 4; 212 | } 213 | 214 | /** 215 | * Returns the symbol type, extracted from the {@link #st_info} field. 216 | * 217 | * @return the type of this symbol 218 | */ 219 | public int getType() { 220 | return st_info & 0x0F; 221 | } 222 | 223 | /** 224 | * Returns the name of the symbol or null if the symbol has no name. 225 | * 226 | * @return the name of this symbol, if any 227 | */ 228 | public String getName() throws ElfException { 229 | // Check to make sure this symbol has a name. 230 | if (st_name == 0) return null; 231 | 232 | // Retrieve the name of the symbol from the correct string table. 233 | String symbol_name = null; 234 | if (section_type == ElfSectionHeader.SHT_SYMTAB) { 235 | symbol_name = elfHeader.getStringTable().get(st_name); 236 | } else if (section_type == ElfSectionHeader.SHT_DYNSYM) { 237 | symbol_name = elfHeader.getDynamicStringTable().get(st_name); 238 | } 239 | return symbol_name; 240 | } 241 | 242 | public Visibility getVisibility() { 243 | if (st_other < 0 || st_other > 3) throw new ElfException("Unsupported st_other=" + st_other); 244 | return Visibility.values()[st_other]; 245 | } 246 | 247 | @Override 248 | public String toString() { 249 | String typeString; 250 | int typeInt = getType(); 251 | switch (typeInt) { 252 | case STT_NOTYPE: 253 | typeString = "unspecified"; 254 | break; 255 | case STT_OBJECT: 256 | typeString = "object"; 257 | break; 258 | case STT_FUNC: 259 | typeString = "function"; 260 | break; 261 | case STT_SECTION: 262 | typeString = "section"; 263 | break; 264 | case STT_FILE: 265 | typeString = "file"; 266 | break; 267 | case STT_LOPROC: 268 | typeString = "loproc"; 269 | break; 270 | case STT_HIPROC: 271 | typeString = "hiproc"; 272 | break; 273 | default: 274 | typeString = Integer.toString(typeInt); 275 | break; 276 | } 277 | 278 | return "ElfSymbol[name=" + getName() + ", type=" + typeString + ", size=" + st_size + "]"; 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/ElfSymbolTableSection.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | /** 4 | * An ELF section with symbol information. 5 | * 6 | * This class represents either of two section types: 7 | *

    8 | *
  • {@link ElfSectionHeader#SHT_DYNSYM}: For a minimal set of symbols adequate for dynamic linking. Can be stripped and has no runtime cost (is non-allocable). Normally named ".dynsym".
  • 9 | *
  • {@link ElfSectionHeader#SHT_SYMTAB}: A complete symbol table typically used for link editing. Can not be stripped (is allocable). Normally named ".symtab".
  • 10 | *
11 | */ 12 | public class ElfSymbolTableSection extends ElfSection { 13 | 14 | public final ElfSymbol[] symbols; 15 | 16 | public ElfSymbolTableSection(ElfParser parser, ElfSectionHeader header) { 17 | super(parser, header); 18 | 19 | int num_entries = (int) (header.sh_size / header.sh_entsize); 20 | symbols = new ElfSymbol[num_entries]; 21 | for (int i = 0; i < num_entries; i++) { 22 | final long symbolOffset = header.sh_offset + (i * header.sh_entsize); 23 | symbols[i] = new ElfSymbol(parser, symbolOffset, header.sh_type); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/MappedFile.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | import java.nio.MappedByteBuffer; 4 | import java.nio.ByteBuffer; 5 | 6 | public class MappedFile implements BackingFile{ 7 | private final MappedByteBuffer mappedByteBuffer; 8 | 9 | public MappedFile(MappedByteBuffer mappedByteBuffer) { 10 | this.mappedByteBuffer = mappedByteBuffer; 11 | this.mappedByteBuffer.position((int) 0); 12 | } 13 | 14 | public void seek(long offset) { 15 | this.mappedByteBuffer.position((int)(offset)); // we may be limited to sub-4GB mapped filess 16 | } 17 | 18 | public void skip(int bytesToSkip) { 19 | mappedByteBuffer.position(mappedByteBuffer.position() + bytesToSkip); 20 | } 21 | 22 | public short readUnsignedByte() { 23 | int val = -1; 24 | byte temp = mappedByteBuffer.get(); 25 | val = temp & 0xFF; // bytes are signed in Java =_= so assigning them to a longer type risks sign extension. 26 | if (val < 0) throw new ElfException("Trying to read outside file"); 27 | return (short) val; 28 | } 29 | 30 | public int read(byte[] data) { 31 | mappedByteBuffer.get(data); 32 | return data.length; 33 | } 34 | 35 | public byte get() { 36 | return mappedByteBuffer.get(); 37 | } 38 | 39 | public int write(byte[] data) { 40 | mappedByteBuffer.put(data); 41 | return data.length; 42 | } 43 | public void put(byte data) { 44 | mappedByteBuffer.put(data); 45 | } 46 | 47 | public ByteBuffer getBuffer() { 48 | return mappedByteBuffer; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/net/fornwall/jelf/MemoizedObject.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | /** 4 | * A memoized object. Override {@link #computeValue} in subclasses; call {@link #getValue} in using code. 5 | */ 6 | abstract class MemoizedObject { 7 | private boolean computed; 8 | private T value; 9 | 10 | /** 11 | * Should compute the value of this memoized object. This will only be called once, upon the first call to 12 | * {@link #getValue}. 13 | */ 14 | protected abstract T computeValue() throws ElfException; 15 | 16 | /** Public accessor for the memoized value. */ 17 | public final T getValue() throws ElfException { 18 | if (!computed) { 19 | value = computeValue(); 20 | computed = true; 21 | } 22 | return value; 23 | } 24 | 25 | @SuppressWarnings("unchecked") 26 | public static MemoizedObject[] uncheckedArray(int size) { 27 | return new MemoizedObject[size]; 28 | } 29 | } -------------------------------------------------------------------------------- /src/test/java/net/fornwall/jelf/BasicTest.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.util.Arrays; 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | class BasicTest { 11 | 12 | @Test 13 | void testAndroidArmBinTset() throws Exception { 14 | TestHelper.parseFile("android_arm_tset", file -> { 15 | Assertions.assertEquals(ElfFile.CLASS_32, file.ei_class); 16 | Assertions.assertEquals(ElfFile.DATA_LSB, file.ei_data); 17 | Assertions.assertEquals(ElfFile.ET_EXEC, file.e_type); 18 | Assertions.assertEquals(ElfFile.ARCH_ARM, file.e_machine); 19 | Assertions.assertEquals(32, file.e_phentsize); 20 | Assertions.assertEquals(7, file.e_phnum); 21 | Assertions.assertEquals(52, file.e_phoff); 22 | Assertions.assertEquals(40, file.e_shentsize); 23 | Assertions.assertEquals(25, file.e_shnum); 24 | Assertions.assertEquals(15856, file.e_shoff); 25 | TestHelper.assertSectionNames(file, null, ".interp", ".dynsym", ".dynstr", ".hash", ".rel.dyn", ".rel.plt", 26 | ".plt", ".text"); 27 | Assertions.assertEquals("/system/bin/linker", file.getInterpreter()); 28 | 29 | ElfDynamicSection dynamic = file.getDynamicSection(); 30 | Assertions.assertNotNull(dynamic); 31 | Assertions.assertEquals(".dynamic", dynamic.header.getName()); 32 | Assertions.assertEquals(8, dynamic.header.sh_entsize); 33 | Assertions.assertEquals(248, dynamic.header.sh_size); 34 | Assertions.assertEquals(ElfDynamicSection.DF_BIND_NOW, dynamic.getFlags()); 35 | Assertions.assertEquals(ElfDynamicSection.DF_1_NOW, dynamic.getFlags1()); 36 | 37 | Assertions.assertEquals(Arrays.asList("libncursesw.so.6", "libc.so", "libdl.so"), 38 | dynamic.getNeededLibraries()); 39 | Assertions.assertEquals("/data/data/com.termux/files/usr/lib", dynamic.getRunPath()); 40 | 41 | Assertions.assertEquals(26, dynamic.entries.size()); 42 | Assertions.assertEquals(new ElfDynamicSection.ElfDynamicStructure(3, 0xbf44), dynamic.entries.get(0)); 43 | Assertions.assertEquals(new ElfDynamicSection.ElfDynamicStructure(2, 352), dynamic.entries.get(1)); 44 | Assertions.assertEquals(new ElfDynamicSection.ElfDynamicStructure(0x17, 0x8868), dynamic.entries.get(2)); 45 | Assertions.assertEquals(new ElfDynamicSection.ElfDynamicStructure(0x6ffffffb, 1), dynamic.entries.get(24)); 46 | Assertions.assertEquals(new ElfDynamicSection.ElfDynamicStructure(0, 0), dynamic.entries.get(25)); 47 | 48 | TestHelper.validateHashTable(file); 49 | }); 50 | } 51 | 52 | @Test 53 | void testAndroidArmLibNcurses() throws Exception { 54 | TestHelper.parseFile("android_arm_libncurses", file -> { 55 | Assertions.assertEquals(ElfFile.CLASS_32, file.ei_class); 56 | Assertions.assertEquals(ElfFile.DATA_LSB, file.ei_data); 57 | Assertions.assertEquals(ElfFile.ET_DYN, file.e_type); 58 | Assertions.assertEquals(ElfFile.ARCH_ARM, file.e_machine); 59 | Assertions.assertEquals("/system/bin/linker", file.getInterpreter()); 60 | 61 | List noteSections = file.sectionsOfType(ElfSectionHeader.SHT_NOTE); 62 | Assertions.assertEquals(1, noteSections.size()); 63 | Assertions.assertEquals(".note.gnu.gold-version", noteSections.get(0).header.getName()); 64 | Assertions.assertEquals("GNU", ((ElfNoteSection) noteSections.get(0)).getName()); 65 | Assertions.assertEquals(ElfNoteSection.NT_GNU_GOLD_VERSION, ((ElfNoteSection) noteSections.get(0)).n_type); 66 | Assertions.assertEquals("gold 1.11", ((ElfNoteSection) noteSections.get(0)).descriptorAsString()); 67 | 68 | ElfNoteSection noteSection = file.firstSectionByType(ElfNoteSection.class); 69 | Assertions.assertNotNull(noteSection); 70 | Assertions.assertEquals(".note.gnu.gold-version", noteSection.header.getName()); 71 | Assertions.assertSame(noteSection, noteSections.get(0)); 72 | 73 | ElfSymbolTableSection dynsym = (ElfSymbolTableSection) file.firstSectionByType(ElfSectionHeader.SHT_DYNSYM); 74 | Assertions.assertNotNull(dynsym); 75 | Assertions.assertEquals(".dynsym", dynsym.header.getName()); 76 | Assertions.assertEquals(768, dynsym.symbols.length); 77 | 78 | ElfSymbol symbol = dynsym.symbols[0]; 79 | Assertions.assertNull(symbol.getName()); 80 | Assertions.assertEquals(ElfSymbol.STT_NOTYPE, symbol.getType()); 81 | Assertions.assertEquals(0, symbol.st_size); 82 | Assertions.assertEquals(ElfSymbol.BINDING_LOCAL, symbol.getBinding()); 83 | Assertions.assertEquals(ElfSymbol.Visibility.STV_DEFAULT, symbol.getVisibility()); 84 | symbol = dynsym.symbols[1]; 85 | Assertions.assertEquals("__cxa_finalize", symbol.getName()); 86 | Assertions.assertEquals(ElfSymbol.STT_FUNC, symbol.getType()); 87 | Assertions.assertEquals(0, symbol.st_size); 88 | Assertions.assertEquals(ElfSymbol.BINDING_GLOBAL, symbol.getBinding()); 89 | Assertions.assertEquals(ElfSymbol.Visibility.STV_DEFAULT, symbol.getVisibility()); 90 | symbol = dynsym.symbols[767]; 91 | Assertions.assertEquals("_Unwind_GetTextRelBase", symbol.getName()); 92 | Assertions.assertEquals(ElfSymbol.STT_FUNC, symbol.getType()); 93 | Assertions.assertEquals(8, symbol.st_size); 94 | Assertions.assertEquals(ElfSymbol.BINDING_GLOBAL, symbol.getBinding()); 95 | Assertions.assertEquals(ElfSymbol.Visibility.STV_DEFAULT, symbol.getVisibility()); 96 | 97 | ElfSymbolTableSection symtab = (ElfSymbolTableSection) file.firstSectionByType(ElfSectionHeader.SHT_SYMTAB); 98 | Assertions.assertNotNull(symtab); 99 | Assertions.assertEquals(".symtab", symtab.header.getName()); 100 | Assertions.assertEquals(2149, symtab.symbols.length); 101 | symbol = symtab.symbols[0]; 102 | Assertions.assertNull(symbol.getName()); 103 | Assertions.assertEquals(ElfSymbol.STT_NOTYPE, symbol.getType()); 104 | Assertions.assertEquals(ElfSymbol.BINDING_LOCAL, symbol.getBinding()); 105 | Assertions.assertEquals(ElfSymbol.Visibility.STV_DEFAULT, symbol.getVisibility()); 106 | symbol = symtab.symbols[1]; 107 | Assertions.assertEquals("crtbegin_so.c", symbol.getName()); 108 | Assertions.assertEquals(ElfSymbol.STT_FILE, symbol.getType()); 109 | Assertions.assertEquals(ElfSymbol.BINDING_LOCAL, symbol.getBinding()); 110 | Assertions.assertEquals(ElfSymbol.Visibility.STV_DEFAULT, symbol.getVisibility()); 111 | symbol = symtab.symbols[2148]; 112 | Assertions.assertEquals("_Unwind_GetTextRelBase", symbol.getName()); 113 | Assertions.assertEquals(ElfSymbol.STT_FUNC, symbol.getType()); 114 | Assertions.assertEquals(ElfSymbol.BINDING_GLOBAL, symbol.getBinding()); 115 | Assertions.assertEquals(ElfSymbol.Visibility.STV_DEFAULT, symbol.getVisibility()); 116 | 117 | TestHelper.validateHashTable(file); 118 | 119 | ElfDynamicSection dynamic = file.firstSectionByType(ElfDynamicSection.class); 120 | Assertions.assertNotNull(dynamic); 121 | Assertions.assertEquals(ElfDynamicSection.DF_SYMBOLIC | ElfDynamicSection.DF_BIND_NOW, dynamic.getFlags()); 122 | Assertions.assertEquals(ElfDynamicSection.DF_1_NOW, dynamic.getFlags1()); 123 | 124 | Assertions.assertTrue(file.getProgramHeader(0).isReadable()); 125 | Assertions.assertFalse(file.getProgramHeader(0).isWriteable()); 126 | Assertions.assertFalse(file.getProgramHeader(0).isExecutable()); 127 | 128 | Assertions.assertTrue(file.getProgramHeader(2).isReadable()); 129 | Assertions.assertFalse(file.getProgramHeader(2).isWriteable()); 130 | Assertions.assertTrue(file.getProgramHeader(2).isExecutable()); 131 | }); 132 | } 133 | 134 | @Test 135 | void testLinuxAmd64BinDash() throws Exception { 136 | TestHelper.parseFile("linux_amd64_bindash", file -> { 137 | Assertions.assertEquals(ElfFile.CLASS_64, file.ei_class); 138 | Assertions.assertEquals(ElfFile.DATA_LSB, file.ei_data); 139 | Assertions.assertEquals(ElfFile.ET_DYN, file.e_type); 140 | Assertions.assertEquals(ElfFile.ARCH_X86_64, file.e_machine); 141 | Assertions.assertEquals(56, file.e_phentsize); 142 | Assertions.assertEquals(9, file.e_phnum); 143 | Assertions.assertEquals(64, file.e_shentsize); 144 | Assertions.assertEquals(64, file.e_phoff); 145 | Assertions.assertEquals(27, file.e_shnum); 146 | Assertions.assertEquals(119544, file.e_shoff); 147 | TestHelper.assertSectionNames(file, null, ".interp", ".note.ABI-tag", ".note.gnu.build-id", ".gnu.hash", 148 | ".dynsym"); 149 | 150 | ElfDynamicSection ds = file.getDynamicSection(); 151 | Assertions.assertEquals(Collections.singletonList("libc.so.6"), ds.getNeededLibraries()); 152 | 153 | Assertions.assertEquals("/lib64/ld-linux-x86-64.so.2", file.getInterpreter()); 154 | 155 | ElfSection rodata = file.firstSectionByName(ElfSectionHeader.NAME_RODATA); 156 | Assertions.assertNotNull(rodata); 157 | Assertions.assertEquals(ElfSectionHeader.SHT_PROGBITS, rodata.header.sh_type); 158 | 159 | List noteSections = file.sectionsOfType(ElfSectionHeader.SHT_NOTE); 160 | Assertions.assertEquals(2, noteSections.size()); 161 | ElfNoteSection note1 = (ElfNoteSection) noteSections.get(0); 162 | ElfNoteSection note2 = (ElfNoteSection) noteSections.get(1); 163 | Assertions.assertEquals(".note.ABI-tag", note1.header.getName()); 164 | Assertions.assertEquals("GNU", note1.getName()); 165 | Assertions.assertEquals(ElfNoteSection.NT_GNU_ABI_TAG, note1.n_type); 166 | Assertions.assertEquals(ElfNoteSection.GnuAbiDescriptor.ELF_NOTE_OS_LINUX, 167 | note1.descriptorAsGnuAbi().operatingSystem); 168 | Assertions.assertEquals(2, note1.descriptorAsGnuAbi().majorVersion); 169 | Assertions.assertEquals(6, note1.descriptorAsGnuAbi().minorVersion); 170 | Assertions.assertEquals(24, note1.descriptorAsGnuAbi().subminorVersion); 171 | Assertions.assertEquals(".note.gnu.build-id", note2.header.getName()); 172 | Assertions.assertEquals("GNU", note2.getName()); 173 | Assertions.assertEquals(ElfNoteSection.NT_GNU_BUILD_ID, note2.n_type); 174 | Assertions.assertEquals(0x14, note2.descriptorBytes().length); 175 | Assertions.assertEquals(0x0f, note2.descriptorBytes()[0]); 176 | Assertions.assertArrayEquals(new byte[]{0x0f, 0x7f, (byte) 0xf2, (byte) 0x87, (byte) 0xcf, 0x26, 177 | (byte) 0xeb, (byte) 0xa9, (byte) 0xa6, 0x64, 0x3b, 0x12, 0x26, 0x08, (byte) 0x9e, (byte) 0xea, 0x57, 178 | (byte) 0xcb, 0x7e, 0x44}, note2.descriptorBytes()); 179 | 180 | TestHelper.validateHashTable(file); 181 | }); 182 | } 183 | 184 | @Test 185 | public void testObjectFile() throws Exception { 186 | TestHelper.parseFile("objectFile.o", file -> { 187 | Assertions.assertEquals(ElfFile.CLASS_32, file.ei_class); 188 | Assertions.assertEquals(ElfFile.DATA_LSB, file.ei_data); 189 | Assertions.assertEquals(ElfFile.ET_REL, file.e_type); 190 | TestHelper.assertSectionNames(file, null, ".text", ".rel.text", ".data", ".bss", ".comment", 191 | ".ARM.attributes", ".symtab", ".strtab", ".shstrtab"); 192 | 193 | List sections = file.sectionsOfType(ElfSectionHeader.SHT_REL); 194 | Assertions.assertEquals(1, sections.size()); 195 | ElfRelocationSection relocations = (ElfRelocationSection) sections.get(0); 196 | Assertions.assertEquals(1, relocations.relocations.length); 197 | 198 | // "Relocation section '.rel.text' at offset 0x14c contains 1 entry: 199 | // Offset Info Type Sym.Value Sym. Name 200 | //00000006 0000080a R_ARM_THM_CALL 00000001 callee" 201 | ElfRelocation rel = relocations.relocations[0]; 202 | Assertions.assertEquals(0x0000_0006, rel.r_offset); 203 | Assertions.assertEquals(0x0000_080A, rel.r_info); 204 | Assertions.assertEquals(ElfRelocationTypes.R_ARM_THM_CALL, rel.getType()); 205 | Assertions.assertEquals("callee", rel.getSymbol().getName()); 206 | }); 207 | } 208 | 209 | @Test 210 | public void testObjectFile64() throws Exception { 211 | TestHelper.parseFile("objectFile-64.o", file -> { 212 | Assertions.assertEquals(ElfFile.CLASS_64, file.ei_class); 213 | Assertions.assertEquals(ElfFile.DATA_LSB, file.ei_data); 214 | Assertions.assertEquals(ElfFile.ET_REL, file.e_type); 215 | TestHelper.assertSectionNames(file, null, ".text", ".rela.text", ".data", ".bss", ".comment", 216 | ".note.GNU-stack", ".note.gnu.property", ".eh_frame", ".rela.eh_frame", ".symtab", ".strtab", ".shstrtab"); 217 | 218 | List sections = file.sectionsOfType(ElfSectionHeader.SHT_RELA); 219 | Assertions.assertEquals(2, sections.size()); 220 | Assertions.assertEquals(".rela.text", sections.get(0).header.getName()); 221 | Assertions.assertEquals(".rela.eh_frame", sections.get(1).header.getName()); 222 | 223 | ElfRelocationAddendSection relocations = (ElfRelocationAddendSection) sections.get(0); 224 | // readelf -a: 225 | // "Relocation section '.rela.text' at offset 0x1a0 contains 1 entry: 226 | // Offset Info Type Sym. Value Sym. Name + Addend 227 | //00000000000d 000400000002 R_X86_64_PC32 0000000000000000 value_to_add - 4" 228 | Assertions.assertEquals(1, relocations.relocations.length); 229 | ElfRelocationAddend rel = relocations.relocations[0]; 230 | Assertions.assertEquals(0x0000_000d, rel.r_offset); 231 | Assertions.assertEquals(0x0004_0000_0002L, rel.r_info); 232 | Assertions.assertEquals(-4, rel.r_addend); 233 | Assertions.assertEquals(ElfRelocationTypes.R_X86_64_PC32, rel.getType()); 234 | Assertions.assertEquals("value_to_add", rel.getSymbol().getName()); 235 | 236 | relocations = (ElfRelocationAddendSection) sections.get(1); 237 | // readelf -a: 238 | // "Relocation section '.rela.eh_frame' at offset 0x1b8 contains 1 entry: 239 | // Offset Info Type Sym. Value Sym. Name + Addend 240 | // 000000000020 000200000002 R_X86_64_PC32 0000000000000000 .text + 0 241 | // No processor specific unwind information to decode" 242 | Assertions.assertEquals(1, relocations.relocations.length); 243 | rel = relocations.relocations[0]; 244 | Assertions.assertEquals(0x0000_0020, rel.r_offset); 245 | Assertions.assertEquals(0x0002_0000_0002L, rel.r_info); 246 | Assertions.assertEquals(0, rel.r_addend); 247 | Assertions.assertEquals(ElfRelocationTypes.R_X86_64_PC32, rel.getType()); 248 | }); 249 | } 250 | 251 | @Test 252 | void testLinuxUsrBinYes() throws Exception { 253 | TestHelper.parseFile("usr-bin-yes", file -> { 254 | ElfSymbol s = file.getELFSymbol("fputc_unlocked"); 255 | Assertions.assertNotNull(s); 256 | Assertions.assertEquals("fputc_unlocked", s.getName()); 257 | 258 | ElfSection interpSection = file.getSection(1); 259 | // objcopy --dump-section .interp=OUT src/test/resources/usr-bin-yes: 260 | Assertions.assertArrayEquals(new byte[]{ 261 | 0x2f, 0x6c, 0x69, 0x62, 0x36, 0x34, 0x2f, 0x6c, 0x64, 0x2d, 0x6c, 0x69, 0x6e, 0x75, 262 | 0x78, 0x2d, 0x78, 0x38, 0x36, 0x2d, 0x36, 0x34, 0x2e, 0x73, 0x6f, 0x2e, 0x32, 0x00 263 | }, interpSection.getData()); 264 | }); 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /src/test/java/net/fornwall/jelf/ElfGnuHashTableTest.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | class ElfGnuHashTableTest { 7 | 8 | @Test 9 | void gnuHash() { 10 | Assertions.assertEquals(0xfde460be, ElfGnuHashTable.gnuHash("foobar")); 11 | Assertions.assertEquals(0x90f1e4b0, ElfGnuHashTable.gnuHash("strsigna")); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/test/java/net/fornwall/jelf/ElfHashTableTest.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | class ElfHashTableTest { 7 | 8 | @Test 9 | void elfHash() { 10 | Assertions.assertEquals(0x0bc334fc, ElfHashTable.elfHash("freelocal")); 11 | Assertions.assertEquals(0x06d65882, ElfHashTable.elfHash("foobar")); 12 | Assertions.assertEquals(0x007b7cb3, ElfHashTable.elfHash("tputs")); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/net/fornwall/jelf/EndianProblemTest.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | public class EndianProblemTest { 7 | @Test 8 | public void testObjectFile() throws Exception { 9 | TestHelper.parseFile("little-endian-test", file -> { 10 | Assertions.assertEquals(ElfFile.CLASS_64, file.ei_class); 11 | Assertions.assertEquals(ElfFile.DATA_LSB, file.ei_data); 12 | Assertions.assertEquals(0x8000_0040L, file.e_entry); 13 | }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/net/fornwall/jelf/ReadmeTest.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.io.IOException; 7 | import java.nio.file.Files; 8 | import java.nio.file.Paths; 9 | 10 | public class ReadmeTest { 11 | 12 | @Test 13 | void versionInReadmeUpToDate() throws IOException { 14 | String currentVersion = System.getProperty("jelf.version"); 15 | String readmeContent = new String(Files.readAllBytes(Paths.get("./README.md"))); 16 | Assertions.assertTrue(readmeContent.contains("" + currentVersion + "")); 17 | Assertions.assertTrue(readmeContent.contains("implementation 'net.fornwall:jelf:" + currentVersion)); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/net/fornwall/jelf/TestHelper.java: -------------------------------------------------------------------------------- 1 | package net.fornwall.jelf; 2 | 3 | import java.nio.MappedByteBuffer; 4 | import java.nio.channels.FileChannel; 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import java.nio.file.Paths; 8 | import java.nio.file.StandardOpenOption; 9 | import java.util.EnumSet; 10 | 11 | import org.junit.jupiter.api.Assertions; 12 | 13 | public class TestHelper { 14 | public interface TestMethod { 15 | void test(ElfFile file) throws Exception; 16 | } 17 | 18 | public static void parseFile(String fileName, TestMethod consumer) throws Exception { 19 | ElfFile fromStream = ElfFile.from(BasicTest.class.getResourceAsStream('/' + fileName)); 20 | consumer.test(fromStream); 21 | 22 | Path path = Paths.get(BasicTest.class.getResource('/' + fileName).toURI()); 23 | try (FileChannel fileChannel = (FileChannel) Files.newByteChannel(path, EnumSet.of(StandardOpenOption.READ))) { 24 | MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()); 25 | ElfFile fromMappedBuffer = ElfFile.from(mappedByteBuffer); 26 | consumer.test(fromMappedBuffer); 27 | } 28 | } 29 | 30 | public static void assertSectionNames(ElfFile file, String... expectedSectionNames) { 31 | for (int i = 0; i < expectedSectionNames.length; i++) { 32 | String expected = expectedSectionNames[i]; 33 | String actual = file.getSection(i).header.getName(); 34 | if (expected == null) { 35 | Assertions.assertNull(actual); 36 | } else { 37 | Assertions.assertEquals(expected, actual); 38 | } 39 | } 40 | } 41 | 42 | public static void validateHashTable(ElfFile file) { 43 | ElfSymbolTableSection dynsym = (ElfSymbolTableSection) file.firstSectionByType(ElfSectionHeader.SHT_DYNSYM); 44 | 45 | ElfHashTable hashTable = file.firstSectionByType(ElfHashTable.class); 46 | if (hashTable != null) { 47 | for (ElfSymbol s : dynsym.symbols) { 48 | if (s.getName() != null) { 49 | Assertions.assertSame(s, hashTable.lookupSymbol(s.getName(), dynsym)); 50 | } 51 | } 52 | Assertions.assertNull(hashTable.lookupSymbol("non_existing", dynsym)); 53 | } 54 | 55 | ElfGnuHashTable gnuHashTable = file.firstSectionByType(ElfGnuHashTable.class); 56 | if (gnuHashTable != null) { 57 | int i = 0; 58 | for (ElfSymbol s : dynsym.symbols) { 59 | if (i++ < gnuHashTable.symoffset) 60 | continue; 61 | Assertions.assertSame(s, gnuHashTable.lookupSymbol(s.getName(), dynsym)); 62 | } 63 | Assertions.assertNull(gnuHashTable.lookupSymbol("non_existing", dynsym)); 64 | } 65 | 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/test/resources/.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fornwall/jelf/546d87c3ed46efe291070477a3b4bd7cf9f729ea/src/test/resources/.swp -------------------------------------------------------------------------------- /src/test/resources/android_arm_libncurses: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fornwall/jelf/546d87c3ed46efe291070477a3b4bd7cf9f729ea/src/test/resources/android_arm_libncurses -------------------------------------------------------------------------------- /src/test/resources/android_arm_tset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fornwall/jelf/546d87c3ed46efe291070477a3b4bd7cf9f729ea/src/test/resources/android_arm_tset -------------------------------------------------------------------------------- /src/test/resources/linux_amd64_bindash: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fornwall/jelf/546d87c3ed46efe291070477a3b4bd7cf9f729ea/src/test/resources/linux_amd64_bindash -------------------------------------------------------------------------------- /src/test/resources/little-endian-test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fornwall/jelf/546d87c3ed46efe291070477a3b4bd7cf9f729ea/src/test/resources/little-endian-test -------------------------------------------------------------------------------- /src/test/resources/objectFile-64.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fornwall/jelf/546d87c3ed46efe291070477a3b4bd7cf9f729ea/src/test/resources/objectFile-64.o -------------------------------------------------------------------------------- /src/test/resources/objectFile.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fornwall/jelf/546d87c3ed46efe291070477a3b4bd7cf9f729ea/src/test/resources/objectFile.o -------------------------------------------------------------------------------- /src/test/resources/usr-bin-yes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fornwall/jelf/546d87c3ed46efe291070477a3b4bd7cf9f729ea/src/test/resources/usr-bin-yes --------------------------------------------------------------------------------