OSDev.org

The Place to Start for Operating System Developers
It is currently Tue Oct 20, 2020 1:20 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: C and the GNU assembler: how to deal with structs?
PostPosted: Wed Oct 16, 2019 12:14 pm 
Offline

Joined: Fri Apr 10, 2009 7:04 am
Posts: 11
Location: The Netherlands
Hi everyone,

For my task switching code i have a piece of assembly that has to deal with some structs from C.
In order to access the structs fields, i need to know the right offsets.
Of course i could hard-code them in my assembly, but this means that i have to change everything when i modify a struct in C.
Also i cannot include the struct definitions from C, since that is not understood by the assembler

So my question is: What is a good method to keep these structs synchronised between assembly and C?
I am using the GNU assembler


Top
 Profile  
 
 Post subject: Re: C and the GNU assembler: how to deal with structs?
PostPosted: Wed Oct 16, 2019 12:39 pm 
Offline
Member
Member

Joined: Tue Mar 04, 2014 5:27 am
Posts: 1042
AFAIR, you can still use #define in .S files, meaning you can include the same constants in C and assembly. That may not help with everything though.
Other options include: generating both from the same source, using tools to extract debug info out of C/C++ code (e.g. pahole, example).


Top
 Profile  
 
 Post subject: Re: C and the GNU assembler: how to deal with structs?
PostPosted: Wed Oct 16, 2019 12:53 pm 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 3961
Location: Chichester, UK
Probably not the best solution, but I wrote a simple program that reads the C header files and produces corresponding include files for the assembler files. This works out the offsets for structs and in the assembler files you use these offsets in conjunction with a register containing the base address of the struct.


Top
 Profile  
 
 Post subject: Re: C and the GNU assembler: how to deal with structs?
PostPosted: Wed Oct 16, 2019 10:16 pm 
Offline
Member
Member

Joined: Wed Aug 30, 2017 8:24 am
Posts: 662
iansjack's solution is also used by Linux. I'd wager there is no other solution. GNU as has no support for structures of any kind. At the moment, I'm going with manual maintenance, but even at my small project size, it is becoming annoying.

_________________
Thou hast outraged, not insulted me, sir; but for that I ask thee not to beware of Starbuck; thou wouldst but laugh; but let Ahab beware of Ahab; beware of thyself, old man.


Top
 Profile  
 
 Post subject: Re: C and the GNU assembler: how to deal with structs?
PostPosted: Thu Oct 17, 2019 10:10 am 
Offline
Member
Member
User avatar

Joined: Thu Nov 16, 2006 12:01 pm
Posts: 7480
Location: Germany
AVRbeginners.net: Accessing C Structs in Assembler

(iansjack's solution explained, including example code.)

_________________
Every good solution is obvious once you've found it.


Top
 Profile  
 
 Post subject: Re: C and the GNU assembler: how to deal with structs?
PostPosted: Fri Oct 18, 2019 5:09 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 937
I think that generating C structs into Assembly defines programatically is an unnecessary slow-down in the build process (even when it's just comparing file modification timestamps). From my personal experience, I don't change the structs that much. I personally have thrown out my converter script entirely.

So instead I do the following: I create a separated header file for each shared structs. The same files are then included by both Assembly and C. I start it with the field offset defines, then I have a C struct guarded by an "ifndef _AS" block. That way if I need to change the struct (rarely), then there's only one file to update (not two as with a converter), and I still won't leave out any Assembly reference for sure. Simple, and requires no additional tools.

Example:
Code:
#define tcb_magic 0
#define tcb_flags 4

#ifndef _AS
typedef struct {
    char[4] magic;
    uint32_t flags;
} tcb_t;
#endif

Surplus, you only have to define the field's offsets that are actually referenced from Assembly, meaning if you just add C-related fields to the struct at the end, no need to change the defines.

I haven't changed any of my struct for a while now, so I'm not sure, but maybe if you use gcc to compile your Assembly sources then you can use the "offsetof" in the defines. Depends whether the pre-compiler resolves those before it assigns the define or not.

Cheers,
bzt


Last edited by bzt on Fri Oct 18, 2019 5:15 am, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: C and the GNU assembler: how to deal with structs?
PostPosted: Fri Oct 18, 2019 5:13 am 
Offline
Member
Member
User avatar

Joined: Thu Nov 16, 2006 12:01 pm
Posts: 7480
Location: Germany
1) Those offsets could change depending on platform, compiler, compiler version, or compiler flags.

2) Never define "magic numbers" in source, especially not if you can derive them.

3) Doing this to avoid one little automatted step in your build system is exactly the kind of micropessimisation that Knuth labelled "the root of all evil". :wink:

_________________
Every good solution is obvious once you've found it.


Top
 Profile  
 
 Post subject: Re: C and the GNU assembler: how to deal with structs?
