File: create-corefile.c

package info (click to toggle)
llvm-toolchain-14 1%3A14.0.6-16
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,496,368 kB
  • sloc: cpp: 5,593,980; ansic: 986,873; asm: 585,869; python: 184,223; objc: 72,530; lisp: 31,119; f90: 27,793; javascript: 9,780; pascal: 9,762; sh: 9,482; perl: 7,468; ml: 5,432; awk: 3,523; makefile: 2,547; xml: 953; cs: 573; fortran: 567
file content (189 lines) | stat: -rw-r--r-- 5,797 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#include <mach-o/loader.h>
#include <stdio.h>
#include <stdlib.h>
#include <mach/machine.h>
#include <string.h>
#include <mach/machine/thread_state.h>
#include <inttypes.h>
#include <sys/syslimits.h>
#include <uuid/uuid.h>

// Given an executable binary with 
//   "fmain" (a function pointer to main)
//   "main"
// symbols, create a fake arm64e corefile that
// contains a memory segment for the fmain 
// function pointer, with the value of the 
// address of main() with ptrauth bits masked on.
//
// The corefile does not include the "addrable bits"
// LC_NOTE, so lldb will need to fall back on its 
// default value from the Darwin arm64 ABI.

int main(int argc, char **argv)
{
  if (argc != 3) {
    fprintf (stderr, "usage: %s executable-binary output-file\n", argv[0]);
    exit(1);
  }
  FILE *exe = fopen(argv[1], "r");
  if (!exe) {
    fprintf (stderr, "Unable to open executable %s for reading\n", argv[1]);
    exit(1);
  }
  FILE *out = fopen(argv[2], "w");
  if (!out) {
    fprintf (stderr, "Unable to open %s for writing\n", argv[2]);
    exit(1);
  }

  char buf[PATH_MAX + 6];
  sprintf (buf, "nm '%s'", argv[1]);
  FILE *nm = popen(buf, "r");
  if (!nm) {
    fprintf (stderr, "Unable to run nm on '%s'", argv[1]);
    exit (1);
  }
  uint64_t main_addr = 0;
  uint64_t fmain_addr = 0;
  while (fgets (buf, sizeof(buf), nm)) {
    if (strstr (buf, "_fmain")) {
      fmain_addr = strtoul (buf, NULL, 16);
    }
    if (strstr (buf, "_main")) {
      main_addr = strtoul (buf, NULL, 16);
    }
  }
  pclose (nm);

  sprintf (buf, "dwarfdump -u '%s'", argv[1]);
  FILE *dwarfdump = popen(buf, "r");
  if (!dwarfdump) {
    fprintf (stderr, "Unable to run dwarfdump -u on '%s'\n", argv[1]);
    exit (1);
  }
  uuid_t uuid;
  uuid_clear (uuid);
  while (fgets (buf, sizeof(buf), dwarfdump)) {
    if (strncmp (buf, "UUID: ", 6) == 0) {
      buf[6 + 36] = '\0';
      if (uuid_parse (buf + 6, uuid) != 0) {
        fprintf (stderr, "Unable to parse UUID in '%s'\n", buf);
        exit (1);
      }
    }
  }
  if (uuid_is_null(uuid)) {
    fprintf (stderr, "Got a null uuid for the binary\n");
    exit (1);
  }

  if (main_addr == 0 || fmain_addr == 0) {
    fprintf(stderr, "Unable to find address of main or fmain in %s.\n",
        argv[1]);
    exit (1);
  }

  // Write out a corefile with contents in this order:
  //    1. mach header
  //    2. LC_THREAD load command
  //    3. LC_SEGMENT_64 load command
  //    4. LC_NOTE load command
  //    5. memory segment contents
  //    6. "load binary" note contents

  // struct thread_command {
  //       uint32_t        cmd;    
  //       uint32_t        cmdsize;
  //       uint32_t flavor      
  //       uint32_t count       
  //       struct XXX_thread_state state
  int size_of_thread_cmd = 4 + 4 + 4 + 4 + sizeof (arm_thread_state64_t);

  struct mach_header_64 mh;
  mh.magic = 0xfeedfacf;
  mh.cputype = CPU_TYPE_ARM64;
  mh.cpusubtype = CPU_SUBTYPE_ARM64E;
  mh.filetype = MH_CORE;
  mh.ncmds = 3; // LC_THREAD, LC_SEGMENT_64, LC_NOTE
  mh.sizeofcmds = size_of_thread_cmd + sizeof(struct segment_command_64) + sizeof(struct note_command);
  mh.flags = 0;
  mh.reserved = 0;

  fwrite(&mh, sizeof (mh), 1, out);

  struct note_command lcnote;
  struct segment_command_64 seg;
  seg.cmd = LC_SEGMENT_64;
  seg.cmdsize = sizeof(seg);
  memset (&seg.segname, 0, 16);
  seg.vmaddr = fmain_addr;
  seg.vmsize = 8;
  // Offset to segment contents
  seg.fileoff = sizeof (mh) + size_of_thread_cmd + sizeof(seg) + sizeof(lcnote);
  seg.filesize = 8;
  seg.maxprot = 3;
  seg.initprot = 3;
  seg.nsects = 0;
  seg.flags = 0;

  fwrite (&seg, sizeof (seg), 1, out);

  uint32_t cmd = LC_THREAD;
  fwrite (&cmd, sizeof (cmd), 1, out);
  uint32_t cmdsize = size_of_thread_cmd;
  fwrite (&cmdsize, sizeof (cmdsize), 1, out);
  uint32_t flavor = ARM_THREAD_STATE64;
  fwrite (&flavor, sizeof (flavor), 1, out);
  // count is number of uint32_t's of the register context
  uint32_t count = sizeof (arm_thread_state64_t) / 4;
  fwrite (&count, sizeof (count), 1, out);
  arm_thread_state64_t regstate;
  memset (&regstate, 0, sizeof (regstate));
  fwrite (&regstate, sizeof (regstate), 1, out);

  lcnote.cmd = LC_NOTE;
  lcnote.cmdsize = sizeof (lcnote);
  strcpy (lcnote.data_owner, "load binary");

  // 8 is the size of the LC_SEGMENT contents
  lcnote.offset = sizeof (mh) + size_of_thread_cmd + sizeof(seg) + sizeof(lcnote) + 8;

  // struct load_binary
  // {
  // uint32_t version;        // currently 1
  // uuid_t   uuid;           // all zeroes if uuid not specified
  // uint64_t load_address;   // virtual address where the macho is loaded, UINT64_MAX if unavail
  // uint64_t slide;          // slide to be applied to file address to get load address, 0 if unavail
  // char     name_cstring[]; // must be nul-byte terminated c-string, '\0' alone if name unavail
  // } __attribute__((packed));
  lcnote.size = 4 + 16 + 8 + 8 + sizeof("a.out");

  fwrite (&lcnote, sizeof(lcnote), 1, out);

  // Write the contents of the memory segment

  // Or together a random PAC value from a system using 39 bits 
  // of addressing with the address of main().  lldb will need
  // to correctly strip off the high bits to find the address of
  // main.
  uint64_t segment_contents = 0xe46bff0000000000 | main_addr;
  fwrite (&segment_contents, sizeof (segment_contents), 1, out);

  // Now write the contents of the "load binary" LC_NOTE.
  {
    uint32_t version = 1;
    fwrite (&version, sizeof (version), 1, out);
    fwrite (&uuid, sizeof (uuid), 1, out);
    uint64_t load_address = UINT64_MAX;
    fwrite (&load_address, sizeof (load_address), 1, out);
    uint64_t slide = 0;
    fwrite (&slide, sizeof (slide), 1, out);
    strcpy (buf, "a.out");
    fwrite (buf, 6, 1, out);
  }

  fclose (out);

  exit (0);
}