LuaJit分析(十一)去除string.dump函数

news2024/9/30 7:15:25

Lua脚本中的string.dump函数用于生成字节码文件,根据对 luajit -b命令的分析可以得出,最终dump出字节码文件都是使用的string.dump函数。

因此即使我们的指令顺序被打乱,通过loadfile系统调用,再通过string.dump调用,可以得到字节码文件,再通过与标准的luajit生成的字节码文件比对,即可找出差异所在。

String.dump的实现在lib_string.c文件中,实现如下:

LJLIB_CF(string_dump)
{
GCfunc *fn = lj_lib_checkfunc(L, 1);
int strip = L->base+1 < L->top && tvistruecond(L->base+1);
SBuf *sb = lj_buf_tmp_(L); /* Assumes lj_bcwrite() doesn't use tmpbuf. */
L->top = L->base+1;
if (!isluafunc(fn) || lj_bcwrite(L, funcproto(fn), writer_buf, sb, strip))
lj_err_caller(L, LJ_ERR_STRDUMP);
setstrV(L, L->top-1, lj_buf_str(L, sb));
lj_gc_check(L);
return 1;
}

可以看出,它最终是调用lj_bcwriter函数,来完成字节码的生成。但是如果直接将它注释掉,在编译时会出现如下错误:

提示lj_libdef.h这个文件中没有声明lj_cf_string_dump这个标识符,很明显string_dump这个函数的声明为LJLIB_CF(string_dump),其中LJLIB_CF为宏定义,定义如下:

#define LJLIB_CF(name)    static int lj_cf_##name(lua_State *L)

从这个定义就可以得出,注释了string.dump后,这个函数的实现找不到了,但是又被引用,那么找到lj_libdef.h中的第123行,如下:

#ifdef LJLIB_MODULE_string
#undef LJLIB_MODULE_string
static const lua_CFunction
lj_lib_cf_string[] = {
  lj_ffh_string_byte,
  lj_ffh_string_char,
  lj_ffh_string_sub,
  lj_cf_string_rep,
  lj_ffh_string_reverse,
  lj_cf_string_dump,
  lj_cf_string_find,
  lj_cf_string_match,
  lj_cf_string_gmatch,
  lj_cf_string_gsub,
  lj_cf_string_format
};

这里记录了这个函数的实现,同时在lj_ffdef.h中也记录了string.dump:

FFDEF(string_upper)
FFDEF(string_dump)
FFDEF(string_find)

 同时lj_libdef.h和lj_ffdef.h这两个文件都是自动生成的,在msvcbuild.bat中,使用如下命令生成

buildvm -m ffdef -o lj_ffdef.h %ALL_LIB%
buildvm -m libdef -o lj_libdef.h %ALL_LIB%

其中%ALL_LIB%是所有lib_*.c的文件,即各种类型的库的c源文件。
从这里就可以得出,这两个头文件都是使用buildvm生成,附带不同的 -m参数,和各种库文件的c源文件。
那么我们直接定位到buildvm.c的main函数中,从main函数中可以看出它先处理了输入的参数,然后在一个switch结构中根据不同的 -m参数进行不同转换:

case BUILD_ffdef:
case BUILD_libdef:
case BUILD_recdef:
emit_lib(ctx);
break;

可以看到这两个头文件都是使用 emit_lib函数生成,emit_lib实现如下:

/* Emit C source code for library function definitions. */
void emit_lib(BuildCtx *ctx)
{
  const char *fname;
  if (ctx->mode == BUILD_ffdef || ctx->mode == BUILD_libdef ||
      ctx->mode == BUILD_recdef)
    fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n");
  else if (ctx->mode == BUILD_vmdef)
    fprintf(ctx->fp, "ffnames = {\n[0]=\"Lua\",\n\"C\",\n");
  if (ctx->mode == BUILD_recdef)
    fprintf(ctx->fp, "static const uint16_t recff_idmap[] = {\n0,\n0x0100");
  recffid = ffid = FF_C+1;
  ffasmfunc = 0;
 
  while ((fname = *ctx->args++)) {
    char buf[256];  /* We don't care about analyzing lines longer than that. */
    FILE *fp;
    if (fname[0] == '-' && fname[1] == '\0') {
      fp = stdin;
    } else {
      fp = fopen(fname, "r");
      if (!fp) {
  fprintf(stderr, "Error: cannot open input file '%s': %s\n",
    fname, strerror(errno));
  exit(1);
      }
    }
    modstate = 0;
    regfunc = REGFUNC_OK;
    while (fgets(buf, sizeof(buf), fp) != NULL) {
      char *p;
      /* Simplistic pre-processor. Only handles top-level #if/#endif. */
      if (buf[0] == '#' && buf[1] == 'i' && buf[2] == 'f') {
  int ok = 1;
  if (!strcmp(buf, "#if LJ_52\n"))
    ok = LJ_52;
  else if (!strcmp(buf, "#if LJ_HASJIT\n"))
    ok = LJ_HASJIT;
  else if (!strcmp(buf, "#if LJ_HASFFI\n"))
    ok = LJ_HASFFI;
  if (!ok) {
    int lvl = 1;
    while (fgets(buf, sizeof(buf), fp) != NULL) {
      if (buf[0] == '#' && buf[1] == 'e' && buf[2] == 'n') {
        if (--lvl == 0) break;
      } else if (buf[0] == '#' && buf[1] == 'i' && buf[2] == 'f') {
        lvl++;
      }
    }
    continue;
  }
      }
      for (p = buf; (p = strstr(p, LIBDEF_PREFIX)) != NULL; ) {
  const LibDefHandler *ldh;
  p += sizeof(LIBDEF_PREFIX)-1;
  for (ldh = libdef_handlers; ldh->suffix != NULL; ldh++) {
    size_t n, len = strlen(ldh->suffix);
    if (!strncmp(p, ldh->suffix, len)) {
      p += len;
      n = ldh->stop ? strcspn(p, ldh->stop) : 0;
      if (!p[n]) break;
      p[n] = '\0';
      ldh->func(ctx, p, ldh->arg);
      p += n+1;
      break;
    }
  }
  if (ldh->suffix == NULL) {
    buf[strlen(buf)-1] = '\0';
    fprintf(stderr, "Error: unknown library definition tag %s%s\n",
      LIBDEF_PREFIX, p);
    exit(1);
  }
      }
    }
    fclose(fp);
    if (ctx->mode == BUILD_libdef) {
      libdef_endmodule(ctx);
    }
  }
 
  if (ctx->mode == BUILD_ffdef) {
    fprintf(ctx->fp, "\n#undef FFDEF\n\n");
    fprintf(ctx->fp,
      "#ifndef FF_NUM_ASMFUNC\n#define FF_NUM_ASMFUNC %d\n#endif\n\n",
      ffasmfunc);
  } else if (ctx->mode == BUILD_vmdef) {
    fprintf(ctx->fp, "},\n\n");
  } else if (ctx->mode == BUILD_bcdef) {
    int i;
    fprintf(ctx->fp, "\n};\n\n");
    fprintf(ctx->fp, "LJ_DATADEF const uint16_t lj_bc_mode[] = {\n");
    fprintf(ctx->fp, "BCDEF(BCMODE)\n");
    for (i = ffasmfunc-1; i > 0; i--)
      fprintf(ctx->fp, "BCMODE_FF,\n");
    fprintf(ctx->fp, "BCMODE_FF\n};\n\n");
  } else if (ctx->mode == BUILD_recdef) {
    char *p = (char *)obuf;
    fprintf(ctx->fp, "\n};\n\n");
    fprintf(ctx->fp, "static const RecordFunc recff_func[] = {\n"
      "recff_nyi,\n"
      "recff_c");
    while (*p) {
      fprintf(ctx->fp, ",\nrecff_%s", p);
      p += strlen(p)+1;
    }
    fprintf(ctx->fp, "\n};\n\n");
  }
}

从这个函数可以看到,这是一个生成头文件的总的入口函数,并实现了所有内容的生成,它通过读取源文件,匹配关键字符串,提取出宏定义中的函数名,求中有一个关键的循环:

for (ldh = libdef_handlers; ldh->suffix != NULL; ldh++) {
  size_t n, len = strlen(ldh->suffix);
  if (!strncmp(p, ldh->suffix, len)) {
    p += len;
    n = ldh->stop ? strcspn(p, ldh->stop) : 0;
    if (!p[n]) break;
    p[n] = '\0';
    ldh->func(ctx, p, ldh->arg);
    p += n+1;
    break;
  }
}

它从libdef_handlers中读取了所有保存的函数并调用。它的定义如下:

static const LibDefHandler libdef_handlers[] = {
  { "MODULE_",  " \t\r\n",  libdef_module,    0 },
  { "CF(",  ")",    libdef_func,    LIBINIT_CF },
  { "ASM(", ")",    libdef_func,    LIBINIT_ASM },
  { "ASM_(",  ")",    libdef_func,    LIBINIT_ASM_ },
  { "LUA(", ")",    libdef_lua,   0 },
  { "REC(", ")",    libdef_rec,   0 },
  { "PUSH(",  ")",    libdef_push,    0 },
  { "SET(", ")",    libdef_set,   0 },
  { "NOREGUV",  NULL,   libdef_regfunc,   REGFUNC_NOREGUV },
  { "NOREG",  NULL,   libdef_regfunc,   REGFUNC_NOREG },
  { NULL, NULL,   (LibDefFunc)0,    0 }
};

从这里可以看出,它对应了所有类型函数的处理,string_dump由libdef_func处理,libdef_func定义如下:

static void libdef_func(BuildCtx *ctx, char *p, int arg)
{
  if (arg != LIBINIT_CF)
    ffasmfunc++;
  if (ctx->mode == BUILD_libdef) {
    if (modstate == 0) {
      fprintf(stderr, "Error: no module for function definition %s\n", p);
      exit(1);
    }
    if (regfunc == REGFUNC_NOREG) {
      if (optr+1 > obuf+sizeof(obuf)) {
  fprintf(stderr, "Error: output buffer overflow\n");
  exit(1);
      }
      *optr++ = LIBINIT_FFID;
    } else {
      if (arg != LIBINIT_ASM_) {
  if (modstate != 1) fprintf(ctx->fp, ",\n");
  modstate = 2;
  fprintf(ctx->fp, "  %s%s", arg ? LABEL_PREFIX_FFH : LABEL_PREFIX_CF, p);
      }
      if (regfunc != REGFUNC_NOREGUV) obuf[2]++;  /* Bump hash table size. */
      libdef_name(regfunc == REGFUNC_NOREGUV ? "" : p, arg);
    }
  } else if (ctx->mode == BUILD_ffdef) {
    fprintf(ctx->fp, "FFDEF(%s)\n", p);
  } else if (ctx->mode == BUILD_recdef) {
    if (strlen(p) > sizeof(funcname)-1) {
      fprintf(stderr, "Error: function name too long: '%s'\n", p);
      exit(1);
    }
    strcpy(funcname, p);
  } else if (ctx->mode == BUILD_vmdef) {
    int i;
    for (i = 1; p[i] && modname[i-1]; i++)
      if (p[i] == '_') p[i] = '.';
    fprintf(ctx->fp, "\"%s\",\n", p);
  } else if (ctx->mode == BUILD_bcdef) {
    if (arg != LIBINIT_CF)
      fprintf(ctx->fp, ",\n%d", find_ffofs(ctx, p));
  }
  ffid++;
  regfunc = REGFUNC_OK;
}


这里输出了与lib_*.c中对应的函数名。
总结:luajit中库函数的实现使用了宏定义,buildvm在生成对应头文件时,直接扫描源文件中的标志,如LJLIB_CF(string_dump)并提取出得到 string_dump名称,并将其输出到生成的头文件中,因此直接注释依然会识别到该函数名。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2087455.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

缓存解决方案。Redis 和 Amazon ElastiCache 比较

