abiAndDynamicLinkerFromFile: return an error when the file is not actually dynamic (#19928)

This commit is contained in:
Pratham 2025-03-27 01:24:10 +05:30 committed by GitHub
parent 263ba34619
commit 5bb9963bbb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -505,6 +505,7 @@ pub const AbiAndDynamicLinkerFromFileError = error{
UnexpectedEndOfFile, UnexpectedEndOfFile,
NameTooLong, NameTooLong,
ProcessNotFound, ProcessNotFound,
StaticElfFile,
}; };
pub fn abiAndDynamicLinkerFromFile( pub fn abiAndDynamicLinkerFromFile(
@ -550,6 +551,8 @@ pub fn abiAndDynamicLinkerFromFile(
if (phentsize > @sizeOf(elf.Elf64_Phdr)) return error.InvalidElfFile; if (phentsize > @sizeOf(elf.Elf64_Phdr)) return error.InvalidElfFile;
var ph_i: u16 = 0; var ph_i: u16 = 0;
var got_dyn_section: bool = false;
while (ph_i < phnum) { while (ph_i < phnum) {
// Reserve some bytes so that we can deref the 64-bit struct fields // Reserve some bytes so that we can deref the 64-bit struct fields
// even when the ELF file is 32-bits. // even when the ELF file is 32-bits.
@ -565,61 +568,69 @@ pub fn abiAndDynamicLinkerFromFile(
const ph64: *elf.Elf64_Phdr = @ptrCast(@alignCast(&ph_buf[ph_buf_i])); const ph64: *elf.Elf64_Phdr = @ptrCast(@alignCast(&ph_buf[ph_buf_i]));
const p_type = elfInt(is_64, need_bswap, ph32.p_type, ph64.p_type); const p_type = elfInt(is_64, need_bswap, ph32.p_type, ph64.p_type);
switch (p_type) { switch (p_type) {
elf.PT_INTERP => if (look_for_ld) { elf.PT_INTERP => {
const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset); got_dyn_section = true;
const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz);
if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong;
const filesz: usize = @intCast(p_filesz);
_ = try preadAtLeast(file, result.dynamic_linker.buffer[0..filesz], p_offset, filesz);
// PT_INTERP includes a null byte in filesz.
const len = filesz - 1;
// dynamic_linker.max_byte is "max", not "len".
// We know it will fit in u8 because we check against dynamic_linker.buffer.len above.
result.dynamic_linker.len = @intCast(len);
// Use it to determine ABI. if (look_for_ld) {
const full_ld_path = result.dynamic_linker.buffer[0..len]; const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset);
for (ld_info_list) |ld_info| { const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz);
const standard_ld_basename = fs.path.basename(ld_info.ld.get().?); if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong;
if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) { const filesz: usize = @intCast(p_filesz);
result.abi = ld_info.abi; _ = try preadAtLeast(file, result.dynamic_linker.buffer[0..filesz], p_offset, filesz);
break; // PT_INTERP includes a null byte in filesz.
const len = filesz - 1;
// dynamic_linker.max_byte is "max", not "len".
// We know it will fit in u8 because we check against dynamic_linker.buffer.len above.
result.dynamic_linker.len = @intCast(len);
// Use it to determine ABI.
const full_ld_path = result.dynamic_linker.buffer[0..len];
for (ld_info_list) |ld_info| {
const standard_ld_basename = fs.path.basename(ld_info.ld.get().?);
if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) {
result.abi = ld_info.abi;
break;
}
} }
} }
}, },
// We only need this for detecting glibc version. // We only need this for detecting glibc version.
elf.PT_DYNAMIC => if (builtin.target.os.tag == .linux and result.isGnuLibC() and elf.PT_DYNAMIC => {
query.glibc_version == null) got_dyn_section = true;
{
var dyn_off = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset); if (builtin.target.os.tag == .linux and result.isGnuLibC() and
const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz); query.glibc_version == null)
const dyn_size: usize = if (is_64) @sizeOf(elf.Elf64_Dyn) else @sizeOf(elf.Elf32_Dyn); {
const dyn_num = p_filesz / dyn_size; var dyn_off = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset);
var dyn_buf: [16 * @sizeOf(elf.Elf64_Dyn)]u8 align(@alignOf(elf.Elf64_Dyn)) = undefined; const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz);
var dyn_i: usize = 0; const dyn_size: usize = if (is_64) @sizeOf(elf.Elf64_Dyn) else @sizeOf(elf.Elf32_Dyn);
dyn: while (dyn_i < dyn_num) { const dyn_num = p_filesz / dyn_size;
// Reserve some bytes so that we can deref the 64-bit struct fields var dyn_buf: [16 * @sizeOf(elf.Elf64_Dyn)]u8 align(@alignOf(elf.Elf64_Dyn)) = undefined;
// even when the ELF file is 32-bits. var dyn_i: usize = 0;
const dyn_reserve: usize = @sizeOf(elf.Elf64_Dyn) - @sizeOf(elf.Elf32_Dyn); dyn: while (dyn_i < dyn_num) {
const dyn_read_byte_len = try preadAtLeast( // Reserve some bytes so that we can deref the 64-bit struct fields
file, // even when the ELF file is 32-bits.
dyn_buf[0 .. dyn_buf.len - dyn_reserve], const dyn_reserve: usize = @sizeOf(elf.Elf64_Dyn) - @sizeOf(elf.Elf32_Dyn);
dyn_off, const dyn_read_byte_len = try preadAtLeast(
dyn_size, file,
); dyn_buf[0 .. dyn_buf.len - dyn_reserve],
var dyn_buf_i: usize = 0; dyn_off,
while (dyn_buf_i < dyn_read_byte_len and dyn_i < dyn_num) : ({ dyn_size,
dyn_i += 1; );
dyn_off += dyn_size; var dyn_buf_i: usize = 0;
dyn_buf_i += dyn_size; while (dyn_buf_i < dyn_read_byte_len and dyn_i < dyn_num) : ({
}) { dyn_i += 1;
const dyn32: *elf.Elf32_Dyn = @ptrCast(@alignCast(&dyn_buf[dyn_buf_i])); dyn_off += dyn_size;
const dyn64: *elf.Elf64_Dyn = @ptrCast(@alignCast(&dyn_buf[dyn_buf_i])); dyn_buf_i += dyn_size;
const tag = elfInt(is_64, need_bswap, dyn32.d_tag, dyn64.d_tag); }) {
const val = elfInt(is_64, need_bswap, dyn32.d_val, dyn64.d_val); const dyn32: *elf.Elf32_Dyn = @ptrCast(@alignCast(&dyn_buf[dyn_buf_i]));
if (tag == elf.DT_RUNPATH) { const dyn64: *elf.Elf64_Dyn = @ptrCast(@alignCast(&dyn_buf[dyn_buf_i]));
rpath_offset = val; const tag = elfInt(is_64, need_bswap, dyn32.d_tag, dyn64.d_tag);
break :dyn; const val = elfInt(is_64, need_bswap, dyn32.d_val, dyn64.d_val);
if (tag == elf.DT_RUNPATH) {
rpath_offset = val;
break :dyn;
}
} }
} }
} }
@ -629,6 +640,10 @@ pub fn abiAndDynamicLinkerFromFile(
} }
} }
if (!got_dyn_section) {
return error.StaticElfFile;
}
if (builtin.target.os.tag == .linux and result.isGnuLibC() and if (builtin.target.os.tag == .linux and result.isGnuLibC() and
query.glibc_version == null) query.glibc_version == null)
{ {
@ -1208,6 +1223,7 @@ fn detectAbiAndDynamicLinker(
error.Unexpected, error.Unexpected,
error.UnexpectedEndOfFile, error.UnexpectedEndOfFile,
error.NameTooLong, error.NameTooLong,
error.StaticElfFile,
// Finally, we fall back on the standard path. // Finally, we fall back on the standard path.
=> |e| { => |e| {
std.log.warn("Encountered error: {s}, falling back to default ABI and dynamic linker.", .{@errorName(e)}); std.log.warn("Encountered error: {s}, falling back to default ABI and dynamic linker.", .{@errorName(e)});