PostPosted: Fri Oct 18, 2019 5:24 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 937
Solar wrote:
1) Those offsets could change depending on platform, compiler, compiler version, or compiler flags.

2) Never define "magic numbers" in source, especially not if you can derive them.

3) Doing this to avoid one little automatted step in your build system is exactly the kind of micropessimisation that Knuth labelled "the root of all evil". :wink:
Yes, but you know the rules exactly how they could change. If you keep a simple rule: always start with the biggest member, then there would be no surprises.

If you're not dumb or an absolute amateur, then you can easily use packed structs too, aligning the members yourself. It is not THAT hard so it would worth slowing down the build process instead. But if you don't know your compiler, then sure, generate as much files as you want, maybe you miss to update one and failing D.R.Y. would be the "the root of all evil" ;-)

Cheers,
bzt


Top
 Profile  
 
 Post subject: Re: C and the GNU assembler: how to deal with structs?
PostPosted: Fri Oct 18, 2019 5:47 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 3961
Location: Chichester, UK
The point of a make file is to ensure that you don't miss any steps.

I've always thought that computers were there to do the easily defined leg work. Update one file and let the computer do the rest. I guess I'm just lazy.

But each to their own.


Top
 Profile  
 
 Post subject: Re: C and the GNU assembler: how to deal with structs?
PostPosted: Fri Oct 18, 2019 6:08 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 937
iansjack wrote:
The point of a make file is to ensure that you don't miss any steps.

I've always thought that computers were there to do the easily defined leg work. Update one file and let the computer do the rest. I guess I'm just lazy.

But each to their own.
That's correct.

However in this particular case (from my own experience), let's say at the early development phase,
N times you create a new shared struct
10N times you change one of the shared structs (this is an overestimate)
1000N times you run "make" (or whatever build system you have, and this is an underestimate)

Later, when your task switching code (or whatever part that needs C-Assembly shared structs) is tested and works perfectly,
0 times you create a new shared struct
0 times you change one of the shared structs
10000 times you run "make" (or even more)

So I still wouldn't say slowing down the build process worth it, but go ahead if that fits you. Each to their own.

Cheers,
bzt

ps.: Just for the records, I never had problems with my manually aligned packed structs, and my kernel is multi-platform. I had a problem once, but that's totally unrelated, as the unaligned abort was thrown in C code, and it would have caused problems in Assembly too, generated defines or not. It's not that you can rearrange a packed BPB struct's members.


Top
 Profile  
 
 Post subject: Re: C and the GNU assembler: how to deal with structs?
PostPosted: Fri Oct 18, 2019 6:33 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 3961
Location: Chichester, UK
How does it slow down the build process? I doubt that you could measure the time it takes for make to check whether a header file has changed.


Top
 Profile  
 
 Post subject: Re: C and the GNU assembler: how to deal with structs?
PostPosted: Tue Oct 22, 2019 6:15 am 
Offline
Member
Member
User avatar

Joined: Mon May 22, 2017 5:56 am
Posts: 320
If it slows down the build process, the scripting language interpreter is not optimized for the task. For instance, Python infamously takes time to bind symbols before starting to run the script. Or you're feeding a galaxy-size header file to an interpreter which is slow at run-time. In either case, a different interpreter may be fine. I keep coming up with other issues and shooting them down because I can't think of anything that would add more than a 1-second delay to the total build process, or even that much!

The worst-case slow string-processing script experience i ever had was running about a dozen script files totalling about 900 lines as a web server + CMS on a 466MHz PPC running OS X. It was too slow for me to be happy with it serving my web site, but if it had been a compiler I would have thought the compile time was very good! ;) On multicore machines, the speed wasn't an issue at all. OS X was the worst OS for the task by a small margin, Plan 9 was better and Linux better still, but multicore made much more difference. But this is all only relevant to long pipelines which will not be present when running a single interpreter to do a comparatively simple bit of header parsing. It's more comparable to just the markdown processor; just one of the scripts within my worst-case example.

Now I'm tempted to try the Amazing Awk Assembler.


Top
 Profile  
 
 Post subject: Re: C and the GNU assembler: how to deal with structs?
PostPosted: Mon Oct 28, 2019 10:46 am 
Offline
Member
Member
User avatar

Joined: Tue Mar 06, 2007 11:17 am
Posts: 1224
gcc hello.c -S -save-temps
Code:
#include <stdio.h>

int main(int argc, char **argv)
{

return 0;
}




hello.s
Code:
   .file   "hello.c"
   .section .text
   .globl   _main
_main:
LFB0:
   .cfi_startproc
   pushl   %ebp
   .cfi_def_cfa_offset 8
   .cfi_offset 5, -8
   movl   %esp, %ebp
   .cfi_def_cfa_register 5
   movl   $0, %eax
   popl   %ebp
   .cfi_restore 5
   .cfi_def_cfa 4, 4
   ret
   .cfi_endproc
