From 74a8755fe3ea755183c2a70a9fcf1a478dda2d68 Mon Sep 17 00:00:00 2001 From: David DeHaven Date: Tue, 9 Dec 2008 00:18:43 -0800 Subject: [PATCH] Mach-O alignment fix Several projects have taken to using .text to store read-only data when building on Mac OS X due to crashes in SSE code from the .rodata section being mis-aligned. It seems there was a misunderstanding about how ld/ld64 handles section alignment in outmacho.c so I wrote a patch to fix it. I tested it against x264 git, modified it to use ".rodata align=16" for the data section and use movdqa instructions (guaranteed to crash when built with unpatched nasm) and it passed all tests in its checkasm tool. If you want more data I can provide, but it's late and I've had a couple glasses of mulled wine :) -DrD- --- output/outmacho.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/output/outmacho.c b/output/outmacho.c index 06ae8ee8..30234a96 100644 --- a/output/outmacho.c +++ b/output/outmacho.c @@ -63,6 +63,7 @@ struct section { /* data that goes into the file */ char sectname[16]; /* what this section is called */ char segname[16]; /* segment this section will be in */ + uint32_t addr; /* in-memory address (subject to alignment) */ uint32_t size; /* in-memory and -file size */ uint32_t nreloc; /* relocation entry count */ uint32_t flags; /* type and attributes (masked) */ @@ -815,11 +816,24 @@ static void macho_calculate_sizes (void) /* count sections and calculate in-memory and in-file offsets */ for (s = sects; s != NULL; s = s->next) { + uint32_t pad = 0; + /* zerofill sections aren't actually written to the file */ if ((s->flags & SECTION_TYPE) != S_ZEROFILL) seg_filesize += s->size; - seg_vmsize += s->size; + /* recalculate segment address based on alignment and vm size */ + s->addr = seg_vmsize; + /* we need section alignment to calculate final section address */ + if (s->align == -1) + s->align = DEFAULT_SECTION_ALIGNMENT; + if(s->align) { + uint32_t newaddr = align(s->addr, 1 << s->align); + pad = newaddr - s->addr; + s->addr = newaddr; + } + + seg_vmsize += s->size + pad; ++seg_nsects; } @@ -854,7 +868,6 @@ static void macho_write_header (void) static uint32_t macho_write_segment (uint32_t offset) { - uint32_t s_addr = 0; uint32_t rel_base = alignint32_t (offset + seg_filesize); uint32_t s_reloff = 0; struct section *s; @@ -881,7 +894,7 @@ static uint32_t macho_write_segment (uint32_t offset) for (s = sects; s != NULL; s = s->next) { fwrite(s->sectname, sizeof(s->sectname), 1, machofp); fwrite(s->segname, sizeof(s->segname), 1, machofp); - fwriteint32_t(s_addr, machofp); + fwriteint32_t(s->addr, machofp); fwriteint32_t(s->size, machofp); /* dummy data for zerofill sections or proper values */ @@ -909,8 +922,6 @@ static uint32_t macho_write_segment (uint32_t offset) fwriteint32_t(s->flags, machofp); /* flags */ fwriteint32_t(0, machofp); /* reserved */ fwriteint32_t(0, machofp); /* reserved */ - - s_addr += s->size; } rel_padcnt = rel_base - offset; @@ -946,7 +957,7 @@ static void macho_write_section (void) struct section *s, *s2; struct reloc *r; char *rel_paddata = "\0\0\0"; - uint8_t fi, *p, *q, blk[4]; + uint8_t *p, *q, blk[4]; int32_t l; for (s = sects; s != NULL; s = s->next) { @@ -980,10 +991,10 @@ static void macho_write_section (void) offset which we already have. The linker takes care of the rest of the address. */ if (!r->ext) { - /* add sizes of previous sections to current offset */ - for (s2 = sects, fi = 1; - s2 != NULL && fi < r->snum; s2 = s2->next, fi++) - l += s2->size; + /* generate final address by section address and offset */ + s2 = get_section_by_index(r->snum); + if(s2) + l += s2->addr; // else what?!? } /* write new offset back */ -- 2.11.4.GIT