Skip to content

Commit 231ca63

Browse files
committed
Rename PyInit syms to avoid clashes
Built extensions in packages often have common names like speedups, utils, _objects, cpython, etc. which reside inside the package namespace. The compiled extensions each have a PyInit_<module> which needs to be renamed to PyInit_<pkg>_<module> to avoid clashes when combined into a static binary. Fixes #169
1 parent 35a7035 commit 231ca63

4 files changed

Lines changed: 189 additions & 3 deletions

File tree

pyoxidizer/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ hex = "0.3"
3636
itertools = "0.8"
3737
lazy_static = "1.3"
3838
libc = "0.2"
39+
object = { version = "0.15.0", features = ["read", "std", "write"] }
3940
regex = "1"
4041
reqwest = "0.9"
4142
rustc_version = "0.2"

pyoxidizer/src/pyrepackager/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub mod bytecode;
66
pub mod config;
77
pub mod dist;
88
pub mod fsscan;
9+
pub mod object;
910
pub mod packaging_rule;
1011
pub mod repackage;
1112
pub mod resource;
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
use object::{write, Object, ObjectSection, RelocationTarget, SectionKind, SymbolKind};
6+
use slog::{info, warn};
7+
use std::collections::{HashMap};
8+
use std::error::Error;
9+
use std::fmt;
10+
11+
#[derive(Debug, Clone)]
12+
pub struct NoRewriteError;
13+
14+
impl fmt::Display for NoRewriteError {
15+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
16+
write!(f, "no object rewriting was performed")
17+
}
18+
}
19+
20+
impl Error for NoRewriteError {
21+
fn source(&self) -> Option<&(dyn Error + 'static)> {
22+
// Generic error, underlying cause isn't tracked.
23+
None
24+
}
25+
}
26+
27+
/// Rename object syn PyInit_foo to PyInit_<full_name> to avoid clashes
28+
pub fn rename_init (
29+
logger: &slog::Logger,
30+
name: &String,
31+
object_data: &[u8]
32+
) -> Result<Vec<u8>, NoRewriteError> {
33+
let mut rewritten = false;
34+
35+
let name_prefix = name.split('.').next().unwrap();
36+
37+
let in_object = match object::File::parse(object_data) {
38+
Ok(object) => object,
39+
Err(err) => {
40+
panic!("Failed to parse compiled object for {}: {}", name, err);
41+
}
42+
};
43+
44+
let mut out_object = write::Object::new(in_object.format(), in_object.architecture());
45+
out_object.mangling = write::Mangling::None;
46+
47+
let mut out_sections = HashMap::new();
48+
for in_section in in_object.sections() {
49+
if in_section.kind() == SectionKind::Metadata {
50+
continue;
51+
}
52+
let section_id = out_object.add_section(
53+
in_section.segment_name().unwrap_or("").as_bytes().to_vec(),
54+
in_section.name().unwrap_or("").as_bytes().to_vec(),
55+
in_section.kind(),
56+
);
57+
let out_section = out_object.section_mut(section_id);
58+
if out_section.is_bss() {
59+
out_section.append_bss(in_section.size(), in_section.align());
60+
} else {
61+
out_section.set_data(in_section.uncompressed_data().into(), in_section.align());
62+
}
63+
out_sections.insert(in_section.index(), section_id);
64+
}
65+
66+
let mut out_symbols = HashMap::new();
67+
for (symbol_index, in_symbol) in in_object.symbols() {
68+
if in_symbol.kind() == SymbolKind::Null {
69+
continue;
70+
}
71+
let (section, value) = match in_symbol.section_index() {
72+
Some(index) => (
73+
Some(*out_sections.get(&index).unwrap()),
74+
in_symbol.address() - in_object.section_by_index(index).unwrap().address(),
75+
),
76+
None => (None, in_symbol.address()),
77+
};
78+
let in_sym_name = in_symbol.name().unwrap_or("");
79+
let sym_name = if in_sym_name.starts_with("PyInit_") && !in_sym_name.contains(name_prefix) {
80+
format!("PyInit_{}", name.replace(".", "_"))
81+
} else {
82+
String::from(in_sym_name)
83+
};
84+
if sym_name != in_sym_name {
85+
warn!(
86+
logger,
87+
"rewrote object symbol name {} to {}",
88+
in_sym_name,
89+
sym_name,
90+
);
91+
92+
rewritten = true;
93+
}
94+
95+
let out_symbol = write::Symbol {
96+
name: sym_name.as_bytes().to_vec(),
97+
value,
98+
size: in_symbol.size(),
99+
kind: in_symbol.kind(),
100+
scope: in_symbol.scope(),
101+
weak: in_symbol.is_weak(),
102+
section,
103+
};
104+
let symbol_id = out_object.add_symbol(out_symbol);
105+
out_symbols.insert(symbol_index, symbol_id);
106+
}
107+
108+
109+
if !rewritten {
110+
info!(
111+
logger,
112+
"no symbol name rewriting occurred for {}",
113+
name,
114+
);
115+
return Err(NoRewriteError)
116+
}
117+
118+
for in_section in in_object.sections() {
119+
if in_section.kind() == SectionKind::Metadata {
120+
continue;
121+
}
122+
let out_section = *out_sections.get(&in_section.index()).unwrap();
123+
for (offset, in_relocation) in in_section.relocations() {
124+
let symbol = match in_relocation.target() {
125+
RelocationTarget::Symbol(symbol) => *out_symbols.get(&symbol).unwrap(),
126+
RelocationTarget::Section(section) => {
127+
out_object.section_symbol(*out_sections.get(&section).unwrap())
128+
}
129+
};
130+
let out_relocation = write::Relocation {
131+
offset,
132+
size: in_relocation.size(),
133+
kind: in_relocation.kind(),
134+
encoding: in_relocation.encoding(),
135+
symbol,
136+
addend: in_relocation.addend(),
137+
};
138+
out_object
139+
.add_relocation(out_section, out_relocation)
140+
.unwrap();
141+
}
142+
}
143+
144+
Ok(out_object.write().unwrap())
145+
}

