lcpu: officially support legacy 32bit branch

32-bit builds using the development headers work on the legacy 32-bit branch so I don't really see a reason to not support it..

sure, it sucks that I now won't be able to get rid of a pretty gnarly codepath, but /shrug
This commit is contained in:
Lily Tsuru 2023-07-29 16:59:51 -04:00
parent 6eada83eeb
commit 7ae6acfc3b
14 changed files with 66 additions and 324 deletions

View File

@ -9,7 +9,7 @@ It provides:
(that and the more reasons to not use Lua for a part of this codebase, the better...)
- Interoperation with the Wiremod addon (and addons which implement Wiremod integration)
This addon (for now) works with both the non-beta branch and the x86-64 beta branches of GMod.
This addon works with both the non-beta branch and the x86-64 beta branches of GMod, however rigorous testing is usually only done with the x86-64 beta branch.
Note that the GitHub mirror is provided only for convinence, and is not used for active development.
See [this link](https://git.crustywindo.ws/modeco80/gmod-lcpu) for the actual development repository.
@ -20,28 +20,15 @@ You will need Wiremod installed, either from the Workshop or cloned as a filesys
This repository is set up to be a Filesystem Addon; therefore, workflows which clone repositories from Git and put them in addons/ should be able to work with the LCPU addon just fine.
Preliminary installation steps (by hand):
Preliminary installation steps (for Linux):
```
garrysmod/addons$ git clone --recursive https://git.crustywindo.ws/modeco80/gmod-lcpu.git lcpu
garrysmod/addons$ cd lcpu
# Build the LCPU native module. These steps build the linux64 version of the module
# on linux; you'll need to alter it if you want to build for 32-bit.
garrysmod/addons/lcpu$ cmake -Wno-dev -GNinja -S native -B build \
-DCMAKE_BUILD_TYPE=Release
garrysmod/addons/lcpu$ ninja -C build
# Install the native module (Linux)
# For Windows you can do the same thing, just replace this
# with how you'd do it in batch (or use Explorer, I'm not your dad)
garrysmod/addons/lcpu$ [[ ! -d '../../lua/bin']] && mkdir -p ../../lua/bin && cp build/projects/lcpu/*.dll ../../lua/bin
garrysmod/addons/lcpu$ ./build_module.sh
# Tada!
```
On Linux you can alternatively use the `./build_module.sh` script that will do all the build and installation steps automatically, once cloning the repository in the garrysmod/addons folder.
Windows building is currently untested; I see no reason why it wouldn't work.

View File

@ -1,6 +1,6 @@
#!/bin/bash
# Build the LCPU addon for the reccomended environment
# Build the LCPU native module for both linux32 and linux64
# and install it into the proper directory gmod wants native modules to be.
set -x
@ -8,11 +8,38 @@ set -x
# where your game server is
GS_PATH="/home/lily/gs/gmod"
cmake -Wno-dev -GNinja -S native -B module_build -DCMAKE_BUILD_TYPE=Release
ninja -C module_build
[[ ! -d "$GS_PATH/garrysmod/lua/bin" ]] && {
mkdir -p $GS_PATH/garrysmod/lua/bin
# make the module build directory
[[ ! -d "module_build" ]] && {
mkdir module_build
}
cp -v module_build/projects/lcpu/*.dll $GS_PATH/garrysmod/lua/bin
# $1 cmake build dir
# $@ any other args to cmake
cmake_gen() {
local BD=$1
shift
cmake -Wno-dev -GNinja -S native -B module_build/$BD $@ -DCMAKE_BUILD_TYPE=Release
}
# $1 cmake build dir
cmake_build() {
ninja -C module_build/$1
}
build_and_place() {
cmake_gen linux32 --toolchain $PWD/native/cmake/linux32-toolchain.cmake
cmake_gen linux64
cmake_build linux32
cmake_build linux64
# GMod doesn't actually make the lua/bin directory on its own
# so we have to check if it exists first and make if it doesn't
[[ ! -d "$GS_PATH/garrysmod/lua/bin" ]] && {
mkdir -p $GS_PATH/garrysmod/lua/bin
}
cp -v module_build/linux32/projects/lcpu/*.dll $GS_PATH/garrysmod/lua/bin
cp -v module_build/linux64/projects/lcpu/*.dll $GS_PATH/garrysmod/lua/bin
}
build_and_place

View File

@ -9,8 +9,6 @@ add_subdirectory(projects/lucore)
# RISC-V emulator library
add_subdirectory(projects/riscv)
add_subdirectory(projects/riscv_test_harness)
# Garry's Mod native bindings to RISC-V emulator
# Also lua device stuff
add_subdirectory(projects/lcpu)

View File

@ -0,0 +1,23 @@
# toolchain file for linux multilib building
set(CMAKE_C_FLAGS "-m32" CACHE STRING "C compiler flags" FORCE)
set(CMAKE_CXX_FLAGS "-m32" CACHE STRING "C++ compiler flags" FORCE)
if(EXISTS /usr/lib32)
set(LIB32 /usr/lib32)
else()
set(LIB32 /usr/lib)
endif()
set(CMAKE_SYSTEM_LIBRARY_PATH ${LIB32} CACHE STRING "system library search path" FORCE)
set(CMAKE_LIBRARY_PATH ${LIB32} CACHE STRING "library search path" FORCE)
# this is probably unlikely to be needed, but just in case
set(CMAKE_EXE_LINKER_FLAGS "-m32 -L${LIB32}" CACHE STRING "executable linker flags" FORCE)
set(CMAKE_SHARED_LINKER_FLAGS "-m32 -L${LIB32}" CACHE STRING "shared library linker flags" FORCE)
set(CMAKE_MODULE_LINKER_FLAGS "-m32 -L${LIB32}" CACHE STRING "module linker flags" FORCE)
# point pkg-config to lib32
if(EXISTS ${LIB32}/pkgconfig)
set(ENV{PKG_CONFIG_LIBDIR} ${LIB32}/pkgconfig:/usr/share/pkgconfig:/usr/lib/pkgconfig:/usr/lib64/pkgconfig)
endif()

View File

@ -1,15 +1,9 @@
#include "SourceSink.hpp"
#include <cstdlib>
#include <lucore/Assert.hpp>
#include <lucore/Library.hpp>
#include <lucore/Types.hpp>
// The old non-beta branch of GMod on Linux has multiple tier0 libraries for client and server.
// This compatibility define allows to support that case (for now). Once this define is removed,
// the old codepath can be totally removed.
#define LCPU_SUPPORT_OLD_GMOD
namespace tier0 {
lucore::Library* library = nullptr;
@ -17,11 +11,10 @@ namespace tier0 {
Msg_t Msg {};
bool OpenLibrary() {
#ifdef LCPU_SUPPORT_OLD_GMOD
constexpr static std::string_view tier0_libraries[] {
#ifdef __linux__
#ifdef __linux__
"tier0_srv",
#endif
#endif
"tier0"
};
@ -32,12 +25,6 @@ namespace tier0 {
break;
}
}
#else
// The x86-64 branch of GMod, including the 32-bit binaries in the branch,
// have a single tier0 library, which makes the codepath much simpler.
// Hopefully I can switch to this path at some point.
tier0::library = lucore::Library::OpenExisting("tier0");
#endif
if(tier0::library == nullptr)
return false;
@ -65,14 +52,14 @@ namespace lcpu {
SourceSink::SourceSink() {
if(!tier0::OpenLibrary()) {
std::printf("Tier0 could not be opened\n");
std::printf("Tier0 could not be opened - aborting\n");
std::quick_exit(10);
}
// TODO: A bit nicer of an error message?
// Explain *what* to do if you see this message.
if(!tier0::GrabSymbols()) {
std::printf("Tier0 symbols could not be grabbed\n");
std::printf("Tier0 symbols could not be grabbed - aborting\n");
std::quick_exit(10);
}
}

View File

@ -14,7 +14,7 @@ namespace lucore::detail {
}
void* OsLibrarySymbol(OsLibraryHandle dll, const char* symbolName) {
return GetProcAddressA(reinterpret_cast<HMODULE*>(dll), symbolName);
return GetProcAddressA(reinterpret_cast<HMODULE>(dll), symbolName);
}
void OsFreeLibrary(OsLibraryHandle handle) {

View File

@ -12,4 +12,4 @@ Depends on lucore.
# Usage
TBD (if this is moved to another repo). See the riscv_test_harness project.
TBD (if this is moved to another repo). See the lcpu project

View File

@ -1,7 +0,0 @@
add_executable(rvtest
main.cpp
)
target_link_libraries(rvtest PUBLIC
riscv::riscv
)

View File

@ -1,69 +0,0 @@
//! A test harness for testing if the riscv library actually works.
#include <cstdio> // I know, I know, but this is a test program. Yell later :)
#include <lucore/Assert.hpp>
#include <lucore/Logger.hpp>
#include <lucore/StdoutSink.hpp>
#include <riscv/System.hpp>
#include <thread>
/// simple 16550 UART implementation
struct SimpleUartDevice : public riscv::Bus::MmioDevice {
constexpr static riscv::Address BASE_ADDRESS = 0x10000000;
riscv::Address Base() const override { return BASE_ADDRESS; }
riscv::Address Size() const override { return 12; } // for now
u32 Peek(riscv::Address address) override {
switch(address) {
case BASE_ADDRESS: return '\0'; // just return 0 for the input register
case BASE_ADDRESS + 5: return 0x60; // active, but no keyboard input
}
return 0;
}
void Poke(riscv::Address address, u32 value) override {
if(address == BASE_ADDRESS) {
char c = value & 0x000000ff;
fputc(c, stderr);
}
}
};
int main(int argc, char** argv) {
lucore::LoggerAttachStdout();
LUCORE_CHECK(argc == 2, "this test harness expects one argument (the file to load into riscv memory and execute). got {} arguments", argc);
// 128 KB of ram. Won't be enough to boot linux but should be good enough to test most baremetal apps
auto system = riscv::System::Create(128 * 1024);
LUCORE_CHECK(system, "could not create system for some reason.");
// Attach our UART device
system->bus->AttachDevice(new SimpleUartDevice);
auto fp = std::fopen(argv[1], "rb");
LUCORE_CHECK(fp, "could not open file \"{}\"", argv[1]);
std::fseek(fp, 0, SEEK_END);
auto len = std::ftell(fp);
std::fseek(fp, 0, SEEK_SET);
std::fread(system->ram->Raw(), 1, len, fp);
std::fclose(fp);
// This allows the host program running under the test
// harness to tell us to shut down.
bool shouldExit = false;
system->OnPowerOff = [&shouldExit]() { shouldExit = true; };
while(!shouldExit) {
system->Step();
//std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
delete system;
return 0;
}

View File

@ -1,53 +0,0 @@
PROJECT = test
# where your rv32 toolchain is
TCPATH = /home/lily/bin/riscv/bin
PREFIX = $(TCPATH)/riscv32-unknown-elf
CC = $(PREFIX)-gcc
CXX = $(PREFIX)-g++
ARCHFLAGS = -ffreestanding -fno-stack-protector -fdata-sections -ffunction-sections -march=rv32ima -mabi=ilp32
CCFLAGS = -g -Os $(ARCHFLAGS) -std=c18
CXXFLAGS = $(ARCHFLAGS) -g -Os -std=c++20 -fno-exceptions -fno-rtti
LDFLAGS = -T binary.ld -nostdlib -Wl,--gc-sections
OBJS = start.o \
main.o
.PHONY: all test clean
all: $(PROJECT).bin $(PROJECT).debug.txt
# this assumes the lcpu project build dir you're using is
# [lcpu repo root]/build
test: $(PROJECT).bin $(PROJECT).debug.txt
../../../../build/projects/riscv_test_harness/rvtest $<
clean:
rm $(PROJECT).elf $(PROJECT).bin $(PROJECT).debug.txt $(OBJS)
# Link rules
$(PROJECT).elf: $(OBJS)
$(CC) $(CCFLAGS) $(LDFLAGS) -o $@ $(OBJS)
$(PROJECT).bin : $(PROJECT).elf
$(PREFIX)-objcopy $^ -O binary $@
$(PROJECT).debug.txt : $(PROJECT).elf
$(PREFIX)-objdump -t $^ > $@
$(PREFIX)-objdump -S $^ >> $@
# Compile rules
%.o: %.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
%.o: %.c
$(CC) -c $(CCFLAGS) $< -o $@
%.o: %.S
$(CC) -x assembler-with-cpp -march=rv32ima -mabi=ilp32 -c $< -o $@

View File

@ -1,5 +0,0 @@
# what
This is a simple bare-metal riscv program that is able to run on the test harness that does some testing.
It is barebones, I know.

View File

@ -1,73 +0,0 @@
__heap_size = 0x1000;
__stack_size = 0x1000;
ENTRY(_start)
SECTIONS
{
. = 0x80000000;
.text : ALIGN(16) {
__TEXT_BEGIN__ = .;
*(.initial_jump)
*(.entry.text)
*(.init.literal)
*(.init)
*(.text)
*(.literal .text .literal.* .text.* .stub)
*(.out_jump.literal.*)
*(.out_jump.*)
__TEXT_END__ = .;
}
/* If we're on a newer compiler */
/DISCARD/ :
{
*(.interp)
*(.dynsym)
*(.dynstr)
*(.header)
} : phdr
.data : ALIGN(16) {
__DATA_BEGIN__ = .;
*(.rodata)
*(.rodata.*)
*(.gnu.linkonce.r.*)
*(.rodata1)
*(.dynsbss)
*(.gnu.linkonce.sb.*)
*(.scommon)
*(.gnu.linkonce.sb2.*)
*(.sbss)
*(.sbss.*)
*(.sbss2)
*(.sbss2.*)
*(.dynbss)
*(.data)
*(.data.*)
*(.got)
*(.got.*)
__DATA_END__ = .;
}
.bss : ALIGN( 16 ) {
__BSS_BEGIN__ = .;
*(.bss) /* Tricky: BSS needs to be allocated but not sent. GCC Will not populate these for calculating data size */
*(.bss.*)
__BSS_END__ = .;
}
.heap : ALIGN( 16 ) {
_sheap = .;
. = . + __heap_size;
_eheap = .;
}
.stack : ALIGN( 16 ) {
_estack = .;
. = . + __stack_size;
_sstack = .;
}
}

View File

@ -1,60 +0,0 @@
// a simple test program
#include <stdint.h>
uint32_t strlen(const char* str) {
if(!str)
return 0;
const char* c = str;
while(*c++)
;
return c - str;
}
#define SYSCON *(volatile uint32_t*)0x11100000
#define UART_BASE 0x10000000
#define UART_DATA *(volatile uint32_t*)UART_BASE
#define UART_STATUS UART_DATA
void putc(char c) {
UART_DATA = (uint32_t)c;
}
__attribute__((noinline)) void puts(const char* str) {
const uint32_t length = strlen(str);
for(uint32_t i = 0; i < length; ++i)
putc(str[i]);
}
static uint32_t value = 0;
static uint16_t shortvalue = 0;
static uint8_t bytevalue = 0;
#define COUNTER_TEST(var, max) \
for(int i = 0; i < max; ++i) { \
puts(#var " is (before modification): "); \
putc("0123456789"[var]); \
putc('\n'); \
\
var = i; \
puts(#var " is (after modification): "); \
putc("0123456789"[var]); \
putc('\n'); \
}
void main() {
puts("hello world I guess\n");
#if 1
COUNTER_TEST(value, 9);
COUNTER_TEST(shortvalue, 9);
COUNTER_TEST(bytevalue, 9);
#endif
// Shut down the test harness once we're done testing.
puts("Tests done, shutting down test harness...\n");
//SYSCON = 0x5555;
// loop forever
for(;;);
}

View File

@ -1,13 +0,0 @@
# Simple bare-metal RISCV startup code.
.section .initial_jump
.global _start
.extern main
.align 4
_start:
la sp, _sstack # set up C stack
addi sp,sp,-16 # ...
sw ra,12(sp) # ...
jal ra, main # jump to C code!