2

In a Mach-O executable, I am trying to increase the size of the __LLVM segment that precedes the __LINKEDIT segment (with a home-grown tool). I am considering two strategies: (a) move the __LLVM segment to after the __LINKEDIT segment, producing a file that is not what ld would create (now with a gap and section addresses out of order), and (b) move the __LINKEDIT segment to allow resizing of the __LLVM segment that precedes it. I need the result to be accepted for downstream processing, e.g. generating an .ipa file or sending to the App Store.

This question is about my assumptions and the viability of these approaches. Specifically, what are the potential pitfalls of each that might lead them to fail?

I implemented the first approach (a) is understood by segedit's -extract option, but its -replace option complains that the segments are out of order. I append a new segment to the file and update the address and length values in the corresponding load command to refer to this new segment data (both in the file and the destination memory). This might be fine, as long as the other downstream processing will accept the result (still to check; e.g. any local signature is likely invalidated).

The second approach (b) would seem cleaner, as long as there are no references into the __LINKEDIT segment, which I guess contains linking information (symbol tables etc., rather than code). I have not tried this yet, though it seems to be a foregone conclusion that segedit will be happy with the result, which may suggest other processing might also be happier. Are there likely to be any references that are invalidated due to simply moving this segment? I am guessing that I will have to update further load commands (they seem to reference into the __LINKEDIT segment), which I have not examined, but this should be fairly straightforward.

EDIT: Replaced my confused use of "section" with "segment" (mentioned in answer).

ADDED: Context is where I have no control of generating the original executable. I need to post-process it, essentially performing a 'segedit -replace' process, wherein the a section in the segment is to be replaced with a section that is larger than space previously allocated for the segment.

RUN-ON clarifying question: It seems from the answer that moving the __LINKEDIT segment will break it. Can this be fixed by adjusting load commands only (e.g. LC_DYLD_INFO_ONLY, LC_LOAD_DYLINKER, LC_LOAD_DYLIB), not data in any segments? I am not yet familiar with these load commands, and would like to know whether to pursue this.

qman
  • 123
  • 4

2 Answers2

2

So basically the segments and sections describe how the physical file maps onto virtual memory. As I mentioned in my previous iteration of the answer there are limitations on the segments order:

  1. __TEXT section must start at executable physical file offset 0
  2. __LINKEDIT section must not start at physical file offset 0
  3. __LINKEDIT's File Offset + File Size should be equal to physical executable size (this implies __LINKEDIT being the last segment). Otherwise code signing won't work.

__DYLD_INFO_ONLY contains file offsets to dyld loading bind opcodes for:

  • rebase
  • bind at load
  • weak bind
  • lazy bind
  • export

For each kind there is file offset and size entry in __DYLD_INFO_ONLY describing the data in file that matches __LINKEDIT (in a "regular" ld linked executable). __DYLD_INFO_ONLY does not use any segment & section information from __LINKEDIT directly, the file offsets and sizes are enough.

EDIT also as mentioned in @kirelagin answer here
"Apparently, the new version of dyld from 10.12 Sierra performs a check that previous versions did not perform: it makes sure that the LC_SYMTAB symbols table is entirely within the __LINKEDIT segment."

I assume since you want to inflate the size of the preceding __LLVM segment you would also want some extra data in the file itself. Typically data described by __LINKEDIT (i.e. not the segment & sections themselves, but the actual data) won't use 100% of it's space so it could be modified to start "later" and occupy less space.

A tool called jtool by Jonathan Levin could probably do it for you.

Kamil.S
  • 5,205
  • 2
  • 22
  • 51
  • Sorry, I meant 'segment' (corrected). My starting point is an executable that I am given. It has segments __PAGEZERO, __TEXT, __DATA, __LLVM, __LINKEDIT. The first three segments will remain untouched. Your points 1 and 2 are satisfied. There are further load commands, including LC_DYLD_INFO_ONLY. Your answer indicates to me that this implies that simply moving the __LINKEDIT section will fail. I can do something more sophisticated, which I'll post as a run-on question. – qman May 03 '19 at 14:08
  • I was successful (a while ago now, just revisiting this) using my approach (b). As your answer indicates, (a) does not work. I cannot remember the whole list of adjustments, but the trickiest was adjusting all the offsets *into* the `__LINKEDIT` segment scattered around the load commands. I was unable to use jtool for the purpose. – qman Apr 24 '20 at 01:25
1

I know this is an old question, but I solved this problem while solving another problem.

  1. define the slide amount, this must be page-aligned, so I choose 0x4000.
    1. add the slide amount to the relevant load commands, this includes but is not limited to:
      • __LINKEDIT segment (duh)
      • dyld_info_command
      • symtab_command
      • dysymtab_command
      • linkedit_data_commands
    2. physically move the __LINKEDIT in the file.
ArandomDev
  • 125
  • 2
  • 10