authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2024-08-17 11:42:32+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-08-23 11:09:20-07:00
log5dd2bb525d1f19969c450c2b99a71f866a4f01ff
tree3dac4b3d3d5facfdb2c8998ff50a45bfa0015da2
parentdf6907f6019ec178735f06e1b55cef6a90234201

glibc: Define _IO_stdin_used in start code and reference it in stub asm.

This is necessary to inform the real, non-stub glibc that a program built with Zig is using a modern `FILE` structure, i.e. glibc 2.1+. This is particularly important on lesser-used architectures where the legacy code is poorly tested; for example, glibc 2.40 introduced a regression for the legacy case in the libio cleanup code, causing all Zig-compiled MIPS binaries to crash on exit.

2 files changed, 59 insertions(+), 1 deletions(-)

lib/libc/glibc/csu/init.c created+23
...@@ -0,0 +1,23 @@
1/* Special startup support.
2 Copyright (C) 1997-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19/* Vestigial libio version number. Some code in libio checks whether
20 this symbol exists in the executable, but nothing looks at its
21 value anymore; the value it was historically set to has been
22 preserved out of an abundance of caution. */
23const int _IO_stdin_used = 0x20001;
src/glibc.zig+36-1
...@@ -286,7 +286,11 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progre...@@ -286,7 +286,11 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progre
286 .owner = undefined,286 .owner = undefined,
287 };287 };
288 };288 };
289 var files = [_]Compilation.CSourceFile{ start_o, abi_note_o };289 const init_o: Compilation.CSourceFile = .{
290 .src_path = try lib_path(comp, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "init.c"),
291 .owner = undefined,
292 };
293 var files = [_]Compilation.CSourceFile{ start_o, abi_note_o, init_o };
290 return comp.build_crt_file("Scrt1", .Obj, .@"glibc Scrt1.o", prog_node, &files);294 return comp.build_crt_file("Scrt1", .Obj, .@"glibc Scrt1.o", prog_node, &files);
291 },295 },
292 .libc_nonshared_a => {296 .libc_nonshared_a => {
...@@ -682,6 +686,12 @@ pub const BuiltSharedObjects = struct {...@@ -682,6 +686,12 @@ pub const BuiltSharedObjects = struct {
682686
683const all_map_basename = "all.map";687const all_map_basename = "all.map";
684688
689fn wordDirective(target: std.Target) []const u8 {
690 // Based on its description in the GNU `as` manual, you might assume that `.word` is sized
691 // according to the target word size. But no; that would just make too much sense.
692 return if (target.ptrBitWidth() == 64) ".quad" else ".long";
693}
694
685pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !void {695pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !void {
686 const tracy = trace(@src());696 const tracy = trace(@src());
687 defer tracy.end();697 defer tracy.end();
...@@ -923,6 +933,31 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi...@@ -923,6 +933,31 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
923933
924 try stubs_asm.appendSlice(".data\n");934 try stubs_asm.appendSlice(".data\n");
925935
936 // For some targets, the real `libc.so.6` will contain a weak reference to `_IO_stdin_used`,
937 // making the linker put the symbol in the dynamic symbol table. We likewise need to emit a
938 // reference to it here for that effect, or it will not show up, which in turn will cause
939 // the real glibc to think that the program was built against an ancient `FILE` structure
940 // (pre-glibc 2.1).
941 //
942 // Note that glibc only compiles in the legacy compatibility code for some targets; it
943 // depends on what is defined in the `shlib-versions` file for the particular architecture
944 // and ABI. Those files are preprocessed by 2 separate tools during the glibc build to get
945 // the final `abi-versions.h`, so it would be quite brittle to try to condition our emission
946 // of the `_IO_stdin_used` reference in the exact same way. The only downside of emitting
947 // the reference unconditionally is that it ends up being unused for newer targets; it
948 // otherwise has no negative effect.
949 //
950 // glibc uses a weak reference because it has to work with programs compiled against pre-2.1
951 // versions where the symbol didn't exist. We only care about modern glibc versions, so use
952 // a strong reference.
953 if (std.mem.eql(u8, lib.name, "c")) {
954 try stubs_asm.writer().print(
955 \\.globl _IO_stdin_used
956 \\{s} _IO_stdin_used
957 \\
958 , .{wordDirective(target)});
959 }
960
926 const obj_inclusions_len = mem.readInt(u16, metadata.inclusions[inc_i..][0..2], .little);961 const obj_inclusions_len = mem.readInt(u16, metadata.inclusions[inc_i..][0..2], .little);
927 inc_i += 2;962 inc_i += 2;
928963