Skip to content

Commit 8b20a7b

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 8b20a7b

2 files changed

Lines changed: 160 additions & 4 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/repackage.rs

Lines changed: 159 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ use byteorder::{LittleEndian, WriteBytesExt};
66
use glob::glob as findglob;
77
use itertools::Itertools;
88
use lazy_static::lazy_static;
9+
use object::{write, Object, ObjectSection, RelocationTarget, SectionKind, SymbolKind};
910
use slog::{info, warn};
10-
use std::collections::{BTreeMap, BTreeSet};
11+
use std::collections::{BTreeMap, BTreeSet, HashMap};
1112
use std::env;
13+
use std::error::Error;
14+
use std::fmt;
1215
use std::fs;
1316
use std::fs::create_dir_all;
1417
use std::io::{BufRead, BufReader, Cursor, Error as IOError, Read, Write};
@@ -1126,7 +1129,13 @@ fn make_config_c(
11261129
}
11271130

11281131
for em in built_extension_modules.values() {
1129-
lines.push(format!("extern PyObject* {}(void);", em.init_fn));
1132+
let name_prefix = em.name.split('.').next().unwrap();
1133+
if em.init_fn.contains(name_prefix) {
1134+
lines.push(format!("extern PyObject* {}(void);", em.init_fn));
1135+
}
1136+
else {
1137+
lines.push(format!("extern PyObject* PyInit_{}(void);", em.name.replace(".", "_")));
1138+
}
11301139
}
11311140

11321141
lines.push(String::from("struct _inittab _PyImport_Inittab[] = {"));
@@ -1142,7 +1151,13 @@ fn make_config_c(
11421151
}
11431152

11441153
for em in built_extension_modules.values() {
1145-
lines.push(format!("{{\"{}\", {}}},", em.name, em.init_fn));
1154+
let name_prefix = em.name.split('.').next().unwrap();
1155+
if em.init_fn.contains(name_prefix) {
1156+
lines.push(format!("{{\"{}\", {}}},", em.name, em.init_fn));
1157+
}
1158+
else {
1159+
lines.push(format!("{{\"{}\", PyInit_{}}},", em.name, em.name.replace(".", "_")));
1160+
}
11461161
}
11471162

11481163
lines.push(String::from("{0, 0}"));
@@ -1151,6 +1166,142 @@ fn make_config_c(
11511166
lines.join("\n")
11521167
}
11531168

