Getting the code size of a function in C

I need to determine the size of a function or module since I need to temporary relocate the function or module from flash into sram for firmware updates.

How can I find that out during runtime. The sizeof( myfunction) generates an error saying "size of function unknown".

Thanks.

Reply to
Tosca Berisha
Loading thread data ...

You'll need to do something in your linker script to create global symbols before and after the function. In your C code, you should then be able to take the address of those global symboals and do some math to determine the size.

--
Grant Edwards                   grante             Yow!  UH-OH!! We're out
                                  at               of AUTOMOBILE PARTS and
                               visi.com            RUBBER GOODS!
Reply to
Grant Edwards

If you're using GCC, probably you do not need to copy anything, just locate your function to the initialized data section:

/* Write unlock sequence */

static void unlock(void) __attribute__((section(".data"))); static void unlock(void) { FLASHMEM[KEYLOC1] = KEY1; FLASHMEM[KEYLOC2] = KEY2; }

This is the Flash unlock routine from an embedded system.

--

Tauno Voipio
tauno voipio (at) iki fi
Reply to
Tauno Voipio

That works, but it can be a bit of a waste since the function is sitting in both flash and RAM 100% of the time. If there are multiple routines that need to be run from RAM at different points in time, you can save a bit of RAM by overlaying them.

--
Grant Edwards                   grante             Yow!  I'll show you MY
                                  at               telex number if you show
                               visi.com            me YOURS...
Reply to
Grant Edwards

What you want to do is highly non-portable but try this working code snippet I use for the MSP430:

// Blinks the LED void codeFunction(void) { uartPrintf(": BLINK Press ESC to quit\r\n");

for(;;) { if(uartGetByte() == ESC) break;

LED_ON(P3OUT,BIT0); delay(100); LED_OFF(P3OUT,BIT0); delay(100); } }

void codeFunction_END(void) { }

// Executes the BLINK command void funcBLINK(int count,char *cmd,char *arg1,char *arg2) { byte codeRam[90]; size_t loadSize; void (*pfnFunction)(void);

USE(count); USE(cmd); USE(arg1); USE(arg2);

loadSize = (size_t)(codeFunction_END) - (size_t)(codeFunction); uartPrintf(": BLINK Load size = %u\r\n",loadSize);

if(loadSize >= sizeof(codeRam)) { uartPrintf(": BLINK Can't load code\r\n"); } else { // Copy the code to RAM memset(codeRam,0,sizeof(codeRam)); memcpy(codeRam,codeFunction,loadSize);

// Point to the function starting in RAM pfnFunction = (void (*)(void))(codeRam);

// Execute from RAM pfnFunction();

uartPrintf(": BLINK ok\r\n"); } }

Note that delay() and uartPrintf() and uartGetByte() do NOT get copied to RAM. That's part of the challenge.

JJS

Reply to
johnspeth

The amount of code needed in RAM for Flash writing is pretty small: about 250 bytes on an ARM for garden-variety AMD Flashes.

--

Tauno Voipio
tauno voipio (at) iki fi
Reply to
Tauno Voipio

But where is the "flash writing" code getting the data?

In cases I've dealt with the "flash writing" code has to impliment some sort of communications protocol in order to receive the data to be programmed into flash. In the product I'm working with right now, it takes 3KB of code to do a flash update. That's a pretty significant chunk out of my 32KB of RAM.

--
Grant Edwards                   grante             Yow!  Xerox your lunch
                                  at               and file it under "sex
                               visi.com            offenders"!
Reply to
Grant Edwards

You do not need to keep the whole chunk in RAM at the same time. Also, only the inner loop of a Flash writer is necessary to be kept in RAM, the rest can reside in the Flash. The code responsible for the handling from the unlock sequence till the chip responds ready to poll has to be in RAM, but this is for erasing a segment or writing a single byte. The rest of code can be safely in the Flash without any problems.

The incoming data is usually transferred in some kind of chunks (e.g. 128 bytes with Xmodem). It's no too difficult to keep one chunk in the RAM and read more when that's written.

--

Tauno Voipio
tauno voipio (at) iki fi
Reply to
Tauno Voipio

In ELF files generated by GCC, the symbol table has not only the name, type (FUNC), and address of each symbol, but its size as well. Thus the symbol table in the ELF file will tell you how big the code for the function is; something like objdump usually does not print out this information, but it is there.

Reply to
diablovision

You're right. I could split up the loader and keep part of it in flash, but it's extra work and it doesn't buy me anything. Once the decision is made to update flash, the entire RAM space becomes available. The data that's there belongs to an application that's being erased. It's a lot simpler to just copy the entire loader into RAM and run it from there. Up until that point, the loader is using 0 bytes of RAM.

It could, but there's no advantage to doing so.

Right.

--
Grant Edwards                   grante             Yow!  The Osmonds! You are
                                  at               all Osmonds!! Throwing up
                               visi.com            on a freeway at dawn!!!
Reply to
Grant Edwards

The ELF file isn't available to the program at run-time. It's quite simple to put global simbols at the beginning and end of a section. Then all you have to do in C is declare those symbols as external char arrays.

--
Grant Edwards                   grante             Yow!  I'm having an
                                  at               EMOTIONAL OUTBURST!! But,
                               visi.com            uh, WHY is there a WAFFLE
                                                   in my PAJAMA POCKET??
Reply to
Grant Edwards

... so just assume the function occupies all the RAM, and copy that many bytes.

Reply to
Mike Harrison