欢迎来到雲闪世界。Redis 和 Amazon ElastiCache 等缓存解决方案是通过将频繁访问的数据存储在内存中来提高应用程序性能的热门选择。让我们从实施简单性、性能、成本和维护方面对它们进行比较。 实施简单 设置 Redis 需要在基础设施或云实例上安装和配置 Redis 服务器。它可…

回归预测|基于CNN-LSTM-Attention结合Adaboost集成数据预测Matlab程序 多特征输入单输出

回归预测|基于CNN-LSTM-Attention结合Adaboost集成数据预测Matlab程序 多特征输入单输出 文章目录 前言回归预测|基于CNN-LSTM-Attention结合Adaboost集成数据预测Matlab程序 多特征输入单输出 一、CNN-LSTM-Attention-Adaboost模型**详细流程&#xff1a;****基本原理&#xf…

Golang | Leetcode Golang题解之第381题O(1)时间插入、删除和获取随机元素-允许重复

题目&#xff1a; 题解&#xff1a; type RandomizedCollection struct {idx map[int]map[int]struct{}nums []int }/** Initialize your data structure here. */ func Constructor() RandomizedCollection {return RandomizedCollection{idx: map[int]map[int]struct{}{},}…

服务器数据恢复—LeftHand存储中raid5阵列多块磁盘离线的数据恢复案例

LeftHand存储支持RAID5、RAID6、RAID10磁盘阵列&#xff0c;同时还支持卷快照&#xff0c;卷动态扩容等。下面简单聊一下LeftHand存储的结构和一个LeftHand p4500存储中磁盘阵列数据恢复案例。 服务端&#xff1a; 客户端&#xff1a; LeftHand存储结构&#xff1a; Lefthand存…

打架目标检测数据集 9000张 打架数据集 带标注voc yolo

本项目的目标是开发一个能够自动检测公共场所中打架行为的系统。该系统利用先进的计算机视觉技术和深度学习方法&#xff0c;在实时视频流或静态图像中准确地识别出打架行为&#xff0c;这对于维护公共安全至关重要。 技术栈 YOLOv8: 作为主要的目标检测框架&#xff0c;因其在…

基于my Batis优化图书管理系统(二)

4. 图书列表 添加图书之后, 跳转到图书列表⻚⾯, 并没有显⽰刚才添加的图书信息, 接下来我们来实现图 书列表 4.1 需求分析 当查询到我们的图书数据很多的时候&#xff0c;一个页可能存放不了&#xff0c;所以我们进行分页处理数据&#xff0c;并且分页进行查询&#xff1b;如…

最新视频合成后调优技术ExVideo模型部署

ExVideo是一种新型的视频合成模型后调优技术&#xff0c;由华东师范大学和阿里巴巴的研究人员共同开发。 ExVideo提出了一种新的后调优策略&#xff0c;无需对整个模型进行大规模重训&#xff0c;仅通过对模型中时序相关组件的微调&#xff0c;就能够显著增强其生成更长视频片…

【大模型系列篇】大语言模型架构分类和对比

在预训练语言模型时代&#xff0c;自然语言处理领域广泛采用了预训练(Pre-training) 微调(SFT)的范式&#xff0c; 并诞生了以 BERT 为代表的编码器&#xff08;Encoder-only&#xff09;架构、以 GPT 为代表的解码器&#xff08;Decoder-only&#xff09;架构和以 T5 为代表的…

关于springboot对接chatglm3-6b大模型的尝试

之前我们通过阿里提供的cloud ai对接了通义千问。cloud ai对接通义千问 那么接下来我们尝试一些别的模型看一下&#xff0c;其实这个文章主要是表达一种对接方式&#xff0c;其他的都大同小异。都可以依此方法进行处理。 一、明确模型参数 本次我们对接的理论支持来自于阿里云…

模型 DFEAS营销法

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。需求触发&#xff0c;精准营销转化。 1 DFEAS营销法的应用 1.1 个性化健身应用的市场拓展策略 随着健康意识的增强&#xff0c;个性化健身应用市场迅速发展。一款名为“FitMyLife”的个性化健身应用…

FancyVideo

