@@ -26,6 +26,13 @@ pub const Libs = struct {
2626 // Platforms that need a strong __memset (overrides the weak recursive stub):
2727 // built as a TRUE object so ld.lld sees the strong definition before the archive.
2828 mem : ? * std.Build.Step.Compile = null ,
29+ // C64/CBM: printf.cc + varint.cc compiled with lto=.none (C++ bitcode crashes LLVM LTO codegen for 6502).
30+ printf : ? * std.Build.Step.Compile = null ,
31+ // C64/CBM: crt0.S must be a TRUE object (not an archive member).
32+ // common-crt0-o in CMake = add_platform_object_file → always force-linked.
33+ // Without this, .call_main (jsr main) is a section-only archive member with no
34+ // exported symbol, so ld.lld never extracts it → main is never called.
35+ crt0_obj : ? * std.Build.Step.Compile = null ,
2936};
3037
3138/// Build platform libraries for `pd` from the SDK source tree at `sdk_root`.
@@ -138,16 +145,28 @@ pub fn buildPlatform(b: *std.Build, sdk_root: []const u8, pd: Platform, opt: std
138145 libcrt0 .root_module .addIncludePath (.{ .cwd_relative = com_inc });
139146 libcrt0 .root_module .addCSourceFiles (.{
140147 .root = .{ .cwd_relative = crt0_dir },
141- .files = &.{ "crt0.S" , "init-stack.S" },
148+ // crt0.S is kept here for sim (sim uses Zig start.zig + forceUndefinedSymbol
149+ // for startup, so the archive copy is harmless). For CBM platforms, crt0.S
150+ // is built as a TRUE object (crt0_obj) and added directly to each exe, because
151+ // its .call_main section has no exported symbol → ld.lld would silently skip
152+ // the archive member → main is never called.
153+ .files = if (std .mem .eql (u8 , pd .name , "sim" )) &.{ "crt0.S" , "init-stack.S" } else &.{"init-stack.S" },
142154 });
143155 libcrt0 .root_module .addCSourceFiles (.{
144156 .root = .{ .cwd_relative = crt0_dir },
145157 .files = &.{"copy-zp-data.c" },
146158 });
147- const exit_file : []const u8 = if (std .mem .eql (u8 , pd .name , "sim" )) "exit-custom.S" else "exit-loop.c" ;
159+ // CBM exit: exit-custom.S defines __after_main (jmp exit) so crt0.S's undefined
160+ // __after_main reference pulls it from the archive. exit.c provides exit() which
161+ // calls _fini() + _Exit(). exit-loop.c provides _Exit() = infinite loop.
162+ // sim only needs exit-custom.S (it defines __after_main for that platform too).
163+ const exit_files : []const []const u8 = if (std .mem .eql (u8 , pd .name , "sim" ))
164+ &.{"exit-custom.S" }
165+ else
166+ &.{ "exit-custom.S" , "exit-loop.c" , "exit.c" };
148167 libcrt0 .root_module .addCSourceFiles (.{
149168 .root = .{ .cwd_relative = b .fmt ("{s}/exit" , .{crt0_dir }) },
150- .files = &.{ exit_file } ,
169+ .files = exit_files ,
151170 });
152171
153172 // libc — platform I/O and kernal wrappers.
@@ -160,12 +179,14 @@ pub fn buildPlatform(b: *std.Build, sdk_root: []const u8, pd: Platform, opt: std
160179 .files = &.{ "putchar.c" , "stdlib.c" , "sim-io.c" },
161180 });
162181 } else {
182+ const com_c_dir = b .fmt ("{s}/c" , .{common });
163183 const asm_files : []const []const u8 = if (std .mem .eql (u8 , pd .name , "mega65" ))
164184 &.{ "filevars.s" , "kernal.S" }
165185 else
166186 &.{ "basic-header.S" , "kernal.S" , "unmap-basic.S" , "devnum.s" };
167187 libc .root_module .addIncludePath (.{ .cwd_relative = plat_dir });
168188 libc .root_module .addIncludePath (.{ .cwd_relative = comm_dir });
189+ libc .root_module .addIncludePath (.{ .cwd_relative = com_c_dir });
169190 libc .root_module .addIncludePath (.{ .cwd_relative = com_asm });
170191 libc .root_module .addIncludePath (.{ .cwd_relative = com_inc });
171192 libc .root_module .addCSourceFiles (.{
@@ -174,8 +195,48 @@ pub fn buildPlatform(b: *std.Build, sdk_root: []const u8, pd: Platform, opt: std
174195 });
175196 libc .root_module .addCSourceFiles (.{
176197 .root = .{ .cwd_relative = comm_dir },
177- .files = &.{ "abort.c" , "cbm_k_bsout.c" , "cbm_k_chrout.c" , "chrout.c" , "char-conv.c" },
198+ .files = &.{ "abort.c" , "cbm_k_bsout.c" , "cbm_k_chrout.c" , "chrout.c" , "char-conv.c" , "putchar.c" , "getchar.c" },
199+ });
200+ libc .root_module .addCSourceFiles (.{
201+ .root = .{ .cwd_relative = com_c_dir },
202+ .files = &.{ "stdio-minimal.c" , "mem.c" , "util.c" , "string.c" },
203+ });
204+ // Build printf.cc + varint.cc with lto=.none: C++ bitcode crashes LLVM LTO
205+ // codegen for the 6502 target. Pre-compiling to native code avoids the crash.
206+ const libprintf = addLib (b , "printf" , target , opt );
207+ libprintf .lto = .none ;
208+ libprintf .root_module .addIncludePath (.{ .cwd_relative = plat_dir });
209+ libprintf .root_module .addIncludePath (.{ .cwd_relative = comm_dir });
210+ libprintf .root_module .addIncludePath (.{ .cwd_relative = com_c_dir });
211+ libprintf .root_module .addIncludePath (.{ .cwd_relative = com_asm });
212+ libprintf .root_module .addIncludePath (.{ .cwd_relative = com_inc });
213+ libprintf .root_module .addCSourceFiles (.{
214+ .root = .{ .cwd_relative = com_c_dir },
215+ .files = &.{ "printf.cc" , "varint.cc" },
216+ .flags = &.{ "-fno-exceptions" , "-fno-rtti" },
217+ });
218+ // sdk/mem.s — strong __memset + abort TRUE object (same pattern as NES/SNES).
219+ const mem_obj = b .addObject (.{
220+ .name = "mem" ,
221+ .root_module = b .createModule (.{ .target = target , .optimize = opt }),
222+ });
223+ mem_obj .root_module .addCSourceFiles (.{ .root = b .path ("sdk" ), .files = &.{"mem.s" } });
224+ mem_obj .lto = .none ;
225+ // crt0.S TRUE object — mirrors cmake add_platform_object_file(common-crt0-o).
226+ // Must be linked directly (not via archive) so its .call_main / .fini_rts
227+ // anonymous sections reach the linker. lto=.none matches libcrt0 policy.
228+ const crt0_obj = b .addObject (.{
229+ .name = "crt0" ,
230+ .root_module = b .createModule (.{ .target = target , .optimize = opt }),
231+ });
232+ crt0_obj .root_module .addIncludePath (.{ .cwd_relative = com_asm });
233+ crt0_obj .root_module .addIncludePath (.{ .cwd_relative = com_inc });
234+ crt0_obj .root_module .addCSourceFiles (.{
235+ .root = .{ .cwd_relative = crt0_dir },
236+ .files = &.{"crt0.S" },
178237 });
238+ crt0_obj .lto = .none ;
239+ return .{ .crt = libcrt , .crt0 = libcrt0 , .c = libc , .printf = libprintf , .mem = mem_obj , .crt0_obj = crt0_obj };
179240 }
180241
181242 return .{ .crt = libcrt , .crt0 = libcrt0 , .c = libc };
0 commit comments