mirror of
https://github.com/wiiu-env/wut.git
synced 2024-12-05 03:04:16 +01:00
Use devkitpro installed wut-tools.
This commit is contained in:
parent
7495a9997f
commit
edd0753aeb
9
.gitmodules
vendored
9
.gitmodules
vendored
@ -1,9 +0,0 @@
|
||||
[submodule "tools/libraries/fmt"]
|
||||
path = tools/libraries/fmt
|
||||
url = https://github.com/fmtlib/fmt.git
|
||||
[submodule "tools/libraries/excmd"]
|
||||
path = tools/libraries/excmd
|
||||
url = https://github.com/exjam/excmd.git
|
||||
[submodule "tools/libraries/zlib"]
|
||||
path = tools/libraries/zlib
|
||||
url = https://github.com/madler/zlib.git
|
27
.travis.yml
27
.travis.yml
@ -3,12 +3,9 @@ language: cpp
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
dist: xenial
|
||||
sudo: required
|
||||
dist: trusty
|
||||
env: DEPLOY_FILE=wut.linux64.7z
|
||||
- os: osx
|
||||
osx_image: xcode9.3
|
||||
env: DEPLOY_FILE=wut.macos.7z
|
||||
env: DEPLOY_FILE=wut.7z
|
||||
|
||||
addons:
|
||||
apt:
|
||||
@ -16,28 +13,21 @@ addons:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: 'ppa:cginternals/backports-ppa'
|
||||
packages:
|
||||
- gcc-7
|
||||
- g++-7
|
||||
- zlib1g-dev
|
||||
- p7zip-full
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- "$HOME/.local"
|
||||
- "/opt/devkitpro"
|
||||
|
||||
git:
|
||||
submodules: true
|
||||
|
||||
install:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 90; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-7 90; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then wget https://github.com/devkitPro/pacman/releases/download/devkitpro-pacman-1.0.1/devkitpro-pacman.deb -O /tmp/devkitpro-pacman.deb; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo dpkg -i /tmp/devkitpro-pacman.deb; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install p7zip; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then wget https://github.com/devkitPro/pacman/releases/download/devkitpro-pacman-1.0.1/devkitpro-pacman-installer.pkg -O /tmp/devkitpro-pacman-installer.pkg; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then sudo installer -pkg /tmp/devkitpro-pacman-installer.pkg -target /; fi
|
||||
- yes | sudo dkp-pacman -Syu devkitPPC
|
||||
- wget https://github.com/devkitPro/pacman/releases/download/devkitpro-pacman-1.0.1/devkitpro-pacman.deb -O /tmp/devkitpro-pacman.deb
|
||||
- sudo dpkg -i /tmp/devkitpro-pacman.deb
|
||||
- yes | sudo dkp-pacman -Syu devkitPPC wut-tools
|
||||
- export DEVKITPRO=/opt/devkitpro
|
||||
- export DEVKITPPC=/opt/devkitpro/devkitPPC
|
||||
|
||||
script:
|
||||
@ -48,7 +38,6 @@ script:
|
||||
- make -j4 install
|
||||
- export WUT_ROOT=$PWD/wut_install
|
||||
- cd ../
|
||||
- chmod +x $WUT_ROOT/bin/elf2rpl
|
||||
# Build tests
|
||||
- cd tests
|
||||
- mkdir build && cd build
|
||||
@ -73,5 +62,5 @@ deploy:
|
||||
file_glob: true
|
||||
file: $WUT_ROOT/$DEPLOY_FILE
|
||||
on:
|
||||
repo: decaf-emu/wut
|
||||
repo: devkitPro/wut
|
||||
tags: true
|
||||
|
@ -1,13 +1,10 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
set(WUT_ROOT "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
set(ENV{WUT_ROOT} "${WUT_ROOT}")
|
||||
set(CMAKE_TOOLCHAIN_FILE "${WUT_ROOT}/share/wut.toolchain.cmake")
|
||||
|
||||
project(wut)
|
||||
include(ExternalProject)
|
||||
|
||||
option(WUT_BUILD_DOCS "Build documentation" OFF)
|
||||
option(WUT_BUILD_TOOLS "Build tools" ON)
|
||||
option(WUT_BUILD_PPC "Build PPC code using devkitPPC" ON)
|
||||
|
||||
set(WUT_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(WUT_STAGING "${CMAKE_BINARY_DIR}/staging")
|
||||
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/lib")
|
||||
@ -25,39 +22,13 @@ if(WUT_BUILD_DOCS)
|
||||
add_subdirectory(docs)
|
||||
endif()
|
||||
|
||||
if(WUT_BUILD_TOOLS)
|
||||
add_subdirectory(tools)
|
||||
find_program(WUT_RPLIMPORTGEN NAMES rplimportgen PATHS "${DEVKITPRO}/tools/bin")
|
||||
if(NOT WUT_RPLIMPORTGEN)
|
||||
message(FATAL_ERROR "Could not find rplimportgen.")
|
||||
endif()
|
||||
|
||||
if(WUT_BUILD_PPC)
|
||||
if(NOT WUT_BUILD_TOOLS)
|
||||
message(FATAL_ERROR "WUT_BUILD_PPC requires WUT_BUILD_TOOLS.")
|
||||
endif()
|
||||
|
||||
set(WUT_TOOLCHAIN "${CMAKE_CURRENT_SOURCE_DIR}/share/wut.toolchain.cmake")
|
||||
|
||||
externalproject_add(cafe
|
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cafe"
|
||||
CMAKE_GENERATOR "Unix Makefiles"
|
||||
INSTALL_DIR "${WUT_STAGING}"
|
||||
CMAKE_CACHE_ARGS
|
||||
-DWUT_ROOT:filepath=${WUT_ROOT}
|
||||
-DWUT_RPLIMPORTGEN:filepath=$<TARGET_FILE:rplimportgen>
|
||||
-DCMAKE_TOOLCHAIN_FILE:filepath=${WUT_TOOLCHAIN}
|
||||
-DCMAKE_INSTALL_PREFIX:string=<INSTALL_DIR>
|
||||
DEPENDS rplimportgen
|
||||
BUILD_ALWAYS 1)
|
||||
|
||||
externalproject_add(libraries
|
||||
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries"
|
||||
CMAKE_GENERATOR "Unix Makefiles"
|
||||
INSTALL_DIR "${WUT_STAGING}"
|
||||
CMAKE_CACHE_ARGS
|
||||
-DWUT_ROOT:filepath=${WUT_ROOT}
|
||||
-DCMAKE_TOOLCHAIN_FILE:filepath=${WUT_TOOLCHAIN}
|
||||
-DCMAKE_INSTALL_PREFIX:string=<INSTALL_DIR>
|
||||
BUILD_ALWAYS 1)
|
||||
endif()
|
||||
add_subdirectory(cafe)
|
||||
add_subdirectory(libraries)
|
||||
|
||||
install(DIRECTORY "${CMAKE_SOURCE_DIR}/include/"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/include"
|
||||
@ -65,6 +36,3 @@ install(DIRECTORY "${CMAKE_SOURCE_DIR}/include/"
|
||||
|
||||
install(DIRECTORY "${CMAKE_SOURCE_DIR}/share/"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share")
|
||||
|
||||
install(DIRECTORY "${CMAKE_BINARY_DIR}/staging/"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}")
|
||||
|
52
README.md
52
README.md
@ -1,4 +1,4 @@
|
||||
[![Build Status](https://travis-ci.org/decaf-emu/wut.svg?branch=rewrite)](https://travis-ci.org/decaf-emu/wut)
|
||||
[![Build Status](https://travis-ci.org/devkitPro/wut.svg?branch=rewrite)](https://travis-ci.org/devkitPro/wut)
|
||||
|
||||
# wut
|
||||
Let's try make a Wii U Toolchain / SDK for creating rpx/rpl.
|
||||
@ -7,11 +7,11 @@ Licensed under the terms of the GNU General Public License, version 2 or later (
|
||||
|
||||
## Install
|
||||
|
||||
Grab the latest [release](https://github.com/decaf-emu/wut/releases) extract to a folder, then export that folder as WUT_ROOT.
|
||||
Grab the latest [release](https://github.com/devkitPro/wut/releases) extract to a folder, then export that folder as WUT_ROOT.
|
||||
|
||||
For example:
|
||||
```
|
||||
wget https://github.com/decaf-emu/wut/releases/download/1.0.0-alpha/wut.linux64.7z
|
||||
wget https://github.com/devkitPro/wut/releases/download/1.0.0-alpha/wut.linux64.7z
|
||||
mkdir wut && cd wut
|
||||
7z x ../wut.linux64.7z
|
||||
export WUT_ROOT=$PWD
|
||||
@ -51,41 +51,20 @@ make
|
||||
|
||||
## Building
|
||||
Requires:
|
||||
- A modern compiler with C++14/17 support
|
||||
- CMake
|
||||
- zlib
|
||||
- [devkitPPC r31+](https://devkitpro.org/wiki/Getting_Started)
|
||||
- [devkitPro](https://devkitpro.org/wiki/Getting_Started)
|
||||
|
||||
### Building on Windows
|
||||
If you are using devkitPro then you can build wut using the provided msys2 environment:
|
||||
### Building with devkitPro
|
||||
Ensure you have the devkitPPC and wut-tools packages from [devkitPro](https://devkitpro.org/wiki/Getting_Started):
|
||||
```
|
||||
export PATH=$PATH:/opt/devkitpro/devkitPPC/bin
|
||||
sudo dkp-pacman -Syu devkitPPC wut-tools
|
||||
export DEVKITPRO=/opt/devkitpro
|
||||
export DEVKITPPC=/opt/devkitpro/devkitPPC
|
||||
pacman -S gcc cmake zlib-devel
|
||||
git clone --recursive https://github.com/decaf-emu/wut.git
|
||||
cd wut
|
||||
mkdir build && cd build
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/opt/wut ../
|
||||
make install
|
||||
export WUT_ROOT=/opt/wut
|
||||
```
|
||||
|
||||
Or use [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10) and then follow the Linux instructions after preparing your environment.
|
||||
|
||||
For example, if you installed Ubuntu 18.04 then you might setup your environment like:
|
||||
|
||||
Then you can build wut like any other CMake project:
|
||||
```
|
||||
sudo apt install cmake zlib1g-dev gcc g++ build-essential
|
||||
wget https://github.com/devkitPro/pacman/releases/download/devkitpro-pacman-1.0.1/devkitpro-pacman.deb
|
||||
sudo dpkg -i devkitpro-pacman.deb
|
||||
sudo ln -s /proc/mounts /etc/mtab
|
||||
sudo dkp-pacman -S devkitPPC wiiload
|
||||
```
|
||||
|
||||
### Building on Linux / MacOS
|
||||
```
|
||||
export DEVKITPPC=/opt/devkitpro/devkitPPC
|
||||
git clone --recursive https://github.com/decaf-emu/wut.git
|
||||
git clone --recursive https://github.com/devkitPro/wut.git
|
||||
cd wut
|
||||
mkdir build && cd build
|
||||
cmake -DCMAKE_INSTALL_PREFIX=<path/to/install> ../
|
||||
@ -94,10 +73,19 @@ export WUT_ROOT=<path/to/install>
|
||||
```
|
||||
|
||||
Then for any wut project you want to build you must use the wut.toolchain.cmake script:
|
||||
|
||||
```
|
||||
cd ../samples/helloworld
|
||||
mkdir build && cd build
|
||||
cmake -DCMAKE_TOOLCHAIN_FILE=$WUT_ROOT/share/wut.toolchain.cmake ../
|
||||
make
|
||||
```
|
||||
|
||||
### Building with a locally built wut-tools
|
||||
If you have locally built wut-tools then just add the directory containing the built binaries to PATH and they should be used instead:
|
||||
```
|
||||
export PATH=/path/to/wut-tools/bin:$PATH
|
||||
cd wut
|
||||
mkdir build && cd build
|
||||
cmake ../
|
||||
make
|
||||
```
|
||||
|
@ -5,7 +5,18 @@ set(CMAKE_SYSTEM_VERSION 1)
|
||||
set(CMAKE_SYSTEM_PROCESSOR "ppc")
|
||||
set(CMAKE_CROSSCOMPILING 1)
|
||||
|
||||
find_program(DEVKITPPC_GCC NAMES powerpc-eabi-gcc)
|
||||
# Find DEVKITPRO
|
||||
if(NOT DEFINED ENV{DEVKITPRO})
|
||||
message(FATAL_ERROR "You must have defined DEVKITPRO before calling cmake.")
|
||||
endif()
|
||||
|
||||
set(DEVKITPRO $ENV{DEVKITPRO})
|
||||
|
||||
# Find DEVKITPPC
|
||||
find_program(DEVKITPPC_GCC
|
||||
NAMES powerpc-eabi-gcc
|
||||
PATHS "${DEVKITPRO}/devkitPPC/bin")
|
||||
|
||||
if(DEVKITPPC_GCC)
|
||||
get_filename_component(DEVKITPPC_BIN ${DEVKITPPC_GCC} DIRECTORY)
|
||||
get_filename_component(DEVKITPPC ${DEVKITPPC_BIN} DIRECTORY)
|
||||
@ -17,12 +28,29 @@ else()
|
||||
set(DEVKITPPC_BIN "${DEVKITPPC}/bin")
|
||||
endif()
|
||||
|
||||
# Find WUT
|
||||
if(NOT DEFINED ENV{WUT_ROOT})
|
||||
message(FATAL_ERROR "You must have defined WUT_ROOT before calling cmake.")
|
||||
endif()
|
||||
|
||||
set(WUT_ROOT $ENV{WUT_ROOT})
|
||||
|
||||
# Find elf2rpl
|
||||
find_program(ELF2RPL_BIN
|
||||
NAMES elf2rpl
|
||||
PATHS "${DEVKITPRO}/tools/bin")
|
||||
if(NOT ELF2RPL_BIN)
|
||||
message(FATAL_ERROR "Could not find elf2rpl")
|
||||
endif()
|
||||
|
||||
# Find rplexportgen
|
||||
find_program(RPLEXPORTGEN_BIN
|
||||
NAMES rplexportgen
|
||||
PATHS "${DEVKITPRO}/tools/bin")
|
||||
if(NOT RPLEXPORTGEN_BIN)
|
||||
message(FATAL_ERROR "Could not find rplexportgen")
|
||||
endif()
|
||||
|
||||
set(CMAKE_ASM_COMPILER "${DEVKITPPC_BIN}/powerpc-eabi-gcc" CACHE PATH "")
|
||||
set(CMAKE_C_COMPILER "${DEVKITPPC_BIN}/powerpc-eabi-gcc" CACHE PATH "")
|
||||
set(CMAKE_CXX_COMPILER "${DEVKITPPC_BIN}/powerpc-eabi-g++" CACHE PATH "")
|
||||
@ -40,15 +68,15 @@ set(CMAKE_EXE_LINKER_FLAGS "-Wl,-z,nocopyreloc -T \"${WUT_ROOT}/share/wut.ld\" \
|
||||
include_directories(BEFORE SYSTEM "${WUT_ROOT}/include")
|
||||
|
||||
# Setup root to exclude host system headers + libraries
|
||||
set(CMAKE_FIND_ROOT_PATH "${DEVKITPPC}" "${WUT_ROOT}/bin" "${CMAKE_INSTALL_PREFIX}" "${CMAKE_INSTALL_PREFIX}/share")
|
||||
set(CMAKE_FIND_ROOT_PATH "${DEVKITPPC}" "${DEVKITPRO}/tools/bin" "${CMAKE_INSTALL_PREFIX}" "${CMAKE_INSTALL_PREFIX}/share")
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
||||
|
||||
# Tools
|
||||
set(WUT_ELF2RPL "${WUT_ROOT}/bin/elf2rpl" CACHE PATH "")
|
||||
set(WUT_RPLEXPORTGEN "${WUT_ROOT}/bin/rplexportgen" CACHE PATH "")
|
||||
set(WUT_ELF2RPL "${ELF2RPL_BIN}" CACHE PATH "")
|
||||
set(WUT_RPLEXPORTGEN "${RPLEXPORTGEN_BIN}" CACHE PATH "")
|
||||
|
||||
# Flags
|
||||
set(WUT TRUE)
|
||||
|
@ -1,14 +0,0 @@
|
||||
project(tools)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
add_subdirectory(libraries)
|
||||
|
||||
include_directories(common)
|
||||
add_subdirectory(elf2rpl)
|
||||
add_subdirectory(readrpl)
|
||||
add_subdirectory(rplimportgen)
|
||||
add_subdirectory(rplexportgen)
|
||||
add_subdirectory(udplogserver)
|
@ -1,372 +0,0 @@
|
||||
#pragma once
|
||||
#include "utils.h"
|
||||
#include "type_traits.h"
|
||||
#include <utility>
|
||||
|
||||
template<typename Type>
|
||||
class be_val
|
||||
{
|
||||
public:
|
||||
static_assert(!std::is_array<Type>::value,
|
||||
"be_val invalid type: array");
|
||||
|
||||
static_assert(!std::is_pointer<Type>::value,
|
||||
"be_val invalid type: pointer");
|
||||
|
||||
static_assert(sizeof(Type) == 1 || sizeof(Type) == 2 || sizeof(Type) == 4 || sizeof(Type) == 8,
|
||||
"be_val invalid type size");
|
||||
|
||||
using value_type = Type;
|
||||
|
||||
be_val() = default;
|
||||
|
||||
be_val(const value_type &value) :
|
||||
mStorage(byte_swap(value))
|
||||
{
|
||||
}
|
||||
|
||||
value_type value() const
|
||||
{
|
||||
return byte_swap(mStorage);
|
||||
}
|
||||
|
||||
void setValue(value_type value)
|
||||
{
|
||||
mStorage = byte_swap(value);
|
||||
}
|
||||
|
||||
operator value_type() const
|
||||
{
|
||||
return value();
|
||||
}
|
||||
|
||||
template<typename T = Type,
|
||||
typename = typename std::enable_if<std::is_convertible<T, bool>::value ||
|
||||
std::is_constructible<bool, T>::value
|
||||
>::type>
|
||||
explicit operator bool() const
|
||||
{
|
||||
return static_cast<bool>(value());
|
||||
}
|
||||
|
||||
template<typename OtherType,
|
||||
typename = typename std::enable_if<std::is_convertible<Type, OtherType>::value ||
|
||||
std::is_constructible<OtherType, Type>::value ||
|
||||
std::is_convertible<Type, typename safe_underlying_type<OtherType>::type>::value
|
||||
>::type>
|
||||
explicit operator OtherType() const
|
||||
{
|
||||
return static_cast<OtherType>(value());
|
||||
}
|
||||
|
||||
template<typename OtherType,
|
||||
typename = typename std::enable_if<std::is_constructible<value_type, const OtherType &>::value>::type>
|
||||
be_val & operator =(const OtherType &other)
|
||||
{
|
||||
setValue(value_type { other });
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename OtherType,
|
||||
typename = typename std::enable_if<std::is_constructible<value_type, const OtherType &>::value>::type>
|
||||
be_val & operator =(OtherType &&other)
|
||||
{
|
||||
setValue(value_type { std::forward<OtherType>(other) });
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename OtherType,
|
||||
typename = typename std::enable_if<std::is_constructible<value_type, const OtherType &>::value>::type>
|
||||
be_val & operator =(const be_val<OtherType> &other)
|
||||
{
|
||||
setValue(value_type { other.value() });
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename OtherType,
|
||||
typename = typename std::enable_if<std::is_constructible<value_type, const OtherType &>::value>::type>
|
||||
be_val & operator =(be_val<OtherType> &&other)
|
||||
{
|
||||
setValue(value_type { other.value() });
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename OtherType, typename K = value_type>
|
||||
auto operator ==(const OtherType &other)
|
||||
-> decltype(std::declval<const K>().operator ==(std::declval<const OtherType>())) const
|
||||
{
|
||||
return value() == other;
|
||||
}
|
||||
|
||||
template<typename OtherType, typename K = value_type>
|
||||
auto operator !=(const OtherType &other)
|
||||
-> decltype(std::declval<const K>().operator !=(std::declval<const OtherType>())) const
|
||||
{
|
||||
return value() != other;
|
||||
}
|
||||
|
||||
template<typename OtherType, typename K = value_type>
|
||||
auto operator >=(const OtherType &other)
|
||||
-> decltype(std::declval<const K>().operator >=(std::declval<const OtherType>())) const
|
||||
{
|
||||
return value() >= other;
|
||||
}
|
||||
|
||||
template<typename OtherType, typename K = value_type>
|
||||
auto operator <=(const OtherType &other)
|
||||
-> decltype(std::declval<const K>().operator <=(std::declval<const OtherType>())) const
|
||||
{
|
||||
return value() <= other;
|
||||
}
|
||||
|
||||
template<typename OtherType, typename K = value_type>
|
||||
auto operator >(const OtherType &other)
|
||||
-> decltype(std::declval<const K>().operator >(std::declval<const OtherType>())) const
|
||||
{
|
||||
return value() > other;
|
||||
}
|
||||
|
||||
template<typename OtherType, typename K = value_type>
|
||||
auto operator <(const OtherType &other)
|
||||
-> decltype(std::declval<const K>().operator <(std::declval<const OtherType>())) const
|
||||
{
|
||||
return value() < other;
|
||||
}
|
||||
|
||||
template<typename K = value_type>
|
||||
auto operator +()
|
||||
-> decltype(std::declval<const K>(). operator+()) const
|
||||
{
|
||||
return +value();
|
||||
}
|
||||
|
||||
template<typename K = value_type>
|
||||
auto operator -()
|
||||
-> decltype(std::declval<const K>(). operator-()) const
|
||||
{
|
||||
return -value();
|
||||
}
|
||||
|
||||
template<typename OtherType, typename K = value_type>
|
||||
auto operator +(const OtherType &other)
|
||||
-> decltype(std::declval<const K>().operator +(std::declval<const OtherType>())) const
|
||||
{
|
||||
return value() + other;
|
||||
}
|
||||
|
||||
template<typename OtherType, typename K = value_type>
|
||||
auto operator -(const OtherType &other)
|
||||
-> decltype(std::declval<const K>().operator -(std::declval<const OtherType>())) const
|
||||
{
|
||||
return value() - other;
|
||||
}
|
||||
|
||||
template<typename OtherType, typename K = value_type>
|
||||
auto operator *(const OtherType &other)
|
||||
-> decltype(std::declval<const K>().operator *(std::declval<const OtherType>())) const
|
||||
{
|
||||
return value() * other;
|
||||
}
|
||||
|
||||
template<typename OtherType, typename K = value_type>
|
||||
auto operator /(const OtherType &other)
|
||||
-> decltype(std::declval<const K>().operator /(std::declval<const OtherType>())) const
|
||||
{
|
||||
return value() / other;
|
||||
}
|
||||
|
||||
template<typename OtherType, typename K = value_type>
|
||||
auto operator %(const OtherType &other)
|
||||
-> decltype(std::declval<const K>().operator %(std::declval<const OtherType>())) const
|
||||
{
|
||||
return value() % other;
|
||||
}
|
||||
|
||||
template<typename OtherType, typename K = value_type>
|
||||
auto operator |(const OtherType &other)
|
||||
-> decltype(std::declval<const K>().operator |(std::declval<const OtherType>())) const
|
||||
{
|
||||
return value() | other;
|
||||
}
|
||||
|
||||
template<typename OtherType, typename K = value_type>
|
||||
auto operator &(const OtherType &other)
|
||||
-> decltype(std::declval<const K>().operator &(std::declval<const OtherType>())) const
|
||||
{
|
||||
return value() & other;
|
||||
}
|
||||
|
||||
template<typename OtherType, typename K = value_type>
|
||||
auto operator ^(const OtherType &other)
|
||||
-> decltype(std::declval<const K>().operator ^(std::declval<const OtherType>())) const
|
||||
{
|
||||
return value() ^ other;
|
||||
}
|
||||
|
||||
template<typename OtherType, typename K = value_type>
|
||||
auto operator <<(const OtherType &other)
|
||||
-> decltype(std::declval<const K>().operator <<(std::declval<const OtherType>())) const
|
||||
{
|
||||
return value() << other;
|
||||
}
|
||||
|
||||
template<typename OtherType, typename K = value_type>
|
||||
auto operator >>(const OtherType &other)
|
||||
-> decltype(std::declval<const K>().operator >>(std::declval<const OtherType>())) const
|
||||
{
|
||||
return value() >> other;
|
||||
}
|
||||
|
||||
template<typename OtherType,
|
||||
typename = decltype(std::declval<const value_type>() + std::declval<const OtherType>())>
|
||||
be_val &operator +=(const OtherType &other)
|
||||
{
|
||||
*this = value() + other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename OtherType,
|
||||
typename = decltype(std::declval<const value_type>() - std::declval<const OtherType>())>
|
||||
be_val &operator -=(const OtherType &other)
|
||||
{
|
||||
*this = value() - other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename OtherType,
|
||||
typename = decltype(std::declval<const value_type>() * std::declval<const OtherType>())>
|
||||
be_val &operator *=(const OtherType &other)
|
||||
{
|
||||
*this = value() * other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename OtherType,
|
||||
typename = decltype(std::declval<const value_type>() / std::declval<const OtherType>())>
|
||||
be_val &operator /=(const OtherType &other)
|
||||
{
|
||||
*this = value() / other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename OtherType,
|
||||
typename = decltype(std::declval<const value_type>() % std::declval<const OtherType>())>
|
||||
be_val &operator %=(const OtherType &other)
|
||||
{
|
||||
*this = value() % other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename OtherType,
|
||||
typename = decltype(std::declval<const value_type>() | std::declval<const OtherType>())>
|
||||
be_val &operator |=(const OtherType &other)
|
||||
{
|
||||
*this = static_cast<Type>(value() | other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename OtherType,
|
||||
typename = decltype(std::declval<const value_type>() & std::declval<const OtherType>())>
|
||||
be_val &operator &=(const OtherType &other)
|
||||
{
|
||||
*this = static_cast<Type>(value() & other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename OtherType,
|
||||
typename = decltype(std::declval<const value_type>() ^ std::declval<const OtherType>())>
|
||||
be_val &operator ^=(const OtherType &other)
|
||||
{
|
||||
*this = static_cast<Type>(value() ^ other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename OtherType,
|
||||
typename = decltype(std::declval<const value_type>() << std::declval<const OtherType>())>
|
||||
be_val &operator <<=(const OtherType &other)
|
||||
{
|
||||
*this = value() << other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename OtherType,
|
||||
typename = decltype(std::declval<const value_type>() >> std::declval<const OtherType>())>
|
||||
be_val &operator >>=(const OtherType &other)
|
||||
{
|
||||
*this = value() >> other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T = Type,
|
||||
typename = decltype(std::declval<const T>() + 1)>
|
||||
be_val &operator ++()
|
||||
{
|
||||
setValue(value() + 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T = Type,
|
||||
typename = decltype(std::declval<const T>() + 1)>
|
||||
be_val operator ++(int)
|
||||
{
|
||||
auto before = *this;
|
||||
setValue(value() + 1);
|
||||
return before;
|
||||
}
|
||||
|
||||
template<typename T = Type,
|
||||
typename = decltype(std::declval<const T>() - 1)>
|
||||
be_val &operator --()
|
||||
{
|
||||
setValue(value() - 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T = Type,
|
||||
typename = decltype(std::declval<const T>() - 1)>
|
||||
be_val operator --(int)
|
||||
{
|
||||
auto before = *this;
|
||||
setValue(value() - 1);
|
||||
return before;
|
||||
}
|
||||
|
||||
template<typename IndexType,
|
||||
typename K = value_type>
|
||||
auto operator [](const IndexType &index)
|
||||
-> decltype(std::declval<K>().operator [](std::declval<IndexType>()))
|
||||
{
|
||||
return value().operator [](index);
|
||||
}
|
||||
|
||||
template<typename K = value_type>
|
||||
auto operator ->()
|
||||
-> decltype(std::declval<K>().operator ->())
|
||||
{
|
||||
return value().operator ->();
|
||||
}
|
||||
|
||||
template<typename K = value_type>
|
||||
auto operator ->() const
|
||||
-> decltype(std::declval<const K>().operator ->())
|
||||
{
|
||||
return value().operator ->();
|
||||
}
|
||||
|
||||
template<typename K = value_type>
|
||||
auto operator *()
|
||||
-> decltype(std::declval<K>().operator *())
|
||||
{
|
||||
return value().operator *();
|
||||
}
|
||||
|
||||
template<typename K = value_type>
|
||||
auto operator *() const
|
||||
-> decltype(std::declval<const K>().operator *())
|
||||
{
|
||||
return value().operator ->();
|
||||
}
|
||||
|
||||
private:
|
||||
value_type mStorage;
|
||||
};
|
@ -1,321 +0,0 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include "be_val.h"
|
||||
#include "utils.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
namespace elf
|
||||
{
|
||||
|
||||
enum Machine : uint16_t // e_machine
|
||||
{
|
||||
EM_PPC = 20 // PowerPC
|
||||
};
|
||||
|
||||
enum Encoding : uint8_t // e_encoding
|
||||
{
|
||||
ELFDATANONE = 0,
|
||||
ELFDATA2LSB = 1,
|
||||
ELFDATA2MSB = 2
|
||||
};
|
||||
|
||||
enum Class : uint8_t // e_class
|
||||
{
|
||||
ELFCLASSNONE = 0,
|
||||
ELFCLASS32 = 1,
|
||||
ELFCLASS64 = 2
|
||||
};
|
||||
|
||||
enum Version : uint8_t // e_elf_version
|
||||
{
|
||||
EV_NONE = 0,
|
||||
EV_CURRENT = 1,
|
||||
};
|
||||
|
||||
enum FileType : uint32_t // e_type
|
||||
{
|
||||
ET_NONE = 0, // No file type
|
||||
ET_REL = 1, // Relocatable file
|
||||
ET_EXEC = 2, // Executable file
|
||||
ET_DYN = 3, // Shared object file
|
||||
ET_CORE = 4, // Core file
|
||||
ET_LOPROC = 0xff00, // Beginning of processor-specific codes
|
||||
ET_CAFE_RPL = 0xff01, // Cafe RPL file
|
||||
ET_HIPROC = 0xffff // Processor-specific
|
||||
};
|
||||
|
||||
enum EABI : uint16_t // e_abi
|
||||
{
|
||||
EABI_CAFE = 0xcafe // WiiU CafeOS
|
||||
};
|
||||
|
||||
enum SectionFlags : uint32_t // sh_flags
|
||||
{
|
||||
SHF_WRITE = 0x1,
|
||||
SHF_ALLOC = 0x2,
|
||||
SHF_EXECINSTR = 0x4,
|
||||
SHF_DEFLATED = 0x08000000,
|
||||
SHF_MASKPROC = 0xF0000000,
|
||||
};
|
||||
|
||||
enum SectionType : uint32_t // sh_type
|
||||
{
|
||||
SHT_NULL = 0, // No associated section (inactive entry).
|
||||
SHT_PROGBITS = 1, // Program-defined contents.
|
||||
SHT_SYMTAB = 2, // Symbol table.
|
||||
SHT_STRTAB = 3, // String table.
|
||||
SHT_RELA = 4, // Relocation entries; explicit addends.
|
||||
SHT_HASH = 5, // Symbol hash table.
|
||||
SHT_DYNAMIC = 6, // Information for dynamic linking.
|
||||
SHT_NOTE = 7, // Information about the file.
|
||||
SHT_NOBITS = 8, // Data occupies no space in the file.
|
||||
SHT_REL = 9, // Relocation entries; no explicit addends.
|
||||
SHT_SHLIB = 10, // Reserved.
|
||||
SHT_DYNSYM = 11, // Symbol table.
|
||||
SHT_INIT_ARRAY = 14, // Pointers to initialization functions.
|
||||
SHT_FINI_ARRAY = 15, // Pointers to termination functions.
|
||||
SHT_PREINIT_ARRAY = 16, // Pointers to pre-init functions.
|
||||
SHT_GROUP = 17, // Section group.
|
||||
SHT_SYMTAB_SHNDX = 18, // Indices for SHN_XINDEX entries.
|
||||
SHT_LOPROC = 0x70000000, // Lowest processor arch-specific type.
|
||||
SHT_HIPROC = 0x7fffffff, // Highest processor arch-specific type.
|
||||
SHT_LOUSER = 0x80000000, // Lowest type reserved for applications.
|
||||
SHT_RPL_EXPORTS = 0x80000001, // RPL Exports
|
||||
SHT_RPL_IMPORTS = 0x80000002, // RPL Imports
|
||||
SHT_RPL_CRCS = 0x80000003, // RPL CRCs
|
||||
SHT_RPL_FILEINFO = 0x80000004,// RPL FileInfo
|
||||
SHT_HIUSER = 0xffffffff // Highest type reserved for applications.
|
||||
};
|
||||
|
||||
enum SymbolBinding : uint32_t // st_info > 4
|
||||
{
|
||||
STB_LOCAL = 0, // Local symbol, not visible outside obj file containing def
|
||||
STB_GLOBAL = 1, // Global symbol, visible to all object files being combined
|
||||
STB_WEAK = 2, // Weak symbol, like global but lower-precedence
|
||||
STB_GNU_UNIQUE = 10,
|
||||
STB_LOOS = 10, // Lowest operating system-specific binding type
|
||||
STB_HIOS = 12, // Highest operating system-specific binding type
|
||||
STB_LOPROC = 13, // Lowest processor-specific binding type
|
||||
STB_HIPROC = 15 // Highest processor-specific binding type
|
||||
};
|
||||
|
||||
enum SymbolType : uint32_t // st_info & f
|
||||
{
|
||||
STT_NOTYPE = 0, // Symbol's type is not specified
|
||||
STT_OBJECT = 1, // Symbol is a data object (variable, array, etc.)
|
||||
STT_FUNC = 2, // Symbol is executable code (function, etc.)
|
||||
STT_SECTION = 3, // Symbol refers to a section
|
||||
STT_FILE = 4, // Local, absolute symbol that refers to a file
|
||||
STT_COMMON = 5, // An uninitialized common block
|
||||
STT_TLS = 6, // Thread local data object
|
||||
STT_LOOS = 7, // Lowest operating system-specific symbol type
|
||||
STT_HIOS = 8, // Highest operating system-specific symbol type
|
||||
STT_GNU_IFUNC = 10, // GNU indirect function
|
||||
STT_LOPROC = 13, // Lowest processor-specific symbol type
|
||||
STT_HIPROC = 15 // Highest processor-specific symbol type
|
||||
};
|
||||
|
||||
enum SectionIndex : uint16_t // st_shndx
|
||||
{
|
||||
SHN_UNDEF = 0, // Undefined
|
||||
SHN_LORESERVE = 0xff00, // Reserved range
|
||||
SHN_ABS = 0xfff1, // Absolute symbols
|
||||
SHN_COMMON = 0xfff2, // Common symbols
|
||||
SHN_XINDEX = 0xffff, // Escape -- index stored elsewhere
|
||||
SHN_HIRESERVE = 0xffff
|
||||
};
|
||||
|
||||
enum RelocationType : uint32_t // r_info & 0xff
|
||||
{
|
||||
R_PPC_NONE = 0,
|
||||
R_PPC_ADDR32 = 1,
|
||||
R_PPC_ADDR24 = 2,
|
||||
R_PPC_ADDR16 = 3,
|
||||
R_PPC_ADDR16_LO = 4,
|
||||
R_PPC_ADDR16_HI = 5,
|
||||
R_PPC_ADDR16_HA = 6,
|
||||
R_PPC_ADDR14 = 7,
|
||||
R_PPC_ADDR14_BRTAKEN = 8,
|
||||
R_PPC_ADDR14_BRNTAKEN = 9,
|
||||
R_PPC_REL24 = 10,
|
||||
R_PPC_REL14 = 11,
|
||||
R_PPC_REL14_BRTAKEN = 12,
|
||||
R_PPC_REL14_BRNTAKEN = 13,
|
||||
R_PPC_GOT16 = 14,
|
||||
R_PPC_GOT16_LO = 15,
|
||||
R_PPC_GOT16_HI = 16,
|
||||
R_PPC_GOT16_HA = 17,
|
||||
R_PPC_PLTREL24 = 18,
|
||||
R_PPC_JMP_SLOT = 21,
|
||||
R_PPC_RELATIVE = 22,
|
||||
R_PPC_LOCAL24PC = 23,
|
||||
R_PPC_REL32 = 26,
|
||||
R_PPC_TLS = 67,
|
||||
R_PPC_DTPMOD32 = 68,
|
||||
R_PPC_TPREL16 = 69,
|
||||
R_PPC_TPREL16_LO = 70,
|
||||
R_PPC_TPREL16_HI = 71,
|
||||
R_PPC_TPREL16_HA = 72,
|
||||
R_PPC_TPREL32 = 73,
|
||||
R_PPC_DTPREL16 = 74,
|
||||
R_PPC_DTPREL16_LO = 75,
|
||||
R_PPC_DTPREL16_HI = 76,
|
||||
R_PPC_DTPREL16_HA = 77,
|
||||
R_PPC_DTPREL32 = 78,
|
||||
R_PPC_GOT_TLSGD16 = 79,
|
||||
R_PPC_GOT_TLSGD16_LO = 80,
|
||||
R_PPC_GOT_TLSGD16_HI = 81,
|
||||
R_PPC_GOT_TLSGD16_HA = 82,
|
||||
R_PPC_GOT_TLSLD16 = 83,
|
||||
R_PPC_GOT_TLSLD16_LO = 84,
|
||||
R_PPC_GOT_TLSLD16_HI = 85,
|
||||
R_PPC_GOT_TLSLD16_HA = 86,
|
||||
R_PPC_GOT_TPREL16 = 87,
|
||||
R_PPC_GOT_TPREL16_LO = 88,
|
||||
R_PPC_GOT_TPREL16_HI = 89,
|
||||
R_PPC_GOT_TPREL16_HA = 90,
|
||||
R_PPC_GOT_DTPREL16 = 91,
|
||||
R_PPC_GOT_DTPREL16_LO = 92,
|
||||
R_PPC_GOT_DTPREL16_HI = 93,
|
||||
R_PPC_GOT_DTPREL16_HA = 94,
|
||||
R_PPC_TLSGD = 95,
|
||||
R_PPC_TLSLD = 96,
|
||||
R_PPC_EMB_SDA21 = 109,
|
||||
R_PPC_EMB_RELSDA = 116,
|
||||
R_PPC_DIAB_SDA21_LO = 180,
|
||||
R_PPC_DIAB_SDA21_HI = 181,
|
||||
R_PPC_DIAB_SDA21_HA = 182,
|
||||
R_PPC_DIAB_RELSDA_LO = 183,
|
||||
R_PPC_DIAB_RELSDA_HI = 184,
|
||||
R_PPC_DIAB_RELSDA_HA = 185,
|
||||
R_PPC_GHS_REL16_HA = 251,
|
||||
R_PPC_GHS_REL16_HI = 252,
|
||||
R_PPC_GHS_REL16_LO = 253,
|
||||
};
|
||||
|
||||
enum RplFileInfoFlag : uint32_t
|
||||
{
|
||||
RPL_IS_RPX = 0x2,
|
||||
};
|
||||
|
||||
static const unsigned HeaderMagic = 0x7f454c46;
|
||||
|
||||
struct Header
|
||||
{
|
||||
be_val<uint32_t> magic; // File identification.
|
||||
be_val<uint8_t> fileClass; // File class.
|
||||
be_val<uint8_t> encoding; // Data encoding.
|
||||
be_val<uint8_t> elfVersion; // File version.
|
||||
be_val<uint16_t> abi; // OS/ABI identification. (EABI_*)
|
||||
be_val<uint8_t> pad[7];
|
||||
|
||||
be_val<uint16_t> type; // Type of file (ET_*)
|
||||
be_val<uint16_t> machine; // Required architecture for this file (EM_*)
|
||||
be_val<uint32_t> version; // Must be equal to 1
|
||||
be_val<uint32_t> entry; // Address to jump to in order to start program
|
||||
be_val<uint32_t> phoff; // Program header table's file offset, in bytes
|
||||
be_val<uint32_t> shoff; // Section header table's file offset, in bytes
|
||||
be_val<uint32_t> flags; // Processor-specific flags
|
||||
be_val<uint16_t> ehsize; // Size of ELF header, in bytes
|
||||
be_val<uint16_t> phentsize; // Size of an entry in the program header table
|
||||
be_val<uint16_t> phnum; // Number of entries in the program header table
|
||||
be_val<uint16_t> shentsize; // Size of an entry in the section header table
|
||||
be_val<uint16_t> shnum; // Number of entries in the section header table
|
||||
be_val<uint16_t> shstrndx; // Sect hdr table index of sect name string table
|
||||
};
|
||||
CHECK_SIZE(Header, 0x34);
|
||||
|
||||
struct SectionHeader
|
||||
{
|
||||
be_val<uint32_t> name; // Section name (index into string table)
|
||||
be_val<uint32_t> type; // Section type (SHT_*)
|
||||
be_val<uint32_t> flags; // Section flags (SHF_*)
|
||||
be_val<uint32_t> addr; // Address where section is to be loaded
|
||||
be_val<uint32_t> offset; // File offset of section data, in bytes
|
||||
be_val<uint32_t> size; // Size of section, in bytes
|
||||
be_val<uint32_t> link; // Section type-specific header table index link
|
||||
be_val<uint32_t> info; // Section type-specific extra information
|
||||
be_val<uint32_t> addralign; // Section address alignment
|
||||
be_val<uint32_t> entsize; // Size of records contained within the section
|
||||
};
|
||||
CHECK_SIZE(SectionHeader, 0x28);
|
||||
|
||||
struct Symbol
|
||||
{
|
||||
be_val<uint32_t> name; // Symbol name (index into string table)
|
||||
be_val<uint32_t> value; // Value or address associated with the symbol
|
||||
be_val<uint32_t> size; // Size of the symbol
|
||||
be_val<uint8_t> info; // Symbol's type and binding attributes
|
||||
be_val<uint8_t> other; // Must be zero; reserved
|
||||
be_val<uint16_t> shndx; // Which section (header table index) it's defined in (SHN_*)
|
||||
};
|
||||
CHECK_SIZE(Symbol, 0x10);
|
||||
|
||||
struct Rela
|
||||
{
|
||||
be_val<uint32_t> offset;
|
||||
be_val<uint32_t> info;
|
||||
be_val<int32_t> addend;
|
||||
};
|
||||
CHECK_SIZE(Rela, 0x0C);
|
||||
|
||||
struct RplImport
|
||||
{
|
||||
be_val<uint32_t> count;
|
||||
be_val<uint32_t> signature;
|
||||
char name[1];
|
||||
};
|
||||
|
||||
struct RplExport
|
||||
{
|
||||
struct Export
|
||||
{
|
||||
be_val<uint32_t> value;
|
||||
be_val<uint32_t> name;
|
||||
};
|
||||
|
||||
be_val<uint32_t> count;
|
||||
be_val<uint32_t> signature;
|
||||
Export exports[1];
|
||||
};
|
||||
|
||||
struct RplCrc
|
||||
{
|
||||
be_val<uint32_t> crc;
|
||||
};
|
||||
CHECK_SIZE(RplCrc, 0x04);
|
||||
|
||||
struct RplFileInfo
|
||||
{
|
||||
be_val<uint32_t> version;
|
||||
be_val<uint32_t> textSize;
|
||||
be_val<uint32_t> textAlign;
|
||||
be_val<uint32_t> dataSize;
|
||||
be_val<uint32_t> dataAlign;
|
||||
be_val<uint32_t> loadSize;
|
||||
be_val<uint32_t> loadAlign;
|
||||
be_val<uint32_t> tempSize;
|
||||
be_val<uint32_t> trampAdjust;
|
||||
be_val<uint32_t> sdaBase;
|
||||
be_val<uint32_t> sda2Base;
|
||||
be_val<uint32_t> stackSize;
|
||||
be_val<uint32_t> filename;
|
||||
be_val<uint32_t> flags;
|
||||
be_val<uint32_t> heapSize;
|
||||
be_val<uint32_t> tagOffset;
|
||||
be_val<uint32_t> minVersion;
|
||||
be_val<int32_t> compressionLevel;
|
||||
be_val<uint32_t> trampAddition;
|
||||
be_val<uint32_t> fileInfoPad;
|
||||
be_val<uint32_t> cafeSdkVersion;
|
||||
be_val<uint32_t> cafeSdkRevision;
|
||||
be_val<uint16_t> tlsModuleIndex;
|
||||
be_val<uint16_t> tlsAlignShift;
|
||||
be_val<uint32_t> runtimeFileInfoSize;
|
||||
};
|
||||
CHECK_SIZE(RplFileInfo, 0x60);
|
||||
|
||||
} // namespace elf
|
||||
|
||||
#pragma pack(pop)
|
@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
#include <type_traits>
|
||||
|
||||
// Same as std::underlying_type but works for non-enum Types
|
||||
template<class T, bool = std::is_enum<T>::value>
|
||||
struct safe_underlying_type : std::underlying_type<T> { };
|
||||
|
||||
template<class T>
|
||||
struct safe_underlying_type<T, false>
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
// Maps bool value to a std::bool_constant type
|
||||
template<bool>
|
||||
struct is_true;
|
||||
|
||||
template<>
|
||||
struct is_true<false> : std::false_type { };
|
||||
|
||||
template<>
|
||||
struct is_true<true> : std::true_type { };
|
@ -1,167 +0,0 @@
|
||||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(_MSC_VER)
|
||||
#define PLATFORM_WINDOWS
|
||||
#elif __APPLE__
|
||||
#define PLATFORM_APPLE
|
||||
#define PLATFORM_POSIX
|
||||
#elif __linux__
|
||||
#define PLATFORM_LINUX
|
||||
#define PLATFORM_POSIX
|
||||
#endif
|
||||
|
||||
#ifdef PLATFORM_LINUX
|
||||
#include <byteswap.h>
|
||||
#endif
|
||||
|
||||
// reinterpret_cast for value types
|
||||
template<typename DstType, typename SrcType>
|
||||
inline DstType
|
||||
bit_cast(const SrcType& src)
|
||||
{
|
||||
static_assert(sizeof(SrcType) == sizeof(DstType), "bit_cast must be between same sized types");
|
||||
static_assert(std::is_trivially_copyable<SrcType>::value, "SrcType is not trivially copyable.");
|
||||
static_assert(std::is_trivially_copyable<DstType>::value, "DstType is not trivially copyable.");
|
||||
|
||||
DstType dst;
|
||||
std::memcpy(&dst, &src, sizeof(SrcType));
|
||||
return dst;
|
||||
}
|
||||
|
||||
// Utility class to swap endian for types of size 1, 2, 4, 8
|
||||
// other type sizes are not supported
|
||||
template<typename Type, unsigned Size = sizeof(Type)>
|
||||
struct byte_swap_t;
|
||||
|
||||
template<typename Type>
|
||||
struct byte_swap_t<Type, 1>
|
||||
{
|
||||
static Type swap(Type src)
|
||||
{
|
||||
return src;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
struct byte_swap_t<Type, 2>
|
||||
{
|
||||
static Type swap(Type src)
|
||||
{
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
return bit_cast<Type>(_byteswap_ushort(bit_cast<uint16_t>(src)));
|
||||
#elif defined(PLATFORM_LINUX)
|
||||
return bit_cast<Type>(bswap_16(bit_cast<uint16_t>(src)));
|
||||
#else
|
||||
const uint16_t data = bit_cast<uint16_t>(src);
|
||||
return bit_cast<Type>(static_cast<uint16_t>((data >> 8) | (data << 8)));
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
struct byte_swap_t<Type, 4>
|
||||
{
|
||||
static Type swap(Type src)
|
||||
{
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
return bit_cast<Type>(_byteswap_ulong(bit_cast<uint32_t>(src)));
|
||||
#elif defined(PLATFORM_APPLE)
|
||||
return bit_cast<Type>(__builtin_bswap32(bit_cast<uint32_t>(src)));
|
||||
#elif defined(PLATFORM_LINUX)
|
||||
return bit_cast<Type>(bswap_32(bit_cast<uint32_t>(src)));
|
||||
#else
|
||||
const uint32_t data = bit_cast<uint32_t>(src);
|
||||
return bit_cast<Type>(
|
||||
((data & 0xFF000000u) >> 24) |
|
||||
((data & 0x00FF0000u) >> 8) |
|
||||
((data & 0x0000FF00u) << 8) |
|
||||
((data & 0x000000FFu) << 24)
|
||||
);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
struct byte_swap_t<Type, 8>
|
||||
{
|
||||
static Type swap(Type src)
|
||||
{
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
return bit_cast<Type>(_byteswap_uint64(bit_cast<uint64_t>(src)));
|
||||
#elif defined(PLATFORM_APPLE)
|
||||
return bit_cast<Type>(__builtin_bswap64(bit_cast<uint64_t>(src)));
|
||||
#elif defined(PLATFORM_LINUX)
|
||||
return bit_cast<Type>(bswap_64(bit_cast<uint64_t>(src)));
|
||||
#else
|
||||
uint64_t data = bit_cast<uint64_t>(src);
|
||||
data = ((data & 0x00000000FFFFFFFFull) << 32) | ((data & 0xFFFFFFFF00000000ull) >> 32);
|
||||
data = ((data & 0x0000FFFF0000FFFFull) << 16) | ((data & 0xFFFF0000FFFF0000ull) >> 16);
|
||||
data = ((data & 0x00FF00FF00FF00FFull) << 8) | ((data & 0xFF00FF00FF00FF00ull) >> 8);
|
||||
return bit_cast<Type>(data);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
// Swaps endian of src
|
||||
template<typename Type>
|
||||
inline Type
|
||||
byte_swap(Type src)
|
||||
{
|
||||
return byte_swap_t<Type>::swap(src);
|
||||
}
|
||||
|
||||
// Alignment helpers
|
||||
template<typename Type>
|
||||
constexpr inline Type
|
||||
align_up(Type value, size_t alignment)
|
||||
{
|
||||
return static_cast<Type>((static_cast<size_t>(value) + (alignment - 1)) & ~(alignment - 1));
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
constexpr inline Type
|
||||
align_down(Type value, size_t alignment)
|
||||
{
|
||||
return static_cast<Type>(static_cast<size_t>(value) & ~(alignment - 1));
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
constexpr bool
|
||||
align_check(Type value, size_t alignment)
|
||||
{
|
||||
return (static_cast<size_t>(value) & (alignment - 1)) == 0;
|
||||
}
|
||||
|
||||
#define CHECK_SIZE(Type, Size) \
|
||||
static_assert(sizeof(Type) == Size, \
|
||||
#Type " must be " #Size " bytes")
|
||||
|
||||
// trim from start
|
||||
// Taken from https://stackoverflow.com/a/217605
|
||||
static inline std::string ltrim(std::string s) {
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
|
||||
return !std::isspace(ch);
|
||||
}));
|
||||
return s;
|
||||
}
|
||||
|
||||
// trim from end (in place)
|
||||
static inline std::string rtrim(std::string s) {
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
|
||||
return !std::isspace(ch);
|
||||
}).base(), s.end());
|
||||
return s;
|
||||
}
|
||||
|
||||
// trim from both ends
|
||||
static inline std::string
|
||||
trim(std::string s)
|
||||
{
|
||||
return rtrim(ltrim(s));
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
project(elf2rpl)
|
||||
|
||||
add_executable(elf2rpl
|
||||
main.cpp)
|
||||
|
||||
target_link_libraries(elf2rpl
|
||||
excmd
|
||||
fmt
|
||||
zlibstatic)
|
||||
|
||||
install(TARGETS elf2rpl RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
|
@ -1,834 +0,0 @@
|
||||
#include "elf.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <excmd.h>
|
||||
#include <fmt/format.h>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <zlib.h>
|
||||
|
||||
constexpr auto DeflateMinSectionSize = 0x18u;
|
||||
constexpr auto CodeBaseAddress = 0x02000000u;
|
||||
constexpr auto DataBaseAddress = 0x10000000u;
|
||||
constexpr auto LoadBaseAddress = 0xC0000000u;
|
||||
|
||||
struct ElfFile
|
||||
{
|
||||
struct Section
|
||||
{
|
||||
elf::SectionHeader header;
|
||||
std::string name;
|
||||
std::vector<char> data;
|
||||
};
|
||||
|
||||
elf::Header header;
|
||||
std::vector<std::unique_ptr<Section>> sections;
|
||||
};
|
||||
|
||||
static int
|
||||
getSectionIndex(ElfFile &file, const char *name)
|
||||
{
|
||||
int index = 0;
|
||||
for (const auto §ion : file.sections) {
|
||||
if (section->name == name) {
|
||||
return index;
|
||||
}
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static ElfFile::Section *
|
||||
getSectionByType(ElfFile &file, elf::SectionType type)
|
||||
{
|
||||
for (const auto §ion : file.sections) {
|
||||
if (section->header.type == type) {
|
||||
return section.get();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read the .elf file generated by compiler.
|
||||
*/
|
||||
static bool
|
||||
readElf(ElfFile &file, const std::string &filename)
|
||||
{
|
||||
std::ifstream in { filename, std::ifstream::binary };
|
||||
if (!in.is_open()) {
|
||||
fmt::print("Could not open {} for reading\n", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read header
|
||||
in.read(reinterpret_cast<char *>(&file.header), sizeof(elf::Header));
|
||||
|
||||
if (file.header.magic != elf::HeaderMagic) {
|
||||
fmt::print("Invalid ELF magic header {:08X}\n", elf::HeaderMagic);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.header.fileClass != elf::ELFCLASS32) {
|
||||
fmt::print("Unexpected ELF file class {}, expected {}\n", file.header.fileClass, elf::ELFCLASS32);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.header.encoding != elf::ELFDATA2MSB) {
|
||||
fmt::print("Unexpected ELF encoding {}, expected {}\n", file.header.encoding, elf::ELFDATA2MSB);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.header.machine != elf::EM_PPC) {
|
||||
fmt::print("Unexpected ELF machine type {}, expected {}\n", file.header.machine, elf::EM_PPC);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.header.elfVersion != elf::EV_CURRENT) {
|
||||
fmt::print("Unexpected ELF version {}, expected {}\n", file.header.elfVersion, elf::EV_CURRENT);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read section headers and data
|
||||
in.seekg(static_cast<size_t>(file.header.shoff));
|
||||
|
||||
for (auto i = 0u; i < file.header.shnum; ++i) {
|
||||
file.sections.emplace_back(std::make_unique<ElfFile::Section>());
|
||||
auto §ion = *file.sections.back();
|
||||
|
||||
in.read(reinterpret_cast<char *>(§ion.header), sizeof(elf::SectionHeader));
|
||||
|
||||
if (!section.header.size || section.header.type == elf::SHT_NOBITS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto pos = in.tellg();
|
||||
in.seekg(static_cast<size_t>(section.header.offset));
|
||||
section.data.resize(section.header.size);
|
||||
in.read(section.data.data(), section.data.size());
|
||||
in.seekg(pos);
|
||||
}
|
||||
|
||||
// Set section header names
|
||||
auto shStrTab = file.sections[file.header.shstrndx]->data.data();
|
||||
|
||||
for (auto §ion : file.sections) {
|
||||
section->name = shStrTab + section->header.name;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate SHT_RPL_FILEINFO section.
|
||||
*/
|
||||
static bool
|
||||
generateFileInfoSection(ElfFile &file,
|
||||
uint32_t flags)
|
||||
{
|
||||
elf::RplFileInfo info;
|
||||
info.version = 0xCAFE0402u;
|
||||
info.textSize = 0u;
|
||||
info.textAlign = 32u;
|
||||
info.dataSize = 0u;
|
||||
info.dataAlign = 4096u;
|
||||
info.loadSize = 0u;
|
||||
info.loadAlign = 4u;
|
||||
info.tempSize = 0u;
|
||||
info.trampAdjust = 0u;
|
||||
info.trampAddition = 0u;
|
||||
info.sdaBase = 0u;
|
||||
info.sda2Base = 0u;
|
||||
info.stackSize = 0x10000u;
|
||||
info.heapSize = 0x8000u;
|
||||
info.filename = 0u;
|
||||
info.flags = flags;
|
||||
info.minVersion = 0x5078u;
|
||||
info.compressionLevel = 6;
|
||||
info.fileInfoPad = 0u;
|
||||
info.cafeSdkVersion = 0x5335u;
|
||||
info.cafeSdkRevision = 0x10D4Bu;
|
||||
info.tlsAlignShift = uint16_t { 0u };
|
||||
info.tlsModuleIndex = uint16_t { 0u };
|
||||
info.runtimeFileInfoSize = 0u;
|
||||
info.tagOffset = 0u;
|
||||
|
||||
// Count file info textSize, dataSize, loadSize
|
||||
for (auto §ion : file.sections) {
|
||||
auto size = static_cast<uint32_t>(section->data.size());
|
||||
|
||||
if (section->header.type == elf::SHT_NOBITS) {
|
||||
size = section->header.size;
|
||||
}
|
||||
|
||||
if (section->header.addr >= CodeBaseAddress &&
|
||||
section->header.addr < DataBaseAddress) {
|
||||
auto val = section->header.addr + section->header.size - CodeBaseAddress;
|
||||
if (val > info.textSize) {
|
||||
info.textSize = val;
|
||||
}
|
||||
} else if (section->header.addr >= DataBaseAddress &&
|
||||
section->header.addr < LoadBaseAddress) {
|
||||
auto val = section->header.addr + section->header.size - DataBaseAddress;
|
||||
if (val > info.dataSize) {
|
||||
info.dataSize = val;
|
||||
}
|
||||
} else if (section->header.addr >= LoadBaseAddress) {
|
||||
auto val = section->header.addr + section->header.size - LoadBaseAddress;
|
||||
if (val > info.loadSize) {
|
||||
info.loadSize = val;
|
||||
}
|
||||
} else if (section->header.addr == 0 &&
|
||||
section->header.type != elf::SHT_RPL_CRCS &&
|
||||
section->header.type != elf::SHT_RPL_FILEINFO) {
|
||||
info.tempSize += (size + 128);
|
||||
}
|
||||
}
|
||||
|
||||
info.textSize = align_up(info.textSize, info.textAlign);
|
||||
info.dataSize = align_up(info.dataSize, info.dataAlign);
|
||||
info.loadSize = align_up(info.loadSize, info.loadAlign);
|
||||
|
||||
auto section = std::make_unique<ElfFile::Section>();
|
||||
section->header.name = 0u;
|
||||
section->header.type = elf::SHT_RPL_FILEINFO;
|
||||
section->header.flags = 0u;
|
||||
section->header.addr = 0u;
|
||||
section->header.offset = 0u;
|
||||
section->header.size = 0u;
|
||||
section->header.link = 0u;
|
||||
section->header.info = 0u;
|
||||
section->header.addralign = 4u;
|
||||
section->header.entsize = 0u;
|
||||
section->data.insert(section->data.end(),
|
||||
reinterpret_cast<char *>(&info),
|
||||
reinterpret_cast<char *>(&info + 1));
|
||||
file.sections.emplace_back(std::move(section));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate SHT_RPL_CRCS section.
|
||||
*/
|
||||
static bool
|
||||
generateCrcSection(ElfFile &file)
|
||||
{
|
||||
std::vector<be_val<uint32_t>> crcs;
|
||||
for (auto §ion : file.sections) {
|
||||
auto crc = uint32_t { 0u };
|
||||
|
||||
if (section->data.size()) {
|
||||
crc = crc32(0, Z_NULL, 0);
|
||||
crc = crc32(crc, reinterpret_cast<Bytef *>(section->data.data()), section->data.size());
|
||||
}
|
||||
|
||||
crcs.push_back(crc);
|
||||
}
|
||||
|
||||
// Insert a 0 crc for this section
|
||||
crcs.insert(crcs.end() - 1, 0);
|
||||
|
||||
auto section = std::make_unique<ElfFile::Section>();
|
||||
section->header.name = 0u;
|
||||
section->header.type = elf::SHT_RPL_CRCS;
|
||||
section->header.flags = 0u;
|
||||
section->header.addr = 0u;
|
||||
section->header.offset = 0u;
|
||||
section->header.size = 0u;
|
||||
section->header.link = 0u;
|
||||
section->header.info = 0u;
|
||||
section->header.addralign = 4u;
|
||||
section->header.entsize = 4u;
|
||||
section->data.insert(section->data.end(),
|
||||
reinterpret_cast<char *>(crcs.data()),
|
||||
reinterpret_cast<char *>(crcs.data() + crcs.size()));
|
||||
|
||||
// Insert before FILEINFO
|
||||
file.sections.insert(file.sections.end() - 1, std::move(section));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
getSymbol(ElfFile::Section §ion,
|
||||
size_t index,
|
||||
elf::Symbol &symbol)
|
||||
{
|
||||
auto symbols = reinterpret_cast<elf::Symbol *>(section.data.data());
|
||||
auto numSymbols = section.data.size() / sizeof(elf::Symbol);
|
||||
if (index >= numSymbols) {
|
||||
return false;
|
||||
}
|
||||
|
||||
symbol = symbols[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fix relocations.
|
||||
*
|
||||
* The Wii U does not support every type of relocation.
|
||||
*/
|
||||
static bool
|
||||
fixRelocations(ElfFile &file)
|
||||
{
|
||||
std::set<unsigned int> unsupportedTypes;
|
||||
auto result = true;
|
||||
|
||||
for (auto §ion : file.sections) {
|
||||
std::vector<elf::Rela> newRelocations;
|
||||
|
||||
if (section->header.type != elf::SHT_RELA) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Clear flags
|
||||
section->header.flags = 0u;
|
||||
|
||||
auto &symbolSection = file.sections[section->header.link];
|
||||
auto &targetSection = file.sections[section->header.info];
|
||||
|
||||
auto rels = reinterpret_cast<elf::Rela *>(section->data.data());
|
||||
auto numRels = section->data.size() / sizeof(elf::Rela);
|
||||
for (auto i = 0u; i < numRels; ++i) {
|
||||
auto info = rels[i].info;
|
||||
auto addend = rels[i].addend;
|
||||
auto offset = rels[i].offset;
|
||||
auto index = info >> 8;
|
||||
auto type = info & 0xFF;
|
||||
|
||||
switch (type) {
|
||||
case elf::R_PPC_NONE:
|
||||
case elf::R_PPC_ADDR32:
|
||||
case elf::R_PPC_ADDR16_LO:
|
||||
case elf::R_PPC_ADDR16_HI:
|
||||
case elf::R_PPC_ADDR16_HA:
|
||||
case elf::R_PPC_REL24:
|
||||
case elf::R_PPC_REL14:
|
||||
case elf::R_PPC_DTPMOD32:
|
||||
case elf::R_PPC_DTPREL32:
|
||||
case elf::R_PPC_EMB_SDA21:
|
||||
case elf::R_PPC_EMB_RELSDA:
|
||||
case elf::R_PPC_DIAB_SDA21_LO:
|
||||
case elf::R_PPC_DIAB_SDA21_HI:
|
||||
case elf::R_PPC_DIAB_SDA21_HA:
|
||||
case elf::R_PPC_DIAB_RELSDA_LO:
|
||||
case elf::R_PPC_DIAB_RELSDA_HI:
|
||||
case elf::R_PPC_DIAB_RELSDA_HA:
|
||||
// All valid relocations on Wii U, do nothing
|
||||
break;
|
||||
|
||||
/*
|
||||
* Convert a R_PPC_REL32 into two GHS_REL16
|
||||
*/
|
||||
case elf::R_PPC_REL32:
|
||||
{
|
||||
elf::Symbol symbol;
|
||||
if (!getSymbol(*symbolSection, index, symbol)) {
|
||||
fmt::print("ERROR: Could not find symbol {} for fixing a R_PPC_REL32 relocation\n", index);
|
||||
result = false;
|
||||
} else {
|
||||
newRelocations.emplace_back();
|
||||
auto &newRel = newRelocations.back();
|
||||
|
||||
// Modify current relocation to R_PPC_GHS_REL16_HI
|
||||
rels[i].info = (index << 8) | elf::R_PPC_GHS_REL16_HI;
|
||||
rels[i].addend = addend;
|
||||
rels[i].offset = offset;
|
||||
|
||||
// Create a R_PPC_GHS_REL16_LO
|
||||
newRel.info = (index << 8) | elf::R_PPC_GHS_REL16_LO;
|
||||
newRel.addend = addend + 2;
|
||||
newRel.offset = offset + 2;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// Only print error once per type
|
||||
if (!unsupportedTypes.count(type)) {
|
||||
fmt::print("ERROR: Unsupported relocation type {}\n", type);
|
||||
unsupportedTypes.insert(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
section->data.insert(section->data.end(),
|
||||
reinterpret_cast<char *>(newRelocations.data()),
|
||||
reinterpret_cast<char *>(newRelocations.data() + newRelocations.size()));
|
||||
}
|
||||
|
||||
return result && unsupportedTypes.size() == 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fix file header to look like an RPL file!
|
||||
*/
|
||||
static bool
|
||||
fixFileHeader(ElfFile &file)
|
||||
{
|
||||
file.header.magic = elf::HeaderMagic;
|
||||
file.header.fileClass = uint8_t { 1 };
|
||||
file.header.encoding = elf::ELFDATA2MSB;
|
||||
file.header.elfVersion = elf::EV_CURRENT;
|
||||
file.header.abi = elf::EABI_CAFE;
|
||||
memset(&file.header.pad, 0, 7);
|
||||
file.header.type = uint16_t { 0xFE01 };
|
||||
file.header.machine = elf::EM_PPC;
|
||||
file.header.version = 1u;
|
||||
file.header.flags = 0u;
|
||||
file.header.phoff = 0u;
|
||||
file.header.phentsize = uint16_t { 0 };
|
||||
file.header.phnum = uint16_t { 0 };
|
||||
file.header.shoff = align_up(static_cast<uint32_t>(sizeof(elf::Header)), 64);
|
||||
file.header.shnum = static_cast<uint16_t>(file.sections.size());
|
||||
file.header.shentsize = static_cast<uint16_t>(sizeof(elf::SectionHeader));
|
||||
file.header.ehsize = static_cast<uint16_t>(sizeof(elf::Header));
|
||||
file.header.shstrndx = static_cast<uint16_t>(getSectionIndex(file, ".shstrtab"));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Relocate a section to a new address.
|
||||
*/
|
||||
static bool
|
||||
relocateSection(ElfFile &file,
|
||||
ElfFile::Section §ion,
|
||||
uint32_t sectionIndex,
|
||||
uint32_t newSectionAddress)
|
||||
{
|
||||
auto sectionSize = section.data.size() ? section.data.size() : static_cast<size_t>(section.header.size);
|
||||
auto oldSectionAddress = section.header.addr;
|
||||
auto oldSectionAddressEnd = section.header.addr + sectionSize;
|
||||
|
||||
// Relocate symbols pointing into this section
|
||||
for (auto &symSection : file.sections) {
|
||||
if (symSection->header.type != elf::SectionType::SHT_SYMTAB) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto symbols = reinterpret_cast<elf::Symbol *>(symSection->data.data());
|
||||
auto numSymbols = symSection->data.size() / sizeof(elf::Symbol);
|
||||
for (auto i = 0u; i < numSymbols; ++i) {
|
||||
auto type = symbols[i].info & 0xf;
|
||||
auto value = symbols[i].value;
|
||||
|
||||
// Only relocate data, func, section symbols
|
||||
if (type != elf::STT_OBJECT &&
|
||||
type != elf::STT_FUNC &&
|
||||
type != elf::STT_SECTION) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value >= oldSectionAddress && value <= oldSectionAddressEnd) {
|
||||
symbols[i].value = (value - oldSectionAddress) + newSectionAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Relocate relocations pointing into this section
|
||||
for (auto &relaSection : file.sections) {
|
||||
if (relaSection->header.type != elf::SectionType::SHT_RELA ||
|
||||
relaSection->header.info != sectionIndex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto rela = reinterpret_cast<elf::Rela *>(relaSection->data.data());
|
||||
auto numRelas = relaSection->data.size() / sizeof(elf::Rela);
|
||||
for (auto i = 0u; i < numRelas; ++i) {
|
||||
auto offset = rela[i].offset;
|
||||
|
||||
if (offset >= oldSectionAddress && offset <= oldSectionAddressEnd) {
|
||||
rela[i].offset = (offset - oldSectionAddress) + newSectionAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
section.header.addr = newSectionAddress;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fix the loader virtual addresses.
|
||||
*
|
||||
* Linker script won't put symtab & strtab sections in our loader address, so
|
||||
* we must fix that.
|
||||
*/
|
||||
static bool
|
||||
fixLoaderVirtualAddresses(ElfFile &file)
|
||||
{
|
||||
auto loadMax = LoadBaseAddress;
|
||||
for (auto §ion : file.sections) {
|
||||
if (section->header.addr >= loadMax) {
|
||||
loadMax = section->header.addr + section->data.size();
|
||||
}
|
||||
}
|
||||
|
||||
// Relocate .symtab and .strtab to be in loader memory
|
||||
for (auto i = 0u; i < file.sections.size(); ++i) {
|
||||
auto §ion = file.sections[i];
|
||||
if (section->header.type == elf::SHT_SYMTAB ||
|
||||
section->header.type == elf::SHT_STRTAB) {
|
||||
relocateSection(file, *section, i,
|
||||
align_up(loadMax, section->header.addralign));
|
||||
section->header.flags |= elf::SHF_ALLOC;
|
||||
loadMax += section->data.size();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* zlib deflate any suitable section.
|
||||
*/
|
||||
static bool
|
||||
deflateSections(ElfFile &file)
|
||||
{
|
||||
std::vector<char> chunk;
|
||||
chunk.resize(16 * 1024);
|
||||
|
||||
for (auto §ion : file.sections) {
|
||||
if (section->data.size() < DeflateMinSectionSize ||
|
||||
section->header.type == elf::SHT_RPL_CRCS ||
|
||||
section->header.type == elf::SHT_RPL_FILEINFO) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Allocate space for the 4 bytes inflated size
|
||||
std::vector<char> deflated;
|
||||
deflated.resize(4);
|
||||
|
||||
// Deflate section data
|
||||
auto stream = z_stream { };
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
stream.zalloc = Z_NULL;
|
||||
stream.zfree = Z_NULL;
|
||||
stream.opaque = Z_NULL;
|
||||
deflateInit(&stream, 6);
|
||||
|
||||
stream.avail_in = section->data.size();
|
||||
stream.next_in = reinterpret_cast<Bytef *>(section->data.data());
|
||||
|
||||
do {
|
||||
stream.avail_out = static_cast<uInt>(chunk.size());
|
||||
stream.next_out = reinterpret_cast<Bytef *>(chunk.data());
|
||||
|
||||
auto ret = deflate(&stream, Z_FINISH);
|
||||
if (ret == Z_STREAM_ERROR) {
|
||||
deflateEnd(&stream);
|
||||
return false;
|
||||
}
|
||||
|
||||
deflated.insert(deflated.end(),
|
||||
chunk.data(),
|
||||
reinterpret_cast<char *>(stream.next_out));
|
||||
} while (stream.avail_out == 0);
|
||||
deflateEnd(&stream);
|
||||
|
||||
// Set the inflated size at start of section
|
||||
*reinterpret_cast<be_val<uint32_t> *>(&deflated[0]) =
|
||||
static_cast<uint32_t>(section->data.size());
|
||||
|
||||
// Update the section data
|
||||
section->data = std::move(deflated);
|
||||
section->header.flags |= elf::SHF_DEFLATED;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate section file offsets.
|
||||
*
|
||||
* Expected order:
|
||||
* RPL_CRCS > RPL_FILEINFO >
|
||||
* Data sections > Read sections > Text sections > Temp sections
|
||||
*/
|
||||
static bool
|
||||
calculateSectionOffsets(ElfFile &file)
|
||||
{
|
||||
auto offset = file.header.shoff;
|
||||
offset += align_up(static_cast<uint32_t>(file.sections.size() * sizeof(elf::SectionHeader)), 64);
|
||||
|
||||
for (auto §ion : file.sections) {
|
||||
if (section->header.type == elf::SHT_NOBITS ||
|
||||
section->header.type == elf::SHT_NULL) {
|
||||
section->header.offset = 0u;
|
||||
section->data.clear();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto §ion : file.sections) {
|
||||
if (section->header.type == elf::SHT_RPL_CRCS) {
|
||||
section->header.offset = offset;
|
||||
section->header.size = static_cast<uint32_t>(section->data.size());
|
||||
offset += section->header.size;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto §ion : file.sections) {
|
||||
if (section->header.type == elf::SHT_RPL_FILEINFO) {
|
||||
section->header.offset = offset;
|
||||
section->header.size = static_cast<uint32_t>(section->data.size());
|
||||
offset += section->header.size;
|
||||
}
|
||||
}
|
||||
|
||||
// First the "dataMin / dataMax" sections, which are:
|
||||
// - !(flags & SHF_EXECINSTR)
|
||||
// - flags & SHF_WRITE
|
||||
// - flags & SHF_ALLOC
|
||||
for (auto §ion : file.sections) {
|
||||
if (section->header.size == 0 ||
|
||||
section->header.type == elf::SHT_RPL_FILEINFO ||
|
||||
section->header.type == elf::SHT_RPL_IMPORTS ||
|
||||
section->header.type == elf::SHT_RPL_CRCS ||
|
||||
section->header.type == elf::SHT_NOBITS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(section->header.flags & elf::SHF_EXECINSTR) &&
|
||||
(section->header.flags & elf::SHF_WRITE) &&
|
||||
(section->header.flags & elf::SHF_ALLOC)) {
|
||||
section->header.offset = offset;
|
||||
section->header.size = static_cast<uint32_t>(section->data.size());
|
||||
offset += section->header.size;
|
||||
}
|
||||
}
|
||||
|
||||
// Next the "readMin / readMax" sections, which are:
|
||||
// - !(flags & SHF_EXECINSTR) || type == SHT_RPL_EXPORTS
|
||||
// - !(flags & SHF_WRITE)
|
||||
// - flags & SHF_ALLOC
|
||||
for (auto §ion : file.sections) {
|
||||
if (section->header.size == 0 ||
|
||||
section->header.type == elf::SHT_RPL_FILEINFO ||
|
||||
section->header.type == elf::SHT_RPL_IMPORTS ||
|
||||
section->header.type == elf::SHT_RPL_CRCS ||
|
||||
section->header.type == elf::SHT_NOBITS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((!(section->header.flags & elf::SHF_EXECINSTR) ||
|
||||
section->header.type == elf::SHT_RPL_EXPORTS) &&
|
||||
!(section->header.flags & elf::SHF_WRITE) &&
|
||||
(section->header.flags & elf::SHF_ALLOC)) {
|
||||
section->header.offset = offset;
|
||||
section->header.size = static_cast<uint32_t>(section->data.size());
|
||||
offset += section->header.size;
|
||||
}
|
||||
}
|
||||
|
||||
// Import sections are part of the read sections, but have execinstr flag set
|
||||
// so let's insert them here to avoid complicating the above logic.
|
||||
for (auto §ion : file.sections) {
|
||||
if (section->header.type == elf::SHT_RPL_IMPORTS) {
|
||||
section->header.offset = offset;
|
||||
section->header.size = static_cast<uint32_t>(section->data.size());
|
||||
offset += section->header.size;
|
||||
}
|
||||
}
|
||||
|
||||
// Next the "textMin / textMax" sections, which are:
|
||||
// - flags & SHF_EXECINSTR
|
||||
// - type != SHT_RPL_EXPORTS
|
||||
for (auto §ion : file.sections) {
|
||||
if (section->header.size == 0 ||
|
||||
section->header.type == elf::SHT_RPL_FILEINFO ||
|
||||
section->header.type == elf::SHT_RPL_IMPORTS ||
|
||||
section->header.type == elf::SHT_RPL_CRCS ||
|
||||
section->header.type == elf::SHT_NOBITS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((section->header.flags & elf::SHF_EXECINSTR) &&
|
||||
section->header.type != elf::SHT_RPL_EXPORTS) {
|
||||
section->header.offset = offset;
|
||||
section->header.size = static_cast<uint32_t>(section->data.size());
|
||||
offset += section->header.size;
|
||||
}
|
||||
}
|
||||
|
||||
// Next the "tempMin / tempMax" sections, which are:
|
||||
// - !(flags & SHF_EXECINSTR)
|
||||
// - !(flags & SHF_ALLOC)
|
||||
for (auto §ion : file.sections) {
|
||||
if (section->header.size == 0 ||
|
||||
section->header.type == elf::SHT_RPL_FILEINFO ||
|
||||
section->header.type == elf::SHT_RPL_IMPORTS ||
|
||||
section->header.type == elf::SHT_RPL_CRCS ||
|
||||
section->header.type == elf::SHT_NOBITS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(section->header.flags & elf::SHF_EXECINSTR) &&
|
||||
!(section->header.flags & elf::SHF_ALLOC)) {
|
||||
section->header.offset = offset;
|
||||
section->header.size = static_cast<uint32_t>(section->data.size());
|
||||
offset += section->header.size;
|
||||
}
|
||||
}
|
||||
|
||||
auto index = 0u;
|
||||
for (auto §ion : file.sections) {
|
||||
if (section->header.offset == 0 &&
|
||||
section->header.type != elf::SHT_NULL &&
|
||||
section->header.type != elf::SHT_NOBITS) {
|
||||
fmt::print("Failed to calculate offset for section {}\n", index);
|
||||
return false;
|
||||
}
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write out the final RPL.
|
||||
*/
|
||||
static bool
|
||||
writeRpl(ElfFile &file, const std::string &filename)
|
||||
{
|
||||
auto shoff = file.header.shoff;
|
||||
|
||||
// Write the file out
|
||||
std::ofstream out { filename, std::ofstream::binary };
|
||||
|
||||
if (!out.is_open()) {
|
||||
fmt::print("Could not open {} for writing\n", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write file header
|
||||
out.seekp(0, std::ios::beg);
|
||||
out.write(reinterpret_cast<const char *>(&file.header), sizeof(elf::Header));
|
||||
|
||||
// Write section headers
|
||||
out.seekp(shoff, std::ios::beg);
|
||||
for (const auto §ion : file.sections) {
|
||||
out.write(reinterpret_cast<const char *>(§ion->header), sizeof(elf::SectionHeader));
|
||||
}
|
||||
|
||||
// Write sections
|
||||
for (const auto §ion : file.sections) {
|
||||
if (section->data.size()) {
|
||||
out.seekp(section->header.offset, std::ios::beg);
|
||||
out.write(section->data.data(), section->data.size());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
excmd::parser parser;
|
||||
excmd::option_state options;
|
||||
using excmd::description;
|
||||
using excmd::value;
|
||||
|
||||
try {
|
||||
parser.global_options()
|
||||
.add_option("H,help",
|
||||
description { "Show help." })
|
||||
.add_option("r,rpl",
|
||||
description { "Generate an RPL instead of an RPX" });
|
||||
|
||||
parser.default_command()
|
||||
.add_argument("src",
|
||||
description { "Path to input elf file" },
|
||||
value<std::string> {})
|
||||
.add_argument("dst",
|
||||
description { "Path to output rpl file" },
|
||||
value<std::string> {});
|
||||
|
||||
options = parser.parse(argc, argv);
|
||||
} catch (excmd::exception ex) {
|
||||
fmt::print("Error parsing options: {}\n", ex.what());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (options.empty()
|
||||
|| options.has("help")
|
||||
|| !options.has("src")
|
||||
|| !options.has("dst")) {
|
||||
fmt::print("{} <options> src dst\n", argv[0]);
|
||||
fmt::print("{}\n", parser.format_help(argv[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto src = options.get<std::string>("src");
|
||||
auto dst = options.get<std::string>("dst");
|
||||
auto isRpl = options.has("rpl");
|
||||
|
||||
// Read elf into memory object!
|
||||
ElfFile elf;
|
||||
|
||||
if (!readElf(elf, src)) {
|
||||
fmt::print("ERROR: readElf failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!fixRelocations(elf)) {
|
||||
fmt::print("ERROR: fixRelocations failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!fixLoaderVirtualAddresses(elf)) {
|
||||
fmt::print("ERROR: fixLoaderVirtualAddresses failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!generateFileInfoSection(elf, isRpl ? 0 : elf::RPL_IS_RPX)) {
|
||||
fmt::print("ERROR: generateFileInfoSection failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!generateCrcSection(elf)) {
|
||||
fmt::print("ERROR: generateCrcSection failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!fixFileHeader(elf)) {
|
||||
fmt::print("ERROR: fixFileHeader faile.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!deflateSections(elf)) {
|
||||
fmt::print("ERROR: deflateSections failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!calculateSectionOffsets(elf)) {
|
||||
fmt::print("ERROR: calculateSectionOffsets failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!writeRpl(elf, dst)) {
|
||||
fmt::print("ERROR: writeRpl failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
include(CheckTypeSize)
|
||||
|
||||
# excmd
|
||||
add_library(excmd INTERFACE)
|
||||
target_include_directories(excmd INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/excmd/src")
|
||||
|
||||
# fmt
|
||||
add_subdirectory(fmt)
|
||||
|
||||
# zlib
|
||||
find_package(ZLIB QUIET)
|
||||
|
||||
if(NOT ZLIB_FOUND)
|
||||
# Disable zlib install
|
||||
set(SKIP_INSTALL_ALL 1)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
add_subdirectory(zlib)
|
||||
|
||||
# Fix zlib tool / example includes
|
||||
target_include_directories(example PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/zlib")
|
||||
target_include_directories(minigzip PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/zlib")
|
||||
|
||||
check_type_size(off64_t OFF64_T)
|
||||
if(HAVE_OFF64_T)
|
||||
target_include_directories(example64 PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/zlib")
|
||||
target_include_directories(minigzip64 PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/zlib")
|
||||
endif()
|
||||
|
||||
target_include_directories(zlibstatic INTERFACE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/zlib"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/zlib")
|
||||
else()
|
||||
# Not actually static, but who cares...
|
||||
add_library(zlibstatic INTERFACE)
|
||||
target_link_libraries(zlibstatic INTERFACE ${ZLIB_LIBRARIES})
|
||||
target_include_directories(zlibstatic INTERFACE ${ZLIB_INCLUDE_DIR})
|
||||
endif()
|
@ -1 +0,0 @@
|
||||
Subproject commit b48dab4954bdc074d7768e38065171441aaff7ce
|
@ -1 +0,0 @@
|
||||
Subproject commit 5386f1df20392a08844f5034e8436c6ec7ce0b03
|
@ -1 +0,0 @@
|
||||
Subproject commit cacf7f1d4e3d44d871b605da3b647f07d718623f
|
@ -1,18 +0,0 @@
|
||||
project(readrpl)
|
||||
|
||||
add_executable(readrpl
|
||||
generate_exports_def.cpp
|
||||
generate_exports_def.h
|
||||
main.cpp
|
||||
print.cpp
|
||||
print.h
|
||||
readrpl.h
|
||||
verify.cpp
|
||||
verify.h)
|
||||
|
||||
target_link_libraries(readrpl
|
||||
excmd
|
||||
fmt
|
||||
zlibstatic)
|
||||
|
||||
install(TARGETS readrpl RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
|
@ -1,106 +0,0 @@
|
||||
#include "generate_exports_def.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fmt/format.h>
|
||||
|
||||
static const char *
|
||||
sExportBlacklist[] = {
|
||||
"__get_eh_globals",
|
||||
"__get_eh_init_block",
|
||||
"__get_eh_mem_manage",
|
||||
"__get_eh_store_globals",
|
||||
"__get_eh_store_globals_tdeh",
|
||||
"__gh_errno_ptr",
|
||||
"__gh_get_errno",
|
||||
"__gh_iob_init",
|
||||
"__gh_lock_init",
|
||||
"__gh_set_errno",
|
||||
"__ghsLock",
|
||||
"__ghsUnlock",
|
||||
"__ghs_at_exit",
|
||||
"__ghs_at_exit_cleanup",
|
||||
"__ghs_flock_create",
|
||||
"__ghs_flock_destroy",
|
||||
"__ghs_flock_file",
|
||||
"__ghs_flock_ptr",
|
||||
"__ghs_ftrylock_file",
|
||||
"__ghs_funlock_file",
|
||||
"__ghs_mtx_dst",
|
||||
"__ghs_mtx_init",
|
||||
"__ghs_mtx_lock",
|
||||
"__ghs_mtx_unlock",
|
||||
"__tls_get_addr",
|
||||
"memclr",
|
||||
"memcpy",
|
||||
"memmove",
|
||||
"memset",
|
||||
"__atexit_cleanup",
|
||||
"__cpp_exception_cleanup_ptr",
|
||||
"__cpp_exception_init_ptr",
|
||||
"__gh_FOPEN_MAX",
|
||||
"__ghs_cpp_locks",
|
||||
"__stdio_cleanup",
|
||||
"_iob",
|
||||
"_iob_lock",
|
||||
"environ",
|
||||
"errno",
|
||||
};
|
||||
|
||||
static bool
|
||||
inBlacklist(const char *name)
|
||||
{
|
||||
for (auto i = 0u; i < sizeof(sExportBlacklist) / sizeof(sExportBlacklist[0]); ++i) {
|
||||
if (strcmp(name, sExportBlacklist[i]) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
generateExportsDef(const Rpl &rpl,
|
||||
const std::string &rplName,
|
||||
const std::string &outFileName)
|
||||
{
|
||||
FILE *fh = fopen(outFileName.c_str(), "w");
|
||||
if (!fh) {
|
||||
fmt::print("Failed to open {} for writing!", outFileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
fmt::print(fh, ":NAME {}\n", rplName);
|
||||
|
||||
for (auto §ion : rpl.sections) {
|
||||
if (section.header.type == elf::SHT_RPL_EXPORTS) {
|
||||
auto exports = reinterpret_cast<const elf::RplExport *>(section.data.data());
|
||||
auto strTab = section.data.data();
|
||||
|
||||
if (section.header.flags & elf::SHF_EXECINSTR) {
|
||||
fmt::print(fh, "\n:TEXT\n");
|
||||
} else {
|
||||
fmt::print(fh, "\n:DATA\n");
|
||||
}
|
||||
|
||||
for (auto i = 0u; i < exports->count; ++i) {
|
||||
if (exports->exports[i].name & 0x80000000) {
|
||||
// Skip TLS exports for now.
|
||||
continue;
|
||||
}
|
||||
|
||||
auto name = strTab + (exports->exports[i].name & 0x7FFFFFFF);
|
||||
|
||||
if (inBlacklist(name)) {
|
||||
fmt::print(fh, "//");
|
||||
}
|
||||
|
||||
fmt::print(fh, "{}\n", name);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fh);
|
||||
return true;
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
#pragma once
|
||||
#include "readrpl.h"
|
||||
#include <string>
|
||||
|
||||
bool
|
||||
generateExportsDef(const Rpl &rpl,
|
||||
const std::string &rplName,
|
||||
const std::string &outFileName);
|
@ -1,298 +0,0 @@
|
||||
#include "elf.h"
|
||||
#include "generate_exports_def.h"
|
||||
#include "print.h"
|
||||
#include "verify.h"
|
||||
|
||||
#include <excmd.h>
|
||||
#include <fmt/format.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <zlib.h>
|
||||
|
||||
static std::string
|
||||
getFileBasename(std::string path)
|
||||
{
|
||||
auto pos = path.find_last_of("\\/");
|
||||
if (pos != std::string::npos) {
|
||||
path.erase(0, pos + 1);
|
||||
}
|
||||
|
||||
pos = path.rfind('.');
|
||||
if (pos != std::string::npos) {
|
||||
path.erase(pos);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
getSectionIndex(const Rpl &rpl,
|
||||
const Section §ion)
|
||||
{
|
||||
return static_cast<uint32_t>(§ion - &rpl.sections[0]);
|
||||
}
|
||||
|
||||
bool
|
||||
readSection(std::ifstream &fh,
|
||||
Section §ion,
|
||||
size_t i)
|
||||
{
|
||||
// Read section header
|
||||
fh.read(reinterpret_cast<char*>(§ion.header), sizeof(elf::SectionHeader));
|
||||
|
||||
if (section.header.type == elf::SHT_NOBITS || !section.header.size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read section data
|
||||
if (section.header.flags & elf::SHF_DEFLATED) {
|
||||
auto stream = z_stream {};
|
||||
auto ret = Z_OK;
|
||||
|
||||
// Read the original size
|
||||
uint32_t size = 0;
|
||||
fh.seekg(section.header.offset.value());
|
||||
fh.read(reinterpret_cast<char *>(&size), sizeof(uint32_t));
|
||||
size = byte_swap(size);
|
||||
section.data.resize(size);
|
||||
|
||||
// Inflate
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
stream.zalloc = Z_NULL;
|
||||
stream.zfree = Z_NULL;
|
||||
stream.opaque = Z_NULL;
|
||||
|
||||
ret = inflateInit(&stream);
|
||||
|
||||
if (ret != Z_OK) {
|
||||
fmt::print("Couldn't decompress .rpx section because inflateInit returned {}\n", ret);
|
||||
section.data.clear();
|
||||
return false;
|
||||
} else {
|
||||
std::vector<char> temp;
|
||||
temp.resize(section.header.size-sizeof(uint32_t));
|
||||
fh.read(temp.data(), temp.size());
|
||||
|
||||
stream.avail_in = section.header.size;
|
||||
stream.next_in = reinterpret_cast<Bytef *>(temp.data());
|
||||
stream.avail_out = static_cast<uInt>(section.data.size());
|
||||
stream.next_out = reinterpret_cast<Bytef *>(section.data.data());
|
||||
|
||||
ret = inflate(&stream, Z_FINISH);
|
||||
|
||||
if (ret != Z_OK && ret != Z_STREAM_END) {
|
||||
fmt::print("Couldn't decompress .rpx section because inflate returned {}\n", ret);
|
||||
section.data.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
inflateEnd(&stream);
|
||||
}
|
||||
} else {
|
||||
section.data.resize(section.header.size);
|
||||
fh.seekg(section.header.offset.value());
|
||||
fh.read(section.data.data(), section.header.size);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
excmd::parser parser;
|
||||
excmd::option_state options;
|
||||
using excmd::description;
|
||||
using excmd::value;
|
||||
|
||||
try {
|
||||
parser.global_options()
|
||||
.add_option("H,help",
|
||||
description { "Show help." })
|
||||
.add_option("a,all",
|
||||
description { "Equivalent to: -h -S -s -r -i -x -c -f" })
|
||||
.add_option("h,file-header",
|
||||
description { "Display the ELF file header" })
|
||||
.add_option("S,sections",
|
||||
description { "Display the sections' header" })
|
||||
.add_option("s,symbols",
|
||||
description { "Display the symbol table" })
|
||||
.add_option("r,relocs",
|
||||
description { "Display the relocations" })
|
||||
.add_option("i,imports",
|
||||
description { "Display the RPL imports" })
|
||||
.add_option("x,exports",
|
||||
description { "Display the RPL exports" })
|
||||
.add_option("c,crc",
|
||||
description { "Display the RPL crc" })
|
||||
.add_option("f,file-info",
|
||||
description { "Display the RPL file info" })
|
||||
.add_option("exports-def",
|
||||
description { "Generate exports.def for wut library linking" },
|
||||
value<std::string> {});
|
||||
|
||||
parser.default_command()
|
||||
.add_argument("path",
|
||||
description { "Path to RPL file" },
|
||||
value<std::string> {});
|
||||
|
||||
options = parser.parse(argc, argv);
|
||||
} catch (excmd::exception ex) {
|
||||
std::cout << "Error parsing options: " << ex.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (options.empty() || options.has("help") || !options.has("path")) {
|
||||
fmt::print("{} <options> path\n", argv[0]);
|
||||
fmt::print("{}\n", parser.format_help(argv[0]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto all = options.has("all");
|
||||
|
||||
auto dumpElfHeader = all || options.has("file-header");
|
||||
auto dumpSectionSummary = all || options.has("sections");
|
||||
auto dumpSectionRela = all || options.has("relocs");
|
||||
auto dumpSectionSymtab = all || options.has("symbols");
|
||||
auto dumpSectionRplExports = all || options.has("exports");
|
||||
auto dumpSectionRplImports = all || options.has("imports");
|
||||
auto dumpSectionRplCrcs = all || options.has("crc");
|
||||
auto dumpSectionRplFileinfo = all || options.has("file-info");
|
||||
auto path = options.get<std::string>("path");
|
||||
|
||||
// If no options are set (other than "path"), let's default to a summary
|
||||
if (options.set_options.size() == 1) {
|
||||
dumpElfHeader = true;
|
||||
dumpSectionSummary = true;
|
||||
dumpSectionRplFileinfo = true;
|
||||
}
|
||||
|
||||
// Read file
|
||||
std::ifstream fh { path, std::ifstream::binary };
|
||||
if (!fh.is_open()) {
|
||||
fmt::print("Could not open {} for reading\n", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Rpl rpl;
|
||||
fh.read(reinterpret_cast<char*>(&rpl.header), sizeof(elf::Header));
|
||||
|
||||
if (rpl.header.magic != elf::HeaderMagic) {
|
||||
fmt::print("Invalid ELF magic header\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Read sections
|
||||
for (auto i = 0u; i < rpl.header.shnum; ++i) {
|
||||
Section section;
|
||||
fh.seekg(rpl.header.shoff + rpl.header.shentsize * i);
|
||||
|
||||
if (!readSection(fh, section, i)) {
|
||||
fmt::print("Error reading section {}", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rpl.sections.push_back(section);
|
||||
}
|
||||
|
||||
// Set section names
|
||||
auto shStrTab = reinterpret_cast<const char *>(rpl.sections[rpl.header.shstrndx].data.data());
|
||||
for (auto §ion : rpl.sections) {
|
||||
section.name = shStrTab + section.header.name;
|
||||
}
|
||||
|
||||
// Verify rpl format
|
||||
verifyFile(rpl);
|
||||
verifyCrcs(rpl);
|
||||
verifyFileBounds(rpl);
|
||||
verifyRelocationTypes(rpl);
|
||||
verifySectionAlignment(rpl);
|
||||
verifySectionOrder(rpl);
|
||||
|
||||
// Format shit
|
||||
if (dumpElfHeader) {
|
||||
printHeader(rpl);
|
||||
}
|
||||
|
||||
if (dumpSectionSummary) {
|
||||
printSectionSummary(rpl);
|
||||
}
|
||||
|
||||
// Print section data
|
||||
for (auto i = 0u; i < rpl.sections.size(); ++i) {
|
||||
auto §ion = rpl.sections[i];
|
||||
auto printSectionHeader = [&](){
|
||||
fmt::print(
|
||||
"Section {}: {}, {}, {} bytes\n",
|
||||
i, formatSHT(section.header.type), section.name, section.data.size());
|
||||
};
|
||||
|
||||
switch (section.header.type) {
|
||||
case elf::SHT_NULL:
|
||||
case elf::SHT_NOBITS:
|
||||
// Print nothing
|
||||
break;
|
||||
case elf::SHT_RELA:
|
||||
if (!dumpSectionRela) {
|
||||
continue;
|
||||
}
|
||||
|
||||
printSectionHeader();
|
||||
printRela(rpl, section);
|
||||
break;
|
||||
case elf::SHT_SYMTAB:
|
||||
if (!dumpSectionSymtab) {
|
||||
continue;
|
||||
}
|
||||
|
||||
printSectionHeader();
|
||||
printSymTab(rpl, section);
|
||||
break;
|
||||
case elf::SHT_STRTAB:
|
||||
break;
|
||||
case elf::SHT_PROGBITS:
|
||||
break;
|
||||
case elf::SHT_RPL_EXPORTS:
|
||||
if (!dumpSectionRplExports) {
|
||||
continue;
|
||||
}
|
||||
|
||||
printSectionHeader();
|
||||
printRplExports(rpl, section);
|
||||
break;
|
||||
case elf::SHT_RPL_IMPORTS:
|
||||
if (!dumpSectionRplImports) {
|
||||
continue;
|
||||
}
|
||||
|
||||
printSectionHeader();
|
||||
printRplImports(rpl, section);
|
||||
break;
|
||||
case elf::SHT_RPL_CRCS:
|
||||
if (!dumpSectionRplCrcs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
printSectionHeader();
|
||||
printRplCrcs(rpl, section);
|
||||
break;
|
||||
case elf::SHT_RPL_FILEINFO:
|
||||
if (!dumpSectionRplFileinfo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
printSectionHeader();
|
||||
printFileInfo(rpl, section);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.has("exports-def")) {
|
||||
auto output = options.get<std::string>("exports-def");
|
||||
if (!generateExportsDef(rpl, getFileBasename(path), output)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,477 +0,0 @@
|
||||
#include "print.h"
|
||||
#include <fmt/format.h>
|
||||
#include <string>
|
||||
|
||||
static std::string
|
||||
formatET(uint32_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case elf::ET_NONE:
|
||||
return "ET_NONE";
|
||||
case elf::ET_REL:
|
||||
return "ET_REL";
|
||||
case elf::ET_EXEC:
|
||||
return "ET_EXEC";
|
||||
case elf::ET_DYN:
|
||||
return "ET_DYN";
|
||||
case elf::ET_CORE:
|
||||
return "ET_CORE";
|
||||
case elf::ET_CAFE_RPL:
|
||||
return "ET_CAFE_RPL";
|
||||
default:
|
||||
return fmt::format("{}", type);
|
||||
}
|
||||
}
|
||||
|
||||
static std::string
|
||||
formatEM(uint32_t machine)
|
||||
{
|
||||
switch (machine) {
|
||||
case elf::EM_PPC:
|
||||
return "EM_PPC";
|
||||
default:
|
||||
return fmt::format("{}", machine);
|
||||
}
|
||||
}
|
||||
|
||||
static std::string
|
||||
formatEABI(uint32_t eabi)
|
||||
{
|
||||
switch (eabi) {
|
||||
case elf::EABI_CAFE:
|
||||
return "EABI_CAFE";
|
||||
default:
|
||||
return fmt::format("{}", eabi);
|
||||
}
|
||||
}
|
||||
|
||||
static std::string
|
||||
formatSHF(uint32_t flags)
|
||||
{
|
||||
std::string result = "";
|
||||
|
||||
if (flags & elf::SHF_WRITE) {
|
||||
result += "W";
|
||||
}
|
||||
|
||||
if (flags & elf::SHF_ALLOC) {
|
||||
result += "A";
|
||||
}
|
||||
|
||||
if (flags & elf::SHF_EXECINSTR) {
|
||||
result += "X";
|
||||
}
|
||||
|
||||
if (flags & elf::SHF_DEFLATED) {
|
||||
result += "Z";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string
|
||||
formatSHT(uint32_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case elf::SHT_NULL:
|
||||
return "SHT_NULL";
|
||||
case elf::SHT_PROGBITS:
|
||||
return "SHT_PROGBITS";
|
||||
case elf::SHT_SYMTAB:
|
||||
return "SHT_SYMTAB";
|
||||
case elf::SHT_STRTAB:
|
||||
return "SHT_STRTAB";
|
||||
case elf::SHT_RELA:
|
||||
return "SHT_RELA";
|
||||
case elf::SHT_HASH:
|
||||
return "SHT_HASH";
|
||||
case elf::SHT_DYNAMIC:
|
||||
return "SHT_DYNAMIC";
|
||||
case elf::SHT_NOTE:
|
||||
return "SHT_NOTE";
|
||||
case elf::SHT_NOBITS:
|
||||
return "SHT_NOBITS";
|
||||
case elf::SHT_REL:
|
||||
return "SHT_REL";
|
||||
case elf::SHT_SHLIB:
|
||||
return "SHT_SHLIB";
|
||||
case elf::SHT_DYNSYM:
|
||||
return "SHT_DYNSYM";
|
||||
case elf::SHT_INIT_ARRAY:
|
||||
return "SHT_INIT_ARRAY";
|
||||
case elf::SHT_FINI_ARRAY:
|
||||
return "SHT_FINI_ARRAY";
|
||||
case elf::SHT_PREINIT_ARRAY:
|
||||
return "SHT_PREINIT_ARRAY";
|
||||
case elf::SHT_GROUP:
|
||||
return "SHT_GROUP";
|
||||
case elf::SHT_SYMTAB_SHNDX:
|
||||
return "SHT_SYMTAB_SHNDX";
|
||||
case elf::SHT_LOPROC:
|
||||
return "SHT_LOPROC";
|
||||
case elf::SHT_HIPROC:
|
||||
return "SHT_HIPROC";
|
||||
case elf::SHT_LOUSER:
|
||||
return "SHT_LOUSER";
|
||||
case elf::SHT_RPL_EXPORTS:
|
||||
return "SHT_RPL_EXPORTS";
|
||||
case elf::SHT_RPL_IMPORTS:
|
||||
return "SHT_RPL_IMPORTS";
|
||||
case elf::SHT_RPL_CRCS:
|
||||
return "SHT_RPL_CRCS";
|
||||
case elf::SHT_RPL_FILEINFO:
|
||||
return "SHT_RPL_FILEINFO";
|
||||
case elf::SHT_HIUSER:
|
||||
return "SHT_HIUSER";
|
||||
default:
|
||||
return fmt::format("{}", type);
|
||||
}
|
||||
}
|
||||
|
||||
static std::string
|
||||
formatRelType(uint32_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case elf::R_PPC_NONE:
|
||||
return "NONE";
|
||||
case elf::R_PPC_ADDR32:
|
||||
return "ADDR32";
|
||||
case elf::R_PPC_ADDR16_LO:
|
||||
return "ADDR16_LO";
|
||||
case elf::R_PPC_ADDR16_HI:
|
||||
return "ADDR16_HI";
|
||||
case elf::R_PPC_ADDR16_HA:
|
||||
return "ADDR16_HA";
|
||||
case elf::R_PPC_REL24:
|
||||
return "REL24";
|
||||
case elf::R_PPC_REL14:
|
||||
return "REL14";
|
||||
case elf::R_PPC_DTPMOD32:
|
||||
return "DTPMOD32";
|
||||
case elf::R_PPC_DTPREL32:
|
||||
return "DTPREL32";
|
||||
case elf::R_PPC_EMB_SDA21:
|
||||
return "EMB_SDA21";
|
||||
case elf::R_PPC_EMB_RELSDA:
|
||||
return "EMB_RELSDA";
|
||||
case elf::R_PPC_DIAB_SDA21_LO:
|
||||
return "DIAB_SDA21_LO";
|
||||
case elf::R_PPC_DIAB_SDA21_HI:
|
||||
return "DIAB_SDA21_HI";
|
||||
case elf::R_PPC_DIAB_SDA21_HA:
|
||||
return "DIAB_SDA21_HA";
|
||||
case elf::R_PPC_DIAB_RELSDA_LO:
|
||||
return "DIAB_RELSDA_LO";
|
||||
case elf::R_PPC_DIAB_RELSDA_HI:
|
||||
return "DIAB_RELSDA_HI";
|
||||
case elf::R_PPC_DIAB_RELSDA_HA:
|
||||
return "DIAB_RELSDA_HA";
|
||||
case elf::R_PPC_GHS_REL16_HA:
|
||||
return "GHS_REL16_HA";
|
||||
case elf::R_PPC_GHS_REL16_HI:
|
||||
return "GHS_REL16_HI";
|
||||
case elf::R_PPC_GHS_REL16_LO:
|
||||
return "GHS_REL16_LO";
|
||||
default:
|
||||
return fmt::format("{}", type);
|
||||
}
|
||||
}
|
||||
|
||||
static std::string
|
||||
formatSymType(uint32_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case elf::STT_NOTYPE:
|
||||
return "NOTYPE";
|
||||
case elf::STT_OBJECT:
|
||||
return "OBJECT";
|
||||
case elf::STT_FUNC:
|
||||
return "FUNC";
|
||||
case elf::STT_SECTION:
|
||||
return "SECTION";
|
||||
case elf::STT_FILE:
|
||||
return "FILE";
|
||||
case elf::STT_COMMON:
|
||||
return "COMMON";
|
||||
case elf::STT_TLS:
|
||||
return "TLS";
|
||||
case elf::STT_LOOS:
|
||||
return "LOOS";
|
||||
case elf::STT_HIOS:
|
||||
return "HIOS";
|
||||
case elf::STT_GNU_IFUNC:
|
||||
return "GNU_IFUNC";
|
||||
default:
|
||||
return fmt::format("{}", type);
|
||||
}
|
||||
}
|
||||
|
||||
static std::string
|
||||
formatSymBinding(uint32_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case elf::STB_LOCAL:
|
||||
return "LOCAL";
|
||||
case elf::STB_GLOBAL:
|
||||
return "GLOBAL";
|
||||
case elf::STB_WEAK:
|
||||
return "WEAK";
|
||||
case elf::STB_GNU_UNIQUE:
|
||||
return "UNIQUE";
|
||||
default:
|
||||
return fmt::format("{}", type);
|
||||
}
|
||||
}
|
||||
|
||||
static std::string
|
||||
formatSymShndx(uint32_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case elf::SHN_UNDEF:
|
||||
return "UND";
|
||||
case elf::SHN_ABS:
|
||||
return "ABS";
|
||||
case elf::SHN_COMMON:
|
||||
return "CMN";
|
||||
case elf::SHN_XINDEX:
|
||||
return "UND";
|
||||
default:
|
||||
return fmt::format("{}", type);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
printHeader(const Rpl &rpl)
|
||||
{
|
||||
const auto &header = rpl.header;
|
||||
fmt::print("ElfHeader\n");
|
||||
fmt::print(" {:<20} = 0x{:08X}\n", "magic", header.magic);
|
||||
fmt::print(" {:<20} = {}\n", "fileClass", header.fileClass);
|
||||
fmt::print(" {:<20} = {}\n", "encoding", header.encoding);
|
||||
fmt::print(" {:<20} = {}\n", "elfVersion", header.elfVersion);
|
||||
fmt::print(" {:<20} = {} 0x{:04x}\n", "abi", formatEABI(header.abi), header.abi);
|
||||
fmt::print(" {:<20} = {} 0x{:04X}\n", "type", formatET(header.type), header.type);
|
||||
fmt::print(" {:<20} = {} {}\n", "machine", formatEM(header.machine), header.machine);
|
||||
fmt::print(" {:<20} = 0x{:X}\n", "version", header.version);
|
||||
fmt::print(" {:<20} = 0x{:08X}\n", "entry", header.entry);
|
||||
fmt::print(" {:<20} = 0x{:X}\n", "phoff", header.phoff);
|
||||
fmt::print(" {:<20} = 0x{:X}\n", "shoff", header.shoff);
|
||||
fmt::print(" {:<20} = 0x{:X}\n", "flags", header.flags);
|
||||
fmt::print(" {:<20} = {}\n", "ehsize", header.ehsize);
|
||||
fmt::print(" {:<20} = {}\n", "phentsize", header.phentsize);
|
||||
fmt::print(" {:<20} = {}\n", "phnum", header.phnum);
|
||||
fmt::print(" {:<20} = {}\n", "shentsize", header.shentsize);
|
||||
fmt::print(" {:<20} = {}\n", "shnum", header.shnum);
|
||||
fmt::print(" {:<20} = {}\n", "shstrndx", header.shstrndx);
|
||||
}
|
||||
|
||||
void
|
||||
printSectionSummary(const Rpl &rpl)
|
||||
{
|
||||
fmt::print("Sections:\n");
|
||||
fmt::print(
|
||||
" {:<4} {:<20} {:<16} {:<8} {:<6} {:<6} {:<2} {:<4} {:<2} {:<4} {:<5}\n",
|
||||
"[Nr]", "Name", "Type", "Addr", "Off", "Size", "ES", "Flag", "Lk", "Info", "Align");
|
||||
|
||||
for (auto i = 0u; i < rpl.sections.size(); ++i) {
|
||||
auto §ion = rpl.sections[i];
|
||||
auto type = formatSHT(section.header.type);
|
||||
auto flags = formatSHF(section.header.flags);
|
||||
|
||||
fmt::print(
|
||||
" [{:>2}] {:<20} {:<16} {:08X} {:06X} {:06X} {:02X} {:>4} {:>2} {:>4} {:>5}\n",
|
||||
i,
|
||||
section.name,
|
||||
type,
|
||||
section.header.addr,
|
||||
section.header.offset,
|
||||
section.header.size,
|
||||
section.header.entsize,
|
||||
flags,
|
||||
section.header.link,
|
||||
section.header.info,
|
||||
section.header.addralign);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
printFileInfo(const Rpl &rpl,
|
||||
const Section §ion)
|
||||
{
|
||||
auto &info = *reinterpret_cast<const elf::RplFileInfo *>(section.data.data());
|
||||
fmt::print(" {:<20} = 0x{:08X}\n", "version", info.version);
|
||||
fmt::print(" {:<20} = 0x{:08X}\n", "textSize", info.textSize);
|
||||
fmt::print(" {:<20} = 0x{:X}\n", "textAlign", info.textAlign);
|
||||
fmt::print(" {:<20} = 0x{:08X}\n", "dataSize", info.dataSize);
|
||||
fmt::print(" {:<20} = 0x{:X}\n", "dataAlign", info.dataAlign);
|
||||
fmt::print(" {:<20} = 0x{:08X}\n", "loadSize", info.loadSize);
|
||||
fmt::print(" {:<20} = 0x{:X}\n", "loadAlign", info.loadAlign);
|
||||
fmt::print(" {:<20} = 0x{:X}\n", "tempSize", info.tempSize);
|
||||
fmt::print(" {:<20} = 0x{:X}\n", "trampAdjust", info.trampAdjust);
|
||||
fmt::print(" {:<20} = 0x{:X}\n", "trampAddition", info.trampAddition);
|
||||
fmt::print(" {:<20} = 0x{:08X}\n", "sdaBase", info.sdaBase);
|
||||
fmt::print(" {:<20} = 0x{:08X}\n", "sda2Base", info.sda2Base);
|
||||
fmt::print(" {:<20} = 0x{:08X}\n", "stackSize", info.stackSize);
|
||||
fmt::print(" {:<20} = 0x{:08X}\n", "heapSize", info.heapSize);
|
||||
|
||||
if (info.filename) {
|
||||
auto filename = section.data.data() + info.filename;
|
||||
fmt::print(" {:<20} = {}\n", "filename", filename);
|
||||
} else {
|
||||
fmt::print(" {:<20} = 0\n", "filename");
|
||||
}
|
||||
|
||||
fmt::print(" {:<20} = 0x{:X}\n", "flags", info.flags);
|
||||
fmt::print(" {:<20} = 0x{:08X}\n", "minSdkVersion", info.minVersion);
|
||||
fmt::print(" {:<20} = {}\n", "compressionLevel", info.compressionLevel);
|
||||
fmt::print(" {:<20} = 0x{:X}\n", "fileInfoPad", info.fileInfoPad);
|
||||
fmt::print(" {:<20} = 0x{:X}\n", "sdkVersion", info.cafeSdkVersion);
|
||||
fmt::print(" {:<20} = 0x{:X}\n", "sdkRevision", info.cafeSdkRevision);
|
||||
fmt::print(" {:<20} = 0x{:X}\n", "tlsModuleIndex", info.tlsModuleIndex);
|
||||
fmt::print(" {:<20} = 0x{:X}\n", "tlsAlignShift", info.tlsAlignShift);
|
||||
fmt::print(" {:<20} = 0x{:X}\n", "runtimeFileInfoSize", info.runtimeFileInfoSize);
|
||||
|
||||
if (info.tagOffset) {
|
||||
const char *tags = section.data.data() + info.tagOffset;
|
||||
fmt::print(" Tags:\n");
|
||||
|
||||
while (*tags) {
|
||||
auto key = tags;
|
||||
tags += strlen(tags) + 1;
|
||||
auto value = tags;
|
||||
tags += strlen(tags) + 1;
|
||||
|
||||
fmt::print(" \"{}\" = \"{}\"\n", key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
printRela(const Rpl &rpl,
|
||||
const Section §ion)
|
||||
{
|
||||
fmt::print(
|
||||
" {:<8} {:<8} {:<16} {:<8} {}\n", "Offset", "Info", "Type", "Value", "Name + Addend");
|
||||
|
||||
auto &symSec = rpl.sections[section.header.link];
|
||||
auto symbols = reinterpret_cast<const elf::Symbol *>(symSec.data.data());
|
||||
auto &symStrTab = rpl.sections[symSec.header.link];
|
||||
|
||||
auto relas = reinterpret_cast<const elf::Rela *>(section.data.data());
|
||||
auto count = section.data.size() / sizeof(elf::Rela);
|
||||
|
||||
for (auto i = 0u; i < count; ++i) {
|
||||
auto &rela = relas[i];
|
||||
|
||||
auto index = rela.info >> 8;
|
||||
auto type = rela.info & 0xff;
|
||||
auto typeName = formatRelType(type);
|
||||
|
||||
auto symbol = symbols[index];
|
||||
auto name = reinterpret_cast<const char*>(symStrTab.data.data()) + symbol.name;
|
||||
|
||||
fmt::print(
|
||||
" {:08X} {:08X} {:<16} {:08X} {} + {:X}\n",
|
||||
rela.offset,
|
||||
rela.info,
|
||||
typeName,
|
||||
symbol.value,
|
||||
name,
|
||||
rela.addend);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
printSymTab(const Rpl &rpl,
|
||||
const Section §ion)
|
||||
{
|
||||
auto strTab = reinterpret_cast<const char*>(rpl.sections[section.header.link].data.data());
|
||||
|
||||
fmt::print(
|
||||
" {:<4} {:<8} {:<6} {:<8} {:<8} {:<3} {}\n",
|
||||
"Num", "Value", "Size", "Type", "Bind", "Ndx", "Name");
|
||||
|
||||
auto id = 0u;
|
||||
auto symbols = reinterpret_cast<const elf::Symbol *>(section.data.data());
|
||||
auto count = section.data.size() / sizeof(elf::Symbol);
|
||||
|
||||
for (auto i = 0u; i < count; ++i) {
|
||||
auto &symbol = symbols[i];
|
||||
|
||||
auto name = strTab + symbol.name;
|
||||
auto binding = symbol.info >> 4;
|
||||
auto type = symbol.info & 0xf;
|
||||
auto typeName = formatSymType(type);
|
||||
auto bindingName = formatSymBinding(binding);
|
||||
auto ndx = formatSymShndx(symbol.shndx);
|
||||
|
||||
fmt::print(
|
||||
" {:>4} {:08X} {:>6} {:<8} {:<8} {:>3} {}\n",
|
||||
id, symbol.value, symbol.size, typeName, bindingName, ndx, name);
|
||||
|
||||
++id;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
printRplImports(const Rpl &rpl,
|
||||
const Section §ion)
|
||||
{
|
||||
auto sectionIndex = getSectionIndex(rpl, section);
|
||||
auto import = reinterpret_cast<const elf::RplImport *>(section.data.data());
|
||||
fmt::print(" {:<20} = {}\n", "name", import->name);
|
||||
fmt::print(" {:<20} = 0x{:08X}\n", "signature", import->signature);
|
||||
fmt::print(" {:<20} = {}\n", "count", import->count);
|
||||
|
||||
if (import->count) {
|
||||
for (auto &symSection : rpl.sections) {
|
||||
if (symSection.header.type != elf::SHT_SYMTAB) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto symbols = reinterpret_cast<const elf::Symbol *>(symSection.data.data());
|
||||
auto count = symSection.data.size() / sizeof(elf::Symbol);
|
||||
auto strTab = reinterpret_cast<const char*>(rpl.sections[symSection.header.link].data.data());
|
||||
|
||||
for (auto i = 0u; i < count; ++i) {
|
||||
auto &symbol = symbols[i];
|
||||
auto type = symbol.info & 0xf;
|
||||
|
||||
if (symbol.shndx == sectionIndex &&
|
||||
(type == elf::STT_FUNC || type == elf::STT_OBJECT)) {
|
||||
fmt::print(" {}\n", strTab + symbol.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
printRplCrcs(const Rpl &rpl,
|
||||
const Section §ion)
|
||||
{
|
||||
auto crcs = reinterpret_cast<const elf::RplCrc *>(section.data.data());
|
||||
auto count = section.data.size() / sizeof(elf::RplCrc);
|
||||
|
||||
for (auto i = 0u; i < count; ++i) {
|
||||
fmt::print(" [{:>2}] 0x{:08X} {}\n", i, crcs[i].crc, section.name);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
printRplExports(const Rpl &rpl,
|
||||
const Section §ion)
|
||||
{
|
||||
auto exports = reinterpret_cast<const elf::RplExport *>(section.data.data());
|
||||
auto strTab = section.data.data();
|
||||
fmt::print(" {:<20} = 0x{:08X}\n", "signature", exports->signature);
|
||||
fmt::print(" {:<20} = {}\n", "count", exports->count);
|
||||
|
||||
for (auto i = 0u; i < exports->count; ++i) {
|
||||
// TLS exports have the high bit set in name for some unknown reason...
|
||||
auto name = strTab + (exports->exports[i].name & 0x7FFFFFFF);
|
||||
auto value = exports->exports[i].value;
|
||||
|
||||
fmt::print(" 0x{:08X} {}\n", value, name);
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
#include "readrpl.h"
|
||||
|
||||
std::string
|
||||
formatSHT(uint32_t type);
|
||||
|
||||
void
|
||||
printHeader(const Rpl &rpl);
|
||||
|
||||
void
|
||||
printSectionSummary(const Rpl &rpl);
|
||||
|
||||
void
|
||||
printFileInfo(const Rpl &rpl,
|
||||
const Section §ion);
|
||||
|
||||
void
|
||||
printRela(const Rpl &rpl,
|
||||
const Section §ion);
|
||||
|
||||
void
|
||||
printSymTab(const Rpl &rpl,
|
||||
const Section §ion);
|
||||
|
||||
void
|
||||
printRplImports(const Rpl &rpl,
|
||||
const Section §ion);
|
||||
|
||||
void
|
||||
printRplCrcs(const Rpl &rpl,
|
||||
const Section §ion);
|
||||
|
||||
void
|
||||
printRplExports(const Rpl &rpl,
|
||||
const Section §ion);
|
@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
#include "elf.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct Section
|
||||
{
|
||||
elf::SectionHeader header;
|
||||
std::string name;
|
||||
std::vector<char> data;
|
||||
};
|
||||
|
||||
struct Rpl
|
||||
{
|
||||
elf::Header header;
|
||||
uint32_t fileSize;
|
||||
std::vector<Section> sections;
|
||||
};
|
||||
|
||||
uint32_t
|
||||
getSectionIndex(const Rpl &rpl,
|
||||
const Section §ion);
|
@ -1,586 +0,0 @@
|
||||
#include "verify.h"
|
||||
#include <algorithm>
|
||||
#include <fmt/format.h>
|
||||
#include <set>
|
||||
#include <zlib.h>
|
||||
|
||||
static bool
|
||||
sValidateRelocsAddTable(const Rpl &rpl,
|
||||
const Section §ion)
|
||||
{
|
||||
const auto &header = section.header;
|
||||
if (!header.size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto entsize = static_cast<uint32_t>(header.entsize);
|
||||
if (!entsize) {
|
||||
entsize = static_cast<uint32_t>(sizeof(elf::Rela));
|
||||
}
|
||||
|
||||
if (entsize < sizeof(elf::Rela)) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0002E);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto numRelas = (header.size / entsize);
|
||||
if (!numRelas) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0000A);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!header.link || header.link >= rpl.header.shnum) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0000B);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &symbolSection = rpl.sections[header.link];
|
||||
if (symbolSection.header.type != elf::SHT_SYMTAB) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0000C);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto symEntsize = symbolSection.header.entsize ?
|
||||
static_cast<uint32_t>(symbolSection.header.entsize) :
|
||||
static_cast<uint32_t>(sizeof(elf::Symbol));
|
||||
if (symEntsize < sizeof(elf::Symbol)) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0002F);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.info >= rpl.header.shnum) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0000D);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto &targetSection = rpl.sections[header.info];
|
||||
if (targetSection.header.type != elf::SHT_NULL) {
|
||||
auto numSymbols = symbolSection.data.size() / symEntsize;
|
||||
for (auto i = 0u; i < numRelas; ++i) {
|
||||
auto rela = reinterpret_cast<const elf::Rela *>(section.data.data() + i * entsize);
|
||||
if (rela->info && (rela->info >> 8) >= numSymbols) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0000F);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
sValidateSymbolTable(const Rpl &rpl,
|
||||
const Section §ion)
|
||||
{
|
||||
auto result = true;
|
||||
const auto &header = section.header;
|
||||
if (!header.size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const Section *symStrTabSection = nullptr;
|
||||
if (header.link) {
|
||||
if (header.link >= rpl.header.shnum) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00001);
|
||||
return false;
|
||||
}
|
||||
|
||||
symStrTabSection = &rpl.sections[header.link];
|
||||
if (symStrTabSection->header.type != elf::SHT_STRTAB) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00002);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto entsize = header.entsize ?
|
||||
static_cast<uint32_t>(header.entsize) :
|
||||
static_cast<uint32_t>(sizeof(elf::Symbol));
|
||||
if (entsize < sizeof(elf::Symbol)) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0002D);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto numSymbols = header.size / entsize;
|
||||
if (!numSymbols) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00003);
|
||||
result = false;
|
||||
}
|
||||
|
||||
for (auto i = 0u; i < numSymbols; ++i) {
|
||||
auto symbol = reinterpret_cast<const elf::Symbol *>(section.data.data() + i * entsize);
|
||||
|
||||
if (symStrTabSection &&
|
||||
symbol->name > symStrTabSection->data.size()) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00004);
|
||||
}
|
||||
|
||||
auto type = symbol->info & 0xF;
|
||||
if (symbol->shndx &&
|
||||
symbol->shndx < elf::SHN_LORESERVE &&
|
||||
type != elf::STT_SECTION &&
|
||||
type != elf::STT_FILE) {
|
||||
if (symbol->shndx >= rpl.header.shnum) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00005);
|
||||
result = false;
|
||||
} else if (type == elf::STT_OBJECT) {
|
||||
const auto &targetSection = rpl.sections[symbol->shndx];
|
||||
auto targetSectionSize = targetSection.data.size() ?
|
||||
static_cast<uint32_t>(targetSection.data.size()) :
|
||||
static_cast<uint32_t>(targetSection.header.size);
|
||||
|
||||
if (targetSectionSize &&
|
||||
targetSection.header.flags & elf::SHF_ALLOC) {
|
||||
if (targetSection.header.type == elf::SHT_NULL) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00006);
|
||||
result = false;
|
||||
}
|
||||
|
||||
auto position = symbol->value - targetSection.header.addr;
|
||||
if (position > targetSectionSize || position + symbol->size > targetSectionSize) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00007);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
} else if (type == elf::STT_FUNC) {
|
||||
const auto &targetSection = rpl.sections[symbol->shndx];
|
||||
auto targetSectionSize = targetSection.data.size() ?
|
||||
static_cast<uint32_t>(targetSection.data.size()) :
|
||||
static_cast<uint32_t>(targetSection.header.size);
|
||||
|
||||
if (targetSectionSize &&
|
||||
targetSection.header.flags & elf::SHF_ALLOC) {
|
||||
if (targetSection.header.type == elf::SHT_NULL) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00008);
|
||||
result = false;
|
||||
}
|
||||
|
||||
auto position = symbol->value - targetSection.header.addr;
|
||||
if (position > targetSectionSize || position + symbol->size > targetSectionSize) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00009);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to loader.elf ELFFILE_ValidateAndPrepare
|
||||
*/
|
||||
bool
|
||||
verifyFile(const Rpl &rpl)
|
||||
{
|
||||
const auto &header = rpl.header;
|
||||
auto result = true;
|
||||
|
||||
if (rpl.fileSize < 0x104) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00018);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.magic != elf::HeaderMagic) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00019);
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (header.fileClass != elf::ELFCLASS32) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0001A);
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (header.elfVersion > elf::EV_CURRENT) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0001B);
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (!header.machine) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0001C);
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (!header.version != 1) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0001D);
|
||||
result = false;
|
||||
}
|
||||
|
||||
auto ehsize = static_cast<uint32_t>(header.ehsize);
|
||||
if (ehsize) {
|
||||
if (header.ehsize < sizeof(elf::Header)) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0001E);
|
||||
result = false;
|
||||
}
|
||||
} else {
|
||||
ehsize = static_cast<uint32_t>(sizeof(elf::Header));
|
||||
}
|
||||
|
||||
auto phoff = header.phoff;
|
||||
if (phoff && (phoff < ehsize || phoff >= rpl.fileSize)) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0001F);
|
||||
result = false;
|
||||
}
|
||||
|
||||
auto shoff = header.shoff;
|
||||
if (shoff && (shoff < ehsize || shoff >= rpl.fileSize)) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00020);
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (header.shstrndx && header.shstrndx >= header.shnum) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00021);
|
||||
result = false;
|
||||
}
|
||||
|
||||
auto phentsize = header.phentsize ?
|
||||
static_cast<uint16_t>(header.phentsize) :
|
||||
static_cast<uint16_t>(32);
|
||||
if (header.phoff &&
|
||||
(header.phoff + phentsize * header.phnum) > rpl.fileSize) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00022);
|
||||
result = false;
|
||||
}
|
||||
|
||||
auto shentsize = header.shentsize ?
|
||||
static_cast<uint32_t>(header.shentsize) :
|
||||
static_cast<uint32_t>(sizeof(elf::SectionHeader));
|
||||
if (header.shoff &&
|
||||
(header.shoff + shentsize * header.shnum) > rpl.fileSize) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00023);
|
||||
result = false;
|
||||
}
|
||||
|
||||
for (auto §ion : rpl.sections) {
|
||||
if (section.header.size &&
|
||||
section.header.type != elf::SHT_NOBITS) {
|
||||
if (section.header.offset < ehsize) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00024);
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (section.header.offset >= shoff &&
|
||||
section.header.offset < (shoff + header.shnum * shentsize)) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD00027);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (header.shstrndx) {
|
||||
const auto &shStrTabSection = rpl.sections[header.shstrndx];
|
||||
if (shStrTabSection.header.type != elf::SHT_STRTAB) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0002A);
|
||||
result = false;
|
||||
} else {
|
||||
for (auto §ion : rpl.sections) {
|
||||
if (section.header.name >= shStrTabSection.data.size()) {
|
||||
fmt::format("*** Failed ELF file checks (err=0x{:08X})\n", 0xBAD0002B);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto §ion : rpl.sections) {
|
||||
if (section.header.type == elf::SHT_RELA) {
|
||||
result = sValidateRelocsAddTable(rpl, section) && result;
|
||||
} else if (section.header.type == elf::SHT_SYMTAB) {
|
||||
result = sValidateSymbolTable(rpl, section) && result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verify values in SHT_RPL_CRCS
|
||||
*/
|
||||
bool
|
||||
verifyCrcs(const Rpl &rpl)
|
||||
{
|
||||
const elf::RplCrc *crcs = NULL;
|
||||
auto result = true;
|
||||
|
||||
for (const auto §ion : rpl.sections) {
|
||||
if (section.header.type == elf::SHT_RPL_CRCS) {
|
||||
crcs = reinterpret_cast<const elf::RplCrc *>(section.data.data());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!crcs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto sectionIndex = 0u;
|
||||
for (const auto §ion : rpl.sections) {
|
||||
auto crc = uint32_t { 0u };
|
||||
if (section.header.type != elf::SHT_RPL_CRCS &&
|
||||
section.data.size()) {
|
||||
crc = crc32(0, Z_NULL, 0);
|
||||
crc = crc32(crc, reinterpret_cast<const Bytef *>(section.data.data()), section.data.size());
|
||||
}
|
||||
|
||||
if (crc != crcs[sectionIndex].crc) {
|
||||
fmt::print("Unexpected crc for section {}, read 0x{:08X} but calculated 0x{:08X}",
|
||||
sectionIndex, crcs[sectionIndex].crc, crc);
|
||||
result = false;
|
||||
}
|
||||
|
||||
sectionIndex++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Equivalent to loader.elf LiCheckFileBounds
|
||||
*/
|
||||
bool
|
||||
verifyFileBounds(const Rpl &rpl)
|
||||
{
|
||||
auto result = true;
|
||||
auto dataMin = 0xFFFFFFFFu;
|
||||
auto dataMax = 0u;
|
||||
|
||||
auto readMin = 0xFFFFFFFFu;
|
||||
auto readMax = 0u;
|
||||
|
||||
auto textMin = 0xFFFFFFFFu;
|
||||
auto textMax = 0u;
|
||||
|
||||
auto tempMin = 0xFFFFFFFFu;
|
||||
auto tempMax = 0u;
|
||||
|
||||
for (const auto §ion : rpl.sections) {
|
||||
if (section.header.size == 0 ||
|
||||
section.header.type == elf::SHT_RPL_FILEINFO ||
|
||||
section.header.type == elf::SHT_RPL_CRCS ||
|
||||
section.header.type == elf::SHT_NOBITS ||
|
||||
section.header.type == elf::SHT_RPL_IMPORTS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((section.header.flags & elf::SHF_EXECINSTR) &&
|
||||
section.header.type != elf::SHT_RPL_EXPORTS) {
|
||||
textMin = std::min<uint32_t>(textMin, section.header.offset);
|
||||
textMax = std::max<uint32_t>(textMax, section.header.offset + section.header.size);
|
||||
} else {
|
||||
if (section.header.flags & elf::SHF_ALLOC) {
|
||||
if (section.header.flags & elf::SHF_WRITE) {
|
||||
dataMin = std::min<uint32_t>(dataMin, section.header.offset);
|
||||
dataMax = std::max<uint32_t>(dataMax, section.header.offset + section.header.size);
|
||||
} else {
|
||||
readMin = std::min<uint32_t>(readMin, section.header.offset);
|
||||
readMax = std::max<uint32_t>(readMax, section.header.offset + section.header.size);
|
||||
}
|
||||
} else {
|
||||
tempMin = std::min<uint32_t>(tempMin, section.header.offset);
|
||||
tempMax = std::max<uint32_t>(tempMax, section.header.offset + section.header.size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dataMin == 0xFFFFFFFFu) {
|
||||
dataMin = (rpl.header.shnum * rpl.header.shentsize) + rpl.header.shoff;
|
||||
dataMax = dataMin;
|
||||
}
|
||||
|
||||
if (readMin == 0xFFFFFFFFu) {
|
||||
readMin = dataMax;
|
||||
readMax = dataMax;
|
||||
}
|
||||
|
||||
if (textMin == 0xFFFFFFFFu) {
|
||||
textMin = readMax;
|
||||
textMax = readMax;
|
||||
}
|
||||
|
||||
if (tempMin == 0xFFFFFFFFu) {
|
||||
tempMin = textMax;
|
||||
tempMax = textMax;
|
||||
}
|
||||
|
||||
if (dataMin < rpl.header.shoff) {
|
||||
fmt::print("*** SecHrs, FileInfo, or CRCs in bad spot in file. Return %d.\n", -470026);
|
||||
result = false;
|
||||
}
|
||||
|
||||
// Data
|
||||
if (dataMin > dataMax) {
|
||||
fmt::print("*** DataMin > DataMax. break.\n");
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (dataMin > readMin) {
|
||||
fmt::print("*** DataMin > ReadMin. break.\n");
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (dataMax > readMin) {
|
||||
fmt::print("*** DataMax > ReadMin, break.\n");
|
||||
result = false;
|
||||
}
|
||||
|
||||
// Read
|
||||
if (readMin > readMax) {
|
||||
fmt::print("*** ReadMin > ReadMax. break.\n");
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (readMin > textMin) {
|
||||
fmt::print("*** ReadMin > TextMin. break.\n");
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (readMax > textMin) {
|
||||
fmt::print("*** ReadMax > TextMin. break.\n");
|
||||
result = false;
|
||||
}
|
||||
|
||||
// Text
|
||||
if (textMin > textMax) {
|
||||
fmt::print("*** TextMin > TextMax. break.\n");
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (textMin > tempMin) {
|
||||
fmt::print("*** TextMin > TempMin. break.\n");
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (textMax > tempMin) {
|
||||
fmt::print("*** TextMax > TempMin. break.\n");
|
||||
result = false;
|
||||
}
|
||||
|
||||
// Temp
|
||||
if (tempMin > tempMax) {
|
||||
fmt::print("*** TempMin > TempMax. break.\n");
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
fmt::print("dataMin = 0x{:08X}\n", dataMin);
|
||||
fmt::print("dataMax = 0x{:08X}\n", dataMax);
|
||||
fmt::print("readMin = 0x{:08X}\n", readMin);
|
||||
fmt::print("readMax = 0x{:08X}\n", readMax);
|
||||
fmt::print("textMin = 0x{:08X}\n", textMin);
|
||||
fmt::print("textMax = 0x{:08X}\n", textMax);
|
||||
fmt::print("tempMin = 0x{:08X}\n", tempMin);
|
||||
fmt::print("tempMax = 0x{:08X}\n", tempMax);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check that the rpl only uses relocation types which are supported by
|
||||
* loader.elf
|
||||
*/
|
||||
bool
|
||||
verifyRelocationTypes(const Rpl &rpl)
|
||||
{
|
||||
std::set<unsigned int> unsupportedTypes;
|
||||
|
||||
for (auto §ion : rpl.sections) {
|
||||
if (section.header.type != elf::SHT_RELA) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto &symbolSection = rpl.sections[section.header.link];
|
||||
auto &targetSection = rpl.sections[section.header.info];
|
||||
auto rels = reinterpret_cast<const elf::Rela *>(section.data.data());
|
||||
auto numRels = section.data.size() / sizeof(elf::Rela);
|
||||
|
||||
for (auto i = 0u; i < numRels; ++i) {
|
||||
auto info = rels[i].info;
|
||||
auto addend = rels[i].addend;
|
||||
auto offset = rels[i].offset;
|
||||
auto index = info >> 8;
|
||||
auto type = info & 0xFF;
|
||||
|
||||
switch (type) {
|
||||
case elf::R_PPC_NONE:
|
||||
case elf::R_PPC_ADDR32:
|
||||
case elf::R_PPC_ADDR16_LO:
|
||||
case elf::R_PPC_ADDR16_HI:
|
||||
case elf::R_PPC_ADDR16_HA:
|
||||
case elf::R_PPC_REL24:
|
||||
case elf::R_PPC_REL14:
|
||||
case elf::R_PPC_DTPMOD32:
|
||||
case elf::R_PPC_DTPREL32:
|
||||
case elf::R_PPC_EMB_SDA21:
|
||||
case elf::R_PPC_EMB_RELSDA:
|
||||
case elf::R_PPC_DIAB_SDA21_LO:
|
||||
case elf::R_PPC_DIAB_SDA21_HI:
|
||||
case elf::R_PPC_DIAB_SDA21_HA:
|
||||
case elf::R_PPC_DIAB_RELSDA_LO:
|
||||
case elf::R_PPC_DIAB_RELSDA_HI:
|
||||
case elf::R_PPC_DIAB_RELSDA_HA:
|
||||
case elf::R_PPC_GHS_REL16_HA:
|
||||
case elf::R_PPC_GHS_REL16_HI:
|
||||
case elf::R_PPC_GHS_REL16_LO:
|
||||
// All valid relocations on Wii U, do nothing
|
||||
break;
|
||||
default:
|
||||
// Only print error once per type
|
||||
if (!unsupportedTypes.count(type)) {
|
||||
fmt::print("Unsupported relocation type {}\n", type);
|
||||
unsupportedTypes.insert(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return unsupportedTypes.empty();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verify that section.addr is aligned by section.addralign
|
||||
*/
|
||||
bool
|
||||
verifySectionAlignment(const Rpl &rpl)
|
||||
{
|
||||
auto result = true;
|
||||
for (auto §ion : rpl.sections) {
|
||||
if (!align_check(section.header.addr, section.header.addralign)) {
|
||||
fmt::print("Unaligned section {}, addr {}, addralign {}",
|
||||
getSectionIndex(rpl, section),
|
||||
section.header.addr,
|
||||
section.header.addralign);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
verifySectionOrder(const Rpl &rpl)
|
||||
{
|
||||
auto lastSection = rpl.sections[rpl.header.shnum - 1];
|
||||
auto penultimateSection = rpl.sections[rpl.header.shnum - 2];
|
||||
|
||||
|
||||
if (lastSection.header.type != elf::SHT_RPL_FILEINFO ||
|
||||
(lastSection.header.flags & elf::SHF_DEFLATED)) {
|
||||
fmt::print("***shnum-1 section type = 0x{:08X}, flags=0x{:08X}\n",
|
||||
lastSection.header.type,
|
||||
lastSection.header.flags);
|
||||
}
|
||||
|
||||
if (penultimateSection.header.type != elf::SHT_RPL_CRCS ||
|
||||
(penultimateSection.header.flags & elf::SHF_DEFLATED)) {
|
||||
fmt::print("***shnum-2 section type = 0x{:08X}, flags=0x{:08X}\n",
|
||||
penultimateSection.header.type,
|
||||
penultimateSection.header.flags);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
#include "readrpl.h"
|
||||
|
||||
bool
|
||||
verifyFile(const Rpl &rpl);
|
||||
|
||||
bool
|
||||
verifyCrcs(const Rpl &rpl);
|
||||
|
||||
bool
|
||||
verifyFileBounds(const Rpl &rpl);
|
||||
|
||||
bool
|
||||
verifyRelocationTypes(const Rpl &rpl);
|
||||
|
||||
bool
|
||||
verifySectionAlignment(const Rpl &rpl);
|
||||
|
||||
bool
|
||||
verifySectionOrder(const Rpl &rpl);
|
@ -1,9 +0,0 @@
|
||||
project(rplexportgen)
|
||||
|
||||
add_executable(rplexportgen
|
||||
rplexportgen.cpp)
|
||||
|
||||
target_link_libraries(rplexportgen
|
||||
zlibstatic)
|
||||
|
||||
install(TARGETS rplexportgen RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
|
@ -1,170 +0,0 @@
|
||||
#include "utils.h"
|
||||
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <fstream>
|
||||
#include <locale>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <zlib.h>
|
||||
|
||||
/*
|
||||
.extern __preinit_user
|
||||
|
||||
.section .fexports, "", @0x80000001
|
||||
.align 4
|
||||
|
||||
.long 1
|
||||
.long 0x13371337
|
||||
|
||||
.long __preinit_user
|
||||
.long 0x10
|
||||
|
||||
.string "__preinit_user"
|
||||
.byte 0
|
||||
*/
|
||||
|
||||
enum class ReadMode
|
||||
{
|
||||
INVALID,
|
||||
TEXT,
|
||||
DATA
|
||||
};
|
||||
|
||||
void
|
||||
writeExports(std::ofstream &out,
|
||||
bool isData,
|
||||
const std::vector<std::string> &exports)
|
||||
{
|
||||
// Calculate signature
|
||||
uint32_t signature = crc32(0, Z_NULL, 0);
|
||||
for (const auto &name : exports) {
|
||||
signature = crc32(signature, reinterpret_cast<const Bytef *>(name.data()), name.size() + 1);
|
||||
}
|
||||
|
||||
// Write out .extern to declare the symbols
|
||||
for (const auto &name : exports) {
|
||||
out << ".extern " << name << std::endl;
|
||||
}
|
||||
out << std::endl;
|
||||
|
||||
// Write out header
|
||||
if (isData) {
|
||||
out << ".section .dexports, \"a\", @0x80000001" << std::endl;
|
||||
} else {
|
||||
out << ".section .fexports, \"ax\", @0x80000001" << std::endl;
|
||||
}
|
||||
|
||||
out << ".align 4" << std::endl;
|
||||
out << std::endl;
|
||||
|
||||
out << ".long " << exports.size() << std::endl;
|
||||
out << ".long 0x" << std::hex << signature << std::endl;
|
||||
out << std::endl;
|
||||
|
||||
// Write out each export
|
||||
auto nameOffset = 8 + 8 * exports.size();
|
||||
for (const auto &name : exports) {
|
||||
out << ".long " << name << std::endl;
|
||||
out << ".long 0x" << std::hex << nameOffset << std::endl;
|
||||
nameOffset += name.size() + 1;
|
||||
}
|
||||
out << std::endl;
|
||||
|
||||
// Write out the strings
|
||||
for (const auto &name : exports) {
|
||||
out << ".string \"" << name << "\"" << std::endl;
|
||||
out << ".byte 0" << std::endl;
|
||||
nameOffset += name.size() + 1;
|
||||
}
|
||||
out << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
std::vector<std::string> funcExports, dataExports;
|
||||
ReadMode readMode = ReadMode::INVALID;
|
||||
|
||||
if (argc < 3) {
|
||||
std::cout << argv[0] << " <exports.def> <output.S>" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
{
|
||||
std::ifstream in;
|
||||
in.open(argv[1]);
|
||||
|
||||
if (!in.is_open()) {
|
||||
std::cout << "Could not open file " << argv[1] << " for reading" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
while (std::getline(in, line)) {
|
||||
// Trim comments
|
||||
std::size_t commentOffset = line.find("//");
|
||||
if (commentOffset != std::string::npos) {
|
||||
line = line.substr(0, commentOffset);
|
||||
}
|
||||
|
||||
// Trim whitespace
|
||||
line = trim(line);
|
||||
|
||||
// Skip blank lines
|
||||
if (line.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Look for section headers
|
||||
if (line[0] == ':') {
|
||||
if (line.substr(1) == "TEXT") {
|
||||
readMode = ReadMode::TEXT;
|
||||
} else if (line.substr(1) == "DATA") {
|
||||
readMode = ReadMode::DATA;
|
||||
} else {
|
||||
std::cout << "Unexpected section type" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (readMode == ReadMode::TEXT) {
|
||||
funcExports.push_back(line);
|
||||
} else if (readMode == ReadMode::DATA) {
|
||||
dataExports.push_back(line);
|
||||
} else {
|
||||
std::cout << "Unexpected section data" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exports must be in alphabetical order because loader.elf uses binary search
|
||||
std::sort(funcExports.begin(), funcExports.end());
|
||||
std::sort(dataExports.begin(), dataExports.end());
|
||||
|
||||
{
|
||||
std::ofstream out;
|
||||
out.open(argv[2]);
|
||||
|
||||
if (!out.is_open()) {
|
||||
std::cout << "Could not open file " << argv[2] << " for writing" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (funcExports.size() > 0) {
|
||||
writeExports(out, false, funcExports);
|
||||
}
|
||||
|
||||
if (dataExports.size() > 0) {
|
||||
writeExports(out, true, dataExports);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
project(rplimportgen)
|
||||
|
||||
add_executable(rplimportgen
|
||||
rplimportgen.cpp)
|
||||
|
||||
target_link_libraries(rplimportgen
|
||||
zlibstatic)
|
||||
|
||||
install(TARGETS rplimportgen RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
|
@ -1,166 +0,0 @@
|
||||
#include "utils.h"
|
||||
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <fstream>
|
||||
#include <locale>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <zlib.h>
|
||||
|
||||
enum class ReadMode
|
||||
{
|
||||
INVALID,
|
||||
TEXT,
|
||||
DATA
|
||||
};
|
||||
|
||||
void
|
||||
writeExports(std::ofstream &out,
|
||||
const std::string &moduleName,
|
||||
bool isData,
|
||||
const std::vector<std::string> &exports)
|
||||
{
|
||||
// Align module name up to 8 bytes
|
||||
auto moduleNameSize = (moduleName.length() + 1 + 7) & ~7;
|
||||
|
||||
// Calculate the data block size
|
||||
auto exportSecSize = exports.size() * 8;
|
||||
|
||||
if (exportSecSize < moduleNameSize) {
|
||||
exportSecSize = moduleNameSize;
|
||||
}
|
||||
|
||||
// Calculate export hash
|
||||
uint32_t exportsHash = crc32(0, Z_NULL, 0);
|
||||
|
||||
for (auto &exp : exports) {
|
||||
exportsHash = crc32(exportsHash, reinterpret_cast<const Bytef *>(exp.data()), exp.size() + 1);
|
||||
}
|
||||
|
||||
std::array<Bytef, 0xE> extraHashBytes;
|
||||
extraHashBytes.fill(0);
|
||||
exportsHash = crc32(exportsHash, extraHashBytes.data(), extraHashBytes.size());
|
||||
|
||||
// Setup section data
|
||||
std::vector<uint32_t> secData;
|
||||
secData.resize(exportSecSize / 4, 0);
|
||||
memcpy(secData.data(), moduleName.c_str(), moduleName.length());
|
||||
|
||||
out << std::endl;
|
||||
|
||||
if (isData) {
|
||||
out << ".section .dimport_" << moduleName << ", \"a\", @0x80000002" << std::endl;
|
||||
} else {
|
||||
out << ".section .fimport_" << moduleName << ", \"ax\", @0x80000002" << std::endl;
|
||||
}
|
||||
|
||||
out << ".align 4" << std::endl;
|
||||
out << std::endl;
|
||||
|
||||
out << ".long " << exports.size() << std::endl;
|
||||
out << ".long 0x" << std::hex << exportsHash << std::endl;
|
||||
out << std::endl;
|
||||
|
||||
const char *type = isData ? "@object" : "@function";
|
||||
|
||||
for (auto i = 0; i < exportSecSize / 8; ++i) {
|
||||
if (i < exports.size()) {
|
||||
out << ".global " << exports[i] << std::endl;
|
||||
out << ".type " << exports[i] << ", " << type << std::endl;
|
||||
out << exports[i] << ":" << std::endl;
|
||||
}
|
||||
|
||||
out << ".long 0x" << std::hex << byte_swap(secData[i * 2 + 0]) << std::endl;
|
||||
out << ".long 0x" << std::hex << byte_swap(secData[i * 2 + 1]) << std::endl;
|
||||
out << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
std::string moduleName;
|
||||
std::vector<std::string> funcExports, dataExports;
|
||||
ReadMode readMode = ReadMode::INVALID;
|
||||
|
||||
if (argc < 3) {
|
||||
std::cout << argv[0] << " <exports.def> <output.S>" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
{
|
||||
std::ifstream in;
|
||||
in.open(argv[1]);
|
||||
|
||||
if (!in.is_open()) {
|
||||
std::cout << "Could not open file " << argv[1] << " for reading" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
while (std::getline(in, line)) {
|
||||
// Trim comments
|
||||
std::size_t commentOffset = line.find("//");
|
||||
if (commentOffset != std::string::npos) {
|
||||
line = line.substr(0, commentOffset);
|
||||
}
|
||||
|
||||
// Trim whitespace
|
||||
line = trim(line);
|
||||
|
||||
// Skip blank lines
|
||||
if (line.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Look for section headers
|
||||
if (line[0] == ':') {
|
||||
if (line.substr(1) == "TEXT") {
|
||||
readMode = ReadMode::TEXT;
|
||||
} else if (line.substr(1) == "DATA") {
|
||||
readMode = ReadMode::DATA;
|
||||
} else if (line.substr(1, 4) == "NAME") {
|
||||
moduleName = line.substr(6);
|
||||
} else {
|
||||
std::cout << "Unexpected section type" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (readMode == ReadMode::TEXT) {
|
||||
funcExports.push_back(line);
|
||||
} else if (readMode == ReadMode::DATA) {
|
||||
dataExports.push_back(line);
|
||||
} else {
|
||||
std::cout << "Unexpected section data" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::ofstream out;
|
||||
out.open(argv[2]);
|
||||
|
||||
if (!out.is_open()) {
|
||||
std::cout << "Could not open file " << argv[2] << " for writing" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (funcExports.size() > 0) {
|
||||
writeExports(out, moduleName, false, funcExports);
|
||||
}
|
||||
|
||||
if (dataExports.size() > 0) {
|
||||
writeExports(out, moduleName, true, dataExports);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
project(udplogserver)
|
||||
|
||||
add_executable(udplogserver
|
||||
main.cpp)
|
||||
|
||||
if(MSVC)
|
||||
target_link_libraries(udplogserver PRIVATE ws2_32)
|
||||
endif()
|
||||
|
||||
install(TARGETS udplogserver RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
|
@ -1,106 +0,0 @@
|
||||
#ifdef _WIN32
|
||||
#include <WinSock2.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#define SERVER_PORT 4405
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
unsigned short port = SERVER_PORT;
|
||||
|
||||
if (argc == 2) {
|
||||
port = atoi(argv[1]);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsaData) == SOCKET_ERROR) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Create socket
|
||||
auto fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
#ifdef _WIN32
|
||||
if (fd == INVALID_SOCKET) {
|
||||
WSACleanup();
|
||||
#else
|
||||
if (fd < 0) {
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set non blocking
|
||||
#ifdef _WIN32
|
||||
u_long mode = 1;
|
||||
ioctlsocket(fd, FIONBIO, &mode);
|
||||
#else
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||
#endif
|
||||
|
||||
// Bind socket
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
addr.sin_port = htons(port);
|
||||
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
#ifdef _WIN32
|
||||
closesocket(fd);
|
||||
WSACleanup();
|
||||
#else
|
||||
close(fd);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Receive data
|
||||
char buffer[2048];
|
||||
bool running = true;
|
||||
|
||||
while (running) {
|
||||
fd_set fdsRead;
|
||||
FD_ZERO(&fdsRead);
|
||||
FD_SET(fd, &fdsRead);
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 10000;
|
||||
|
||||
if (select(fd + 1, &fdsRead, NULL, NULL, &tv) == 1) {
|
||||
struct sockaddr_in from;
|
||||
#ifdef _WIN32
|
||||
int fromLen = sizeof(from);
|
||||
#else
|
||||
socklen_t fromLen = sizeof(from);
|
||||
#endif
|
||||
int recvd = recvfrom(fd, buffer, sizeof(buffer), 0, (struct sockaddr *) &from, &fromLen);
|
||||
|
||||
if (recvd > 0) {
|
||||
buffer[recvd] = 0;
|
||||
std::cout << buffer;
|
||||
std::cout.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
closesocket(fd);
|
||||
WSACleanup();
|
||||
#else
|
||||
close(fd);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user