Rust custom bare metal compile target: linker expects "_start" symbol and discards unused ones: How can I specify a custom entry symbol? - rust

I'm cross compiling bare metal 32-bit code for x86 with Rust and I'm facing the problem, that the final object file is empty, if the entry function is not exactly called _start; the linker throws all code away because it sees it as dead. I'm familiar with the fact, that _start is a well-known entry point name, but the question is still:
What part in Rust, LLVM or Linker forces this? Also attributes like extern "C" fn ..., #[no_mangle] or #[export_name = "foobar"] do not work (get thrown away by the linker). My guess is, that it's not the Rust compiler but the linker. As you can see, I use rust-lld as linker and ld.lld as linker-flavor in my case (see below).
Where does the required _start-come from? Why does the linker throw my other code away?
Whats the best option to specify my custom entry point to the linker?
x86-unknown-bare_metal.json
{
"llvm-target": "i686-unknown-none",
"data-layout": "e-m:e-i32:32-f80:128-n8:16:32-S128-p:32:32",
"arch": "x86",
"target-endian": "little",
"target-pointer-width": "32",
"target-c-int-width": "32",
"os": "none",
"executables": true,
"linker-flavor": "ld.lld",
"linker": "rust-lld",
"panic-strategy": "abort",
"disable-redzone": true,
"features": "+soft-float,+sse"
}
I'm using Rust 1.54.0 nightly and built it on a Linux 5.8.0-system.
I spend some time searching the internet and found discussions, that Rust should eventually get a #[entrypoint="foobar annotation or something like this, but I didn't found an usable solution unfortunately.
My attempt was to append
"pre-link-args": {
"ld.lld": [
"-e,foobar"
]
}
to the target definition (function also called foobar) but the object file is still empty. An other attempt was to keep all dead code. This works but this solution is dirty.
Minimal code example:
// disable rust standard library
#![no_std]
// disables Rust runtime init,
#![no_main]
// see https://docs.rust-embedded.org/embedonomicon/smallest-no-std.html
#![feature(lang_items)]
// see https://docs.rust-embedded.org/embedonomicon/smallest-no-std.html
#[lang = "eh_personality"]
extern "C" fn eh_personality() {}
use core::panic::PanicInfo;
use core::sync::atomic;
use core::sync::atomic::Ordering;
#[no_mangle]
/// The name **must be** `_start`, otherwise the compiler doesn't output anything
/// to the object file. I don't know why it is like this.
/// Also `pub` or `pub extern "C"` doesn't work
fn _start() -> ! {
loop {}
}
#[inline(never)]
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {
atomic::compiler_fence(Ordering::SeqCst);
}
}

New Answer [Solution]
The real solution is quite simple but was hard to find, because it's hard to digg for possible options and solutions in this relatively undocumented field. I found out, that llvm-ld uses the same options, as GNU ld. So I checked against the GNU ld link options and found the solution. It has to be
"pre-link-args": {
"ld.lld": [
"--entry=entry_32_bit"
]
}
and the value is the name of the function in the file. The function must be annotated with #[no_mangle].
Also see: https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html
Old Answer:
A quick and really dirty solution would be
#[no_mangle]
/// The name **must be** `_start`, otherwise the compiler doesn't output anything
/// to the object file. I don't know it is like this.
fn _start() -> ! {
entry_32_bit();
}
#[no_mangle]
#[inline(never)]
fn entry_32_bit() -> ! {
loop {}
}
With this, one can directly jump to symbol entry_32_bit from assembly. But this "solution" is far from ideal! Especially when you want to link multiple bins together, there will be a name collision.

Related

How to use `clippy::disallowed_method` with methods inside the same crate

I have some functions defined in my crate that I want to disallow the use of inside of the same, using clippy::disallowed_methods.
For example:
fn f() { ... }
fn g() {
f(); // Warning
}
I've tried the following clippy.toml
disallowed-methods = [
"crate::f"
]
But no warning shows up when I run cargo clippy.
I've also tried f, my_crate::f and ::my_crate::f, where my_crate is the name of the crate both functions are defined in, but none of them work.
I've tried it with other methods in external crates, such as std::vec::Vec::new and a warning successfully shows up.
Is there any way to make disallowed_methods work without moving the methods to another crate?

How to make one argument imply another without needing an explicit value? (--foo, not --foo true)

I want one argument to imply another, though they don't take explicit values. --simple-anime or --complex-anime should imply --anime. The API that should work is default_value_ifs, saying that if either of the former is present, --anime will also be true. The problem is that that option turns on takes_value, and if I turn that off, the implication doesn't happen.
Simple example: --dog implies --mammal. Neither one should require a value--it is true if the argument is present.
use clap::Parser;
fn main() {
let args = Args::parse_from(["prog-name", "--dog"]);
assert_eq!(args.dog, true);
assert_eq!(args.mammal, true);
dbg!(&args);
let args = Args::try_parse_from(["prog-name", "--mammal"]);
dbg!(&args);
assert!(matches!(args, Ok(_)));
}
#[derive(Parser, Debug)]
#[clap()]
struct Args {
//#[clap(long, default_value_if("dog", None, Some("true")), takes_value(false))]
#[clap(long, default_value_if("dog", None, Some("true")))]
mammal: bool,
#[clap(long)]
dog: bool,
}
Try it in rust playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=4855a88381f65cef8d07f7eab4d41e78
Instead of takes_value(false) use min_values(0) (playground):
#[clap(long, default_value_if("dog", None, Some("true")), min_values(0))]
mammal: bool,
It looks like the default value implementation uses the same code as the values, so if you disable one you disable the other.

