From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id 525CC48CF8E; Tue, 30 May 2023 11:09:53 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 525CC48CF8E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1685434193; bh=pD4Da8f8XrNRs0/qNdiONf8E4Xb4AFW1QWbpH2e6ETI=; h=To:Date:Subject:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=aZIOV2w7jG6uwecJJcVBWrwakpuhO6p0of8YYBWtyeWsIq39so4ZZ/NuhCscVh9pj mUGDBqIohPvcSGNR6OmhLhJgqVLtwoCoRrzihY8tgP0JFun4oKxEocWLFXZs5+sORG HFQm3izAZoBSB72rLPWwy+o2aOkpxplRKJhy7Yok= Received: from mail-ed1-f52.google.com (mail-ed1-f52.google.com [209.85.208.52]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id CA87C41377D for ; Tue, 30 May 2023 11:09:51 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org CA87C41377D Received: by mail-ed1-f52.google.com with SMTP id 4fb4d7f45d1cf-5147dce372eso6049148a12.0 for ; Tue, 30 May 2023 01:09:51 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1685434190; x=1688026190; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=g3Q5kJ203IFSPvjqDUPaKGrDUMR5FgjPVOM/d4TM8BA=; b=P0aEwNPPTwfaJVW6WJQbeXBH3iG3XR+s4WGovkUAkFFouAf4Mm+coyHGAlURT3qIVq sZJ4gIUG8N9no6oKgEUoK3isOfSLEaURmo6ocJh8cdEyM8yGHzt1r6cT/Ow4b92VqbxL Rwws2hLe5cEI8pfoVdbO2qLfnabqUMN52uJHgDvQedHIeqdlmePm0MaiTt6ik+L67ti8 yk0XQHu60yeYHLiODU2bKqPMd+AarlN3+tXr5GqcRyPgaq/kQJ1UGVZslgdzVA5xioOc u+Yuz+JAm3jwZGHhlexbMjR0ks0DtQcEXGpNMsGRNaNXQeWdGhscMAKn4yYlEReOKRnx nzNQ== X-Gm-Message-State: AC+VfDyyG/NqEWYUxPWDsU8z4skElS4U7wAX/gz8SR3jV0SnWWvQwCeh EW6BNmbH4rXL2h9H/89K06r//P1aA9bbjQ== X-Google-Smtp-Source: ACHHUZ5bHBcdQIeinBqdVvs795m34Oq0kNyQN1qsp04jtyi+g4mIdKQD8qZqnmPCHy9S0LG1Vl1Qkw== X-Received: by 2002:a17:907:7b98:b0:973:940e:a01b with SMTP id ne24-20020a1709077b9800b00973940ea01bmr1763542ejc.60.1685434190258; Tue, 30 May 2023 01:09:50 -0700 (PDT) Received: from pony.. ([185.6.247.97]) by smtp.gmail.com with ESMTPSA id j23-20020a17090643d700b0095850aef138sm7000735ejn.6.2023.05.30.01.09.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 30 May 2023 01:09:49 -0700 (PDT) To: tarantool-patches@dev.tarantool.org, Sergey Kaplun , Maxim Kokryashkin Date: Tue, 30 May 2023 11:09:34 +0300 Message-Id: <7be88fa24695ad2f3c15a9a23bd884ca0acc36f5.1685433737.git.sergeyb@tarantool.org> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [Tarantool-patches] [PATCH luajit v2 1/1] Fix saved bytecode encapsulated in ELF objects. X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Sergey Bronnikov via Tarantool-patches Reply-To: Sergey Bronnikov Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" From: Sergey Bronnikov Thanks to Dimitry Andric. (cherry picked from commit 7dbf0b05f1228c1c719866db5e5f3d58f87f74c8) Function `bcsave.lua:bcsave_elfobj()` produced an object file in ELF format with a wrong size size of `.strtab`. Wrong .strtab size causes lld to show an error message: ``` $ luajit -b -n "module_name" -e "print()" xxx.obj $ ld.lld xxx.obj ld.lld: error: xxx.obj: SHT_STRTAB string table section [index 3] is non-null terminated ``` Sergey Bronnikov: * added the description and the test for the problem Signed-off-by: Sergey Bronnikov Reviewed-by: Sergey Kaplun Reviewed-by: Maxim Kokryashkin --- Changes in v2: - Fixed comments as per review by Sergey. Branch: https://github.com/tarantool/luajit/tree/ligurio/gh-366-strtab-section-correct-size Tarantool CI: https://github.com/ligurio/tarantool/tree/ligurio/gh-xxxx-strtab-section-correct-size src/jit/bcsave.lua | 2 +- .../lj-366-strtab-correct-size.test.lua | 202 ++++++++++++++++++ 2 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 test/tarantool-tests/lj-366-strtab-correct-size.test.lua diff --git a/src/jit/bcsave.lua b/src/jit/bcsave.lua index c17c88e0..2553d97e 100644 --- a/src/jit/bcsave.lua +++ b/src/jit/bcsave.lua @@ -275,7 +275,7 @@ typedef struct { o.sect[2].size = fofs(ofs) o.sect[3].type = f32(3) -- .strtab o.sect[3].ofs = fofs(sofs + ofs) - o.sect[3].size = fofs(#symname+1) + o.sect[3].size = fofs(#symname+2) ffi.copy(o.space+ofs+1, symname) ofs = ofs + #symname + 2 o.sect[4].type = f32(1) -- .rodata diff --git a/test/tarantool-tests/lj-366-strtab-correct-size.test.lua b/test/tarantool-tests/lj-366-strtab-correct-size.test.lua new file mode 100644 index 00000000..5dec6751 --- /dev/null +++ b/test/tarantool-tests/lj-366-strtab-correct-size.test.lua @@ -0,0 +1,202 @@ +local tap = require('tap') +local test = tap.test('lj-366-strtab-correct-size'):skipcond({ + -- The test is ELF-specific, and because LuaJIT exports object + -- files in ELF format for all operating systems except macOS + -- and Windows we skip test on these OSes. + -- See src/jit/bcsave.lua:bcsave_obj. + ['Disabled on Windows'] = jit.os == 'Windows', + ['Disabled on macOS'] = jit.os == 'OSX', +}) + +local ffi = require 'ffi' + +-- Command below exports bytecode as an object file in ELF format: +-- $ luajit -b -n 'lango_team' -e 'print()' xxx.obj +-- $ file xxx.obj +-- xxx.obj: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped +-- +-- With read_elf(1) it is possible displaying the entries in symbol table +-- section of the file, if it has one. Object file contains a single symbol +-- with name 'luaJIT_BC_lango_team': +-- +-- $ readelf --symbols xxx.obj +-- +-- Symbol table '.symtab' contains 2 entries: +-- Num: Value Size Type Bind Vis Ndx Name +-- 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND +-- 1: 0000000000000000 66 OBJECT GLOBAL DEFAULT 4 luaJIT_BC_lango_team +-- +-- and displaying the information contained in the file's section headers, if +-- it has any. For our purposes we are interesting in section .symtab, +-- so other sections snipped in the output: +-- +-- $ readelf --section-headers xxx.obj +-- There are 6 section headers, starting at offset 0x40: +-- +-- Section Headers: +-- [Nr] Name Type Address Offset +-- Size EntSize Flags Link Info Align +-- ... +-- +-- [ 3] .strtab STRTAB 0000000000000000 00000223 +-- 0000000000000016 0000000000000000 0 0 1 +-- ... +-- Reference numbers for strtab offset and size could be obtained with +-- readelf(1). Note that number system of these numbers are hexadecimal. + +local expected_strtab_size = 0x16 +local expected_strtab_offset = 0x223 +local module_name = 'lango_team' + +-- Symbol name prefix for LuaJIT bytecode defined in bcsave.lua. +local LJBC_PREFIX = 'luaJIT_BC_' + +-- Defined in elf.h. +local SHT_SYMTAB = 2 + +-- Using the same declarations as defined in . +ffi.cdef[[ +typedef struct { + uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7]; + uint16_t type, machine; + uint32_t version; + uint32_t entry, phofs, shofs; + uint32_t flags; + uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx; +} ELF32header; + +typedef struct { + uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7]; + uint16_t type, machine; + uint32_t version; + uint64_t entry, phofs, shofs; + uint32_t flags; + uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx; +} ELF64header; + +typedef struct { + uint32_t name, type, flags, addr, ofs, size, link, info, align, entsize; +} ELF32sectheader; + +typedef struct { + uint32_t name, type; + uint64_t flags, addr, ofs, size; + uint32_t link, info; + uint64_t align, entsize; +} ELF64sectheader; + +typedef struct { + uint32_t name, value, size; + uint8_t info, other; + uint16_t sectidx; +} ELF32symbol; + +typedef struct { + uint32_t name; + uint8_t info, other; + uint16_t sectidx; + uint64_t value, size; +} ELF64symbol; + +typedef struct { + ELF32header hdr; + ELF32sectheader sect[6]; + ELF32symbol sym[2]; + uint8_t space[4096]; +} ELF32obj; + +typedef struct { + ELF64header hdr; + ELF64sectheader sect[6]; + ELF64symbol sym[2]; + uint8_t space[4096]; +} ELF64obj; +]] + +local is64_arch = { + ['x64'] = true, + ['arm64'] = true, + ['arm64be'] = true, + ['ppc'] = false, + ['mips'] = false, +} + +local is64 = is64_arch[jit.arch] or false + +local function create_obj_file(name) + local elf_filename = os.tmpname() .. '.obj' + local lua_path = os.getenv('LUA_PATH') + local lua_bin = require('utils').luacmd(arg):match('%S+') + local cmd_fmt = 'LUA_PATH="%s" %s -b -n "%s" -e "print()" %s' + local cmd = (cmd_fmt):format(lua_path, lua_bin, name, elf_filename) + local ret = os.execute(cmd) + assert(ret == 0, 'create an object file') + return elf_filename +end + +-- Reads a file located in a specified path and returns its content. +local function read_file(path) + local file = assert(io.open(path), 'cannot open an object file') + local content = file:read('*a') + file:close() + return content +end + +-- Parses a buffer in an ELF format and returns an offset and a size of strtab +-- and symtab sections. +local function read_elf(elf_content) + local ELFobj_type = ffi.typeof(is64 and 'ELF64obj *' or 'ELF32obj *') + local ELFsectheader_type = ffi.typeof(is64 and 'ELF64sectheader *' or 'ELF32sectheader *') + local elf = ffi.cast(ELFobj_type, elf_content) + local symtab_hdr, strtab_hdr + -- Iterate by section headers. + for i = 0, elf.hdr.shnum do + local sec = ffi.cast(ELFsectheader_type, elf.sect[i]) + if sec.type == SHT_SYMTAB then + symtab_hdr = sec + strtab_hdr = ffi.cast(ELFsectheader_type, elf.sect[symtab_hdr.link]) + break + end + end + + assert(strtab_hdr ~= nil, 'section .strtab was not found') + assert(symtab_hdr ~= nil, 'section .symtab was not found') + + return strtab_hdr, symtab_hdr +end + +test:plan(3) + +local elf_filename = create_obj_file(module_name) +local elf_content = read_file(elf_filename) +assert(#elf_content ~= 0, 'cannot read an object file') + +local strtab, symtab = read_elf(elf_content) +local strtab_size = tonumber(strtab.size) +local strtab_offset = tonumber(strtab.ofs) +local symtab_size = tonumber(symtab.size) +local sym_cnt = tonumber(symtab_size / symtab.entsize) +assert(sym_cnt ~= 0, 'number of symbols is zero') + +test:is(strtab_size, expected_strtab_size, 'check .strtab size') +test:is(strtab_offset, expected_strtab_offset, 'check .strtab offset') + +local strtab_str = string.sub(elf_content, strtab_offset, strtab_offset + strtab_size) + +local strtab_p = ffi.cast('char *', strtab_str) +local sym_name_expected = LJBC_PREFIX .. module_name +local sym_is_found = false +for i = 1, sym_cnt do + local sym_name = ffi.string(strtab_p + i) + if sym_name_expected == sym_name then + sym_is_found = true + break + end +end + +test:ok(sym_is_found == true, 'symbol is found') + +local ret = os.remove(elf_filename) +assert(ret == true, 'cannot remove an object file') + +os.exit(test:check() and 0 or 1) -- 2.34.1