pyoxidizer/src/pyrepackager/repackage.rs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use super::dist::{
2323
analyze_python_distribution_tar_zst, resolve_python_distribution_archive, ExtensionModule,
2424
LicenseInfo, PythonDistributionInfo,
2525
};
26+
use super::object::{rename_init};
2627
use super::packaging_rule::{
2728
packages_from_module_name, packages_from_module_names, resolve_python_packaging,
2829
};
@@ -1126,23 +1127,39 @@ fn make_config_c(
11261127
}
11271128

11281129
for em in built_extension_modules.values() {
1129-
lines.push(format!("extern PyObject* {}(void);", em.init_fn));
1130+
let ambiguous_line = format!("extern PyObject* {}(void);", em.init_fn);
1131+
1132+
if lines.contains(&ambiguous_line) {
1133+
lines.push(format!("extern PyObject* PyInit_{}(void);", em.name.replace(".", "_")));
1134+
}
1135+
else {
1136+
lines.push(ambiguous_line);
1137+
}
11301138
}
11311139

11321140
lines.push(String::from("struct _inittab _PyImport_Inittab[] = {"));
11331141

1142+
let mut ambiguous_init_fns: Vec<String> = Vec::new();
1143+
11341144
for em in extension_modules.values() {
11351145
if let Some(init_fn) = &em.init_fn {
11361146
if init_fn == "NULL" {
11371147
continue;
11381148
}
11391149

11401150
lines.push(format!("{{\"{}\", {}}},", em.module, init_fn));
1151+
ambiguous_init_fns.push(init_fn.to_string());
11411152
}
11421153
}
11431154

11441155
for em in built_extension_modules.values() {
1145-
lines.push(format!("{{\"{}\", {}}},", em.name, em.init_fn));
1156+
if ambiguous_init_fns.contains(&em.init_fn) {
1157+
lines.push(format!("{{\"{}\", PyInit_{}}},", em.name, em.name.replace(".", "_")));
1158+
}
1159+
else {
1160+
lines.push(format!("{{\"{}\", {}}},", em.name, em.init_fn));
1161+
ambiguous_init_fns.push(em.init_fn.clone());
1162+
}
11461163
}
11471164

11481165
lines.push(String::from("{0, 0}"));
@@ -1284,12 +1301,20 @@ pub fn link_libpython(
12841301
// TODO handle static/dynamic libraries.
12851302
}
12861303

1304+
let mut ambiguous_init_fns: Vec<String> = Vec::new();
1305+
12871306
warn!(
12881307
logger,
12891308
"resolving inputs for {} extension modules...",
12901309
extension_modules.len() + built_extension_modules.len()
12911310
);
12921311
for (name, em) in extension_modules {
1312+
if let Some(init_fn) = &em.init_fn {
1313+
if init_fn != "NULL" {
1314+
ambiguous_init_fns.push(init_fn.to_string());
1315+
}
1316+
}
1317+
12931318
if em.builtin_default {
12941319
continue;
12951320
}
@@ -1341,7 +1366,17 @@ pub fn link_libpython(
13411366
for (i, object_data) in em.object_file_data.iter().enumerate() {
13421367
let out_path = temp_dir_path.join(format!("{}.{}.o", name, i));
13431368

1344-
fs::write(&out_path, object_data).expect("unable to write object file");
1369+
if i == em.object_file_data.len() - 1 && ambiguous_init_fns.contains(&em.init_fn) {
1370+
//if ambiguous_init_fns.contains(&em.init_fn) {
1371+
match rename_init(logger, name, object_data) {
1372+
Ok(val) => fs::write(&out_path, val).expect("unable to write object file"),
1373+
Err(_) => fs::write(&out_path, object_data).expect("unable to write object file")
1374+
};
1375+
}
1376+
else {
1377+
fs::write(&out_path, object_data).expect("unable to write object file");
1378+
}
1379+
13451380
build.object(&out_path);
13461381
}
13471382

@@ -1350,6 +1385,10 @@ pub fn link_libpython(
13501385
needed_libraries_external.insert(&library);
13511386
}
13521387

1388+
if !ambiguous_init_fns.contains(&em.init_fn) {
1389+
ambiguous_init_fns.push(em.init_fn.clone());
1390+
}
1391+
13531392
// TODO do something with library_dirs.
13541393
}
13551394

0 commit comments

Comments
 (0)