67 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
			
		
		
	
	
			67 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
# REQUIRES: x86
 | 
						|
 | 
						|
# LLD used to crash when linking against a DSO with an undefined STB_LOCAL
 | 
						|
# symbol in the global part of the dynamic symbol table (i.e. an STB_LOCAL
 | 
						|
# symbol with an index >= the sh_info of the dynamic symbol table section). Such
 | 
						|
# a DSO is very broken, because local symbols should precede all global symbols
 | 
						|
# in the symbol table, and because having a symbol that's both undefined and
 | 
						|
# STB_LOCAL is a nonsensical combination. Nevertheless, we should warn on such
 | 
						|
# input files instead of crashing.
 | 
						|
 | 
						|
# We've found actual broken DSOs of this sort in the wild, but for this test, we
 | 
						|
# created a reduced broken input file. There are no tools capable of producing a
 | 
						|
# broken DSO of this nature, so instead we created a valid DSO with an undefined
 | 
						|
# global symbol in the dynamic symbol table and then manually edited the binary
 | 
						|
# to make that symbol local. The valid DSO was created as follows:
 | 
						|
 | 
						|
```
 | 
						|
% cat undef.s
 | 
						|
.hidden bar
 | 
						|
bar:
 | 
						|
	movq	foo@GOT, %rax
 | 
						|
 | 
						|
% llvm-mc -triple=x86_64-linux-gnu -filetype=obj -o undef.o undef.s
 | 
						|
% ld.lld --no-rosegment -shared -o undefined-local-symbol-in-dso.so undef.o
 | 
						|
% strip undef.so
 | 
						|
```
 | 
						|
 | 
						|
# (--no-rosegment and stripping are unnecessary; they just produce a smaller
 | 
						|
# binary)
 | 
						|
 | 
						|
# This DSO should only have a single dynamic symbol table entry for foo, and
 | 
						|
# then we can use a small C program to modify that symbol table entry to be
 | 
						|
# STB_LOCAL instead of STB_GLOBAL.
 | 
						|
 | 
						|
```
 | 
						|
#include <elf.h>
 | 
						|
#include <stdio.h>
 | 
						|
 | 
						|
int main(int argc, char *argv[]) {
 | 
						|
  FILE *F = fopen(argv[1], "rb+");
 | 
						|
 | 
						|
  Elf64_Ehdr Ehdr;
 | 
						|
  fread(&Ehdr, sizeof(Ehdr), 1, F);
 | 
						|
  fseek(F, Ehdr.e_shoff, SEEK_SET);
 | 
						|
 | 
						|
  Elf64_Shdr Shdr;
 | 
						|
  do {
 | 
						|
    fread(&Shdr, sizeof(Shdr), 1, F);
 | 
						|
  } while (Shdr.sh_type != SHT_DYNSYM);
 | 
						|
 | 
						|
  Elf64_Sym Sym;
 | 
						|
  fseek(F, Shdr.sh_offset + sizeof(Elf64_Sym), SEEK_SET);
 | 
						|
  fread(&Sym, sizeof(Sym), 1, F);
 | 
						|
  Sym.st_info = STB_LOCAL << 4 | ELF64_ST_TYPE(Sym.st_info);
 | 
						|
  fseek(F, Shdr.sh_offset + sizeof(Elf64_Sym), SEEK_SET);
 | 
						|
  fwrite(&Sym, sizeof(Sym), 1, F);
 | 
						|
  fclose(F);
 | 
						|
}
 | 
						|
```
 | 
						|
 | 
						|
# (the C program just takes its input DSO and modifies the binding of the first
 | 
						|
# dynamic symbol table entry to be STB_LOCAL instead of STB_GLOBAL)
 | 
						|
 | 
						|
# RUN: ld.lld %p/Inputs/undefined-local-symbol-in-dso.so -o %t 2>&1 | \
 | 
						|
# RUN:   FileCheck -check-prefix=WARN %s
 | 
						|
# WARN: found local symbol 'foo' in global part of symbol table in file {{.*}}undefined-local-symbol-in-dso.so
 |