(* BSD 2-Clause License Copyright (c) 2019, Anton Krotov All rights reserved. *) MODULE ELF; IMPORT BIN, WR := WRITER, CHL := CHUNKLISTS, LISTS; CONST EI_NIDENT = 16; ET_EXEC = 2; ET_DYN = 3; EM_386 = 3; EM_8664 = 3EH; ELFCLASS32 = 1; ELFCLASS64 = 2; ELFDATA2LSB = 1; ELFDATA2MSB = 2; PF_X = 1; PF_W = 2; PF_R = 4; TYPE Elf32_Ehdr = RECORD e_ident: ARRAY EI_NIDENT OF BYTE; e_type, e_machine: WCHAR; e_version, e_entry, e_phoff, e_shoff, e_flags: INTEGER; e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum, e_shstrndx: WCHAR END; Elf32_Phdr = RECORD p_type, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz, p_flags, p_align: INTEGER END; Elf32_Dyn = POINTER TO RECORD (LISTS.ITEM) d_tag, d_val: INTEGER END; Elf32_Sym = POINTER TO RECORD (LISTS.ITEM) name, value, size: INTEGER; info, other: CHAR; shndx: WCHAR END; FILE = WR.FILE; VAR dynamic: LISTS.LIST; strtab: CHL.BYTELIST; symtab: LISTS.LIST; hashtab, bucket, chain: CHL.INTLIST; PROCEDURE align (n, _align: INTEGER): INTEGER; BEGIN IF n MOD _align # 0 THEN n := n + _align - (n MOD _align) END RETURN n END align; PROCEDURE Write16 (file: FILE; w: WCHAR); BEGIN WR.Write16LE(file, ORD(w)) END Write16; PROCEDURE WritePH (file: FILE; ph: Elf32_Phdr); BEGIN WR.Write32LE(file, ph.p_type); WR.Write32LE(file, ph.p_offset); WR.Write32LE(file, ph.p_vaddr); WR.Write32LE(file, ph.p_paddr); WR.Write32LE(file, ph.p_filesz); WR.Write32LE(file, ph.p_memsz); WR.Write32LE(file, ph.p_flags); WR.Write32LE(file, ph.p_align) END WritePH; PROCEDURE WritePH64 (file: FILE; ph: Elf32_Phdr); BEGIN WR.Write32LE(file, ph.p_type); WR.Write32LE(file, ph.p_flags); WR.Write64LE(file, ph.p_offset); WR.Write64LE(file, ph.p_vaddr); WR.Write64LE(file, ph.p_paddr); WR.Write64LE(file, ph.p_filesz); WR.Write64LE(file, ph.p_memsz); WR.Write64LE(file, ph.p_align) END WritePH64; PROCEDURE fixup (program: BIN.PROGRAM; text, data, bss: INTEGER; amd64: BOOLEAN); VAR reloc: BIN.RELOC; L, delta: INTEGER; BEGIN reloc := program.rel_list.first(BIN.RELOC); WHILE reloc # NIL DO L := BIN.get32le(program.code, reloc.offset); delta := 3 - reloc.offset - text - 7 * ORD(amd64); CASE reloc.opcode OF |BIN.PICDATA: BIN.put32le(program.code, reloc.offset, L + data + delta) |BIN.PICCODE: BIN.put32le(program.code, reloc.offset, BIN.GetLabel(program, L) + text + delta) |BIN.PICBSS: BIN.put32le(program.code, reloc.offset, L + bss + delta) END; reloc := reloc.next(BIN.RELOC) END; END fixup; PROCEDURE NewDyn (tag, val: INTEGER); VAR dyn: Elf32_Dyn; BEGIN NEW(dyn); dyn.d_tag := tag; dyn.d_val := val; LISTS.push(dynamic, dyn) END NewDyn; PROCEDURE NewSym (name, value, size: INTEGER; info, other: CHAR; shndx: WCHAR); VAR sym: Elf32_Sym; BEGIN NEW(sym); sym.name := name; sym.value := value; sym.size := size; sym.info := info; sym.other := other; sym.shndx := shndx; LISTS.push(symtab, sym) END NewSym; PROCEDURE HashStr (name: ARRAY OF CHAR): INTEGER; VAR i, h: INTEGER; g: SET; BEGIN h := 0; i := 0; WHILE name[i] # 0X DO h := h * 16 + ORD(name[i]); g := BITS(h) * {28..31}; h := ORD(BITS(h) / BITS(LSR(ORD(g), 24)) - g); INC(i) END RETURN h END HashStr; PROCEDURE MakeHash (bucket, chain: CHL.INTLIST; symCount: INTEGER); VAR symi, hi, k: INTEGER; BEGIN FOR symi := 0 TO symCount - 1 DO CHL.SetInt(chain, symi, 0); hi := CHL.GetInt(hashtab, symi) MOD symCount; IF CHL.GetInt(bucket, hi) # 0 THEN k := symi; WHILE CHL.GetInt(chain, k) # 0 DO k := CHL.GetInt(chain, k) END; CHL.SetInt(chain, k, CHL.GetInt(bucket, hi)) END; CHL.SetInt(bucket, hi, symi) END END MakeHash; PROCEDURE write* (program: BIN.PROGRAM; FileName: ARRAY OF CHAR; fini: INTEGER; so, amd64: BOOLEAN); CONST interp = 0; dyn = 1; header = 2; text = 3; data = 4; bss = 5; linuxInterpreter64 = "/lib64/ld-linux-x86-64.so.2"; linuxInterpreter32 = "/lib/ld-linux.so.2"; exeBaseAddress32 = 8048000H; exeBaseAddress64 = 400000H; dllBaseAddress = 0; DT_NULL = 0; DT_NEEDED = 1; DT_HASH = 4; DT_STRTAB = 5; DT_SYMTAB = 6; DT_RELA = 7; DT_RELASZ = 8; DT_RELAENT = 9; DT_STRSZ = 10; DT_SYMENT = 11; DT_INIT = 12; DT_FINI = 13; DT_SONAME = 14; DT_REL = 17; DT_RELSZ = 18; DT_RELENT = 19; VAR ehdr: Elf32_Ehdr; phdr: ARRAY 16 OF Elf32_Phdr; i, BaseAdr, offset, pad, VA, symCount: INTEGER; SizeOf: RECORD header, code, data, bss: INTEGER END; Offset: RECORD symtab, reltab, hash, strtab, dyn: INTEGER END; File: FILE; Interpreter: ARRAY 40 OF CHAR; lenInterpreter: INTEGER; item: LISTS.ITEM; Name: ARRAY 2048 OF CHAR; BEGIN dynamic := LISTS.create(NIL); symtab := LISTS.create(NIL); strtab := CHL.CreateByteList(); IF amd64 THEN BaseAdr := exeBaseAddress64; Interpreter := linuxInterpreter64 ELSE BaseAdr := exeBaseAddress32; Interpreter := linuxInterpreter32 END; IF so THEN BaseAdr := dllBaseAddress END; lenInterpreter := LENGTH(Interpreter) + 1; SizeOf.code := CHL.Length(program.code); SizeOf.data := CHL.Length(program.data); SizeOf.bss := program.bss; ehdr.e_ident[0] := 7FH; ehdr.e_ident[1] := ORD("E"); ehdr.e_ident[2] := ORD("L"); ehdr.e_ident[3] := ORD("F"); IF amd64 THEN ehdr.e_ident[4] := ELFCLASS64 ELSE ehdr.e_ident[4] := ELFCLASS32 END; ehdr.e_ident[5] := ELFDATA2LSB; ehdr.e_ident[6] := 1; ehdr.e_ident[7] := 3; FOR i := 8 TO EI_NIDENT - 1 DO ehdr.e_ident[i] := 0 END; IF so THEN ehdr.e_type := WCHR(ET_DYN) ELSE ehdr.e_type := WCHR(ET_EXEC) END; ehdr.e_version := 1; ehdr.e_shoff := 0; ehdr.e_flags := 0; ehdr.e_shnum := WCHR(0); ehdr.e_shstrndx := WCHR(0); ehdr.e_phnum := WCHR(6); IF amd64 THEN ehdr.e_machine := WCHR(EM_8664); ehdr.e_phoff := 40H; ehdr.e_ehsize := WCHR(40H); ehdr.e_phentsize := WCHR(38H); ehdr.e_shentsize := WCHR(40H) ELSE ehdr.e_machine := WCHR(EM_386); ehdr.e_phoff := 34H; ehdr.e_ehsize := WCHR(34H); ehdr.e_phentsize := WCHR(20H); ehdr.e_shentsize := WCHR(28H) END; SizeOf.header := ORD(ehdr.e_ehsize) + ORD(ehdr.e_phentsize) * ORD(ehdr.e_phnum); phdr[interp].p_type := 3; phdr[interp].p_offset := SizeOf.header; phdr[interp].p_vaddr := BaseAdr + phdr[interp].p_offset; phdr[interp].p_paddr := phdr[interp].p_vaddr; phdr[interp].p_filesz := lenInterpreter; phdr[interp].p_memsz := lenInterpreter; phdr[interp].p_flags := PF_R; phdr[interp].p_align := 1; phdr[dyn].p_type := 2; phdr[dyn].p_offset := phdr[interp].p_offset + phdr[interp].p_filesz; phdr[dyn].p_vaddr := BaseAdr + phdr[dyn].p_offset; phdr[dyn].p_paddr := phdr[dyn].p_vaddr; hashtab := CHL.CreateIntList(); CHL.PushInt(hashtab, HashStr("")); NewSym(CHL.PushStr(strtab, ""), 0, 0, 0X, 0X, 0X); CHL.PushInt(hashtab, HashStr("dlopen")); NewSym(CHL.PushStr(strtab, "dlopen"), 0, 0, 12X, 0X, 0X); CHL.PushInt(hashtab, HashStr("dlsym")); NewSym(CHL.PushStr(strtab, "dlsym"), 0, 0, 12X, 0X, 0X); IF so THEN item := program.exp_list.first; WHILE item # NIL DO ASSERT(CHL.GetStr(program.export, item(BIN.EXPRT).nameoffs, Name)); CHL.PushInt(hashtab, HashStr(Name)); NewSym(CHL.PushStr(strtab, Name), item(BIN.EXPRT).label, 0, 12X, 0X, 0X); item := item.next END; ASSERT(CHL.GetStr(program.data, program.modname, Name)) END; symCount := LISTS.count(symtab); bucket := CHL.CreateIntList(); chain := CHL.CreateIntList(); FOR i := 1 TO symCount DO CHL.PushInt(bucket, 0); CHL.PushInt(chain, 0) END; MakeHash(bucket, chain, symCount); NewDyn(DT_NEEDED, CHL.PushStr(strtab, "libdl.so.2")); NewDyn(DT_STRTAB, 0); NewDyn(DT_STRSZ, CHL.Length(strtab)); NewDyn(DT_SYMTAB, 0); IF amd64 THEN NewDyn(DT_SYMENT, 24); NewDyn(DT_RELA, 0); NewDyn(DT_RELASZ, 48); NewDyn(DT_RELAENT, 24) ELSE NewDyn(DT_SYMENT, 16); NewDyn(DT_REL, 0); NewDyn(DT_RELSZ, 16); NewDyn(DT_RELENT, 8) END; NewDyn(DT_HASH, 0); IF so THEN NewDyn(DT_SONAME, CHL.PushStr(strtab, Name)); NewDyn(DT_INIT, 0); NewDyn(DT_FINI, 0) END; NewDyn(DT_NULL, 0); Offset.symtab := LISTS.count(dynamic) * (8 + 8 * ORD(amd64)); Offset.reltab := Offset.symtab + symCount * (16 + 8 * ORD(amd64)); Offset.hash := Offset.reltab + (8 + 16 * ORD(amd64)) * 2; Offset.strtab := Offset.hash + (symCount * 2 + 2) * 4; Offset.dyn := phdr[dyn].p_offset; item := LISTS.getidx(dynamic, 1); item(Elf32_Dyn).d_val := Offset.strtab + Offset.dyn + BaseAdr; item := LISTS.getidx(dynamic, 3); item(Elf32_Dyn).d_val := Offset.symtab + Offset.dyn + BaseAdr; item := LISTS.getidx(dynamic, 5); item(Elf32_Dyn).d_val := Offset.reltab + Offset.dyn + BaseAdr; item := LISTS.getidx(dynamic, 8); item(Elf32_Dyn).d_val := Offset.hash + Offset.dyn + BaseAdr; phdr[dyn].p_filesz := Offset.strtab + CHL.Length(strtab) + 8 + 8 * ORD(amd64); phdr[dyn].p_memsz := phdr[dyn].p_filesz; phdr[dyn].p_flags := PF_R; phdr[dyn].p_align := 1; offset := 0; phdr[header].p_type := 1; phdr[header].p_offset := offset; phdr[header].p_vaddr := BaseAdr; phdr[header].p_paddr := BaseAdr; phdr[header].p_filesz := 244 + 156 * ORD(amd64) + lenInterpreter + phdr[dyn].p_filesz; phdr[header].p_memsz := phdr[header].p_filesz; phdr[header].p_flags := PF_R + PF_W; phdr[header].p_align := 1000H; offset := offset + phdr[header].p_filesz; VA := BaseAdr + offset + 1000H; phdr[text].p_type := 1; phdr[text].p_offset := offset; phdr[text].p_vaddr := VA; phdr[text].p_paddr := VA; phdr[text].p_filesz := SizeOf.code; phdr[text].p_memsz := SizeOf.code; phdr[text].p_flags := PF_X + PF_R; phdr[text].p_align := 1000H; ehdr.e_entry := phdr[text].p_vaddr; offset := offset + phdr[text].p_filesz; VA := BaseAdr + offset + 2000H; pad := (16 - VA MOD 16) MOD 16; phdr[data].p_type := 1; phdr[data].p_offset := offset; phdr[data].p_vaddr := VA; phdr[data].p_paddr := VA; phdr[data].p_filesz := SizeOf.data + pad; phdr[data].p_memsz := SizeOf.data + pad; phdr[data].p_flags := PF_R + PF_W; phdr[data].p_align := 1000H; offset := offset + phdr[data].p_filesz; VA := BaseAdr + offset + 3000H; phdr[bss].p_type := 1; phdr[bss].p_offset := offset; phdr[bss].p_vaddr := VA; phdr[bss].p_paddr := VA; phdr[bss].p_filesz := 0; phdr[bss].p_memsz := SizeOf.bss + 16; phdr[bss].p_flags := PF_R + PF_W; phdr[bss].p_align := 1000H; fixup(program, ehdr.e_entry, phdr[data].p_vaddr + pad, align(phdr[bss].p_vaddr, 16), amd64); item := symtab.first; WHILE item # NIL DO IF item(Elf32_Sym).value # 0 THEN INC(item(Elf32_Sym).value, ehdr.e_entry) END; item := item.next END; IF so THEN item := LISTS.getidx(dynamic, 10); item(Elf32_Dyn).d_val := ehdr.e_entry; item := LISTS.getidx(dynamic, 11); item(Elf32_Dyn).d_val := BIN.GetLabel(program, fini) + ehdr.e_entry END; File := WR.Create(FileName); FOR i := 0 TO EI_NIDENT - 1 DO WR.WriteByte(File, ehdr.e_ident[i]) END; Write16(File, ehdr.e_type); Write16(File, ehdr.e_machine); WR.Write32LE(File, ehdr.e_version); IF amd64 THEN WR.Write64LE(File, ehdr.e_entry); WR.Write64LE(File, ehdr.e_phoff); WR.Write64LE(File, ehdr.e_shoff) ELSE WR.Write32LE(File, ehdr.e_entry); WR.Write32LE(File, ehdr.e_phoff); WR.Write32LE(File, ehdr.e_shoff) END; WR.Write32LE(File, ehdr.e_flags); Write16(File, ehdr.e_ehsize); Write16(File, ehdr.e_phentsize); Write16(File, ehdr.e_phnum); Write16(File, ehdr.e_shentsize); Write16(File, ehdr.e_shnum); Write16(File, ehdr.e_shstrndx); IF amd64 THEN WritePH64(File, phdr[interp]); WritePH64(File, phdr[dyn]); WritePH64(File, phdr[header]); WritePH64(File, phdr[text]); WritePH64(File, phdr[data]); WritePH64(File, phdr[bss]) ELSE WritePH(File, phdr[interp]); WritePH(File, phdr[dyn]); WritePH(File, phdr[header]); WritePH(File, phdr[text]); WritePH(File, phdr[data]); WritePH(File, phdr[bss]) END; FOR i := 0 TO lenInterpreter - 1 DO WR.WriteByte(File, ORD(Interpreter[i])) END; i := 0; IF amd64 THEN item := dynamic.first; WHILE item # NIL DO WR.Write64LE(File, item(Elf32_Dyn).d_tag); WR.Write64LE(File, item(Elf32_Dyn).d_val); item := item.next END; item := symtab.first; WHILE item # NIL DO WR.Write32LE(File, item(Elf32_Sym).name); WR.WriteByte(File, ORD(item(Elf32_Sym).info)); WR.WriteByte(File, ORD(item(Elf32_Sym).other)); Write16(File, item(Elf32_Sym).shndx); WR.Write64LE(File, item(Elf32_Sym).value); WR.Write64LE(File, item(Elf32_Sym).size); item := item.next END; WR.Write64LE(File, phdr[dyn].p_filesz + Offset.dyn + BaseAdr - 16); WR.Write32LE(File, 1); WR.Write32LE(File, 1); WR.Write64LE(File, 0); WR.Write64LE(File, phdr[dyn].p_filesz + Offset.dyn + BaseAdr - 8); WR.Write32LE(File, 1); WR.Write32LE(File, 2); WR.Write64LE(File, 0); WR.Write32LE(File, symCount); WR.Write32LE(File, symCount); FOR i := 0 TO symCount - 1 DO WR.Write32LE(File, CHL.GetInt(bucket, i)) END; FOR i := 0 TO symCount - 1 DO WR.Write32LE(File, CHL.GetInt(chain, i)) END; CHL.WriteToFile(File, strtab); WR.Write64LE(File, 0); WR.Write64LE(File, 0) ELSE item := dynamic.first; WHILE item # NIL DO WR.Write32LE(File, item(Elf32_Dyn).d_tag); WR.Write32LE(File, item(Elf32_Dyn).d_val); item := item.next END; item := symtab.first; WHILE item # NIL DO WR.Write32LE(File, item(Elf32_Sym).name); WR.Write32LE(File, item(Elf32_Sym).value); WR.Write32LE(File, item(Elf32_Sym).size); WR.WriteByte(File, ORD(item(Elf32_Sym).info)); WR.WriteByte(File, ORD(item(Elf32_Sym).other)); Write16(File, item(Elf32_Sym).shndx); item := item.next END; WR.Write32LE(File, phdr[dyn].p_filesz + Offset.dyn + BaseAdr - 8); WR.Write32LE(File, 00000101H); WR.Write32LE(File, phdr[dyn].p_filesz + Offset.dyn + BaseAdr - 4); WR.Write32LE(File, 00000201H); WR.Write32LE(File, symCount); WR.Write32LE(File, symCount); FOR i := 0 TO symCount - 1 DO WR.Write32LE(File, CHL.GetInt(bucket, i)) END; FOR i := 0 TO symCount - 1 DO WR.Write32LE(File, CHL.GetInt(chain, i)) END; CHL.WriteToFile(File, strtab); WR.Write32LE(File, 0); WR.Write32LE(File, 0) END; CHL.WriteToFile(File, program.code); WHILE pad > 0 DO WR.WriteByte(File, 0); DEC(pad) END; CHL.WriteToFile(File, program.data); WR.Close(File) END write; END ELF.