Skip to content

Commit cf87df2

Browse files
authored
Update main.rs
1 parent 21dd5f3 commit cf87df2

File tree

1 file changed

+81
-23
lines changed

1 file changed

+81
-23
lines changed

hacker-compiler/src/main.rs

Lines changed: 81 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,32 @@ use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
1111
use cranelift_module::{DataDescription, Linkage, Module};
1212
use cranelift_object::{ObjectBuilder, ObjectModule};
1313
use subprocess::{Exec, PopenError};
14-
const HACKER_DIR: &str = "~/.hacker-lang";
14+
use std::os::unix::fs::PermissionsExt;
15+
use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
16+
use base64::Engine;
17+
18+
const HACKER_DIR: &str = "~/.hackeros/hacker-lang";
19+
1520
fn expand_home(path: &str) -> String {
1621
if path.starts_with("~/") {
17-
if let Some(home) = std::env::var_os("HOME") {
18-
return path.replacen("~", home.to_str().unwrap(), 1);
22+
if let Some(home) = env::var_os("HOME") {
23+
return path.replacen("~", home.to_str().unwrap_or(""), 1);
1924
}
2025
}
2126
path.to_string()
2227
}
23-
fn parse_hacker_file(path: &Path, verbose: bool) -> io::Result<(Vec<String>, Vec<String>, Vec<(String, String)>, Vec<String>, Vec<String>, Vec<String>)> {
28+
29+
fn parse_hacker_file(path: &Path, verbose: bool) -> io::Result<(Vec<String>, Vec<String>, Vec<(String, String)>, Vec<String>, Vec<String>, Vec<String>, Vec<String>, std::collections::HashMap<String, String>)> {
2430
let file = File::open(path)?;
2531
let mut deps = Vec::new();
2632
let mut libs = Vec::new();
2733
let mut vars = Vec::new();
2834
let mut cmds = Vec::new();
2935
let mut includes = Vec::new();
36+
let mut binaries = Vec::new(); // Binary lib paths
3037
let mut errors = Vec::new();
38+
let mut config = std::collections::HashMap::new();
3139
let mut in_config = false;
32-
let mut config_lines = Vec::new();
3340
let mut line_num = 0;
3441
for line in io::BufReader::new(file).lines() {
3542
line_num += 1;
@@ -43,7 +50,6 @@ fn parse_hacker_file(path: &Path, verbose: bool) -> io::Result<(Vec<String>, Vec
4350
errors.push(format!("Line {}: Nested config section", line_num));
4451
}
4552
in_config = true;
46-
config_lines = Vec::new();
4753
continue;
4854
} else if line == "]" {
4955
if !in_config {
@@ -53,7 +59,11 @@ fn parse_hacker_file(path: &Path, verbose: bool) -> io::Result<(Vec<String>, Vec
5359
continue;
5460
}
5561
if in_config {
56-
config_lines.push(line);
62+
if let Some(eq_idx) = line.find('=') {
63+
let key = line[..eq_idx].trim().to_string();
64+
let value = line[eq_idx + 1..].trim().to_string();
65+
config.insert(key, value);
66+
}
5767
continue;
5868
}
5969
if line.starts_with("//") {
@@ -68,19 +78,24 @@ fn parse_hacker_file(path: &Path, verbose: bool) -> io::Result<(Vec<String>, Vec
6878
if lib.is_empty() {
6979
errors.push(format!("Line {}: Empty library/include", line_num));
7080
} else {
71-
let lib_path_str = expand_home(&format!("{}/libs/{}/main.hacker", HACKER_DIR, lib));
72-
let lib_path = Path::new(&lib_path_str);
73-
if lib_path.exists() {
81+
let lib_dir = expand_home(&format!("{}/libs/{}", HACKER_DIR, lib));
82+
let lib_hacker_path = format!("{}/main.hacker", lib_dir);
83+
let lib_bin_path = expand_home(&format!("{}/libs/{}", HACKER_DIR, lib)); // Binary file
84+
if Path::new(&lib_hacker_path).exists() {
7485
includes.push(lib.clone());
75-
let (sub_deps, sub_libs, sub_vars, sub_cmds, sub_includes, sub_errors) = parse_hacker_file(lib_path, verbose)?;
86+
let (sub_deps, sub_libs, sub_vars, sub_cmds, sub_includes, sub_binaries, sub_errors, sub_config) = parse_hacker_file(Path::new(&lib_hacker_path), verbose)?;
7687
deps.extend(sub_deps);
7788
libs.extend(sub_libs);
7889
vars.extend(sub_vars);
7990
cmds.extend(sub_cmds);
8091
includes.extend(sub_includes);
92+
binaries.extend(sub_binaries);
8193
for err in sub_errors {
8294
errors.push(format!("In {}: {}", lib, err));
8395
}
96+
config.extend(sub_config);
97+
} else if Path::new(&lib_bin_path).exists() && Path::new(&lib_bin_path).metadata()?.permissions().mode() & 0o111 != 0 {
98+
binaries.push(lib_bin_path);
8499
} else {
85100
libs.push(lib);
86101
}
@@ -147,7 +162,7 @@ fn parse_hacker_file(path: &Path, verbose: bool) -> io::Result<(Vec<String>, Vec
147162
cmds.push(format!("{} &", cmd));
148163
}
149164
} else if line.starts_with("!") {
150-
// Ignore comment
165+
// Comment
151166
} else {
152167
errors.push(format!("Line {}: Invalid syntax", line_num));
153168
}
@@ -161,18 +176,22 @@ fn parse_hacker_file(path: &Path, verbose: bool) -> io::Result<(Vec<String>, Vec
161176
println!("Vars: {:?}", vars);
162177
println!("Cmds: {:?}", cmds);
163178
println!("Includes: {:?}", includes);
179+
println!("Binaries: {:?}", binaries);
180+
println!("Config: {:?}", config);
164181
if !errors.is_empty() {
165182
println!("Errors: {:?}", errors);
166183
}
167184
}
168-
Ok((deps, libs, vars, cmds, includes, errors))
185+
Ok((deps, libs, vars, cmds, includes, binaries, errors, config))
169186
}
187+
170188
fn generate_check_cmd(dep: &str) -> String {
171189
if dep == "sudo" {
172190
return String::new();
173191
}
174192
format!("command -v {} &> /dev/null || (sudo apt update && sudo apt install -y {})", dep, dep)
175193
}
194+
176195
fn main() -> io::Result<()> {
177196
let args: Vec<String> = env::args().collect();
178197
if args.len() < 3 || args.len() > 4 {
@@ -182,7 +201,7 @@ fn main() -> io::Result<()> {
182201
let verbose = args.len() == 4 && args[3] == "--verbose";
183202
let input_path = Path::new(&args[1]);
184203
let output_path = Path::new(&args[2]);
185-
let (deps, _libs, vars, cmds, _includes, errors) = parse_hacker_file(input_path, verbose)?;
204+
let (deps, _libs, vars, cmds, _includes, binaries, errors, _config) = parse_hacker_file(input_path, verbose)?;
186205
if !errors.is_empty() {
187206
for err in errors {
188207
eprintln!("{}", err);
@@ -197,6 +216,19 @@ fn main() -> io::Result<()> {
197216
}
198217
}
199218
final_cmds.extend(cmds);
219+
// For binaries, add commands to extract and run them
220+
let mut bin_extract_cmds = Vec::new();
221+
for (i, bin_path) in binaries.iter().enumerate() {
222+
let bin_data = fs::read(bin_path)?;
223+
let data_name = format!("bin_lib_{i}");
224+
// We'll embed later in Cranelift
225+
// But for cmds, add extraction: e.g., write to /tmp and run
226+
let extract_cmd = format!("echo '{}' | base64 -d > /tmp/{} && chmod +x /tmp/{} && /tmp/{}", BASE64_STANDARD.encode(&bin_data), data_name, data_name, data_name);
227+
bin_extract_cmds.push(extract_cmd);
228+
}
229+
final_cmds.extend(bin_extract_cmds);
230+
231+
// Cranelift setup
200232
let flag_builder = settings::builder();
201233
let flags = settings::Flags::new(flag_builder);
202234
let triple = target_lexicon::Triple::host();
@@ -205,36 +237,45 @@ fn main() -> io::Result<()> {
205237
let builder = ObjectBuilder::new(
206238
isa,
207239
output_path.file_stem().unwrap().to_str().unwrap().as_bytes().to_vec(),
208-
cranelift_module::default_libcall_names(),
209-
).expect("Failed to create ObjectBuilder");
240+
cranelift_module::default_libcall_names(),
241+
).expect("ObjectBuilder failed");
210242
let mut module = ObjectModule::new(builder);
211243
let pointer_type = module.target_config().pointer_type();
244+
245+
// Declare external functions
212246
let mut sig_system = module.make_signature();
213247
sig_system.params.push(AbiParam::new(pointer_type));
214248
sig_system.returns.push(AbiParam::new(types::I32));
215249
sig_system.call_conv = module.target_config().default_call_conv;
216250
let system_id = module.declare_function("system", Linkage::Import, &sig_system).unwrap();
251+
217252
let mut sig_putenv = module.make_signature();
218253
sig_putenv.params.push(AbiParam::new(pointer_type));
219254
sig_putenv.returns.push(AbiParam::new(types::I32));
220255
sig_putenv.call_conv = module.target_config().default_call_conv;
221256
let putenv_id = module.declare_function("putenv", Linkage::Import, &sig_putenv).unwrap();
257+
258+
// Main function
222259
let mut sig_main = module.make_signature();
223260
sig_main.returns.push(AbiParam::new(types::I32));
224261
sig_main.call_conv = module.target_config().default_call_conv;
225262
let main_id = module.declare_function("main", Linkage::Export, &sig_main).unwrap();
263+
226264
let mut ctx = cranelift_codegen::Context::for_function(Function::with_name_signature(Default::default(), sig_main.clone()));
227265
let mut func_builder_ctx = FunctionBuilderContext::new();
228266
let mut builder = FunctionBuilder::new(&mut ctx.func, &mut func_builder_ctx);
229267
let entry_block = builder.create_block();
230268
builder.switch_to_block(entry_block);
231269
builder.seal_block(entry_block);
270+
232271
let local_system = module.declare_func_in_func(system_id, &mut builder.func);
233272
let local_putenv = module.declare_func_in_func(putenv_id, &mut builder.func);
273+
274+
// Embed vars as putenv
234275
let mut var_data_ids = Vec::new();
235-
for (var, value) in &vars {
276+
for (i, (var, value)) in vars.iter().enumerate() {
236277
let env_str = format!("{}={}", var, value);
237-
let data_name = format!("env_{}", var_data_ids.len());
278+
let data_name = format!("env_{i}");
238279
let data_id = module.declare_data(&data_name, Linkage::Local, true, false).unwrap();
239280
let mut data_ctx = DataDescription::new();
240281
let mut bytes: Vec<u8> = env_str.into_bytes();
@@ -246,8 +287,10 @@ fn main() -> io::Result<()> {
246287
for data_id in var_data_ids {
247288
let global = module.declare_data_in_func(data_id, &mut builder.func);
248289
let ptr = builder.ins().global_value(pointer_type, global);
249-
let _ = builder.ins().call(local_putenv, &[ptr]);
290+
builder.ins().call(local_putenv, &[ptr]);
250291
}
292+
293+
// Embed cmd strings
251294
let mut cmd_data_ids = Vec::new();
252295
for (i, cmd) in final_cmds.iter().enumerate() {
253296
let data_name = format!("cmd_{i}");
@@ -262,19 +305,34 @@ fn main() -> io::Result<()> {
262305
for data_id in cmd_data_ids {
263306
let global = module.declare_data_in_func(data_id, &mut builder.func);
264307
let ptr = builder.ins().global_value(pointer_type, global);
265-
let _ = builder.ins().call(local_system, &[ptr]);
308+
builder.ins().call(local_system, &[ptr]);
309+
}
310+
311+
// Embed binary libs data
312+
for (i, bin_path) in binaries.iter().enumerate() {
313+
let bin_data = fs::read(bin_path)?;
314+
let data_name = format!("bin_lib_data_{i}");
315+
let data_id = module.declare_data(&data_name, Linkage::Local, true, false).unwrap();
316+
let mut data_ctx = DataDescription::new();
317+
data_ctx.define(bin_data.into_boxed_slice());
318+
module.define_data(data_id, &data_ctx).unwrap();
319+
// In runtime, we would need to extract, but since we're using system calls, we embed extraction cmds above
266320
}
321+
267322
let zero = builder.ins().iconst(types::I32, 0);
268323
builder.ins().return_(&[zero]);
269324
builder.finalize();
325+
270326
module.define_function(main_id, &mut ctx).unwrap();
271-
let obj = module.finish().object.write().expect("Failed to write object");
327+
let obj = module.finish().object.write().expect("Write object failed");
328+
272329
let temp_obj_path = output_path.with_extension("o");
273330
let mut file = File::create(&temp_obj_path)?;
274331
file.write_all(&obj)?;
332+
275333
let status = Exec::shell(format!("gcc -o {} {}", output_path.display(), temp_obj_path.display()))
276-
.join()
277-
.map_err(|e: PopenError| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
334+
.join()
335+
.map_err(|e: PopenError| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
278336
if !status.success() {
279337
return Err(io::Error::new(io::ErrorKind::Other, "Linking failed"));
280338
}

0 commit comments

Comments
 (0)