Skip to content

Commit 721c5bb

Browse files
committed
More work on proper linkage name-mangling. Almost right, aside from version numbers.
1 parent 7034a28 commit 721c5bb

File tree

9 files changed

+381
-143
lines changed

9 files changed

+381
-143
lines changed

src/comp/back/link.rs

Lines changed: 244 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
import driver::session;
22
import lib::llvm::llvm;
33
import middle::trans;
4+
import middle::metadata;
5+
import middle::ty;
46
import std::str;
57
import std::fs;
8+
import std::vec;
9+
import std::option;
10+
import option::some;
11+
import option::none;
12+
import std::sha1::sha1;
13+
import std::sort;
14+
import trans::crate_ctxt;
15+
import front::ast;
616

717
import lib::llvm::llvm::ModuleRef;
818
import lib::llvm::llvm::ValueRef;
@@ -49,7 +59,7 @@ fn link_intrinsics(session::session sess, ModuleRef llmod) {
4959

5060
auto linkres = llvm::LLVMLinkModules(llmod, llintrinsicsmod);
5161
llvm::LLVMDisposeModule(llintrinsicsmod);
52-
62+
5363
if (linkres == False) {
5464
llvm_err(sess, "couldn't link the module with the intrinsics");
5565
fail;
@@ -58,7 +68,7 @@ fn link_intrinsics(session::session sess, ModuleRef llmod) {
5868

5969
mod write {
6070
fn is_object_or_assembly_or_exe(output_type ot) -> bool {
61-
if ( (ot == output_type_assembly) ||
71+
if ( (ot == output_type_assembly) ||
6272
(ot == output_type_object) ||
6373
(ot == output_type_exe) ) {
6474
ret true;
@@ -218,3 +228,235 @@ mod write {
218228
}
219229
}
220230

231+
/*
232+
* Name mangling and its relationship to metadata. This is complex. Read
233+
* carefully.
234+
*
235+
* The semantic model of Rust linkage is, broadly, that "there's no global
236+
* namespace" between crates. Our aim is to preserve the illusion of this
237+
* model despite the fact that it's not *quite* possible to implement on
238+
* modern linkers. We initially didn't use system linkers at all, but have
239+
* been convinced of their utility.
240+
*
241+
* There are a few issues to handle:
242+
*
243+
* - Linkers operate on a flat namespace, so we have to flatten names.
244+
* We do this using the C++ namespace-mangling technique. Foo::bar
245+
* symbols and such.
246+
*
247+
* - Symbols with the same name but different types need to get different
248+
* linkage-names. We do this by hashing a string-encoding of the type into
249+
* a fixed-size (currently 16-byte hex) cryptographic hash function (CHF:
250+
* we use SHA1) to "prevent collisions". This is not airtight but 16 hex
251+
* digits on uniform probability means you're going to need 2**32 same-name
252+
* symbols in the same process before you're even hitting birthday-paradox
253+
* collision probability.
254+
*
255+
* - Symbols in dirrerent crates but with same names "within" the crate need
256+
* to get different linkage-names.
257+
*
258+
* So here is what we do:
259+
*
260+
* - Separate the meta tags into two sets: exported and local. Only work with
261+
* the exported ones when considering linkage.
262+
*
263+
* - Consider two exported tags as special (and madatory): name and vers.
264+
* Every crate gets them; if it doesn't name them explicitly we infer them
265+
* as basename(crate) and "0.1", respectively. Call these CNAME, CVERS.
266+
*
267+
* - Define CMETA as all the non-name, non-vers exported meta tags in the
268+
* crate (in sorted order).
269+
*
270+
* - Define CMH as hash(CMETA).
271+
*
272+
* - Compile our crate to lib CNAME-CMH-CVERS.so
273+
*
274+
* - Define STH(sym) as hash(CNAME, CMH, type_str(sym))
275+
*
276+
* - Suffix a mangled sym with ::STH@CVERS, so that it is unique in the
277+
* name, non-name metadata, and type sense, and versioned in the way
278+
* system linkers understand.
279+
*
280+
*/
281+
282+
283+
iter crate_export_metas(ast::crate c) -> @ast::meta_item {
284+
for (@ast::crate_directive cdir in c.node.directives) {
285+
alt (cdir.node) {
286+
case (ast::cdir_meta(?v, ?mis)) {
287+
if (v == ast::export_meta) {
288+
for (@ast::meta_item mi in mis) {
289+
put mi;
290+
}
291+
}
292+
}
293+
case (_) {}
294+
}
295+
}
296+
}
297+
fn get_crate_meta(&session::session sess,
298+
&ast::crate c, str k, str default,
299+
bool warn_default) -> str {
300+
let vec[@ast::meta_item] v = [];
301+
for each (@ast::meta_item mi in crate_export_metas(c)) {
302+
if (mi.node.name == k) {
303+
v += [mi];
304+
}
305+
}
306+
alt (vec::len(v)) {
307+
case (0u) {
308+
if (warn_default) {
309+
sess.warn(#fmt("missing meta '%s', using '%s' as default",
310+
k, default));
311+
}
312+
ret default;
313+
}
314+
case (1u) {
315+
ret v.(0).node.value;
316+
}
317+
case (_) {
318+
sess.span_err(v.(1).span, #fmt("duplicate meta '%s'", k));
319+
}
320+
}
321+
}
322+
323+
// This calculates CMH as defined above
324+
fn crate_meta_extras_hash(sha1 sha, &ast::crate crate) -> str {
325+
fn lteq(&@ast::meta_item ma,
326+
&@ast::meta_item mb) -> bool {
327+
ret ma.node.name <= mb.node.name;
328+
}
329+
330+
fn len_and_str(&str s) -> str {
331+
ret #fmt("%u_%s", str::byte_len(s), s);
332+
}
333+
334+
let vec[mutable @ast::meta_item] v = [mutable];
335+
for each (@ast::meta_item mi in crate_export_metas(crate)) {
336+
if (mi.node.name != "name" &&
337+
mi.node.name != "vers") {
338+
v += [mutable mi];
339+
}
340+
}
341+
sort::quick_sort(lteq, v);
342+
sha.reset();
343+
for (@ast::meta_item m in v) {
344+
sha.input_str(len_and_str(m.node.name));
345+
sha.input_str(len_and_str(m.node.value));
346+
}
347+
ret truncated_sha1_result(sha);
348+
}
349+
350+
fn crate_meta_name(&session::session sess, &ast::crate crate,
351+
&str output) -> str {
352+
auto os = str::split(fs::basename(output), '.' as u8);
353+
assert vec::len(os) >= 2u;
354+
vec::pop(os);
355+
ret get_crate_meta(sess, crate, "name", str::connect(os, "."),
356+
sess.get_opts().shared);
357+
}
358+
359+
fn crate_meta_vers(&session::session sess, &ast::crate crate) -> str {
360+
ret get_crate_meta(sess, crate, "vers", "0.0",
361+
sess.get_opts().shared);
362+
}
363+
364+
fn truncated_sha1_result(sha1 sha) -> str {
365+
ret str::substr(sha.result_str(), 0u, 16u);
366+
}
367+
368+
369+
370+
// This calculates STH for a symbol, as defined above
371+
fn symbol_hash(ty::ctxt tcx, sha1 sha, &ty::t t,
372+
str crate_meta_name,
373+
str crate_meta_extras_hash) -> str {
374+
// NB: do *not* use abbrevs here as we want the symbol names
375+
// to be independent of one another in the crate.
376+
auto cx = @rec(ds=metadata::def_to_str, tcx=tcx,
377+
abbrevs=metadata::ac_no_abbrevs);
378+
sha.reset();
379+
sha.input_str(crate_meta_name);
380+
sha.input_str("-");
381+
sha.input_str(crate_meta_name);
382+
sha.input_str("-");
383+
sha.input_str(metadata::Encode::ty_str(cx, t));
384+
auto hash = truncated_sha1_result(sha);
385+
// Prefix with _ so that it never blends into adjacent digits
386+
ret "_" + hash;
387+
}
388+
389+
fn get_symbol_hash(&@crate_ctxt ccx, &ty::t t) -> str {
390+
auto hash = "";
391+
alt (ccx.type_sha1s.find(t)) {
392+
case (some(?h)) { hash = h; }
393+
case (none) {
394+
hash = symbol_hash(ccx.tcx, ccx.sha, t,
395+
ccx.crate_meta_name,
396+
ccx.crate_meta_extras_hash);
397+
ccx.type_sha1s.insert(t, hash);
398+
}
399+
}
400+
ret hash;
401+
}
402+
403+
404+
fn mangle(&vec[str] ss) -> str {
405+
406+
// Follow C++ namespace-mangling style
407+
408+
auto n = "_ZN"; // Begin name-sequence.
409+
410+
for (str s in ss) {
411+
n += #fmt("%u%s", str::byte_len(s), s);
412+
}
413+
414+
n += "E"; // End name-sequence.
415+
ret n;
416+
}
417+
418+
419+
fn exported_name(&vec[str] path, &str hash, &str vers) -> str {
420+
// FIXME: versioning isn't working yet
421+
ret mangle(path + [hash]); // + "@" + vers;
422+
}
423+
424+
fn mangle_exported_name(&@crate_ctxt ccx, &vec[str] path,
425+
&ty::t t) -> str {
426+
auto hash = get_symbol_hash(ccx, t);
427+
ret exported_name(path, hash, ccx.crate_meta_vers);
428+
}
429+
430+
fn mangle_internal_name_by_type_only(&@crate_ctxt ccx, &ty::t t,
431+
&str name) -> str {
432+
auto f = metadata::def_to_str;
433+
auto cx = @rec(ds=f, tcx=ccx.tcx, abbrevs=metadata::ac_no_abbrevs);
434+
auto s = ty::ty_to_short_str(ccx.tcx, t);
435+
436+
auto hash = get_symbol_hash(ccx, t);
437+
ret mangle([name, s, hash]);
438+
}
439+
440+
fn mangle_internal_name_by_path_and_seq(&@crate_ctxt ccx, &vec[str] path,
441+
&str flav) -> str {
442+
ret mangle(path + [ccx.names.next(flav)]);
443+
}
444+
445+
fn mangle_internal_name_by_path(&@crate_ctxt ccx, &vec[str] path) -> str {
446+
ret mangle(path);
447+
}
448+
449+
fn mangle_internal_name_by_seq(&@crate_ctxt ccx, &str flav) -> str {
450+
ret ccx.names.next(flav);
451+
}
452+
453+
//
454+
// Local Variables:
455+
// mode: rust
456+
// fill-column: 78;
457+
// indent-tabs-mode: nil
458+
// c-basic-offset: 4
459+
// buffer-file-coding-system: utf-8-unix
460+
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
461+
// End:
462+
//

src/comp/driver/session.rs

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import std::uint;
66
import std::term;
77
import std::io;
88
import std::map;
9+
import std::option;
10+
import std::option::some;
11+
import std::option::none;
912

1013
tag os {
1114
os_win32;
@@ -48,9 +51,16 @@ fn span_to_str(span sp, codemap::codemap cm) -> str {
4851
lo.col, hi.line, hi.col));
4952
}
5053

51-
fn emit_diagnostic(span sp, str msg, str kind, u8 color,
54+
fn emit_diagnostic(option::t[span] sp, str msg, str kind, u8 color,
5255
codemap::codemap cm) {
53-
io::stdout().write_str(span_to_str(sp, cm) + ": ");
56+
auto ss = "<input>:0:0:0:0";
57+
alt (sp) {
58+
case (some(?ssp)) {
59+
ss = span_to_str(ssp, cm);
60+
}
61+
case (none) {}
62+
}
63+
io::stdout().write_str(ss + ": ");
5464

5565
if (term::color_supported()) {
5666
term::fg(io::stdout().get_buf_writer(), color);
@@ -85,12 +95,12 @@ state obj session(ast::crate_num cnum,
8595

8696
fn span_err(span sp, str msg) -> ! {
8797
// FIXME: Use constants, but rustboot doesn't know how to export them.
88-
emit_diagnostic(sp, msg, "error", 9u8, cm);
98+
emit_diagnostic(some(sp), msg, "error", 9u8, cm);
8999
fail;
90100
}
91101

92102
fn err(str msg) -> ! {
93-
log_err #fmt("error: %s", msg);
103+
emit_diagnostic(none[span], msg, "error", 9u8, cm);
94104
fail;
95105
}
96106

@@ -103,29 +113,32 @@ state obj session(ast::crate_num cnum,
103113

104114
fn span_warn(span sp, str msg) {
105115
// FIXME: Use constants, but rustboot doesn't know how to export them.
106-
emit_diagnostic(sp, msg, "warning", 11u8, cm);
116+
emit_diagnostic(some(sp), msg, "warning", 11u8, cm);
117+
}
118+
119+
fn warn(str msg) {
120+
emit_diagnostic(none[span], msg, "warning", 11u8, cm);
107121
}
108122

109123
fn span_note(span sp, str msg) {
110124
// FIXME: Use constants, but rustboot doesn't know how to export them.
111-
emit_diagnostic(sp, msg, "note", 10u8, cm);
125+
emit_diagnostic(some(sp), msg, "note", 10u8, cm);
126+
}
127+
128+
fn span_bug(span sp, str msg) -> ! {
129+
self.span_err(sp, #fmt("internal compiler error %s", msg));
112130
}
113131

114132
fn bug(str msg) -> ! {
115-
log_err #fmt("error: internal compiler error %s", msg);
116-
fail;
133+
self.err(#fmt("internal compiler error %s", msg));
117134
}
118135

119136
fn span_unimpl(span sp, str msg) -> ! {
120-
// FIXME: Use constants, but rustboot doesn't know how to export them.
121-
emit_diagnostic(sp, "internal compiler error: unimplemented " + msg,
122-
"error", 9u8, cm);
123-
fail;
137+
self.span_bug(sp, "unimplemented " + msg);
124138
}
125-
139+
126140
fn unimpl(str msg) -> ! {
127-
log_err #fmt("error: unimplemented %s", msg);
128-
fail;
141+
self.bug("unimplemented " + msg);
129142
}
130143

131144
fn get_external_crate(int num) -> crate_metadata {

src/comp/front/ast.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ type crate = spanned[crate_];
7171
type crate_ = rec(vec[@crate_directive] directives,
7272
_mod module);
7373

74+
tag meta_visibility {
75+
export_meta;
76+
local_meta;
77+
}
78+
7479
tag crate_directive_ {
7580
cdir_expr(@expr);
7681
// FIXME: cdir_let should be eliminated
@@ -80,7 +85,7 @@ tag crate_directive_ {
8085
cdir_src_mod(ident, option::t[filename]);
8186
cdir_dir_mod(ident, option::t[filename], vec[@crate_directive]);
8287
cdir_view_item(@view_item);
83-
cdir_meta(vec[@meta_item]);
88+
cdir_meta(meta_visibility, vec[@meta_item]);
8489
cdir_syntax(path);
8590
cdir_auth(path, _auth);
8691
}

src/comp/front/eval.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,8 +418,13 @@ fn eval_crate_directive(ctx cx,
418418
vec::push[@ast::view_item](view_items, vi);
419419
}
420420

421-
case (ast::cdir_meta(?mi)) {
422-
cx.sess.add_metadata(mi);
421+
case (ast::cdir_meta(?vi, ?mi)) {
422+
// FIXME: we should actually record, for documentation-sake,
423+
// the metadata that's not exported. It would be nice to have
424+
// compiled-in to the target crate, not just in theh AST.
425+
if (vi == ast::export_meta) {
426+
cx.sess.add_metadata(mi);
427+
}
423428
}
424429

425430
case (ast::cdir_syntax(?pth)) {}

0 commit comments

Comments
 (0)