Binary Makeself

本文展示了Linux 系统下的二进制的makeself 程序的制作:将数据和对数据的操作同时包含在一个巨大的可执行文件当中。

1. Makeself 是什么?

Makeself 是一类包含数据的shell 脚本,通常这些数据是一个归档文件,Makeself 脚本不依赖任何额外的文件,自身就可以完成工作。

说道这里不知道会不会有人感到陌生,不过说两个例子你马上就会想起来「喔,我见过这种东西」:

名称 类型 实质
支付宝控件 shell 脚本 shell 脚本
NVIDIA 驱动安装程序 run 文件 shell 脚本

下面以一个例子来具体的解释一下何为Makeself:

#!/usr/bin/env bash

# filename: makeself.sh
# A example of a makeself script by iSpeller

DATA_FILE=/tmp/self-playing.data

OFFSET=$(awk '/^__HERE_OLD_WORLD_END_AND_A_NEW_WORLD_BEGAN__/
              {print NR + 1; exit 0; }' "$0")
tail -n+$OFFSET "$0" > $DATA_FILE
mplayer $DATA_FILE
rm -f $DATA_FILE
exit 0

# Your data must below next line
__HERE_OLD_WORLD_END_AND_A_NEW_WORLD_BEGAN__

上面是一个简化的makeself 脚本,脚本最后一行设置了一个标号,而脚本所做的工作就是找到这个标号(使用awk),然后把标号之后的数据全部输出成一个文件(使用tail 重定向),之后就是对数据的处理了(这里我们使用的mplayer)。

既然如此,我们只要把多媒体文件追加到脚本的最后,脚本就可以调用mplayer 来播放,并按照你期望的运行:

当然这里我假定了你的系统上安装了mplayer ;p

2. 制作一个二进制的makeself 程序

现在回到了我们的标题:如何制作一个「Binary Makeself」?

根据之前制作shell 脚本的方法来看,我们需要将数据包含到可执行文件当中,同时又不损坏可执行文件,完成这个工作后,我们就可以在可执行文件中操作这些数据了。

下面以C 语言为例展示了在64 位Linux 系统下的二进制的makeself 程序的制作,功能同之前制作的「makeself.sh」相同,运行后调用mplayer 播放「あなたがいた森.mp3」——即使你的电脑无法获取到这首歌。

2.1 转储数据文件

不知道你看到这里会不会觉得我们做的事情和一个东西很相像:静态链接。

刚学C 语言的时候往往都会接触到动态链接和静态链接的概念,很多的书中作者一边讲解着这个变革一边缅怀那个自身都没经历过的时代。

我们现在干的事情和静态编译有些类似:数据文件是无法在C 代码中操作的,C 代码能操作的极限就是有符号表(symbol table)的目标文件(object file)。那么第一个工作就是将数据文件转换成带符号表的目标文件。

「objcopy」,不知道你对这个指令是否熟悉,它可以在目标文件之间进行操作,理所当然也能生成目标文件。我们先用这条指令转储数据文件(楼主的环境是linux x64 所以生成了x64 的elf 格式,如果是x86 的自己改一下参数):

OK,现在一首mp3 的数据都被转储到了目标文件中。

2.2 生成二进制makeself

现在的步骤就容易多了,我们只需要在代码中操作这些数据,并最终生成一个(可能体积巨大的)可执行文件即可。

首先看看目标文件的符号表,记录下我们需要的符号名称:

这便是需要的符号,顾名思义,它们分别表示了数据的起始、终止和长度。

把这些符号打印出来以便在代码中使用:

之后只需要在打码中引用这些符号即可,就像下面一样:

extern char _binary_______________________mp3_start[];
extern char _binary_______________________mp3_end[];
extern char _binary_______________________mp3_size[];

最后是一段C 代码示例,功能同前面的「makeself.sh」一样,调用mplayer 来播放音乐数据文件。其中引用的符号需要换成你从符号表中得到的符号:

/* A program to show C-style makeself by iSpeller
 * Complie with gcc on Linux
 */

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>

const char * const DATA_FILE = "/tmp/self-playing.data";
/* See your object file's symbol table and replace next 3 array
 * We will not use all of them in this program
 */
extern char _binary_______________________mp3_start[];
extern char _binary_______________________mp3_end[];
extern char _binary_______________________mp3_size[];

void
write_data (void)
{
  int datafd;
  char *data = _binary_______________________mp3_start;
  uint64_t WRITE_LEN = (uint64_t)_binary_______________________mp3_size;

  if (0 == access (DATA_FILE, F_OK))
    {
      if (-1 == unlink (DATA_FILE))
        {
          exit (1);
        }
    }

  if ((datafd = open(DATA_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0)
    {
      perror ("open");
      exit (1);
    }
  else
    {
      if (-1 == write (datafd, data, WRITE_LEN))
        {
          perror ("write");
        }
      close (datafd);
    }
}

void
clear (void)
{
  if (0 == access (DATA_FILE, F_OK))
    {
      if (-1 == remove (DATA_FILE))
        {
          perror ("remove");
        }
    }
}

int
main (void)
{
  pid_t pid;

  write_data ();

  if ((pid = fork ()) < 0)
    {
      perror ("fork");
      exit (1);
    }
  else if (0 == pid)
    {
      execlp ("mplayer", "mplayer", DATA_FILE, NULL);
    }

  if (pid != waitpid (pid, NULL, 0))
    {
      perror ("waitpid");
    }
  clear ();

  return 0;
}

最后,只需要编译成一个巨大的可执行文件即可,可执行文件中包含了所需要的数据和对数据的操作,这就是一个二进制版本的Makeself。可执行文件的体积视你插入的数据而定,但是一般来说它会比普通的程序大很多:

就这样,祝你愉快~

时间: 2024-08-27 04:16:39

Binary Makeself的相关文章

Maximum Depth of Binary Tree

这道题为简单题 题目: Given a binary tree, find its maximum depth.The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node. 思路: 我是用递归做的,当然也可以用深搜和广搜,递归的话就是比较左右子树的深度然后返回 代码: 1 # Definition for a binary tre

[LeetCode] Find Mode in Binary Search Tree 找二分搜索数的众数

Given a binary search tree (BST) with duplicates, find all the mode(s) (the most frequently occurred element) in the given BST. Assume a BST is defined as follows: The left subtree of a node contains only nodes with keys less than or equal to the nod

226反转二叉树 Invert Binary Tree

Invert a binary tree. 4 / 2 7 / \ / 1 3 6 9 to 4 / 7 2 / \ / 9 6 3 1 Trivia:This problem was inspired by this original tweet by Max Howell: Google: 90% of our engineers use the software you wrote (Homebrew), but you can't invert a binary tree on a wh

[leetcode] 104. Maximum Depth of Binary Tree

Given a binary tree, find its maximum depth. The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node. 递归遍历 左子树 和 右子树 一刷: public int maxDepth(TreeNode root) { if(root == null){ return 0; } int

235. Lowest Common Ancestor of a Binary Search Tree

1. 问题描述 Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST.According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes v and w as the lowest node in T th

leetcode 109 Convert Sorted List to Binary Search Tree

题目连接 https://leetcode.com/problems/convert-sorted-list-to-binary-search-tree/ Convert Sorted List to Binary Search Tree Description Given a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST. /** * De

Lowest Common Ancestor of a Binary Search Tree

1. Title 235. Lowest Common Ancestor of a Binary Search Tree 2. Http address https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/ 3. The question Given a binary search tree (BST), find the lowest common ancestor (LCA) of two

LeetCode 145 Binary Tree Postorder Traversal(二叉树的后续遍历)+(二叉树、迭代)

翻译 给定一个二叉树,返回其后续遍历的节点的值. 例如: 给定二叉树为 {1, #, 2, 3} 1 2 / 3 返回 [3, 2, 1] 备注:用递归是微不足道的,你可以用迭代来完成它吗? 原文 Given a binary tree, return the postorder traversal of its nodes' values. For example: Given binary tree {1,#,2,3}, 1 2 / 3 return [3,2,1]. Note: Recur

LeetCode 50 Pow(x, n)(Math、Binary Search)(*)

翻译 实现pow(x, n). 原文 Implement pow(x, n). 分析 首先给大家推荐维基百科: zh.wikipedia.org/wiki/二元搜尋樹 en.wikipedia.org/wiki/Binary_search_tree 其次,大家也可以看看类似的一道题: LeetCode 69 Sqrt(x)(Math.Binary Search)(*) 然而这题我还是没有解出来,看看别人的解法-- class Solution { private: double myPowHel