328 lines
13 KiB
C++
328 lines
13 KiB
C++
// SPDX-License-Identifier: MPL-2.0
|
|
#include <cstdint>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <filesystem>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <cstdlib>
|
|
#include <random>
|
|
#include <unistd.h>
|
|
#include <limits.h>
|
|
#include "elfio.hpp"
|
|
#include "elfio/elf_types.hpp"
|
|
using namespace std;
|
|
namespace fs=filesystem;
|
|
const string entry_flags="-ffreestanding -nostdlib -nostartfiles -Wall -Wextra -fno-stack-protector -fno-builtin -fno-pic -m64 -mcmodel=large";
|
|
struct vyx_addresses {
|
|
uint64_t text_offset;
|
|
uint64_t stack_base;
|
|
};
|
|
uint64_t rand_in_range(uint64_t min, uint64_t max) {
|
|
static random_device rd;
|
|
static mt19937_64 gen(rd());
|
|
std::uniform_int_distribution<uint64_t> dis(min, max);
|
|
return dis(gen);
|
|
}
|
|
uint64_t random_canonical(uint64_t start,uint64_t end) {
|
|
uint64_t addr=rand_in_range(start/4096,end/4096)*4096;
|
|
return addr;
|
|
}
|
|
vyx_addresses generate_vyx_addresses() {
|
|
vyx_addresses addr;
|
|
addr.text_offset=random_canonical(0xFFFF800000000000ULL,0xFFFF900000000000ULL-1);
|
|
uint64_t stack_window_start=0xFFFFF00000000000ULL;
|
|
uint64_t stack_window_end=0xFFFFFF8000000000ULL-0x100000ULL;
|
|
addr.stack_base=random_canonical(stack_window_start,stack_window_end);
|
|
return addr;
|
|
}
|
|
size_t pad_size(size_t value,size_t multiple) {
|
|
if (multiple==0) return value;
|
|
size_t remainder=value%multiple;
|
|
if (remainder==0) return value;
|
|
return value+multiple-remainder;
|
|
}
|
|
int main(int argc,char **argv) {
|
|
cout<<"[Vyld] Vystem Executable Linker v0.1"<<endl;
|
|
if (argc<3) {
|
|
cout<<"[Vyld] Error: Wrong number of arguments."<<endl;
|
|
return -1;
|
|
}
|
|
char buf[PATH_MAX];
|
|
ssize_t len=readlink("/proc/self/exe",buf,sizeof(buf)-1);
|
|
fs::path exe_path;
|
|
if (len!=-1) {
|
|
buf[len]='\0';
|
|
exe_path=fs::path(buf);
|
|
} else {
|
|
cout<<"[Vyld] Error: couldn't obtain vyld executable path."<<endl;
|
|
return -1;
|
|
}
|
|
exe_path=exe_path.parent_path();
|
|
fs::path res_file=exe_path/fs::path("res.txt");
|
|
fs::path combined_o=exe_path/fs::path("combined.o");
|
|
fs::path compiled_elf=exe_path/fs::path("compiled.elf");
|
|
fs::path script_ld=exe_path/fs::path("script.ld");
|
|
if (!fs::exists(exe_path/fs::path("_vyx_start.c"))) {
|
|
cout<<"[Vyld] Error: couldn't find entry point."<<endl;
|
|
return -1;
|
|
}
|
|
const char* flags_c=getenv("VYLD_COMPILATION_FLAGS");
|
|
string flags;
|
|
if (flags_c) {
|
|
flags=string(flags_c);
|
|
if (flags!="") {
|
|
cout<<"[Vyld] Using following flags: "<<flags<<endl;
|
|
} else {
|
|
cout<<"[Vyld] Warning: VYLD_COMPILATION_FLAGS is defined but empty."<<endl;
|
|
}
|
|
} else {
|
|
flags="";
|
|
cout<<"[Vyld] Warning: VYLD_COMPILATION_FLAGS isn't defined."<<endl;
|
|
}
|
|
cout<<"[Vyld] Compiling _vyx_start.c "<<endl;
|
|
fs::remove(exe_path/"_vyx_start.o");
|
|
int ret=system(string("gcc -c "+string(exe_path/"_vyx_start.c")+" -o "+string(exe_path/"_vyx_start.o")+" "+entry_flags+" > "+string(res_file)).c_str());
|
|
if (ret!=0) {
|
|
cout<<"[Vyld] Error: gcc didn't returned success. Error code: "<<ret<<endl;
|
|
int ret=system(string("cat "+string(res_file)).c_str());
|
|
return -1;
|
|
} else {
|
|
fs::remove(string(res_file));
|
|
}
|
|
vector<string> c_files(argv+1,argv+argc-1);
|
|
vector<string> o_files;
|
|
cout<<"[Vyld] Finding provided files:"<<endl;
|
|
for (int i=0;i<c_files.size();i++) {
|
|
if (!fs::exists(c_files[i])) {
|
|
cout<<"[Vyld] Error: can't found file \""<<c_files[i]<<"\"."<<endl;
|
|
return -1;
|
|
}
|
|
string filename=fs::path(c_files[i]).stem().string();
|
|
o_files.push_back(exe_path/fs::path(filename+".o"));
|
|
cout<<"[Vyld] Found \""<<fs::absolute(fs::path(c_files[i])).string()<<"\" that will be compile into \""<<o_files[i]<<"\" ("<<i+1<<"/"<<c_files.size()<<")."<<endl;
|
|
}
|
|
cout<<"[Vyld] Compiling provided files:"<<endl;
|
|
for (int i=0;i<c_files.size();i++) {
|
|
int ret=system(string("gcc -c "+c_files[i]+" -fno-pic -m64 -mcmodel=large "+flags+" -o "+o_files[i]+" > "+string(res_file)).c_str());
|
|
if (ret!=0) {
|
|
cout<<"[Vyld] Error: gcc didn't returned success. File \""<<c_files[i]<<"\" returned error code: "<<ret<<endl;
|
|
int ret=system(string("cat "+string(res_file)).c_str());
|
|
return -1;
|
|
} else {
|
|
fs::remove(string(res_file));
|
|
}
|
|
cout<<"[Vyld] Successfully compiled \""<<fs::absolute(fs::path(c_files[i])).string()<<"\" into \""<<o_files[i]<<"\" ("<<i+1<<"/"<<c_files.size()<<")."<<endl;
|
|
}
|
|
cout<<"[Vyld] Compiling all objects files into one...";
|
|
string command_line_gcc="gcc -r "+string(exe_path/"_vyx_start.o")+" ";
|
|
for (auto o:o_files) {
|
|
command_line_gcc+=o+" ";
|
|
}
|
|
command_line_gcc+="-o "+string(combined_o)+" > "+string(res_file);
|
|
ret=system(command_line_gcc.c_str());
|
|
if (ret!=0) {
|
|
cout<<endl<<"[Vyld] Error: gcc didn't returned success. Error code: "<<ret<<endl;
|
|
int ret=system(string("cat "+string(res_file)).c_str());
|
|
return -1;
|
|
}
|
|
cout<<" done."<<endl;
|
|
cout<<"[Vyld] Opening combined object files...";
|
|
ELFIO::elfio reader;
|
|
if (!reader.load(string(combined_o))) {
|
|
cout<<endl<<"[Vyld] Error: couldn't open combined.o."<<endl;
|
|
return -1;
|
|
}
|
|
if (reader.get_type()!=ELFIO::ET_REL) {
|
|
cout<<endl<<"[Vyld] Error: combined.o isn't a relocatable object file."<<endl;
|
|
return -1;
|
|
}
|
|
if (reader.get_class()!=ELFIO::ELFCLASS64) {
|
|
cout<<endl<<"[Vyld] Error: combined.o isn't a 64-bit ELF file."<<endl;
|
|
return -1;
|
|
}
|
|
if (reader.get_machine()!=ELFIO::EM_X86_64) {
|
|
cout<<endl<<"[Vyld] Error: combined.o isn't a x86_64 file."<<endl;
|
|
return -1;
|
|
}
|
|
cout<<" done."<<endl;
|
|
cout<<"[Vyld] Extracting important sections."<<endl;
|
|
const ELFIO::section* text_sec=reader.sections[".text"];
|
|
const ELFIO::section* data_sec=reader.sections[".data"];
|
|
const ELFIO::section* rodata_sec=reader.sections[".rodata"];
|
|
const ELFIO::section* bss_sec=reader.sections[".bss"];
|
|
if (!text_sec) {
|
|
cout<<endl<<"[Vyld] Error: no .text found."<<endl;
|
|
return -1;
|
|
}
|
|
if (!data_sec) {
|
|
cout<<"[Vyld] Warning: no .data found."<<endl;
|
|
}
|
|
if (!rodata_sec) {
|
|
cout<<"[Vyld] Warning: no .rodata found."<<endl;
|
|
}
|
|
if (!bss_sec) {
|
|
cout<<"[Vyld] Warning: no .bss found."<<endl;
|
|
}
|
|
if (text_sec->get_size()==0) {
|
|
cout<<endl<<"[Vyld] Error: .text is empty."<<endl;
|
|
return -1;
|
|
}
|
|
cout<<"[Vyld] Generating linker script:"<<endl;
|
|
size_t text_size=text_sec->get_size();
|
|
size_t text_size_padded=pad_size(text_size,4096);
|
|
cout<<"[Vyld] text_size=0x"<<hex<<text_size<<", text_size_padded=0x"<<hex<<text_size_padded<<endl;
|
|
size_t data_size=0;
|
|
size_t data_size_padded=0;
|
|
size_t rodata_size=0;
|
|
size_t rodata_size_padded=0;
|
|
size_t bss_size=0;
|
|
size_t bss_size_padded=0;
|
|
if (data_sec) {
|
|
data_size=data_sec->get_size();
|
|
data_size_padded=pad_size(data_size,4096);
|
|
cout<<"[Vyld] data_size=0x"<<hex<<data_size<<", data_size_padded=0x"<<hex<<data_size_padded<<endl;
|
|
}
|
|
if (rodata_sec) {
|
|
rodata_size=rodata_sec->get_size();
|
|
for (const auto &s:reader.sections) {
|
|
if (s->get_name().size()>7 && s->get_name().substr(0,7)==".rodata" && s->get_name()!=".rodata") {
|
|
rodata_size+=s->get_size();
|
|
}
|
|
}
|
|
rodata_size_padded=pad_size(rodata_size,4096);
|
|
cout<<"[Vyld] rodata_size=0x"<<hex<<rodata_size<<", rodata_size_padded=0x"<<hex<<rodata_size_padded<<endl;
|
|
}
|
|
if (bss_sec) {
|
|
bss_size=bss_sec->get_size();
|
|
bss_size_padded=pad_size(bss_size,4096);
|
|
cout<<"[Vyld] bss_size=0x"<<hex<<bss_size<<", bss_size_padded=0x"<<hex<<bss_size_padded<<endl;
|
|
}
|
|
vyx_addresses va=generate_vyx_addresses();
|
|
size_t text_offset=va.text_offset;
|
|
cout<<"[Vyld] Loading address (va.text_offset): 0x"<<hex<<text_offset<<endl;
|
|
cout<<"[Vyld] Stack address base (va.stack_base): 0x"<<hex<<va.stack_base<<endl;
|
|
size_t data_offset=text_offset+text_size_padded;
|
|
size_t rodata_offset=data_offset+data_size_padded;
|
|
size_t bss_offset=rodata_offset+rodata_size_padded;
|
|
ofstream ld(script_ld);
|
|
ld<<"ENTRY(_vyx_start)\n";
|
|
ld<<"MEMORY\n{\n";
|
|
ld<<" TEXT (rx) : ORIGIN = 0x"<<hex<<text_offset<<", LENGTH = "<<dec<<text_size_padded<<"\n";
|
|
ld<<" DATA (rw) : ORIGIN = 0x"<<hex<<data_offset<<", LENGTH = "<<dec<<data_size_padded<<"\n";
|
|
ld<<" RODATA (r) : ORIGIN = 0x"<<hex<<rodata_offset<<", LENGTH = "<<dec<<rodata_size_padded<<"\n";
|
|
ld<<" BSS (rw) : ORIGIN = 0x"<<hex<<bss_offset<<", LENGTH = "<<dec<<bss_size_padded<<"\n";
|
|
ld<<"}\nSECTIONS\n{\n";
|
|
ld<<" .text :\n";
|
|
ld<<" {\n";
|
|
ld<<" . = ORIGIN(TEXT);\n";
|
|
ld<<" *(.text)\n";
|
|
ld<<" *(.text.*)\n";
|
|
ld<<" . = ALIGN(0x1000);\n";
|
|
ld<<" } > TEXT\n\n";
|
|
ld<<" .data :\n";
|
|
ld<<" {\n";
|
|
ld<<" . = ORIGIN(DATA);\n";
|
|
ld<<" *(.data)\n";
|
|
ld<<" *(.data.*)\n";
|
|
ld<<" . = ALIGN(0x1000);\n";
|
|
ld<<" } > DATA\n\n";
|
|
ld<<" .rodata :\n";
|
|
ld<<" {\n";
|
|
ld<<" . = ORIGIN(RODATA);\n";
|
|
ld<<" *(.rodata)\n";
|
|
ld<<" *(.rodata.*)\n";
|
|
ld<<" . = ALIGN(0x1000);\n";
|
|
ld<<" } > RODATA\n\n";
|
|
ld<<" .bss :\n";
|
|
ld<<" {\n";
|
|
ld<<" . = ORIGIN(BSS);\n";
|
|
ld<<" *(.bss)\n";
|
|
ld<<" *(.bss.*)\n";
|
|
ld<<" . = ALIGN(0x1000);\n";
|
|
ld<<" } > BSS\n\n";
|
|
ld<<" /DISCARD/ :\n";
|
|
ld<<" {\n";
|
|
ld<<" *(.note.*)\n";
|
|
ld<<" *(.comment)\n";
|
|
ld<<" *(.eh_frame)\n";
|
|
ld<<" *(.eh_frame_hdr)\n";
|
|
ld<<" }\n}";
|
|
ld.close();
|
|
cout<<"[Vyld] Linking relocatable object."<<endl;
|
|
ret=system(string("ld -T "+string(script_ld)+" -o "+string(compiled_elf)+" "+string(combined_o)).c_str());
|
|
if (ret!=0) {
|
|
cout<<endl<<"[Vyld] Error: ld didn't returned success. Error code: "<<ret<<endl;
|
|
int ret=system(string("cat res.txt").c_str());
|
|
return -1;
|
|
}
|
|
cout<<"[Vyld] Opening compiled.elf."<<endl;
|
|
if (!reader.load(string(compiled_elf))) {
|
|
cout<<"[Vyld] Error: couldn't open compiled.elf."<<endl;
|
|
return -1;
|
|
}
|
|
if (reader.get_type()!=ELFIO::ET_EXEC) {
|
|
cout<<"[Vyld] Error: compiled.elf isn't a relocatable object file."<<endl;
|
|
return -1;
|
|
}
|
|
if (reader.get_class()!=ELFIO::ELFCLASS64) {
|
|
cout<<"[Vyld] Error: compiled.elf isn't a 64-bit ELF file."<<endl;
|
|
return -1;
|
|
}
|
|
if (reader.get_machine()!=ELFIO::EM_X86_64) {
|
|
cout<<"[Vyld] Error: compiled.elf isn't a x86_64 file."<<endl;
|
|
return -1;
|
|
}
|
|
cout<<"[Vyld] Extracting linked sections from compiled.elf."<<endl;
|
|
const ELFIO::section* elf_text_sec=reader.sections[".text"];
|
|
const ELFIO::section* elf_data_sec=reader.sections[".data"];
|
|
const ELFIO::section* elf_rodata_sec=reader.sections[".rodata"];
|
|
const ELFIO::section* elf_bss_sec=reader.sections[".bss"];
|
|
if (!elf_text_sec) {
|
|
cout<<"[Vyld] Error: no .text found."<<endl;
|
|
return -1;
|
|
}
|
|
if (!elf_data_sec) {
|
|
cout<<"[Vyld] Warning: no .data found."<<endl;
|
|
}
|
|
if (!elf_rodata_sec) {
|
|
cout<<"[Vyld] Warning: no .rodata found."<<endl;
|
|
}
|
|
if (!elf_bss_sec) {
|
|
cout<<"[Vyld] Warning: no .bss found."<<endl;
|
|
}
|
|
if (elf_text_sec->get_size()==0) {
|
|
cout<<"[Vyld] Error: .text is empty."<<endl;
|
|
return -1;
|
|
}
|
|
vector<unsigned char> elf_text_bin(text_size_padded,0);
|
|
vector<unsigned char> elf_data_bin(data_size_padded,0);
|
|
vector<unsigned char> elf_rodata_bin(rodata_size_padded,0);
|
|
memcpy(elf_text_bin.data(),elf_text_sec->get_data(),elf_text_sec->get_size());
|
|
if (elf_data_sec->get_size()!=0) {
|
|
memcpy(elf_data_bin.data(),elf_data_sec->get_data(),elf_data_sec->get_size());
|
|
}
|
|
if (elf_rodata_sec->get_size()!=0) {
|
|
memcpy(elf_rodata_bin.data(),elf_rodata_sec->get_data(),elf_rodata_sec->get_size());
|
|
}
|
|
uint8_t vyx_sig[3]={'V','y','X'};
|
|
uint16_t vyx_ver=0x0001;
|
|
string outfile=string(argv[argc-1]);
|
|
ofstream out(outfile,ios::binary);
|
|
out.write(reinterpret_cast<char*>(vyx_sig),3);
|
|
out.write(reinterpret_cast<char*>(&vyx_ver),2);
|
|
out.write(reinterpret_cast<char*>(&va.text_offset),8);
|
|
out.write(reinterpret_cast<char*>(&va.stack_base),8);
|
|
out.write(reinterpret_cast<char*>(&text_size_padded),8);
|
|
out.write(reinterpret_cast<char*>(&data_size_padded),8);
|
|
out.write(reinterpret_cast<char*>(&rodata_size_padded),8);
|
|
uint64_t bss_size_elf=elf_bss_sec->get_size();
|
|
out.write(reinterpret_cast<char*>(&bss_size_elf),8);
|
|
out.write(reinterpret_cast<char*>(elf_text_bin.data()),elf_text_bin.size());
|
|
if (data_size_padded!=0) out.write(reinterpret_cast<char*>(elf_data_bin.data()),elf_data_bin.size());
|
|
if (rodata_size_padded!=0) out.write(reinterpret_cast<char*>(elf_rodata_bin.data()),elf_rodata_bin.size());
|
|
out.close();
|
|
cout<<"[Vyld] Succesfully compiled "<<outfile<<endl;
|
|
return 0;
|
|
}
|