LFE0:
   .ident   "GCC: (GNU) 6.1.0"




hello.i
Code:
# 1 "hello.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "hello.c"
# 1 "c:/djgpp/include/stdio.h" 1 3
# 19 "c:/djgpp/include/stdio.h" 3
# 1 "c:/djgpp/include/sys/version.h" 1 3
# 20 "c:/djgpp/include/stdio.h" 2 3
# 1 "c:/djgpp/include/sys/djtypes.h" 1 3
# 43 "c:/djgpp/include/sys/djtypes.h" 3

# 43 "c:/djgpp/include/sys/djtypes.h" 3
typedef short __attribute__((__may_alias__)) __dj_short_a;
typedef int __attribute__((__may_alias__)) __dj_int_a;
typedef long __attribute__((__may_alias__)) __dj_long_a;
typedef long long __attribute__((__may_alias__)) __dj_long_long_a;
typedef unsigned short __attribute__((__may_alias__)) __dj_unsigned_short_a;
typedef unsigned int __attribute__((__may_alias__)) __dj_unsigned_int_a;
typedef unsigned long __attribute__((__may_alias__)) __dj_unsigned_long_a;
typedef unsigned long long __attribute__((__may_alias__)) __dj_unsigned_long_long_a;
typedef float __attribute__((__may_alias__)) __dj_float_a;
typedef double __attribute__((__may_alias__)) __dj_double_a;
typedef long double __attribute__((__may_alias__)) __dj_long_double_a;
# 21 "c:/djgpp/include/stdio.h" 2 3
# 48 "c:/djgpp/include/stdio.h" 3
typedef __builtin_va_list va_list;




typedef long unsigned int size_t;




typedef long signed int ssize_t;







typedef struct {
  ssize_t _cnt;
  char *_ptr;
  char *_base;
  size_t _bufsiz;
  int _flag;
  int _file;
  char *_name_to_remove;
  size_t _fillsize;
} FILE;

typedef unsigned long fpos_t;

extern FILE __dj_stdin, __dj_stdout, __dj_stderr;




void clearerr(FILE *_stream);
int fclose(FILE *_stream);
int feof(FILE *_stream);
int ferror(FILE *_stream);
int fflush(FILE *_stream);
int fgetc(FILE *_stream);
int fgetpos(FILE *_stream, fpos_t *_pos);
char * fgets(char *_s, int _n, FILE *_stream);
FILE * fopen(const char *_filename, const char *_mode);
int fprintf(FILE *_stream, const char *_format, ...);
int fputc(int _c, FILE *_stream);
int fputs(const char *_s, FILE *_stream);
size_t fread(void *_ptr, size_t _size, size_t _nelem, FILE *_stream);
FILE * freopen(const char *_filename, const char *_mode, FILE *_stream);
int fscanf(FILE *_stream, const char *_format, ...);
int fseek(FILE *_stream, long _offset, int _mode);
int fsetpos(FILE *_stream, const fpos_t *_pos);
long ftell(FILE *_stream);
size_t fwrite(const void *_ptr, size_t _size, size_t _nelem, FILE *_stream);
int getc(FILE *_stream);
int getchar(void);
char * gets(char *_s);
void perror(const char *_s);
int printf(const char *_format, ...);
int putc(int _c, FILE *_stream);
int putchar(int _c);
int puts(const char *_s);
int remove(const char *_filename);
int rename(const char *_old, const char *_new);
void rewind(FILE *_stream);
int scanf(const char *_format, ...);
void setbuf(FILE *_stream, char *_buf);
int setvbuf(FILE *_stream, char *_buf, int _mode, size_t _size);
int sprintf(char *_s, const char *_format, ...);
int sscanf(const char *_s, const char *_format, ...);
FILE * tmpfile(void);
char * tmpnam(char *_s);
int ungetc(int _c, FILE *_stream);
int vfprintf(FILE *_stream, const char *_format, va_list _ap);
int vprintf(const char *_format, va_list _ap);
int vsprintf(char *_s, const char *_format, va_list _ap);




int snprintf(char *str, size_t n, const char *fmt, ...);
int vfscanf(FILE *_stream, const char *_format, va_list _ap);
int vscanf(const char *_format, va_list _ap);
int vsnprintf(char *str, size_t n, const char *fmt, va_list ap);
int vsscanf(const char *_s, const char *_format, va_list _ap);
# 143 "c:/djgpp/include/stdio.h" 3
int dprintf(int _fd, const char *_format, ...) __attribute__ ((__format__ (__printf__, 2, 3)));
int fileno(FILE *_stream);
FILE * fdopen(int _fildes, const char *_type);
int mkstemp(char *_template);
int pclose(FILE *_pf);
FILE * popen(const char *_command, const char *_mode);
char * tempnam(const char *_dir, const char *_prefix);
int vdprintf(int _fd, const char *_format, va_list _ap) __attribute__ ((__format__ (__printf__, 2, 0)));



