summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2022-11-07 11:19:03 -0500
committerpommicket <pommicket@gmail.com>2022-11-07 11:19:03 -0500
commit978248bc21b336960efcf8b2599f13bb5ec1cf6a (patch)
treeead0bc7a996a1fd9b85ab3e214df20a3bcb12541
parent23fb593011ce985e6e50e26e9765f78d27994d40 (diff)
c (working) and c++ (not working) inputs
-rw-r--r--src/linker.rs186
-rw-r--r--src/main.rs34
-rw-r--r--test.c10
-rw-r--r--tests/basic.c10
-rw-r--r--tests/cpp.cpp10
5 files changed, 203 insertions, 47 deletions
diff --git a/src/linker.rs b/src/linker.rs
index 5ff1ca0..9500495 100644
--- a/src/linker.rs
+++ b/src/linker.rs
@@ -17,6 +17,7 @@ Example usage:
let mut linker = Linker::new();
linker.add_input("main.o")?;
linker.add_input("libc.so.6")?;
+linker.add_input("libstdc++.so.6")?;
linker.link_to_file("a.out", "entry")?;
```
@@ -113,10 +114,19 @@ impl From<&LinkWarning> for String {
/// error produced by [Linker::add_object]
pub enum ObjectError {
+ IO(io::Error),
/// ELF format error
Elf(elf::Error),
/// wrong type of ELF file
BadType,
+ /// compile command failed
+ CommandFailed(std::process::ExitStatus),
+}
+
+impl From<io::Error> for ObjectError {
+ fn from(e: io::Error) -> Self {
+ Self::IO(e)
+ }
}
impl From<elf::Error> for ObjectError {
@@ -135,10 +145,10 @@ impl fmt::Display for ObjectError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
use ObjectError::*;
match self {
- // Display for UnexpectedEof *should* be this but is less clear
- // ("failed to fill whole buffer")
+ IO(e) => write!(f, "{e}"),
Elf(e) => write!(f, "{e}"),
BadType => write!(f, "wrong type of ELF file (not an object file)"),
+ CommandFailed(status) => write!(f, "command failed: {status}"),
}
}
}
@@ -324,6 +334,14 @@ struct Relocation {
pub struct Linker<'a> {
symbols: Symbols,
symbol_names: SymbolNames,
+ /// C compiler
+ cc: String,
+ /// C compiler flags
+ cflags: Vec<String>,
+ /// C++ compiler
+ cxx: String,
+ /// C++ compiler flags
+ cxxflags: Vec<String>,
relocations: Vec<Relocation>,
/// `sources[n]` = name of source corresponding to [SourceId]`(n)`.
/// These aren't necessarily valid paths. They're just names
@@ -726,6 +744,9 @@ impl LinkerOutput {
}
impl<'a> Linker<'a> {
+ pub const DEFAULT_CFLAGS: [&str; 5] = ["-Wall", "-Os", "-m32", "-fno-pic", "-c"];
+ pub const DEFAULT_CXXFLAGS: [&str; 5] = Self::DEFAULT_CFLAGS;
+
fn default_warning_handler(warning: LinkWarning) {
eprintln!("warning: {warning}");
}
@@ -741,12 +762,42 @@ impl<'a> Linker<'a> {
symbols: Symbols::new(),
symbol_names: SymbolNames::new(),
bss_size: 0,
+ cc: "cc".into(),
+ cxx: "c++".into(),
+ cflags: Self::DEFAULT_CFLAGS.iter().map(|&r| r.into()).collect(),
+ cxxflags: Self::DEFAULT_CXXFLAGS.iter().map(|&r| r.into()).collect(),
relocations: vec![],
sources: vec![],
libraries: vec![],
warn: Box::new(Self::default_warning_handler),
}
}
+
+ /// Set the C compiler.
+ pub fn set_cc(&mut self, cc: &str) {
+ self.cc = cc.into();
+ }
+
+ /// Set the C compiler flags.
+ ///
+ /// These had better include something like `-c` and
+ /// something like `-fno-pic`.
+ pub fn set_cflags(&mut self, cflags: &[String]) {
+ self.cflags = cflags.to_vec();
+ }
+
+ /// Set the C++ compiler.
+ pub fn set_cxx(&mut self, cxx: &str) {
+ self.cxx = cxx.into();
+ }
+
+ /// Set the C++ compiler flags.
+ ///
+ /// These had better include something like `-c` and
+ /// something like `-fno-pic`.
+ pub fn set_cxxflags(&mut self, cxxflags: &[String]) {
+ self.cxxflags = cxxflags.to_vec();
+ }
/// Get name of source file.
fn source_name(&self, id: SourceId) -> &str {
@@ -848,12 +899,104 @@ impl<'a> Linker<'a> {
Ok(())
}
+ pub fn add_object_from_file(&mut self, path: impl AsRef<path::Path>) -> Result<(), ObjectError> {
+ let path = path.as_ref();
+ let file = fs::File::open(path)?;
+ let mut file = io::BufReader::new(file);
+ self.add_object(&path.to_string_lossy(), &mut file)
+ }
+
/// Add a dynamic library (.so). `name` can be a full path or
/// something like "libc.so.6" --- any string you would pass to `dlopen`.
pub fn add_dynamic_library(&mut self, name: &str) -> Result<(), ObjectError> {
self.libraries.push(name.into());
Ok(())
}
+
+ fn compile(&self, compiler: &str, flags: &[String], path: &str) -> Result<String, ObjectError> {
+ use std::process::Command;
+
+ let ext_idx = path.rfind('.').unwrap_or(path.len());
+ let output_filename = path[..ext_idx].to_string() + ".o";
+
+ let status = Command::new(compiler)
+ .args(flags)
+ .arg(path)
+ .arg("-o")
+ .arg(&output_filename)
+ .status()?;
+ if status.success() {
+ Ok(output_filename)
+ } else {
+ Err(ObjectError::CommandFailed(status))
+ }
+ }
+
+ /// Add a C file (.c). This calls out to an external C compiler.
+ pub fn add_c(&mut self, path: &str) -> Result<(), ObjectError> {
+ let output = self.compile(&self.cc, &self.cflags, path)?;
+ self.add_object_from_file(&output)
+ }
+
+ /// Add a C++ file (.cpp/.cc/etc). This calls out to an external C++ compiler.
+ pub fn add_cpp(&mut self, path: &str) -> Result<(), ObjectError> {
+ let output = self.compile(&self.cxx, &self.cxxflags, path)?;
+ self.add_object_from_file(&output)
+ }
+
+ /// Easy input API.
+ /// Infers the file type of input, and calls the appropriate function (e.g. [Self::add_object]).
+ pub fn add_input(&mut self, input: &str) -> Result<(), String> {
+ enum FileType {
+ Object,
+ DynamicLibrary,
+ C,
+ CPlusPlus,
+ Other,
+ }
+
+ use FileType::*;
+
+ fn file_type(input: &str) -> FileType {
+ if input.ends_with(".o") {
+ return Object;
+ }
+ if input.ends_with(".c") {
+ return C;
+ }
+ if input.ends_with(".cpp") || input.ends_with(".cc") || input.ends_with(".cxx")
+ || input.ends_with(".C") {
+ return CPlusPlus;
+ }
+ if input.ends_with(".so") {
+ return DynamicLibrary;
+ }
+ if input.contains(".so.") {
+ // e.g. libc.so.6, some_library.so.12.7.3
+ return DynamicLibrary;
+ }
+ Other
+ }
+
+ match file_type(input) {
+ Object => {
+ self.add_object_from_file(input)
+ .map_err(|e| format!("Failed to process object file {input}: {e}"))
+ }
+ C => {
+ self.add_c(input)
+ .map_err(|e| format!("Failed to process C file {input}: {e}"))
+ },
+ CPlusPlus => {
+ self.add_cpp(input)
+ .map_err(|e| format!("Failed to process C++ file {input}: {e}"))
+ },
+ DynamicLibrary => self
+ .add_dynamic_library(input)
+ .map_err(|e| format!("Failed to process library file {input}: {e}")),
+ Other => Err(format!("Unrecognized file type: {input}")),
+ }
+ }
/// Get name of symbol.
fn symbol_name_str(&self, id: SymbolName) -> &str {
@@ -975,45 +1118,6 @@ impl<'a> Linker<'a> {
Ok(())
}
- /// Easy input API.
- /// Infers the file type of input, and calls the appropriate function (e.g. [Self::add_object]).
- pub fn add_input(&mut self, input: &str) -> Result<(), String> {
- enum FileType {
- Object,
- DynamicLibrary,
- Other,
- }
-
- use FileType::*;
-
- fn file_type(input: &str) -> FileType {
- if input.ends_with(".o") {
- return Object;
- }
- if input.ends_with(".so") {
- return DynamicLibrary;
- }
- if input.contains(".so.") {
- // e.g. libc.so.6, some_library.so.12.7.3
- return DynamicLibrary;
- }
- Other
- }
-
- match file_type(input) {
- Object => {
- let file =
- fs::File::open(input).map_err(|e| format!("Couldn't open {input}: {e}"))?;
- let mut file = io::BufReader::new(file);
- self.add_object(input, &mut file)
- .map_err(|e| format!("Failed to process object file {input}: {e}"))
- }
- DynamicLibrary => self
- .add_dynamic_library(input)
- .map_err(|e| format!("Failed to process library file {input}: {e}")),
- Other => Err(format!("Unrecognized file type: {input}")),
- }
- }
/// Add data for a symbol.
/// We don't want to link unused symbols.
diff --git a/src/main.rs b/src/main.rs
index 2526716..ed9c8ea 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -10,6 +10,7 @@
extern crate clap;
+use std::env;
use clap::Parser;
#[cfg(target_endian = "big")]
@@ -27,6 +28,11 @@ struct Args {
/// This makes the executable smaller.
#[arg(long = "no-stdlib", default_value_t = false)]
no_std_lib: bool,
+ /// If set, the program will be linked against libstdc++.
+ ///
+ /// This is needed when using the standard template library.
+ #[arg(long = "stdc++", default_value_t = false)]
+ std_cpp: bool,
/// Output executable path.
#[arg(short = 'o', long = "output", default_value = "a.out")]
output: String,
@@ -36,6 +42,29 @@ struct Args {
/// :3
#[arg(long = "nya")]
nya: bool,
+ /// C compiler
+ ///
+ /// First this is checked, then the `CC` environment variable,
+ /// then if both aren't set, (/usr/bin/)cc is used.
+ #[arg(long = "cc", default_value = "auto")]
+ cc: String,
+ /// C compiler flags
+ ///
+ /// The C compiler is invoked using `(cc) (cflags) (C file) -o (object file)`
+ #[arg(long = "cflags", default_values = linker::Linker::DEFAULT_CFLAGS)]
+ cflags: Vec<String>,
+}
+
+impl Args {
+ fn get_c_compiler(&self) -> String {
+ if self.cc != "auto" {
+ self.cc.clone()
+ } else if let Ok(env_cc) = env::var("CC") {
+ env_cc
+ } else {
+ "cc".into()
+ }
+ }
}
fn main_() -> Result<(), String> {
@@ -49,6 +78,8 @@ fn main_() -> Result<(), String> {
let inputs = &args.inputs;
let mut linker = linker::Linker::new();
+
+ linker.set_cc(&args.get_c_compiler());
let warning_handler = |w| {
// termcolor is a goddamn nightmare to use
@@ -70,6 +101,9 @@ fn main_() -> Result<(), String> {
if !args.no_std_lib {
linker.add_input("libc.so.6")?;
}
+ if args.std_cpp {
+ linker.add_input("libstdc++.so.6")?;
+ }
for input in inputs.iter() {
linker.add_input(input)?;
diff --git a/test.c b/test.c
index 4b86a0b..b8a6d61 100644
--- a/test.c
+++ b/test.c
@@ -1,12 +1,10 @@
#include <stdio.h>
#include <stdlib.h>
-extern int errno;
-int main() {
- errno = 0;
- fopen("zzz","r");
- printf("%d\n",errno);
- //printf("hello\n");
+int my_number;
+void entry() {
+ my_number = 137;
+ printf("%d\n",my_number);
exit(0);
}
diff --git a/tests/basic.c b/tests/basic.c
new file mode 100644
index 0000000..b8a6d61
--- /dev/null
+++ b/tests/basic.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+int my_number;
+void entry() {
+ my_number = 137;
+ printf("%d\n",my_number);
+ exit(0);
+
+}
diff --git a/tests/cpp.cpp b/tests/cpp.cpp
new file mode 100644
index 0000000..5da16e5
--- /dev/null
+++ b/tests/cpp.cpp
@@ -0,0 +1,10 @@
+#include <vector>
+#include <iostream>
+#include <algorithm>
+#include <climits>
+
+extern "C" int main(void) {
+ std::vector<int> numbers = {};
+
+ exit((int)(long)&numbers);
+}