Can't use a neon JsArray: This function takes 3 parameters but 2 were supplied

I'm learning how to use neon, but I don't understand a thing. If I try to execute this code:
#[macro_use]
extern crate neon;
use neon::vm::{Call, JsResult};
use neon::mem::Handle;
use neon::js::{JsInteger, JsNumber, JsString, JsObject, JsArray, JsValue, Object, Key};
use neon::js::error::{JsError, Kind};
fn test(call: Call) -> JsResult<JsArray> {
let scope = call.scope;
let js_arr: Handle<JsArray> = try!(try!(call.arguments.require(scope, 1)).check::<JsArray>());
js_arr.set(0, JsNumber::new(scope, 1000));
Ok(js_arr)
}
register_module!(m, {
m.export("test", test)
});
I get this error when I call js_arr.set: This function takes 3 parameters but 2 were supplied.
I don't understand why since it's a JsArray. Even Racer tells me that the set method takes 2 parameters. No matter what, js_arr.set takes 3 parameters in this order: &mut bool, neon::macro_internal::runtime::raw::Local and neon::macro_internal::runtime::raw::Local.
What's happening? I can't understand how JsArray works.
As paulsevere says on a GitHub issue for Neon, import neon::js::Object. In addition, do not import Key, which also provides a set method:
#[macro_use]
extern crate neon;
use neon::vm::{Call, JsResult};
use neon::js::{Object, JsArray, JsInteger, JsObject, JsNumber};
fn make_an_array(call: Call) -> JsResult<JsArray> {
let scope = call.scope; // the current scope for rooting handles
let array = JsArray::new(scope, 3);
array.set(0, JsInteger::new(scope, 9000))?;
array.set(1, JsObject::new(scope))?;
array.set(2, JsNumber::new(scope, 3.14159))?;
Ok(array)
}
register_module!(m, {
m.export("main", make_an_array)
});
This creates a brand new array. If you'd like to accept an array as the first argument to your function and then modify it, this works:
#[macro_use]
extern crate neon;
use neon::vm::{Call, JsResult};
use neon::js::{Object, JsArray, JsInteger, JsUndefined};
use neon::mem::Handle;
fn hello(call: Call) -> JsResult<JsUndefined> {
let scope = call.scope;
let js_arr: Handle<JsArray> = call.arguments.require(scope, 0)?.check::<JsArray>()?;
js_arr.set(0, JsInteger::new(scope, 1000))?;
Ok(JsUndefined::new())
}
register_module!(m, {
m.export("hello", hello)
});
let js_arr: Handle<JsArray> makes it clear that js_arr is a Handle<JsArray> and Handle<T> has this method:
unsafe fn set(self, out: &mut bool, obj: Local, val: Local) -> bool
I'd guess that you're accidentally trying to call Handle::set (which is unsafe and takes three non-self arguments) rather than JsArray::set (which is safe and takes two non-self arguments).
If that's the case, you need to force a deref_mut to occur. (_mut because JsArray::set takes &mut self.)
I haven't run into this sort of naming collision before, so I can't be certain whether the auto-deref is smart enough, but something like this may work:
(&mut js_arr).set(0, JsNumber::new(scope, 1000));
Failing that, two other things to try are:
JsArray::set(&mut js_arr, 0, JsNumber::new(scope, 1000));
(If the former example fails because it's too much like C++-style method overloading. This is known as Fully Qualified Syntax and is normally used to disambiguate when an object implements two traits which provide methods of the same name.)
Call js_arr.deref_mut() directly to get a mutable reference to the underlying JsArray, then call set on that.

Invalid operand for inline asm constraint 'i' when writing inline x86_64 assembly

The code below used to build just fine back in April (Rust version ~1.6), but it doesn't anymore.
#![feature(asm)]
enum MyEnum { One = 1 }
fn main() {
unsafe {
asm!("nop" : : "i" (MyEnum::One as isize) : : ); // broken
}
}
The error message does not point to any obvious changes that might be causing this.
The value for the "i" constraint must be a compile-time constant and you are supplying it something that isn't. If you move the addition into Rust, you could use a register as well as a constant using the constraints "ri".
Whether something is a constant for the purpose of inline assembler can be affected by optimization options.

Declaring a map in a separate file and reading its contents

I'm trying to declare a map in a separate file, and then access it from my main function.
I want Rust's equivalent (or whatever comes closest) to this C++ map:
static const std::map<std::string, std::vector<std::string>> table = {
{ "a", { "foo" } },
{ "e", { "bar", "baz" } }
};
This is my attempt in Rust.
table.rs
use std::container::Map;
pub static table: &'static Map<~str, ~[~str]> = (~[
(~"a", ~[~"foo"]),
(~"e", ~[~"bar", ~"baz"])
]).move_iter().collect();
main.rs
mod table;
fn main() {
println(fmt!("%?", table::table));
}
The above gives two compiler errors in table.rs, saying "constant contains unimplemented expression type".
I also have the feeling that the map declaration is less than optimal for the purpose.
Finally, I'm using Rust 0.8.
As Chris Morgan noted, rust doesn't allow you to run user code in order to initialize global variables before main is entered, unlike C++. So you are mostly limited to primitive types that you can initialize with literal expressions. This is, afaik, part of the design and unlikely to change, even though the particular error message is probably not final.
Depending on your use case, you might want to change your code so you're manually passing your map as an argument to all the functions that will want to use it (ugh!), use task-local storage to initialize a tls slot with your map early on and then refer to it later in the same task (ugh?), or use unsafe code and a static mut variable to do much the same with your map wrapped in an Option maybe so it can start its life as None (ugh!).

Resources