一、模型介绍 合成运动丰富且时间一致的视频仍然是人工智能领域的一项挑战&#xff0c;尤其是在处理较长的持续时间时。现有的文本到视频 (T2V) 模型通常采用空间交叉注意进行文本控制&#xff0c;等效地指导不同帧的生成而无需特定于帧的文本指导。因此&#xff0c;模型理解提…

经典算法之链表篇(二)

目录 一&#xff1a;重排链表&#xff08;LeetCode.143&#xff09; 二&#xff1a;删除链表的节点&#xff08;LCR 136. 删除链表的节点&#xff09; 三&#xff1a;K个一组反转链表&#xff08;LeetCode.25&#xff09; 有关经典算法链表的第一篇内容&#xff0c;可以查看我…

在线考试系统源码功能分析

在线考试系统源码的功能分析涵盖了多个关键方面&#xff0c;以确保系统能够满足教育机构和个人的需求。以下是一些常见的功能分析&#xff1a; 权限控制&#xff1a;系统通常支持多个角色&#xff0c;如教师、管理员和学生&#xff0c;并使用JWT等技术进行用户身份的合法性校验…

Leetcode JAVA刷刷站(101)对称二叉树

一、题目概述 二、思路方向 在Java中&#xff0c;要检查一个二叉树是否是轴对称的&#xff08;也称为镜像对称的&#xff09;&#xff0c;你可以通过递归地比较树的左子树和右子树是否镜像对称来实现。轴对称的二叉树意味着树的左子树和右子树关于根节点对称&#xff0c;即左子…

微信小程序:手机联调同一个网段无法找到本地接口

我们在开发微信小程序的时候&#xff0c;一般会启动本地服务器进行API连调&#xff0c;不过模拟器上面往往一些问题及细节发现不了&#xff0c;需要真机调试&#xff0c;结果调试的时候发现&#xff0c;不能访问到 localhost或者本机IP&#xff0c;也就访问不到本地接口&#x…

【HarmonyOS NEXT开发】鸿蒙开发环境准备,ArkTS基础语法入门

文章目录 鸿蒙开发环境准备&#xff0c;ArkTS基础语法入门大纲简介DevEco Studio简介运行环境要求 安装与配置开发工具下载Harmony OS 和 OpenHarmony 的区别Previewer汉化插件的配置 ArkTS基础快速入门1. 解释说明2. 变量与常量3. 变量命名规则4. 数组5. 函数定义函数调用函数…

Mini型LoRa DTU远距离无线传输“小体积大作为”

Mini型LoRa DTU&#xff08;数据传输单元&#xff09;CL61M凭借其小巧的体积、低功耗、远距离通信和高可靠性等特点&#xff0c;在远距离无线传输领域展现出了巨大的应用潜力。使RS485/232串口终端设备能够轻松实现十公里的远距离无线通信&#xff0c;适用于多种复杂环境&#…

(三)Kafka离线安装 - ZooKeeper开机自启

手动启动方式 一般通过指令手动来启动zookeeper的方法是&#xff0c;先进入到zookeeper的安装目录下的bin目录&#xff0c;然后执行启动指令。 cd /usr/local/zookeeper/zookeeper-3.8.4/bin/zkServer.sh start 停止指令 zkServer.sh stop 查看状态 zkServer.sh status 上…

如何在知行之桥上通过业务单号查找原始报文?

在知行之桥中接收或发送的数据通常是EDI原始报文&#xff0c;知行之桥会对EDI原始报文进行格式转换&#xff0c;以方便用户后端系统的处理。因此&#xff0c;一般情况下&#xff0c;用户看到的都是转换后的数据结构&#xff0c;例如Json、XML或Excel等&#xff0c;无需直接查看…

window上部署kafka3.6.1,并配置sasl认证

1 安装kafka 第一步安装kafka,并能成功启动&#xff0c;可参考文章Windows下安装Kafka3-CSDN博客 2 修改kafka的配置文件 server.properties是kafka的主要配置文件&#xff0c;里面有很多参数可以调整。 主要修改如下 listenersSASL_PLAINTEXT://127.0.0.1:9092 sasl.enable…