No way. A portable program has no way at all of finding this out, i.e. it cannot be done "in C" at all. And even a non-portable program, using features outside the C programming language proper, typically won't be able to do this at run-time. Stuff like this has to be set up at link time, the latest, and often you'll need to do it at compile time already. There's even a non-negligible probability that it can't be done at all, or only in assembly.

Some toolsets have special extensions (#pragma or __attribute__()) in the source, or some magic incantation to put in the linker commands) that tell the linker a particular function will be stored in a location different from the one it's meant to run from.

--
Hans-Bernhard Broeker (broeker@physik.rwth-aachen.de)
Even if all the snow were burnt, ashes would remain.
Reply to
Hans-Bernhard Broeker

Sure, but once you've put in a label so you know where the function starts, putting in a second one so you know where it ends only takes a couple more keystrokes.

--
Grant Edwards                   grante             Yow!  An air of FRENCH
                                  at               FRIES permeates my
                               visi.com            nostrils!!
Reply to
Grant Edwards

Except that unlike the single starting point, which usually must exist for the C function to be callable from unrelated translation units, there's no particular reason for a given C function to even *have* exactly one end where such a label could be put. Nor is there a requirement that the entry point be at the start of the code region for that function.

There's not even a requirement that a C compiler would have to translate a single function to a single, consequtive block of code. Optimization by function tail merging exists.

As I stated elsewhere in this thread: it's easily possible that on a given platform the only way of pulling this off is by coding all the relevant parts in assembler.

--
Hans-Bernhard Broeker (broeker@physik.rwth-aachen.de)
Even if all the snow were burnt, ashes would remain.
Reply to
Hans-Bernhard Broeker

Nonsense. There is some address X such that all bytes in the function have addresses less than X. Unless you've got some weird non-linear memory addressing scheme.

That's why adding global labels before and after the function is much safer than useing the function pointer for anything.

It may not be a requirement, but I've never seen a C compiler that didn't. This is comp.arch.EMBEDDED. We've got to work with real-world toolchains here, not some imaginary "could do anything the ISO spec allows" toolchain. comp.lang.c is that way --->

I've been doing embedded C for 20 years on a dozen different target architectures and at least as many toolchains. I've never seen a target/toolchain where what the OP wants isn't easily doable with some trivial linker-script hacking.

--
Grant Edwards                   grante             Yow!  WHOA!! Ken and
                                  at               Barbie are having TOO
                               visi.com            MUCH FUN!! It must be the
                                                   NEGATIVE IONS!!
Reply to
Grant Edwards

OK. Have a look at the Hi-Cross C Compiler. It translates every function into a separately linked unit. The units are linked in only if there are references to them. The addresses allocated by the linker for consecutive functions are not consecutive. For the reasons in this discussion (Flash writer) I tried to find the function sizes, but failed miserably.

The binary file created was also a jumble of criss- crossed function-size pieces, so that I had to make an extra pre-sorting and merge pass before writing to Flash.

My guess is that the functions were ordered by the stored call tree order collapsed in a weird way.

--

Tauno Voipio
tauno voipio (at) iki fi
Reply to
Tauno Voipio

Indeed, such an address X must exist --- but calling 'the end' of that function may be premature. There's no general way of making sure that between X and that other label, you'll have enclosed significantly less than the entire program, and even less that positioning such labels in the C source has any relation with the actual address range covered by (fragments of) the function.

And that's only scratching the surface. Next you'll have to worry about other functions being called by the function in question, often without the C source showing any sign of their existence. E.g. on some C-unfriendly architecture, accessing a structure element in an array of structures in "far" memory can easily cost 3 or more calls to "secret" C runtime library functions.

Then, with all due respect, you haven't been looking closely enough.

Let's stay right here, shall we? I hope we can agree that Keil C51 (current version) is quite definitely a realq-world toolchain, and as embedded as they come, right? So: inspect what its optimization options like "reuse common entry code", "common block subroutines", and particularly the machinery they call "linker code packing" can do.

Fact is that a single function can fall apart into multiple distinct blocks of code, which the linker can principally distribute all over the place, if it so wishes, and that toolchains doing this not actually exist, but are highly relevant in this newsgroup.

--
Hans-Bernhard Broeker (broeker@physik.rwth-aachen.de)
Even if all the snow were burnt, ashes would remain.
Reply to
Hans-Bernhard Broeker

Yup, that's exactly the same as the way gcc/ld works (at least that's how I use it for embedded work).

In the linker script isn't there any way to tell it to link in a "unit" and put that "unit" into a specified section of memory?

But the link should still put them into the specified output sections, shouldn't it?

--
Grant Edwards                   grante             Yow!  With YOU, I can be
                                  at               MYSELF... We don't NEED
                               visi.com            Dan Rather...
Reply to
Grant Edwards

I never said anything about position labels in C source. I thought I was quite explicit that I was talking about using the linker script for placing symbols before/after the memory section containing the function.

Of course.

Then relocating that function probably won't work if the called functions aren't available. It's quite easy to determine if that's the case by looking at the generated assembly.

Then don't use those optimizations.

With all the toolchains I've used, there was always a way to place a function into a distinct section of memory such that that section's start/end addresses could be made globally visible at link-time. It may have required placing the function in a separate file and not using optimizations, but it was never that hard. If the Keil 51 compiler really is that difficult to work with, then I'm glad I never had to use it.

--
Grant Edwards                   grante             Yow!  Excuse me, but didn't
                                  at               I tell you there's NO HOPE
                               visi.com            for the survival of OFFSET
                                                   PRINTING?
Reply to
Grant Edwards

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.