extern FILE __dj_stdprn, __dj_stdaux;





void _djstat_describe_lossage(FILE *_to_where);
int _doprnt(const char *_fmt, va_list _args, FILE *_f);
int _doscan(FILE *_f, const char *_fmt, va_list _args);
int _doscan_low(FILE *, int (*)(FILE *_get), int (*_unget)(int, FILE *), const char *_fmt, va_list _args);
int fpurge(FILE *_f);
int getw(FILE *_f);
char * mktemp(char *_template);
int putw(int _v, FILE *_f);
void setbuffer(FILE *_f, void *_buf, int _size);
void setlinebuf(FILE *_f);
int _rename(const char *_old, const char *_new);
int asprintf(char **_sp, const char *_format, ...) __attribute__((format (__printf__, 2, 3)));
char * asnprintf(char *_s, size_t *_np, const char *_format, ...) __attribute__((format (__printf__, 3, 4)));
int vasprintf(char **_sp, const char *_format, va_list _ap) __attribute__((format (__printf__, 2, 0)));
char * vasnprintf(char *_s, size_t *_np, const char *_format, va_list _ap) __attribute__((format (__printf__, 3, 0)));


typedef int off_t;



__extension__ typedef long long off64_t;


int fseeko(FILE *_stream, off_t _offset, int _mode);
off_t ftello(FILE *_stream);
int fseeko64(FILE *_stream, off64_t _offset, int _mode);
off64_t ftello64(FILE *_stream);
# 2 "hello.c" 2


# 3 "hello.c"
int main(int argc, char **argv)
{

return 0;
}




Problems include the actual address of each member due to packing.

The best is to always inspect the structure of the produced assembly and produce portable labels to reach the data.

Also, direct the compiler in a way that will always produce code/data exactly at the addresses we physically want in the binary.

_________________
http://www.ebay.com/usr/udocproject3 (Updated IP)
http://190.53.102.175/api/doc/en/ (My OS compatible with DOS)

(udocproject@yahoo.com)
-----------------------------
IP for hosts file (all domains):
190.53.102.175 api.exe


Top
 Profile  
 
 Post subject: Re: C and the GNU assembler: how to deal with structs?
PostPosted: Mon Oct 28, 2019 1:18 pm 
Offline
Member
Member
User avatar

Joined: Tue Mar 06, 2007 11:17 am
Posts: 1224
bzt wrote:
I think that generating C structs into Assembly defines programatically is an unnecessary slow-down in the build process (even when it's just comparing file modification timestamps). From my personal experience, I don't change the structs that much. I personally have thrown out my converter script entirely.

So instead I do the following: I create a separated header file for each shared structs. The same files are then included by both Assembly and C. I start it with the field offset defines, then I have a C struct guarded by an "ifndef _AS" block. That way if I need to change the struct (rarely), then there's only one file to update (not two as with a converter), and I still won't leave out any Assembly reference for sure. Simple, and requires no additional tools.

Example:
Code:
#define tcb_magic 0
#define tcb_flags 4

#ifndef _AS
typedef struct {
    char[4] magic;
    uint32_t flags;
} tcb_t;
#endif

Surplus, you only have to define the field's offsets that are actually referenced from Assembly, meaning if you just add C-related fields to the struct at the end, no need to change the defines.

I haven't changed any of my struct for a while now, so I'm not sure, but maybe if you use gcc to compile your Assembly sources then you can use the "offsetof" in the defines. Depends whether the pre-compiler resolves those before it assigns the define or not.

Cheers,
bzt
You start modifying in assembly and end up in C.

_________________
http://www.ebay.com/usr/udocproject3 (Updated IP)
http://190.53.102.175/api/doc/en/ (My OS compatible with DOS)

(udocproject@yahoo.com)
-----------------------------
IP for hosts file (all domains):
190.53.102.175 api.exe


Top
 Profile  
 
 Post subject: Re: C and the GNU assembler: how to deal with structs?
PostPosted: Wed Oct 30, 2019 7:00 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 937
~ wrote:
You start modifying in assembly and end up in C.
That's a bad habit you should quit ASAP. The whole point in having both Assembly and C declarations in the same header file is to stop you from doing it separately. Keep your asm defines and C structs synchronized, ALWAYS. You can avoid lots of trouble and endless hours of debugging by doing so.

Cheers,
bzt


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next

All times are UTC - 6 hours


Who is online

Users browsing this forum: Majestic-12 [Bot] and 4 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group