1169+
#[derive(Debug, Clone)]
1170+
struct NoRewriteError;
1171+
1172+
impl fmt::Display for NoRewriteError {
1173+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1174+
write!(f, "no object rewriting was performed")
1175+
}
1176+
}
1177+
1178+
impl Error for NoRewriteError {
1179+
fn source(&self) -> Option<&(dyn Error + 'static)> {
1180+
// Generic error, underlying cause isn't tracked.
1181+
None
1182+
}
1183+
}
1184+
1185+
/// Rename object syn PyInit_foo to PyInit_<full_name> to avoid clashes
1186+
fn rename_init (
1187+
logger: &slog::Logger,
1188+
name: &String,
1189+
object_data: &[u8]
1190+
) -> Result<Vec<u8>, NoRewriteError> {
1191+
let mut rewritten = false;
1192+
1193+
let name_prefix = name.split('.').next().unwrap();
1194+
1195+
let in_object = match object::File::parse(object_data) {
1196+
Ok(object) => object,
1197+
Err(err) => {
1198+
panic!("Failed to parse compiled object for {}: {}", name, err);
1199+
}
1200+
};
1201+
1202+
let mut out_object = write::Object::new(in_object.format(), in_object.architecture());
1203+
out_object.mangling = write::Mangling::None;
1204+
1205+
let mut out_sections = HashMap::new();
1206+
for in_section in in_object.sections() {
1207+
if in_section.kind() == SectionKind::Metadata {
1208+
continue;
1209+
}
1210+
let section_id = out_object.add_section(
1211+
in_section.segment_name().unwrap_or("").as_bytes().to_vec(),
1212+
in_section.name().unwrap_or("").as_bytes().to_vec(),
1213+
in_section.kind(),
1214+
);
1215+
let out_section = out_object.section_mut(section_id);
1216+
if out_section.is_bss() {
1217+
out_section.append_bss(in_section.size(), in_section.align());
1218+
} else {
1219+
out_section.set_data(in_section.uncompressed_data().into(), in_section.align());
1220+
}
1221+
out_sections.insert(in_section.index(), section_id);
1222+
}
1223+
1224+
let mut out_symbols = HashMap::new();
1225+
for (symbol_index, in_symbol) in in_object.symbols() {
1226+
if in_symbol.kind() == SymbolKind::Null {
1227+
continue;
1228+
}
1229+
let (section, value) = match in_symbol.section_index() {
1230+
Some(index) => (
1231+
Some(*out_sections.get(&index).unwrap()),
1232+
in_symbol.address() - in_object.section_by_index(index).unwrap().address(),
1233+
),
1234+
None => (None, in_symbol.address()),
1235+
};
1236+
let in_sym_name = in_symbol.name().unwrap_or("");
1237+
let sym_name = if in_sym_name.starts_with("PyInit_") && !in_sym_name.contains(name_prefix) {
1238+
format!("PyInit_{}", name.replace(".", "_"))
1239+
} else {
1240+
String::from(in_sym_name)
1241+
};
1242+
if sym_name != in_sym_name {
1243+
warn!(
1244+
logger,
1245+
"rewrote object symbol name {} to {}",
1246+
in_sym_name,
1247+
sym_name,
1248+
);
1249+
1250+
rewritten = true;
1251+
}
1252+
1253+
let out_symbol = write::Symbol {
1254+
name: sym_name.as_bytes().to_vec(),
1255+
value,
1256+
size: in_symbol.size(),
1257+
kind: in_symbol.kind(),
1258+
scope: in_symbol.scope(),
1259+
weak: in_symbol.is_weak(),
1260+
section,
1261+
};
1262+
let symbol_id = out_object.add_symbol(out_symbol);
1263+
out_symbols.insert(symbol_index, symbol_id);
1264+
}
1265+
1266+
1267+
if !rewritten {
1268+
info!(
1269+
logger,
1270+
"no symbol name rewriting occurred for {}",
1271+
name,
1272+
);
1273+
return Err(NoRewriteError)
1274+
}
1275+
1276+
for in_section in in_object.sections() {
1277+
if in_section.kind() == SectionKind::Metadata {
1278+
continue;
1279+
}
1280+
let out_section = *out_sections.get(&in_section.index()).unwrap();
1281+
for (offset, in_relocation) in in_section.relocations() {
1282+
let symbol = match in_relocation.target() {
1283+
RelocationTarget::Symbol(symbol) => *out_symbols.get(&symbol).unwrap(),
1284+
RelocationTarget::Section(section) => {
1285+
out_object.section_symbol(*out_sections.get(&section).unwrap())
1286+
}
1287+
};
1288+
let out_relocation = write::Relocation {
1289+
offset,
1290+
size: in_relocation.size(),
1291+
kind: in_relocation.kind(),
1292+
encoding: in_relocation.encoding(),
1293+
symbol,
1294+
addend: in_relocation.addend(),
1295+
};
1296+
out_object
1297+
.add_relocation(out_section, out_relocation)
1298+
.unwrap();
1299+
}
1300+
}
1301+
1302+
Ok(out_object.write().unwrap())
1303+
}
1304+
11541305
#[derive(Debug)]
11551306
pub struct LibpythonInfo {
11561307
path: PathBuf,
@@ -1341,7 +1492,11 @@ pub fn link_libpython(
13411492
for (i, object_data) in em.object_file_data.iter().enumerate() {
13421493
let out_path = temp_dir_path.join(format!("{}.{}.o", name, i));
13431494

1344-
fs::write(&out_path, object_data).expect("unable to write object file");
1495+
match rename_init(logger, name, object_data) {
1496+
Ok(val) => fs::write(&out_path, val).expect("unable to write object file"),
1497+
Err(_) => fs::write(&out_path, object_data).expect("unable to write object file")
1498+
};
1499+
13451500
build.object(&out_path);
13461501
}
13471502

0 commit comments

Comments
 (0)